NfcoreCitationUtils
Citation management and topic channel utilities for nf-core pipelines.
Overview
The NfcoreCitationUtils utility provides comprehensive citation management functionality for nf-core pipelines, supporting both traditional file-based approaches and modern topic channel patterns. This utility enables progressive migration from legacy meta.yml citation files to the more efficient and accurate topic channel system.
Topic Channel Migration Strategy for Citations
Similar to version management, the citation system supports a three-phase migration approach:
- Legacy Stage: Traditional meta.yml file processing with manual collection
- Transition Stage: Mixed approach supporting both files and topic channels
- Modern Stage: Pure topic channel approach with automatic runtime citation collection
This migration strategy ensures backward compatibility while enabling pipelines to benefit from runtime-accurate citations where only tools that actually execute are included in the final reports.
Available Functions
Citations on the Fly (Recommended)
The lowest-friction way to make citations reflect the tools a pipeline actually ran — generated on the fly from the run itself, not from a hand-maintained list. It requires no per-module changes: every nf-core module already emits a [process, tool, version] tuple to the versions topic when it executes, so that topic is the list of tools that ran. These functions intersect that list with citation metadata parsed from each module’s meta.yml, so only tools that actually executed are cited.
Two functions cover the flow:
| Function | Purpose | When to call |
|---|---|---|
toolsFromVersionsTopic(topicVersions) |
Reduce collected versions data to the tool names used |
Workflow completion |
citationsOnTheFly(topicVersions, metaFilePaths) |
Citations for only the tools that ran, from meta.yml |
Workflow completion |
citationsOnTheFly()
citationsOnTheFly(List topicVersions, List<String> metaFilePaths)
Description:
Builds a citations map for only the tools that ran, by intersecting the versions topic (what executed) with citations parsed from the supplied meta.yml files (the citation source). The returned map plugs directly into toolCitationText(), toolBibliographyText(), and methodsDescriptionText().
Each short citation is formatted for direct copy-paste into a methods paragraph — tool (version, Author et al. year) — with the version taken from the versions topic and the author/year from meta.yml. The reference segment degrades gracefully: short author + year → doi: … → homepage url → description. So richer meta.yml files produce richer citations:
meta.yml fields present |
Short citation |
|---|---|
author, year |
fastqc (0.12.1, Andrews 2010) |
author (multiple), year |
samtools (1.21, Danecek et al. 2021) |
doi only |
multiqc (1.21, doi: 10.1093/bioinformatics/btw354) |
homepage only |
seqtk (1.4, https://github.com/lh3/seqtk) |
The full bibliography entry (author, year, title, journal, doi, url) is unchanged.
Function Signature:
Map citationsOnTheFly(List topicVersions, List<String> metaFilePaths)Parameters:
topicVersions(List): Collectedversionstopic data —[process, tool, version]tuples. Collect with.collect(flat: false)so the tuples are preserved (plain.collect()flattens them, and the function logs a warning if it receives flattened data).metaFilePaths(List): Paths to module meta.ymlfiles to resolve citation metadata from.extraTools(List, optional): Tool names to cite even though they never emit a version — e.g. a MultiQC plugin like multiqcsav. They’re resolved frommeta.ymllike everything else (with no version segment), so you can still add a tool by hand:citationsOnTheFly(versions, meta_yml_paths, ['multiqcsav']).
Returns:
Map: Citations keyed by tool name ([citation: ..., bibliography: ...]), restricted to tools that ran.
Tool names from the versions topic are matched against meta.yml tool keys exactly first, then case-insensitively (so FastQC lines up with fastqc). A tool that ran but has no matching meta.yml citation entry is logged as a warning — add it to that module’s meta.yml tools: section to silence it.
Pipeline Integration (utils_nfcore_<pipeline>_pipeline/main.nf):
This is the reproducible replacement for a hand-maintained toolReferencesMap and a static toolCitationText() / toolBibliographyText(). Wire it into the methods-description channel:
include { citationsOnTheFly; methodsDescriptionText } from 'plugin/nf-core-utils'
// Resolve every module meta.yml once (citation metadata source).// files() returns an empty list when nothing matches the glob.def meta_yml_paths = files("${projectDir}/modules/**/meta.yml").collect { it.toString() }
ch_methods_description = channel.topic('versions') // every module emits here when it runs .collect(flat: false) // flat: false keeps each [process, tool, version] tuple .map { topic_versions -> // Citations for ONLY the tools that ran, resolved from meta.yml def citations = citationsOnTheFly(topic_versions, meta_yml_paths) methodsDescriptionText( "${projectDir}/assets/methods_description_template.yml", citations, [:] ) }
ch_multiqc_files = ch_multiqc_files.mix( ch_methods_description.collectFile(name: 'methods_description_mqc.yaml', sort: true))Before / After:
// Every tool listed by hand; cited even if it never randef citation_text = [ "Tools used in the workflow included:", "BWAMEM2 (Vasimuddin et al. 2019)", "FastQC (Andrews 2010),", // ...one line per tool, forever...].join(' ').trim()// Cites exactly the tools that ran, metadata from each module's meta.ymldef citations = citationsOnTheFly(topic_versions, meta_yml_paths)def citation_text = toolCitationText(citations)toolsFromVersionsTopic()
Description:
Extracts the unique, sorted set of tool names that actually executed from collected versions topic data. Useful on its own — e.g. for conditional logic like if ('star' in tools). (citationsOnTheFly() uses it internally to select which citations to emit.)
Function Signature:
List<String> toolsFromVersionsTopic(List topicVersions)Parameters:
topicVersions(List): Collectedversionstopic data —[process, tool, version]tuples (collect with.collect(flat: false)).
Returns:
List<String>: Sorted, de-duplicated tool names. Empty list fornull/empty input.
Usage Example:
include { toolsFromVersionsTopic } from 'plugin/nf-core-utils'
channel.topic('versions').collect(flat: false).map { versions -> def tools = toolsFromVersionsTopic(versions) log.info "Tools that ran: ${tools.join(', ')}" // e.g. "Tools that ran: fastqc, multiqc, samtools"}citationsOnTheFly() keeps the citation source as canonical meta.yml files while gating by what ran — no need to add getCitation(...) emissions to every module. If you have already migrated modules to emit a dedicated citation topic, the getCitation + autoToolCitationText path below is equivalent and needs no meta.yml paths.
Modern Approach Functions (Recommended)
getCitation()
Description:
Extracts citation information from a module’s meta.yml file and formats it for topic channel emission. This is the modern approach for automatic citation collection that only includes citations for tools that actually execute.
Function Signature:
List getCitation(String metaYmlPath)Parameters:
metaYmlPath(String): Path to the module’s meta.yml file, typically"${moduleDir}/meta.yml"
Returns:
List: Citation data formatted for topic channel emission[module, tool, citation_data]
Usage in Process:
include { getCitation } from 'plugin/nf-core-utils'
process FASTQC { input: tuple val(meta), path(reads)
output: tuple val(meta), path("*.html"), emit: html tuple val(meta), path("*.zip"), emit: zip val citation_data, topic: citations
script: citation_data = getCitation("${moduleDir}/meta.yml") """ fastqc --quiet --threads ${task.cpus} ${reads} """}Pipeline Integration:
workflow { FASTQC(samples) MULTIQC(reports)
// Collect all citations automatically - only from processes that executed citation_ch = channel.topic('citations').collect()
// Generate citation text and bibliography citation_ch.map { citations -> def citationText = autoToolCitationText(citations) def bibliography = autoToolBibliographyText(citations)
// Write to MultiQC template files file("${params.outdir}/multiqc_citations.txt").text = citationText file("${params.outdir}/multiqc_bibliography.html").text = bibliography }}autoToolCitationText()
Description:
Automatically generates formatted citation text from topic channel citation data. This processes the automatic citation collection from executed processes, ensuring runtime accuracy.
Function Signature:
String autoToolCitationText(List topicCitations = [])Parameters:
topicCitations(List): Citation data collected from topic channel
Returns:
String: Formatted citation text for methods descriptions
Usage Example:
include { autoToolCitationText } from 'plugin/nf-core-utils'
workflow { // Process data and collect citations PROCESS_SAMPLES(samples)
// Collect all citations from topic channel def citation_ch = channel.topic('citations').collect()
citation_ch.map { citations -> def citationText = autoToolCitationText(citations) log.info "Generated citations: ${citationText}" // Returns: "Tools used in the workflow included: fastqc (Andrews et al. 2010), multiqc (Ewels et al. 2016)." }}autoToolBibliographyText()
Description:
Automatically generates HTML bibliography from topic channel citation data. Creates bibliography entries for tools that were actually executed.
Function Signature:
String autoToolBibliographyText(List topicCitations = [])Parameters:
topicCitations(List): Citation data collected from topic channel
Returns:
String: HTML bibliography for MultiQC reports
Usage Example:
include { autoToolBibliographyText } from 'plugin/nf-core-utils'
workflow { // After processing with citation-enabled modules def citation_ch = channel.topic('citations').collect()
citation_ch.map { citations -> def bibliography = autoToolBibliographyText(citations) // Write bibliography for MultiQC file("${params.outdir}/multiqc_bibliography.html").text = bibliography }}Legacy Functions (Still Supported)
generateModuleToolCitation()
Description:
Extracts citation information from a module’s meta.yml file using the traditional approach. This function processes all tools in the meta.yml regardless of execution.
Function Signature:
Map generateModuleToolCitation(Object metaFilePath)Parameters:
metaFilePath(String|File): Path to the meta.yml file
Returns:
Map: Citations map with tool names as keys and citation/bibliography data as values
Usage Example:
include { generateModuleToolCitation } from 'plugin/nf-core-utils'
// Extract citations from specific moduledef fastqcCitations = generateModuleToolCitation('modules/nf-core/fastqc/meta.yml')// Returns: [fastqc: [citation: 'fastqc (Andrews et al. 2010)', bibliography: '<li>Andrews S...</li>']]
log.info "FASTQC citations: ${fastqcCitations}"toolCitationText()
Description:
Generates formatted citation text from collected citations map (legacy approach).
Function Signature:
String toolCitationText(Map collectedCitations)Parameters:
collectedCitations(Map): Map of tool citations fromgenerateModuleToolCitation()
Returns:
String: Formatted citation text for use in methods descriptions
Usage Example:
include { generateModuleToolCitation; toolCitationText } from 'plugin/nf-core-utils'
// Collect citations from multiple modulesdef allCitations = [:]allCitations.putAll(generateModuleToolCitation('modules/nf-core/fastqc/meta.yml'))allCitations.putAll(generateModuleToolCitation('modules/nf-core/multiqc/meta.yml'))
def citationText = toolCitationText(allCitations)log.info "Citations: ${citationText}"// Returns: "Tools used in the workflow included: fastqc (Andrews et al. 2010), multiqc (Ewels et al. 2016)."toolBibliographyText()
Description:
Generates HTML bibliography from collected citations (legacy approach).
Function Signature:
String toolBibliographyText(Map collectedCitations)Parameters:
collectedCitations(Map): Map of tool citations
Returns:
String: HTML bibliography for MultiQC reports
Usage Example:
include { collectCitationsFromFiles; toolBibliographyText } from 'plugin/nf-core-utils'
def citationFiles = ['modules/nf-core/fastqc/meta.yml', 'modules/nf-core/multiqc/meta.yml']def citations = collectCitationsFromFiles(citationFiles)def bibliography = toolBibliographyText(citations)
file("${params.outdir}/bibliography.html").text = bibliographymethodsDescriptionText()
Description:
Generates methods description text using collected citations and a MultiQC methods template. This function substitutes citation and bibliography variables into the template.
Function Signature:
String methodsDescriptionText(String mqcMethodsYamlPath, Map collectedCitations = [:], Map meta = [:])Parameters:
mqcMethodsYamlPath(String): Path to MultiQC methods YAML template filecollectedCitations(Map, optional): Map containing all tool citationsmeta(Map, optional): Additional metadata for template substitution
Returns:
String: Formatted methods description HTML
Usage Example:
include { collectCitationsFromFiles; methodsDescriptionText } from 'plugin/nf-core-utils'
// Collect citations and generate methods descriptiondef citations = collectCitationsFromFiles(['modules/nf-core/fastqc/meta.yml'])def methodsYaml = "${projectDir}/assets/methods_description_template.yml"
def methodsDescription = methodsDescriptionText(methodsYaml, citations, [ 'pipeline_name': workflow.manifest.name, 'pipeline_version': workflow.manifest.version])
file("${params.outdir}/methods_description.html").text = methodsDescriptionTemplate Example (methods_description_template.yml):
id: "custom-methods"section_name: "nf-core/rnaseq Methods Description"description: | <h4>Methods</h4> <p>Data was processed using nf-core/rnaseq v${pipeline_version}. ${tool_citations_text}</p>
<h4>References</h4> ${tool_bibliography_text}collectCitationsFromFiles()
Description:
Collects citations from multiple meta.yml files (legacy batch processing approach).
Function Signature:
Map collectCitationsFromFiles(List<String> metaFilePaths)Parameters:
metaFilePaths(List): List of paths to module meta.yml files
Returns:
Map: Combined citations from all files
Usage Example:
include { collectCitationsFromFiles } from 'plugin/nf-core-utils'
// Batch collect citations from multiple modulesdef metaFiles = [ 'modules/nf-core/fastqc/meta.yml', 'modules/nf-core/multiqc/meta.yml', 'modules/local/custom_tool/meta.yml']
def allCitations = collectCitationsFromFiles(metaFiles)log.info "Collected ${allCitations.size()} tool citations"Migration Functions
processMixedCitationSources()
Description:
Processes citations from both topic channels (modern approach) and legacy files for progressive migration.
Function Signature:
Map processMixedCitationSources(List<List> topicCitations, List<String> citationFiles)Parameters:
topicCitations(List- ): Citation data from topic channels
citationFiles(List): List of meta.yml file paths
Returns:
Map: Combined citations from both sources
Migration Example:
include { processMixedCitationSources } from 'plugin/nf-core-utils'
workflow { // Modern processes using topic channels MODERN_PROCESS(input_modern)
// Legacy processes still using file-based citations LEGACY_PROCESS(input_legacy)
// Collect from both sources def topicCitations = channel.topic('citations').collect() def legacyFiles = ['modules/legacy/old_tool/meta.yml']
// Combine both approaches during migration def allCitations = processMixedCitationSources(topicCitations, legacyFiles)
// Generate final citation text def citationText = toolCitationText(allCitations)}convertMetaYamlToTopicFormat()
Description:
Converts legacy meta.yml data to new topic channel format for migration purposes.
Function Signature:
List<List> convertMetaYamlToTopicFormat(String metaFilePath, String moduleName = null)Parameters:
metaFilePath(String): Path to meta.yml filemoduleName(String, optional): Name of the module (defaults to filename)
Returns:
List<List>: List of[module, tool, citation_data]tuples
Migration Usage:
include { convertMetaYamlToTopicFormat } from 'plugin/nf-core-utils'
// Convert existing meta.yml to topic format for consistencydef legacyMetaPath = 'modules/legacy/old_tool/meta.yml'def topicFormatCitations = convertMetaYamlToTopicFormat(legacyMetaPath, 'OLD_TOOL')
// Emit to topic channel for unified processingchannel.fromList(topicFormatCitations) .set { converted_citations_ch }Topic Channel Processing Functions
processCitationsFromTopic()
Description:
Processes citations exclusively from topic channel format (modern approach).
Function Signature:
Map processCitationsFromTopic(List<List> topicData)Parameters:
topicData(List- ): List of
[module, tool, citation_data]tuples
Returns:
Map: Processed citations map
Usage Example:
include { processCitationsFromTopic } from 'plugin/nf-core-utils'
workflow { // Pure topic channel approach PROCESS_A(input_a) PROCESS_B(input_b)
// Collect topic channel citations def citations_ch = channel.topic('citations').collect()
citations_ch.map { topicCitations -> def citations = processCitationsFromTopic(topicCitations) def citationText = toolCitationText(citations)
file("${params.outdir}/citations.txt").text = citationText }}processCitationsFromFile()
Description:
Processes citations exclusively from traditional YAML files (legacy approach).
Function Signature:
Map processCitationsFromFile(List<String> citationFiles)Parameters:
citationFiles(List): List of file paths to meta.yml files
Returns:
Map: Tool citations from files
Legacy Usage:
include { processCitationsFromFile } from 'plugin/nf-core-utils'
// Legacy file-based processingdef citationFiles = [ 'modules/nf-core/fastqc/meta.yml', 'modules/nf-core/multiqc/meta.yml']
def citations = processCitationsFromFile(citationFiles)def citationText = toolCitationText(citations)Topic Channel Migration Guide
Stage 1: Legacy File-Based Approach
Traditional approach collecting citations from all meta.yml files:
include { collectCitationsFromFiles; toolCitationText; toolBibliographyText } from 'plugin/nf-core-utils'
workflow { // Process samples with various tools FASTQC(samples) MULTIQC(reports)
// Manually collect citations from all potential modules def metaFiles = [ 'modules/nf-core/fastqc/meta.yml', 'modules/nf-core/multiqc/meta.yml' ]
// Problem: Citations included even if process didn't execute def citations = collectCitationsFromFiles(metaFiles) def citationText = toolCitationText(citations) def bibliography = toolBibliographyText(citations)}Stage 2: Mixed Migration Approach
Gradual migration supporting both approaches:
include { processMixedCitationSources; toolCitationText } from 'plugin/nf-core-utils'
workflow { // Modern processes using topic channels MODERN_FASTQC(samples) // Emits to citations topic
// Legacy processes still using manual collection LEGACY_MULTIQC(reports) // No topic channel emission
// Collect from both sources def topicCitations = channel.topic('citations').collect() def legacyFiles = ['modules/nf-core/multiqc/meta.yml']
// Combine approaches during migration def allCitations = processMixedCitationSources(topicCitations, legacyFiles) def citationText = toolCitationText(allCitations)}Stage 3: Modern Topic Channel Approach
Fully migrated with automatic runtime-accurate citations:
include { getCitation; autoToolCitationText; autoToolBibliographyText } from 'plugin/nf-core-utils'
process FASTQC { output: val citation_data, topic: citations
script: citation_data = getCitation("${moduleDir}/meta.yml") """ fastqc ${reads} """}
workflow { // All processes automatically emit citations when they execute FASTQC(samples) MULTIQC(reports)
// Automatic collection - only includes executed tools def citation_ch = channel.topic('citations').collect()
citation_ch.map { citations -> def citationText = autoToolCitationText(citations) def bibliography = autoToolBibliographyText(citations)
// Zero-maintenance: accurate citations without manual collection file("${params.outdir}/citations.txt").text = citationText file("${params.outdir}/bibliography.html").text = bibliography }}Best Practices
1. Progressive Module Migration
Migrate modules gradually to topic channels:
// Phase 1: High-priority modules firstprocess CRITICAL_ANALYSIS { output: val citation_data, topic: citations // Modernize first
script: citation_data = getCitation("${moduleDir}/meta.yml") """ # Critical analysis here """}
// Phase 2: Supporting modulesprocess SUPPORTING_TOOL { // Still using legacy approach temporarily script: """ # Supporting analysis """}
// Phase 3: Utility modules lastprocess UTILITY_PROCESS { // Convert when convenient}2. Runtime Citation Accuracy
Ensure citations reflect actual tool execution:
workflow { // Conditional processing with accurate citations if (params.run_fastqc) { FASTQC(samples) // Only emits citation if it runs }
if (params.run_multiqc) { MULTIQC(reports) // Only emits citation if it runs }
// Citations automatically match actual execution channel.topic('citations').collect().map { citations -> autoToolCitationText(citations) // Only includes tools that ran }}3. Error Handling and Graceful Degradation
Handle missing citation data gracefully:
include { getCitation; autoToolCitationText } from 'plugin/nf-core-utils'
process ROBUST_PROCESS { output: val citation_data, topic: citations
script: try { citation_data = getCitation("${moduleDir}/meta.yml") } catch (Exception e) { log.warn "Citation extraction failed for ${task.process}: ${e.message}" citation_data = ['UNKNOWN', 'unknown_tool', [description: 'Citation unavailable']] } """ # Process execution """}4. Testing Citation Functions
Test citation processing across migration stages:
test("Modern topic channel citations") { when { def topicCitations = [ ['FASTQC', 'fastqc', [doi: '10.1093/bioinformatics/btv033', author: 'Andrews S']], ['MULTIQC', 'multiqc', [doi: '10.1093/bioinformatics/btw354', author: 'Ewels P']] ] def result = autoToolCitationText(topicCitations) }
then { assert result.contains('fastqc') assert result.contains('multiqc') assert result.contains('Andrews') }}
test("Legacy file citations") { when { def citations = generateModuleToolCitation('test_meta.yml') def result = toolCitationText(citations) }
then { assert result.contains('Tools used in the workflow') assert citations.size() > 0 }}
test("Mixed citation processing") { when { def topicCitations = [['MODERN', 'modern_tool', [doi: 'test']]] def legacyFiles = ['test_legacy_meta.yml'] def result = processMixedCitationSources(topicCitations, legacyFiles) }
then { assert result.containsKey('modern_tool') // Also contains legacy citations }}Integration Examples
Complete Modern Pipeline Integration
#!/usr/bin/env nextflow
include { getCitation; autoToolCitationText; autoToolBibliographyText } from 'plugin/nf-core-utils'
// Modern citation-enabled processesprocess FASTQC { input: tuple val(meta), path(reads)
output: tuple val(meta), path("*.html"), emit: html val citation_data, topic: citations
script: citation_data = getCitation("${moduleDir}/meta.yml") """ fastqc --quiet --threads ${task.cpus} ${reads} """}
process MULTIQC { input: path('*')
output: path("multiqc_report.html"), emit: report val citation_data, topic: citations
script: citation_data = getCitation("${moduleDir}/meta.yml") """ multiqc . """}
workflow { // Process samples FASTQC(samples)
// Only run MultiQC if reports exist if (params.skip_multiqc == false) { MULTIQC(FASTQC.out.html.collect()) }
// Automatic citation collection - runtime accurate def citation_ch = channel.topic('citations').collect()
citation_ch.map { citations -> // Generate citation components def citationText = autoToolCitationText(citations) def bibliography = autoToolBibliographyText(citations)
// Write files for MultiQC template substitution file("${params.outdir}/pipeline_info/citations.txt").text = citationText file("${params.outdir}/pipeline_info/bibliography.html").text = bibliography
log.info "Generated citations for ${citations.size()} tools" }}MultiQC Methods Template Integration
include { autoToolCitationText; methodsDescriptionText } from 'plugin/nf-core-utils'
workflow { // Process with citation-enabled modules ANALYSIS_PROCESSES(data)
// Collect citations and generate methods description def citation_ch = channel.topic('citations').collect()
citation_ch.map { citations -> // Generate components def citationText = autoToolCitationText(citations) def bibliography = autoToolBibliographyText(citations)
// Create comprehensive methods description def methodsYaml = "${projectDir}/assets/methods_description_template.yml" def methodsDescription = methodsDescriptionText( methodsYaml, [ tool_citations_text: citationText, tool_bibliography_text: bibliography ], [ pipeline_name: workflow.manifest.name, pipeline_version: workflow.manifest.version, pipeline_doi: workflow.manifest.doi ?: 'N/A' ] )
// Write for MultiQC inclusion file("${params.outdir}/multiqc_methods_description.html").text = methodsDescription }}Common Issues and Solutions
Issue: Empty Citation Output
Problem:
def citations = autoToolCitationText([])// Result: "Tools used in the workflow included: ."Solution:
// Verify citations are being emittedchannel.topic('citations').view { "Citation collected: $it" }
// Check citation collectionchannel.topic('citations').collect().view { "All citations: $it" }
// Handle empty case gracefullydef citations = channel.topic('citations').collect().map { citationList -> if (citationList.isEmpty()) { return "No tool citations available." } else { return autoToolCitationText(citationList) }}Issue: Missing Module Citations
Problem:
Process doesn’t emit citations even with getCitation() call
Solution:
process DEBUG_CITATIONS { output: val citation_data, topic: citations
script: citation_data = getCitation("${moduleDir}/meta.yml")
// Debug: Log citation data log.info "Citation data for ${task.process}: ${citation_data}"
""" # Check if meta.yml exists ls -la ${moduleDir}/ """}Issue: Legacy to Modern Migration Errors
Problem: Mixed processing produces inconsistent citation formats
Solution:
// Validate citation data format before processingdef validatedCitations = topicCitations.findAll { citation -> citation instanceof List && citation.size() == 3 && citation[0] && citation[1] && citation[2]}
def citations = processCitationsFromTopic(validatedCitations)Performance Considerations
Topic Channel Benefits
- Runtime Accuracy: Only tools that execute appear in citations
- Zero Maintenance: No manual citation file management
- Better Performance: No file I/O for citation collection
- Automatic Updates: Citations update when modules change
Migration Performance
// Optimize citation processing during migrationworkflow { // Collect in parallel def topicFuture = channel.topic('citations').collect() def legacyFiles = legacy_citation_files.collect()
// Process efficiently tuple(topicFuture, legacyFiles).map { topic, files -> processMixedCitationSources(topic, files) }.set { final_citations }}For more information on citation management and topic channels, see the Nextflow documentation and nf-core modules documentation.