1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
//! Molecular symmetry element detection for linear systems.
use anyhow::{self, ensure, format_err};
use super::{PreSymmetry, Symmetry};
use crate::rotsym::RotationalSymmetry;
use crate::symmetry::symmetry_element::SIG;
use crate::symmetry::symmetry_element_order::{ElementOrder, ORDER_1, ORDER_2, ORDER_I};
use approx;
use log;
use nalgebra::Vector3;
impl Symmetry {
/// Performs point-group detection analysis for a linear molecule.
///
/// # Arguments
///
/// * `presym` - A pre-symmetry-analysis structure containing information about the molecular
/// system.
/// * `tr` - A flag indicating if time reversal should also be considered. A time-reversed
/// symmetry element will only be considered if its non-time-reversed version turns out to be
/// not a symmetry element.
pub(super) fn analyse_linear(
&mut self,
presym: &PreSymmetry,
tr: bool,
) -> Result<(), anyhow::Error> {
let (mois, principal_axes) = presym.recentred_molecule.calc_moi();
ensure!(
matches!(
presym.rotational_symmetry,
RotationalSymmetry::ProlateLinear
),
"Unexpected rotational symmetry -- expected: {}, actual: {}",
RotationalSymmetry::ProlateLinear,
presym.rotational_symmetry
);
ensure!(
presym
.sea_groups
.iter()
.all(|sea_group| sea_group.len() <= 2),
"Unexpected SEA groups of more than two atoms found for a linear molecule."
);
// C∞
ensure!(
approx::relative_eq!(
mois[0],
0.0,
epsilon = presym.dist_threshold,
max_relative = presym.dist_threshold
),
"Unexpected non-zero smallest principal moment of inertia."
);
ensure!(
self.add_proper(
ORDER_I,
&principal_axes[0],
true,
presym.dist_threshold,
false
),
"Expected C∞ axis not added."
);
if let Some(improper_kind) = presym.check_improper(
&ElementOrder::Int(2),
&Vector3::new(0.0, 0.0, 1.0),
&SIG,
tr,
) {
// i
log::debug!("Located an inversion centre.");
ensure!(
self.add_improper(
ORDER_2,
&Vector3::new(0.0, 0.0, 1.0),
false,
SIG,
None,
presym.dist_threshold,
improper_kind.contains_time_reversal()
),
"Expected inversion centre not added."
);
// σh must exist if C∞ and i both exist.
log::debug!("σh implied from C∞ and i.");
let sigma_check = presym.check_improper(&ORDER_1, &principal_axes[0], &SIG, tr);
ensure!(
sigma_check.is_some(),
"Expected σh implied by C∞ and i not found."
);
ensure!(
self.add_improper(
ORDER_1,
&principal_axes[0],
true,
SIG,
Some("h".to_owned()),
presym.dist_threshold,
sigma_check
.ok_or_else(|| format_err!(
"Expected mirror plane implied by C∞ and i not found."
))?
.contains_time_reversal(),
),
"Expected σh implied by C∞ and i not added."
);
if let Some(proper_kind) = presym.check_proper(&ORDER_2, &principal_axes[1], tr) {
// C2
log::debug!("Located a C2 axis perpendicular to C∞.");
self.add_proper(
ORDER_2,
&principal_axes[1],
true,
presym.dist_threshold,
proper_kind.contains_time_reversal(),
);
self.set_group_name("D∞h".to_owned());
} else {
// No C2
log::debug!("No C2 axes perpendicular to C∞ found.");
self.set_group_name("C∞h".to_owned());
}
} else {
// No i
log::debug!("No inversion centres found.");
if let Some(improper_kind) =
presym.check_improper(&ORDER_1, &principal_axes[1], &SIG, tr)
{
// σv
log::debug!("Located a σv plane.");
self.add_improper(
ORDER_1,
&principal_axes[1],
true,
SIG,
Some("v".to_owned()),
presym.dist_threshold,
improper_kind.contains_time_reversal(),
);
self.set_group_name("C∞v".to_owned());
} else {
// No σv
log::debug!("No σv planes found.");
self.set_group_name("C∞".to_owned());
}
}
Ok(())
}
}