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
//! The anisotropic displacement record that follows an atom row.
use super::pdb::{alt_field, atom_name_field, element_column, insertion_code};
use molframe_core::structure::{AtomRef, ResidueRef, Structure};
use num_traits::ToPrimitive;
use std::fmt;
/// A tensor's identity columns, copied from the atom row before it.
pub(super) struct Record<'a> {
/// The row the record pairs with, for its alternate-location field.
pub atom: &'a AtomRef<'a>,
/// The structure the atom belongs to, for resolving names.
pub structure: &'a Structure,
/// The residue the atom belongs to, for its sequence and insertion code.
pub residue: &'a ResidueRef<'a>,
/// The validated atom identifier the atom row carried.
pub name: &'a str,
/// The component identifier the atom row carried.
pub component: &'a str,
/// The chain label the atom row carried.
pub chain: &'a str,
/// The already-formatted sequence field of the atom row.
pub seq: &'a str,
/// The already-formatted serial field of the atom row.
pub serial: &'a str,
/// The tensor components, in `[U11, U22, U33, U12, U13, U23]` order.
pub u: &'a [f32; 6],
}
impl Record<'_> {
/// Writes the record under the identity columns it copied.
pub(super) fn write(&self, out: &mut impl fmt::Write) {
let units = self.u.map(field);
let element = element_column(self.atom);
let name = atom_name_field(self.name, &element);
let _ = writeln!(
out,
"ANISOU{serial}{name:<4}{alt:1}{comp:>3} {chain:>1}{seq}{ins:1} \
{u0:>7}{u1:>7}{u2:>7}{u3:>7}{u4:>7}{u5:>7} {element:>2}",
serial = self.serial,
alt = alt_field(self.structure, self.atom),
comp = self.component,
chain = self.chain,
seq = self.seq,
ins = insertion_code(*self.residue),
u0 = units[0],
u1 = units[1],
u2 = units[2],
u3 = units[3],
u4 = units[4],
u5 = units[5],
);
}
}
/// One 7-column integer field, in units of 0.0001 ångström squared.
///
/// The reader divides by 10 000, so writing multiplies by it; a component the
/// format cannot hold as an integer clamps to the widest representable field
/// rather than silently becoming a different ellipsoid.
fn field(value: f32) -> i64 {
let rounded = (f64::from(value) * 10_000.0).round();
match rounded.to_i64() {
Some(field) => field,
None if rounded.is_sign_positive() => 99_999_999,
None => -99_999_999,
}
}