#
# @include "_analyzer_struct.mro"
#

#
# Copyright (c) 2022 10X Genomics, Inc. All rights reserved.
#

filetype csv;
filetype h5;
filetype html;
filetype json;
filetype pickle;
filetype binary;
#
# @include "_sc_antibody_analyzer_stages.mro"
#

#
# Copyright (c) 2021 10X Genomics, Inc. All rights reserved.
#

filetype csv;
filetype pdf;
filetype h5;
filetype json;
#
# @include "_antibody_analyzer.mro"
#

filetype pdf;
filetype csv;
filetype h5;
filetype json;
#
# @include "_cr_ana_stages.mro"
#

# Copyright 2023 10x Genomics, Inc. All rights reserved.
#
# Code generated by cr_ana.  DO NOT EDIT.
#

filetype bincode.lz4;
filetype h5;
filetype npy;
#
# @include "_batch_correction_pca.mro"
#

filetype pickle;
#
# @include "_run_kmeans.mro"
#

filetype h5;
#
# @include "_sc_rna_analyzer_stages.mro"
#

#
# Copyright (c) 2019 10X Genomics, Inc. All rights reserved.
#

filetype csv;
filetype h5;
filetype html;
filetype json;
filetype pickle;
filetype binary;
#
# @include "_cr_aggr_stages.mro"
#

# Copyright 2023 10x Genomics, Inc. All rights reserved.
#
# Code generated by cr_aggr.  DO NOT EDIT.
#

filetype bdf.bincode;
filetype csv;
filetype em.json;
filetype fa;
filetype fasta;
filetype h5;
filetype html;
filetype json;
filetype pb;
filetype tsv;
filetype vloupe;
#
# @include "_sc_crispr_analyzer_stages.mro"
#

#
# Copyright (c) 2018 10X Genomics, Inc. All rights reserved.
#

filetype csv;
filetype pdf;
filetype h5;
filetype json;
#
# @include "_crispr_analyzer.mro"
#

filetype pdf;
filetype csv;
filetype h5;
filetype json;
#
# @include "_sc_rna_aggregator_stages.mro"
#

#
# Copyright (c) 2022 10X Genomics, Inc. All rights reserved.
#

filetype bam;
filetype bam.bai;
filetype csv;
filetype tsv;
filetype fastq;
filetype json;
filetype h5;
filetype html;
filetype pickle;
#
# @include "_cr_vdj_stages.mro"
#

# Copyright 2023 10x Genomics, Inc. All rights reserved.
#
# Code generated by cr_vdj.  DO NOT EDIT.
#

filetype arp.bincode;
filetype bam;
filetype bam.bai;
filetype bd.bincode;
filetype bdf.bincode;
filetype bed;
filetype bin;
filetype bincode;
filetype bincode.lz4;
filetype csv;
filetype em.json;
filetype fa;
filetype fasta;
filetype fasta.fai;
filetype fastq;
filetype fprint.json;
filetype h5;
filetype html;
filetype json;
filetype json.lz4;
filetype parquet;
filetype pb;
filetype tsv;
filetype txt;
#
# @include "_cell_annotation_common_stages.mro"
#

filetype csv;
filetype h5;
filetype json.gz;
filetype json;
filetype cloupe;
#
# @include "_cr_lib_stages.mro"
#

# Copyright 2023 10x Genomics, Inc. All rights reserved.
#
# Code generated by cr_lib.  DO NOT EDIT.
#

filetype ag.vdj.bincode;
filetype ann.bincode.lz4;
filetype asf;
filetype bam;
filetype bam.bai;
filetype bam.csi;
filetype bcc.bincode;
filetype bcm.bincode;
filetype bi.bincode;
filetype bincode;
filetype bincode.lz4;
filetype blf.json;
filetype bmsf;
filetype bsc.bincode;
filetype bsf.bincode;
filetype bui;
filetype cmf.bincode;
filetype csf;
filetype csv;
filetype fbc.bincode;
filetype fprint.json;
filetype frf.bincode;
filetype h5;
filetype html;
filetype json;
filetype msh.bincode;
filetype msm.bincode;
filetype parquet;
filetype rpc;
filetype shard;
filetype smf.json;
filetype svg;
filetype tbcc.bincode;
filetype tps.json;
filetype umi;
filetype vwc.json;
#
# @include "cell_annotation_service.mro"
#

filetype csv;
filetype h5;
filetype html;
filetype json;
filetype json.gz;
filetype png;
filetype h5ad;
filetype cloupe;
filetype sring;
filetype fload;
#
# @include "_assign_tags_stages.mro"
#

#
# Copyright (c) 2021 10X Genomics, Inc. All rights reserved.
#

filetype csv;
filetype h5;
filetype json;
filetype pickle;
#
# @include "_assign_tags.mro"
#

filetype csv;
filetype h5;
filetype json;
filetype html;
filetype pickle;
#
# @include "_v1_pattern_fix.mro"
#

#
# Copyright 2024 10x Genomics, Inc. All rights reserved.
#

filetype h5;
#
# @include "_basic_sc_rna_counter_stages.mro"
#

filetype bam;
filetype bam.bai;
filetype csv;
filetype fastq;
filetype json;
filetype h5;
filetype pickle;
filetype bincode;
#
# @include "_common_cloupe_stages.mro"
#

#
# Copyright (c) 2016 10X Genomics, Inc. All rights reserved.
#

filetype cloupe;
filetype csv;
filetype json;
filetype h5;
filetype txt;
#
# @include "_common_stages.mro"
#

#
# Copyright (c) 2017 10X Genomics, Inc. All rights reserved.
#

filetype bam;
filetype bam.bai;
filetype bam.csi;
filetype csv;
filetype fastq;
filetype json;
filetype tps.json;
filetype pickle;
filetype fa;
filetype h5;
#
# @include "_sc_rna_counter_stages.mro"
#

filetype csv;
filetype json;
filetype h5;
filetype html;
filetype tps.json;
#
# @include "_sc_vdj_assembler_stages.mro"
#

filetype bam;
filetype bam.bai;
filetype sam;
filetype fasta;
filetype fasta.fai;
filetype fastq;
filetype fastq.lz4;
filetype h5;
filetype json;
filetype pickle;
filetype gtf;
filetype csv;
filetype tsv;
filetype html;
filetype lz4;
filetype bin;
filetype txt;
#
# @include "_sc_rna_targeted_analyzer_stages.mro"
#

filetype csv;
filetype pdf;
filetype h5;
filetype json;
filetype fa;
filetype tps.json;
#
# @include "_targeted_analyzer.mro"
#

filetype csv;
filetype h5;
filetype json;
#
# @include "_vloupe_stages.mro"
#

#
# Copyright (c) 2017 10X Genomics, Inc. All rights reserved.
#

filetype csv;
filetype h5;
filetype pb;
filetype vloupe;
#
# @include "_sc_multi_defs.mro"
#

filetype bam;
filetype bam.bai;
filetype bam.csi;
filetype html;
filetype json;
filetype vloupe;
filetype svg;
filetype ChemistryDefs;
#
# @include "sc_vdj_aggregator.mro"
#

filetype json;
filetype vloupe;

#
# @include "_analyzer_struct.mro"
#

struct AnalyzerInputs(
    h5     filtered_matrices_h5,
    h5     molecule_info,
    map[]  aggr_library_info,
    bool   no_secondary_analysis,
    csv    use_genes,
    csv    exclude_genes,
    csv    use_bcs,
    int    num_analysis_bcs,
    int    random_seed,
    int    num_pca_bcs,
    int    num_pca_genes,
    int    num_principal_comps,
    bool   chemistry_batch_correction,
    bool   is_spatial,
    bool   is_visium_hd,
    bool   is_pd,
    int    cbc_knn,
    float  cbc_alpha,
    float  cbc_sigma,
    bool   cbc_realign_panorama,
    int    max_clusters,
    int    graphclust_neighbors,
    float  neighbor_a,
    float  neighbor_b,
    float  graphclust_resolution,
    int    tsne_perplexity,
    int    tsne_input_pcs,
    int    tsne_max_dims,
    int    tsne_max_iter,
    int    tsne_stop_lying_iter,
    int    tsne_mom_switch_iter,
    float  tsne_theta,
    string umap_implementation,
    int    umap_n_neighbors,
    int    umap_input_pcs,
    int    umap_max_dims,
    float  umap_min_dist,
    string umap_metric,
    int    force_cells,
    bool   skip_multigenome_analysis,
    bool   enable_tsne,
)

struct AnalyzerOutputs(
    path analysis,
    path analysis_csv,
    h5   cloupe_matrix_h5,
    json summary,
    bool skip_tsne,
)

#
# @include "_cr_ana_stages.mro"
#

struct PcaOutputs(
    h5   pca_h5,
    path pca_csv,
)

#
# @include "_sc_rna_analyzer_stages.mro"
#

struct PcaOutputs(
    h5   pca_h5,
    path pca_csv,
)

#
# @include "_cr_aggr_stages.mro"
#

struct VdjAggrCsvLibrary(
    string      library_id,
    path        vdj_contig_info,
    string      donor,
    string      origin,
    map<string> meta,
)

struct VdjAggrInput(
    VdjAggrCsvLibrary[] libraries,
)

struct AntigenAggrResults(
    csv antigen_specificity_scores,
    csv per_barcode_csv,
)

struct VdjAggrResults(
    tsv    airr_rearrangement,
    csv    clonotypes,
    fa     donor_regions,
    fasta  consensus_fasta,
    csv    filtered_contig_annotations_csv,
    csv    consensus_annotations_csv,
    json   web_summary_data,
    vloupe vloupe,
    html   filter_summary,
    pb     enclone_output,
)

#
# @include "_cr_vdj_stages.mro"
#

struct VdjRefFastaFolder(
    fa regions,
)

struct VdjRefFolder(
    VdjRefFastaFolder fasta,
    json              reference,
)

struct FilterSwitch(
    bool asm_shared_contig     "Turn on/off filters in the assembler that makes use of shared contigs to detect potential artifacts",
    bool enclone_shared_contig "Turn on/off enclone filters that makes use of shared contigs across clonotypes to detect",
    bool enclone_multiplet     "Filters that remove putative doublets",
    bool enclone_umi           "Turn on/off UMI thresholds in enclone",
    bool enclone_cross         "Turn on/off enclone filters that detect fake clonotype expansions",
)

struct ContigAnnotationSource(
    json merged_liblevel,
    json post_cell_filtering,
    json post_clonotyping,
)

struct WhitelistSpec(
    string name,
    bool   translation,
    file   translation_whitelist_path,
    string slide,
    string part,
)

struct BarcodeReadComponent(
    string        read_type,
    string        kind,
    int           offset,
    int           length,
    WhitelistSpec whitelist,
)

struct UmiWhitelistSpec(
    string slide,
    string part,
    string translation,
)

struct UmiReadComponent(
    string           read_type,
    int              offset,
    int              length     "The length of the UMI. At most this number of bases will be extracted for use as a UMI.",
    int              min_length "If a shorter UMI can be used, add it here. None indicates that the full",
    UmiWhitelistSpec whitelist,
)

struct RnaReadComponent(
    string read_type,
    int    offset,
    int    length,
    int    min_length,
)

struct ChemistryDef(
    string                 name,
    string                 description,
    string                 endedness,
    string                 strandedness,
    BarcodeReadComponent[] barcode,
    UmiReadComponent[]     umi,
    RnaReadComponent       rna,
    RnaReadComponent       rna2,
    map                    barcode_extraction,
)

struct VdjPrimers(
    int[][] inner_primers,
    int[][] outer_primers,
)

#
# @include "_cell_annotation_common_stages.mro"
#

struct CellTypeResults(
    csv     cell_types,
    json.gz results,
    json    metadata,
    float   frac_returned_bcs,
    string  model_used,
    bool    skip_downstream,
)

#
# @include "_cr_lib_stages.mro"
#

struct ReadShards(
    shard[] valid_reads,
    shard[] corrected_reads,
    shard[] invalid_reads,
)

struct WhitelistSpec(
    string name,
    bool   translation,
    file   translation_whitelist_path,
    string slide,
    string part,
)

struct BarcodeReadComponent(
    string        read_type,
    string        kind,
    int           offset,
    int           length,
    WhitelistSpec whitelist,
)

struct UmiWhitelistSpec(
    string slide,
    string part,
    string translation,
)

struct UmiReadComponent(
    string           read_type,
    int              offset,
    int              length     "The length of the UMI. At most this number of bases will be extracted for use as a UMI.",
    int              min_length "If a shorter UMI can be used, add it here. None indicates that the full",
    UmiWhitelistSpec whitelist,
)

struct RnaReadComponent(
    string read_type,
    int    offset,
    int    length,
    int    min_length,
)

struct ChemistryDef(
    string                 name,
    string                 description,
    string                 endedness,
    string                 strandedness,
    BarcodeReadComponent[] barcode,
    UmiReadComponent[]     umi,
    RnaReadComponent       rna,
    RnaReadComponent       rna2,
    map                    barcode_extraction,
)

struct AnnotationFiles(
    int               num_reads,
    ann.bincode.lz4[] files,
)

struct VdjFilterFlags(
    bool multiplet_filter,
    bool shared_contig_filter,
    bool umi_baseline_filter,
)

struct VdjGenInputs(
    path           reference_path,
    path           vdj_reference_path,
    int            min_contig_length,
    VdjFilterFlags filter_flags,
    bool           skip_clonotyping,
)

struct SampleMetrics(
    string sample,
    json   summary,
    csv    per_barcode_metrics,
)

struct SpecificityControls(
    map<string> control_for_allele,
    bool        has_mhc_allele_column,
)

struct FeatureConfig(
    string              beam_mode,
    SpecificityControls specificity_controls,
    map<string>         functional_map,
    string[]            hashtag_ids,
)

struct GemWellFiles(
    int[]           gem_groups,
    asf[]           alignments,
    map[]           read_chunks,
    bui[]           bc_umi_info,
    bmsf[]          per_barcode_metrics_shard,
    AnnotationFiles annotation_files,
    string          target_set_name,
    path            bam_header,
    frf.bincode     slfe_feature_reference,
)

struct FileOrBytes(
    file   file,
    string bytes,
)

struct CommonInputs(
    string sample_id,
    string sample_desc,
    string multi_config_sha,
)

struct CellCallingParam(
    float      per_gem_well,
    map<float> per_sample,
)

struct CellCalling(
    CellCallingParam recovered_cells,
    CellCallingParam force_cells,
    CellCallingParam emptydrops_minimum_umis,
    CellCallingParam global_minimum_umis,
    CellCallingParam max_mito_percent,
    json             cell_barcodes,
    string           override_mode,
    string[]         override_library_types,
    bool             disable_ab_aggregate_detection,
    bool             disable_high_occupancy_gem_detection,
)

struct BarcodeAssignments(
    json sample_barcodes,
    json non_singlet_barcodes,
    json cells_per_tag,
)

struct CountInputs(
    map[]              sample_def,
    csv                target_set,
    string             target_set_name,
    map<string>        chemistry_specs,
    ChemistryDef       custom_chemistry_def,
    path               reference_path,
    json               gene_index,
    map[]              primers,
    CellCalling        cell_calling_config,
    float              subsample_rate,
    int                initial_reads,
    int                primer_initial_reads,
    string[]           special_genomic_regions,
    int                r1_length,
    int                r2_length,
    int                trim_polya_min_score,
    int                trim_tso_min_score,
    bool               no_secondary_analysis,
    bool               no_target_umi_filter,
    bool               filter_probes,
    csv                feature_reference,
    bool               include_exons,
    bool               include_introns,
    string             targeting_method,
    string             aligner,
    map                genetic_demux_params,
    string             throughput,
    bool               check_library_compatibility,
    bool               no_bam,
    BarcodeAssignments force_sample_barcodes,
    bool               tenx_cmos,
    float              min_assignment_confidence,
    int                min_crispr_umi_threshold,
    csv[]              annotations,
    string             cell_annotation_model,
    bool               skip_cell_annotation,
    string             tenx_cloud_token_path,
    bool               enable_tsne,
)

struct VdjInputs(
    map[]        sample_def,
    string       chemistry_spec,
    ChemistryDef custom_chemistry_def,
    map[]        primers,
    float        subsample_rate,
    int          initial_reads,
    int          primer_initial_reads,
    string[]     special_genomic_regions,
    bool         denovo,
    int          r1_length,
    int          r2_length,
    path         inner_enrichment_primers,
    string       chain_type,
    string       physical_library_id,
)

struct BasicPipelineConfig(
    bool disable_count,
    bool disable_vdj,
    bool disable_multi       "boolean to disable stages that are only needed in the multi pipeline",
    bool disable_multi_count "boolean to disable stages that are only needed when count libraries are",
    bool disable_rtl         "boolean to disable stages that are only needed when probes are present",
    bool disable_annotate    "boolean to disable annotate stages",
)

struct BeamAnalyzerOutputs(
    csv            antigen_specificity_scores,
    csv            antigen_assignment,
    csv            clonotype_concordance,
    csv            exact_subclonotype_concordance,
    json           specificity_summary,
    json           antigen_vdj_metrics_json,
    ag.vdj.bincode antigen_vdj_metrics_bin,
    csv            per_barcode,
)

struct VdjAnalysisConfig(
    bool per_sample,
    bool is_multi,
    bool has_no_vdj_ref,
    bool denovo,
    bool has_antigen,
    bool skip_clonotyping,
)

struct GexMatrices(
    h5  filtered_matrix_h5,
    h5  raw_matrix_h5,
    csv filtered_barcodes,
)

struct VdjDemuxSampleInfo(
    string      sample_id,
    GexMatrices gex_matrices,
    fprint.json fingerprint,
)

struct SampleMatrices(
    string sample,
    h5     filtered_matrix_h5,
    path   filtered_matrix_mex,
    h5     raw_matrix_h5,
    h5     raw_probe_bc_matrix,
    path   raw_matrix_mex,
    csv    filtered_barcodes,
    csv    aggregate_barcodes,
    csv    per_probe_metrics,
)

struct SampleMoleculeInfo(
    string sample,
    h5     h5_file,
    json   summary,
)

struct SampleBamFile(
    string  sample,
    bam     bam_file,
    bam.bai bai_index_file,
    bam.csi csi_index_file,
)

#
# @include "cell_annotation_service.mro"
#

struct CellTypes(
    csv     cell_types,
    json.gz cell_annotation_results,
    html    web_summary_cell_types,
    csv     cell_annotation_differential_expression,
)

#
# @include "_assign_tags_stages.mro"
#

struct BarcodeAssignments(
    json sample_barcodes,
    json non_singlet_barcodes,
    json cells_per_tag,
)

struct AssignTagsOuts(
    BarcodeAssignments force_sample_barcodes,
    json               sample_barcodes               "Maps sample names to list of barcodes"                                                                          "sample_barcodes.json",
    json               sample_cell_barcodes,
    csv                tag_calls_per_cell            "Specifies tag assignments per cell"                                                                             "tag_calls_per_cell.csv",
    csv                tag_calls_summary             "Summarizes basic statistics about tag assignments"                                                              "tag_calls_summary",
    csv                frp_gem_barcode_overlap       "Gel-bead barcodes in common for all pairs of probe barcodes"                                                    "frp_gem_barcode_overlap.csv",
    csv                assignment_confidence_table   "Lists the posterior probabilities for tag assignments provided by JIBES model"                                  "assignment_confidence_table",
    json               cells_per_tag                 "Provides a JSON that lists, for each tag, the cells it has been assigned to"                                    "cells_per_tag.json",
    json               non_singlet_barcodes          "Provides a JSON that lists, for each non-tag assignment (blanks/unassigned), the cells it has been assigned to" "non_singlet_barcodes.json",
    csv                tag_umi_thresholds_csv        "tag UMI thresholds csv"                                                                                         "tag_umi_thresholds_csv.csv",
    csv                marginal_tag_frequencies      "marginal_tag_frequencies"                                                                                       "marginal_tag_frequencies.csv",
    csv                jibes_model_summary           "jibes_model_summary"                                                                                            "jibes_model_summary.csv",
    json               jibes_parameters              "jibes_parameters"                                                                                               "jibes_parameters.json",
    json               jibes_summary_data            "jibes_summary_data"                                                                                             "jibes_summary_data.json",
    json               tag_call_metrics              "tag_call_metrics"                                                                                               "tag_call_metrics.json",
    json               tag_umi_thresholds_json       "tag_umi_thresholds_json"                                                                                        "tag_umi_thresholds_json.json",
    json               tag_contaminant_info          "tag_contaminant_info"                                                                                           "tag_contaminant_info.json",
    pickle             tag_assigner_pickle           "tag_assigner_pickle"                                                                                            "tag_assigner_pickle.pickle",
    map<json>          sample_assignment_metrics     "Per-sample sample assignment summary metrics"                                                                   "sample_assignment_metrics.json",
    json               gem_well_inferred_throughputs "Gem well inferred throughput"                                                                                   "gem_well_inferred_throughputs.json",
    string             multiplexing_method,
    bool               output_per_sample_raw_matrix,
)

#
# @include "_v1_pattern_fix.mro"
#

struct V1PatternFixArgs(
    h5  v1_filtered_fbm,
    int v1_pattern_type,
)

#
# @include "_basic_sc_rna_counter_stages.mro"
#

struct ProbeBCDef(
    string   id,
    string[] sequence,
    int      offset,
    int      length,
)

#
# @include "_common_stages.mro"
#

# duplicating this here to avoid circular dependency
struct SampleSlfeOuts(
    string  sample,
    bam     bam_file,
    bam.bai bai_index_file,
    bam.csi csi_index_file,
    json    metrics_summary,
    csv     per_barcode_metrics,
    h5      molecule_info,
    h5      filtered_matrix_h5,
    path    filtered_matrix_mex,
    h5      raw_matrix_h5,
    h5      raw_probe_bc_matrix,
    path    raw_matrix_mex,
    csv     filtered_barcodes,
    csv     aggregate_barcodes,
    csv     feature_reference,
    csv     target_panel,
    csv     probe_set,
    csv     per_probe_metrics,
)

struct CellCallingParam(
    float      per_gem_well,
    map<float> per_sample,
)

#
# @include "_sc_multi_defs.mro"
#

###############################################################################
# Pipeline configuration that tells you which pipelines among count and vdj
# needs to be disabled. Any sub-pipeline that has stages from both count and
# vdj will accept this struct as one of the inputs
###############################################################################
struct FullPipelineConfig(
    bool disable_count,
    bool disable_vdj_t,
    bool disable_vdj_t_gd,
    bool disable_vdj_b,
    bool has_no_vdj_ref,
    bool disable_multi,
    bool disable_multi_count,
)

struct VdjInputsCS(
    map[]  sample_def,
    bool   denovo,
    path   inner_enrichment_primers,
    string chain_type,
    string physical_library_id,
    int    r1_length,
    int    r2_length,
    string chemistry_spec,
)

struct CountInputsCS(
    map[]              sample_def,
    csv                target_set,
    string             target_set_name,
    path               reference_path,
    json               gene_index,
    bool               no_bam,
    bool               filter_probes,
    bool               no_secondary_analysis,
    bool               no_target_umi_filter,
    CellCalling        cell_calling_config,
    map<string>        chemistry_specs,
    int                r1_length,
    int                r2_length,
    int                trim_polya_min_score,
    int                trim_tso_min_score,
    csv                feature_reference,
    bool               include_introns,
    bool               check_library_compatibility,
    string             targeting_method,
    string             aligner,
    BarcodeAssignments force_sample_barcodes,
    bool               tenx_cmos,
    float              min_assignment_confidence,
    int                emptydrops_minimum_umis,
    int                global_minimum_umis,
    int                max_mito_percent,
    int                min_crispr_umi_threshold,
    string             tenx_cloud_token_path,
    bool               enable_tsne,
    string             cell_annotation_model,
    bool               skip_cell_annotation,
)

struct GemWellInputs(
    CommonInputs common_inputs,
    CountInputs  count_inputs,
    VdjInputs[]  vdj_inputs,
)

struct VdjOutputsCS(
    csv       metrics_summary_csv             "Run summary CSV"                                  "metrics_summary.csv",
    csv       clonotypes                      "Clonotype info",
    fasta     filtered_contig_fasta           "Filtered contig sequences FASTA"                  "filtered_contig.fasta",
    fastq     filtered_contig_fastq           "Filtered contig sequences FASTQ"                  "filtered_contig.fastq",
    csv       filtered_contig_annotations_csv "Filtered contigs (CSV)"                           "filtered_contig_annotations.csv",
    fasta     all_contig_fasta                "All-contig FASTA"                                 "all_contig.fasta",
    fasta.fai all_contig_fasta_fai            "All-contig FASTA index"                           "all_contig.fasta.fai",
    fastq     all_contig_fastq                "All-contig FASTQ"                                 "all_contig.fastq",
    bam       all_contig_bam                  "Read-contig alignments"                           "all_contig.bam",
    bam.bai   all_contig_bam_bai              "Read-contig alignment index"                      "all_contig.bam.bai",
    json      all_contig_annotations_json     "All contig annotations (JSON)"                    "all_contig_annotations.json",
    bed       all_contig_annotations_bed      "All contig annotations (BED)"                     "all_contig_annotations.bed",
    csv       all_contig_annotations_csv      "All contig annotations (CSV)"                     "all_contig_annotations.csv",
    json      cell_barcodes                   "Barcodes that are declared to be targetted cells",
    fasta     consensus_fasta                 "Clonotype consensus FASTA"                        "consensus.fasta",
    fasta.fai consensus_fasta_fai             "Clonotype consensus FASTA index"                  "consensus.fasta.fai",
    bam       consensus_bam                   "Contig-consensus alignments"                      "consensus.bam",
    bam.bai   consensus_bam_bai               "Contig-consensus alignment index"                 "consensus.bam.bai",
    csv       consensus_annotations_csv       "Clonotype consensus annotations (CSV)"            "consensus_annotations.csv",
    fasta     concat_ref_fasta                "Concatenated reference sequences"                 "concat_ref.fasta",
    fasta.fai concat_ref_fasta_fai            "Concatenated reference index"                     "concat_ref.fasta.fai",
    bam       concat_ref_bam                  "Contig-reference alignments"                      "concat_ref.bam",
    bam.bai   concat_ref_bam_bai              "Contig-reference alignment index"                 "concat_ref.bam.bai",
    vloupe    vloupe                          "Loupe V(D)J Browser file"                         "vloupe.vloupe",
    tsv       airr_rearrangement              "AIRR Rearrangement TSV",
    fa        donor_regions                   "Inferred germline sequences",
    pb        vdj_contig_info                 "All contig info (ProtoBuf format)",
)

struct CountOutputsCS(
    csv     metrics_summary               "Run summary CSV",
    bam     possorted_genome_bam          "BAM"                                      "possorted_genome_bam.bam",
    bam.bai possorted_genome_bai_index    "BAM csi index"                            "possorted_genome_bam.bam.bai",
    bam.csi possorted_genome_csi_index    "BAM bai index"                            "possorted_genome_bam.bam.csi",
    path    filtered_feature_bc_matrix    "Filtered feature-barcode matrices MEX",
    h5      filtered_feature_bc_matrix_h5 "Filtered feature-barcode matrices HDF5"   "filtered_feature_bc_matrix.h5",
    path    raw_feature_bc_matrix         "Unfiltered feature-barcode matrices MEX",
    h5      raw_feature_bc_matrix_h5      "Unfiltered feature-barcode matrices HDF5" "raw_feature_bc_matrix.h5",
    path    analysis                      "Secondary analysis output CSV",
    h5      molecule_info                 "Per-molecule read information",
    path    crispr_analysis               "CRISPR-specific analysis",
    path    antibody_analysis             "CSP-specific analysis",
    path    multiplexing_analysis         "Multiplexing-specific analysis",
    cloupe  cloupe                        "Loupe Browser file",
    csv     feature_reference             "Feature Reference",
    csv     target_panel                  "Target Panel File",
    csv     probe_set                     "Probe Set File",
)

struct MultiVdjOutputsCS(
    fasta     all_contig_fasta            "All-contig FASTA"              "all_contig.fasta",
    fasta.fai all_contig_fasta_fai        "All-contig FASTA index"        "all_contig.fasta.fai",
    fastq     all_contig_fastq            "All-contig FASTQ"              "all_contig.fastq",
    bam       all_contig_bam              "Read-contig alignments"        "all_contig.bam",
    bam.bai   all_contig_bam_bai          "Read-contig alignment index"   "all_contig.bam.bai",
    json      all_contig_annotations_json "All contig annotations (JSON)" "all_contig_annotations.json",
    bed       all_contig_annotations_bed  "All contig annotations (BED)"  "all_contig_annotations.bed",
    csv       all_contig_annotations_csv  "All contig annotations (CSV)"  "all_contig_annotations.csv",
)

struct MultiCountOutputsCS(
    csv     feature_reference_csv           "Feature reference file"                                              "feature_reference.csv",
    h5      raw_molecule_info_h5            "Molecule info file containing all molecules in the experiment"       "raw_molecule_info.h5",
    cloupe  raw_cloupe                      "Raw Loupe Browser file containing all molecules in the experiment",
    path    raw_feature_bc_matrix_mex       "Contains counts for all features and all barcodes in the experiment" "raw_feature_bc_matrix",
    h5      raw_feature_bc_matrix_h5        "Contains counts for all features and all barcodes in the experiment" "raw_feature_bc_matrix.h5",
    h5      raw_probe_bc_matrix             "Contains counts for all probes and all barcodes in a FRP experiment" "raw_probe_bc_matrix.h5",
    bam     unassigned_alignments           "Alignments from unassigned barcodes"                                 "unassigned_alignments.bam",
    bam.bai unassigned_alignments_bai_index "BAI Index for alignments from unassigned barcodes"                   "unassigned_alignments.bam.bai",
    bam.csi unassigned_alignments_csi_index "CSI Index for alignments from unassigned barcodes"                   "unassigned_alignments.bam.csi",
    csv     aggregate_barcodes              "Antibody and Antigen aggregate barcodes",
)

struct MultiplexingAnalysisCS(
    csv  tag_calls_per_cell          "Specifies tag assignments per cell",
    csv  tag_calls_summary           "Summarizes basic statistics about tag assignments",
    csv  frp_gem_barcode_overlap     "Gel-bead barcodes in common for all pairs of probe barcodes",
    csv  assignment_confidence_table "Lists the posterior probabilities for tag assignments provided by JIBES model",
    json cells_per_tag               "Provides a JSON that lists, for each tag, the cells it has been assigned to",
    csv  barcode_sample_assignments  "Original barcode sample assignment passed in the Multi configuration",
)

struct MultiOutputsCS(
    MultiplexingAnalysisCS multiplexing_analysis,
    MultiCountOutputsCS    count,
    MultiVdjOutputsCS      vdj_b,
    MultiVdjOutputsCS      vdj_t,
    MultiVdjOutputsCS      vdj_t_gd,
)

struct SampleVdjOutputsCS(
    csv       clonotypes                      "Clonotype info",
    fasta     filtered_contig_fasta           "Filtered contig sequences FASTA"                  "filtered_contig.fasta",
    fastq     filtered_contig_fastq           "Filtered contig sequences FASTQ"                  "filtered_contig.fastq",
    csv       filtered_contig_annotations_csv "Filtered contigs (CSV)"                           "filtered_contig_annotations.csv",
    json      cell_barcodes                   "Barcodes that are declared to be targetted cells",
    fasta     consensus_fasta                 "Clonotype consensus FASTA"                        "consensus.fasta",
    fasta.fai consensus_fasta_fai             "Clonotype consensus FASTA index"                  "consensus.fasta.fai",
    bam       consensus_bam                   "Contig-consensus alignments"                      "consensus.bam",
    bam.bai   consensus_bam_bai               "Contig-consensus alignment index"                 "consensus.bam.bai",
    csv       consensus_annotations_csv       "Clonotype consensus annotations (CSV)"            "consensus_annotations.csv",
    fasta     concat_ref_fasta                "Concatenated reference sequences"                 "concat_ref.fasta",
    fasta.fai concat_ref_fasta_fai            "Concatenated reference index"                     "concat_ref.fasta.fai",
    bam       concat_ref_bam                  "Contig-reference alignments"                      "concat_ref.bam",
    bam.bai   concat_ref_bam_bai              "Contig-reference alignment index"                 "concat_ref.bam.bai",
    vloupe    vloupe                          "Loupe V(D)J Browser file"                         "vloupe.vloupe",
    tsv       airr_rearrangement              "AIRR Rearrangement TSV",
    pb        vdj_contig_info                 "Contig info (ProtoBuf format)",
    fa        donor_regions                   "Inferred germline sequences",
)

struct SampleCountOutputsCS(
    path      analysis                              "Secondary analysis output CSV",
    cloupe    sample_cloupe                         "Loupe Browser File",
    path      crispr_analysis                       "CRISPR analysis outputs",
    csv       aggregate_barcodes                    "Sample Antibody and Antigen aggregate barcodes",
    csv       feature_reference_csv                 "Feature reference"                                               "feature_reference.csv",
    csv       sample_filtered_barcodes_csv          "Sample barcodes"                                                 "sample_filtered_barcodes.csv",
    path      sample_filtered_feature_bc_matrix_mex "Sample filtered feature-barcode matrices MEX"                    "sample_filtered_feature_bc_matrix",
    h5        sample_filtered_feature_bc_matrix     "Sample filtered feature-barcode matrices H5"                     "sample_filtered_feature_bc_matrix.h5",
    path      sample_raw_feature_bc_matrix_mex      "Sample raw feature-barcode matrices MEX"                         "sample_raw_feature_bc_matrix",
    h5        sample_raw_feature_bc_matrix          "Sample raw feature-barcode matrices H5"                          "sample_raw_feature_bc_matrix.h5",
    h5        sample_raw_probe_bc_matrix            "Sample raw probe-barcode matrix H5"                              "sample_raw_probe_bc_matrix.h5",
    bam       sample_alignments                     "BAM alignments for reads assigned to this sample"                "sample_alignments.bam",
    bam.bai   sample_alignments_index_bai           "BAM BAI index for reads assigned to this sample"                 "sample_alignments.bam.bai",
    bam.csi   sample_alignments_index_csi           "BAM CSI index for reads assigned to this sample"                 "sample_alignments.bam.csi",
    h5        sample_molecule_info                  "Per-molecule read information for reads assigned to this sample",
    csv       target_panel                          "Target Panel File",
    csv       probe_set                             "Probe Set File",
    CellTypes cell_types                            "Outputs of cell annotation cell typing",
)

struct SampleBeamOutputsCS(
    csv antigen_specificity_scores "Antigen Specificity scores",
    csv per_barcode,
)

struct SampleOutputsCS(
    SampleCountOutputsCS count,
    SampleVdjOutputsCS   vdj_b,
    SampleVdjOutputsCS   vdj_t,
    SampleVdjOutputsCS   vdj_t_gd,
    SampleBeamOutputsCS  antigen_analysis,
    html                 web_summary,
    csv                  metrics_summary,
)

struct SampleSlfeOuts(
    string  sample,
    bam     bam_file,
    bam.bai bai_index_file,
    bam.csi csi_index_file,
    json    metrics_summary,
    csv     per_barcode_metrics,
    h5      molecule_info,
    h5      filtered_matrix_h5,
    path    filtered_matrix_mex,
    h5      raw_matrix_h5,
    h5      raw_probe_bc_matrix,
    path    raw_matrix_mex,
    csv     filtered_barcodes,
    csv     aggregate_barcodes,
    csv     feature_reference,
    csv     target_panel,
    csv     probe_set,
    csv     per_probe_metrics,
)

###############################################################################
# Chemistry detection inputs
###############################################################################
# ducktypes from CountInputs
struct CountChemistryInputs(
    map[]        sample_def,
    path         reference_path,
    map<string>  chemistry_specs,
    int          r1_length,
    int          r2_length,
    bool         check_library_compatibility,
    ChemistryDef custom_chemistry_def,
    file         feature_reference,
)

# ducktypes from VdjInputs
struct VdjChemistryInputs(
    map[]        sample_def,
    string       chemistry_spec,
    int          r1_length,
    int          r2_length,
    string       chain_type,
    ChemistryDef custom_chemistry_def,
)

###############################################################################
# Gem well processor inputs
###############################################################################
# In Count, the basic rna counter is run for each gem well
struct CounterInputs(
    map[]              sample_def,
    csv                target_set,
    string             target_set_name,
    path               reference_path,
    json               gene_index,
    csv                feature_reference,
    ChemistryDef       custom_chemistry_def,
    CellCalling        cell_calling_config,
    float              subsample_rate,
    int                initial_reads,
    map[]              primers,
    int                r1_length,
    int                r2_length,
    int                trim_polya_min_score,
    int                trim_tso_min_score,
    bool               include_exons,
    bool               include_introns,
    string             targeting_method,
    string             aligner,
    bool               filter_probes,
    bool               no_target_umi_filter,
    bool               check_library_compatibility,
    bool               no_bam,
    BarcodeAssignments force_sample_barcodes,
    float              min_assignment_confidence,
    int                min_crispr_umi_threshold,
)

# In VDJ, the contig assembler is run for each gem well
struct VdjAssemblerInputs(
    map[]        sample_def,
    ChemistryDef custom_chemistry_def,
    int          r1_length,
    int          r2_length,
    int          initial_reads,
    float        subsample_rate,
    bool         denovo,
    path         inner_enrichment_primers,
    string       physical_library_id,
)

###############################################################################
# Outputs from VDJ reporter
###############################################################################
struct VdjReport(
    pb          vdj_contig_info,
    vloupe      vloupe,
    json        metrics_summary_json,
    csv         metrics_summary_csv,
    html        web_summary,
    json        web_summary_data,
    fastq       contig_fastq,
    fastq       filtered_contig_fastq,
    fasta       contig_fasta,
    fasta.fai   contig_fasta_fai,
    fasta       filtered_contig_fasta,
    bed         annotations_bed,
    json        cell_barcodes,
    json        cdr3_barcodes,
    json        all_contig_barcodes,
    json        productive_barcodes,
    json        productive_cell_barcodes,
    html        filter_summary,
    json        filter_metrics,
    parquet     per_bc_filters,
    tsv         umi_summary,
    bdf.bincode barcode_brief,
    txt         report,
)

