Skip to main content

lfq_member_geometry/
lfq_member_geometry.rs

1//! Inspect aligned original observations in selected cached candidate groups.
2use koth_ms::{
3    alignment::{align_runs, RunInput},
4    config::AlignConfig,
5    input::{check_batch_mobility_scales, discover_runs, read_features},
6};
7use std::{collections::HashSet, path::PathBuf};
8fn main() -> anyhow::Result<()> {
9    let args: Vec<_> = std::env::args().skip(1).collect();
10    anyhow::ensure!(
11        args.len() == 5,
12        "usage: lfq_member_geometry BATCH CONFIG MEMBERS_TSV OUT_TSV ID,ID"
13    );
14    let cfg = AlignConfig::from_toml(&PathBuf::from(&args[1]))?;
15    let paths = discover_runs(&PathBuf::from(&args[0]))?;
16    let runs: Vec<_> = paths
17        .iter()
18        .map(|r| {
19            Ok(RunInput {
20                name: r.name.clone(),
21                features: read_features(&r.features_path)?,
22                hills: Vec::new(),
23                scan_times: Vec::new(),
24                rt_bounds: None,
25            })
26        })
27        .collect::<anyhow::Result<_>>()?;
28    check_batch_mobility_scales(
29        paths
30            .iter()
31            .zip(&runs)
32            .map(|(p, r)| (p, r.features.as_slice())),
33    )?;
34    let alignment = align_runs(&runs, &cfg.alignment);
35    let ids: HashSet<usize> = args[4]
36        .split(',')
37        .map(str::parse)
38        .collect::<Result<_, _>>()?;
39    let mut input = csv::ReaderBuilder::new()
40        .delimiter(b'\t')
41        .from_path(&args[2])?;
42    let mut output = csv::WriterBuilder::new()
43        .delimiter(b'\t')
44        .from_path(&args[3])?;
45    output.write_record([
46        "candidate_id",
47        "run_id",
48        "feature_idx",
49        "mass",
50        "rt",
51        "im",
52        "quality",
53        "is_primary",
54        "neutron_offset",
55    ])?;
56    for row in input.records() {
57        let row = row?;
58        let id: usize = row[0].parse()?;
59        if !ids.contains(&id) {
60            continue;
61        }
62        let r: usize = row[1].parse()?;
63        let f: usize = row[2].parse()?;
64        let feat = &runs[r].features[f];
65        let mz = feat.monoisotopic_mz();
66        let rt = feat.feature.rt_apex();
67        let im = feat.feature.im_apex();
68        let (mz, rt, im) = if r == alignment.reference_idx {
69            (mz, rt, im)
70        } else {
71            let a = &alignment.alignments[&runs[r].name];
72            (a.correct_mz(mz, rt), a.warp_rt(rt), a.correct_im(im, rt))
73        };
74        let mass = (mz - 1.007276466621) * feat.feature.charge as f64;
75        output.write_record([
76            id.to_string(),
77            r.to_string(),
78            f.to_string(),
79            mass.to_string(),
80            rt.to_string(),
81            im.to_string(),
82            feat.combined_score.to_string(),
83            row[3].to_string(),
84            feat.neutron_offset.to_string(),
85        ])?;
86    }
87    output.flush()?;
88    Ok(())
89}