Skill Tracer
Skills › Scientific Agent Skills › pyopenms › export_gnps_sirius.py

export_gnps_sirius.py

106 lines · skills/pyopenms/scripts/export_gnps_sirius.py at 5f2bc03

1#!/usr/bin/env python3
2"""
3Export for GNPS (FBMN) and SIRIUS
4
5Generate the input files required by downstream annotation tools:
6
7 gnps Feature-Based Molecular Networking: writes an MGF of MS2 spectra
8 (from a consensusXML linked across samples) plus the GNPS
9 quantification table.
10 sirius Writes a SIRIUS .ms file (and compound-info TSV) from mzML +
11 featureXML input for formula/structure elucidation.
13Usage:
14 python export_gnps_sirius.py gnps study.consensusXML --mzml s1.mzML s2.mzML --out-prefix gnps_out
15 python export_gnps_sirius.py sirius sample.mzML --featurexml sample.featureXML --out sample.ms
16"""
18import argparse
19import os
20import sys
22try:
23 import pyopenms as ms
24except ImportError:
25 print("Error: pyopenms not installed. Install with: uv pip install pyopenms")
26 sys.exit(1)
29def export_gnps(args):
30 cm = ms.ConsensusMap()
31 ms.ConsensusXMLFile().load(args.consensus, cm)
32 print(f"Loaded {cm.size()} consensus features")
34 mgf_out = f"{args.out_prefix}.mgf"
35 quant_out = f"{args.out_prefix}_quant.txt"
37 mapped = [pid for feature in cm for pid in feature.getPeptideIdentifications()
38 if pid.metaValueExists("map_index") and pid.metaValueExists("spectrum_index")]
39 if not mapped:
40 raise ValueError("GNPS export needs MS2 feature annotations (map_index and spectrum_index); plain linked MS1 features are insufficient.")
41 on_disk = {}
42 for pid in mapped:
43 i = int(pid.getMetaValue("map_index"))
44 if not 0 <= i < len(args.mzml):
45 raise ValueError("GNPS map_index is outside the supplied mzML list; preserve sample order.")
46 if i not in on_disk:
47 run = ms.OnDiscMSExperiment()
48 if not run.openFile(args.mzml[i]):
49 raise ValueError("GNPS requires readable indexed mzML files")
50 on_disk[i] = run
51 j = int(pid.getMetaValue("spectrum_index"))
52 if not 0 <= j < on_disk[i].getNrSpectra() or on_disk[i].getSpectrum(j).getMSLevel() != 2:
53 raise ValueError("GNPS spectrum_index must address an MS2 spectrum in its source map")
54 ms.GNPSMGFFile().store(args.consensus, args.mzml, mgf_out)
55 print(f"Wrote {mgf_out}")
57 ms.GNPSQuantificationFile().store(cm, quant_out)
58 print(f"Wrote {quant_out}")
59 print("Upload both to GNPS Feature-Based Molecular Networking.")
62def export_sirius(args):
63 out_ms = args.out or os.path.splitext(args.input)[0] + ".ms"
64 out_info = args.compound_info or os.path.splitext(out_ms)[0] + "_compounds.tsv"
66 exporter = ms.SiriusExportAlgorithm()
67 feature_files = [args.featurexml] if args.featurexml else []
68 try:
69 exporter.run([args.input], feature_files, out_ms, out_info)
70 except RuntimeError as e:
71 if "SourceFile" in str(e):
72 print("Error: the mzML lacks proper SourceFile annotation required by SIRIUS export.")
73 print("This is normal for synthetic/hand-built mzML. Re-export the file through")
74 print("a suitable converter and verify source format/native-ID metadata before retrying.")
75 print("Do not fabricate instrument provenance; vendor conversion alone is not a guarantee.")
76 sys.exit(2)
77 raise
78 print(f"Wrote {out_ms}")
79 print(f"Wrote {out_info}")
80 print("Run SIRIUS on the .ms file for formula/structure elucidation.")
83def main():
84 parser = argparse.ArgumentParser(description="Export for GNPS FBMN or SIRIUS.")
85 sub = parser.add_subparsers(dest="mode", required=True)
87 g = sub.add_parser("gnps", help="Export GNPS FBMN inputs")
88 g.add_argument("consensus", help="consensusXML linked across samples")
89 g.add_argument("--mzml", nargs="+", required=True, help="Source mzML files (with MS2)")
90 g.add_argument("--out-prefix", default="gnps_export", help="Output prefix")
92 s = sub.add_parser("sirius", help="Export SIRIUS .ms file")
93 s.add_argument("input", help="mzML file (with MS2)")
94 s.add_argument("--featurexml", help="Optional featureXML to group spectra")
95 s.add_argument("--out", help="Output .ms path")
96 s.add_argument("--compound-info", help="Output compound-info TSV path")
98 args = parser.parse_args()
99 if args.mode == "gnps":
100 export_gnps(args)
101 else:
102 export_sirius(args)
105if __name__ == "__main__":
106 main()