###############################################################################
# Merge GEM Wells
##############################################################################

struct CountAggrSampleDef(
    string library_id,
    h5     molecule_h5,
)

struct BeamAnalyzerInputs(
    h5     filtered_matrix_h5,
    bmsf[] per_barcode_count_metrics,
    string beam_mode,
    bool   disable_beam,
)

struct VdjAssemblerAnalyzerInputs(
    json        asm_contig_annotations,
    arp.bincode assemblable_reads_per_bc,
    bdf.bincode barcode_brief,
    bd.bincode  barcode_full,
    csv         barcode_support,
    json        corrected_barcode_counts,
    int         n50_n50_rpu,
    json        summary,
    tsv         summary_tsv,
    int         total_read_pairs,
    tsv         umi_summary_tsv,
)

struct VdjAnalyzerClonotypeOuts(
    tsv       airr_rearrangement,
    csv       all_contig_annotations_csv,
    json      all_contig_annotations_json,
    csv       clonotypes_csv,
    bam       concat_ref_bam,
    bam.bai   concat_ref_bam_bai,
    fasta     concat_ref_fasta,
    fasta.fai concat_ref_fasta_fai,
    csv       consensus_annotations_csv,
    bam       consensus_bam,
    bam.bai   consensus_bam_bai,
    fasta     consensus_fasta,
    fasta.fai consensus_fasta_fai,
    fa        donor_ref_fa,
    pb        enclone_output,
    csv       filtered_contig_annotations_csv,
)

#
# @include "rna/sc_rna_aggregator_cs.mro"
#

struct CountAggrOutputs(
    json        summary                       "Aggregation metrics summary JSON",
    path        analysis                      "Secondary analysis output CSV",
    path        crispr_analysis               "Crispr analysis output",
    path        filtered_feature_bc_matrix    "Filtered feature-barcode matrices MEX",
    h5          filtered_feature_bc_matrix_h5 "Filtered feature-barcode matrices HDF5" "filtered_feature_bc_matrix.h5",
    cloupe      cloupe                        "Loupe Browser file",
    string      beam_mode                     "Beam mode",
    map<string> antigen_specificity_controls  "Antigen Specificity Controls",
    csv         feature_reference             "feature_reference",
    bool        disable_antigen_aggr          "Disable antigen aggregation",
)

struct VdjAggrOutputsCS(
    tsv    airr_rearrangement              "AIRR Rearrangement TSV",
    csv    clonotypes                      "Clonotypes csv",
    fasta  consensus_fasta                 "Clonotype consensus FASTA"                                   "consensus.fasta",
    csv    filtered_contig_annotations_csv "Annotations of filtered contigs with library metadata (CSV)" "filtered_contig_annotations.csv",
    csv    consensus_annotations_csv       "Clonotype consensus annotations (CSV)"                       "consensus_annotations.csv",
    vloupe vloupe                          "Loupe VDJ Browser file",
    fa     donor_regions                   "Inferred germline gene sequences",
)

struct VdjAggrOutputsPD(
    tsv    airr_rearrangement              "AIRR Rearrangement TSV",
    csv    clonotypes                      "Clonotypes csv",
    fasta  consensus_fasta                 "Clonotype consensus FASTA"                                   "consensus.fasta",
    csv    filtered_contig_annotations_csv "Annotations of filtered contigs with library metadata (CSV)" "filtered_contig_annotations.csv",
    csv    consensus_annotations_csv       "Clonotype consensus annotations (CSV)"                       "consensus_annotations.csv",
    vloupe vloupe                          "Loupe VDJ Browser file",
    fa     donor_regions                   "Inferred germline gene sequences",
    html   filter_summary                  "Summary of enclone filtering",
    pb     enclone_output                  "Clonotypes computed by enclone",
)

#
# @include "_sc_antibody_analyzer_stages.mro"
#

stage CALL_ANTIBODIES(
    in  h5     filtered_feature_counts_matrix,
    in  bool   is_antibody,
    in  bool   is_spatial,
    in  json   multi_graph,
    in  string sample_id,
    out json   antibody_histograms_json,
    out json   antibody_treemap_json,
    src py     "stages/feature/call_antibodies",
) split (
) using (
    volatile = strict,
)

stage SUMMARIZE_ANTIBODY_ANALYSIS(
    in  csv  aggregate_barcodes,
    in  bool is_antibody,
    out path antibody_analysis,
    src py   "stages/feature/summarize_antibody_analysis",
) using (
    mem_gb   = 4,
    volatile = strict,
)

#
# @include "_antibody_analyzer.mro"
#

pipeline _ANTIBODY_ANALYZER(
    in  h5     filtered_feature_counts_matrix,
    in  csv    aggregate_barcodes,
    in  bool   is_antibody,
    in  bool   is_spatial,
    in  json   multi_graph,
    in  string sample_id,
    out path   antibody_analysis,
    out json   antibody_histograms_json,
    out json   antibody_treemap_json,
)
{
    # Currently makes histograms
    call CALL_ANTIBODIES(
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        is_antibody = self.is_antibody,
        is_spatial  = self.is_spatial,
        multi_graph = self.multi_graph,
        sample_id   = self.sample_id,
    )

    # Currently copies this file
    call SUMMARIZE_ANTIBODY_ANALYSIS(
        aggregate_barcodes = self.aggregate_barcodes,
        is_antibody        = self.is_antibody,
    )

    return (
        antibody_analysis        = SUMMARIZE_ANTIBODY_ANALYSIS.antibody_analysis,
        antibody_histograms_json = CALL_ANTIBODIES.antibody_histograms_json,
        antibody_treemap_json    = CALL_ANTIBODIES.antibody_treemap_json,
    )
}

#
# @include "_cr_ana_stages.mro"
#

stage RUN_DIFFERENTIAL_EXPRESSION_NG(
    in  h5          matrix_h5,
    in  h5          clustering_h5,
    in  bool        is_antibody_only,
    out h5          diffexp_h5,
    out path        diffexp_csv,
    src comp        "cr_ana martian diff_exp_stage",
) split (
    in  map[]       cluster_keys,
    out bincode.lz4 diffexp,
) using (
    mem_gb   = 2,
    volatile = strict,
)

stage RUN_GRAPH_CLUSTERING(
    in  h5     matrix_h5,
    in  h5     pca_h5,
    in  int    num_neighbors,
    in  float  neighbor_a,
    in  float  neighbor_b,
    in  int    input_pcs,
    in  float  resolution,
    in  int    random_seed,
    in  int    threads,
    in  bool   parallel_clustering,
    out h5     clusters_h5,
    out path   clusters_csv,
    src comp   "cr_ana martian graph_clustering_stage",
) split (
    in  string feature_type,
) using (
    volatile = strict,
)

stage RUN_HIERARCHICAL_CLUSTERING(
    in  h5     matrix_h5,
    in  h5     graph_clusters_h5,
    out h5     clusters_h5,
    out path   clusters_csv,
    src comp   "cr_ana martian hierarchical_clustering_stage",
) split (
    in  string feature_type,
) using (
    mem_gb  = 4,
    threads = 1,
)

stage RUN_PCA_NG(
    in  h5              matrix_h5,
    in  int             num_pca_genes,
    in  int             num_principal_comps,
    in  bool            is_spatial,
    in  map<PcaOutputs> pca_map,
    out h5              pca_h5,
    out path            pca_csv,
    src comp            "cr_ana martian pca_stage",
) split (
    in  string          feature_type,
) using (
    volatile = strict,
)

stage RUN_PCA2(
    in  h5  matrix_h5,
    in  int num_pcs,
    out npy dimred_matrix,
    src comp "cr_ana martian pca2_stage",
) split (
) using (
    volatile = strict,
)

stage RUN_TSNE(
    in  h5     matrix_h5,
    in  h5     pca_h5,
    in  int    random_seed,
    in  float  perplexity,
    in  int    input_pcs,
    in  int    max_dims,
    in  int    max_iter,
    in  int    stop_lying_iter,
    in  int    mom_switch_iter,
    in  float  theta,
    out h5     tsne_h5,
    out path   tsne_csv,
    src comp   "cr_ana martian run_tsne",
) split (
    in  int    tsne_dims,
    in  string feature_type,
) using (
    volatile = strict,
)

stage RUN_UMAP(
    in  h5     matrix_h5,
    in  h5     pca_h5,
    in  int    random_seed,
    in  int    n_neighbors,
    in  int    input_pcs,
    in  int    max_dims,
    in  float  min_dist,
    in  string metric,
    in  string implementation,
    out h5     umap_h5,
    out path   umap_csv,
    src comp   "cr_ana martian umap_stage",
) split (
    in  int    umap_dims,
    in  string feature_type,
) using (
    volatile = strict,
)

#
# @include "_batch_correction_pca.mro"
#

stage PCA_PREP(
    in  h5     matrix_h5,
    in  bool   is_antibody_only,
    in  bool   is_atac,
    out float  mem_gb,
    out h5     filt_matrix,
    out string library_type,
    src py     "stages/analyzer/pca_prep",
) split (
) using (
    volatile = strict,
)

stage POST_PCA(
    in  h5     matrix_h5,
    in  h5     filt_matrix_h5,
    in  npy    dimred_matrix_npy,
    in  float  mem_gb,
    out pickle dimred_matrix,
    out pickle matrix_barcode_feature_info,
    src py     "stages/analyzer/post_pca",
) split (
) using (
    volatile = strict,
)

# Replacement for the old RUN_FBPCA which uses the PCA implemented in scan-rs
pipeline RUN_BATCH_CORRECTION_PCA(
    in  h5     matrix_h5,
    in  int    num_pcs,
    in  bool   is_antibody_only,
    in  bool   is_atac,
    out pickle dimred_matrix,
    out pickle matrix_barcode_feature_info,
    out string library_type,
)
{
    call PCA_PREP(
        matrix_h5        = self.matrix_h5,
        is_antibody_only = self.is_antibody_only,
        is_atac          = self.is_atac,
    ) using (
        volatile = true,
    )

    call RUN_PCA2(
        matrix_h5 = PCA_PREP.filt_matrix,
        num_pcs   = self.num_pcs,
    ) using (
        volatile = true,
    )

    call POST_PCA(
        matrix_h5         = self.matrix_h5,
        filt_matrix_h5    = PCA_PREP.filt_matrix,
        dimred_matrix_npy = RUN_PCA2.dimred_matrix,
        mem_gb            = PCA_PREP.mem_gb,
    ) using (
        volatile = true,
    )

    return (
        dimred_matrix               = POST_PCA.dimred_matrix,
        matrix_barcode_feature_info = POST_PCA.matrix_barcode_feature_info,
        library_type                = PCA_PREP.library_type,
    )
}

#
# @include "_run_kmeans.mro"
#

stage RUN_KMEANS(
    in  h5     matrix_h5,
    in  h5     pca_h5,
    in  int    random_seed,
    in  int    max_clusters,
    in  int    num_bcs,
    in  int    num_pcs,
    out h5     kmeans_h5,
    out path   kmeans_csv,
    src py     "stages/analyzer/run_kmeans",
) split (
    in  int    n_clusters,
    in  string library,
    in  int    which_pca,
) using (
    mem_gb   = 3,
    volatile = strict,
)

#
# @include "_sc_rna_analyzer_stages.mro"
#

stage ANALYZER_PREFLIGHT(
    in  bool   no_secondary_analysis,
    in  h5     filtered_matrices_h5,
    in  csv    use_genes,
    in  csv    exclude_genes,
    in  csv    use_bcs,
    in  int    num_analysis_bcs,
    in  int    force_cells,
    in  int    random_seed,
    in  int    num_pca_bcs,
    in  int    num_pca_genes,
    in  int    num_principal_comps,
    in  int    cbc_knn,
    in  float  cbc_alpha,
    in  float  cbc_sigma,
    in  bool   cbc_realign_panorama,
    in  int    max_clusters,
    in  int    graphclust_neighbors,
    in  float  neighbor_a,
    in  float  neighbor_b,
    in  int    tsne_perplexity,
    in  int    tsne_input_pcs,
    in  int    tsne_max_dims,
    in  int    tsne_max_iter,
    in  int    tsne_stop_lying_iter,
    in  int    tsne_mom_switch_iter,
    in  float  tsne_theta,
    in  int    umap_n_neighbors,
    in  int    umap_input_pcs,
    in  int    umap_max_dims,
    in  float  umap_min_dist,
    in  string umap_metric,
    in  bool   chemistry_batch_correction,
    in  bool   skip_multigenome_analysis,
    out bool   skip,
    out bool   is_antibody_only,
    out bool   disable_run_pca,
    out bool   disable_correct_chemistry_batch,
    out bool   skip_multigenome_analysis,
    src py     "stages/analyzer/analyzer_preflight",
) using (
    volatile = strict,
)

stage REANALYZER_PREFLIGHT(
    in  h5 filtered_matrices_h5,
    src py "stages/analyzer/reanalyzer_preflight",
) using (
    volatile = strict,
)

stage REANALYZE_VERIFY_SAMPLE_IDS(
    in  h5    matrix_h5,
    in  map[] sample_defs,
    out map[] sample_defs,
    src py    "stages/analyzer/reanalyze_verify_sample_ids",
) using (
    volatile = strict,
)

stage PREPROCESS_MATRIX(
    in  h5   matrix_h5,
    in  int  random_seed,
    in  csv  use_genes,
    in  csv  exclude_genes,
    in  csv  use_bcs,
    in  int  num_bcs,
    in  int  force_cells,
    in  bool get_peak_matrix,
    in  bool skip,
    in  bool is_visium_hd,
    in  bool is_pd,
    in  bool is_antibody_only,
    in  bool disable_run_pca,
    in  bool disable_correct_chemistry_batch,
    in  bool skip_multigenome_analysis,
    in  bool enable_tsne,
    out bool skip_antibody_analysis,
    out bool skip_antigen_analysis,
    out h5   cloupe_matrix_h5,
    out h5   preprocessed_matrix_h5,
    out bool is_multi_genome,
    out bool skip,
    out bool skip_tsne,
    out bool is_antibody_only,
    out bool disable_run_pca,
    out bool disable_correct_chemistry_batch,
    out bool skip_multigenome_analysis,
    out bool disable_hierarchical_clustering,
    src py   "stages/analyzer/preprocess_matrix",
) split (
) using (
    volatile = strict,
)

stage RUN_MULTIGENOME_ANALYSIS(
    in  h5   filtered_matrices_h5,
    in  bool is_multi_genome,
    out path multi_genome_csv,
    out path multi_genome_json,
    out json multi_genome_summary,
    src py   "stages/analyzer/run_multigenome_analysis",
) split (
) using (
    volatile = strict,
)

stage RUN_PCA(
    in  h5   matrix_h5,
    in  int  random_seed,
    in  int  num_pca_bcs,
    in  int  num_pca_genes,
    in  int  num_principal_comps,
    in  bool is_antibody_only,
    out h5   pca_h5,
    out path pca_csv,
    src py   "stages/analyzer/run_pca",
) split (
) using (
    volatile = strict,
)

stage COMBINE_CLUSTERING(
    in  h5   kmeans_h5,
    in  path kmeans_csv,
    in  h5   graphclust_h5,
    in  path graphclust_csv,
    in  h5   hclust_h5,
    in  path hclust_csv,
    out h5   clustering_h5,
    out path clustering_csv,
    src py   "stages/analyzer/combine_clustering",
) using (
    volatile = strict,
)

stage RUN_DIFFERENTIAL_EXPRESSION(
    in  h5     matrix_h5,
    in  h5     clustering_h5,
    in  int    random_seed,
    in  int    max_clusters,
    in  bool   is_antibody_only,
    out h5     diffexp_h5,
    out path   diffexp_csv,
    src py     "stages/analyzer/run_differential_expression",
) split (
    in  string clustering_key,
) using (
    volatile = strict,
)

stage SUMMARIZE_ANALYSIS(
    in  h5    matrix_h5,
    in  h5    pca_h5,
    in  h5    clustering_h5,
    in  h5    diffexp_h5,
    in  h5    tsne_h5,
    in  h5    umap_h5,
    in  path  pca_csv,
    in  path  clustering_csv,
    in  path  diffexp_csv,
    in  path  tsne_csv,
    in  path  umap_csv,
    in  json  multi_genome_summary,
    in  path  multi_genome_csv,
    in  path  multi_genome_json,
    in  bool  is_multi_genome,
    in  bool  chemistry_batch_correction,
    in  float batch_score_before_correction,
    in  float batch_score_after_correction,
    out path  analysis,
    out path  analysis_csv,
    out json  summary,
    src py    "stages/analyzer/summarize_analysis",
) split (
) using (
    volatile = strict,
)

stage PARSE_PARAM_CSV(
    in  csv    params_csv,
    out csv    params_csv,
    out int    num_analysis_bcs,
    out int    random_seed,
    out int    num_pca_bcs,
    out int    num_pca_genes,
    out int    num_principal_comps,
    out int    cbc_knn,
    out float  cbc_alpha,
    out float  cbc_sigma,
    out bool   cbc_realign_panorama,
    out int    max_clusters,
    out int    graphclust_neighbors,
    out float  neighbor_a,
    out float  neighbor_b,
    out int    tsne_perplexity,
    out int    tsne_input_pcs,
    out int    tsne_max_dims,
    out int    tsne_max_iter,
    out int    tsne_stop_lying_iter,
    out int    tsne_mom_switch_iter,
    out float  tsne_theta,
    out int    umap_n_neighbors,
    out int    umap_input_pcs,
    out int    umap_max_dims,
    out float  umap_min_dist,
    out string umap_metric,
    src py     "stages/analyzer/parse_csv",
) using (
    volatile = strict,
)

stage SUMMARIZE_REANALYSIS(
    in  string sample_id,
    in  string sample_desc,
    in  h5     filtered_matrices,
    in  path   analysis,
    in  json   analyze_matrices_summary,
    in  json   antibody_histograms,
    in  json   antibody_treemap,
    out html   web_summary,
    out json   summary,
    out path   feature_bc_matrix_mex,
    src py     "stages/analyzer/summarize_reanalysis",
) split (
) using (
    volatile = strict,
) retain (
    summary,
)

stage CORRECT_CHEMISTRY_BATCH(
    in  pickle          dimred_matrix,
    in  pickle          matrix_barcode_feature_info,
    in  map[]           library_info,
    in  string          library_type,
    in  int             cbc_knn,
    in  float           cbc_alpha,
    in  float           cbc_sigma,
    in  bool            cbc_realign_panorama,
    out float           batch_score_before_correction,
    out float           batch_score_after_correction,
    out h5              aligned_pca_h5,
    out path            aligned_pca_csv,
    out map<PcaOutputs> aligned_pca_map,
    src py              "stages/analyzer/correct_chemistry_batch",
) split (
    in  int             batch_id,
    in  map             batch_to_bc_indices,
    in  pickle          ordered_dimred_matrix,
    in  pickle          idx_to_batch_id,
    in  bool            need_reorder_barcode,
    in  pickle          barcode_reorder_index,
    out binary          batch_nearest_neighbor,
) using (
    mem_gb   = 4,
    volatile = strict,
)

#
# @include "sc_rna_analyzer.mro"
#

pipeline SC_RNA_ANALYZER(
    in  csv                aggregate_barcodes,
    in  AnalyzerInputs     analyzer_inputs,
    out AnalyzerOutputs    common_analyzer,
    out _ANTIBODY_ANALYZER antibody_analyzer,
    out _ANTIBODY_ANALYZER antigen_analyzer,
    out h5                 clustering_h5,
)
{
    call ANALYZER_PREFLIGHT(
        * = self.analyzer_inputs,
    ) using (
        volatile = true,
    )

    call PREPROCESS_MATRIX(
        matrix_h5       = self.analyzer_inputs.filtered_matrices_h5,
        random_seed     = self.analyzer_inputs.random_seed,
        use_genes       = self.analyzer_inputs.use_genes,
        exclude_genes   = self.analyzer_inputs.exclude_genes,
        use_bcs         = self.analyzer_inputs.use_bcs,
        num_bcs         = self.analyzer_inputs.num_analysis_bcs,
        force_cells     = self.analyzer_inputs.force_cells,
        is_visium_hd    = self.analyzer_inputs.is_visium_hd,
        is_pd           = self.analyzer_inputs.is_pd,
        get_peak_matrix = false,
        enable_tsne     = self.analyzer_inputs.enable_tsne,
        *               = ANALYZER_PREFLIGHT,
    ) using (
        volatile = true,
    )

    call RUN_MULTIGENOME_ANALYSIS(
        filtered_matrices_h5 = self.analyzer_inputs.filtered_matrices_h5,
        is_multi_genome      = PREPROCESS_MATRIX.is_multi_genome,
    ) using (
        disabled = PREPROCESS_MATRIX.skip_multigenome_analysis,
        volatile = true,
    )

    call RUN_BATCH_CORRECTION_PCA(
        matrix_h5        = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        num_pcs          = self.analyzer_inputs.num_principal_comps,
        is_antibody_only = PREPROCESS_MATRIX.is_antibody_only,
        is_atac          = false,
    ) using (
        disabled = PREPROCESS_MATRIX.disable_correct_chemistry_batch,
    )

    call CORRECT_CHEMISTRY_BATCH(
        dimred_matrix               = RUN_BATCH_CORRECTION_PCA.dimred_matrix,
        matrix_barcode_feature_info = RUN_BATCH_CORRECTION_PCA.matrix_barcode_feature_info,
        library_info                = self.analyzer_inputs.aggr_library_info,
        library_type                = RUN_BATCH_CORRECTION_PCA.library_type,
        cbc_knn                     = self.analyzer_inputs.cbc_knn,
        cbc_alpha                   = self.analyzer_inputs.cbc_alpha,
        cbc_sigma                   = self.analyzer_inputs.cbc_sigma,
        cbc_realign_panorama        = self.analyzer_inputs.cbc_realign_panorama,
    ) using (
        disabled = PREPROCESS_MATRIX.disable_correct_chemistry_batch,
        volatile = true,
    )

    call RUN_PCA_NG as RUN_PCA(
        matrix_h5           = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        num_pca_genes       = self.analyzer_inputs.num_pca_genes,
        num_principal_comps = self.analyzer_inputs.num_principal_comps,
        is_spatial          = self.analyzer_inputs.is_spatial,
        pca_map             = CORRECT_CHEMISTRY_BATCH.aligned_pca_map,
    ) using (
        disabled = PREPROCESS_MATRIX.disable_run_pca,
        volatile = true,
    )

    call RUN_KMEANS(
        matrix_h5    = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        pca_h5       = RUN_PCA.pca_h5,
        random_seed  = self.analyzer_inputs.random_seed,
        max_clusters = self.analyzer_inputs.max_clusters,
        num_bcs      = null,
        num_pcs      = null,
    ) using (
        disabled = PREPROCESS_MATRIX.skip,
        volatile = true,
    )

    call RUN_GRAPH_CLUSTERING(
        matrix_h5           = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        pca_h5              = RUN_PCA.pca_h5,
        num_neighbors       = self.analyzer_inputs.graphclust_neighbors,
        neighbor_a          = self.analyzer_inputs.neighbor_a,
        neighbor_b          = self.analyzer_inputs.neighbor_b,
        input_pcs           = null,
        resolution          = self.analyzer_inputs.graphclust_resolution,
        random_seed         = self.analyzer_inputs.random_seed,
        threads             = 4,
        parallel_clustering = false,
    ) using (
        disabled = PREPROCESS_MATRIX.skip,
        volatile = true,
    )

    call RUN_HIERARCHICAL_CLUSTERING(
        matrix_h5         = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        graph_clusters_h5 = RUN_GRAPH_CLUSTERING.clusters_h5,
    ) using (
        disabled = PREPROCESS_MATRIX.disable_hierarchical_clustering,
        volatile = true,
    )

    call COMBINE_CLUSTERING(
        kmeans_h5      = RUN_KMEANS.kmeans_h5,
        kmeans_csv     = RUN_KMEANS.kmeans_csv,
        graphclust_h5  = RUN_GRAPH_CLUSTERING.clusters_h5,
        graphclust_csv = RUN_GRAPH_CLUSTERING.clusters_csv,
        hclust_h5      = RUN_HIERARCHICAL_CLUSTERING.clusters_h5,
        hclust_csv     = RUN_HIERARCHICAL_CLUSTERING.clusters_csv,
    ) using (
        disabled = PREPROCESS_MATRIX.skip,
        volatile = true,
    )

    call RUN_DIFFERENTIAL_EXPRESSION_NG as RUN_DIFFERENTIAL_EXPRESSION(
        matrix_h5        = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        clustering_h5    = COMBINE_CLUSTERING.clustering_h5,
        is_antibody_only = PREPROCESS_MATRIX.is_antibody_only,
    ) using (
        disabled = PREPROCESS_MATRIX.skip,
        volatile = true,
    )

    call RUN_TSNE(
        matrix_h5       = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        pca_h5          = RUN_PCA.pca_h5,
        random_seed     = self.analyzer_inputs.random_seed,
        perplexity      = self.analyzer_inputs.tsne_perplexity,
        input_pcs       = self.analyzer_inputs.tsne_input_pcs,
        max_dims        = self.analyzer_inputs.tsne_max_dims,
        max_iter        = self.analyzer_inputs.tsne_max_iter,
        stop_lying_iter = self.analyzer_inputs.tsne_stop_lying_iter,
        mom_switch_iter = self.analyzer_inputs.tsne_mom_switch_iter,
        theta           = self.analyzer_inputs.tsne_theta,
    ) using (
        disabled = PREPROCESS_MATRIX.skip_tsne,
        volatile = true,
    )

    call RUN_UMAP(
        matrix_h5      = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        pca_h5         = RUN_PCA.pca_h5,
        implementation = self.analyzer_inputs.umap_implementation,
        random_seed    = self.analyzer_inputs.random_seed,
        n_neighbors    = self.analyzer_inputs.umap_n_neighbors,
        input_pcs      = self.analyzer_inputs.umap_input_pcs,
        max_dims       = self.analyzer_inputs.umap_max_dims,
        min_dist       = self.analyzer_inputs.umap_min_dist,
        metric         = self.analyzer_inputs.umap_metric,
    ) using (
        disabled = PREPROCESS_MATRIX.skip,
        volatile = true,
    )

    call SUMMARIZE_ANALYSIS(
        matrix_h5                     = PREPROCESS_MATRIX.preprocessed_matrix_h5,
        pca_h5                        = RUN_PCA.pca_h5,
        clustering_h5                 = COMBINE_CLUSTERING.clustering_h5,
        diffexp_h5                    = RUN_DIFFERENTIAL_EXPRESSION.diffexp_h5,
        tsne_h5                       = RUN_TSNE.tsne_h5,
        umap_h5                       = RUN_UMAP.umap_h5,
        pca_csv                       = RUN_PCA.pca_csv,
        clustering_csv                = COMBINE_CLUSTERING.clustering_csv,
        diffexp_csv                   = RUN_DIFFERENTIAL_EXPRESSION.diffexp_csv,
        tsne_csv                      = RUN_TSNE.tsne_csv,
        umap_csv                      = RUN_UMAP.umap_csv,
        multi_genome_summary          = RUN_MULTIGENOME_ANALYSIS.multi_genome_summary,
        multi_genome_csv              = RUN_MULTIGENOME_ANALYSIS.multi_genome_csv,
        multi_genome_json             = RUN_MULTIGENOME_ANALYSIS.multi_genome_json,
        is_multi_genome               = PREPROCESS_MATRIX.is_multi_genome,
        chemistry_batch_correction    = self.analyzer_inputs.chemistry_batch_correction,
        batch_score_before_correction = CORRECT_CHEMISTRY_BATCH.batch_score_before_correction,
        batch_score_after_correction  = CORRECT_CHEMISTRY_BATCH.batch_score_after_correction,
    ) using (
        disabled = PREPROCESS_MATRIX.skip,
    )

    # Note this stage uses the original and the the preprocessed matrix.
    call _ANTIBODY_ANALYZER(
        filtered_feature_counts_matrix = self.analyzer_inputs.filtered_matrices_h5,
        aggregate_barcodes = self.aggregate_barcodes,
        is_antibody        = true,
        is_spatial         = self.analyzer_inputs.is_spatial,
        multi_graph        = null,
        sample_id          = null,
    ) using (
        disabled = PREPROCESS_MATRIX.skip_antibody_analysis,
    )

    call _ANTIBODY_ANALYZER as _ANTIGEN_ANALYZER(
        filtered_feature_counts_matrix = self.analyzer_inputs.filtered_matrices_h5,
        aggregate_barcodes = self.aggregate_barcodes,
        is_antibody        = false,
        is_spatial         = self.analyzer_inputs.is_spatial,
        multi_graph        = null,
        sample_id          = null,
    ) using (
        disabled = PREPROCESS_MATRIX.skip_antigen_analysis,
    )

    return (
        antibody_analyzer = _ANTIBODY_ANALYZER,
        antigen_analyzer  = _ANTIGEN_ANALYZER,
        clustering_h5     = COMBINE_CLUSTERING.clustering_h5,
        common_analyzer   = {
            analysis:         SUMMARIZE_ANALYSIS.analysis,
            analysis_csv:     SUMMARIZE_ANALYSIS.analysis_csv,
            cloupe_matrix_h5: PREPROCESS_MATRIX.cloupe_matrix_h5,
            skip_tsne:        PREPROCESS_MATRIX.skip_tsne,
            summary:          SUMMARIZE_ANALYSIS.summary,
        },
    )
}

#
# @include "_cr_aggr_stages.mro"
#

stage MERGE_MOLECULES(
    in  map[]      sample_defs,
    in  map[]      libraries,
    out h5         merged_molecules,
    out map<int[]> gem_group_barcode_ranges,
    src comp       "cr_aggr martian merge_molecules",
) split (
    in  map        sample_def,
    out map        sample_def,
) using (
    volatile = strict,
)

stage PROCESS_VDJ_PROTO(
    in  VdjAggrCsvLibrary[] libraries,
    in  map                 count_gem_well_map,
    out string              receptor,
    out map                 gem_well_map,
    src comp                "cr_aggr martian process_vdj_proto",
)

stage SETUP_VDJ_AGGR(
    in  VdjAggrCsvLibrary[] libraries,
    in  map                 gem_well_map,
    in  string              receptor,
    out json[]              contig_ann_json_files,
    out csv                 enclone_input_csv,
    out em.json             enclone_gem_well_meta,
    out path                vdj_reference_path,
    out json                combined_ann_json,
    src comp                "cr_aggr martian setup_vdj_aggr",
) split (
    in  int                 chunk_id,
    out json                chunk_ann_json,
    out map                 enclone_meta_row,
    out map                 enclone_gem_well_info,
)

stage PARSE_AGGR_CSV(
    in  path           pipestance_root,
    in  csv            aggregation_csv,
    out csv            aggregation_csv,
    out map[]          count_libraries,
    out VdjAggrInput[] vdj_aggr_inputs,
    out bool           disable_count_aggr,
    out bool           disable_vdj_aggr,
    src comp           "cr_aggr martian parse_aggr_csv",
)

stage WRITE_CONTIG_PROTO(
    in  path        vdj_reference_path,
    in  json        contig_annotations_json,
    in  json        metrics_summary_json,
    in  string      receptor,
    in  int[]       gem_wells,
    in  json        cell_barcodes,
    in  string      sample_id,
    in  string      sample_desc,
    in  string      multi_config_sha,
    in  bdf.bincode barcode_brief,
    in  string      multiplexing_method,
    out pb          vdj_contig_info,
    src comp        "cr_aggr martian write_contig_proto",
)

stage MATCH_VDJ_AGGR_OUTS(
    in  string[]             receptors,
    in  csv[]                clonotypes,
    in  fa[]                 donor_ref_fas,
    in  fasta[]              consensus_fastas,
    in  path[]               vdj_reference_paths,
    in  csv[]                filtered_contig_annotations_csvs,
    in  csv[]                consensus_annotations_csvs,
    in  json[]               web_summary_data,
    in  vloupe[]             vloupes,
    in  AntigenAggrResults[] antigen_analysis,
    in  json[]               antigen_aggr_web_summary_data_in,
    in  tsv[]                airr_rearrangements,
    in  html[]               filter_summaries,
    in  pb[]                 enclone_outputs,
    out VdjAggrResults       vdj_t_results,
    out VdjAggrResults       vdj_t_gd_results,
    out VdjAggrResults       vdj_b_results,
    out path                 vdj_reference_path,
    out AntigenAggrResults   antigen_results,
    out json                 antigen_aggr_web_summary_data,
    src comp                 "cr_aggr martian match_vdj_outs",
)

stage WRITE_AGGR_ANN(
    in  em.json enclone_gem_well_meta,
    in  csv     annotation_csv,
    out csv     augmented_annotation_csv,
    src comp    "cr_aggr martian write_aggr_ann",
)

stage WRITE_WEB_SUMMARY_JSON(
    in  path                vdj_reference_path,
    in  VdjAggrCsvLibrary[] libraries,
    in  pb                  enclone_output,
    in  em.json             enclone_gem_well_meta,
    in  string              sample_id,
    in  string              sample_desc,
    in  csv                 clonotypes_csv,
    in  string              receptor,
    out json                web_summary_content,
    out json                per_origin_hist,
    src comp                "cr_aggr martian write_ws_json",
)

stage CREATE_CLONOTYPE_CLUSTERMAP(
    in  csv  antigen_specificity,
    out json antigen_clonotype_clustermap,
    src comp "cr_aggr martian create_clonotype_clustermap",
)

#
# @include "_sc_crispr_analyzer_stages.mro"
#

stage CALL_PROTOSPACERS(
    in  h5   filtered_feature_counts_matrix,
    in  int  min_crispr_umi_threshold,
    out csv  protospacer_calls_summary,
    out csv  protospacer_calls_per_cell,
    out json protospacer_call_metrics_json,
    out json cells_per_protospacer,
    out json protospacer_umi_thresholds_json,
    out csv  protospacer_umi_thresholds_csv,
    src py   "stages/feature/call_protospacers",
) split (
    in  int  chunk_start,
    in  int  chunk_end,
    out json chunk_cells_per_protospacer,
    out json chunk_protospacer_umi_thresholds,
) using (
    volatile = strict,
)

stage MEASURE_PERTURBATIONS(
    in  csv  protospacer_calls_per_cell,
    in  h5   filtered_feature_counts_matrix,
    in  csv  feature_reference,
    in  bool by_feature,
    in  bool ignore_multiples,
    out csv  perturbation_efficiencies,
    out path perturbation_effects_path,
    src py   "stages/feature/measure_perturbations",
) split (
) using (
    volatile = strict,
)

stage SUMMARIZE_CRISPR_ANALYSIS(
    in  csv  feature_reference,
    in  csv  protospacer_calls_summary,
    in  csv  protospacer_calls_per_cell,
    in  json cells_per_protospacer,
    in  csv  protospacer_umi_thresholds_csv,
    in  json protospacer_umi_thresholds_json,
    in  csv  perturbation_efficiencies_by_feature,
    in  csv  perturbations_efficiencies_by_target,
    in  path perturbation_effects_by_feature,
    in  path perturbation_effects_by_target,
    out path crispr_analysis,
    src py   "stages/feature/summarize_crispr_analysis",
) using (
    mem_gb   = 4,
    volatile = strict,
)

#
# @include "_crispr_analyzer.mro"
#

pipeline _CRISPR_ANALYZER(
    in  h5   filtered_feature_counts_matrix,
    in  csv  feature_reference,
    in  int  min_crispr_umi_threshold,
    out json cells_per_protospacer,
    out json crispr_analysis_metrics,
    out path crispr_analysis,
)
{
    call CALL_PROTOSPACERS(
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        min_crispr_umi_threshold = self.min_crispr_umi_threshold,
    )

    call MEASURE_PERTURBATIONS as _PERTURBATIONS_BY_FEATURE(
        protospacer_calls_per_cell = CALL_PROTOSPACERS.protospacer_calls_per_cell,
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        feature_reference          = self.feature_reference,
        by_feature                 = true,
        ignore_multiples           = false,
    )

    call MEASURE_PERTURBATIONS as _PERTURBATIONS_BY_TARGET(
        protospacer_calls_per_cell = CALL_PROTOSPACERS.protospacer_calls_per_cell,
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        feature_reference          = self.feature_reference,
        by_feature                 = false,
        ignore_multiples           = false,
    )

    call SUMMARIZE_CRISPR_ANALYSIS(
        feature_reference          = self.feature_reference,
        protospacer_calls_summary  = CALL_PROTOSPACERS.protospacer_calls_summary,
        protospacer_calls_per_cell = CALL_PROTOSPACERS.protospacer_calls_per_cell,
        cells_per_protospacer      = CALL_PROTOSPACERS.cells_per_protospacer,
        protospacer_umi_thresholds_csv = CALL_PROTOSPACERS.protospacer_umi_thresholds_csv,
        protospacer_umi_thresholds_json = CALL_PROTOSPACERS.protospacer_umi_thresholds_json,
        perturbation_efficiencies_by_feature = _PERTURBATIONS_BY_FEATURE.perturbation_efficiencies,
        perturbations_efficiencies_by_target = _PERTURBATIONS_BY_TARGET.perturbation_efficiencies,
        perturbation_effects_by_feature = _PERTURBATIONS_BY_FEATURE.perturbation_effects_path,
        perturbation_effects_by_target = _PERTURBATIONS_BY_TARGET.perturbation_effects_path,
    )

    return (
        cells_per_protospacer   = CALL_PROTOSPACERS.cells_per_protospacer,
        crispr_analysis_metrics = CALL_PROTOSPACERS.protospacer_call_metrics_json,
        crispr_analysis         = SUMMARIZE_CRISPR_ANALYSIS.crispr_analysis,
    )
}

