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}