Skip to content

Commit

Permalink
Basic changes to support (only) pdbqt
Browse files Browse the repository at this point in the history
  • Loading branch information
ivnsch committed Jan 15, 2025
1 parent 2580827 commit 51249b5
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 118 deletions.
60 changes: 31 additions & 29 deletions src/read/mmcif/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,36 +572,38 @@ fn parse_atoms(input: &Loop, pdb: &mut PDB, options: &ReadOptions) -> Option<Vec
context.clone(),
))
}
if let Some(mut atom) = Atom::new(
hetero,
serial_number,
name,
pos_x,
pos_y,
pos_z,
occupancy,
b_factor,
element,
charge,
) {
if let Some(matrix) = aniso {
atom.set_anisotropic_temperature_factors(matrix);
}
// not used for .pdbqt
todo!()
// if let Some(mut atom) = Atom::new(
// hetero,
// serial_number,
// name,
// pos_x,
// pos_y,
// pos_z,
// occupancy,
// b_factor,
// element,
// charge,
// ) {
// if let Some(matrix) = aniso {
// atom.set_anisotropic_temperature_factors(matrix);
// }

model.add_atom(
atom,
chain_name,
(residue_number, insertion_code.as_deref()),
(residue_name, alt_loc.as_deref()),
);
} else {
errors.push(PDBError::new(
ErrorLevel::InvalidatingError,
"Atom definition incorrect",
"The atom name and element should only contain valid characters.",
context.clone(),
))
}
// model.add_atom(
// atom,
// chain_name,
// (residue_number, insertion_code.as_deref()),
// (residue_name, alt_loc.as_deref()),
// );
// } else {
// errors.push(PDBError::new(
// ErrorLevel::InvalidatingError,
// "Atom definition incorrect",
// "The atom name and element should only contain valid characters.",
// context.clone(),
// ))
// }
}
if !errors.is_empty() {
Some(errors)
Expand Down
68 changes: 11 additions & 57 deletions src/read/pdb/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ fn lex_atom(
segment_id,
element,
charge,
autodock_type,
),
basic_errors,
) = lex_atom_basics(linenumber, line);
Expand All @@ -184,6 +185,7 @@ fn lex_atom(
segment_id,
element,
charge,
autodock_type,
),
errors,
))
Expand Down Expand Up @@ -220,39 +222,8 @@ fn lex_anisou(linenumber: usize, line: &str) -> (LexItem, Vec<PDBError>) {
],
];

let (
(
serial_number,
atom_name,
alternate_location,
residue_name,
chain_id,
residue_serial_number,
insertion,
segment_id,
element,
charge,
),
basic_errors,
) = lex_atom_basics(linenumber, line);
errors.extend(basic_errors);

(
LexItem::Anisou(
serial_number,
atom_name,
alternate_location,
residue_name,
chain_id,
residue_serial_number,
insertion,
factors,
segment_id,
element,
charge,
),
errors,
)
// not used for pdbqt
todo!()
}

