Near real-time data on the human neutralizing antibody landscape to influenza virus as of early 2026 to inform vaccine-strain selection
This study uses sequencing-based neutralization assays to measure titers to influenza viruses with HAs from seasonal H3N2 and H1N1 viruses representative of those circulating in late 2025 and early 2026 against human sera collected in mid to late 2025.
The data here are described in Kikawa et al, 2026; see that paper for fully study details.
Here are quick links with key data/results:
-
results/final_titer_data has the final titer data and information on the viruses and sera for which these final data were obtained. Specifically:
- For human sera:
- results/final_titer_data/human_titers.csv: final set of titers for each virus/serum pair (keeping only viruses for which titers measured against most sera, and sera for which titers measured against most viruses).
- results/final_titer_data/human_sera.csv: detailed information about the sera for which these titers were measured.
- results/final_titer_data/human_sera_multicohort.csv: sera assigned to multiple cohorts (original, "All", and days-post-vax cohorts); differs from results/final_titer_data/human_sera.csv in that each serum may appear in multiple rows.
- results/final_titer_data/human_viruses.csv: detailed information about the viruses for which these titers were measured.
- results/final_titer_data/human_titers_summarized_by_virus.csv: summary statistics about the titers against each virus.
- For human sera:
-
Interactive titer summary plots showing median titers, individual serum titers, interquartile ranges, and fraction of sera below titer cutoffs for each subtype and strain type. These plots are generated in
results/titer_plots/(not tracked in git) but viewable at the bottom of the GitHub Pages documentation at https://jbloomlab.github.io/flu-seqneut-2025to2026. This documentation also has detailed QC for all of the curves, titers, etc. -
Interactive Nextstrain protein trees (can be colored by subclade, median titer, or fraction below titer cutoff for each cohort):
See Kikawa et al (2026) for details on this particular the study. It uses sequencing-based neutralization assays as also described in Loes et al. (2024), Journal of Virology and Kikawa et al. (2025), Virus Evolution.
If you don't care to understand the overall repo structure and are just looking for final QC-ed titer data and information on the sera / viruses for which those titers were generated, look in results/final_titer_data/.
- Influenza strains (H3N2 and H1N1 pdm09) from late 2025 to early 2026 circulation
- Multiple barcodes per strain for redundancy
- Each viral construct contains the circulating HA ectodomain with signal peptide, endodomain, and cytoplasmid tail constant across strains for a given subtype
- Library defined in in CSVs in data/viral_libraries/ (see config.yml for active library file for each experiment)
- Each viral library CSV must have the following required columns (additional columns are allowed):
- strain: strain name; there can be multiple rows for each strain (as we may have several barcodes for a strain), but each strain name must be uniquely paired with a single value for all other required columns except barcode (where a strain can be paired with multiple barcode values). Note that non-required columns (like Twist_name) may vary per barcode. Strain name must also end in "_H3N2" or "_H1N1"
- subtype: H3N2 or H1N1
- strain_type: should be "vaccine" or the circulating_strain_type defined in
config.yml(eg "circulating_2025to2026") - vaccine_type: if strain_type is "vaccine", this should be "egg" or "cell"; otherwise it should be null
- barcode: should be 16-nt string, all barcode entries should be unique for each row
- accession: Genbank accession if available
- subclade: can only be null for strain_type of "vaccine"
- derived_haplotype: description of derived haplotype from clade (eg, clade plus additional HA1 mutations), can only be null for strain_type of "vaccine". Note that HA2 mutations are not included in the derived_haplotype naming, so multiple strains may share the same derived_haplotype if they differ only in HA2.
- collection_date: collection date as float (eg, 2025.5); in many cases this may refer to the date that HA1 haplotype was last identified rather than the actual collection date of the particular named strain.
- nt_sequence_HA_ectodomain: nucleotide sequence of HA ectodomain, must be all A, T, C, or G (either case) and length multiple of three. Note that this is not the same as the full Twist synthesized insert, rather it is the part of the HA that is taken from that strain, and does not include flanking constant regions not derived from the strain (recall signal peptide and transmembrane / cytoplasmic tail are constant in our barcoded constructs), so it is not the full-length HA.
- protein_sequence_HA_ectodomain: protein sequence, must meet these criteria:
- must not contain any stop codons ("*" characters) and must be the result of translating nt_sequence_HA_ectodomain.
- must start with "CIGY" if a H1N1 subtype, and with "Q[KNR][IL]P" if a H3N2 subtype (note this is start of HA ectodomain for H3N2, for H1N1 a "DTL" must be added to get HA ectodomain since the plasmid construct used in experiments provides the first 3 ectodomain amino acids from WSN strain).
- must end with "NNRFQ" if a H3N2 subtype, and "[EK]IDG[VI]" if a H1N1 subtype.
- the length should be 500 for H1N1 subtype and 501 for H3N2 subtype (note this might change of H1N1 is modified to include pre-2009 seasonal H1N1 as well as pdmH1N1 lineage).
- Each unique protein sequence should only be associated with one strain.
There are two CSVs in data/viral_libraries, one giving the originally designed library and the other giving the library that had strains that passed various QC and are used for the actual titer measurements. The strain used for the actual measurements are in data/viral_libraries/flu-seqneut-2025to2026-barcode-to-strain-actual.csv.
Details about the viruses in these files that are actually used to generate final titer data are in results/final_titer_data/.
- Human sera collected in mid to late 2025 from multiple cohorts
- Per-cohort metadata CSVs stored in data/sera_metadata/; note that sera may be listed here for which no titers were measured
- Each sera metadata CSV must have the following required columns (additional columns are allowed):
- bloom_lab_id: unique identifier for each serum sample; must be non-null and unique across all files
- cohort: cohort identifier (e.g., "HKU", "NIID", "PENN"); must be non-null
- species: species of serum donor (e.g., "human"); must be non-null
- age: age of donor; can be numeric (e.g., "45"), a range with or without 'y' suffix (e.g., "10-19y", "18-29"), or open-ended (e.g., "75+")
- sex: sex of donor; accepts various formats ("M"/"F", "male"/"female", "Male"/"Female") which are normalized during aggregation
- collection_date: date of serum collection; accepts "Mon-YYYY" (e.g., "Aug-2025") or "Mon-YY" (e.g., "Nov-25") formats
The per-cohort metadata files are aggregated and validated by scripts/aggregate_sera_metadata.py (Snakemake rule aggregate_sera_metadata) into results/sera_metadata/all_sera_metadata.csv.
The aggregation performs these standardizations:
- Renames bloom_lab_id to serum
- Normalizes sex to "Male", "Female", or "Unknown"
- Parses age to create age_numeric column (midpoint in years for ranges, lower bound for open-ended)
- Standardizes collection_date to "YYYY-MM" format
- Validates uniqueness of serum IDs across all cohorts
Details about the sera in these files for which we actually obtained high-quality titer data are in results/final_titer_data/.
The titers aggregated across plates are in results/aggregated_titers/.
The titers after classifying by species from which the sera came and subsetting for just titers for viruses and sera which are well measured are in results/final_titer_data/.
flu-seqneut-2025to2026/
├── Snakefile # Top-level Snakemake workflow
├── config.yml # Main pipeline configuration
├── run_Hutch_cluster.bash # SLURM cluster submission script
│
├── scripts/ # Custom analysis scripts
│ ├── validate_viral_library.py # Viral library validation
│ ├── aggregate_sera_metadata.py # Sera metadata aggregation
│ ├── process_final_titer_data.py # Final titer data processing
│ └── nextstrain_prot_titers_tree_alignment_and_metadata.py # Tree input preparation
│
├── notebooks/ # Marimo notebooks for visualizations
│ └── plot_titer_summaries.py # Interactive titer summary plots
│
├── data/ # Input data
│ ├── viral_libraries/ # Barcode-to-strain mappings
│ ├── neut_standard_sets/ # Neutralization control barcodes (Loes et al. 2024)
│ ├── plates/ # Per-plate sample metadata CSVs
│ ├── miscellaneous_plates/ # QC and pooling validation data
│ ├── sera_metadata/ # Per-cohort serum metadata CSVs
│ └── nextstrain-prot-titers-tree_data/ # Outgroup and site numbering for trees
│
├── results/ # Pipeline outputs (key files tracked in git)
│ ├── barcode_counts/ # Per-sample barcode counts
│ ├── barcode_fates/ # Read processing statistics
│ ├── plates/ # Per-plate analyses
│ ├── sera/ # Per-serum titer aggregations
│ ├── aggregated_titers/ # Final titer matrices
│ ├── final_titer_data/ # QC'd titer data with metadata (tracked in git)
│ ├── titer_plots/ # Interactive titer summary plots (not tracked in git)
│ ├── qc_drops/ # QC filtering documentation
│ └── miscellaneous_plates/ # Library pooling QC results
│
├── auspice/ # Nextstrain auspice JSON files for tree visualization
│
├── seqneut-pipeline/ # Analysis pipeline git submodule
└── nextstrain-prot-titers-tree/ # Tree building git submodule
Note all input data for the pipeline are in ./data/. All output generated by pipeline goes in ./results/, although only some results are tracked in the GitHub repo (see .gitignore).
The pipeline is specified in Snakefile, which is a snakemake file that includes analyses run by the submodules as well as additional custom rules.
This repository uses the following git submodules:
- seqneut-pipeline for the core analysis workflow.
- nextstrain-prot-titers-tree for building interactive Nextstrain protein trees with titer data.
The non-pipeline_analyses/ subdirectory contains auxiliary workflows (library design and pooling optimization) that are separate from the main neutralization assay pipeline. See the README in non-pipeline_analyses for details.
All pipeline parameters are specified in config.yml. Key configuration sections include:
- Viral Libraries: Barcode-to-strain CSV file paths
- Neutralization Standards: Control barcode definitions (Loes et al. 2024)
- Illumina Barcode Parser: Parameters for extracting barcodes from FASTQ files
- Plates: Per-plate configuration including sample metadata, QC thresholds, and curve fitting parameters
- Miscellaneous Plates: Plates for QC/pooling (barcode counting only, no curve fitting)
- Serum QC: Thresholds for replicate consistency and minimum replicates
- Titer Calculation: Method (midpoint or NT50) and display options
See seqneut-pipeline/README.md for detailed configuration documentation.
# Create conda environment
conda env create -f seqneut-pipeline/environment.yml
# Activate environment
conda activate seqneut-pipelinesnakemake -j <num_cores> --software-deployment-method condabash run_Hutch_cluster.bashAs described in seqneut-pipeline/README.md, the pipeline generates HTML documentation that can be pushed to a gh-pages branch for publishing on GitHub Pages with:
./seqneut-pipeline/publish_docs_gh-pages.shThis is then displayed on GitHub Pages via the gh-pages branch at https://jbloomlab.github.io/flu-seqneut-2025to2026.
The command to publish to the gh-pages branch must be run manually, and GitHub must be manually configured to display the Pages from this branch.
The complete analysis workflow proceeds through these stages:
The viral library (barcode-to-strain mapping CSV in data/viral_libraries/) is designed and produced prior to running the main pipeline. See non-pipeline_analyses/ for library design and pooling optimization workflows (documented separately in that directory).
The pipeline validates viral library CSVs against the specifications above using scripts/validate_viral_library.py. Validation output is written to results/validate_viral_library/.
Goal: Incubate viral library with serially diluted human sera to measure neutralization.
Process (when serum samples available):
- Sera are heat inactivated and serially diluted
- Diluted sera incubated with viral library
- Non-neutralized viruses infect target cells and express barcodes
- Each plate includes serum dilution series, replicates, and no-serum controls
Configuration: Plates configured in config.yml with sample metadata CSV specifying wells, sera, dilutions, replicates, and FASTQ file paths.
Goal: Generate FASTQ files with barcode counts for each well.
Process:
- RNA extraction from infected cells
- PCR amplification of barcode region with well-specific indexes
- Illumina sequencing
- Demultiplexing by well index
Output: FASTQ files listed in plate sample CSVs.
Pipeline Rule: count_barcodes
Process:
- Parses FASTQ files using
dms_variants.illuminabarcodeparser.IlluminaBarcodeParser - Extracts barcodes and validates against viral library
- Classifies read fates (valid, invalid, unparseable, low quality)
- Aggregates counts per barcode per well
Key Outputs:
results/barcode_counts/{sample}.csv- Barcode countsresults/barcode_fates/{sample}.csv- Read fate statisticsresults/barcode_invalid/{sample}.csv- Counts of invalid barcodes; not tracked in GitHub repo but can be useful for debugging
Pipeline Rule: process_plate
Process:
- Applies multi-stage QC filters (thresholds specified in
config.yml) - Computes fraction infectivity by normalizing to neutralization standards and no-serum wells
- Applies manual drops specified in configuration
- Generates interactive marimo notebook with QC visualizations
Key Outputs:
results/plates/{plate}/frac_infectivity.csv- Normalized infectivity matrixresults/plates/{plate}/qc_drops.yml- QC filtering documentationresults/plates/{plate}/process_plate.html- Interactive QC report
Pipeline Rule: Integrated into process_plate
Process:
- Groups fraction infectivity by barcode, serum replicate, and dilution
- Fits Hill curves using
neutcurve.CurveFits(parameters specified inconfig.yml) - Computes neutralization titers as midpoint or NT50 (configurable)
- Applies goodness-of-fit QC (thresholds in
config.yml)
Key Outputs:
results/plates/{plate}/curvefits.csv- Fitted parameters and titersresults/plates/{plate}/curvefits.pickle- Pickledneutcurve.CurveFitsobjects- Curve plots in
process_plate.html
Pipeline Rule: group_serum_titers
Process:
- Groups replicates of each serum across plates
- Applies serum-level QC (thresholds in
config.yml) - Computes median titers across passing replicates
- Generates per-serum neutralization curve visualizations
Key Outputs:
results/sera/{group}_{serum}/titers.csv- Median titers per virusresults/sera/{group}_{serum}/titers_per_replicate.csv- Individual replicate titersresults/sera/{group}_{serum}/curves.pdf- Neutralization curvesresults/sera/{group}_{serum}/qc_drops.yml- Serum-level QC drops
Pipeline Rule: aggregate_titers
Process:
- Combines all sera titers into matrices (sera × viruses)
- Creates summary statistics and visualizations
- Generates interactive marimo notebook for data exploration
Key Outputs:
results/aggregated_titers/titers_{group}.csv- Final titer matrix (tracked in git)results/aggregated_titers/curvefits_{group}.pickle- All curve fits for custom analysis
Pipeline Rules: aggregate_qc_drops, build_docs
Process:
- Aggregates QC filtering across all plates and sera
- Builds HTML documentation with navigation index
- Collects all marimo notebook outputs into
docs/for GitHub Pages
Key Outputs:
results/qc_drops/*.yml- Comprehensive QC summaries (tracked in git)docs/- Complete HTML documentation
Pipeline Rules: nextstrain_prot_titers_tree_alignment_and_metadata, plus rules from nextstrain-prot-titers-tree submodule
Goal: Build interactive phylogenetic trees of library sequences that can be colored by neutralization titers.
Process:
- Extracts protein sequences from viral library for circulating strains and recent vaccine strains
- Prepends prefix amino acids if needed (H1N1 sequences need "DTL" added to match full ectodomain)
- Generates alignment FASTA and metadata TSV for each subtype
- When titers configured: processes summarized titers to add median titer and fraction below cutoff columns to metadata, and creates per-serum titers TSV for measurements panel
- Builds phylogenetic tree using IQ-TREE
- Refines tree and extracts amino acid mutations using TreeTime
- Exports to Auspice JSON format for Nextstrain visualization
- Overlays per-serum titer measurements in interactive measurements panel
Key Inputs (in data/nextstrain-prot-titers-tree_data/):
{subtype}_outgroup.fa- Reference sequence for rooting the tree{subtype}_site_numbering_map.tsv- Maps alignment positions to HA1/HA2 numbering
Key Outputs:
results/nextstrain-prot-titers-tree/{subtype}/alignment.fa- Protein alignmentresults/nextstrain-prot-titers-tree/{subtype}/metadata.tsv- Strain metadata with titer summary columnsresults/nextstrain-prot-titers-tree/{subtype}/titers.tsv- Per-serum titers for tree overlayauspice/flu-seqneut-2025to2026_{subtype}.json- Auspice tree JSON (tracked in git)auspice/flu-seqneut-2025to2026_{subtype}_measurements.json- Auspice titer measurements JSON (tracked in git), created only if titers specified for tree
Visualization: Trees are viewable as Nextstrain Community Builds:
- H3N2: https://nextstrain.org/community/jbloomlab/flu-seqneut-2025to2026@main/H3N2
- H1N1: https://nextstrain.org/community/jbloomlab/flu-seqneut-2025to2026@main/H1N1
Configuration: Tree parameters are in config.yml under nextstrain-prot-titers-tree_config. Key settings:
nextstrain-prot-titers-tree_viral_library: Which viral library key to use for building tree alignment and metadata (e.g.,designedoractual).nextstrain-prot-titers-tree_titers_from: Species for titer data (e.g.,human). When set, enables titer overlay on trees with input files derived fromresults/final_titer_data/{species}_*.csv.serum_cohorts_for_tree: List of cohorts to include in tree coloring. Generatesmedian_titer_{cohort}_seraandfrac_w_titer_below_{cutoff}_{cohort}_seracolumns.color_by_metadata: Columns available for tree coloring (strain_type, subclade, and titer columns).titers: Configuration for the per-serum titers measurement panel.display_defaults: Default tree display options (color_by, distance_measure, branch_label).
Pipeline Rule: process_final_titer_data
Goal: Produce QC'd, publication-ready titer datasets with associated metadata.
Process:
- Validates that all sera and viruses in titers have corresponding metadata
- Drops explicitly specified sera and viruses (configured in
process_final_titer_data) - Filters sera/viruses that don't meet minimum completeness thresholds:
min_frac_viruses: Minimum fraction of viruses each serum must have titers againstmin_frac_sera: Minimum fraction of sera each virus must have titers from (applies to ALL viruses in viral library, including those with 0 titer measurements)- Viruses with no titers (0 measurements) are evaluated and reported; they have frac_sera=0.0 and fail filtering unless min_frac_sera=0
- Generates separate CSV files for titers, sera metadata, and virus metadata
- Computes summary statistics by virus and cohort
Key Outputs (in results/final_titer_data/):
{group}_titers.csv- One row per serum-virus titer measurement{group}_sera.csv- Metadata for sera included in final dataset (one row per serum){group}_sera_multicohort.csv- Sera with multi-cohort assignments; each serum appears in multiple rows (once for "All", once for its original cohort, and once for each days-post-vax cohort if applicable){group}_viruses.csv- Metadata for viruses included in final dataset{group}_titers_summarized_by_virus.csv- Per-virus statistics across sera, computed for each cohort{group}_summary.txt- Processing log with counts at each step
Configuration: Parameters in config.yml under process_final_titer_data.
Pipeline Rule: plot_titer_summaries
Goal: Generate interactive Altair visualizations summarizing titer data across sera and viruses.
Process:
- Reads final titer data, sera metadata (including multi-cohort assignments), and virus metadata
- Creates interactive charts with selections for filtering by cohort, age range, and virus subclade/vaccine type
- Generates plots for each subtype (H1N1, H3N2) × strain type (recent, vaccine) × chart type
- Outputs both vertical and horizontal orientations for each chart
- Adds charts to auto-generated pipeline documentation
Chart Types:
individual_sera: Shows median titers (points) and per-serum titers (lines) for each virusinterquartile_range: Shows median titers (points) and interquartile range bandsfrac_below_cutoff: Bar chart showing fraction of sera below a configurable titer cutoff
Interactive Features:
- Click legend to filter by serum cohort or virus subclade/vaccine type
- Adjust age range sliders to filter sera by donor age
- Hover for detailed tooltips (virus name, derived haplotype, titer values, serum metadata)
- Adjustable titer cutoff slider for fraction-below-cutoff charts
Key Outputs (in results/titer_plots/, not tracked in git but included in GitHub Pages documentation):
{group}_plot_titer_summaries_{orientation}.html- Full marimo notebook with all charts{group}_plot_titer_summaries_{orientation}_context.pickle- Context pickle for interactive editing{group}_{subtype}_{strain_type}_{chart_type}_{orientation}.html- Individual chart HTMLs (12 per group per orientation)
Configuration: Parameters in config.yml under plot_titer_summaries_params:
titer_cutoff: Initial slider value for fraction-below-cutoff chartstiter_lower_limit: Lower limit for log-scale titer axisfacet_size: Width/height of each cohort facetprop_colors: Pre-assigned colors for vaccine type labelsother_prop_colors: Color palette for subclade labels
Notebook: notebooks/plot_titer_summaries.py - Marimo notebook implementing the visualizations
Pipeline Rule: plot_fold_changes
Goal: Generate interactive Altair visualizations comparing titers between paired sera cohorts (e.g., pre- vs post-vaccination), with fold-change panels.
Process:
- Reads final titer data, sera multi-cohort assignments, and virus metadata
- Pairs sera across cohorts by
subject_id(only subjects present in all specified cohorts are included) - Computes fold change in titers relative to a baseline cohort (first listed in config)
- Creates two-panel charts: top panel shows titers, bottom panel shows fold changes
- Outputs both vertical and horizontal orientations for each chart
- Adds charts to auto-generated pipeline documentation
Chart Types:
individual_sera: Per-subject lines colored by condition + median points, in both titer and fold-change panelsinterquartile_range: IQR error bands per condition + median points, in both titer and fold-change panels
Interactive Features:
- Click legend to filter by condition (cohort) or virus subclade/vaccine type
- Adjust age range sliders to filter sera by donor age
- Hover for linked tooltips across titer and fold-change panels (virus name, derived haplotype, subject ID, condition, titer/fold-change values)
Key Outputs (in results/titer_plots/, not tracked in git but included in GitHub Pages documentation):
{fold_change_name}_plot_fold_changes_{orientation}.html- Full marimo notebook{fold_change_name}_{subtype}_{strain_type}_{chart_type}_{orientation}.html- Individual chart HTMLs (8 per fold_change_name per orientation)
Configuration: Parameters in config.yml under plot_fold_changes. Each entry specifies:
species: Which species group to use (e.g.,human)title: Display title for the chartscohorts: Ordered mapping of cohort names to display labels (first is baseline for fold change)condition_colors: List of hex colors for each condition
Visual parameters (axis limits, colors, facet sizes) are shared from plot_titer_summaries_params.
Notebook: notebooks/plot_fold_changes.py - Marimo notebook implementing the visualizations
The pipeline implements multi-stage QC:
- Technical QC (Plate-Level): Removes wells and barcodes with insufficient sequencing depth or technical issues
- Curve Fit QC: Removes poorly-fitted neutralization curves
- Biological QC (Serum-Level): Removes replicate outliers and requires minimum replicates
All QC thresholds are configurable in config.yml. QC decisions are logged in YAML files and visualized in interactive notebooks.
-
Full details on current study: Kikawa, C. et al. Near real-time data on the human neutralizing antibody landscape to influenza virus as of early 2026 to inform vaccine-strain selection. bioRxiv. https://doi.org/10.64898/2026.02.18.706711
-
Assay Methodology: Loes, A. N. et al. (2024). High-throughput sequencing-based neutralization assay reveals how repeated vaccinations impact titers to recent human H1N1 influenza strains. Journal of Virology 98(5):e00689-24. https://doi.org/10.1128/jvi.00689-24
-
Previous Application:
- Kikawa, C. et al. (2025). Near real-time data on the human neutralizing antibody landscape to influenza virus to inform vaccine-strain selection in September 2025. Virus Evolution. https://academic.oup.com/ve/article/11/1/veaf086/8313343
- Kikawa, C. et al. (2025). High-throughput neutralization measurements correlate strongly with evolutionary success of human influenza strains. eLife. https://elifesciences.org/reviewed-preprints/106811
-
Pipeline Repository: https://github.com/jbloomlab/seqneut-pipeline
-
Curve Fitting Package: https://jbloomlab.github.io/neutcurve/
- flu-seqneut-2025: Data for September 2025 vaccine selection - https://github.com/jbloomlab/flu-seqneut-2025
For questions about this study:
- Caroline Kikawa (ckikawa@fredhutch.org)
- Jesse Bloom (jbloom@fredhutch.org)
For pipeline issues: Open an issue at seqneut-pipeline GitHub