Skip to main content

gorder/
lib.rs

1// Released under MIT License.
2// Copyright (c) 2024-2026 Ladislav Bartos
3
4//! # gorder: Everything you will ever need for lipid order calculations
5//!
6//! A crate for calculating lipid order parameters from Gromacs simulations.
7//! `gorder` can calculate atomistic, coarse-grained, as well as united-atom lipid order parameters.
8//!
9//! **It is recommended to first read the [gorder manual](https://ladme.github.io/gorder-manual/) to understand the capabilities
10//! of `gorder` and then refer to this documentation for details about the Rust API.**
11//!
12//! ## Usage
13//!
14//! Run:
15//!
16//! ```bash
17//! $ cargo add gorder
18//! ```
19//!
20//! Import the crate in your Rust code:
21//!
22//! ```rust
23//! use gorder::prelude::*;
24//! ```
25//!
26//! `gorder` is also available as a command-line tool. You can install it using:
27//!
28//! ```bash
29//! $ cargo install gorder
30//! ```
31//!
32//! ## Quick examples
33//!
34//! Basic analysis of atomistic lipid order parameters:
35//!
36//! ```no_run
37//! use gorder::prelude::*;
38//!
39//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
40//!     // Construct the analysis
41//!     let analysis = Analysis::builder()
42//!         .structure("system.tpr")                   // Structure file
43//!         .trajectory("md.xtc")                      // Trajectory file to analyze
44//!         .output("order.yaml")                      // Output YAML file
45//!         .analysis_type(AnalysisType::aaorder(      // Type of analysis to perform
46//!             "@membrane and element name carbon",   // Selection of heavy atoms
47//!             "@membrane and element name hydrogen", // Selection of hydrogens
48//!         ))
49//!         .build()?;                                 // Build the analysis
50//!
51//!     // Activate colog for logging (requires the `colog` crate)
52//!     colog::init();
53//!
54//!     // Run the analysis and write the output
55//!     analysis.run()?.write()?;
56//!
57//!     Ok(())
58//! }
59//! ```
60//!
61//! ***
62//!
63//! Basic analysis of coarse-grained lipid order parameters:
64//!
65//! ```no_run
66//! use gorder::prelude::*;
67//!
68//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
69//!     // Construct the analysis
70//!     let analysis = Analysis::builder()
71//!         .structure("system.tpr")                   // Structure file
72//!         .trajectory("md.xtc")                      // Trajectory file to analyze
73//!         .output("order.yaml")                      // Output YAML file
74//!         .analysis_type(AnalysisType::cgorder(      // Type of analysis to perform
75//!             "@membrane",                           // Selection of beads
76//!         ))
77//!         .build()?;                                 // Build the analysis
78//!
79//!     // Activate colog for logging (requires the `colog` crate)
80//!     colog::init();
81//!
82//!     // Run the analysis and write the output
83//!     analysis.run()?.write()?;
84//!
85//!     Ok(())
86//! }
87//! ```
88//!
89//! ***
90//!
91//! Basic analysis of united-atom lipid order parameters:
92//!
93//! ```no_run
94//! use gorder::prelude::*;
95//!
96//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
97//!     // Construct the analysis
98//!     let analysis = Analysis::builder()
99//!         .structure("system.tpr")                   // Structure file
100//!         .trajectory("md.xtc")                      // Trajectory file to analyze
101//!         .output("order.yaml")                      // Output YAML file
102//!         .analysis_type(AnalysisType::uaorder(      // Type of analysis to perform
103//!             Some("element name carbon and not name C15 C34 C24 C25"),  // Selection of satured carbons
104//!             Some("name C24 C25"),                  // Selection of unsaturated carbons
105//!             None,                                  // Selection of atoms to ignore
106//!         ))
107//!         .build()?;                                 // Build the analysis
108//!
109//!     // Activate colog for logging (requires the `colog` crate)
110//!     colog::init();
111//!
112//!     // Run the analysis and write the output
113//!     analysis.run()?.write()?;
114//!
115//!     Ok(())
116//! }
117//! ```
118//!
119//! ***
120//!
121//! The [`Analysis`](crate::prelude::Analysis) structure includes many optional fields.
122//!
123//! ```no_run
124//! use gorder::prelude::*;
125//!
126//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
127//!     // Construct the analysis
128//!     let analysis = Analysis::builder()
129//!         .structure("system.tpr")                   // Structure file
130//!         .bonds("bonds.bnd")                        // Topology file containing bonds (not needed with TPR structure file)
131//!         .trajectory("md.xtc")                      // Trajectory file to analyze
132//!         .index("index.ndx")                        // Input NDX file
133//!         .output_yaml("order.yaml")                 // Output YAML file
134//!         .output_tab("order.tab")                   // Output table file
135//!         .output_xvg("order.xvg")                   // Pattern for output XVG files
136//!         .output_csv("order.csv")                   // Output CSV file
137//!         .analysis_type(AnalysisType::cgorder(      // Type of analysis to perform
138//!             "@membrane",                           // Selection of beads
139//!         ))
140//!         .membrane_normal(Axis::Z)                  // Membrane normal
141//!         .begin(100_000.0)                          // Start time of analysis
142//!         .end(200_000.0)                            // End time of analysis
143//!         .step(5)                                   // Analyze every Nth frame
144//!         .min_samples(100)                          // Minimum required samples
145//!         .n_threads(4)                              // Number of threads to use
146//!         .leaflets(                                 // Calculate order for individual leaflets
147//!             LeafletClassification::global(         // Method for classifying lipids into leaflets
148//!                 "@membrane",                       // Lipids for membrane center
149//!                 "name PO4"                         // Lipid heads selection
150//!             )
151//!             .with_frequency(Frequency::once())     // Frequency of classification
152//!         )
153//!         .ordermaps(                                // Construct maps of order parameters
154//!             OrderMap::builder()
155//!                 .output_directory("ordermaps")     // Directory for order maps
156//!                 .dim([                             // Dimensions of the map
157//!                     GridSpan::Manual {             // X-dimension span
158//!                         start: 5.0,                // Start at 5 nm
159//!                         end: 10.0,                 // End at 10 nm
160//!                     },
161//!                     GridSpan::Auto,                // Auto span for Y-dimension
162//!                 ])
163//!                 .bin_size([0.05, 0.2])             // Grid bin size
164//!                 .min_samples(30)                   // Minimum samples per bin
165//!                 .plane(Plane::XY)                  // Orientation of the map
166//!                 .build()?
167//!         )  
168//!         .estimate_error(EstimateError::new(        // Estimate error for calculations
169//!             Some(10),                              // Number of blocks for averaging
170//!             Some("convergence.xvg")                // Output file for convergence
171//!         )?)
172//!         .geometry(Geometry::cylinder(              // Only consider bonds inside a cylinder
173//!             "@protein",                            // Reference position for the cylinder
174//!             3.0,                                   // Radius of the cylinder
175//!             [-2.0, 2.0],                           // Span of the cylinder relative to reference
176//!             Axis::Z                                // Orientation of the main cylinder axis
177//!         )?)               
178//!         .handle_pbc(true)                          // Handle periodic boundary conditions?
179//!         .build()?;                                 // Build the analysis
180//!
181//!     // Activate colog for logging (requires the `colog` crate)
182//!     colog::init();
183//!
184//!     // Run the analysis and write the output
185//!     analysis.run()?.write()?;
186//!
187//!     Ok(())
188//! }
189//! ```
190//!
191//! ## Detailed usage
192//!
193//! **It is recommended to first read the [gorder manual](https://ladme.github.io/gorder-manual/) to understand the capabilities
194//! of `gorder` and then refer to this documentation for details about the Rust API.**
195//!
196//! Performing lipid order parameter calculations using the `gorder` crate consists of three main steps:
197//!
198//! 1. Constructing the [`Analysis`](crate::prelude::Analysis) structure.
199//! 2. Running the analysis.
200//! 3. Inspecting the results.
201//!
202//! ### Step 1: Constructing the `Analysis` structure
203//!
204//! Start by including the prelude of the `gorder` crate:
205//!
206//! ```no_run
207//! use gorder::prelude::*;
208//! ```
209//!
210//! The [`Analysis`](crate::prelude::Analysis) structure is constructed using the "builder" pattern.
211//! First, initiate the builder:
212//!
213//! ```no_run
214//! # use gorder::prelude::*;
215//! #
216//! # let analysis =
217//! Analysis::builder()
218//! # ;
219//! ```
220//!
221//! Then, add your options to it:
222//!
223//! ```no_run
224//! # use gorder::prelude::*;
225//! #
226//! # let builder = Analysis::builder()
227//! .structure("system.tpr")
228//! .trajectory("md.xtc")
229//! .analysis_type(AnalysisType::aaorder(
230//!     "@membrane and element name carbon",
231//!     "@membrane and element name hydrogen")
232//! )
233//! # ;
234//! ```
235//! > *When using the `gorder` application, specifying the output YAML file is mandatory.
236//! > However, when you are using `gorder` as a crate, specifying the output file is optional,
237//! > as you might not require any output to be generated.*
238//!
239//! Finally, assemble the `Analysis`:
240//!
241//! ```no_run
242//! # use gorder::prelude::*;
243//! #
244//! # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
245//! # let analysis = Analysis::builder()
246//! # .structure("system.tpr")
247//! # .trajectory("md.xtc")
248//! # .analysis_type(AnalysisType::aaorder(
249//! #     "@membrane and element name carbon",
250//! #     "@membrane and element name hydrogen")
251//! # )
252//! .build()?;
253//! # Ok(())
254//! # }
255//! ```
256//!
257//! Alternatively, you can construct the `Analysis` using an input YAML file that is also used by the CLI version of `gorder`:
258//!
259//! ```no_run
260//! # use gorder::prelude::*;
261//! #
262//! # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
263//! let analysis = Analysis::from_file("analysis.yaml")?;
264//! # Ok(())
265//! # }
266//! ```
267//!
268//! See the [gorder manual](https://ladme.github.io/gorder-manual/) for the format of this input YAML file.
269//!
270//! To learn more about the various input parameters for `Analysis`, refer to:
271//! 1. [`AnalysisBuilder`](crate::prelude::AnalysisBuilder) for an overview of the builder.
272//! 2. [`AnalysisType`](crate::prelude::AnalysisType) for the types of analysis.
273//! 3. [`OrderMapBuilder`](crate::prelude::OrderMapBuilder) and [`OrderMap`](crate::prelude::OrderMap) for specifying order parameter maps.
274//! 4. [`LeafletClassification`](crate::prelude::LeafletClassification) for leaflet classification.
275//! 5. [`MembraneNormal`](crate::prelude::MembraneNormal), [`Axis`](crate::prelude::Axis), and [`DynamicNormal`](crate::prelude::DynamicNormal) for membrane normal specification.
276//! 6. [`EstimateError`](crate::prelude::EstimateError) for error estimation.
277//! 7. [`Geometry`](crate::prelude::Geometry) for geometry selection.
278//!
279//! ### Step 2: Running the analysis
280//!
281//! Once the `Analysis` structure is ready, running the analysis is straightforward:
282//!
283//! ```no_run
284//! # use gorder::prelude::*;
285//! #
286//! # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
287//! # let analysis = Analysis::builder()
288//! # .structure("system.tpr")
289//! # .trajectory("md.xtc")
290//! # .analysis_type(AnalysisType::aaorder(
291//! #     "@membrane and element name carbon",
292//! #     "@membrane and element name hydrogen")
293//! # ).build()?;
294//! #
295//! let results = analysis.run()?;
296//! # Ok(())
297//! # }
298//! ```
299//!
300//! It is also recommened to initialize some logging crate, if you want to see information about the progress of the analysis.
301//!
302//! ```no_run
303//! # use gorder::prelude::*;
304//! #
305//! # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
306//! # let analysis = Analysis::builder()
307//! # .structure("system.tpr")
308//! # .trajectory("md.xtc")
309//! # .analysis_type(AnalysisType::aaorder(
310//! #     "@membrane and element name carbon",
311//! #     "@membrane and element name hydrogen")
312//! # ).build()?;
313//! #
314//! colog::init();
315//! let results = analysis.run()?;
316//! # Ok(())
317//! # }
318//! ```
319//!
320//! The [`Analysis::run`](crate::prelude::Analysis::run) method returns an
321//! [`AnalysisResults`](crate::prelude::AnalysisResults) enum containing the results.
322//!
323//! ### Step 3: Inspecting the results
324//!
325//! The simplest way to inspect results is by writing output files. These files must be specified during the construction of the `Analysis` structure:
326//!
327//! 1. [`AnalysisBuilder::output_yaml`](`crate::prelude::AnalysisBuilder::output_yaml`) for YAML file,
328//! 2. [`AnalysisBuilder::output_csv`](`crate::prelude::AnalysisBuilder::output_csv`) for CSV file,
329//! 3. [`AnalysisBuilder::output_xvg`](`crate::prelude::AnalysisBuilder::output_xvg`) for XVG files,
330//! 4. [`AnalysisBuilder::output_tab`](`crate::prelude::AnalysisBuilder::output_tab`) for files in "table" format.
331//!
332//! To write the output files, call:
333//!
334//! ```no_run
335//! # use gorder::prelude::*;
336//! #
337//! # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
338//! # let analysis = Analysis::builder()
339//! # .structure("system.tpr")
340//! # .trajectory("md.xtc")
341//! # .analysis_type(AnalysisType::aaorder(
342//! #     "@membrane and element name carbon",
343//! #     "@membrane and element name hydrogen")
344//! # ).build()?;
345//! #
346//! # let results = analysis.run()?;
347//! results.write()?;
348//! # Ok(())
349//! # }
350//! ```
351//!
352//! Alternatively, results can be extracted programmatically. Match the [`AnalysisResults`](crate::prelude::AnalysisResults) enum to access the results:
353//!
354//! ```no_run
355//! # use gorder::prelude::*;
356//! #
357//! # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
358//! # let analysis = Analysis::builder()
359//! # .structure("system.tpr")
360//! # .trajectory("md.xtc")
361//! # .analysis_type(AnalysisType::aaorder(
362//! #     "@membrane and element name carbon",
363//! #     "@membrane and element name hydrogen")
364//! # ).build()?;
365//! #
366//! # let results = analysis.run()?;
367//! let aa_results = match results {
368//!     AnalysisResults::AA(aa_results) => aa_results,
369//!     _ => panic!("Expected atomistic results."),
370//! };
371//! # Ok(())
372//! # }
373//! ```
374//!
375//! Then, inspect the results as needed. Refer to:
376//! 1. [`AAOrderResults`](crate::prelude::AAOrderResults) for atomistic results.
377//! 2. [`CGOrderResults`](crate::prelude::CGOrderResults) for coarse-grained results.
378//! 3. [`UAOrderResults`](crate::prelude::UAOrderResults) for united-atom results.
379
380// `doc_overindented_list_items` lint does not work correctly...
381#![allow(clippy::doc_overindented_list_items)]
382
383/// Version of the `gorder` crate.
384pub const GORDER_VERSION: &str = env!("CARGO_PKG_VERSION");
385
386/// Message that should be added to every panic.
387pub(crate) const PANIC_MESSAGE: &str =
388    "\n\n\n            >>> THIS SHOULD NOT HAVE HAPPENED! PLEASE REPORT THIS ERROR <<<
389(open an issue at 'github.com/VachaLab/gorder/issues' or write an e-mail to 'ladmeb@gmail.com')\n\n";
390
391/// Log colored info message.
392#[macro_export]
393macro_rules! colog_info {
394    ($msg:expr) => {
395        log::info!($msg)
396    };
397    ($msg:expr, $($arg:expr),+ $(,)?) => {{
398        use colored::Colorize;
399        log::info!($msg, $( $arg.to_string().cyan() ),+)
400    }};
401}
402
403/// Log colored warning message.
404#[macro_export]
405macro_rules! colog_warn {
406    ($msg:expr) => {
407        log::warn!($msg)
408    };
409    ($msg:expr, $($arg:expr),+ $(,)?) => {{
410        use colored::Colorize;
411        log::warn!($msg, $( $arg.to_string().yellow() ),+)
412    }};
413}
414
415/// Specifies the leaflet a lipid is in.
416#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
417pub enum Leaflet {
418    #[serde(alias = "1")]
419    Upper,
420    #[serde(alias = "0")]
421    Lower,
422}
423
424impl Display for Leaflet {
425    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426        match self {
427            Leaflet::Upper => write!(f, "upper"),
428            Leaflet::Lower => write!(f, "lower"),
429        }
430    }
431}
432
433mod analysis;
434pub mod errors;
435pub mod input;
436mod lanczos;
437pub mod presentation;
438
439use std::fmt::Display;
440
441use serde::{Deserialize, Serialize};
442
443/// This module contains re-exported public structures of the `gorder` crate.
444pub mod prelude {
445    pub use crate::Leaflet;
446
447    pub use super::input::{
448        analysis::AnalysisBuilder, ordermap::OrderMapBuilder, Analysis, AnalysisType, Axis,
449        DynamicNormal, EstimateError, Frequency, GeomReference, Geometry, GridSpan,
450        LeafletClassification, MembraneNormal, OrderMap, Plane,
451    };
452
453    pub use super::analysis::topology::atom::AtomType;
454
455    pub use super::presentation::{
456        aaresults::{AAAtomResults, AAMoleculeResults, AAOrderResults},
457        cgresults::{CGMoleculeResults, CGOrderResults},
458        convergence::Convergence,
459        uaresults::{UAAtomResults, UABondResults, UAMoleculeResults, UAOrderResults},
460        AnalysisResults, BondResults, GridMapF32, Order, OrderCollection, OrderMapsCollection,
461        PublicMoleculeResults, PublicOrderResults,
462    };
463
464    pub use groan_rs::prelude::Vector3D;
465}