#
# @include "_sc_rna_aggregator_stages.mro"
#

stage AGGREGATOR_PREFLIGHT(
    in  map[]  sample_defs,
    in  string normalization_mode,
    in  bool   is_pd,
    src py     "stages/aggregator/aggregator_preflight",
) using (
    mem_gb   = 7,
    volatile = strict,
)

stage PARSE_CSV(
    in  path   pipestance_root,
    in  csv    aggregation_csv,
    in  bool   reanalyze,
    in  h5     matrix_h5,
    in  string product_type,
    out csv    aggregation_csv,
    out map[]  sample_defs,
    src py     "stages/aggregator/parse_csv",
) using (
    volatile = strict,
)

stage CHECK_MOLECULE_INFO_VERSION(
    in  map[]       sample_defs,
    in  string      product_type,
    in  bool        is_pd,
    out map[]       updated_sample_defs,
    out bool        is_not_pd,
    out string      beam_mode,
    out bool        is_spatial,
    out map<string> antigen_specificity_controls,
    out csv         feature_reference,
    out bool        disable_antigen_aggr,
    src py          "stages/aggregator/check_molecule_info_version",
) split (
    in  int         mol_h5_version,
    in  map         sample_def,
    out map         updated_sample_def,
) using (
    volatile = strict,
)

stage SETUP_SAMPLES(
    in  map[] sample_defs,
    out map   gem_group_index,
    out map[] libraries,
    out json  gem_group_index_json,
    out bool  chemistry_batch_correction,
    out bool  disable_crispr_aggr,
    src py    "stages/aggregator/setup_samples",
) using (
    volatile = strict,
)

stage NORMALIZE_DEPTH(
    in  map        gem_group_index,
    in  h5         molecules,
    in  string     normalization_mode,
    in  map<int[]> gem_group_barcode_ranges,
    in  float      targeted_depth_factor,
    out h5[]       raw_matrices_h5,
    out int        raw_nnz,
    out h5[]       filtered_matrices_h5,
    out int        filtered_nnz,
    out json       summary,
    src py         "stages/aggregator/normalize_depth",
) split (
    in  float[]    frac_reads_kept,
    in  int[]      num_cells,
    in  int        chunk_start,
    in  int        chunk_len,
    in  json       reads_per_library,
    out json       chunk_summary,
    out h5         raw_matrix_h5,
    out h5         filtered_matrix_h5,
) using (
    mem_gb   = 4,
    volatile = strict,
)

stage WRITE_MATRICES(
    in  map[] sample_defs,
    in  map   gem_group_index,
    in  h5    molecules,
    in  h5[]  raw_matrices_h5,
    in  int   raw_nnz,
    in  h5[]  filtered_matrices_h5,
    in  int   filtered_nnz,
    in  json  summary,
    in  bool  is_pd,
    out h5    raw_matrix_h5,
    out h5    filtered_matrix_h5,
    out path  filtered_matrix_mex,
    out h5    barcode_summary_h5,
    out json  summary,
    src py    "stages/aggregator/write_matrices",
) split (
) using (
    volatile = strict,
)

stage CRISPR_AGGR_INPUT_PREP(
    in  h5  merged_molecules,
    out csv feature_reference,
    src py  "stages/aggregator/crispr_aggr_input_prep",
) using (
    mem_gb   = 4,
    volatile = strict,
)

stage CHECK_INVARIANTS(
    in  map[] input_sample_defs,
    in  h5    merged_raw_gene_bc_matrices_h5,
    out json  summary,
    src py    "stages/aggregator/check_invariants",
) split (
) using (
    volatile = strict,
)

stage SUMMARIZE_AGGREGATED_REPORTS(
    in  string sample_id,
    in  string sample_desc,
    in  map    gem_group_index,
    in  h5     filtered_matrices_h5,
    in  path   analysis,
    in  map[]  sample_defs,
    in  json   normalize_depth_summary,
    in  json   analyze_matrices_summary,
    in  json   antibody_histograms,
    in  json   antibody_treemap,
    in  json   crispr_analysis_metrics,
    in  string product_type,
    in  bool   skip_tsne,
    out json   summary,
    out html   web_summary,
    out json   web_summary_data,
    src py     "stages/aggregator/summarize_aggregated_reports",
) split (
) using (
    volatile = strict,
)

#
# @include "sc_rna_aggregator.mro"
#

pipeline SC_RNA_AGGREGATOR(
    in  string      sample_id,
    in  string      sample_desc,
    in  map[]       sample_defs,
    in  string      normalization_mode,
    in  bool        no_secondary_analysis,
    in  int         num_analysis_bcs,
    in  int         num_pca_bcs,
    in  int         num_pca_genes,
    in  int         num_principal_comps,
    in  int         cbc_knn,
    in  float       cbc_alpha,
    in  float       cbc_sigma,
    in  bool        cbc_realign_panorama,
    in  int         max_clusters,
    in  int         graphclust_neighbors,
    in  float       neighbor_a,
    in  float       neighbor_b,
    in  int         tsne_perplexity,
    in  int         tsne_input_pcs,
    in  int         random_seed,
    in  int         tsne_max_dims,
    in  int         tsne_max_iter,
    in  int         tsne_stop_lying_iter,
    in  int         tsne_mom_switch_iter,
    in  float       tsne_theta,
    in  string      product_type,
    in  bool        is_pd,
    in  int         min_crispr_umi_threshold,
    in  bool        enable_tsne,
    out h5          raw_gene_bc_matrices_h5,
    out h5          filtered_gene_bc_matrices_h5,
    out path        filtered_gene_bc_matrices_mex,
    out h5          molecule_info,
    out path        analysis,
    out path        crispr_analysis,
    out json        cells_per_protospacer,
    out path        analysis_csv,
    out json        analysis_summary,
    out json        summary,
    out html        web_summary,
    out json        web_summary_data,
    out map         gem_group_index,
    out json        gem_group_index_json,
    out string      beam_mode,
    out map<string> antigen_specificity_controls,
    out csv         feature_reference,
    out bool        disable_antigen_aggr,
)
{
    call AGGREGATOR_PREFLIGHT(
        sample_defs        = self.sample_defs,
        normalization_mode = self.normalization_mode,
        is_pd              = self.is_pd,
    ) using (
        preflight = true,
    )

    call CHECK_MOLECULE_INFO_VERSION(
        sample_defs  = self.sample_defs,
        product_type = self.product_type,
        is_pd        = self.is_pd,
    )

    call SETUP_SAMPLES(
        sample_defs = CHECK_MOLECULE_INFO_VERSION.updated_sample_defs,
    ) using (
        volatile = true,
    )

    call MERGE_MOLECULES(
        sample_defs = CHECK_MOLECULE_INFO_VERSION.updated_sample_defs,
        libraries   = SETUP_SAMPLES.libraries,
    ) using (
        volatile = true,
    )

    call NORMALIZE_DEPTH(
        gem_group_index          = SETUP_SAMPLES.gem_group_index,
        normalization_mode       = self.normalization_mode,
        molecules                = MERGE_MOLECULES.merged_molecules,
        gem_group_barcode_ranges = MERGE_MOLECULES.gem_group_barcode_ranges,
        targeted_depth_factor    = 2,
    )

    call WRITE_MATRICES(
        sample_defs          = CHECK_MOLECULE_INFO_VERSION.updated_sample_defs,
        gem_group_index      = SETUP_SAMPLES.gem_group_index,
        molecules            = MERGE_MOLECULES.merged_molecules,
        raw_matrices_h5      = NORMALIZE_DEPTH.raw_matrices_h5,
        filtered_matrices_h5 = NORMALIZE_DEPTH.filtered_matrices_h5,
        raw_nnz              = NORMALIZE_DEPTH.raw_nnz,
        filtered_nnz         = NORMALIZE_DEPTH.filtered_nnz,
        summary              = NORMALIZE_DEPTH.summary,
        is_pd                = self.is_pd,
    )

    call CRISPR_AGGR_INPUT_PREP(
        merged_molecules = MERGE_MOLECULES.merged_molecules,
    ) using (
        disabled = SETUP_SAMPLES.disable_crispr_aggr,
    )

    call _CRISPR_ANALYZER(
        filtered_feature_counts_matrix = WRITE_MATRICES.filtered_matrix_h5,
        feature_reference        = CRISPR_AGGR_INPUT_PREP.feature_reference,
        min_crispr_umi_threshold = self.min_crispr_umi_threshold,
    ) using (
        disabled = SETUP_SAMPLES.disable_crispr_aggr,
    )

    call SC_RNA_ANALYZER(
        aggregate_barcodes = null,
        analyzer_inputs    = {
            aggr_library_info:          SETUP_SAMPLES.libraries,
            cbc_alpha:                  self.cbc_alpha,
            cbc_knn:                    self.cbc_knn,
            cbc_realign_panorama:       self.cbc_realign_panorama,
            cbc_sigma:                  self.cbc_sigma,
            chemistry_batch_correction: SETUP_SAMPLES.chemistry_batch_correction,
            enable_tsne:                self.enable_tsne,
            exclude_genes:              null,
            filtered_matrices_h5:       WRITE_MATRICES.filtered_matrix_h5,
            force_cells:                null,
            graphclust_neighbors:       self.graphclust_neighbors,
            graphclust_resolution:      null,
            is_pd:                      self.is_pd,
            is_spatial:                 CHECK_MOLECULE_INFO_VERSION.is_spatial,
            is_visium_hd:               false,
            max_clusters:               self.max_clusters,
            molecule_info:              MERGE_MOLECULES.merged_molecules,
            neighbor_a:                 self.neighbor_a,
            neighbor_b:                 self.neighbor_b,
            no_secondary_analysis:      self.no_secondary_analysis,
            num_analysis_bcs:           self.num_analysis_bcs,
            num_pca_bcs:                self.num_pca_bcs,
            num_pca_genes:              self.num_pca_genes,
            num_principal_comps:        self.num_principal_comps,
            random_seed:                self.random_seed,
            skip_multigenome_analysis:  false,
            tsne_input_pcs:             self.tsne_input_pcs,
            tsne_max_dims:              self.tsne_max_dims,
            tsne_max_iter:              self.tsne_max_iter,
            tsne_mom_switch_iter:       self.tsne_mom_switch_iter,
            tsne_perplexity:            self.tsne_perplexity,
            tsne_stop_lying_iter:       self.tsne_stop_lying_iter,
            tsne_theta:                 self.tsne_theta,
            umap_implementation:        "original",
            umap_input_pcs:             null,
            umap_max_dims:              null,
            umap_metric:                null,
            umap_min_dist:              null,
            umap_n_neighbors:           null,
            use_bcs:                    null,
            use_genes:                  null,
        },
    )

    call SUMMARIZE_AGGREGATED_REPORTS(
        sample_id                = self.sample_id,
        sample_desc              = self.sample_desc,
        gem_group_index          = SETUP_SAMPLES.gem_group_index,
        filtered_matrices_h5     = WRITE_MATRICES.filtered_matrix_h5,
        analysis                 = SC_RNA_ANALYZER.common_analyzer.analysis,
        normalize_depth_summary  = WRITE_MATRICES.summary,
        analyze_matrices_summary = SC_RNA_ANALYZER.common_analyzer.summary,
        antibody_histograms      = SC_RNA_ANALYZER.antibody_analyzer.antibody_histograms_json,
        antibody_treemap         = SC_RNA_ANALYZER.antibody_analyzer.antibody_treemap_json,
        crispr_analysis_metrics  = _CRISPR_ANALYZER.crispr_analysis_metrics,
        product_type             = self.product_type,
        sample_defs              = CHECK_MOLECULE_INFO_VERSION.updated_sample_defs,
        skip_tsne                = SC_RNA_ANALYZER.common_analyzer.skip_tsne,
    )

    call CHECK_INVARIANTS(
        input_sample_defs = CHECK_MOLECULE_INFO_VERSION.updated_sample_defs,
        merged_raw_gene_bc_matrices_h5 = WRITE_MATRICES.raw_matrix_h5,
    ) using (
        disabled = CHECK_MOLECULE_INFO_VERSION.is_not_pd,
    )

    return (
        filtered_gene_bc_matrices_h5  = WRITE_MATRICES.filtered_matrix_h5,
        filtered_gene_bc_matrices_mex = WRITE_MATRICES.filtered_matrix_mex,
        raw_gene_bc_matrices_h5       = WRITE_MATRICES.raw_matrix_h5,
        analysis                      = SC_RNA_ANALYZER.common_analyzer.analysis,
        crispr_analysis               = _CRISPR_ANALYZER.crispr_analysis,
        cells_per_protospacer         = _CRISPR_ANALYZER.cells_per_protospacer,
        analysis_csv                  = SC_RNA_ANALYZER.common_analyzer.analysis_csv,
        analysis_summary              = SC_RNA_ANALYZER.common_analyzer.summary,
        summary                       = SUMMARIZE_AGGREGATED_REPORTS.summary,
        web_summary                   = SUMMARIZE_AGGREGATED_REPORTS.web_summary,
        web_summary_data              = SUMMARIZE_AGGREGATED_REPORTS.web_summary_data,
        gem_group_index               = SETUP_SAMPLES.gem_group_index,
        gem_group_index_json          = SETUP_SAMPLES.gem_group_index_json,
        molecule_info                 = MERGE_MOLECULES.merged_molecules,
        beam_mode                     = CHECK_MOLECULE_INFO_VERSION.beam_mode,
        antigen_specificity_controls  = CHECK_MOLECULE_INFO_VERSION.antigen_specificity_controls,
        feature_reference             = CHECK_MOLECULE_INFO_VERSION.feature_reference,
        disable_antigen_aggr          = CHECK_MOLECULE_INFO_VERSION.disable_antigen_aggr,
    )
}

#
# @include "_cr_vdj_stages.mro"
#

stage COPY_VDJ_REFERENCE(
    in  path         vdj_reference_path,
    out VdjRefFolder vdj_reference,
    src comp         "cr_vdj martian copy_vdj_reference",
)

stage SUMMARIZE_VDJ_FILTERS(
    in  string   sample_id,
    in  string   sample_description,
    in  json     all_contig_annotations,
    in  json.lz4 asm_filter_diagnostics,
    in  json     enclone_barcode_fate,
    in  h5       raw_matrix_h5,
    out html     filter_summary,
    out json     metrics_summary,
    out parquet  per_bc_filters,
    src comp     "cr_vdj martian summarize_vdj_filters",
) using (
    mem_gb = 5,
)

stage CREATE_BARCODE_CSV(
    in  h5  gex_filtered_matrix,
    in  csv vdj_filtered_annotations,
    in  map count_gem_well_map,
    out csv per_barcode_csv,
    src comp "cr_vdj martian create_barcode_csv",
)

stage RUN_ENCLONE(
    in  FilterSwitch filter_switch,
    in  path         vdj_reference_path,
    in  json         contig_annotations,
    in  string       receptor,
    out json         summary,
    out pb           enclone_output,
    out fa           donor_ref_fa,
    out json         barcode_fate,
    out bool         disable_vloupe,
    src comp         "cr_vdj martian assigner",
) using (
    mem_gb  = 5,
    threads = -4,
    vmem_gb = 12,
)

stage WRITE_CLONOTYPE_OUTS(
    in  string sample_id,
    in  string receptor,
    in  pb     enclone_output,
    out csv    clonotypes_csv,
    src comp   "cr_vdj martian write_clonotype_outs",
) using (
    mem_gb = 8,
)

stage FILL_CLONOTYPE_INFO(
    in  string sample_id,
    in  json   contig_annotations,
    in  pb     enclone_output,
    out json   all_contig_annotations_json,
    src comp   "cr_vdj martian fill_clonotype_info",
) using (
    mem_gb = 2,
)

stage HANDLE_NO_CLONOTYPING(
    in  ContigAnnotationSource contigs,
    in  bool                   disable_clonotyping,
    out json                   final_contig_annotations,
    src comp                   "cr_vdj martian handle_no_clonotyping",
)

stage WRITE_CONCAT_REF_OUTS(
    in  string    sample_id,
    in  pb        enclone_output,
    in  json      all_contig_annotations_json,
    out bam       concat_ref_bam,
    out bam.bai   concat_ref_bam_bai,
    out fasta     concat_ref_fasta,
    out fasta.fai concat_ref_fasta_fai,
    src comp      "cr_vdj martian write_concat_ref_outs",
) using (
    mem_gb  = 12,
    threads = 4,
)

stage WRITE_CONSENSUS_BAM(
    in  string  sample_id,
    in  pb      enclone_output,
    in  json    all_contig_annotations_json,
    out bam     consensus_bam,
    out bam.bai consensus_bam_bai,
    src comp    "cr_vdj martian write_consensus_bam",
) using (
    mem_gb  = 4,
    threads = 4,
)

stage WRITE_CONSENSUS_TXT(
    in  string    sample_id,
    in  pb        enclone_output,
    out fasta     consensus_fasta,
    out fasta.fai consensus_fasta_fai,
    out csv       consensus_annotations_csv,
    src comp      "cr_vdj martian write_consensus_txt",
) using (
    mem_gb  = 4,
    threads = 1,
)

stage RUN_ENCLONE_AGGR(
    in  json[]       contig_ann_json_files,
    in  csv          enclone_input_csv,
    in  em.json      enclone_gem_well_meta,
    in  path         vdj_reference_path,
    in  FilterSwitch filter_switch,
    in  bool         mix_donors,
    out pb           enclone_output,
    out fa           donor_ref_fa,
    out json         barcode_fate,
    src comp         "cr_vdj martian run_enclone_aggr",
) using (
    mem_gb  = 16,
    threads = 4,
)

stage ASSEMBLE_VDJ(
    in  string            sample_id,
    in  map<ChemistryDef> chemistry_defs,
    in  bincode.lz4[]     bc_sorted_rna_reads,
    in  path              vdj_reference_path,
    in  string            receptor,
    in  int               n50_n50_rpu,
    in  int               npairs,
    in  bool              denovo,
    in  path              inner_enrichment_primers,
    in  int               total_read_pairs,
    in  json              corrected_bc_counts,
    in  int               min_contig_length,
    out bam               contig_bam,
    out bam.bai           contig_bam_bai,
    out tsv               summary_tsv,
    out tsv               umi_summary_tsv,
    out json              contig_annotations,
    out bdf.bincode       barcode_brief,
    out bd.bincode        barcode_full,
    out csv               barcode_support,
    out json[]            barcodes_in_chunks,
    out arp.bincode       assemblable_reads_per_bc,
    out txt               align_info,
    out fastq             unmapped_sample_fastq,
    src comp              "cr_vdj martian assembly",
) split (
    in  bincode.lz4       chunk_rna_reads,
    in  int               chunk_id,
    in  VdjPrimers        primers,
    out json              barcodes_in_chunk,
    out bd.bincode        barcode_data,
    out bin               barcode_data_brief,
    out bincode           outs_builder,
)

stage ASM_CALL_CELLS(
    in  string            receptor,
    in  bool              denovo,
    in  path              vdj_reference_path,
    in  map<ChemistryDef> vdj_chemistry_def,
    in  json              contig_annotations,
    in  bdf.bincode       barcode_brief,
    in  int               n50_n50_rpu,
    in  FilterSwitch      filter_switch,
    in  fprint.json       sample_fingerprint,
    out json              contig_annotations,
    out json.lz4          filter_diagnostics,
    src comp              "cr_vdj martian asm_call_cells",
) using (
    mem_gb = 4,
) retain (
    filter_diagnostics,
)

stage MAKE_EXACT_CLONOTYPES(
    in  json contig_annotations,
    out json exact_clonotypes,
    src comp "cr_vdj martian make_exact_clonotypes",
) using (
    mem_gb = 4,
)

stage FILTER_EXACT_CLONOTYPES(
    in  json     exact_clonotypes,
    in  json     contig_annotations,
    in  json.lz4 filter_diagnostics,
    out json     contig_annotations,
    out json.lz4 filter_diagnostics,
    src comp     "cr_vdj martian filter_exact_clonotypes",
) using (
    mem_gb = 4,
) retain (
    filter_diagnostics,
)

stage CREATE_AIRR_TSV(
    in  json  contig_annotations,
    in  fasta concat_ref_fasta,
    in  map   gem_well_map,
    out tsv   airr_annotations,
    src comp  "cr_vdj martian airr_filter",
)

stage WRITE_CONTIG_OUTS(
    in  json        contig_annotations,
    in  int         total_read_pairs,
    in  json        corrected_bc_counts,
    in  arp.bincode assemblable_reads_per_bc,
    out fastq       contig_fastq,
    out fastq       filtered_contig_fastq,
    out fasta       contig_fasta,
    out fasta.fai   contig_fasta_fai,
    out fasta       filtered_contig_fasta,
    out bed         annotations_bed,
    out json        cell_barcodes,
    out json        paired_cell_barcodes,
    out json        paired_prod_barcodes,
    out json        paired_cdr3_barcodes,
    out json        prod_barcodes,
    out json        cdr3_barcodes,
    out json        all_contig_barcodes,
    out json        summary,
    src comp        "cr_vdj martian write_contig_outs",
)

stage HANDLE_GEX_CELLS(
    in  json asm_contig_annotations,
    in  csv  filtered_barcodes,
    in  bool is_antibody_only,
    in  bool is_non_targeted_gex,
    out json contig_annotations,
    src comp "cr_vdj martian handle_gex_cells",
)

stage MAKE_FILTER_SWITCH(
    in  bool         disable_count,
    in  bool         is_antibody_only,
    in  bool         is_non_targeted_gex,
    in  bool         multiplet_filter,
    in  bool         shared_contig_filter,
    in  bool         umi_baseline_filter,
    out FilterSwitch filter_switch,
    src comp         "cr_vdj martian make_filter_switch",
)

stage MERGE_PER_SAMPLE_ANNOTATIONS(
    in  map<json>    per_sample_annotations,
    in  json         asm_contig_annotations,
    in  string       multiplexing_method,
    in  json         gex_cells_per_tag,
    in  ChemistryDef vdj_chemistry_def,
    out json         contig_annotations,
    out json         vdj_cells_per_tag_json,
    src comp         "cr_vdj martian merge_per_sample_annotations",
)

stage WRITE_ANN_CSV(
    in  json all_contig_annotations_json,
    out csv  all_contig_annotations_csv,
    out csv  filtered_contig_annotations_csv,
    src comp "cr_vdj martian write_ann_csv",
)

stage SUBSET_ASSEMBLY_OUTS(
    in  bool              per_sample,
    in  string            multiplexing_method,
    in  map<ChemistryDef> vdj_chemistry_def,
    in  fprint.json       sample_fingerprint,
    in  json              contig_annotations,
    in  json              merged_annotations,
    in  int               total_read_pairs,
    in  json              corrected_barcode_counts,
    in  arp.bincode       assemblable_reads_per_bc,
    in  tsv               umi_summary,
    in  csv               barcode_support,
    in  bdf.bincode       barcode_brief,
    in  bd.bincode        barcode_full,
    out json              contig_annotations,
    out int               total_read_pairs,
    out json              corrected_barcode_counts,
    out arp.bincode       assemblable_reads_per_bc,
    out tsv               umi_summary,
    out csv               barcode_support,
    out bdf.bincode       barcode_brief,
    out bd.bincode        barcode_full,
    src comp              "cr_vdj martian subset_assembly_outs",
)

stage ASM_METRICS(
    in  ChemistryDef chemistry_def,
    in  path         vdj_reference_path,
    in  string       receptor,
    in  path         inner_enrichment_primers,
    in  int          total_read_pairs,
    in  bd.bincode   barcode_full,
    out json         metrics_summary_json,
    out txt          report,
    src comp         "cr_vdj martian asm_metrics",
) using (
    mem_gb = 8,
)

#
# @include "_cell_annotation_common_stages.mro"
#

stage CELL_ANNOTATION_PREFLIGHT(
    in  bool   is_cr_annotate,
    in  bool   is_multi,
    in  string tenx_cloud_token_path,
    in  string cell_annotation_model,
    in  path   reference_path,
    in  string cas_track_name,
    in  bool   skip_cell_annotation,
    in  h5     cr_annotate_filtered_matrix,
    in  cloupe cr_annotate_sample_cloupe,
    src py     "stages/cas_cell_typing/cell_annotation_preflight",
) using (
    mem_gb   = 8,
    volatile = strict,
)

stage CALL_CLOUD_CELL_TYPES(
    in  string          sample_id,
    in  string          sample_desc,
    in  h5              filtered_matrix,
    in  string          cell_annotation_model,
    in  file            tenx_cloud_token_path,
    in  string          pipestance_type,
    in  bool            override_num_bc_limit,
    in  cloupe          sample_cloupe,
    out CellTypeResults cell_type_results,
    out bool            cas_success,
    out bool            disable_cas_ws,
    out bool            disable_summarize,
    out json            summary,
    src py              "stages/cas_cell_typing/call_cloud_cell_types",
) split (
) using (
)

#
# @include "_cr_lib_stages.mro"
#

stage ALIGN_AND_COUNT(
    in  int               gem_well,
    in  map[]             read_chunks,
    in  path              reference_path,
    in  ReadShards        read_shards,
    in  fbc.bincode       feature_counts,
    in  frf.bincode       feature_reference,
    in  csv               target_set,
    in  map<ChemistryDef> chemistry_defs,
    in  string            aligner,
    in  bool              include_exons,
    in  bool              include_introns,
    in  bool              is_pd,
    in  bool              no_bam,
    in  int               targeted_umi_min_read_count,
    in  int               transcriptome_min_score,
    in  int               trim_polya_min_score,
    in  int               trim_tso_min_score,
    in  tbcc.bincode      total_barcode_counts,
    in  blf.json          barcode_subset,
    in  float             chevron_correction_factor,
    in  json              chevron_affected_barcodes,
    out csf[]             counts_bc_order,
    out csf[]             probe_barcode_counts,
    out bui[]             bc_umi_info,
    out asf[]             pos_sorted,
    out path              bam_header,
    out csv               barcode_summary,
    out AnnotationFiles   annotation_files,
    out parquet[]         per_read_gap_align,
    out bmsf[]            per_barcode_metrics,
    out json              summary,
    out bool              no_star_alignments,
    src comp              "cr_lib martian align_and_count",
) split (
    in  map               range,
    in  float             read_ann_subsample_rate,
    out csf               counts_bc_order_shard,
    out csf               probe_barcode_counts_shard,
    out bui               bc_umi_info_shard,
    out asf               pos_sorted_shard,
    out bsf.bincode       barcode_summary_shard,
    out bmsf              metrics_shard,
) using (
    mem_gb   = 4,
    volatile = strict,
)

stage BARCODE_CORRECTION(
    in  int               gem_well,
    in  shard[]           invalid_uncorrected,
    in  map<ChemistryDef> chemistry_defs,
    in  bsc.bincode       barcode_segment_counts,
    in  bcc.bincode       barcode_counts,
    in  bcm.bincode       valid_read_metrics,
    in  cmf.bincode       correction_map,
    in  int               min_reads_to_report_bc,
    in  json              barcodes_under_tissue,
    out shard[]           valid_corrected,
    out shard[]           invalid,
    out json              summary,
    out bcc.bincode       corrected_barcode_counts,
    out tbcc.bincode      total_barcode_counts,
    src comp              "cr_lib martian barcode_correction",
) split (
    in  int               index,
    in  int               count,
    out shard             valid_shard,
    out shard             invalid_shard,
    out bcm.bincode       chunk_summary,
) using (
    volatile = strict,
)

stage BUILD_PER_SAMPLE_VDJ_WS_CONTENTS(
    in  string        receptor,
    in  string        physical_library_id,
    in  string        multiplexing_method,
    in  json          lib_level_metrics,
    in  map<json>     per_sample_metrics,
    in  VdjGenInputs  vdj_gen_inputs,
    in  smf.json      sequencing_metrics,
    in  json          lib_level_vdj_ws_json,
    in  map<json>     per_sample_vdj_ws_json,
    in  map<json>     filter_metrics,
    in  json          multi_graph,
    in  json          vdj_cells_per_tag_json,
    out map<vwc.json> vdj_ws_contents,
    src comp          "cr_lib martian build_per_sample_vdj_ws_contents",
) split (
    in  string        sample,
    out vwc.json      vdj_ws,
) using (
    volatile = strict,
)

stage CALL_TAGS_OH(
    in  map<ChemistryDef> chemistry_defs,
    in  h5                raw_feature_bc_matrix,
    in  h5                filtered_feature_bc_matrix,
    out json              barcodes_per_tag,
    out json              summary,
    src comp              "cr_lib martian call_tags_oh",
) using (
    mem_gb   = 8,
    volatile = strict,
)

stage CALL_TAGS_RTL(
    in  map<ChemistryDef> chemistry_defs,
    in  h5                raw_feature_bc_matrix,
    in  h5                filtered_feature_bc_matrix,
    in  json              multi_graph,
    out json              barcodes_per_tag,
    out csv               frp_gem_barcode_overlap,
    out json              summary,
    src comp              "cr_lib martian call_tags_rtl",
) split (
) using (
    volatile = strict,
)

stage CHECK_BARCODES_COMPATIBILITY(
    in  map<ChemistryDef> chemistry_defs,
    in  map[]             sample_def,
    in  bool              check_library_compatibility,
    src comp              "cr_lib martian check_barcodes_compatibility",
) using (
    mem_gb = 2,
)

stage CHECK_BARCODES_COMPATIBILITY_VDJ(
    in  ChemistryDef      vdj_chemistry_def,
    in  map[]             vdj_sample_def,
    in  map<ChemistryDef> count_chemistry_defs,
    in  map[]             gex_sample_def,
    in  bool              check_library_compatibility,
    out float             similarity_score,
    src comp              "cr_lib martian check_barcodes_compatibility_vdj",
)

stage CHECK_SINGLE_BEAM_MODE(
    in  string[] beam_modes,
    out string   beam_mode,
    src comp     "cr_lib martian check_single_beam_mode",
) using (
    volatile = strict,
)

stage COLLATE_METRICS(
    in  bmsf[]          per_barcode_metrics,
    in  path            reference_path,
    in  csv             target_set,
    in  frf.bincode     feature_reference,
    in  csv             filtered_barcodes,
    in  csv             aggregate_barcodes,
    in  json            sample_barcodes,
    out json            summary,
    out csv             per_barcode_metrics,
    out SampleMetrics[] multi_metrics,
    src comp            "cr_lib martian collate_metrics",
) split (
    in  string          sample,
) using (
    volatile = strict,
)

stage COLLATE_PROBE_METRICS(
    in  csf[]      probe_barcode_counts,
    in  path       reference_path,
    in  csv        probe_set,
    in  csv        filtered_barcodes,
    in  string     probe_set_name,
    in  bi.bincode barcode_index_path,
    out csv        per_probe_metrics,
    out h5         raw_probe_bc_matrix,
    out json       estimated_gdna_metrics,
    src comp       "cr_lib martian collate_probe_metrics",
) using (
    mem_gb   = 8,
    volatile = strict,
)

stage COMPUTE_ANTIGEN_VDJ_METRICS(
    in  json           vdj_cell_barcodes,
    in  bmsf[]         per_barcode_count_metrics,
    out json           metrics_json,
    out ag.vdj.bincode metrics_bin,
    src comp           "cr_lib martian compute_antigen_vdj_metrics",
) using (
    mem_gb   = 4,
    volatile = strict,
)

stage COPY_CHEMISTRY_SPEC(
    in  map[]       sample_defs,
    in  string      chemistry_spec,
    out map<string> chemistry_specs,
    src comp        "cr_lib martian copy_chemistry_spec",
) using (
    volatile = strict,
)

stage CREATE_MULTI_GRAPH(
    in  string sample_id,
    in  string sample_desc,
    in  csv    multi_config,
    in  json   detected_probe_barcode_pairing,
    out json   multi_graph,
    src comp   "cr_lib martian create_multi_graph",
) using (
    volatile = strict,
)

stage DEMUX_PROBE_BC_MATRIX(
    in  csf[]    probe_barcode_counts,
    in  path     reference_path,
    in  csv      probe_set,
    in  string   probe_set_name,
    in  json     sample_barcodes,
    in  json     sample_cell_barcodes,
    out map<h5>  sample_raw_probe_bc_matrices,
    out map<csv> samples_per_probe_metrics,
    src comp     "cr_lib martian demux_probe_bc_matrix",
) split (
    in  string   sample_name,
    out csv      sample_per_probe_metrics,
    out h5       sample_raw_probe_bc_matrix,
) using (
    volatile = strict,
)

stage DETECT_CHEMISTRY(
    in  map[]             sample_def,
    in  path              reference_path,
    in  csv               feature_reference,
    in  map<string>       chemistry_specs,
    in  string[]          allowed_chems,
    in  int               r1_length,
    in  int               r2_length,
    in  csv               multi_config,
    in  bool              is_pd,
    in  ChemistryDef      custom_chemistry_def,
    in  FeatureConfig     feature_config,
    out map<ChemistryDef> chemistry_defs,
    out bool              is_antibody_only,
    out csv               probe_barcode_overlap,
    out json              detected_probe_barcode_pairing,
    src comp              "cr_lib martian detect_chemistry",
) using (
    mem_gb   = 20,
    volatile = strict,
) retain (
    detected_probe_barcode_pairing,
    probe_barcode_overlap,
)

stage DETECT_VDJ_RECEPTOR(
    in  string        force_receptor,
    in  path          vdj_reference_path,
    in  csv           feature_reference,
    in  map[]         gex_sample_def,
    in  map[]         vdj_sample_def,
    in  bool          is_multi,
    in  FeatureConfig feature_config,
    out string        receptor,
    out string        beam_mode,
    src comp          "cr_lib martian detect_vdj_receptor",
) using (
    volatile = strict,
)

stage EXPECT_SINGLE_BARCODE_WHITELIST(
    in  string[] barcode_whitelists,
    out string   barcode_whitelist,
    src comp     "cr_lib martian expect_single_barcode_whitelist",
)

stage EXTRACT_SINGLE_CHEMISTRY(
    in  map<ChemistryDef> chemistry_defs,
    in  string            library_to_extract,
    out ChemistryDef      chemistry_def,
    src comp              "cr_lib martian extract_single_chemistry",
) using (
    volatile = strict,
)

stage GENERATE_CAS_WEBSUMMARY(
    in  string sample_id,
    in  string sample_desc,
    in  float  cas_frac_returned_bcs,
    in  json   metadata,
    in  string cell_annotation_model,
    in  json   cell_type_bar_chart,
    in  json   spatial_cell_types_chart,
    in  json   cell_type_interactive_bar_chart,
    in  json   cell_types_box_plot,
    in  json   cell_types_umap_plot,
    in  json   diffexp,
    in  bool   cas_success,
    in  bool   disable_differential_expression,
    in  string alert_string,
    in  bool   disable_cas_ws,
    in  string pipestance_type,
    out html   summary,
    out json   cell_annotation_metrics,
    out string cell_annotation_cloupe_name,
    src comp   "cr_lib martian generate_cas_websummary",
)

stage GET_CHEMISTRY_DEF(
    in  string       chemistry_name,
    in  ChemistryDef custom_chemistry_def,
    out ChemistryDef chemistry_def,
    src comp         "cr_lib martian get_chemistry_def",
) using (
    volatile = strict,
)

stage GET_GDNA_METRICS(
    in  h5   molecule_info,
    in  csv  probe_set,
    out json summary,
    out json gdna_plot_sufficient_stats,
    src comp "cr_lib martian get_gdna_metrics",
) using (
    mem_gb   = 4,
    volatile = strict,
)

stage LOGIC_NOT(
    in  bool boolean,
    out bool not_boolean,
    src comp "cr_lib martian logic_not",
) using (
    volatile = strict,
)

stage MAKE_CORRECTION_MAP(
    in  map<ChemistryDef> chemistry_defs,
    in  bsc.bincode       barcode_segment_counts,
    out cmf.bincode       correction_map,
    src comp              "cr_lib martian make_correction_map",
) split (
) using (
    volatile = strict,
)

stage MAKE_SHARD(
    in  map<ChemistryDef> chemistry_defs,
    in  int               gem_well,
    in  map[]             read_chunks,
    in  int               r1_length,
    in  int               r2_length,
    in  float             subsample_rate,
    in  int               initial_read_pairs,
    in  path              reference_path,
    in  csv               feature_reference_path,
    in  csv               target_features,
    in  csv               target_set,
    in  string            target_set_name,
    in  FeatureConfig     feature_config,
    out shard[]           valid,
    out shard[]           invalid,
    out bcc.bincode       barcode_counts,
    out bsc.bincode       barcode_segment_counts,
    out fbc.bincode       feature_counts,
    out json              summary,
    out int               total_read_pairs,
    out frf.bincode       feature_reference,
    out bcm.bincode       bc_correct_summary,
    out smf.json          sequencing_metrics,
    src comp              "cr_lib martian make_shard",
) split (
    in  int               chunk_id,
    in  frf.bincode       feature_reference,
    out shard             valid_shard,
    out shard             invalid_shard,
    out rpc               read_prefix_counts,
    out umi               umi_counts,
    out msm.bincode       chunk_summary,
    out msh.bincode       chunk_hist,
) using (
    mem_gb   = 2,
    volatile = strict,
)

stage MERGE_GEM_WELL_FILES(
    in  GemWellFiles[] unmerged_gem_well_files,
    out GemWellFiles   merged_gem_well_files,
    src comp           "cr_lib martian merge_gem_well_files",
) using (
    volatile = strict,
)

stage MERGE_METRICS(
    in  json[] summaries,
    out json   summary,
    src comp   "cr_lib martian merge_metrics",
) using (
    volatile = strict,
)

stage MULTI_PREFLIGHT(
    in  FileOrBytes config,
    in  bool        is_pd,
    src comp        "cr_lib martian multi_preflight",
) using (
    volatile = strict,
)

