Skip to content
This repository was archived by the owner on Mar 10, 2026. It is now read-only.

Commit 022328b

Browse files
authored
Merge pull request #47 from caltechmsc/bugfix/46-bgf-writer-conect-records-incompatible-with-strict-parsers-vmd-mpsim
fix(core): Correct BGF CONECT Record Formatting for VMD Compatibility
2 parents 9622f6d + 30b3e65 commit 022328b

1 file changed

Lines changed: 38 additions & 21 deletions

File tree

  • crates/scream-core/src/core/io

crates/scream-core/src/core/io/bgf.rs

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::core::models::residue::{Residue, ResidueType};
77
use crate::core::models::system::MolecularSystem;
88
use crate::core::models::topology::BondOrder;
99
use nalgebra::Point3;
10-
use std::collections::{BTreeSet, HashMap};
10+
use std::collections::{BTreeMap, HashMap};
1111
use std::io::{self, BufRead, Write};
1212
use thiserror::Error;
1313

@@ -270,25 +270,41 @@ impl MolecularFile for BgfFile {
270270
}
271271

272272
// --- Phase 4: Write CONECT records using the new serial numbers ---
273-
let mut bond_pairs = BTreeSet::new();
274-
for bond in system.bonds() {
275-
if let (Some(&serial1), Some(&serial2)) = (
276-
atom_id_to_new_serial.get(&bond.atom1_id),
277-
atom_id_to_new_serial.get(&bond.atom2_id),
278-
) {
279-
let bond_pair = if serial1 < serial2 {
280-
(serial1, serial2)
281-
} else {
282-
(serial2, serial1)
283-
};
284-
bond_pairs.insert(bond_pair);
273+
274+
// Step 4.1: Build an adjacency list using the new serial numbers.
275+
let mut serial_adjacency_list = BTreeMap::<usize, Vec<usize>>::new();
276+
for canonical_atom in &sorted_atoms {
277+
let base_serial = atom_id_to_new_serial[&canonical_atom.id];
278+
279+
if let Some(neighbor_ids) = system.get_bonded_neighbors(canonical_atom.id) {
280+
if neighbor_ids.is_empty() {
281+
continue;
282+
}
283+
284+
let neighbor_serials: Vec<usize> = neighbor_ids
285+
.iter()
286+
.map(|neighbor_id| atom_id_to_new_serial[neighbor_id])
287+
.collect();
288+
289+
serial_adjacency_list.insert(base_serial, neighbor_serials);
285290
}
286291
}
287292

288293
writeln!(writer, "FORMAT CONECT (a6,12i6)")?;
289294

290-
for (s1, s2) in bond_pairs {
291-
writeln!(writer, "CONECT {:>5} {:>6}", s1, s2)?;
295+
// Step 4.2: Write a CONECT record for each atom that has bonds.
296+
const MAX_NEIGHBORS_PER_LINE: usize = 12;
297+
298+
for (base_serial, mut neighbors) in serial_adjacency_list {
299+
neighbors.sort_unstable();
300+
301+
for neighbor_chunk in neighbors.chunks(MAX_NEIGHBORS_PER_LINE) {
302+
let mut line = format!("CONECT{:>6}", base_serial);
303+
for &neighbor_serial in neighbor_chunk {
304+
line.push_str(&format!("{:>6}", neighbor_serial));
305+
}
306+
writeln!(writer, "{}", line)?;
307+
}
292308
}
293309

294310
writeln!(writer, "END")?;
@@ -513,11 +529,12 @@ ATOM 6 CA ALA B 2 3.00000 0.00000 1.00000 C_3 1 4 0.07000
513529
ATOM 7 CB ALA B 2 3.50000 1.50000 1.00000 C_3 1 4 -0.18000
514530
FORMAT CONECT (a6,12i6)
515531
CONECT 1 2
516-
CONECT 2 3
517-
CONECT 3 4
518-
CONECT 3 5
519-
CONECT 5 6
520-
CONECT 6 7
532+
CONECT 2 1 3
533+
CONECT 3 2 4 5
534+
CONECT 4 3
535+
CONECT 5 3 6
536+
CONECT 6 5 7
537+
CONECT 7 6
521538
END
522539
"#;
523540

@@ -661,7 +678,7 @@ END
661678
.iter()
662679
.filter(|l| l.starts_with("CONECT"))
663680
.collect();
664-
assert_eq!(conect_lines.len(), 2);
681+
assert_eq!(conect_lines.len(), 4);
665682

666683
let bond1_found = conect_lines
667684
.iter()

0 commit comments

Comments
 (0)