dreid_typer/lib.rs
1//! A high-performance Rust library for DREIDING force field atom typing
2//! and molecular topology perception.
3//!
4//! # DreidTyper
5//!
6//! **DreidTyper** is a foundational software library for the automated assignment
7//! of DREIDING atom types from molecular connectivity. It provides a modern, robust
8//! solution for translating a simple chemical graph (`MolecularGraph`) into a
9//! complete, simulation-ready `MolecularTopology`.
10//!
11//! The library is engineered in Rust for performance, memory safety, and reliability.
12//! It employs a deterministic, three-phase pipeline (**Perceive-Type-Build**) to
13//! ensure accurate and reproducible results.
14//!
15//! # Quickstart
16//!
17//! The primary entry point of the library is the [`assign_topology`] function.
18//! Here's how to build a simple ethanol molecule and perceive its topology:
19//!
20//! ```
21//! use dreid_typer::{
22//! assign_topology, MolecularGraph, MolecularTopology,
23//! Element, GraphBondOrder,
24//! };
25//!
26//! // 1. Define the molecule's connectivity using a `MolecularGraph`.
27//! let mut graph = MolecularGraph::new();
28//! let c1 = graph.add_atom(Element::C); // Atom for the CH3 group
29//! let c2 = graph.add_atom(Element::C); // Atom for the CH2 group
30//! let o = graph.add_atom(Element::O);
31//! let h_c1_1 = graph.add_atom(Element::H);
32//! let h_c1_2 = graph.add_atom(Element::H);
33//! let h_c1_3 = graph.add_atom(Element::H);
34//! let h_c2_1 = graph.add_atom(Element::H);
35//! let h_c2_2 = graph.add_atom(Element::H);
36//! let h_o = graph.add_atom(Element::H);
37//!
38//! graph.add_bond(c1, c2, GraphBondOrder::Single).unwrap();
39//! graph.add_bond(c2, o, GraphBondOrder::Single).unwrap();
40//! graph.add_bond(c1, h_c1_1, GraphBondOrder::Single).unwrap();
41//! graph.add_bond(c1, h_c1_2, GraphBondOrder::Single).unwrap();
42//! graph.add_bond(c1, h_c1_3, GraphBondOrder::Single).unwrap();
43//! graph.add_bond(c2, h_c2_1, GraphBondOrder::Single).unwrap();
44//! graph.add_bond(c2, h_c2_2, GraphBondOrder::Single).unwrap();
45//! graph.add_bond(o, h_o, GraphBondOrder::Single).unwrap();
46//!
47//! // 2. Call the main function to perceive the topology using default rules.
48//! let topology: MolecularTopology = assign_topology(&graph).unwrap();
49//!
50//! // 3. Inspect the results.
51//! assert_eq!(topology.atoms.len(), 9);
52//! assert_eq!(topology.bonds.len(), 8);
53//! assert_eq!(topology.angles.len(), 13);
54//! assert_eq!(topology.torsions.len(), 12);
55//!
56//! // Check the assigned DREIDING atom types.
57//! assert_eq!(topology.atoms[c1].atom_type, "C_3"); // sp3 Carbon
58//! assert_eq!(topology.atoms[c2].atom_type, "C_3"); // sp3 Carbon
59//! assert_eq!(topology.atoms[o].atom_type, "O_3"); // sp3 Oxygen
60//! assert_eq!(topology.atoms[h_o].atom_type, "H_HB"); // Hydrogen-bonding Hydrogen
61//! assert_eq!(topology.atoms[h_c1_1].atom_type, "H_"); // Standard Hydrogen
62//! ```
63
64mod builder;
65mod core;
66mod perception;
67mod typing;
68
69pub use crate::core::error::{AssignmentError, GraphValidationError, PerceptionError, TyperError};
70pub use crate::core::graph::{AtomNode, BondEdge, MolecularGraph};
71pub use crate::core::properties::{
72 Element, GraphBondOrder, Hybridization, ParseBondOrderError, ParseElementError,
73 ParseHybridizationError, TopologyBondOrder,
74};
75pub use crate::core::topology::{Angle, Atom, Bond, Inversion, MolecularTopology, Torsion};
76
77/// Rule parsing and customization utilities.
78///
79/// The core types needed to parse and inspect DREIDING
80/// atom-typing rules from TOML configuration files.
81pub mod rules {
82 pub use crate::typing::rules::{Conditions, Rule, get_default_rules, parse_rules};
83}
84
85/// Assigns a full molecular topology using the default embedded DREIDING ruleset.
86///
87/// This is the primary, high-level entry point for the library. It orchestrates the
88/// entire three-phase pipeline: chemical perception, atom typing, and topology
89/// construction. It takes a [`MolecularGraph`] representing the chemical
90/// connectivity and returns a complete [`MolecularTopology`] ready for use in
91/// molecular simulations.
92///
93/// # Arguments
94///
95/// * `graph` - A reference to the [`MolecularGraph`] to be processed.
96///
97/// # Returns
98///
99/// A `Result` containing the fully perceived [`MolecularTopology`] on success.
100///
101/// # Errors
102///
103/// Returns a [`TyperError`] if any stage of the process fails. This can include:
104/// * [`GraphValidationError`] if the input graph is inconsistent.
105/// * [`PerceptionError`] if the chemical perception logic fails.
106/// * [`AssignmentError`] if the rule engine cannot assign a type to one or more atoms.
107///
108/// # Panics
109///
110/// Panics if the embedded default rules file is malformed, which indicates a
111/// critical library bug.
112pub fn assign_topology(graph: &MolecularGraph) -> Result<MolecularTopology, TyperError> {
113 let default_rules = typing::rules::get_default_rules();
114 assign_topology_internal(graph, default_rules)
115}
116
117/// Assigns a full molecular topology using a custom set of typing rules.
118///
119/// This function provides the same functionality as [`assign_topology`] but allows
120/// for customization of the atom typing logic by supplying a custom slice of [`rules::Rule`]s.
121/// This is useful for extending the DREIDING force field to new elements or
122/// defining special types for specific chemical environments.
123///
124/// # Arguments
125///
126/// * `graph` - A reference to the [`MolecularGraph`] to be processed.
127/// * `rules` - A slice of [`rules::Rule`] structs that the typing engine will use.
128///
129/// # Errors
130///
131/// Returns a [`TyperError`] under the same conditions as [`assign_topology`].
132pub fn assign_topology_with_rules(
133 graph: &MolecularGraph,
134 rules: &[rules::Rule],
135) -> Result<MolecularTopology, TyperError> {
136 assign_topology_internal(graph, rules)
137}
138
139/// Internal core function that executes the perception, typing, and building pipeline.
140fn assign_topology_internal(
141 graph: &MolecularGraph,
142 rules: &[rules::Rule],
143) -> Result<MolecularTopology, TyperError> {
144 let annotated_molecule = perception::perceive(graph)?;
145
146 let atom_types = typing::engine::assign_types(&annotated_molecule, rules)
147 .map_err(TyperError::AssignmentFailed)?;
148
149 let topology = builder::build_topology(&annotated_molecule, &atom_types);
150
151 Ok(topology)
152}