stage MULTI_SETUP_CHUNKS(
    in  string            sample_id,
    in  map[]             sample_def,
    in  map<ChemistryDef> chemistry_defs,
    in  string            default_library_type,
    out map[]             chunks,
    out string[]          barcode_whitelists,
    out string            visium_hd_slide_name,
    src comp              "cr_lib martian multi_setup_chunks",
)

stage PARSE_MULTI_CONFIG(
    in  string              sample_id,
    in  string              sample_desc,
    in  FileOrBytes         config,
    in  string              config_hash,
    in  map                 params,
    in  bool                is_pd,
    out CommonInputs        common_input,
    out CountInputs         count_input,
    out VdjInputs[]         vdj_inputs,
    out VdjGenInputs        vdj_gen_inputs,
    out BasicPipelineConfig basic_config,
    out csv                 config_file,
    out FeatureConfig       feature_config,
    out csv                 feature_ref,
    out csv                 target_set,
    out json                cell_barcodes,
    out json                sample_barcodes,
    out json                non_singlet_barcodes,
    out json                cells_per_tag,
    out csv                 barcode_sample_assignments,
    src comp                "cr_lib martian parse_multi_config",
) using (
    mem_gb   = 6,
    volatile = strict,
) retain (
    barcode_sample_assignments,
    cell_barcodes,
    cells_per_tag,
    feature_ref,
    non_singlet_barcodes,
    sample_barcodes,
    target_set,
)

stage PICK_BEAM_ANALYZER(
    in  map<BeamAnalyzerOutputs> vdj_t,
    in  map<BeamAnalyzerOutputs> vdj_t_gd,
    in  map<BeamAnalyzerOutputs> vdj_b,
    out map<BeamAnalyzerOutputs> output,
    src comp                     "cr_lib martian pick_beam_analyzer",
) using (
    volatile = strict,
)

stage RUST_BRIDGE(
    in  map<ChemistryDef> chemistry_defs,
    in  int               gem_well,
    in  shard[]           valid_uncorrected,
    in  shard[]           valid_corrected,
    in  bcc.bincode       raw_barcode_counts,
    in  bcc.bincode       corrected_barcode_counts,
    out bincode.lz4[]     bc_sorted_rna_reads,
    out int[]             gem_groups,
    out json[]            barcodes,
    out json              raw_barcode_counts_json,
    out json              corrected_barcode_counts_json,
    out json              summary,
    out int               n50_n50_rpu,
    out int               processed_read_pairs,
    src comp              "cr_lib martian rust_bridge",
) split (
    in  map               range,
    in  shard[]           valid_shards,
    out bincode.lz4       chunk_bc_sorted_rna_reads,
    out json              barcodes_shard,
    out bincode           n50s_shard,
) using (
    mem_gb = 4,
)

stage SET_TARGETED_UMI_FILTER(
    in  bui[]       bc_umi_info,
    in  frf.bincode feature_reference,
    out int         umi_read_count_threshold,
    out json        summary,
    src comp        "cr_lib martian set_targeted_umi_filter",
) using (
    mem_gb   = 8,
    volatile = strict,
)

stage SETUP_VDJ_ANALYSIS(
    in  string             receptor,
    in  VdjAnalysisConfig  vdj_config,
    in  VdjDemuxSampleInfo demux_sample_info,
    in  GexMatrices        lib_level_gex,
    out string             receptor,
    out bool               disable_cell_calling,
    out bool               disable_clonotyping,
    out bool               disable_beam,
    out bool               disable_asm_metrics,
    out string             multiplexing_method,
    out string             beam_mode,
    out h5                 filtered_matrix_h5,
    out h5                 raw_matrix_h5,
    out csv                filtered_barcodes,
    src comp               "cr_lib martian setup_vdj_analysis",
)

stage SETUP_VDJ_DEMUX(
    in  SampleMatrices[]        multi_matrices,
    in  json                    multi_graph,
    out bool                    is_multi,
    out bool                    is_not_multi,
    out bool                    has_antigen,
    out map<VdjDemuxSampleInfo> per_sample_info,
    out string                  multiplexing_method,
    src comp                    "cr_lib martian setup_vdj_demux",
)

stage SUBSAMPLE_BARCODES(
    in  bcc.bincode corrected_barcode_counts,
    out blf.json    barcode_subset,
    src comp        "cr_lib martian subsample_barcodes",
) using (
    mem_gb   = 4,
    volatile = strict,
)

stage WRITE_BARCODE_INDEX(
    in  bcc.bincode barcode_counts,
    in  json        barcodes_under_tissue,
    in  json        barcode_correction_summary,
    out bi.bincode  barcode_index,
    src comp        "cr_lib martian write_barcode_index",
) split (
) using (
    volatile = strict,
)

stage WRITE_BARCODE_SUMMARY(
    in  bmsf[]      per_barcode_metrics,
    in  frf.bincode feature_reference,
    in  bi.bincode  barcode_index,
    in  json        barcode_correction_summary,
    out h5          barcode_summary,
    src comp        "cr_lib martian write_barcode_summary",
) split (
) using (
    volatile = strict,
)

stage WRITE_GENE_INDEX(
    in  path reference_path,
    out json gene_index,
    src comp "cr_lib martian write_gene_index",
) using (
    mem_gb   = 6,
    volatile = strict,
)

stage WRITE_H5_MATRIX(
    in  int               gem_well,
    in  csf[]             counts,
    in  frf.bincode       feature_reference,
    in  map<ChemistryDef> chemistry_defs,
    in  string            sample_id,
    in  bi.bincode        barcode_index,
    in  json              barcode_correction_summary,
    out h5                matrix,
    src comp              "cr_lib martian write_h5_matrix",
) split (
) using (
    volatile = strict,
)

stage WRITE_MATRIX_MARKET(
    in  csf[]       counts,
    in  frf.bincode feature_reference,
    in  bi.bincode  barcode_index,
    in  json        barcode_correction_summary,
    out path        feature_bc_matrix,
    src comp        "cr_lib martian write_matrix_market",
) split (
) using (
    volatile = strict,
)

stage WRITE_MOLECULE_INFO(
    in  map<ChemistryDef>    chemistry_defs,
    in  int                  gem_well,
    in  bui[]                counts_bc_order,
    in  path                 reference_path,
    in  map[]                read_chunks,
    in  frf.bincode          feature_reference,
    in  csv                  filtered_barcodes,
    in  csv                  per_probe_metrics,
    in  tps.json             target_panel_summary,
    in  json                 matrix_computer_summary,
    in  CellCallingParam     recovered_cells,
    in  CellCallingParam     force_cells,
    in  bool                 include_introns,
    in  bool                 disable_ab_aggregate_detection,
    in  bool                 disable_high_occupancy_gem_detection,
    in  bool                 filter_probes,
    in  string               multi_config_sha,
    in  json                 sample_barcodes,
    in  SampleMetrics[]      per_sample_metrics,
    in  bi.bincode           barcode_index,
    in  string               slide_serial_capture_area,
    out SampleMoleculeInfo   single_mol_info,
    out SampleMoleculeInfo[] multi_mol_info,
    src comp                 "cr_lib martian write_molecule_info",
) split (
) using (
    volatile = strict,
)

stage WRITE_MULTI_WEB_SUMMARY_JSON(
    in  map<json>           per_sample_metrics,
    in  json                library_metrics,
    in  smf.json            sequencing_metrics,
    in  csv                 multi_config,
    in  json                multi_graph,
    in  svg                 multi_graph_svg,
    in  CommonInputs        common_inputs,
    in  CountInputs         count_inputs,
    in  VdjGenInputs        vdj_gen_inputs,
    in  json                tag_contaminant_info,
    in  map<json>           sample_projection_plots,
    in  map<json>           sample_barcode_rank_plots,
    in  map<json>           sample_treemap_plots,
    in  json                barcode_rank_plots,
    in  json                jibes_biplot_histogram,
    in  json                antibody_histograms,
    in  map<json>           sample_antibody_histograms,
    in  json                antigen_histograms,
    in  json                cmo_projection_plot,
    in  map<vwc.json>       vdj_t_contents,
    in  map<vwc.json>       vdj_t_gd_contents,
    in  map<vwc.json>       vdj_b_contents,
    in  string              target_set_name,
    in  map<ag.vdj.bincode> antigen_vdj_metrics,
    in  map<csv>            antigen_specificity,
    in  map<json>           cell_annotation_barcharts,
    in  map<json>           cell_annotation_box_plots,
    in  map<json>           cell_annotation_umap_plots,
    in  map<json>           cell_annotation_diffexp_tables,
    in  map<json>           cell_annotation_metrics_jsons,
    in  map<bool>           cell_annotation_viable_but_not_requested,
    in  FeatureConfig       feature_config,
    in  map<ChemistryDef>   chemistry_defs,
    in  json                detected_probe_barcode_pairing,
    in  bool                no_preflight,
    out map<json>           web_summary_json,
    out map<csv>            metrics_summary_csv,
    src comp                "cr_lib martian write_multi_web_summary_json",
) using (
    mem_gb   = 5,
    volatile = strict,
) retain (
    web_summary_json,
)

stage WRITE_POS_BAM(
    in  path            bam_header,
    in  asf[]           alignments,
    in  map[]           read_chunks,
    in  string          target_set_name,
    in  json            sample_barcodes,
    in  string          slide_serial_capture_area,
    out SampleBamFile   pos_sorted_bam,
    out SampleBamFile[] multi_pos_sorted_bam,
    src comp            "cr_lib martian write_pos_bam",
) split (
    in  map             range,
    in  bool            write_header,
    out map<bam>        sample_pos_sorted_bam_chunks,
) using (
    volatile = strict,
)

#
# @include "cell_annotation_service.mro"
#

stage GET_CLOUPE_CELL_TYPES(
    in  csv  cell_types,
    out json cloupe_cas_types,
    src py   "stages/cas_cell_typing/get_cloupe_cell_types",
) using (
    volatile = strict,
)

stage GET_CELL_TYPES_BARCHART(
    in  csv  cell_types,
    out json cell_type_interactive_bar_chart,
    src py   "stages/cas_cell_typing/get_cell_types_barchart",
) using (
    volatile = strict,
)

stage GET_CELL_TYPES_BOX_PLOT(
    in  csv    cell_types,
    in  h5     filtered_matrix,
    in  string pipestance_type,
    out json   cell_types_box_plot,
    src py     "stages/cas_cell_typing/get_cell_types_box_plot",
) split (
) using (
    volatile = strict,
)

stage GET_CELL_TYPES_UMAP_PLOT(
    in  path   analysis,
    in  csv    cell_types,
    in  string pipestance_type,
    in  csv    cloupe_projection,
    out json   cell_types_umap_plot,
    src py     "stages/cas_cell_typing/get_cell_types_umap_plot",
) using (
    mem_gb   = 2,
    volatile = strict,
)

stage EXTRACT_LOUPE_PROJECTION(
    in  cloupe sample_cloupe,
    in  string projection_name,
    out csv    projection,
    src py     "stages/cas_cell_typing/extract_loupe_projection",
) using (
    mem_gb   = 2,
    volatile = strict,
)

stage ANALYZE_CELL_TYPES(
    in  csv    cell_types,
    in  path   analysis,
    in  csv    tissue_positions,
    in  png    tissue_lowres_image,
    in  json   scale_factors,
    in  float  cas_frac_returned_bcs     "Fraction of barcodes that went into cell annotation that were returned",
    in  string cas_model_used,
    in  string pipestance_type,
    in  bool   cas_success,
    in  bool   is_pd,
    in  json   metadata,
    out json   cas_metrics,
    out json   cell_type_bar_chart,
    out json   spatial_cell_types_chart,
    src py     "stages/cas_cell_typing/analyze_cell_types",
) using (
    volatile = strict,
)

stage DISABLE_CAS_REPORTER_STAGES(
    in  csv  cell_types,
    out bool disable_differential_expression,
    src py   "stages/cas_cell_typing/disable_cas_reporter_stages",
) using (
    volatile = strict,
)

stage APPEND_CELL_TYPES_CLOUPE(
    in  cloupe sample_cloupe,
    in  json   cloupe_cas_types,
    in  string cas_track_name,
    out cloupe cell_annotation_sample_cloupe,
    src py     "stages/cas_cell_typing/append_cell_types_cloupe",
)

stage WRITE_CELL_TYPES_H5(
    in  csv  cell_types,
    out h5   cell_types,
    out json cell_types_map,
    src py   "stages/cas_cell_typing/write_cell_types_h5",
)

stage DEMUX_CLOUPE_TRACK_NAME(
    in  string cas_track_name_from_user,
    in  string cas_track_name_from_ppln,
    out string cas_track_name,
    src py     "stages/cas_cell_typing/demux_cloupe_track_name",
)

stage CHECK_CLOUPE_MATRIX_CONSISTENT(
    in  cloupe sample_cloupe,
    in  h5     filtered_matrix,
    out string alert_string,
    src py     "stages/cas_cell_typing/check_cloupe_matrix_consistent",
)

stage CELL_ANNOTATION_VIABLE_BUT_NOT_REQUESTED(
    in  h5   filtered_matrix,
    in  bool skip_cell_annotation,
    out bool cell_annotation_viable_but_not_requested,
    src py   "stages/cas_cell_typing/cell_annotation_viable_but_not_requested",
)

stage TIDY_CELLTYPE_DIFFEXP(
    in  path diffexp_csv,
    in  json cell_types_map,
    in  h5   filtered_matrix,
    out csv  cas_diffexp_csv,
    out json diffexp,
    src py   "stages/cas_cell_typing/tidy_celltype_diffexp",
)

pipeline CELL_TYPE_DIFF_EXP(
    in  csv  cell_types,
    in  h5   filtered_matrix,
    out json diffexp,
    out csv  cas_diffexp_csv,
)
{
    call WRITE_CELL_TYPES_H5(
        cell_types = self.cell_types,
    )

    call RUN_DIFFERENTIAL_EXPRESSION_NG as DIFF_EXP_CAS(
        matrix_h5        = self.filtered_matrix,
        clustering_h5    = WRITE_CELL_TYPES_H5.cell_types,
        is_antibody_only = false,
    )

    call TIDY_CELLTYPE_DIFFEXP(
        diffexp_csv     = DIFF_EXP_CAS.diffexp_csv,
        cell_types_map  = WRITE_CELL_TYPES_H5.cell_types_map,
        filtered_matrix = self.filtered_matrix,
    )

    return (
        * = TIDY_CELLTYPE_DIFFEXP,
    )
}

pipeline SUMMARIZE_CELL_TYPES(
    in  h5              filtered_matrix,
    in  CellTypeResults cell_type_results,
    in  path            analysis,
    in  csv             tissue_positions,
    in  png             tissue_lowres_image,
    in  json            scale_factors,
    in  string          pipestance_type,
    in  bool            cas_success,
    in  bool            is_pd,
    in  csv             cloupe_projection,
    out json            cas_metrics,
    out json            cloupe_cas_types,
    out json            cell_type_bar_chart,
    out json            spatial_cell_types_chart,
    out json            cell_type_interactive_bar_chart,
    out json            cell_types_box_plot,
    out json            cell_types_umap_plot,
    out csv             cas_diffexp_csv,
    out json            diffexp,
    out bool            disable_differential_expression,
)
{
    call DISABLE_CAS_REPORTER_STAGES(
        cell_types = self.cell_type_results.cell_types,
    )

    call GET_CELL_TYPES_BARCHART(
        cell_types = self.cell_type_results.cell_types,
    )

    call GET_CELL_TYPES_BOX_PLOT(
        cell_types      = self.cell_type_results.cell_types,
        filtered_matrix = self.filtered_matrix,
        pipestance_type = self.pipestance_type,
    )

    call GET_CELL_TYPES_UMAP_PLOT(
        analysis          = self.analysis,
        cell_types        = self.cell_type_results.cell_types,
        pipestance_type   = self.pipestance_type,
        cloupe_projection = self.cloupe_projection,
    )

    call ANALYZE_CELL_TYPES(
        cell_types            = self.cell_type_results.cell_types,
        cas_frac_returned_bcs = self.cell_type_results.frac_returned_bcs,
        analysis              = self.analysis,
        tissue_positions      = self.tissue_positions,
        tissue_lowres_image   = self.tissue_lowres_image,
        scale_factors         = self.scale_factors,
        cas_model_used        = self.cell_type_results.model_used,
        pipestance_type       = self.pipestance_type,
        cas_success           = self.cas_success,
        metadata              = self.cell_type_results.metadata,
        is_pd                 = self.is_pd,
    )

    call GET_CLOUPE_CELL_TYPES(
        cell_types = self.cell_type_results.cell_types,
    )

    call CELL_TYPE_DIFF_EXP(
        cell_types      = self.cell_type_results.cell_types,
        filtered_matrix = self.filtered_matrix,
    ) using (
        disabled = DISABLE_CAS_REPORTER_STAGES.disable_differential_expression,
    )

    return (
        cas_metrics              = ANALYZE_CELL_TYPES.cas_metrics,
        cloupe_cas_types         = GET_CLOUPE_CELL_TYPES.cloupe_cas_types,
        cell_type_bar_chart      = ANALYZE_CELL_TYPES.cell_type_bar_chart,
        spatial_cell_types_chart = ANALYZE_CELL_TYPES.spatial_cell_types_chart,
        cell_type_interactive_bar_chart = GET_CELL_TYPES_BARCHART.cell_type_interactive_bar_chart,
        cell_types_box_plot      = GET_CELL_TYPES_BOX_PLOT.cell_types_box_plot,
        cell_types_umap_plot     = GET_CELL_TYPES_UMAP_PLOT.cell_types_umap_plot,
        diffexp                  = CELL_TYPE_DIFF_EXP.diffexp,
        cas_diffexp_csv          = CELL_TYPE_DIFF_EXP.cas_diffexp_csv,
        disable_differential_expression = DISABLE_CAS_REPORTER_STAGES.disable_differential_expression,
    )
}

pipeline CELL_ANNOTATION_SERVICE(
    in  h5        filtered_matrix,
    in  path      analysis,
    in  string    sample_id,
    in  string    sample_desc,
    in  csv       tissue_positions,
    in  png       tissue_lowres_image,
    in  json      scale_factors,
    in  string    cell_annotation_model,
    in  string    tenx_cloud_token_path,
    in  string    pipestance_type,
    in  string    alert_string,
    in  bool      is_pd,
    in  csv       cloupe_projection,
    in  cloupe    sample_cloupe,
    out CellTypes cell_types,
    out json      cas_metrics,
    out json      cloupe_cas_types,
    out json      cell_type_bar_chart,
    out json      spatial_cell_types_chart,
    out json      cell_type_interactive_bar_chart,
    out json      cell_types_box_plot,
    out json      cell_types_umap_plot,
    out json      cell_annotation_metrics,
    out json      diffexp,
    out string    cell_annotation_cloupe_name,
    out bool      disable_summarize,
)
{
    call CALL_CLOUD_CELL_TYPES(
        override_num_bc_limit = false,
        *                     = self,
    )

    call SUMMARIZE_CELL_TYPES(
        filtered_matrix     = self.filtered_matrix,
        cell_type_results   = CALL_CLOUD_CELL_TYPES.cell_type_results,
        analysis            = self.analysis,
        tissue_positions    = self.tissue_positions,
        tissue_lowres_image = self.tissue_lowres_image,
        scale_factors       = self.scale_factors,
        pipestance_type     = self.pipestance_type,
        cas_success         = CALL_CLOUD_CELL_TYPES.cas_success,
        is_pd               = self.is_pd,
        cloupe_projection   = self.cloupe_projection,
    ) using (
        disabled = CALL_CLOUD_CELL_TYPES.disable_summarize,
    )

    call GENERATE_CAS_WEBSUMMARY(
        sample_id                = self.sample_id,
        sample_desc              = self.sample_desc,
        alert_string             = self.alert_string,
        cas_frac_returned_bcs    = CALL_CLOUD_CELL_TYPES.cell_type_results.frac_returned_bcs,
        cell_annotation_model    = CALL_CLOUD_CELL_TYPES.cell_type_results.model_used,
        cell_type_bar_chart      = SUMMARIZE_CELL_TYPES.cell_type_bar_chart,
        diffexp                  = SUMMARIZE_CELL_TYPES.diffexp,
        spatial_cell_types_chart = SUMMARIZE_CELL_TYPES.spatial_cell_types_chart,
        cell_type_interactive_bar_chart = SUMMARIZE_CELL_TYPES.cell_type_interactive_bar_chart,
        cell_types_box_plot      = SUMMARIZE_CELL_TYPES.cell_types_box_plot,
        cell_types_umap_plot     = SUMMARIZE_CELL_TYPES.cell_types_umap_plot,
        metadata                 = CALL_CLOUD_CELL_TYPES.cell_type_results.metadata,
        cas_success              = CALL_CLOUD_CELL_TYPES.cas_success,
        disable_cas_ws           = CALL_CLOUD_CELL_TYPES.disable_cas_ws,
        disable_differential_expression = SUMMARIZE_CELL_TYPES.disable_differential_expression,
        pipestance_type          = self.pipestance_type,
    ) using (
        disabled = CALL_CLOUD_CELL_TYPES.disable_summarize,
    )

    return (
        cell_types                  = {
            cell_annotation_differential_expression: SUMMARIZE_CELL_TYPES.cas_diffexp_csv,
            cell_annotation_results:                 CALL_CLOUD_CELL_TYPES.cell_type_results.results,
            cell_types:                              CALL_CLOUD_CELL_TYPES.cell_type_results.cell_types,
            web_summary_cell_types:                  GENERATE_CAS_WEBSUMMARY.summary,
        },
        cell_annotation_metrics     = GENERATE_CAS_WEBSUMMARY.cell_annotation_metrics,
        cell_annotation_cloupe_name = GENERATE_CAS_WEBSUMMARY.cell_annotation_cloupe_name,
        disable_summarize           = CALL_CLOUD_CELL_TYPES.disable_summarize,
        *                           = SUMMARIZE_CELL_TYPES,
    )
}

pipeline CELLRANGER_ANNOTATE_CS(
    in  h5      filtered_matrix,
    in  string  tenx_cloud_token_path,
    in  string  sample_id,
    in  string  sample_desc,
    in  string  cell_annotation_model,
    in  cloupe  sample_cloupe,
    in  string  cas_track_name,
    out csv     cell_types,
    out json.gz cell_annotation_results,
    out html    web_summary_cell_types,
    out csv     cell_annotation_differential_expression,
    out cloupe  cell_annotation_sample_cloupe,
)
{
    call CELL_ANNOTATION_PREFLIGHT(
        is_cr_annotate              = true,
        is_multi                    = false,
        tenx_cloud_token_path       = self.tenx_cloud_token_path,
        cell_annotation_model       = self.cell_annotation_model,
        cas_track_name              = self.cas_track_name,
        reference_path              = null,
        cr_annotate_filtered_matrix = self.filtered_matrix,
        cr_annotate_sample_cloupe   = self.sample_cloupe,
        skip_cell_annotation        = false,
    ) using (
        preflight = true,
    )

    call CHECK_CLOUPE_MATRIX_CONSISTENT(
        * = self,
    )

    call EXTRACT_LOUPE_PROJECTION(
        sample_cloupe   = self.sample_cloupe,
        projection_name = "umap",
    )

    call CELL_ANNOTATION_SERVICE(
        filtered_matrix       = self.filtered_matrix,
        analysis              = null,
        sample_id             = self.sample_id,
        sample_desc           = self.sample_desc,
        tissue_positions      = null,
        tissue_lowres_image   = null,
        scale_factors         = null,
        cell_annotation_model = self.cell_annotation_model,
        tenx_cloud_token_path = self.tenx_cloud_token_path,
        pipestance_type       = "CELLRANGER_ANNOTATE_CS",
        alert_string          = CHECK_CLOUPE_MATRIX_CONSISTENT.alert_string,
        is_pd                 = false,
        cloupe_projection     = EXTRACT_LOUPE_PROJECTION.projection,
        sample_cloupe         = self.sample_cloupe,
    )

    call DEMUX_CLOUPE_TRACK_NAME(
        cas_track_name_from_user = self.cas_track_name,
        cas_track_name_from_ppln = CELL_ANNOTATION_SERVICE.cell_annotation_cloupe_name,
    )

    call APPEND_CELL_TYPES_CLOUPE(
        sample_cloupe    = self.sample_cloupe,
        cloupe_cas_types = CELL_ANNOTATION_SERVICE.cloupe_cas_types,
        cas_track_name   = DEMUX_CLOUPE_TRACK_NAME.cas_track_name,
    ) using (
        disabled = CELL_ANNOTATION_SERVICE.disable_summarize,
    )

    return (
        cell_types                    = CELL_ANNOTATION_SERVICE.cell_types.cell_types,
        cell_annotation_results       = CELL_ANNOTATION_SERVICE.cell_types.cell_annotation_results,
        cell_annotation_differential_expression = CELL_ANNOTATION_SERVICE.cell_types.cell_annotation_differential_expression,
        web_summary_cell_types        = CELL_ANNOTATION_SERVICE.cell_types.web_summary_cell_types,
        cell_annotation_sample_cloupe = APPEND_CELL_TYPES_CLOUPE.cell_annotation_sample_cloupe,
    )
}

#
# @include "_assign_tags_stages.mro"
#

stage MULTIPLEXING_METHOD(
    in  json   multi_graph,
    out bool   multiplexing_is_not_rtl,
    out bool   multiplexing_is_not_cmo_or_hashtag,
    out bool   multiplexing_is_not_oh,
    out bool   output_per_sample_raw_matrix,
    out string multiplexing_method,
    src py     "../rna/stages/multi/multiplexing_method",
) using (
    mem_gb   = 1,
    threads  = 1,
    volatile = strict,
)

stage CALL_TAGS_MARGINAL(
    in  csv    filtered_barcodes,
    in  h5     filtered_feature_counts_matrix,
    in  string throughput,
    in  string multiplexing_method,
    in  string library_type,
    out csv    marginal_tag_calls_per_cell,
    out csv    marginal_tag_frequencies,
    out json   tag_contaminant_info,
    src py     "stages/feature/call_tags_marginal",
) split (
)

stage CALL_TAGS_JIBES(
    in  csv    marginal_tag_calls_per_cell,
    in  csv    marginal_tag_frequencies,
    in  csv    filtered_barcodes,
    in  h5     filtered_feature_counts_matrix,
    in  h5     molecule_info,
    in  string throughput,
    in  string library_type,
    in  string multiplexing_method,
    in  float  min_assignment_confidence,
    out json   jibes_parameters,
    out csv    jibes_model_summary,
    out json   jibes_summary_data,
    out csv    assignment_confidence_table,
    out csv    tag_calls_summary,
    out csv    tag_calls_per_cell,
    out json   tag_call_metrics,
    out json   cells_per_tag,
    out json   tag_umi_thresholds_json,
    out csv    tag_umi_thresholds_csv,
    out pickle tag_assigner_pickle,
    out json   non_singlet_barcodes,
    src py     "stages/feature/call_tags_jibes",
) split (
) using (
    volatile = strict,
)

stage DETERMINE_SAMPLE_ASSIGNMENTS(
    in  json[]             barcodes_per_tag,
    in  BarcodeAssignments force_sample_barcodes,
    in  csv                filtered_barcodes,
    in  json               multi_graph,
    in  json               non_singlet_barcodes,
    in  int                gem_well,
    in  h5                 raw_feature_bc_matrix,
    out json               cells_per_tag,
    out json               sample_barcodes,
    out json               sample_cell_barcodes,
    out json               non_singlet_barcodes,
    out map<json>          sample_summaries,
    out json               summary,
    src py                 "stages/multi/determine_sample_assignments",
) using (
    mem_gb   = 8,
    volatile = strict,
)

stage COMPUTE_EXTRA_MULTIPLEXING_METRICS(
    in  h5     molecule_info,
    in  h5     filtered_feature_counts_matrix,
    in  json   multi_graph,
    in  json   sample_cell_barcodes,
    in  json   non_singlet_barcodes,
    in  string multiplexing_method,
    out json   summary,
    src py     "stages/multi/compute_extra_multiplexing_metrics",
) split (
) using (
    volatile = strict,
)

#
# @include "_assign_tags.mro"
#

pipeline _ASSIGN_TAGS(
    in  map<ChemistryDef>  chemistry_defs,
    in  csv                filtered_barcodes,
    in  h5                 filtered_feature_counts_matrix,
    in  h5                 raw_feature_bc_matrix,
    in  h5                 molecule_info,
    in  BarcodeAssignments force_sample_barcodes,
    in  json               multi_graph,
    in  int                gem_well,
    in  float              min_assignment_confidence,
    in  string             throughput,
    in  json               inferred_throughputs,
    out AssignTagsOuts     assign_tags_outs,
)
{
    call MULTIPLEXING_METHOD(
        multi_graph = self.multi_graph,
    )

    call CALL_TAGS_MARGINAL(
        filtered_barcodes   = self.filtered_barcodes,
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        throughput          = self.throughput,
        multiplexing_method = MULTIPLEXING_METHOD.multiplexing_method,
        library_type        = null,
    ) using (
        disabled = MULTIPLEXING_METHOD.multiplexing_is_not_cmo_or_hashtag,
    )

    call CALL_TAGS_JIBES(
        marginal_tag_calls_per_cell = CALL_TAGS_MARGINAL.marginal_tag_calls_per_cell,
        filtered_barcodes           = self.filtered_barcodes,
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        molecule_info               = self.molecule_info,
        marginal_tag_frequencies    = CALL_TAGS_MARGINAL.marginal_tag_frequencies,
        throughput                  = self.throughput,
        min_assignment_confidence   = self.min_assignment_confidence,
        multiplexing_method         = MULTIPLEXING_METHOD.multiplexing_method,
        library_type                = null,
    ) using (
        disabled = MULTIPLEXING_METHOD.multiplexing_is_not_cmo_or_hashtag,
    )

    call CALL_TAGS_RTL(
        chemistry_defs             = self.chemistry_defs,
        raw_feature_bc_matrix      = self.raw_feature_bc_matrix,
        filtered_feature_bc_matrix = self.filtered_feature_counts_matrix,
        multi_graph                = self.multi_graph,
    ) using (
        disabled = MULTIPLEXING_METHOD.multiplexing_is_not_rtl,
    )

    call CALL_TAGS_OH(
        chemistry_defs             = self.chemistry_defs,
        raw_feature_bc_matrix      = self.raw_feature_bc_matrix,
        filtered_feature_bc_matrix = self.filtered_feature_counts_matrix,
    ) using (
        disabled = MULTIPLEXING_METHOD.multiplexing_is_not_oh,
    )

    call DETERMINE_SAMPLE_ASSIGNMENTS(
        barcodes_per_tag      = [
            CALL_TAGS_JIBES.cells_per_tag,
            CALL_TAGS_RTL.barcodes_per_tag,
            CALL_TAGS_OH.barcodes_per_tag,
        ],
        force_sample_barcodes = self.force_sample_barcodes,
        filtered_barcodes     = self.filtered_barcodes,
        multi_graph           = self.multi_graph,
        non_singlet_barcodes  = CALL_TAGS_JIBES.non_singlet_barcodes,
        gem_well              = self.gem_well,
        raw_feature_bc_matrix = self.raw_feature_bc_matrix,
    )

    call COMPUTE_EXTRA_MULTIPLEXING_METRICS(
        molecule_info        = self.molecule_info,
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        multi_graph          = self.multi_graph,
        sample_cell_barcodes = DETERMINE_SAMPLE_ASSIGNMENTS.sample_cell_barcodes,
        non_singlet_barcodes = DETERMINE_SAMPLE_ASSIGNMENTS.non_singlet_barcodes,
        multiplexing_method  = MULTIPLEXING_METHOD.multiplexing_method,
    ) using (
        disabled = MULTIPLEXING_METHOD.multiplexing_is_not_cmo_or_hashtag,
    )

    call MERGE_METRICS(
        summaries = [
            CALL_TAGS_JIBES.tag_call_metrics,
            CALL_TAGS_RTL.summary,
            CALL_TAGS_OH.summary,
            DETERMINE_SAMPLE_ASSIGNMENTS.summary,
            COMPUTE_EXTRA_MULTIPLEXING_METRICS.summary,
        ],
    )

    return (
        assign_tags_outs = {
            assignment_confidence_table:   CALL_TAGS_JIBES.assignment_confidence_table,
            cells_per_tag:                 DETERMINE_SAMPLE_ASSIGNMENTS.cells_per_tag,
            force_sample_barcodes:         self.force_sample_barcodes,
            frp_gem_barcode_overlap:       CALL_TAGS_RTL.frp_gem_barcode_overlap,
            gem_well_inferred_throughputs: self.inferred_throughputs,
            jibes_model_summary:           CALL_TAGS_JIBES.jibes_model_summary,
            jibes_parameters:              CALL_TAGS_JIBES.jibes_parameters,
            jibes_summary_data:            CALL_TAGS_JIBES.jibes_summary_data,
            marginal_tag_frequencies:      CALL_TAGS_MARGINAL.marginal_tag_frequencies,
            multiplexing_method:           MULTIPLEXING_METHOD.multiplexing_method,
            non_singlet_barcodes:          DETERMINE_SAMPLE_ASSIGNMENTS.non_singlet_barcodes,
            output_per_sample_raw_matrix:  MULTIPLEXING_METHOD.output_per_sample_raw_matrix,
            sample_assignment_metrics:     DETERMINE_SAMPLE_ASSIGNMENTS.sample_summaries,
            sample_barcodes:               DETERMINE_SAMPLE_ASSIGNMENTS.sample_barcodes,
            sample_cell_barcodes:          DETERMINE_SAMPLE_ASSIGNMENTS.sample_cell_barcodes,
            tag_assigner_pickle:           CALL_TAGS_JIBES.tag_assigner_pickle,
            tag_call_metrics:              MERGE_METRICS.summary,
            tag_calls_per_cell:            CALL_TAGS_JIBES.tag_calls_per_cell,
            tag_calls_summary:             CALL_TAGS_JIBES.tag_calls_summary,
            tag_contaminant_info:          CALL_TAGS_MARGINAL.tag_contaminant_info,
            tag_umi_thresholds_csv:        CALL_TAGS_JIBES.tag_umi_thresholds_csv,
            tag_umi_thresholds_json:       CALL_TAGS_JIBES.tag_umi_thresholds_json,
        },
    )
}

#
# @include "_basic_sc_rna_counter_stages.mro"
#

stage FILTER_BARCODES(
    in  map<ChemistryDef> chemistry_defs,
    in  string            sample_id,
    in  h5                matrices_h5,
    in  csv               barcode_correction_csv,
    in  bool              is_antibody_only,
    in  path              reference_path,
    in  int[]             gem_groups,
    in  CellCalling       config,
    in  csv               target_set,
    in  json              multi_graph,
    in  csv               per_barcode_metrics,
    in  bool              is_spatial,
    out json              summary,
    out csv               filtered_barcodes,
    out csv               aggregate_barcodes,
    out h5                filtered_matrices_h5,
    out path              filtered_matrices_mex,
    out csv               nonambient_calls,
    out csv               mitochondrial_summary,
    out csv               isotype_normalization_factors,
    src py                "stages/counter/filter_barcodes",
) split (
    in  ProbeBCDef        probe_bc_def,
    out json              filtered_metrics_groups,
    out bincode           filtered_bcs_groups,
    out csv               co_mitochondrial_summary,
) using (
    mem_gb   = 8,
    volatile = strict,
)

stage INFER_GEM_WELL_THROUGHPUT(
    in  map<ChemistryDef> chemistry_defs,
    in  string            throughput,
    in  h5                filtered_feature_counts_matrix,
    in  path              reference_path,
    in  h5                barcode_summary_h5,
    out string            throughput,
    out json              inferred_throughputs,
    src py                "stages/feature/infer_gem_well_throughput",
) using (
    mem_gb   = 2,
    volatile = strict,
)

stage MULTI_WRITE_PER_SAMPLE_MATRICES(
    in  h5               matrix_h5,
    in  h5               raw_matrix_h5,
    in  csv              filtered_barcodes,
    in  csv              aggregate_barcodes,
    in  json             sample_barcodes,
    in  json             sample_cell_barcodes,
    in  json             multi_graph,
    in  map<h5>          sample_raw_probe_bc_matrices,
    in  map<csv>         samples_per_probe_metrics,
    out SampleMatrices[] sample_matrices,
    src py               "stages/multi/multi_write_per_sample_matrices",
) split (
    in  string           sample,
    out SampleMatrices   matrices,
) using (
    mem_gb   = 4,
    volatile = strict,
)

stage SUMMARIZE_BASIC_REPORTS(
    in  string           sample,
    in  h5               matrices_h5,
    in  csv              filtered_barcodes,
    in  csv              per_barcode_metrics,
    in  json             matrix_computer_summary,
    in  h5               barcode_summary,
    in  CellCallingParam recovered_cells,
    in  path             reference_path,
    in  json[]           summary_jsons,
    in  bool             sample_bcs_only,
    in  tps.json         target_panel_summary,
    out json             summary,
    src py               "stages/counter/summarize_basic_reports",
) split (
) using (
    volatile = strict,
)

stage DISABLE_STAGES(
    in  bool  no_bam,
    in  bool  disable_multi,
    in  bool  is_pd,
    in  bool  is_visium_hd,
    in  h5    raw_feature_bc_matrix,
    in  csf[] probe_barcode_counts,
    out bool  disable_legacy_bam,
    out bool  disable_sample_bams,
    out bool  disable_assign_tags,
    out bool  disable_subsampling,
    out bool  no_probe_barcode_counts,
    out bool  no_probe_barcode_matrix_demux,
    src py    "stages/multi/disable_stages",
) using (
    volatile = strict,
)

