1use crate::helpers::symbol_to_atomic_number;
2use crate::iterators::{self, ConFrameIterator};
3use crate::types::{ConFrame, ConFrameBuilder, meta};
4use crate::writer::{ConFrameWriter, binding_rewrite_frame};
5use std::cell::RefCell;
6use std::ffi::{CStr, CString, c_char};
7use std::fs::File;
8use std::path::Path;
9use std::ptr;
10pub const RKR_ABI_VERSION_MAJOR: u32 = 1;
15pub const RKR_ABI_VERSION_MINOR: u32 = 1;
17pub const RKR_ABI_LAYOUT_REVISION: u32 = 1;
19
20#[unsafe(no_mangle)]
22pub extern "C" fn rkr_abi_version_major() -> u32 {
23 RKR_ABI_VERSION_MAJOR
24}
25
26#[unsafe(no_mangle)]
28pub extern "C" fn rkr_abi_version_minor() -> u32 {
29 RKR_ABI_VERSION_MINOR
30}
31
32#[unsafe(no_mangle)]
34pub extern "C" fn rkr_abi_layout_revision() -> u32 {
35 RKR_ABI_LAYOUT_REVISION
36}
37
38#[unsafe(no_mangle)]
40pub extern "C" fn rkr_abi_stamp() -> *const c_char {
41 const STAMP: &[u8] = b"readcon-core/abi-1.1/layout-1\0";
42 STAMP.as_ptr() as *const c_char
43}
44
45pub const RKR_CON_SPEC_VERSION: u32 = 3;
53#[unsafe(no_mangle)]
55pub extern "C" fn rkr_con_spec_version() -> u32 {
56 crate::CON_SPEC_VERSION
57}
58#[unsafe(no_mangle)]
61pub extern "C" fn rkr_library_version() -> *const c_char {
62 const VERSION_NUL: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
64 VERSION_NUL.as_ptr() as *const c_char
65}
66#[unsafe(no_mangle)]
78pub unsafe extern "C" fn rkr_frame_atom_index_by_id(
79 frame_handle: *const RKRConFrame,
80 atom_id: u64,
81) -> u64 {
82 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
83 Some(f) => f,
84 None => return u64::MAX,
85 };
86 match frame.atom_index_by_id(atom_id) {
87 Some(idx) => idx as u64,
88 None => u64::MAX,
89 }
90}
91#[unsafe(no_mangle)]
100pub unsafe extern "C" fn rkr_symbol_to_z(symbol: *const c_char) -> u64 {
101 if symbol.is_null() {
102 return 0;
103 }
104 match unsafe { CStr::from_ptr(symbol) }.to_str() {
105 Ok(s) => symbol_to_atomic_number(s),
106 Err(_) => 0,
107 }
108}
109#[unsafe(no_mangle)]
114pub extern "C" fn rkr_z_to_symbol(z: u64) -> *const c_char {
115 macro_rules! cstrs {
120 ($($lit:literal),* $(,)?) => {
121 [$(concat!($lit, "\0").as_bytes()),*]
122 };
123 }
124 const TABLE: [&[u8]; 93] = cstrs![
125 "X", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P",
126 "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
127 "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh",
128 "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
129 "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re",
130 "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
131 "Pa", "U",
132 ];
133 let idx = if (1..=92).contains(&z) { z as usize } else { 0 };
134 TABLE[idx].as_ptr() as *const c_char
135}
136#[unsafe(no_mangle)]
139pub extern "C" fn rkr_frame_spec_version(frame_handle: *const RKRConFrame) -> u32 {
140 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
141 Some(f) => f.header.spec_version,
142 None => 0,
143 }
144}
145#[unsafe(no_mangle)]
152pub unsafe extern "C" fn rkr_frame_metadata_json(frame_handle: *const RKRConFrame) -> *mut c_char {
153 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
154 Some(f) => f,
155 None => return ptr::null_mut(),
156 };
157 let mut obj = serde_json::Map::new();
158 obj.insert(
159 meta::CON_SPEC_VERSION.into(),
160 serde_json::Value::from(frame.header.spec_version),
161 );
162 for (k, v) in &frame.header.metadata {
163 obj.insert(k.clone(), v.clone());
164 }
165 let json_str = serde_json::Value::Object(obj).to_string();
166 match CString::new(json_str) {
167 Ok(cs) => cs.into_raw(),
168 Err(_) => ptr::null_mut(),
169 }
170}
171#[unsafe(no_mangle)]
173pub extern "C" fn rkr_frame_energy(frame_handle: *const RKRConFrame) -> f64 {
174 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
175 Some(f) => f.header.energy().unwrap_or(f64::NAN),
176 None => f64::NAN,
177 }
178}
179
180#[unsafe(no_mangle)]
183pub extern "C" fn rkr_frame_index_energy(frame_handle: *const RKRConFrame) -> f64 {
184 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
185 Some(f) => crate::index_proj::finite_energy(f).unwrap_or(f64::NAN),
186 None => f64::NAN,
187 }
188}
189
190#[unsafe(no_mangle)]
192pub unsafe extern "C" fn rkr_frame_composition_formula(
193 frame_handle: *const RKRConFrame,
194) -> *mut c_char {
195 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
196 Some(f) => f,
197 None => return ptr::null_mut(),
198 };
199 let s = crate::index_proj::frame_composition_formula(frame);
200 match CString::new(s) {
201 Ok(cs) => cs.into_raw(),
202 Err(_) => ptr::null_mut(),
203 }
204}
205
206#[unsafe(no_mangle)]
208pub extern "C" fn rkr_frame_total_mass(frame_handle: *const RKRConFrame) -> f64 {
209 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
210 Some(f) => crate::index_proj::frame_total_mass(f).unwrap_or(f64::NAN),
211 None => f64::NAN,
212 }
213}
214
215#[unsafe(no_mangle)]
217pub extern "C" fn rkr_frame_cell_volume(frame_handle: *const RKRConFrame) -> f64 {
218 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
219 Some(f) => crate::index_proj::frame_cell_volume(f).unwrap_or(f64::NAN),
220 None => f64::NAN,
221 }
222}
223
224#[unsafe(no_mangle)]
226pub extern "C" fn rkr_frame_fmax(frame_handle: *const RKRConFrame) -> f64 {
227 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
228 Some(f) => crate::index_proj::frame_fmax(f).unwrap_or(f64::NAN),
229 None => f64::NAN,
230 }
231}
232
233#[unsafe(no_mangle)]
235pub extern "C" fn rkr_frame_sections_mask(frame_handle: *const RKRConFrame) -> u8 {
236 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
237 Some(f) => crate::index_proj::sections_present_mask(f),
238 None => 0,
239 }
240}
241
242#[unsafe(no_mangle)]
244pub extern "C" fn rkr_frame_index_natoms(frame_handle: *const RKRConFrame) -> u32 {
245 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
246 Some(f) => f.atom_data.len() as u32,
247 None => 0,
248 }
249}
250
251#[unsafe(no_mangle)]
254pub unsafe extern "C" fn rkr_frame_index_projection_json(
255 frame_handle: *const RKRConFrame,
256) -> *mut c_char {
257 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
258 Some(f) => f,
259 None => return ptr::null_mut(),
260 };
261 let p = crate::index_proj::FrameIndexProjection::from_frame(frame);
262 let v = serde_json::json!({
263 "n_atoms": p.n_atoms,
264 "formula": p.formula,
265 "energy": p.energy,
266 "fmax": p.fmax,
267 "total_mass": p.total_mass,
268 "cell_volume": p.cell_volume,
269 "sections_mask": p.sections_mask,
270 "has_forces": p.has_forces,
271 "has_velocities": p.has_velocities,
272 "has_energy": p.has_energy,
273 "symbols": p.symbols,
274 "species_counts": p.species_counts.iter().map(|(s,c)| serde_json::json!([s, c])).collect::<Vec<_>>(),
275 "time": p.time,
276 "timestep": p.timestep,
277 "frame_index": p.frame_index,
278 "neb_bead": p.neb_bead,
279 "neb_band": p.neb_band,
280 "charge": p.charge,
281 "magmom": p.magmom,
282 });
283 match CString::new(v.to_string()) {
284 Ok(cs) => cs.into_raw(),
285 Err(_) => ptr::null_mut(),
286 }
287}
288#[unsafe(no_mangle)]
295pub unsafe extern "C" fn rkr_frame_potential_type(frame_handle: *const RKRConFrame) -> *mut c_char {
296 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
297 Some(f) => f,
298 None => return ptr::null_mut(),
299 };
300 match frame.header.potential_type() {
301 Some(pot_type) => match CString::new(pot_type) {
302 Ok(cs) => cs.into_raw(),
303 Err(_) => ptr::null_mut(),
304 },
305 None => ptr::null_mut(),
306 }
307}
308#[unsafe(no_mangle)]
310pub extern "C" fn rkr_frame_frame_index(frame_handle: *const RKRConFrame) -> u64 {
311 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
312 Some(f) => f.header.frame_index().unwrap_or(u64::MAX),
313 None => u64::MAX,
314 }
315}
316#[unsafe(no_mangle)]
318pub extern "C" fn rkr_frame_time(frame_handle: *const RKRConFrame) -> f64 {
319 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
320 Some(f) => f.header.time().unwrap_or(f64::NAN),
321 None => f64::NAN,
322 }
323}
324#[unsafe(no_mangle)]
326pub extern "C" fn rkr_frame_timestep(frame_handle: *const RKRConFrame) -> f64 {
327 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
328 Some(f) => f.header.timestep().unwrap_or(f64::NAN),
329 None => f64::NAN,
330 }
331}
332#[unsafe(no_mangle)]
334pub extern "C" fn rkr_frame_neb_bead(frame_handle: *const RKRConFrame) -> u64 {
335 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
336 Some(f) => f.header.neb_bead().unwrap_or(u64::MAX),
337 None => u64::MAX,
338 }
339}
340#[unsafe(no_mangle)]
342pub extern "C" fn rkr_frame_neb_band(frame_handle: *const RKRConFrame) -> u64 {
343 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
344 Some(f) => f.header.neb_band().unwrap_or(u64::MAX),
345 None => u64::MAX,
346 }
347}
348#[repr(C)]
353#[allow(non_camel_case_types)]
354#[derive(Debug, PartialEq, Eq)]
355pub enum RKRStatus {
356 RKR_STATUS_SUCCESS = 0,
358 RKR_STATUS_NULL_POINTER = -1,
360 RKR_STATUS_INVALID_UTF8 = -2,
362 RKR_STATUS_INVALID_JSON = -3,
364 RKR_STATUS_IO_ERROR = -4,
366 RKR_STATUS_INDEX_OUT_OF_BOUNDS = -5,
368 RKR_STATUS_BUFFER_TOO_SMALL = -6,
370 RKR_STATUS_INTERNAL_ERROR = -7,
372 RKR_STATUS_SECTION_ABSENT = -8,
375 RKR_STATUS_VALIDATION_ERROR = -9,
377 RKR_STATUS_SELECTION_ERROR = -10,
379 RKR_STATUS_FEATURE_DISABLED = -11,
382 RKR_STATUS_DEVICE_MISMATCH = -12,
384 RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED = -13,
386}
387#[unsafe(no_mangle)]
392pub unsafe extern "C" fn rkr_frame_bond_count(frame_handle: *const RKRConFrame) -> u64 {
393 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
394 Some(f) => f.bonds().len() as u64,
395 None => 0,
396 }
397}
398#[unsafe(no_mangle)]
407pub unsafe extern "C" fn rkr_frame_bond_at(
408 frame_handle: *const RKRConFrame,
409 index: u64,
410 out_i: *mut u32,
411 out_j: *mut u32,
412 out_has_order: *mut u8,
413 out_order: *mut i32,
414) -> RKRStatus {
415 if frame_handle.is_null()
416 || out_i.is_null()
417 || out_j.is_null()
418 || out_has_order.is_null()
419 || out_order.is_null()
420 {
421 return RKRStatus::RKR_STATUS_NULL_POINTER;
422 }
423 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
424 return RKRStatus::RKR_STATUS_NULL_POINTER;
425 };
426 let bonds = frame.bonds();
427 let Some(bond) = bonds.get(index as usize) else {
428 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
429 };
430 unsafe {
431 *out_i = bond.i;
432 *out_j = bond.j;
433 if let Some(order) = bond.order {
434 *out_has_order = 1;
435 *out_order = order;
436 } else {
437 *out_has_order = 0;
438 *out_order = 0;
439 }
440 }
441 RKRStatus::RKR_STATUS_SUCCESS
442}
443thread_local! {
444 static LAST_ERROR: RefCell<Option<CString>> = const { RefCell::new(None) };
445}
446
447fn set_last_error(message: &str) {
448 let stored = CString::new(message.replace('\0', ""))
449 .unwrap_or_else(|_| c"unrepresentable error".to_owned());
450 LAST_ERROR.with(|slot| *slot.borrow_mut() = Some(stored));
451}
452
453fn clear_last_error() {
454 LAST_ERROR.with(|slot| *slot.borrow_mut() = None);
455}
456
457#[unsafe(no_mangle)]
460pub extern "C" fn rkr_status_message(status: RKRStatus) -> *const c_char {
461 match status {
462 RKRStatus::RKR_STATUS_SUCCESS => c"success".as_ptr(),
463 RKRStatus::RKR_STATUS_NULL_POINTER => c"null pointer".as_ptr(),
464 RKRStatus::RKR_STATUS_INVALID_UTF8 => c"invalid UTF-8".as_ptr(),
465 RKRStatus::RKR_STATUS_INVALID_JSON => c"invalid JSON".as_ptr(),
466 RKRStatus::RKR_STATUS_IO_ERROR => c"I/O error".as_ptr(),
467 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS => c"index out of bounds".as_ptr(),
468 RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL => c"buffer too small".as_ptr(),
469 RKRStatus::RKR_STATUS_INTERNAL_ERROR => c"internal error".as_ptr(),
470 RKRStatus::RKR_STATUS_SECTION_ABSENT => c"section absent".as_ptr(),
471 RKRStatus::RKR_STATUS_VALIDATION_ERROR => c"validation error".as_ptr(),
472 RKRStatus::RKR_STATUS_SELECTION_ERROR => c"selection error".as_ptr(),
473 RKRStatus::RKR_STATUS_FEATURE_DISABLED => c"feature disabled in this build".as_ptr(),
474 RKRStatus::RKR_STATUS_DEVICE_MISMATCH => c"DLPack device mismatch".as_ptr(),
475 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED => {
476 c"device allocation unsupported in this build".as_ptr()
477 }
478 }
479}
480
481#[unsafe(no_mangle)]
492pub extern "C" fn rkr_last_error_message() -> *const c_char {
493 LAST_ERROR.with(|slot| match slot.borrow().as_ref() {
494 Some(message) => message.as_ptr(),
495 None => ptr::null(),
496 })
497}
498pub struct RKRConFrame {
503 _private: [u8; 0],
504}
505pub struct RKRConFrameWriter {
510 _private: [u8; 0],
511}
512#[repr(C)]
518pub struct RKRArrayView {
519 pub data: *const std::ffi::c_void,
520 pub n: usize,
521 pub cols: u32,
522 pub dtype_code: u8,
523 pub dtype_bits: u8,
524}
525
526impl RKRArrayView {
527 fn empty() -> Self {
528 Self {
529 data: std::ptr::null(),
530 n: 0,
531 cols: 0,
532 dtype_code: 0,
533 dtype_bits: 0,
534 }
535 }
536
537 fn from_array2(arr: &crate::storage_dtype::FloatArray2) -> Self {
538 let kind = arr.kind();
539 Self {
540 data: arr.data_ptr() as *const std::ffi::c_void,
541 n: arr.nrows(),
542 cols: arr.ncols() as u32,
543 dtype_code: kind.dlpack_code(),
544 dtype_bits: kind.dlpack_bits(),
545 }
546 }
547
548 fn from_array1(arr: &crate::storage_dtype::FloatArray1) -> Self {
549 let kind = arr.kind();
550 Self {
551 data: arr.data_ptr() as *const std::ffi::c_void,
552 n: arr.len(),
553 cols: 1,
554 dtype_code: kind.dlpack_code(),
555 dtype_bits: kind.dlpack_bits(),
556 }
557 }
558}
559
560#[repr(C)]
561pub struct CFrame {
562 pub atoms: *mut CAtom,
563 pub num_atoms: usize,
564 pub cell: [f64; 3],
565 pub angles: [f64; 3],
566 pub has_velocities: bool,
567 pub has_forces: bool,
568 pub has_energies: bool,
569}
570#[repr(C)]
580pub struct CAtom {
581 pub atomic_number: u64,
582 pub x: f64,
583 pub y: f64,
584 pub z: f64,
585 pub atom_id: u64,
586 pub mass: f64,
587 pub is_fixed: bool,
590 pub fixed_x: bool,
591 pub fixed_y: bool,
592 pub fixed_z: bool,
593 pub vx: f64,
594 pub vy: f64,
595 pub vz: f64,
596 pub has_velocity: bool,
597 pub fx: f64,
598 pub fy: f64,
599 pub fz: f64,
600 pub has_forces: bool,
601 pub energy: f64,
604 pub has_energy: bool,
605}
606#[repr(C)]
607pub struct CConFrameIterator {
608 iterator: *mut ConFrameIterator<'static>,
609 file_contents: *mut String,
610}
611
612fn c_iterator_from_owned_string(contents: String) -> *mut CConFrameIterator {
614 let file_contents_box = Box::new(contents);
615 let file_contents_ptr = Box::into_raw(file_contents_box);
616 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
617 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
618 let c_iterator = Box::new(CConFrameIterator {
619 iterator: Box::into_raw(iterator),
620 file_contents: file_contents_ptr,
621 });
622 Box::into_raw(c_iterator)
623}
624
625#[unsafe(no_mangle)]
641pub unsafe extern "C" fn read_con_file_iterator(
642 filename_c: *const c_char,
643) -> *mut CConFrameIterator {
644 if filename_c.is_null() {
645 return ptr::null_mut();
646 }
647 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
648 Ok(s) => s,
649 Err(_) => return ptr::null_mut(),
650 };
651 let owned = match crate::compression::read_file_contents(Path::new(filename)) {
652 Ok(fc) => match fc.as_str() {
653 Ok(s) => s.to_owned(),
654 Err(_) => return ptr::null_mut(),
655 },
656 Err(_) => return ptr::null_mut(),
657 };
658 c_iterator_from_owned_string(owned)
659}
660
661#[unsafe(no_mangle)]
669pub unsafe extern "C" fn read_con_string_iterator(
670 contents_c: *const c_char,
671) -> *mut CConFrameIterator {
672 if contents_c.is_null() {
673 return ptr::null_mut();
674 }
675 let contents = match unsafe { CStr::from_ptr(contents_c).to_str() } {
676 Ok(s) => s.to_owned(),
677 Err(_) => return ptr::null_mut(),
678 };
679 c_iterator_from_owned_string(contents)
680}
681
682#[unsafe(no_mangle)]
689pub unsafe extern "C" fn read_con_buffer_iterator(
690 data: *const u8,
691 len: usize,
692) -> *mut CConFrameIterator {
693 if data.is_null() && len > 0 {
694 return ptr::null_mut();
695 }
696 if len == 0 {
697 return c_iterator_from_owned_string(String::new());
698 }
699 let slice = unsafe { std::slice::from_raw_parts(data, len) };
700 let contents = match std::str::from_utf8(slice) {
701 Ok(s) => s.to_owned(),
702 Err(_) => return ptr::null_mut(),
703 };
704 c_iterator_from_owned_string(contents)
705}
706#[unsafe(no_mangle)]
712pub unsafe extern "C" fn con_frame_iterator_next(
713 iterator: *mut CConFrameIterator,
714) -> *mut RKRConFrame {
715 if iterator.is_null() {
716 return ptr::null_mut();
717 }
718 let iter = unsafe { &mut *(*iterator).iterator };
719 match iter.next() {
720 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
721 _ => ptr::null_mut(),
722 }
723}
724
725#[unsafe(no_mangle)]
728pub unsafe extern "C" fn con_frame_iterator_forward(iterator: *mut CConFrameIterator) -> RKRStatus {
729 if iterator.is_null() {
730 return RKRStatus::RKR_STATUS_NULL_POINTER;
731 }
732 let iter = unsafe { &mut *(*iterator).iterator };
733 match iter.forward() {
734 Some(Ok(())) => RKRStatus::RKR_STATUS_SUCCESS,
735 Some(Err(_)) => RKRStatus::RKR_STATUS_IO_ERROR,
736 None => RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
737 }
738}
739
740#[unsafe(no_mangle)]
743pub unsafe extern "C" fn con_frame_iterator_skip(
744 iterator: *mut CConFrameIterator,
745 n: usize,
746) -> usize {
747 if iterator.is_null() {
748 return usize::MAX;
749 }
750 let iter = unsafe { &mut *(*iterator).iterator };
751 iter.skip_frames(n).unwrap_or(usize::MAX)
752}
753
754#[unsafe(no_mangle)]
756pub unsafe extern "C" fn con_frame_iterator_nth(
757 iterator: *mut CConFrameIterator,
758 index: usize,
759) -> *mut RKRConFrame {
760 if iterator.is_null() {
761 return ptr::null_mut();
762 }
763 let iter = unsafe { &mut *(*iterator).iterator };
764 match iter.skip_frames(index) {
765 Ok(skipped) if skipped == index => {}
766 _ => return ptr::null_mut(),
767 }
768 match iter.next() {
769 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
770 _ => ptr::null_mut(),
771 }
772}
773
774#[unsafe(no_mangle)]
776pub unsafe extern "C" fn rkr_count_frames(filename_c: *const c_char) -> usize {
777 if filename_c.is_null() {
778 return usize::MAX;
779 }
780 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
781 Ok(s) => s,
782 Err(_) => return usize::MAX,
783 };
784 crate::iterators::count_frames(Path::new(filename)).unwrap_or(usize::MAX)
785}
786
787#[unsafe(no_mangle)]
789pub unsafe extern "C" fn rkr_read_nth_frame(
790 filename_c: *const c_char,
791 index: usize,
792) -> *mut RKRConFrame {
793 if filename_c.is_null() {
794 return ptr::null_mut();
795 }
796 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
797 Ok(s) => s,
798 Err(_) => return ptr::null_mut(),
799 };
800 match crate::iterators::read_nth_frame(Path::new(filename), index) {
801 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
802 Err(_) => ptr::null_mut(),
803 }
804}
805
806pub struct RKRTrajectory {
810 _private: [u8; 0],
811}
812
813#[unsafe(no_mangle)]
823pub unsafe extern "C" fn rkr_trajectory_open(
824 filename_c: *const c_char,
825 write_index: i32,
826) -> *mut RKRTrajectory {
827 if filename_c.is_null() {
828 set_last_error("filename is NULL");
829 return ptr::null_mut();
830 }
831 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
832 Ok(s) => s,
833 Err(_) => {
834 set_last_error("filename is not valid UTF-8");
835 return ptr::null_mut();
836 }
837 };
838 let opened = std::panic::catch_unwind(|| {
839 crate::trajectory::ConTrajectory::open(Path::new(filename), write_index != 0)
840 .map_err(|e| e.to_string())
841 })
842 .unwrap_or_else(|_| Err("internal error while opening the file".to_owned()));
843 match opened {
844 Ok(traj) => {
845 clear_last_error();
846 Box::into_raw(Box::new(traj)) as *mut RKRTrajectory
847 }
848 Err(msg) => {
849 set_last_error(&msg);
850 ptr::null_mut()
851 }
852 }
853}
854
855#[unsafe(no_mangle)]
863pub unsafe extern "C" fn rkr_trajectory_len(handle: *const RKRTrajectory) -> usize {
864 match unsafe { (handle as *const crate::trajectory::ConTrajectory).as_ref() } {
865 Some(traj) => traj.len(),
866 None => {
867 set_last_error("null argument");
868 usize::MAX
869 }
870 }
871}
872
873#[unsafe(no_mangle)]
886pub unsafe extern "C" fn rkr_trajectory_read_frame(
887 handle: *const RKRTrajectory,
888 index: usize,
889 out_frame: *mut *mut RKRConFrame,
890) -> RKRStatus {
891 if !out_frame.is_null() {
892 unsafe { *out_frame = ptr::null_mut() };
893 }
894 if handle.is_null() || out_frame.is_null() {
895 set_last_error("null argument");
896 return RKRStatus::RKR_STATUS_NULL_POINTER;
897 }
898 let traj = unsafe { &*(handle as *const crate::trajectory::ConTrajectory) };
899 let parsed = std::panic::catch_unwind(|| traj.frame(index));
900 let (status, msg) = match parsed {
901 Ok(Ok(frame)) => {
902 clear_last_error();
903 unsafe { *out_frame = Box::into_raw(Box::new(frame)) as *mut RKRConFrame };
904 return RKRStatus::RKR_STATUS_SUCCESS;
905 }
906 Ok(Err(crate::error::ParseError::IndexOutOfBounds { index, len })) => (
907 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
908 format!("frame index {index} is out of range for a trajectory of {len} frames"),
909 ),
910 Ok(Err(e)) => (
911 RKRStatus::RKR_STATUS_IO_ERROR,
912 format!("frame {index}: {e}"),
913 ),
914 Err(_) => (
915 RKRStatus::RKR_STATUS_INTERNAL_ERROR,
916 format!("internal error while reading frame {index}"),
917 ),
918 };
919 set_last_error(&msg);
920 status
921}
922
923#[unsafe(no_mangle)]
928pub unsafe extern "C" fn rkr_trajectory_free(handle: *mut RKRTrajectory) {
929 if !handle.is_null() {
930 let _ = unsafe { Box::from_raw(handle as *mut crate::trajectory::ConTrajectory) };
931 }
932}
933#[unsafe(no_mangle)]
938pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
939 if !frame_handle.is_null() {
940 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
941 }
942}
943#[unsafe(no_mangle)]
948pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
949 if iterator.is_null() {
950 return;
951 }
952 unsafe {
953 let c_iterator_box = Box::from_raw(iterator);
954 let _ = Box::from_raw(c_iterator_box.iterator);
955 let _ = Box::from_raw(c_iterator_box.file_contents);
956 }
957}
958#[unsafe(no_mangle)]
967pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
968 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
969 Some(f) => f,
970 None => return ptr::null_mut(),
971 };
972 let masses_iter = frame
973 .header
974 .natms_per_type
975 .iter()
976 .zip(frame.header.masses_per_type.iter())
977 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
978 let has_velocities = frame.has_velocities();
979 let mut c_atoms: Vec<CAtom> = frame
980 .atom_data
981 .iter()
982 .zip(masses_iter)
983 .map(|(atom_datum, mass)| {
984 let [vx, vy, vz] = atom_datum.velocity().unwrap_or([0.0; 3]);
985 let [fx, fy, fz] = atom_datum.force().unwrap_or([0.0; 3]);
986 CAtom {
987 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
988 x: atom_datum.x,
989 y: atom_datum.y,
990 z: atom_datum.z,
991 is_fixed: atom_datum.is_fixed(),
992 fixed_x: atom_datum.fixed[0],
993 fixed_y: atom_datum.fixed[1],
994 fixed_z: atom_datum.fixed[2],
995 atom_id: atom_datum.atom_id,
996 mass,
997 vx,
998 vy,
999 vz,
1000 has_velocity: atom_datum.has_velocity(),
1001 fx,
1002 fy,
1003 fz,
1004 has_forces: atom_datum.has_forces(),
1005 energy: atom_datum.energy().unwrap_or(0.0),
1006 has_energy: atom_datum.has_energy(),
1007 }
1008 })
1009 .collect();
1010 let atoms_ptr = c_atoms.as_mut_ptr();
1011 let num_atoms = c_atoms.len();
1012 std::mem::forget(c_atoms);
1013 let has_forces = frame.has_forces();
1014 let has_energies = frame.has_energies();
1015 let c_frame = Box::new(CFrame {
1016 atoms: atoms_ptr,
1017 num_atoms,
1018 cell: frame.header.boxl,
1019 angles: frame.header.angles,
1020 has_velocities,
1021 has_forces,
1022 has_energies,
1023 });
1024 Box::into_raw(c_frame)
1025}
1026#[unsafe(no_mangle)]
1031pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
1032 if frame.is_null() {
1033 return;
1034 }
1035 unsafe {
1036 let frame_box = Box::from_raw(frame);
1037 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
1038 }
1039}
1040#[unsafe(no_mangle)]
1059pub unsafe extern "C" fn rkr_frame_get_header_line(
1060 frame_handle: *const RKRConFrame,
1061 is_prebox: bool,
1062 line_index: usize,
1063 buffer: *mut c_char,
1064 buffer_len: usize,
1065) -> RKRStatus {
1066 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
1067 Some(f) => f,
1068 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1069 };
1070 if buffer.is_null() {
1071 return RKRStatus::RKR_STATUS_NULL_POINTER;
1072 }
1073 if buffer_len == 0 {
1074 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
1075 }
1076 let line_to_copy: Option<&str> = if is_prebox {
1077 match line_index {
1078 0 => Some(frame.header.prebox_header.user.as_str()),
1079 1 => Some(frame.header.prebox_header.metadata_line()),
1080 _ => None,
1081 }
1082 } else {
1083 frame
1084 .header
1085 .postbox_header
1086 .get(line_index)
1087 .map(String::as_str)
1088 };
1089 if let Some(line) = line_to_copy {
1090 let bytes = line.as_bytes();
1091 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
1092 unsafe {
1093 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
1094 *buffer.add(len_to_copy) = 0;
1095 }
1096 RKRStatus::RKR_STATUS_SUCCESS
1097 } else {
1098 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1099 }
1100}
1101#[unsafe(no_mangle)]
1114pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
1115 frame_handle: *const RKRConFrame,
1116 is_prebox: bool,
1117 line_index: usize,
1118) -> *mut c_char {
1119 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
1120 Some(f) => f,
1121 None => return ptr::null_mut(),
1122 };
1123 let line_to_copy: Option<&str> = if is_prebox {
1124 match line_index {
1125 0 => Some(frame.header.prebox_header.user.as_str()),
1126 1 => Some(frame.header.prebox_header.metadata_line()),
1127 _ => None,
1128 }
1129 } else {
1130 frame
1131 .header
1132 .postbox_header
1133 .get(line_index)
1134 .map(String::as_str)
1135 };
1136 if let Some(line) = line_to_copy {
1137 match CString::new(line) {
1139 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
1142 } else {
1143 ptr::null_mut() }
1145}
1146#[unsafe(no_mangle)]
1154pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
1155 if !s.is_null() {
1156 let _ = unsafe { CString::from_raw(s) };
1158 }
1159}
1160type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
1174#[inline]
1177fn into_rkr_writer(sink: Box<dyn std::io::Write>, precision: Option<u8>) -> *mut RKRConFrameWriter {
1178 let writer: RkrWriter = match precision {
1179 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
1180 None => ConFrameWriter::new(sink),
1181 };
1182 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1183}
1184#[inline]
1186unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
1187 if filename_c.is_null() {
1188 return None;
1189 }
1190 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
1191}
1192#[unsafe(no_mangle)]
1198pub unsafe extern "C" fn create_writer_from_path_c(
1199 filename_c: *const c_char,
1200) -> *mut RKRConFrameWriter {
1201 let filename = match unsafe { cstr_path(filename_c) } {
1202 Some(s) => s,
1203 None => return ptr::null_mut(),
1204 };
1205 match File::create(filename) {
1206 Ok(file) => into_rkr_writer(Box::new(file), None),
1207 Err(_) => ptr::null_mut(),
1208 }
1209}
1210#[unsafe(no_mangle)]
1216pub unsafe extern "C" fn create_writer_from_path_round_trip_c(
1217 filename_c: *const c_char,
1218) -> *mut RKRConFrameWriter {
1219 let filename = match unsafe { cstr_path(filename_c) } {
1220 Some(s) => s,
1221 None => return ptr::null_mut(),
1222 };
1223 match File::create(filename) {
1224 Ok(file) => {
1225 let writer: RkrWriter = ConFrameWriter::with_float_format(
1226 Box::new(file),
1227 crate::writer::FloatFormat::RoundTrip,
1228 );
1229 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1230 }
1231 Err(_) => ptr::null_mut(),
1232 }
1233}
1234
1235#[unsafe(no_mangle)]
1243pub unsafe extern "C" fn rkr_writer_flush(writer_handle: *mut RKRConFrameWriter) -> RKRStatus {
1244 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1245 Some(w) => w,
1246 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1247 };
1248 match writer.flush() {
1249 Ok(()) => {
1250 clear_last_error();
1251 RKRStatus::RKR_STATUS_SUCCESS
1252 }
1253 Err(err) => {
1254 set_last_error(&err.to_string());
1255 RKRStatus::RKR_STATUS_IO_ERROR
1256 }
1257 }
1258}
1259
1260#[unsafe(no_mangle)]
1265pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
1266 if !writer_handle.is_null() {
1267 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
1268 }
1269}
1270#[unsafe(no_mangle)]
1279pub unsafe extern "C" fn rkr_frame_binding_rewrite(
1280 frame_handle: *const RKRConFrame,
1281) -> *mut RKRConFrame {
1282 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
1283 return ptr::null_mut();
1284 };
1285 match binding_rewrite_frame(frame) {
1286 Ok(rewritten) => Box::into_raw(Box::new(rewritten)) as *mut RKRConFrame,
1287 Err(_) => ptr::null_mut(),
1288 }
1289}
1290
1291#[unsafe(no_mangle)]
1301pub unsafe extern "C" fn rkr_writer_extend(
1302 writer_handle: *mut RKRConFrameWriter,
1303 frame_handles: *const *const RKRConFrame,
1304 num_frames: usize,
1305) -> RKRStatus {
1306 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1307 Some(w) => w,
1308 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1309 };
1310 if frame_handles.is_null() {
1311 return RKRStatus::RKR_STATUS_NULL_POINTER;
1312 }
1313 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
1314 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
1315 if handles_slice.iter().any(|&handle| handle.is_null()) {
1316 return RKRStatus::RKR_STATUS_NULL_POINTER;
1319 }
1320 for &handle in handles_slice.iter() {
1321 match unsafe { (handle as *const ConFrame).as_ref() } {
1323 Some(frame) => rust_frames.push(frame),
1324 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1326 }
1327 }
1328 match writer.extend(rust_frames.into_iter()) {
1329 Ok(_) => {
1330 clear_last_error();
1331 RKRStatus::RKR_STATUS_SUCCESS
1332 }
1333 Err(err) => {
1334 set_last_error(&err.to_string());
1335 RKRStatus::RKR_STATUS_IO_ERROR
1336 }
1337 }
1338}
1339
1340#[unsafe(no_mangle)]
1346pub unsafe extern "C" fn rkr_writer_set_canonical(
1347 writer_handle: *mut RKRConFrameWriter,
1348 canonical: u8,
1349) -> RKRStatus {
1350 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1351 Some(w) => w,
1352 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1353 };
1354 writer.set_canonical(canonical != 0);
1355 RKRStatus::RKR_STATUS_SUCCESS
1356}
1357
1358#[unsafe(no_mangle)]
1360pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
1361 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
1362 Some(w) => u8::from(w.is_canonical()),
1363 None => 0,
1364 }
1365}
1366
1367#[cfg(test)]
1368mod index_proj_ffi_tests {
1369 use super::*;
1370 use std::ffi::CStr;
1371 use std::fs;
1372 use std::path::PathBuf;
1373
1374 fn fixture_path() -> PathBuf {
1375 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
1376 }
1377
1378 #[test]
1379 fn ffi_projection_matches_index_proj() {
1380 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1381 let fr = &frames[0];
1382 let handle = fr as *const ConFrame as *const RKRConFrame;
1383 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
1384 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
1386 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
1387 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
1388 assert!(!formula_c.is_null());
1389 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
1390 assert_eq!(formula, proj.formula);
1391 unsafe { rkr_free_string(formula_c) };
1392 let ie = rkr_frame_index_energy(handle);
1393 match proj.energy {
1394 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
1395 None => assert!(ie.is_nan()),
1396 }
1397 let tm = rkr_frame_total_mass(handle);
1398 match proj.total_mass {
1399 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
1400 None => assert!(tm.is_nan()),
1401 }
1402 let cv = rkr_frame_cell_volume(handle);
1403 match proj.cell_volume {
1404 Some(v) => assert!(
1405 (cv - v).abs() < 1e-6 * v.max(1.0),
1406 "cell_volume C={cv} proj={v}"
1407 ),
1408 None => assert!(cv.is_nan()),
1409 }
1410 let fm = rkr_frame_fmax(handle);
1411 match proj.fmax {
1412 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
1413 None => assert!(fm.is_nan()),
1414 }
1415 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
1416 assert!(!json_c.is_null());
1417 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
1418 assert!(json.contains("\"formula\""));
1419 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
1420 assert!(json.contains("\"n_atoms\""));
1421 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
1422 unsafe { rkr_free_string(json_c) };
1423 }
1424
1425 #[test]
1426 fn ffi_canonical_writer_byte_identical() {
1427 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1428 let fr = &frames[0];
1429 let dir = tempfile::tempdir().unwrap();
1430 let p1 = dir.path().join("a.con");
1431 let p2 = dir.path().join("b.con");
1432 for p in [&p1, &p2] {
1433 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
1434 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
1435 assert!(!w.is_null());
1436 assert_eq!(
1437 unsafe { rkr_writer_set_canonical(w, 1) },
1438 RKRStatus::RKR_STATUS_SUCCESS
1439 );
1440 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
1441 let handles = [fr as *const ConFrame as *const RKRConFrame];
1442 assert_eq!(
1443 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
1444 RKRStatus::RKR_STATUS_SUCCESS
1445 );
1446 unsafe { free_rkr_writer(w) };
1447 }
1448 let b1 = fs::read(&p1).unwrap();
1449 let b2 = fs::read(&p2).unwrap();
1450 assert_eq!(b1, b2);
1451 assert!(!b1.is_empty());
1452 }
1453}
1454#[unsafe(no_mangle)]
1463pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
1464 filename_c: *const c_char,
1465 precision: u8,
1466) -> *mut RKRConFrameWriter {
1467 let filename = match unsafe { cstr_path(filename_c) } {
1468 Some(s) => s,
1469 None => return ptr::null_mut(),
1470 };
1471 match File::create(filename) {
1472 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1473 Err(_) => ptr::null_mut(),
1474 }
1475}
1476pub struct RKRConFrameBuilder {
1483 _private: [u8; 0],
1484}
1485#[allow(clippy::too_many_arguments)]
1486unsafe fn add_builder_atom(
1487 builder_handle: *mut RKRConFrameBuilder,
1488 symbol: *const c_char,
1489 x: f64,
1490 y: f64,
1491 z: f64,
1492 fixed: [bool; 3],
1493 atom_id: u64,
1494 mass: f64,
1495 velocity: Option<[f64; 3]>,
1496 forces: Option<[f64; 3]>,
1497) -> RKRStatus {
1498 if builder_handle.is_null() || symbol.is_null() {
1499 return RKRStatus::RKR_STATUS_NULL_POINTER;
1500 }
1501 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1502 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
1503 Ok(s) => s,
1504 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1505 };
1506 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
1507 if let Some(v) = velocity {
1508 builder.with_velocity(v);
1509 }
1510 if let Some(f) = forces {
1511 builder.with_force(f);
1512 }
1513 RKRStatus::RKR_STATUS_SUCCESS
1514}
1515#[unsafe(no_mangle)]
1521pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
1522 builder_handle: *mut RKRConFrameBuilder,
1523 velocity: *const f64,
1524) -> RKRStatus {
1525 if builder_handle.is_null() || velocity.is_null() {
1526 return RKRStatus::RKR_STATUS_NULL_POINTER;
1527 }
1528 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1529 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1530 builder.with_velocity(v);
1531 RKRStatus::RKR_STATUS_SUCCESS
1532}
1533#[unsafe(no_mangle)]
1539pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
1540 builder_handle: *mut RKRConFrameBuilder,
1541 force: *const f64,
1542) -> RKRStatus {
1543 if builder_handle.is_null() || force.is_null() {
1544 return RKRStatus::RKR_STATUS_NULL_POINTER;
1545 }
1546 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1547 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1548 builder.with_force(f);
1549 RKRStatus::RKR_STATUS_SUCCESS
1550}
1551#[unsafe(no_mangle)]
1558pub unsafe extern "C" fn rkr_frame_builder_set_last_displacement(
1559 builder_handle: *mut RKRConFrameBuilder,
1560 displacement: *const f64,
1561) -> RKRStatus {
1562 if builder_handle.is_null() || displacement.is_null() {
1563 return RKRStatus::RKR_STATUS_NULL_POINTER;
1564 }
1565 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1566 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1567 builder.with_displacement(d);
1568 RKRStatus::RKR_STATUS_SUCCESS
1569}
1570#[unsafe(no_mangle)]
1577pub unsafe extern "C" fn rkr_frame_builder_set_last_spread(
1578 builder_handle: *mut RKRConFrameBuilder,
1579 spread: *const f64,
1580) -> RKRStatus {
1581 if builder_handle.is_null() || spread.is_null() {
1582 return RKRStatus::RKR_STATUS_NULL_POINTER;
1583 }
1584 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1585 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
1586 builder.with_spread(d);
1587 RKRStatus::RKR_STATUS_SUCCESS
1588}
1589#[unsafe(no_mangle)]
1599pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1600 builder_handle: *mut RKRConFrameBuilder,
1601 energy: f64,
1602) -> RKRStatus {
1603 if builder_handle.is_null() {
1604 return RKRStatus::RKR_STATUS_NULL_POINTER;
1605 }
1606 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1607 builder.with_energy(energy);
1608 RKRStatus::RKR_STATUS_SUCCESS
1609}
1610fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1624 use crate::error::ParseError;
1625 match e {
1626 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1627 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1628 }
1629 ParseError::MassMismatch { .. } | ParseError::ValidationError(_) => {
1630 RKRStatus::RKR_STATUS_VALIDATION_ERROR
1631 }
1632 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1633 }
1634}
1635#[unsafe(no_mangle)]
1642pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1643 builder_handle: *const RKRConFrameBuilder,
1644) -> usize {
1645 if builder_handle.is_null() {
1646 return 0;
1647 }
1648 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1649 builder.atom_count()
1650}
1651#[unsafe(no_mangle)]
1655pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1656 builder_handle: *mut RKRConFrameBuilder,
1657 index: usize,
1658 x: f64,
1659 y: f64,
1660 z: f64,
1661) -> RKRStatus {
1662 if builder_handle.is_null() {
1663 return RKRStatus::RKR_STATUS_NULL_POINTER;
1664 }
1665 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1666 match builder.set_atom_position(index, x, y, z) {
1667 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1668 Err(e) => map_builder_err(e),
1669 }
1670}
1671#[unsafe(no_mangle)]
1675pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1676 builder_handle: *mut RKRConFrameBuilder,
1677 index: usize,
1678 velocity: *const f64,
1679) -> RKRStatus {
1680 if builder_handle.is_null() || velocity.is_null() {
1681 return RKRStatus::RKR_STATUS_NULL_POINTER;
1682 }
1683 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1684 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1685 match builder.set_atom_velocity(index, v) {
1686 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1687 Err(e) => map_builder_err(e),
1688 }
1689}
1690#[unsafe(no_mangle)]
1694pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1695 builder_handle: *mut RKRConFrameBuilder,
1696 index: usize,
1697 force: *const f64,
1698) -> RKRStatus {
1699 if builder_handle.is_null() || force.is_null() {
1700 return RKRStatus::RKR_STATUS_NULL_POINTER;
1701 }
1702 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1703 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1704 match builder.set_atom_force(index, f) {
1705 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1706 Err(e) => map_builder_err(e),
1707 }
1708}
1709#[unsafe(no_mangle)]
1714pub unsafe extern "C" fn rkr_frame_builder_set_atom_displacement(
1715 builder_handle: *mut RKRConFrameBuilder,
1716 index: usize,
1717 displacement: *const f64,
1718) -> RKRStatus {
1719 if builder_handle.is_null() || displacement.is_null() {
1720 return RKRStatus::RKR_STATUS_NULL_POINTER;
1721 }
1722 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1723 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1724 match builder.set_atom_displacement(index, d) {
1725 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1726 Err(e) => map_builder_err(e),
1727 }
1728}
1729#[unsafe(no_mangle)]
1734pub unsafe extern "C" fn rkr_frame_builder_set_atom_spread(
1735 builder_handle: *mut RKRConFrameBuilder,
1736 index: usize,
1737 spread: *const f64,
1738) -> RKRStatus {
1739 if builder_handle.is_null() || spread.is_null() {
1740 return RKRStatus::RKR_STATUS_NULL_POINTER;
1741 }
1742 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1743 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
1744 match builder.set_atom_spread(index, d) {
1745 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1746 Err(e) => map_builder_err(e),
1747 }
1748}
1749#[unsafe(no_mangle)]
1753pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1754 builder_handle: *mut RKRConFrameBuilder,
1755 index: usize,
1756 energy: f64,
1757) -> RKRStatus {
1758 if builder_handle.is_null() {
1759 return RKRStatus::RKR_STATUS_NULL_POINTER;
1760 }
1761 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1762 match builder.set_atom_energy(index, energy) {
1763 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1764 Err(e) => map_builder_err(e),
1765 }
1766}
1767#[unsafe(no_mangle)]
1771pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1772 builder_handle: *mut RKRConFrameBuilder,
1773 index: usize,
1774 fixed_x: bool,
1775 fixed_y: bool,
1776 fixed_z: bool,
1777) -> RKRStatus {
1778 if builder_handle.is_null() {
1779 return RKRStatus::RKR_STATUS_NULL_POINTER;
1780 }
1781 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1782 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1783 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1784 Err(e) => map_builder_err(e),
1785 }
1786}
1787#[unsafe(no_mangle)]
1791pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1792 builder_handle: *mut RKRConFrameBuilder,
1793 index: usize,
1794 mass: f64,
1795) -> RKRStatus {
1796 if builder_handle.is_null() {
1797 return RKRStatus::RKR_STATUS_NULL_POINTER;
1798 }
1799 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1800 match builder.set_atom_mass(index, mass) {
1801 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1802 Err(e) => map_builder_err(e),
1803 }
1804}
1805#[unsafe(no_mangle)]
1812pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1813 builder_handle: *mut RKRConFrameBuilder,
1814 index: usize,
1815 atom_id: u64,
1816) -> RKRStatus {
1817 if builder_handle.is_null() {
1818 return RKRStatus::RKR_STATUS_NULL_POINTER;
1819 }
1820 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1821 match builder.set_atom_id(index, atom_id) {
1822 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1823 Err(e) => map_builder_err(e),
1824 }
1825}
1826#[unsafe(no_mangle)]
1830pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1831 builder_handle: *mut RKRConFrameBuilder,
1832 index: usize,
1833) -> RKRStatus {
1834 if builder_handle.is_null() {
1835 return RKRStatus::RKR_STATUS_NULL_POINTER;
1836 }
1837 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1838 match builder.clear_atom_velocity(index) {
1839 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1840 Err(e) => map_builder_err(e),
1841 }
1842}
1843#[unsafe(no_mangle)]
1846pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1847 builder_handle: *mut RKRConFrameBuilder,
1848 index: usize,
1849) -> RKRStatus {
1850 if builder_handle.is_null() {
1851 return RKRStatus::RKR_STATUS_NULL_POINTER;
1852 }
1853 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1854 match builder.clear_atom_force(index) {
1855 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1856 Err(e) => map_builder_err(e),
1857 }
1858}
1859#[unsafe(no_mangle)]
1862pub unsafe extern "C" fn rkr_frame_builder_clear_atom_displacement(
1863 builder_handle: *mut RKRConFrameBuilder,
1864 index: usize,
1865) -> RKRStatus {
1866 if builder_handle.is_null() {
1867 return RKRStatus::RKR_STATUS_NULL_POINTER;
1868 }
1869 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1870 match builder.clear_atom_displacement(index) {
1871 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1872 Err(e) => map_builder_err(e),
1873 }
1874}
1875#[unsafe(no_mangle)]
1878pub unsafe extern "C" fn rkr_frame_builder_clear_atom_spread(
1879 builder_handle: *mut RKRConFrameBuilder,
1880 index: usize,
1881) -> RKRStatus {
1882 if builder_handle.is_null() {
1883 return RKRStatus::RKR_STATUS_NULL_POINTER;
1884 }
1885 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1886 match builder.clear_atom_spread(index) {
1887 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1888 Err(e) => map_builder_err(e),
1889 }
1890}
1891#[unsafe(no_mangle)]
1894pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1895 builder_handle: *mut RKRConFrameBuilder,
1896 index: usize,
1897) -> RKRStatus {
1898 if builder_handle.is_null() {
1899 return RKRStatus::RKR_STATUS_NULL_POINTER;
1900 }
1901 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1902 match builder.clear_atom_energy(index) {
1903 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1904 Err(e) => map_builder_err(e),
1905 }
1906}
1907#[unsafe(no_mangle)]
1912pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1913 builder_handle: *mut RKRConFrameBuilder,
1914 positions: *const f64,
1915 len: usize,
1916) -> RKRStatus {
1917 if builder_handle.is_null() || positions.is_null() {
1918 return RKRStatus::RKR_STATUS_NULL_POINTER;
1919 }
1920 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1921 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1922 match builder.set_positions_from_flat(slice) {
1923 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1924 Err(e) => map_builder_err(e),
1925 }
1926}
1927#[unsafe(no_mangle)]
1931pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1932 builder_handle: *mut RKRConFrameBuilder,
1933 forces: *const f64,
1934 len: usize,
1935) -> RKRStatus {
1936 if builder_handle.is_null() || forces.is_null() {
1937 return RKRStatus::RKR_STATUS_NULL_POINTER;
1938 }
1939 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1940 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1941 match builder.set_forces_from_flat(slice) {
1942 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1943 Err(e) => map_builder_err(e),
1944 }
1945}
1946#[unsafe(no_mangle)]
1951pub unsafe extern "C" fn rkr_frame_builder_set_displacements_from_flat(
1952 builder_handle: *mut RKRConFrameBuilder,
1953 displacements: *const f64,
1954 len: usize,
1955) -> RKRStatus {
1956 if builder_handle.is_null() || displacements.is_null() {
1957 return RKRStatus::RKR_STATUS_NULL_POINTER;
1958 }
1959 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1960 let slice = unsafe { std::slice::from_raw_parts(displacements, len) };
1961 match builder.set_displacements_from_flat(slice) {
1962 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1963 Err(e) => map_builder_err(e),
1964 }
1965}
1966#[unsafe(no_mangle)]
1971pub unsafe extern "C" fn rkr_frame_builder_set_spreads_from_flat(
1972 builder_handle: *mut RKRConFrameBuilder,
1973 spreads: *const f64,
1974 len: usize,
1975) -> RKRStatus {
1976 if builder_handle.is_null() || spreads.is_null() {
1977 return RKRStatus::RKR_STATUS_NULL_POINTER;
1978 }
1979 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1980 let slice = unsafe { std::slice::from_raw_parts(spreads, len) };
1981 match builder.set_spreads_from_flat(slice) {
1982 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1983 Err(e) => map_builder_err(e),
1984 }
1985}
1986#[unsafe(no_mangle)]
1990pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1991 builder_handle: *mut RKRConFrameBuilder,
1992 energies: *const f64,
1993 len: usize,
1994) -> RKRStatus {
1995 if builder_handle.is_null() || energies.is_null() {
1996 return RKRStatus::RKR_STATUS_NULL_POINTER;
1997 }
1998 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1999 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
2000 match builder.set_atom_energies_from_flat(slice) {
2001 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
2002 Err(e) => map_builder_err(e),
2003 }
2004}
2005#[unsafe(no_mangle)]
2009pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
2010 builder_handle: *const RKRConFrameBuilder,
2011 index: usize,
2012 out_xyz: *mut f64,
2013) -> RKRStatus {
2014 if builder_handle.is_null() || out_xyz.is_null() {
2015 return RKRStatus::RKR_STATUS_NULL_POINTER;
2016 }
2017 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2018 match builder.get_atom_position(index) {
2019 Ok((x, y, z)) => unsafe {
2020 *out_xyz = x;
2021 *out_xyz.add(1) = y;
2022 *out_xyz.add(2) = z;
2023 RKRStatus::RKR_STATUS_SUCCESS
2024 },
2025 Err(e) => map_builder_err(e),
2026 }
2027}
2028#[unsafe(no_mangle)]
2035pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
2036 builder_handle: *const RKRConFrameBuilder,
2037 index: usize,
2038 out_xyz: *mut f64,
2039 has_value: *mut bool,
2040) -> RKRStatus {
2041 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
2042 return RKRStatus::RKR_STATUS_NULL_POINTER;
2043 }
2044 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2045 match builder.get_atom_velocity(index) {
2046 Ok(Some(v)) => unsafe {
2047 *out_xyz = v[0];
2048 *out_xyz.add(1) = v[1];
2049 *out_xyz.add(2) = v[2];
2050 *has_value = true;
2051 RKRStatus::RKR_STATUS_SUCCESS
2052 },
2053 Ok(None) => unsafe {
2054 *has_value = false;
2055 RKRStatus::RKR_STATUS_SUCCESS
2056 },
2057 Err(e) => map_builder_err(e),
2058 }
2059}
2060#[unsafe(no_mangle)]
2063pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
2064 builder_handle: *const RKRConFrameBuilder,
2065 index: usize,
2066 out_xyz: *mut f64,
2067 has_value: *mut bool,
2068) -> RKRStatus {
2069 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
2070 return RKRStatus::RKR_STATUS_NULL_POINTER;
2071 }
2072 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2073 match builder.get_atom_force(index) {
2074 Ok(Some(f)) => unsafe {
2075 *out_xyz = f[0];
2076 *out_xyz.add(1) = f[1];
2077 *out_xyz.add(2) = f[2];
2078 *has_value = true;
2079 RKRStatus::RKR_STATUS_SUCCESS
2080 },
2081 Ok(None) => unsafe {
2082 *has_value = false;
2083 RKRStatus::RKR_STATUS_SUCCESS
2084 },
2085 Err(e) => map_builder_err(e),
2086 }
2087}
2088#[unsafe(no_mangle)]
2094pub unsafe extern "C" fn rkr_frame_builder_get_atom_displacement(
2095 builder_handle: *const RKRConFrameBuilder,
2096 index: usize,
2097 out_xyz: *mut f64,
2098 has_value: *mut bool,
2099) -> RKRStatus {
2100 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
2101 return RKRStatus::RKR_STATUS_NULL_POINTER;
2102 }
2103 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2104 match builder.get_atom_displacement(index) {
2105 Ok(Some(d)) => unsafe {
2106 *out_xyz = d[0];
2107 *out_xyz.add(1) = d[1];
2108 *out_xyz.add(2) = d[2];
2109 *has_value = true;
2110 RKRStatus::RKR_STATUS_SUCCESS
2111 },
2112 Ok(None) => unsafe {
2113 *has_value = false;
2114 RKRStatus::RKR_STATUS_SUCCESS
2115 },
2116 Err(e) => map_builder_err(e),
2117 }
2118}
2119#[unsafe(no_mangle)]
2125pub unsafe extern "C" fn rkr_frame_builder_get_atom_spread(
2126 builder_handle: *const RKRConFrameBuilder,
2127 index: usize,
2128 out_xyz: *mut f64,
2129 has_value: *mut bool,
2130) -> RKRStatus {
2131 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
2132 return RKRStatus::RKR_STATUS_NULL_POINTER;
2133 }
2134 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2135 match builder.get_atom_spread(index) {
2136 Ok(Some(d)) => unsafe {
2137 *out_xyz = d[0];
2138 *out_xyz.add(1) = d[1];
2139 *out_xyz.add(2) = d[2];
2140 *has_value = true;
2141 RKRStatus::RKR_STATUS_SUCCESS
2142 },
2143 Ok(None) => unsafe {
2144 *has_value = false;
2145 RKRStatus::RKR_STATUS_SUCCESS
2146 },
2147 Err(e) => map_builder_err(e),
2148 }
2149}
2150#[unsafe(no_mangle)]
2156pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
2157 builder_handle: *const RKRConFrameBuilder,
2158 index: usize,
2159 out_value: *mut f64,
2160 has_value: *mut bool,
2161) -> RKRStatus {
2162 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
2163 return RKRStatus::RKR_STATUS_NULL_POINTER;
2164 }
2165 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2166 match builder.get_atom_energy(index) {
2167 Ok(Some(e)) => unsafe {
2168 *out_value = e;
2169 *has_value = true;
2170 RKRStatus::RKR_STATUS_SUCCESS
2171 },
2172 Ok(None) => unsafe {
2173 *has_value = false;
2174 RKRStatus::RKR_STATUS_SUCCESS
2175 },
2176 Err(e) => map_builder_err(e),
2177 }
2178}
2179#[unsafe(no_mangle)]
2183pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
2184 builder_handle: *const RKRConFrameBuilder,
2185 index: usize,
2186 out_mass: *mut f64,
2187) -> RKRStatus {
2188 if builder_handle.is_null() || out_mass.is_null() {
2189 return RKRStatus::RKR_STATUS_NULL_POINTER;
2190 }
2191 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2192 match builder.get_atom_mass(index) {
2193 Ok(m) => unsafe {
2194 *out_mass = m;
2195 RKRStatus::RKR_STATUS_SUCCESS
2196 },
2197 Err(e) => map_builder_err(e),
2198 }
2199}
2200pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
2223fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
2224 use crate::error::ParseError;
2225 match e {
2226 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
2227 RKRStatus::RKR_STATUS_DEVICE_MISMATCH
2228 }
2229 ParseError::ValidationError(ref msg)
2230 if msg.contains("no device allocator") || msg.contains("allocator") =>
2231 {
2232 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED
2233 }
2234 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2235 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
2236 }
2237}
2238pub mod rkr_dl_type_code {
2242 pub const RKR_DL_INT: u8 = 0;
2243 pub const RKR_DL_UINT: u8 = 1;
2244 pub const RKR_DL_FLOAT: u8 = 2;
2245 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
2246 pub const RKR_DL_BFLOAT: u8 = 4;
2247 pub const RKR_DL_COMPLEX: u8 = 5;
2248 pub const RKR_DL_BOOL: u8 = 6;
2249}
2250
2251pub mod rkr_dl_device_type {
2253 pub const RKR_DL_CPU: i32 = 1;
2254 pub const RKR_DL_CUDA: i32 = 2;
2255 pub const RKR_DL_CUDA_HOST: i32 = 3;
2256}
2257
2258#[repr(C)]
2262#[derive(Clone, Copy, Debug)]
2263pub struct RKRDLDataType {
2264 pub code: u8,
2266 pub bits: u8,
2268 pub lanes: u16,
2270}
2271
2272#[repr(C)]
2275#[derive(Clone, Copy, Debug)]
2276pub struct RKRDLDevice {
2277 pub device_type: i32,
2279 pub device_id: i32,
2280}
2281
2282#[repr(C)]
2296#[derive(Clone, Copy, Debug)]
2297pub struct RKRDlpackExportOptions {
2298 pub dtype: RKRDLDataType,
2300 pub device: RKRDLDevice,
2302}
2303
2304impl Default for RKRDlpackExportOptions {
2305 fn default() -> Self {
2306 Self {
2307 dtype: RKRDLDataType {
2308 code: rkr_dl_type_code::RKR_DL_FLOAT,
2309 bits: 64,
2310 lanes: 1,
2311 },
2312 device: RKRDLDevice {
2313 device_type: rkr_dl_device_type::RKR_DL_CPU,
2314 device_id: 0,
2315 },
2316 }
2317 }
2318}
2319
2320fn resolve_dlpack_opts(
2323 opts: *const RKRDlpackExportOptions,
2324) -> Result<RKRDlpackExportOptions, RKRStatus> {
2325 let o = if opts.is_null() {
2326 RKRDlpackExportOptions::default()
2327 } else {
2328 unsafe { *opts }
2329 };
2330 let dt = o.device.device_type;
2331 if dt == rkr_dl_device_type::RKR_DL_CPU {
2332 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
2334 #[cfg(not(feature = "cuda"))]
2335 {
2336 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
2337 }
2338 #[cfg(feature = "cuda")]
2339 {
2340 }
2342 } else {
2343 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
2344 }
2345 if o.dtype.lanes != 1 {
2346 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
2347 }
2348 Ok(o)
2349}
2350
2351fn finish_dlpack_tensor<E: std::fmt::Display>(
2352 result: Result<dlpk::DLPackTensor, E>,
2353 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2354) -> RKRStatus {
2355 match result {
2356 Ok(tensor) => {
2357 let raw = tensor.into_raw();
2358 unsafe {
2359 *out_tensor = raw.as_ptr();
2360 }
2361 RKRStatus::RKR_STATUS_SUCCESS
2362 }
2363 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
2364 "DLPack export failed: {e}"
2365 ))),
2366 }
2367}
2368
2369fn export_f64_slice_as_dlpack(
2372 data: &[f64],
2373 shape: &[usize],
2374 dtype: RKRDLDataType,
2375 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2376) -> RKRStatus {
2377 use rkr_dl_type_code::*;
2378 if dtype.lanes != 1 {
2379 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2380 }
2381 macro_rules! arc_export {
2382 ($ty:ty, $map:expr) => {{
2383 let v: Vec<$ty> = data.iter().map($map).collect();
2384 match *shape {
2385 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
2386 Ok(a) => dlpk::DLPackTensor::try_from(a),
2387 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2388 },
2389 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
2390 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2391 }
2392 }};
2393 }
2394 let tensor = match (dtype.code, dtype.bits) {
2395 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
2396 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
2397 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
2398 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
2399 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
2400 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
2401 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
2402 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
2403 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
2404 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
2405 (RKR_DL_BOOL, 8) => {
2406 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
2408 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
2409 }
2410 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2412 };
2413 finish_dlpack_tensor(tensor, out_tensor)
2414}
2415
2416fn export_owned_array2_dlpack_opts(
2417 arr: &ndarray::ArcArray2<f64>,
2418 opts: &RKRDlpackExportOptions,
2419 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2420) -> RKRStatus {
2421 let (r, c) = arr.dim();
2422 let flat: Vec<f64> = arr.iter().copied().collect();
2423 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2424 #[cfg(feature = "cuda")]
2425 {
2426 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2428 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2429 }
2430 return finish_dlpack_tensor(
2431 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2432 &[r, c],
2433 &flat,
2434 opts.device.device_id,
2435 ),
2436 out_tensor,
2437 );
2438 }
2439 #[cfg(not(feature = "cuda"))]
2440 {
2441 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2442 }
2443 }
2444 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
2445}
2446
2447fn export_owned_array1_f64_dlpack_opts(
2448 arr: &ndarray::ArcArray1<f64>,
2449 opts: &RKRDlpackExportOptions,
2450 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2451) -> RKRStatus {
2452 let flat = arr.to_vec();
2453 let n = flat.len();
2454 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2455 #[cfg(feature = "cuda")]
2456 {
2457 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2458 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2459 }
2460 return finish_dlpack_tensor(
2461 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2462 &[n],
2463 &flat,
2464 opts.device.device_id,
2465 ),
2466 out_tensor,
2467 );
2468 }
2469 #[cfg(not(feature = "cuda"))]
2470 {
2471 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2472 }
2473 }
2474 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
2475}
2476
2477fn export_owned_array1_u64_dlpack(
2478 arr: &ndarray::ArcArray1<u64>,
2479 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2480) -> RKRStatus {
2481 finish_dlpack_tensor(dlpk::DLPackTensor::try_from(arr.clone()), out_tensor)
2482}
2483
2484fn export_owned_array1_u64_dlpack_opts(
2486 arr: &ndarray::ArcArray1<u64>,
2487 opts: &RKRDlpackExportOptions,
2488 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2489) -> RKRStatus {
2490 use rkr_dl_type_code::*;
2491 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
2492 return export_owned_array1_u64_dlpack(arr, out_tensor);
2493 }
2494 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
2495 let n = as_f64.len();
2496 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
2497}
2498#[unsafe(no_mangle)]
2509pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
2510 builder_handle: *const RKRConFrameBuilder,
2511 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2512) -> RKRStatus {
2513 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2514}
2515
2516#[unsafe(no_mangle)]
2523pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
2524 builder_handle: *const RKRConFrameBuilder,
2525 opts: *const RKRDlpackExportOptions,
2526 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2527) -> RKRStatus {
2528 if builder_handle.is_null() || out_tensor.is_null() {
2529 return RKRStatus::RKR_STATUS_NULL_POINTER;
2530 }
2531 let o = match resolve_dlpack_opts(opts) {
2532 Ok(o) => o,
2533 Err(st) => return st,
2534 };
2535 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2536 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
2537}
2538
2539#[unsafe(no_mangle)]
2549pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
2550 builder_handle: *const RKRConFrameBuilder,
2551 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2552) -> RKRStatus {
2553 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2554}
2555
2556#[unsafe(no_mangle)]
2557pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
2558 builder_handle: *const RKRConFrameBuilder,
2559 opts: *const RKRDlpackExportOptions,
2560 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2561) -> RKRStatus {
2562 if builder_handle.is_null() || out_tensor.is_null() {
2563 return RKRStatus::RKR_STATUS_NULL_POINTER;
2564 }
2565 let o = match resolve_dlpack_opts(opts) {
2566 Ok(o) => o,
2567 Err(st) => return st,
2568 };
2569 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2570 if !builder.has_velocities_section() {
2571 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2572 }
2573 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
2574}
2575
2576#[unsafe(no_mangle)]
2585pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
2586 builder_handle: *const RKRConFrameBuilder,
2587 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2588) -> RKRStatus {
2589 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2590}
2591
2592#[unsafe(no_mangle)]
2593pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
2594 builder_handle: *const RKRConFrameBuilder,
2595 opts: *const RKRDlpackExportOptions,
2596 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2597) -> RKRStatus {
2598 if builder_handle.is_null() || out_tensor.is_null() {
2599 return RKRStatus::RKR_STATUS_NULL_POINTER;
2600 }
2601 let o = match resolve_dlpack_opts(opts) {
2602 Ok(o) => o,
2603 Err(st) => return st,
2604 };
2605 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2606 if !builder.has_forces_section() {
2607 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2608 }
2609 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
2610}
2611
2612#[unsafe(no_mangle)]
2621pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack(
2622 builder_handle: *const RKRConFrameBuilder,
2623 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2624) -> RKRStatus {
2625 unsafe {
2626 rkr_frame_builder_displacements_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2627 }
2628}
2629
2630#[unsafe(no_mangle)]
2637pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack_ex(
2638 builder_handle: *const RKRConFrameBuilder,
2639 opts: *const RKRDlpackExportOptions,
2640 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2641) -> RKRStatus {
2642 if builder_handle.is_null() || out_tensor.is_null() {
2643 return RKRStatus::RKR_STATUS_NULL_POINTER;
2644 }
2645 let o = match resolve_dlpack_opts(opts) {
2646 Ok(o) => o,
2647 Err(st) => return st,
2648 };
2649 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2650 if !builder.has_displacements_section() {
2651 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2652 }
2653 export_owned_array2_dlpack_opts(builder.displacements_2d_ref(), &o, out_tensor)
2654}
2655
2656#[unsafe(no_mangle)]
2665pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack(
2666 builder_handle: *const RKRConFrameBuilder,
2667 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2668) -> RKRStatus {
2669 unsafe { rkr_frame_builder_spreads_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2670}
2671
2672#[unsafe(no_mangle)]
2679pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack_ex(
2680 builder_handle: *const RKRConFrameBuilder,
2681 opts: *const RKRDlpackExportOptions,
2682 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2683) -> RKRStatus {
2684 if builder_handle.is_null() || out_tensor.is_null() {
2685 return RKRStatus::RKR_STATUS_NULL_POINTER;
2686 }
2687 let o = match resolve_dlpack_opts(opts) {
2688 Ok(o) => o,
2689 Err(st) => return st,
2690 };
2691 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2692 if !builder.has_spreads_section() {
2693 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2694 }
2695 export_owned_array2_dlpack_opts(builder.spreads_2d_ref(), &o, out_tensor)
2696}
2697
2698#[unsafe(no_mangle)]
2707pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
2708 builder_handle: *const RKRConFrameBuilder,
2709 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2710) -> RKRStatus {
2711 unsafe {
2712 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2713 }
2714}
2715
2716#[unsafe(no_mangle)]
2717pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
2718 builder_handle: *const RKRConFrameBuilder,
2719 opts: *const RKRDlpackExportOptions,
2720 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2721) -> RKRStatus {
2722 if builder_handle.is_null() || out_tensor.is_null() {
2723 return RKRStatus::RKR_STATUS_NULL_POINTER;
2724 }
2725 let o = match resolve_dlpack_opts(opts) {
2726 Ok(o) => o,
2727 Err(st) => return st,
2728 };
2729 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2730 if !builder.has_energies_section() {
2731 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2732 }
2733 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
2734}
2735
2736#[unsafe(no_mangle)]
2742pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
2743 builder_handle: *const RKRConFrameBuilder,
2744 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2745) -> RKRStatus {
2746 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2747}
2748
2749#[unsafe(no_mangle)]
2750pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
2751 builder_handle: *const RKRConFrameBuilder,
2752 opts: *const RKRDlpackExportOptions,
2753 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2754) -> RKRStatus {
2755 if builder_handle.is_null() || out_tensor.is_null() {
2756 return RKRStatus::RKR_STATUS_NULL_POINTER;
2757 }
2758 let o = match resolve_dlpack_opts(opts) {
2759 Ok(o) => o,
2760 Err(st) => return st,
2761 };
2762 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2763 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
2764}
2765#[unsafe(no_mangle)]
2771pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
2772 builder_handle: *const RKRConFrameBuilder,
2773 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2774) -> RKRStatus {
2775 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2776}
2777
2778#[unsafe(no_mangle)]
2779pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2780 builder_handle: *const RKRConFrameBuilder,
2781 opts: *const RKRDlpackExportOptions,
2782 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2783) -> RKRStatus {
2784 if builder_handle.is_null() || out_tensor.is_null() {
2785 return RKRStatus::RKR_STATUS_NULL_POINTER;
2786 }
2787 let o = match resolve_dlpack_opts(opts) {
2788 Ok(o) => o,
2789 Err(st) => return st,
2790 };
2791 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2792 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2793}
2794#[unsafe(no_mangle)]
2818pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2819 builder_handle: *mut RKRConFrameBuilder,
2820) -> *mut f64 {
2821 if builder_handle.is_null() {
2822 return std::ptr::null_mut();
2823 }
2824 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2825 builder
2826 .positions_view_mut()
2827 .as_slice_memory_order_mut()
2828 .map(|s| s.as_mut_ptr())
2829 .unwrap_or(std::ptr::null_mut())
2830}
2831#[unsafe(no_mangle)]
2838pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
2839 builder_handle: *mut RKRConFrameBuilder,
2840) -> *mut f64 {
2841 if builder_handle.is_null() {
2842 return std::ptr::null_mut();
2843 }
2844 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2845 if !builder.has_velocities_section() {
2846 return std::ptr::null_mut();
2847 }
2848 let slice = builder.velocities_mut();
2849 if slice.is_empty() {
2850 std::ptr::null_mut()
2851 } else {
2852 slice.as_mut_ptr()
2853 }
2854}
2855#[unsafe(no_mangle)]
2861pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2862 builder_handle: *mut RKRConFrameBuilder,
2863) -> *mut f64 {
2864 if builder_handle.is_null() {
2865 return std::ptr::null_mut();
2866 }
2867 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2868 if !builder.has_forces_section() {
2869 return std::ptr::null_mut();
2870 }
2871 let slice = builder.forces_mut();
2872 if slice.is_empty() {
2873 std::ptr::null_mut()
2874 } else {
2875 slice.as_mut_ptr()
2876 }
2877}
2878#[unsafe(no_mangle)]
2884pub unsafe extern "C" fn rkr_frame_builder_displacements_data(
2885 builder_handle: *mut RKRConFrameBuilder,
2886) -> *mut f64 {
2887 if builder_handle.is_null() {
2888 return std::ptr::null_mut();
2889 }
2890 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2891 if !builder.has_displacements_section() {
2892 return std::ptr::null_mut();
2893 }
2894 let slice = builder.displacements_mut();
2895 if slice.is_empty() {
2896 std::ptr::null_mut()
2897 } else {
2898 slice.as_mut_ptr()
2899 }
2900}
2901#[unsafe(no_mangle)]
2907pub unsafe extern "C" fn rkr_frame_builder_spreads_data(
2908 builder_handle: *mut RKRConFrameBuilder,
2909) -> *mut f64 {
2910 if builder_handle.is_null() {
2911 return std::ptr::null_mut();
2912 }
2913 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2914 if !builder.has_spreads_section() {
2915 return std::ptr::null_mut();
2916 }
2917 let slice = builder.spreads_mut();
2918 if slice.is_empty() {
2919 std::ptr::null_mut()
2920 } else {
2921 slice.as_mut_ptr()
2922 }
2923}
2924#[unsafe(no_mangle)]
2930pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2931 builder_handle: *mut RKRConFrameBuilder,
2932) -> *mut f64 {
2933 if builder_handle.is_null() {
2934 return std::ptr::null_mut();
2935 }
2936 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2937 if !builder.has_energies_section() {
2938 return std::ptr::null_mut();
2939 }
2940 let slice = builder.atom_energies_mut();
2941 if slice.is_empty() {
2942 std::ptr::null_mut()
2943 } else {
2944 slice.as_mut_ptr()
2945 }
2946}
2947#[unsafe(no_mangle)]
2952pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2953 builder_handle: *mut RKRConFrameBuilder,
2954) -> *mut f64 {
2955 if builder_handle.is_null() {
2956 return std::ptr::null_mut();
2957 }
2958 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2959 let slice = builder.masses_mut();
2960 if slice.is_empty() {
2961 std::ptr::null_mut()
2962 } else {
2963 slice.as_mut_ptr()
2964 }
2965}
2966#[unsafe(no_mangle)]
2971pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2972 builder_handle: *const RKRConFrameBuilder,
2973) -> *const u64 {
2974 if builder_handle.is_null() {
2975 return std::ptr::null();
2976 }
2977 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2978 let slice = builder.atom_ids();
2979 if slice.is_empty() {
2980 std::ptr::null()
2981 } else {
2982 slice.as_ptr()
2983 }
2984}
2985#[unsafe(no_mangle)]
2997#[allow(clippy::too_many_arguments)]
2998pub unsafe extern "C" fn rkr_frame_add_atom_full(
2999 builder_handle: *mut RKRConFrameBuilder,
3000 symbol: *const c_char,
3001 x: f64,
3002 y: f64,
3003 z: f64,
3004 fixed_x: bool,
3005 fixed_y: bool,
3006 fixed_z: bool,
3007 atom_id: u64,
3008 mass: f64,
3009 velocity: *const f64,
3010 force: *const f64,
3011) -> RKRStatus {
3012 let velocity = if velocity.is_null() {
3013 None
3014 } else {
3015 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
3016 };
3017 let force = if force.is_null() {
3018 None
3019 } else {
3020 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
3021 };
3022 unsafe {
3023 add_builder_atom(
3024 builder_handle,
3025 symbol,
3026 x,
3027 y,
3028 z,
3029 [fixed_x, fixed_y, fixed_z],
3030 atom_id,
3031 mass,
3032 velocity,
3033 force,
3034 )
3035 }
3036}
3037#[unsafe(no_mangle)]
3053pub unsafe extern "C" fn rkr_frame_new(
3054 cell: *const f64,
3055 angles: *const f64,
3056 prebox0: *const c_char,
3057 prebox1: *const c_char,
3058 postbox0: *const c_char,
3059 postbox1: *const c_char,
3060) -> *mut RKRConFrameBuilder {
3061 if cell.is_null() || angles.is_null() {
3062 return ptr::null_mut();
3063 }
3064 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
3065 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
3066 let get_str = |p: *const c_char| -> String {
3067 if p.is_null() {
3068 String::new()
3069 } else {
3070 unsafe { CStr::from_ptr(p) }
3071 .to_str()
3072 .unwrap_or("")
3073 .to_string()
3074 }
3075 };
3076 let _ = get_str(prebox1);
3079 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
3080 builder
3081 .prebox_header(get_str(prebox0))
3082 .postbox_header([get_str(postbox0), get_str(postbox1)]);
3083 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
3084}
3085#[unsafe(no_mangle)]
3091pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
3092 builder_handle: *mut RKRConFrameBuilder,
3093 metadata_json: *const c_char,
3094) -> RKRStatus {
3095 if builder_handle.is_null() || metadata_json.is_null() {
3096 return RKRStatus::RKR_STATUS_NULL_POINTER;
3097 }
3098 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3099 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
3100 Ok(s) => s,
3101 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3102 };
3103 match builder.set_metadata_json(metadata_json) {
3104 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
3105 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
3106 }
3107}
3108#[unsafe(no_mangle)]
3118pub unsafe extern "C" fn rkr_frame_builder_set_units(
3119 builder_handle: *mut RKRConFrameBuilder,
3120 units_json: *const c_char,
3121) -> RKRStatus {
3122 if builder_handle.is_null() || units_json.is_null() {
3123 return RKRStatus::RKR_STATUS_NULL_POINTER;
3124 }
3125 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3126 let units_json = match unsafe { CStr::from_ptr(units_json).to_str() } {
3127 Ok(s) => s,
3128 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3129 };
3130 let units: serde_json::Value = match serde_json::from_str(units_json) {
3131 Ok(value) => value,
3132 Err(_) => return RKRStatus::RKR_STATUS_INVALID_JSON,
3133 };
3134 match builder.set_units(units) {
3135 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
3136 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
3137 }
3138}
3139#[unsafe(no_mangle)]
3146pub unsafe extern "C" fn rkr_frame_builder_set_con_spec_minor(
3147 builder_handle: *mut RKRConFrameBuilder,
3148 minor: u32,
3149) -> RKRStatus {
3150 if builder_handle.is_null() {
3151 return RKRStatus::RKR_STATUS_NULL_POINTER;
3152 }
3153 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3154 builder.set_con_spec_minor(minor);
3155 RKRStatus::RKR_STATUS_SUCCESS
3156}
3157#[unsafe(no_mangle)]
3163pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
3164 builder_handle: *mut RKRConFrameBuilder,
3165 key: *const c_char,
3166 value: f64,
3167) -> RKRStatus {
3168 if builder_handle.is_null() || key.is_null() {
3169 return RKRStatus::RKR_STATUS_NULL_POINTER;
3170 }
3171 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3172 let key = match unsafe { CStr::from_ptr(key).to_str() } {
3173 Ok(s) => s,
3174 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3175 };
3176 builder.set_scalar_metadata(key, value);
3177 RKRStatus::RKR_STATUS_SUCCESS
3178}
3179#[unsafe(no_mangle)]
3185pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
3186 builder_handle: *mut RKRConFrameBuilder,
3187 key: *const c_char,
3188 value: *const c_char,
3189) -> RKRStatus {
3190 if builder_handle.is_null() || key.is_null() || value.is_null() {
3191 return RKRStatus::RKR_STATUS_NULL_POINTER;
3192 }
3193 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3194 let key = match unsafe { CStr::from_ptr(key).to_str() } {
3195 Ok(s) => s,
3196 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3197 };
3198 let value = match unsafe { CStr::from_ptr(value).to_str() } {
3199 Ok(s) => s,
3200 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3201 };
3202 builder.set_string_metadata(key, value);
3203 RKRStatus::RKR_STATUS_SUCCESS
3204}
3205#[unsafe(no_mangle)]
3211pub unsafe extern "C" fn rkr_frame_builder_set_energy(
3212 builder_handle: *mut RKRConFrameBuilder,
3213 energy: f64,
3214) -> RKRStatus {
3215 if builder_handle.is_null() {
3216 return RKRStatus::RKR_STATUS_NULL_POINTER;
3217 }
3218 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3219 builder.set_energy(energy);
3220 RKRStatus::RKR_STATUS_SUCCESS
3221}
3222#[unsafe(no_mangle)]
3228pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
3229 builder_handle: *mut RKRConFrameBuilder,
3230 idx: u64,
3231) -> RKRStatus {
3232 if builder_handle.is_null() {
3233 return RKRStatus::RKR_STATUS_NULL_POINTER;
3234 }
3235 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3236 builder.set_frame_index(idx);
3237 RKRStatus::RKR_STATUS_SUCCESS
3238}
3239#[unsafe(no_mangle)]
3245pub unsafe extern "C" fn rkr_frame_builder_set_time(
3246 builder_handle: *mut RKRConFrameBuilder,
3247 time: f64,
3248) -> RKRStatus {
3249 if builder_handle.is_null() {
3250 return RKRStatus::RKR_STATUS_NULL_POINTER;
3251 }
3252 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3253 builder.set_time(time);
3254 RKRStatus::RKR_STATUS_SUCCESS
3255}
3256#[unsafe(no_mangle)]
3262pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
3263 builder_handle: *mut RKRConFrameBuilder,
3264 dt: f64,
3265) -> RKRStatus {
3266 if builder_handle.is_null() {
3267 return RKRStatus::RKR_STATUS_NULL_POINTER;
3268 }
3269 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3270 builder.set_timestep(dt);
3271 RKRStatus::RKR_STATUS_SUCCESS
3272}
3273#[unsafe(no_mangle)]
3279pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
3280 builder_handle: *mut RKRConFrameBuilder,
3281 bead: u64,
3282) -> RKRStatus {
3283 if builder_handle.is_null() {
3284 return RKRStatus::RKR_STATUS_NULL_POINTER;
3285 }
3286 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3287 builder.set_neb_bead(bead);
3288 RKRStatus::RKR_STATUS_SUCCESS
3289}
3290#[unsafe(no_mangle)]
3296pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
3297 builder_handle: *mut RKRConFrameBuilder,
3298 band: u64,
3299) -> RKRStatus {
3300 if builder_handle.is_null() {
3301 return RKRStatus::RKR_STATUS_NULL_POINTER;
3302 }
3303 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3304 builder.set_neb_band(band);
3305 RKRStatus::RKR_STATUS_SUCCESS
3306}
3307#[unsafe(no_mangle)]
3324pub unsafe extern "C" fn rkr_frame_add_atom(
3325 builder_handle: *mut RKRConFrameBuilder,
3326 symbol: *const c_char,
3327 x: f64,
3328 y: f64,
3329 z: f64,
3330 is_fixed: bool,
3331 atom_id: u64,
3332 mass: f64,
3333) -> RKRStatus {
3334 unsafe {
3335 add_builder_atom(
3336 builder_handle,
3337 symbol,
3338 x,
3339 y,
3340 z,
3341 [is_fixed; 3],
3342 atom_id,
3343 mass,
3344 None,
3345 None,
3346 )
3347 }
3348}
3349#[unsafe(no_mangle)]
3356pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
3357 builder_handle: *mut RKRConFrameBuilder,
3358 symbol: *const c_char,
3359 x: f64,
3360 y: f64,
3361 z: f64,
3362 fixed_x: bool,
3363 fixed_y: bool,
3364 fixed_z: bool,
3365 atom_id: u64,
3366 mass: f64,
3367) -> RKRStatus {
3368 unsafe {
3369 add_builder_atom(
3370 builder_handle,
3371 symbol,
3372 x,
3373 y,
3374 z,
3375 [fixed_x, fixed_y, fixed_z],
3376 atom_id,
3377 mass,
3378 None,
3379 None,
3380 )
3381 }
3382}
3383#[unsafe(no_mangle)]
3390pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
3391 builder_handle: *mut RKRConFrameBuilder,
3392 symbol: *const c_char,
3393 x: f64,
3394 y: f64,
3395 z: f64,
3396 is_fixed: bool,
3397 atom_id: u64,
3398 mass: f64,
3399 vx: f64,
3400 vy: f64,
3401 vz: f64,
3402) -> RKRStatus {
3403 unsafe {
3404 add_builder_atom(
3405 builder_handle,
3406 symbol,
3407 x,
3408 y,
3409 z,
3410 [is_fixed; 3],
3411 atom_id,
3412 mass,
3413 Some([vx, vy, vz]),
3414 None,
3415 )
3416 }
3417}
3418#[unsafe(no_mangle)]
3425pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
3426 builder_handle: *mut RKRConFrameBuilder,
3427 symbol: *const c_char,
3428 x: f64,
3429 y: f64,
3430 z: f64,
3431 fixed_x: bool,
3432 fixed_y: bool,
3433 fixed_z: bool,
3434 atom_id: u64,
3435 mass: f64,
3436 vx: f64,
3437 vy: f64,
3438 vz: f64,
3439) -> RKRStatus {
3440 unsafe {
3441 add_builder_atom(
3442 builder_handle,
3443 symbol,
3444 x,
3445 y,
3446 z,
3447 [fixed_x, fixed_y, fixed_z],
3448 atom_id,
3449 mass,
3450 Some([vx, vy, vz]),
3451 None,
3452 )
3453 }
3454}
3455#[unsafe(no_mangle)]
3462pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
3463 builder_handle: *mut RKRConFrameBuilder,
3464 symbol: *const c_char,
3465 x: f64,
3466 y: f64,
3467 z: f64,
3468 is_fixed: bool,
3469 atom_id: u64,
3470 mass: f64,
3471 fx: f64,
3472 fy: f64,
3473 fz: f64,
3474) -> RKRStatus {
3475 unsafe {
3476 add_builder_atom(
3477 builder_handle,
3478 symbol,
3479 x,
3480 y,
3481 z,
3482 [is_fixed; 3],
3483 atom_id,
3484 mass,
3485 None,
3486 Some([fx, fy, fz]),
3487 )
3488 }
3489}
3490#[unsafe(no_mangle)]
3497pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
3498 builder_handle: *mut RKRConFrameBuilder,
3499 symbol: *const c_char,
3500 x: f64,
3501 y: f64,
3502 z: f64,
3503 fixed_x: bool,
3504 fixed_y: bool,
3505 fixed_z: bool,
3506 atom_id: u64,
3507 mass: f64,
3508 fx: f64,
3509 fy: f64,
3510 fz: f64,
3511) -> RKRStatus {
3512 unsafe {
3513 add_builder_atom(
3514 builder_handle,
3515 symbol,
3516 x,
3517 y,
3518 z,
3519 [fixed_x, fixed_y, fixed_z],
3520 atom_id,
3521 mass,
3522 None,
3523 Some([fx, fy, fz]),
3524 )
3525 }
3526}
3527#[unsafe(no_mangle)]
3534pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
3535 builder_handle: *mut RKRConFrameBuilder,
3536 symbol: *const c_char,
3537 x: f64,
3538 y: f64,
3539 z: f64,
3540 is_fixed: bool,
3541 atom_id: u64,
3542 mass: f64,
3543 vx: f64,
3544 vy: f64,
3545 vz: f64,
3546 fx: f64,
3547 fy: f64,
3548 fz: f64,
3549) -> RKRStatus {
3550 unsafe {
3551 add_builder_atom(
3552 builder_handle,
3553 symbol,
3554 x,
3555 y,
3556 z,
3557 [is_fixed; 3],
3558 atom_id,
3559 mass,
3560 Some([vx, vy, vz]),
3561 Some([fx, fy, fz]),
3562 )
3563 }
3564}
3565#[unsafe(no_mangle)]
3572pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
3573 builder_handle: *mut RKRConFrameBuilder,
3574 symbol: *const c_char,
3575 x: f64,
3576 y: f64,
3577 z: f64,
3578 fixed_x: bool,
3579 fixed_y: bool,
3580 fixed_z: bool,
3581 atom_id: u64,
3582 mass: f64,
3583 vx: f64,
3584 vy: f64,
3585 vz: f64,
3586 fx: f64,
3587 fy: f64,
3588 fz: f64,
3589) -> RKRStatus {
3590 unsafe {
3591 add_builder_atom(
3592 builder_handle,
3593 symbol,
3594 x,
3595 y,
3596 z,
3597 [fixed_x, fixed_y, fixed_z],
3598 atom_id,
3599 mass,
3600 Some([vx, vy, vz]),
3601 Some([fx, fy, fz]),
3602 )
3603 }
3604}
3605#[unsafe(no_mangle)]
3614pub unsafe extern "C" fn rkr_frame_builder_build(
3615 builder_handle: *mut RKRConFrameBuilder,
3616) -> *mut RKRConFrame {
3617 if builder_handle.is_null() {
3618 return ptr::null_mut();
3619 }
3620 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3621 match builder.build() {
3622 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3623 Err(_) => ptr::null_mut(),
3624 }
3625}
3626#[unsafe(no_mangle)]
3631pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
3632 if !builder_handle.is_null() {
3633 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3634 }
3635}
3636#[unsafe(no_mangle)]
3653pub unsafe extern "C" fn rkr_frame_builder_clone(
3654 builder_handle: *const RKRConFrameBuilder,
3655) -> *mut RKRConFrameBuilder {
3656 if builder_handle.is_null() {
3657 return std::ptr::null_mut();
3658 }
3659 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3660 let cloned = builder.clone();
3661 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
3662}
3663#[unsafe(no_mangle)]
3669pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3670 let filename = match unsafe { cstr_path(filename_c) } {
3671 Some(s) => s,
3672 None => return ptr::null_mut(),
3673 };
3674 match crate::compression::gzip_writer(Path::new(filename)) {
3675 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3676 Err(_) => ptr::null_mut(),
3677 }
3678}
3679#[unsafe(no_mangle)]
3686pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
3687 filename_c: *const c_char,
3688 precision: u8,
3689) -> *mut RKRConFrameWriter {
3690 let filename = match unsafe { cstr_path(filename_c) } {
3691 Some(s) => s,
3692 None => return ptr::null_mut(),
3693 };
3694 match crate::compression::gzip_writer(Path::new(filename)) {
3695 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3696 Err(_) => ptr::null_mut(),
3697 }
3698}
3699#[cfg(feature = "zstd")]
3709#[unsafe(no_mangle)]
3710pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3711 let filename = match unsafe { cstr_path(filename_c) } {
3712 Some(s) => s,
3713 None => return ptr::null_mut(),
3714 };
3715 match crate::compression::zstd_writer(Path::new(filename)) {
3716 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3717 Err(_) => ptr::null_mut(),
3718 }
3719}
3720#[cfg(feature = "zstd")]
3731#[unsafe(no_mangle)]
3732pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3733 filename_c: *const c_char,
3734 precision: u8,
3735) -> *mut RKRConFrameWriter {
3736 let filename = match unsafe { cstr_path(filename_c) } {
3737 Some(s) => s,
3738 None => return ptr::null_mut(),
3739 };
3740 match crate::compression::zstd_writer(Path::new(filename)) {
3741 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3742 Err(_) => ptr::null_mut(),
3743 }
3744}
3745#[unsafe(no_mangle)]
3757pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
3758 if filename_c.is_null() {
3759 return ptr::null_mut();
3760 }
3761 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3762 Ok(s) => s,
3763 Err(_) => return ptr::null_mut(),
3764 };
3765 match iterators::read_first_frame(Path::new(filename)) {
3766 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3767 Err(_) => ptr::null_mut(),
3768 }
3769}
3770#[unsafe(no_mangle)]
3779pub unsafe extern "C" fn rkr_read_all_frames(
3780 filename_c: *const c_char,
3781 num_frames: *mut usize,
3782) -> *mut *mut RKRConFrame {
3783 if filename_c.is_null() || num_frames.is_null() {
3784 return ptr::null_mut();
3785 }
3786 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3787 Ok(s) => s,
3788 Err(_) => return ptr::null_mut(),
3789 };
3790 match iterators::read_all_frames(Path::new(filename)) {
3791 Ok(frames) => pack_frame_handles(frames, num_frames),
3792 Err(_) => ptr::null_mut(),
3793 }
3794}
3795
3796#[unsafe(no_mangle)]
3806pub unsafe extern "C" fn rkr_read_all_frames_n_threads(
3807 filename_c: *const c_char,
3808 num_frames: *mut usize,
3809 n_threads: usize,
3810) -> *mut *mut RKRConFrame {
3811 if filename_c.is_null() || num_frames.is_null() {
3812 return ptr::null_mut();
3813 }
3814 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3815 Ok(s) => s,
3816 Err(_) => return ptr::null_mut(),
3817 };
3818 let threads = if n_threads == 0 {
3819 None
3820 } else {
3821 Some(n_threads)
3822 };
3823 match iterators::read_all_frames_with_threads(Path::new(filename), threads) {
3824 Ok(frames) => pack_frame_handles(frames, num_frames),
3825 Err(_) => ptr::null_mut(),
3826 }
3827}
3828
3829pub(crate) fn pack_frame_handles(
3831 frames: Vec<ConFrame>,
3832 num_frames: *mut usize,
3833) -> *mut *mut RKRConFrame {
3834 let count = frames.len();
3835 let mut handles: Vec<*mut RKRConFrame> = frames
3836 .into_iter()
3837 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3838 .collect();
3839 handles.shrink_to_fit();
3840 debug_assert_eq!(handles.len(), handles.capacity());
3841 let ptr = handles.as_mut_ptr();
3842 std::mem::forget(handles);
3843 unsafe { *num_frames = count };
3844 ptr
3845}
3846#[unsafe(no_mangle)]
3852pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3853 if frames.is_null() {
3854 return;
3855 }
3856 unsafe {
3857 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3858 for handle in handles {
3859 if !handle.is_null() {
3860 let _ = Box::from_raw(handle as *mut ConFrame);
3861 }
3862 }
3863 }
3864}
3865#[unsafe(no_mangle)]
3871pub unsafe extern "C" fn free_rkr_frame_ptr_array(
3872 frames: *mut *mut RKRConFrame,
3873 num_frames: usize,
3874) {
3875 if frames.is_null() {
3876 return;
3877 }
3878 unsafe {
3879 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3880 }
3881}
3882
3883#[cfg(feature = "metatensor")]
3898#[unsafe(no_mangle)]
3899pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3900 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3901}
3902#[cfg(feature = "metatensor")]
3904#[unsafe(no_mangle)]
3905pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3906 frame_handle: *const RKRConFrame,
3907 out_block: *mut *mut metatensor::c_api::mts_block_t,
3908) -> RKRStatus {
3909 if frame_handle.is_null() || out_block.is_null() {
3910 return RKRStatus::RKR_STATUS_NULL_POINTER;
3911 }
3912 unsafe { *out_block = std::ptr::null_mut() };
3913 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3914 return RKRStatus::RKR_STATUS_NULL_POINTER;
3915 };
3916 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3917 crate::metatensor_export::frame_positions_block(frame)
3918 })) {
3919 Ok(Ok(b)) => {
3920 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3921 RKRStatus::RKR_STATUS_SUCCESS
3922 }
3923 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3924 }
3925}
3926#[cfg(feature = "metatensor")]
3927#[unsafe(no_mangle)]
3928pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3929 frame_handle: *const RKRConFrame,
3930 out_block: *mut *mut metatensor::c_api::mts_block_t,
3931) -> RKRStatus {
3932 if frame_handle.is_null() || out_block.is_null() {
3933 return RKRStatus::RKR_STATUS_NULL_POINTER;
3934 }
3935 unsafe { *out_block = std::ptr::null_mut() };
3936 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3937 return RKRStatus::RKR_STATUS_NULL_POINTER;
3938 };
3939 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3940 crate::metatensor_export::frame_velocities_block(frame)
3941 })) {
3942 Ok(Ok(Some(b))) => {
3943 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3944 RKRStatus::RKR_STATUS_SUCCESS
3945 }
3946 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3947 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3948 }
3949}
3950#[cfg(feature = "metatensor")]
3951#[unsafe(no_mangle)]
3952pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3953 frame_handle: *const RKRConFrame,
3954 out_block: *mut *mut metatensor::c_api::mts_block_t,
3955) -> RKRStatus {
3956 if frame_handle.is_null() || out_block.is_null() {
3957 return RKRStatus::RKR_STATUS_NULL_POINTER;
3958 }
3959 unsafe { *out_block = std::ptr::null_mut() };
3960 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3961 return RKRStatus::RKR_STATUS_NULL_POINTER;
3962 };
3963 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3964 crate::metatensor_export::frame_forces_block(frame)
3965 })) {
3966 Ok(Ok(Some(b))) => {
3967 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3968 RKRStatus::RKR_STATUS_SUCCESS
3969 }
3970 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3971 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3972 }
3973}
3974#[cfg(feature = "metatensor")]
3975#[unsafe(no_mangle)]
3976pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3977 frame_handle: *const RKRConFrame,
3978 out_block: *mut *mut metatensor::c_api::mts_block_t,
3979) -> RKRStatus {
3980 if frame_handle.is_null() || out_block.is_null() {
3981 return RKRStatus::RKR_STATUS_NULL_POINTER;
3982 }
3983 unsafe { *out_block = std::ptr::null_mut() };
3984 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3985 return RKRStatus::RKR_STATUS_NULL_POINTER;
3986 };
3987 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3988 crate::metatensor_export::frame_energies_block(frame)
3989 })) {
3990 Ok(Ok(Some(b))) => {
3991 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3992 RKRStatus::RKR_STATUS_SUCCESS
3993 }
3994 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3995 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3996 }
3997}
3998#[cfg(not(feature = "zstd"))]
3999#[unsafe(no_mangle)]
4000pub unsafe extern "C" fn create_writer_zstd_c(
4001 _filename_c: *const c_char,
4002) -> *mut RKRConFrameWriter {
4003 ptr::null_mut()
4004}
4005#[cfg(not(feature = "zstd"))]
4006#[unsafe(no_mangle)]
4007pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
4008 _filename_c: *const c_char,
4009 _precision: u8,
4010) -> *mut RKRConFrameWriter {
4011 ptr::null_mut()
4012}
4013#[cfg(not(feature = "metatensor"))]
4017pub struct mts_block_t {
4020 _private: [u8; 0],
4021}
4022#[cfg(not(feature = "metatensor"))]
4023#[unsafe(no_mangle)]
4024pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
4025#[cfg(not(feature = "metatensor"))]
4026#[unsafe(no_mangle)]
4027pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
4028 _frame_handle: *const RKRConFrame,
4029 out_block: *mut *mut mts_block_t,
4030) -> RKRStatus {
4031 if !out_block.is_null() {
4032 unsafe { *out_block = std::ptr::null_mut() };
4033 }
4034 RKRStatus::RKR_STATUS_FEATURE_DISABLED
4035}
4036#[cfg(not(feature = "metatensor"))]
4037#[unsafe(no_mangle)]
4038pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
4039 _frame_handle: *const RKRConFrame,
4040 out_block: *mut *mut mts_block_t,
4041) -> RKRStatus {
4042 if !out_block.is_null() {
4043 unsafe { *out_block = std::ptr::null_mut() };
4044 }
4045 RKRStatus::RKR_STATUS_FEATURE_DISABLED
4046}
4047#[cfg(not(feature = "metatensor"))]
4048#[unsafe(no_mangle)]
4049pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
4050 _frame_handle: *const RKRConFrame,
4051 out_block: *mut *mut mts_block_t,
4052) -> RKRStatus {
4053 if !out_block.is_null() {
4054 unsafe { *out_block = std::ptr::null_mut() };
4055 }
4056 RKRStatus::RKR_STATUS_FEATURE_DISABLED
4057}
4058#[cfg(not(feature = "metatensor"))]
4059#[unsafe(no_mangle)]
4060pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
4061 _frame_handle: *const RKRConFrame,
4062 out_block: *mut *mut mts_block_t,
4063) -> RKRStatus {
4064 if !out_block.is_null() {
4065 unsafe { *out_block = std::ptr::null_mut() };
4066 }
4067 RKRStatus::RKR_STATUS_FEATURE_DISABLED
4068}
4069#[unsafe(no_mangle)]
4072pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
4073 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4074 return 0;
4075 };
4076 frame.atom_data.len()
4077}
4078#[unsafe(no_mangle)]
4080pub unsafe extern "C" fn rkr_frame_xyz_view(
4081 frame_handle: *const RKRConFrame,
4082 out: *mut RKRArrayView,
4083) -> RKRStatus {
4084 fill_array2_view(frame_handle, out, |f| &f.positions, false)
4085}
4086
4087#[unsafe(no_mangle)]
4089pub unsafe extern "C" fn rkr_frame_velocities_view(
4090 frame_handle: *const RKRConFrame,
4091 out: *mut RKRArrayView,
4092) -> RKRStatus {
4093 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
4094}
4095
4096#[unsafe(no_mangle)]
4098pub unsafe extern "C" fn rkr_frame_forces_view(
4099 frame_handle: *const RKRConFrame,
4100 out: *mut RKRArrayView,
4101) -> RKRStatus {
4102 fill_array2_view(frame_handle, out, |f| &f.forces, true)
4103}
4104
4105#[unsafe(no_mangle)]
4111pub unsafe extern "C" fn rkr_frame_has_displacements(frame_handle: *const RKRConFrame) -> bool {
4112 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
4113 Some(f) => f.has_displacements(),
4114 None => false,
4115 }
4116}
4117
4118#[unsafe(no_mangle)]
4123pub unsafe extern "C" fn rkr_frame_displacements_view(
4124 frame_handle: *const RKRConFrame,
4125 out: *mut RKRArrayView,
4126) -> RKRStatus {
4127 fill_array2_view(frame_handle, out, |f| &f.displacements, true)
4128}
4129
4130#[unsafe(no_mangle)]
4136pub unsafe extern "C" fn rkr_frame_has_spreads(frame_handle: *const RKRConFrame) -> bool {
4137 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
4138 Some(f) => f.has_spreads(),
4139 None => false,
4140 }
4141}
4142
4143#[unsafe(no_mangle)]
4148pub unsafe extern "C" fn rkr_frame_spreads_view(
4149 frame_handle: *const RKRConFrame,
4150 out: *mut RKRArrayView,
4151) -> RKRStatus {
4152 fill_array2_view(frame_handle, out, |f| &f.spreads, true)
4153}
4154
4155#[unsafe(no_mangle)]
4157pub unsafe extern "C" fn rkr_frame_energies_view(
4158 frame_handle: *const RKRConFrame,
4159 out: *mut RKRArrayView,
4160) -> RKRStatus {
4161 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
4162}
4163
4164#[unsafe(no_mangle)]
4166pub unsafe extern "C" fn rkr_frame_masses_view(
4167 frame_handle: *const RKRConFrame,
4168 out: *mut RKRArrayView,
4169) -> RKRStatus {
4170 fill_array1_view(frame_handle, out, |f| &f.masses, false)
4171}
4172
4173#[unsafe(no_mangle)]
4175pub unsafe extern "C" fn rkr_frame_atom_ids_view(
4176 frame_handle: *const RKRConFrame,
4177 out: *mut RKRArrayView,
4178) -> RKRStatus {
4179 if frame_handle.is_null() || out.is_null() {
4180 return RKRStatus::RKR_STATUS_NULL_POINTER;
4181 }
4182 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4183 return RKRStatus::RKR_STATUS_NULL_POINTER;
4184 };
4185 let slice = frame.atom_ids.as_slice_memory_order();
4186 unsafe {
4187 *out = RKRArrayView {
4188 data: slice
4189 .map(|s| s.as_ptr() as *const std::ffi::c_void)
4190 .unwrap_or(std::ptr::null()),
4191 n: frame.atom_ids.len(),
4192 cols: 1,
4193 dtype_code: 1, dtype_bits: 64,
4195 };
4196 }
4197 RKRStatus::RKR_STATUS_SUCCESS
4198}
4199
4200#[unsafe(no_mangle)]
4203pub unsafe extern "C" fn rkr_frame_xyz_f64(
4204 frame_handle: *const RKRConFrame,
4205 n: *mut usize,
4206) -> *const f64 {
4207 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
4208}
4209
4210#[unsafe(no_mangle)]
4212pub unsafe extern "C" fn rkr_frame_velocities_f64(
4213 frame_handle: *const RKRConFrame,
4214 n: *mut usize,
4215) -> *const f64 {
4216 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
4217}
4218
4219#[unsafe(no_mangle)]
4221pub unsafe extern "C" fn rkr_frame_forces_f64(
4222 frame_handle: *const RKRConFrame,
4223 n: *mut usize,
4224) -> *const f64 {
4225 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
4226}
4227
4228#[unsafe(no_mangle)]
4233pub unsafe extern "C" fn rkr_frame_displacements_f64(
4234 frame_handle: *const RKRConFrame,
4235 n: *mut usize,
4236) -> *const f64 {
4237 f64_col_ptr(frame_handle, n, |f| f.displacements.f64_slice())
4238}
4239
4240#[unsafe(no_mangle)]
4245pub unsafe extern "C" fn rkr_frame_spreads_f64(
4246 frame_handle: *const RKRConFrame,
4247 n: *mut usize,
4248) -> *const f64 {
4249 f64_col_ptr(frame_handle, n, |f| f.spreads.f64_slice())
4250}
4251
4252fn fill_array2_view(
4253 frame_handle: *const RKRConFrame,
4254 out: *mut RKRArrayView,
4255 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4256 section: bool,
4257) -> RKRStatus {
4258 if frame_handle.is_null() || out.is_null() {
4259 return RKRStatus::RKR_STATUS_NULL_POINTER;
4260 }
4261 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4262 return RKRStatus::RKR_STATUS_NULL_POINTER;
4263 };
4264 let arr = get(frame);
4265 if section && arr.nrows() == 0 {
4266 unsafe { *out = RKRArrayView::empty() };
4267 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4268 }
4269 unsafe { *out = RKRArrayView::from_array2(arr) };
4270 RKRStatus::RKR_STATUS_SUCCESS
4271}
4272
4273fn fill_array1_view(
4274 frame_handle: *const RKRConFrame,
4275 out: *mut RKRArrayView,
4276 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4277 section: bool,
4278) -> RKRStatus {
4279 if frame_handle.is_null() || out.is_null() {
4280 return RKRStatus::RKR_STATUS_NULL_POINTER;
4281 }
4282 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4283 return RKRStatus::RKR_STATUS_NULL_POINTER;
4284 };
4285 let arr = get(frame);
4286 if section && arr.len() == 0 {
4287 unsafe { *out = RKRArrayView::empty() };
4288 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4289 }
4290 unsafe { *out = RKRArrayView::from_array1(arr) };
4291 RKRStatus::RKR_STATUS_SUCCESS
4292}
4293
4294fn f64_col_ptr(
4295 frame_handle: *const RKRConFrame,
4296 n: *mut usize,
4297 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
4298) -> *const f64 {
4299 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4300 if !n.is_null() {
4301 unsafe { *n = 0 };
4302 }
4303 return std::ptr::null();
4304 };
4305 match get(frame) {
4306 Some(s) if !s.is_empty() => {
4307 if !n.is_null() {
4308 unsafe { *n = s.len() / 3 };
4309 }
4310 s.as_ptr()
4311 }
4312 _ => {
4313 if !n.is_null() {
4314 unsafe { *n = 0 };
4315 }
4316 std::ptr::null()
4317 }
4318 }
4319}
4320
4321#[unsafe(no_mangle)]
4324pub unsafe extern "C" fn rkr_frame_copy_positions(
4325 frame_handle: *const RKRConFrame,
4326 out: *mut f64,
4327 out_len: usize,
4328) -> RKRStatus {
4329 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
4330}
4331fn copy_array2_f64(
4332 frame_handle: *const RKRConFrame,
4333 out: *mut f64,
4334 out_len: usize,
4335 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4336 section: bool,
4337) -> RKRStatus {
4338 if frame_handle.is_null() || out.is_null() {
4339 return RKRStatus::RKR_STATUS_NULL_POINTER;
4340 }
4341 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4342 return RKRStatus::RKR_STATUS_NULL_POINTER;
4343 };
4344 let arr = get(frame);
4345 let n = if arr.nrows() > 0 {
4346 arr.nrows()
4347 } else {
4348 frame.atom_data.len()
4349 };
4350 if section && arr.nrows() == 0 {
4351 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4352 }
4353 let need = n.saturating_mul(3);
4354 if out_len < need {
4355 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4356 }
4357 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
4358 if arr.nrows() == n {
4359 if let Some(src) = arr.f64_slice() {
4360 dest.copy_from_slice(&src[..need.min(src.len())]);
4361 return RKRStatus::RKR_STATUS_SUCCESS;
4362 }
4363 for i in 0..n {
4364 let row = arr.as_f64_row(i);
4365 dest[i * 3] = row[0];
4366 dest[i * 3 + 1] = row[1];
4367 dest[i * 3 + 2] = row[2];
4368 }
4369 return RKRStatus::RKR_STATUS_SUCCESS;
4370 }
4371 for (i, a) in frame.atom_data.iter().enumerate() {
4372 dest[i * 3] = a.x;
4373 dest[i * 3 + 1] = a.y;
4374 dest[i * 3 + 2] = a.z;
4375 }
4376 RKRStatus::RKR_STATUS_SUCCESS
4377}
4378
4379fn copy_array1_f64(
4380 frame_handle: *const RKRConFrame,
4381 out: *mut f64,
4382 out_len: usize,
4383 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4384 section: bool,
4385) -> RKRStatus {
4386 if frame_handle.is_null() || out.is_null() {
4387 return RKRStatus::RKR_STATUS_NULL_POINTER;
4388 }
4389 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4390 return RKRStatus::RKR_STATUS_NULL_POINTER;
4391 };
4392 let arr = get(frame);
4393 let n = if arr.len() > 0 {
4394 arr.len()
4395 } else {
4396 frame.atom_data.len()
4397 };
4398 if section && arr.len() == 0 {
4399 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4400 }
4401 if out_len < n {
4402 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4403 }
4404 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4405 if let Some(src) = arr.f64_slice() {
4406 dest.copy_from_slice(&src[..n.min(src.len())]);
4407 return RKRStatus::RKR_STATUS_SUCCESS;
4408 }
4409 for i in 0..n {
4410 dest[i] = arr.get_f64(i);
4411 }
4412 RKRStatus::RKR_STATUS_SUCCESS
4413}
4414
4415#[unsafe(no_mangle)]
4416pub unsafe extern "C" fn rkr_frame_copy_velocities(
4417 frame_handle: *const RKRConFrame,
4418 out: *mut f64,
4419 out_len: usize,
4420) -> RKRStatus {
4421 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
4422}
4423#[unsafe(no_mangle)]
4424pub unsafe extern "C" fn rkr_frame_copy_forces(
4425 frame_handle: *const RKRConFrame,
4426 out: *mut f64,
4427 out_len: usize,
4428) -> RKRStatus {
4429 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
4430}
4431#[unsafe(no_mangle)]
4437pub unsafe extern "C" fn rkr_frame_copy_displacements(
4438 frame_handle: *const RKRConFrame,
4439 out: *mut f64,
4440 out_len: usize,
4441) -> RKRStatus {
4442 copy_array2_f64(frame_handle, out, out_len, |f| &f.displacements, true)
4443}
4444#[unsafe(no_mangle)]
4450pub unsafe extern "C" fn rkr_frame_copy_spreads(
4451 frame_handle: *const RKRConFrame,
4452 out: *mut f64,
4453 out_len: usize,
4454) -> RKRStatus {
4455 copy_array2_f64(frame_handle, out, out_len, |f| &f.spreads, true)
4456}
4457#[unsafe(no_mangle)]
4458pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
4459 frame_handle: *const RKRConFrame,
4460 out: *mut f64,
4461 out_len: usize,
4462) -> RKRStatus {
4463 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
4464}
4465#[unsafe(no_mangle)]
4466pub unsafe extern "C" fn rkr_frame_copy_masses(
4467 frame_handle: *const RKRConFrame,
4468 out: *mut f64,
4469 out_len: usize,
4470) -> RKRStatus {
4471 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
4472}
4473#[unsafe(no_mangle)]
4474pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
4475 frame_handle: *const RKRConFrame,
4476 out: *mut u64,
4477 out_len: usize,
4478) -> RKRStatus {
4479 if frame_handle.is_null() || out.is_null() {
4480 return RKRStatus::RKR_STATUS_NULL_POINTER;
4481 }
4482 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4483 return RKRStatus::RKR_STATUS_NULL_POINTER;
4484 };
4485 let n = frame.atom_ids.len().max(frame.atom_data.len());
4486 if out_len < n {
4487 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4488 }
4489 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4490 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
4491 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
4492 return RKRStatus::RKR_STATUS_SUCCESS;
4493 }
4494 for (i, a) in frame.atom_data.iter().enumerate() {
4495 dest[i] = a.atom_id;
4496 }
4497 RKRStatus::RKR_STATUS_SUCCESS
4498}
4499fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
4500 let n = frame.atom_data.len();
4501 let mut data = Vec::with_capacity(n * 3);
4502 for a in &frame.atom_data {
4503 data.extend_from_slice(&[a.x, a.y, a.z]);
4504 }
4505 ndarray::ArcArray2::from_shape_vec((n, 3), data)
4506 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
4507}
4508
4509#[unsafe(no_mangle)]
4519pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
4520 frame_handle: *const RKRConFrame,
4521 device_type: i32,
4522 device_id: i32,
4523 _stream: i64,
4524 _max_version_major: u32,
4525 _max_version_minor: u32,
4526 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4527) -> RKRStatus {
4528 if frame_handle.is_null() || out_tensor.is_null() {
4529 return RKRStatus::RKR_STATUS_NULL_POINTER;
4530 }
4531 unsafe { *out_tensor = std::ptr::null_mut() };
4532 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
4533 dlpk::sys::DLDevice::cpu()
4534 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
4535 #[cfg(feature = "cuda")]
4536 {
4537 dlpk::sys::DLDevice::cuda(device_id)
4538 }
4539 #[cfg(not(feature = "cuda"))]
4540 {
4541 let _ = device_id;
4542 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4543 }
4544 } else {
4545 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4546 };
4547 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4548 return RKRStatus::RKR_STATUS_NULL_POINTER;
4549 };
4550 match frame.positions_as_dlpack(dl_device) {
4552 Ok(tensor) => {
4553 let raw = tensor.into_raw();
4554 unsafe {
4555 *out_tensor = raw.as_ptr();
4556 }
4557 RKRStatus::RKR_STATUS_SUCCESS
4558 }
4559 Err(e) => map_dlpack_err(e),
4560 }
4561}
4562
4563#[unsafe(no_mangle)]
4569pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
4570 frame_handle: *mut RKRConFrame,
4571 tensor: *const RKRDLManagedTensorVersioned,
4572) -> RKRStatus {
4573 if frame_handle.is_null() || tensor.is_null() {
4574 return RKRStatus::RKR_STATUS_NULL_POINTER;
4575 }
4576 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
4577 let dl = unsafe { &(*tensor).dl_tensor };
4578 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
4579 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4580 }
4581 let n = frame.atom_data.len();
4582 let need = n.saturating_mul(3);
4583 let ndim = dl.ndim as usize;
4584 let shape = if dl.shape.is_null() {
4585 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4586 } else {
4587 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
4588 };
4589 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
4590 need
4591 } else if ndim == 1 && shape[0] == need as i64 {
4592 need
4593 } else {
4594 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4595 };
4596 let code = dl.dtype.code as u8;
4597 let bits = dl.dtype.bits;
4598 if dl.data.is_null() {
4599 return RKRStatus::RKR_STATUS_NULL_POINTER;
4600 }
4601 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
4602 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
4603 s.to_vec()
4604 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
4605 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
4606 s.iter().map(|&x| x as f64).collect()
4607 } else {
4608 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4609 };
4610 if frame.positions.nrows() != n {
4611 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4612 }
4613 for i in 0..n {
4614 frame
4615 .positions
4616 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
4617 }
4618 frame.sync_atom_data_from_arrays();
4619 RKRStatus::RKR_STATUS_SUCCESS
4620}
4621
4622#[unsafe(no_mangle)]
4625pub unsafe extern "C" fn rkr_frame_positions_dlpack(
4626 frame_handle: *const RKRConFrame,
4627 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4628) -> RKRStatus {
4629 unsafe {
4630 rkr_frame_positions_as_dlpack(
4631 frame_handle,
4632 rkr_dl_device_type::RKR_DL_CPU,
4633 0,
4634 0,
4635 1,
4636 0,
4637 out_tensor,
4638 )
4639 }
4640}
4641
4642#[unsafe(no_mangle)]
4647pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
4648 frame_handle: *const RKRConFrame,
4649 opts: *const RKRDlpackExportOptions,
4650 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4651) -> RKRStatus {
4652 if frame_handle.is_null() || out_tensor.is_null() {
4653 return RKRStatus::RKR_STATUS_NULL_POINTER;
4654 }
4655 unsafe { *out_tensor = std::ptr::null_mut() };
4656 let o = match resolve_dlpack_opts(opts) {
4657 Ok(o) => o,
4658 Err(st) => return st,
4659 };
4660 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4661 return RKRStatus::RKR_STATUS_NULL_POINTER;
4662 };
4663 let arr = frame_positions_arc(frame);
4664 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4665}
4666
4667#[unsafe(no_mangle)]
4669pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
4670 frame_handle: *const RKRConFrame,
4671 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4672) -> RKRStatus {
4673 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4674}
4675
4676#[unsafe(no_mangle)]
4677pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
4678 frame_handle: *const RKRConFrame,
4679 opts: *const RKRDlpackExportOptions,
4680 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4681) -> RKRStatus {
4682 if frame_handle.is_null() || out_tensor.is_null() {
4683 return RKRStatus::RKR_STATUS_NULL_POINTER;
4684 }
4685 unsafe { *out_tensor = std::ptr::null_mut() };
4686 let o = match resolve_dlpack_opts(opts) {
4687 Ok(o) => o,
4688 Err(st) => return st,
4689 };
4690 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4691 return RKRStatus::RKR_STATUS_NULL_POINTER;
4692 };
4693 if !frame.has_velocities() {
4694 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4695 }
4696 let n = frame.atom_data.len();
4697 let mut data = Vec::with_capacity(n * 3);
4698 for a in &frame.atom_data {
4699 let v = a.velocity().unwrap_or([0.0; 3]);
4700 data.extend_from_slice(&v);
4701 }
4702 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4703 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4704 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4705}
4706
4707#[unsafe(no_mangle)]
4709pub unsafe extern "C" fn rkr_frame_forces_dlpack(
4710 frame_handle: *const RKRConFrame,
4711 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4712) -> RKRStatus {
4713 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4714}
4715
4716#[unsafe(no_mangle)]
4717pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
4718 frame_handle: *const RKRConFrame,
4719 opts: *const RKRDlpackExportOptions,
4720 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4721) -> RKRStatus {
4722 if frame_handle.is_null() || out_tensor.is_null() {
4723 return RKRStatus::RKR_STATUS_NULL_POINTER;
4724 }
4725 unsafe { *out_tensor = std::ptr::null_mut() };
4726 let o = match resolve_dlpack_opts(opts) {
4727 Ok(o) => o,
4728 Err(st) => return st,
4729 };
4730 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4731 return RKRStatus::RKR_STATUS_NULL_POINTER;
4732 };
4733 if !frame.has_forces() {
4734 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4735 }
4736 let n = frame.atom_data.len();
4737 let mut data = Vec::with_capacity(n * 3);
4738 for a in &frame.atom_data {
4739 let f = a.force().unwrap_or([0.0; 3]);
4740 data.extend_from_slice(&f);
4741 }
4742 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4743 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4744 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4745}
4746
4747#[unsafe(no_mangle)]
4754pub unsafe extern "C" fn rkr_frame_displacements_dlpack(
4755 frame_handle: *const RKRConFrame,
4756 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4757) -> RKRStatus {
4758 unsafe { rkr_frame_displacements_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4759}
4760
4761#[unsafe(no_mangle)]
4768pub unsafe extern "C" fn rkr_frame_displacements_dlpack_ex(
4769 frame_handle: *const RKRConFrame,
4770 opts: *const RKRDlpackExportOptions,
4771 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4772) -> RKRStatus {
4773 if frame_handle.is_null() || out_tensor.is_null() {
4774 return RKRStatus::RKR_STATUS_NULL_POINTER;
4775 }
4776 unsafe { *out_tensor = std::ptr::null_mut() };
4777 let o = match resolve_dlpack_opts(opts) {
4778 Ok(o) => o,
4779 Err(st) => return st,
4780 };
4781 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4782 return RKRStatus::RKR_STATUS_NULL_POINTER;
4783 };
4784 if !frame.has_displacements() {
4785 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4786 }
4787 let n = frame.atom_data.len();
4788 let mut data = Vec::with_capacity(n * 3);
4789 for a in &frame.atom_data {
4790 let d = a.displacement().unwrap_or([0.0; 3]);
4791 data.extend_from_slice(&d);
4792 }
4793 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4794 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4795 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4796}
4797
4798#[unsafe(no_mangle)]
4805pub unsafe extern "C" fn rkr_frame_spreads_dlpack(
4806 frame_handle: *const RKRConFrame,
4807 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4808) -> RKRStatus {
4809 unsafe { rkr_frame_spreads_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4810}
4811
4812#[unsafe(no_mangle)]
4819pub unsafe extern "C" fn rkr_frame_spreads_dlpack_ex(
4820 frame_handle: *const RKRConFrame,
4821 opts: *const RKRDlpackExportOptions,
4822 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4823) -> RKRStatus {
4824 if frame_handle.is_null() || out_tensor.is_null() {
4825 return RKRStatus::RKR_STATUS_NULL_POINTER;
4826 }
4827 unsafe { *out_tensor = std::ptr::null_mut() };
4828 let o = match resolve_dlpack_opts(opts) {
4829 Ok(o) => o,
4830 Err(st) => return st,
4831 };
4832 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4833 return RKRStatus::RKR_STATUS_NULL_POINTER;
4834 };
4835 if !frame.has_spreads() {
4836 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4837 }
4838 let n = frame.atom_data.len();
4839 let mut data = Vec::with_capacity(n * 3);
4840 for a in &frame.atom_data {
4841 let d = a.spread().unwrap_or([0.0; 3]);
4842 data.extend_from_slice(&d);
4843 }
4844 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4845 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4846 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4847}
4848
4849#[unsafe(no_mangle)]
4851pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
4852 frame_handle: *const RKRConFrame,
4853 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4854) -> RKRStatus {
4855 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4856}
4857
4858#[unsafe(no_mangle)]
4859pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
4860 frame_handle: *const RKRConFrame,
4861 opts: *const RKRDlpackExportOptions,
4862 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4863) -> RKRStatus {
4864 if frame_handle.is_null() || out_tensor.is_null() {
4865 return RKRStatus::RKR_STATUS_NULL_POINTER;
4866 }
4867 unsafe { *out_tensor = std::ptr::null_mut() };
4868 let o = match resolve_dlpack_opts(opts) {
4869 Ok(o) => o,
4870 Err(st) => return st,
4871 };
4872 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4873 return RKRStatus::RKR_STATUS_NULL_POINTER;
4874 };
4875 if !frame.has_energies() {
4876 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4877 }
4878 let data: Vec<f64> = frame
4879 .atom_data
4880 .iter()
4881 .map(|a| a.energy().unwrap_or(0.0))
4882 .collect();
4883 let arr = ndarray::ArcArray1::from_vec(data);
4884 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
4885}
4886
4887pub struct RKRSelectionResult;
4891#[unsafe(no_mangle)]
4902pub unsafe extern "C" fn rkr_frame_select(
4903 frame_handle: *const RKRConFrame,
4904 selection: *const c_char,
4905 out_result: *mut *mut RKRSelectionResult,
4906) -> RKRStatus {
4907 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
4908 return RKRStatus::RKR_STATUS_NULL_POINTER;
4909 }
4910 let frame = unsafe { &*(frame_handle as *const ConFrame) };
4911 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
4912 Ok(s) => s,
4913 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
4914 };
4915 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
4916 Ok(result) => {
4917 let boxed = Box::new(result);
4918 unsafe {
4919 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
4920 }
4921 RKRStatus::RKR_STATUS_SUCCESS
4922 }
4923 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
4924 }
4925}
4926#[unsafe(no_mangle)]
4931pub unsafe extern "C" fn rkr_selection_result_match_count(
4932 result_handle: *const RKRSelectionResult,
4933) -> u64 {
4934 if result_handle.is_null() {
4935 return 0;
4936 }
4937 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4938 result.matches.len() as u64
4939}
4940#[unsafe(no_mangle)]
4945pub unsafe extern "C" fn rkr_selection_result_context_size(
4946 result_handle: *const RKRSelectionResult,
4947) -> u32 {
4948 if result_handle.is_null() {
4949 return 0;
4950 }
4951 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4952 result.context_size as u32
4953}
4954#[unsafe(no_mangle)]
4960pub unsafe extern "C" fn rkr_selection_result_match_at(
4961 result_handle: *const RKRSelectionResult,
4962 match_index: u64,
4963 out_atoms: *mut u64,
4964 out_size: *mut u32,
4965) -> RKRStatus {
4966 if result_handle.is_null() || out_atoms.is_null() {
4967 return RKRStatus::RKR_STATUS_NULL_POINTER;
4968 }
4969 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4970 let idx = match_index as usize;
4971 if idx >= result.matches.len() {
4972 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
4973 }
4974 let m = &result.matches[idx];
4975 unsafe {
4976 for i in 0..4 {
4977 *out_atoms.add(i) = if i < m.size {
4978 m.atoms[i] as u64
4979 } else {
4980 u64::MAX
4981 };
4982 }
4983 if !out_size.is_null() {
4984 *out_size = m.size as u32;
4985 }
4986 }
4987 RKRStatus::RKR_STATUS_SUCCESS
4988}
4989#[unsafe(no_mangle)]
4995pub unsafe extern "C" fn rkr_selection_result_primary_indices(
4996 result_handle: *const RKRSelectionResult,
4997 out_indices: *mut u64,
4998 capacity: u64,
4999 out_written: *mut u64,
5000) -> RKRStatus {
5001 if result_handle.is_null() {
5002 return RKRStatus::RKR_STATUS_NULL_POINTER;
5003 }
5004 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
5005 let n = result.matches.len() as u64;
5006 if !out_written.is_null() {
5007 unsafe {
5008 *out_written = n;
5009 }
5010 }
5011 if n == 0 {
5012 return RKRStatus::RKR_STATUS_SUCCESS;
5013 }
5014 if out_indices.is_null() {
5015 return RKRStatus::RKR_STATUS_NULL_POINTER;
5016 }
5017 if capacity < n {
5018 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5019 }
5020 unsafe {
5021 for (i, m) in result.matches.iter().enumerate() {
5022 *out_indices.add(i) = m.atoms[0] as u64;
5023 }
5024 }
5025 RKRStatus::RKR_STATUS_SUCCESS
5026}
5027#[unsafe(no_mangle)]
5032pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
5033 if result_handle.is_null() {
5034 return;
5035 }
5036 unsafe {
5037 drop(Box::from_raw(
5038 result_handle as *mut crate::chemfiles_selection::SelectionResult,
5039 ));
5040 }
5041}
5042#[unsafe(no_mangle)]
5044pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
5045 #[cfg(feature = "chemfiles")]
5046 {
5047 1
5048 }
5049 #[cfg(not(feature = "chemfiles"))]
5050 {
5051 0
5052 }
5053}
5054
5055#[unsafe(no_mangle)]
5057pub extern "C" fn rkr_has_parallel_support() -> u8 {
5058 #[cfg(feature = "parallel")]
5059 {
5060 1
5061 }
5062 #[cfg(not(feature = "parallel"))]
5063 {
5064 0
5065 }
5066}
5067#[unsafe(no_mangle)]
5073pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
5074 if path_c.is_null() {
5075 return std::ptr::null_mut();
5076 }
5077 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
5078 return std::ptr::null_mut();
5079 };
5080 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
5081 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
5082 Err(_) => std::ptr::null_mut(),
5083 }
5084}
5085
5086#[unsafe(no_mangle)]
5092pub unsafe extern "C" fn rkr_read_chemfiles(
5093 path_c: *const c_char,
5094 num_frames: *mut usize,
5095) -> *mut *mut RKRConFrame {
5096 if path_c.is_null() || num_frames.is_null() {
5097 return std::ptr::null_mut();
5098 }
5099 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
5100 return std::ptr::null_mut();
5101 };
5102 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
5103 Ok(frames) => pack_frame_handles(frames, num_frames),
5104 Err(_) => std::ptr::null_mut(),
5105 }
5106}
5107
5108#[unsafe(no_mangle)]
5114pub unsafe extern "C" fn rkr_read_chemfiles_nth(
5115 path_c: *const c_char,
5116 index: usize,
5117) -> *mut RKRConFrame {
5118 if path_c.is_null() {
5119 return std::ptr::null_mut();
5120 }
5121 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
5122 return std::ptr::null_mut();
5123 };
5124 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
5125 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
5126 Err(_) => std::ptr::null_mut(),
5127 }
5128}
5129
5130#[unsafe(no_mangle)]
5136pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
5137 if path_c.is_null() {
5138 return usize::MAX;
5139 }
5140 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
5141 return usize::MAX;
5142 };
5143 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
5144}
5145
5146#[unsafe(no_mangle)]
5154pub unsafe extern "C" fn rkr_read_chemfiles_range(
5155 path_c: *const c_char,
5156 start: usize,
5157 step: usize,
5158 stop: usize,
5159 topology_c: *const c_char,
5160 guess_bonds: u8,
5161 num_frames: *mut usize,
5162) -> *mut *mut RKRConFrame {
5163 if path_c.is_null() || num_frames.is_null() {
5164 return std::ptr::null_mut();
5165 }
5166 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
5167 return std::ptr::null_mut();
5168 };
5169 let topology = if topology_c.is_null() {
5170 None
5171 } else {
5172 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
5173 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
5174 _ => return std::ptr::null_mut(),
5175 }
5176 };
5177 let opts = crate::chemfiles_import::ChemfilesReadOpts {
5178 start,
5179 step,
5180 stop: if stop == usize::MAX { None } else { Some(stop) },
5181 format: None,
5182 topology,
5183 topology_format: None,
5184 guess_bonds: guess_bonds != 0,
5185 };
5186 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
5187 Ok(frames) => pack_frame_handles(frames, num_frames),
5188 Err(_) => std::ptr::null_mut(),
5189 }
5190}
5191#[unsafe(no_mangle)]
5198pub unsafe extern "C" fn rkr_read_chemfiles_memory(
5199 data_c: *const c_char,
5200 format_c: *const c_char,
5201 num_frames: *mut usize,
5202) -> *mut *mut RKRConFrame {
5203 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
5204 return std::ptr::null_mut();
5205 }
5206 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
5207 return std::ptr::null_mut();
5208 };
5209 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
5210 return std::ptr::null_mut();
5211 };
5212 match crate::chemfiles_import::con_frames_from_memory(data, format) {
5213 Ok(frames) => {
5214 let n = frames.len();
5215 unsafe { *num_frames = n };
5216 let mut ptrs: Vec<*mut RKRConFrame> = frames
5217 .into_iter()
5218 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
5219 .collect();
5220 let p = ptrs.as_mut_ptr();
5221 std::mem::forget(ptrs);
5222 p
5223 }
5224 Err(_) => std::ptr::null_mut(),
5225 }
5226}
5227#[unsafe(no_mangle)]
5232pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
5233 if tensor.is_null() {
5234 return;
5235 }
5236 unsafe {
5237 let t = &mut *tensor;
5238 if let Some(del) = t.deleter {
5239 del(tensor);
5240 }
5241 }
5242}
5243
5244#[unsafe(no_mangle)]
5254pub unsafe extern "C" fn rkr_pack_rcso(
5255 frame_handle: *const RKRConFrame,
5256 buf: *mut u8,
5257 buflen: usize,
5258 out_len: *mut usize,
5259) -> RKRStatus {
5260 if out_len.is_null() {
5261 return RKRStatus::RKR_STATUS_NULL_POINTER;
5262 }
5263 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
5264 Some(f) => f,
5265 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
5266 };
5267 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
5268 Ok(b) => b,
5269 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5270 };
5271 unsafe { *out_len = bytes.len() };
5272 if buf.is_null() {
5273 return RKRStatus::RKR_STATUS_SUCCESS;
5274 }
5275 if buflen < bytes.len() {
5276 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5277 }
5278 unsafe {
5279 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
5280 }
5281 RKRStatus::RKR_STATUS_SUCCESS
5282}
5283
5284#[unsafe(no_mangle)]
5289pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
5290 buf: *const u8,
5291 buflen: usize,
5292 out_natoms: *mut u32,
5293) -> RKRStatus {
5294 if buf.is_null() || out_natoms.is_null() {
5295 return RKRStatus::RKR_STATUS_NULL_POINTER;
5296 }
5297 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5298 match crate::rcso::Rcso::decode(bytes) {
5299 Ok(s) => {
5300 unsafe { *out_natoms = s.natoms };
5301 RKRStatus::RKR_STATUS_SUCCESS
5302 }
5303 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5304 }
5305}
5306
5307#[unsafe(no_mangle)]
5312pub unsafe extern "C" fn rkr_unpack_rcso_positions(
5313 buf: *const u8,
5314 buflen: usize,
5315 dest: *mut f64,
5316 dest_natoms: u32,
5317) -> RKRStatus {
5318 if buf.is_null() || dest.is_null() {
5319 return RKRStatus::RKR_STATUS_NULL_POINTER;
5320 }
5321 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5322 let soa = match crate::rcso::Rcso::decode(bytes) {
5323 Ok(s) => s,
5324 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5325 };
5326 if soa.natoms != dest_natoms {
5327 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5328 }
5329 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5330 for (i, p) in soa.positions.iter().enumerate() {
5331 out[i * 3] = p[0];
5332 out[i * 3 + 1] = p[1];
5333 out[i * 3 + 2] = p[2];
5334 }
5335 RKRStatus::RKR_STATUS_SUCCESS
5336}
5337
5338#[unsafe(no_mangle)]
5344pub unsafe extern "C" fn rkr_unpack_rcso_forces(
5345 buf: *const u8,
5346 buflen: usize,
5347 dest: *mut f64,
5348 dest_natoms: u32,
5349) -> RKRStatus {
5350 if buf.is_null() || dest.is_null() {
5351 return RKRStatus::RKR_STATUS_NULL_POINTER;
5352 }
5353 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5354 let soa = match crate::rcso::Rcso::decode(bytes) {
5355 Ok(s) => s,
5356 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5357 };
5358 let Some(forces) = soa.forces else {
5359 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
5360 };
5361 if soa.natoms != dest_natoms {
5362 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5363 }
5364 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5365 for (i, f) in forces.iter().enumerate() {
5366 out[i * 3] = f[0];
5367 out[i * 3 + 1] = f[1];
5368 out[i * 3 + 2] = f[2];
5369 }
5370 RKRStatus::RKR_STATUS_SUCCESS
5371}
5372
5373#[cfg(test)]
5374mod tests {
5375 use super::*;
5376 use std::ffi::{CStr, CString};
5377 #[test]
5378 fn frame_copy_positions_without_cframe() {
5379 let handle = test_frame_handle();
5380 let n = unsafe { rkr_frame_atom_count(handle) };
5381 assert_eq!(n, 1);
5382 let mut buf = vec![0.0f64; n * 3];
5383 assert_eq!(
5384 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
5385 RKRStatus::RKR_STATUS_SUCCESS
5386 );
5387 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5388 assert_eq!(
5389 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
5390 RKRStatus::RKR_STATUS_SUCCESS
5391 );
5392 assert!(!tensor.is_null());
5393 unsafe { rkr_dlpack_delete(tensor) };
5394 assert_eq!(
5395 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
5396 RKRStatus::RKR_STATUS_SECTION_ABSENT
5397 );
5398 unsafe { free_rkr_frame(handle) };
5399 }
5400 #[test]
5401 fn read_all_frames_c_abi_tiny() {
5402 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5403 let mut n: usize = 0;
5404 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5405 assert!(!arr.is_null() && n >= 1);
5406 let first = unsafe { *arr };
5407 let nat = unsafe { rkr_frame_atom_count(first) };
5408 let mut buf = vec![0.0f64; nat * 3];
5409 assert_eq!(
5410 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
5411 RKRStatus::RKR_STATUS_SUCCESS
5412 );
5413 unsafe { free_rkr_frame_array(arr, n) };
5414 }
5415
5416 #[test]
5417 fn read_all_frames_n_threads_matches_auto() {
5418 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5419 let mut n_auto: usize = 0;
5420 let mut n_one: usize = 0;
5421 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
5422 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
5423 assert!(!auto.is_null() && !one.is_null());
5424 assert_eq!(n_auto, n_one);
5425 assert_eq!(n_auto, 1);
5426 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
5427 let nat_one = unsafe { rkr_frame_atom_count(*one) };
5428 assert_eq!(nat_auto, nat_one);
5429 unsafe { free_rkr_frame_array(auto, n_auto) };
5430 unsafe { free_rkr_frame_array(one, n_one) };
5431 }
5432
5433 #[test]
5434 fn free_rkr_frame_ptr_array_keeps_frames() {
5435 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5436 let mut n: usize = 0;
5437 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5438 assert!(!arr.is_null() && n >= 1);
5439 let first = unsafe { *arr };
5440 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
5442 unsafe { free_rkr_frame_ptr_array(arr, n) };
5443 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
5444 unsafe { free_rkr_frame(first) };
5445 for h in rest {
5446 if !h.is_null() {
5447 unsafe { free_rkr_frame(h) };
5448 }
5449 }
5450 }
5451 fn test_frame_handle() -> *mut RKRConFrame {
5452 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5453 builder
5454 .prebox_header("Generated by test")
5455 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
5456 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
5457 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
5458 }
5459 #[test]
5460 fn header_line_rejects_null_buffer() {
5461 let frame = test_frame_handle();
5462 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
5463 unsafe { free_rkr_frame(frame) };
5464 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5465 }
5466 #[test]
5467 fn header_line_rejects_empty_buffer() {
5468 let frame = test_frame_handle();
5469 let mut buffer = [0 as c_char; 1];
5470 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
5471 unsafe { free_rkr_frame(frame) };
5472 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
5473 }
5474 #[test]
5475 fn header_line_truncates_and_terminates_buffer() {
5476 let frame = test_frame_handle();
5477 let mut buffer = [0 as c_char; 10];
5478 let status =
5479 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
5480 unsafe { free_rkr_frame(frame) };
5481 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5482 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
5483 assert_eq!(copied.to_str().unwrap(), "Generated");
5484 }
5485
5486 #[test]
5487 fn pack_rcso_size_query_then_dest() {
5488 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
5489 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
5490 assert!(!frame.is_null());
5491 let mut need = 0usize;
5492 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
5493 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5494 assert!(need >= 24);
5495 let mut buf = vec![0u8; need];
5496 let mut wrote = 0usize;
5497 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
5498 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5499 assert_eq!(wrote, need);
5500 assert_eq!(&buf[0..4], b"RCSO");
5501 let mut natoms = 0u32;
5502 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
5503 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5504 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
5505 let mut xyz = vec![0.0f64; natoms as usize * 3];
5506 let st =
5507 unsafe { rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms) };
5508 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5509 let mut frc = vec![0.0f64; natoms as usize * 3];
5510 let st =
5511 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
5512 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5513 unsafe { free_rkr_frame(frame) };
5514 }
5515
5516 fn test_builder_handle() -> *mut RKRConFrameBuilder {
5517 let cell = [10.0, 11.0, 12.0];
5518 let angles = [90.0, 91.0, 92.0];
5519 unsafe {
5520 rkr_frame_new(
5521 cell.as_ptr(),
5522 angles.as_ptr(),
5523 ptr::null(),
5524 ptr::null(),
5525 ptr::null(),
5526 ptr::null(),
5527 )
5528 }
5529 }
5530 fn c_string(s: &str) -> CString {
5531 CString::new(s).unwrap()
5532 }
5533 #[test]
5534 fn builder_set_units_c_abi_stores_the_object() {
5535 let builder = test_builder_handle();
5536 let units = c_string(
5537 r#"{"length":"A","energy":"ev","mass":"amu","time":"(amu*angstrom^2/eV)^0.5"}"#,
5538 );
5539 let status = unsafe { rkr_frame_builder_set_units(builder, units.as_ptr()) };
5540 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5541 let symbol = c_string("H");
5542 assert_eq!(
5543 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5544 RKRStatus::RKR_STATUS_SUCCESS
5545 );
5546 let frame = unsafe { rkr_frame_builder_build(builder) };
5547 assert!(!frame.is_null());
5548 let json = unsafe { rkr_frame_metadata_json(frame) };
5549 assert!(!json.is_null());
5550 let text = unsafe { CStr::from_ptr(json) }.to_str().unwrap();
5551 assert!(text.contains(r#""length":"angstrom""#), "{text}");
5552 assert!(text.contains(r#""energy":"eV""#), "{text}");
5553 assert!(
5554 text.contains(r#""time":"(amu*angstrom^2/eV)^0.5""#),
5555 "{text}"
5556 );
5557 unsafe {
5558 rkr_free_string(json);
5559 free_rkr_frame(frame);
5560 }
5561 }
5562 #[test]
5563 fn builder_stamps_con_spec_minor_one_unless_set() {
5564 let builder = test_builder_handle();
5565 let symbol = c_string("H");
5566 assert_eq!(
5567 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5568 RKRStatus::RKR_STATUS_SUCCESS
5569 );
5570 let frame = unsafe { rkr_frame_builder_build(builder) };
5571 let json = unsafe { rkr_frame_metadata_json(frame) };
5572 let text = unsafe { CStr::from_ptr(json) }
5573 .to_str()
5574 .unwrap()
5575 .to_string();
5576 assert!(text.contains(r#""con_spec_minor":1"#), "{text}");
5577 unsafe {
5578 rkr_free_string(json);
5579 free_rkr_frame(frame);
5580 }
5581
5582 let builder = test_builder_handle();
5583 assert_eq!(
5584 unsafe { rkr_frame_builder_set_con_spec_minor(builder, 4) },
5585 RKRStatus::RKR_STATUS_SUCCESS
5586 );
5587 let symbol = c_string("H");
5588 assert_eq!(
5589 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5590 RKRStatus::RKR_STATUS_SUCCESS
5591 );
5592 let frame = unsafe { rkr_frame_builder_build(builder) };
5593 let json = unsafe { rkr_frame_metadata_json(frame) };
5594 let text = unsafe { CStr::from_ptr(json) }
5595 .to_str()
5596 .unwrap()
5597 .to_string();
5598 assert!(text.contains(r#""con_spec_minor":4"#), "{text}");
5599 unsafe {
5600 rkr_free_string(json);
5601 free_rkr_frame(frame);
5602 }
5603 }
5604 unsafe fn assert_single_atom(
5605 frame: *mut RKRConFrame,
5606 fixed: [bool; 3],
5607 velocity: Option<[f64; 3]>,
5608 forces: Option<[f64; 3]>,
5609 ) {
5610 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5611 assert!(!c_frame.is_null());
5612 let c_frame_ref = unsafe { &*c_frame };
5613 assert_eq!(c_frame_ref.num_atoms, 1);
5614 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
5615 assert_eq!(c_frame_ref.has_forces, forces.is_some());
5616 let atom = unsafe { &*c_frame_ref.atoms };
5617 assert_eq!(atom.fixed_x, fixed[0]);
5618 assert_eq!(atom.fixed_y, fixed[1]);
5619 assert_eq!(atom.fixed_z, fixed[2]);
5620 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
5621 assert_eq!(atom.has_velocity, velocity.is_some());
5622 assert_eq!(atom.has_forces, forces.is_some());
5623 if let Some([vx, vy, vz]) = velocity {
5624 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
5625 }
5626 if let Some([fx, fy, fz]) = forces {
5627 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
5628 }
5629 unsafe { free_c_frame(c_frame) };
5630 unsafe { free_rkr_frame(frame) };
5631 }
5632 #[test]
5633 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
5634 let builder = test_builder_handle();
5635 let symbol = c_string("Cu");
5636 let status = unsafe {
5637 rkr_frame_add_atom_with_fixed_mask(
5638 builder,
5639 symbol.as_ptr(),
5640 1.0,
5641 2.0,
5642 3.0,
5643 true,
5644 false,
5645 true,
5646 7,
5647 63.546,
5648 )
5649 };
5650 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5651 let frame = unsafe { rkr_frame_builder_build(builder) };
5652 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
5653 }
5654 #[test]
5655 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
5656 let builder = test_builder_handle();
5657 let symbol = c_string("H");
5658 let status = unsafe {
5659 rkr_frame_add_atom_with_velocity_fixed_mask(
5660 builder,
5661 symbol.as_ptr(),
5662 1.0,
5663 2.0,
5664 3.0,
5665 false,
5666 true,
5667 false,
5668 9,
5669 1.008,
5670 0.1,
5671 0.2,
5672 0.3,
5673 )
5674 };
5675 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5676 let frame = unsafe { rkr_frame_builder_build(builder) };
5677 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
5678 }
5679 #[test]
5680 fn builder_preserves_fixed_mask_for_atom_with_forces() {
5681 let builder = test_builder_handle();
5682 let symbol = c_string("O");
5683 let status = unsafe {
5684 rkr_frame_add_atom_with_forces_fixed_mask(
5685 builder,
5686 symbol.as_ptr(),
5687 1.0,
5688 2.0,
5689 3.0,
5690 true,
5691 true,
5692 false,
5693 11,
5694 15.999,
5695 -0.1,
5696 -0.2,
5697 -0.3,
5698 )
5699 };
5700 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5701 let frame = unsafe { rkr_frame_builder_build(builder) };
5702 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
5703 }
5704 #[test]
5705 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
5706 let builder = test_builder_handle();
5707 let symbol = c_string("N");
5708 let status = unsafe {
5709 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5710 builder,
5711 symbol.as_ptr(),
5712 1.0,
5713 2.0,
5714 3.0,
5715 false,
5716 true,
5717 true,
5718 13,
5719 14.007,
5720 0.4,
5721 0.5,
5722 0.6,
5723 -0.4,
5724 -0.5,
5725 -0.6,
5726 )
5727 };
5728 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5729 let frame = unsafe { rkr_frame_builder_build(builder) };
5730 unsafe {
5731 assert_single_atom(
5732 frame,
5733 [false, true, true],
5734 Some([0.4, 0.5, 0.6]),
5735 Some([-0.4, -0.5, -0.6]),
5736 )
5737 };
5738 }
5739 #[test]
5740 fn builder_bool_fixed_functions_set_all_axes_together() {
5741 let builder = test_builder_handle();
5742 let cu = c_string("Cu");
5743 let h = c_string("H");
5744 let atom_status =
5745 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
5746 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
5747 let velocity_status = unsafe {
5748 rkr_frame_add_atom_with_velocity(
5749 builder,
5750 h.as_ptr(),
5751 4.0,
5752 5.0,
5753 6.0,
5754 false,
5755 2,
5756 1.008,
5757 0.7,
5758 0.8,
5759 0.9,
5760 )
5761 };
5762 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
5763 let frame = unsafe { rkr_frame_builder_build(builder) };
5764 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5765 assert!(!c_frame.is_null());
5766 let c_frame_ref = unsafe { &*c_frame };
5767 assert_eq!(c_frame_ref.num_atoms, 2);
5768 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
5769 assert_eq!(
5770 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
5771 [true, true, true]
5772 );
5773 assert_eq!(
5774 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
5775 [false, false, false]
5776 );
5777 unsafe { free_c_frame(c_frame) };
5778 unsafe { free_rkr_frame(frame) };
5779 }
5780 #[test]
5781 fn status_message_returns_static_strings_for_all_status_values() {
5782 let cases = [
5783 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
5784 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
5785 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
5786 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
5787 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
5788 (
5789 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
5790 "index out of bounds",
5791 ),
5792 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
5793 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
5794 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
5795 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
5796 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
5797 (
5798 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
5799 "feature disabled in this build",
5800 ),
5801 ];
5802 for (status, expected) in cases {
5803 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
5804 assert_eq!(message.to_str().unwrap(), expected);
5805 }
5806 }
5807 unsafe fn assert_dlpack_cpu_float(
5811 t: *mut RKRDLManagedTensorVersioned,
5812 expect_ndim: i32,
5813 expect_shape: &[i64],
5814 expect_bits: u8,
5815 ) {
5816 assert!(!t.is_null());
5817 let dl = unsafe { &(*t).dl_tensor };
5818 assert_eq!(dl.ndim, expect_ndim);
5819 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
5820 assert_eq!(shape, expect_shape);
5821 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5822 assert_eq!(dl.dtype.bits, expect_bits);
5823 assert_eq!(dl.dtype.lanes, 1);
5824 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5825 assert!(!dl.data.is_null());
5826 }
5827
5828 #[test]
5829 fn frame_optional_section_dlpack_present_and_absent() {
5830 let handle = test_frame_handle();
5832 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5833 assert_eq!(
5834 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
5835 RKRStatus::RKR_STATUS_SECTION_ABSENT
5836 );
5837 assert!(t.is_null());
5838 assert_eq!(
5839 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
5840 RKRStatus::RKR_STATUS_SECTION_ABSENT
5841 );
5842 assert_eq!(
5843 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
5844 RKRStatus::RKR_STATUS_SECTION_ABSENT
5845 );
5846 unsafe { free_rkr_frame(handle) };
5847
5848 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
5850 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
5851 assert!(!fr.is_null());
5852 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
5853 assert!(n > 0);
5854 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5855 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
5856 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5857 unsafe {
5858 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
5859 rkr_dlpack_delete(vel);
5860 free_rkr_frame(fr);
5861 }
5862
5863 let cell = [10.0f64; 3];
5865 let ang = [90.0f64; 3];
5866 let b = unsafe {
5867 rkr_frame_new(
5868 cell.as_ptr(),
5869 ang.as_ptr(),
5870 std::ptr::null(),
5871 std::ptr::null(),
5872 std::ptr::null(),
5873 std::ptr::null(),
5874 )
5875 };
5876 assert!(!b.is_null());
5877 let sym = CString::new("H").unwrap();
5878 unsafe {
5879 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5880 b,
5881 sym.as_ptr(),
5882 0.0,
5883 0.0,
5884 0.0,
5885 false,
5886 false,
5887 false,
5888 0,
5889 1.0,
5890 0.1,
5891 0.0,
5892 0.0,
5893 0.0,
5894 0.0,
5895 -1.0,
5896 );
5897 rkr_frame_builder_set_last_energy(b, -0.5);
5898 }
5899 let built = unsafe { rkr_frame_builder_build(b) };
5900 assert!(!built.is_null());
5901 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
5902 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5903 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5904 assert_eq!(
5905 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
5906 RKRStatus::RKR_STATUS_SUCCESS
5907 );
5908 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
5909 assert_eq!(
5910 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
5911 RKRStatus::RKR_STATUS_SUCCESS
5912 );
5913 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
5914 let opts32 = RKRDlpackExportOptions {
5916 dtype: RKRDLDataType {
5917 code: rkr_dl_type_code::RKR_DL_FLOAT,
5918 bits: 32,
5919 lanes: 1,
5920 },
5921 device: RKRDLDevice {
5922 device_type: rkr_dl_device_type::RKR_DL_CPU,
5923 device_id: 0,
5924 },
5925 };
5926 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5927 assert_eq!(
5928 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
5929 RKRStatus::RKR_STATUS_SUCCESS
5930 );
5931 unsafe {
5932 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
5933 rkr_dlpack_delete(pos32);
5934 }
5935 let cuda_dev = RKRDlpackExportOptions {
5938 dtype: RKRDLDataType {
5939 code: rkr_dl_type_code::RKR_DL_FLOAT,
5940 bits: 64,
5941 lanes: 1,
5942 },
5943 device: RKRDLDevice {
5944 device_type: rkr_dl_device_type::RKR_DL_CUDA,
5945 device_id: 0,
5946 },
5947 };
5948 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5949 #[cfg(not(feature = "cuda"))]
5950 assert_eq!(
5951 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5952 RKRStatus::RKR_STATUS_FEATURE_DISABLED
5953 );
5954 #[cfg(feature = "cuda")]
5955 {
5956 assert_eq!(
5957 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5958 RKRStatus::RKR_STATUS_SUCCESS
5959 );
5960 assert!(!junk.is_null());
5961 let dl = unsafe { &(*junk).dl_tensor };
5962 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
5963 assert!(!dl.data.is_null());
5964 unsafe { rkr_dlpack_delete(junk) };
5965 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5967 assert_eq!(
5968 unsafe {
5969 rkr_frame_positions_as_dlpack(
5970 built,
5971 rkr_dl_device_type::RKR_DL_CUDA,
5972 0,
5973 0,
5974 1,
5975 0,
5976 &mut as_cuda,
5977 )
5978 },
5979 RKRStatus::RKR_STATUS_SUCCESS
5980 );
5981 assert!(!as_cuda.is_null());
5982 unsafe {
5983 assert_eq!(
5984 (*as_cuda).dl_tensor.device.device_type,
5985 dlpk::sys::DLDeviceType::kDLCUDA
5986 );
5987 rkr_dlpack_delete(as_cuda);
5988 }
5989 }
5990 let bad_bits = RKRDlpackExportOptions {
5992 dtype: RKRDLDataType {
5993 code: rkr_dl_type_code::RKR_DL_FLOAT,
5994 bits: 16,
5995 lanes: 1,
5996 },
5997 device: RKRDLDevice {
5998 device_type: rkr_dl_device_type::RKR_DL_CPU,
5999 device_id: 0,
6000 },
6001 };
6002 assert_eq!(
6003 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
6004 RKRStatus::RKR_STATUS_VALIDATION_ERROR
6005 );
6006 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
6008 assert_eq!(
6009 unsafe {
6010 rkr_frame_positions_as_dlpack(
6011 built,
6012 rkr_dl_device_type::RKR_DL_CPU,
6013 0,
6014 0,
6015 1,
6016 0,
6017 &mut as_t,
6018 )
6019 },
6020 RKRStatus::RKR_STATUS_SUCCESS
6021 );
6022 unsafe {
6023 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
6024 }
6025 {
6027 let fr = unsafe { &mut *(built as *mut ConFrame) };
6028 for a in fr.atom_data.iter_mut() {
6029 a.x = -99.0;
6030 a.y = -99.0;
6031 a.z = -99.0;
6032 }
6033 }
6034 assert_eq!(
6035 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
6036 RKRStatus::RKR_STATUS_SUCCESS
6037 );
6038 {
6039 let fr = unsafe { &*(built as *const ConFrame) };
6040 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
6041 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
6042 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
6043 }
6044 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
6046 assert_eq!(
6047 unsafe {
6048 rkr_frame_positions_as_dlpack(
6049 built,
6050 rkr_dl_device_type::RKR_DL_CPU,
6051 0,
6052 0,
6053 1,
6054 0,
6055 &mut again,
6056 )
6057 },
6058 RKRStatus::RKR_STATUS_SUCCESS
6059 );
6060 unsafe {
6061 let dl = &(*again).dl_tensor;
6062 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
6063 assert!((data[0] - 0.0).abs() < 1e-12);
6064 assert!((data[1] - 0.0).abs() < 1e-12);
6065 assert!((data[2] - 0.0).abs() < 1e-12);
6066 rkr_dlpack_delete(again);
6067 rkr_dlpack_delete(as_t);
6068 rkr_dlpack_delete(frc);
6069 rkr_dlpack_delete(eng);
6070 free_rkr_frame(built);
6071 }
6072 }
6073
6074 #[test]
6075 fn ffi_positions_dlpack_round_trip() {
6076 let handle = test_builder_handle();
6077 let sym = c_string("Cu");
6078 unsafe {
6079 rkr_frame_add_atom_full(
6080 handle,
6081 sym.as_ptr(),
6082 1.0,
6083 2.0,
6084 3.0,
6085 false,
6086 false,
6087 false,
6088 7,
6089 63.5,
6090 ptr::null(),
6091 ptr::null(),
6092 )
6093 };
6094 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
6095 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
6096 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
6097 assert!(!t.is_null());
6098 let dl = unsafe { &(*t).dl_tensor };
6100 assert_eq!(dl.ndim, 2);
6101 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
6102 assert_eq!(shape, &[1, 3]);
6103 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
6104 assert_eq!(dl.dtype.bits, 64);
6105 assert_eq!(dl.dtype.lanes, 1);
6106 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
6107 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
6108 assert_eq!(data, &[1.0, 2.0, 3.0]);
6109 let deleter = unsafe { (*t).deleter };
6111 if let Some(del) = deleter {
6112 unsafe { del(t) };
6113 }
6114 unsafe { free_rkr_frame_builder(handle) };
6115 }
6116 #[test]
6117 fn ffi_velocities_dlpack_section_absent() {
6118 let handle = test_builder_handle();
6119 let sym = c_string("Cu");
6120 unsafe {
6121 rkr_frame_add_atom_full(
6122 handle,
6123 sym.as_ptr(),
6124 0.0,
6125 0.0,
6126 0.0,
6127 false,
6128 false,
6129 false,
6130 0,
6131 63.5,
6132 ptr::null(),
6133 ptr::null(),
6134 )
6135 };
6136 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
6137 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
6138 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
6139 assert!(t.is_null());
6140 unsafe { free_rkr_frame_builder(handle) };
6141 }
6142 #[test]
6143 fn ffi_dlpack_null_handle_rejects() {
6144 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
6145 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
6146 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
6147 assert!(t.is_null());
6148 }
6149 #[cfg(feature = "chemfiles")]
6150 #[test]
6151 fn rkr_frame_select_finds_oxygen() {
6152 use crate::types::ConFrameBuilder;
6153 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
6154 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
6155 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
6156 let frame = b.build().unwrap();
6157 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
6158 let sel = CString::new("name O").unwrap();
6159 let mut out: *mut RKRSelectionResult = ptr::null_mut();
6160 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
6161 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6162 assert!(!out.is_null());
6163 let n = unsafe { rkr_selection_result_match_count(out) };
6164 assert_eq!(n, 1);
6165 let mut atoms = [u64::MAX; 4];
6166 let mut size = 0u32;
6167 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
6168 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
6169 assert_eq!(size, 1);
6170 assert_eq!(atoms[0], 0);
6171 unsafe {
6172 rkr_selection_result_free(out);
6173 free_rkr_frame(frame_ptr);
6174 }
6175 }
6176 #[cfg(feature = "chemfiles")]
6178 #[test]
6179 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
6180 use crate::types::{Bond, ConFrameBuilder};
6181 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
6183 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
6184 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
6185 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
6186 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
6187 let mut frame = b.build().unwrap();
6188 let id_to = |id: u64| {
6189 frame
6190 .atom_data
6191 .iter()
6192 .position(|a| a.atom_id == id)
6193 .unwrap() as u32
6194 };
6195 frame.header.set_bonds(&[
6196 Bond::new(id_to(0), id_to(1)),
6197 Bond::new(id_to(1), id_to(2)),
6198 Bond::new(id_to(2), id_to(3)),
6199 ]);
6200 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
6201 let run = |sel: &str| -> (u64, u32) {
6202 let csel = CString::new(sel).unwrap();
6203 let mut out: *mut RKRSelectionResult = ptr::null_mut();
6204 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
6205 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
6206 let n = unsafe { rkr_selection_result_match_count(out) };
6207 let ctx = unsafe { rkr_selection_result_context_size(out) };
6208 unsafe { rkr_selection_result_free(out) };
6209 (n, ctx)
6210 };
6211 assert_eq!(run("bonds: all"), (3, 2));
6212 assert_eq!(run("angles: all"), (2, 3));
6213 assert_eq!(run("dihedrals: all"), (1, 4));
6214 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
6215 assert_eq!(
6216 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
6217 run("bonds: type(#1) H and name(#2) O").0
6218 );
6219 unsafe { free_rkr_frame(frame_ptr) };
6220 }
6221 #[cfg(feature = "metatensor")]
6222 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
6223 assert!(!block.is_null());
6224 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
6225 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
6226 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
6227 let shape_fn = array
6228 .shape
6229 .expect("mts_array_t.shape from metatensor C API");
6230 let mut shape_ptr: *const usize = std::ptr::null();
6231 let mut shape_count: usize = 0;
6232 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
6233 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
6234 assert_eq!(shape_count, 2);
6235 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
6236 assert_eq!(shape[0], n);
6237 assert_eq!(shape[1], props);
6238 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
6239 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
6240 assert!(!samples.is_null() && !prop_lab.is_null());
6241 }
6242 #[cfg(feature = "metatensor")]
6243 #[test]
6244 fn metatensor_positions_via_c_abi() {
6245 let handle = test_frame_handle();
6246 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6247 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
6248 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6249 assert_mts_block_shape(out, 1, 3);
6250 unsafe { rkr_mts_block_free(out) };
6251 for (name, export) in [
6252 (
6253 "velocities",
6254 rkr_frame_metatensor_velocities_block
6255 as unsafe extern "C" fn(
6256 *const RKRConFrame,
6257 *mut *mut metatensor::c_api::mts_block_t,
6258 ) -> RKRStatus,
6259 ),
6260 (
6261 "forces",
6262 rkr_frame_metatensor_forces_block
6263 as unsafe extern "C" fn(
6264 *const RKRConFrame,
6265 *mut *mut metatensor::c_api::mts_block_t,
6266 ) -> RKRStatus,
6267 ),
6268 (
6269 "atom_energies",
6270 rkr_frame_metatensor_atom_energies_block
6271 as unsafe extern "C" fn(
6272 *const RKRConFrame,
6273 *mut *mut metatensor::c_api::mts_block_t,
6274 ) -> RKRStatus,
6275 ),
6276 ] {
6277 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6278 let st_abs = unsafe { export(handle, &mut o) };
6279 assert_eq!(
6280 st_abs,
6281 RKRStatus::RKR_STATUS_SECTION_ABSENT,
6282 "{name} must be SECTION_ABSENT on minimal test frame"
6283 );
6284 assert!(o.is_null());
6285 }
6286 unsafe { free_rkr_frame(handle) };
6287 }
6288
6289 #[cfg(feature = "metatensor")]
6290 #[test]
6291 fn metatensor_optional_sections_via_c_abi() {
6292 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
6294 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
6295 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
6296 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
6297 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
6298 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
6299 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
6300 builder.set_atom_energy(0, -0.5).unwrap();
6301 builder.set_atom_energy(1, -1.0).unwrap();
6302 let frame = builder.build().unwrap();
6303 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
6304 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6305 assert_eq!(
6306 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
6307 RKRStatus::RKR_STATUS_SUCCESS
6308 );
6309 assert_mts_block_shape(pos, 2, 3);
6310 unsafe { rkr_mts_block_free(pos) };
6311 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6312 assert_eq!(
6313 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
6314 RKRStatus::RKR_STATUS_SUCCESS
6315 );
6316 assert_mts_block_shape(vel, 2, 3);
6317 unsafe { rkr_mts_block_free(vel) };
6318 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6319 assert_eq!(
6320 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
6321 RKRStatus::RKR_STATUS_SUCCESS
6322 );
6323 assert_mts_block_shape(frc, 2, 3);
6324 unsafe { rkr_mts_block_free(frc) };
6325 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6326 assert_eq!(
6327 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
6328 RKRStatus::RKR_STATUS_SUCCESS
6329 );
6330 assert_mts_block_shape(eng, 2, 1);
6331 unsafe { rkr_mts_block_free(eng) };
6332 unsafe { free_rkr_frame(handle) };
6333 }
6334
6335 #[test]
6336 fn string_iterator_yields_frames_from_buffer() {
6337 let text =
6338 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
6339 let c_text = CString::new(text.as_str()).unwrap();
6340 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
6341 assert!(!it.is_null());
6342 let mut n = 0usize;
6343 loop {
6344 let fr = unsafe { con_frame_iterator_next(it) };
6345 if fr.is_null() {
6346 break;
6347 }
6348 n += 1;
6349 unsafe { free_rkr_frame(fr) };
6350 }
6351 unsafe { free_con_frame_iterator(it) };
6352 assert!(n >= 1, "string iterator should yield >=1 frame");
6353
6354 let bytes = text.as_bytes();
6355 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
6356 assert!(!it2.is_null());
6357 let fr2 = unsafe { con_frame_iterator_next(it2) };
6358 assert!(!fr2.is_null());
6359 unsafe {
6360 free_rkr_frame(fr2);
6361 free_con_frame_iterator(it2);
6362 }
6363 }
6364
6365 #[test]
6366 fn file_iterator_reads_gzip_when_present() {
6367 use flate2::Compression;
6368 use flate2::write::GzEncoder;
6369 use std::io::Write;
6370 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
6371 let dir = tempfile::tempdir().expect("tempdir");
6372 let gz_path = dir.path().join("tiny_cuh2.con.gz");
6373 {
6374 let f = std::fs::File::create(&gz_path).unwrap();
6375 let mut enc = GzEncoder::new(f, Compression::default());
6376 enc.write_all(&plain).unwrap();
6377 enc.finish().unwrap();
6378 }
6379 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
6380 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
6381 assert!(
6382 !it.is_null(),
6383 "path iterator must decompress .con.gz transparently"
6384 );
6385 let fr = unsafe { con_frame_iterator_next(it) };
6386 assert!(!fr.is_null());
6387 let n = unsafe { rkr_frame_atom_count(fr) };
6388 assert!(n > 0);
6389 unsafe {
6390 free_rkr_frame(fr);
6391 free_con_frame_iterator(it);
6392 }
6393 }
6394}