1use crate::helpers::symbol_to_atomic_number;
2use crate::iterators::{self, ConFrameIterator};
3use crate::types::{ConFrame, ConFrameBuilder, meta};
4use crate::writer::ConFrameWriter;
5use std::ffi::{CStr, CString, c_char};
6use std::fs::File;
7use std::path::Path;
8use std::ptr;
9pub const RKR_CON_SPEC_VERSION: u32 = 3;
20#[unsafe(no_mangle)]
22pub extern "C" fn rkr_con_spec_version() -> u32 {
23 crate::CON_SPEC_VERSION
24}
25#[unsafe(no_mangle)]
28pub extern "C" fn rkr_library_version() -> *const c_char {
29 const VERSION_NUL: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
31 VERSION_NUL.as_ptr() as *const c_char
32}
33#[unsafe(no_mangle)]
45pub unsafe extern "C" fn rkr_frame_atom_index_by_id(
46 frame_handle: *const RKRConFrame,
47 atom_id: u64,
48) -> u64 {
49 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
50 Some(f) => f,
51 None => return u64::MAX,
52 };
53 match frame.atom_index_by_id(atom_id) {
54 Some(idx) => idx as u64,
55 None => u64::MAX,
56 }
57}
58#[unsafe(no_mangle)]
67pub unsafe extern "C" fn rkr_symbol_to_z(symbol: *const c_char) -> u64 {
68 if symbol.is_null() {
69 return 0;
70 }
71 match unsafe { CStr::from_ptr(symbol) }.to_str() {
72 Ok(s) => symbol_to_atomic_number(s),
73 Err(_) => 0,
74 }
75}
76#[unsafe(no_mangle)]
81pub extern "C" fn rkr_z_to_symbol(z: u64) -> *const c_char {
82 macro_rules! cstrs {
87 ($($lit:literal),* $(,)?) => {
88 [$(concat!($lit, "\0").as_bytes()),*]
89 };
90 }
91 const TABLE: [&[u8]; 93] = cstrs![
92 "X", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P",
93 "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
94 "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh",
95 "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
96 "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re",
97 "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
98 "Pa", "U",
99 ];
100 let idx = if (1..=92).contains(&z) { z as usize } else { 0 };
101 TABLE[idx].as_ptr() as *const c_char
102}
103#[unsafe(no_mangle)]
106pub extern "C" fn rkr_frame_spec_version(frame_handle: *const RKRConFrame) -> u32 {
107 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
108 Some(f) => f.header.spec_version,
109 None => 0,
110 }
111}
112#[unsafe(no_mangle)]
119pub unsafe extern "C" fn rkr_frame_metadata_json(frame_handle: *const RKRConFrame) -> *mut c_char {
120 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
121 Some(f) => f,
122 None => return ptr::null_mut(),
123 };
124 let mut obj = serde_json::Map::new();
125 obj.insert(
126 meta::CON_SPEC_VERSION.into(),
127 serde_json::Value::from(frame.header.spec_version),
128 );
129 for (k, v) in &frame.header.metadata {
130 obj.insert(k.clone(), v.clone());
131 }
132 let json_str = serde_json::Value::Object(obj).to_string();
133 match CString::new(json_str) {
134 Ok(cs) => cs.into_raw(),
135 Err(_) => ptr::null_mut(),
136 }
137}
138#[unsafe(no_mangle)]
140pub extern "C" fn rkr_frame_energy(frame_handle: *const RKRConFrame) -> f64 {
141 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
142 Some(f) => f.header.energy().unwrap_or(f64::NAN),
143 None => f64::NAN,
144 }
145}
146
147#[unsafe(no_mangle)]
150pub extern "C" fn rkr_frame_index_energy(frame_handle: *const RKRConFrame) -> f64 {
151 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
152 Some(f) => crate::index_proj::finite_energy(f).unwrap_or(f64::NAN),
153 None => f64::NAN,
154 }
155}
156
157#[unsafe(no_mangle)]
159pub unsafe extern "C" fn rkr_frame_composition_formula(
160 frame_handle: *const RKRConFrame,
161) -> *mut c_char {
162 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
163 Some(f) => f,
164 None => return ptr::null_mut(),
165 };
166 let s = crate::index_proj::frame_composition_formula(frame);
167 match CString::new(s) {
168 Ok(cs) => cs.into_raw(),
169 Err(_) => ptr::null_mut(),
170 }
171}
172
173#[unsafe(no_mangle)]
175pub extern "C" fn rkr_frame_total_mass(frame_handle: *const RKRConFrame) -> f64 {
176 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
177 Some(f) => crate::index_proj::frame_total_mass(f).unwrap_or(f64::NAN),
178 None => f64::NAN,
179 }
180}
181
182#[unsafe(no_mangle)]
184pub extern "C" fn rkr_frame_cell_volume(frame_handle: *const RKRConFrame) -> f64 {
185 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
186 Some(f) => crate::index_proj::frame_cell_volume(f).unwrap_or(f64::NAN),
187 None => f64::NAN,
188 }
189}
190
191#[unsafe(no_mangle)]
193pub extern "C" fn rkr_frame_fmax(frame_handle: *const RKRConFrame) -> f64 {
194 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
195 Some(f) => crate::index_proj::frame_fmax(f).unwrap_or(f64::NAN),
196 None => f64::NAN,
197 }
198}
199
200#[unsafe(no_mangle)]
202pub extern "C" fn rkr_frame_sections_mask(frame_handle: *const RKRConFrame) -> u8 {
203 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
204 Some(f) => crate::index_proj::sections_present_mask(f),
205 None => 0,
206 }
207}
208
209#[unsafe(no_mangle)]
211pub extern "C" fn rkr_frame_index_natoms(frame_handle: *const RKRConFrame) -> u32 {
212 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
213 Some(f) => f.atom_data.len() as u32,
214 None => 0,
215 }
216}
217
218#[unsafe(no_mangle)]
221pub unsafe extern "C" fn rkr_frame_index_projection_json(
222 frame_handle: *const RKRConFrame,
223) -> *mut c_char {
224 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
225 Some(f) => f,
226 None => return ptr::null_mut(),
227 };
228 let p = crate::index_proj::FrameIndexProjection::from_frame(frame);
229 let v = serde_json::json!({
230 "n_atoms": p.n_atoms,
231 "formula": p.formula,
232 "energy": p.energy,
233 "fmax": p.fmax,
234 "total_mass": p.total_mass,
235 "cell_volume": p.cell_volume,
236 "sections_mask": p.sections_mask,
237 "has_forces": p.has_forces,
238 "has_velocities": p.has_velocities,
239 "has_energy": p.has_energy,
240 "symbols": p.symbols,
241 "species_counts": p.species_counts.iter().map(|(s,c)| serde_json::json!([s, c])).collect::<Vec<_>>(),
242 "time": p.time,
243 "timestep": p.timestep,
244 "frame_index": p.frame_index,
245 "neb_bead": p.neb_bead,
246 "neb_band": p.neb_band,
247 "charge": p.charge,
248 "magmom": p.magmom,
249 });
250 match CString::new(v.to_string()) {
251 Ok(cs) => cs.into_raw(),
252 Err(_) => ptr::null_mut(),
253 }
254}
255#[unsafe(no_mangle)]
262pub unsafe extern "C" fn rkr_frame_potential_type(frame_handle: *const RKRConFrame) -> *mut c_char {
263 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
264 Some(f) => f,
265 None => return ptr::null_mut(),
266 };
267 match frame.header.potential_type() {
268 Some(pot_type) => match CString::new(pot_type) {
269 Ok(cs) => cs.into_raw(),
270 Err(_) => ptr::null_mut(),
271 },
272 None => ptr::null_mut(),
273 }
274}
275#[unsafe(no_mangle)]
277pub extern "C" fn rkr_frame_frame_index(frame_handle: *const RKRConFrame) -> u64 {
278 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
279 Some(f) => f.header.frame_index().unwrap_or(u64::MAX),
280 None => u64::MAX,
281 }
282}
283#[unsafe(no_mangle)]
285pub extern "C" fn rkr_frame_time(frame_handle: *const RKRConFrame) -> f64 {
286 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
287 Some(f) => f.header.time().unwrap_or(f64::NAN),
288 None => f64::NAN,
289 }
290}
291#[unsafe(no_mangle)]
293pub extern "C" fn rkr_frame_timestep(frame_handle: *const RKRConFrame) -> f64 {
294 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
295 Some(f) => f.header.timestep().unwrap_or(f64::NAN),
296 None => f64::NAN,
297 }
298}
299#[unsafe(no_mangle)]
301pub extern "C" fn rkr_frame_neb_bead(frame_handle: *const RKRConFrame) -> u64 {
302 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
303 Some(f) => f.header.neb_bead().unwrap_or(u64::MAX),
304 None => u64::MAX,
305 }
306}
307#[unsafe(no_mangle)]
309pub extern "C" fn rkr_frame_neb_band(frame_handle: *const RKRConFrame) -> u64 {
310 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
311 Some(f) => f.header.neb_band().unwrap_or(u64::MAX),
312 None => u64::MAX,
313 }
314}
315#[repr(C)]
320#[allow(non_camel_case_types)]
321#[derive(Debug, PartialEq, Eq)]
322pub enum RKRStatus {
323 RKR_STATUS_SUCCESS = 0,
325 RKR_STATUS_NULL_POINTER = -1,
327 RKR_STATUS_INVALID_UTF8 = -2,
329 RKR_STATUS_INVALID_JSON = -3,
331 RKR_STATUS_IO_ERROR = -4,
333 RKR_STATUS_INDEX_OUT_OF_BOUNDS = -5,
335 RKR_STATUS_BUFFER_TOO_SMALL = -6,
337 RKR_STATUS_INTERNAL_ERROR = -7,
339 RKR_STATUS_SECTION_ABSENT = -8,
342 RKR_STATUS_VALIDATION_ERROR = -9,
344 RKR_STATUS_SELECTION_ERROR = -10,
346 RKR_STATUS_FEATURE_DISABLED = -11,
349}
350#[unsafe(no_mangle)]
355pub unsafe extern "C" fn rkr_frame_bond_count(frame_handle: *const RKRConFrame) -> u64 {
356 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
357 Some(f) => f.bonds().len() as u64,
358 None => 0,
359 }
360}
361#[unsafe(no_mangle)]
370pub unsafe extern "C" fn rkr_frame_bond_at(
371 frame_handle: *const RKRConFrame,
372 index: u64,
373 out_i: *mut u32,
374 out_j: *mut u32,
375 out_has_order: *mut u8,
376 out_order: *mut i32,
377) -> RKRStatus {
378 if frame_handle.is_null()
379 || out_i.is_null()
380 || out_j.is_null()
381 || out_has_order.is_null()
382 || out_order.is_null()
383 {
384 return RKRStatus::RKR_STATUS_NULL_POINTER;
385 }
386 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
387 return RKRStatus::RKR_STATUS_NULL_POINTER;
388 };
389 let bonds = frame.bonds();
390 let Some(bond) = bonds.get(index as usize) else {
391 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
392 };
393 unsafe {
394 *out_i = bond.i;
395 *out_j = bond.j;
396 if let Some(order) = bond.order {
397 *out_has_order = 1;
398 *out_order = order;
399 } else {
400 *out_has_order = 0;
401 *out_order = 0;
402 }
403 }
404 RKRStatus::RKR_STATUS_SUCCESS
405}
406#[unsafe(no_mangle)]
409pub extern "C" fn rkr_status_message(status: RKRStatus) -> *const c_char {
410 match status {
411 RKRStatus::RKR_STATUS_SUCCESS => c"success".as_ptr(),
412 RKRStatus::RKR_STATUS_NULL_POINTER => c"null pointer".as_ptr(),
413 RKRStatus::RKR_STATUS_INVALID_UTF8 => c"invalid UTF-8".as_ptr(),
414 RKRStatus::RKR_STATUS_INVALID_JSON => c"invalid JSON".as_ptr(),
415 RKRStatus::RKR_STATUS_IO_ERROR => c"I/O error".as_ptr(),
416 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS => c"index out of bounds".as_ptr(),
417 RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL => c"buffer too small".as_ptr(),
418 RKRStatus::RKR_STATUS_INTERNAL_ERROR => c"internal error".as_ptr(),
419 RKRStatus::RKR_STATUS_SECTION_ABSENT => c"section absent".as_ptr(),
420 RKRStatus::RKR_STATUS_VALIDATION_ERROR => c"validation error".as_ptr(),
421 RKRStatus::RKR_STATUS_SELECTION_ERROR => c"selection error".as_ptr(),
422 RKRStatus::RKR_STATUS_FEATURE_DISABLED => c"feature disabled in this build".as_ptr(),
423 }
424}
425#[repr(C)]
428pub struct RKRConFrame {
429 _private: [u8; 0],
430}
431#[repr(C)]
434pub struct RKRConFrameWriter {
435 _private: [u8; 0],
436}
437#[repr(C)]
441pub struct CFrame {
442 pub atoms: *mut CAtom,
443 pub num_atoms: usize,
444 pub cell: [f64; 3],
445 pub angles: [f64; 3],
446 pub has_velocities: bool,
447 pub has_forces: bool,
448 pub has_energies: bool,
449}
450#[repr(C)]
460pub struct CAtom {
461 pub atomic_number: u64,
462 pub x: f64,
463 pub y: f64,
464 pub z: f64,
465 pub atom_id: u64,
466 pub mass: f64,
467 pub is_fixed: bool,
470 pub fixed_x: bool,
471 pub fixed_y: bool,
472 pub fixed_z: bool,
473 pub vx: f64,
474 pub vy: f64,
475 pub vz: f64,
476 pub has_velocity: bool,
477 pub fx: f64,
478 pub fy: f64,
479 pub fz: f64,
480 pub has_forces: bool,
481 pub energy: f64,
484 pub has_energy: bool,
485}
486#[repr(C)]
487pub struct CConFrameIterator {
488 iterator: *mut ConFrameIterator<'static>,
489 file_contents: *mut String,
490}
491
492fn c_iterator_from_owned_string(contents: String) -> *mut CConFrameIterator {
494 let file_contents_box = Box::new(contents);
495 let file_contents_ptr = Box::into_raw(file_contents_box);
496 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
497 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
498 let c_iterator = Box::new(CConFrameIterator {
499 iterator: Box::into_raw(iterator),
500 file_contents: file_contents_ptr,
501 });
502 Box::into_raw(c_iterator)
503}
504
505#[unsafe(no_mangle)]
521pub unsafe extern "C" fn read_con_file_iterator(
522 filename_c: *const c_char,
523) -> *mut CConFrameIterator {
524 if filename_c.is_null() {
525 return ptr::null_mut();
526 }
527 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
528 Ok(s) => s,
529 Err(_) => return ptr::null_mut(),
530 };
531 let owned = match crate::compression::read_file_contents(Path::new(filename)) {
532 Ok(fc) => match fc.as_str() {
533 Ok(s) => s.to_owned(),
534 Err(_) => return ptr::null_mut(),
535 },
536 Err(_) => return ptr::null_mut(),
537 };
538 c_iterator_from_owned_string(owned)
539}
540
541#[unsafe(no_mangle)]
549pub unsafe extern "C" fn read_con_string_iterator(
550 contents_c: *const c_char,
551) -> *mut CConFrameIterator {
552 if contents_c.is_null() {
553 return ptr::null_mut();
554 }
555 let contents = match unsafe { CStr::from_ptr(contents_c).to_str() } {
556 Ok(s) => s.to_owned(),
557 Err(_) => return ptr::null_mut(),
558 };
559 c_iterator_from_owned_string(contents)
560}
561
562#[unsafe(no_mangle)]
569pub unsafe extern "C" fn read_con_buffer_iterator(
570 data: *const u8,
571 len: usize,
572) -> *mut CConFrameIterator {
573 if data.is_null() && len > 0 {
574 return ptr::null_mut();
575 }
576 if len == 0 {
577 return c_iterator_from_owned_string(String::new());
578 }
579 let slice = unsafe { std::slice::from_raw_parts(data, len) };
580 let contents = match std::str::from_utf8(slice) {
581 Ok(s) => s.to_owned(),
582 Err(_) => return ptr::null_mut(),
583 };
584 c_iterator_from_owned_string(contents)
585}
586#[unsafe(no_mangle)]
592pub unsafe extern "C" fn con_frame_iterator_next(
593 iterator: *mut CConFrameIterator,
594) -> *mut RKRConFrame {
595 if iterator.is_null() {
596 return ptr::null_mut();
597 }
598 let iter = unsafe { &mut *(*iterator).iterator };
599 match iter.next() {
600 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
601 _ => ptr::null_mut(),
602 }
603}
604#[unsafe(no_mangle)]
609pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
610 if !frame_handle.is_null() {
611 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
612 }
613}
614#[unsafe(no_mangle)]
619pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
620 if iterator.is_null() {
621 return;
622 }
623 unsafe {
624 let c_iterator_box = Box::from_raw(iterator);
625 let _ = Box::from_raw(c_iterator_box.iterator);
626 let _ = Box::from_raw(c_iterator_box.file_contents);
627 }
628}
629#[unsafe(no_mangle)]
638pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
639 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
640 Some(f) => f,
641 None => return ptr::null_mut(),
642 };
643 let masses_iter = frame
644 .header
645 .natms_per_type
646 .iter()
647 .zip(frame.header.masses_per_type.iter())
648 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
649 let has_velocities = frame.has_velocities();
650 let mut c_atoms: Vec<CAtom> = frame
651 .atom_data
652 .iter()
653 .zip(masses_iter)
654 .map(|(atom_datum, mass)| {
655 let [vx, vy, vz] = atom_datum.velocity.unwrap_or([0.0; 3]);
656 let [fx, fy, fz] = atom_datum.force.unwrap_or([0.0; 3]);
657 CAtom {
658 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
659 x: atom_datum.x,
660 y: atom_datum.y,
661 z: atom_datum.z,
662 is_fixed: atom_datum.is_fixed(),
663 fixed_x: atom_datum.fixed[0],
664 fixed_y: atom_datum.fixed[1],
665 fixed_z: atom_datum.fixed[2],
666 atom_id: atom_datum.atom_id,
667 mass,
668 vx,
669 vy,
670 vz,
671 has_velocity: atom_datum.has_velocity(),
672 fx,
673 fy,
674 fz,
675 has_forces: atom_datum.has_forces(),
676 energy: atom_datum.energy.unwrap_or(0.0),
677 has_energy: atom_datum.has_energy(),
678 }
679 })
680 .collect();
681 let atoms_ptr = c_atoms.as_mut_ptr();
682 let num_atoms = c_atoms.len();
683 std::mem::forget(c_atoms);
684 let has_forces = frame.has_forces();
685 let has_energies = frame.has_energies();
686 let c_frame = Box::new(CFrame {
687 atoms: atoms_ptr,
688 num_atoms,
689 cell: frame.header.boxl,
690 angles: frame.header.angles,
691 has_velocities,
692 has_forces,
693 has_energies,
694 });
695 Box::into_raw(c_frame)
696}
697#[unsafe(no_mangle)]
702pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
703 if frame.is_null() {
704 return;
705 }
706 unsafe {
707 let frame_box = Box::from_raw(frame);
708 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
709 }
710}
711#[unsafe(no_mangle)]
730pub unsafe extern "C" fn rkr_frame_get_header_line(
731 frame_handle: *const RKRConFrame,
732 is_prebox: bool,
733 line_index: usize,
734 buffer: *mut c_char,
735 buffer_len: usize,
736) -> RKRStatus {
737 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
738 Some(f) => f,
739 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
740 };
741 if buffer.is_null() {
742 return RKRStatus::RKR_STATUS_NULL_POINTER;
743 }
744 if buffer_len == 0 {
745 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
746 }
747 let line_to_copy: Option<&str> = if is_prebox {
748 match line_index {
749 0 => Some(frame.header.prebox_header.user.as_str()),
750 1 => Some(frame.header.prebox_header.metadata_line()),
751 _ => None,
752 }
753 } else {
754 frame.header.postbox_header.get(line_index).map(String::as_str)
755 };
756 if let Some(line) = line_to_copy {
757 let bytes = line.as_bytes();
758 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
759 unsafe {
760 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
761 *buffer.add(len_to_copy) = 0;
762 }
763 RKRStatus::RKR_STATUS_SUCCESS
764 } else {
765 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
766 }
767}
768#[unsafe(no_mangle)]
781pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
782 frame_handle: *const RKRConFrame,
783 is_prebox: bool,
784 line_index: usize,
785) -> *mut c_char {
786 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
787 Some(f) => f,
788 None => return ptr::null_mut(),
789 };
790 let line_to_copy: Option<&str> = if is_prebox {
791 match line_index {
792 0 => Some(frame.header.prebox_header.user.as_str()),
793 1 => Some(frame.header.prebox_header.metadata_line()),
794 _ => None,
795 }
796 } else {
797 frame.header.postbox_header.get(line_index).map(String::as_str)
798 };
799 if let Some(line) = line_to_copy {
800 match CString::new(line) {
802 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
805 } else {
806 ptr::null_mut() }
808}
809#[unsafe(no_mangle)]
817pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
818 if !s.is_null() {
819 let _ = unsafe { CString::from_raw(s) };
821 }
822}
823type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
837#[inline]
840fn into_rkr_writer(
841 sink: Box<dyn std::io::Write>,
842 precision: Option<u8>,
843) -> *mut RKRConFrameWriter {
844 let writer: RkrWriter = match precision {
845 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
846 None => ConFrameWriter::new(sink),
847 };
848 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
849}
850#[inline]
852unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
853 if filename_c.is_null() {
854 return None;
855 }
856 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
857}
858#[unsafe(no_mangle)]
864pub unsafe extern "C" fn create_writer_from_path_c(
865 filename_c: *const c_char,
866) -> *mut RKRConFrameWriter {
867 let filename = match unsafe { cstr_path(filename_c) } {
868 Some(s) => s,
869 None => return ptr::null_mut(),
870 };
871 match File::create(filename) {
872 Ok(file) => into_rkr_writer(Box::new(file), None),
873 Err(_) => ptr::null_mut(),
874 }
875}
876#[unsafe(no_mangle)]
881pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
882 if !writer_handle.is_null() {
883 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
884 }
885}
886#[unsafe(no_mangle)]
892pub unsafe extern "C" fn rkr_writer_extend(
893 writer_handle: *mut RKRConFrameWriter,
894 frame_handles: *const *const RKRConFrame,
895 num_frames: usize,
896) -> RKRStatus {
897 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
898 Some(w) => w,
899 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
900 };
901 if frame_handles.is_null() {
902 return RKRStatus::RKR_STATUS_NULL_POINTER;
903 }
904 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
905 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
906 if handles_slice.iter().any(|&handle| handle.is_null()) {
907 return RKRStatus::RKR_STATUS_NULL_POINTER;
910 }
911 for &handle in handles_slice.iter() {
912 match unsafe { (handle as *const ConFrame).as_ref() } {
914 Some(frame) => rust_frames.push(frame),
915 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
917 }
918 }
919 match writer.extend(rust_frames.into_iter()) {
920 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
921 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
922 }
923}
924
925#[unsafe(no_mangle)]
931pub unsafe extern "C" fn rkr_writer_set_canonical(
932 writer_handle: *mut RKRConFrameWriter,
933 canonical: u8,
934) -> RKRStatus {
935 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
936 Some(w) => w,
937 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
938 };
939 writer.set_canonical(canonical != 0);
940 RKRStatus::RKR_STATUS_SUCCESS
941}
942
943#[unsafe(no_mangle)]
945pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
946 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
947 Some(w) => u8::from(w.is_canonical()),
948 None => 0,
949 }
950}
951
952#[cfg(test)]
953mod index_proj_ffi_tests {
954 use super::*;
955 use std::ffi::CStr;
956 use std::fs;
957 use std::path::PathBuf;
958
959 fn fixture_path() -> PathBuf {
960 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
961 }
962
963 #[test]
964 fn ffi_projection_matches_index_proj() {
965 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
966 let fr = &frames[0];
967 let handle = fr as *const ConFrame as *const RKRConFrame;
968 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
969 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
971 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
972 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
973 assert!(!formula_c.is_null());
974 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
975 assert_eq!(formula, proj.formula);
976 unsafe { rkr_free_string(formula_c) };
977 let ie = rkr_frame_index_energy(handle);
978 match proj.energy {
979 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
980 None => assert!(ie.is_nan()),
981 }
982 let tm = rkr_frame_total_mass(handle);
983 match proj.total_mass {
984 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
985 None => assert!(tm.is_nan()),
986 }
987 let cv = rkr_frame_cell_volume(handle);
988 match proj.cell_volume {
989 Some(v) => assert!((cv - v).abs() < 1e-6 * v.max(1.0), "cell_volume C={cv} proj={v}"),
990 None => assert!(cv.is_nan()),
991 }
992 let fm = rkr_frame_fmax(handle);
993 match proj.fmax {
994 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
995 None => assert!(fm.is_nan()),
996 }
997 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
998 assert!(!json_c.is_null());
999 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
1000 assert!(json.contains("\"formula\""));
1001 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
1002 assert!(json.contains("\"n_atoms\""));
1003 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
1004 unsafe { rkr_free_string(json_c) };
1005 }
1006
1007 #[test]
1008 fn ffi_canonical_writer_byte_identical() {
1009 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1010 let fr = &frames[0];
1011 let dir = tempfile::tempdir().unwrap();
1012 let p1 = dir.path().join("a.con");
1013 let p2 = dir.path().join("b.con");
1014 for p in [&p1, &p2] {
1015 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
1016 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
1017 assert!(!w.is_null());
1018 assert_eq!(
1019 unsafe { rkr_writer_set_canonical(w, 1) },
1020 RKRStatus::RKR_STATUS_SUCCESS
1021 );
1022 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
1023 let handles = [fr as *const ConFrame as *const RKRConFrame];
1024 assert_eq!(
1025 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
1026 RKRStatus::RKR_STATUS_SUCCESS
1027 );
1028 unsafe { free_rkr_writer(w) };
1029 }
1030 let b1 = fs::read(&p1).unwrap();
1031 let b2 = fs::read(&p2).unwrap();
1032 assert_eq!(b1, b2);
1033 assert!(!b1.is_empty());
1034 }
1035}
1036#[unsafe(no_mangle)]
1045pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
1046 filename_c: *const c_char,
1047 precision: u8,
1048) -> *mut RKRConFrameWriter {
1049 let filename = match unsafe { cstr_path(filename_c) } {
1050 Some(s) => s,
1051 None => return ptr::null_mut(),
1052 };
1053 match File::create(filename) {
1054 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1055 Err(_) => ptr::null_mut(),
1056 }
1057}
1058#[repr(C)]
1063pub struct RKRConFrameBuilder {
1064 _private: [u8; 0],
1065}
1066#[allow(clippy::too_many_arguments)]
1067unsafe fn add_builder_atom(
1068 builder_handle: *mut RKRConFrameBuilder,
1069 symbol: *const c_char,
1070 x: f64,
1071 y: f64,
1072 z: f64,
1073 fixed: [bool; 3],
1074 atom_id: u64,
1075 mass: f64,
1076 velocity: Option<[f64; 3]>,
1077 forces: Option<[f64; 3]>,
1078) -> RKRStatus {
1079 if builder_handle.is_null() || symbol.is_null() {
1080 return RKRStatus::RKR_STATUS_NULL_POINTER;
1081 }
1082 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1083 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
1084 Ok(s) => s,
1085 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1086 };
1087 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
1088 if let Some(v) = velocity {
1089 builder.with_velocity(v);
1090 }
1091 if let Some(f) = forces {
1092 builder.with_force(f);
1093 }
1094 RKRStatus::RKR_STATUS_SUCCESS
1095}
1096#[unsafe(no_mangle)]
1102pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
1103 builder_handle: *mut RKRConFrameBuilder,
1104 velocity: *const f64,
1105) -> RKRStatus {
1106 if builder_handle.is_null() || velocity.is_null() {
1107 return RKRStatus::RKR_STATUS_NULL_POINTER;
1108 }
1109 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1110 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1111 builder.with_velocity(v);
1112 RKRStatus::RKR_STATUS_SUCCESS
1113}
1114#[unsafe(no_mangle)]
1120pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
1121 builder_handle: *mut RKRConFrameBuilder,
1122 force: *const f64,
1123) -> RKRStatus {
1124 if builder_handle.is_null() || force.is_null() {
1125 return RKRStatus::RKR_STATUS_NULL_POINTER;
1126 }
1127 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1128 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1129 builder.with_force(f);
1130 RKRStatus::RKR_STATUS_SUCCESS
1131}
1132#[unsafe(no_mangle)]
1142pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1143 builder_handle: *mut RKRConFrameBuilder,
1144 energy: f64,
1145) -> RKRStatus {
1146 if builder_handle.is_null() {
1147 return RKRStatus::RKR_STATUS_NULL_POINTER;
1148 }
1149 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1150 builder.with_energy(energy);
1151 RKRStatus::RKR_STATUS_SUCCESS
1152}
1153fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1167 use crate::error::ParseError;
1168 match e {
1169 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1170 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1171 }
1172 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1173 }
1174}
1175#[unsafe(no_mangle)]
1182pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1183 builder_handle: *const RKRConFrameBuilder,
1184) -> usize {
1185 if builder_handle.is_null() {
1186 return 0;
1187 }
1188 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1189 builder.atom_count()
1190}
1191#[unsafe(no_mangle)]
1195pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1196 builder_handle: *mut RKRConFrameBuilder,
1197 index: usize,
1198 x: f64,
1199 y: f64,
1200 z: f64,
1201) -> RKRStatus {
1202 if builder_handle.is_null() {
1203 return RKRStatus::RKR_STATUS_NULL_POINTER;
1204 }
1205 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1206 match builder.set_atom_position(index, x, y, z) {
1207 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1208 Err(e) => map_builder_err(e),
1209 }
1210}
1211#[unsafe(no_mangle)]
1215pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1216 builder_handle: *mut RKRConFrameBuilder,
1217 index: usize,
1218 velocity: *const f64,
1219) -> RKRStatus {
1220 if builder_handle.is_null() || velocity.is_null() {
1221 return RKRStatus::RKR_STATUS_NULL_POINTER;
1222 }
1223 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1224 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1225 match builder.set_atom_velocity(index, v) {
1226 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1227 Err(e) => map_builder_err(e),
1228 }
1229}
1230#[unsafe(no_mangle)]
1234pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1235 builder_handle: *mut RKRConFrameBuilder,
1236 index: usize,
1237 force: *const f64,
1238) -> RKRStatus {
1239 if builder_handle.is_null() || force.is_null() {
1240 return RKRStatus::RKR_STATUS_NULL_POINTER;
1241 }
1242 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1243 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1244 match builder.set_atom_force(index, f) {
1245 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1246 Err(e) => map_builder_err(e),
1247 }
1248}
1249#[unsafe(no_mangle)]
1253pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1254 builder_handle: *mut RKRConFrameBuilder,
1255 index: usize,
1256 energy: f64,
1257) -> RKRStatus {
1258 if builder_handle.is_null() {
1259 return RKRStatus::RKR_STATUS_NULL_POINTER;
1260 }
1261 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1262 match builder.set_atom_energy(index, energy) {
1263 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1264 Err(e) => map_builder_err(e),
1265 }
1266}
1267#[unsafe(no_mangle)]
1271pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1272 builder_handle: *mut RKRConFrameBuilder,
1273 index: usize,
1274 fixed_x: bool,
1275 fixed_y: bool,
1276 fixed_z: bool,
1277) -> RKRStatus {
1278 if builder_handle.is_null() {
1279 return RKRStatus::RKR_STATUS_NULL_POINTER;
1280 }
1281 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1282 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1283 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1284 Err(e) => map_builder_err(e),
1285 }
1286}
1287#[unsafe(no_mangle)]
1291pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1292 builder_handle: *mut RKRConFrameBuilder,
1293 index: usize,
1294 mass: f64,
1295) -> RKRStatus {
1296 if builder_handle.is_null() {
1297 return RKRStatus::RKR_STATUS_NULL_POINTER;
1298 }
1299 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1300 match builder.set_atom_mass(index, mass) {
1301 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1302 Err(e) => map_builder_err(e),
1303 }
1304}
1305#[unsafe(no_mangle)]
1312pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1313 builder_handle: *mut RKRConFrameBuilder,
1314 index: usize,
1315 atom_id: u64,
1316) -> RKRStatus {
1317 if builder_handle.is_null() {
1318 return RKRStatus::RKR_STATUS_NULL_POINTER;
1319 }
1320 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1321 match builder.set_atom_id(index, atom_id) {
1322 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1323 Err(e) => map_builder_err(e),
1324 }
1325}
1326#[unsafe(no_mangle)]
1330pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1331 builder_handle: *mut RKRConFrameBuilder,
1332 index: usize,
1333) -> RKRStatus {
1334 if builder_handle.is_null() {
1335 return RKRStatus::RKR_STATUS_NULL_POINTER;
1336 }
1337 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1338 match builder.clear_atom_velocity(index) {
1339 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1340 Err(e) => map_builder_err(e),
1341 }
1342}
1343#[unsafe(no_mangle)]
1346pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1347 builder_handle: *mut RKRConFrameBuilder,
1348 index: usize,
1349) -> RKRStatus {
1350 if builder_handle.is_null() {
1351 return RKRStatus::RKR_STATUS_NULL_POINTER;
1352 }
1353 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1354 match builder.clear_atom_force(index) {
1355 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1356 Err(e) => map_builder_err(e),
1357 }
1358}
1359#[unsafe(no_mangle)]
1362pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1363 builder_handle: *mut RKRConFrameBuilder,
1364 index: usize,
1365) -> RKRStatus {
1366 if builder_handle.is_null() {
1367 return RKRStatus::RKR_STATUS_NULL_POINTER;
1368 }
1369 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1370 match builder.clear_atom_energy(index) {
1371 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1372 Err(e) => map_builder_err(e),
1373 }
1374}
1375#[unsafe(no_mangle)]
1380pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1381 builder_handle: *mut RKRConFrameBuilder,
1382 positions: *const f64,
1383 len: usize,
1384) -> RKRStatus {
1385 if builder_handle.is_null() || positions.is_null() {
1386 return RKRStatus::RKR_STATUS_NULL_POINTER;
1387 }
1388 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1389 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1390 match builder.set_positions_from_flat(slice) {
1391 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1392 Err(e) => map_builder_err(e),
1393 }
1394}
1395#[unsafe(no_mangle)]
1399pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1400 builder_handle: *mut RKRConFrameBuilder,
1401 forces: *const f64,
1402 len: usize,
1403) -> RKRStatus {
1404 if builder_handle.is_null() || forces.is_null() {
1405 return RKRStatus::RKR_STATUS_NULL_POINTER;
1406 }
1407 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1408 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1409 match builder.set_forces_from_flat(slice) {
1410 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1411 Err(e) => map_builder_err(e),
1412 }
1413}
1414#[unsafe(no_mangle)]
1418pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1419 builder_handle: *mut RKRConFrameBuilder,
1420 energies: *const f64,
1421 len: usize,
1422) -> RKRStatus {
1423 if builder_handle.is_null() || energies.is_null() {
1424 return RKRStatus::RKR_STATUS_NULL_POINTER;
1425 }
1426 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1427 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1428 match builder.set_atom_energies_from_flat(slice) {
1429 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1430 Err(e) => map_builder_err(e),
1431 }
1432}
1433#[unsafe(no_mangle)]
1437pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1438 builder_handle: *const RKRConFrameBuilder,
1439 index: usize,
1440 out_xyz: *mut f64,
1441) -> RKRStatus {
1442 if builder_handle.is_null() || out_xyz.is_null() {
1443 return RKRStatus::RKR_STATUS_NULL_POINTER;
1444 }
1445 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1446 match builder.get_atom_position(index) {
1447 Ok((x, y, z)) => unsafe {
1448 *out_xyz = x;
1449 *out_xyz.add(1) = y;
1450 *out_xyz.add(2) = z;
1451 RKRStatus::RKR_STATUS_SUCCESS
1452 },
1453 Err(e) => map_builder_err(e),
1454 }
1455}
1456#[unsafe(no_mangle)]
1463pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1464 builder_handle: *const RKRConFrameBuilder,
1465 index: usize,
1466 out_xyz: *mut f64,
1467 has_value: *mut bool,
1468) -> RKRStatus {
1469 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1470 return RKRStatus::RKR_STATUS_NULL_POINTER;
1471 }
1472 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1473 match builder.get_atom_velocity(index) {
1474 Ok(Some(v)) => unsafe {
1475 *out_xyz = v[0];
1476 *out_xyz.add(1) = v[1];
1477 *out_xyz.add(2) = v[2];
1478 *has_value = true;
1479 RKRStatus::RKR_STATUS_SUCCESS
1480 },
1481 Ok(None) => unsafe {
1482 *has_value = false;
1483 RKRStatus::RKR_STATUS_SUCCESS
1484 },
1485 Err(e) => map_builder_err(e),
1486 }
1487}
1488#[unsafe(no_mangle)]
1491pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1492 builder_handle: *const RKRConFrameBuilder,
1493 index: usize,
1494 out_xyz: *mut f64,
1495 has_value: *mut bool,
1496) -> RKRStatus {
1497 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1498 return RKRStatus::RKR_STATUS_NULL_POINTER;
1499 }
1500 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1501 match builder.get_atom_force(index) {
1502 Ok(Some(f)) => unsafe {
1503 *out_xyz = f[0];
1504 *out_xyz.add(1) = f[1];
1505 *out_xyz.add(2) = f[2];
1506 *has_value = true;
1507 RKRStatus::RKR_STATUS_SUCCESS
1508 },
1509 Ok(None) => unsafe {
1510 *has_value = false;
1511 RKRStatus::RKR_STATUS_SUCCESS
1512 },
1513 Err(e) => map_builder_err(e),
1514 }
1515}
1516#[unsafe(no_mangle)]
1522pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
1523 builder_handle: *const RKRConFrameBuilder,
1524 index: usize,
1525 out_value: *mut f64,
1526 has_value: *mut bool,
1527) -> RKRStatus {
1528 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
1529 return RKRStatus::RKR_STATUS_NULL_POINTER;
1530 }
1531 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1532 match builder.get_atom_energy(index) {
1533 Ok(Some(e)) => unsafe {
1534 *out_value = e;
1535 *has_value = true;
1536 RKRStatus::RKR_STATUS_SUCCESS
1537 },
1538 Ok(None) => unsafe {
1539 *has_value = false;
1540 RKRStatus::RKR_STATUS_SUCCESS
1541 },
1542 Err(e) => map_builder_err(e),
1543 }
1544}
1545#[unsafe(no_mangle)]
1549pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
1550 builder_handle: *const RKRConFrameBuilder,
1551 index: usize,
1552 out_mass: *mut f64,
1553) -> RKRStatus {
1554 if builder_handle.is_null() || out_mass.is_null() {
1555 return RKRStatus::RKR_STATUS_NULL_POINTER;
1556 }
1557 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1558 match builder.get_atom_mass(index) {
1559 Ok(m) => unsafe {
1560 *out_mass = m;
1561 RKRStatus::RKR_STATUS_SUCCESS
1562 },
1563 Err(e) => map_builder_err(e),
1564 }
1565}
1566pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
1589fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
1590 use crate::error::ParseError;
1591 match e {
1592 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1593 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1594 }
1595}
1596pub mod rkr_dl_type_code {
1600 pub const RKR_DL_INT: u8 = 0;
1601 pub const RKR_DL_UINT: u8 = 1;
1602 pub const RKR_DL_FLOAT: u8 = 2;
1603 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
1604 pub const RKR_DL_BFLOAT: u8 = 4;
1605 pub const RKR_DL_COMPLEX: u8 = 5;
1606 pub const RKR_DL_BOOL: u8 = 6;
1607}
1608
1609pub mod rkr_dl_device_type {
1611 pub const RKR_DL_CPU: i32 = 1;
1612 pub const RKR_DL_CUDA: i32 = 2;
1613 pub const RKR_DL_CUDA_HOST: i32 = 3;
1614}
1615
1616#[repr(C)]
1620#[derive(Clone, Copy, Debug)]
1621pub struct RKRDLDataType {
1622 pub code: u8,
1624 pub bits: u8,
1626 pub lanes: u16,
1628}
1629
1630#[repr(C)]
1633#[derive(Clone, Copy, Debug)]
1634pub struct RKRDLDevice {
1635 pub device_type: i32,
1637 pub device_id: i32,
1638}
1639
1640#[repr(C)]
1655#[derive(Clone, Copy, Debug)]
1656pub struct RKRDlpackExportOptions {
1657 pub dtype: RKRDLDataType,
1659 pub device: RKRDLDevice,
1661}
1662
1663impl Default for RKRDlpackExportOptions {
1664 fn default() -> Self {
1665 Self {
1666 dtype: RKRDLDataType {
1667 code: rkr_dl_type_code::RKR_DL_FLOAT,
1668 bits: 64,
1669 lanes: 1,
1670 },
1671 device: RKRDLDevice {
1672 device_type: rkr_dl_device_type::RKR_DL_CPU,
1673 device_id: 0,
1674 },
1675 }
1676 }
1677}
1678
1679fn resolve_dlpack_opts(opts: *const RKRDlpackExportOptions) -> Result<RKRDlpackExportOptions, RKRStatus> {
1681 let o = if opts.is_null() {
1682 RKRDlpackExportOptions::default()
1683 } else {
1684 unsafe { *opts }
1685 };
1686 if o.device.device_type != rkr_dl_device_type::RKR_DL_CPU {
1687 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1689 }
1690 if o.dtype.lanes != 1 {
1691 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
1692 }
1693 Ok(o)
1694}
1695
1696fn finish_dlpack_tensor<E: std::fmt::Display>(
1697 result: Result<dlpk::DLPackTensor, E>,
1698 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1699) -> RKRStatus {
1700 match result {
1701 Ok(tensor) => {
1702 let raw = tensor.into_raw();
1703 unsafe {
1704 *out_tensor = raw.as_ptr();
1705 }
1706 RKRStatus::RKR_STATUS_SUCCESS
1707 }
1708 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
1709 "DLPack export failed: {e}"
1710 ))),
1711 }
1712}
1713
1714fn export_f64_slice_as_dlpack(
1717 data: &[f64],
1718 shape: &[usize],
1719 dtype: RKRDLDataType,
1720 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1721) -> RKRStatus {
1722 use rkr_dl_type_code::*;
1723 if dtype.lanes != 1 {
1724 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
1725 }
1726 macro_rules! arc_export {
1727 ($ty:ty, $map:expr) => {{
1728 let v: Vec<$ty> = data.iter().map($map).collect();
1729 match *shape {
1730 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
1731 Ok(a) => dlpk::DLPackTensor::try_from(a),
1732 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1733 },
1734 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
1735 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1736 }
1737 }};
1738 }
1739 let tensor = match (dtype.code, dtype.bits) {
1740 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
1741 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
1742 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
1743 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
1744 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
1745 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
1746 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
1747 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
1748 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
1749 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
1750 (RKR_DL_BOOL, 8) => {
1751 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
1753 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
1754 }
1755 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1757 };
1758 finish_dlpack_tensor(tensor, out_tensor)
1759}
1760
1761fn export_owned_array2_dlpack_opts(
1762 arr: &ndarray::ArcArray2<f64>,
1763 opts: &RKRDlpackExportOptions,
1764 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1765) -> RKRStatus {
1766 let (r, c) = arr.dim();
1767 let flat: Vec<f64> = arr.iter().copied().collect();
1768 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
1769}
1770
1771fn export_owned_array1_f64_dlpack_opts(
1772 arr: &ndarray::ArcArray1<f64>,
1773 opts: &RKRDlpackExportOptions,
1774 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1775) -> RKRStatus {
1776 let flat = arr.to_vec();
1777 let n = flat.len();
1778 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
1779}
1780
1781fn export_owned_array1_u64_dlpack(
1782 arr: &ndarray::ArcArray1<u64>,
1783 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1784) -> RKRStatus {
1785 finish_dlpack_tensor(
1786 dlpk::DLPackTensor::try_from(arr.clone()),
1787 out_tensor,
1788 )
1789}
1790
1791fn export_owned_array1_u64_dlpack_opts(
1793 arr: &ndarray::ArcArray1<u64>,
1794 opts: &RKRDlpackExportOptions,
1795 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1796) -> RKRStatus {
1797 use rkr_dl_type_code::*;
1798 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
1799 return export_owned_array1_u64_dlpack(arr, out_tensor);
1800 }
1801 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
1802 let n = as_f64.len();
1803 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
1804}
1805#[unsafe(no_mangle)]
1816pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
1817 builder_handle: *const RKRConFrameBuilder,
1818 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1819) -> RKRStatus {
1820 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
1821}
1822
1823#[unsafe(no_mangle)]
1830pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
1831 builder_handle: *const RKRConFrameBuilder,
1832 opts: *const RKRDlpackExportOptions,
1833 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1834) -> RKRStatus {
1835 if builder_handle.is_null() || out_tensor.is_null() {
1836 return RKRStatus::RKR_STATUS_NULL_POINTER;
1837 }
1838 let o = match resolve_dlpack_opts(opts) {
1839 Ok(o) => o,
1840 Err(st) => return st,
1841 };
1842 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1843 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
1844}
1845
1846#[unsafe(no_mangle)]
1856pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
1857 builder_handle: *const RKRConFrameBuilder,
1858 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1859) -> RKRStatus {
1860 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
1861}
1862
1863#[unsafe(no_mangle)]
1864pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
1865 builder_handle: *const RKRConFrameBuilder,
1866 opts: *const RKRDlpackExportOptions,
1867 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1868) -> RKRStatus {
1869 if builder_handle.is_null() || out_tensor.is_null() {
1870 return RKRStatus::RKR_STATUS_NULL_POINTER;
1871 }
1872 let o = match resolve_dlpack_opts(opts) {
1873 Ok(o) => o,
1874 Err(st) => return st,
1875 };
1876 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1877 if !builder.has_velocities_section() {
1878 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
1879 }
1880 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
1881}
1882
1883#[unsafe(no_mangle)]
1892pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
1893 builder_handle: *const RKRConFrameBuilder,
1894 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1895) -> RKRStatus {
1896 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
1897}
1898
1899#[unsafe(no_mangle)]
1900pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
1901 builder_handle: *const RKRConFrameBuilder,
1902 opts: *const RKRDlpackExportOptions,
1903 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1904) -> RKRStatus {
1905 if builder_handle.is_null() || out_tensor.is_null() {
1906 return RKRStatus::RKR_STATUS_NULL_POINTER;
1907 }
1908 let o = match resolve_dlpack_opts(opts) {
1909 Ok(o) => o,
1910 Err(st) => return st,
1911 };
1912 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1913 if !builder.has_forces_section() {
1914 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
1915 }
1916 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
1917}
1918
1919#[unsafe(no_mangle)]
1928pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
1929 builder_handle: *const RKRConFrameBuilder,
1930 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1931) -> RKRStatus {
1932 unsafe {
1933 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
1934 }
1935}
1936
1937#[unsafe(no_mangle)]
1938pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
1939 builder_handle: *const RKRConFrameBuilder,
1940 opts: *const RKRDlpackExportOptions,
1941 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1942) -> RKRStatus {
1943 if builder_handle.is_null() || out_tensor.is_null() {
1944 return RKRStatus::RKR_STATUS_NULL_POINTER;
1945 }
1946 let o = match resolve_dlpack_opts(opts) {
1947 Ok(o) => o,
1948 Err(st) => return st,
1949 };
1950 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1951 if !builder.has_energies_section() {
1952 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
1953 }
1954 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
1955}
1956
1957#[unsafe(no_mangle)]
1963pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
1964 builder_handle: *const RKRConFrameBuilder,
1965 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1966) -> RKRStatus {
1967 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
1968}
1969
1970#[unsafe(no_mangle)]
1971pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
1972 builder_handle: *const RKRConFrameBuilder,
1973 opts: *const RKRDlpackExportOptions,
1974 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1975) -> RKRStatus {
1976 if builder_handle.is_null() || out_tensor.is_null() {
1977 return RKRStatus::RKR_STATUS_NULL_POINTER;
1978 }
1979 let o = match resolve_dlpack_opts(opts) {
1980 Ok(o) => o,
1981 Err(st) => return st,
1982 };
1983 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1984 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
1985}
1986#[unsafe(no_mangle)]
1992pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
1993 builder_handle: *const RKRConFrameBuilder,
1994 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1995) -> RKRStatus {
1996 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
1997}
1998
1999#[unsafe(no_mangle)]
2000pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2001 builder_handle: *const RKRConFrameBuilder,
2002 opts: *const RKRDlpackExportOptions,
2003 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2004) -> RKRStatus {
2005 if builder_handle.is_null() || out_tensor.is_null() {
2006 return RKRStatus::RKR_STATUS_NULL_POINTER;
2007 }
2008 let o = match resolve_dlpack_opts(opts) {
2009 Ok(o) => o,
2010 Err(st) => return st,
2011 };
2012 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2013 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2014}
2015#[unsafe(no_mangle)]
2039pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2040 builder_handle: *mut RKRConFrameBuilder,
2041) -> *mut f64 {
2042 if builder_handle.is_null() {
2043 return std::ptr::null_mut();
2044 }
2045 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2046 builder
2047 .positions_view_mut()
2048 .as_slice_memory_order_mut()
2049 .map(|s| s.as_mut_ptr())
2050 .unwrap_or(std::ptr::null_mut())
2051}
2052#[unsafe(no_mangle)]
2059pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
2060 builder_handle: *mut RKRConFrameBuilder,
2061) -> *mut f64 {
2062 if builder_handle.is_null() {
2063 return std::ptr::null_mut();
2064 }
2065 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2066 if !builder.has_velocities_section() {
2067 return std::ptr::null_mut();
2068 }
2069 let slice = builder.velocities_mut();
2070 if slice.is_empty() {
2071 std::ptr::null_mut()
2072 } else {
2073 slice.as_mut_ptr()
2074 }
2075}
2076#[unsafe(no_mangle)]
2082pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2083 builder_handle: *mut RKRConFrameBuilder,
2084) -> *mut f64 {
2085 if builder_handle.is_null() {
2086 return std::ptr::null_mut();
2087 }
2088 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2089 if !builder.has_forces_section() {
2090 return std::ptr::null_mut();
2091 }
2092 let slice = builder.forces_mut();
2093 if slice.is_empty() {
2094 std::ptr::null_mut()
2095 } else {
2096 slice.as_mut_ptr()
2097 }
2098}
2099#[unsafe(no_mangle)]
2105pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2106 builder_handle: *mut RKRConFrameBuilder,
2107) -> *mut f64 {
2108 if builder_handle.is_null() {
2109 return std::ptr::null_mut();
2110 }
2111 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2112 if !builder.has_energies_section() {
2113 return std::ptr::null_mut();
2114 }
2115 let slice = builder.atom_energies_mut();
2116 if slice.is_empty() {
2117 std::ptr::null_mut()
2118 } else {
2119 slice.as_mut_ptr()
2120 }
2121}
2122#[unsafe(no_mangle)]
2127pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2128 builder_handle: *mut RKRConFrameBuilder,
2129) -> *mut f64 {
2130 if builder_handle.is_null() {
2131 return std::ptr::null_mut();
2132 }
2133 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2134 let slice = builder.masses_mut();
2135 if slice.is_empty() {
2136 std::ptr::null_mut()
2137 } else {
2138 slice.as_mut_ptr()
2139 }
2140}
2141#[unsafe(no_mangle)]
2146pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2147 builder_handle: *const RKRConFrameBuilder,
2148) -> *const u64 {
2149 if builder_handle.is_null() {
2150 return std::ptr::null();
2151 }
2152 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2153 let slice = builder.atom_ids();
2154 if slice.is_empty() {
2155 std::ptr::null()
2156 } else {
2157 slice.as_ptr()
2158 }
2159}
2160#[unsafe(no_mangle)]
2172#[allow(clippy::too_many_arguments)]
2173pub unsafe extern "C" fn rkr_frame_add_atom_full(
2174 builder_handle: *mut RKRConFrameBuilder,
2175 symbol: *const c_char,
2176 x: f64,
2177 y: f64,
2178 z: f64,
2179 fixed_x: bool,
2180 fixed_y: bool,
2181 fixed_z: bool,
2182 atom_id: u64,
2183 mass: f64,
2184 velocity: *const f64,
2185 force: *const f64,
2186) -> RKRStatus {
2187 let velocity = if velocity.is_null() {
2188 None
2189 } else {
2190 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
2191 };
2192 let force = if force.is_null() {
2193 None
2194 } else {
2195 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
2196 };
2197 unsafe {
2198 add_builder_atom(
2199 builder_handle,
2200 symbol,
2201 x,
2202 y,
2203 z,
2204 [fixed_x, fixed_y, fixed_z],
2205 atom_id,
2206 mass,
2207 velocity,
2208 force,
2209 )
2210 }
2211}
2212#[unsafe(no_mangle)]
2228pub unsafe extern "C" fn rkr_frame_new(
2229 cell: *const f64,
2230 angles: *const f64,
2231 prebox0: *const c_char,
2232 prebox1: *const c_char,
2233 postbox0: *const c_char,
2234 postbox1: *const c_char,
2235) -> *mut RKRConFrameBuilder {
2236 if cell.is_null() || angles.is_null() {
2237 return ptr::null_mut();
2238 }
2239 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
2240 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
2241 let get_str = |p: *const c_char| -> String {
2242 if p.is_null() {
2243 String::new()
2244 } else {
2245 unsafe { CStr::from_ptr(p) }
2246 .to_str()
2247 .unwrap_or("")
2248 .to_string()
2249 }
2250 };
2251 let _ = get_str(prebox1);
2254 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
2255 builder
2256 .prebox_header(get_str(prebox0))
2257 .postbox_header([get_str(postbox0), get_str(postbox1)]);
2258 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
2259}
2260#[unsafe(no_mangle)]
2266pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
2267 builder_handle: *mut RKRConFrameBuilder,
2268 metadata_json: *const c_char,
2269) -> RKRStatus {
2270 if builder_handle.is_null() || metadata_json.is_null() {
2271 return RKRStatus::RKR_STATUS_NULL_POINTER;
2272 }
2273 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2274 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
2275 Ok(s) => s,
2276 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2277 };
2278 match builder.set_metadata_json(metadata_json) {
2279 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
2280 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
2281 }
2282}
2283#[unsafe(no_mangle)]
2289pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
2290 builder_handle: *mut RKRConFrameBuilder,
2291 key: *const c_char,
2292 value: f64,
2293) -> RKRStatus {
2294 if builder_handle.is_null() || key.is_null() {
2295 return RKRStatus::RKR_STATUS_NULL_POINTER;
2296 }
2297 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2298 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2299 Ok(s) => s,
2300 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2301 };
2302 builder.set_scalar_metadata(key, value);
2303 RKRStatus::RKR_STATUS_SUCCESS
2304}
2305#[unsafe(no_mangle)]
2311pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
2312 builder_handle: *mut RKRConFrameBuilder,
2313 key: *const c_char,
2314 value: *const c_char,
2315) -> RKRStatus {
2316 if builder_handle.is_null() || key.is_null() || value.is_null() {
2317 return RKRStatus::RKR_STATUS_NULL_POINTER;
2318 }
2319 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2320 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2321 Ok(s) => s,
2322 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2323 };
2324 let value = match unsafe { CStr::from_ptr(value).to_str() } {
2325 Ok(s) => s,
2326 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2327 };
2328 builder.set_string_metadata(key, value);
2329 RKRStatus::RKR_STATUS_SUCCESS
2330}
2331#[unsafe(no_mangle)]
2337pub unsafe extern "C" fn rkr_frame_builder_set_energy(
2338 builder_handle: *mut RKRConFrameBuilder,
2339 energy: f64,
2340) -> RKRStatus {
2341 if builder_handle.is_null() {
2342 return RKRStatus::RKR_STATUS_NULL_POINTER;
2343 }
2344 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2345 builder.set_energy(energy);
2346 RKRStatus::RKR_STATUS_SUCCESS
2347}
2348#[unsafe(no_mangle)]
2354pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
2355 builder_handle: *mut RKRConFrameBuilder,
2356 idx: u64,
2357) -> RKRStatus {
2358 if builder_handle.is_null() {
2359 return RKRStatus::RKR_STATUS_NULL_POINTER;
2360 }
2361 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2362 builder.set_frame_index(idx);
2363 RKRStatus::RKR_STATUS_SUCCESS
2364}
2365#[unsafe(no_mangle)]
2371pub unsafe extern "C" fn rkr_frame_builder_set_time(
2372 builder_handle: *mut RKRConFrameBuilder,
2373 time: f64,
2374) -> RKRStatus {
2375 if builder_handle.is_null() {
2376 return RKRStatus::RKR_STATUS_NULL_POINTER;
2377 }
2378 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2379 builder.set_time(time);
2380 RKRStatus::RKR_STATUS_SUCCESS
2381}
2382#[unsafe(no_mangle)]
2388pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
2389 builder_handle: *mut RKRConFrameBuilder,
2390 dt: f64,
2391) -> RKRStatus {
2392 if builder_handle.is_null() {
2393 return RKRStatus::RKR_STATUS_NULL_POINTER;
2394 }
2395 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2396 builder.set_timestep(dt);
2397 RKRStatus::RKR_STATUS_SUCCESS
2398}
2399#[unsafe(no_mangle)]
2405pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
2406 builder_handle: *mut RKRConFrameBuilder,
2407 bead: u64,
2408) -> RKRStatus {
2409 if builder_handle.is_null() {
2410 return RKRStatus::RKR_STATUS_NULL_POINTER;
2411 }
2412 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2413 builder.set_neb_bead(bead);
2414 RKRStatus::RKR_STATUS_SUCCESS
2415}
2416#[unsafe(no_mangle)]
2422pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
2423 builder_handle: *mut RKRConFrameBuilder,
2424 band: u64,
2425) -> RKRStatus {
2426 if builder_handle.is_null() {
2427 return RKRStatus::RKR_STATUS_NULL_POINTER;
2428 }
2429 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2430 builder.set_neb_band(band);
2431 RKRStatus::RKR_STATUS_SUCCESS
2432}
2433#[unsafe(no_mangle)]
2450pub unsafe extern "C" fn rkr_frame_add_atom(
2451 builder_handle: *mut RKRConFrameBuilder,
2452 symbol: *const c_char,
2453 x: f64,
2454 y: f64,
2455 z: f64,
2456 is_fixed: bool,
2457 atom_id: u64,
2458 mass: f64,
2459) -> RKRStatus {
2460 unsafe {
2461 add_builder_atom(
2462 builder_handle,
2463 symbol,
2464 x,
2465 y,
2466 z,
2467 [is_fixed; 3],
2468 atom_id,
2469 mass,
2470 None,
2471 None,
2472 )
2473 }
2474}
2475#[unsafe(no_mangle)]
2482pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
2483 builder_handle: *mut RKRConFrameBuilder,
2484 symbol: *const c_char,
2485 x: f64,
2486 y: f64,
2487 z: f64,
2488 fixed_x: bool,
2489 fixed_y: bool,
2490 fixed_z: bool,
2491 atom_id: u64,
2492 mass: f64,
2493) -> RKRStatus {
2494 unsafe {
2495 add_builder_atom(
2496 builder_handle,
2497 symbol,
2498 x,
2499 y,
2500 z,
2501 [fixed_x, fixed_y, fixed_z],
2502 atom_id,
2503 mass,
2504 None,
2505 None,
2506 )
2507 }
2508}
2509#[unsafe(no_mangle)]
2516pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
2517 builder_handle: *mut RKRConFrameBuilder,
2518 symbol: *const c_char,
2519 x: f64,
2520 y: f64,
2521 z: f64,
2522 is_fixed: bool,
2523 atom_id: u64,
2524 mass: f64,
2525 vx: f64,
2526 vy: f64,
2527 vz: f64,
2528) -> RKRStatus {
2529 unsafe {
2530 add_builder_atom(
2531 builder_handle,
2532 symbol,
2533 x,
2534 y,
2535 z,
2536 [is_fixed; 3],
2537 atom_id,
2538 mass,
2539 Some([vx, vy, vz]),
2540 None,
2541 )
2542 }
2543}
2544#[unsafe(no_mangle)]
2551pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
2552 builder_handle: *mut RKRConFrameBuilder,
2553 symbol: *const c_char,
2554 x: f64,
2555 y: f64,
2556 z: f64,
2557 fixed_x: bool,
2558 fixed_y: bool,
2559 fixed_z: bool,
2560 atom_id: u64,
2561 mass: f64,
2562 vx: f64,
2563 vy: f64,
2564 vz: f64,
2565) -> RKRStatus {
2566 unsafe {
2567 add_builder_atom(
2568 builder_handle,
2569 symbol,
2570 x,
2571 y,
2572 z,
2573 [fixed_x, fixed_y, fixed_z],
2574 atom_id,
2575 mass,
2576 Some([vx, vy, vz]),
2577 None,
2578 )
2579 }
2580}
2581#[unsafe(no_mangle)]
2588pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
2589 builder_handle: *mut RKRConFrameBuilder,
2590 symbol: *const c_char,
2591 x: f64,
2592 y: f64,
2593 z: f64,
2594 is_fixed: bool,
2595 atom_id: u64,
2596 mass: f64,
2597 fx: f64,
2598 fy: f64,
2599 fz: f64,
2600) -> RKRStatus {
2601 unsafe {
2602 add_builder_atom(
2603 builder_handle,
2604 symbol,
2605 x,
2606 y,
2607 z,
2608 [is_fixed; 3],
2609 atom_id,
2610 mass,
2611 None,
2612 Some([fx, fy, fz]),
2613 )
2614 }
2615}
2616#[unsafe(no_mangle)]
2623pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
2624 builder_handle: *mut RKRConFrameBuilder,
2625 symbol: *const c_char,
2626 x: f64,
2627 y: f64,
2628 z: f64,
2629 fixed_x: bool,
2630 fixed_y: bool,
2631 fixed_z: bool,
2632 atom_id: u64,
2633 mass: f64,
2634 fx: f64,
2635 fy: f64,
2636 fz: f64,
2637) -> RKRStatus {
2638 unsafe {
2639 add_builder_atom(
2640 builder_handle,
2641 symbol,
2642 x,
2643 y,
2644 z,
2645 [fixed_x, fixed_y, fixed_z],
2646 atom_id,
2647 mass,
2648 None,
2649 Some([fx, fy, fz]),
2650 )
2651 }
2652}
2653#[unsafe(no_mangle)]
2660pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
2661 builder_handle: *mut RKRConFrameBuilder,
2662 symbol: *const c_char,
2663 x: f64,
2664 y: f64,
2665 z: f64,
2666 is_fixed: bool,
2667 atom_id: u64,
2668 mass: f64,
2669 vx: f64,
2670 vy: f64,
2671 vz: f64,
2672 fx: f64,
2673 fy: f64,
2674 fz: f64,
2675) -> RKRStatus {
2676 unsafe {
2677 add_builder_atom(
2678 builder_handle,
2679 symbol,
2680 x,
2681 y,
2682 z,
2683 [is_fixed; 3],
2684 atom_id,
2685 mass,
2686 Some([vx, vy, vz]),
2687 Some([fx, fy, fz]),
2688 )
2689 }
2690}
2691#[unsafe(no_mangle)]
2698pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
2699 builder_handle: *mut RKRConFrameBuilder,
2700 symbol: *const c_char,
2701 x: f64,
2702 y: f64,
2703 z: f64,
2704 fixed_x: bool,
2705 fixed_y: bool,
2706 fixed_z: bool,
2707 atom_id: u64,
2708 mass: f64,
2709 vx: f64,
2710 vy: f64,
2711 vz: f64,
2712 fx: f64,
2713 fy: f64,
2714 fz: f64,
2715) -> RKRStatus {
2716 unsafe {
2717 add_builder_atom(
2718 builder_handle,
2719 symbol,
2720 x,
2721 y,
2722 z,
2723 [fixed_x, fixed_y, fixed_z],
2724 atom_id,
2725 mass,
2726 Some([vx, vy, vz]),
2727 Some([fx, fy, fz]),
2728 )
2729 }
2730}
2731#[unsafe(no_mangle)]
2739pub unsafe extern "C" fn rkr_frame_builder_build(
2740 builder_handle: *mut RKRConFrameBuilder,
2741) -> *mut RKRConFrame {
2742 if builder_handle.is_null() {
2743 return ptr::null_mut();
2744 }
2745 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2746 let frame = builder.build();
2747 Box::into_raw(Box::new(frame)) as *mut RKRConFrame
2748}
2749#[unsafe(no_mangle)]
2754pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
2755 if !builder_handle.is_null() {
2756 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2757 }
2758}
2759#[unsafe(no_mangle)]
2776pub unsafe extern "C" fn rkr_frame_builder_clone(
2777 builder_handle: *const RKRConFrameBuilder,
2778) -> *mut RKRConFrameBuilder {
2779 if builder_handle.is_null() {
2780 return std::ptr::null_mut();
2781 }
2782 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2783 let cloned = builder.clone();
2784 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
2785}
2786#[unsafe(no_mangle)]
2792pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
2793 let filename = match unsafe { cstr_path(filename_c) } {
2794 Some(s) => s,
2795 None => return ptr::null_mut(),
2796 };
2797 match crate::compression::gzip_writer(Path::new(filename)) {
2798 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
2799 Err(_) => ptr::null_mut(),
2800 }
2801}
2802#[unsafe(no_mangle)]
2809pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
2810 filename_c: *const c_char,
2811 precision: u8,
2812) -> *mut RKRConFrameWriter {
2813 let filename = match unsafe { cstr_path(filename_c) } {
2814 Some(s) => s,
2815 None => return ptr::null_mut(),
2816 };
2817 match crate::compression::gzip_writer(Path::new(filename)) {
2818 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
2819 Err(_) => ptr::null_mut(),
2820 }
2821}
2822#[cfg(feature = "zstd")]
2832#[unsafe(no_mangle)]
2833pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
2834 let filename = match unsafe { cstr_path(filename_c) } {
2835 Some(s) => s,
2836 None => return ptr::null_mut(),
2837 };
2838 match crate::compression::zstd_writer(Path::new(filename)) {
2839 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
2840 Err(_) => ptr::null_mut(),
2841 }
2842}
2843#[cfg(feature = "zstd")]
2854#[unsafe(no_mangle)]
2855pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
2856 filename_c: *const c_char,
2857 precision: u8,
2858) -> *mut RKRConFrameWriter {
2859 let filename = match unsafe { cstr_path(filename_c) } {
2860 Some(s) => s,
2861 None => return ptr::null_mut(),
2862 };
2863 match crate::compression::zstd_writer(Path::new(filename)) {
2864 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
2865 Err(_) => ptr::null_mut(),
2866 }
2867}
2868#[unsafe(no_mangle)]
2880pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
2881 if filename_c.is_null() {
2882 return ptr::null_mut();
2883 }
2884 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
2885 Ok(s) => s,
2886 Err(_) => return ptr::null_mut(),
2887 };
2888 match iterators::read_first_frame(Path::new(filename)) {
2889 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
2890 Err(_) => ptr::null_mut(),
2891 }
2892}
2893#[unsafe(no_mangle)]
2902pub unsafe extern "C" fn rkr_read_all_frames(
2903 filename_c: *const c_char,
2904 num_frames: *mut usize,
2905) -> *mut *mut RKRConFrame {
2906 if filename_c.is_null() || num_frames.is_null() {
2907 return ptr::null_mut();
2908 }
2909 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
2910 Ok(s) => s,
2911 Err(_) => return ptr::null_mut(),
2912 };
2913 match iterators::read_all_frames(Path::new(filename)) {
2914 Ok(frames) => {
2915 let count = frames.len();
2916 let mut handles: Vec<*mut RKRConFrame> = frames
2920 .into_iter()
2921 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
2922 .collect();
2923 handles.shrink_to_fit();
2924 debug_assert_eq!(handles.len(), handles.capacity());
2925 let ptr = handles.as_mut_ptr();
2926 std::mem::forget(handles);
2927 unsafe { *num_frames = count };
2928 ptr
2929 }
2930 Err(_) => ptr::null_mut(),
2931 }
2932}
2933#[unsafe(no_mangle)]
2939pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
2940 if frames.is_null() {
2941 return;
2942 }
2943 unsafe {
2944 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
2945 for handle in handles {
2946 if !handle.is_null() {
2947 let _ = Box::from_raw(handle as *mut ConFrame);
2948 }
2949 }
2950 }
2951}
2952#[unsafe(no_mangle)]
2958pub unsafe extern "C" fn free_rkr_frame_ptr_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
2959 if frames.is_null() {
2960 return;
2961 }
2962 unsafe {
2963 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
2964 }
2965}
2966
2967#[cfg(feature = "metatensor")]
2982#[unsafe(no_mangle)]
2983pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
2984 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
2985}
2986#[cfg(feature = "metatensor")]
2988#[unsafe(no_mangle)]
2989pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
2990 frame_handle: *const RKRConFrame,
2991 out_block: *mut *mut metatensor::c_api::mts_block_t,
2992) -> RKRStatus {
2993 if frame_handle.is_null() || out_block.is_null() {
2994 return RKRStatus::RKR_STATUS_NULL_POINTER;
2995 }
2996 unsafe { *out_block = std::ptr::null_mut() };
2997 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
2998 return RKRStatus::RKR_STATUS_NULL_POINTER;
2999 };
3000 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3001 crate::metatensor_export::frame_positions_block(frame)
3002 })) {
3003 Ok(Ok(b)) => {
3004 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3005 RKRStatus::RKR_STATUS_SUCCESS
3006 }
3007 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3008 }
3009}
3010#[cfg(feature = "metatensor")]
3011#[unsafe(no_mangle)]
3012pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3013 frame_handle: *const RKRConFrame,
3014 out_block: *mut *mut metatensor::c_api::mts_block_t,
3015) -> RKRStatus {
3016 if frame_handle.is_null() || out_block.is_null() {
3017 return RKRStatus::RKR_STATUS_NULL_POINTER;
3018 }
3019 unsafe { *out_block = std::ptr::null_mut() };
3020 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3021 return RKRStatus::RKR_STATUS_NULL_POINTER;
3022 };
3023 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3024 crate::metatensor_export::frame_velocities_block(frame)
3025 })) {
3026 Ok(Ok(Some(b))) => {
3027 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3028 RKRStatus::RKR_STATUS_SUCCESS
3029 }
3030 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3031 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3032 }
3033}
3034#[cfg(feature = "metatensor")]
3035#[unsafe(no_mangle)]
3036pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3037 frame_handle: *const RKRConFrame,
3038 out_block: *mut *mut metatensor::c_api::mts_block_t,
3039) -> RKRStatus {
3040 if frame_handle.is_null() || out_block.is_null() {
3041 return RKRStatus::RKR_STATUS_NULL_POINTER;
3042 }
3043 unsafe { *out_block = std::ptr::null_mut() };
3044 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3045 return RKRStatus::RKR_STATUS_NULL_POINTER;
3046 };
3047 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3048 crate::metatensor_export::frame_forces_block(frame)
3049 })) {
3050 Ok(Ok(Some(b))) => {
3051 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3052 RKRStatus::RKR_STATUS_SUCCESS
3053 }
3054 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3055 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3056 }
3057}
3058#[cfg(feature = "metatensor")]
3059#[unsafe(no_mangle)]
3060pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3061 frame_handle: *const RKRConFrame,
3062 out_block: *mut *mut metatensor::c_api::mts_block_t,
3063) -> RKRStatus {
3064 if frame_handle.is_null() || out_block.is_null() {
3065 return RKRStatus::RKR_STATUS_NULL_POINTER;
3066 }
3067 unsafe { *out_block = std::ptr::null_mut() };
3068 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3069 return RKRStatus::RKR_STATUS_NULL_POINTER;
3070 };
3071 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3072 crate::metatensor_export::frame_energies_block(frame)
3073 })) {
3074 Ok(Ok(Some(b))) => {
3075 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3076 RKRStatus::RKR_STATUS_SUCCESS
3077 }
3078 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3079 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3080 }
3081}
3082#[cfg(not(feature = "zstd"))]
3083#[unsafe(no_mangle)]
3084pub unsafe extern "C" fn create_writer_zstd_c(_filename_c: *const c_char) -> *mut RKRConFrameWriter {
3085 ptr::null_mut()
3086}
3087#[cfg(not(feature = "zstd"))]
3088#[unsafe(no_mangle)]
3089pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3090 _filename_c: *const c_char,
3091 _precision: u8,
3092) -> *mut RKRConFrameWriter {
3093 ptr::null_mut()
3094}
3095#[cfg(not(feature = "metatensor"))]
3099#[repr(C)]
3100pub struct mts_block_t {
3101 _private: [u8; 0],
3102}
3103#[cfg(not(feature = "metatensor"))]
3104#[unsafe(no_mangle)]
3105pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3106#[cfg(not(feature = "metatensor"))]
3107#[unsafe(no_mangle)]
3108pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3109 _frame_handle: *const RKRConFrame,
3110 out_block: *mut *mut mts_block_t,
3111) -> RKRStatus {
3112 if !out_block.is_null() {
3113 unsafe { *out_block = std::ptr::null_mut() };
3114 }
3115 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3116}
3117#[cfg(not(feature = "metatensor"))]
3118#[unsafe(no_mangle)]
3119pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3120 _frame_handle: *const RKRConFrame,
3121 out_block: *mut *mut mts_block_t,
3122) -> RKRStatus {
3123 if !out_block.is_null() {
3124 unsafe { *out_block = std::ptr::null_mut() };
3125 }
3126 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3127}
3128#[cfg(not(feature = "metatensor"))]
3129#[unsafe(no_mangle)]
3130pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3131 _frame_handle: *const RKRConFrame,
3132 out_block: *mut *mut mts_block_t,
3133) -> RKRStatus {
3134 if !out_block.is_null() {
3135 unsafe { *out_block = std::ptr::null_mut() };
3136 }
3137 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3138}
3139#[cfg(not(feature = "metatensor"))]
3140#[unsafe(no_mangle)]
3141pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3142 _frame_handle: *const RKRConFrame,
3143 out_block: *mut *mut mts_block_t,
3144) -> RKRStatus {
3145 if !out_block.is_null() {
3146 unsafe { *out_block = std::ptr::null_mut() };
3147 }
3148 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3149}
3150#[unsafe(no_mangle)]
3153pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3154 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3155 return 0;
3156 };
3157 frame.atom_data.len()
3158}
3159#[unsafe(no_mangle)]
3161pub unsafe extern "C" fn rkr_frame_copy_positions(
3162 frame_handle: *const RKRConFrame,
3163 out: *mut f64,
3164 out_len: usize,
3165) -> RKRStatus {
3166 if frame_handle.is_null() || out.is_null() {
3167 return RKRStatus::RKR_STATUS_NULL_POINTER;
3168 }
3169 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3170 return RKRStatus::RKR_STATUS_NULL_POINTER;
3171 };
3172 let n = frame.atom_data.len();
3173 let need = n.saturating_mul(3);
3174 if out_len < need {
3175 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3176 }
3177 let slice = unsafe { std::slice::from_raw_parts_mut(out, need) };
3178 for (i, a) in frame.atom_data.iter().enumerate() {
3179 slice[i * 3] = a.x;
3180 slice[i * 3 + 1] = a.y;
3181 slice[i * 3 + 2] = a.z;
3182 }
3183 RKRStatus::RKR_STATUS_SUCCESS
3184}
3185#[unsafe(no_mangle)]
3186pub unsafe extern "C" fn rkr_frame_copy_velocities(
3187 frame_handle: *const RKRConFrame,
3188 out: *mut f64,
3189 out_len: usize,
3190) -> RKRStatus {
3191 if frame_handle.is_null() || out.is_null() {
3192 return RKRStatus::RKR_STATUS_NULL_POINTER;
3193 }
3194 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3195 return RKRStatus::RKR_STATUS_NULL_POINTER;
3196 };
3197 if !frame.has_velocities() {
3198 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3199 }
3200 let n = frame.atom_data.len();
3201 let need = n.saturating_mul(3);
3202 if out_len < need {
3203 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3204 }
3205 let slice = unsafe { std::slice::from_raw_parts_mut(out, need) };
3206 for (i, a) in frame.atom_data.iter().enumerate() {
3207 let [vx, vy, vz] = a.velocity.unwrap_or([0.0; 3]);
3208 slice[i * 3] = vx;
3209 slice[i * 3 + 1] = vy;
3210 slice[i * 3 + 2] = vz;
3211 }
3212 RKRStatus::RKR_STATUS_SUCCESS
3213}
3214#[unsafe(no_mangle)]
3215pub unsafe extern "C" fn rkr_frame_copy_forces(
3216 frame_handle: *const RKRConFrame,
3217 out: *mut f64,
3218 out_len: usize,
3219) -> RKRStatus {
3220 if frame_handle.is_null() || out.is_null() {
3221 return RKRStatus::RKR_STATUS_NULL_POINTER;
3222 }
3223 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3224 return RKRStatus::RKR_STATUS_NULL_POINTER;
3225 };
3226 if !frame.has_forces() {
3227 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3228 }
3229 let n = frame.atom_data.len();
3230 let need = n.saturating_mul(3);
3231 if out_len < need {
3232 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3233 }
3234 let slice = unsafe { std::slice::from_raw_parts_mut(out, need) };
3235 for (i, a) in frame.atom_data.iter().enumerate() {
3236 let [fx, fy, fz] = a.force.unwrap_or([0.0; 3]);
3237 slice[i * 3] = fx;
3238 slice[i * 3 + 1] = fy;
3239 slice[i * 3 + 2] = fz;
3240 }
3241 RKRStatus::RKR_STATUS_SUCCESS
3242}
3243#[unsafe(no_mangle)]
3244pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
3245 frame_handle: *const RKRConFrame,
3246 out: *mut f64,
3247 out_len: usize,
3248) -> RKRStatus {
3249 if frame_handle.is_null() || out.is_null() {
3250 return RKRStatus::RKR_STATUS_NULL_POINTER;
3251 }
3252 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3253 return RKRStatus::RKR_STATUS_NULL_POINTER;
3254 };
3255 if !frame.has_energies() {
3256 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3257 }
3258 let n = frame.atom_data.len();
3259 if out_len < n {
3260 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3261 }
3262 let slice = unsafe { std::slice::from_raw_parts_mut(out, n) };
3263 for (i, a) in frame.atom_data.iter().enumerate() {
3264 slice[i] = a.energy.unwrap_or(0.0);
3265 }
3266 RKRStatus::RKR_STATUS_SUCCESS
3267}
3268#[unsafe(no_mangle)]
3269pub unsafe extern "C" fn rkr_frame_copy_masses(
3270 frame_handle: *const RKRConFrame,
3271 out: *mut f64,
3272 out_len: usize,
3273) -> RKRStatus {
3274 if frame_handle.is_null() || out.is_null() {
3275 return RKRStatus::RKR_STATUS_NULL_POINTER;
3276 }
3277 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3278 return RKRStatus::RKR_STATUS_NULL_POINTER;
3279 };
3280 let n = frame.atom_data.len();
3281 if out_len < n {
3282 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3283 }
3284 let slice = unsafe { std::slice::from_raw_parts_mut(out, n) };
3285 let mut expanded: Vec<f64> = Vec::with_capacity(n);
3286 for (ti, &count) in frame.header.natms_per_type.iter().enumerate() {
3287 let m = frame.header.masses_per_type.get(ti).copied().unwrap_or(0.0);
3288 expanded.extend(std::iter::repeat_n(m, count));
3289 }
3290 if expanded.len() != n {
3291 expanded.resize(n, 0.0);
3292 }
3293 slice.copy_from_slice(&expanded[..n]);
3294 RKRStatus::RKR_STATUS_SUCCESS
3295}
3296#[unsafe(no_mangle)]
3297pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
3298 frame_handle: *const RKRConFrame,
3299 out: *mut u64,
3300 out_len: usize,
3301) -> RKRStatus {
3302 if frame_handle.is_null() || out.is_null() {
3303 return RKRStatus::RKR_STATUS_NULL_POINTER;
3304 }
3305 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3306 return RKRStatus::RKR_STATUS_NULL_POINTER;
3307 };
3308 let n = frame.atom_data.len();
3309 if out_len < n {
3310 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3311 }
3312 let slice = unsafe { std::slice::from_raw_parts_mut(out, n) };
3313 for (i, a) in frame.atom_data.iter().enumerate() {
3314 slice[i] = a.atom_id;
3315 }
3316 RKRStatus::RKR_STATUS_SUCCESS
3317}
3318fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
3319 let n = frame.atom_data.len();
3320 let mut data = Vec::with_capacity(n * 3);
3321 for a in &frame.atom_data {
3322 data.extend_from_slice(&[a.x, a.y, a.z]);
3323 }
3324 ndarray::ArcArray2::from_shape_vec((n, 3), data)
3325 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
3326}
3327
3328#[unsafe(no_mangle)]
3338pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
3339 frame_handle: *const RKRConFrame,
3340 device_type: i32,
3341 device_id: i32,
3342 _stream: i64,
3343 _max_version_major: u32,
3344 _max_version_minor: u32,
3345 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3346) -> RKRStatus {
3347 if frame_handle.is_null() || out_tensor.is_null() {
3348 return RKRStatus::RKR_STATUS_NULL_POINTER;
3349 }
3350 unsafe { *out_tensor = std::ptr::null_mut() };
3351 if device_type != rkr_dl_device_type::RKR_DL_CPU {
3352 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3353 }
3354 let _ = device_id;
3355 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3356 return RKRStatus::RKR_STATUS_NULL_POINTER;
3357 };
3358 match frame.positions_as_dlpack(dlpk::sys::DLDevice::cpu()) {
3360 Ok(tensor) => {
3361 let raw = tensor.into_raw();
3362 unsafe {
3363 *out_tensor = raw.as_ptr();
3364 }
3365 RKRStatus::RKR_STATUS_SUCCESS
3366 }
3367 Err(e) => map_dlpack_err(e),
3368 }
3369}
3370
3371#[unsafe(no_mangle)]
3377pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
3378 frame_handle: *mut RKRConFrame,
3379 tensor: *const RKRDLManagedTensorVersioned,
3380) -> RKRStatus {
3381 if frame_handle.is_null() || tensor.is_null() {
3382 return RKRStatus::RKR_STATUS_NULL_POINTER;
3383 }
3384 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
3385 let dl = unsafe { &(*tensor).dl_tensor };
3386 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
3387 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3388 }
3389 let n = frame.atom_data.len();
3390 let need = n.saturating_mul(3);
3391 let ndim = dl.ndim as usize;
3392 let shape = if dl.shape.is_null() {
3393 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3394 } else {
3395 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
3396 };
3397 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
3398 need
3399 } else if ndim == 1 && shape[0] == need as i64 {
3400 need
3401 } else {
3402 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3403 };
3404 let code = dl.dtype.code as u8;
3405 let bits = dl.dtype.bits;
3406 if dl.data.is_null() {
3407 return RKRStatus::RKR_STATUS_NULL_POINTER;
3408 }
3409 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
3410 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
3411 s.to_vec()
3412 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
3413 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
3414 s.iter().map(|&x| x as f64).collect()
3415 } else {
3416 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3417 };
3418 if frame.positions.nrows() != n {
3419 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3420 }
3421 for i in 0..n {
3422 frame.positions.set_f64_row(
3423 i,
3424 [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]],
3425 );
3426 }
3427 frame.sync_atom_data_from_arrays();
3428 RKRStatus::RKR_STATUS_SUCCESS
3429}
3430
3431#[unsafe(no_mangle)]
3434pub unsafe extern "C" fn rkr_frame_positions_dlpack(
3435 frame_handle: *const RKRConFrame,
3436 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3437) -> RKRStatus {
3438 unsafe {
3439 rkr_frame_positions_as_dlpack(frame_handle, rkr_dl_device_type::RKR_DL_CPU, 0, 0, 1, 0, out_tensor)
3440 }
3441}
3442
3443#[unsafe(no_mangle)]
3448pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
3449 frame_handle: *const RKRConFrame,
3450 opts: *const RKRDlpackExportOptions,
3451 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3452) -> RKRStatus {
3453 if frame_handle.is_null() || out_tensor.is_null() {
3454 return RKRStatus::RKR_STATUS_NULL_POINTER;
3455 }
3456 unsafe { *out_tensor = std::ptr::null_mut() };
3457 let o = match resolve_dlpack_opts(opts) {
3458 Ok(o) => o,
3459 Err(st) => return st,
3460 };
3461 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3462 return RKRStatus::RKR_STATUS_NULL_POINTER;
3463 };
3464 let arr = frame_positions_arc(frame);
3465 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3466}
3467
3468#[unsafe(no_mangle)]
3470pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
3471 frame_handle: *const RKRConFrame,
3472 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3473) -> RKRStatus {
3474 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3475}
3476
3477#[unsafe(no_mangle)]
3478pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
3479 frame_handle: *const RKRConFrame,
3480 opts: *const RKRDlpackExportOptions,
3481 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3482) -> RKRStatus {
3483 if frame_handle.is_null() || out_tensor.is_null() {
3484 return RKRStatus::RKR_STATUS_NULL_POINTER;
3485 }
3486 unsafe { *out_tensor = std::ptr::null_mut() };
3487 let o = match resolve_dlpack_opts(opts) {
3488 Ok(o) => o,
3489 Err(st) => return st,
3490 };
3491 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3492 return RKRStatus::RKR_STATUS_NULL_POINTER;
3493 };
3494 if !frame.has_velocities() {
3495 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3496 }
3497 let n = frame.atom_data.len();
3498 let mut data = Vec::with_capacity(n * 3);
3499 for a in &frame.atom_data {
3500 let v = a.velocity.unwrap_or([0.0; 3]);
3501 data.extend_from_slice(&v);
3502 }
3503 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
3504 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
3505 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3506}
3507
3508#[unsafe(no_mangle)]
3510pub unsafe extern "C" fn rkr_frame_forces_dlpack(
3511 frame_handle: *const RKRConFrame,
3512 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3513) -> RKRStatus {
3514 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3515}
3516
3517#[unsafe(no_mangle)]
3518pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
3519 frame_handle: *const RKRConFrame,
3520 opts: *const RKRDlpackExportOptions,
3521 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3522) -> RKRStatus {
3523 if frame_handle.is_null() || out_tensor.is_null() {
3524 return RKRStatus::RKR_STATUS_NULL_POINTER;
3525 }
3526 unsafe { *out_tensor = std::ptr::null_mut() };
3527 let o = match resolve_dlpack_opts(opts) {
3528 Ok(o) => o,
3529 Err(st) => return st,
3530 };
3531 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3532 return RKRStatus::RKR_STATUS_NULL_POINTER;
3533 };
3534 if !frame.has_forces() {
3535 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3536 }
3537 let n = frame.atom_data.len();
3538 let mut data = Vec::with_capacity(n * 3);
3539 for a in &frame.atom_data {
3540 let f = a.force.unwrap_or([0.0; 3]);
3541 data.extend_from_slice(&f);
3542 }
3543 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
3544 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
3545 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3546}
3547
3548#[unsafe(no_mangle)]
3550pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
3551 frame_handle: *const RKRConFrame,
3552 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3553) -> RKRStatus {
3554 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3555}
3556
3557#[unsafe(no_mangle)]
3558pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
3559 frame_handle: *const RKRConFrame,
3560 opts: *const RKRDlpackExportOptions,
3561 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3562) -> RKRStatus {
3563 if frame_handle.is_null() || out_tensor.is_null() {
3564 return RKRStatus::RKR_STATUS_NULL_POINTER;
3565 }
3566 unsafe { *out_tensor = std::ptr::null_mut() };
3567 let o = match resolve_dlpack_opts(opts) {
3568 Ok(o) => o,
3569 Err(st) => return st,
3570 };
3571 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3572 return RKRStatus::RKR_STATUS_NULL_POINTER;
3573 };
3574 if !frame.has_energies() {
3575 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3576 }
3577 let data: Vec<f64> = frame
3578 .atom_data
3579 .iter()
3580 .map(|a| a.energy.unwrap_or(0.0))
3581 .collect();
3582 let arr = ndarray::ArcArray1::from_vec(data);
3583 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
3584}
3585
3586pub struct RKRSelectionResult;
3590#[unsafe(no_mangle)]
3601pub unsafe extern "C" fn rkr_frame_select(
3602 frame_handle: *const RKRConFrame,
3603 selection: *const c_char,
3604 out_result: *mut *mut RKRSelectionResult,
3605) -> RKRStatus {
3606 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
3607 return RKRStatus::RKR_STATUS_NULL_POINTER;
3608 }
3609 let frame = unsafe { &*(frame_handle as *const ConFrame) };
3610 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
3611 Ok(s) => s,
3612 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3613 };
3614 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
3615 Ok(result) => {
3616 let boxed = Box::new(result);
3617 unsafe {
3618 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
3619 }
3620 RKRStatus::RKR_STATUS_SUCCESS
3621 }
3622 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
3623 }
3624}
3625#[unsafe(no_mangle)]
3630pub unsafe extern "C" fn rkr_selection_result_match_count(
3631 result_handle: *const RKRSelectionResult,
3632) -> u64 {
3633 if result_handle.is_null() {
3634 return 0;
3635 }
3636 let result =
3637 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
3638 result.matches.len() as u64
3639}
3640#[unsafe(no_mangle)]
3645pub unsafe extern "C" fn rkr_selection_result_context_size(
3646 result_handle: *const RKRSelectionResult,
3647) -> u32 {
3648 if result_handle.is_null() {
3649 return 0;
3650 }
3651 let result =
3652 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
3653 result.context_size as u32
3654}
3655#[unsafe(no_mangle)]
3661pub unsafe extern "C" fn rkr_selection_result_match_at(
3662 result_handle: *const RKRSelectionResult,
3663 match_index: u64,
3664 out_atoms: *mut u64,
3665 out_size: *mut u32,
3666) -> RKRStatus {
3667 if result_handle.is_null() || out_atoms.is_null() {
3668 return RKRStatus::RKR_STATUS_NULL_POINTER;
3669 }
3670 let result =
3671 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
3672 let idx = match_index as usize;
3673 if idx >= result.matches.len() {
3674 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
3675 }
3676 let m = &result.matches[idx];
3677 unsafe {
3678 for i in 0..4 {
3679 *out_atoms.add(i) = if i < m.size {
3680 m.atoms[i] as u64
3681 } else {
3682 u64::MAX
3683 };
3684 }
3685 if !out_size.is_null() {
3686 *out_size = m.size as u32;
3687 }
3688 }
3689 RKRStatus::RKR_STATUS_SUCCESS
3690}
3691#[unsafe(no_mangle)]
3697pub unsafe extern "C" fn rkr_selection_result_primary_indices(
3698 result_handle: *const RKRSelectionResult,
3699 out_indices: *mut u64,
3700 capacity: u64,
3701 out_written: *mut u64,
3702) -> RKRStatus {
3703 if result_handle.is_null() {
3704 return RKRStatus::RKR_STATUS_NULL_POINTER;
3705 }
3706 let result =
3707 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
3708 let n = result.matches.len() as u64;
3709 if !out_written.is_null() {
3710 unsafe {
3711 *out_written = n;
3712 }
3713 }
3714 if n == 0 {
3715 return RKRStatus::RKR_STATUS_SUCCESS;
3716 }
3717 if out_indices.is_null() {
3718 return RKRStatus::RKR_STATUS_NULL_POINTER;
3719 }
3720 if capacity < n {
3721 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3722 }
3723 unsafe {
3724 for (i, m) in result.matches.iter().enumerate() {
3725 *out_indices.add(i) = m.atoms[0] as u64;
3726 }
3727 }
3728 RKRStatus::RKR_STATUS_SUCCESS
3729}
3730#[unsafe(no_mangle)]
3735pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
3736 if result_handle.is_null() {
3737 return;
3738 }
3739 unsafe {
3740 drop(Box::from_raw(
3741 result_handle as *mut crate::chemfiles_selection::SelectionResult,
3742 ));
3743 }
3744}
3745#[unsafe(no_mangle)]
3747pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
3748 #[cfg(feature = "chemfiles")]
3749 {
3750 1
3751 }
3752 #[cfg(not(feature = "chemfiles"))]
3753 {
3754 0
3755 }
3756}
3757#[unsafe(no_mangle)]
3763pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
3764 if path_c.is_null() {
3765 return std::ptr::null_mut();
3766 }
3767 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
3768 return std::ptr::null_mut();
3769 };
3770 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
3771 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3772 Err(_) => std::ptr::null_mut(),
3773 }
3774}
3775#[unsafe(no_mangle)]
3782pub unsafe extern "C" fn rkr_read_chemfiles_memory(
3783 data_c: *const c_char,
3784 format_c: *const c_char,
3785 num_frames: *mut usize,
3786) -> *mut *mut RKRConFrame {
3787 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
3788 return std::ptr::null_mut();
3789 }
3790 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
3791 return std::ptr::null_mut();
3792 };
3793 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
3794 return std::ptr::null_mut();
3795 };
3796 match crate::chemfiles_import::con_frames_from_memory(data, format) {
3797 Ok(frames) => {
3798 let n = frames.len();
3799 unsafe { *num_frames = n };
3800 let mut ptrs: Vec<*mut RKRConFrame> = frames
3801 .into_iter()
3802 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3803 .collect();
3804 let p = ptrs.as_mut_ptr();
3805 std::mem::forget(ptrs);
3806 p
3807 }
3808 Err(_) => std::ptr::null_mut(),
3809 }
3810}
3811#[unsafe(no_mangle)]
3816pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
3817 if tensor.is_null() {
3818 return;
3819 }
3820 unsafe {
3821 let t = &mut *tensor;
3822 if let Some(del) = t.deleter {
3823 del(tensor);
3824 }
3825 }
3826}
3827#[cfg(test)]
3828mod tests {
3829 use super::*;
3830 use std::ffi::{CStr, CString};
3831 #[test]
3832 fn frame_copy_positions_without_cframe() {
3833 let handle = test_frame_handle();
3834 let n = unsafe { rkr_frame_atom_count(handle) };
3835 assert_eq!(n, 1);
3836 let mut buf = vec![0.0f64; n * 3];
3837 assert_eq!(
3838 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
3839 RKRStatus::RKR_STATUS_SUCCESS
3840 );
3841 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
3842 assert_eq!(
3843 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
3844 RKRStatus::RKR_STATUS_SUCCESS
3845 );
3846 assert!(!tensor.is_null());
3847 unsafe { rkr_dlpack_delete(tensor) };
3848 assert_eq!(
3849 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
3850 RKRStatus::RKR_STATUS_SECTION_ABSENT
3851 );
3852 unsafe { free_rkr_frame(handle) };
3853 }
3854 #[test]
3855 fn read_all_frames_c_abi_tiny() {
3856 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
3857 let mut n: usize = 0;
3858 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
3859 assert!(!arr.is_null() && n >= 1);
3860 let first = unsafe { *arr };
3861 let nat = unsafe { rkr_frame_atom_count(first) };
3862 let mut buf = vec![0.0f64; nat * 3];
3863 assert_eq!(
3864 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
3865 RKRStatus::RKR_STATUS_SUCCESS
3866 );
3867 unsafe { free_rkr_frame_array(arr, n) };
3868 }
3869
3870 #[test]
3871 fn free_rkr_frame_ptr_array_keeps_frames() {
3872 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
3873 let mut n: usize = 0;
3874 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
3875 assert!(!arr.is_null() && n >= 1);
3876 let first = unsafe { *arr };
3877 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
3879 unsafe { free_rkr_frame_ptr_array(arr, n) };
3880 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
3881 unsafe { free_rkr_frame(first) };
3882 for h in rest {
3883 if !h.is_null() {
3884 unsafe { free_rkr_frame(h) };
3885 }
3886 }
3887 }
3888 fn test_frame_handle() -> *mut RKRConFrame {
3889 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
3890 builder
3891 .prebox_header("Generated by test")
3892 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
3893 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
3894 Box::into_raw(Box::new(builder.build())) as *mut RKRConFrame
3895 }
3896 #[test]
3897 fn header_line_rejects_null_buffer() {
3898 let frame = test_frame_handle();
3899 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
3900 unsafe { free_rkr_frame(frame) };
3901 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
3902 }
3903 #[test]
3904 fn header_line_rejects_empty_buffer() {
3905 let frame = test_frame_handle();
3906 let mut buffer = [0 as c_char; 1];
3907 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
3908 unsafe { free_rkr_frame(frame) };
3909 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
3910 }
3911 #[test]
3912 fn header_line_truncates_and_terminates_buffer() {
3913 let frame = test_frame_handle();
3914 let mut buffer = [0 as c_char; 10];
3915 let status =
3916 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
3917 unsafe { free_rkr_frame(frame) };
3918 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
3919 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
3920 assert_eq!(copied.to_str().unwrap(), "Generated");
3921 }
3922 fn test_builder_handle() -> *mut RKRConFrameBuilder {
3923 let cell = [10.0, 11.0, 12.0];
3924 let angles = [90.0, 91.0, 92.0];
3925 unsafe {
3926 rkr_frame_new(
3927 cell.as_ptr(),
3928 angles.as_ptr(),
3929 ptr::null(),
3930 ptr::null(),
3931 ptr::null(),
3932 ptr::null(),
3933 )
3934 }
3935 }
3936 fn c_string(s: &str) -> CString {
3937 CString::new(s).unwrap()
3938 }
3939 unsafe fn assert_single_atom(
3940 frame: *mut RKRConFrame,
3941 fixed: [bool; 3],
3942 velocity: Option<[f64; 3]>,
3943 forces: Option<[f64; 3]>,
3944 ) {
3945 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
3946 assert!(!c_frame.is_null());
3947 let c_frame_ref = unsafe { &*c_frame };
3948 assert_eq!(c_frame_ref.num_atoms, 1);
3949 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
3950 assert_eq!(c_frame_ref.has_forces, forces.is_some());
3951 let atom = unsafe { &*c_frame_ref.atoms };
3952 assert_eq!(atom.fixed_x, fixed[0]);
3953 assert_eq!(atom.fixed_y, fixed[1]);
3954 assert_eq!(atom.fixed_z, fixed[2]);
3955 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
3956 assert_eq!(atom.has_velocity, velocity.is_some());
3957 assert_eq!(atom.has_forces, forces.is_some());
3958 if let Some([vx, vy, vz]) = velocity {
3959 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
3960 }
3961 if let Some([fx, fy, fz]) = forces {
3962 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
3963 }
3964 unsafe { free_c_frame(c_frame) };
3965 unsafe { free_rkr_frame(frame) };
3966 }
3967 #[test]
3968 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
3969 let builder = test_builder_handle();
3970 let symbol = c_string("Cu");
3971 let status = unsafe {
3972 rkr_frame_add_atom_with_fixed_mask(
3973 builder,
3974 symbol.as_ptr(),
3975 1.0,
3976 2.0,
3977 3.0,
3978 true,
3979 false,
3980 true,
3981 7,
3982 63.546,
3983 )
3984 };
3985 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
3986 let frame = unsafe { rkr_frame_builder_build(builder) };
3987 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
3988 }
3989 #[test]
3990 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
3991 let builder = test_builder_handle();
3992 let symbol = c_string("H");
3993 let status = unsafe {
3994 rkr_frame_add_atom_with_velocity_fixed_mask(
3995 builder,
3996 symbol.as_ptr(),
3997 1.0,
3998 2.0,
3999 3.0,
4000 false,
4001 true,
4002 false,
4003 9,
4004 1.008,
4005 0.1,
4006 0.2,
4007 0.3,
4008 )
4009 };
4010 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4011 let frame = unsafe { rkr_frame_builder_build(builder) };
4012 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
4013 }
4014 #[test]
4015 fn builder_preserves_fixed_mask_for_atom_with_forces() {
4016 let builder = test_builder_handle();
4017 let symbol = c_string("O");
4018 let status = unsafe {
4019 rkr_frame_add_atom_with_forces_fixed_mask(
4020 builder,
4021 symbol.as_ptr(),
4022 1.0,
4023 2.0,
4024 3.0,
4025 true,
4026 true,
4027 false,
4028 11,
4029 15.999,
4030 -0.1,
4031 -0.2,
4032 -0.3,
4033 )
4034 };
4035 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4036 let frame = unsafe { rkr_frame_builder_build(builder) };
4037 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
4038 }
4039 #[test]
4040 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
4041 let builder = test_builder_handle();
4042 let symbol = c_string("N");
4043 let status = unsafe {
4044 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4045 builder,
4046 symbol.as_ptr(),
4047 1.0,
4048 2.0,
4049 3.0,
4050 false,
4051 true,
4052 true,
4053 13,
4054 14.007,
4055 0.4,
4056 0.5,
4057 0.6,
4058 -0.4,
4059 -0.5,
4060 -0.6,
4061 )
4062 };
4063 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4064 let frame = unsafe { rkr_frame_builder_build(builder) };
4065 unsafe {
4066 assert_single_atom(
4067 frame,
4068 [false, true, true],
4069 Some([0.4, 0.5, 0.6]),
4070 Some([-0.4, -0.5, -0.6]),
4071 )
4072 };
4073 }
4074 #[test]
4075 fn builder_bool_fixed_functions_set_all_axes_together() {
4076 let builder = test_builder_handle();
4077 let cu = c_string("Cu");
4078 let h = c_string("H");
4079 let atom_status =
4080 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
4081 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
4082 let velocity_status = unsafe {
4083 rkr_frame_add_atom_with_velocity(
4084 builder,
4085 h.as_ptr(),
4086 4.0,
4087 5.0,
4088 6.0,
4089 false,
4090 2,
4091 1.008,
4092 0.7,
4093 0.8,
4094 0.9,
4095 )
4096 };
4097 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
4098 let frame = unsafe { rkr_frame_builder_build(builder) };
4099 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
4100 assert!(!c_frame.is_null());
4101 let c_frame_ref = unsafe { &*c_frame };
4102 assert_eq!(c_frame_ref.num_atoms, 2);
4103 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
4104 assert_eq!(
4105 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
4106 [true, true, true]
4107 );
4108 assert_eq!(
4109 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
4110 [false, false, false]
4111 );
4112 unsafe { free_c_frame(c_frame) };
4113 unsafe { free_rkr_frame(frame) };
4114 }
4115 #[test]
4116 fn status_message_returns_static_strings_for_all_status_values() {
4117 let cases = [
4118 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
4119 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
4120 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
4121 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
4122 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
4123 (
4124 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
4125 "index out of bounds",
4126 ),
4127 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
4128 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
4129 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
4130 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
4131 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
4132 (
4133 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
4134 "feature disabled in this build",
4135 ),
4136 ];
4137 for (status, expected) in cases {
4138 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
4139 assert_eq!(message.to_str().unwrap(), expected);
4140 }
4141 }
4142 unsafe fn assert_dlpack_cpu_float(
4146 t: *mut RKRDLManagedTensorVersioned,
4147 expect_ndim: i32,
4148 expect_shape: &[i64],
4149 expect_bits: u8,
4150 ) {
4151 assert!(!t.is_null());
4152 let dl = unsafe { &(*t).dl_tensor };
4153 assert_eq!(dl.ndim, expect_ndim);
4154 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
4155 assert_eq!(shape, expect_shape);
4156 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
4157 assert_eq!(dl.dtype.bits, expect_bits);
4158 assert_eq!(dl.dtype.lanes, 1);
4159 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
4160 assert!(!dl.data.is_null());
4161 }
4162
4163 #[test]
4164 fn frame_optional_section_dlpack_present_and_absent() {
4165 let handle = test_frame_handle();
4167 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4168 assert_eq!(
4169 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
4170 RKRStatus::RKR_STATUS_SECTION_ABSENT
4171 );
4172 assert!(t.is_null());
4173 assert_eq!(
4174 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
4175 RKRStatus::RKR_STATUS_SECTION_ABSENT
4176 );
4177 assert_eq!(
4178 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
4179 RKRStatus::RKR_STATUS_SECTION_ABSENT
4180 );
4181 unsafe { free_rkr_frame(handle) };
4182
4183 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
4185 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
4186 assert!(!fr.is_null());
4187 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
4188 assert!(n > 0);
4189 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4190 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
4191 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4192 unsafe {
4193 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
4194 rkr_dlpack_delete(vel);
4195 free_rkr_frame(fr);
4196 }
4197
4198 let cell = [10.0f64; 3];
4200 let ang = [90.0f64; 3];
4201 let b = unsafe {
4202 rkr_frame_new(
4203 cell.as_ptr(),
4204 ang.as_ptr(),
4205 std::ptr::null(),
4206 std::ptr::null(),
4207 std::ptr::null(),
4208 std::ptr::null(),
4209 )
4210 };
4211 assert!(!b.is_null());
4212 let sym = CString::new("H").unwrap();
4213 unsafe {
4214 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4215 b,
4216 sym.as_ptr(),
4217 0.0,
4218 0.0,
4219 0.0,
4220 false,
4221 false,
4222 false,
4223 0,
4224 1.0,
4225 0.1,
4226 0.0,
4227 0.0,
4228 0.0,
4229 0.0,
4230 -1.0,
4231 );
4232 rkr_frame_builder_set_last_energy(b, -0.5);
4233 }
4234 let built = unsafe { rkr_frame_builder_build(b) };
4235 assert!(!built.is_null());
4236 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
4237 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4238 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4239 assert_eq!(
4240 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
4241 RKRStatus::RKR_STATUS_SUCCESS
4242 );
4243 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
4244 assert_eq!(
4245 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
4246 RKRStatus::RKR_STATUS_SUCCESS
4247 );
4248 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
4249 let opts32 = RKRDlpackExportOptions {
4251 dtype: RKRDLDataType {
4252 code: rkr_dl_type_code::RKR_DL_FLOAT,
4253 bits: 32,
4254 lanes: 1,
4255 },
4256 device: RKRDLDevice {
4257 device_type: rkr_dl_device_type::RKR_DL_CPU,
4258 device_id: 0,
4259 },
4260 };
4261 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4262 assert_eq!(
4263 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
4264 RKRStatus::RKR_STATUS_SUCCESS
4265 );
4266 unsafe {
4267 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
4268 rkr_dlpack_delete(pos32);
4269 }
4270 let bad_dev = RKRDlpackExportOptions {
4272 dtype: RKRDLDataType {
4273 code: rkr_dl_type_code::RKR_DL_FLOAT,
4274 bits: 64,
4275 lanes: 1,
4276 },
4277 device: RKRDLDevice {
4278 device_type: rkr_dl_device_type::RKR_DL_CUDA,
4279 device_id: 0,
4280 },
4281 };
4282 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4283 assert_eq!(
4284 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_dev, &mut junk) },
4285 RKRStatus::RKR_STATUS_FEATURE_DISABLED
4286 );
4287 let bad_bits = RKRDlpackExportOptions {
4288 dtype: RKRDLDataType {
4289 code: rkr_dl_type_code::RKR_DL_FLOAT,
4290 bits: 16,
4291 lanes: 1,
4292 },
4293 device: RKRDLDevice {
4294 device_type: rkr_dl_device_type::RKR_DL_CPU,
4295 device_id: 0,
4296 },
4297 };
4298 assert_eq!(
4299 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
4300 RKRStatus::RKR_STATUS_VALIDATION_ERROR
4301 );
4302 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4304 assert_eq!(
4305 unsafe {
4306 rkr_frame_positions_as_dlpack(
4307 built,
4308 rkr_dl_device_type::RKR_DL_CPU,
4309 0,
4310 0,
4311 1,
4312 0,
4313 &mut as_t,
4314 )
4315 },
4316 RKRStatus::RKR_STATUS_SUCCESS
4317 );
4318 unsafe {
4319 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
4320 }
4321 {
4323 let fr = unsafe { &mut *(built as *mut ConFrame) };
4324 for a in fr.atom_data.iter_mut() {
4325 a.x = -99.0;
4326 a.y = -99.0;
4327 a.z = -99.0;
4328 }
4329 }
4330 assert_eq!(
4331 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
4332 RKRStatus::RKR_STATUS_SUCCESS
4333 );
4334 {
4335 let fr = unsafe { &*(built as *const ConFrame) };
4336 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
4337 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
4338 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
4339 }
4340 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4342 assert_eq!(
4343 unsafe {
4344 rkr_frame_positions_as_dlpack(
4345 built,
4346 rkr_dl_device_type::RKR_DL_CPU,
4347 0,
4348 0,
4349 1,
4350 0,
4351 &mut again,
4352 )
4353 },
4354 RKRStatus::RKR_STATUS_SUCCESS
4355 );
4356 unsafe {
4357 let dl = &(*again).dl_tensor;
4358 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
4359 assert!((data[0] - 0.0).abs() < 1e-12);
4360 assert!((data[1] - 0.0).abs() < 1e-12);
4361 assert!((data[2] - 0.0).abs() < 1e-12);
4362 rkr_dlpack_delete(again);
4363 rkr_dlpack_delete(as_t);
4364 rkr_dlpack_delete(frc);
4365 rkr_dlpack_delete(eng);
4366 free_rkr_frame(built);
4367 }
4368 }
4369
4370 #[test]
4371 fn ffi_positions_dlpack_round_trip() {
4372 let handle = test_builder_handle();
4373 let sym = c_string("Cu");
4374 unsafe {
4375 rkr_frame_add_atom_full(
4376 handle,
4377 sym.as_ptr(),
4378 1.0,
4379 2.0,
4380 3.0,
4381 false,
4382 false,
4383 false,
4384 7,
4385 63.5,
4386 ptr::null(),
4387 ptr::null(),
4388 )
4389 };
4390 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
4391 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
4392 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4393 assert!(!t.is_null());
4394 let dl = unsafe { &(*t).dl_tensor };
4396 assert_eq!(dl.ndim, 2);
4397 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
4398 assert_eq!(shape, &[1, 3]);
4399 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
4400 assert_eq!(dl.dtype.bits, 64);
4401 assert_eq!(dl.dtype.lanes, 1);
4402 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
4403 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
4404 assert_eq!(data, &[1.0, 2.0, 3.0]);
4405 let deleter = unsafe { (*t).deleter };
4407 if let Some(del) = deleter {
4408 unsafe { del(t) };
4409 }
4410 unsafe { free_rkr_frame_builder(handle) };
4411 }
4412 #[test]
4413 fn ffi_velocities_dlpack_section_absent() {
4414 let handle = test_builder_handle();
4415 let sym = c_string("Cu");
4416 unsafe {
4417 rkr_frame_add_atom_full(
4418 handle,
4419 sym.as_ptr(),
4420 0.0,
4421 0.0,
4422 0.0,
4423 false,
4424 false,
4425 false,
4426 0,
4427 63.5,
4428 ptr::null(),
4429 ptr::null(),
4430 )
4431 };
4432 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
4433 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
4434 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
4435 assert!(t.is_null());
4436 unsafe { free_rkr_frame_builder(handle) };
4437 }
4438 #[test]
4439 fn ffi_dlpack_null_handle_rejects() {
4440 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
4441 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
4442 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
4443 assert!(t.is_null());
4444 }
4445 #[cfg(feature = "chemfiles")]
4446 #[test]
4447 fn rkr_frame_select_finds_oxygen() {
4448 use crate::types::ConFrameBuilder;
4449 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
4450 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
4451 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
4452 let frame = b.build();
4453 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
4454 let sel = CString::new("name O").unwrap();
4455 let mut out: *mut RKRSelectionResult = ptr::null_mut();
4456 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
4457 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4458 assert!(!out.is_null());
4459 let n = unsafe { rkr_selection_result_match_count(out) };
4460 assert_eq!(n, 1);
4461 let mut atoms = [u64::MAX; 4];
4462 let mut size = 0u32;
4463 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
4464 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
4465 assert_eq!(size, 1);
4466 assert_eq!(atoms[0], 0);
4467 unsafe {
4468 rkr_selection_result_free(out);
4469 free_rkr_frame(frame_ptr);
4470 }
4471 }
4472 #[cfg(feature = "chemfiles")]
4474 #[test]
4475 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
4476 use crate::types::{Bond, ConFrameBuilder};
4477 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
4479 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
4480 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
4481 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
4482 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
4483 let mut frame = b.build();
4484 let id_to = |id: u64| {
4485 frame
4486 .atom_data
4487 .iter()
4488 .position(|a| a.atom_id == id)
4489 .unwrap() as u32
4490 };
4491 frame.header.set_bonds(&[
4492 Bond::new(id_to(0), id_to(1)),
4493 Bond::new(id_to(1), id_to(2)),
4494 Bond::new(id_to(2), id_to(3)),
4495 ]);
4496 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
4497 let run = |sel: &str| -> (u64, u32) {
4498 let csel = CString::new(sel).unwrap();
4499 let mut out: *mut RKRSelectionResult = ptr::null_mut();
4500 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
4501 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
4502 let n = unsafe { rkr_selection_result_match_count(out) };
4503 let ctx = unsafe { rkr_selection_result_context_size(out) };
4504 unsafe { rkr_selection_result_free(out) };
4505 (n, ctx)
4506 };
4507 assert_eq!(run("bonds: all"), (3, 2));
4508 assert_eq!(run("angles: all"), (2, 3));
4509 assert_eq!(run("dihedrals: all"), (1, 4));
4510 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
4511 assert_eq!(
4512 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
4513 run("bonds: type(#1) H and name(#2) O").0
4514 );
4515 unsafe { free_rkr_frame(frame_ptr) };
4516 }
4517 #[cfg(feature = "metatensor")]
4518 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
4519 assert!(!block.is_null());
4520 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
4521 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
4522 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
4523 let shape_fn = array.shape.expect("mts_array_t.shape from metatensor C API");
4524 let mut shape_ptr: *const usize = std::ptr::null();
4525 let mut shape_count: usize = 0;
4526 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
4527 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
4528 assert_eq!(shape_count, 2);
4529 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
4530 assert_eq!(shape[0], n);
4531 assert_eq!(shape[1], props);
4532 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
4533 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
4534 assert!(!samples.is_null() && !prop_lab.is_null());
4535 }
4536 #[cfg(feature = "metatensor")]
4537 #[test]
4538 fn metatensor_positions_via_c_abi() {
4539 let handle = test_frame_handle();
4540 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4541 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
4542 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4543 assert_mts_block_shape(out, 1, 3);
4544 unsafe { rkr_mts_block_free(out) };
4545 for (name, export) in [
4546 (
4547 "velocities",
4548 rkr_frame_metatensor_velocities_block
4549 as unsafe extern "C" fn(
4550 *const RKRConFrame,
4551 *mut *mut metatensor::c_api::mts_block_t,
4552 ) -> RKRStatus,
4553 ),
4554 (
4555 "forces",
4556 rkr_frame_metatensor_forces_block
4557 as unsafe extern "C" fn(
4558 *const RKRConFrame,
4559 *mut *mut metatensor::c_api::mts_block_t,
4560 ) -> RKRStatus,
4561 ),
4562 (
4563 "atom_energies",
4564 rkr_frame_metatensor_atom_energies_block
4565 as unsafe extern "C" fn(
4566 *const RKRConFrame,
4567 *mut *mut metatensor::c_api::mts_block_t,
4568 ) -> RKRStatus,
4569 ),
4570 ] {
4571 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4572 let st_abs = unsafe { export(handle, &mut o) };
4573 assert_eq!(
4574 st_abs,
4575 RKRStatus::RKR_STATUS_SECTION_ABSENT,
4576 "{name} must be SECTION_ABSENT on minimal test frame"
4577 );
4578 assert!(o.is_null());
4579 }
4580 unsafe { free_rkr_frame(handle) };
4581 }
4582
4583 #[cfg(feature = "metatensor")]
4584 #[test]
4585 fn metatensor_optional_sections_via_c_abi() {
4586 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
4588 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
4589 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
4590 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
4591 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
4592 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
4593 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
4594 builder.set_atom_energy(0, -0.5).unwrap();
4595 builder.set_atom_energy(1, -1.0).unwrap();
4596 let frame = builder.build();
4597 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
4598 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4599 assert_eq!(
4600 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
4601 RKRStatus::RKR_STATUS_SUCCESS
4602 );
4603 assert_mts_block_shape(pos, 2, 3);
4604 unsafe { rkr_mts_block_free(pos) };
4605 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4606 assert_eq!(
4607 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
4608 RKRStatus::RKR_STATUS_SUCCESS
4609 );
4610 assert_mts_block_shape(vel, 2, 3);
4611 unsafe { rkr_mts_block_free(vel) };
4612 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4613 assert_eq!(
4614 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
4615 RKRStatus::RKR_STATUS_SUCCESS
4616 );
4617 assert_mts_block_shape(frc, 2, 3);
4618 unsafe { rkr_mts_block_free(frc) };
4619 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4620 assert_eq!(
4621 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
4622 RKRStatus::RKR_STATUS_SUCCESS
4623 );
4624 assert_mts_block_shape(eng, 2, 1);
4625 unsafe { rkr_mts_block_free(eng) };
4626 unsafe { free_rkr_frame(handle) };
4627 }
4628
4629 #[test]
4630 fn string_iterator_yields_frames_from_buffer() {
4631 let text = std::fs::read_to_string("resources/test/tiny_cuh2.con")
4632 .expect("fixture tiny_cuh2.con");
4633 let c_text = CString::new(text.as_str()).unwrap();
4634 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
4635 assert!(!it.is_null());
4636 let mut n = 0usize;
4637 loop {
4638 let fr = unsafe { con_frame_iterator_next(it) };
4639 if fr.is_null() {
4640 break;
4641 }
4642 n += 1;
4643 unsafe { free_rkr_frame(fr) };
4644 }
4645 unsafe { free_con_frame_iterator(it) };
4646 assert!(n >= 1, "string iterator should yield >=1 frame");
4647
4648 let bytes = text.as_bytes();
4649 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
4650 assert!(!it2.is_null());
4651 let fr2 = unsafe { con_frame_iterator_next(it2) };
4652 assert!(!fr2.is_null());
4653 unsafe {
4654 free_rkr_frame(fr2);
4655 free_con_frame_iterator(it2);
4656 }
4657 }
4658
4659 #[test]
4660 fn file_iterator_reads_gzip_when_present() {
4661 use flate2::write::GzEncoder;
4662 use flate2::Compression;
4663 use std::io::Write;
4664 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
4665 let dir = tempfile::tempdir().expect("tempdir");
4666 let gz_path = dir.path().join("tiny_cuh2.con.gz");
4667 {
4668 let f = std::fs::File::create(&gz_path).unwrap();
4669 let mut enc = GzEncoder::new(f, Compression::default());
4670 enc.write_all(&plain).unwrap();
4671 enc.finish().unwrap();
4672 }
4673 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
4674 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
4675 assert!(
4676 !it.is_null(),
4677 "path iterator must decompress .con.gz transparently"
4678 );
4679 let fr = unsafe { con_frame_iterator_next(it) };
4680 assert!(!fr.is_null());
4681 let n = unsafe { rkr_frame_atom_count(fr) };
4682 assert!(n > 0);
4683 unsafe {
4684 free_rkr_frame(fr);
4685 free_con_frame_iterator(it);
4686 }
4687 }
4688}