stage SUBSAMPLE_READS(
    in  h5     molecule_info,
    in  csv    filtered_barcodes,
    in  string target_mode,
    out json   summary,
    src py     "stages/counter/subsample_reads",
) split (
    in  int    chunk_start,
    in  int    chunk_len,
    in  map[]  subsample_info,
    out pickle metrics,
) using (
    mem_gb   = 4,
    volatile = strict,
)

stage COMPUTE_CORRECTION_FACTOR(
    in  V1PatternFixArgs v1_pattern_fix,
    in  json             barcodes_under_tissue,
    out float            correction_factor,
    out json             affected_barcodes,
    out bool             disable_downsampling,
    src py               "stages/spatial/compute_correction_factor",
) using (
    volatile = strict,
)

stage DISABLE_CORRECTION_FACTOR(
    in  V1PatternFixArgs v1_pattern_fix,
    out bool             disable_correction_factor,
    src py               "stages/spatial/disable_correction_factor",
) using (
    volatile = strict,
)

stage CHECK_CORRECTION_FACTOR(
    in  V1PatternFixArgs v1_pattern_fix,
    in  h5               filtered_fbm,
    src py               "stages/spatial/check_correction_factor",
) using (
    volatile = strict,
)

#
# @include "_slfe_cells_reporter.mro"
#

pipeline _SLFE_CELLS_REPORTER(
    in  map<ChemistryDef> chemistry_defs,
    in  path              reference_path,
    in  CellCallingParam  recovered_cells,
    in  CellCallingParam  force_cells,
    in  frf.bincode       slfe_feature_reference,
    in  tps.json          target_panel_summary,
    in  h5                matrices_h5,
    in  map[]             read_chunks,
    in  int               gem_well,
    in  bui[]             report_mol_inputs,
    in  json              matrix_computer_summary,
    in  h5                barcode_summary,
    in  csv               filtered_barcodes,
    in  csv               per_probe_metrics,
    in  json              filter_barcodes_summary,
    in  csv               per_barcode_metrics,
    in  bool              include_introns,
    in  bool              filter_probes,
    in  bool              disable_ab_aggregate_detection,
    in  bool              disable_high_occupancy_gem_detection,
    in  bool              disable_subsampling,
    in  string            multi_config_sha,
    in  bi.bincode        barcode_index,
    in  string            slide_serial_capture_area,
    out json              summary,
    out h5                molecule_info,
)
{
    call WRITE_MOLECULE_INFO(
        chemistry_defs            = self.chemistry_defs,
        gem_well                  = self.gem_well,
        counts_bc_order           = self.report_mol_inputs,
        reference_path            = self.reference_path,
        read_chunks               = self.read_chunks,
        feature_reference         = self.slfe_feature_reference,
        target_panel_summary      = self.target_panel_summary,
        matrix_computer_summary   = self.matrix_computer_summary,
        recovered_cells           = self.recovered_cells,
        force_cells               = self.force_cells,
        filtered_barcodes         = self.filtered_barcodes,
        per_probe_metrics         = self.per_probe_metrics,
        include_introns           = self.include_introns,
        filter_probes             = self.filter_probes,
        disable_ab_aggregate_detection = self.disable_ab_aggregate_detection,
        disable_high_occupancy_gem_detection = self.disable_high_occupancy_gem_detection,
        multi_config_sha          = self.multi_config_sha,
        sample_barcodes           = null,
        per_sample_metrics        = null,
        barcode_index             = self.barcode_index,
        slide_serial_capture_area = self.slide_serial_capture_area,
    )

    call SUBSAMPLE_READS(
        molecule_info     = WRITE_MOLECULE_INFO.single_mol_info.h5_file,
        filtered_barcodes = self.filtered_barcodes,
        target_mode       = null,
    ) using (
        disabled = self.disable_subsampling,
        volatile = true,
    )

    call SUMMARIZE_BASIC_REPORTS(
        sample                  = null,
        matrices_h5             = self.matrices_h5,
        filtered_barcodes       = self.filtered_barcodes,
        per_barcode_metrics     = self.per_barcode_metrics,
        matrix_computer_summary = self.matrix_computer_summary,
        barcode_summary         = self.barcode_summary,
        recovered_cells         = self.recovered_cells,
        reference_path          = self.reference_path,
        # this is being run "library level", use all bcs
        sample_bcs_only         = false,
        target_panel_summary    = self.target_panel_summary,
        summary_jsons           = [
            self.matrix_computer_summary,
            SUBSAMPLE_READS.summary,
            WRITE_MOLECULE_INFO.single_mol_info.summary,
            self.filter_barcodes_summary,
        ],
    )

    return (
        summary       = SUMMARIZE_BASIC_REPORTS.summary,
        molecule_info = WRITE_MOLECULE_INFO.single_mol_info.h5_file,
    )
}

# CELLS_REPORTER but for sliced samples, does not write the molecule info
pipeline _SAMPLE_CELLS_REPORTER(
    in  string           sample,
    in  h5               molecule_info,
    in  path             reference_path,
    in  CellCallingParam recovered_cells,
    in  h5               matrices_h5,
    in  json             matrix_computer_summary,
    in  csv              filtered_barcodes,
    in  csv              per_barcode_metrics,
    in  h5               barcode_summary,
    in  json             sample_assignment_metrics,
    in  json             count_analyzer_metrics,
    in  json             crispr_analyzer_metrics,
    in  json             targeted_analyzer_metrics,
    in  tps.json         target_panel_summary,
    out json             summary,
)
{
    call SUBSAMPLE_READS(
        molecule_info     = self.molecule_info,
        filtered_barcodes = self.filtered_barcodes,
        target_mode       = null,
    ) using (
        volatile = true,
    )

    call SUMMARIZE_BASIC_REPORTS(
        sample                  = self.sample,
        matrices_h5             = self.matrices_h5,
        filtered_barcodes       = self.filtered_barcodes,
        per_barcode_metrics     = self.per_barcode_metrics,
        matrix_computer_summary = self.matrix_computer_summary,
        barcode_summary         = self.barcode_summary,
        recovered_cells         = self.recovered_cells,
        reference_path          = self.reference_path,
        # we want "all reads" etc to include only those with sample barcodes.
        sample_bcs_only         = true,
        target_panel_summary    = self.target_panel_summary,
        summary_jsons           = [
            self.matrix_computer_summary,
            SUBSAMPLE_READS.summary,
            self.sample_assignment_metrics,
            self.count_analyzer_metrics,
            self.crispr_analyzer_metrics,
            self.targeted_analyzer_metrics,
        ],
    )

    return (
        summary = SUMMARIZE_BASIC_REPORTS.summary,
    )
}

#
# @include "_slfe_partial_first_pass.mro"
#

# Inputs copied crom _cr_lib_stages. Cleaner way to do this?
pipeline _SLFE_PARTIAL_FIRST_PASS(
    in  int               gem_well,
    in  map[]             read_chunks,
    in  path              reference_path,
    in  ReadShards        read_shards,
    in  fbc.bincode       feature_counts,
    in  frf.bincode       feature_reference,
    in  csv               target_set,
    in  map<ChemistryDef> chemistry_defs,
    in  bool              include_introns,
    in  string            aligner,
    in  bool              is_pd,
    in  int               trim_polya_min_score,
    in  int               trim_tso_min_score,
    in  tbcc.bincode      total_barcode_counts,
    in  bcc.bincode       corrected_barcode_counts,
    out int               umi_read_count_threshold,
    out json              umi_filtering_summary,
)
{
    call SUBSAMPLE_BARCODES(
        corrected_barcode_counts = self.corrected_barcode_counts,
    )

    call ALIGN_AND_COUNT as INITIAL_ALIGN_AND_COUNT(
        gem_well                    = self.gem_well,
        read_chunks                 = self.read_chunks,
        reference_path              = self.reference_path,
        read_shards                 = self.read_shards,
        feature_counts              = self.feature_counts,
        feature_reference           = self.feature_reference,
        target_set                  = self.target_set,
        chemistry_defs              = self.chemistry_defs,
        include_exons               = true,
        include_introns             = self.include_introns,
        no_bam                      = true,
        aligner                     = self.aligner,
        is_pd                       = self.is_pd,
        transcriptome_min_score     = 30,
        trim_polya_min_score        = self.trim_polya_min_score,
        trim_tso_min_score          = self.trim_tso_min_score,
        targeted_umi_min_read_count = null,
        total_barcode_counts        = self.total_barcode_counts,
        barcode_subset              = SUBSAMPLE_BARCODES.barcode_subset,
        chevron_correction_factor   = null,
        chevron_affected_barcodes   = null,
    )

    call SET_TARGETED_UMI_FILTER(
        bc_umi_info       = INITIAL_ALIGN_AND_COUNT.bc_umi_info,
        feature_reference = self.feature_reference,
    )

    return (
        umi_read_count_threshold = SET_TARGETED_UMI_FILTER.umi_read_count_threshold,
        umi_filtering_summary    = SET_TARGETED_UMI_FILTER.summary,
    )
}

#
# @include "_slfe_matrix_computer.mro"
#

pipeline MAKE_READ_SHARDS_STRUCT(
    in  shard[]    valid_reads,
    in  shard[]    corrected_reads,
    in  shard[]    invalid_reads,
    out ReadShards read_shards,
)
{
    return (
        read_shards = {
            corrected_reads: self.corrected_reads,
            invalid_reads:   self.invalid_reads,
            valid_reads:     self.valid_reads,
        },
    )
}

pipeline _SLFE_MATRIX_COMPUTER(
    in  string            sample_id,
    in  map<ChemistryDef> chemistry_defs,
    in  json              barcodes_under_tissue,
    in  bool              is_pd,
    in  map[]             chunks,
    in  path              reference_path,
    in  float             subsample_rate,
    in  int               initial_reads,
    in  int               r1_length,
    in  int               r2_length,
    in  int               trim_polya_min_score,
    in  int               trim_tso_min_score,
    in  int               min_reads_to_report_bc,
    in  csv               feature_reference,
    in  csv               target_features,
    in  csv               target_set,
    in  string            target_set_name,
    in  bool              include_exons,
    in  bool              include_introns,
    in  bool              no_bam,
    in  string            aligner,
    in  bool              disable_target_umi_filter,
    in  FeatureConfig     feature_config,
    # Note: _SLFE_MATRIX_COMPUTER processes data from a single gem well.
    in  int               gem_well,
    in  V1PatternFixArgs  v1_pattern_fix,
    out frf.bincode       slfe_feature_reference,
    out csv               barcode_correction_csv,
    out h5                barcode_summary,
    out h5                raw_gene_bc_matrices_h5,
    out path              raw_gene_bc_matrices_mex,
    out ReadShards        read_shards,
    out csf[]             counts_bc_order,
    out bui[]             report_mol_inputs,
    out json              summary,
    out AnnotationFiles   annotation_files,
    out csv               per_barcode_metrics,
    out bmsf[]            per_barcode_metrics_shard,
    out bui[]             bc_umi_info,
    out csf[]             probe_barcode_counts,
    out path              bam_header,
    out asf[]             alignments,
    out map[]             read_chunks,
    out SampleMetrics[]   multi_metrics,
    out json              gem_well_alignment_metrics,
    out bi.bincode        barcode_index,
    out smf.json          sequencing_metrics,
    ### One file has bc_counts from MAKE_SHARD for use by ATAC
    out bcc.bincode       make_shard_bc_counts,
    ### This has total barcodes from BARCODE_CORRECTION for use in Spatial
    out tbcc.bincode      barcode_counts,
    out bool              no_star_alignments,
    out parquet[]         per_read_gap_align,
)
{
    call MAKE_SHARD(
        gem_well               = self.gem_well,
        chemistry_defs         = self.chemistry_defs,
        read_chunks            = self.chunks,
        r1_length              = self.r1_length,
        r2_length              = self.r2_length,
        subsample_rate         = self.subsample_rate,
        initial_read_pairs     = self.initial_reads,
        reference_path         = self.reference_path,
        target_features        = self.target_features,
        # TODO: Replace these with the target_panel_summary
        target_set             = self.target_set,
        target_set_name        = self.target_set_name,
        feature_reference_path = self.feature_reference,
        feature_config         = self.feature_config,
    )

    call MAKE_CORRECTION_MAP(
        chemistry_defs         = self.chemistry_defs,
        barcode_segment_counts = MAKE_SHARD.barcode_segment_counts,
    )

    call BARCODE_CORRECTION(
        gem_well               = self.gem_well,
        barcode_counts         = MAKE_SHARD.barcode_counts,
        barcode_segment_counts = MAKE_SHARD.barcode_segment_counts,
        chemistry_defs         = self.chemistry_defs,
        invalid_uncorrected    = MAKE_SHARD.invalid,
        valid_read_metrics     = MAKE_SHARD.bc_correct_summary,
        min_reads_to_report_bc = self.min_reads_to_report_bc,
        correction_map         = MAKE_CORRECTION_MAP.correction_map,
        barcodes_under_tissue  = self.barcodes_under_tissue,
    )

    call MAKE_READ_SHARDS_STRUCT(
        valid_reads     = MAKE_SHARD.valid,
        corrected_reads = BARCODE_CORRECTION.valid_corrected,
        invalid_reads   = BARCODE_CORRECTION.invalid,
    )

    call _SLFE_PARTIAL_FIRST_PASS(
        gem_well                 = self.gem_well,
        read_chunks              = self.chunks,
        reference_path           = self.reference_path,
        read_shards              = MAKE_READ_SHARDS_STRUCT.read_shards,
        feature_counts           = MAKE_SHARD.feature_counts,
        feature_reference        = MAKE_SHARD.feature_reference,
        target_set               = self.target_set,
        chemistry_defs           = self.chemistry_defs,
        include_introns          = self.include_introns,
        aligner                  = self.aligner,
        is_pd                    = self.is_pd,
        trim_polya_min_score     = self.trim_polya_min_score,
        trim_tso_min_score       = self.trim_tso_min_score,
        total_barcode_counts     = BARCODE_CORRECTION.total_barcode_counts,
        corrected_barcode_counts = BARCODE_CORRECTION.corrected_barcode_counts,
    ) using (
        disabled = self.disable_target_umi_filter,
    )

    call DISABLE_CORRECTION_FACTOR(
        v1_pattern_fix = self.v1_pattern_fix,
    )

    call COMPUTE_CORRECTION_FACTOR(
        v1_pattern_fix        = self.v1_pattern_fix,
        barcodes_under_tissue = self.barcodes_under_tissue,
    ) using (
        disabled = DISABLE_CORRECTION_FACTOR.disable_correction_factor,
    )

    call ALIGN_AND_COUNT(
        gem_well                    = self.gem_well,
        read_chunks                 = self.chunks,
        reference_path              = self.reference_path,
        read_shards                 = MAKE_READ_SHARDS_STRUCT.read_shards,
        feature_counts              = MAKE_SHARD.feature_counts,
        feature_reference           = MAKE_SHARD.feature_reference,
        target_set                  = self.target_set,
        chemistry_defs              = self.chemistry_defs,
        include_exons               = self.include_exons,
        include_introns             = self.include_introns,
        no_bam                      = self.no_bam,
        aligner                     = self.aligner,
        is_pd                       = self.is_pd,
        transcriptome_min_score     = 30,
        trim_polya_min_score        = self.trim_polya_min_score,
        trim_tso_min_score          = self.trim_tso_min_score,
        targeted_umi_min_read_count = _SLFE_PARTIAL_FIRST_PASS.umi_read_count_threshold,
        total_barcode_counts        = BARCODE_CORRECTION.total_barcode_counts,
        barcode_subset              = null,
        chevron_correction_factor   = COMPUTE_CORRECTION_FACTOR.correction_factor,
        chevron_affected_barcodes   = COMPUTE_CORRECTION_FACTOR.affected_barcodes,
    )

    call COLLATE_METRICS(
        per_barcode_metrics = ALIGN_AND_COUNT.per_barcode_metrics,
        reference_path      = self.reference_path,
        target_set          = self.target_set,
        feature_reference   = MAKE_SHARD.feature_reference,
        filtered_barcodes   = null,
        aggregate_barcodes  = null,
        sample_barcodes     = null,
    )

    call WRITE_BARCODE_INDEX(
        barcode_counts             = BARCODE_CORRECTION.corrected_barcode_counts,
        barcodes_under_tissue      = self.barcodes_under_tissue,
        barcode_correction_summary = BARCODE_CORRECTION.summary,
    )

    call WRITE_BARCODE_SUMMARY(
        per_barcode_metrics        = ALIGN_AND_COUNT.per_barcode_metrics,
        feature_reference          = MAKE_SHARD.feature_reference,
        barcode_index              = WRITE_BARCODE_INDEX.barcode_index,
        barcode_correction_summary = BARCODE_CORRECTION.summary,
    )

    call WRITE_H5_MATRIX(
        gem_well                   = self.gem_well,
        counts                     = ALIGN_AND_COUNT.counts_bc_order,
        feature_reference          = MAKE_SHARD.feature_reference,
        chemistry_defs             = self.chemistry_defs,
        sample_id                  = self.sample_id,
        barcode_index              = WRITE_BARCODE_INDEX.barcode_index,
        barcode_correction_summary = BARCODE_CORRECTION.summary,
    )

    call WRITE_MATRIX_MARKET(
        counts                     = ALIGN_AND_COUNT.counts_bc_order,
        feature_reference          = MAKE_SHARD.feature_reference,
        barcode_index              = WRITE_BARCODE_INDEX.barcode_index,
        barcode_correction_summary = BARCODE_CORRECTION.summary,
    )

    call MERGE_METRICS(
        summaries = [
            MAKE_SHARD.summary,
            BARCODE_CORRECTION.summary,
            _SLFE_PARTIAL_FIRST_PASS.umi_filtering_summary,
            ALIGN_AND_COUNT.summary,
            COLLATE_METRICS.summary,
        ],
    )

    return (
        barcode_correction_csv     = ALIGN_AND_COUNT.barcode_summary,
        barcode_summary            = WRITE_BARCODE_SUMMARY.barcode_summary,
        raw_gene_bc_matrices_h5    = WRITE_H5_MATRIX.matrix,
        raw_gene_bc_matrices_mex   = WRITE_MATRIX_MARKET.feature_bc_matrix,
        read_shards                = MAKE_READ_SHARDS_STRUCT.read_shards,
        counts_bc_order            = ALIGN_AND_COUNT.counts_bc_order,
        report_mol_inputs          = ALIGN_AND_COUNT.bc_umi_info,
        summary                    = MERGE_METRICS.summary,
        slfe_feature_reference     = MAKE_SHARD.feature_reference,
        annotation_files           = ALIGN_AND_COUNT.annotation_files,
        per_barcode_metrics        = COLLATE_METRICS.per_barcode_metrics,
        per_barcode_metrics_shard  = ALIGN_AND_COUNT.per_barcode_metrics,
        bc_umi_info                = ALIGN_AND_COUNT.bc_umi_info,
        bam_header                 = ALIGN_AND_COUNT.bam_header,
        alignments                 = ALIGN_AND_COUNT.pos_sorted,
        read_chunks                = self.chunks,
        multi_metrics              = COLLATE_METRICS.multi_metrics,
        gem_well_alignment_metrics = COLLATE_METRICS.summary,
        barcode_index              = WRITE_BARCODE_INDEX.barcode_index,
        sequencing_metrics         = MAKE_SHARD.sequencing_metrics,
        make_shard_bc_counts       = MAKE_SHARD.barcode_counts,
        barcode_counts             = BARCODE_CORRECTION.total_barcode_counts,
        probe_barcode_counts       = ALIGN_AND_COUNT.probe_barcode_counts,
        no_star_alignments         = ALIGN_AND_COUNT.no_star_alignments,
        per_read_gap_align         = ALIGN_AND_COUNT.per_read_gap_align,
    )
}

#
# @include "_basic_sc_rna_counter.mro"
#

pipeline _BASIC_SC_RNA_COUNTER(
    in  int                  gem_well,
    in  string               sample_id,
    in  map<ChemistryDef>    chemistry_defs,
    in  bool                 is_antibody_only,
    in  bool                 is_pd,
    in  map[]                chunks,
    in  path                 reference_path,
    in  CellCalling          cell_calling_config,
    in  float                subsample_rate,
    in  int                  initial_reads,
    in  int                  r1_length,
    in  int                  r2_length,
    in  int                  trim_polya_min_score,
    in  int                  trim_tso_min_score,
    in  int                  min_reads_to_report_bc,
    in  csv                  feature_reference,
    in  csv                  target_features,
    in  csv                  target_set,
    in  string               target_set_name,
    in  tps.json             target_panel_summary,
    in  bool                 include_exons,
    in  bool                 include_introns,
    in  bool                 filter_probes,
    in  string               aligner,
    in  bool                 disable_target_umi_filter,
    in  string               multi_config_sha,
    in  bool                 no_bam,
    in  BarcodeAssignments   force_sample_barcodes,
    in  bool                 disable_multi,
    in  json                 multi_graph,
    in  bool                 is_spatial,
    in  bool                 is_visium_hd,
    in  float                min_assignment_confidence,
    in  string               slide_serial_capture_area,
    in  FeatureConfig        feature_config,
    in  V1PatternFixArgs     v1_pattern_fix,
    out csv                  filtered_barcodes,
    out csv                  aggregate_barcodes,
    out csv                  nonambient_cell_calls,
    out csv                  barcode_correction_csv,
    out path                 bam_header,
    out bam                  possorted_genome_bam,
    out bam.bai              possorted_genome_bai_index,
    out bam.csi              possorted_genome_csi_index,
    out json                 summary,
    out h5                   barcode_summary,
    out tbcc.bincode         barcode_counts,
    out h5                   molecule_info,
    out h5                   raw_gene_bc_matrices_h5,
    out path                 raw_gene_bc_matrices_mex,
    out h5                   filtered_gene_bc_matrices_h5,
    out path                 filtered_gene_bc_matrices_mex,
    out int[]                gem_groups,
    out ReadShards           read_shards,
    out AnnotationFiles      annotation_files,
    out smf.json             sequencing_metrics,
    out csv                  per_probe_metrics,
    out h5                   raw_probe_bc_matrix,
    # subset of summary json, needed only for verifying correct sample metrics
    out json                 gem_well_alignment_metrics,
    # sliced outputs for multi
    out AssignTagsOuts       assign_tags,
    out SampleBamFile[]      multi_pos_sorted_bam,
    out SampleMoleculeInfo[] multi_molecule_info,
    out SampleMetrics[]      multi_metrics,
    out SampleMatrices[]     multi_matrices,
    out map<json>            sample_assignment_metrics,
    out json                 sample_barcodes,
    # everything below here is needed only for gem group merging
    out csv                  per_barcode_metrics,
    out csv                  isotype_normalization_factors,
    out bmsf[]               per_barcode_metrics_shard,
    out bui[]                bc_umi_info,
    out asf[]                alignments,
    out map[]                read_chunks,
    out string               target_set_name,
    out frf.bincode          slfe_feature_reference,
    # Shard files of feature x barcode counts sorted by barcode
    out csf[]                counts_bc_order,
    out bool                 no_star_alignments,
    out bi.bincode           barcode_index,
    out parquet[]            per_read_gap_align,
)
{
    call _SLFE_MATRIX_COMPUTER as _MATRIX_COMPUTER(
        gem_well                  = self.gem_well,
        sample_id                 = self.sample_id,
        chemistry_defs            = self.chemistry_defs,
        barcodes_under_tissue     = self.cell_calling_config.cell_barcodes,
        is_pd                     = self.is_pd,
        chunks                    = self.chunks,
        reference_path            = self.reference_path,
        subsample_rate            = self.subsample_rate,
        initial_reads             = self.initial_reads,
        r1_length                 = self.r1_length,
        r2_length                 = self.r2_length,
        trim_polya_min_score      = self.trim_polya_min_score,
        trim_tso_min_score        = self.trim_tso_min_score,
        min_reads_to_report_bc    = self.min_reads_to_report_bc,
        feature_reference         = self.feature_reference,
        target_features           = self.target_features,
        target_set                = self.target_set,
        target_set_name           = self.target_set_name,
        include_exons             = self.include_exons,
        include_introns           = self.include_introns,
        no_bam                    = self.no_bam,
        aligner                   = self.aligner,
        disable_target_umi_filter = self.disable_target_umi_filter,
        feature_config            = self.feature_config,
        v1_pattern_fix            = self.v1_pattern_fix,
    )

    call FILTER_BARCODES(
        chemistry_defs         = self.chemistry_defs,
        sample_id              = self.sample_id,
        matrices_h5            = _MATRIX_COMPUTER.raw_gene_bc_matrices_h5,
        barcode_correction_csv = _MATRIX_COMPUTER.barcode_correction_csv,
        config                 = self.cell_calling_config,
        gem_groups             = [self.gem_well],
        is_antibody_only       = self.is_antibody_only,
        reference_path         = self.reference_path,
        target_set             = self.target_set,
        multi_graph            = self.multi_graph,
        per_barcode_metrics    = _MATRIX_COMPUTER.per_barcode_metrics,
        is_spatial             = self.is_spatial,
    )

    call CHECK_CORRECTION_FACTOR(
        v1_pattern_fix = self.v1_pattern_fix,
        filtered_fbm   = FILTER_BARCODES.filtered_matrices_h5,
    )

    call DISABLE_STAGES(
        raw_feature_bc_matrix = _MATRIX_COMPUTER.raw_gene_bc_matrices_h5,
        probe_barcode_counts  = _MATRIX_COMPUTER.probe_barcode_counts,
        *                     = self,
    )

    call COLLATE_PROBE_METRICS(
        probe_barcode_counts = _MATRIX_COMPUTER.probe_barcode_counts,
        reference_path       = self.reference_path,
        probe_set            = self.target_set,
        filtered_barcodes    = FILTER_BARCODES.filtered_barcodes,
        probe_set_name       = self.target_set_name,
        barcode_index_path   = _MATRIX_COMPUTER.barcode_index,
    ) using (
        disabled = DISABLE_STAGES.no_probe_barcode_counts,
    )

    call WRITE_POS_BAM(
        target_set_name           = self.target_set_name,
        sample_barcodes           = null,
        slide_serial_capture_area = self.slide_serial_capture_area,
        *                         = _MATRIX_COMPUTER,
    ) using (
        disabled = DISABLE_STAGES.disable_legacy_bam,
    )

    call _SLFE_CELLS_REPORTER as _CELLS_REPORTER(
        chemistry_defs            = self.chemistry_defs,
        gem_well                  = self.gem_well,
        reference_path            = self.reference_path,
        recovered_cells           = self.cell_calling_config.recovered_cells,
        force_cells               = self.cell_calling_config.force_cells,
        disable_ab_aggregate_detection = self.cell_calling_config.disable_ab_aggregate_detection,
        disable_high_occupancy_gem_detection = self.cell_calling_config.disable_high_occupancy_gem_detection,
        slfe_feature_reference    = _MATRIX_COMPUTER.slfe_feature_reference,
        target_panel_summary      = self.target_panel_summary,
        matrices_h5               = _MATRIX_COMPUTER.raw_gene_bc_matrices_h5,
        read_chunks               = self.chunks,
        report_mol_inputs         = _MATRIX_COMPUTER.report_mol_inputs,
        matrix_computer_summary   = _MATRIX_COMPUTER.summary,
        barcode_summary           = _MATRIX_COMPUTER.barcode_summary,
        filtered_barcodes         = FILTER_BARCODES.filtered_barcodes,
        per_probe_metrics         = COLLATE_PROBE_METRICS.per_probe_metrics,
        filter_barcodes_summary   = FILTER_BARCODES.summary,
        per_barcode_metrics       = _MATRIX_COMPUTER.per_barcode_metrics,
        include_introns           = self.include_introns,
        filter_probes             = self.filter_probes,
        multi_config_sha          = self.multi_config_sha,
        disable_subsampling       = DISABLE_STAGES.disable_subsampling,
        barcode_index             = _MATRIX_COMPUTER.barcode_index,
        slide_serial_capture_area = self.slide_serial_capture_area,
    )

    call INFER_GEM_WELL_THROUGHPUT(
        chemistry_defs     = self.chemistry_defs,
        throughput         = null,
        filtered_feature_counts_matrix = FILTER_BARCODES.filtered_matrices_h5,
        reference_path     = self.reference_path,
        barcode_summary_h5 = _MATRIX_COMPUTER.barcode_summary,
    ) using (
        disabled = self.is_spatial,
    )

    call _ASSIGN_TAGS(
        chemistry_defs            = self.chemistry_defs,
        filtered_barcodes         = FILTER_BARCODES.filtered_barcodes,
        filtered_feature_counts_matrix = FILTER_BARCODES.filtered_matrices_h5,
        raw_feature_bc_matrix     = _MATRIX_COMPUTER.raw_gene_bc_matrices_h5,
        molecule_info             = _CELLS_REPORTER.molecule_info,
        multi_graph               = self.multi_graph,
        force_sample_barcodes     = self.force_sample_barcodes,
        gem_well                  = self.gem_well,
        min_assignment_confidence = self.min_assignment_confidence,
        throughput                = INFER_GEM_WELL_THROUGHPUT.throughput,
        inferred_throughputs      = INFER_GEM_WELL_THROUGHPUT.inferred_throughputs,
    ) using (
        disabled = DISABLE_STAGES.disable_assign_tags,
    )

    # stages/pipelines below here are for multiplexing sliced outputs
    # sample_barcodes is passed on by the _CELLS_REPORTER
    # and was either calculated from tags or is equal to self.force_sample_barcodes

    call DEMUX_PROBE_BC_MATRIX(
        probe_barcode_counts = _MATRIX_COMPUTER.probe_barcode_counts,
        reference_path       = self.reference_path,
        probe_set            = self.target_set,
        probe_set_name       = self.target_set_name,
        sample_barcodes      = _ASSIGN_TAGS.assign_tags_outs.sample_barcodes,
        sample_cell_barcodes = _ASSIGN_TAGS.assign_tags_outs.sample_cell_barcodes,
    ) using (
        disabled = DISABLE_STAGES.no_probe_barcode_matrix_demux,
    )

    call MULTI_WRITE_PER_SAMPLE_MATRICES(
        matrix_h5                    = FILTER_BARCODES.filtered_matrices_h5,
        raw_matrix_h5                = _MATRIX_COMPUTER.raw_gene_bc_matrices_h5,
        sample_barcodes              = _ASSIGN_TAGS.assign_tags_outs.sample_barcodes,
        sample_cell_barcodes         = _ASSIGN_TAGS.assign_tags_outs.sample_cell_barcodes,
        multi_graph                  = self.multi_graph,
        sample_raw_probe_bc_matrices = DEMUX_PROBE_BC_MATRIX.sample_raw_probe_bc_matrices,
        samples_per_probe_metrics    = DEMUX_PROBE_BC_MATRIX.samples_per_probe_metrics,
        filtered_barcodes            = FILTER_BARCODES.filtered_barcodes,
        aggregate_barcodes           = FILTER_BARCODES.aggregate_barcodes,
    ) using (
        disabled = self.disable_multi,
    )

    call WRITE_POS_BAM as MULTI_WRITE_PER_SAMPLE_BAM(
        target_set_name           = self.target_set_name,
        slide_serial_capture_area = self.slide_serial_capture_area,
        sample_barcodes           = _ASSIGN_TAGS.assign_tags_outs.sample_barcodes,
        *                         = _MATRIX_COMPUTER,
    ) using (
        disabled = DISABLE_STAGES.disable_sample_bams,
    )

    call COLLATE_METRICS as MULTI_COLLATE_PER_SAMPLE_METRICS(
        per_barcode_metrics = _MATRIX_COMPUTER.per_barcode_metrics_shard,
        reference_path      = self.reference_path,
        target_set          = self.target_set,
        feature_reference   = _MATRIX_COMPUTER.slfe_feature_reference,
        filtered_barcodes   = FILTER_BARCODES.filtered_barcodes,
        aggregate_barcodes  = FILTER_BARCODES.aggregate_barcodes,
        sample_barcodes     = _ASSIGN_TAGS.assign_tags_outs.sample_barcodes,
    ) using (
        disabled = self.disable_multi,
    )

    call WRITE_MOLECULE_INFO as MULTI_WRITE_PER_SAMPLE_MOLECULE_INFO(
        chemistry_defs            = self.chemistry_defs,
        gem_well                  = self.gem_well,
        counts_bc_order           = _MATRIX_COMPUTER.report_mol_inputs,
        reference_path            = self.reference_path,
        read_chunks               = self.chunks,
        feature_reference         = _MATRIX_COMPUTER.slfe_feature_reference,
        target_panel_summary      = self.target_panel_summary,
        matrix_computer_summary   = _MATRIX_COMPUTER.summary,
        recovered_cells           = self.cell_calling_config.recovered_cells,
        force_cells               = self.cell_calling_config.force_cells,
        filtered_barcodes         = FILTER_BARCODES.filtered_barcodes,
        per_probe_metrics         = COLLATE_PROBE_METRICS.per_probe_metrics,
        include_introns           = self.include_introns,
        filter_probes             = self.filter_probes,
        disable_ab_aggregate_detection = self.cell_calling_config.disable_ab_aggregate_detection,
        disable_high_occupancy_gem_detection = self.cell_calling_config.disable_high_occupancy_gem_detection,
        multi_config_sha          = self.multi_config_sha,
        sample_barcodes           = _ASSIGN_TAGS.assign_tags_outs.sample_barcodes,
        per_sample_metrics        = MULTI_COLLATE_PER_SAMPLE_METRICS.multi_metrics,
        barcode_index             = _MATRIX_COMPUTER.barcode_index,
        slide_serial_capture_area = self.slide_serial_capture_area,
    ) using (
        disabled = self.disable_multi,
    )

    call MERGE_METRICS(
        summaries = [
            _CELLS_REPORTER.summary,
            _ASSIGN_TAGS.assign_tags_outs.tag_call_metrics,
            COLLATE_PROBE_METRICS.estimated_gdna_metrics,
        ],
    )

    return (
        filtered_barcodes             = FILTER_BARCODES.filtered_barcodes,
        aggregate_barcodes            = FILTER_BARCODES.aggregate_barcodes,
        nonambient_cell_calls         = FILTER_BARCODES.nonambient_calls,
        barcode_correction_csv        = _MATRIX_COMPUTER.barcode_correction_csv,
        bam_header                    = _MATRIX_COMPUTER.bam_header,
        possorted_genome_bam          = WRITE_POS_BAM.pos_sorted_bam.bam_file,
        possorted_genome_bai_index    = WRITE_POS_BAM.pos_sorted_bam.bai_index_file,
        possorted_genome_csi_index    = WRITE_POS_BAM.pos_sorted_bam.csi_index_file,
        summary                       = MERGE_METRICS.summary,
        barcode_summary               = _MATRIX_COMPUTER.barcode_summary,
        barcode_counts                = _MATRIX_COMPUTER.barcode_counts,
        molecule_info                 = _CELLS_REPORTER.molecule_info,
        raw_gene_bc_matrices_h5       = _MATRIX_COMPUTER.raw_gene_bc_matrices_h5,
        raw_gene_bc_matrices_mex      = _MATRIX_COMPUTER.raw_gene_bc_matrices_mex,
        filtered_gene_bc_matrices_h5  = FILTER_BARCODES.filtered_matrices_h5,
        filtered_gene_bc_matrices_mex = FILTER_BARCODES.filtered_matrices_mex,
        gem_groups                    = [self.gem_well],
        read_shards                   = _MATRIX_COMPUTER.read_shards,
        annotation_files              = _MATRIX_COMPUTER.annotation_files,
        sequencing_metrics            = _MATRIX_COMPUTER.sequencing_metrics,
        per_probe_metrics             = COLLATE_PROBE_METRICS.per_probe_metrics,
        raw_probe_bc_matrix           = COLLATE_PROBE_METRICS.raw_probe_bc_matrix,
        # sliced outputs for multi
        assign_tags                   = _ASSIGN_TAGS.assign_tags_outs,
        multi_pos_sorted_bam          = MULTI_WRITE_PER_SAMPLE_BAM.multi_pos_sorted_bam,
        multi_molecule_info           = MULTI_WRITE_PER_SAMPLE_MOLECULE_INFO.multi_mol_info,
        multi_metrics                 = MULTI_COLLATE_PER_SAMPLE_METRICS.multi_metrics,
        multi_matrices                = MULTI_WRITE_PER_SAMPLE_MATRICES.sample_matrices,
        sample_assignment_metrics     = _ASSIGN_TAGS.assign_tags_outs.sample_assignment_metrics,
        sample_barcodes               = _ASSIGN_TAGS.assign_tags_outs.sample_barcodes,
        # everything below here is needed only for gem well merging
        bc_umi_info                   = _MATRIX_COMPUTER.bc_umi_info,
        per_barcode_metrics           = _MATRIX_COMPUTER.per_barcode_metrics,
        isotype_normalization_factors = FILTER_BARCODES.isotype_normalization_factors,
        per_barcode_metrics_shard     = _MATRIX_COMPUTER.per_barcode_metrics_shard,
        alignments                    = _MATRIX_COMPUTER.alignments,
        read_chunks                   = self.chunks,
        target_set_name               = self.target_set_name,
        slfe_feature_reference        = _MATRIX_COMPUTER.slfe_feature_reference,
        gem_well_alignment_metrics    = _MATRIX_COMPUTER.gem_well_alignment_metrics,
        counts_bc_order               = _MATRIX_COMPUTER.counts_bc_order,
        no_star_alignments            = _MATRIX_COMPUTER.no_star_alignments,
        barcode_index                 = _MATRIX_COMPUTER.barcode_index,
        per_read_gap_align            = _MATRIX_COMPUTER.per_read_gap_align,
    )
}

#
# @include "_common_cloupe_stages.mro"
#

