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 RKR_STATUS_DEVICE_MISMATCH = -12,
351 RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED = -13,
353}
354#[unsafe(no_mangle)]
359pub unsafe extern "C" fn rkr_frame_bond_count(frame_handle: *const RKRConFrame) -> u64 {
360 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
361 Some(f) => f.bonds().len() as u64,
362 None => 0,
363 }
364}
365#[unsafe(no_mangle)]
374pub unsafe extern "C" fn rkr_frame_bond_at(
375 frame_handle: *const RKRConFrame,
376 index: u64,
377 out_i: *mut u32,
378 out_j: *mut u32,
379 out_has_order: *mut u8,
380 out_order: *mut i32,
381) -> RKRStatus {
382 if frame_handle.is_null()
383 || out_i.is_null()
384 || out_j.is_null()
385 || out_has_order.is_null()
386 || out_order.is_null()
387 {
388 return RKRStatus::RKR_STATUS_NULL_POINTER;
389 }
390 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
391 return RKRStatus::RKR_STATUS_NULL_POINTER;
392 };
393 let bonds = frame.bonds();
394 let Some(bond) = bonds.get(index as usize) else {
395 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
396 };
397 unsafe {
398 *out_i = bond.i;
399 *out_j = bond.j;
400 if let Some(order) = bond.order {
401 *out_has_order = 1;
402 *out_order = order;
403 } else {
404 *out_has_order = 0;
405 *out_order = 0;
406 }
407 }
408 RKRStatus::RKR_STATUS_SUCCESS
409}
410#[unsafe(no_mangle)]
413pub extern "C" fn rkr_status_message(status: RKRStatus) -> *const c_char {
414 match status {
415 RKRStatus::RKR_STATUS_SUCCESS => c"success".as_ptr(),
416 RKRStatus::RKR_STATUS_NULL_POINTER => c"null pointer".as_ptr(),
417 RKRStatus::RKR_STATUS_INVALID_UTF8 => c"invalid UTF-8".as_ptr(),
418 RKRStatus::RKR_STATUS_INVALID_JSON => c"invalid JSON".as_ptr(),
419 RKRStatus::RKR_STATUS_IO_ERROR => c"I/O error".as_ptr(),
420 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS => c"index out of bounds".as_ptr(),
421 RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL => c"buffer too small".as_ptr(),
422 RKRStatus::RKR_STATUS_INTERNAL_ERROR => c"internal error".as_ptr(),
423 RKRStatus::RKR_STATUS_SECTION_ABSENT => c"section absent".as_ptr(),
424 RKRStatus::RKR_STATUS_VALIDATION_ERROR => c"validation error".as_ptr(),
425 RKRStatus::RKR_STATUS_SELECTION_ERROR => c"selection error".as_ptr(),
426 RKRStatus::RKR_STATUS_FEATURE_DISABLED => c"feature disabled in this build".as_ptr(),
427 RKRStatus::RKR_STATUS_DEVICE_MISMATCH => c"DLPack device mismatch".as_ptr(),
428 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED => {
429 c"device allocation unsupported in this build".as_ptr()
430 }
431 }
432}
433#[repr(C)]
436pub struct RKRConFrame {
437 _private: [u8; 0],
438}
439#[repr(C)]
442pub struct RKRConFrameWriter {
443 _private: [u8; 0],
444}
445#[repr(C)]
449pub struct CFrame {
450 pub atoms: *mut CAtom,
451 pub num_atoms: usize,
452 pub cell: [f64; 3],
453 pub angles: [f64; 3],
454 pub has_velocities: bool,
455 pub has_forces: bool,
456 pub has_energies: bool,
457}
458#[repr(C)]
468pub struct CAtom {
469 pub atomic_number: u64,
470 pub x: f64,
471 pub y: f64,
472 pub z: f64,
473 pub atom_id: u64,
474 pub mass: f64,
475 pub is_fixed: bool,
478 pub fixed_x: bool,
479 pub fixed_y: bool,
480 pub fixed_z: bool,
481 pub vx: f64,
482 pub vy: f64,
483 pub vz: f64,
484 pub has_velocity: bool,
485 pub fx: f64,
486 pub fy: f64,
487 pub fz: f64,
488 pub has_forces: bool,
489 pub energy: f64,
492 pub has_energy: bool,
493}
494#[repr(C)]
495pub struct CConFrameIterator {
496 iterator: *mut ConFrameIterator<'static>,
497 file_contents: *mut String,
498}
499
500fn c_iterator_from_owned_string(contents: String) -> *mut CConFrameIterator {
502 let file_contents_box = Box::new(contents);
503 let file_contents_ptr = Box::into_raw(file_contents_box);
504 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
505 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
506 let c_iterator = Box::new(CConFrameIterator {
507 iterator: Box::into_raw(iterator),
508 file_contents: file_contents_ptr,
509 });
510 Box::into_raw(c_iterator)
511}
512
513#[unsafe(no_mangle)]
529pub unsafe extern "C" fn read_con_file_iterator(
530 filename_c: *const c_char,
531) -> *mut CConFrameIterator {
532 if filename_c.is_null() {
533 return ptr::null_mut();
534 }
535 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
536 Ok(s) => s,
537 Err(_) => return ptr::null_mut(),
538 };
539 let owned = match crate::compression::read_file_contents(Path::new(filename)) {
540 Ok(fc) => match fc.as_str() {
541 Ok(s) => s.to_owned(),
542 Err(_) => return ptr::null_mut(),
543 },
544 Err(_) => return ptr::null_mut(),
545 };
546 c_iterator_from_owned_string(owned)
547}
548
549#[unsafe(no_mangle)]
557pub unsafe extern "C" fn read_con_string_iterator(
558 contents_c: *const c_char,
559) -> *mut CConFrameIterator {
560 if contents_c.is_null() {
561 return ptr::null_mut();
562 }
563 let contents = match unsafe { CStr::from_ptr(contents_c).to_str() } {
564 Ok(s) => s.to_owned(),
565 Err(_) => return ptr::null_mut(),
566 };
567 c_iterator_from_owned_string(contents)
568}
569
570#[unsafe(no_mangle)]
577pub unsafe extern "C" fn read_con_buffer_iterator(
578 data: *const u8,
579 len: usize,
580) -> *mut CConFrameIterator {
581 if data.is_null() && len > 0 {
582 return ptr::null_mut();
583 }
584 if len == 0 {
585 return c_iterator_from_owned_string(String::new());
586 }
587 let slice = unsafe { std::slice::from_raw_parts(data, len) };
588 let contents = match std::str::from_utf8(slice) {
589 Ok(s) => s.to_owned(),
590 Err(_) => return ptr::null_mut(),
591 };
592 c_iterator_from_owned_string(contents)
593}
594#[unsafe(no_mangle)]
600pub unsafe extern "C" fn con_frame_iterator_next(
601 iterator: *mut CConFrameIterator,
602) -> *mut RKRConFrame {
603 if iterator.is_null() {
604 return ptr::null_mut();
605 }
606 let iter = unsafe { &mut *(*iterator).iterator };
607 match iter.next() {
608 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
609 _ => ptr::null_mut(),
610 }
611}
612#[unsafe(no_mangle)]
617pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
618 if !frame_handle.is_null() {
619 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
620 }
621}
622#[unsafe(no_mangle)]
627pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
628 if iterator.is_null() {
629 return;
630 }
631 unsafe {
632 let c_iterator_box = Box::from_raw(iterator);
633 let _ = Box::from_raw(c_iterator_box.iterator);
634 let _ = Box::from_raw(c_iterator_box.file_contents);
635 }
636}
637#[unsafe(no_mangle)]
646pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
647 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
648 Some(f) => f,
649 None => return ptr::null_mut(),
650 };
651 let masses_iter = frame
652 .header
653 .natms_per_type
654 .iter()
655 .zip(frame.header.masses_per_type.iter())
656 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
657 let has_velocities = frame.has_velocities();
658 let mut c_atoms: Vec<CAtom> = frame
659 .atom_data
660 .iter()
661 .zip(masses_iter)
662 .map(|(atom_datum, mass)| {
663 let [vx, vy, vz] = atom_datum.velocity.unwrap_or([0.0; 3]);
664 let [fx, fy, fz] = atom_datum.force.unwrap_or([0.0; 3]);
665 CAtom {
666 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
667 x: atom_datum.x,
668 y: atom_datum.y,
669 z: atom_datum.z,
670 is_fixed: atom_datum.is_fixed(),
671 fixed_x: atom_datum.fixed[0],
672 fixed_y: atom_datum.fixed[1],
673 fixed_z: atom_datum.fixed[2],
674 atom_id: atom_datum.atom_id,
675 mass,
676 vx,
677 vy,
678 vz,
679 has_velocity: atom_datum.has_velocity(),
680 fx,
681 fy,
682 fz,
683 has_forces: atom_datum.has_forces(),
684 energy: atom_datum.energy.unwrap_or(0.0),
685 has_energy: atom_datum.has_energy(),
686 }
687 })
688 .collect();
689 let atoms_ptr = c_atoms.as_mut_ptr();
690 let num_atoms = c_atoms.len();
691 std::mem::forget(c_atoms);
692 let has_forces = frame.has_forces();
693 let has_energies = frame.has_energies();
694 let c_frame = Box::new(CFrame {
695 atoms: atoms_ptr,
696 num_atoms,
697 cell: frame.header.boxl,
698 angles: frame.header.angles,
699 has_velocities,
700 has_forces,
701 has_energies,
702 });
703 Box::into_raw(c_frame)
704}
705#[unsafe(no_mangle)]
710pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
711 if frame.is_null() {
712 return;
713 }
714 unsafe {
715 let frame_box = Box::from_raw(frame);
716 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
717 }
718}
719#[unsafe(no_mangle)]
738pub unsafe extern "C" fn rkr_frame_get_header_line(
739 frame_handle: *const RKRConFrame,
740 is_prebox: bool,
741 line_index: usize,
742 buffer: *mut c_char,
743 buffer_len: usize,
744) -> RKRStatus {
745 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
746 Some(f) => f,
747 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
748 };
749 if buffer.is_null() {
750 return RKRStatus::RKR_STATUS_NULL_POINTER;
751 }
752 if buffer_len == 0 {
753 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
754 }
755 let line_to_copy: Option<&str> = if is_prebox {
756 match line_index {
757 0 => Some(frame.header.prebox_header.user.as_str()),
758 1 => Some(frame.header.prebox_header.metadata_line()),
759 _ => None,
760 }
761 } else {
762 frame.header.postbox_header.get(line_index).map(String::as_str)
763 };
764 if let Some(line) = line_to_copy {
765 let bytes = line.as_bytes();
766 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
767 unsafe {
768 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
769 *buffer.add(len_to_copy) = 0;
770 }
771 RKRStatus::RKR_STATUS_SUCCESS
772 } else {
773 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
774 }
775}
776#[unsafe(no_mangle)]
789pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
790 frame_handle: *const RKRConFrame,
791 is_prebox: bool,
792 line_index: usize,
793) -> *mut c_char {
794 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
795 Some(f) => f,
796 None => return ptr::null_mut(),
797 };
798 let line_to_copy: Option<&str> = if is_prebox {
799 match line_index {
800 0 => Some(frame.header.prebox_header.user.as_str()),
801 1 => Some(frame.header.prebox_header.metadata_line()),
802 _ => None,
803 }
804 } else {
805 frame.header.postbox_header.get(line_index).map(String::as_str)
806 };
807 if let Some(line) = line_to_copy {
808 match CString::new(line) {
810 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
813 } else {
814 ptr::null_mut() }
816}
817#[unsafe(no_mangle)]
825pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
826 if !s.is_null() {
827 let _ = unsafe { CString::from_raw(s) };
829 }
830}
831type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
845#[inline]
848fn into_rkr_writer(
849 sink: Box<dyn std::io::Write>,
850 precision: Option<u8>,
851) -> *mut RKRConFrameWriter {
852 let writer: RkrWriter = match precision {
853 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
854 None => ConFrameWriter::new(sink),
855 };
856 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
857}
858#[inline]
860unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
861 if filename_c.is_null() {
862 return None;
863 }
864 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
865}
866#[unsafe(no_mangle)]
872pub unsafe extern "C" fn create_writer_from_path_c(
873 filename_c: *const c_char,
874) -> *mut RKRConFrameWriter {
875 let filename = match unsafe { cstr_path(filename_c) } {
876 Some(s) => s,
877 None => return ptr::null_mut(),
878 };
879 match File::create(filename) {
880 Ok(file) => into_rkr_writer(Box::new(file), None),
881 Err(_) => ptr::null_mut(),
882 }
883}
884#[unsafe(no_mangle)]
889pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
890 if !writer_handle.is_null() {
891 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
892 }
893}
894#[unsafe(no_mangle)]
900pub unsafe extern "C" fn rkr_writer_extend(
901 writer_handle: *mut RKRConFrameWriter,
902 frame_handles: *const *const RKRConFrame,
903 num_frames: usize,
904) -> RKRStatus {
905 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
906 Some(w) => w,
907 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
908 };
909 if frame_handles.is_null() {
910 return RKRStatus::RKR_STATUS_NULL_POINTER;
911 }
912 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
913 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
914 if handles_slice.iter().any(|&handle| handle.is_null()) {
915 return RKRStatus::RKR_STATUS_NULL_POINTER;
918 }
919 for &handle in handles_slice.iter() {
920 match unsafe { (handle as *const ConFrame).as_ref() } {
922 Some(frame) => rust_frames.push(frame),
923 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
925 }
926 }
927 match writer.extend(rust_frames.into_iter()) {
928 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
929 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
930 }
931}
932
933#[unsafe(no_mangle)]
939pub unsafe extern "C" fn rkr_writer_set_canonical(
940 writer_handle: *mut RKRConFrameWriter,
941 canonical: u8,
942) -> RKRStatus {
943 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
944 Some(w) => w,
945 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
946 };
947 writer.set_canonical(canonical != 0);
948 RKRStatus::RKR_STATUS_SUCCESS
949}
950
951#[unsafe(no_mangle)]
953pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
954 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
955 Some(w) => u8::from(w.is_canonical()),
956 None => 0,
957 }
958}
959
960#[cfg(test)]
961mod index_proj_ffi_tests {
962 use super::*;
963 use std::ffi::CStr;
964 use std::fs;
965 use std::path::PathBuf;
966
967 fn fixture_path() -> PathBuf {
968 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
969 }
970
971 #[test]
972 fn ffi_projection_matches_index_proj() {
973 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
974 let fr = &frames[0];
975 let handle = fr as *const ConFrame as *const RKRConFrame;
976 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
977 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
979 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
980 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
981 assert!(!formula_c.is_null());
982 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
983 assert_eq!(formula, proj.formula);
984 unsafe { rkr_free_string(formula_c) };
985 let ie = rkr_frame_index_energy(handle);
986 match proj.energy {
987 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
988 None => assert!(ie.is_nan()),
989 }
990 let tm = rkr_frame_total_mass(handle);
991 match proj.total_mass {
992 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
993 None => assert!(tm.is_nan()),
994 }
995 let cv = rkr_frame_cell_volume(handle);
996 match proj.cell_volume {
997 Some(v) => assert!((cv - v).abs() < 1e-6 * v.max(1.0), "cell_volume C={cv} proj={v}"),
998 None => assert!(cv.is_nan()),
999 }
1000 let fm = rkr_frame_fmax(handle);
1001 match proj.fmax {
1002 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
1003 None => assert!(fm.is_nan()),
1004 }
1005 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
1006 assert!(!json_c.is_null());
1007 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
1008 assert!(json.contains("\"formula\""));
1009 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
1010 assert!(json.contains("\"n_atoms\""));
1011 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
1012 unsafe { rkr_free_string(json_c) };
1013 }
1014
1015 #[test]
1016 fn ffi_canonical_writer_byte_identical() {
1017 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1018 let fr = &frames[0];
1019 let dir = tempfile::tempdir().unwrap();
1020 let p1 = dir.path().join("a.con");
1021 let p2 = dir.path().join("b.con");
1022 for p in [&p1, &p2] {
1023 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
1024 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
1025 assert!(!w.is_null());
1026 assert_eq!(
1027 unsafe { rkr_writer_set_canonical(w, 1) },
1028 RKRStatus::RKR_STATUS_SUCCESS
1029 );
1030 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
1031 let handles = [fr as *const ConFrame as *const RKRConFrame];
1032 assert_eq!(
1033 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
1034 RKRStatus::RKR_STATUS_SUCCESS
1035 );
1036 unsafe { free_rkr_writer(w) };
1037 }
1038 let b1 = fs::read(&p1).unwrap();
1039 let b2 = fs::read(&p2).unwrap();
1040 assert_eq!(b1, b2);
1041 assert!(!b1.is_empty());
1042 }
1043}
1044#[unsafe(no_mangle)]
1053pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
1054 filename_c: *const c_char,
1055 precision: u8,
1056) -> *mut RKRConFrameWriter {
1057 let filename = match unsafe { cstr_path(filename_c) } {
1058 Some(s) => s,
1059 None => return ptr::null_mut(),
1060 };
1061 match File::create(filename) {
1062 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1063 Err(_) => ptr::null_mut(),
1064 }
1065}
1066#[repr(C)]
1071pub struct RKRConFrameBuilder {
1072 _private: [u8; 0],
1073}
1074#[allow(clippy::too_many_arguments)]
1075unsafe fn add_builder_atom(
1076 builder_handle: *mut RKRConFrameBuilder,
1077 symbol: *const c_char,
1078 x: f64,
1079 y: f64,
1080 z: f64,
1081 fixed: [bool; 3],
1082 atom_id: u64,
1083 mass: f64,
1084 velocity: Option<[f64; 3]>,
1085 forces: Option<[f64; 3]>,
1086) -> RKRStatus {
1087 if builder_handle.is_null() || symbol.is_null() {
1088 return RKRStatus::RKR_STATUS_NULL_POINTER;
1089 }
1090 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1091 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
1092 Ok(s) => s,
1093 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1094 };
1095 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
1096 if let Some(v) = velocity {
1097 builder.with_velocity(v);
1098 }
1099 if let Some(f) = forces {
1100 builder.with_force(f);
1101 }
1102 RKRStatus::RKR_STATUS_SUCCESS
1103}
1104#[unsafe(no_mangle)]
1110pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
1111 builder_handle: *mut RKRConFrameBuilder,
1112 velocity: *const f64,
1113) -> RKRStatus {
1114 if builder_handle.is_null() || velocity.is_null() {
1115 return RKRStatus::RKR_STATUS_NULL_POINTER;
1116 }
1117 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1118 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1119 builder.with_velocity(v);
1120 RKRStatus::RKR_STATUS_SUCCESS
1121}
1122#[unsafe(no_mangle)]
1128pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
1129 builder_handle: *mut RKRConFrameBuilder,
1130 force: *const f64,
1131) -> RKRStatus {
1132 if builder_handle.is_null() || force.is_null() {
1133 return RKRStatus::RKR_STATUS_NULL_POINTER;
1134 }
1135 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1136 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1137 builder.with_force(f);
1138 RKRStatus::RKR_STATUS_SUCCESS
1139}
1140#[unsafe(no_mangle)]
1150pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1151 builder_handle: *mut RKRConFrameBuilder,
1152 energy: f64,
1153) -> RKRStatus {
1154 if builder_handle.is_null() {
1155 return RKRStatus::RKR_STATUS_NULL_POINTER;
1156 }
1157 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1158 builder.with_energy(energy);
1159 RKRStatus::RKR_STATUS_SUCCESS
1160}
1161fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1175 use crate::error::ParseError;
1176 match e {
1177 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1178 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1179 }
1180 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1181 }
1182}
1183#[unsafe(no_mangle)]
1190pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1191 builder_handle: *const RKRConFrameBuilder,
1192) -> usize {
1193 if builder_handle.is_null() {
1194 return 0;
1195 }
1196 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1197 builder.atom_count()
1198}
1199#[unsafe(no_mangle)]
1203pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1204 builder_handle: *mut RKRConFrameBuilder,
1205 index: usize,
1206 x: f64,
1207 y: f64,
1208 z: f64,
1209) -> RKRStatus {
1210 if builder_handle.is_null() {
1211 return RKRStatus::RKR_STATUS_NULL_POINTER;
1212 }
1213 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1214 match builder.set_atom_position(index, x, y, z) {
1215 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1216 Err(e) => map_builder_err(e),
1217 }
1218}
1219#[unsafe(no_mangle)]
1223pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1224 builder_handle: *mut RKRConFrameBuilder,
1225 index: usize,
1226 velocity: *const f64,
1227) -> RKRStatus {
1228 if builder_handle.is_null() || velocity.is_null() {
1229 return RKRStatus::RKR_STATUS_NULL_POINTER;
1230 }
1231 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1232 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1233 match builder.set_atom_velocity(index, v) {
1234 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1235 Err(e) => map_builder_err(e),
1236 }
1237}
1238#[unsafe(no_mangle)]
1242pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1243 builder_handle: *mut RKRConFrameBuilder,
1244 index: usize,
1245 force: *const f64,
1246) -> RKRStatus {
1247 if builder_handle.is_null() || force.is_null() {
1248 return RKRStatus::RKR_STATUS_NULL_POINTER;
1249 }
1250 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1251 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1252 match builder.set_atom_force(index, f) {
1253 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1254 Err(e) => map_builder_err(e),
1255 }
1256}
1257#[unsafe(no_mangle)]
1261pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1262 builder_handle: *mut RKRConFrameBuilder,
1263 index: usize,
1264 energy: f64,
1265) -> RKRStatus {
1266 if builder_handle.is_null() {
1267 return RKRStatus::RKR_STATUS_NULL_POINTER;
1268 }
1269 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1270 match builder.set_atom_energy(index, energy) {
1271 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1272 Err(e) => map_builder_err(e),
1273 }
1274}
1275#[unsafe(no_mangle)]
1279pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1280 builder_handle: *mut RKRConFrameBuilder,
1281 index: usize,
1282 fixed_x: bool,
1283 fixed_y: bool,
1284 fixed_z: bool,
1285) -> RKRStatus {
1286 if builder_handle.is_null() {
1287 return RKRStatus::RKR_STATUS_NULL_POINTER;
1288 }
1289 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1290 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1291 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1292 Err(e) => map_builder_err(e),
1293 }
1294}
1295#[unsafe(no_mangle)]
1299pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1300 builder_handle: *mut RKRConFrameBuilder,
1301 index: usize,
1302 mass: f64,
1303) -> RKRStatus {
1304 if builder_handle.is_null() {
1305 return RKRStatus::RKR_STATUS_NULL_POINTER;
1306 }
1307 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1308 match builder.set_atom_mass(index, mass) {
1309 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1310 Err(e) => map_builder_err(e),
1311 }
1312}
1313#[unsafe(no_mangle)]
1320pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1321 builder_handle: *mut RKRConFrameBuilder,
1322 index: usize,
1323 atom_id: u64,
1324) -> RKRStatus {
1325 if builder_handle.is_null() {
1326 return RKRStatus::RKR_STATUS_NULL_POINTER;
1327 }
1328 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1329 match builder.set_atom_id(index, atom_id) {
1330 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1331 Err(e) => map_builder_err(e),
1332 }
1333}
1334#[unsafe(no_mangle)]
1338pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1339 builder_handle: *mut RKRConFrameBuilder,
1340 index: usize,
1341) -> RKRStatus {
1342 if builder_handle.is_null() {
1343 return RKRStatus::RKR_STATUS_NULL_POINTER;
1344 }
1345 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1346 match builder.clear_atom_velocity(index) {
1347 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1348 Err(e) => map_builder_err(e),
1349 }
1350}
1351#[unsafe(no_mangle)]
1354pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1355 builder_handle: *mut RKRConFrameBuilder,
1356 index: usize,
1357) -> RKRStatus {
1358 if builder_handle.is_null() {
1359 return RKRStatus::RKR_STATUS_NULL_POINTER;
1360 }
1361 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1362 match builder.clear_atom_force(index) {
1363 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1364 Err(e) => map_builder_err(e),
1365 }
1366}
1367#[unsafe(no_mangle)]
1370pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1371 builder_handle: *mut RKRConFrameBuilder,
1372 index: usize,
1373) -> RKRStatus {
1374 if builder_handle.is_null() {
1375 return RKRStatus::RKR_STATUS_NULL_POINTER;
1376 }
1377 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1378 match builder.clear_atom_energy(index) {
1379 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1380 Err(e) => map_builder_err(e),
1381 }
1382}
1383#[unsafe(no_mangle)]
1388pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1389 builder_handle: *mut RKRConFrameBuilder,
1390 positions: *const f64,
1391 len: usize,
1392) -> RKRStatus {
1393 if builder_handle.is_null() || positions.is_null() {
1394 return RKRStatus::RKR_STATUS_NULL_POINTER;
1395 }
1396 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1397 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1398 match builder.set_positions_from_flat(slice) {
1399 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1400 Err(e) => map_builder_err(e),
1401 }
1402}
1403#[unsafe(no_mangle)]
1407pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1408 builder_handle: *mut RKRConFrameBuilder,
1409 forces: *const f64,
1410 len: usize,
1411) -> RKRStatus {
1412 if builder_handle.is_null() || forces.is_null() {
1413 return RKRStatus::RKR_STATUS_NULL_POINTER;
1414 }
1415 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1416 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1417 match builder.set_forces_from_flat(slice) {
1418 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1419 Err(e) => map_builder_err(e),
1420 }
1421}
1422#[unsafe(no_mangle)]
1426pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1427 builder_handle: *mut RKRConFrameBuilder,
1428 energies: *const f64,
1429 len: usize,
1430) -> RKRStatus {
1431 if builder_handle.is_null() || energies.is_null() {
1432 return RKRStatus::RKR_STATUS_NULL_POINTER;
1433 }
1434 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1435 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1436 match builder.set_atom_energies_from_flat(slice) {
1437 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1438 Err(e) => map_builder_err(e),
1439 }
1440}
1441#[unsafe(no_mangle)]
1445pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1446 builder_handle: *const RKRConFrameBuilder,
1447 index: usize,
1448 out_xyz: *mut f64,
1449) -> RKRStatus {
1450 if builder_handle.is_null() || out_xyz.is_null() {
1451 return RKRStatus::RKR_STATUS_NULL_POINTER;
1452 }
1453 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1454 match builder.get_atom_position(index) {
1455 Ok((x, y, z)) => unsafe {
1456 *out_xyz = x;
1457 *out_xyz.add(1) = y;
1458 *out_xyz.add(2) = z;
1459 RKRStatus::RKR_STATUS_SUCCESS
1460 },
1461 Err(e) => map_builder_err(e),
1462 }
1463}
1464#[unsafe(no_mangle)]
1471pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1472 builder_handle: *const RKRConFrameBuilder,
1473 index: usize,
1474 out_xyz: *mut f64,
1475 has_value: *mut bool,
1476) -> RKRStatus {
1477 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1478 return RKRStatus::RKR_STATUS_NULL_POINTER;
1479 }
1480 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1481 match builder.get_atom_velocity(index) {
1482 Ok(Some(v)) => unsafe {
1483 *out_xyz = v[0];
1484 *out_xyz.add(1) = v[1];
1485 *out_xyz.add(2) = v[2];
1486 *has_value = true;
1487 RKRStatus::RKR_STATUS_SUCCESS
1488 },
1489 Ok(None) => unsafe {
1490 *has_value = false;
1491 RKRStatus::RKR_STATUS_SUCCESS
1492 },
1493 Err(e) => map_builder_err(e),
1494 }
1495}
1496#[unsafe(no_mangle)]
1499pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1500 builder_handle: *const RKRConFrameBuilder,
1501 index: usize,
1502 out_xyz: *mut f64,
1503 has_value: *mut bool,
1504) -> RKRStatus {
1505 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1506 return RKRStatus::RKR_STATUS_NULL_POINTER;
1507 }
1508 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1509 match builder.get_atom_force(index) {
1510 Ok(Some(f)) => unsafe {
1511 *out_xyz = f[0];
1512 *out_xyz.add(1) = f[1];
1513 *out_xyz.add(2) = f[2];
1514 *has_value = true;
1515 RKRStatus::RKR_STATUS_SUCCESS
1516 },
1517 Ok(None) => unsafe {
1518 *has_value = false;
1519 RKRStatus::RKR_STATUS_SUCCESS
1520 },
1521 Err(e) => map_builder_err(e),
1522 }
1523}
1524#[unsafe(no_mangle)]
1530pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
1531 builder_handle: *const RKRConFrameBuilder,
1532 index: usize,
1533 out_value: *mut f64,
1534 has_value: *mut bool,
1535) -> RKRStatus {
1536 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
1537 return RKRStatus::RKR_STATUS_NULL_POINTER;
1538 }
1539 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1540 match builder.get_atom_energy(index) {
1541 Ok(Some(e)) => unsafe {
1542 *out_value = e;
1543 *has_value = true;
1544 RKRStatus::RKR_STATUS_SUCCESS
1545 },
1546 Ok(None) => unsafe {
1547 *has_value = false;
1548 RKRStatus::RKR_STATUS_SUCCESS
1549 },
1550 Err(e) => map_builder_err(e),
1551 }
1552}
1553#[unsafe(no_mangle)]
1557pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
1558 builder_handle: *const RKRConFrameBuilder,
1559 index: usize,
1560 out_mass: *mut f64,
1561) -> RKRStatus {
1562 if builder_handle.is_null() || out_mass.is_null() {
1563 return RKRStatus::RKR_STATUS_NULL_POINTER;
1564 }
1565 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1566 match builder.get_atom_mass(index) {
1567 Ok(m) => unsafe {
1568 *out_mass = m;
1569 RKRStatus::RKR_STATUS_SUCCESS
1570 },
1571 Err(e) => map_builder_err(e),
1572 }
1573}
1574pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
1597fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
1598 use crate::error::ParseError;
1599 match e {
1600 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
1601 RKRStatus::RKR_STATUS_DEVICE_MISMATCH
1602 }
1603 ParseError::ValidationError(ref msg)
1604 if msg.contains("no device allocator") || msg.contains("allocator") =>
1605 {
1606 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED
1607 }
1608 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1609 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1610 }
1611}
1612pub mod rkr_dl_type_code {
1616 pub const RKR_DL_INT: u8 = 0;
1617 pub const RKR_DL_UINT: u8 = 1;
1618 pub const RKR_DL_FLOAT: u8 = 2;
1619 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
1620 pub const RKR_DL_BFLOAT: u8 = 4;
1621 pub const RKR_DL_COMPLEX: u8 = 5;
1622 pub const RKR_DL_BOOL: u8 = 6;
1623}
1624
1625pub mod rkr_dl_device_type {
1627 pub const RKR_DL_CPU: i32 = 1;
1628 pub const RKR_DL_CUDA: i32 = 2;
1629 pub const RKR_DL_CUDA_HOST: i32 = 3;
1630}
1631
1632#[repr(C)]
1636#[derive(Clone, Copy, Debug)]
1637pub struct RKRDLDataType {
1638 pub code: u8,
1640 pub bits: u8,
1642 pub lanes: u16,
1644}
1645
1646#[repr(C)]
1649#[derive(Clone, Copy, Debug)]
1650pub struct RKRDLDevice {
1651 pub device_type: i32,
1653 pub device_id: i32,
1654}
1655
1656#[repr(C)]
1670#[derive(Clone, Copy, Debug)]
1671pub struct RKRDlpackExportOptions {
1672 pub dtype: RKRDLDataType,
1674 pub device: RKRDLDevice,
1676}
1677
1678impl Default for RKRDlpackExportOptions {
1679 fn default() -> Self {
1680 Self {
1681 dtype: RKRDLDataType {
1682 code: rkr_dl_type_code::RKR_DL_FLOAT,
1683 bits: 64,
1684 lanes: 1,
1685 },
1686 device: RKRDLDevice {
1687 device_type: rkr_dl_device_type::RKR_DL_CPU,
1688 device_id: 0,
1689 },
1690 }
1691 }
1692}
1693
1694fn resolve_dlpack_opts(opts: *const RKRDlpackExportOptions) -> Result<RKRDlpackExportOptions, RKRStatus> {
1697 let o = if opts.is_null() {
1698 RKRDlpackExportOptions::default()
1699 } else {
1700 unsafe { *opts }
1701 };
1702 let dt = o.device.device_type;
1703 if dt == rkr_dl_device_type::RKR_DL_CPU {
1704 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
1706 #[cfg(not(feature = "cuda"))]
1707 {
1708 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1709 }
1710 #[cfg(feature = "cuda")]
1711 {
1712 }
1714 } else {
1715 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1716 }
1717 if o.dtype.lanes != 1 {
1718 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
1719 }
1720 Ok(o)
1721}
1722
1723fn finish_dlpack_tensor<E: std::fmt::Display>(
1724 result: Result<dlpk::DLPackTensor, E>,
1725 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1726) -> RKRStatus {
1727 match result {
1728 Ok(tensor) => {
1729 let raw = tensor.into_raw();
1730 unsafe {
1731 *out_tensor = raw.as_ptr();
1732 }
1733 RKRStatus::RKR_STATUS_SUCCESS
1734 }
1735 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
1736 "DLPack export failed: {e}"
1737 ))),
1738 }
1739}
1740
1741fn export_f64_slice_as_dlpack(
1744 data: &[f64],
1745 shape: &[usize],
1746 dtype: RKRDLDataType,
1747 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1748) -> RKRStatus {
1749 use rkr_dl_type_code::*;
1750 if dtype.lanes != 1 {
1751 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
1752 }
1753 macro_rules! arc_export {
1754 ($ty:ty, $map:expr) => {{
1755 let v: Vec<$ty> = data.iter().map($map).collect();
1756 match *shape {
1757 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
1758 Ok(a) => dlpk::DLPackTensor::try_from(a),
1759 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1760 },
1761 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
1762 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1763 }
1764 }};
1765 }
1766 let tensor = match (dtype.code, dtype.bits) {
1767 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
1768 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
1769 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
1770 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
1771 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
1772 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
1773 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
1774 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
1775 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
1776 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
1777 (RKR_DL_BOOL, 8) => {
1778 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
1780 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
1781 }
1782 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1784 };
1785 finish_dlpack_tensor(tensor, out_tensor)
1786}
1787
1788fn export_owned_array2_dlpack_opts(
1789 arr: &ndarray::ArcArray2<f64>,
1790 opts: &RKRDlpackExportOptions,
1791 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1792) -> RKRStatus {
1793 let (r, c) = arr.dim();
1794 let flat: Vec<f64> = arr.iter().copied().collect();
1795 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
1796 #[cfg(feature = "cuda")]
1797 {
1798 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
1800 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
1801 }
1802 return finish_dlpack_tensor(
1803 crate::cuda_array::export_host_f64_as_cuda_dlpack(
1804 &[r, c],
1805 &flat,
1806 opts.device.device_id,
1807 ),
1808 out_tensor,
1809 );
1810 }
1811 #[cfg(not(feature = "cuda"))]
1812 {
1813 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
1814 }
1815 }
1816 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
1817}
1818
1819fn export_owned_array1_f64_dlpack_opts(
1820 arr: &ndarray::ArcArray1<f64>,
1821 opts: &RKRDlpackExportOptions,
1822 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1823) -> RKRStatus {
1824 let flat = arr.to_vec();
1825 let n = flat.len();
1826 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
1827 #[cfg(feature = "cuda")]
1828 {
1829 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
1830 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
1831 }
1832 return finish_dlpack_tensor(
1833 crate::cuda_array::export_host_f64_as_cuda_dlpack(
1834 &[n],
1835 &flat,
1836 opts.device.device_id,
1837 ),
1838 out_tensor,
1839 );
1840 }
1841 #[cfg(not(feature = "cuda"))]
1842 {
1843 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
1844 }
1845 }
1846 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
1847}
1848
1849fn export_owned_array1_u64_dlpack(
1850 arr: &ndarray::ArcArray1<u64>,
1851 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1852) -> RKRStatus {
1853 finish_dlpack_tensor(
1854 dlpk::DLPackTensor::try_from(arr.clone()),
1855 out_tensor,
1856 )
1857}
1858
1859fn export_owned_array1_u64_dlpack_opts(
1861 arr: &ndarray::ArcArray1<u64>,
1862 opts: &RKRDlpackExportOptions,
1863 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1864) -> RKRStatus {
1865 use rkr_dl_type_code::*;
1866 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
1867 return export_owned_array1_u64_dlpack(arr, out_tensor);
1868 }
1869 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
1870 let n = as_f64.len();
1871 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
1872}
1873#[unsafe(no_mangle)]
1884pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
1885 builder_handle: *const RKRConFrameBuilder,
1886 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1887) -> RKRStatus {
1888 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
1889}
1890
1891#[unsafe(no_mangle)]
1898pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
1899 builder_handle: *const RKRConFrameBuilder,
1900 opts: *const RKRDlpackExportOptions,
1901 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1902) -> RKRStatus {
1903 if builder_handle.is_null() || out_tensor.is_null() {
1904 return RKRStatus::RKR_STATUS_NULL_POINTER;
1905 }
1906 let o = match resolve_dlpack_opts(opts) {
1907 Ok(o) => o,
1908 Err(st) => return st,
1909 };
1910 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1911 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
1912}
1913
1914#[unsafe(no_mangle)]
1924pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
1925 builder_handle: *const RKRConFrameBuilder,
1926 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1927) -> RKRStatus {
1928 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
1929}
1930
1931#[unsafe(no_mangle)]
1932pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
1933 builder_handle: *const RKRConFrameBuilder,
1934 opts: *const RKRDlpackExportOptions,
1935 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1936) -> RKRStatus {
1937 if builder_handle.is_null() || out_tensor.is_null() {
1938 return RKRStatus::RKR_STATUS_NULL_POINTER;
1939 }
1940 let o = match resolve_dlpack_opts(opts) {
1941 Ok(o) => o,
1942 Err(st) => return st,
1943 };
1944 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1945 if !builder.has_velocities_section() {
1946 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
1947 }
1948 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
1949}
1950
1951#[unsafe(no_mangle)]
1960pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
1961 builder_handle: *const RKRConFrameBuilder,
1962 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1963) -> RKRStatus {
1964 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
1965}
1966
1967#[unsafe(no_mangle)]
1968pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
1969 builder_handle: *const RKRConFrameBuilder,
1970 opts: *const RKRDlpackExportOptions,
1971 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1972) -> RKRStatus {
1973 if builder_handle.is_null() || out_tensor.is_null() {
1974 return RKRStatus::RKR_STATUS_NULL_POINTER;
1975 }
1976 let o = match resolve_dlpack_opts(opts) {
1977 Ok(o) => o,
1978 Err(st) => return st,
1979 };
1980 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1981 if !builder.has_forces_section() {
1982 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
1983 }
1984 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
1985}
1986
1987#[unsafe(no_mangle)]
1996pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
1997 builder_handle: *const RKRConFrameBuilder,
1998 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1999) -> RKRStatus {
2000 unsafe {
2001 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2002 }
2003}
2004
2005#[unsafe(no_mangle)]
2006pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
2007 builder_handle: *const RKRConFrameBuilder,
2008 opts: *const RKRDlpackExportOptions,
2009 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2010) -> RKRStatus {
2011 if builder_handle.is_null() || out_tensor.is_null() {
2012 return RKRStatus::RKR_STATUS_NULL_POINTER;
2013 }
2014 let o = match resolve_dlpack_opts(opts) {
2015 Ok(o) => o,
2016 Err(st) => return st,
2017 };
2018 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2019 if !builder.has_energies_section() {
2020 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2021 }
2022 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
2023}
2024
2025#[unsafe(no_mangle)]
2031pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
2032 builder_handle: *const RKRConFrameBuilder,
2033 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2034) -> RKRStatus {
2035 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2036}
2037
2038#[unsafe(no_mangle)]
2039pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
2040 builder_handle: *const RKRConFrameBuilder,
2041 opts: *const RKRDlpackExportOptions,
2042 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2043) -> RKRStatus {
2044 if builder_handle.is_null() || out_tensor.is_null() {
2045 return RKRStatus::RKR_STATUS_NULL_POINTER;
2046 }
2047 let o = match resolve_dlpack_opts(opts) {
2048 Ok(o) => o,
2049 Err(st) => return st,
2050 };
2051 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2052 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
2053}
2054#[unsafe(no_mangle)]
2060pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
2061 builder_handle: *const RKRConFrameBuilder,
2062 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2063) -> RKRStatus {
2064 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2065}
2066
2067#[unsafe(no_mangle)]
2068pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2069 builder_handle: *const RKRConFrameBuilder,
2070 opts: *const RKRDlpackExportOptions,
2071 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2072) -> RKRStatus {
2073 if builder_handle.is_null() || out_tensor.is_null() {
2074 return RKRStatus::RKR_STATUS_NULL_POINTER;
2075 }
2076 let o = match resolve_dlpack_opts(opts) {
2077 Ok(o) => o,
2078 Err(st) => return st,
2079 };
2080 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2081 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2082}
2083#[unsafe(no_mangle)]
2107pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2108 builder_handle: *mut RKRConFrameBuilder,
2109) -> *mut f64 {
2110 if builder_handle.is_null() {
2111 return std::ptr::null_mut();
2112 }
2113 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2114 builder
2115 .positions_view_mut()
2116 .as_slice_memory_order_mut()
2117 .map(|s| s.as_mut_ptr())
2118 .unwrap_or(std::ptr::null_mut())
2119}
2120#[unsafe(no_mangle)]
2127pub unsafe extern "C" fn rkr_frame_builder_velocities_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 if !builder.has_velocities_section() {
2135 return std::ptr::null_mut();
2136 }
2137 let slice = builder.velocities_mut();
2138 if slice.is_empty() {
2139 std::ptr::null_mut()
2140 } else {
2141 slice.as_mut_ptr()
2142 }
2143}
2144#[unsafe(no_mangle)]
2150pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2151 builder_handle: *mut RKRConFrameBuilder,
2152) -> *mut f64 {
2153 if builder_handle.is_null() {
2154 return std::ptr::null_mut();
2155 }
2156 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2157 if !builder.has_forces_section() {
2158 return std::ptr::null_mut();
2159 }
2160 let slice = builder.forces_mut();
2161 if slice.is_empty() {
2162 std::ptr::null_mut()
2163 } else {
2164 slice.as_mut_ptr()
2165 }
2166}
2167#[unsafe(no_mangle)]
2173pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2174 builder_handle: *mut RKRConFrameBuilder,
2175) -> *mut f64 {
2176 if builder_handle.is_null() {
2177 return std::ptr::null_mut();
2178 }
2179 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2180 if !builder.has_energies_section() {
2181 return std::ptr::null_mut();
2182 }
2183 let slice = builder.atom_energies_mut();
2184 if slice.is_empty() {
2185 std::ptr::null_mut()
2186 } else {
2187 slice.as_mut_ptr()
2188 }
2189}
2190#[unsafe(no_mangle)]
2195pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2196 builder_handle: *mut RKRConFrameBuilder,
2197) -> *mut f64 {
2198 if builder_handle.is_null() {
2199 return std::ptr::null_mut();
2200 }
2201 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2202 let slice = builder.masses_mut();
2203 if slice.is_empty() {
2204 std::ptr::null_mut()
2205 } else {
2206 slice.as_mut_ptr()
2207 }
2208}
2209#[unsafe(no_mangle)]
2214pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2215 builder_handle: *const RKRConFrameBuilder,
2216) -> *const u64 {
2217 if builder_handle.is_null() {
2218 return std::ptr::null();
2219 }
2220 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2221 let slice = builder.atom_ids();
2222 if slice.is_empty() {
2223 std::ptr::null()
2224 } else {
2225 slice.as_ptr()
2226 }
2227}
2228#[unsafe(no_mangle)]
2240#[allow(clippy::too_many_arguments)]
2241pub unsafe extern "C" fn rkr_frame_add_atom_full(
2242 builder_handle: *mut RKRConFrameBuilder,
2243 symbol: *const c_char,
2244 x: f64,
2245 y: f64,
2246 z: f64,
2247 fixed_x: bool,
2248 fixed_y: bool,
2249 fixed_z: bool,
2250 atom_id: u64,
2251 mass: f64,
2252 velocity: *const f64,
2253 force: *const f64,
2254) -> RKRStatus {
2255 let velocity = if velocity.is_null() {
2256 None
2257 } else {
2258 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
2259 };
2260 let force = if force.is_null() {
2261 None
2262 } else {
2263 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
2264 };
2265 unsafe {
2266 add_builder_atom(
2267 builder_handle,
2268 symbol,
2269 x,
2270 y,
2271 z,
2272 [fixed_x, fixed_y, fixed_z],
2273 atom_id,
2274 mass,
2275 velocity,
2276 force,
2277 )
2278 }
2279}
2280#[unsafe(no_mangle)]
2296pub unsafe extern "C" fn rkr_frame_new(
2297 cell: *const f64,
2298 angles: *const f64,
2299 prebox0: *const c_char,
2300 prebox1: *const c_char,
2301 postbox0: *const c_char,
2302 postbox1: *const c_char,
2303) -> *mut RKRConFrameBuilder {
2304 if cell.is_null() || angles.is_null() {
2305 return ptr::null_mut();
2306 }
2307 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
2308 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
2309 let get_str = |p: *const c_char| -> String {
2310 if p.is_null() {
2311 String::new()
2312 } else {
2313 unsafe { CStr::from_ptr(p) }
2314 .to_str()
2315 .unwrap_or("")
2316 .to_string()
2317 }
2318 };
2319 let _ = get_str(prebox1);
2322 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
2323 builder
2324 .prebox_header(get_str(prebox0))
2325 .postbox_header([get_str(postbox0), get_str(postbox1)]);
2326 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
2327}
2328#[unsafe(no_mangle)]
2334pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
2335 builder_handle: *mut RKRConFrameBuilder,
2336 metadata_json: *const c_char,
2337) -> RKRStatus {
2338 if builder_handle.is_null() || metadata_json.is_null() {
2339 return RKRStatus::RKR_STATUS_NULL_POINTER;
2340 }
2341 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2342 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
2343 Ok(s) => s,
2344 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2345 };
2346 match builder.set_metadata_json(metadata_json) {
2347 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
2348 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
2349 }
2350}
2351#[unsafe(no_mangle)]
2357pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
2358 builder_handle: *mut RKRConFrameBuilder,
2359 key: *const c_char,
2360 value: f64,
2361) -> RKRStatus {
2362 if builder_handle.is_null() || key.is_null() {
2363 return RKRStatus::RKR_STATUS_NULL_POINTER;
2364 }
2365 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2366 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2367 Ok(s) => s,
2368 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2369 };
2370 builder.set_scalar_metadata(key, value);
2371 RKRStatus::RKR_STATUS_SUCCESS
2372}
2373#[unsafe(no_mangle)]
2379pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
2380 builder_handle: *mut RKRConFrameBuilder,
2381 key: *const c_char,
2382 value: *const c_char,
2383) -> RKRStatus {
2384 if builder_handle.is_null() || key.is_null() || value.is_null() {
2385 return RKRStatus::RKR_STATUS_NULL_POINTER;
2386 }
2387 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2388 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2389 Ok(s) => s,
2390 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2391 };
2392 let value = match unsafe { CStr::from_ptr(value).to_str() } {
2393 Ok(s) => s,
2394 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2395 };
2396 builder.set_string_metadata(key, value);
2397 RKRStatus::RKR_STATUS_SUCCESS
2398}
2399#[unsafe(no_mangle)]
2405pub unsafe extern "C" fn rkr_frame_builder_set_energy(
2406 builder_handle: *mut RKRConFrameBuilder,
2407 energy: f64,
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_energy(energy);
2414 RKRStatus::RKR_STATUS_SUCCESS
2415}
2416#[unsafe(no_mangle)]
2422pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
2423 builder_handle: *mut RKRConFrameBuilder,
2424 idx: 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_frame_index(idx);
2431 RKRStatus::RKR_STATUS_SUCCESS
2432}
2433#[unsafe(no_mangle)]
2439pub unsafe extern "C" fn rkr_frame_builder_set_time(
2440 builder_handle: *mut RKRConFrameBuilder,
2441 time: f64,
2442) -> RKRStatus {
2443 if builder_handle.is_null() {
2444 return RKRStatus::RKR_STATUS_NULL_POINTER;
2445 }
2446 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2447 builder.set_time(time);
2448 RKRStatus::RKR_STATUS_SUCCESS
2449}
2450#[unsafe(no_mangle)]
2456pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
2457 builder_handle: *mut RKRConFrameBuilder,
2458 dt: f64,
2459) -> RKRStatus {
2460 if builder_handle.is_null() {
2461 return RKRStatus::RKR_STATUS_NULL_POINTER;
2462 }
2463 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2464 builder.set_timestep(dt);
2465 RKRStatus::RKR_STATUS_SUCCESS
2466}
2467#[unsafe(no_mangle)]
2473pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
2474 builder_handle: *mut RKRConFrameBuilder,
2475 bead: u64,
2476) -> RKRStatus {
2477 if builder_handle.is_null() {
2478 return RKRStatus::RKR_STATUS_NULL_POINTER;
2479 }
2480 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2481 builder.set_neb_bead(bead);
2482 RKRStatus::RKR_STATUS_SUCCESS
2483}
2484#[unsafe(no_mangle)]
2490pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
2491 builder_handle: *mut RKRConFrameBuilder,
2492 band: u64,
2493) -> RKRStatus {
2494 if builder_handle.is_null() {
2495 return RKRStatus::RKR_STATUS_NULL_POINTER;
2496 }
2497 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2498 builder.set_neb_band(band);
2499 RKRStatus::RKR_STATUS_SUCCESS
2500}
2501#[unsafe(no_mangle)]
2518pub unsafe extern "C" fn rkr_frame_add_atom(
2519 builder_handle: *mut RKRConFrameBuilder,
2520 symbol: *const c_char,
2521 x: f64,
2522 y: f64,
2523 z: f64,
2524 is_fixed: bool,
2525 atom_id: u64,
2526 mass: f64,
2527) -> RKRStatus {
2528 unsafe {
2529 add_builder_atom(
2530 builder_handle,
2531 symbol,
2532 x,
2533 y,
2534 z,
2535 [is_fixed; 3],
2536 atom_id,
2537 mass,
2538 None,
2539 None,
2540 )
2541 }
2542}
2543#[unsafe(no_mangle)]
2550pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
2551 builder_handle: *mut RKRConFrameBuilder,
2552 symbol: *const c_char,
2553 x: f64,
2554 y: f64,
2555 z: f64,
2556 fixed_x: bool,
2557 fixed_y: bool,
2558 fixed_z: bool,
2559 atom_id: u64,
2560 mass: f64,
2561) -> RKRStatus {
2562 unsafe {
2563 add_builder_atom(
2564 builder_handle,
2565 symbol,
2566 x,
2567 y,
2568 z,
2569 [fixed_x, fixed_y, fixed_z],
2570 atom_id,
2571 mass,
2572 None,
2573 None,
2574 )
2575 }
2576}
2577#[unsafe(no_mangle)]
2584pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
2585 builder_handle: *mut RKRConFrameBuilder,
2586 symbol: *const c_char,
2587 x: f64,
2588 y: f64,
2589 z: f64,
2590 is_fixed: bool,
2591 atom_id: u64,
2592 mass: f64,
2593 vx: f64,
2594 vy: f64,
2595 vz: f64,
2596) -> RKRStatus {
2597 unsafe {
2598 add_builder_atom(
2599 builder_handle,
2600 symbol,
2601 x,
2602 y,
2603 z,
2604 [is_fixed; 3],
2605 atom_id,
2606 mass,
2607 Some([vx, vy, vz]),
2608 None,
2609 )
2610 }
2611}
2612#[unsafe(no_mangle)]
2619pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
2620 builder_handle: *mut RKRConFrameBuilder,
2621 symbol: *const c_char,
2622 x: f64,
2623 y: f64,
2624 z: f64,
2625 fixed_x: bool,
2626 fixed_y: bool,
2627 fixed_z: bool,
2628 atom_id: u64,
2629 mass: f64,
2630 vx: f64,
2631 vy: f64,
2632 vz: f64,
2633) -> RKRStatus {
2634 unsafe {
2635 add_builder_atom(
2636 builder_handle,
2637 symbol,
2638 x,
2639 y,
2640 z,
2641 [fixed_x, fixed_y, fixed_z],
2642 atom_id,
2643 mass,
2644 Some([vx, vy, vz]),
2645 None,
2646 )
2647 }
2648}
2649#[unsafe(no_mangle)]
2656pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
2657 builder_handle: *mut RKRConFrameBuilder,
2658 symbol: *const c_char,
2659 x: f64,
2660 y: f64,
2661 z: f64,
2662 is_fixed: bool,
2663 atom_id: u64,
2664 mass: f64,
2665 fx: f64,
2666 fy: f64,
2667 fz: f64,
2668) -> RKRStatus {
2669 unsafe {
2670 add_builder_atom(
2671 builder_handle,
2672 symbol,
2673 x,
2674 y,
2675 z,
2676 [is_fixed; 3],
2677 atom_id,
2678 mass,
2679 None,
2680 Some([fx, fy, fz]),
2681 )
2682 }
2683}
2684#[unsafe(no_mangle)]
2691pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
2692 builder_handle: *mut RKRConFrameBuilder,
2693 symbol: *const c_char,
2694 x: f64,
2695 y: f64,
2696 z: f64,
2697 fixed_x: bool,
2698 fixed_y: bool,
2699 fixed_z: bool,
2700 atom_id: u64,
2701 mass: f64,
2702 fx: f64,
2703 fy: f64,
2704 fz: f64,
2705) -> RKRStatus {
2706 unsafe {
2707 add_builder_atom(
2708 builder_handle,
2709 symbol,
2710 x,
2711 y,
2712 z,
2713 [fixed_x, fixed_y, fixed_z],
2714 atom_id,
2715 mass,
2716 None,
2717 Some([fx, fy, fz]),
2718 )
2719 }
2720}
2721#[unsafe(no_mangle)]
2728pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
2729 builder_handle: *mut RKRConFrameBuilder,
2730 symbol: *const c_char,
2731 x: f64,
2732 y: f64,
2733 z: f64,
2734 is_fixed: bool,
2735 atom_id: u64,
2736 mass: f64,
2737 vx: f64,
2738 vy: f64,
2739 vz: f64,
2740 fx: f64,
2741 fy: f64,
2742 fz: f64,
2743) -> RKRStatus {
2744 unsafe {
2745 add_builder_atom(
2746 builder_handle,
2747 symbol,
2748 x,
2749 y,
2750 z,
2751 [is_fixed; 3],
2752 atom_id,
2753 mass,
2754 Some([vx, vy, vz]),
2755 Some([fx, fy, fz]),
2756 )
2757 }
2758}
2759#[unsafe(no_mangle)]
2766pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
2767 builder_handle: *mut RKRConFrameBuilder,
2768 symbol: *const c_char,
2769 x: f64,
2770 y: f64,
2771 z: f64,
2772 fixed_x: bool,
2773 fixed_y: bool,
2774 fixed_z: bool,
2775 atom_id: u64,
2776 mass: f64,
2777 vx: f64,
2778 vy: f64,
2779 vz: f64,
2780 fx: f64,
2781 fy: f64,
2782 fz: f64,
2783) -> RKRStatus {
2784 unsafe {
2785 add_builder_atom(
2786 builder_handle,
2787 symbol,
2788 x,
2789 y,
2790 z,
2791 [fixed_x, fixed_y, fixed_z],
2792 atom_id,
2793 mass,
2794 Some([vx, vy, vz]),
2795 Some([fx, fy, fz]),
2796 )
2797 }
2798}
2799#[unsafe(no_mangle)]
2807pub unsafe extern "C" fn rkr_frame_builder_build(
2808 builder_handle: *mut RKRConFrameBuilder,
2809) -> *mut RKRConFrame {
2810 if builder_handle.is_null() {
2811 return ptr::null_mut();
2812 }
2813 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2814 let frame = builder.build();
2815 Box::into_raw(Box::new(frame)) as *mut RKRConFrame
2816}
2817#[unsafe(no_mangle)]
2822pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
2823 if !builder_handle.is_null() {
2824 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2825 }
2826}
2827#[unsafe(no_mangle)]
2844pub unsafe extern "C" fn rkr_frame_builder_clone(
2845 builder_handle: *const RKRConFrameBuilder,
2846) -> *mut RKRConFrameBuilder {
2847 if builder_handle.is_null() {
2848 return std::ptr::null_mut();
2849 }
2850 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2851 let cloned = builder.clone();
2852 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
2853}
2854#[unsafe(no_mangle)]
2860pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
2861 let filename = match unsafe { cstr_path(filename_c) } {
2862 Some(s) => s,
2863 None => return ptr::null_mut(),
2864 };
2865 match crate::compression::gzip_writer(Path::new(filename)) {
2866 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
2867 Err(_) => ptr::null_mut(),
2868 }
2869}
2870#[unsafe(no_mangle)]
2877pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
2878 filename_c: *const c_char,
2879 precision: u8,
2880) -> *mut RKRConFrameWriter {
2881 let filename = match unsafe { cstr_path(filename_c) } {
2882 Some(s) => s,
2883 None => return ptr::null_mut(),
2884 };
2885 match crate::compression::gzip_writer(Path::new(filename)) {
2886 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
2887 Err(_) => ptr::null_mut(),
2888 }
2889}
2890#[cfg(feature = "zstd")]
2900#[unsafe(no_mangle)]
2901pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
2902 let filename = match unsafe { cstr_path(filename_c) } {
2903 Some(s) => s,
2904 None => return ptr::null_mut(),
2905 };
2906 match crate::compression::zstd_writer(Path::new(filename)) {
2907 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
2908 Err(_) => ptr::null_mut(),
2909 }
2910}
2911#[cfg(feature = "zstd")]
2922#[unsafe(no_mangle)]
2923pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
2924 filename_c: *const c_char,
2925 precision: u8,
2926) -> *mut RKRConFrameWriter {
2927 let filename = match unsafe { cstr_path(filename_c) } {
2928 Some(s) => s,
2929 None => return ptr::null_mut(),
2930 };
2931 match crate::compression::zstd_writer(Path::new(filename)) {
2932 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
2933 Err(_) => ptr::null_mut(),
2934 }
2935}
2936#[unsafe(no_mangle)]
2948pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
2949 if filename_c.is_null() {
2950 return ptr::null_mut();
2951 }
2952 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
2953 Ok(s) => s,
2954 Err(_) => return ptr::null_mut(),
2955 };
2956 match iterators::read_first_frame(Path::new(filename)) {
2957 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
2958 Err(_) => ptr::null_mut(),
2959 }
2960}
2961#[unsafe(no_mangle)]
2970pub unsafe extern "C" fn rkr_read_all_frames(
2971 filename_c: *const c_char,
2972 num_frames: *mut usize,
2973) -> *mut *mut RKRConFrame {
2974 if filename_c.is_null() || num_frames.is_null() {
2975 return ptr::null_mut();
2976 }
2977 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
2978 Ok(s) => s,
2979 Err(_) => return ptr::null_mut(),
2980 };
2981 match iterators::read_all_frames(Path::new(filename)) {
2982 Ok(frames) => {
2983 let count = frames.len();
2984 let mut handles: Vec<*mut RKRConFrame> = frames
2988 .into_iter()
2989 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
2990 .collect();
2991 handles.shrink_to_fit();
2992 debug_assert_eq!(handles.len(), handles.capacity());
2993 let ptr = handles.as_mut_ptr();
2994 std::mem::forget(handles);
2995 unsafe { *num_frames = count };
2996 ptr
2997 }
2998 Err(_) => ptr::null_mut(),
2999 }
3000}
3001#[unsafe(no_mangle)]
3007pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3008 if frames.is_null() {
3009 return;
3010 }
3011 unsafe {
3012 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3013 for handle in handles {
3014 if !handle.is_null() {
3015 let _ = Box::from_raw(handle as *mut ConFrame);
3016 }
3017 }
3018 }
3019}
3020#[unsafe(no_mangle)]
3026pub unsafe extern "C" fn free_rkr_frame_ptr_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3027 if frames.is_null() {
3028 return;
3029 }
3030 unsafe {
3031 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3032 }
3033}
3034
3035#[cfg(feature = "metatensor")]
3050#[unsafe(no_mangle)]
3051pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3052 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3053}
3054#[cfg(feature = "metatensor")]
3056#[unsafe(no_mangle)]
3057pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3058 frame_handle: *const RKRConFrame,
3059 out_block: *mut *mut metatensor::c_api::mts_block_t,
3060) -> RKRStatus {
3061 if frame_handle.is_null() || out_block.is_null() {
3062 return RKRStatus::RKR_STATUS_NULL_POINTER;
3063 }
3064 unsafe { *out_block = std::ptr::null_mut() };
3065 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3066 return RKRStatus::RKR_STATUS_NULL_POINTER;
3067 };
3068 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3069 crate::metatensor_export::frame_positions_block(frame)
3070 })) {
3071 Ok(Ok(b)) => {
3072 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3073 RKRStatus::RKR_STATUS_SUCCESS
3074 }
3075 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3076 }
3077}
3078#[cfg(feature = "metatensor")]
3079#[unsafe(no_mangle)]
3080pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3081 frame_handle: *const RKRConFrame,
3082 out_block: *mut *mut metatensor::c_api::mts_block_t,
3083) -> RKRStatus {
3084 if frame_handle.is_null() || out_block.is_null() {
3085 return RKRStatus::RKR_STATUS_NULL_POINTER;
3086 }
3087 unsafe { *out_block = std::ptr::null_mut() };
3088 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3089 return RKRStatus::RKR_STATUS_NULL_POINTER;
3090 };
3091 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3092 crate::metatensor_export::frame_velocities_block(frame)
3093 })) {
3094 Ok(Ok(Some(b))) => {
3095 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3096 RKRStatus::RKR_STATUS_SUCCESS
3097 }
3098 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3099 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3100 }
3101}
3102#[cfg(feature = "metatensor")]
3103#[unsafe(no_mangle)]
3104pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3105 frame_handle: *const RKRConFrame,
3106 out_block: *mut *mut metatensor::c_api::mts_block_t,
3107) -> RKRStatus {
3108 if frame_handle.is_null() || out_block.is_null() {
3109 return RKRStatus::RKR_STATUS_NULL_POINTER;
3110 }
3111 unsafe { *out_block = std::ptr::null_mut() };
3112 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3113 return RKRStatus::RKR_STATUS_NULL_POINTER;
3114 };
3115 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3116 crate::metatensor_export::frame_forces_block(frame)
3117 })) {
3118 Ok(Ok(Some(b))) => {
3119 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3120 RKRStatus::RKR_STATUS_SUCCESS
3121 }
3122 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3123 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3124 }
3125}
3126#[cfg(feature = "metatensor")]
3127#[unsafe(no_mangle)]
3128pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3129 frame_handle: *const RKRConFrame,
3130 out_block: *mut *mut metatensor::c_api::mts_block_t,
3131) -> RKRStatus {
3132 if frame_handle.is_null() || out_block.is_null() {
3133 return RKRStatus::RKR_STATUS_NULL_POINTER;
3134 }
3135 unsafe { *out_block = std::ptr::null_mut() };
3136 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3137 return RKRStatus::RKR_STATUS_NULL_POINTER;
3138 };
3139 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3140 crate::metatensor_export::frame_energies_block(frame)
3141 })) {
3142 Ok(Ok(Some(b))) => {
3143 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3144 RKRStatus::RKR_STATUS_SUCCESS
3145 }
3146 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3147 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3148 }
3149}
3150#[cfg(not(feature = "zstd"))]
3151#[unsafe(no_mangle)]
3152pub unsafe extern "C" fn create_writer_zstd_c(_filename_c: *const c_char) -> *mut RKRConFrameWriter {
3153 ptr::null_mut()
3154}
3155#[cfg(not(feature = "zstd"))]
3156#[unsafe(no_mangle)]
3157pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3158 _filename_c: *const c_char,
3159 _precision: u8,
3160) -> *mut RKRConFrameWriter {
3161 ptr::null_mut()
3162}
3163#[cfg(not(feature = "metatensor"))]
3167#[repr(C)]
3168pub struct mts_block_t {
3169 _private: [u8; 0],
3170}
3171#[cfg(not(feature = "metatensor"))]
3172#[unsafe(no_mangle)]
3173pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3174#[cfg(not(feature = "metatensor"))]
3175#[unsafe(no_mangle)]
3176pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3177 _frame_handle: *const RKRConFrame,
3178 out_block: *mut *mut mts_block_t,
3179) -> RKRStatus {
3180 if !out_block.is_null() {
3181 unsafe { *out_block = std::ptr::null_mut() };
3182 }
3183 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3184}
3185#[cfg(not(feature = "metatensor"))]
3186#[unsafe(no_mangle)]
3187pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3188 _frame_handle: *const RKRConFrame,
3189 out_block: *mut *mut mts_block_t,
3190) -> RKRStatus {
3191 if !out_block.is_null() {
3192 unsafe { *out_block = std::ptr::null_mut() };
3193 }
3194 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3195}
3196#[cfg(not(feature = "metatensor"))]
3197#[unsafe(no_mangle)]
3198pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3199 _frame_handle: *const RKRConFrame,
3200 out_block: *mut *mut mts_block_t,
3201) -> RKRStatus {
3202 if !out_block.is_null() {
3203 unsafe { *out_block = std::ptr::null_mut() };
3204 }
3205 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3206}
3207#[cfg(not(feature = "metatensor"))]
3208#[unsafe(no_mangle)]
3209pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3210 _frame_handle: *const RKRConFrame,
3211 out_block: *mut *mut mts_block_t,
3212) -> RKRStatus {
3213 if !out_block.is_null() {
3214 unsafe { *out_block = std::ptr::null_mut() };
3215 }
3216 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3217}
3218#[unsafe(no_mangle)]
3221pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3222 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3223 return 0;
3224 };
3225 frame.atom_data.len()
3226}
3227#[unsafe(no_mangle)]
3229pub unsafe extern "C" fn rkr_frame_copy_positions(
3230 frame_handle: *const RKRConFrame,
3231 out: *mut f64,
3232 out_len: usize,
3233) -> RKRStatus {
3234 if frame_handle.is_null() || out.is_null() {
3235 return RKRStatus::RKR_STATUS_NULL_POINTER;
3236 }
3237 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3238 return RKRStatus::RKR_STATUS_NULL_POINTER;
3239 };
3240 let n = frame.atom_data.len();
3241 let need = n.saturating_mul(3);
3242 if out_len < need {
3243 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3244 }
3245 let slice = unsafe { std::slice::from_raw_parts_mut(out, need) };
3246 for (i, a) in frame.atom_data.iter().enumerate() {
3247 slice[i * 3] = a.x;
3248 slice[i * 3 + 1] = a.y;
3249 slice[i * 3 + 2] = a.z;
3250 }
3251 RKRStatus::RKR_STATUS_SUCCESS
3252}
3253#[unsafe(no_mangle)]
3254pub unsafe extern "C" fn rkr_frame_copy_velocities(
3255 frame_handle: *const RKRConFrame,
3256 out: *mut f64,
3257 out_len: usize,
3258) -> RKRStatus {
3259 if frame_handle.is_null() || out.is_null() {
3260 return RKRStatus::RKR_STATUS_NULL_POINTER;
3261 }
3262 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3263 return RKRStatus::RKR_STATUS_NULL_POINTER;
3264 };
3265 if !frame.has_velocities() {
3266 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3267 }
3268 let n = frame.atom_data.len();
3269 let need = n.saturating_mul(3);
3270 if out_len < need {
3271 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3272 }
3273 let slice = unsafe { std::slice::from_raw_parts_mut(out, need) };
3274 for (i, a) in frame.atom_data.iter().enumerate() {
3275 let [vx, vy, vz] = a.velocity.unwrap_or([0.0; 3]);
3276 slice[i * 3] = vx;
3277 slice[i * 3 + 1] = vy;
3278 slice[i * 3 + 2] = vz;
3279 }
3280 RKRStatus::RKR_STATUS_SUCCESS
3281}
3282#[unsafe(no_mangle)]
3283pub unsafe extern "C" fn rkr_frame_copy_forces(
3284 frame_handle: *const RKRConFrame,
3285 out: *mut f64,
3286 out_len: usize,
3287) -> RKRStatus {
3288 if frame_handle.is_null() || out.is_null() {
3289 return RKRStatus::RKR_STATUS_NULL_POINTER;
3290 }
3291 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3292 return RKRStatus::RKR_STATUS_NULL_POINTER;
3293 };
3294 if !frame.has_forces() {
3295 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3296 }
3297 let n = frame.atom_data.len();
3298 let need = n.saturating_mul(3);
3299 if out_len < need {
3300 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3301 }
3302 let slice = unsafe { std::slice::from_raw_parts_mut(out, need) };
3303 for (i, a) in frame.atom_data.iter().enumerate() {
3304 let [fx, fy, fz] = a.force.unwrap_or([0.0; 3]);
3305 slice[i * 3] = fx;
3306 slice[i * 3 + 1] = fy;
3307 slice[i * 3 + 2] = fz;
3308 }
3309 RKRStatus::RKR_STATUS_SUCCESS
3310}
3311#[unsafe(no_mangle)]
3312pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
3313 frame_handle: *const RKRConFrame,
3314 out: *mut f64,
3315 out_len: usize,
3316) -> RKRStatus {
3317 if frame_handle.is_null() || out.is_null() {
3318 return RKRStatus::RKR_STATUS_NULL_POINTER;
3319 }
3320 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3321 return RKRStatus::RKR_STATUS_NULL_POINTER;
3322 };
3323 if !frame.has_energies() {
3324 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3325 }
3326 let n = frame.atom_data.len();
3327 if out_len < n {
3328 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3329 }
3330 let slice = unsafe { std::slice::from_raw_parts_mut(out, n) };
3331 for (i, a) in frame.atom_data.iter().enumerate() {
3332 slice[i] = a.energy.unwrap_or(0.0);
3333 }
3334 RKRStatus::RKR_STATUS_SUCCESS
3335}
3336#[unsafe(no_mangle)]
3337pub unsafe extern "C" fn rkr_frame_copy_masses(
3338 frame_handle: *const RKRConFrame,
3339 out: *mut f64,
3340 out_len: usize,
3341) -> RKRStatus {
3342 if frame_handle.is_null() || out.is_null() {
3343 return RKRStatus::RKR_STATUS_NULL_POINTER;
3344 }
3345 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3346 return RKRStatus::RKR_STATUS_NULL_POINTER;
3347 };
3348 let n = frame.atom_data.len();
3349 if out_len < n {
3350 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3351 }
3352 let slice = unsafe { std::slice::from_raw_parts_mut(out, n) };
3353 let mut expanded: Vec<f64> = Vec::with_capacity(n);
3354 for (ti, &count) in frame.header.natms_per_type.iter().enumerate() {
3355 let m = frame.header.masses_per_type.get(ti).copied().unwrap_or(0.0);
3356 expanded.extend(std::iter::repeat_n(m, count));
3357 }
3358 if expanded.len() != n {
3359 expanded.resize(n, 0.0);
3360 }
3361 slice.copy_from_slice(&expanded[..n]);
3362 RKRStatus::RKR_STATUS_SUCCESS
3363}
3364#[unsafe(no_mangle)]
3365pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
3366 frame_handle: *const RKRConFrame,
3367 out: *mut u64,
3368 out_len: usize,
3369) -> RKRStatus {
3370 if frame_handle.is_null() || out.is_null() {
3371 return RKRStatus::RKR_STATUS_NULL_POINTER;
3372 }
3373 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3374 return RKRStatus::RKR_STATUS_NULL_POINTER;
3375 };
3376 let n = frame.atom_data.len();
3377 if out_len < n {
3378 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3379 }
3380 let slice = unsafe { std::slice::from_raw_parts_mut(out, n) };
3381 for (i, a) in frame.atom_data.iter().enumerate() {
3382 slice[i] = a.atom_id;
3383 }
3384 RKRStatus::RKR_STATUS_SUCCESS
3385}
3386fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
3387 let n = frame.atom_data.len();
3388 let mut data = Vec::with_capacity(n * 3);
3389 for a in &frame.atom_data {
3390 data.extend_from_slice(&[a.x, a.y, a.z]);
3391 }
3392 ndarray::ArcArray2::from_shape_vec((n, 3), data)
3393 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
3394}
3395
3396#[unsafe(no_mangle)]
3406pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
3407 frame_handle: *const RKRConFrame,
3408 device_type: i32,
3409 device_id: i32,
3410 _stream: i64,
3411 _max_version_major: u32,
3412 _max_version_minor: u32,
3413 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3414) -> RKRStatus {
3415 if frame_handle.is_null() || out_tensor.is_null() {
3416 return RKRStatus::RKR_STATUS_NULL_POINTER;
3417 }
3418 unsafe { *out_tensor = std::ptr::null_mut() };
3419 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
3420 dlpk::sys::DLDevice::cpu()
3421 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
3422 #[cfg(feature = "cuda")]
3423 {
3424 dlpk::sys::DLDevice::cuda(device_id)
3425 }
3426 #[cfg(not(feature = "cuda"))]
3427 {
3428 let _ = device_id;
3429 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3430 }
3431 } else {
3432 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3433 };
3434 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3435 return RKRStatus::RKR_STATUS_NULL_POINTER;
3436 };
3437 match frame.positions_as_dlpack(dl_device) {
3439 Ok(tensor) => {
3440 let raw = tensor.into_raw();
3441 unsafe {
3442 *out_tensor = raw.as_ptr();
3443 }
3444 RKRStatus::RKR_STATUS_SUCCESS
3445 }
3446 Err(e) => map_dlpack_err(e),
3447 }
3448}
3449
3450#[unsafe(no_mangle)]
3456pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
3457 frame_handle: *mut RKRConFrame,
3458 tensor: *const RKRDLManagedTensorVersioned,
3459) -> RKRStatus {
3460 if frame_handle.is_null() || tensor.is_null() {
3461 return RKRStatus::RKR_STATUS_NULL_POINTER;
3462 }
3463 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
3464 let dl = unsafe { &(*tensor).dl_tensor };
3465 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
3466 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3467 }
3468 let n = frame.atom_data.len();
3469 let need = n.saturating_mul(3);
3470 let ndim = dl.ndim as usize;
3471 let shape = if dl.shape.is_null() {
3472 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3473 } else {
3474 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
3475 };
3476 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
3477 need
3478 } else if ndim == 1 && shape[0] == need as i64 {
3479 need
3480 } else {
3481 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3482 };
3483 let code = dl.dtype.code as u8;
3484 let bits = dl.dtype.bits;
3485 if dl.data.is_null() {
3486 return RKRStatus::RKR_STATUS_NULL_POINTER;
3487 }
3488 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
3489 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
3490 s.to_vec()
3491 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
3492 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
3493 s.iter().map(|&x| x as f64).collect()
3494 } else {
3495 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3496 };
3497 if frame.positions.nrows() != n {
3498 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3499 }
3500 for i in 0..n {
3501 frame.positions.set_f64_row(
3502 i,
3503 [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]],
3504 );
3505 }
3506 frame.sync_atom_data_from_arrays();
3507 RKRStatus::RKR_STATUS_SUCCESS
3508}
3509
3510#[unsafe(no_mangle)]
3513pub unsafe extern "C" fn rkr_frame_positions_dlpack(
3514 frame_handle: *const RKRConFrame,
3515 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3516) -> RKRStatus {
3517 unsafe {
3518 rkr_frame_positions_as_dlpack(frame_handle, rkr_dl_device_type::RKR_DL_CPU, 0, 0, 1, 0, out_tensor)
3519 }
3520}
3521
3522#[unsafe(no_mangle)]
3527pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
3528 frame_handle: *const RKRConFrame,
3529 opts: *const RKRDlpackExportOptions,
3530 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3531) -> RKRStatus {
3532 if frame_handle.is_null() || out_tensor.is_null() {
3533 return RKRStatus::RKR_STATUS_NULL_POINTER;
3534 }
3535 unsafe { *out_tensor = std::ptr::null_mut() };
3536 let o = match resolve_dlpack_opts(opts) {
3537 Ok(o) => o,
3538 Err(st) => return st,
3539 };
3540 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3541 return RKRStatus::RKR_STATUS_NULL_POINTER;
3542 };
3543 let arr = frame_positions_arc(frame);
3544 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3545}
3546
3547#[unsafe(no_mangle)]
3549pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
3550 frame_handle: *const RKRConFrame,
3551 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3552) -> RKRStatus {
3553 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3554}
3555
3556#[unsafe(no_mangle)]
3557pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
3558 frame_handle: *const RKRConFrame,
3559 opts: *const RKRDlpackExportOptions,
3560 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3561) -> RKRStatus {
3562 if frame_handle.is_null() || out_tensor.is_null() {
3563 return RKRStatus::RKR_STATUS_NULL_POINTER;
3564 }
3565 unsafe { *out_tensor = std::ptr::null_mut() };
3566 let o = match resolve_dlpack_opts(opts) {
3567 Ok(o) => o,
3568 Err(st) => return st,
3569 };
3570 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3571 return RKRStatus::RKR_STATUS_NULL_POINTER;
3572 };
3573 if !frame.has_velocities() {
3574 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3575 }
3576 let n = frame.atom_data.len();
3577 let mut data = Vec::with_capacity(n * 3);
3578 for a in &frame.atom_data {
3579 let v = a.velocity.unwrap_or([0.0; 3]);
3580 data.extend_from_slice(&v);
3581 }
3582 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
3583 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
3584 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3585}
3586
3587#[unsafe(no_mangle)]
3589pub unsafe extern "C" fn rkr_frame_forces_dlpack(
3590 frame_handle: *const RKRConFrame,
3591 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3592) -> RKRStatus {
3593 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3594}
3595
3596#[unsafe(no_mangle)]
3597pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
3598 frame_handle: *const RKRConFrame,
3599 opts: *const RKRDlpackExportOptions,
3600 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3601) -> RKRStatus {
3602 if frame_handle.is_null() || out_tensor.is_null() {
3603 return RKRStatus::RKR_STATUS_NULL_POINTER;
3604 }
3605 unsafe { *out_tensor = std::ptr::null_mut() };
3606 let o = match resolve_dlpack_opts(opts) {
3607 Ok(o) => o,
3608 Err(st) => return st,
3609 };
3610 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3611 return RKRStatus::RKR_STATUS_NULL_POINTER;
3612 };
3613 if !frame.has_forces() {
3614 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3615 }
3616 let n = frame.atom_data.len();
3617 let mut data = Vec::with_capacity(n * 3);
3618 for a in &frame.atom_data {
3619 let f = a.force.unwrap_or([0.0; 3]);
3620 data.extend_from_slice(&f);
3621 }
3622 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
3623 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
3624 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3625}
3626
3627#[unsafe(no_mangle)]
3629pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
3630 frame_handle: *const RKRConFrame,
3631 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3632) -> RKRStatus {
3633 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3634}
3635
3636#[unsafe(no_mangle)]
3637pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
3638 frame_handle: *const RKRConFrame,
3639 opts: *const RKRDlpackExportOptions,
3640 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3641) -> RKRStatus {
3642 if frame_handle.is_null() || out_tensor.is_null() {
3643 return RKRStatus::RKR_STATUS_NULL_POINTER;
3644 }
3645 unsafe { *out_tensor = std::ptr::null_mut() };
3646 let o = match resolve_dlpack_opts(opts) {
3647 Ok(o) => o,
3648 Err(st) => return st,
3649 };
3650 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3651 return RKRStatus::RKR_STATUS_NULL_POINTER;
3652 };
3653 if !frame.has_energies() {
3654 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3655 }
3656 let data: Vec<f64> = frame
3657 .atom_data
3658 .iter()
3659 .map(|a| a.energy.unwrap_or(0.0))
3660 .collect();
3661 let arr = ndarray::ArcArray1::from_vec(data);
3662 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
3663}
3664
3665pub struct RKRSelectionResult;
3669#[unsafe(no_mangle)]
3680pub unsafe extern "C" fn rkr_frame_select(
3681 frame_handle: *const RKRConFrame,
3682 selection: *const c_char,
3683 out_result: *mut *mut RKRSelectionResult,
3684) -> RKRStatus {
3685 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
3686 return RKRStatus::RKR_STATUS_NULL_POINTER;
3687 }
3688 let frame = unsafe { &*(frame_handle as *const ConFrame) };
3689 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
3690 Ok(s) => s,
3691 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3692 };
3693 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
3694 Ok(result) => {
3695 let boxed = Box::new(result);
3696 unsafe {
3697 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
3698 }
3699 RKRStatus::RKR_STATUS_SUCCESS
3700 }
3701 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
3702 }
3703}
3704#[unsafe(no_mangle)]
3709pub unsafe extern "C" fn rkr_selection_result_match_count(
3710 result_handle: *const RKRSelectionResult,
3711) -> u64 {
3712 if result_handle.is_null() {
3713 return 0;
3714 }
3715 let result =
3716 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
3717 result.matches.len() as u64
3718}
3719#[unsafe(no_mangle)]
3724pub unsafe extern "C" fn rkr_selection_result_context_size(
3725 result_handle: *const RKRSelectionResult,
3726) -> u32 {
3727 if result_handle.is_null() {
3728 return 0;
3729 }
3730 let result =
3731 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
3732 result.context_size as u32
3733}
3734#[unsafe(no_mangle)]
3740pub unsafe extern "C" fn rkr_selection_result_match_at(
3741 result_handle: *const RKRSelectionResult,
3742 match_index: u64,
3743 out_atoms: *mut u64,
3744 out_size: *mut u32,
3745) -> RKRStatus {
3746 if result_handle.is_null() || out_atoms.is_null() {
3747 return RKRStatus::RKR_STATUS_NULL_POINTER;
3748 }
3749 let result =
3750 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
3751 let idx = match_index as usize;
3752 if idx >= result.matches.len() {
3753 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
3754 }
3755 let m = &result.matches[idx];
3756 unsafe {
3757 for i in 0..4 {
3758 *out_atoms.add(i) = if i < m.size {
3759 m.atoms[i] as u64
3760 } else {
3761 u64::MAX
3762 };
3763 }
3764 if !out_size.is_null() {
3765 *out_size = m.size as u32;
3766 }
3767 }
3768 RKRStatus::RKR_STATUS_SUCCESS
3769}
3770#[unsafe(no_mangle)]
3776pub unsafe extern "C" fn rkr_selection_result_primary_indices(
3777 result_handle: *const RKRSelectionResult,
3778 out_indices: *mut u64,
3779 capacity: u64,
3780 out_written: *mut u64,
3781) -> RKRStatus {
3782 if result_handle.is_null() {
3783 return RKRStatus::RKR_STATUS_NULL_POINTER;
3784 }
3785 let result =
3786 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
3787 let n = result.matches.len() as u64;
3788 if !out_written.is_null() {
3789 unsafe {
3790 *out_written = n;
3791 }
3792 }
3793 if n == 0 {
3794 return RKRStatus::RKR_STATUS_SUCCESS;
3795 }
3796 if out_indices.is_null() {
3797 return RKRStatus::RKR_STATUS_NULL_POINTER;
3798 }
3799 if capacity < n {
3800 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3801 }
3802 unsafe {
3803 for (i, m) in result.matches.iter().enumerate() {
3804 *out_indices.add(i) = m.atoms[0] as u64;
3805 }
3806 }
3807 RKRStatus::RKR_STATUS_SUCCESS
3808}
3809#[unsafe(no_mangle)]
3814pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
3815 if result_handle.is_null() {
3816 return;
3817 }
3818 unsafe {
3819 drop(Box::from_raw(
3820 result_handle as *mut crate::chemfiles_selection::SelectionResult,
3821 ));
3822 }
3823}
3824#[unsafe(no_mangle)]
3826pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
3827 #[cfg(feature = "chemfiles")]
3828 {
3829 1
3830 }
3831 #[cfg(not(feature = "chemfiles"))]
3832 {
3833 0
3834 }
3835}
3836#[unsafe(no_mangle)]
3842pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
3843 if path_c.is_null() {
3844 return std::ptr::null_mut();
3845 }
3846 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
3847 return std::ptr::null_mut();
3848 };
3849 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
3850 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3851 Err(_) => std::ptr::null_mut(),
3852 }
3853}
3854#[unsafe(no_mangle)]
3861pub unsafe extern "C" fn rkr_read_chemfiles_memory(
3862 data_c: *const c_char,
3863 format_c: *const c_char,
3864 num_frames: *mut usize,
3865) -> *mut *mut RKRConFrame {
3866 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
3867 return std::ptr::null_mut();
3868 }
3869 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
3870 return std::ptr::null_mut();
3871 };
3872 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
3873 return std::ptr::null_mut();
3874 };
3875 match crate::chemfiles_import::con_frames_from_memory(data, format) {
3876 Ok(frames) => {
3877 let n = frames.len();
3878 unsafe { *num_frames = n };
3879 let mut ptrs: Vec<*mut RKRConFrame> = frames
3880 .into_iter()
3881 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3882 .collect();
3883 let p = ptrs.as_mut_ptr();
3884 std::mem::forget(ptrs);
3885 p
3886 }
3887 Err(_) => std::ptr::null_mut(),
3888 }
3889}
3890#[unsafe(no_mangle)]
3895pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
3896 if tensor.is_null() {
3897 return;
3898 }
3899 unsafe {
3900 let t = &mut *tensor;
3901 if let Some(del) = t.deleter {
3902 del(tensor);
3903 }
3904 }
3905}
3906#[cfg(test)]
3907mod tests {
3908 use super::*;
3909 use std::ffi::{CStr, CString};
3910 #[test]
3911 fn frame_copy_positions_without_cframe() {
3912 let handle = test_frame_handle();
3913 let n = unsafe { rkr_frame_atom_count(handle) };
3914 assert_eq!(n, 1);
3915 let mut buf = vec![0.0f64; n * 3];
3916 assert_eq!(
3917 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
3918 RKRStatus::RKR_STATUS_SUCCESS
3919 );
3920 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
3921 assert_eq!(
3922 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
3923 RKRStatus::RKR_STATUS_SUCCESS
3924 );
3925 assert!(!tensor.is_null());
3926 unsafe { rkr_dlpack_delete(tensor) };
3927 assert_eq!(
3928 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
3929 RKRStatus::RKR_STATUS_SECTION_ABSENT
3930 );
3931 unsafe { free_rkr_frame(handle) };
3932 }
3933 #[test]
3934 fn read_all_frames_c_abi_tiny() {
3935 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
3936 let mut n: usize = 0;
3937 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
3938 assert!(!arr.is_null() && n >= 1);
3939 let first = unsafe { *arr };
3940 let nat = unsafe { rkr_frame_atom_count(first) };
3941 let mut buf = vec![0.0f64; nat * 3];
3942 assert_eq!(
3943 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
3944 RKRStatus::RKR_STATUS_SUCCESS
3945 );
3946 unsafe { free_rkr_frame_array(arr, n) };
3947 }
3948
3949 #[test]
3950 fn free_rkr_frame_ptr_array_keeps_frames() {
3951 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
3952 let mut n: usize = 0;
3953 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
3954 assert!(!arr.is_null() && n >= 1);
3955 let first = unsafe { *arr };
3956 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
3958 unsafe { free_rkr_frame_ptr_array(arr, n) };
3959 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
3960 unsafe { free_rkr_frame(first) };
3961 for h in rest {
3962 if !h.is_null() {
3963 unsafe { free_rkr_frame(h) };
3964 }
3965 }
3966 }
3967 fn test_frame_handle() -> *mut RKRConFrame {
3968 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
3969 builder
3970 .prebox_header("Generated by test")
3971 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
3972 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
3973 Box::into_raw(Box::new(builder.build())) as *mut RKRConFrame
3974 }
3975 #[test]
3976 fn header_line_rejects_null_buffer() {
3977 let frame = test_frame_handle();
3978 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
3979 unsafe { free_rkr_frame(frame) };
3980 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
3981 }
3982 #[test]
3983 fn header_line_rejects_empty_buffer() {
3984 let frame = test_frame_handle();
3985 let mut buffer = [0 as c_char; 1];
3986 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
3987 unsafe { free_rkr_frame(frame) };
3988 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
3989 }
3990 #[test]
3991 fn header_line_truncates_and_terminates_buffer() {
3992 let frame = test_frame_handle();
3993 let mut buffer = [0 as c_char; 10];
3994 let status =
3995 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
3996 unsafe { free_rkr_frame(frame) };
3997 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
3998 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
3999 assert_eq!(copied.to_str().unwrap(), "Generated");
4000 }
4001 fn test_builder_handle() -> *mut RKRConFrameBuilder {
4002 let cell = [10.0, 11.0, 12.0];
4003 let angles = [90.0, 91.0, 92.0];
4004 unsafe {
4005 rkr_frame_new(
4006 cell.as_ptr(),
4007 angles.as_ptr(),
4008 ptr::null(),
4009 ptr::null(),
4010 ptr::null(),
4011 ptr::null(),
4012 )
4013 }
4014 }
4015 fn c_string(s: &str) -> CString {
4016 CString::new(s).unwrap()
4017 }
4018 unsafe fn assert_single_atom(
4019 frame: *mut RKRConFrame,
4020 fixed: [bool; 3],
4021 velocity: Option<[f64; 3]>,
4022 forces: Option<[f64; 3]>,
4023 ) {
4024 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
4025 assert!(!c_frame.is_null());
4026 let c_frame_ref = unsafe { &*c_frame };
4027 assert_eq!(c_frame_ref.num_atoms, 1);
4028 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
4029 assert_eq!(c_frame_ref.has_forces, forces.is_some());
4030 let atom = unsafe { &*c_frame_ref.atoms };
4031 assert_eq!(atom.fixed_x, fixed[0]);
4032 assert_eq!(atom.fixed_y, fixed[1]);
4033 assert_eq!(atom.fixed_z, fixed[2]);
4034 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
4035 assert_eq!(atom.has_velocity, velocity.is_some());
4036 assert_eq!(atom.has_forces, forces.is_some());
4037 if let Some([vx, vy, vz]) = velocity {
4038 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
4039 }
4040 if let Some([fx, fy, fz]) = forces {
4041 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
4042 }
4043 unsafe { free_c_frame(c_frame) };
4044 unsafe { free_rkr_frame(frame) };
4045 }
4046 #[test]
4047 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
4048 let builder = test_builder_handle();
4049 let symbol = c_string("Cu");
4050 let status = unsafe {
4051 rkr_frame_add_atom_with_fixed_mask(
4052 builder,
4053 symbol.as_ptr(),
4054 1.0,
4055 2.0,
4056 3.0,
4057 true,
4058 false,
4059 true,
4060 7,
4061 63.546,
4062 )
4063 };
4064 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4065 let frame = unsafe { rkr_frame_builder_build(builder) };
4066 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
4067 }
4068 #[test]
4069 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
4070 let builder = test_builder_handle();
4071 let symbol = c_string("H");
4072 let status = unsafe {
4073 rkr_frame_add_atom_with_velocity_fixed_mask(
4074 builder,
4075 symbol.as_ptr(),
4076 1.0,
4077 2.0,
4078 3.0,
4079 false,
4080 true,
4081 false,
4082 9,
4083 1.008,
4084 0.1,
4085 0.2,
4086 0.3,
4087 )
4088 };
4089 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4090 let frame = unsafe { rkr_frame_builder_build(builder) };
4091 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
4092 }
4093 #[test]
4094 fn builder_preserves_fixed_mask_for_atom_with_forces() {
4095 let builder = test_builder_handle();
4096 let symbol = c_string("O");
4097 let status = unsafe {
4098 rkr_frame_add_atom_with_forces_fixed_mask(
4099 builder,
4100 symbol.as_ptr(),
4101 1.0,
4102 2.0,
4103 3.0,
4104 true,
4105 true,
4106 false,
4107 11,
4108 15.999,
4109 -0.1,
4110 -0.2,
4111 -0.3,
4112 )
4113 };
4114 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4115 let frame = unsafe { rkr_frame_builder_build(builder) };
4116 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
4117 }
4118 #[test]
4119 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
4120 let builder = test_builder_handle();
4121 let symbol = c_string("N");
4122 let status = unsafe {
4123 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4124 builder,
4125 symbol.as_ptr(),
4126 1.0,
4127 2.0,
4128 3.0,
4129 false,
4130 true,
4131 true,
4132 13,
4133 14.007,
4134 0.4,
4135 0.5,
4136 0.6,
4137 -0.4,
4138 -0.5,
4139 -0.6,
4140 )
4141 };
4142 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4143 let frame = unsafe { rkr_frame_builder_build(builder) };
4144 unsafe {
4145 assert_single_atom(
4146 frame,
4147 [false, true, true],
4148 Some([0.4, 0.5, 0.6]),
4149 Some([-0.4, -0.5, -0.6]),
4150 )
4151 };
4152 }
4153 #[test]
4154 fn builder_bool_fixed_functions_set_all_axes_together() {
4155 let builder = test_builder_handle();
4156 let cu = c_string("Cu");
4157 let h = c_string("H");
4158 let atom_status =
4159 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
4160 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
4161 let velocity_status = unsafe {
4162 rkr_frame_add_atom_with_velocity(
4163 builder,
4164 h.as_ptr(),
4165 4.0,
4166 5.0,
4167 6.0,
4168 false,
4169 2,
4170 1.008,
4171 0.7,
4172 0.8,
4173 0.9,
4174 )
4175 };
4176 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
4177 let frame = unsafe { rkr_frame_builder_build(builder) };
4178 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
4179 assert!(!c_frame.is_null());
4180 let c_frame_ref = unsafe { &*c_frame };
4181 assert_eq!(c_frame_ref.num_atoms, 2);
4182 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
4183 assert_eq!(
4184 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
4185 [true, true, true]
4186 );
4187 assert_eq!(
4188 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
4189 [false, false, false]
4190 );
4191 unsafe { free_c_frame(c_frame) };
4192 unsafe { free_rkr_frame(frame) };
4193 }
4194 #[test]
4195 fn status_message_returns_static_strings_for_all_status_values() {
4196 let cases = [
4197 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
4198 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
4199 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
4200 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
4201 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
4202 (
4203 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
4204 "index out of bounds",
4205 ),
4206 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
4207 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
4208 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
4209 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
4210 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
4211 (
4212 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
4213 "feature disabled in this build",
4214 ),
4215 ];
4216 for (status, expected) in cases {
4217 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
4218 assert_eq!(message.to_str().unwrap(), expected);
4219 }
4220 }
4221 unsafe fn assert_dlpack_cpu_float(
4225 t: *mut RKRDLManagedTensorVersioned,
4226 expect_ndim: i32,
4227 expect_shape: &[i64],
4228 expect_bits: u8,
4229 ) {
4230 assert!(!t.is_null());
4231 let dl = unsafe { &(*t).dl_tensor };
4232 assert_eq!(dl.ndim, expect_ndim);
4233 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
4234 assert_eq!(shape, expect_shape);
4235 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
4236 assert_eq!(dl.dtype.bits, expect_bits);
4237 assert_eq!(dl.dtype.lanes, 1);
4238 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
4239 assert!(!dl.data.is_null());
4240 }
4241
4242 #[test]
4243 fn frame_optional_section_dlpack_present_and_absent() {
4244 let handle = test_frame_handle();
4246 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4247 assert_eq!(
4248 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
4249 RKRStatus::RKR_STATUS_SECTION_ABSENT
4250 );
4251 assert!(t.is_null());
4252 assert_eq!(
4253 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
4254 RKRStatus::RKR_STATUS_SECTION_ABSENT
4255 );
4256 assert_eq!(
4257 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
4258 RKRStatus::RKR_STATUS_SECTION_ABSENT
4259 );
4260 unsafe { free_rkr_frame(handle) };
4261
4262 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
4264 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
4265 assert!(!fr.is_null());
4266 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
4267 assert!(n > 0);
4268 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4269 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
4270 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4271 unsafe {
4272 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
4273 rkr_dlpack_delete(vel);
4274 free_rkr_frame(fr);
4275 }
4276
4277 let cell = [10.0f64; 3];
4279 let ang = [90.0f64; 3];
4280 let b = unsafe {
4281 rkr_frame_new(
4282 cell.as_ptr(),
4283 ang.as_ptr(),
4284 std::ptr::null(),
4285 std::ptr::null(),
4286 std::ptr::null(),
4287 std::ptr::null(),
4288 )
4289 };
4290 assert!(!b.is_null());
4291 let sym = CString::new("H").unwrap();
4292 unsafe {
4293 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4294 b,
4295 sym.as_ptr(),
4296 0.0,
4297 0.0,
4298 0.0,
4299 false,
4300 false,
4301 false,
4302 0,
4303 1.0,
4304 0.1,
4305 0.0,
4306 0.0,
4307 0.0,
4308 0.0,
4309 -1.0,
4310 );
4311 rkr_frame_builder_set_last_energy(b, -0.5);
4312 }
4313 let built = unsafe { rkr_frame_builder_build(b) };
4314 assert!(!built.is_null());
4315 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
4316 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4317 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4318 assert_eq!(
4319 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
4320 RKRStatus::RKR_STATUS_SUCCESS
4321 );
4322 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
4323 assert_eq!(
4324 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
4325 RKRStatus::RKR_STATUS_SUCCESS
4326 );
4327 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
4328 let opts32 = RKRDlpackExportOptions {
4330 dtype: RKRDLDataType {
4331 code: rkr_dl_type_code::RKR_DL_FLOAT,
4332 bits: 32,
4333 lanes: 1,
4334 },
4335 device: RKRDLDevice {
4336 device_type: rkr_dl_device_type::RKR_DL_CPU,
4337 device_id: 0,
4338 },
4339 };
4340 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4341 assert_eq!(
4342 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
4343 RKRStatus::RKR_STATUS_SUCCESS
4344 );
4345 unsafe {
4346 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
4347 rkr_dlpack_delete(pos32);
4348 }
4349 let cuda_dev = RKRDlpackExportOptions {
4352 dtype: RKRDLDataType {
4353 code: rkr_dl_type_code::RKR_DL_FLOAT,
4354 bits: 64,
4355 lanes: 1,
4356 },
4357 device: RKRDLDevice {
4358 device_type: rkr_dl_device_type::RKR_DL_CUDA,
4359 device_id: 0,
4360 },
4361 };
4362 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4363 #[cfg(not(feature = "cuda"))]
4364 assert_eq!(
4365 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
4366 RKRStatus::RKR_STATUS_FEATURE_DISABLED
4367 );
4368 #[cfg(feature = "cuda")]
4369 {
4370 assert_eq!(
4371 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
4372 RKRStatus::RKR_STATUS_SUCCESS
4373 );
4374 assert!(!junk.is_null());
4375 let dl = unsafe { &(*junk).dl_tensor };
4376 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
4377 assert!(!dl.data.is_null());
4378 unsafe { rkr_dlpack_delete(junk) };
4379 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4381 assert_eq!(
4382 unsafe {
4383 rkr_frame_positions_as_dlpack(
4384 built,
4385 rkr_dl_device_type::RKR_DL_CUDA,
4386 0,
4387 0,
4388 1,
4389 0,
4390 &mut as_cuda,
4391 )
4392 },
4393 RKRStatus::RKR_STATUS_SUCCESS
4394 );
4395 assert!(!as_cuda.is_null());
4396 unsafe {
4397 assert_eq!(
4398 (*as_cuda).dl_tensor.device.device_type,
4399 dlpk::sys::DLDeviceType::kDLCUDA
4400 );
4401 rkr_dlpack_delete(as_cuda);
4402 }
4403 }
4404 let bad_bits = RKRDlpackExportOptions {
4406 dtype: RKRDLDataType {
4407 code: rkr_dl_type_code::RKR_DL_FLOAT,
4408 bits: 16,
4409 lanes: 1,
4410 },
4411 device: RKRDLDevice {
4412 device_type: rkr_dl_device_type::RKR_DL_CPU,
4413 device_id: 0,
4414 },
4415 };
4416 assert_eq!(
4417 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
4418 RKRStatus::RKR_STATUS_VALIDATION_ERROR
4419 );
4420 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4422 assert_eq!(
4423 unsafe {
4424 rkr_frame_positions_as_dlpack(
4425 built,
4426 rkr_dl_device_type::RKR_DL_CPU,
4427 0,
4428 0,
4429 1,
4430 0,
4431 &mut as_t,
4432 )
4433 },
4434 RKRStatus::RKR_STATUS_SUCCESS
4435 );
4436 unsafe {
4437 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
4438 }
4439 {
4441 let fr = unsafe { &mut *(built as *mut ConFrame) };
4442 for a in fr.atom_data.iter_mut() {
4443 a.x = -99.0;
4444 a.y = -99.0;
4445 a.z = -99.0;
4446 }
4447 }
4448 assert_eq!(
4449 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
4450 RKRStatus::RKR_STATUS_SUCCESS
4451 );
4452 {
4453 let fr = unsafe { &*(built as *const ConFrame) };
4454 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
4455 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
4456 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
4457 }
4458 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4460 assert_eq!(
4461 unsafe {
4462 rkr_frame_positions_as_dlpack(
4463 built,
4464 rkr_dl_device_type::RKR_DL_CPU,
4465 0,
4466 0,
4467 1,
4468 0,
4469 &mut again,
4470 )
4471 },
4472 RKRStatus::RKR_STATUS_SUCCESS
4473 );
4474 unsafe {
4475 let dl = &(*again).dl_tensor;
4476 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
4477 assert!((data[0] - 0.0).abs() < 1e-12);
4478 assert!((data[1] - 0.0).abs() < 1e-12);
4479 assert!((data[2] - 0.0).abs() < 1e-12);
4480 rkr_dlpack_delete(again);
4481 rkr_dlpack_delete(as_t);
4482 rkr_dlpack_delete(frc);
4483 rkr_dlpack_delete(eng);
4484 free_rkr_frame(built);
4485 }
4486 }
4487
4488 #[test]
4489 fn ffi_positions_dlpack_round_trip() {
4490 let handle = test_builder_handle();
4491 let sym = c_string("Cu");
4492 unsafe {
4493 rkr_frame_add_atom_full(
4494 handle,
4495 sym.as_ptr(),
4496 1.0,
4497 2.0,
4498 3.0,
4499 false,
4500 false,
4501 false,
4502 7,
4503 63.5,
4504 ptr::null(),
4505 ptr::null(),
4506 )
4507 };
4508 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
4509 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
4510 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4511 assert!(!t.is_null());
4512 let dl = unsafe { &(*t).dl_tensor };
4514 assert_eq!(dl.ndim, 2);
4515 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
4516 assert_eq!(shape, &[1, 3]);
4517 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
4518 assert_eq!(dl.dtype.bits, 64);
4519 assert_eq!(dl.dtype.lanes, 1);
4520 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
4521 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
4522 assert_eq!(data, &[1.0, 2.0, 3.0]);
4523 let deleter = unsafe { (*t).deleter };
4525 if let Some(del) = deleter {
4526 unsafe { del(t) };
4527 }
4528 unsafe { free_rkr_frame_builder(handle) };
4529 }
4530 #[test]
4531 fn ffi_velocities_dlpack_section_absent() {
4532 let handle = test_builder_handle();
4533 let sym = c_string("Cu");
4534 unsafe {
4535 rkr_frame_add_atom_full(
4536 handle,
4537 sym.as_ptr(),
4538 0.0,
4539 0.0,
4540 0.0,
4541 false,
4542 false,
4543 false,
4544 0,
4545 63.5,
4546 ptr::null(),
4547 ptr::null(),
4548 )
4549 };
4550 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
4551 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
4552 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
4553 assert!(t.is_null());
4554 unsafe { free_rkr_frame_builder(handle) };
4555 }
4556 #[test]
4557 fn ffi_dlpack_null_handle_rejects() {
4558 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
4559 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
4560 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
4561 assert!(t.is_null());
4562 }
4563 #[cfg(feature = "chemfiles")]
4564 #[test]
4565 fn rkr_frame_select_finds_oxygen() {
4566 use crate::types::ConFrameBuilder;
4567 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
4568 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
4569 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
4570 let frame = b.build();
4571 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
4572 let sel = CString::new("name O").unwrap();
4573 let mut out: *mut RKRSelectionResult = ptr::null_mut();
4574 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
4575 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4576 assert!(!out.is_null());
4577 let n = unsafe { rkr_selection_result_match_count(out) };
4578 assert_eq!(n, 1);
4579 let mut atoms = [u64::MAX; 4];
4580 let mut size = 0u32;
4581 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
4582 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
4583 assert_eq!(size, 1);
4584 assert_eq!(atoms[0], 0);
4585 unsafe {
4586 rkr_selection_result_free(out);
4587 free_rkr_frame(frame_ptr);
4588 }
4589 }
4590 #[cfg(feature = "chemfiles")]
4592 #[test]
4593 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
4594 use crate::types::{Bond, ConFrameBuilder};
4595 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
4597 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
4598 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
4599 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
4600 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
4601 let mut frame = b.build();
4602 let id_to = |id: u64| {
4603 frame
4604 .atom_data
4605 .iter()
4606 .position(|a| a.atom_id == id)
4607 .unwrap() as u32
4608 };
4609 frame.header.set_bonds(&[
4610 Bond::new(id_to(0), id_to(1)),
4611 Bond::new(id_to(1), id_to(2)),
4612 Bond::new(id_to(2), id_to(3)),
4613 ]);
4614 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
4615 let run = |sel: &str| -> (u64, u32) {
4616 let csel = CString::new(sel).unwrap();
4617 let mut out: *mut RKRSelectionResult = ptr::null_mut();
4618 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
4619 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
4620 let n = unsafe { rkr_selection_result_match_count(out) };
4621 let ctx = unsafe { rkr_selection_result_context_size(out) };
4622 unsafe { rkr_selection_result_free(out) };
4623 (n, ctx)
4624 };
4625 assert_eq!(run("bonds: all"), (3, 2));
4626 assert_eq!(run("angles: all"), (2, 3));
4627 assert_eq!(run("dihedrals: all"), (1, 4));
4628 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
4629 assert_eq!(
4630 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
4631 run("bonds: type(#1) H and name(#2) O").0
4632 );
4633 unsafe { free_rkr_frame(frame_ptr) };
4634 }
4635 #[cfg(feature = "metatensor")]
4636 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
4637 assert!(!block.is_null());
4638 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
4639 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
4640 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
4641 let shape_fn = array.shape.expect("mts_array_t.shape from metatensor C API");
4642 let mut shape_ptr: *const usize = std::ptr::null();
4643 let mut shape_count: usize = 0;
4644 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
4645 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
4646 assert_eq!(shape_count, 2);
4647 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
4648 assert_eq!(shape[0], n);
4649 assert_eq!(shape[1], props);
4650 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
4651 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
4652 assert!(!samples.is_null() && !prop_lab.is_null());
4653 }
4654 #[cfg(feature = "metatensor")]
4655 #[test]
4656 fn metatensor_positions_via_c_abi() {
4657 let handle = test_frame_handle();
4658 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4659 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
4660 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4661 assert_mts_block_shape(out, 1, 3);
4662 unsafe { rkr_mts_block_free(out) };
4663 for (name, export) in [
4664 (
4665 "velocities",
4666 rkr_frame_metatensor_velocities_block
4667 as unsafe extern "C" fn(
4668 *const RKRConFrame,
4669 *mut *mut metatensor::c_api::mts_block_t,
4670 ) -> RKRStatus,
4671 ),
4672 (
4673 "forces",
4674 rkr_frame_metatensor_forces_block
4675 as unsafe extern "C" fn(
4676 *const RKRConFrame,
4677 *mut *mut metatensor::c_api::mts_block_t,
4678 ) -> RKRStatus,
4679 ),
4680 (
4681 "atom_energies",
4682 rkr_frame_metatensor_atom_energies_block
4683 as unsafe extern "C" fn(
4684 *const RKRConFrame,
4685 *mut *mut metatensor::c_api::mts_block_t,
4686 ) -> RKRStatus,
4687 ),
4688 ] {
4689 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4690 let st_abs = unsafe { export(handle, &mut o) };
4691 assert_eq!(
4692 st_abs,
4693 RKRStatus::RKR_STATUS_SECTION_ABSENT,
4694 "{name} must be SECTION_ABSENT on minimal test frame"
4695 );
4696 assert!(o.is_null());
4697 }
4698 unsafe { free_rkr_frame(handle) };
4699 }
4700
4701 #[cfg(feature = "metatensor")]
4702 #[test]
4703 fn metatensor_optional_sections_via_c_abi() {
4704 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
4706 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
4707 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
4708 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
4709 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
4710 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
4711 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
4712 builder.set_atom_energy(0, -0.5).unwrap();
4713 builder.set_atom_energy(1, -1.0).unwrap();
4714 let frame = builder.build();
4715 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
4716 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4717 assert_eq!(
4718 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
4719 RKRStatus::RKR_STATUS_SUCCESS
4720 );
4721 assert_mts_block_shape(pos, 2, 3);
4722 unsafe { rkr_mts_block_free(pos) };
4723 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4724 assert_eq!(
4725 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
4726 RKRStatus::RKR_STATUS_SUCCESS
4727 );
4728 assert_mts_block_shape(vel, 2, 3);
4729 unsafe { rkr_mts_block_free(vel) };
4730 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4731 assert_eq!(
4732 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
4733 RKRStatus::RKR_STATUS_SUCCESS
4734 );
4735 assert_mts_block_shape(frc, 2, 3);
4736 unsafe { rkr_mts_block_free(frc) };
4737 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
4738 assert_eq!(
4739 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
4740 RKRStatus::RKR_STATUS_SUCCESS
4741 );
4742 assert_mts_block_shape(eng, 2, 1);
4743 unsafe { rkr_mts_block_free(eng) };
4744 unsafe { free_rkr_frame(handle) };
4745 }
4746
4747 #[test]
4748 fn string_iterator_yields_frames_from_buffer() {
4749 let text = std::fs::read_to_string("resources/test/tiny_cuh2.con")
4750 .expect("fixture tiny_cuh2.con");
4751 let c_text = CString::new(text.as_str()).unwrap();
4752 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
4753 assert!(!it.is_null());
4754 let mut n = 0usize;
4755 loop {
4756 let fr = unsafe { con_frame_iterator_next(it) };
4757 if fr.is_null() {
4758 break;
4759 }
4760 n += 1;
4761 unsafe { free_rkr_frame(fr) };
4762 }
4763 unsafe { free_con_frame_iterator(it) };
4764 assert!(n >= 1, "string iterator should yield >=1 frame");
4765
4766 let bytes = text.as_bytes();
4767 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
4768 assert!(!it2.is_null());
4769 let fr2 = unsafe { con_frame_iterator_next(it2) };
4770 assert!(!fr2.is_null());
4771 unsafe {
4772 free_rkr_frame(fr2);
4773 free_con_frame_iterator(it2);
4774 }
4775 }
4776
4777 #[test]
4778 fn file_iterator_reads_gzip_when_present() {
4779 use flate2::write::GzEncoder;
4780 use flate2::Compression;
4781 use std::io::Write;
4782 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
4783 let dir = tempfile::tempdir().expect("tempdir");
4784 let gz_path = dir.path().join("tiny_cuh2.con.gz");
4785 {
4786 let f = std::fs::File::create(&gz_path).unwrap();
4787 let mut enc = GzEncoder::new(f, Compression::default());
4788 enc.write_all(&plain).unwrap();
4789 enc.finish().unwrap();
4790 }
4791 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
4792 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
4793 assert!(
4794 !it.is_null(),
4795 "path iterator must decompress .con.gz transparently"
4796 );
4797 let fr = unsafe { con_frame_iterator_next(it) };
4798 assert!(!fr.is_null());
4799 let n = unsafe { rkr_frame_atom_count(fr) };
4800 assert!(n > 0);
4801 unsafe {
4802 free_rkr_frame(fr);
4803 free_con_frame_iterator(it);
4804 }
4805 }
4806}