/// Lex the basic structure of the ATOM/HETATM/ANISOU Records, to minimise code duplication
Expand All @@ -271,7 +242,8 @@ fn lex_atom_basics(
Option<String>,
String,
String,
isize,
f32,
String,
),
Vec<PDBError>,
) {
Expand All @@ -288,30 +260,11 @@ fn lex_atom_basics(
let segment_id = parse(linenumber, line, 72..76, &mut errors);
let element = parse(linenumber, line, 76..78, &mut errors);

let mut charge = 0;
#[allow(clippy::unwrap_used)]
if chars.len() >= 80 && !(chars[78] == ' ' && chars[79] == ' ') {
if !chars[78].is_ascii_digit() {
errors.push(PDBError::new(
ErrorLevel::InvalidatingError,
"Atom charge is not correct",
"The charge is not numeric, it is defined to be [0-9][+-], so two characters in total.",
Context::line(linenumber, line, 78, 1),
));
} else if chars[79] != '-' && chars[79] != '+' {
errors.push(PDBError::new(
ErrorLevel::InvalidatingError,
"Atom charge is not correct",
"The charge is not properly signed, it is defined to be [0-9][+-], so two characters in total.",
Context::line(linenumber, line, 79, 1),
));
} else {
charge = isize::try_from(chars[78].to_digit(10).unwrap()).unwrap();
if chars[79] == '-' {
charge *= -1;
}
}
let mut charge: f32 = parse(linenumber, line, 71..76, &mut errors);
if chars[70] == '-' {
charge *= -1.;
}
let autodock_type: String = parse(linenumber, line, 76..79, &mut errors);

(
(
Expand All @@ -333,6 +286,7 @@ fn lex_atom_basics(
segment_id,
element,
charge,
autodock_type,
),
errors,
)
Expand Down
4 changes: 3 additions & 1 deletion src/read/pdb/lexitem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub enum LexItem {
/// * segment id
/// * element
/// * charge
/// * autodock type
Atom(
bool,
usize,
Expand All @@ -44,7 +45,8 @@ pub enum LexItem {
f64,
String,
String,
isize,
f32,
String,
),
/// An Anisou record with all its information, including the deprecated and rarely used fields.
/// * serial number
Expand Down
2 changes: 2 additions & 0 deletions src/read/pdb/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ where
_,
element,
charge,
autodock_type,
) => {
if options.discard_hydrogens & (element == "H") {
continue;
Expand Down Expand Up @@ -184,6 +185,7 @@ where
b,
element,
charge,
autodock_type,
)
.expect("Invalid characters in atom creation");
let conformer_id = (residue_name.as_str(), alt_loc.as_deref());
Expand Down
15 changes: 10 additions & 5 deletions src/read/read_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub enum Format {
/// Automatically detect the format
#[default]
Auto,

Pdbqt,
}

impl From<&str> for Format {
Expand Down Expand Up @@ -151,7 +153,7 @@ impl ReadOptions {
self.read_auto(filename)
} else {
match self.format {
Format::Pdb => super::pdb::open_pdb_with_options(path, self),
Format::Pdb | Format::Pdbqt => super::pdb::open_pdb_with_options(path, self),
Format::Mmcif => super::mmcif::open_mmcif_with_options(path, self),
Format::Auto => self.read_auto(path),
}
Expand All @@ -175,7 +177,7 @@ impl ReadOptions {
let decompressor = flate2::read::GzDecoder::new(file);
let reader = std::io::BufReader::new(decompressor);
match file_format {
Format::Pdb => {
Format::Pdb | Format::Pdbqt => {
super::pdb::open_pdb_raw_with_options(reader, Context::None, self)
}
Format::Mmcif => super::mmcif::open_mmcif_raw_with_options(reader, self),
Expand All @@ -188,7 +190,7 @@ impl ReadOptions {
}
} else {
match file_format {
Format::Pdb => super::pdb::open_pdb_with_options(path, self),
Format::Pdb | Format::Pdbqt => super::pdb::open_pdb_with_options(path, self),
Format::Mmcif => super::mmcif::open_mmcif_with_options(path, self),
_ => Err(vec![PDBError::new(
crate::ErrorLevel::BreakingError,
Expand All @@ -202,7 +204,7 @@ impl ReadOptions {
Err(vec![PDBError::new(
crate::ErrorLevel::BreakingError,
"Missing extension",
"The given file does not have an extension, make it .pdb or .cif",
"The given file does not have an extension, make it .pdbqt",
Context::show(path.as_ref()),
)])
}
Expand All @@ -219,7 +221,9 @@ impl ReadOptions {
T: std::io::Read,
{
match self.format {
Format::Pdb => super::pdb::open_pdb_raw_with_options(input, Context::None, self),
Format::Pdb | Format::Pdbqt => {
super::pdb::open_pdb_raw_with_options(input, Context::None, self)
}
Format::Mmcif => super::mmcif::open_mmcif_raw_with_options(input, self),
Format::Auto => Err(vec![PDBError::new(
crate::ErrorLevel::BreakingError,
Expand All @@ -236,6 +240,7 @@ fn guess_format(filename: &str) -> Option<(Format, bool)> {
let path = Path::new(filename);

match path.extension().and_then(OsStr::to_str) {
Some("pdbqt") => Some((Format::Pdbqt, false)),
Some("pdb") | Some("pdb1") => Some((Format::Pdb, false)),
Some("cif") | Some("mmcif") => Some((Format::Mmcif, false)),
Some("gz") => {
Expand Down
35 changes: 20 additions & 15 deletions src/structs/atom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ pub struct Atom {
/// The element of the Atom, can only use the standard allowed characters
element: Option<Element>,
/// The charge of the Atom
charge: isize,
charge: f32,
/// The anisotropic temperature factors, if applicable
atf: Option<[[f64; 3]; 3]>,
/// Autodock type
autodock_type: String,
}

impl Atom {
Expand All @@ -54,7 +56,8 @@ impl Atom {
occupancy: f64,
b_factor: f64,
element: impl Into<String>,
charge: isize,
charge: f32,
autodock_type: impl Into<String>,
) -> Option<Atom> {
let atom_name = atom_name.into().trim().to_string();
let element = element.into().trim().to_string();
Expand All @@ -80,6 +83,7 @@ impl Atom {
} else {
None
};
let autodock_type = autodock_type.into().trim().to_string();
Some(Atom {
counter: ATOM_COUNTER.fetch_add(1, AtomicOrdering::SeqCst),
hetero,
Expand All @@ -93,6 +97,7 @@ impl Atom {
element,
charge,
atf: None,
autodock_type,
})
} else {
None
Expand Down Expand Up @@ -314,29 +319,26 @@ impl Atom {

/// Get the charge of the atom.
/// In PDB files the charge is one digit with a sign.
pub const fn charge(&self) -> isize {
pub const fn charge(&self) -> f32 {
self.charge
}

/// Set the charge of this atom.
/// In PDB files the charge is one digit with a sign.
pub fn set_charge(&mut self, new_charge: isize) {
pub fn set_charge(&mut self, new_charge: f32) {
self.charge = new_charge;
}

/// Get the charge in the PDB format `[0-9][-+]`. If the charge is 0 or outside bounds (below -9 or above 9) it returns an empty string.
/// note: temporary (?) modification for f32
#[allow(clippy::cast_possible_truncation)]
pub fn pdb_charge(&self) -> String {
if self.charge == 0 || self.charge < -9 || self.charge > 9 {
String::new()
} else {
let mut sign = '+';
let charge = (48 + self.charge.unsigned_abs() as u8) as char;
if self.charge < 0 {
sign = '-';
}
format!("{charge}{sign}")
let mut sign: char = '+';
let charge = self.charge;
if self.charge < 0. {
sign = '-';
}
format!("{charge}{sign}")
}

/// Get the anisotropic temperature factors, if available.
Expand Down Expand Up @@ -580,7 +582,7 @@ impl fmt::Display for Atom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ATOM ID: {}, Number: {}, Element: {}, X: {}, Y: {}, Z: {}, OCC: {}, B: {}, ANISOU: {}",
"ATOM ID: {}, Number: {}, Element: {}, X: {}, Y: {}, Z: {}, OCC: {}, B: {}, ANISOU: {}, Q: {}, AT: {}",
self.name(),
self.serial_number(),
self.element()
Expand All @@ -590,7 +592,9 @@ impl fmt::Display for Atom {
self.z(),
self.occupancy(),
self.b_factor(),
self.atf.is_some()
self.atf.is_some(),
self.charge,
self.autodock_type
)
}
}
Expand All @@ -609,6 +613,7 @@ impl Clone for Atom {
self.b_factor,
self.element.map_or_else(|| "", |e| e.symbol()),
self.charge,
&self.autodock_type,
)
.expect("Invalid Atom properties in a clone");
atom.atf = self.atf;
Expand Down
11 changes: 0 additions & 11 deletions src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,17 +160,6 @@ pub fn validate_pdb(pdb: &PDB) -> Vec<PDBError> {
Context::None,
));
}
if atom.charge() > 9 || atom.charge() < -9 {
errors.push(PDBError::new(
ErrorLevel::LooseWarning,
"Atom charge out of bounds",
format!(
"Atom {} has a charge which is out of bounds, max is 9 min is -9.",
atom.serial_number()
),
Context::None,
));
}
if atom.occupancy() > 999.99 {
errors.push(PDBError::new(
ErrorLevel::LooseWarning,
Expand Down

0 comments on commit 51249b5

Please sign in to comment.