stage CLOUPE_PREPROCESS(
    in  string   pipestance_type,
    in  string   sample_id,
    in  string   sample_desc,
    in  path     analysis,
    in  h5       filtered_gene_bc_matrices_h5,
    in  json     metrics_json,
    in  csv      aggregation_csv,
    in  json     gem_group_index_json,
    in  string[] image_page_names,
    in  file[]   tissue_image_paths,
    in  int      dark_images,
    in  csv      tissue_positions,
    in  txt      fiducial_positions_list,
    in  json     dzi_info,
    in  path[]   dzi_tiles_paths,
    in  json     scale_factors_json,
    in  bool     no_secondary_analysis,
    in  string   barcode_whitelist,
    in  string   hd_slide_name,
    in  json     loupe_map,
    in  string   product_type,
    in  json     cells_per_sample,
    in  json     cells_per_tag,
    in  json     cells_per_protospacer,
    in  csv      spatial_enrichment,
    in  path     spatial_deconvolution_path,
    in  bool     disable_cloupe,
    out cloupe   output_for_cloupe,
    out json     gem_group_index_json,
    src py       "stages/cloupe/cloupe_preprocess",
) split (
) using (
    volatile = strict,
)

#
# @include "_common_stages.mro"
#

stage CELLRANGER_PREFLIGHT(
    in  bool             full_check,
    in  string           chemistry,
    in  map[]            sample_def,
    in  csv              target_set,
    in  path             reference_path,
    in  csv              feature_reference,
    in  CellCallingParam recovered_cells,
    in  CellCallingParam force_cells,
    in  int              r1_length,
    in  int              r2_length,
    in  string           targeting_method,
    src py               "stages/common/cellranger_preflight",
) using (
    mem_gb   = 8,
    volatile = strict,
)

stage DISABLE_FEATURE_STAGES(
    in  map[]               sample_def,
    in  bool                disable_multi,
    in  bool                disable_count,
    in  bool                is_pd,
    in  bool                in_disable_targeted,
    in  map<SampleSlfeOuts> sample_outs,
    in  json                multi_graph,
    out bool                disable_crispr,
    out bool                disable_antibody,
    out bool                disable_antigen,
    out bool                disable_multiplexing,
    out bool                disable_targeted,
    out bool                disable_legacy_stages,
    out bool                disable_library_cloupe,
    out bool                disable_gex,
    src py                  "stages/common/disable_feature_stages",
) using (
    volatile = strict,
)

stage DISABLE_SECONDARY_ANALYSIS(
    in  bool is_spatial,
    in  h5   filtered_matrices_h5,
    in  bool no_secondary_analysis,
    in  bool is_visium_hd_main_run  "Boolean indicating if this is being called from a main (not-binning) Visium HD run",
    out bool no_secondary_analysis,
    src py   "stages/common/disable_secondary_analysis",
) using (
    volatile = strict,
)

stage PARSE_TARGET_FEATURES(
    in  string   target_set_name,
    in  csv      target_set,
    in  path     reference_path,
    in  json     gene_index,
    in  bool     filter_probes,
    in  bool     no_target_umi_filter,
    in  bool     no_bam,
    in  bool     is_pd,
    in  bool     is_antibody_only,
    out fa       bait_fasta,
    out csv      target_panel,
    out csv      probe_set,
    out csv      target_panel_or_probe_set,
    out csv      target_gene_indices,
    out bool     disable_targeted,
    out bool     disable_target_umi_filter,
    out bool     no_bam,
    out string   target_set_name,
    out string   targeting_method,
    out tps.json target_panel_summary,
    src py       "stages/common/parse_target_features",
) using (
    mem_gb = 4,
)

#
# @include "_sc_rna_counter_stages.mro"
#

stage GET_AGGREGATE_BARCODES_OUT(
    in  path   antibody_analysis,
    in  bool   is_multi,
    in  string multiplexing_method,
    out csv    aggregate_barcodes,
    src py     "stages/counter/get_aggregate_barcodes_out",
)

stage SUMMARIZE_REPORTS(
    in  map<ChemistryDef> chemistry_defs,
    in  json[]            summaries,
    in  string            sample_id,
    in  string            sample_desc,
    in  path              reference_path,
    in  path              analysis,
    in  h5                barcode_summary_h5,
    in  h5                filtered_gene_bc_matrices_h5,
    in  csv               filtered_barcodes,
    in  tps.json          target_panel_summary,
    in  json              antibody_histograms,
    in  json              antibody_treemap,
    in  json              antigen_histograms,
    in  json              antigen_treemap,
    in  csv               feature_reference,
    in  string            target_set_name,
    in  csv               per_feature_metrics_csv,
    in  bool              include_introns,
    out json              metrics_summary_json,
    out csv               metrics_summary_csv,
    out html              web_summary,
    out csv               feature_reference,
    out json              ws_data,
    src py                "stages/counter/summarize_reports",
) split (
) using (
    volatile = strict,
) retain (
    metrics_summary_json,
)

#
# @include "_sc_vdj_assembler_stages.mro"
#

stage VDJ_PREFLIGHT(
    in  map[]  sample_def,
    in  path   vdj_reference_path,
    in  bool   denovo,
    in  bool   full_check,
    in  path   inner_enrichment_primers,
    in  string chain_type,
    src py     "stages/vdj/vdj_preflight",
)

stage REPORT_CONTIGS(
    in  path   vdj_reference_path,
    in  json   cell_barcodes,
    in  fasta  contigs,
    in  json   annotations,
    in  csv    filter_summary,
    in  tsv    umi_summary,
    in  string prefix,
    out json   summary,
    src py     "stages/vdj/report_contigs",
) split (
)

stage SUMMARIZE_VDJ_REPORTS(
    in  string       sample_id,
    in  string       sample_desc,
    in  ChemistryDef vdj_chemistry_def,
    in  json[]       summaries,
    in  int          total_read_pairs,
    in  json         cell_barcodes,
    in  csv          clonotype_summary,
    in  csv          barcode_support,
    in  string       receptor,
    in  int          n50_n50_rpu,
    out string       receptor,
    out json         metrics_summary_json,
    out csv          metrics_summary_csv,
    out html         web_summary,
    out json         web_summary_data,
    src py           "stages/vdj/summarize_reports",
) split (
) retain (
    metrics_summary_json,
)

#
# @include "_sc_vdj_clonotype_assigner.mro"
#

pipeline CLONOTYPE_ASSIGNER(
    in  path         vdj_reference_path,
    in  json         contig_annotations,
    in  string       receptor,
    in  FilterSwitch filter_switch,
    in  string       sample_id,
    out json         contig_annotations_json,
    out csv          clonotypes_csv,
    out fasta        consensus_fasta,
    out fasta.fai    consensus_fasta_fai,
    out fasta        concat_ref_fasta,
    out fasta.fai    concat_ref_fasta_fai,
    out bam          concat_ref_bam,
    out bam.bai      concat_ref_bam_bai,
    out bam          consensus_bam,
    out bam.bai      consensus_bam_bai,
    out csv          consensus_annotations_csv,
    out json         summary,
    out tsv          airr_rearrangement,
    out pb           enclone_output,
    out json         enclone_barcode_fate,
    out bool         disable_vloupe,
    out fa           donor_ref_fa,
)
{
    call RUN_ENCLONE(
        vdj_reference_path = self.vdj_reference_path,
        contig_annotations = self.contig_annotations,
        receptor           = self.receptor,
        filter_switch      = self.filter_switch,
    )

    call FILL_CLONOTYPE_INFO(
        sample_id          = self.sample_id,
        contig_annotations = self.contig_annotations,
        enclone_output     = RUN_ENCLONE.enclone_output,
    )

    call WRITE_CONCAT_REF_OUTS(
        sample_id                   = self.sample_id,
        all_contig_annotations_json = FILL_CLONOTYPE_INFO.all_contig_annotations_json,
        enclone_output              = RUN_ENCLONE.enclone_output,
    )

    call WRITE_CONSENSUS_BAM(
        sample_id                   = self.sample_id,
        all_contig_annotations_json = FILL_CLONOTYPE_INFO.all_contig_annotations_json,
        enclone_output              = RUN_ENCLONE.enclone_output,
    )

    call WRITE_CONSENSUS_TXT(
        sample_id      = self.sample_id,
        enclone_output = RUN_ENCLONE.enclone_output,
    )

    call CREATE_AIRR_TSV(
        contig_annotations = FILL_CLONOTYPE_INFO.all_contig_annotations_json,
        concat_ref_fasta   = WRITE_CONCAT_REF_OUTS.concat_ref_fasta,
        gem_well_map       = null,
    )

    call WRITE_CLONOTYPE_OUTS(
        sample_id      = self.sample_id,
        enclone_output = RUN_ENCLONE.enclone_output,
        receptor       = self.receptor,
    )

    return (
        contig_annotations_json   = FILL_CLONOTYPE_INFO.all_contig_annotations_json,
        summary                   = RUN_ENCLONE.summary,
        clonotypes_csv            = WRITE_CLONOTYPE_OUTS.clonotypes_csv,
        consensus_annotations_csv = WRITE_CONSENSUS_TXT.consensus_annotations_csv,
        consensus_fasta           = WRITE_CONSENSUS_TXT.consensus_fasta,
        consensus_fasta_fai       = WRITE_CONSENSUS_TXT.consensus_fasta_fai,
        concat_ref_fasta          = WRITE_CONCAT_REF_OUTS.concat_ref_fasta,
        concat_ref_fasta_fai      = WRITE_CONCAT_REF_OUTS.concat_ref_fasta_fai,
        consensus_bam             = WRITE_CONSENSUS_BAM.consensus_bam,
        consensus_bam_bai         = WRITE_CONSENSUS_BAM.consensus_bam_bai,
        concat_ref_bam            = WRITE_CONCAT_REF_OUTS.concat_ref_bam,
        concat_ref_bam_bai        = WRITE_CONCAT_REF_OUTS.concat_ref_bam_bai,
        airr_rearrangement        = CREATE_AIRR_TSV.airr_annotations,
        enclone_output            = RUN_ENCLONE.enclone_output,
        enclone_barcode_fate      = RUN_ENCLONE.barcode_fate,
        disable_vloupe            = RUN_ENCLONE.disable_vloupe,
        donor_ref_fa              = RUN_ENCLONE.donor_ref_fa,
    )
}

#
# @include "_sc_vdj_contig_assembler.mro"
#

pipeline SC_VDJ_CONTIG_ASSEMBLER(
    in  string            sample_id,
    in  map<ChemistryDef> chemistry_defs,
    in  int               gem_well,
    in  map[]             chunks,
    in  int               r1_length,
    in  int               r2_length,
    in  int               initial_reads,
    in  float             subsample_rate,
    in  path              vdj_reference_folder,
    in  bool              denovo,
    in  path              inner_primers,
    in  string            receptor,
    in  FeatureConfig     feature_config,
    in  int               min_contig_length,
    out json              summary,
    out ReadShards        read_shards,
    out int[]             gem_groups,
    out json              raw_barcode_counts,
    out json              corrected_barcode_counts,
    out int               n50_n50_rpu,
    out int               processed_read_pairs,
    out bam               contig_bam,
    out bam.bai           contig_bam_bai,
    out tsv               summary_tsv,
    out tsv               umi_summary_tsv,
    out json              asm_contig_annotations,
    out csv               barcode_support,
    out json[]            barcodes_in_chunks,
    out fastq             unmapped_sample_fastq,
    out int               total_read_pairs,
    out arp.bincode       assemblable_reads_per_bc,
    out smf.json          sequencing_metrics,
    out bdf.bincode       barcode_brief,
    out bd.bincode        barcode_full,
)
{
    call MAKE_SHARD(
        gem_well               = self.gem_well,
        chemistry_defs         = self.chemistry_defs,
        read_chunks            = self.chunks,
        r1_length              = self.r1_length,
        r2_length              = self.r2_length,
        subsample_rate         = self.subsample_rate,
        initial_read_pairs     = self.initial_reads,
        reference_path         = null,
        target_features        = null,
        target_set             = null,
        target_set_name        = null,
        feature_reference_path = null,
        feature_config         = self.feature_config,
    )

    call BARCODE_CORRECTION(
        gem_well               = self.gem_well,
        barcode_counts         = MAKE_SHARD.barcode_counts,
        barcode_segment_counts = MAKE_SHARD.barcode_segment_counts,
        chemistry_defs         = self.chemistry_defs,
        invalid_uncorrected    = MAKE_SHARD.invalid,
        valid_read_metrics     = MAKE_SHARD.bc_correct_summary,
        min_reads_to_report_bc = 1000,
        correction_map         = null,
        barcodes_under_tissue  = null,
    )

    call RUST_BRIDGE(
        chemistry_defs           = self.chemistry_defs,
        gem_well                 = self.gem_well,
        valid_uncorrected        = MAKE_SHARD.valid,
        valid_corrected          = BARCODE_CORRECTION.valid_corrected,
        raw_barcode_counts       = MAKE_SHARD.barcode_counts,
        corrected_barcode_counts = BARCODE_CORRECTION.corrected_barcode_counts,
    )

    call ASSEMBLE_VDJ(
        sample_id                = self.sample_id,
        chemistry_defs           = self.chemistry_defs,
        bc_sorted_rna_reads      = RUST_BRIDGE.bc_sorted_rna_reads,
        vdj_reference_path       = self.vdj_reference_folder,
        n50_n50_rpu              = RUST_BRIDGE.n50_n50_rpu,
        npairs                   = RUST_BRIDGE.processed_read_pairs,
        receptor                 = self.receptor,
        denovo                   = self.denovo,
        inner_enrichment_primers = self.inner_primers,
        total_read_pairs         = MAKE_SHARD.total_read_pairs,
        corrected_bc_counts      = RUST_BRIDGE.corrected_barcode_counts_json,
        min_contig_length        = self.min_contig_length,
    )

    call MERGE_METRICS(
        summaries = [
            MAKE_SHARD.summary,
            BARCODE_CORRECTION.summary,
            RUST_BRIDGE.summary,
        ],
    )

    return (
        summary                  = MERGE_METRICS.summary,
        read_shards              = {
            corrected_reads: BARCODE_CORRECTION.valid_corrected,
            invalid_reads:   BARCODE_CORRECTION.invalid,
            valid_reads:     MAKE_SHARD.valid,
        },
        gem_groups               = RUST_BRIDGE.gem_groups,
        raw_barcode_counts       = RUST_BRIDGE.raw_barcode_counts_json,
        corrected_barcode_counts = RUST_BRIDGE.corrected_barcode_counts_json,
        n50_n50_rpu              = RUST_BRIDGE.n50_n50_rpu,
        processed_read_pairs     = RUST_BRIDGE.processed_read_pairs,
        contig_bam               = ASSEMBLE_VDJ.contig_bam,
        contig_bam_bai           = ASSEMBLE_VDJ.contig_bam_bai,
        summary_tsv              = ASSEMBLE_VDJ.summary_tsv,
        umi_summary_tsv          = ASSEMBLE_VDJ.umi_summary_tsv,
        asm_contig_annotations   = ASSEMBLE_VDJ.contig_annotations,
        barcode_support          = ASSEMBLE_VDJ.barcode_support,
        barcodes_in_chunks       = ASSEMBLE_VDJ.barcodes_in_chunks,
        unmapped_sample_fastq    = ASSEMBLE_VDJ.unmapped_sample_fastq,
        total_read_pairs         = MAKE_SHARD.total_read_pairs,
        assemblable_reads_per_bc = ASSEMBLE_VDJ.assemblable_reads_per_bc,
        sequencing_metrics       = MAKE_SHARD.sequencing_metrics,
        barcode_brief            = ASSEMBLE_VDJ.barcode_brief,
        barcode_full             = ASSEMBLE_VDJ.barcode_full,
    )
}

#
# @include "_sc_rna_targeted_analyzer_stages.mro"
#

stage CALCULATE_TARGETED_METRICS(
    in  h5       molecule_info,
    in  h5       filtered_gene_bc_matrices,
    in  json     basic_counter_summary,
    in  tps.json target_panel_summary,
    in  bool     is_spatial,
    out json     summary,
    out csv      per_feature_metrics_csv,
    src py       "stages/targeted/calculate_targeted_metrics",
) split (
) using (
    volatile = strict,
)

stage DISABLE_TARGETED_STAGES(
    in  csv  probe_set,
    in  bool is_visium_hd,
    out bool disable_targeted_gdna,
    out bool disable_sampling_stages,
    src py   "stages/targeted/disable_targeted_stages",
) using (
    volatile = strict,
)

stage GET_GDNA_PLOT(
    in  json gdna_plot_sufficient_stats,
    out json summary,
    src py   "stages/targeted/get_gdna_plot",
) using (
    volatile = strict,
)

#
# @include "_targeted_analyzer.mro"
#

pipeline _RUN_GDNA_ANALYSIS(
    in  h5   molecule_info,
    in  csv  probe_set,
    out json gdna_summary,
)
{
    call GET_GDNA_METRICS(
        molecule_info = self.molecule_info,
        probe_set     = self.probe_set,
    )

    call GET_GDNA_PLOT(
        gdna_plot_sufficient_stats = GET_GDNA_METRICS.gdna_plot_sufficient_stats,
    )

    call MERGE_METRICS(
        summaries = [
            GET_GDNA_METRICS.summary,
            GET_GDNA_PLOT.summary,
        ],
    )

    return (
        gdna_summary = MERGE_METRICS.summary,
    )
}

pipeline _TARGETED_ANALYZER(
    in  h5       molecule_info,
    in  h5       filtered_gene_bc_matrices,
    in  csv      filtered_barcodes,
    in  json     basic_counter_summary,
    in  csv      probe_set,
    in  bool     is_visium_hd,
    in  tps.json target_panel_summary,
    in  bool     is_spatial,
    out json     targeted_analysis_metrics,
    out csv      per_feature_metrics_csv,
)
{
    call DISABLE_TARGETED_STAGES(
        * = self,
    )

    call _RUN_GDNA_ANALYSIS(
        molecule_info = self.molecule_info,
        probe_set     = self.probe_set,
    ) using (
        disabled = DISABLE_TARGETED_STAGES.disable_targeted_gdna,
    )

    call CALCULATE_TARGETED_METRICS(
        molecule_info             = self.molecule_info,
        filtered_gene_bc_matrices = self.filtered_gene_bc_matrices,
        basic_counter_summary     = self.basic_counter_summary,
        target_panel_summary      = self.target_panel_summary,
        is_spatial                = self.is_spatial,
    )

    call SUBSAMPLE_READS as SUBSAMPLE_ON_TARGET_READS(
        molecule_info     = self.molecule_info,
        filtered_barcodes = self.filtered_barcodes,
        target_mode       = "ontarget",
    ) using (
        disabled = DISABLE_TARGETED_STAGES.disable_sampling_stages,
    )

    call SUBSAMPLE_READS as SUBSAMPLE_OFF_TARGET_READS(
        molecule_info     = self.molecule_info,
        filtered_barcodes = self.filtered_barcodes,
        target_mode       = "offtarget",
    ) using (
        disabled = DISABLE_TARGETED_STAGES.disable_sampling_stages,
    )

    call MERGE_METRICS(
        summaries = [
            CALCULATE_TARGETED_METRICS.summary,
            SUBSAMPLE_ON_TARGET_READS.summary,
            SUBSAMPLE_OFF_TARGET_READS.summary,
            _RUN_GDNA_ANALYSIS.gdna_summary,
        ],
    )

    return (
        targeted_analysis_metrics = MERGE_METRICS.summary,
        per_feature_metrics_csv   = CALCULATE_TARGETED_METRICS.per_feature_metrics_csv,
    )
}

#
# @include "_vloupe_stages.mro"
#

stage VLOUPE_PREPROCESS(
    in  string      pipestance_type,
    in  string      sample_id,
    in  string      sample_desc,
    in  pb          enclone_output,
    in  bool        disable_vloupe,
    in  string      beam_mode,
    in  csv         feature_reference,
    in  h5          feature_barcode_matrix,
    in  csv         antigen_specificity_scores,
    in  map<string> antigen_specificity_controls,
    out vloupe      output_for_vloupe,
    src py          "stages/vloupe/vloupe_preprocess",
) using (
    mem_gb = 15,
)

#
# @include "_sc_multi_defs.mro"
#

stage _MAKE_VDJ_CONFIG(
    in  VdjInputsCS vdj_t_input,
    in  VdjInputsCS vdj_t_gd_input,
    in  VdjInputsCS vdj_b_input,
    in  bool        disable_vdj,
    in  path        vdj_reference_path,
    out bool        disable_vdj_b,
    out bool        disable_vdj_t,
    out bool        disable_vdj_t_gd,
    out bool        has_no_vdj_ref,
    src py          "stages/common/make_vdj_config",
)

# This is a pipeline, so that martian can determine at compile time that
# config.disable_count in the output is the same as basic_config.disable_count,
# which has several beneficial knock-on effects.
pipeline MAKE_FULL_CONFIG(
    in  VdjInputsCS         vdj_t_input,
    in  VdjInputsCS         vdj_t_gd_input,
    in  VdjInputsCS         vdj_b_input,
    in  BasicPipelineConfig basic_config,
    in  path                vdj_reference_path,
    out FullPipelineConfig  config,
)
{
    call _MAKE_VDJ_CONFIG(
        vdj_t_input        = self.vdj_t_input,
        vdj_t_gd_input     = self.vdj_t_gd_input,
        vdj_b_input        = self.vdj_b_input,
        vdj_reference_path = self.vdj_reference_path,
        *                  = self.basic_config,
    )

    return (
        config = {
            disable_count:       self.basic_config.disable_count,
            disable_multi:       self.basic_config.disable_multi,
            disable_multi_count: self.basic_config.disable_multi_count,
            disable_vdj_b:       _MAKE_VDJ_CONFIG.disable_vdj_b,
            disable_vdj_t:       _MAKE_VDJ_CONFIG.disable_vdj_t,
            disable_vdj_t_gd:    _MAKE_VDJ_CONFIG.disable_vdj_t_gd,
            has_no_vdj_ref:      _MAKE_VDJ_CONFIG.has_no_vdj_ref,
        },
    )
}

stage SPLIT_VDJ_INPUTS(
    in  VdjInputs[]    vdj_inputs,
    in  ChemistryDef[] vdj_chemistry_defs,
    in  string[]       vdj_receptors,
    out VdjInputs      vdj_t_input,
    out ChemistryDef   vdj_t_chemistry_def,
    out string         vdj_t_receptor,
    out VdjInputs      vdj_t_gd_input,
    out ChemistryDef   vdj_t_gd_chemistry_def,
    out string         vdj_t_gd_receptor,
    out VdjInputs      vdj_b_input,
    out ChemistryDef   vdj_b_chemistry_def,
    out string         vdj_b_receptor,
    src py             "stages/vdj/split_vdj_inputs",
)

stage PICK_VDJ_OUTS(
    in  bool         disable_vdj_t,
    in  bool         disable_vdj_b,
    in  VdjOutputsCS vdj_t_outs,
    in  html         vdj_t_web_summary,
    in  VdjOutputsCS vdj_b_outs,
    in  html         vdj_b_web_summary,
    out VdjOutputsCS vdj_outs,
    out html         web_summary,
    src py           "stages/vdj/pick_vdj_outs",
)

stage MERGE_GEM_WELL_CSVS(
    in  csv[] filtered_barcodes,
    in  csv[] barcode_correction_csv,
    out csv   filtered_barcodes,
    out csv   barcode_correction_csv,
    src py    "stages/multi/merge_gem_well_filtered_barcode_csvs",
) using (
    volatile = strict,
)

###############################################################################
# Chemistry detector pipelines

pipeline VDJ_CHEMISTRY_DETECTOR(
    in  path               vdj_reference_path,
    in  csv                feature_reference,
    in  VdjChemistryInputs vdj_chem_inputs,
    in  map<ChemistryDef>  count_chemistry_defs,
    in  map[]              gex_sample_def,
    in  bool               disable_count,
    in  bool               is_multi,
    in  bool               is_pd,
    in  bool               check_library_compatibility,
    in  FeatureConfig      feature_config,
    in  string[]           vdj_allowed_chems,
    in  csv                multi_config,
    out ChemistryDef       chemistry_def,
    out string             receptor,
    out string             chain_type,
    out string             beam_mode,
)
{
    call COPY_CHEMISTRY_SPEC(
        sample_defs    = self.vdj_chem_inputs.sample_def,
        chemistry_spec = self.vdj_chem_inputs.chemistry_spec,
    )

    call DETECT_CHEMISTRY(
        reference_path    = null,
        allowed_chems     = self.vdj_allowed_chems,
        feature_reference = null,
        multi_config      = self.multi_config,
        is_pd             = self.is_pd,
        feature_config    = self.feature_config,
        chemistry_specs   = COPY_CHEMISTRY_SPEC.chemistry_specs,
        *                 = self.vdj_chem_inputs,
    )

    call EXTRACT_SINGLE_CHEMISTRY(
        chemistry_defs     = DETECT_CHEMISTRY.chemistry_defs,
        library_to_extract = null,
    )

    call DETECT_VDJ_RECEPTOR(
        force_receptor     = self.vdj_chem_inputs.chain_type,
        vdj_reference_path = self.vdj_reference_path,
        feature_reference  = self.feature_reference,
        gex_sample_def     = self.gex_sample_def,
        vdj_sample_def     = self.vdj_chem_inputs.sample_def,
        is_multi           = self.is_multi,
        feature_config     = self.feature_config,
    )

    call CHECK_BARCODES_COMPATIBILITY_VDJ(
        vdj_chemistry_def           = EXTRACT_SINGLE_CHEMISTRY.chemistry_def,
        vdj_sample_def              = self.vdj_chem_inputs.sample_def,
        count_chemistry_defs        = self.count_chemistry_defs,
        gex_sample_def              = self.gex_sample_def,
        check_library_compatibility = self.check_library_compatibility,
    ) using (
        disabled = self.disable_count,
    )

    return (
        chemistry_def = EXTRACT_SINGLE_CHEMISTRY.chemistry_def,
        receptor      = DETECT_VDJ_RECEPTOR.receptor,
        chain_type    = self.vdj_chem_inputs.chain_type,
        beam_mode     = DETECT_VDJ_RECEPTOR.beam_mode,
    )
}

# Detect chemistry and check barcodes compatibility for multiple gem wells, gene expression and vdj
pipeline MULTI_CHEMISTRY_DETECTOR(
    in  CountChemistryInputs     count_inputs,
    in  VdjChemistryInputs[]     vdj_inputs,
    in  VdjGenInputs             vdj_gen_inputs,
    in  BasicPipelineConfig      basic_config,
    in  string[]                 count_allowed_chems,
    in  csv                      multi_config,
    in  bool                     is_multi,
    in  bool                     is_pd,
    in  FeatureConfig            feature_config,
    in  string[]                 vdj_allowed_chems,
    out bool                     is_antibody_only,
    out string                   beam_mode,
    out map[]                    sample_defs_count,
    out DETECT_CHEMISTRY         detect_count_chem,
    out VDJ_CHEMISTRY_DETECTOR[] detect_vdj_chem,
)
{
    call DETECT_CHEMISTRY as DETECT_COUNT_CHEMISTRY(
        allowed_chems  = self.count_allowed_chems,
        multi_config   = self.multi_config,
        is_pd          = self.is_pd,
        feature_config = self.feature_config,
        *              = self.count_inputs,
    ) using (
        disabled = self.basic_config.disable_count,
    )

    map call VDJ_CHEMISTRY_DETECTOR(
        vdj_chem_inputs             = split self.vdj_inputs,
        vdj_reference_path          = self.vdj_gen_inputs.vdj_reference_path,
        feature_reference           = self.count_inputs.feature_reference,
        count_chemistry_defs        = DETECT_COUNT_CHEMISTRY.chemistry_defs,
        gex_sample_def              = self.count_inputs.sample_def,
        check_library_compatibility = self.count_inputs.check_library_compatibility,
        disable_count               = self.basic_config.disable_count,
        is_multi                    = self.is_multi,
        is_pd                       = self.is_pd,
        feature_config              = self.feature_config,
        vdj_allowed_chems           = self.vdj_allowed_chems,
        multi_config                = self.multi_config,
    ) using (
        disabled = self.basic_config.disable_vdj,
    )

    call CHECK_BARCODES_COMPATIBILITY(
        chemistry_defs              = DETECT_COUNT_CHEMISTRY.chemistry_defs,
        sample_def                  = self.count_inputs.sample_def,
        check_library_compatibility = self.count_inputs.check_library_compatibility,
    ) using (
        disabled = self.basic_config.disable_count,
    )

    call CHECK_SINGLE_BEAM_MODE(
        beam_modes = VDJ_CHEMISTRY_DETECTOR.beam_mode,
    ) using (
        disabled = self.basic_config.disable_vdj,
    )

    return (
        is_antibody_only  = DETECT_COUNT_CHEMISTRY.is_antibody_only,
        beam_mode         = CHECK_SINGLE_BEAM_MODE.beam_mode,
        sample_defs_count = self.count_inputs.sample_def,
        detect_count_chem = DETECT_COUNT_CHEMISTRY,
        detect_vdj_chem   = VDJ_CHEMISTRY_DETECTOR,
    )
}

###############################################################################
# Gem well processor pipelines
###############################################################################
pipeline COUNT_GEM_WELL_PROCESSOR(
    in  int                   gem_group,
    in  string                sample_id,
    in  string                multi_config_sha,
    in  CounterInputs         inputs,
    in  DETECT_CHEMISTRY      chem,
    in  bool                  is_pd,
    in  bool                  disable_multi,
    in  json                  multi_graph,
    in  FeatureConfig         feature_config,
    out PARSE_TARGET_FEATURES target_outs,
    out MULTI_SETUP_CHUNKS    setup_chunks_outs,
    out _BASIC_SC_RNA_COUNTER basic_counter_outs,
)
{
    call PARSE_TARGET_FEATURES(
        is_pd            = self.is_pd,
        is_antibody_only = self.chem.is_antibody_only,
        *                = self.inputs,
    )

    call MULTI_SETUP_CHUNKS(
        sample_id            = self.sample_id,
        chemistry_defs       = self.chem.chemistry_defs,
        default_library_type = null,
        *                    = self.inputs,
    ) using (
        volatile = true,
    )

    call _BASIC_SC_RNA_COUNTER(
        gem_well                  = self.gem_group,
        sample_id                 = self.sample_id,
        multi_config_sha          = self.multi_config_sha,
        is_pd                     = self.is_pd,
        chemistry_defs            = self.chem.chemistry_defs,
        is_antibody_only          = self.chem.is_antibody_only,
        chunks                    = MULTI_SETUP_CHUNKS.chunks,
        target_panel_summary      = PARSE_TARGET_FEATURES.target_panel_summary,
        disable_target_umi_filter = PARSE_TARGET_FEATURES.disable_target_umi_filter,
        target_features           = PARSE_TARGET_FEATURES.target_gene_indices,
        target_set                = PARSE_TARGET_FEATURES.target_panel_or_probe_set,
        target_set_name           = PARSE_TARGET_FEATURES.target_set_name,
        disable_multi             = self.disable_multi,
        multi_graph               = self.multi_graph,
        is_spatial                = false,
        filter_probes             = self.inputs.filter_probes,
        # This is a gross manual unpacking of self.inputs to override `no_bam` based on PARSE_TARGETED_FEATURES
        # to account for the need for BAM in TARGETED_ANALYZER_PD
        no_bam                    = PARSE_TARGET_FEATURES.no_bam,
        reference_path            = self.inputs.reference_path,
        feature_reference         = self.inputs.feature_reference,
        cell_calling_config       = self.inputs.cell_calling_config,
        subsample_rate            = self.inputs.subsample_rate,
        initial_reads             = self.inputs.initial_reads,
        r1_length                 = self.inputs.r1_length,
        r2_length                 = self.inputs.r2_length,
        trim_polya_min_score      = self.inputs.trim_polya_min_score,
        trim_tso_min_score        = self.inputs.trim_tso_min_score,
        min_reads_to_report_bc    = 1000,
        include_exons             = self.inputs.include_exons,
        include_introns           = self.inputs.include_introns,
        aligner                   = self.inputs.aligner,
        force_sample_barcodes     = self.inputs.force_sample_barcodes,
        min_assignment_confidence = self.inputs.min_assignment_confidence,
        slide_serial_capture_area = null,
        feature_config            = self.feature_config,
        v1_pattern_fix            = null,
        is_visium_hd              = false,
    )

    return (
        target_outs        = PARSE_TARGET_FEATURES,
        setup_chunks_outs  = MULTI_SETUP_CHUNKS,
        basic_counter_outs = _BASIC_SC_RNA_COUNTER,
    )
}

pipeline BEAM_ANALYZER(
    in  h5                  filtered_feature_counts_matrix,
    in  bmsf[]              per_barcode_count_metrics,
    in  string              beam_mode,
    in  csv                 filtered_contig_annotations,
    in  csv                 clonotypes_csv,
    in  json                vdj_cell_barcodes,
    out BeamAnalyzerOutputs outputs,
)
{
    call CALCULATE_ANTIGEN_SPECIFICITY(
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        beam_mode                   = self.beam_mode,
        filtered_contig_annotations = self.filtered_contig_annotations,
        clonotypes_csv              = self.clonotypes_csv,
        count_gem_well_map          = null,
    )

    call COMPUTE_ANTIGEN_VDJ_METRICS(
        vdj_cell_barcodes         = self.vdj_cell_barcodes,
        per_barcode_count_metrics = self.per_barcode_count_metrics,
    )

    call CREATE_BARCODE_CSV(
        gex_filtered_matrix      = self.filtered_feature_counts_matrix,
        vdj_filtered_annotations = self.filtered_contig_annotations,
        count_gem_well_map       = null,
    )

    return (
        outputs = {
            antigen_assignment:             CALCULATE_ANTIGEN_SPECIFICITY.antigen_assignment,
            antigen_specificity_scores:     CALCULATE_ANTIGEN_SPECIFICITY.antigen_specificity_scores,
            antigen_vdj_metrics_bin:        COMPUTE_ANTIGEN_VDJ_METRICS.metrics_bin,
            antigen_vdj_metrics_json:       COMPUTE_ANTIGEN_VDJ_METRICS.metrics_json,
            clonotype_concordance:          CALCULATE_ANTIGEN_SPECIFICITY.clonotype_concordance,
            exact_subclonotype_concordance: CALCULATE_ANTIGEN_SPECIFICITY.exact_subclonotype_concordance,
            per_barcode:                    CREATE_BARCODE_CSV.per_barcode_csv,
            specificity_summary:            CALCULATE_ANTIGEN_SPECIFICITY.summary,
        },
    )
}

pipeline VDJ_CALL_CELLS(
    in  string       receptor,
    in  bool         is_antibody_only,
    in  bool         is_non_targeted_gex,
    in  bool         denovo,
    in  ChemistryDef vdj_chemistry_def,
    in  FilterSwitch filter_switch,
    in  path         vdj_reference_path,
    in  int          n50_n50_rpu,
    in  json         contig_annotations,
    in  bdf.bincode  barcode_brief,
    in  fprint.json  sample_fingerprint,
    in  csv          filtered_barcodes,
    out json         contig_annotations,
    out json.lz4     asm_filter_diagnostics,
)
{
    call ASM_CALL_CELLS(
        receptor           = self.receptor,
        denovo             = self.denovo,
        vdj_reference_path = self.vdj_reference_path,
        vdj_chemistry_def  = {
            "VDJ": self.vdj_chemistry_def,
        },
        contig_annotations = self.contig_annotations,
        barcode_brief      = self.barcode_brief,
        n50_n50_rpu        = self.n50_n50_rpu,
        filter_switch      = self.filter_switch,
        sample_fingerprint = self.sample_fingerprint,
    )

    call HANDLE_GEX_CELLS(
        asm_contig_annotations = ASM_CALL_CELLS.contig_annotations,
        filtered_barcodes      = self.filtered_barcodes,
        is_antibody_only       = self.is_antibody_only,
        is_non_targeted_gex    = self.is_non_targeted_gex,
    )

    return (
        contig_annotations     = HANDLE_GEX_CELLS.contig_annotations,
        asm_filter_diagnostics = ASM_CALL_CELLS.filter_diagnostics,
    )
}

