Skip to content

Release 3.6.0

Latest

Choose a tag to compare

@github-actions github-actions released this 30 Sep 08:58
· 141 commits to develop since this release
5d5cbff

Dear OpenMS-Users,

We are proud to announce the release of OpenMS 3.6.0.

Grab it here

In the following you find all important changes to the previous version:

OpenMS 3.6.0 is a substantial release and an important step toward our next major version. It addresses many feature requests,
including extended support for ion mobility (FAIMS and PASEF), arrow and parquet support, and new nanobind-based pyOpenMS bindings that enable more Pythonic and faster code.
A restructured build system now supports building OpenMS with vcpkg, replacing the deprecated contrib repository.
These are just a few of the many improvements in this release.

General:

  • FIX: Normalize source-file paths when converting them to file URIs, including Windows paths with mixed separators (#10275)
  • Updated the bundled PSI-MS controlled vocabulary from 4.1.155 to 4.2.2, including CV
    term-name changes in mzML/TraML output and vocabulary versions in mzTab-M/mzQC output
    (#8691)
  • New native readers for Bruker timsTOF .d and Thermo .raw, which FileConverter and many other
    tools use, and for imzML (library and pyOpenMS) and Bruker MALDI imaging (library only); no
    TOPP tool reads imaging data yet. See OpenMS Library for capabilities and runtime requirements.
  • New .idparquet, .featureparquet and .consensusparquet directory formats for native
    identification, feature and consensus-map storage, supported across TOPP tools
    (#9225, #9232, #9236, #9237, #9241, #9242, #9396, #9405).
  • New: modification definition records let a tool register a named, non-vocabulary modification (an
    Id, formula and site not shipped in unimod.xml/PSI-MOD/XLMOD) so that files naming it stay readable
    by another process. ResidueModification::toDefinitionString()/fromDefinitionString() (de)serialise
    one record; ModificationsDB::registerDefinition()/hasDefinedModification() register and query them;
    the new ModificationDefinitionIO class collects the definitions a run's identifications, features or
    consensus elements reference and attaches them to ProteinIdentification::SearchParameters under the
    "modification_definitions" meta value. ResidueModification::Provenance (DEFINED/CV/MASS_ONLY) records
    where a modification's description came from. idXML, featureXML, consensusXML and the
    .idparquet/.featureparquet/.consensusparquet bundles all carry the definitions their peptidoforms
    reference and register them before parsing any sequence; ProForma writes a tool-defined modification
    as its chemistry plus an INFO: name (e.g. "[Formula:C9H11N2O8P1|INFO:NuXL:U-H2O]") so a reader
    without the definition still gets the right mass, and resolves the name first when it is registered.
    OpenNuXL is the first consumer (see OpenNuXL below). BREAKING: an idXML, featureXML or consensusXML
    file naming such a modification is a hard load failure ("Cannot convert string to peptide modification")
    in an OpenMS build without this support (#10003, #10026, #10028, #10036, #10037, #10038, #10039, #10040)
  • Transparent .zip input for the XML formats (except mzIdentML) and Bruker .d directories;
    mzML, mzXML, mzData, featureXML, consensusXML, traML and mzIdentML output is compressed
    with gzip or bzip2 when the file name ends in .gz or .bz2, in any letter case. TOPP tools
    refuse a compressed output name for any other format (#7560, #9139, #9259, #10317).
  • BREAKING: OpenMS::String/StringView replaced by std::string/std::string_view;
    StringList is std::vectorstd::string. Use OpenMS::StringUtils free functions for
    conversion, splitting, trimming and substitution. String, StringConversions and
    StringUtilsSimple shim headers were removed (#9450, #9468). "Migrating from
    OpenMS::String" in the developer documentation lists the replacement for each member
    of String and the changes the compiler does not catch.
  • Core modularization reduces dependencies between KERNEL, FORMAT, PROCESSING,
    APPLICATIONS and QC. Shared utilities cover spectrum-type estimation, resampling,
    alignment defaults, filename recognition, ion-mobility metadata and data-processing
    provenance; compatibility entry points remain unless listed below (#10083, #10092,
    #10101). Include used types directly instead of relying on transitive headers;
    ToolInfo moved to DATASTRUCTURES/ToolInfo.h (#8946, #10083).
  • Shared-data lookup uses the compiled-in path, then the executable location, then
    OPENMS_DATA_PATH, preventing stale installations from overriding bundled resources.
    Missing-file errors identify the resolved directory (#9635, #9636, #9650).
  • A TOPP tool may keep its own sources and headers in a subfolder src/topp// with
    a CMakeLists.txt of its own, next to the single-file src/topp/.cpp layout. The
    class test of a tool-local class builds that class straight from the tool folder, so
    moving code out of the library does not cost it its test coverage.
    BREAKING (installed headers): OpenNuXL moved to src/topp/OpenNuXL/ and took the whole
    NuXL pipeline with it. Nothing outside this tool ever used it, so OpenMS/ANALYSIS/NUXL
    is gone: its thirteen headers are no longer installed and its classes are no longer
    part of libOpenMS. The five NuXL class tests are unchanged and still run (#10228).
  • The TOPP tool registry (which tools exist, and their TOPPAS/CTD category) is generated
    at build time from each tool's openms_topp_tool() declaration in
    src/topp/executables.cmake, instead of being hard-coded as 154 literal entries in
    ToolHandler::getTOPPToolList(). The build writes share/OpenMS/TOOLS/OpenMS.tsv from the
    declarations, so a build option that removes a tool (e.g. DISABLE_OPENSWATH) now also
    removes it from the registry, and a tool built outside this repository can register
    itself by installing its own .tsv next to it, without an OpenMS rebuild. ToolHandler
    looks for a registry at the installation's shared-data path, then next to the
    executable, then in the build tree if earlier locations contain no registry. It also
    reads .tsv files from directories named by OPENMS_TOOL_REGISTRY_PATH or the legacy
    OPENMS_TTD_INTERNAL_PATH, and caches the result after the first read.
    ToolHandler::getTypes() returns an empty list for a tool the registry does not know
    instead of throwing, matching its documented behavior. BREAKING (installed headers):
    ToolDescriptionFile, ToolDescriptionHandler and Internal::ToolDescription's
    foreign-executable fields (FileMapping, MappingParam, ToolExternalDetails,
    external_details, is_internal) are removed; nothing used them once the .ttd XML format
    was replaced by the generated TSV (#10216).
  • BREAKING: TOPPBase no longer takes a bool official, and neither do SearchEngineBase,
    TOPPOpenSwathBase, TOPPMapAlignerBase and TOPPFeatureLinkerBase, which only forwarded
    it. A tool is registered by its openms_topp_tool() declaration, the same declaration
    that builds it, so every tool that exists is registered and the flag named nothing.
    What it did instead was let a tool disown its own registration: OpenNuXL, PeakPickerIM,
    FeatureFinderLFQ, ProSE, QCEmbedder and ImageCreator each wrote an empty category into
    their CTD while the registry held a real one, so TOPPAS and every other CTD consumer
    saw them uncategorised; all six now carry their category. Out-of-tree code deriving
    from one of these classes drops the argument; something that is not a registered TOPP
    tool at all passes toolhandler_test = false instead, which is now the only way to skip
    the registry check (#10228).
  • BREAKING (installed headers): Move tool-only helpers without pyOpenMS bindings
    into the CometAdapter, DecoyDatabase, INIUpdater, NucleicAcidSearchEngine,
    OpenSwathInfer, QualityControl and UniPEFF folders. Their 21 headers are no longer
    installed and 20 implementations no longer live in libOpenMS or libOpenMS_CLI.
    Class tests travel with the tools; reusable inference tests also remain available
    in library-only builds (#10239). Helpers built on a private dependency of libOpenMS
    stay in the library even with a single tool as user, so that no tool has to find
    that dependency itself: the Arrow/Parquet-based OpenSWATH exporters, OSW Parquet
    reader/writer, Percolator scoring, XIPMParquetConsumer and ParquetTableComparator.
    OpenSwathExport, OpenSwathPeakMapExtractor, OpenSwathPercolatorScoring,
    OpenSwathWorkflow and ParquetDiff link nothing but OpenMS (#10247).
  • BREAKING: KNIME plugin support was removed. The ENABLE_PREPARE_KNIME_PACKAGE option
    and the prepare_knime_package targets are gone, as are the KNIME user tutorial, the
    LaTeX tutorial handout and the .knwf workflow collection under doc/tutorials (still
    available in the archived OpenMS/Tutorials repository). CTD generation is unaffected:
    -write_ctd and -write_cwl continue to work for Galaxy and CWL (#10135).
  • The API documentation no longer generates Graphviz (dot) graphs: include, included-by,
    collaboration, directory and graphical class-hierarchy graphs are gone, class
    inheritance diagrams use doxygen's built-in renderer, and the few hand-drawn
    diagrams are pre-rendered SVGs. This shrinks the HTML documentation by more than half
    and makes Graphviz optional; the 'doc_dot' target still builds all graphs. The
    OPENMS_HASDOXYGENDOT macro in the installed OpenMS/config.h, which was always 0 and
    no longer used, was removed (#10265).

Dependencies:

  • Minimum versions: CMake 3.24, Eigen 3.4.0, Boost 1.81, Arrow/Parquet 23 and nanobind
    3.1. Arrow/Parquet is required on all platforms; WITH_PARQUET was removed and
    versions 24+ are accepted. Compiler minimums are checked before vcpkg runs
    (#8366, #8370, #8422, #8679, #8699, #8991, #9042, #9095, #9196, #9221, #10112,
    #10133, #10221).
  • C++23 is required (#9037). The minimum compilers are GCC 13, Clang 17, AppleClang 16
    (Xcode 16) and Visual Studio 2022 17.14; configuring with an older one stops with a
    message.
  • Zstandard (zstd) is now a required dependency (mzML zstd binary array compression; #9033).
    It is found via its CMake config package or as a plain library (e.g. libzstd-dev).
  • Qt6 is required only with WITH_GUI=ON. Core/non-GUI code now uses standard C++ and
    platform APIs, libcurl for HTTP, boost-process for subprocesses and nlohmann/json.
    ZIP support uses libzip instead of minizip-ng. ExecutePipeline, INIUpdater and
    ImageCreator require the GUI build (#8808, #8841, #8936, #8937, #8938, #8939,
    #8940, #8965, #9041).
  • BREAKING: Qt-based public APIs were removed: Date's QDate constructor, QString-based
    filesystem parameters and QObject inheritance in ExternalProcess. Qt string interop
    and Qt5Port.h were removed; downstream code using Qt must link Qt6::Core explicitly.
  • LibSVM, Xerces-C++, SQLite, Eigen, CURL and nlohmann/json no longer leak into the
    public dependency interface. Shared-library consumers no longer require CURL/Xerces
    development packages; OpenSwathAlgo's Boost headers are private. See OpenMS Library
    for XMLHandler/Matrix API migration (#8511, #8547, #8558, #9712, #9737, #10084, #10103).
  • Boost is used header-only: OpenMS, its tools and tests no longer link the compiled
    Boost.Regex, Boost.Iostreams and Boost.DateTime libraries, of which only header-only
    parts were used. Building needs just the Boost headers, and a static Boost works as
    well as a shared one. A static Boost used to be linked into the shared libOpenMS,
    which could fail at link time: the archives are not always built with -fPIC, and
    their own dependencies (zstd, lzma, ICU) had to be installed as development packages.
    The BOOST_USE_STATIC option and the Homebrew static-Boost link fixups (#10187) were
    removed; passing -DBOOST_USE_STATIC now only triggers CMake's unused-variable
    warning (#3319).
  • BREAKING (installed headers): two internal headers are no longer installed, and two
    public ones no longer pull in a private dependency.
    DATASTRUCTURES/MatrixEigen.h (the Eigen::Map views on Matrix) and SYSTEM/SIMDe.h
    both already documented themselves as internal -- MatrixEigen.h says not to include
    it from public headers, SIMDe.h says to include it from .cpp files only -- and only
    libOpenMS sources and class tests include them; they stay where they are in the
    source tree, so in-tree builds are unaffected. FORMAT/GzipIfstream.h and
    FORMAT/Bzip2Ifstream.h no longer include <zlib.h> and <bzlib.h>: they store the
    library handle as an opaque pointer (a forward-declared gzFile_s*, resp. a void*,
    which is what bzlib's BZFILE is), so neither header needs the zlib/bzip2 include
    directory any more. External code that obtained Eigen, SIMDe, zlib or bzlib
    declarations through one of these headers has to include the library itself.
    find_package(OpenMS) no longer looks for Eigen at all: CMake omits the PRIVATE
    dependencies of a shared library from the exported link interface, and the OpenMS
    build always produces shared libraries, so OpenMSTargets.cmake names no Eigen
    target and a consumer needs neither the Eigen CMake package nor its headers.
    Previously find_package(OpenMS REQUIRED) failed outright when Eigen was absent.
    The lookup is kept for a static OpenMS, whose export does carry
    $<LINK_ONLY:Eigen3::Eigen>, and runs only for such an installation. Eigen is still
    required to build OpenMS and pyOpenMS (#10169).
  • The class-test framework (CONCEPT/ClassTest.h, ClassTestUtils.h,
    FuzzyStringComparator.h, MacrosTest.h) is no longer installed by default. It is an
    in-repo development tool: its static library was already EXCLUDE_FROM_ALL and is not
    exported, so an installation carried the headers without anything to link them
    against. cmake --install . --component OpenMSTestFramework_headers still installs
    them explicitly.
  • CMake package: find_package(OpenMS CONFIG) provides namespaced imported targets, OpenMS::OpenMS,
    OpenMS::OpenSwathAlgo and, via COMPONENTS GUI, OpenMS::OpenMS_GUI together with the Qt6
    modules it was built against; the un-namespaced names remain as aliases, while the
    bundled third-party libraries exist under the OpenMS:: namespace only. The build options
    of an installation are reported as OpenMS_WITH_GUI, OpenMS_WITH_HDF5, OpenMS_WITH_OPENTIMS,
    OpenMS_WITH_THERMO_RAW, OpenMS_WITH_OPENMP and OpenMS_BUILD_TOPP_TOOLS instead of the
    unprefixed WITH_* variables, which shadowed options of the consuming project, and the
    OPENMS_*_DIR variables no longer point into the prefix of a dependency found before them.
    The empty OPENMS_ADDCXX_FLAGS variable was dropped, and the Find modules of the OpenMS
    build (FindCOIN, FindLIBSVM, ...) are no longer installed next to the package: they served
    the package's former lookups of dependencies that are PRIVATE now. Consuming projects need
    CMake 3.24: find_package(OpenMS) rejects older versions with an explanatory message
    instead of failing later (#10158).
  • The source tarball contains vcpkg.json, vcpkg-configuration.json and vcpkg-overlays/, so
    it builds with an external vcpkg (OPENMS_USE_VCPKG=ON and its CMAKE_TOOLCHAIN_FILE) as well
    as against system packages.
    With OPENMS_USE_VCPKG=ON, a missing or non-vcpkg toolchain stops the configure with a
    message that names the alternatives (#10327).
  • The contrib (OpenMS/contrib) is deprecated and will be removed after 3.6: vcpkg
    builds the dependencies (see the vcpkg install guide and the CMake presets). It is no
    longer a git submodule; a build that still needs it takes it from a separate clone of
    OpenMS/contrib. Configuring with OPENMS_CONTRIB_LIBS, which requires
    OPENMS_USE_VCPKG=OFF, prints a deprecation warning (#10327).
  • The dev container (.devcontainer/) builds with the vcpkg presets on Ubuntu 24.04 instead of
    the contrib image. The Gitpod configuration and tools/quickbuild.sh/quickbuild-osx.sh, which
    built contrib, were removed; use cmake --preset -release instead (#10327).
  • Optional readers: WITH_OPENTIMS enables timsTOF support (default ON); opentims_DIR or
    opentims_ROOT selects a system installation before downloading. WITH_THERMO_RAW is
    ON by default on every supported platform; .NET 8+ is required at runtime (#9147,
    #9148, #9389, #9736, #10206).
  • WITH_WNETALIGN (default OFF) fetches wnetalign/wnet/pylmcf when enabled. WITH_ONNX
    (default OFF) requires an external ONNX Runtime installation (#8992, #9453, #10255).
  • HiGHS joins COIN-OR and GLPK as LP backends. LP_SOLVER=AUTO tries COIN-OR, GLPK,
    then downloads HiGHS (#9246).
  • ENABLE_TDL now defaults to OFF; set it to ON for CWL support (#9067).
  • macOS Intel builds discontinued; the minimum macOS version is 15 (Sequoia), for the installer
    and for the pyOpenMS wheels (#8450, #9037, #10202).
  • vcpkg builds use OpenSSL 3.6.4, and the Linux DEBs and the macOS package bundle it (the 3.5.0 DEBs
    bundled 3.0.2). vcpkg.json pins the version because the baseline's 3.6.2 is affected by the
    High-severity CVE-2026-45447.
  • The SQLite amalgamation vendored with SQLiteCpp is updated from 3.49.2, which
    CVE-2025-6965 affects, to 3.53.1, the version vcpkg builds. Builds with
    USE_EXTERNAL_SQLITECPP=OFF, the default, compile it into libOpenMS.
  • The bundled MaRaCluster is updated from 0.05.0 to 1.04.1 on Linux and Windows x86_64,
    and the macOS arm64 packages now include it too. MaRaClusterAdapter now converts the
    input files in MaRaCluster's index step, single-threaded, before it clusters them:
    1.04.1's Windows build crashes in about one run in ten when it converts several files
    in parallel (OpenMS/THIRDPARTY#113, #10259).

PyOpenMS:

  • BREAKING: boolean parameters are Python bools (#10116). Reading/writing Params
    now uses Python bool for boolean entries; assigning bool to non-boolean
    parameters raises TypeError; boolean-ness is preserved through Param updates,
    dict conversion, repr and INI round-trips. Param::setDefaults now gives existing
    string entries the defaults' valid strings (values unchanged), so a partial Param
    cannot override an algorithm's restrictions.
  • Fixed keyword defaults that did not match the wrapped C++ signatures. Four bindings
    declared a parameter with no default after parameters that had one: the transf
    argument of MultipleTesting.lfdr, spectra in both IDMapper.annotate overloads and
    plugin_consumer in SwathFile.loadMzML. They are now optional and carry the C++
    defaults (LfdrTransform.Probit, an empty MSExperiment and None). This also makes
    the generated type stubs valid Python again: _pyopenms_datastructures.pyi and
    _pyopenms_misc.pyi did not parse, both failing with "non-default argument follows
    default argument" (#10137).
  • BREAKING: MultipleTesting.lfdr's gridsize and cut defaults were 100 and 0.05, while
    the wrapped C++ signature (and its documentation) use 512 and 3.0, so Python callers
    who omitted them silently got different results than C++ callers. The bindings now
    use the C++ values (#10137).
  • Wheels are now built in nanobind split mode against the CPython 3.11 stable ABI:
    each platform ships a single cp311-abi3 wheel that serves Python 3.11 and every
    later version, instead of one wheel per interpreter. Adding a new CPython minor
    version normally only means adding it to the test matrix. nanobind's
    version-specific runtime moves into the separate nanobind-backend package, which
    is a new runtime dependency (nanobind-backend>=1.0, deliberately unpinned: newer
    backends serve extensions built against older backend ABI revisions).
    python_versions.json is now purely the list of versions the wheel is tested on;
    the version it is compiled for comes from abi3_minimum_cpython_version in
    pyproject.toml, so the two cannot drift apart.
  • Wheels now ship the .pyi type stubs and the py.typed marker. The pyopenms_stubs
    target was never part of the build, so PYOPENMS_GENERATE_STUBS=ON defined it and
    nothing ran it. It is now built with the pyopenms target, requires every extension
    module to import before generating anything, and py.typed is written only once the
    stubs exist. On Windows the stub generator and the ctest suite are handed the
    contrib bin/ directory, where a contrib built with shared libraries keeps its DLLs;
    a failed import now names the DLL that could not be resolved (#10141).
  • Stub generation uses named free_mem fallbacks on Windows and unsupported
    platforms, avoiding invalid definitions and re-exports in .pyi files.
    Generated stubs are syntax-checked after post-processing, before the build
    writes py.typed and its success stamp.
  • The type stubs export the aliases PeakMap, PeakSpectrum, Kernel_MassTrace,
    TM_DataPoint and MZTrafoModel_MODELTYPE, which mypy rejected with "does not
    explicitly export". The nested enums that pyOpenMS also exports at module level,
    such as FileType and LogType, now have their types; type checkers saw them as Any.
    The package no longer exposes names its own imports left behind: the modules c,
    ctypes, importlib and warnings, and the future feature annotations.
  • The wheel metadata declares the license as the SPDX expression BSD-3-Clause
    (Metadata-Version 2.4) and ships the license text as dist-info/licenses/LICENSE.
    Before, dist-info/LICENSE held only the string "BSD-3-Clause".
  • The Windows wheel is built against vcpkg's static libraries (triplet
    x64-windows-static-md-release) instead of the prebuilt contrib. Every dependency
    is linked into OpenMS.dll, so the only native DLLs the wheel bundles besides
    OpenMS's own are those of the MSVC runtime; the managed Thermo RawFileReader
    assemblies ship as before. Earlier Windows wheels carried an unrelated libcurl
    and a second zlib from the CI image. The dependency versions are those the main
    CI tests, e.g. Arrow 24, Xerces-C 3.3, bzip2 1.0.8 and libsvm 3.35 (#10327).
  • The Linux wheels (x86_64 and aarch64) are built in the stock manylinux_2_34 images
    against static vcpkg libraries instead of the contrib. They carry the libraries the
    other builds already test, e.g. bzip2 1.0.8 instead of 1.0.5 (CVE-2019-12900),
    Xerces-C 3.3.0 instead of 3.2.0 (CVE-2018-1311), libsvm 3.35 and Arrow 24 (#10327).
  • The Linux and Windows wheels take SQLiteCpp and SQLite from vcpkg, as the packages do,
    instead of the copies OpenMS vendors, which the macOS wheel keeps. All wheels carry
    SQLite 3.53.1 instead of 3.49.2 (#10327).
  • The custom type casters build their result lists with the checked
    PyList_SetItem() instead of the PyList_SET_ITEM() macro, which is unavailable
    under the Limited API. Behaviour is unchanged; failures release the partially
    built list rather than leaking it.
  • Building pyOpenMS in split mode requires CMake 3.26 or newer, for FindPython's
    Development.SABIModule component. This applies to pyOpenMS only -- the C++-only
    OpenMS build still requires no more than CMake 3.24. Configure with
    -DPYOPENMS_SPLIT_MODE=OFF for interpreter-specific modules; that mode refuses to
    produce a wheel, which would otherwise be mislabelled abi3.
  • BREAKING (source builds): nanobind 3.1 or newer is required; nanobind 2.x no longer
    works. The custom type casters follow the nanobind 3 caster protocol, whose
    from_python() takes uint32_t instead of uint8_t flags. pyproject.toml asks for
    nanobind>=3.1,<4 and py-build-cmake~=0.5.1, and the CMake fallback fetches nanobind
    v3.1.0. nanobind discovery enforces that minimum instead of accepting whatever
    find_package() returns, and reports the version it selected (#10133, #10221).
  • BREAKING: Python 3.10 is no longer supported. Wheels and source builds require
    Python 3.11 or newer; the wheel matrix covers 3.11 to 3.14. With 3.10 gone, pandas 3
    is the floor for DataFrame support (the dataframes/all extras and the test
    requirements ask for pandas>=3), so string columns consistently use the pandas str
    dtype and Arrow-backed large_string. The pyarrow 24.0.0/25.0.0 exclusions were
    dropped: both only worked around cp310-specific problems (#10119).
  • Autowrap/Cython replaced by nanobind, with PEP 561 type stubs, smaller binaries,
    faster builds and clearer errors. Fixed mutable-reference output parameters and
    restored missing default arguments; Python addons remain supported (#8699).
  • BREAKING: pyopenms.String removed; use Python str (string arguments also accept
    bytes). MzMLFile.storeBuffer() and MSNumpressCoder.encodeNP() return str directly;
    encodeNPRaw() returns bytes (#8602, #8604, #10109).
  • BREAKING: output arguments are return values. A method that filled a list or object
    passed by the caller now returns it, and the old call raises TypeError. For example:
    picked = oms.PeakPickerHiRes().pickExperiment(exp) # was pickExperiment(exp, picked, True)
    traces = oms.MassTraceDetection().run(exp) # was run(exp, traces, 0)
    trafo = aligner.align(feature_map) # was align(feature_map, trafo)
    decomps = decomposer.getDecompositions(262.1) # was getDecompositions(decomps, 262.1)
    proteins, peptides = oms.ProtXMLFile().load(path) # was load(path, proteins, peptides)
    ok, errors, warnings = oms.MzMLFile().isSemanticallyValid(path)
    names = oms.RNaseDB().getAllNames() # was getAllNames(names)
    FileHandler.loadFeatures() and the load() of MzTabFile, SqMassFile, DTAFile, DTA2DFile,
    EDTAFile, KroenikFile, ChromeleonFile and MRMFeatureQCFile changed the same way, 108 call
    forms in all; the type stubs show the new form of each. MzMLFile, FeatureXMLFile,
    ConsensusXMLFile and FileHandler.loadExperiment still fill their argument, and
    IdXMLFile.load accepts both forms (#8699). PeptideIndexing.run(),
    PosteriorErrorProbabilityModel.fit() and initPlots(), IDFilter.updateProteinGroups() and
    AbsoluteQuantitation.optimizeCalibrationCurveIterative() keep their 3.5 arguments but
    return a tuple, their 3.5 result followed by the updated lists. A check such as
    if model.fit(...) is therefore always true; unpack the tuple instead (#10260).
  • BREAKING: strings inside returned containers are str instead of bytes: list and set
    elements, dict keys (EmpiricalFormula("H2O").getElementalComposition() is now
    {'H': 2, 'O': 1}), Param keys and tags, list-valued string meta values and string data
    arrays, as well as the few methods that returned bytes. Methods that return a single
    String already returned str. Drop .decode() calls and b"..." keys and comparisons.
  • BREAKING: C++ char arguments take a one-character str and reject bytes, the only type
    3.5 accepted: PeptideEvidence.setAABefore("K"), not setAABefore(b"K"). This affects
    PeptideEvidence.setAABefore() and setAAAfter(), ResidueModification.setOrigin(),
    Ribonucleotide.setOrigin() and its constructor, GaussTraceFitter.getGnuplotFormula(),
    the separator of CsvFile.load() and AAIndex, whose methods are now static and which
    has no constructor: AAIndex.aliphatic("A") instead of AAIndex().aliphatic(b"A")
    (#10260).
  • BREAKING: LogConfigHandler has no constructor any more. Use
    oms.LogConfigHandler.getInstance().setLogLevel("ERROR") instead of
    oms.LogConfigHandler().setLogLevel("ERROR").
  • BREAKING: CrossLinksDB, ElementDB, ModificationsDB, ProteaseDB, ResidueDB, RibonucleotideDB
    and RNaseDB are callable singletons. ModificationsDB() and ModificationsDB.getInstance()
    return the one shared instance, but the names are no longer classes:
    isinstance(x, oms.ModificationsDB) is False, even for that instance. To check the type,
    use isinstance(x, type(oms.ModificationsDB.getInstance())).
  • BREAKING: classes removed from pyOpenMS. PeptideSearchEngineFIAlgorithm is now
    ProSEAlgorithm, after the C++ rename; its exit codes are
    ProSEAlgorithm.ProSEAlgorithm_ExitCodes (#9137). OPXL_PreprocessedPairSpectra is
    PreprocessedPairSpectra. DPosition1 and DPosition2 are a float and an (x, y) tuple.
    ArrayWrapperDouble and ArrayWrapperFloat are not needed any more, because peak data comes
    back as NumPy arrays. ProteinInference was deleted (#9482); PeptideAndProteinQuant covers
    protein quantification. KDTreeFeatureMaps, KDTreeFeatureNode and PeakIndex were dropped
    because they handed out references that could outlive their data; use
    FeatureGroupingAlgorithmKD, and fm[i] or exp[s][p] (#9803). IdentificationRuns,
    Internal_MzMLValidator, IsotopePattern, Seed, TheoreticalIsotopePattern, TraceInfo and
    OSWFile are no longer bound; use IDRipper.rip(), MzMLFile.isSemanticallyValid() and
    CoarseIsotopePatternGenerator.
  • BREAKING: MassTrace is now the kernel mass trace (getCentroidMZ(), getSize(), ...), and
    Kernel_MassTrace is an alias for it. 3.5.0's MassTrace was the FeatureFinder helper
    struct with max_rt and theoretical_int, which is gone.
  • BREAKING: CVTerm_ControlledVocabulary, the term class of ControlledVocabulary, is now
    ControlledVocabulary.CVTerm, and XRefType_CVTerm_ControlledVocabulary is now
    ControlledVocabulary.CVTerm.XRefType. A term prints as
    CVTerm(id='MS:1002252', name='Comet:xcorr') (#10284).
  • BREAKING: some nested enums were renamed. IonDetector.Type_IonDetector is now
    IonDetector.Type, FeatureDeconvolution.CHARGEMODE_FD and
    MetaboliteFeatureDeconvolution.CHARGEMODE_MFD are now CHARGEMODE, and
    OpenPepXLAlgorithm.OpenPepXLAlgorithm_ExitCodes,
    PeptideIndexing.PeptideIndexing_ExitCodes and
    PercolatorOutfile.PercolatorOutfile_ScoreType are now ExitCodes and ScoreType.
    getMapping() is now called on a member, e.g.
    IonSource.IonizationMethod.ESI.getMapping(), not on an instance of the enum class such
    as IonSource.IonizationMethod(). Thirty-two enums are no longer ints: int() raises
    TypeError, and == with a number is False without an error, so spec.getType() == 1 is
    now False for a centroided spectrum. Compare with a member such as
    SpectrumSettings.SpectrumType.CENTROID, or use .value. The 32 are AnnotationState,
    ChecksumType, DecoyTransitionType, DimensionDescription,
    IMFormat, MT_QUANTMETHOD, MZTrafoModel_MODELTYPE, DataFilters.FilterOperation,
    DataFilters.FilterType, DataProcessing.ProcessingAction,
    FeatureOverlapFilter.MergeIntensityMode, Instrument.IonOpticsType,
    InstrumentSettings.ScanMode, IonDetector.AcquisitionMode, IonDetector.Type,
    IonSource.InletType, IonSource.IonizationMethod, IonSource.Polarity,
    IsotopeModel.Averagines, the six MassAnalyzer enums,
    NonNegativeLeastSquaresSolver.RETURN_STATUS, Precursor.ActivationMethod,
    ProteinIdentification.PeakMassType, RetentionTime.RTType, RetentionTime.RTUnit,
    Sample.SampleState and SpectrumSettings.SpectrumType. Setters such as
    IonSource.setIonizationMethod() still accept an int (#8405, #8604).
  • The names List, Union, np, numpy, print_function and common_meta_value_types, which
    3.5.0 leaked from its implementation, and the streampos class are no longer exported.
  • Getters and ordinary element access return owned copies, restoring 3.5 semantics
    after the initial nanobind port. Modify and assign back, e.g.
    s = exp.getSpectrum(0); s.setRT(1.0); exp[0] = s
    Explicit live access uses spectrum_view(i), spectrum_views(), iter_spectrum_views()
    and corresponding accessors on feature/consensus maps, identification lists,
    transition groups and data arrays. Views keep parents alive; resizing/reordering
    invalidates them. See src/pyOpenMS/OWNERSHIP.md for rules and exceptions
    (#9792, #9794).
  • BREAKING: metadata enums are strongly typed; activation-method enum handling changed
    (#8405, #8604). LPWrapper, SolverParam and nested enums are no longer exposed; use
    high-level algorithms such as MRMFeatureSelector or FeatureDecharger (#9983).
  • DataFrame methods get_df()/get_df_columns() become to_df()/df_columns(); _mv methods use _view
    (e.g. get_data_mv() -> data_view()). Deprecated aliases remain (#8655, #8857).
  • Expanded DataFrame, Arrow/Parquet and zero-copy pyarrow support for spectra,
    chromatograms, mobilograms, experiments, feature/consensus maps, identification lists
    and transition groups. Added column discovery, container operations, hashing and
    snake_case scalar properties alongside existing getters/setters. Param supports dict
    construction and readable values, descriptions, restrictions and tags in str/repr
    (#8436, #8501, #8503, #8510, #8520, #8576, #8631, #8632, #8655, #8729, #9760, #10097).
  • DataFrame exports preserve long strings and use nulls for missing strings instead of
    'None', 'nan' or 'unknown'. Transition-group metadata handles str/bytes names equally;
    missing integer values produce float columns with NaN instead of errors (#8583).
  • New/expanded bindings include ProForma, USI, PEFF, XIC/XIM Parquet, BrukerTimsFile,
    FileInfo, IsoelectricPoint, FAIMSHelper, SpectrumNativeIDParser, FileHandler I/O,
    rasterize2D, quantification results, rank aggregation, chemistry/spectrum generation,
    RANSAC seeding, ppm helpers and DRange1 (#8504, #8525, #8543, #8637, #8648, #8684,
    #8690, #8737, #8738, #8739, #8762, #8894, #8907, #9275, #9379, #9407, #9412,
    #9415, #9419, #9602).
  • SpectrumAccessOpenMSCached is available and selected automatically for cached-data
    experiments. SystemSettings, TempDir and TempFiles are exposed; existing Python
    File settings/temp-file methods remain available (#9952, #10083).
  • Ion mobility: explicit-unit setters for per-peak arrays, drift-time/unit getters and
    zero-copy peaks_struct() for chromatograms/mobilograms. Fixed unit detection and
    preservation of live NumPy buffers on same-length drift-array replacement
    (#8423, #8825, #8842, #9792).
  • Fixed iterator/consumer lifetimes, borrowed-pointer ownership, out-of-range access,
    copy-constructor dispatch, AASequence string conversion and peak-buffer dtype
    offsets. set_peaks() keeps parallel data arrays consistent; enum/FileTypes and
    identification-file wrappers were completed or corrected (#8502, #8553, #8589,
    #8720, #9098, #9705, #9792, #9794, #9795).
  • Fixed SimpleSearchEngineAlgorithm.search(), which ran the search and then raised
    std::bad_cast because its ExitCodes enum was not registered. It returns
    (exit_code, protein_ids) and fills pep_ids; the codes are
    SimpleSearchEngineAlgorithm.ExitCodes (#10260).
  • Copying works again for the classes that supported it in 3.5.0. Sixty-one classes,
    most of them derived from DefaultParamHandler, ProgressLogger, CVTermList or CsvFile,
    either had no copy or inherited their base's, so copy.copy() and copy.deepcopy()
    failed or returned the base class: a DefaultParamHandler for PeakPickerHiRes, for
    example. FeatureDistance, MultiplexResolverAlgorithm and SwathMapMassCorrection, which
    returned a DefaultParamHandler too, get their own copy and deepcopy as well.
    The copy constructor X(other) is back on 136 classes that had it in 3.5.0,
    and new on four more. It stays gone from six: FeatureFindingMetabo and
    OpenSwathOSWWriter have no C++ copy constructor any more, and MSDataSqlConsumer,
    CachedSwathFileConsumer, MzMLSwathFileConsumer and PeakWidthEstimator delete objects
    that their C++ copy shares, so a copy and its original freed them twice
    (MSDataSqlConsumer(other) aborted Python in 3.5.0). copy.copy() and copy.deepcopy()
    raise TypeError for FeatureFindingMetabo and these four (#10260).
  • ControlledVocabulary.getTerm() and getTermByName() return a ControlledVocabulary.CVTerm
    again, as in 3.5.0, instead of a dict with id, name and description. With the dict,
    PeptideIdentificationList.to_df() and df_columns() kept PSI-MS accessions such as
    MS:1002252 as column names, although decode_ontology=True asks for the term names
    (#10284).
  • Restored C++ default arguments that the port had dropped, e.g. Residue.getMonoWeight(),
    getAverageWeight() and getFormula() without a residue type, MSDataCachedConsumer(filename),
    MetaInfo.getValue(name), FineIsotopePatternGenerator(threshold),
    ModificationDefinition(mod, fixed), TextFile(filename, trim_lines, first_n),
    IDFilter.removeDuplicatePeptideHits(peptides) and
    IDConflictResolverAlgorithm.resolve(features) (#10260).
  • Methods that update a list argument in C++ update a Python list passed there again, as
    in 3.5, besides returning the updated list, so both f(prots, peps) and
    prots = f(prots, peps) work. Without this, 3.5 code ran but its list silently stayed
    unchanged. Among them are IDFilter.removeUnreferencedProteins(),
    FalseDiscoveryRate.apply() and applyEstimated(), BasicProteinInferenceAlgorithm.run(),
    BayesianProteinInferenceAlgorithm.inferPosteriorProbabilities(),
    PeptideProteinResolution.run(), the PercolatorFeatureSetHelper feature methods and
    TransformationModel*.weightData(); pyopenms/addons/inout_lists.py lists all of them.
    Tuples and other sequences are not updated (#10260).
  • Restored calls that 3.5 had: MapAlignmentAlgorithmIdentification.align() aligns
    several maps, returning one TransformationDescription per map or, in the 3.5 form
    align(maps, transformations, reference_index), filling the list passed in, and
    setReference() takes a PeptideIdentificationList; IDFilter.filterHitsByScore() takes
    a list of ProteinIdentification; IDFilter.updateProteinReferences() is a deprecated
    alias of removeDanglingProteinReferences(). OPXLHelper.computeDeltaScores() takes the
    identifications to score (it took no argument), and the OPXLHelper methods update a
    PeptideIdentificationList in place again instead of a copy. TransitionTSVFile and
    TransitionPQPFile accept bytes file names, like the other file classes (#10260).
  • plot_spectrum() and mirror_plot_spectrum() plot annotated spectra again. They called
    .decode() on the peak annotations, which the bindings now return as str (#10260).
  • The deprecated MSExperiment.get_df() and get_df_columns() accept long= again, 3.5.0's
    name for long_format (#10260).
  • FeatureMap.to_peptide_df() and peptide_df_columns() export the PeptideIdentifications
    assigned to features, one row per identification, with the feature_id of each. Merge the
    frame with FeatureMap.to_df() on feature_id (#10260).
  • BREAKING: FeatureMap.get_assigned_peptide_identifications() returns the identifications
    unchanged. 3.5.0 added feature_id, ID_native_id and ID_filename as meta values to every
    peptide hit, to merge on all three. Merge FeatureMap.to_df() with to_peptide_df() on
    feature_id instead (#10260).
  • BREAKING: FeatureMap.to_df() indexes features by feature_id as an unsigned 64-bit
    integer, where 3.5.0's get_df() used text; the deprecated get_df() returns it as a
    column (#10260).
  • Thermo .raw reading is included in every wheel, Linux aarch64 among them; install
    .NET 8+ and set DOTNET_ROOT for non-standard locations. Optional WNet/Bruker bindings
    are exposed only when built; use hasattr() for feature detection (#9946, #9957,
    #10070, #10205, #10206).
  • See src/pyOpenMS/THREAD_SAFETY.md for concurrency rules and OpenMP oversubscription
    guidance; releasing the GIL does not guarantee thread safety (#9792, #9857).
  • BREAKING: IMSWeights, NoiseEstimator, Ratio, SpectrumAccessQuadMZTransforming and
    SplineSpectrum_Navigator are no longer bound. The methods that take or return them are
    still exposed but cannot be used: RealMassDecomposer(weights),
    SignalToNoiseEstimatorMedianRapid.estimateNoise(), ConsensusFeature.addRatio(),
    setRatios() and getRatios(), and SplineInterpolatedPeaks.getNavigator().
  • User guide: new pages on ProForma, USI and mzPAF, on glycans and glycopeptide fragment
    spectra, and on threads and parallel processing; new sections on PEFF files, FAIMS
    data, file summaries with FileInfo and the type stubs. Corrected the PEFFFile docstring
    (load() returns a tuple) and four PEFFEntry method docstrings that carried the
    description of their neighbour; the docstring examples of ProForma,
    IdentifierMSRunMapper and PEFFFile now render as code blocks.

TOPP tools:
Changes:
All tools:
- Boolean parameters accept flag syntax or explicit true/false, including overriding
an INI-enabled flag with '-flag false' (#10116).
- A JSON INI ('-ini x.json') reads input file lists in every shape they occur in. A CWL
runner emits a 'File[]' input as an array of File objects, which was rejected with a
json type error, so every tool with an input file list failed through its generated
CWL description once the list was set. Input and output file lists also accept an
array of path strings or an object with a 'path' array; a single file parameter takes
a path string or an object with a 'path' string (#10121).
- BREAKING: the advanced option '-instance ' was removed. It selected the numbered
'ToolName::' section of an INI file and was broken together with '-ini' (every tool
exited with ILLEGAL_PARAMETERS). Tools always read and write the 'ToolName:1:' section
used by all existing INI/CTD/TOPPAS files; the file format is unchanged. INIUpdater lost
its '-instance' option accordingly (fixes #10125).
- BREAKING: repeated command-line parameters use the LAST occurrence, with a warning;
appended workflow arguments can override earlier values (#8670, fixes #5345).
- An output file name ending in .gz, .bz2 or .zip is refused with an error unless the
format is written that way: mzML, mzXML, mzData, featureXML, consensusXML, traML,
mzIdentML and xquest.xml are compressed with gzip or bzip2 (the suffix in any letter
case), and an .oswpq bundle is a ZIP archive anyway. Before, idXML, trafoXML, pepXML,
MGF, text formats and every .zip name silently got uncompressed data under the compressed
name, and so did mzML that a tool streams to disk: FileConverter -process_lowmemory, the
lowmemory processOption of PeakPickerHiRes and NoiseFilterGaussian/SGolay, PeakPickerIM
on mzML input and OpenSwathWorkflow -out_chrom. The check covers output file lists too,
and replaces UniPEFF's own check for PEFF (#10317).
- Native Parquet I/O is supported by identification converters/filters/search adapters,
alignment and feature-linking tools; TextExporter and MzTabExporter read all three
native formats. Output follows input format where applicable (#9232, #9236, #9237,
#9241, #9396, #9405).
- Thermo .raw input: FileConverter, ProSE, SimpleSearchEngine, CometAdapter, SageAdapter,
ProteomicsLFQ and OpenSwathWorkflow. Bruker .d input: FileConverter, CometAdapter,
ProteomicsLFQ, OpenSwathWorkflow and PeakPickerIM (#8975, #9019, #9022, #9030,
#9126, #9418).
- Adapters find the search engines that ship with OpenMS. When an engine such as Comet or
Sage is not on the PATH, they look for it in share/OpenMS/THIRDPARTY, which the Linux and
macOS packages do not put on the PATH; without '-_executable' they stopped with
exit code 14 there (#10304).
- Tools that read Thermo .raw get the spectra centroided by Thermo's peak picking, as
FileConverter writes them by default. For profile spectra, convert with FileConverter
-RawToMzML:no_peak_picking first; in C++ and pyOpenMS, ThermoRawFile keeps the scans as
acquired unless its centroid option is set (#10303).
- On Linux and macOS, FileConverter reads Thermo .raw in-process by default
(-RawToMzML:reader inprocess): the packages there provide neither mono nor
ThermoRawFileParser on the PATH, which the external reader needs. The in-process reader
needs a .NET 8 runtime (with DOTNET_ROOT set if it is not in the standard location, e.g.
installed with Homebrew or into ~/.dotnet) and applies vendor peak picking unless
-RawToMzML:no_peak_picking, as ThermoRawFileParser did. Pipelines that set
-RawToMzML:ThermoRaw_executable must pass
'-RawToMzML:reader external' too; FileConverter warns when that option or
-RawToMzML:NET_executable is set but the in-process reader runs. Windows keeps the
external reader, which runs on the built-in .NET Framework.
- For per-peak IM input, PeakPickerHiRes warns and uses intensity-weighted mean IM;
Resampler, FeatureFinderCentroided, FeatureFinderMultiplex and MultiplexResolver
reject incompatible data (#9018).
ConsensusID:
- Accept empty idXML searches while preserving their run metadata; initialize
per-spectrum search settings even for MS files without peptide IDs. Handle maps
without identification runs and report invalid input combinations cleanly (#1427).
QPX export (ProteomicsLFQ, IsobaricWorkflow, ProteinQuantifier, ProSE):
- BREAKING: -out_feature_qpx replaced by -out_qpx, a directory containing QPX 1.1
quantms.psm.parquet, quantms.feature.parquet and quantms.pg.parquet views.
ProSE also writes per-input PSM/PG files and omits PG output when no groups were
inferred (#8973, #8974, #9146).
- Views use long format, bare run-file stems, scalar label/intensity columns and
grouped_runs matching experimental-design fraction groups. Required identity columns
(psm_id/feature_id/pg_id) follow the QPX reference algorithm; PSM/feature links are
bidirectional and protein-group identity uses full group membership (#9816, #9818,
#9825, #9843, #9864).
- Exports validate required values, identities, channel labels, run references and
experimental design; failed exports leave no partial collection. Batched streaming
substantially reduces memory use and supports deterministic parallel row generation
(n_threads: 1=serial, 0=auto, N=fixed) (#9692, #9697, #9698, #9699, #9817,
#9832, #9834, #9835, #9843).
ProteinQuantifier / PeptideAndProteinQuant:
- BREAKING: quantities are reported per assay (fraction_group, label), replacing sample
aggregation. CSV columns use abundance_fgrouplabel; total_abundances,
peptide_abundances and indistinguishable_proteins
_abundances were removed in
favor of fraction_group_abundances/peptide_fraction_group_abundances. MSstats and
QPX PG values already used run/assay data (#9864, #9869).
- New fractions:aggregate selects sum (unchanged default) or best (one fraction per
peptide and fraction group) (#9876).
- BREAKING: best_charge_and_fraction renamed to best_charge without an alias. It selects
one charge per modified peptide globally by assay coverage, then abundance, then
lower charge, retaining all observations of that charge (#9796, #9864).
- BREAKING: -ratios/-ratiosSILAC and their CSV columns removed; calculate contrasts from
abundance columns downstream, e.g. in MSstats (#9853).
- Fixed zero/undetected reporters biasing peptide selection and protein abundance,
merged-run PSMs assigned to the wrong file, and missing/misaligned fraction columns
in detailed peptide output. Removed quadratic quantification hotspots in large
isobaric experiments (#5518, #9658, #9672, #9683, #9796).
MzTabExporter:
- BREAKING: an assay is now a (fraction_group, label) unit covering all of that group's fractions,
and study_variable lists a sample's assays as its replicates. n_assays was previously
ExperimentalDesign::getNumberOfSamples(), so a sample measured in several fraction groups
collapsed onto a single assay and the replicate axis had nowhere to live -- the opposite of the
mzTab spec, which defines an assay as one measurement and study_variable as the grouping level
above it. Assay abundances are read from PeptideAndProteinQuant's fraction_group_level_abundance
array matched by (fraction_group, label) key rather than by position. Malformed arrays, keys
that are not 1-based and duplicated keys warn and fall back to the previous sample-grain value
instead of emitting a wrong number; an assay whose key the array lacks gets a null abundance,
without a warning. A sample split across several assays reports each assay's own value with a null
study_variable abundance, since no across-replicate aggregator has been agreed on, rather than
silently picking one of them. This also fixes peptide_abundance_assay[i] being assigned rather
than summed -- only ill-defined before, when an assay was forced 1:1 with a study variable, and
now a genuine sum over the ms_runs an assay spans. One shipped reference moves,
ProteomicsLFQ_1_subset_out.mzTab, whose design splits one sample's fractions across two fraction
groups; every other shipped mzTab reference has exactly one (fraction_group, label) per sample
and is byte-identical (#9868, part of #9864)
ProSE:
- BREAKING: renamed from PeptideDataBaseSearchFI. -out replaced by -out_idxml
(one per input), -out_qpx and -out_parquet (native tables); directory outputs require
unique input basenames (#9137, #9146).
- BREAKING: precursor:mass_tolerance and precursor:open_window_lower/_upper replaced
by non-negative precursor:mass_tolerance_lower/upper, applied as [-lower, +upper].
Calibration preserves signed bias and rejects implausible estimates. Open search
is detected above 1000 ppm or 1 Da (#9108, #9132).
- Changed defaults: precursor tolerance 20 -> 10 ppm per side, fragment tolerance
10 -> 20 ppm, isotope errors [-1, +1] -> [0, +2], and fragment:min_ion_index=2
(skips b1/b2/y1/y2). Widen the precursor window or enable calibration for larger
drift (#9088, #9188, #9634).
- Multi-file searches reuse one fragment index. Added semi-/non-specific search,
opt-in SNES indexing, open-search modification discovery, FASTA chunking and
modification-analysis output. Automatic calibration uses a parallel first pass
and empirical tolerance quantiles (#8414, #9109, #9112, #9122, #9177, #9185,
#9191, #9224, #9639).
- Search:decoys supports auto/generate/ignore (default auto), recognizing prefix and
suffix decoys. PSM and optional picked-protein FDR run after rescoring when enabled;
protein FDR uses the complete protein set across files (#9123, #9195, #9633,
#9634, #9644).
- Standalone -out_pin no longer requires Percolator. Expanded rescoring features include
isotope error, delta score, matched ion current, candidate-pool statistics and
complementary-ion evidence. BREAKING: matched_b_ions/matched_y_ions renamed to
matched_prefix_ions/matched_suffix_ions; scoring now counts all enabled ion series.
Fixed isotope-aware precursor errors, fragment-error tolerances and adapter lookup
(#9166, #9167, #9175, #9177, #9188, #9195, #9204, #9970).
- Added PSM annotations, search diagnostics and an end-of-run report with optional
-summary_out. Optimized indexing, modification enumeration, scoring and annotation;
fragment sorting honors -threads/OMP_NUM_THREADS. Failed merged writes preserve
per-file output; target/decoy-shared peptides no longer break merged idXML
(#9088, #9094, #9117, #9121, #9197, #9203, #9205, #9642, #9963, #10129).
- Stop codons ('*') in the database no longer abort ProSE or SimpleSearchEngine. A trailing
stop codon, as in genome-translated databases such as SGD's, is removed so the C-terminal
peptide is searched; peptides containing a stop codon or another symbol are skipped like
X/B/Z (#10262).
- Spectra activated by electrons (ETD, ECD, EThcD or ETciD, as recorded in the input file)
are also searched with c and z+1 (z-dot) ions, their main fragments; other spectra, also
in the same run, are not affected. Switch this off with ions:by_activation. The new
ions:add_zp1_ions adds z+1 ions for all spectra; ions:add_z_ions generates z ions
(y - NH3), which miss them. Calibration now scores with the configured ion series instead
of always b/y.
PercolatorAdapter:
- Defaults to in-process Percolator 3.08 rescoring; no executable is required for
idXML/mzid/idparquet PSM-level FDR. OSW input, peptide/protein-level FDR, -doc and
-init_weights run the percolator executable automatically, which is then required
(#9218, #10020). So do the options only the executable implements (-out_pout
,
-weights, -quick_validation, -static, -test_each_iteration, -override), which the
in-process backend ignored; it now writes the -out_pin file itself, and uses three
threads for -threads 0 instead of one.
- Both backends preserve input CalcMass and apply -score:fdr/-best_per_spectrum_only
consistently. Fixed in-process memory leaks and Debug-build Normalizer assertions
(#9240, #9257, #9997, #10012, #10049, #10057).
ProteomicsLFQ:
- BREAKING: automatic per-fraction normalization removed; output selection no longer
changes intensities. Normalize explicitly with ConsensusMapNormalizer on -out_cxml,
ProteinQuantification:consensus:normalize or downstream software (#9864, #9880).
- New -feat_dir checkpoints support resume, distributed -detect_only runs and final
combination without raw/ID inputs. Requires explicit -design and -fasta; excludes
spectral_counting. Reuse checks parameters, build, design and input size/mtime;
-force_recompute refreshes affected runs. -in/-ids are required only for runs
without reusable checkpoints (#9989).
- Added PIP-ECHO FDR-controlled match-between-runs with IM-aware matching, local adaptive
RT windows (disable via -PipEcho:local_rt:enabled false), corrected mass-error/RT
scoring and isotope-envelope evidence. PipEcho:max_training_points defaults to
50000 per fold; 0 disables the training cap (#9647, #9669, #9674, #9676, #9680).
- Bruker .d feature seeding via Biosaur2 and Seeding:algorithm; advanced
PeptideQuantification:seed_apex_rt_tolerance defaults to 5 s. tree_guided alignment
is available again; star remains the default (#8752, #9030, #9102).
- Linking RT tolerance uses the median FWHM across runs of a fraction, independent of
file order; unmeasurable widths fall back to 30 s. Inconsistent decoy affixes are
reported. Quantification-score SVM training is reproducibly shuffled before capping
(#9981, #9982, #9985).
- Fixed CalcMass preservation, mzTab export of non-string search settings and Biosaur2
FAIMS processing that lost spectra/chromatograms/settings and crashed downstream
feature finding (#9666, #9980, #9996, #9998, #10012).
ProteomicsLFQ and IsobaricWorkflow:
- Duplicate identifications of one (spectrum, peptidoform, charge) are reduced to the
best-scoring hit, avoiding duplicate FDR observations, reporter intensities and QPX
IDs; distinct peptidoforms are retained (#9871).
- Any supported result output can be requested alone; at least one is required (#9866).
- Fixed strictly_unique_peptides quantification; proteins with retained evidence are
represented as singleton groups. IsobaricWorkflow requires peptide uniqueness
annotations in its input (#9979).
IsobaricWorkflow:
- Added TMT 32-/35-plex (identity isotope-correction matrix until calibrated values are
supplied) and optional -count_sps_matches, recording sps_matched_ions and
sps_precursor_count (#4792, #9003, #9706).
- Fixed multi-file mzTab export, MS2 scans without MS3 or preceding MS1, and placeholder
features for unquantifiable IDs. Real all-zero reporter features are retained;
consensus column headers preserve fraction/sample design information
(#8519, #8592, #9817, #9824, #9836).
OpenNuXL:
- Localized adducts use named modifications with empirical formulas, including combined
definitions on already modified residues. idXML, featureXML, consensusXML and the parquet
bundles carry the definitions for reload; unlocalized hits retain CalcMass. BREAKING: older
releases without modification-definition support cannot load these files (#9992, #10003).
- Corrected RNA/DNA UV, DEB, NM and FA preset formulas, ion-series-specific localization
and shifted-immonium charge annotations (#8766, #9961, #10031).
PSMFeatureExtractor:
- Added numeric andes:
rescoring features (#9643).
- BREAKING: removed multi-search-engine mode; -in takes one file. Removed
-multiple_search_engines, -concat, -skip_db_check, -impute and -limit_imputation.
Use ConsensusID to combine search-engine results. PercolatorFeatureSetHelper's
concatMULTISEPeptideIds, mergeMULTISEPeptideIds, addMULTISEFeatures and
addCONCATSEFeatures were also removed from C++/Python.
MapAlignerIdentification, MapAlignerPoseClustering, MapAlignerTreeGuided:
- BREAKING: MapAlignerIdentification without a reference aligns to the input that shares the
most identified sequences with every other input, so that each input can be aligned to it;
ProteomicsLFQ ('star' alignment) and MS1LabeledWorkflow, whose docs already described a
single reference run, change with it. The per-peptide RT consensus over all inputs used
before left part of larger RT shifts uncorrected, because every input contributed to the
consensus it was aligned to. It remains available as 'algorithm:auto_reference consensus',
and is used when no input shares at least two sequences with every other input. If the
chosen input leaves other inputs with fewer alignment points than
'algorithm:auto_reference_min_points' (default 11, the smallest number of points to which
ProteomicsLFQ and MS1LabeledWorkflow fit an RT model), the other inputs and the consensus
are tried as the reference as well, and the one that gives the most inputs that many points
is used (fixes #2541).
- Optional -in_spectra_files/-out_spectra_files transform mzML alongside maps (#8536).
- Fixed tree-guided transformation/residual estimates and reference-state leakage
across repeated identification alignment calls. Pose clustering uses identity
transforms for unalignable files and reports parallel I/O failures cleanly (#7010).
OpenSwathWorkflow:
- Automatic in-memory wave scheduling with batchSize=0; innerBatchSize,
maxConcurrentSwaths and outer_loop_threads allow tuning. OSW buffering adapts to
available memory. Added SRM support, priority iRT sampling and Parquet chromatograms
(#8373, #8569, #8700, #8737, #9190).
- Precursor/transition IDs are consistent across outputs. PQP/OSWPQ retains stored IDs;
TraML/TSV is renumbered with original IDs retained as provenance (#9888).
- Fixed RUN.ID storage/joins, run-ID mismatches, empty compound/gene handling,
single-transition scoring, mass-error denominators and calibration-option conflicts
(#8688, #8790, #8830, #8831, #8833, #8869, #9190, #10050, #10055).
Peak pickers:
- PeakPickerIM adds Sage IM centroiding via bruker:ms1_centroid_mz_ppm and
bruker:ms1_centroid_im_pct, plus hill centroiding (-algorithm hill). Fixed shuffled
IM axes in trace picking, which produced incoherent/split peaks (#9022, #9380, #10051).
- PeakPickerHiRes allows peak cores without leading flank peaks (#8649).
- PeakPickerChromatogram: report_sn adds per-peak apex S/N in an SN array
(default false; unavailable estimates are -1.0) (#9780).
Feature finding and linking:
- BREAKING: FeatureFinderIdentification removes -id_ext, svm:*, SVM FDR estimation,
extract:rt_quantile and FFIDAlgoExternalIDHandler. run() now takes
(peptides, proteins, features, seeds, spectra_file); external/implied/unknown
feature categories and rt_delta/predicted_class/FDR_probabilities annotations are
no longer produced (#9995, #10011).
- BREAKING: FeatureLinkerUnlabeled/UnlabeledQT/UnlabeledKD/WNet remove -design. Link
fractions separately and combine with FileMerger -append_method append_cols, or
use ProteomicsLFQ for fractionated LFQ (#10043).
- BREAKING: FeatureFinderMultiplex/MultiplexResolver rename missed_cleavages to
max_nr_labelled_aas (#8911).
- FeatureFindingMetabo/MetaboliteFeatureDeconvolution add opt-in RT-overlap reference
options for short isotope/adduct traces; FeatureFindingMetabo adds local_im_range
for ion mobility (#4483, #8758, #9711).
- FeatureFinderMetaboIdent accepts an optional Adduct TSV column, including multimers
(#9382). FeatureFinderCentroided uses about 20% less RAM (#9159).
- Fixed swapped mass-trace/isotopic-pattern tolerances in FeatureFinderAlgorithmPicked
and charge-merging crashes in FeatureLinkerUnlabeledKD (#8944, #9247, #9250).
MultiplexResolver, MS1LabeledWorkflow:
- BREAKING: algorithm:mz_tolerance and algorithm:rt_tolerance (re-exported by
MS1LabeledWorkflow as resolver:mz_tolerance / resolver:rt_tolerance) are now double
instead of int, with a minimum of 0.0. They were always read into double members, so a
fractional value was rejected on the command line and, in an INI file, silently stored as 0 --
disabling the blacklist proximity check. An INI carrying them as type="int" must be updated
to type="double" or the tool will refuse to start
Other tools:
- CometAdapter merges compatible variable modifications for faster searches and supports
DDA-PASEF .d input; native-ID translation avoids Comet mzParser crashes (#8407,
#9126, #9168). SageAdapter thread registration corrected (#8441).
- SimpleSearchEngine adds semi-/non-specific search and PSM annotations; candidate
processing scales better across threads (#9112, #9117).
- AssayGeneratorMetabo now applies its feature/masstrace and precursor m/z/RT filtering
options, which were previously ignored (#10120).
- FileConverter adds MSP and sqMass-to-XIC Parquet conversion, plus Bruker aggregation,
centroiding and range filters. FileInfo adds assigned/unassigned ID counts, FASTA
sequence statistics and nucleic-acid support; progress logging restored (#8395,
#8643, #8650, #8829, #9707).
- TextExporter supports XIC Parquet and fixes USI run resolution; IDMerger preserves
input order (#8470, #8737, #8738, #9240, #9657).
- MSstatsConverter exposes -remove_shared_peptides (default true), accepts design
subsets, and warns, as does ProteomicsLFQ, about BioReplicate IDs shared across
conditions (paired designs) (#7314, #8846, #9068, #9864).
- Epifany clarifies -exp_design behavior and reports unidentifiable input as an error
instead of aborting (#9864, #9901).
- MetaboliteAdductDecharger adds multimer detection (max_multimer/multimer_log_penalty).
MetaboliteSpectralMatcher adds optional CCS filtering (ccs_error_percent=0 disables
it), automatic 1/K0-to-CCS conversion and CCS mzTab columns; missing observed CCS
does not filter candidates (#8784, #8888, #9412).
- IDConflictResolver adds rank_aggregation; MaRaClusterAdapter records consensus-spectrum
origins/cluster size; OpenSwathChromatogramExtractor uses CompoundName for metabolite
chromatogram identifiers (#8864, #8907, #9271).
New tools:
- MS1LabeledWorkflow: one-command quantification of MS1-labeled (SILAC, Dimethyl, ...) experiments,
the counterpart of ProteomicsLFQ and IsobaricWorkflow: per run FeatureFinderMultiplex, IDMapper,
IDConflictResolver and MultiplexResolver, then per-fraction alignment and linking (channels kept as
sub-features, fractions combined column-wise), protein inference/FDR and quantification, written as
consensusXML, mzTab and/or a QPX collection; optional match between runs (-match_between_runs)
lets unidentified multiplets take over identifications from other runs. The peptide identity is
the unlabeled sequence (the label belongs to the channel): label modifications are removed after
the multiplet resolution and the label state is kept on every identification (labeled_sequence,
removed_labels, label_channel), reported as mzTab opt_global columns and QPX cv_params; PSM-level
output (mzTab PSM section, QPX psm view) reports the peptidoform as searched, feature-level
output the peptide identity. Each column header describes its channel's labels
(channel_description). A spectrum match mapped onto several multiplets stays with the closest
one; distinct spectra of one peptide on distinct multiplets are all quantified. The reported quantity
is the channel ratio, computed as MaxQuant does (median of the evidence ratios per peptide, median
of the peptide ratios per protein group, a minimum ratio count, and a normalized variant) rather
than as a ratio of aggregated intensities, so no ProteinQuantifier run is needed for it. The
reference channel is reported with its ratio of 1.0 wherever another channel was measured
against it, so a consumer sees a complete set of channels; the
per-channel abundances are reported next to it as summed intensities. Checks up front that
the labels were searched as modifications, that the design enumerates the channels of -labels,
and that -out_qpx is only requested for labels in the QPX vocabulary (#10044).
Channel/pattern order and unique input/design basenames are validated before processing.
Native Thermo RAW input is supported with WITH_THERMO_RAW and a .NET 8+ runtime;
scan metadata and MS1 peaks are extracted in one RAW read, including spectrum-reference repair.
FAIMS CVs are preserved through ID mapping, multiplet completion and linking. A protein's
reference-channel ratio requires a comparison passing min_ratio_count in that fraction group (#10047).
- FeatureLinkerWNet: Wasserstein network-flow grouping across label-free maps; optional,
built with WITH_WNETALIGN=ON (#8992, #10255).
- UniPEFF: UniProtKB XML-to-PEFF conversion with sequence, modification, variant and
disulfide-bond annotations; disulfide connectivity is reported by default via bond-ordered
half-cystine labels; optional global annotation identifiers and external
unimod.obo (#9649, #9829). UniProt isoforms are reconstructed from their splice-variant
features and emitted as additional entries after the parent entry, with the features
UniProt annotates on that isoform; disable with -omit_isoforms (#9966, fixes #9830).
- OpenSwathInfer: peptidoform, peptide, protein and gene inference (#9280).
- OpenSwathExport: TSV/Parquet result export (#9377).
- OpenSwathPercolatorScoring: Percolator scoring of OpenSWATH results (#9695).
- OpenSwathPeakMapExtractor: targeted full-peakmap extraction (#9684).
- TransitionListEvidenceFilter: filter transition lists using mzML evidence (#9161).
- DIAuditor: quality metrics of DIA runs per run and per isolation window (MS1 sampling,
window layout incl. FAIMS/1/K0, cycle times, TIC and peak-count distributions), a
re-implementation of D. L. Tabb's DIAuditor. Reads mzML, Thermo .raw and Bruker timsTOF .d
(diaPASEF) and writes its per-run and per-window tables and an mzQC file with the PSI-MS
DIA metrics (MS:4000190-MS:4000199). Warns when spectra carry several isolation windows
(e.g. MSX; demultiplex first) and when diaPASEF windows have an ion mobility array but no
ion mobility range, as TIMSCONVERT writes them, so windows differing only in ion mobility
count as one (#10271, #10312).
- ParquetDiff: compares two Parquet tables by primary key with a numeric tolerance instead of exact
bytes. FuzzyDiff is string-based and cannot read Parquet, which left the QPX psm/feature/pg exchange
surface with no comparison route at all. The key is auto-derived from a QPX file's own file_type
metadata or given explicitly via -pk for any table; matched rows are compared cell-by-cell recursing
into list/struct columns, and schema drift (added/removed/retyped columns) is reported separately
from value drift. Tolerance mirrors FuzzyStringComparator -- either -ratio or -absdiff must hold, so
a zero-vs-epsilon comparison stays expressible -- and two NaNs compare equal. List-valued key columns
(e.g. QPX's grouped_runs) are canonicalized by sorting before matching, and a duplicate primary key
is reported as an error in its own right. -schema validates a single file against a named QPX view
instead of diffing two files. Exit code is EXECUTION_OK when the comparison (or -schema validation)
passes and every input holds at least -min_rows rows, if set; otherwise INCOMPATIBLE_INPUT_DATA, so it
drops into CTest the way FuzzyDiff does (#9865, part of #9864)
- ParquetConverter: converts featureXML and consensusXML files to Parquet directories and
back (#8970).
Removed tools:
- BREAKING: GenericWrapper and its external-tool definitions removed; use direct TOPP
invocations or TOPPAS workflows (#8981, #9735).
- BREAKING: QuantmsIOConverter removed (renamed QPXConverter in #8756, then removed). QPX
output comes from the -out_qpx option of ProteomicsLFQ, IsobaricWorkflow, MS1LabeledWorkflow,
ProteinQuantifier and ProSE; the QPXFile export API remains (#9693).
- BREAKING: TriqlerConverter, ProteomicsLFQ -out_triqler and TriqlerFile removed. Use
-out_msstats, or convert -out_cxml/-out_qpx downstream (#9898).

GUI tools:

  • TOPPView/TOPPAS accept generic Qt command-line options. TOPPView shows selectable MS2
    precursor markers and isolation windows in 2D; the splash screen is dismissible
    (#4963, #9715, #9723).
  • Fixed TOPPAS shutdown crashes, TOPPView extreme-zoom crashes and large-file context-menu
    delays, non-string metadata conversion, Windows spinbox sizing and Annotation1DItem
    DLL exports (#8997, #9206, #9263, #9723, #9831).
  • Fragment annotations survive viewing and spectrum switches unchanged, including charge
    notation and multiline comments. Corrected duplicate charge display, neutral-loss ion
    ordinals and sequence diagrams with multiple modifications (#8766).
  • TOPPView's scan list shows each MSn scan under the scan its precursor refers to (mzML
    spectrumRef), so SPS-MS3 scans that are interleaved with later MS2 scans appear under their
    own MS2 scan. MS2 scans that follow an MS3 scan are no longer nested under the previous MS2
    scan (#4215).
  • BREAKING: SwathWizard and FLASHDeconvWizard removed; run the TOPP tools they wrapped,
    such as OpenSwathWorkflow and FLASHDeconv, directly (#9472).

OpenMS Library:

  • mzML: read and write Zstandard (zstd) compressed binary data arrays (MS:1003780 zstd,
    MS:1003781 byte-shuffled zstd, MS:1003782 dictionary-encoded zstd and MS:1003783-MS:1003785
    MS-Numpress followed by zstd). Writing is enabled with PeakFileOptions::setZstdCompression(),
    FileConverter -zstd_compression or FileFilter -peak_options:zstd_compression (#9033).
  • Added GlycanStructure and TheoreticalGlycanSpectrumGenerator for composition-based
    diagnostic/oxonium and bounded B/Y/C/Z ions, structural and internal glycan fragments,
    and HCD/ETD/EThcD glycopeptide backbone retention with configurable stubs (#10176, #10204).
    Includes mzPAF annotations, neutral losses, resource limits, and pyOpenMS bindings.
  • Added DIon, VIon and WIon to Residue::ResidueType, with side-chain loss methods on Residue,
    fragment mass calculation in AASequence and MzPAF, TraML support and pyOpenMS bindings (#10175).
    Added:
    • File type aliases are now honoured end to end: TOPP tools accept an alias wherever they accept
      the preferred extension (and a tool declaring an alias accepts the preferred extension),
      TOPPAS connects tools and input-file lists by format rather than by literal suffix, GUI file
      dialogs offer every accepted extension ('FASTA file (*.fasta *.fa *.faa)'), and INI/CTD
      supported_formats list them for input parameters. Output parameters keep the single canonical
      extension, so what OpenMS writes is unchanged (#490)
    • FileTypes::sameFormat() compares two declared format strings by type when both are recognized and
      literally otherwise, so 'fasta' matches 'fa' while two different custom extensions stay distinct.
      FileTypes::supportsCompressedReading(type, compression) reports whether a reader accepts that
      specific container, and FileNameUtils::compressionType() reports which container a filename
      carries (hasCompressionSuffix() answers the same question as a bool). The pair takes the
      container because support is not uniform: the XML-based readers decompress .gz, .bz2 and .zip
      transparently through CompressedInputSource (except the mzIdentML reader, which parses the
      file itself and cannot read a compressed one), while Bruker TDF accepts only a '.d.zip' archive
      that BrukerTimsFile unpacks. A compression suffix alone therefore no longer implies compatibility,
      so '.mgf.gz' is rejected where '.mzML.gz' is accepted, and both the CLI and TOPPAS apply the
      same rule. Compressed output is unaffected: XMLFile still selects gzip/bzip2 by filename (#490)
    • FileTypes::supportsCompressedWriting(type, compression) is the writing counterpart: XMLFile
      compresses the formats marked with the new FileProperties::COMPRESSED_WRITEABLE with gzip or
      bzip2 and never writes a ZIP archive, while an OSWPQ bundle is always one. TOPPBase uses it to
      refuse output names whose writer would store plain data under them (#10317)
    • FileTypes: a file type may now register additional accepted extensions besides its preferred
      one. FileTypes::typeToExtensions() lists them (preferred first) and nameToType() resolves them
      case-insensitively: 'fa'/'faa' for FASTA, 'pep.xml' for pepXML, 'prot.xml' for protXML and
      'pqt' for parquet. typeToName() still returns the single preferred extension, which is what
      OpenMS writes, and formats that merely share an extension family (e.g. CSV and TSV) remain
      distinct types (#490)
    • MultiplexResolverAlgorithm: the multiplet completion and quant/ID conflict resolution of the
      MultiplexResolver tool as a library class (DefaultParamHandler with the tool's 'algorithm' and
      'labels' sections), shared by MultiplexResolver and MS1LabeledWorkflow (#10044)
    • ProteinGroupArrowExport: the QPX pg view fills its previously empty additional_intensities column
      with the channel ratio of that (protein group, fraction group, label) -- named ratio and
      ratio_normalized under the row's own channel label -- and the number of contributing peptides
      with cv_params/ratio_count, when the producer annotated them (MS1LabeledWorkflow does). Rows
      without them are written as before (#10044)
    • MS1LabelState: the label-state meta values of an MS1-labeled identification (labeled_sequence,
      removed_labels, label_channel) and the peptidoform a hit was matched with. The mzTab PSM
      section and the QPX psm view report that matched peptidoform (and derive the psm identity
      from it); the QPX feature and psm views write the three values to the previously empty
      cv_params column (ArrowIOHelpers::qpxCvParams). Identifications without a label state are
      exported as before (#10044)
    • BrukerTimsFile (WITH_OPENTIMS): DDA-/DIA-PASEF and raw 4D access, tiered scan-to-1/K0
      calibration, optional neighboring-MS1-frame aggregation, IM hill centroiding and
      frame/RT filtering. Range-limited reads reduce I/O/memory; aggregation is faster.
      DDA native IDs use 'frame=... scan=... precursor=...' for unique identification;
      exported PASEF spectra are marked CENTROID (#8975, #8999, #9151, #9154, #9233,
      #9243, #9380, #9387).
    • ThermoRawFile: native .raw reading with openms-thermo-bridge 0.3.1 and .NET 8+.
      Preserves sample/method/trailer metadata, SHA-1 provenance, scan/instrument metadata,
      MSn/SPS precursor hierarchy and supplemental activation. Optional centroid charges,
      independent noise arrays and auxiliary detector/PDA data support mzML export.
      Fixed mixed-activation hierarchy reconstruction. Managed assemblies are installed
      with OpenMS; OPENMS_THERMO_MANAGED_DIR overrides their location (#9389, #9623,
      #10070, #10077, #10079).
    • Imaging: IonImage, MSImagingGeometry/Region/Experiment, imzML read/write and on-disc
      access, streaming consumers, multi-region extraction and Bruker MALDI .d support.
      imzML preserves auxiliary float arrays/IM units; warns about unsupported integer/
      string arrays and duplicate pixels (first spectrum used for geometry, all remain
      index-accessible). Both loaders reject zlib-compressed m/z/intensity arrays
      (#9381, #9383, #9521, #9654, #9903, #9908, #10002).
    • Native Parquet: PSMArrowIO, FeatureMapArrowIO, ConsensusMapArrowIO, ArrowSchemaRegistry,
      ArrowExport and ProteinGroupArrowExport. Preserves map metadata/unique IDs and
      identification run links; rejects duplicate run IDs and dangling feature references.
      Native bundles no longer claim a QPX version (#8655, #8970, #8973, #9225, #9232,
      #9242, #9864).
    • Percolator: in-process rescoring with combined or separate training/scoring and model
      persistence. Calls across instances must be serialized because of process-wide
      Percolator state (#9218).
    • OpenSWATH: reusable inference/export APIs ported from PyProphet; KDE, ranking and
      multiple-testing statistics; workflow scheduling, memory-aware OSW output and
      CalibrationWorkflow. Calibration RT windows default to 60-600 s (set either bound
      to 0 to disable it). Added .oswpq archives, .xic chromatograms, .xim mobilograms,
      per-compound RT ranges and a FAIMS-aware adapter for pre-loaded experiments
      (#8524, #8737, #8743, #8813, #8871, #9190, #9271, #9280, #9377, #9842, #9932).
    • PipEchoAlgorithm exposes the new match-between-runs method (#9647).
    • FeatureGroupingAlgorithmWNet/WNetMatcher expose the Wasserstein grouping methods;
      optional, built with WITH_WNETALIGN=ON (#8992, #10255).
    • Optional C++ ONNX predictors for AlphaPeptDeep RT, MS2 and CCS models, with verified
      model downloads and AASequence-based modification support. Unsupported modifications
      are rejected; Python bindings are not yet available (#9453, #9765).
    • Standards: ProForma v2 parsing/serialization (LOSSLESS/CANONICAL), AASequence
      conversion, mass calculation and JSON support; MzPAF peak annotations; USI parsing/
      construction; SpectrumNativeIDParser (#8637, #8648, #8684, #8686).
    • MzPAF accepts and writes d/v/w satellite ions and the da/db/wa/wb subtypes,
      including validation, peak annotation conversion and pyOpenMS bindings (#10175).
      Satellite mass calculations use d = a + H - radical side-chain loss and
      v = y - HR. Direct residue/sequence mass and formula APIs reject unsupported
      or modified cleavage residues instead of returning the parent-ion mass.
      Theoretical spectrum generation for satellite ions remains unsupported.
    • Added IsoelectricPoint, hydrophobicity profiles, protein SequenceCoverage,
      ModifiedSincSmoother, programmatic FileInfo, CCS conversion and MSExperiment
      rasterization. Extended PeakFileOptions and on-disc filtering, peak annotations,
      LightTransition APIs, MetaInfo iteration and ParamIterator STL support
      (#8217, #8362, #8421, #8512, #8526, #8544, #8621, #8728, #8735, #8739,
      #9179, #9419, #9602).
    • IMPeakType tracks IM processing state independently of layout; IMFormat::CENTROIDED
      is deprecated. IM units are recognized across vendor array naming conventions
      (#9007, #9181).
    • I/O: MGF SEQ fields round-trip; MSP reads numeric CCS and long lines; consensusXML
      preserves protein-group abundance arrays. PeptideIndexer settings and upstream
      IDMapper processing metadata are retained (#8489, #8816, #8852, #9186, #9844).
      API and behavior changes:
    • XMLFile::save_() matches the .gz/.bz2 suffix in any letter case (FileNameUtils::compressionType()),
      as input handling does, so 'x.mzML.GZ' is written gzip-compressed rather than plain.
      MSDataWritingConsumer and PlainMSDataWritingConsumer throw Exception::UnableToCreateFile for a
      file name ending in .gz, .bz2 or .zip: they stream uncompressed mzML, which such a name would
      mislabel. Use MzMLFile::store() for compressed mzML (#10317).
    • BREAKING: File settings methods move to SystemSettings (getSystemParameters,
      getTempDirectory, getUserDirectory, findDatabase, getOpenMSHomePath,
      getOpenMSConfigDir). File::TempDir becomes TempDir; File::getTemporaryFile becomes
      TempFiles::getTemporaryFile. Old C++ entry points are removed (#10083).
    • BREAKING: XMLHandler no longer derives from xercesc::DefaultHandler; use
      onStartElement(const char16_t*, const XMLAttributes&), onEndElement() and
      onCharacters(). Matrix uses std::vector storage and no longer inherits Eigen
      (#8511, #8547, #8558, #9712, #9737).
    • BREAKING: database getInstance() methods return const pointers (ElementDB, ResidueDB,
      ModificationsDB, CrossLinksDB, RibonucleotideDB, MonosaccharideDB, ProteaseDB and
      RNaseDB); use const T* or auto. Runtime modification interning is thread-safe.
      ElementDB::addElement() removed: use local CoarseIsotopePatternGenerator::
      setIsotopeOverride(element, distribution), avoiding global isotope-state mutation.
    • BREAKING: PeptideIdentification::get/setBaseName removed; use the run's
      setPrimaryMSRunPath() and IdentifierMSRunMapper. IDFilter::updateProteinReferences
      renamed to removeDanglingProteinReferences; Exception::InvalidSize requires a
      context message (#8500, #8437, #9660).
    • BREAKING: FileWatcher moved from SYSTEM to VISUAL; plain metadata/platform enums
      became enum classes; PeptideAndProteinQuant channel IDs are UInt; ChromatogramPeak
      intensity changed from double to float (#8516, #8591, #8622, #8857).
    • BREAKING: IMFormat::CONCATENATED/MULTIPLE_SPECTRA renamed to IM_PEAK/IM_SPECTRUM;
      MIXED removed. determineIMFormat(experiment, ms_level) requires an explicit MS level
      (#8993, #9011).
    • BREAKING: QuantmsIO renamed to QPXFile; LinearResampler removed in favor of
      LinearResamplerAlign (#8756, #9153).
    • BREAKING: OpenSwath::Scoring removes pointer/C-array overloads of NormalizedManhattanDist,
      RootMeanSquareDeviation, SpectralAngle, normalize_sum and calcxcorr_legacy_mquest_;
      use std::vector overloads. ChromatogramExtractor::prepare_coordinates(TargetedExperiment)
      removed; convert via OpenSwathDataAccessHelper::convertTargetedExp and use
      LightTargetedExperiment (Python API unchanged) (#9252, #9271).
    • BREAKING: NeighborSeq owns its peptide vector, fixing dangling references but changing
      class layout; C++ consumers must rebuild. IDBoostGraph rejects missing target_decoy
      annotations; BayesianProteinInferenceAlgorithm requires one merged identification
      run (#8480, #9488, #9613).
    • Charged EmpiricalFormula isotope generation retains its 3.x implicit-hydrogen behavior
      with a deprecation warning; use addChargeAdduct(count, adduct="H") for the explicit
      replacement before OpenMS 4.0 (#4449, #9714, #9719).
    • SignalToNoiseEstimatorMedian interpolates within histogram bins (floor 1), changing
      S/N-derived peak-picking/scoring results. Corrected MassTrace FWHM interpolation
      affects feature widths, peak filtering, linking tolerances and some quantified
      intensities; traces must be sorted along their x axis (#9781, #9967, #10052).
    • EMGScoring init_mom now defaults to true; enzyme definitions are built in and
      Enzymes.xml is optional. Warnings go to stderr; logging and DataValue conversion
      diagnostics improved (#8409, #8572, #8593, #8620, #8754).
    • ParamEntry::isBool() centralizes boolean-parameter recognition; ThermoRawFileMetadata
      exposes typed ThermoScan/ThermoReaction/ThermoPrecursor access (#10084, #10116).
    • OpenMSTestFramework is a separate static library: tests/FuzzyDiff users must link it.
      Temporary-output schema validation is explicit via VALIDATE_TMP_FILES; test projects
      register unique-ID/exception support. OpenMS_locale removed (#9929).
      Performance:
    • Faster decharging, peptide indexing/resolution, precursor purity/correction, OpenSWATH
      scoring and cross-correlation, plus fewer copies in search and extraction loops.
      AccurateMassSearchEngine honors observed-adduct-only searches; mzIdentML caches CV
      lookups and supports schema 1.3. Parquet PSM/metadata export avoids repeated scans and
      registry locking (#4787, #5867, #8618, #8635, #8651, #8656, #8763, #8764,
      #8785, #8787, #8806, #8848, #8849, #9686, #9689, #9692, #9801).

Fixes:
Chemistry and identifications:
- PercolatorAdapter's in-process backend and Percolator::rescore() read a feature stored as
a string meta value as the number it holds, as the percolator executable does when it
parses the .pin file. Both went through DataValue's conversion to double, which yields an
unrelated number for a string: SageAdapter stores all of Sage's extra features as strings,
so they were constant and Percolator weighted them zero, losing identifications (on a small
dataset, every PSM). A feature value that is not a finite number is now an error, as for
the executable (#10310).
- MatchedIterator no longer stops at the first of two target elements with the same value
(e.g. two peaks with the same m/z). HyperScore, PScore, AScore, SpectrumAlignment (ppm) and
the QC metrics FragmentMassError and PSMExplainedIonCurrent ignored all peaks above such a
pair, so ProSE and SimpleSearchEngine mis-scored spectra with duplicate peaks. (#10291)
- MzIdentMLFile: search engine scores without a score order in the PSI-MS vocabulary take
it from OpenMS' score registry, so Comet:expectation value, X!Tandem:expect, OMSSA:evalue,
OMSSA:pvalue, percolator:Q value and percolator:PEP are read as lower-is-better. Such
lower-is-better engine scores yield to a PSM-level q-value, which is then used. The
registry (Scores) now lists the X!Tandem and OMSSA scores by accession, so QPX and Arrow
exports also report them as additional scores (#8691).
- Fixed signed/stacked unknown mass shifts, precision-aware mass matching and spurious
zero-mass PSI-MOD assignments. Distinct modified residues remain distinct and
mass/formula round-trips no longer silently change peptidoforms (#10006, #10007,
#10010, #10029).
- Fixed UniMod ICAT formulas and site-specific neutral-loss masses; ProForma Formula
tags, anonymous mass shifts and stacked modifications retain mass/chemistry instead
of being dropped or serialized as empty brackets (#10003, #10026, #10030).
- QPX import reports unsupported modifications and checks reconstructed m/z. Modification
definitions are still not carried by the format, so unsupported modifications may
still be lost on import (#10004, #10012).
- AASequence::getFormula() and AASequence::getMonoWeight() handle Residue::Zp1Ion and
Residue::Zp2Ion, the radical z+1/z+2 electron-transfer fragments. Both types used to fall
through to the default branch, which logs an error and returns the internal mass without any
ion offset, and a C-terminal modification was never applied to them. getAverageWeight() and
getMZ() build on those two functions and are fixed with them (#10181).
- Corrected a/b neutral-loss fragment masses, negative-charge MzPAF parsing and
deisotoping precursor constraints (proton mass units; unknown precursor charge no
longer discards all isotope clusters). Unsupported deisotoping tolerances no longer
abort ProSE/SimpleSearchEngine (#9078, #9084, #9620, #10067, #10075).
- Fixed peptide-indexing crashes on short peptides, NucleicAcidSearchEngine multi-hit
handling, modified-sequence decoy duplicate detection, and copy operations on spectrum
generators, SpectrumAnnotator and Normalizer (#532, #8542, #8569, #8575, #8685, #10066).
- PeptideIndexer no longer exhausts memory when a peptide hit carries an empty sequence
(an empty AASequence, or one consisting of stop codons only). The empty needle flagged
the root node of the Aho-Corasick trie as a hit, and since the root's suffix link points
to itself, the search collected hits until memory ran out. ACTrie::addNeedle() now skips
an empty needle with a warning, but still consumes a needle index, so the peptide indices
of the caller stay aligned and such hits are simply reported as unmatched (#2987).
- PeptideIndexer: ACTrieState returns empty hits when no query was set instead of crashing
(potentially happens when the number of threads is larger than number of proteins) (#10257)
- Fixed the m/z of H2O/NH3 neutral-loss peaks of x/y/z linear ions at charge >= 2 in
TheoreticalSpectrumGeneratorXLMS and SimpleTSGXLMS: the loss was subtracted from the already
charge-divided position and divided by the charge again, so these peaks sat at roughly half
their true m/z. OpenPepXL scores and annotations of such peaks change accordingly (#10148).
- TheoreticalSpectrumGeneratorXLMS placed the second isotope peak of the precursor (and of its
H2O/NH3 losses) at the charged mass instead of its m/z, e.g. about 2003 instead of 668 for a
2000 Da precursor at charge 3. OpenPepXL uses these peaks: on the test data only its
log_occupancy scores change; on other data the corrected peak can now match an experimental
peak and change matched ion counts and scores (#10194).
- ConsensusID rejected a single empty idXML run (valid XML with no peptide identifications)
and dropped search settings when combining empty runs; per-spectrum runs also lost their
MS-file metadata, and unannotated feature/consensus maps could crash. Empty runs, and
entirely empty input in RT/mz mode, now keep their original search settings and
spectra_data instead of being discarded (#10203).
- MaRaClusterAdapter passes -precursor_tolerance_units on to MaRaCluster. It appended the
unit's index as a digit instead, so 20 ppm reached MaRaCluster as '-p 20.00' and 0.05 Da
as '-p 0.051', which MaRaCluster reads as 0.051 ppm: a tolerance in Da was never applied.
Quantification and numerical correctness:
- IsobaricWorkflow wrote every consensus feature with the same id ("e_0"), which is invalid
consensusXML ; each feature now gets its own unique ID
(#10201).
- ExperimentalDesign mappings use sample names consistently. Protein-group quantities
survive ID filtering; ProtXML excludes ProteinProphet's unneeded/subsumed groups
(#6038, #8960, #9874).
- Fixed SimpleSVM constant-predictor indexing, degenerate EMG fits, NaN similarity scores,
small-sample quantiles/variance, ROCCurve negative cutoffs and FragmentMassError ppm
variance. Corrected DistanceMatrix cache invalidation/comparison and empty-cell
clustering access (#6239, #9488, #9495, #9497, #9594, #9610, #9617, #9618,
#9659, #9661, #9664, #9670, #9682).
- MRMFeatureSelector works with GLPK; LP/ILP failures now report status and throw instead
of returning silently empty results in feature selection/decharging (#9940, #9944,
#9956).
- MapAlignmentAlgorithmKD::filterCCs_ (used by FeatureGroupingAlgorithmKD, i.e. by
FeatureLinkerUnlabeledKD) rejected a connected component containing conflicting nonzero
charge states only via a continue that escaped the inner charge-scanning loop, so the
component still passed the conflict check and was grouped. Such components are now
correctly rejected, which changes alignment and linking results (#10157).
- MassTrace::updateWeightedMeanRT() weighted each peak by the RT distance to its
predecessor and started the sum at the second peak, so the first peak contributed to
neither the numerator nor the denominator. The centroid was biased towards later RT
and, for a two-point trace, was always the RT of the second peak whatever the
intensities. The intensity-weighted mean now uses mid-interval (trapezoidal) weights
over all peaks, which changes RT positions on the 'epd:enabled=false' path of
FeatureFinderMetabo and MassTraceExtractor (#2777, #10225).
Data integrity and I/O:
- Sorting/filtering/set_peaks keeps peak annotations and binary arrays aligned in
spectra, chromatograms and mobilograms. select() checks bounds before mutation;
selectUnchecked() is available for validated C++ indices. Python raises IndexError
for invalid indices and ValueError for duplicates (#8645, #9795, #9807).
- MSExperiment::reset clears chromatograms; missing named protein data arrays/targeted
references throw ElementNotFound. Fixed stale targeted-reference caches and null
prediction access (#9488, #9492, #9211).
- mzML metadata round-trips preserve UTF-8, whitespace, timestamps, independent noise
grids, detector types/units and precursor-intensity units (#10077, #10079).
- MzMLFile writes activation and analyzer terms that are valid with PSI-MS 4.2.2. EThcD or
ETciD without a supplemental activation term also gets the generic dissociation method,
as PSI-MS now lists combined methods as activation attributes. SWIFT, cyclotron and
ion-storage analyzers, and the analyzer invented for incomplete instruments, use the
generic mass analyzer type; the OpenMS type is kept in a userParam and restored on
reading. HCID, EThcD and ETciD terms use their PSI-MS names (#8691).
- Reading an mzML file only partly (metadata only, or counting its spectra, as the first pass of
MzMLFile::transform() does) ends the progress it started, so the progress output of every later
file is no longer nested one level deeper (#10271).
- mzML chromatograms are written with a precursor or product only if they have one. A TIC used
to get an empty precursor and a product isolation window at m/z 0, and an MS1 chromatogram such
a product. The original DIAuditor, for example, read that product's isolation window as the
last spectrum's and crashed (#10271).
- mzML: the mass resolving power (MS:1000800) is written in the scan, the only place mzML allows
it. It used to be written as a userParam of the spectrum, so other readers, such as the
original DIAuditor, lost it once a file had been written by OpenMS (#10313).
- Thermo .raw: spectra carry the mass resolving power of the scan trailer ('Orbitrap
Resolution:', else 'FT Resolution:'), as msconvert reports it (#10313).
- Bruker timsTOF .d: the start of the acquisition (with its time zone), the instrument model and
its serial number are read from analysis.tdf (#10313).
- mzML: the other scan attributes the reader keeps with the spectrum (filter string, preset scan
configuration, dwell time, scan rate, mass resolution, elution time, analyzer scan offset and
interchannel delay) are written in the scan as well, and scan terms it keeps under their
accession, such as the ion injection time (MS:1000927), as CV terms. Both used to be written
as userParams, which other readers do not find.
- Fixed INI required/advanced parsing, XQuest FDR-type restoration, mzIdentML missing
modification locations, PepXML source-run paths/redundant Comet scores and duplicate
IDMapper spectrum references (#5443, #8905, #9089, #9660, #9763, #9766).
- TOPP tools no longer exit with ILLEGAL_PARAMETERS when the file given via '-ini'
contains a tool-specific 'common::' section; it was merged twice and its
entries were then rejected as unknown parameters. Those values now override 'common:'
as documented (#10126).
- PQP/sqMass SQL writes use bound parameters, preventing quoted-string failures and SQL
injection. Parquet writers remove incomplete outputs; OpenSWATH Arrow errors throw
contextual exceptions instead of aborting. Fixed mixed-IPF target/decoy labeling
and unified XIC/XIM reading (#9691, #9730, #9734, #9739, #9778, #9862).
- Fixed chromatogram MS1-isotope extraction, empty-spectrum mass/IM correction and
metabolite-library GNPS/COMPOUND_NAME compatibility (#7284, #8163, #9270, #9386).
- The MGF reader (MascotGenericFile) starts every BEGIN IONS block from a fresh spectrum:
fields absent from a block (CHARGE, RTINSECONDS, the PEPMASS intensity, MSLEVEL, NAME,
SMILES, INCHI, ...) no longer inherit the previous block's values, and a block without
peak lines is read as an empty spectrum instead of being merged with the next block or
dropped at the end of the file (#10148).
- MzIdentMLFile: loading a Peptide whose PeptideSequence element is empty (as written for a hit
with an empty sequence) no longer crashes with a null-pointer dereference; the reader stores an
empty sequence for it instead. Sequences held in a CDATA section or preceded by a comment inside
the element are now read instead of being discarded (#10148).
- ExperimentalDesignFile rejects a data row with the wrong number of cells with a ParseError
naming the line and the expected/actual count, before the row is indexed. A row ending in
an empty cell is one cell short after trimming and was read past its end, which crashed
ProteomicsLFQ/MSstatsConverter or put garbage into MSstats/Triqler condition and replicate
columns. SampleSection::getFactorValue throws MissingInformation for a row without a value
for the factor instead of reading past its end (#10148).
- UniPEFF wrote UniProt features whose names another isoform
as annotations of the canonical entry, although their coordinates refer to that isoform
(e.g. \ModResPsi phosphoserines on non-S residues or past the sequence end). Such
features now go to the entry of their isoform when UniPEFF writes one; otherwise they
are skipped and counted in a summary log line.
- IDFileConverter: pepXML input with 'mz_file' failed with "Found no experiment with name" unless
the run's 'base_name' ended with the exact path given. This broke TPP results on Windows (TPP
writes 'c:/...', the tool got 'c:...'), moved data and OpenMS' own pepXML output, which stores
the file name only. 'mz_file' is now matched by its file name; 'mz_name' can give more of the
path. PepXMLFile matches the experiment name against whole trailing path components of
'base_name', treating '/' and '' alike, so 'LN1' no longer selects both 'L_LN1' and 'H_LN1'.
A name that matches runs with different 'base_name's is an error, and the not-found error lists
the 'base_name's in the file (#3502).
Robustness:
- mzIdentML writer: the "unknown modification" cvParam (MS:1001460) referenced the undeclared
controlled vocabulary "MS", although the document declares only "PSI-MS". The writer names
every other cvParam PSI-MS; this one now does too, so files with an unrecognized
modification validate against strict readers that check cvRef against the declared
vocabularies (#10245).
- NuXLDeisotoper (a copy of Deisotoper::deisotopeAndSingleCharge) applies the two precursor mass
fixes already made to Deisotoper: a precursor with unknown charge (0) no longer enables the
mass constraint with a neutral mass of 0, which rejected every fragment cluster, and neutral
masses are computed with PROTON_MASS_U (the proton mass in Da) instead of PROTON_MASS (the
proton mass in kg), which had made every mass about 1 u per charge too high
(#10073, #10075, #10231).
- SpectrumCheapDPCorr::operator()(x, y) declared a local "bool keeppeaks_" that shadowed the
member of the same name, so the member itself was never assigned: dynprog_(), which resolves
ambiguous many-to-many peak alignments, read uninitialized memory instead of the keeppeaks
parameter when deciding whether to keep unaligned peaks in the consensus spectrum.
- mzIdentML output with more than one identification run repeated the Measure ids
(Measure_mz, Measure_int, Measure_error) in every SpectrumIdentificationList and failed schema
validation; the Measure definitions are now written once (#10213).
- Chromatograms built from MS1 spectra (FileConverter -convert_to_chromatograms, e.g. on GC-MS/SIM
data) had no native ID, so the mzML output contained many chromatograms with id="" and failed
schema validation. They are now named "XIC mz=<m/z>" (#10214).
- MSExperiment::sortSpectra() keeps spectra with equal retention time in their input order.
std::sort gave no guarantee for them, so ion mobility frames, FAIMS splits and Bruker TIMS
data were reordered depending on the standard library, which silently corrupted mass traces
built over such a map. The sort is also faster now, because it moves an index instead of a
whole spectrum (#10054, #10220).
- FLASHDeconv skips spectra with a non-positive mzml_mass_charge value instead of crashing on them,
and logs a warning. TOPPASEdge falls back to extension-based file type detection when
content-based detection returns UNKNOWN, and no longer mishandles an edge whose referenced
vertex was removed (#9054).
- FileMerger renamed the merged spectra but left precursor spectrumRef attributes pointing to the
original native IDs, so MS2 spectra lost the link to their parent scan and the mzML output
failed schema validation. The references are now updated together with the IDs (#10200).
- BREAKING: FileHandler/FileNameUtils::stripExtension() now removes the whole recognized
extension instead of searching the path for the canonical type name. 'a.pep.xml' yields 'a'
rather than 'a.pep', and 'a.pep.xml.gz' yields 'a' rather than 'a.pep.xml'. File::extension()
reports the same span ('.pep.xml'), and an extensionless file in a dotted directory
('/my.dir/fid') is no longer truncated. swapExtension() follows (#490).
- File::writable checks no longer delete concurrently created files or report spurious
failures; temporary directories are claimed atomically. UUID/unique-ID generation
avoids weak-entropy/parallel-process collisions (#8678, #9703, #9937, #9940, #9941, #9965).
- Fixed concurrent logging heap corruption, stream restoration and scoped suppression;
update checks tolerate unwritable home directories (#8440, #9074, #9075, #9583, #10019).
- Fixed ARM64 Base64 alignment, invalid EICExtractor/MetaProSIP size parameters and
boundary/serialization defects including empty-range accesses and unbounded loops
(#8689, #9767, #9775, #9776, #9790).
- ConsensusMap::split() used raw map indices to address its result vector, so a
consensusXML with column ids other than 0..n-1 (e.g. FileFilter -consensus:map 0 3
followed by QualityControl) wrote past the end of the vector. Feature maps are now placed
by column position (key order); an index with no column raises ElementNotFound (#10148).
- MapConversion::convert(PeakMap -> ConsensusMap) capped the number of peaks by
MSExperiment::getSize() (all MS levels plus chromatogram points) although it collects MS1
peaks only, so MapAlignerPoseClustering with max_num_peaks_considered -1 (or above the MS1
peak count) on runs with MS2 spectra sorted and copied past the end of a vector (#10148).
- StringUtils::skipWhitespace(string_view) and skipNonWhitespace(string_view) returned the
scanned length as int; for inputs longer than INT_MAX characters the value wrapped
negative, and removeWhitespaces() then built its iterators from it, reading and writing
before the start of the buffer. Both now return size_t (#10171).
- FileConverter: converting spectra to consensusXML/consensusparquet kept only as many of the
most intense MS1 peaks as the input had spectra. It now writes one consensus feature per MS1
peak (more memory and runtime for large files); equal intensities are ordered by RT and m/z,
so the output no longer depends on the platform, and every feature gets its own unique ID
(previously all were written as "e_0", which is invalid consensusXML) (#10161).
- BaseFeature::sortPeptideIdentifications() no longer reads the first hit of an
identification without hits (a crash for hit-less identifications loaded from featureXML,
e.g. in FeatureLinkerUnlabeledQT with use_identifications), sorts the hits of every
identification (also of a single one) and uses one score orientation throughout (#10148).
- OpenMS.ini: entries that a hand-written file does not set are now taken from the built-in
defaults, as are entries of the wrong type (with a warning). With a file that set only
temp_dir, the search engine adapters reported a database they could not find as an internal
error ("the element 'id_db_dir' could not be found"), and with id_db_dir given as a single
string instead of a list, as a conversion error. A file without the current 'version' no
longer logs "Broken file" or "deprecated" warnings on every run, since OpenMS never rewrites
the file (#3009).
- ProteomicsLFQ crashed in the QT feature linker (QTClusterFinder) when no run of a fraction
had a feature, e.g. because none of their identifications passed the FDR filter. Such a
fraction now contributes no quantities, a warning names it, and Debug builds no longer fail
an assertion on it (#9790, #10310).

Documentation:

  • New "File formats" page in the OpenMS documentation: which formats tools read and write
    compressed, the .idparquet, .featureparquet and .consensusparquet bundles (their files, the
    tools that use them, how to convert), and that imzML is read by the library and pyOpenMS
    only. The TextExporter page lists the Parquet bundles it reads.
  • Corrected misleading Doxygen contracts in the sqMass/SQLite stack: countTableRows
    exception types, the SpectrumAccessSqMass usage example and getSpectraByRT /
    getMultipleSpectra semantics, SqMassFile::store replacement behaviour and the
    OpenSwathScoring fetchSpectrumSwath spectrum counts (#10148, #10244).
  • The ProteomicsLFQ page describes match-between-runs with PIP-ECHO (-pip_echo) and lists
    Thermo .raw input. The PercolatorAdapter page describes the in-process backend, when the
    percolator executable is run instead, and how many threads each backend uses.
  • Expanded experimental-design terminology and LFQ/fractionated/TMT/SILAC examples,
    targeted quantitation, FAIMS, RT units, file conversions, USI and MetaProSIP CSV docs
    (#4400, #8478, #8588, #8597, #8615, #9173, #9909).
  • New pyOpenMS user-guide pages "Arrow and Parquet" (to_arrow() of spectra, feature maps,
    consensus maps and identifications; reading and writing the Parquet bundles) and "Mass
    Spectrometry Imaging" (loading imzML, ion images, regions, on-disc access, writing).
  • Updated pyOpenMS type hints, docstrings, wrapping/ownership guidance and test templates;
    documented native-reader requirements, core-only builds and vcpkg configuration
    (#8483, #8507, #8548, #8561, #8674, #9162, #9629, #9736, #9752, #9792, #9812, #9928).
  • New "Vendor formats" pages in the OpenMS and pyOpenMS documentation: the tools that read
    Thermo .raw files and Bruker timsTOF .d directories, FileConverter's two Thermo readers
    and their defaults, and that the other tools apply Thermo's peak picking too (#10303).
    Corrected the FileConverter, FeatureFinderIdentification and FeatureFinderMetaboIdent
    pages, the .NET requirement on the Windows installation page, and the macOS installation
    page (macOS 15, notarized installer).
  • The user FAQ explains OpenMS.ini: that it is optional, where OpenMS looks for it and which
    entries it reads. The OpenMSInfo page describes the data, temp and user data paths as they
    are resolved now (#3009).
  • The Windows installation page builds with the vcpkg presets instead of the contrib package,
    and the developer FAQ, the guide to adding a dependency (now via vcpkg.json and overlay
    ports), the external-code pages and the agent notes no longer refer to contrib. The
    orphaned install-contrib page is removed (#10144).

Build System:

  • The Debian package of a nightly or branch build carries its prerelease identifier in its
    control version, for example 3.6.0~nightly.2026.09.27, so it sorts below the release
    and a later nightly above an earlier one. Before, every build of 3.6.0 was version 3.6.0
    to apt, whether nightly or release.
  • Public headers of OpenMS, OpenSwathAlgo, OpenMS_CLI, OpenMS_GUI and the test
    framework use CMake header file sets. Installation paths and components stay
    unchanged and private headers remain excluded. Building OpenMS and consuming
    its CMake package both require CMake 3.24. Linux x64 CI explicitly verifies
    public headers as standalone translation units, with missing includes in
    identification-data and GUI headers corrected; normal builds can opt in with
    OPENMS_VERIFY_INTERFACE_HEADER_SETS=ON (#10177).
  • The TOPP tool framework (TOPPBase, ToolHandler, ParameterInformation,
    SearchEngineBase, TOPPExternalToolBase, MapAlignerBase, OpenSwathBase) moved out of
    libOpenMS into the new library libOpenMS_CLI (src/openms_cli; CMake target OpenMS_CLI,
    installed as OpenMS::OpenMS_CLI, package component CLI, export macro OPENMS_CLI_DLLAPI).
    Include paths are unchanged. The CTD/CWL parameter serializers read the parameter tag
    names from the new core header OpenMS/DATASTRUCTURES/ParamTags.h, so nothing in
    libOpenMS depends on the framework any more and its exported interface is purely
    scientific. BREAKING for external projects that build TOPP-style tools: link
    OpenMS::OpenMS_CLI (which carries OpenMS::OpenMS) instead of OpenMS::OpenMS (#10162).
  • The installed CMake package is layered: the core libraries (libOpenMS, libOpenSwathAlgo and the
    bundled third-party libraries; export set OpenMSTargets, install components library and cmake),
    the TOPP tool framework (libOpenMS_CLI; OpenMSCLITargets, library_cli and cmake_cli) and the GUI
    library (libOpenMS_GUI; OpenMSGUITargets, library_gui and cmake_gui) each have their own export
    set and install components (openms_add_library(... EXPORT_SET )). An installation may stop
    at any layer: OpenMSConfig.cmake provides the targets of the layers present, reports them with
    OpenMS_CLI_FOUND and OpenMS_WITH_GUI (now true only when the GUI library is installed) and
    refuses a required CLI or GUI component that is missing; TOPP-style external tools request
    COMPONENTS CLI. The pyOpenMS wheels build and install the core layer only again, the
    installed-consumer test covers a full and a core-only installation, and the deb/rpm/macOS
    packages include the new library components. The Applications component depends on the library
    layer its binaries link (library_gui in a WITH_GUI build, library_cli otherwise), so a
    component-selectable installation cannot leave the TOPP tools without libOpenMS_CLI. The
    deb/rpm component lists spell that component the way the install rules register it: CPack
    compares component names verbatim when it selects what to install, so a component-wise
    package built from the previous spelling would have contained none of the TOPP tools.
    Reconfiguring a build directory with another WITH_GUI setting no longer leaves a stale GUI
    target file behind (src/tests/package_layers checks this with stub libraries) (#10164).
  • Three places spelled the install component that every TOPP tool, TOPPView and TOPPAS is
    registered under ("Applications") in lower case: the cpack_add_component() declaration and
    the CPACK_COMPONENTS_ALL lists of the deb/rpm packages. CPack folds CPACK_COMPONENT__*
    metadata (DISPLAY_NAME, DEPENDS, ...) to upper case, so that lookup still worked, but compares
    the name verbatim when selecting what to install, so a component-wise deb/rpm build from these
    lists would have matched no install rule and shipped no TOPP tools at all. Both generators
    currently build monolithically, so the shipped packages were unaffected; the naming is now
    "Applications" everywhere (#10199).
  • vcpkg manifests, cross-platform CMake presets, overlay ports/triplets and binary caching
    support Linux, Windows and macOS; main build/test CI uses them. Release-only triplets
    avoid building duplicate Debug dependencies (#9511, #9800, #9927).
  • BUILD_TOPP_TOOLS and INSTALL_OPENMS_EXAMPLES (both default ON) enable a core-only SDK;
    incompatible workflow-generation options fail configuration. Installed headers and
    external find_package(OpenMS) consumers are validated (#9752, #9774, #10103).
  • Linux/macOS install RPATHs include linked dependency locations; vcpkg paths and macOS
    loader-relative paths are handled explicitly. Build-tree linking resolves private
    dependencies consistently, avoiding same-name library conflicts (#10114).
  • Numeric conversion uses std::from_chars/to_chars, reducing compile time and improving
    throughput; scientific formatting uses portable shortest round-trip strings
    (#9648, #9668). Core/OpenSwathAlgo AUTOMOC is disabled (#10099).
  • pyOpenMS uses py-build-cmake/cibuildwheel; improved dev RPATH, Windows DLL discovery,
    local nanobind detection and isolated builds. Thermo bridge assets are bundled from
    verified cached downloads (#8484, #8530, #8706, #8874, #8892, #8933, #9732, #9738, #10070).
  • CI/release packaging updated across platforms, including Ubuntu 24.04, Visual Studio
    2026 and automated backports/version bumps. Windows Release builds regain optimization;
    installer path/staging failures are fixed. macOS installers are signed/notarized and
    exclude pyOpenMS modules distributed as wheels (#8961, #9514, #9523, #9742, #9827,
    #9969, #10009).
  • The macOS package installs bin/qt.conf, so ExecutePipeline, and with it TOPPAS
    pipelines on the command line, can load the Qt platform plugin (#10260, #10280).
  • Fixed Arrow repository keys/version pins, Bioconda synchronization/tooling/dependency
    conflicts and package-list handling; build failures now propagate. Multi-architecture
    images publish a combined amd64/arm64 manifest (#9196, #9726, #9728, #9729, #9750,
    #9798, #9890, #9891, #9905, #9951, #9954, #10071). The nightly Bioconda deploy
    workflow called bioconda-utils build with --pkg-dir, an option that does not exist
    (--package-dir is correct), failing every nightly run since 2026-09-08 (#10167). A later
    upstream bioconda-utils release renamed --mulled-test to --mulled-build-and-test without
    keeping the old name, breaking the same workflow again; it now uses the current flag
    (#10227).
  • Fixed compiler-cache saving and fork-PR workflow failures; documentation lint rules are
    pinned. Added effective Parquet schema/value checks and non-finite numeric comparisons;
    removed parallel-test output collisions and source-timestamp assumptions
    (#9785, #9787, #9859, #9861, #9877, #9878, #9879, #9887, #9899, #9906, #9937,
    #9973, #10018).
  • Fixed a link-order bug that left libOpenMS.so with undefined libxml2 symbols when
    Arrow is linked statically, which ARROW_USE_STATIC (our own option, default ON)
    selects whenever a static Arrow target exists. libxml2 was listed as a direct
    OpenMS dependency, which put it ahead of libarrow_bundled_dependencies.a on the
    link line; an --as-needed linker then dropped it and import pyopenms failed with
    "undefined symbol: xmlBufferFree". It is now appended to Arrow's link interface,
    where the ordering is correct. The symbols come from the bundled
    azure-storage-common, not from the AWS SDK, which brings its own XML parser (#10136).
  • Improved CMake 4/MSVC, clang/Xcode, OpenMP and non-vcpkg ONNX builds, plus Debian
    dependency packaging to avoid .deb conflicts. OpenMP requests fail clearly when
    unavailable; SIMD-only compilation works without a runtime. Fixed
    YAML CTD parsing and optional WNet tool registration (#8368, #8880, #9056, #9223,
    #8496, #9267, #9388, #9651, #9653, #9783, #9784, #9971).
  • Documentation formulas use MathJax, removing Ghostscript. Removed obsolete developer
    scripts, ENABLE_STYLE_TESTING, doc_xml and stale examples/schemas. Runtime schema
    coverage is retained; QcMLFile/PepXMLFileMascot validation remains unavailable and
    now throws NotImplemented (#9740, #9757, #9771, #9945).
  • The Debian package derives its libc6, libstdc++6, libgomp1 and libgcc-s1 dependencies
    from the shipped binaries with dpkg-shlibdeps, so they cannot go stale as the declared
    libc6 (>= 2.28) had, and the package does not install on distributions too old to run
    it. This needed the install tree cleaned up first: the bundled tools were installed
    verbatim, including ThermoRawFileParser's NuGet runtimes// directories, which
    carry libMono.Unix.so for nine runtime identifiers (android-arm, android-arm64,
    android-x64, android-x86, linux-arm, linux-arm64, linux-x64, osx-arm64, osx-x64).
    Most are foreign to whichever host builds the package, and dpkg-shlibdeps treats a
    foreign-architecture binary as an error rather than as missing information, so no
    package was produced at all. Only the runtime identifiers the build targets are
    installed now; the parser's own Mono.Unix.dll.config maps the library to linux-x64,
    osx-x64 and osx-arm64 and to nothing else, so the others could never be loaded
    (#10202, #10207, #10211).
  • Debian package clean-up (#10230). Libraries no longer carry empty RUNPATH entries,
    which the dynamic loader reads as the current working directory: the release
    workflow links under PACKAGE_TYPE=none, whose install RPATH also names the build
    tree's vcpkg directory, and packages after reconfiguring, so CMake kept most of the
    ':' padding of the longer build-tree RPATH. libOpenSwathAlgo (and on arm64 the
    Thermo bridge) shipped with $ORIGIN/../lib/ and about 90 empty entries; CPack now
    removes them before building the package. The arm64 build takes the Thermo bridge
    from vcpkg, as every other platform does: vcpkg.json still left it out on
    linux & arm64 from when Thermo RAW reading was off there (#10206 turned it on), so
    the arm64 build fetched and built the bridge itself and packaged the bridge's
    headers, CMake package, .pdb and a second copy of its managed assemblies. The vcpkg
    port also installs the Thermo RawFileReader license now, so every package ships it
    under share/OpenMS/LICENSES as the from-source build did; before, only the arm64
    package had it. Depends is now exactly what dpkg-shlibdeps derives. The hand-written
    entries had gone stale: the Qt ones are derived anyway, and the derived plain t64
    names made their "t64 | non-t64" alternatives unenforceable; nothing links yaml-cpp
    dynamically; and SQLite ships inside the package, so libsqlite3-0 was never used
    (#10259).
  • The DEB installed the 53 third-party libraries it bundles and the Thermo bridge
    world-writable (mode 0777) in /usr/lib, among them libssl.so.3 and libcrypto.so.3, so any
    local user could replace code that the OpenMS tools load, also when root runs them. The
    macOS package installed the headers and resources of its Qt frameworks world-writable.
    Bundled libraries are now writable by their owner only (0755), like any installed library
    (#10324).
  • The container image build failed on every nightly run once WITH_THERMO_RAW defaulted
    to ON on Linux aarch64 (#10206): the Dockerfile still installed the .NET SDK/runtime
    that openms-thermo-bridge needs only for amd64, so the arm64 build could not find
    the dotnet executable at configure time. Both installs are now unconditional (#10210).
  • Linux wheels are built in a container with the checkout bind-mounted, which made git
    refuse the repository and left the embedded version a placeholder. The build marks the
    checkout safe, and any git failure now falls back to the "exported" version string
    instead of leaking the placeholder into VersionInfo and package file names (#10202).
  • MACOSX_DEPLOYMENT_TARGET is declared once for the wheels; the workflow value silently
    overrode pyproject.toml, which moved the supported macOS floor without notice. A CI
    check compares the Mach-O load commands of each wheel against its platform tag, so a
    wheel can no longer install on a macOS it cannot run on (#10202).
  • The macOS package states the macOS it needs. The build targets macOS 15, the macOS its
    Homebrew libraries are built for, instead of 14. The installer refuses older versions,
    and TOPPView, TOPPAS and INIFileEditor declare the build's target in their Info.plist
    instead of macOS 12 (#10286).
  • vcpkg builds OpenBLAS, which COIN-OR's LAPACK pulls in, for a fixed CPU target on Linux:
    CORE2 (SSSE3, the baseline OpenMS itself is compiled for) on x86_64 and ARMV8 on arm64. It
    used to take the kernels for the CPU of the build machine, and t