pipeline VDJ_ANALYZER(
    in  VdjAnalysisConfig          vdj_config,
    in  CommonInputs               common_input,
    in  string                     receptor,
    in  path                       inner_primers,
    in  path                       vdj_reference_path,
    in  csv                        feature_reference,
    in  FeatureConfig              feature_config,
    in  ChemistryDef               vdj_chemistry_def,
    in  VdjAssemblerAnalyzerInputs assembler_outs,
    in  json                       merged_annotations,
    in  VdjDemuxSampleInfo         demux_sample_info,
    in  GexMatrices                library_level_gex,
    in  bmsf[]                     per_barcode_count_metrics,
    in  FilterSwitch               filter_switch,
    in  bool                       is_antibody_only,
    in  bool                       is_non_targeted_gex,
    out VdjAnalyzerClonotypeOuts   clonotype,
    out BeamAnalyzerOutputs        beam_analyzer,
    out VdjReport                  report,
)
{
    call SETUP_VDJ_ANALYSIS(
        receptor          = self.receptor,
        vdj_config        = self.vdj_config,
        demux_sample_info = self.demux_sample_info,
        lib_level_gex     = self.library_level_gex,
    )

    call VDJ_CALL_CELLS(
        receptor            = SETUP_VDJ_ANALYSIS.receptor,
        is_antibody_only    = self.is_antibody_only,
        is_non_targeted_gex = self.is_non_targeted_gex,
        denovo              = self.vdj_config.denovo,
        vdj_chemistry_def   = self.vdj_chemistry_def,
        filter_switch       = self.filter_switch,
        vdj_reference_path  = self.vdj_reference_path,
        n50_n50_rpu         = self.assembler_outs.n50_n50_rpu,
        contig_annotations  = self.assembler_outs.asm_contig_annotations,
        barcode_brief       = self.assembler_outs.barcode_brief,
        sample_fingerprint  = self.demux_sample_info.fingerprint,
        filtered_barcodes   = SETUP_VDJ_ANALYSIS.filtered_barcodes,
    ) using (
        disabled = SETUP_VDJ_ANALYSIS.disable_cell_calling,
    )

    call SUBSET_ASSEMBLY_OUTS(
        per_sample               = self.vdj_config.per_sample,
        multiplexing_method      = SETUP_VDJ_ANALYSIS.multiplexing_method,
        vdj_chemistry_def        = {
            "VDJ": self.vdj_chemistry_def,
        },
        sample_fingerprint       = self.demux_sample_info.fingerprint,
        contig_annotations       = VDJ_CALL_CELLS.contig_annotations,
        merged_annotations       = self.merged_annotations,
        total_read_pairs         = self.assembler_outs.total_read_pairs,
        corrected_barcode_counts = self.assembler_outs.corrected_barcode_counts,
        assemblable_reads_per_bc = self.assembler_outs.assemblable_reads_per_bc,
        umi_summary              = self.assembler_outs.umi_summary_tsv,
        barcode_support          = self.assembler_outs.barcode_support,
        barcode_brief            = self.assembler_outs.barcode_brief,
        barcode_full             = self.assembler_outs.barcode_full,
    )

    call ASM_METRICS(
        chemistry_def            = self.vdj_chemistry_def,
        vdj_reference_path       = self.vdj_reference_path,
        receptor                 = self.receptor,
        inner_enrichment_primers = self.inner_primers,
        total_read_pairs         = SUBSET_ASSEMBLY_OUTS.total_read_pairs,
        barcode_full             = SUBSET_ASSEMBLY_OUTS.barcode_full,
    ) using (
        disabled = SETUP_VDJ_ANALYSIS.disable_asm_metrics,
    )

    call MAKE_EXACT_CLONOTYPES(
        contig_annotations = SUBSET_ASSEMBLY_OUTS.contig_annotations,
    ) using (
        disabled = SETUP_VDJ_ANALYSIS.disable_cell_calling,
    )

    call FILTER_EXACT_CLONOTYPES(
        exact_clonotypes   = MAKE_EXACT_CLONOTYPES.exact_clonotypes,
        contig_annotations = SUBSET_ASSEMBLY_OUTS.contig_annotations,
        filter_diagnostics = VDJ_CALL_CELLS.asm_filter_diagnostics,
    ) using (
        disabled = SETUP_VDJ_ANALYSIS.disable_cell_calling,
    )

    call CLONOTYPE_ASSIGNER(
        sample_id          = self.demux_sample_info.sample_id,
        vdj_reference_path = self.vdj_reference_path,
        contig_annotations = FILTER_EXACT_CLONOTYPES.contig_annotations,
        receptor           = SETUP_VDJ_ANALYSIS.receptor,
        filter_switch      = self.filter_switch,
    ) using (
        disabled = SETUP_VDJ_ANALYSIS.disable_clonotyping,
    )

    call HANDLE_NO_CLONOTYPING(
        contigs             = {
            merged_liblevel:     self.merged_annotations,
            post_cell_filtering: FILTER_EXACT_CLONOTYPES.contig_annotations,
            post_clonotyping:    CLONOTYPE_ASSIGNER.contig_annotations_json,
        },
        disable_clonotyping = SETUP_VDJ_ANALYSIS.disable_clonotyping,
    )

    call WRITE_ANN_CSV(
        all_contig_annotations_json = HANDLE_NO_CLONOTYPING.final_contig_annotations,
    )

    call SUMMARIZE_VDJ_FILTERS(
        sample_id              = self.common_input.sample_id,
        sample_description     = self.common_input.sample_desc,
        all_contig_annotations = HANDLE_NO_CLONOTYPING.final_contig_annotations,
        asm_filter_diagnostics = FILTER_EXACT_CLONOTYPES.filter_diagnostics,
        enclone_barcode_fate   = CLONOTYPE_ASSIGNER.enclone_barcode_fate,
        raw_matrix_h5          = SETUP_VDJ_ANALYSIS.raw_matrix_h5,
    ) using (
        disabled = SETUP_VDJ_ANALYSIS.disable_clonotyping,
    )

    call WRITE_CONTIG_OUTS(
        contig_annotations       = HANDLE_NO_CLONOTYPING.final_contig_annotations,
        total_read_pairs         = SUBSET_ASSEMBLY_OUTS.total_read_pairs,
        corrected_bc_counts      = SUBSET_ASSEMBLY_OUTS.corrected_barcode_counts,
        assemblable_reads_per_bc = SUBSET_ASSEMBLY_OUTS.assemblable_reads_per_bc,
    )

    call REPORT_CONTIGS(
        vdj_reference_path = self.vdj_reference_path,
        cell_barcodes      = WRITE_CONTIG_OUTS.cell_barcodes,
        contigs            = WRITE_CONTIG_OUTS.contig_fasta,
        annotations        = HANDLE_NO_CLONOTYPING.final_contig_annotations,
        filter_summary     = null,
        umi_summary        = SUBSET_ASSEMBLY_OUTS.umi_summary,
        prefix             = "",
    ) using (
        volatile = true,
    )

    call BEAM_ANALYZER(
        filtered_feature_counts_matrix = SETUP_VDJ_ANALYSIS.filtered_matrix_h5,
        per_barcode_count_metrics   = self.per_barcode_count_metrics,
        beam_mode                   = SETUP_VDJ_ANALYSIS.beam_mode,
        filtered_contig_annotations = WRITE_ANN_CSV.filtered_contig_annotations_csv,
        clonotypes_csv              = CLONOTYPE_ASSIGNER.clonotypes_csv,
        vdj_cell_barcodes           = WRITE_CONTIG_OUTS.cell_barcodes,
    ) using (
        disabled = SETUP_VDJ_ANALYSIS.disable_beam,
    )

    call SUMMARIZE_VDJ_REPORTS(
        sample_id         = self.common_input.sample_id,
        sample_desc       = self.common_input.sample_desc,
        vdj_chemistry_def = self.vdj_chemistry_def,
        barcode_support   = SUBSET_ASSEMBLY_OUTS.barcode_support,
        summaries         = [
            self.assembler_outs.summary,
            ASM_METRICS.metrics_summary_json,
            REPORT_CONTIGS.summary,
            CLONOTYPE_ASSIGNER.summary,
            WRITE_CONTIG_OUTS.summary,
            BEAM_ANALYZER.outputs.antigen_vdj_metrics_json,
            SUMMARIZE_VDJ_FILTERS.metrics_summary,
        ],
        total_read_pairs  = SUBSET_ASSEMBLY_OUTS.total_read_pairs,
        cell_barcodes     = WRITE_CONTIG_OUTS.cell_barcodes,
        clonotype_summary = CLONOTYPE_ASSIGNER.clonotypes_csv,
        receptor          = SETUP_VDJ_ANALYSIS.receptor,
        n50_n50_rpu       = self.assembler_outs.n50_n50_rpu,
    )

    call WRITE_CONTIG_PROTO(
        vdj_reference_path      = self.vdj_reference_path,
        contig_annotations_json = HANDLE_NO_CLONOTYPING.final_contig_annotations,
        metrics_summary_json    = SUMMARIZE_VDJ_REPORTS.metrics_summary_json,
        receptor                = SETUP_VDJ_ANALYSIS.receptor,
        gem_wells               = [1],
        cell_barcodes           = WRITE_CONTIG_OUTS.cell_barcodes,
        sample_id               = self.common_input.sample_id,
        sample_desc             = self.common_input.sample_desc,
        multi_config_sha        = self.common_input.multi_config_sha,
        barcode_brief           = SUBSET_ASSEMBLY_OUTS.barcode_brief,
        multiplexing_method     = SETUP_VDJ_ANALYSIS.multiplexing_method,
    ) using (
        disabled = self.vdj_config.has_no_vdj_ref,
    )

    call VLOUPE_PREPROCESS(
        pipestance_type              = "SC_VDJ_ASSEMBLER_CS",
        sample_id                    = self.common_input.sample_id,
        sample_desc                  = self.common_input.sample_desc,
        enclone_output               = CLONOTYPE_ASSIGNER.enclone_output,
        disable_vloupe               = CLONOTYPE_ASSIGNER.disable_vloupe,
        beam_mode                    = SETUP_VDJ_ANALYSIS.beam_mode,
        feature_reference            = self.feature_reference,
        feature_barcode_matrix       = SETUP_VDJ_ANALYSIS.filtered_matrix_h5,
        antigen_specificity_scores   = BEAM_ANALYZER.outputs.antigen_specificity_scores,
        antigen_specificity_controls = self.feature_config.specificity_controls.control_for_allele,
    )

    return (
        clonotype     = {
            airr_rearrangement:              CLONOTYPE_ASSIGNER.airr_rearrangement,
            all_contig_annotations_csv:      WRITE_ANN_CSV.all_contig_annotations_csv,
            all_contig_annotations_json:     HANDLE_NO_CLONOTYPING.final_contig_annotations,
            clonotypes_csv:                  CLONOTYPE_ASSIGNER.clonotypes_csv,
            concat_ref_bam:                  CLONOTYPE_ASSIGNER.concat_ref_bam,
            concat_ref_bam_bai:              CLONOTYPE_ASSIGNER.concat_ref_bam_bai,
            concat_ref_fasta:                CLONOTYPE_ASSIGNER.concat_ref_fasta,
            concat_ref_fasta_fai:            CLONOTYPE_ASSIGNER.concat_ref_fasta_fai,
            consensus_annotations_csv:       CLONOTYPE_ASSIGNER.consensus_annotations_csv,
            consensus_bam:                   CLONOTYPE_ASSIGNER.consensus_bam,
            consensus_bam_bai:               CLONOTYPE_ASSIGNER.consensus_bam_bai,
            consensus_fasta:                 CLONOTYPE_ASSIGNER.consensus_fasta,
            consensus_fasta_fai:             CLONOTYPE_ASSIGNER.consensus_fasta_fai,
            donor_ref_fa:                    CLONOTYPE_ASSIGNER.donor_ref_fa,
            enclone_output:                  CLONOTYPE_ASSIGNER.enclone_output,
            filtered_contig_annotations_csv: WRITE_ANN_CSV.filtered_contig_annotations_csv,
        },
        beam_analyzer = BEAM_ANALYZER.outputs,
        report        = {
            all_contig_barcodes:      WRITE_CONTIG_OUTS.all_contig_barcodes,
            annotations_bed:          WRITE_CONTIG_OUTS.annotations_bed,
            barcode_brief:            SUBSET_ASSEMBLY_OUTS.barcode_brief,
            cdr3_barcodes:            WRITE_CONTIG_OUTS.cdr3_barcodes,
            cell_barcodes:            WRITE_CONTIG_OUTS.cell_barcodes,
            contig_fasta:             WRITE_CONTIG_OUTS.contig_fasta,
            contig_fasta_fai:         WRITE_CONTIG_OUTS.contig_fasta_fai,
            contig_fastq:             WRITE_CONTIG_OUTS.contig_fastq,
            filter_metrics:           SUMMARIZE_VDJ_FILTERS.metrics_summary,
            filter_summary:           SUMMARIZE_VDJ_FILTERS.filter_summary,
            filtered_contig_fasta:    WRITE_CONTIG_OUTS.filtered_contig_fasta,
            filtered_contig_fastq:    WRITE_CONTIG_OUTS.filtered_contig_fastq,
            metrics_summary_csv:      SUMMARIZE_VDJ_REPORTS.metrics_summary_csv,
            metrics_summary_json:     SUMMARIZE_VDJ_REPORTS.metrics_summary_json,
            per_bc_filters:           SUMMARIZE_VDJ_FILTERS.per_bc_filters,
            productive_barcodes:      WRITE_CONTIG_OUTS.prod_barcodes,
            productive_cell_barcodes: WRITE_CONTIG_OUTS.paired_cell_barcodes,
            report:                   ASM_METRICS.report,
            umi_summary:              SUBSET_ASSEMBLY_OUTS.umi_summary,
            vdj_contig_info:          WRITE_CONTIG_PROTO.vdj_contig_info,
            vloupe:                   VLOUPE_PREPROCESS.output_for_vloupe,
            web_summary:              SUMMARIZE_VDJ_REPORTS.web_summary,
            web_summary_data:         SUMMARIZE_VDJ_REPORTS.web_summary_data,
        },
    )
}

pipeline VDJ_GEM_WELL_PROCESSOR(
    in  CommonInputs            common_input,
    in  ChemistryDef            vdj_chemistry_def,
    in  string                  receptor,
    in  VdjAssemblerInputs      inputs,
    in  VdjGenInputs            gen_inputs,
    in  GexMatrices             library_level_gex,
    in  bmsf[]                  per_barcode_count_metrics,
    in  SampleMatrices[]        multi_matrices,
    in  json                    multi_graph,
    in  bool                    is_antibody_only,
    in  bool                    is_non_targeted_gex,
    in  bool                    has_no_vdj_ref,
    in  csv                     feature_reference,
    in  FeatureConfig           feature_config,
    in  FilterSwitch            filter_switch,
    in  json                    gex_cells_per_tag,
    out ChemistryDef            chemistry_def,
    out MULTI_SETUP_CHUNKS      setup_chunks_outs,
    out SC_VDJ_CONTIG_ASSEMBLER assembler_outs,
    out VDJ_ANALYZER            lib_level,
    out map<VDJ_ANALYZER>       per_sample,
    out map<vwc.json>           vdj_ws_contents,
)
{
    call MULTI_SETUP_CHUNKS(
        sample_id            = self.common_input.sample_id,
        chemistry_defs       = {
            "VDJ": self.vdj_chemistry_def,
        },
        default_library_type = "VDJ",
        *                    = self.inputs,
    ) using (
        volatile = true,
    )

    call SC_VDJ_CONTIG_ASSEMBLER(
        sample_id            = self.common_input.sample_id,
        chemistry_defs       = {
            "VDJ": self.vdj_chemistry_def,
        },
        gem_well             = 1,
        chunks               = MULTI_SETUP_CHUNKS.chunks,
        inner_primers        = self.inputs.inner_enrichment_primers,
        vdj_reference_folder = self.gen_inputs.vdj_reference_path,
        receptor             = self.receptor,
        feature_config       = self.feature_config,
        min_contig_length    = self.gen_inputs.min_contig_length,
        *                    = self.inputs,
    )

    call SETUP_VDJ_DEMUX(
        multi_matrices = self.multi_matrices,
        multi_graph    = self.multi_graph,
    )

    map call VDJ_ANALYZER as PER_SAMPLE_VDJ_ANALYZER(
        vdj_config                = {
            denovo:           self.inputs.denovo,
            has_antigen:      SETUP_VDJ_DEMUX.has_antigen,
            has_no_vdj_ref:   self.has_no_vdj_ref,
            is_multi:         SETUP_VDJ_DEMUX.is_multi,
            per_sample:       true,
            skip_clonotyping: self.gen_inputs.skip_clonotyping,
        },
        common_input              = self.common_input,
        vdj_chemistry_def         = self.vdj_chemistry_def,
        receptor                  = self.receptor,
        inner_primers             = self.inputs.inner_enrichment_primers,
        vdj_reference_path        = self.gen_inputs.vdj_reference_path,
        feature_reference         = self.feature_reference,
        feature_config            = self.feature_config,
        assembler_outs            = {
            asm_contig_annotations:   SC_VDJ_CONTIG_ASSEMBLER.asm_contig_annotations,
            assemblable_reads_per_bc: SC_VDJ_CONTIG_ASSEMBLER.assemblable_reads_per_bc,
            barcode_brief:            SC_VDJ_CONTIG_ASSEMBLER.barcode_brief,
            barcode_full:             SC_VDJ_CONTIG_ASSEMBLER.barcode_full,
            barcode_support:          SC_VDJ_CONTIG_ASSEMBLER.barcode_support,
            corrected_barcode_counts: SC_VDJ_CONTIG_ASSEMBLER.corrected_barcode_counts,
            n50_n50_rpu:              SC_VDJ_CONTIG_ASSEMBLER.n50_n50_rpu,
            summary:                  null,
            summary_tsv:              SC_VDJ_CONTIG_ASSEMBLER.summary_tsv,
            total_read_pairs:         SC_VDJ_CONTIG_ASSEMBLER.total_read_pairs,
            umi_summary_tsv:          SC_VDJ_CONTIG_ASSEMBLER.umi_summary_tsv,
        },
        merged_annotations        = null,
        demux_sample_info         = split SETUP_VDJ_DEMUX.per_sample_info,
        library_level_gex         = self.library_level_gex,
        per_barcode_count_metrics = self.per_barcode_count_metrics,
        filter_switch             = self.filter_switch,
        is_antibody_only          = self.is_antibody_only,
        is_non_targeted_gex       = self.is_non_targeted_gex,
    ) using (
        disabled = SETUP_VDJ_DEMUX.is_not_multi,
    )

    call MERGE_PER_SAMPLE_ANNOTATIONS(
        per_sample_annotations = PER_SAMPLE_VDJ_ANALYZER.clonotype.all_contig_annotations_json,
        asm_contig_annotations = SC_VDJ_CONTIG_ASSEMBLER.asm_contig_annotations,
        multiplexing_method    = SETUP_VDJ_DEMUX.multiplexing_method,
        gex_cells_per_tag      = self.gex_cells_per_tag,
        vdj_chemistry_def      = self.vdj_chemistry_def,
    ) using (
        disabled = SETUP_VDJ_DEMUX.is_not_multi,
    )

    call VDJ_ANALYZER as LIB_VDJ_ANALYZER(
        vdj_config                = {
            denovo:           self.inputs.denovo,
            has_antigen:      SETUP_VDJ_DEMUX.has_antigen,
            has_no_vdj_ref:   self.has_no_vdj_ref,
            is_multi:         SETUP_VDJ_DEMUX.is_multi,
            per_sample:       false,
            skip_clonotyping: self.gen_inputs.skip_clonotyping,
        },
        common_input              = self.common_input,
        vdj_chemistry_def         = self.vdj_chemistry_def,
        receptor                  = self.receptor,
        inner_primers             = self.inputs.inner_enrichment_primers,
        vdj_reference_path        = self.gen_inputs.vdj_reference_path,
        feature_reference         = self.feature_reference,
        feature_config            = self.feature_config,
        assembler_outs            = {
            asm_contig_annotations:   SC_VDJ_CONTIG_ASSEMBLER.asm_contig_annotations,
            assemblable_reads_per_bc: SC_VDJ_CONTIG_ASSEMBLER.assemblable_reads_per_bc,
            barcode_brief:            SC_VDJ_CONTIG_ASSEMBLER.barcode_brief,
            barcode_full:             SC_VDJ_CONTIG_ASSEMBLER.barcode_full,
            barcode_support:          SC_VDJ_CONTIG_ASSEMBLER.barcode_support,
            corrected_barcode_counts: SC_VDJ_CONTIG_ASSEMBLER.corrected_barcode_counts,
            n50_n50_rpu:              SC_VDJ_CONTIG_ASSEMBLER.n50_n50_rpu,
            summary:                  SC_VDJ_CONTIG_ASSEMBLER.summary,
            summary_tsv:              SC_VDJ_CONTIG_ASSEMBLER.summary_tsv,
            total_read_pairs:         SC_VDJ_CONTIG_ASSEMBLER.total_read_pairs,
            umi_summary_tsv:          SC_VDJ_CONTIG_ASSEMBLER.umi_summary_tsv,
        },
        merged_annotations        = MERGE_PER_SAMPLE_ANNOTATIONS.contig_annotations,
        demux_sample_info         = null,
        library_level_gex         = self.library_level_gex,
        per_barcode_count_metrics = self.per_barcode_count_metrics,
        filter_switch             = self.filter_switch,
        is_antibody_only          = self.is_antibody_only,
        is_non_targeted_gex       = self.is_non_targeted_gex,
    )

    call BUILD_PER_SAMPLE_VDJ_WS_CONTENTS(
        lib_level_metrics      = LIB_VDJ_ANALYZER.report.metrics_summary_json,
        per_sample_metrics     = PER_SAMPLE_VDJ_ANALYZER.report.metrics_summary_json,
        receptor               = self.receptor,
        physical_library_id    = self.inputs.physical_library_id,
        multiplexing_method    = SETUP_VDJ_DEMUX.multiplexing_method,
        vdj_gen_inputs         = self.gen_inputs,
        sequencing_metrics     = SC_VDJ_CONTIG_ASSEMBLER.sequencing_metrics,
        lib_level_vdj_ws_json  = LIB_VDJ_ANALYZER.report.web_summary_data,
        per_sample_vdj_ws_json = PER_SAMPLE_VDJ_ANALYZER.report.web_summary_data,
        filter_metrics         = PER_SAMPLE_VDJ_ANALYZER.report.filter_metrics,
        multi_graph            = self.multi_graph,
        vdj_cells_per_tag_json = MERGE_PER_SAMPLE_ANNOTATIONS.vdj_cells_per_tag_json,
    ) using (
        disabled = SETUP_VDJ_DEMUX.is_not_multi,
    )

    return (
        chemistry_def     = self.vdj_chemistry_def,
        setup_chunks_outs = MULTI_SETUP_CHUNKS,
        assembler_outs    = SC_VDJ_CONTIG_ASSEMBLER,
        lib_level         = LIB_VDJ_ANALYZER,
        per_sample        = PER_SAMPLE_VDJ_ANALYZER,
        vdj_ws_contents   = BUILD_PER_SAMPLE_VDJ_WS_CONTENTS.vdj_ws_contents,
    )
}

stage _FORCE_SAMPLE_DEF_GEM_WELL(
    in  map[] sample_def,
    in  int   gem_group,
    out map[] sample_def,
    src py    "stages/multi/_force_sample_def_gem_well",
) using (
    volatile = strict,
)

stage BUILD_MULTI_WEB_SUMMARY(
    in  map<json> web_summary_data,
    in  map<csv>  metrics_summary_csvs,
    out map<html> web_summaries,
    out map<csv>  metrics_summary_csvs,
    src py        "stages/common/build_multi_web_summary",
) using (
    mem_gb   = 3,
    volatile = strict,
)

stage BUILD_MULTI_GRAPH_VIEW(
    in  json multi_graph,
    out svg  view,
    src py   "stages/multi/build_multi_graph_view",
) using (
    mem_gb   = 2,
    volatile = strict,
)

pipeline FORCE_SAMPLE_DEF_GEM_WELL(
    in  CounterInputs count_inputs,
    in  int           gem_group,
    out CounterInputs count_inputs,
)
{
    call _FORCE_SAMPLE_DEF_GEM_WELL(
        sample_def = self.count_inputs.sample_def,
        gem_group  = self.gem_group,
    )

    return (
        count_inputs = {
            aligner:                     self.count_inputs.aligner,
            cell_calling_config:         self.count_inputs.cell_calling_config,
            check_library_compatibility: self.count_inputs.check_library_compatibility,
            custom_chemistry_def:        self.count_inputs.custom_chemistry_def,
            feature_reference:           self.count_inputs.feature_reference,
            filter_probes:               self.count_inputs.filter_probes,
            force_sample_barcodes:       self.count_inputs.force_sample_barcodes,
            gene_index:                  self.count_inputs.gene_index,
            include_exons:               self.count_inputs.include_exons,
            include_introns:             self.count_inputs.include_introns,
            initial_reads:               self.count_inputs.initial_reads,
            min_assignment_confidence:   self.count_inputs.min_assignment_confidence,
            min_crispr_umi_threshold:    self.count_inputs.min_crispr_umi_threshold,
            no_bam:                      self.count_inputs.no_bam,
            no_target_umi_filter:        self.count_inputs.no_target_umi_filter,
            primers:                     self.count_inputs.primers,
            r1_length:                   self.count_inputs.r1_length,
            r2_length:                   self.count_inputs.r2_length,
            reference_path:              self.count_inputs.reference_path,
            sample_def:                  _FORCE_SAMPLE_DEF_GEM_WELL.sample_def,
            subsample_rate:              self.count_inputs.subsample_rate,
            target_set:                  self.count_inputs.target_set,
            target_set_name:             self.count_inputs.target_set_name,
            targeting_method:            self.count_inputs.targeting_method,
            trim_polya_min_score:        self.count_inputs.trim_polya_min_score,
            trim_tso_min_score:          self.count_inputs.trim_tso_min_score,
        },
    )
}

# NOTE: This needs to be map-called if we have data from multiple
# gem wells
pipeline MULTI_GEM_WELL_PROCESSOR(
    in  int                      gem_group,
    in  CommonInputs             common_input,
    in  FullPipelineConfig       config,
    in  bool                     is_pd,
    in  CounterInputs            count_inputs,
    in  DETECT_CHEMISTRY         count_chem,
    in  json                     multi_graph,
    in  VdjAssemblerInputs       vdj_t_inputs,
    in  ChemistryDef             vdj_t_chem_def,
    in  string                   vdj_t_receptor,
    in  VdjAssemblerInputs       vdj_t_gd_inputs,
    in  ChemistryDef             vdj_t_gd_chem_def,
    in  string                   vdj_t_gd_receptor,
    in  VdjAssemblerInputs       vdj_b_inputs,
    in  ChemistryDef             vdj_b_chem_def,
    in  string                   vdj_b_receptor,
    in  VdjGenInputs             vdj_gen_inputs,
    in  FeatureConfig            feature_config,
    out COUNT_GEM_WELL_PROCESSOR count,
    out VDJ_GEM_WELL_PROCESSOR   vdj_t,
    out VDJ_GEM_WELL_PROCESSOR   vdj_t_gd,
    out VDJ_GEM_WELL_PROCESSOR   vdj_b,
    out FilterSwitch             vdj_filter_switch,
)
{
    call COUNT_GEM_WELL_PROCESSOR(
        gem_group        = self.gem_group,
        sample_id        = self.common_input.sample_id,
        multi_config_sha = self.common_input.multi_config_sha,
        inputs           = self.count_inputs,
        chem             = self.count_chem,
        is_pd            = self.is_pd,
        disable_multi    = self.config.disable_multi,
        multi_graph      = self.multi_graph,
        feature_config   = self.feature_config,
    ) using (
        disabled = self.config.disable_count,
    )

    call MAKE_FILTER_SWITCH as MAKE_VDJ_FILTER_SWITCH(
        disable_count       = self.config.disable_count,
        is_antibody_only    = self.count_chem.is_antibody_only,
        is_non_targeted_gex = COUNT_GEM_WELL_PROCESSOR.target_outs.disable_targeted,
        *                   = self.vdj_gen_inputs.filter_flags,
    )

    call VDJ_GEM_WELL_PROCESSOR as VDJ_T_GEM_WELL_PROCESSOR(
        common_input              = self.common_input,
        vdj_chemistry_def         = self.vdj_t_chem_def,
        receptor                  = self.vdj_t_receptor,
        inputs                    = self.vdj_t_inputs,
        gen_inputs                = self.vdj_gen_inputs,
        library_level_gex         = {
            filtered_barcodes:  COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.filtered_barcodes,
            filtered_matrix_h5: COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.filtered_gene_bc_matrices_h5,
            raw_matrix_h5:      COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.raw_gene_bc_matrices_h5,
        },
        per_barcode_count_metrics = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.per_barcode_metrics_shard,
        multi_matrices            = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.multi_matrices,
        multi_graph               = self.multi_graph,
        is_antibody_only          = self.count_chem.is_antibody_only,
        is_non_targeted_gex       = COUNT_GEM_WELL_PROCESSOR.target_outs.disable_targeted,
        feature_reference         = self.count_inputs.feature_reference,
        feature_config            = self.feature_config,
        filter_switch             = MAKE_VDJ_FILTER_SWITCH.filter_switch,
        has_no_vdj_ref            = self.config.has_no_vdj_ref,
        gex_cells_per_tag         = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.assign_tags.cells_per_tag,
    ) using (
        disabled = self.config.disable_vdj_t,
    )

    call VDJ_GEM_WELL_PROCESSOR as VDJ_T_GD_GEM_WELL_PROCESSOR(
        common_input              = self.common_input,
        vdj_chemistry_def         = self.vdj_t_gd_chem_def,
        receptor                  = self.vdj_t_gd_receptor,
        inputs                    = self.vdj_t_gd_inputs,
        gen_inputs                = self.vdj_gen_inputs,
        library_level_gex         = {
            filtered_barcodes:  COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.filtered_barcodes,
            filtered_matrix_h5: COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.filtered_gene_bc_matrices_h5,
            raw_matrix_h5:      COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.raw_gene_bc_matrices_h5,
        },
        per_barcode_count_metrics = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.per_barcode_metrics_shard,
        multi_matrices            = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.multi_matrices,
        multi_graph               = self.multi_graph,
        is_antibody_only          = self.count_chem.is_antibody_only,
        is_non_targeted_gex       = COUNT_GEM_WELL_PROCESSOR.target_outs.disable_targeted,
        feature_reference         = self.count_inputs.feature_reference,
        feature_config            = self.feature_config,
        filter_switch             = MAKE_VDJ_FILTER_SWITCH.filter_switch,
        has_no_vdj_ref            = self.config.has_no_vdj_ref,
        gex_cells_per_tag         = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.assign_tags.cells_per_tag,
    ) using (
        disabled = self.config.disable_vdj_t_gd,
    )

    call VDJ_GEM_WELL_PROCESSOR as VDJ_B_GEM_WELL_PROCESSOR(
        common_input              = self.common_input,
        vdj_chemistry_def         = self.vdj_b_chem_def,
        receptor                  = self.vdj_b_receptor,
        inputs                    = self.vdj_b_inputs,
        gen_inputs                = self.vdj_gen_inputs,
        library_level_gex         = {
            filtered_barcodes:  COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.filtered_barcodes,
            filtered_matrix_h5: COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.filtered_gene_bc_matrices_h5,
            raw_matrix_h5:      COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.raw_gene_bc_matrices_h5,
        },
        per_barcode_count_metrics = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.per_barcode_metrics_shard,
        multi_matrices            = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.multi_matrices,
        multi_graph               = self.multi_graph,
        is_antibody_only          = self.count_chem.is_antibody_only,
        is_non_targeted_gex       = COUNT_GEM_WELL_PROCESSOR.target_outs.disable_targeted,
        feature_reference         = self.count_inputs.feature_reference,
        feature_config            = self.feature_config,
        filter_switch             = MAKE_VDJ_FILTER_SWITCH.filter_switch,
        has_no_vdj_ref            = self.config.has_no_vdj_ref,
        gex_cells_per_tag         = COUNT_GEM_WELL_PROCESSOR.basic_counter_outs.assign_tags.cells_per_tag,
    ) using (
        disabled = self.config.disable_vdj_b,
    )

    # TODO: Add tag calling pipeline here

    return (
        count             = COUNT_GEM_WELL_PROCESSOR,
        vdj_t             = VDJ_T_GEM_WELL_PROCESSOR,
        vdj_t_gd          = VDJ_T_GD_GEM_WELL_PROCESSOR,
        vdj_b             = VDJ_B_GEM_WELL_PROCESSOR,
        vdj_filter_switch = MAKE_VDJ_FILTER_SWITCH.filter_switch,
    )
}

###############################################################################
# Count Analyzer
###############################################################################
pipeline COUNT_ANALYZER(
    in  h5                    filtered_matrices_h5,
    in  h5                    molecule_info,
    in  CounterInputs         count_inputs,
    in  bool                  no_secondary_analysis,
    in  bool                  disable_rna,
    in  bool                  disable_crispr,
    in  bool                  disable_antibody,
    in  bool                  disable_antigen,
    in  bool                  is_pd,
    in  bool                  disable_targeted,
    in  bool                  enable_tsne,
    in  csv                   filtered_barcodes,
    in  csv                   aggregate_barcodes,
    in  csv                   feature_reference,
    in  json                  multi_graph,
    in  json                  counter_metrics_json,
    in  PARSE_TARGET_FEATURES parse_target_features,
    in  string                sample_id,
    out AnalyzerOutputs       common_analyzer,
    out _CRISPR_ANALYZER      crispr_analyzer,
    out _ANTIBODY_ANALYZER    antibody_analyzer,
    out _ANTIBODY_ANALYZER    antigen_analyzer,
    out _TARGETED_ANALYZER    targeted_analyzer,
)
{
    call DISABLE_SECONDARY_ANALYSIS(
        is_spatial            = false,
        is_visium_hd_main_run = false,
        filtered_matrices_h5  = self.filtered_matrices_h5,
        no_secondary_analysis = self.no_secondary_analysis,
    )

    # TODO: It would be cleaner if _CRISPR_ANALYZER can go inside SC_RNA_ANALYZER
    # TODO: This pipeline can be cleaned up using structs for various sets of parameters
    call SC_RNA_ANALYZER(
        aggregate_barcodes = self.aggregate_barcodes,
        analyzer_inputs    = {
            aggr_library_info:          null,
            cbc_alpha:                  null,
            cbc_knn:                    null,
            cbc_realign_panorama:       null,
            cbc_sigma:                  null,
            chemistry_batch_correction: false,
            enable_tsne:                self.enable_tsne,
            exclude_genes:              null,
            filtered_matrices_h5:       self.filtered_matrices_h5,
            # NOTE: this is null because the cells are already forced in FILTER_BARCODES
            force_cells:                null,
            graphclust_neighbors:       null,
            graphclust_resolution:      null,
            is_pd:                      self.is_pd,
            is_spatial:                 false,
            is_visium_hd:               false,
            max_clusters:               null,
            molecule_info:              self.molecule_info,
            neighbor_a:                 null,
            neighbor_b:                 null,
            no_secondary_analysis:      DISABLE_SECONDARY_ANALYSIS.no_secondary_analysis,
            num_analysis_bcs:           null,
            num_pca_bcs:                null,
            num_pca_genes:              null,
            num_principal_comps:        null,
            random_seed:                null,
            skip_multigenome_analysis:  false,
            tsne_input_pcs:             null,
            tsne_max_dims:              null,
            tsne_max_iter:              null,
            tsne_mom_switch_iter:       null,
            tsne_perplexity:            null,
            tsne_stop_lying_iter:       null,
            tsne_theta:                 null,
            umap_implementation:        "original",
            umap_input_pcs:             null,
            umap_max_dims:              null,
            umap_metric:                null,
            umap_min_dist:              null,
            umap_n_neighbors:           null,
            use_bcs:                    null,
            use_genes:                  null,
        },
    ) using (
        disabled = self.disable_rna,
    )

    call _CRISPR_ANALYZER(
        filtered_feature_counts_matrix = self.filtered_matrices_h5,
        feature_reference        = self.feature_reference,
        min_crispr_umi_threshold = self.count_inputs.min_crispr_umi_threshold,
    ) using (
        disabled = self.disable_crispr,
    )

    call _TARGETED_ANALYZER(
        molecule_info             = self.molecule_info,
        filtered_gene_bc_matrices = self.filtered_matrices_h5,
        filtered_barcodes         = self.filtered_barcodes,
        basic_counter_summary     = self.counter_metrics_json,
        probe_set                 = self.parse_target_features.probe_set,
        target_panel_summary      = self.parse_target_features.target_panel_summary,
        is_spatial                = false,
        is_visium_hd              = false,
    ) using (
        disabled = self.disable_targeted,
    )

    # Note: This pipeline is already been called inside `SC_RNA_ANALYZER`
    # where it was placed to enable code sharing between spatial/sc
    # aggr/reanalyze/count.  However, in the future we'd like the other
    # pipelines to all feed through `COUNT_ANALYZER` and so take
    # _ANTIBODY_ANALYZER/_ANTIGEN_ANALYZER out of SC_RNA_ANALYZER but still enable code
    # sharing.
    call _ANTIBODY_ANALYZER(
        filtered_feature_counts_matrix = self.filtered_matrices_h5,
        aggregate_barcodes = self.aggregate_barcodes,
        is_antibody        = true,
        is_spatial         = false,
        multi_graph        = self.multi_graph,
        sample_id          = self.sample_id,
    ) using (
        disabled = self.disable_antibody,
    )

    call _ANTIBODY_ANALYZER as _ANTIGEN_ANALYZER(
        filtered_feature_counts_matrix = self.filtered_matrices_h5,
        aggregate_barcodes = self.aggregate_barcodes,
        is_antibody        = false,
        is_spatial         = false,
        multi_graph        = self.multi_graph,
        sample_id          = self.sample_id,
    ) using (
        disabled = self.disable_antigen,
    )

    return (
        common_analyzer   = SC_RNA_ANALYZER.common_analyzer,
        crispr_analyzer   = _CRISPR_ANALYZER,
        antibody_analyzer = _ANTIBODY_ANALYZER,
        antigen_analyzer  = _ANTIGEN_ANALYZER,
        targeted_analyzer = _TARGETED_ANALYZER,
    )
}

###############################################################################
# Beam Analyzer
###############################################################################
stage CALCULATE_ANTIGEN_SPECIFICITY(
    in  h5     filtered_feature_counts_matrix,
    in  string beam_mode,
    in  csv    filtered_contig_annotations,
    in  csv    clonotypes_csv,
    in  map    count_gem_well_map,
    out csv    antigen_specificity_scores,
    out csv    antigen_assignment,
    out csv    clonotype_concordance,
    out csv    exact_subclonotype_concordance,
    out json   summary,
    src py     "stages/feature/antigen_specificity",
) using (
    mem_gb = 4,
)

stage CHOOSE_CLOUPE(
    in  cloupe      library_cloupe,
    in  map<cloupe> sample_cloupe,
    out cloupe      cloupe,
    src py          "stages/multi/choose_cloupe",
) using (
    volatile = strict,
)

pipeline MULTI_REPORTER(
    in  string                   sample_id,
    in  string                   sample_desc,
    in  FullPipelineConfig       config,
    in  string                   count_pipestance_type,
    in  csv                      feature_reference,
    in  path                     reference_path,
    in  map<ChemistryDef>        chemistry_defs,
    in  COUNT_GEM_WELL_PROCESSOR count_gw,
    in  bool                     include_introns,
    in  AnalyzerOutputs          count_analyzer,
    in  _CRISPR_ANALYZER         crispr_analyzer,
    in  _ANTIBODY_ANALYZER       antibody_analyzer,
    in  _ANTIBODY_ANALYZER       antigen_analyzer,
    in  _TARGETED_ANALYZER       targeted_analyzer,
    in  h5                       barcode_summary,
    in  csv                      filtered_barcodes,
    in  AssignTagsOuts           assign_tags_outs,
    in  json                     cloupe_cas_types,
    in  json                     cas_metrics,
    in  string                   cell_annotation_cloupe_name,
    in  bool                     disable_library_cloupe,
    in  map<cloupe>              sample_cloupe,
    out SUMMARIZE_REPORTS        count_summary,
    out cloupe                   cloupe,
    out json                     antibody_histograms,
    out json                     antigen_histograms,
    out json                     barcode_rank_plots,
    out json                     jibes_biplot_histogram,
    out json                     cmo_projection_plot,
)
{
    call SUMMARIZE_REPORTS(
        chemistry_defs               = self.chemistry_defs,
        summaries                    = [
            self.count_gw.basic_counter_outs.summary,
            self.count_analyzer.summary,
            self.crispr_analyzer.crispr_analysis_metrics,
            self.targeted_analyzer.targeted_analysis_metrics,
            self.assign_tags_outs.gem_well_inferred_throughputs,
            self.cas_metrics,
        ],
        sample_id                    = self.sample_id,
        sample_desc                  = self.sample_desc,
        reference_path               = self.reference_path,
        analysis                     = self.count_analyzer.analysis,
        barcode_summary_h5           = self.count_gw.basic_counter_outs.barcode_summary,
        filtered_gene_bc_matrices_h5 = self.count_gw.basic_counter_outs.filtered_gene_bc_matrices_h5,
        filtered_barcodes            = self.count_gw.basic_counter_outs.filtered_barcodes,
        target_panel_summary         = self.count_gw.target_outs.target_panel_summary,
        antibody_histograms          = self.antibody_analyzer.antibody_histograms_json,
        antibody_treemap             = self.antibody_analyzer.antibody_treemap_json,
        antigen_histograms           = self.antigen_analyzer.antibody_histograms_json,
        antigen_treemap              = self.antigen_analyzer.antibody_treemap_json,
        feature_reference            = self.feature_reference,
        target_set_name              = self.count_gw.target_outs.target_set_name,
        per_feature_metrics_csv      = self.targeted_analyzer.per_feature_metrics_csv,
        include_introns              = self.include_introns,
    ) using (
        disabled = self.config.disable_count,
    )

    call CLOUPE_PREPROCESS(
        pipestance_type              = self.count_pipestance_type,
        sample_id                    = self.sample_id,
        sample_desc                  = self.sample_desc,
        analysis                     = self.count_analyzer.analysis,
        filtered_gene_bc_matrices_h5 = self.count_gw.basic_counter_outs.filtered_gene_bc_matrices_h5,
        metrics_json                 = SUMMARIZE_REPORTS.metrics_summary_json,
        aggregation_csv              = null,
        gem_group_index_json         = null,
        image_page_names             = null,
        tissue_image_paths           = null,
        dark_images                  = null,
        tissue_positions             = null,
        fiducial_positions_list      = null,
        dzi_info                     = null,
        dzi_tiles_paths              = null,
        scale_factors_json           = null,
        no_secondary_analysis        = false,
        barcode_whitelist            = null,
        hd_slide_name                = null,
        loupe_map                    = null,
        product_type                 = "sc",
        cells_per_sample             = self.assign_tags_outs.sample_cell_barcodes,
        cells_per_tag                = self.assign_tags_outs.cells_per_tag,
        cells_per_protospacer        = self.crispr_analyzer.cells_per_protospacer,
        spatial_enrichment           = null,
        spatial_deconvolution_path   = null,
        disable_cloupe               = null,
    ) using (
        disabled = self.disable_library_cloupe,
    )

    call APPEND_CELL_TYPES_CLOUPE(
        sample_cloupe    = CLOUPE_PREPROCESS.output_for_cloupe,
        cloupe_cas_types = self.cloupe_cas_types,
        cas_track_name   = self.cell_annotation_cloupe_name,
    )

    call CHOOSE_CLOUPE(
        library_cloupe = APPEND_CELL_TYPES_CLOUPE.cell_annotation_sample_cloupe,
        sample_cloupe  = self.sample_cloupe,
    ) using (
        disabled = self.config.disable_count,
    )

    call GENERATE_LIBRARY_PLOTS(
        # The stage needs to be disabled for vdj only multi
        disable_count         = self.config.disable_count,
        # pared-down data needed for barcode rank plot
        barcode_summary_h5    = self.barcode_summary,
        filtered_barcodes     = self.filtered_barcodes,
        reference_path        = self.reference_path,
        # for jibes biplot/cmo UMAP
        tag_assigner_pickle   = self.assign_tags_outs.tag_assigner_pickle,
        cells_per_tag         = self.assign_tags_outs.cells_per_tag,
        non_singlet_barcodes  = self.assign_tags_outs.non_singlet_barcodes,
        force_sample_barcodes = self.assign_tags_outs.force_sample_barcodes,
        analysis              = self.count_analyzer.analysis,
        multiplexing_method   = self.assign_tags_outs.multiplexing_method,
    ) using (
        disabled = self.config.disable_multi_count,
    )

    return (
        count_summary          = SUMMARIZE_REPORTS,
        cloupe                 = CHOOSE_CLOUPE.cloupe,
        antibody_histograms    = self.antibody_analyzer.antibody_histograms_json,
        antigen_histograms     = self.antigen_analyzer.antibody_histograms_json,
        barcode_rank_plots     = GENERATE_LIBRARY_PLOTS.library_to_barcode_rank,
        jibes_biplot_histogram = GENERATE_LIBRARY_PLOTS.jibes_biplot_histogram,
        cmo_projection_plot    = GENERATE_LIBRARY_PLOTS.cmo_projection_plot,
    )
}

stage GENERATE_LIBRARY_PLOTS(
    in  bool               disable_count,
    # for barcode rank
    in  h5                 barcode_summary_h5,
    in  csv                filtered_barcodes,
    in  path               reference_path,
    # for jibes biplot
    in  pickle             tag_assigner_pickle,
    # for cmo UMAP plot
    in  path               analysis,
    in  json               cells_per_tag,
    in  json               non_singlet_barcodes,
    in  BarcodeAssignments force_sample_barcodes,
    in  string             multiplexing_method,
    out json               library_to_barcode_rank,
    out json               jibes_biplot_histogram,
    out json               cmo_projection_plot,
    src py                 "stages/multi/generate_library_plots",
) using (
    mem_gb   = 8,
    volatile = strict,
)

stage GENERATE_SAMPLE_PLOTS(
    in  h5   matrices_h5,
    in  h5   raw_matrices_h5,
    in  path analysis,
    in  h5   barcode_summary,
    out json sample_projection_plots,
    out json sample_library_to_barcode_rank,
    out json sample_treemap_plots,
    src py   "stages/multi/generate_sample_plots",
) split (
) using (
    volatile = strict,
)

stage DISABLE_MULTI_CORE_STAGES(
    in  bool is_pd,
    in  bool disable_gex,
    in  bool disable_multi_count,
    in  bool disable_count,
    in  bool no_secondary_analysis,
    in  bool skip_cell_annotation,
    out bool disable_sample_cas_celltyping,
    out bool disable_library_cas_celltyping,
    src py   "stages/multi/disable_multi_core_stages",
) using (
    volatile = strict,
)

pipeline SAMPLE_REPORTER(
    in  SampleSlfeOuts     sample_outs,
    in  string             sample_id,
    in  string             sample_desc,
    in  FullPipelineConfig config,
    in  string             count_pipestance_type,
    in  AnalyzerOutputs    count_analyzer,
    in  _CRISPR_ANALYZER   crispr_analyzer,
    in  _TARGETED_ANALYZER targeted_analyzer,
    in  path               reference_path,
    in  json               cloupe_cas_types,
    in  h5                 barcode_summary,
    in  CellCalling        cell_calling_config,
    in  json               sample_assignment_metrics,
    in  tps.json           target_panel_summary,
    in  json               cells_per_sample,
    in  string             cell_annotation_cloupe_name,
    in  json               cells_per_tag,
    out json               metrics_summary,
    out cloupe             cloupe,
    out json               sample_projection_plots,
    out json               sample_library_to_barcode_rank,
    out json               sample_treemap_plots,
)
{
    call _SAMPLE_CELLS_REPORTER(
        sample                    = self.sample_outs.sample,
        molecule_info             = self.sample_outs.molecule_info,
        reference_path            = self.reference_path,
        recovered_cells           = self.cell_calling_config.recovered_cells,
        matrices_h5               = self.sample_outs.raw_matrix_h5,
        matrix_computer_summary   = self.sample_outs.metrics_summary,
        filtered_barcodes         = self.sample_outs.filtered_barcodes,
        per_barcode_metrics       = self.sample_outs.per_barcode_metrics,
        barcode_summary           = self.barcode_summary,
        sample_assignment_metrics = self.sample_assignment_metrics,
        count_analyzer_metrics    = self.count_analyzer.summary,
        targeted_analyzer_metrics = self.targeted_analyzer.targeted_analysis_metrics,
        crispr_analyzer_metrics   = self.crispr_analyzer.crispr_analysis_metrics,
        target_panel_summary      = self.target_panel_summary,
    )

    call GENERATE_SAMPLE_PLOTS(
        matrices_h5     = self.sample_outs.filtered_matrix_h5,
        raw_matrices_h5 = self.sample_outs.raw_matrix_h5,
        analysis        = self.count_analyzer.analysis,
        barcode_summary = self.barcode_summary,
    )

    call CLOUPE_PREPROCESS(
        pipestance_type              = self.count_pipestance_type,
        sample_id                    = self.sample_id,
        sample_desc                  = self.sample_desc,
        analysis                     = self.count_analyzer.analysis,
        filtered_gene_bc_matrices_h5 = self.sample_outs.filtered_matrix_h5,
        metrics_json                 = _SAMPLE_CELLS_REPORTER.summary,
        aggregation_csv              = null,
        gem_group_index_json         = null,
        image_page_names             = null,
        tissue_image_paths           = null,
        dark_images                  = null,
        tissue_positions             = null,
        fiducial_positions_list      = null,
        dzi_info                     = null,
        dzi_tiles_paths              = null,
        scale_factors_json           = null,
        no_secondary_analysis        = false,
        barcode_whitelist            = null,
        hd_slide_name                = null,
        loupe_map                    = null,
        product_type                 = "sc",
        cells_per_sample             = self.cells_per_sample,
        cells_per_tag                = self.cells_per_tag,
        cells_per_protospacer        = self.crispr_analyzer.cells_per_protospacer,
        spatial_enrichment           = null,
        spatial_deconvolution_path   = null,
        disable_cloupe               = null,
    ) using (
        disabled = self.config.disable_count,
    )

    call APPEND_CELL_TYPES_CLOUPE(
        sample_cloupe    = CLOUPE_PREPROCESS.output_for_cloupe,
        cloupe_cas_types = self.cloupe_cas_types,
        cas_track_name   = self.cell_annotation_cloupe_name,
    )

    return (
        metrics_summary         = _SAMPLE_CELLS_REPORTER.summary,
        cloupe                  = APPEND_CELL_TYPES_CLOUPE.cell_annotation_sample_cloupe,
        sample_projection_plots = GENERATE_SAMPLE_PLOTS.sample_projection_plots,
        sample_library_to_barcode_rank = GENERATE_SAMPLE_PLOTS.sample_library_to_barcode_rank,
        sample_treemap_plots    = GENERATE_SAMPLE_PLOTS.sample_treemap_plots,
    )
}

stage STRUCTIFY_PER_SAMPLE_OUTS(
    in  SampleBamFile[]      sample_bams,
    in  SampleMetrics[]      sample_metrics,
    in  SampleMoleculeInfo[] sample_molecule_infos,
    in  SampleMatrices[]     sample_matrices,
    in  json                 multi_graph,
    in  csv                  feature_reference,
    in  csv                  target_panel,
    in  csv                  probe_set,
    out map<SampleSlfeOuts>  sample_outs,
    out bam                  unassigned_alignments,
    out bam.bai              unassigned_alignments_bai_index,
    out bam.csi              unassigned_alignments_csi_index,
    src py                   "stages/multi/structify_per_sample_outs",
) using (
    volatile = false,
)

stage SANITIZE_MAP_CALLS(
    in  json              multi_graph,
    in  map<path>         in_crispr_analysis,
    in  map<path>         in_rna_analysis,
    in  map<cloupe>       in_cloupe_file,
    in  map<json>         in_metrics_summary,
    in  map<json>         in_sample_projection_plots,
    in  map<json>         in_sample_barcode_rank_plots,
    in  map<json>         in_sample_treemap_plots,
    in  map<VDJ_ANALYZER> in_vdj_t_analyzer,
    in  map<VDJ_ANALYZER> in_vdj_t_gd_analyzer,
    in  map<VDJ_ANALYZER> in_vdj_b_analyzer,
    out map<path>         crispr_analysis,
    out map<path>         rna_analysis,
    out map<cloupe>       cloupe_file,
    out map<json>         metrics_summary,
    out map<json>         sample_projection_plots,
    out map<json>         sample_barcode_rank_plots,
    out map<json>         sample_treemap_plots,
    out map<VDJ_ANALYZER> vdj_t_analyzer,
    out map<VDJ_ANALYZER> vdj_t_gd_analyzer,
    out map<VDJ_ANALYZER> vdj_b_analyzer,
    src py                "stages/multi/sanitize_map_calls",
) using (
    volatile = false,
) retain (
    metrics_summary,
)

stage BUILD_SAMPLE_OUTS(
    in  SampleSlfeOuts      sample_slfe_outs,
    in  path                rna_analysis,
    in  path                crispr_analysis,
    in  cloupe              cloupe,
    in  html                web_summary,
    in  csv                 metrics_summary_csv,
    in  VdjOutputsCS        vdj_b_outs,
    in  VdjOutputsCS        vdj_t_outs,
    in  VdjOutputsCS        vdj_t_gd_outs,
    in  bool                output_per_sample_raw_matrix,
    in  BeamAnalyzerOutputs beam_analyzer,
    in  CellTypes           cell_types,
    out SampleOutputsCS     sample_outs,
    src py                  "stages/multi/build_sample_outs",
)

#
# @include "sc_vdj_aggregator.mro"
#

stage BUILD_AGGR_WEB_SUMMARY(
    in  json content,
    in  json diversity_chart,
    out json web_summary_data,
    src py   "stages/vdj/build_aggr_web_summary",
)

stage BUILD_ANTIGEN_AGGR_WEB_SUMMARY(
    in  json antigen_histograms_json,
    in  json antigen_treemap_json,
    in  json clonotype_clustermap_json,
    out json antigen_tab,
    src py   "stages/aggregator/build_antigen_aggr_web_summary",
)

stage CALCULATE_CLONOTYPE_DIVERSITY(
    in  json per_origin_clonotype_hist,
    out json plotly_diversity_chart,
    src py   "stages/vdj/clonotype_diversity",
)

pipeline ANTIGEN_AGGR(
    in  bool               disable_antigen_aggr,
    in  h5                 filtered_feature_counts_matrix,
    in  string             beam_mode,
    in  csv                filtered_contig_annotations,
    in  csv                clonotypes_csv,
    in  map                count_gem_well_map,
    out AntigenAggrResults antigen_analysis,
    out json               antigen_aggr_web_summary_data,
)
{
    call CALCULATE_ANTIGEN_SPECIFICITY(
        * = self,
    ) using (
        disabled = self.disable_antigen_aggr,
    )

    call CREATE_BARCODE_CSV(
        gex_filtered_matrix      = self.filtered_feature_counts_matrix,
        vdj_filtered_annotations = self.filtered_contig_annotations,
        count_gem_well_map       = self.count_gem_well_map,
    ) using (
        disabled = self.disable_antigen_aggr,
    )

    call _ANTIBODY_ANALYZER as _ANTIGEN_ANALYZER(
        filtered_feature_counts_matrix = self.filtered_feature_counts_matrix,
        aggregate_barcodes = null,
        is_antibody        = false,
        is_spatial         = false,
        multi_graph        = null,
        sample_id          = null,
    ) using (
        disabled = self.disable_antigen_aggr,
    )

    call CREATE_CLONOTYPE_CLUSTERMAP(
        antigen_specificity = CALCULATE_ANTIGEN_SPECIFICITY.antigen_specificity_scores,
    ) using (
        disabled = self.disable_antigen_aggr,
    )

    call BUILD_ANTIGEN_AGGR_WEB_SUMMARY(
        antigen_histograms_json   = _ANTIGEN_ANALYZER.antibody_histograms_json,
        antigen_treemap_json      = _ANTIGEN_ANALYZER.antibody_treemap_json,
        clonotype_clustermap_json = CREATE_CLONOTYPE_CLUSTERMAP.antigen_clonotype_clustermap,
    ) using (
        disabled = self.disable_antigen_aggr,
    )

    return (
        antigen_analysis              = {
            antigen_specificity_scores: CALCULATE_ANTIGEN_SPECIFICITY.antigen_specificity_scores,
            per_barcode_csv:            CREATE_BARCODE_CSV.per_barcode_csv,
        },
        antigen_aggr_web_summary_data = BUILD_ANTIGEN_AGGR_WEB_SUMMARY.antigen_tab,
    )
}

pipeline SC_VDJ_AGGREGATOR(
    in  VdjAggrInput       aggr_input,
    in  map                count_gem_well_map,
    in  h5                 filtered_feature_bc_matrix_h5,
    in  csv                feature_reference,
    in  string             sample_id,
    in  string             sample_desc,
    in  string             beam_mode,
    in  map<string>        antigen_specificity_controls,
    in  bool               disable_antigen_aggr,
    in  bool               disable_count_aggr,
    in  bool               mix_donors,
    in  FilterSwitch       filter_switch,
    out pb                 enclone_output,
    out json               barcode_fate,
    out string             receptor,
    out csv                clonotypes,
    out fa                 donor_ref_fa,
    out fasta              consensus_fasta,
    out path               vdj_reference_path,
    out csv                filt_ann_csv,
    out csv                consensus_ann_csv,
    out json               web_summary_data,
    out vloupe             vloupe,
    out AntigenAggrResults antigen_analysis,
    out json               antigen_aggr_web_summary_data,
    out tsv                airr_rearrangement,
    out html               filter_summary,
)
{
    call PROCESS_VDJ_PROTO(
        libraries          = self.aggr_input.libraries,
        count_gem_well_map = self.count_gem_well_map,
    )

    call SETUP_VDJ_AGGR(
        libraries    = self.aggr_input.libraries,
        gem_well_map = PROCESS_VDJ_PROTO.gem_well_map,
        receptor     = PROCESS_VDJ_PROTO.receptor,
    )

    call RUN_ENCLONE_AGGR(
        mix_donors            = self.mix_donors,
        filter_switch         = self.filter_switch,
        contig_ann_json_files = SETUP_VDJ_AGGR.contig_ann_json_files,
        enclone_input_csv     = SETUP_VDJ_AGGR.enclone_input_csv,
        enclone_gem_well_meta = SETUP_VDJ_AGGR.enclone_gem_well_meta,
        vdj_reference_path    = SETUP_VDJ_AGGR.vdj_reference_path,
    )

    call WRITE_CONSENSUS_TXT(
        sample_id      = null,
        enclone_output = RUN_ENCLONE_AGGR.enclone_output,
    )

    call WRITE_CLONOTYPE_OUTS(
        sample_id      = null,
        receptor       = PROCESS_VDJ_PROTO.receptor,
        enclone_output = RUN_ENCLONE_AGGR.enclone_output,
    )

    call FILL_CLONOTYPE_INFO(
        sample_id          = null,
        contig_annotations = SETUP_VDJ_AGGR.combined_ann_json,
        enclone_output     = RUN_ENCLONE_AGGR.enclone_output,
    )

    call WRITE_CONCAT_REF_OUTS(
        sample_id                   = null,
        all_contig_annotations_json = FILL_CLONOTYPE_INFO.all_contig_annotations_json,
        enclone_output              = RUN_ENCLONE_AGGR.enclone_output,
    )

    call CREATE_AIRR_TSV(
        contig_annotations = FILL_CLONOTYPE_INFO.all_contig_annotations_json,
        concat_ref_fasta   = WRITE_CONCAT_REF_OUTS.concat_ref_fasta,
        gem_well_map       = PROCESS_VDJ_PROTO.gem_well_map,
    )

    call WRITE_ANN_CSV(
        all_contig_annotations_json = FILL_CLONOTYPE_INFO.all_contig_annotations_json,
    )

    call WRITE_AGGR_ANN(
        enclone_gem_well_meta = SETUP_VDJ_AGGR.enclone_gem_well_meta,
        annotation_csv        = WRITE_ANN_CSV.filtered_contig_annotations_csv,
    )

    call WRITE_WEB_SUMMARY_JSON(
        libraries             = self.aggr_input.libraries,
        enclone_gem_well_meta = SETUP_VDJ_AGGR.enclone_gem_well_meta,
        enclone_output        = RUN_ENCLONE_AGGR.enclone_output,
        sample_id             = self.sample_id,
        sample_desc           = self.sample_desc,
        clonotypes_csv        = WRITE_CLONOTYPE_OUTS.clonotypes_csv,
        receptor              = PROCESS_VDJ_PROTO.receptor,
        vdj_reference_path    = SETUP_VDJ_AGGR.vdj_reference_path,
    )

    call ANTIGEN_AGGR(
        disable_antigen_aggr        = self.disable_antigen_aggr,
        filtered_feature_counts_matrix = self.filtered_feature_bc_matrix_h5,
        beam_mode                   = self.beam_mode,
        filtered_contig_annotations = WRITE_ANN_CSV.filtered_contig_annotations_csv,
        clonotypes_csv              = WRITE_CLONOTYPE_OUTS.clonotypes_csv,
        count_gem_well_map          = self.count_gem_well_map,
    ) using (
        disabled = self.disable_count_aggr,
    )

    call CALCULATE_CLONOTYPE_DIVERSITY(
        per_origin_clonotype_hist = WRITE_WEB_SUMMARY_JSON.per_origin_hist,
    )

    call BUILD_AGGR_WEB_SUMMARY(
        content         = WRITE_WEB_SUMMARY_JSON.web_summary_content,
        diversity_chart = CALCULATE_CLONOTYPE_DIVERSITY.plotly_diversity_chart,
    )

    call SUMMARIZE_VDJ_FILTERS(
        sample_id              = self.sample_id,
        sample_description     = self.sample_desc,
        all_contig_annotations = FILL_CLONOTYPE_INFO.all_contig_annotations_json,
        asm_filter_diagnostics = null,
        enclone_barcode_fate   = RUN_ENCLONE_AGGR.barcode_fate,
        raw_matrix_h5          = null,
    )

    call VLOUPE_PREPROCESS(
        pipestance_type              = "SC_VDJ_AGGREGATOR",
        sample_id                    = self.sample_id,
        sample_desc                  = self.sample_desc,
        enclone_output               = RUN_ENCLONE_AGGR.enclone_output,
        disable_vloupe               = false,
        beam_mode                    = self.beam_mode,
        feature_reference            = self.feature_reference,
        feature_barcode_matrix       = self.filtered_feature_bc_matrix_h5,
        antigen_specificity_scores   = ANTIGEN_AGGR.antigen_analysis.antigen_specificity_scores,
        antigen_specificity_controls = self.antigen_specificity_controls,
    )

    return (
        enclone_output                = RUN_ENCLONE_AGGR.enclone_output,
        barcode_fate                  = RUN_ENCLONE_AGGR.barcode_fate,
        receptor                      = PROCESS_VDJ_PROTO.receptor,
        clonotypes                    = WRITE_CLONOTYPE_OUTS.clonotypes_csv,
        donor_ref_fa                  = RUN_ENCLONE_AGGR.donor_ref_fa,
        consensus_fasta               = WRITE_CONSENSUS_TXT.consensus_fasta,
        vdj_reference_path            = SETUP_VDJ_AGGR.vdj_reference_path,
        filt_ann_csv                  = WRITE_AGGR_ANN.augmented_annotation_csv,
        consensus_ann_csv             = WRITE_CONSENSUS_TXT.consensus_annotations_csv,
        web_summary_data              = BUILD_AGGR_WEB_SUMMARY.web_summary_data,
        antigen_aggr_web_summary_data = ANTIGEN_AGGR.antigen_aggr_web_summary_data,
        vloupe                        = VLOUPE_PREPROCESS.output_for_vloupe,
        antigen_analysis              = ANTIGEN_AGGR.antigen_analysis,
        airr_rearrangement            = CREATE_AIRR_TSV.airr_annotations,
        filter_summary                = SUMMARIZE_VDJ_FILTERS.filter_summary,
    )
}

#
# @include "rna/sc_rna_aggregator_cs.mro"
#

stage BUILD_COMBINED_WEB_SUMMARY(
    in  json vdj_t_data,
    in  json vdj_b_data,
    in  json vdj_t_gd_data,
    in  json count_data,
    in  json antigen_data,
    out html web_summary,
    out json web_summary_data,
    src py   "stages/aggregator/build_combined_web_summary",
)

pipeline COUNT_AGGR(
    in  string           sample_id,
    in  string           sample_desc,
    in  map[]            sample_defs,
    in  csv              aggregation_csv,
    in  string           normalization_mode,
    in  bool             no_secondary_analysis,
    in  int              min_crispr_umi_threshold,
    in  bool             is_pd,
    in  bool             enable_tsne,
    out map              gem_group_index,
    out CountAggrOutputs aggr_outputs,
    out json             ws_data,
)
{
    call SC_RNA_AGGREGATOR(
        sample_id                = self.sample_id,
        sample_desc              = self.sample_desc,
        sample_defs              = self.sample_defs,
        normalization_mode       = self.normalization_mode,
        no_secondary_analysis    = self.no_secondary_analysis,
        min_crispr_umi_threshold = self.min_crispr_umi_threshold,
        num_analysis_bcs         = null,
        num_pca_bcs              = null,
        num_pca_genes            = null,
        num_principal_comps      = null,
        cbc_knn                  = null,
        cbc_alpha                = null,
        cbc_sigma                = null,
        cbc_realign_panorama     = null,
        max_clusters             = null,
        graphclust_neighbors     = null,
        neighbor_a               = null,
        neighbor_b               = null,
        tsne_perplexity          = null,
        tsne_input_pcs           = null,
        tsne_theta               = null,
        random_seed              = null,
        tsne_max_dims            = null,
        tsne_max_iter            = null,
        tsne_stop_lying_iter     = null,
        tsne_mom_switch_iter     = null,
        product_type             = "sc",
        is_pd                    = self.is_pd,
        enable_tsne              = self.enable_tsne,
    )

    call CLOUPE_PREPROCESS(
        pipestance_type              = "SC_RNA_AGGREGATOR_CS",
        sample_id                    = self.sample_id,
        sample_desc                  = self.sample_desc,
        analysis                     = SC_RNA_AGGREGATOR.analysis,
        filtered_gene_bc_matrices_h5 = SC_RNA_AGGREGATOR.filtered_gene_bc_matrices_h5,
        metrics_json                 = SC_RNA_AGGREGATOR.summary,
        aggregation_csv              = self.aggregation_csv,
        gem_group_index_json         = SC_RNA_AGGREGATOR.gem_group_index_json,
        image_page_names             = null,
        tissue_image_paths           = null,
        dark_images                  = null,
        tissue_positions             = null,
        fiducial_positions_list      = null,
        dzi_info                     = null,
        dzi_tiles_paths              = null,
        scale_factors_json           = null,
        no_secondary_analysis        = self.no_secondary_analysis,
        barcode_whitelist            = null,
        hd_slide_name                = null,
        loupe_map                    = null,
        product_type                 = "sc",
        cells_per_sample             = null,
        cells_per_tag                = null,
        cells_per_protospacer        = SC_RNA_AGGREGATOR.cells_per_protospacer,
        spatial_enrichment           = null,
        spatial_deconvolution_path   = null,
        disable_cloupe               = null,
    )

    return (
        gem_group_index = SC_RNA_AGGREGATOR.gem_group_index,
        ws_data         = SC_RNA_AGGREGATOR.web_summary_data,
        aggr_outputs    = {
            analysis:                      SC_RNA_AGGREGATOR.analysis_csv,
            antigen_specificity_controls:  SC_RNA_AGGREGATOR.antigen_specificity_controls,
            beam_mode:                     SC_RNA_AGGREGATOR.beam_mode,
            cloupe:                        CLOUPE_PREPROCESS.output_for_cloupe,
            crispr_analysis:               SC_RNA_AGGREGATOR.crispr_analysis,
            disable_antigen_aggr:          SC_RNA_AGGREGATOR.disable_antigen_aggr,
            feature_reference:             SC_RNA_AGGREGATOR.feature_reference,
            filtered_feature_bc_matrix:    SC_RNA_AGGREGATOR.filtered_gene_bc_matrices_mex,
            filtered_feature_bc_matrix_h5: SC_RNA_AGGREGATOR.filtered_gene_bc_matrices_h5,
            summary:                       SC_RNA_AGGREGATOR.summary,
        },
    )
}

pipeline VDJ_AGGR(
    in  VdjAggrInput[]     aggr_inputs,
    in  map                gem_group_index,
    in  h5                 filtered_feature_bc_matrix_h5,
    in  csv                feature_reference,
    in  string             sample_id,
    in  string             sample_desc,
    in  string             beam_mode,
    in  bool               disable_antigen_aggr,
    in  bool               disable_count_aggr,
    in  bool               mix_donors,
    in  FilterSwitch       filter_switch,
    in  map<string>        antigen_specificity_controls,
    out VdjAggrOutputsPD   vdj_t_outputs,
    out json               vdj_t_ws_data,
    out VdjAggrOutputsPD   vdj_t_gd_outputs,
    out json               vdj_t_gd_ws_data,
    out VdjAggrOutputsPD   vdj_b_outputs,
    out json               vdj_b_ws_data,
    out VdjRefFolder       vdj_reference,
    out AntigenAggrResults antigen_analysis,
    out json               antigen_aggr_web_summary_data,
)
{
    map call SC_VDJ_AGGREGATOR(
        aggr_input                    = split self.aggr_inputs,
        count_gem_well_map            = self.gem_group_index,
        filtered_feature_bc_matrix_h5 = self.filtered_feature_bc_matrix_h5,
        feature_reference             = self.feature_reference,
        sample_id                     = self.sample_id,
        sample_desc                   = self.sample_desc,
        beam_mode                     = self.beam_mode,
        antigen_specificity_controls  = self.antigen_specificity_controls,
        disable_antigen_aggr          = self.disable_antigen_aggr,
        disable_count_aggr            = self.disable_count_aggr,
        filter_switch                 = self.filter_switch,
        mix_donors                    = self.mix_donors,
    )

    call MATCH_VDJ_AGGR_OUTS(
        receptors                  = SC_VDJ_AGGREGATOR.receptor,
        clonotypes                 = SC_VDJ_AGGREGATOR.clonotypes,
        donor_ref_fas              = SC_VDJ_AGGREGATOR.donor_ref_fa,
        consensus_fastas           = SC_VDJ_AGGREGATOR.consensus_fasta,
        vdj_reference_paths        = SC_VDJ_AGGREGATOR.vdj_reference_path,
        filtered_contig_annotations_csvs = SC_VDJ_AGGREGATOR.filt_ann_csv,
        consensus_annotations_csvs = SC_VDJ_AGGREGATOR.consensus_ann_csv,
        web_summary_data           = SC_VDJ_AGGREGATOR.web_summary_data,
        vloupes                    = SC_VDJ_AGGREGATOR.vloupe,
        antigen_analysis           = SC_VDJ_AGGREGATOR.antigen_analysis,
        antigen_aggr_web_summary_data_in = SC_VDJ_AGGREGATOR.antigen_aggr_web_summary_data,
        airr_rearrangements        = SC_VDJ_AGGREGATOR.airr_rearrangement,
        filter_summaries           = SC_VDJ_AGGREGATOR.filter_summary,
        enclone_outputs            = SC_VDJ_AGGREGATOR.enclone_output,
    )

    call COPY_VDJ_REFERENCE(
        vdj_reference_path = MATCH_VDJ_AGGR_OUTS.vdj_reference_path,
    )

    return (
        vdj_t_outputs                 = MATCH_VDJ_AGGR_OUTS.vdj_t_results,
        vdj_t_ws_data                 = MATCH_VDJ_AGGR_OUTS.vdj_t_results.web_summary_data,
        vdj_t_gd_outputs              = MATCH_VDJ_AGGR_OUTS.vdj_t_gd_results,
        vdj_t_gd_ws_data              = MATCH_VDJ_AGGR_OUTS.vdj_t_gd_results.web_summary_data,
        vdj_b_outputs                 = MATCH_VDJ_AGGR_OUTS.vdj_b_results,
        vdj_b_ws_data                 = MATCH_VDJ_AGGR_OUTS.vdj_b_results.web_summary_data,
        vdj_reference                 = COPY_VDJ_REFERENCE.vdj_reference,
        antigen_analysis              = MATCH_VDJ_AGGR_OUTS.antigen_results,
        antigen_aggr_web_summary_data = MATCH_VDJ_AGGR_OUTS.antigen_aggr_web_summary_data,
    )
}

pipeline SC_RNA_AGGREGATOR_CS(
    in  string             sample_id,
    in  string             sample_desc,
    in  path               pipestance_root,
    in  csv                aggregation_csv,
    in  string             normalization_mode,
    in  bool               no_secondary_analysis,
    in  int                min_crispr_umi_threshold,
    in  bool               enable_tsne,
    out csv                aggregation_csv           "Copy of the input aggregation CSV"  "aggregation.csv",
    out html               web_summary               "Aggregation metrics summary HTML",
    out CountAggrOutputs   count,
    out VdjAggrOutputsCS   vdj_t,
    out VdjAggrOutputsCS   vdj_t_gd,
    out VdjAggrOutputsCS   vdj_b,
    out VdjRefFolder       vdj_reference             "V(D)J reference",
    out AntigenAggrResults antigen_analysis,
)
{
    call PARSE_AGGR_CSV(
        pipestance_root = self.pipestance_root,
        aggregation_csv = self.aggregation_csv,
    )

    call COUNT_AGGR(
        sample_id                = self.sample_id,
        sample_desc              = self.sample_desc,
        sample_defs              = PARSE_AGGR_CSV.count_libraries,
        aggregation_csv          = self.aggregation_csv,
        normalization_mode       = self.normalization_mode,
        no_secondary_analysis    = self.no_secondary_analysis,
        min_crispr_umi_threshold = self.min_crispr_umi_threshold,
        is_pd                    = false,
        enable_tsne              = self.enable_tsne,
    ) using (
        disabled = PARSE_AGGR_CSV.disable_count_aggr,
    )

    call VDJ_AGGR(
        aggr_inputs                   = PARSE_AGGR_CSV.vdj_aggr_inputs,
        gem_group_index               = COUNT_AGGR.gem_group_index,
        filtered_feature_bc_matrix_h5 = COUNT_AGGR.aggr_outputs.filtered_feature_bc_matrix_h5,
        feature_reference             = COUNT_AGGR.aggr_outputs.feature_reference,
        sample_id                     = self.sample_id,
        sample_desc                   = self.sample_desc,
        mix_donors                    = false,
        beam_mode                     = COUNT_AGGR.aggr_outputs.beam_mode,
        antigen_specificity_controls  = COUNT_AGGR.aggr_outputs.antigen_specificity_controls,
        disable_antigen_aggr          = COUNT_AGGR.aggr_outputs.disable_antigen_aggr,
        disable_count_aggr            = PARSE_AGGR_CSV.disable_count_aggr,
        filter_switch                 = {
            asm_shared_contig:     true,
            enclone_cross:         true,
            enclone_multiplet:     true,
            enclone_shared_contig: true,
            enclone_umi:           true,
        },
    ) using (
        disabled = PARSE_AGGR_CSV.disable_vdj_aggr,
    )

    call BUILD_COMBINED_WEB_SUMMARY(
        vdj_t_data    = VDJ_AGGR.vdj_t_ws_data,
        vdj_t_gd_data = VDJ_AGGR.vdj_t_gd_ws_data,
        vdj_b_data    = VDJ_AGGR.vdj_b_ws_data,
        count_data    = COUNT_AGGR.ws_data,
        antigen_data  = VDJ_AGGR.antigen_aggr_web_summary_data,
    )

    return (
        aggregation_csv  = PARSE_AGGR_CSV.aggregation_csv,
        web_summary      = BUILD_COMBINED_WEB_SUMMARY.web_summary,
        count            = COUNT_AGGR.aggr_outputs,
        vdj_t            = VDJ_AGGR.vdj_t_outputs,
        vdj_t_gd         = VDJ_AGGR.vdj_t_gd_outputs,
        vdj_b            = VDJ_AGGR.vdj_b_outputs,
        vdj_reference    = VDJ_AGGR.vdj_reference,
        antigen_analysis = VDJ_AGGR.antigen_analysis,
    )
}

#
# @include "__cellranger-aggr-Aiptasia.mro"
#

call SC_RNA_AGGREGATOR_CS(
    sample_id                = "cellranger-aggr-Aiptasia",
    sample_desc              = "aggr-Aiptasia",
    pipestance_root          = "/oak/stanford/groups/akundaje/marinovg/various/2025-02-27-Tingting-scRNA-seq",
    aggregation_csv          = "/oak/stanford/groups/akundaje/marinovg/various/2025-02-27-Tingting-scRNA-seq/cellranger-aggr-Aiptasia.csv",
    normalization_mode       = "mapped",
    no_secondary_analysis    = false,
    min_crispr_umi_threshold = 3,
    enable_tsne              = false,
)
