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)]
491pub extern "C" fn rkr_last_error_message() -> *const c_char {
492 LAST_ERROR.with(|slot| match slot.borrow().as_ref() {
493 Some(message) => message.as_ptr(),
494 None => ptr::null(),
495 })
496}
497pub struct RKRConFrame {
502 _private: [u8; 0],
503}
504pub struct RKRConFrameWriter {
509 _private: [u8; 0],
510}
511#[repr(C)]
517pub struct RKRArrayView {
518 pub data: *const std::ffi::c_void,
519 pub n: usize,
520 pub cols: u32,
521 pub dtype_code: u8,
522 pub dtype_bits: u8,
523}
524
525impl RKRArrayView {
526 fn empty() -> Self {
527 Self {
528 data: std::ptr::null(),
529 n: 0,
530 cols: 0,
531 dtype_code: 0,
532 dtype_bits: 0,
533 }
534 }
535
536 fn from_array2(arr: &crate::storage_dtype::FloatArray2) -> Self {
537 let kind = arr.kind();
538 Self {
539 data: arr.data_ptr() as *const std::ffi::c_void,
540 n: arr.nrows(),
541 cols: arr.ncols() as u32,
542 dtype_code: kind.dlpack_code(),
543 dtype_bits: kind.dlpack_bits(),
544 }
545 }
546
547 fn from_array1(arr: &crate::storage_dtype::FloatArray1) -> Self {
548 let kind = arr.kind();
549 Self {
550 data: arr.data_ptr() as *const std::ffi::c_void,
551 n: arr.len(),
552 cols: 1,
553 dtype_code: kind.dlpack_code(),
554 dtype_bits: kind.dlpack_bits(),
555 }
556 }
557}
558
559#[repr(C)]
560pub struct CFrame {
561 pub atoms: *mut CAtom,
562 pub num_atoms: usize,
563 pub cell: [f64; 3],
564 pub angles: [f64; 3],
565 pub has_velocities: bool,
566 pub has_forces: bool,
567 pub has_energies: bool,
568}
569#[repr(C)]
579pub struct CAtom {
580 pub atomic_number: u64,
581 pub x: f64,
582 pub y: f64,
583 pub z: f64,
584 pub atom_id: u64,
585 pub mass: f64,
586 pub is_fixed: bool,
589 pub fixed_x: bool,
590 pub fixed_y: bool,
591 pub fixed_z: bool,
592 pub vx: f64,
593 pub vy: f64,
594 pub vz: f64,
595 pub has_velocity: bool,
596 pub fx: f64,
597 pub fy: f64,
598 pub fz: f64,
599 pub has_forces: bool,
600 pub energy: f64,
603 pub has_energy: bool,
604}
605#[repr(C)]
606pub struct CConFrameIterator {
607 iterator: *mut ConFrameIterator<'static>,
608 file_contents: *mut String,
609}
610
611fn c_iterator_from_owned_string(contents: String) -> *mut CConFrameIterator {
613 let file_contents_box = Box::new(contents);
614 let file_contents_ptr = Box::into_raw(file_contents_box);
615 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
616 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
617 let c_iterator = Box::new(CConFrameIterator {
618 iterator: Box::into_raw(iterator),
619 file_contents: file_contents_ptr,
620 });
621 Box::into_raw(c_iterator)
622}
623
624#[unsafe(no_mangle)]
640pub unsafe extern "C" fn read_con_file_iterator(
641 filename_c: *const c_char,
642) -> *mut CConFrameIterator {
643 if filename_c.is_null() {
644 return ptr::null_mut();
645 }
646 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
647 Ok(s) => s,
648 Err(_) => return ptr::null_mut(),
649 };
650 let owned = match crate::compression::read_file_contents(Path::new(filename)) {
651 Ok(fc) => match fc.as_str() {
652 Ok(s) => s.to_owned(),
653 Err(_) => return ptr::null_mut(),
654 },
655 Err(_) => return ptr::null_mut(),
656 };
657 c_iterator_from_owned_string(owned)
658}
659
660#[unsafe(no_mangle)]
668pub unsafe extern "C" fn read_con_string_iterator(
669 contents_c: *const c_char,
670) -> *mut CConFrameIterator {
671 if contents_c.is_null() {
672 return ptr::null_mut();
673 }
674 let contents = match unsafe { CStr::from_ptr(contents_c).to_str() } {
675 Ok(s) => s.to_owned(),
676 Err(_) => return ptr::null_mut(),
677 };
678 c_iterator_from_owned_string(contents)
679}
680
681#[unsafe(no_mangle)]
688pub unsafe extern "C" fn read_con_buffer_iterator(
689 data: *const u8,
690 len: usize,
691) -> *mut CConFrameIterator {
692 if data.is_null() && len > 0 {
693 return ptr::null_mut();
694 }
695 if len == 0 {
696 return c_iterator_from_owned_string(String::new());
697 }
698 let slice = unsafe { std::slice::from_raw_parts(data, len) };
699 let contents = match std::str::from_utf8(slice) {
700 Ok(s) => s.to_owned(),
701 Err(_) => return ptr::null_mut(),
702 };
703 c_iterator_from_owned_string(contents)
704}
705#[unsafe(no_mangle)]
711pub unsafe extern "C" fn con_frame_iterator_next(
712 iterator: *mut CConFrameIterator,
713) -> *mut RKRConFrame {
714 if iterator.is_null() {
715 return ptr::null_mut();
716 }
717 let iter = unsafe { &mut *(*iterator).iterator };
718 match iter.next() {
719 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
720 _ => ptr::null_mut(),
721 }
722}
723
724#[unsafe(no_mangle)]
727pub unsafe extern "C" fn con_frame_iterator_forward(iterator: *mut CConFrameIterator) -> RKRStatus {
728 if iterator.is_null() {
729 return RKRStatus::RKR_STATUS_NULL_POINTER;
730 }
731 let iter = unsafe { &mut *(*iterator).iterator };
732 match iter.forward() {
733 Some(Ok(())) => RKRStatus::RKR_STATUS_SUCCESS,
734 Some(Err(_)) => RKRStatus::RKR_STATUS_IO_ERROR,
735 None => RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
736 }
737}
738
739#[unsafe(no_mangle)]
742pub unsafe extern "C" fn con_frame_iterator_skip(
743 iterator: *mut CConFrameIterator,
744 n: usize,
745) -> usize {
746 if iterator.is_null() {
747 return usize::MAX;
748 }
749 let iter = unsafe { &mut *(*iterator).iterator };
750 iter.skip_frames(n).unwrap_or(usize::MAX)
751}
752
753#[unsafe(no_mangle)]
755pub unsafe extern "C" fn con_frame_iterator_nth(
756 iterator: *mut CConFrameIterator,
757 index: usize,
758) -> *mut RKRConFrame {
759 if iterator.is_null() {
760 return ptr::null_mut();
761 }
762 let iter = unsafe { &mut *(*iterator).iterator };
763 match iter.skip_frames(index) {
764 Ok(skipped) if skipped == index => {}
765 _ => return ptr::null_mut(),
766 }
767 match iter.next() {
768 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
769 _ => ptr::null_mut(),
770 }
771}
772
773#[unsafe(no_mangle)]
775pub unsafe extern "C" fn rkr_count_frames(filename_c: *const c_char) -> usize {
776 if filename_c.is_null() {
777 return usize::MAX;
778 }
779 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
780 Ok(s) => s,
781 Err(_) => return usize::MAX,
782 };
783 crate::iterators::count_frames(Path::new(filename)).unwrap_or(usize::MAX)
784}
785
786#[unsafe(no_mangle)]
788pub unsafe extern "C" fn rkr_read_nth_frame(
789 filename_c: *const c_char,
790 index: usize,
791) -> *mut RKRConFrame {
792 if filename_c.is_null() {
793 return ptr::null_mut();
794 }
795 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
796 Ok(s) => s,
797 Err(_) => return ptr::null_mut(),
798 };
799 match crate::iterators::read_nth_frame(Path::new(filename), index) {
800 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
801 Err(_) => ptr::null_mut(),
802 }
803}
804#[unsafe(no_mangle)]
809pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
810 if !frame_handle.is_null() {
811 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
812 }
813}
814#[unsafe(no_mangle)]
819pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
820 if iterator.is_null() {
821 return;
822 }
823 unsafe {
824 let c_iterator_box = Box::from_raw(iterator);
825 let _ = Box::from_raw(c_iterator_box.iterator);
826 let _ = Box::from_raw(c_iterator_box.file_contents);
827 }
828}
829#[unsafe(no_mangle)]
838pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
839 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
840 Some(f) => f,
841 None => return ptr::null_mut(),
842 };
843 let masses_iter = frame
844 .header
845 .natms_per_type
846 .iter()
847 .zip(frame.header.masses_per_type.iter())
848 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
849 let has_velocities = frame.has_velocities();
850 let mut c_atoms: Vec<CAtom> = frame
851 .atom_data
852 .iter()
853 .zip(masses_iter)
854 .map(|(atom_datum, mass)| {
855 let [vx, vy, vz] = atom_datum.velocity().unwrap_or([0.0; 3]);
856 let [fx, fy, fz] = atom_datum.force().unwrap_or([0.0; 3]);
857 CAtom {
858 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
859 x: atom_datum.x,
860 y: atom_datum.y,
861 z: atom_datum.z,
862 is_fixed: atom_datum.is_fixed(),
863 fixed_x: atom_datum.fixed[0],
864 fixed_y: atom_datum.fixed[1],
865 fixed_z: atom_datum.fixed[2],
866 atom_id: atom_datum.atom_id,
867 mass,
868 vx,
869 vy,
870 vz,
871 has_velocity: atom_datum.has_velocity(),
872 fx,
873 fy,
874 fz,
875 has_forces: atom_datum.has_forces(),
876 energy: atom_datum.energy().unwrap_or(0.0),
877 has_energy: atom_datum.has_energy(),
878 }
879 })
880 .collect();
881 let atoms_ptr = c_atoms.as_mut_ptr();
882 let num_atoms = c_atoms.len();
883 std::mem::forget(c_atoms);
884 let has_forces = frame.has_forces();
885 let has_energies = frame.has_energies();
886 let c_frame = Box::new(CFrame {
887 atoms: atoms_ptr,
888 num_atoms,
889 cell: frame.header.boxl,
890 angles: frame.header.angles,
891 has_velocities,
892 has_forces,
893 has_energies,
894 });
895 Box::into_raw(c_frame)
896}
897#[unsafe(no_mangle)]
902pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
903 if frame.is_null() {
904 return;
905 }
906 unsafe {
907 let frame_box = Box::from_raw(frame);
908 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
909 }
910}
911#[unsafe(no_mangle)]
930pub unsafe extern "C" fn rkr_frame_get_header_line(
931 frame_handle: *const RKRConFrame,
932 is_prebox: bool,
933 line_index: usize,
934 buffer: *mut c_char,
935 buffer_len: usize,
936) -> RKRStatus {
937 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
938 Some(f) => f,
939 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
940 };
941 if buffer.is_null() {
942 return RKRStatus::RKR_STATUS_NULL_POINTER;
943 }
944 if buffer_len == 0 {
945 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
946 }
947 let line_to_copy: Option<&str> = if is_prebox {
948 match line_index {
949 0 => Some(frame.header.prebox_header.user.as_str()),
950 1 => Some(frame.header.prebox_header.metadata_line()),
951 _ => None,
952 }
953 } else {
954 frame
955 .header
956 .postbox_header
957 .get(line_index)
958 .map(String::as_str)
959 };
960 if let Some(line) = line_to_copy {
961 let bytes = line.as_bytes();
962 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
963 unsafe {
964 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
965 *buffer.add(len_to_copy) = 0;
966 }
967 RKRStatus::RKR_STATUS_SUCCESS
968 } else {
969 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
970 }
971}
972#[unsafe(no_mangle)]
985pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
986 frame_handle: *const RKRConFrame,
987 is_prebox: bool,
988 line_index: usize,
989) -> *mut c_char {
990 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
991 Some(f) => f,
992 None => return ptr::null_mut(),
993 };
994 let line_to_copy: Option<&str> = if is_prebox {
995 match line_index {
996 0 => Some(frame.header.prebox_header.user.as_str()),
997 1 => Some(frame.header.prebox_header.metadata_line()),
998 _ => None,
999 }
1000 } else {
1001 frame
1002 .header
1003 .postbox_header
1004 .get(line_index)
1005 .map(String::as_str)
1006 };
1007 if let Some(line) = line_to_copy {
1008 match CString::new(line) {
1010 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
1013 } else {
1014 ptr::null_mut() }
1016}
1017#[unsafe(no_mangle)]
1025pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
1026 if !s.is_null() {
1027 let _ = unsafe { CString::from_raw(s) };
1029 }
1030}
1031type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
1045#[inline]
1048fn into_rkr_writer(sink: Box<dyn std::io::Write>, precision: Option<u8>) -> *mut RKRConFrameWriter {
1049 let writer: RkrWriter = match precision {
1050 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
1051 None => ConFrameWriter::new(sink),
1052 };
1053 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1054}
1055#[inline]
1057unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
1058 if filename_c.is_null() {
1059 return None;
1060 }
1061 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
1062}
1063#[unsafe(no_mangle)]
1069pub unsafe extern "C" fn create_writer_from_path_c(
1070 filename_c: *const c_char,
1071) -> *mut RKRConFrameWriter {
1072 let filename = match unsafe { cstr_path(filename_c) } {
1073 Some(s) => s,
1074 None => return ptr::null_mut(),
1075 };
1076 match File::create(filename) {
1077 Ok(file) => into_rkr_writer(Box::new(file), None),
1078 Err(_) => ptr::null_mut(),
1079 }
1080}
1081#[unsafe(no_mangle)]
1087pub unsafe extern "C" fn create_writer_from_path_round_trip_c(
1088 filename_c: *const c_char,
1089) -> *mut RKRConFrameWriter {
1090 let filename = match unsafe { cstr_path(filename_c) } {
1091 Some(s) => s,
1092 None => return ptr::null_mut(),
1093 };
1094 match File::create(filename) {
1095 Ok(file) => {
1096 let writer: RkrWriter = ConFrameWriter::with_float_format(
1097 Box::new(file),
1098 crate::writer::FloatFormat::RoundTrip,
1099 );
1100 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1101 }
1102 Err(_) => ptr::null_mut(),
1103 }
1104}
1105
1106#[unsafe(no_mangle)]
1114pub unsafe extern "C" fn rkr_writer_flush(writer_handle: *mut RKRConFrameWriter) -> RKRStatus {
1115 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1116 Some(w) => w,
1117 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1118 };
1119 match writer.flush() {
1120 Ok(()) => {
1121 clear_last_error();
1122 RKRStatus::RKR_STATUS_SUCCESS
1123 }
1124 Err(err) => {
1125 set_last_error(&err.to_string());
1126 RKRStatus::RKR_STATUS_IO_ERROR
1127 }
1128 }
1129}
1130
1131#[unsafe(no_mangle)]
1136pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
1137 if !writer_handle.is_null() {
1138 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
1139 }
1140}
1141#[unsafe(no_mangle)]
1150pub unsafe extern "C" fn rkr_frame_binding_rewrite(
1151 frame_handle: *const RKRConFrame,
1152) -> *mut RKRConFrame {
1153 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
1154 return ptr::null_mut();
1155 };
1156 match binding_rewrite_frame(frame) {
1157 Ok(rewritten) => Box::into_raw(Box::new(rewritten)) as *mut RKRConFrame,
1158 Err(_) => ptr::null_mut(),
1159 }
1160}
1161
1162#[unsafe(no_mangle)]
1172pub unsafe extern "C" fn rkr_writer_extend(
1173 writer_handle: *mut RKRConFrameWriter,
1174 frame_handles: *const *const RKRConFrame,
1175 num_frames: usize,
1176) -> RKRStatus {
1177 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1178 Some(w) => w,
1179 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1180 };
1181 if frame_handles.is_null() {
1182 return RKRStatus::RKR_STATUS_NULL_POINTER;
1183 }
1184 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
1185 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
1186 if handles_slice.iter().any(|&handle| handle.is_null()) {
1187 return RKRStatus::RKR_STATUS_NULL_POINTER;
1190 }
1191 for &handle in handles_slice.iter() {
1192 match unsafe { (handle as *const ConFrame).as_ref() } {
1194 Some(frame) => rust_frames.push(frame),
1195 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1197 }
1198 }
1199 match writer.extend(rust_frames.into_iter()) {
1200 Ok(_) => {
1201 clear_last_error();
1202 RKRStatus::RKR_STATUS_SUCCESS
1203 }
1204 Err(err) => {
1205 set_last_error(&err.to_string());
1206 RKRStatus::RKR_STATUS_IO_ERROR
1207 }
1208 }
1209}
1210
1211#[unsafe(no_mangle)]
1217pub unsafe extern "C" fn rkr_writer_set_canonical(
1218 writer_handle: *mut RKRConFrameWriter,
1219 canonical: u8,
1220) -> RKRStatus {
1221 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1222 Some(w) => w,
1223 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1224 };
1225 writer.set_canonical(canonical != 0);
1226 RKRStatus::RKR_STATUS_SUCCESS
1227}
1228
1229#[unsafe(no_mangle)]
1231pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
1232 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
1233 Some(w) => u8::from(w.is_canonical()),
1234 None => 0,
1235 }
1236}
1237
1238#[cfg(test)]
1239mod index_proj_ffi_tests {
1240 use super::*;
1241 use std::ffi::CStr;
1242 use std::fs;
1243 use std::path::PathBuf;
1244
1245 fn fixture_path() -> PathBuf {
1246 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
1247 }
1248
1249 #[test]
1250 fn ffi_projection_matches_index_proj() {
1251 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1252 let fr = &frames[0];
1253 let handle = fr as *const ConFrame as *const RKRConFrame;
1254 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
1255 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
1257 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
1258 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
1259 assert!(!formula_c.is_null());
1260 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
1261 assert_eq!(formula, proj.formula);
1262 unsafe { rkr_free_string(formula_c) };
1263 let ie = rkr_frame_index_energy(handle);
1264 match proj.energy {
1265 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
1266 None => assert!(ie.is_nan()),
1267 }
1268 let tm = rkr_frame_total_mass(handle);
1269 match proj.total_mass {
1270 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
1271 None => assert!(tm.is_nan()),
1272 }
1273 let cv = rkr_frame_cell_volume(handle);
1274 match proj.cell_volume {
1275 Some(v) => assert!(
1276 (cv - v).abs() < 1e-6 * v.max(1.0),
1277 "cell_volume C={cv} proj={v}"
1278 ),
1279 None => assert!(cv.is_nan()),
1280 }
1281 let fm = rkr_frame_fmax(handle);
1282 match proj.fmax {
1283 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
1284 None => assert!(fm.is_nan()),
1285 }
1286 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
1287 assert!(!json_c.is_null());
1288 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
1289 assert!(json.contains("\"formula\""));
1290 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
1291 assert!(json.contains("\"n_atoms\""));
1292 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
1293 unsafe { rkr_free_string(json_c) };
1294 }
1295
1296 #[test]
1297 fn ffi_canonical_writer_byte_identical() {
1298 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1299 let fr = &frames[0];
1300 let dir = tempfile::tempdir().unwrap();
1301 let p1 = dir.path().join("a.con");
1302 let p2 = dir.path().join("b.con");
1303 for p in [&p1, &p2] {
1304 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
1305 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
1306 assert!(!w.is_null());
1307 assert_eq!(
1308 unsafe { rkr_writer_set_canonical(w, 1) },
1309 RKRStatus::RKR_STATUS_SUCCESS
1310 );
1311 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
1312 let handles = [fr as *const ConFrame as *const RKRConFrame];
1313 assert_eq!(
1314 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
1315 RKRStatus::RKR_STATUS_SUCCESS
1316 );
1317 unsafe { free_rkr_writer(w) };
1318 }
1319 let b1 = fs::read(&p1).unwrap();
1320 let b2 = fs::read(&p2).unwrap();
1321 assert_eq!(b1, b2);
1322 assert!(!b1.is_empty());
1323 }
1324}
1325#[unsafe(no_mangle)]
1334pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
1335 filename_c: *const c_char,
1336 precision: u8,
1337) -> *mut RKRConFrameWriter {
1338 let filename = match unsafe { cstr_path(filename_c) } {
1339 Some(s) => s,
1340 None => return ptr::null_mut(),
1341 };
1342 match File::create(filename) {
1343 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1344 Err(_) => ptr::null_mut(),
1345 }
1346}
1347pub struct RKRConFrameBuilder {
1354 _private: [u8; 0],
1355}
1356#[allow(clippy::too_many_arguments)]
1357unsafe fn add_builder_atom(
1358 builder_handle: *mut RKRConFrameBuilder,
1359 symbol: *const c_char,
1360 x: f64,
1361 y: f64,
1362 z: f64,
1363 fixed: [bool; 3],
1364 atom_id: u64,
1365 mass: f64,
1366 velocity: Option<[f64; 3]>,
1367 forces: Option<[f64; 3]>,
1368) -> RKRStatus {
1369 if builder_handle.is_null() || symbol.is_null() {
1370 return RKRStatus::RKR_STATUS_NULL_POINTER;
1371 }
1372 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1373 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
1374 Ok(s) => s,
1375 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1376 };
1377 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
1378 if let Some(v) = velocity {
1379 builder.with_velocity(v);
1380 }
1381 if let Some(f) = forces {
1382 builder.with_force(f);
1383 }
1384 RKRStatus::RKR_STATUS_SUCCESS
1385}
1386#[unsafe(no_mangle)]
1392pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
1393 builder_handle: *mut RKRConFrameBuilder,
1394 velocity: *const f64,
1395) -> RKRStatus {
1396 if builder_handle.is_null() || velocity.is_null() {
1397 return RKRStatus::RKR_STATUS_NULL_POINTER;
1398 }
1399 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1400 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1401 builder.with_velocity(v);
1402 RKRStatus::RKR_STATUS_SUCCESS
1403}
1404#[unsafe(no_mangle)]
1410pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
1411 builder_handle: *mut RKRConFrameBuilder,
1412 force: *const f64,
1413) -> RKRStatus {
1414 if builder_handle.is_null() || force.is_null() {
1415 return RKRStatus::RKR_STATUS_NULL_POINTER;
1416 }
1417 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1418 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1419 builder.with_force(f);
1420 RKRStatus::RKR_STATUS_SUCCESS
1421}
1422#[unsafe(no_mangle)]
1429pub unsafe extern "C" fn rkr_frame_builder_set_last_displacement(
1430 builder_handle: *mut RKRConFrameBuilder,
1431 displacement: *const f64,
1432) -> RKRStatus {
1433 if builder_handle.is_null() || displacement.is_null() {
1434 return RKRStatus::RKR_STATUS_NULL_POINTER;
1435 }
1436 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1437 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1438 builder.with_displacement(d);
1439 RKRStatus::RKR_STATUS_SUCCESS
1440}
1441#[unsafe(no_mangle)]
1448pub unsafe extern "C" fn rkr_frame_builder_set_last_spread(
1449 builder_handle: *mut RKRConFrameBuilder,
1450 spread: *const f64,
1451) -> RKRStatus {
1452 if builder_handle.is_null() || spread.is_null() {
1453 return RKRStatus::RKR_STATUS_NULL_POINTER;
1454 }
1455 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1456 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
1457 builder.with_spread(d);
1458 RKRStatus::RKR_STATUS_SUCCESS
1459}
1460#[unsafe(no_mangle)]
1470pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1471 builder_handle: *mut RKRConFrameBuilder,
1472 energy: f64,
1473) -> RKRStatus {
1474 if builder_handle.is_null() {
1475 return RKRStatus::RKR_STATUS_NULL_POINTER;
1476 }
1477 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1478 builder.with_energy(energy);
1479 RKRStatus::RKR_STATUS_SUCCESS
1480}
1481fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1495 use crate::error::ParseError;
1496 match e {
1497 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1498 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1499 }
1500 ParseError::MassMismatch { .. } | ParseError::ValidationError(_) => {
1501 RKRStatus::RKR_STATUS_VALIDATION_ERROR
1502 }
1503 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1504 }
1505}
1506#[unsafe(no_mangle)]
1513pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1514 builder_handle: *const RKRConFrameBuilder,
1515) -> usize {
1516 if builder_handle.is_null() {
1517 return 0;
1518 }
1519 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1520 builder.atom_count()
1521}
1522#[unsafe(no_mangle)]
1526pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1527 builder_handle: *mut RKRConFrameBuilder,
1528 index: usize,
1529 x: f64,
1530 y: f64,
1531 z: f64,
1532) -> RKRStatus {
1533 if builder_handle.is_null() {
1534 return RKRStatus::RKR_STATUS_NULL_POINTER;
1535 }
1536 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1537 match builder.set_atom_position(index, x, y, z) {
1538 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1539 Err(e) => map_builder_err(e),
1540 }
1541}
1542#[unsafe(no_mangle)]
1546pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1547 builder_handle: *mut RKRConFrameBuilder,
1548 index: usize,
1549 velocity: *const f64,
1550) -> RKRStatus {
1551 if builder_handle.is_null() || velocity.is_null() {
1552 return RKRStatus::RKR_STATUS_NULL_POINTER;
1553 }
1554 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1555 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1556 match builder.set_atom_velocity(index, v) {
1557 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1558 Err(e) => map_builder_err(e),
1559 }
1560}
1561#[unsafe(no_mangle)]
1565pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1566 builder_handle: *mut RKRConFrameBuilder,
1567 index: usize,
1568 force: *const f64,
1569) -> RKRStatus {
1570 if builder_handle.is_null() || force.is_null() {
1571 return RKRStatus::RKR_STATUS_NULL_POINTER;
1572 }
1573 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1574 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1575 match builder.set_atom_force(index, f) {
1576 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1577 Err(e) => map_builder_err(e),
1578 }
1579}
1580#[unsafe(no_mangle)]
1585pub unsafe extern "C" fn rkr_frame_builder_set_atom_displacement(
1586 builder_handle: *mut RKRConFrameBuilder,
1587 index: usize,
1588 displacement: *const f64,
1589) -> RKRStatus {
1590 if builder_handle.is_null() || displacement.is_null() {
1591 return RKRStatus::RKR_STATUS_NULL_POINTER;
1592 }
1593 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1594 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1595 match builder.set_atom_displacement(index, d) {
1596 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1597 Err(e) => map_builder_err(e),
1598 }
1599}
1600#[unsafe(no_mangle)]
1605pub unsafe extern "C" fn rkr_frame_builder_set_atom_spread(
1606 builder_handle: *mut RKRConFrameBuilder,
1607 index: usize,
1608 spread: *const f64,
1609) -> RKRStatus {
1610 if builder_handle.is_null() || spread.is_null() {
1611 return RKRStatus::RKR_STATUS_NULL_POINTER;
1612 }
1613 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1614 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
1615 match builder.set_atom_spread(index, d) {
1616 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1617 Err(e) => map_builder_err(e),
1618 }
1619}
1620#[unsafe(no_mangle)]
1624pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1625 builder_handle: *mut RKRConFrameBuilder,
1626 index: usize,
1627 energy: f64,
1628) -> RKRStatus {
1629 if builder_handle.is_null() {
1630 return RKRStatus::RKR_STATUS_NULL_POINTER;
1631 }
1632 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1633 match builder.set_atom_energy(index, energy) {
1634 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1635 Err(e) => map_builder_err(e),
1636 }
1637}
1638#[unsafe(no_mangle)]
1642pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1643 builder_handle: *mut RKRConFrameBuilder,
1644 index: usize,
1645 fixed_x: bool,
1646 fixed_y: bool,
1647 fixed_z: bool,
1648) -> RKRStatus {
1649 if builder_handle.is_null() {
1650 return RKRStatus::RKR_STATUS_NULL_POINTER;
1651 }
1652 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1653 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1654 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1655 Err(e) => map_builder_err(e),
1656 }
1657}
1658#[unsafe(no_mangle)]
1662pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1663 builder_handle: *mut RKRConFrameBuilder,
1664 index: usize,
1665 mass: f64,
1666) -> RKRStatus {
1667 if builder_handle.is_null() {
1668 return RKRStatus::RKR_STATUS_NULL_POINTER;
1669 }
1670 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1671 match builder.set_atom_mass(index, mass) {
1672 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1673 Err(e) => map_builder_err(e),
1674 }
1675}
1676#[unsafe(no_mangle)]
1683pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1684 builder_handle: *mut RKRConFrameBuilder,
1685 index: usize,
1686 atom_id: u64,
1687) -> RKRStatus {
1688 if builder_handle.is_null() {
1689 return RKRStatus::RKR_STATUS_NULL_POINTER;
1690 }
1691 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1692 match builder.set_atom_id(index, atom_id) {
1693 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1694 Err(e) => map_builder_err(e),
1695 }
1696}
1697#[unsafe(no_mangle)]
1701pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1702 builder_handle: *mut RKRConFrameBuilder,
1703 index: usize,
1704) -> RKRStatus {
1705 if builder_handle.is_null() {
1706 return RKRStatus::RKR_STATUS_NULL_POINTER;
1707 }
1708 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1709 match builder.clear_atom_velocity(index) {
1710 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1711 Err(e) => map_builder_err(e),
1712 }
1713}
1714#[unsafe(no_mangle)]
1717pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1718 builder_handle: *mut RKRConFrameBuilder,
1719 index: usize,
1720) -> RKRStatus {
1721 if builder_handle.is_null() {
1722 return RKRStatus::RKR_STATUS_NULL_POINTER;
1723 }
1724 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1725 match builder.clear_atom_force(index) {
1726 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1727 Err(e) => map_builder_err(e),
1728 }
1729}
1730#[unsafe(no_mangle)]
1733pub unsafe extern "C" fn rkr_frame_builder_clear_atom_displacement(
1734 builder_handle: *mut RKRConFrameBuilder,
1735 index: usize,
1736) -> RKRStatus {
1737 if builder_handle.is_null() {
1738 return RKRStatus::RKR_STATUS_NULL_POINTER;
1739 }
1740 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1741 match builder.clear_atom_displacement(index) {
1742 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1743 Err(e) => map_builder_err(e),
1744 }
1745}
1746#[unsafe(no_mangle)]
1749pub unsafe extern "C" fn rkr_frame_builder_clear_atom_spread(
1750 builder_handle: *mut RKRConFrameBuilder,
1751 index: usize,
1752) -> RKRStatus {
1753 if builder_handle.is_null() {
1754 return RKRStatus::RKR_STATUS_NULL_POINTER;
1755 }
1756 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1757 match builder.clear_atom_spread(index) {
1758 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1759 Err(e) => map_builder_err(e),
1760 }
1761}
1762#[unsafe(no_mangle)]
1765pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1766 builder_handle: *mut RKRConFrameBuilder,
1767 index: usize,
1768) -> RKRStatus {
1769 if builder_handle.is_null() {
1770 return RKRStatus::RKR_STATUS_NULL_POINTER;
1771 }
1772 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1773 match builder.clear_atom_energy(index) {
1774 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1775 Err(e) => map_builder_err(e),
1776 }
1777}
1778#[unsafe(no_mangle)]
1783pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1784 builder_handle: *mut RKRConFrameBuilder,
1785 positions: *const f64,
1786 len: usize,
1787) -> RKRStatus {
1788 if builder_handle.is_null() || positions.is_null() {
1789 return RKRStatus::RKR_STATUS_NULL_POINTER;
1790 }
1791 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1792 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1793 match builder.set_positions_from_flat(slice) {
1794 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1795 Err(e) => map_builder_err(e),
1796 }
1797}
1798#[unsafe(no_mangle)]
1802pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1803 builder_handle: *mut RKRConFrameBuilder,
1804 forces: *const f64,
1805 len: usize,
1806) -> RKRStatus {
1807 if builder_handle.is_null() || forces.is_null() {
1808 return RKRStatus::RKR_STATUS_NULL_POINTER;
1809 }
1810 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1811 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1812 match builder.set_forces_from_flat(slice) {
1813 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1814 Err(e) => map_builder_err(e),
1815 }
1816}
1817#[unsafe(no_mangle)]
1822pub unsafe extern "C" fn rkr_frame_builder_set_displacements_from_flat(
1823 builder_handle: *mut RKRConFrameBuilder,
1824 displacements: *const f64,
1825 len: usize,
1826) -> RKRStatus {
1827 if builder_handle.is_null() || displacements.is_null() {
1828 return RKRStatus::RKR_STATUS_NULL_POINTER;
1829 }
1830 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1831 let slice = unsafe { std::slice::from_raw_parts(displacements, len) };
1832 match builder.set_displacements_from_flat(slice) {
1833 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1834 Err(e) => map_builder_err(e),
1835 }
1836}
1837#[unsafe(no_mangle)]
1842pub unsafe extern "C" fn rkr_frame_builder_set_spreads_from_flat(
1843 builder_handle: *mut RKRConFrameBuilder,
1844 spreads: *const f64,
1845 len: usize,
1846) -> RKRStatus {
1847 if builder_handle.is_null() || spreads.is_null() {
1848 return RKRStatus::RKR_STATUS_NULL_POINTER;
1849 }
1850 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1851 let slice = unsafe { std::slice::from_raw_parts(spreads, len) };
1852 match builder.set_spreads_from_flat(slice) {
1853 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1854 Err(e) => map_builder_err(e),
1855 }
1856}
1857#[unsafe(no_mangle)]
1861pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1862 builder_handle: *mut RKRConFrameBuilder,
1863 energies: *const f64,
1864 len: usize,
1865) -> RKRStatus {
1866 if builder_handle.is_null() || energies.is_null() {
1867 return RKRStatus::RKR_STATUS_NULL_POINTER;
1868 }
1869 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1870 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1871 match builder.set_atom_energies_from_flat(slice) {
1872 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1873 Err(e) => map_builder_err(e),
1874 }
1875}
1876#[unsafe(no_mangle)]
1880pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1881 builder_handle: *const RKRConFrameBuilder,
1882 index: usize,
1883 out_xyz: *mut f64,
1884) -> RKRStatus {
1885 if builder_handle.is_null() || out_xyz.is_null() {
1886 return RKRStatus::RKR_STATUS_NULL_POINTER;
1887 }
1888 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1889 match builder.get_atom_position(index) {
1890 Ok((x, y, z)) => unsafe {
1891 *out_xyz = x;
1892 *out_xyz.add(1) = y;
1893 *out_xyz.add(2) = z;
1894 RKRStatus::RKR_STATUS_SUCCESS
1895 },
1896 Err(e) => map_builder_err(e),
1897 }
1898}
1899#[unsafe(no_mangle)]
1906pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1907 builder_handle: *const RKRConFrameBuilder,
1908 index: usize,
1909 out_xyz: *mut f64,
1910 has_value: *mut bool,
1911) -> RKRStatus {
1912 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1913 return RKRStatus::RKR_STATUS_NULL_POINTER;
1914 }
1915 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1916 match builder.get_atom_velocity(index) {
1917 Ok(Some(v)) => unsafe {
1918 *out_xyz = v[0];
1919 *out_xyz.add(1) = v[1];
1920 *out_xyz.add(2) = v[2];
1921 *has_value = true;
1922 RKRStatus::RKR_STATUS_SUCCESS
1923 },
1924 Ok(None) => unsafe {
1925 *has_value = false;
1926 RKRStatus::RKR_STATUS_SUCCESS
1927 },
1928 Err(e) => map_builder_err(e),
1929 }
1930}
1931#[unsafe(no_mangle)]
1934pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1935 builder_handle: *const RKRConFrameBuilder,
1936 index: usize,
1937 out_xyz: *mut f64,
1938 has_value: *mut bool,
1939) -> RKRStatus {
1940 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1941 return RKRStatus::RKR_STATUS_NULL_POINTER;
1942 }
1943 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1944 match builder.get_atom_force(index) {
1945 Ok(Some(f)) => unsafe {
1946 *out_xyz = f[0];
1947 *out_xyz.add(1) = f[1];
1948 *out_xyz.add(2) = f[2];
1949 *has_value = true;
1950 RKRStatus::RKR_STATUS_SUCCESS
1951 },
1952 Ok(None) => unsafe {
1953 *has_value = false;
1954 RKRStatus::RKR_STATUS_SUCCESS
1955 },
1956 Err(e) => map_builder_err(e),
1957 }
1958}
1959#[unsafe(no_mangle)]
1965pub unsafe extern "C" fn rkr_frame_builder_get_atom_displacement(
1966 builder_handle: *const RKRConFrameBuilder,
1967 index: usize,
1968 out_xyz: *mut f64,
1969 has_value: *mut bool,
1970) -> RKRStatus {
1971 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1972 return RKRStatus::RKR_STATUS_NULL_POINTER;
1973 }
1974 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1975 match builder.get_atom_displacement(index) {
1976 Ok(Some(d)) => unsafe {
1977 *out_xyz = d[0];
1978 *out_xyz.add(1) = d[1];
1979 *out_xyz.add(2) = d[2];
1980 *has_value = true;
1981 RKRStatus::RKR_STATUS_SUCCESS
1982 },
1983 Ok(None) => unsafe {
1984 *has_value = false;
1985 RKRStatus::RKR_STATUS_SUCCESS
1986 },
1987 Err(e) => map_builder_err(e),
1988 }
1989}
1990#[unsafe(no_mangle)]
1996pub unsafe extern "C" fn rkr_frame_builder_get_atom_spread(
1997 builder_handle: *const RKRConFrameBuilder,
1998 index: usize,
1999 out_xyz: *mut f64,
2000 has_value: *mut bool,
2001) -> RKRStatus {
2002 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
2003 return RKRStatus::RKR_STATUS_NULL_POINTER;
2004 }
2005 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2006 match builder.get_atom_spread(index) {
2007 Ok(Some(d)) => unsafe {
2008 *out_xyz = d[0];
2009 *out_xyz.add(1) = d[1];
2010 *out_xyz.add(2) = d[2];
2011 *has_value = true;
2012 RKRStatus::RKR_STATUS_SUCCESS
2013 },
2014 Ok(None) => unsafe {
2015 *has_value = false;
2016 RKRStatus::RKR_STATUS_SUCCESS
2017 },
2018 Err(e) => map_builder_err(e),
2019 }
2020}
2021#[unsafe(no_mangle)]
2027pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
2028 builder_handle: *const RKRConFrameBuilder,
2029 index: usize,
2030 out_value: *mut f64,
2031 has_value: *mut bool,
2032) -> RKRStatus {
2033 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
2034 return RKRStatus::RKR_STATUS_NULL_POINTER;
2035 }
2036 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2037 match builder.get_atom_energy(index) {
2038 Ok(Some(e)) => unsafe {
2039 *out_value = e;
2040 *has_value = true;
2041 RKRStatus::RKR_STATUS_SUCCESS
2042 },
2043 Ok(None) => unsafe {
2044 *has_value = false;
2045 RKRStatus::RKR_STATUS_SUCCESS
2046 },
2047 Err(e) => map_builder_err(e),
2048 }
2049}
2050#[unsafe(no_mangle)]
2054pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
2055 builder_handle: *const RKRConFrameBuilder,
2056 index: usize,
2057 out_mass: *mut f64,
2058) -> RKRStatus {
2059 if builder_handle.is_null() || out_mass.is_null() {
2060 return RKRStatus::RKR_STATUS_NULL_POINTER;
2061 }
2062 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2063 match builder.get_atom_mass(index) {
2064 Ok(m) => unsafe {
2065 *out_mass = m;
2066 RKRStatus::RKR_STATUS_SUCCESS
2067 },
2068 Err(e) => map_builder_err(e),
2069 }
2070}
2071pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
2094fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
2095 use crate::error::ParseError;
2096 match e {
2097 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
2098 RKRStatus::RKR_STATUS_DEVICE_MISMATCH
2099 }
2100 ParseError::ValidationError(ref msg)
2101 if msg.contains("no device allocator") || msg.contains("allocator") =>
2102 {
2103 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED
2104 }
2105 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2106 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
2107 }
2108}
2109pub mod rkr_dl_type_code {
2113 pub const RKR_DL_INT: u8 = 0;
2114 pub const RKR_DL_UINT: u8 = 1;
2115 pub const RKR_DL_FLOAT: u8 = 2;
2116 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
2117 pub const RKR_DL_BFLOAT: u8 = 4;
2118 pub const RKR_DL_COMPLEX: u8 = 5;
2119 pub const RKR_DL_BOOL: u8 = 6;
2120}
2121
2122pub mod rkr_dl_device_type {
2124 pub const RKR_DL_CPU: i32 = 1;
2125 pub const RKR_DL_CUDA: i32 = 2;
2126 pub const RKR_DL_CUDA_HOST: i32 = 3;
2127}
2128
2129#[repr(C)]
2133#[derive(Clone, Copy, Debug)]
2134pub struct RKRDLDataType {
2135 pub code: u8,
2137 pub bits: u8,
2139 pub lanes: u16,
2141}
2142
2143#[repr(C)]
2146#[derive(Clone, Copy, Debug)]
2147pub struct RKRDLDevice {
2148 pub device_type: i32,
2150 pub device_id: i32,
2151}
2152
2153#[repr(C)]
2167#[derive(Clone, Copy, Debug)]
2168pub struct RKRDlpackExportOptions {
2169 pub dtype: RKRDLDataType,
2171 pub device: RKRDLDevice,
2173}
2174
2175impl Default for RKRDlpackExportOptions {
2176 fn default() -> Self {
2177 Self {
2178 dtype: RKRDLDataType {
2179 code: rkr_dl_type_code::RKR_DL_FLOAT,
2180 bits: 64,
2181 lanes: 1,
2182 },
2183 device: RKRDLDevice {
2184 device_type: rkr_dl_device_type::RKR_DL_CPU,
2185 device_id: 0,
2186 },
2187 }
2188 }
2189}
2190
2191fn resolve_dlpack_opts(
2194 opts: *const RKRDlpackExportOptions,
2195) -> Result<RKRDlpackExportOptions, RKRStatus> {
2196 let o = if opts.is_null() {
2197 RKRDlpackExportOptions::default()
2198 } else {
2199 unsafe { *opts }
2200 };
2201 let dt = o.device.device_type;
2202 if dt == rkr_dl_device_type::RKR_DL_CPU {
2203 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
2205 #[cfg(not(feature = "cuda"))]
2206 {
2207 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
2208 }
2209 #[cfg(feature = "cuda")]
2210 {
2211 }
2213 } else {
2214 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
2215 }
2216 if o.dtype.lanes != 1 {
2217 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
2218 }
2219 Ok(o)
2220}
2221
2222fn finish_dlpack_tensor<E: std::fmt::Display>(
2223 result: Result<dlpk::DLPackTensor, E>,
2224 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2225) -> RKRStatus {
2226 match result {
2227 Ok(tensor) => {
2228 let raw = tensor.into_raw();
2229 unsafe {
2230 *out_tensor = raw.as_ptr();
2231 }
2232 RKRStatus::RKR_STATUS_SUCCESS
2233 }
2234 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
2235 "DLPack export failed: {e}"
2236 ))),
2237 }
2238}
2239
2240fn export_f64_slice_as_dlpack(
2243 data: &[f64],
2244 shape: &[usize],
2245 dtype: RKRDLDataType,
2246 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2247) -> RKRStatus {
2248 use rkr_dl_type_code::*;
2249 if dtype.lanes != 1 {
2250 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2251 }
2252 macro_rules! arc_export {
2253 ($ty:ty, $map:expr) => {{
2254 let v: Vec<$ty> = data.iter().map($map).collect();
2255 match *shape {
2256 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
2257 Ok(a) => dlpk::DLPackTensor::try_from(a),
2258 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2259 },
2260 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
2261 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2262 }
2263 }};
2264 }
2265 let tensor = match (dtype.code, dtype.bits) {
2266 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
2267 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
2268 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
2269 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
2270 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
2271 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
2272 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
2273 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
2274 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
2275 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
2276 (RKR_DL_BOOL, 8) => {
2277 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
2279 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
2280 }
2281 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2283 };
2284 finish_dlpack_tensor(tensor, out_tensor)
2285}
2286
2287fn export_owned_array2_dlpack_opts(
2288 arr: &ndarray::ArcArray2<f64>,
2289 opts: &RKRDlpackExportOptions,
2290 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2291) -> RKRStatus {
2292 let (r, c) = arr.dim();
2293 let flat: Vec<f64> = arr.iter().copied().collect();
2294 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2295 #[cfg(feature = "cuda")]
2296 {
2297 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2299 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2300 }
2301 return finish_dlpack_tensor(
2302 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2303 &[r, c],
2304 &flat,
2305 opts.device.device_id,
2306 ),
2307 out_tensor,
2308 );
2309 }
2310 #[cfg(not(feature = "cuda"))]
2311 {
2312 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2313 }
2314 }
2315 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
2316}
2317
2318fn export_owned_array1_f64_dlpack_opts(
2319 arr: &ndarray::ArcArray1<f64>,
2320 opts: &RKRDlpackExportOptions,
2321 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2322) -> RKRStatus {
2323 let flat = arr.to_vec();
2324 let n = flat.len();
2325 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2326 #[cfg(feature = "cuda")]
2327 {
2328 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2329 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2330 }
2331 return finish_dlpack_tensor(
2332 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2333 &[n],
2334 &flat,
2335 opts.device.device_id,
2336 ),
2337 out_tensor,
2338 );
2339 }
2340 #[cfg(not(feature = "cuda"))]
2341 {
2342 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2343 }
2344 }
2345 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
2346}
2347
2348fn export_owned_array1_u64_dlpack(
2349 arr: &ndarray::ArcArray1<u64>,
2350 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2351) -> RKRStatus {
2352 finish_dlpack_tensor(dlpk::DLPackTensor::try_from(arr.clone()), out_tensor)
2353}
2354
2355fn export_owned_array1_u64_dlpack_opts(
2357 arr: &ndarray::ArcArray1<u64>,
2358 opts: &RKRDlpackExportOptions,
2359 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2360) -> RKRStatus {
2361 use rkr_dl_type_code::*;
2362 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
2363 return export_owned_array1_u64_dlpack(arr, out_tensor);
2364 }
2365 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
2366 let n = as_f64.len();
2367 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
2368}
2369#[unsafe(no_mangle)]
2380pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
2381 builder_handle: *const RKRConFrameBuilder,
2382 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2383) -> RKRStatus {
2384 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2385}
2386
2387#[unsafe(no_mangle)]
2394pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
2395 builder_handle: *const RKRConFrameBuilder,
2396 opts: *const RKRDlpackExportOptions,
2397 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2398) -> RKRStatus {
2399 if builder_handle.is_null() || out_tensor.is_null() {
2400 return RKRStatus::RKR_STATUS_NULL_POINTER;
2401 }
2402 let o = match resolve_dlpack_opts(opts) {
2403 Ok(o) => o,
2404 Err(st) => return st,
2405 };
2406 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2407 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
2408}
2409
2410#[unsafe(no_mangle)]
2420pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
2421 builder_handle: *const RKRConFrameBuilder,
2422 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2423) -> RKRStatus {
2424 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2425}
2426
2427#[unsafe(no_mangle)]
2428pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
2429 builder_handle: *const RKRConFrameBuilder,
2430 opts: *const RKRDlpackExportOptions,
2431 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2432) -> RKRStatus {
2433 if builder_handle.is_null() || out_tensor.is_null() {
2434 return RKRStatus::RKR_STATUS_NULL_POINTER;
2435 }
2436 let o = match resolve_dlpack_opts(opts) {
2437 Ok(o) => o,
2438 Err(st) => return st,
2439 };
2440 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2441 if !builder.has_velocities_section() {
2442 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2443 }
2444 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
2445}
2446
2447#[unsafe(no_mangle)]
2456pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
2457 builder_handle: *const RKRConFrameBuilder,
2458 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2459) -> RKRStatus {
2460 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2461}
2462
2463#[unsafe(no_mangle)]
2464pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
2465 builder_handle: *const RKRConFrameBuilder,
2466 opts: *const RKRDlpackExportOptions,
2467 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2468) -> RKRStatus {
2469 if builder_handle.is_null() || out_tensor.is_null() {
2470 return RKRStatus::RKR_STATUS_NULL_POINTER;
2471 }
2472 let o = match resolve_dlpack_opts(opts) {
2473 Ok(o) => o,
2474 Err(st) => return st,
2475 };
2476 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2477 if !builder.has_forces_section() {
2478 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2479 }
2480 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
2481}
2482
2483#[unsafe(no_mangle)]
2492pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack(
2493 builder_handle: *const RKRConFrameBuilder,
2494 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2495) -> RKRStatus {
2496 unsafe {
2497 rkr_frame_builder_displacements_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2498 }
2499}
2500
2501#[unsafe(no_mangle)]
2508pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack_ex(
2509 builder_handle: *const RKRConFrameBuilder,
2510 opts: *const RKRDlpackExportOptions,
2511 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2512) -> RKRStatus {
2513 if builder_handle.is_null() || out_tensor.is_null() {
2514 return RKRStatus::RKR_STATUS_NULL_POINTER;
2515 }
2516 let o = match resolve_dlpack_opts(opts) {
2517 Ok(o) => o,
2518 Err(st) => return st,
2519 };
2520 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2521 if !builder.has_displacements_section() {
2522 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2523 }
2524 export_owned_array2_dlpack_opts(builder.displacements_2d_ref(), &o, out_tensor)
2525}
2526
2527#[unsafe(no_mangle)]
2536pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack(
2537 builder_handle: *const RKRConFrameBuilder,
2538 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2539) -> RKRStatus {
2540 unsafe { rkr_frame_builder_spreads_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2541}
2542
2543#[unsafe(no_mangle)]
2550pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack_ex(
2551 builder_handle: *const RKRConFrameBuilder,
2552 opts: *const RKRDlpackExportOptions,
2553 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2554) -> RKRStatus {
2555 if builder_handle.is_null() || out_tensor.is_null() {
2556 return RKRStatus::RKR_STATUS_NULL_POINTER;
2557 }
2558 let o = match resolve_dlpack_opts(opts) {
2559 Ok(o) => o,
2560 Err(st) => return st,
2561 };
2562 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2563 if !builder.has_spreads_section() {
2564 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2565 }
2566 export_owned_array2_dlpack_opts(builder.spreads_2d_ref(), &o, out_tensor)
2567}
2568
2569#[unsafe(no_mangle)]
2578pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
2579 builder_handle: *const RKRConFrameBuilder,
2580 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2581) -> RKRStatus {
2582 unsafe {
2583 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2584 }
2585}
2586
2587#[unsafe(no_mangle)]
2588pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
2589 builder_handle: *const RKRConFrameBuilder,
2590 opts: *const RKRDlpackExportOptions,
2591 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2592) -> RKRStatus {
2593 if builder_handle.is_null() || out_tensor.is_null() {
2594 return RKRStatus::RKR_STATUS_NULL_POINTER;
2595 }
2596 let o = match resolve_dlpack_opts(opts) {
2597 Ok(o) => o,
2598 Err(st) => return st,
2599 };
2600 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2601 if !builder.has_energies_section() {
2602 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2603 }
2604 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
2605}
2606
2607#[unsafe(no_mangle)]
2613pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
2614 builder_handle: *const RKRConFrameBuilder,
2615 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2616) -> RKRStatus {
2617 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2618}
2619
2620#[unsafe(no_mangle)]
2621pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
2622 builder_handle: *const RKRConFrameBuilder,
2623 opts: *const RKRDlpackExportOptions,
2624 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2625) -> RKRStatus {
2626 if builder_handle.is_null() || out_tensor.is_null() {
2627 return RKRStatus::RKR_STATUS_NULL_POINTER;
2628 }
2629 let o = match resolve_dlpack_opts(opts) {
2630 Ok(o) => o,
2631 Err(st) => return st,
2632 };
2633 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2634 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
2635}
2636#[unsafe(no_mangle)]
2642pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
2643 builder_handle: *const RKRConFrameBuilder,
2644 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2645) -> RKRStatus {
2646 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2647}
2648
2649#[unsafe(no_mangle)]
2650pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2651 builder_handle: *const RKRConFrameBuilder,
2652 opts: *const RKRDlpackExportOptions,
2653 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2654) -> RKRStatus {
2655 if builder_handle.is_null() || out_tensor.is_null() {
2656 return RKRStatus::RKR_STATUS_NULL_POINTER;
2657 }
2658 let o = match resolve_dlpack_opts(opts) {
2659 Ok(o) => o,
2660 Err(st) => return st,
2661 };
2662 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2663 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2664}
2665#[unsafe(no_mangle)]
2689pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2690 builder_handle: *mut RKRConFrameBuilder,
2691) -> *mut f64 {
2692 if builder_handle.is_null() {
2693 return std::ptr::null_mut();
2694 }
2695 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2696 builder
2697 .positions_view_mut()
2698 .as_slice_memory_order_mut()
2699 .map(|s| s.as_mut_ptr())
2700 .unwrap_or(std::ptr::null_mut())
2701}
2702#[unsafe(no_mangle)]
2709pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
2710 builder_handle: *mut RKRConFrameBuilder,
2711) -> *mut f64 {
2712 if builder_handle.is_null() {
2713 return std::ptr::null_mut();
2714 }
2715 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2716 if !builder.has_velocities_section() {
2717 return std::ptr::null_mut();
2718 }
2719 let slice = builder.velocities_mut();
2720 if slice.is_empty() {
2721 std::ptr::null_mut()
2722 } else {
2723 slice.as_mut_ptr()
2724 }
2725}
2726#[unsafe(no_mangle)]
2732pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2733 builder_handle: *mut RKRConFrameBuilder,
2734) -> *mut f64 {
2735 if builder_handle.is_null() {
2736 return std::ptr::null_mut();
2737 }
2738 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2739 if !builder.has_forces_section() {
2740 return std::ptr::null_mut();
2741 }
2742 let slice = builder.forces_mut();
2743 if slice.is_empty() {
2744 std::ptr::null_mut()
2745 } else {
2746 slice.as_mut_ptr()
2747 }
2748}
2749#[unsafe(no_mangle)]
2755pub unsafe extern "C" fn rkr_frame_builder_displacements_data(
2756 builder_handle: *mut RKRConFrameBuilder,
2757) -> *mut f64 {
2758 if builder_handle.is_null() {
2759 return std::ptr::null_mut();
2760 }
2761 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2762 if !builder.has_displacements_section() {
2763 return std::ptr::null_mut();
2764 }
2765 let slice = builder.displacements_mut();
2766 if slice.is_empty() {
2767 std::ptr::null_mut()
2768 } else {
2769 slice.as_mut_ptr()
2770 }
2771}
2772#[unsafe(no_mangle)]
2778pub unsafe extern "C" fn rkr_frame_builder_spreads_data(
2779 builder_handle: *mut RKRConFrameBuilder,
2780) -> *mut f64 {
2781 if builder_handle.is_null() {
2782 return std::ptr::null_mut();
2783 }
2784 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2785 if !builder.has_spreads_section() {
2786 return std::ptr::null_mut();
2787 }
2788 let slice = builder.spreads_mut();
2789 if slice.is_empty() {
2790 std::ptr::null_mut()
2791 } else {
2792 slice.as_mut_ptr()
2793 }
2794}
2795#[unsafe(no_mangle)]
2801pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2802 builder_handle: *mut RKRConFrameBuilder,
2803) -> *mut f64 {
2804 if builder_handle.is_null() {
2805 return std::ptr::null_mut();
2806 }
2807 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2808 if !builder.has_energies_section() {
2809 return std::ptr::null_mut();
2810 }
2811 let slice = builder.atom_energies_mut();
2812 if slice.is_empty() {
2813 std::ptr::null_mut()
2814 } else {
2815 slice.as_mut_ptr()
2816 }
2817}
2818#[unsafe(no_mangle)]
2823pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2824 builder_handle: *mut RKRConFrameBuilder,
2825) -> *mut f64 {
2826 if builder_handle.is_null() {
2827 return std::ptr::null_mut();
2828 }
2829 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2830 let slice = builder.masses_mut();
2831 if slice.is_empty() {
2832 std::ptr::null_mut()
2833 } else {
2834 slice.as_mut_ptr()
2835 }
2836}
2837#[unsafe(no_mangle)]
2842pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2843 builder_handle: *const RKRConFrameBuilder,
2844) -> *const u64 {
2845 if builder_handle.is_null() {
2846 return std::ptr::null();
2847 }
2848 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2849 let slice = builder.atom_ids();
2850 if slice.is_empty() {
2851 std::ptr::null()
2852 } else {
2853 slice.as_ptr()
2854 }
2855}
2856#[unsafe(no_mangle)]
2868#[allow(clippy::too_many_arguments)]
2869pub unsafe extern "C" fn rkr_frame_add_atom_full(
2870 builder_handle: *mut RKRConFrameBuilder,
2871 symbol: *const c_char,
2872 x: f64,
2873 y: f64,
2874 z: f64,
2875 fixed_x: bool,
2876 fixed_y: bool,
2877 fixed_z: bool,
2878 atom_id: u64,
2879 mass: f64,
2880 velocity: *const f64,
2881 force: *const f64,
2882) -> RKRStatus {
2883 let velocity = if velocity.is_null() {
2884 None
2885 } else {
2886 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
2887 };
2888 let force = if force.is_null() {
2889 None
2890 } else {
2891 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
2892 };
2893 unsafe {
2894 add_builder_atom(
2895 builder_handle,
2896 symbol,
2897 x,
2898 y,
2899 z,
2900 [fixed_x, fixed_y, fixed_z],
2901 atom_id,
2902 mass,
2903 velocity,
2904 force,
2905 )
2906 }
2907}
2908#[unsafe(no_mangle)]
2924pub unsafe extern "C" fn rkr_frame_new(
2925 cell: *const f64,
2926 angles: *const f64,
2927 prebox0: *const c_char,
2928 prebox1: *const c_char,
2929 postbox0: *const c_char,
2930 postbox1: *const c_char,
2931) -> *mut RKRConFrameBuilder {
2932 if cell.is_null() || angles.is_null() {
2933 return ptr::null_mut();
2934 }
2935 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
2936 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
2937 let get_str = |p: *const c_char| -> String {
2938 if p.is_null() {
2939 String::new()
2940 } else {
2941 unsafe { CStr::from_ptr(p) }
2942 .to_str()
2943 .unwrap_or("")
2944 .to_string()
2945 }
2946 };
2947 let _ = get_str(prebox1);
2950 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
2951 builder
2952 .prebox_header(get_str(prebox0))
2953 .postbox_header([get_str(postbox0), get_str(postbox1)]);
2954 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
2955}
2956#[unsafe(no_mangle)]
2962pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
2963 builder_handle: *mut RKRConFrameBuilder,
2964 metadata_json: *const c_char,
2965) -> RKRStatus {
2966 if builder_handle.is_null() || metadata_json.is_null() {
2967 return RKRStatus::RKR_STATUS_NULL_POINTER;
2968 }
2969 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2970 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
2971 Ok(s) => s,
2972 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2973 };
2974 match builder.set_metadata_json(metadata_json) {
2975 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
2976 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
2977 }
2978}
2979#[unsafe(no_mangle)]
2989pub unsafe extern "C" fn rkr_frame_builder_set_units(
2990 builder_handle: *mut RKRConFrameBuilder,
2991 units_json: *const c_char,
2992) -> RKRStatus {
2993 if builder_handle.is_null() || units_json.is_null() {
2994 return RKRStatus::RKR_STATUS_NULL_POINTER;
2995 }
2996 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2997 let units_json = match unsafe { CStr::from_ptr(units_json).to_str() } {
2998 Ok(s) => s,
2999 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3000 };
3001 let units: serde_json::Value = match serde_json::from_str(units_json) {
3002 Ok(value) => value,
3003 Err(_) => return RKRStatus::RKR_STATUS_INVALID_JSON,
3004 };
3005 match builder.set_units(units) {
3006 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
3007 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
3008 }
3009}
3010#[unsafe(no_mangle)]
3017pub unsafe extern "C" fn rkr_frame_builder_set_con_spec_minor(
3018 builder_handle: *mut RKRConFrameBuilder,
3019 minor: u32,
3020) -> RKRStatus {
3021 if builder_handle.is_null() {
3022 return RKRStatus::RKR_STATUS_NULL_POINTER;
3023 }
3024 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3025 builder.set_con_spec_minor(minor);
3026 RKRStatus::RKR_STATUS_SUCCESS
3027}
3028#[unsafe(no_mangle)]
3034pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
3035 builder_handle: *mut RKRConFrameBuilder,
3036 key: *const c_char,
3037 value: f64,
3038) -> RKRStatus {
3039 if builder_handle.is_null() || key.is_null() {
3040 return RKRStatus::RKR_STATUS_NULL_POINTER;
3041 }
3042 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3043 let key = match unsafe { CStr::from_ptr(key).to_str() } {
3044 Ok(s) => s,
3045 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3046 };
3047 builder.set_scalar_metadata(key, value);
3048 RKRStatus::RKR_STATUS_SUCCESS
3049}
3050#[unsafe(no_mangle)]
3056pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
3057 builder_handle: *mut RKRConFrameBuilder,
3058 key: *const c_char,
3059 value: *const c_char,
3060) -> RKRStatus {
3061 if builder_handle.is_null() || key.is_null() || value.is_null() {
3062 return RKRStatus::RKR_STATUS_NULL_POINTER;
3063 }
3064 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3065 let key = match unsafe { CStr::from_ptr(key).to_str() } {
3066 Ok(s) => s,
3067 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3068 };
3069 let value = match unsafe { CStr::from_ptr(value).to_str() } {
3070 Ok(s) => s,
3071 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
3072 };
3073 builder.set_string_metadata(key, value);
3074 RKRStatus::RKR_STATUS_SUCCESS
3075}
3076#[unsafe(no_mangle)]
3082pub unsafe extern "C" fn rkr_frame_builder_set_energy(
3083 builder_handle: *mut RKRConFrameBuilder,
3084 energy: f64,
3085) -> RKRStatus {
3086 if builder_handle.is_null() {
3087 return RKRStatus::RKR_STATUS_NULL_POINTER;
3088 }
3089 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3090 builder.set_energy(energy);
3091 RKRStatus::RKR_STATUS_SUCCESS
3092}
3093#[unsafe(no_mangle)]
3099pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
3100 builder_handle: *mut RKRConFrameBuilder,
3101 idx: u64,
3102) -> RKRStatus {
3103 if builder_handle.is_null() {
3104 return RKRStatus::RKR_STATUS_NULL_POINTER;
3105 }
3106 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3107 builder.set_frame_index(idx);
3108 RKRStatus::RKR_STATUS_SUCCESS
3109}
3110#[unsafe(no_mangle)]
3116pub unsafe extern "C" fn rkr_frame_builder_set_time(
3117 builder_handle: *mut RKRConFrameBuilder,
3118 time: f64,
3119) -> RKRStatus {
3120 if builder_handle.is_null() {
3121 return RKRStatus::RKR_STATUS_NULL_POINTER;
3122 }
3123 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3124 builder.set_time(time);
3125 RKRStatus::RKR_STATUS_SUCCESS
3126}
3127#[unsafe(no_mangle)]
3133pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
3134 builder_handle: *mut RKRConFrameBuilder,
3135 dt: f64,
3136) -> RKRStatus {
3137 if builder_handle.is_null() {
3138 return RKRStatus::RKR_STATUS_NULL_POINTER;
3139 }
3140 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3141 builder.set_timestep(dt);
3142 RKRStatus::RKR_STATUS_SUCCESS
3143}
3144#[unsafe(no_mangle)]
3150pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
3151 builder_handle: *mut RKRConFrameBuilder,
3152 bead: u64,
3153) -> RKRStatus {
3154 if builder_handle.is_null() {
3155 return RKRStatus::RKR_STATUS_NULL_POINTER;
3156 }
3157 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3158 builder.set_neb_bead(bead);
3159 RKRStatus::RKR_STATUS_SUCCESS
3160}
3161#[unsafe(no_mangle)]
3167pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
3168 builder_handle: *mut RKRConFrameBuilder,
3169 band: u64,
3170) -> RKRStatus {
3171 if builder_handle.is_null() {
3172 return RKRStatus::RKR_STATUS_NULL_POINTER;
3173 }
3174 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3175 builder.set_neb_band(band);
3176 RKRStatus::RKR_STATUS_SUCCESS
3177}
3178#[unsafe(no_mangle)]
3195pub unsafe extern "C" fn rkr_frame_add_atom(
3196 builder_handle: *mut RKRConFrameBuilder,
3197 symbol: *const c_char,
3198 x: f64,
3199 y: f64,
3200 z: f64,
3201 is_fixed: bool,
3202 atom_id: u64,
3203 mass: f64,
3204) -> RKRStatus {
3205 unsafe {
3206 add_builder_atom(
3207 builder_handle,
3208 symbol,
3209 x,
3210 y,
3211 z,
3212 [is_fixed; 3],
3213 atom_id,
3214 mass,
3215 None,
3216 None,
3217 )
3218 }
3219}
3220#[unsafe(no_mangle)]
3227pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
3228 builder_handle: *mut RKRConFrameBuilder,
3229 symbol: *const c_char,
3230 x: f64,
3231 y: f64,
3232 z: f64,
3233 fixed_x: bool,
3234 fixed_y: bool,
3235 fixed_z: bool,
3236 atom_id: u64,
3237 mass: f64,
3238) -> RKRStatus {
3239 unsafe {
3240 add_builder_atom(
3241 builder_handle,
3242 symbol,
3243 x,
3244 y,
3245 z,
3246 [fixed_x, fixed_y, fixed_z],
3247 atom_id,
3248 mass,
3249 None,
3250 None,
3251 )
3252 }
3253}
3254#[unsafe(no_mangle)]
3261pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
3262 builder_handle: *mut RKRConFrameBuilder,
3263 symbol: *const c_char,
3264 x: f64,
3265 y: f64,
3266 z: f64,
3267 is_fixed: bool,
3268 atom_id: u64,
3269 mass: f64,
3270 vx: f64,
3271 vy: f64,
3272 vz: f64,
3273) -> RKRStatus {
3274 unsafe {
3275 add_builder_atom(
3276 builder_handle,
3277 symbol,
3278 x,
3279 y,
3280 z,
3281 [is_fixed; 3],
3282 atom_id,
3283 mass,
3284 Some([vx, vy, vz]),
3285 None,
3286 )
3287 }
3288}
3289#[unsafe(no_mangle)]
3296pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
3297 builder_handle: *mut RKRConFrameBuilder,
3298 symbol: *const c_char,
3299 x: f64,
3300 y: f64,
3301 z: f64,
3302 fixed_x: bool,
3303 fixed_y: bool,
3304 fixed_z: bool,
3305 atom_id: u64,
3306 mass: f64,
3307 vx: f64,
3308 vy: f64,
3309 vz: f64,
3310) -> RKRStatus {
3311 unsafe {
3312 add_builder_atom(
3313 builder_handle,
3314 symbol,
3315 x,
3316 y,
3317 z,
3318 [fixed_x, fixed_y, fixed_z],
3319 atom_id,
3320 mass,
3321 Some([vx, vy, vz]),
3322 None,
3323 )
3324 }
3325}
3326#[unsafe(no_mangle)]
3333pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
3334 builder_handle: *mut RKRConFrameBuilder,
3335 symbol: *const c_char,
3336 x: f64,
3337 y: f64,
3338 z: f64,
3339 is_fixed: bool,
3340 atom_id: u64,
3341 mass: f64,
3342 fx: f64,
3343 fy: f64,
3344 fz: f64,
3345) -> RKRStatus {
3346 unsafe {
3347 add_builder_atom(
3348 builder_handle,
3349 symbol,
3350 x,
3351 y,
3352 z,
3353 [is_fixed; 3],
3354 atom_id,
3355 mass,
3356 None,
3357 Some([fx, fy, fz]),
3358 )
3359 }
3360}
3361#[unsafe(no_mangle)]
3368pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
3369 builder_handle: *mut RKRConFrameBuilder,
3370 symbol: *const c_char,
3371 x: f64,
3372 y: f64,
3373 z: f64,
3374 fixed_x: bool,
3375 fixed_y: bool,
3376 fixed_z: bool,
3377 atom_id: u64,
3378 mass: f64,
3379 fx: f64,
3380 fy: f64,
3381 fz: f64,
3382) -> RKRStatus {
3383 unsafe {
3384 add_builder_atom(
3385 builder_handle,
3386 symbol,
3387 x,
3388 y,
3389 z,
3390 [fixed_x, fixed_y, fixed_z],
3391 atom_id,
3392 mass,
3393 None,
3394 Some([fx, fy, fz]),
3395 )
3396 }
3397}
3398#[unsafe(no_mangle)]
3405pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
3406 builder_handle: *mut RKRConFrameBuilder,
3407 symbol: *const c_char,
3408 x: f64,
3409 y: f64,
3410 z: f64,
3411 is_fixed: bool,
3412 atom_id: u64,
3413 mass: f64,
3414 vx: f64,
3415 vy: f64,
3416 vz: f64,
3417 fx: f64,
3418 fy: f64,
3419 fz: f64,
3420) -> RKRStatus {
3421 unsafe {
3422 add_builder_atom(
3423 builder_handle,
3424 symbol,
3425 x,
3426 y,
3427 z,
3428 [is_fixed; 3],
3429 atom_id,
3430 mass,
3431 Some([vx, vy, vz]),
3432 Some([fx, fy, fz]),
3433 )
3434 }
3435}
3436#[unsafe(no_mangle)]
3443pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
3444 builder_handle: *mut RKRConFrameBuilder,
3445 symbol: *const c_char,
3446 x: f64,
3447 y: f64,
3448 z: f64,
3449 fixed_x: bool,
3450 fixed_y: bool,
3451 fixed_z: bool,
3452 atom_id: u64,
3453 mass: f64,
3454 vx: f64,
3455 vy: f64,
3456 vz: f64,
3457 fx: f64,
3458 fy: f64,
3459 fz: f64,
3460) -> RKRStatus {
3461 unsafe {
3462 add_builder_atom(
3463 builder_handle,
3464 symbol,
3465 x,
3466 y,
3467 z,
3468 [fixed_x, fixed_y, fixed_z],
3469 atom_id,
3470 mass,
3471 Some([vx, vy, vz]),
3472 Some([fx, fy, fz]),
3473 )
3474 }
3475}
3476#[unsafe(no_mangle)]
3485pub unsafe extern "C" fn rkr_frame_builder_build(
3486 builder_handle: *mut RKRConFrameBuilder,
3487) -> *mut RKRConFrame {
3488 if builder_handle.is_null() {
3489 return ptr::null_mut();
3490 }
3491 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3492 match builder.build() {
3493 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3494 Err(_) => ptr::null_mut(),
3495 }
3496}
3497#[unsafe(no_mangle)]
3502pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
3503 if !builder_handle.is_null() {
3504 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3505 }
3506}
3507#[unsafe(no_mangle)]
3524pub unsafe extern "C" fn rkr_frame_builder_clone(
3525 builder_handle: *const RKRConFrameBuilder,
3526) -> *mut RKRConFrameBuilder {
3527 if builder_handle.is_null() {
3528 return std::ptr::null_mut();
3529 }
3530 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3531 let cloned = builder.clone();
3532 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
3533}
3534#[unsafe(no_mangle)]
3540pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3541 let filename = match unsafe { cstr_path(filename_c) } {
3542 Some(s) => s,
3543 None => return ptr::null_mut(),
3544 };
3545 match crate::compression::gzip_writer(Path::new(filename)) {
3546 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3547 Err(_) => ptr::null_mut(),
3548 }
3549}
3550#[unsafe(no_mangle)]
3557pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
3558 filename_c: *const c_char,
3559 precision: u8,
3560) -> *mut RKRConFrameWriter {
3561 let filename = match unsafe { cstr_path(filename_c) } {
3562 Some(s) => s,
3563 None => return ptr::null_mut(),
3564 };
3565 match crate::compression::gzip_writer(Path::new(filename)) {
3566 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3567 Err(_) => ptr::null_mut(),
3568 }
3569}
3570#[cfg(feature = "zstd")]
3580#[unsafe(no_mangle)]
3581pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3582 let filename = match unsafe { cstr_path(filename_c) } {
3583 Some(s) => s,
3584 None => return ptr::null_mut(),
3585 };
3586 match crate::compression::zstd_writer(Path::new(filename)) {
3587 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3588 Err(_) => ptr::null_mut(),
3589 }
3590}
3591#[cfg(feature = "zstd")]
3602#[unsafe(no_mangle)]
3603pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3604 filename_c: *const c_char,
3605 precision: u8,
3606) -> *mut RKRConFrameWriter {
3607 let filename = match unsafe { cstr_path(filename_c) } {
3608 Some(s) => s,
3609 None => return ptr::null_mut(),
3610 };
3611 match crate::compression::zstd_writer(Path::new(filename)) {
3612 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3613 Err(_) => ptr::null_mut(),
3614 }
3615}
3616#[unsafe(no_mangle)]
3628pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
3629 if filename_c.is_null() {
3630 return ptr::null_mut();
3631 }
3632 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3633 Ok(s) => s,
3634 Err(_) => return ptr::null_mut(),
3635 };
3636 match iterators::read_first_frame(Path::new(filename)) {
3637 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3638 Err(_) => ptr::null_mut(),
3639 }
3640}
3641#[unsafe(no_mangle)]
3650pub unsafe extern "C" fn rkr_read_all_frames(
3651 filename_c: *const c_char,
3652 num_frames: *mut usize,
3653) -> *mut *mut RKRConFrame {
3654 if filename_c.is_null() || num_frames.is_null() {
3655 return ptr::null_mut();
3656 }
3657 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3658 Ok(s) => s,
3659 Err(_) => return ptr::null_mut(),
3660 };
3661 match iterators::read_all_frames(Path::new(filename)) {
3662 Ok(frames) => pack_frame_handles(frames, num_frames),
3663 Err(_) => ptr::null_mut(),
3664 }
3665}
3666
3667#[unsafe(no_mangle)]
3677pub unsafe extern "C" fn rkr_read_all_frames_n_threads(
3678 filename_c: *const c_char,
3679 num_frames: *mut usize,
3680 n_threads: usize,
3681) -> *mut *mut RKRConFrame {
3682 if filename_c.is_null() || num_frames.is_null() {
3683 return ptr::null_mut();
3684 }
3685 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3686 Ok(s) => s,
3687 Err(_) => return ptr::null_mut(),
3688 };
3689 let threads = if n_threads == 0 {
3690 None
3691 } else {
3692 Some(n_threads)
3693 };
3694 match iterators::read_all_frames_with_threads(Path::new(filename), threads) {
3695 Ok(frames) => pack_frame_handles(frames, num_frames),
3696 Err(_) => ptr::null_mut(),
3697 }
3698}
3699
3700pub(crate) fn pack_frame_handles(
3702 frames: Vec<ConFrame>,
3703 num_frames: *mut usize,
3704) -> *mut *mut RKRConFrame {
3705 let count = frames.len();
3706 let mut handles: Vec<*mut RKRConFrame> = frames
3707 .into_iter()
3708 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3709 .collect();
3710 handles.shrink_to_fit();
3711 debug_assert_eq!(handles.len(), handles.capacity());
3712 let ptr = handles.as_mut_ptr();
3713 std::mem::forget(handles);
3714 unsafe { *num_frames = count };
3715 ptr
3716}
3717#[unsafe(no_mangle)]
3723pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3724 if frames.is_null() {
3725 return;
3726 }
3727 unsafe {
3728 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3729 for handle in handles {
3730 if !handle.is_null() {
3731 let _ = Box::from_raw(handle as *mut ConFrame);
3732 }
3733 }
3734 }
3735}
3736#[unsafe(no_mangle)]
3742pub unsafe extern "C" fn free_rkr_frame_ptr_array(
3743 frames: *mut *mut RKRConFrame,
3744 num_frames: usize,
3745) {
3746 if frames.is_null() {
3747 return;
3748 }
3749 unsafe {
3750 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3751 }
3752}
3753
3754#[cfg(feature = "metatensor")]
3769#[unsafe(no_mangle)]
3770pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3771 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3772}
3773#[cfg(feature = "metatensor")]
3775#[unsafe(no_mangle)]
3776pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3777 frame_handle: *const RKRConFrame,
3778 out_block: *mut *mut metatensor::c_api::mts_block_t,
3779) -> RKRStatus {
3780 if frame_handle.is_null() || out_block.is_null() {
3781 return RKRStatus::RKR_STATUS_NULL_POINTER;
3782 }
3783 unsafe { *out_block = std::ptr::null_mut() };
3784 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3785 return RKRStatus::RKR_STATUS_NULL_POINTER;
3786 };
3787 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3788 crate::metatensor_export::frame_positions_block(frame)
3789 })) {
3790 Ok(Ok(b)) => {
3791 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3792 RKRStatus::RKR_STATUS_SUCCESS
3793 }
3794 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3795 }
3796}
3797#[cfg(feature = "metatensor")]
3798#[unsafe(no_mangle)]
3799pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3800 frame_handle: *const RKRConFrame,
3801 out_block: *mut *mut metatensor::c_api::mts_block_t,
3802) -> RKRStatus {
3803 if frame_handle.is_null() || out_block.is_null() {
3804 return RKRStatus::RKR_STATUS_NULL_POINTER;
3805 }
3806 unsafe { *out_block = std::ptr::null_mut() };
3807 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3808 return RKRStatus::RKR_STATUS_NULL_POINTER;
3809 };
3810 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3811 crate::metatensor_export::frame_velocities_block(frame)
3812 })) {
3813 Ok(Ok(Some(b))) => {
3814 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3815 RKRStatus::RKR_STATUS_SUCCESS
3816 }
3817 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3818 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3819 }
3820}
3821#[cfg(feature = "metatensor")]
3822#[unsafe(no_mangle)]
3823pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3824 frame_handle: *const RKRConFrame,
3825 out_block: *mut *mut metatensor::c_api::mts_block_t,
3826) -> RKRStatus {
3827 if frame_handle.is_null() || out_block.is_null() {
3828 return RKRStatus::RKR_STATUS_NULL_POINTER;
3829 }
3830 unsafe { *out_block = std::ptr::null_mut() };
3831 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3832 return RKRStatus::RKR_STATUS_NULL_POINTER;
3833 };
3834 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3835 crate::metatensor_export::frame_forces_block(frame)
3836 })) {
3837 Ok(Ok(Some(b))) => {
3838 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3839 RKRStatus::RKR_STATUS_SUCCESS
3840 }
3841 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3842 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3843 }
3844}
3845#[cfg(feature = "metatensor")]
3846#[unsafe(no_mangle)]
3847pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3848 frame_handle: *const RKRConFrame,
3849 out_block: *mut *mut metatensor::c_api::mts_block_t,
3850) -> RKRStatus {
3851 if frame_handle.is_null() || out_block.is_null() {
3852 return RKRStatus::RKR_STATUS_NULL_POINTER;
3853 }
3854 unsafe { *out_block = std::ptr::null_mut() };
3855 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3856 return RKRStatus::RKR_STATUS_NULL_POINTER;
3857 };
3858 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3859 crate::metatensor_export::frame_energies_block(frame)
3860 })) {
3861 Ok(Ok(Some(b))) => {
3862 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3863 RKRStatus::RKR_STATUS_SUCCESS
3864 }
3865 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3866 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3867 }
3868}
3869#[cfg(not(feature = "zstd"))]
3870#[unsafe(no_mangle)]
3871pub unsafe extern "C" fn create_writer_zstd_c(
3872 _filename_c: *const c_char,
3873) -> *mut RKRConFrameWriter {
3874 ptr::null_mut()
3875}
3876#[cfg(not(feature = "zstd"))]
3877#[unsafe(no_mangle)]
3878pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3879 _filename_c: *const c_char,
3880 _precision: u8,
3881) -> *mut RKRConFrameWriter {
3882 ptr::null_mut()
3883}
3884#[cfg(not(feature = "metatensor"))]
3888pub struct mts_block_t {
3891 _private: [u8; 0],
3892}
3893#[cfg(not(feature = "metatensor"))]
3894#[unsafe(no_mangle)]
3895pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3896#[cfg(not(feature = "metatensor"))]
3897#[unsafe(no_mangle)]
3898pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3899 _frame_handle: *const RKRConFrame,
3900 out_block: *mut *mut mts_block_t,
3901) -> RKRStatus {
3902 if !out_block.is_null() {
3903 unsafe { *out_block = std::ptr::null_mut() };
3904 }
3905 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3906}
3907#[cfg(not(feature = "metatensor"))]
3908#[unsafe(no_mangle)]
3909pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3910 _frame_handle: *const RKRConFrame,
3911 out_block: *mut *mut mts_block_t,
3912) -> RKRStatus {
3913 if !out_block.is_null() {
3914 unsafe { *out_block = std::ptr::null_mut() };
3915 }
3916 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3917}
3918#[cfg(not(feature = "metatensor"))]
3919#[unsafe(no_mangle)]
3920pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3921 _frame_handle: *const RKRConFrame,
3922 out_block: *mut *mut mts_block_t,
3923) -> RKRStatus {
3924 if !out_block.is_null() {
3925 unsafe { *out_block = std::ptr::null_mut() };
3926 }
3927 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3928}
3929#[cfg(not(feature = "metatensor"))]
3930#[unsafe(no_mangle)]
3931pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3932 _frame_handle: *const RKRConFrame,
3933 out_block: *mut *mut mts_block_t,
3934) -> RKRStatus {
3935 if !out_block.is_null() {
3936 unsafe { *out_block = std::ptr::null_mut() };
3937 }
3938 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3939}
3940#[unsafe(no_mangle)]
3943pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3944 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3945 return 0;
3946 };
3947 frame.atom_data.len()
3948}
3949#[unsafe(no_mangle)]
3951pub unsafe extern "C" fn rkr_frame_xyz_view(
3952 frame_handle: *const RKRConFrame,
3953 out: *mut RKRArrayView,
3954) -> RKRStatus {
3955 fill_array2_view(frame_handle, out, |f| &f.positions, false)
3956}
3957
3958#[unsafe(no_mangle)]
3960pub unsafe extern "C" fn rkr_frame_velocities_view(
3961 frame_handle: *const RKRConFrame,
3962 out: *mut RKRArrayView,
3963) -> RKRStatus {
3964 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
3965}
3966
3967#[unsafe(no_mangle)]
3969pub unsafe extern "C" fn rkr_frame_forces_view(
3970 frame_handle: *const RKRConFrame,
3971 out: *mut RKRArrayView,
3972) -> RKRStatus {
3973 fill_array2_view(frame_handle, out, |f| &f.forces, true)
3974}
3975
3976#[unsafe(no_mangle)]
3982pub unsafe extern "C" fn rkr_frame_has_displacements(frame_handle: *const RKRConFrame) -> bool {
3983 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
3984 Some(f) => f.has_displacements(),
3985 None => false,
3986 }
3987}
3988
3989#[unsafe(no_mangle)]
3994pub unsafe extern "C" fn rkr_frame_displacements_view(
3995 frame_handle: *const RKRConFrame,
3996 out: *mut RKRArrayView,
3997) -> RKRStatus {
3998 fill_array2_view(frame_handle, out, |f| &f.displacements, true)
3999}
4000
4001#[unsafe(no_mangle)]
4007pub unsafe extern "C" fn rkr_frame_has_spreads(frame_handle: *const RKRConFrame) -> bool {
4008 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
4009 Some(f) => f.has_spreads(),
4010 None => false,
4011 }
4012}
4013
4014#[unsafe(no_mangle)]
4019pub unsafe extern "C" fn rkr_frame_spreads_view(
4020 frame_handle: *const RKRConFrame,
4021 out: *mut RKRArrayView,
4022) -> RKRStatus {
4023 fill_array2_view(frame_handle, out, |f| &f.spreads, true)
4024}
4025
4026#[unsafe(no_mangle)]
4028pub unsafe extern "C" fn rkr_frame_energies_view(
4029 frame_handle: *const RKRConFrame,
4030 out: *mut RKRArrayView,
4031) -> RKRStatus {
4032 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
4033}
4034
4035#[unsafe(no_mangle)]
4037pub unsafe extern "C" fn rkr_frame_masses_view(
4038 frame_handle: *const RKRConFrame,
4039 out: *mut RKRArrayView,
4040) -> RKRStatus {
4041 fill_array1_view(frame_handle, out, |f| &f.masses, false)
4042}
4043
4044#[unsafe(no_mangle)]
4046pub unsafe extern "C" fn rkr_frame_atom_ids_view(
4047 frame_handle: *const RKRConFrame,
4048 out: *mut RKRArrayView,
4049) -> RKRStatus {
4050 if frame_handle.is_null() || out.is_null() {
4051 return RKRStatus::RKR_STATUS_NULL_POINTER;
4052 }
4053 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4054 return RKRStatus::RKR_STATUS_NULL_POINTER;
4055 };
4056 let slice = frame.atom_ids.as_slice_memory_order();
4057 unsafe {
4058 *out = RKRArrayView {
4059 data: slice
4060 .map(|s| s.as_ptr() as *const std::ffi::c_void)
4061 .unwrap_or(std::ptr::null()),
4062 n: frame.atom_ids.len(),
4063 cols: 1,
4064 dtype_code: 1, dtype_bits: 64,
4066 };
4067 }
4068 RKRStatus::RKR_STATUS_SUCCESS
4069}
4070
4071#[unsafe(no_mangle)]
4074pub unsafe extern "C" fn rkr_frame_xyz_f64(
4075 frame_handle: *const RKRConFrame,
4076 n: *mut usize,
4077) -> *const f64 {
4078 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
4079}
4080
4081#[unsafe(no_mangle)]
4083pub unsafe extern "C" fn rkr_frame_velocities_f64(
4084 frame_handle: *const RKRConFrame,
4085 n: *mut usize,
4086) -> *const f64 {
4087 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
4088}
4089
4090#[unsafe(no_mangle)]
4092pub unsafe extern "C" fn rkr_frame_forces_f64(
4093 frame_handle: *const RKRConFrame,
4094 n: *mut usize,
4095) -> *const f64 {
4096 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
4097}
4098
4099#[unsafe(no_mangle)]
4104pub unsafe extern "C" fn rkr_frame_displacements_f64(
4105 frame_handle: *const RKRConFrame,
4106 n: *mut usize,
4107) -> *const f64 {
4108 f64_col_ptr(frame_handle, n, |f| f.displacements.f64_slice())
4109}
4110
4111#[unsafe(no_mangle)]
4116pub unsafe extern "C" fn rkr_frame_spreads_f64(
4117 frame_handle: *const RKRConFrame,
4118 n: *mut usize,
4119) -> *const f64 {
4120 f64_col_ptr(frame_handle, n, |f| f.spreads.f64_slice())
4121}
4122
4123fn fill_array2_view(
4124 frame_handle: *const RKRConFrame,
4125 out: *mut RKRArrayView,
4126 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4127 section: bool,
4128) -> RKRStatus {
4129 if frame_handle.is_null() || out.is_null() {
4130 return RKRStatus::RKR_STATUS_NULL_POINTER;
4131 }
4132 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4133 return RKRStatus::RKR_STATUS_NULL_POINTER;
4134 };
4135 let arr = get(frame);
4136 if section && arr.nrows() == 0 {
4137 unsafe { *out = RKRArrayView::empty() };
4138 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4139 }
4140 unsafe { *out = RKRArrayView::from_array2(arr) };
4141 RKRStatus::RKR_STATUS_SUCCESS
4142}
4143
4144fn fill_array1_view(
4145 frame_handle: *const RKRConFrame,
4146 out: *mut RKRArrayView,
4147 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4148 section: bool,
4149) -> RKRStatus {
4150 if frame_handle.is_null() || out.is_null() {
4151 return RKRStatus::RKR_STATUS_NULL_POINTER;
4152 }
4153 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4154 return RKRStatus::RKR_STATUS_NULL_POINTER;
4155 };
4156 let arr = get(frame);
4157 if section && arr.len() == 0 {
4158 unsafe { *out = RKRArrayView::empty() };
4159 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4160 }
4161 unsafe { *out = RKRArrayView::from_array1(arr) };
4162 RKRStatus::RKR_STATUS_SUCCESS
4163}
4164
4165fn f64_col_ptr(
4166 frame_handle: *const RKRConFrame,
4167 n: *mut usize,
4168 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
4169) -> *const f64 {
4170 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4171 if !n.is_null() {
4172 unsafe { *n = 0 };
4173 }
4174 return std::ptr::null();
4175 };
4176 match get(frame) {
4177 Some(s) if !s.is_empty() => {
4178 if !n.is_null() {
4179 unsafe { *n = s.len() / 3 };
4180 }
4181 s.as_ptr()
4182 }
4183 _ => {
4184 if !n.is_null() {
4185 unsafe { *n = 0 };
4186 }
4187 std::ptr::null()
4188 }
4189 }
4190}
4191
4192#[unsafe(no_mangle)]
4195pub unsafe extern "C" fn rkr_frame_copy_positions(
4196 frame_handle: *const RKRConFrame,
4197 out: *mut f64,
4198 out_len: usize,
4199) -> RKRStatus {
4200 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
4201}
4202fn copy_array2_f64(
4203 frame_handle: *const RKRConFrame,
4204 out: *mut f64,
4205 out_len: usize,
4206 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4207 section: bool,
4208) -> RKRStatus {
4209 if frame_handle.is_null() || out.is_null() {
4210 return RKRStatus::RKR_STATUS_NULL_POINTER;
4211 }
4212 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4213 return RKRStatus::RKR_STATUS_NULL_POINTER;
4214 };
4215 let arr = get(frame);
4216 let n = if arr.nrows() > 0 {
4217 arr.nrows()
4218 } else {
4219 frame.atom_data.len()
4220 };
4221 if section && arr.nrows() == 0 {
4222 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4223 }
4224 let need = n.saturating_mul(3);
4225 if out_len < need {
4226 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4227 }
4228 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
4229 if arr.nrows() == n {
4230 if let Some(src) = arr.f64_slice() {
4231 dest.copy_from_slice(&src[..need.min(src.len())]);
4232 return RKRStatus::RKR_STATUS_SUCCESS;
4233 }
4234 for i in 0..n {
4235 let row = arr.as_f64_row(i);
4236 dest[i * 3] = row[0];
4237 dest[i * 3 + 1] = row[1];
4238 dest[i * 3 + 2] = row[2];
4239 }
4240 return RKRStatus::RKR_STATUS_SUCCESS;
4241 }
4242 for (i, a) in frame.atom_data.iter().enumerate() {
4243 dest[i * 3] = a.x;
4244 dest[i * 3 + 1] = a.y;
4245 dest[i * 3 + 2] = a.z;
4246 }
4247 RKRStatus::RKR_STATUS_SUCCESS
4248}
4249
4250fn copy_array1_f64(
4251 frame_handle: *const RKRConFrame,
4252 out: *mut f64,
4253 out_len: usize,
4254 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4255 section: bool,
4256) -> RKRStatus {
4257 if frame_handle.is_null() || out.is_null() {
4258 return RKRStatus::RKR_STATUS_NULL_POINTER;
4259 }
4260 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4261 return RKRStatus::RKR_STATUS_NULL_POINTER;
4262 };
4263 let arr = get(frame);
4264 let n = if arr.len() > 0 {
4265 arr.len()
4266 } else {
4267 frame.atom_data.len()
4268 };
4269 if section && arr.len() == 0 {
4270 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4271 }
4272 if out_len < n {
4273 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4274 }
4275 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4276 if let Some(src) = arr.f64_slice() {
4277 dest.copy_from_slice(&src[..n.min(src.len())]);
4278 return RKRStatus::RKR_STATUS_SUCCESS;
4279 }
4280 for i in 0..n {
4281 dest[i] = arr.get_f64(i);
4282 }
4283 RKRStatus::RKR_STATUS_SUCCESS
4284}
4285
4286#[unsafe(no_mangle)]
4287pub unsafe extern "C" fn rkr_frame_copy_velocities(
4288 frame_handle: *const RKRConFrame,
4289 out: *mut f64,
4290 out_len: usize,
4291) -> RKRStatus {
4292 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
4293}
4294#[unsafe(no_mangle)]
4295pub unsafe extern "C" fn rkr_frame_copy_forces(
4296 frame_handle: *const RKRConFrame,
4297 out: *mut f64,
4298 out_len: usize,
4299) -> RKRStatus {
4300 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
4301}
4302#[unsafe(no_mangle)]
4308pub unsafe extern "C" fn rkr_frame_copy_displacements(
4309 frame_handle: *const RKRConFrame,
4310 out: *mut f64,
4311 out_len: usize,
4312) -> RKRStatus {
4313 copy_array2_f64(frame_handle, out, out_len, |f| &f.displacements, true)
4314}
4315#[unsafe(no_mangle)]
4321pub unsafe extern "C" fn rkr_frame_copy_spreads(
4322 frame_handle: *const RKRConFrame,
4323 out: *mut f64,
4324 out_len: usize,
4325) -> RKRStatus {
4326 copy_array2_f64(frame_handle, out, out_len, |f| &f.spreads, true)
4327}
4328#[unsafe(no_mangle)]
4329pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
4330 frame_handle: *const RKRConFrame,
4331 out: *mut f64,
4332 out_len: usize,
4333) -> RKRStatus {
4334 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
4335}
4336#[unsafe(no_mangle)]
4337pub unsafe extern "C" fn rkr_frame_copy_masses(
4338 frame_handle: *const RKRConFrame,
4339 out: *mut f64,
4340 out_len: usize,
4341) -> RKRStatus {
4342 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
4343}
4344#[unsafe(no_mangle)]
4345pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
4346 frame_handle: *const RKRConFrame,
4347 out: *mut u64,
4348 out_len: usize,
4349) -> RKRStatus {
4350 if frame_handle.is_null() || out.is_null() {
4351 return RKRStatus::RKR_STATUS_NULL_POINTER;
4352 }
4353 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4354 return RKRStatus::RKR_STATUS_NULL_POINTER;
4355 };
4356 let n = frame.atom_ids.len().max(frame.atom_data.len());
4357 if out_len < n {
4358 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4359 }
4360 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4361 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
4362 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
4363 return RKRStatus::RKR_STATUS_SUCCESS;
4364 }
4365 for (i, a) in frame.atom_data.iter().enumerate() {
4366 dest[i] = a.atom_id;
4367 }
4368 RKRStatus::RKR_STATUS_SUCCESS
4369}
4370fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
4371 let n = frame.atom_data.len();
4372 let mut data = Vec::with_capacity(n * 3);
4373 for a in &frame.atom_data {
4374 data.extend_from_slice(&[a.x, a.y, a.z]);
4375 }
4376 ndarray::ArcArray2::from_shape_vec((n, 3), data)
4377 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
4378}
4379
4380#[unsafe(no_mangle)]
4390pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
4391 frame_handle: *const RKRConFrame,
4392 device_type: i32,
4393 device_id: i32,
4394 _stream: i64,
4395 _max_version_major: u32,
4396 _max_version_minor: u32,
4397 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4398) -> RKRStatus {
4399 if frame_handle.is_null() || out_tensor.is_null() {
4400 return RKRStatus::RKR_STATUS_NULL_POINTER;
4401 }
4402 unsafe { *out_tensor = std::ptr::null_mut() };
4403 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
4404 dlpk::sys::DLDevice::cpu()
4405 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
4406 #[cfg(feature = "cuda")]
4407 {
4408 dlpk::sys::DLDevice::cuda(device_id)
4409 }
4410 #[cfg(not(feature = "cuda"))]
4411 {
4412 let _ = device_id;
4413 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4414 }
4415 } else {
4416 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4417 };
4418 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4419 return RKRStatus::RKR_STATUS_NULL_POINTER;
4420 };
4421 match frame.positions_as_dlpack(dl_device) {
4423 Ok(tensor) => {
4424 let raw = tensor.into_raw();
4425 unsafe {
4426 *out_tensor = raw.as_ptr();
4427 }
4428 RKRStatus::RKR_STATUS_SUCCESS
4429 }
4430 Err(e) => map_dlpack_err(e),
4431 }
4432}
4433
4434#[unsafe(no_mangle)]
4440pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
4441 frame_handle: *mut RKRConFrame,
4442 tensor: *const RKRDLManagedTensorVersioned,
4443) -> RKRStatus {
4444 if frame_handle.is_null() || tensor.is_null() {
4445 return RKRStatus::RKR_STATUS_NULL_POINTER;
4446 }
4447 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
4448 let dl = unsafe { &(*tensor).dl_tensor };
4449 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
4450 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4451 }
4452 let n = frame.atom_data.len();
4453 let need = n.saturating_mul(3);
4454 let ndim = dl.ndim as usize;
4455 let shape = if dl.shape.is_null() {
4456 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4457 } else {
4458 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
4459 };
4460 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
4461 need
4462 } else if ndim == 1 && shape[0] == need as i64 {
4463 need
4464 } else {
4465 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4466 };
4467 let code = dl.dtype.code as u8;
4468 let bits = dl.dtype.bits;
4469 if dl.data.is_null() {
4470 return RKRStatus::RKR_STATUS_NULL_POINTER;
4471 }
4472 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
4473 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
4474 s.to_vec()
4475 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
4476 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
4477 s.iter().map(|&x| x as f64).collect()
4478 } else {
4479 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4480 };
4481 if frame.positions.nrows() != n {
4482 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4483 }
4484 for i in 0..n {
4485 frame
4486 .positions
4487 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
4488 }
4489 frame.sync_atom_data_from_arrays();
4490 RKRStatus::RKR_STATUS_SUCCESS
4491}
4492
4493#[unsafe(no_mangle)]
4496pub unsafe extern "C" fn rkr_frame_positions_dlpack(
4497 frame_handle: *const RKRConFrame,
4498 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4499) -> RKRStatus {
4500 unsafe {
4501 rkr_frame_positions_as_dlpack(
4502 frame_handle,
4503 rkr_dl_device_type::RKR_DL_CPU,
4504 0,
4505 0,
4506 1,
4507 0,
4508 out_tensor,
4509 )
4510 }
4511}
4512
4513#[unsafe(no_mangle)]
4518pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
4519 frame_handle: *const RKRConFrame,
4520 opts: *const RKRDlpackExportOptions,
4521 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4522) -> RKRStatus {
4523 if frame_handle.is_null() || out_tensor.is_null() {
4524 return RKRStatus::RKR_STATUS_NULL_POINTER;
4525 }
4526 unsafe { *out_tensor = std::ptr::null_mut() };
4527 let o = match resolve_dlpack_opts(opts) {
4528 Ok(o) => o,
4529 Err(st) => return st,
4530 };
4531 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4532 return RKRStatus::RKR_STATUS_NULL_POINTER;
4533 };
4534 let arr = frame_positions_arc(frame);
4535 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4536}
4537
4538#[unsafe(no_mangle)]
4540pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
4541 frame_handle: *const RKRConFrame,
4542 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4543) -> RKRStatus {
4544 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4545}
4546
4547#[unsafe(no_mangle)]
4548pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
4549 frame_handle: *const RKRConFrame,
4550 opts: *const RKRDlpackExportOptions,
4551 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4552) -> RKRStatus {
4553 if frame_handle.is_null() || out_tensor.is_null() {
4554 return RKRStatus::RKR_STATUS_NULL_POINTER;
4555 }
4556 unsafe { *out_tensor = std::ptr::null_mut() };
4557 let o = match resolve_dlpack_opts(opts) {
4558 Ok(o) => o,
4559 Err(st) => return st,
4560 };
4561 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4562 return RKRStatus::RKR_STATUS_NULL_POINTER;
4563 };
4564 if !frame.has_velocities() {
4565 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4566 }
4567 let n = frame.atom_data.len();
4568 let mut data = Vec::with_capacity(n * 3);
4569 for a in &frame.atom_data {
4570 let v = a.velocity().unwrap_or([0.0; 3]);
4571 data.extend_from_slice(&v);
4572 }
4573 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4574 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4575 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4576}
4577
4578#[unsafe(no_mangle)]
4580pub unsafe extern "C" fn rkr_frame_forces_dlpack(
4581 frame_handle: *const RKRConFrame,
4582 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4583) -> RKRStatus {
4584 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4585}
4586
4587#[unsafe(no_mangle)]
4588pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
4589 frame_handle: *const RKRConFrame,
4590 opts: *const RKRDlpackExportOptions,
4591 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4592) -> RKRStatus {
4593 if frame_handle.is_null() || out_tensor.is_null() {
4594 return RKRStatus::RKR_STATUS_NULL_POINTER;
4595 }
4596 unsafe { *out_tensor = std::ptr::null_mut() };
4597 let o = match resolve_dlpack_opts(opts) {
4598 Ok(o) => o,
4599 Err(st) => return st,
4600 };
4601 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4602 return RKRStatus::RKR_STATUS_NULL_POINTER;
4603 };
4604 if !frame.has_forces() {
4605 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4606 }
4607 let n = frame.atom_data.len();
4608 let mut data = Vec::with_capacity(n * 3);
4609 for a in &frame.atom_data {
4610 let f = a.force().unwrap_or([0.0; 3]);
4611 data.extend_from_slice(&f);
4612 }
4613 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4614 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4615 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4616}
4617
4618#[unsafe(no_mangle)]
4625pub unsafe extern "C" fn rkr_frame_displacements_dlpack(
4626 frame_handle: *const RKRConFrame,
4627 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4628) -> RKRStatus {
4629 unsafe { rkr_frame_displacements_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4630}
4631
4632#[unsafe(no_mangle)]
4639pub unsafe extern "C" fn rkr_frame_displacements_dlpack_ex(
4640 frame_handle: *const RKRConFrame,
4641 opts: *const RKRDlpackExportOptions,
4642 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4643) -> RKRStatus {
4644 if frame_handle.is_null() || out_tensor.is_null() {
4645 return RKRStatus::RKR_STATUS_NULL_POINTER;
4646 }
4647 unsafe { *out_tensor = std::ptr::null_mut() };
4648 let o = match resolve_dlpack_opts(opts) {
4649 Ok(o) => o,
4650 Err(st) => return st,
4651 };
4652 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4653 return RKRStatus::RKR_STATUS_NULL_POINTER;
4654 };
4655 if !frame.has_displacements() {
4656 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4657 }
4658 let n = frame.atom_data.len();
4659 let mut data = Vec::with_capacity(n * 3);
4660 for a in &frame.atom_data {
4661 let d = a.displacement().unwrap_or([0.0; 3]);
4662 data.extend_from_slice(&d);
4663 }
4664 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4665 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4666 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4667}
4668
4669#[unsafe(no_mangle)]
4676pub unsafe extern "C" fn rkr_frame_spreads_dlpack(
4677 frame_handle: *const RKRConFrame,
4678 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4679) -> RKRStatus {
4680 unsafe { rkr_frame_spreads_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4681}
4682
4683#[unsafe(no_mangle)]
4690pub unsafe extern "C" fn rkr_frame_spreads_dlpack_ex(
4691 frame_handle: *const RKRConFrame,
4692 opts: *const RKRDlpackExportOptions,
4693 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4694) -> RKRStatus {
4695 if frame_handle.is_null() || out_tensor.is_null() {
4696 return RKRStatus::RKR_STATUS_NULL_POINTER;
4697 }
4698 unsafe { *out_tensor = std::ptr::null_mut() };
4699 let o = match resolve_dlpack_opts(opts) {
4700 Ok(o) => o,
4701 Err(st) => return st,
4702 };
4703 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4704 return RKRStatus::RKR_STATUS_NULL_POINTER;
4705 };
4706 if !frame.has_spreads() {
4707 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4708 }
4709 let n = frame.atom_data.len();
4710 let mut data = Vec::with_capacity(n * 3);
4711 for a in &frame.atom_data {
4712 let d = a.spread().unwrap_or([0.0; 3]);
4713 data.extend_from_slice(&d);
4714 }
4715 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4716 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4717 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4718}
4719
4720#[unsafe(no_mangle)]
4722pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
4723 frame_handle: *const RKRConFrame,
4724 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4725) -> RKRStatus {
4726 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4727}
4728
4729#[unsafe(no_mangle)]
4730pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
4731 frame_handle: *const RKRConFrame,
4732 opts: *const RKRDlpackExportOptions,
4733 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4734) -> RKRStatus {
4735 if frame_handle.is_null() || out_tensor.is_null() {
4736 return RKRStatus::RKR_STATUS_NULL_POINTER;
4737 }
4738 unsafe { *out_tensor = std::ptr::null_mut() };
4739 let o = match resolve_dlpack_opts(opts) {
4740 Ok(o) => o,
4741 Err(st) => return st,
4742 };
4743 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4744 return RKRStatus::RKR_STATUS_NULL_POINTER;
4745 };
4746 if !frame.has_energies() {
4747 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4748 }
4749 let data: Vec<f64> = frame
4750 .atom_data
4751 .iter()
4752 .map(|a| a.energy().unwrap_or(0.0))
4753 .collect();
4754 let arr = ndarray::ArcArray1::from_vec(data);
4755 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
4756}
4757
4758pub struct RKRSelectionResult;
4762#[unsafe(no_mangle)]
4773pub unsafe extern "C" fn rkr_frame_select(
4774 frame_handle: *const RKRConFrame,
4775 selection: *const c_char,
4776 out_result: *mut *mut RKRSelectionResult,
4777) -> RKRStatus {
4778 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
4779 return RKRStatus::RKR_STATUS_NULL_POINTER;
4780 }
4781 let frame = unsafe { &*(frame_handle as *const ConFrame) };
4782 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
4783 Ok(s) => s,
4784 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
4785 };
4786 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
4787 Ok(result) => {
4788 let boxed = Box::new(result);
4789 unsafe {
4790 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
4791 }
4792 RKRStatus::RKR_STATUS_SUCCESS
4793 }
4794 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
4795 }
4796}
4797#[unsafe(no_mangle)]
4802pub unsafe extern "C" fn rkr_selection_result_match_count(
4803 result_handle: *const RKRSelectionResult,
4804) -> u64 {
4805 if result_handle.is_null() {
4806 return 0;
4807 }
4808 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4809 result.matches.len() as u64
4810}
4811#[unsafe(no_mangle)]
4816pub unsafe extern "C" fn rkr_selection_result_context_size(
4817 result_handle: *const RKRSelectionResult,
4818) -> u32 {
4819 if result_handle.is_null() {
4820 return 0;
4821 }
4822 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4823 result.context_size as u32
4824}
4825#[unsafe(no_mangle)]
4831pub unsafe extern "C" fn rkr_selection_result_match_at(
4832 result_handle: *const RKRSelectionResult,
4833 match_index: u64,
4834 out_atoms: *mut u64,
4835 out_size: *mut u32,
4836) -> RKRStatus {
4837 if result_handle.is_null() || out_atoms.is_null() {
4838 return RKRStatus::RKR_STATUS_NULL_POINTER;
4839 }
4840 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4841 let idx = match_index as usize;
4842 if idx >= result.matches.len() {
4843 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
4844 }
4845 let m = &result.matches[idx];
4846 unsafe {
4847 for i in 0..4 {
4848 *out_atoms.add(i) = if i < m.size {
4849 m.atoms[i] as u64
4850 } else {
4851 u64::MAX
4852 };
4853 }
4854 if !out_size.is_null() {
4855 *out_size = m.size as u32;
4856 }
4857 }
4858 RKRStatus::RKR_STATUS_SUCCESS
4859}
4860#[unsafe(no_mangle)]
4866pub unsafe extern "C" fn rkr_selection_result_primary_indices(
4867 result_handle: *const RKRSelectionResult,
4868 out_indices: *mut u64,
4869 capacity: u64,
4870 out_written: *mut u64,
4871) -> RKRStatus {
4872 if result_handle.is_null() {
4873 return RKRStatus::RKR_STATUS_NULL_POINTER;
4874 }
4875 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4876 let n = result.matches.len() as u64;
4877 if !out_written.is_null() {
4878 unsafe {
4879 *out_written = n;
4880 }
4881 }
4882 if n == 0 {
4883 return RKRStatus::RKR_STATUS_SUCCESS;
4884 }
4885 if out_indices.is_null() {
4886 return RKRStatus::RKR_STATUS_NULL_POINTER;
4887 }
4888 if capacity < n {
4889 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4890 }
4891 unsafe {
4892 for (i, m) in result.matches.iter().enumerate() {
4893 *out_indices.add(i) = m.atoms[0] as u64;
4894 }
4895 }
4896 RKRStatus::RKR_STATUS_SUCCESS
4897}
4898#[unsafe(no_mangle)]
4903pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
4904 if result_handle.is_null() {
4905 return;
4906 }
4907 unsafe {
4908 drop(Box::from_raw(
4909 result_handle as *mut crate::chemfiles_selection::SelectionResult,
4910 ));
4911 }
4912}
4913#[unsafe(no_mangle)]
4915pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
4916 #[cfg(feature = "chemfiles")]
4917 {
4918 1
4919 }
4920 #[cfg(not(feature = "chemfiles"))]
4921 {
4922 0
4923 }
4924}
4925
4926#[unsafe(no_mangle)]
4928pub extern "C" fn rkr_has_parallel_support() -> u8 {
4929 #[cfg(feature = "parallel")]
4930 {
4931 1
4932 }
4933 #[cfg(not(feature = "parallel"))]
4934 {
4935 0
4936 }
4937}
4938#[unsafe(no_mangle)]
4944pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
4945 if path_c.is_null() {
4946 return std::ptr::null_mut();
4947 }
4948 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4949 return std::ptr::null_mut();
4950 };
4951 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
4952 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4953 Err(_) => std::ptr::null_mut(),
4954 }
4955}
4956
4957#[unsafe(no_mangle)]
4963pub unsafe extern "C" fn rkr_read_chemfiles(
4964 path_c: *const c_char,
4965 num_frames: *mut usize,
4966) -> *mut *mut RKRConFrame {
4967 if path_c.is_null() || num_frames.is_null() {
4968 return std::ptr::null_mut();
4969 }
4970 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4971 return std::ptr::null_mut();
4972 };
4973 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
4974 Ok(frames) => pack_frame_handles(frames, num_frames),
4975 Err(_) => std::ptr::null_mut(),
4976 }
4977}
4978
4979#[unsafe(no_mangle)]
4985pub unsafe extern "C" fn rkr_read_chemfiles_nth(
4986 path_c: *const c_char,
4987 index: usize,
4988) -> *mut RKRConFrame {
4989 if path_c.is_null() {
4990 return std::ptr::null_mut();
4991 }
4992 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4993 return std::ptr::null_mut();
4994 };
4995 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
4996 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4997 Err(_) => std::ptr::null_mut(),
4998 }
4999}
5000
5001#[unsafe(no_mangle)]
5007pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
5008 if path_c.is_null() {
5009 return usize::MAX;
5010 }
5011 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
5012 return usize::MAX;
5013 };
5014 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
5015}
5016
5017#[unsafe(no_mangle)]
5025pub unsafe extern "C" fn rkr_read_chemfiles_range(
5026 path_c: *const c_char,
5027 start: usize,
5028 step: usize,
5029 stop: usize,
5030 topology_c: *const c_char,
5031 guess_bonds: u8,
5032 num_frames: *mut usize,
5033) -> *mut *mut RKRConFrame {
5034 if path_c.is_null() || num_frames.is_null() {
5035 return std::ptr::null_mut();
5036 }
5037 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
5038 return std::ptr::null_mut();
5039 };
5040 let topology = if topology_c.is_null() {
5041 None
5042 } else {
5043 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
5044 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
5045 _ => return std::ptr::null_mut(),
5046 }
5047 };
5048 let opts = crate::chemfiles_import::ChemfilesReadOpts {
5049 start,
5050 step,
5051 stop: if stop == usize::MAX { None } else { Some(stop) },
5052 format: None,
5053 topology,
5054 topology_format: None,
5055 guess_bonds: guess_bonds != 0,
5056 };
5057 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
5058 Ok(frames) => pack_frame_handles(frames, num_frames),
5059 Err(_) => std::ptr::null_mut(),
5060 }
5061}
5062#[unsafe(no_mangle)]
5069pub unsafe extern "C" fn rkr_read_chemfiles_memory(
5070 data_c: *const c_char,
5071 format_c: *const c_char,
5072 num_frames: *mut usize,
5073) -> *mut *mut RKRConFrame {
5074 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
5075 return std::ptr::null_mut();
5076 }
5077 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
5078 return std::ptr::null_mut();
5079 };
5080 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
5081 return std::ptr::null_mut();
5082 };
5083 match crate::chemfiles_import::con_frames_from_memory(data, format) {
5084 Ok(frames) => {
5085 let n = frames.len();
5086 unsafe { *num_frames = n };
5087 let mut ptrs: Vec<*mut RKRConFrame> = frames
5088 .into_iter()
5089 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
5090 .collect();
5091 let p = ptrs.as_mut_ptr();
5092 std::mem::forget(ptrs);
5093 p
5094 }
5095 Err(_) => std::ptr::null_mut(),
5096 }
5097}
5098#[unsafe(no_mangle)]
5103pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
5104 if tensor.is_null() {
5105 return;
5106 }
5107 unsafe {
5108 let t = &mut *tensor;
5109 if let Some(del) = t.deleter {
5110 del(tensor);
5111 }
5112 }
5113}
5114
5115#[unsafe(no_mangle)]
5125pub unsafe extern "C" fn rkr_pack_rcso(
5126 frame_handle: *const RKRConFrame,
5127 buf: *mut u8,
5128 buflen: usize,
5129 out_len: *mut usize,
5130) -> RKRStatus {
5131 if out_len.is_null() {
5132 return RKRStatus::RKR_STATUS_NULL_POINTER;
5133 }
5134 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
5135 Some(f) => f,
5136 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
5137 };
5138 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
5139 Ok(b) => b,
5140 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5141 };
5142 unsafe { *out_len = bytes.len() };
5143 if buf.is_null() {
5144 return RKRStatus::RKR_STATUS_SUCCESS;
5145 }
5146 if buflen < bytes.len() {
5147 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5148 }
5149 unsafe {
5150 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
5151 }
5152 RKRStatus::RKR_STATUS_SUCCESS
5153}
5154
5155#[unsafe(no_mangle)]
5160pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
5161 buf: *const u8,
5162 buflen: usize,
5163 out_natoms: *mut u32,
5164) -> RKRStatus {
5165 if buf.is_null() || out_natoms.is_null() {
5166 return RKRStatus::RKR_STATUS_NULL_POINTER;
5167 }
5168 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5169 match crate::rcso::Rcso::decode(bytes) {
5170 Ok(s) => {
5171 unsafe { *out_natoms = s.natoms };
5172 RKRStatus::RKR_STATUS_SUCCESS
5173 }
5174 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5175 }
5176}
5177
5178#[unsafe(no_mangle)]
5183pub unsafe extern "C" fn rkr_unpack_rcso_positions(
5184 buf: *const u8,
5185 buflen: usize,
5186 dest: *mut f64,
5187 dest_natoms: u32,
5188) -> RKRStatus {
5189 if buf.is_null() || dest.is_null() {
5190 return RKRStatus::RKR_STATUS_NULL_POINTER;
5191 }
5192 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5193 let soa = match crate::rcso::Rcso::decode(bytes) {
5194 Ok(s) => s,
5195 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5196 };
5197 if soa.natoms != dest_natoms {
5198 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5199 }
5200 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5201 for (i, p) in soa.positions.iter().enumerate() {
5202 out[i * 3] = p[0];
5203 out[i * 3 + 1] = p[1];
5204 out[i * 3 + 2] = p[2];
5205 }
5206 RKRStatus::RKR_STATUS_SUCCESS
5207}
5208
5209#[unsafe(no_mangle)]
5215pub unsafe extern "C" fn rkr_unpack_rcso_forces(
5216 buf: *const u8,
5217 buflen: usize,
5218 dest: *mut f64,
5219 dest_natoms: u32,
5220) -> RKRStatus {
5221 if buf.is_null() || dest.is_null() {
5222 return RKRStatus::RKR_STATUS_NULL_POINTER;
5223 }
5224 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5225 let soa = match crate::rcso::Rcso::decode(bytes) {
5226 Ok(s) => s,
5227 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5228 };
5229 let Some(forces) = soa.forces else {
5230 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
5231 };
5232 if soa.natoms != dest_natoms {
5233 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5234 }
5235 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5236 for (i, f) in forces.iter().enumerate() {
5237 out[i * 3] = f[0];
5238 out[i * 3 + 1] = f[1];
5239 out[i * 3 + 2] = f[2];
5240 }
5241 RKRStatus::RKR_STATUS_SUCCESS
5242}
5243
5244#[cfg(test)]
5245mod tests {
5246 use super::*;
5247 use std::ffi::{CStr, CString};
5248 #[test]
5249 fn frame_copy_positions_without_cframe() {
5250 let handle = test_frame_handle();
5251 let n = unsafe { rkr_frame_atom_count(handle) };
5252 assert_eq!(n, 1);
5253 let mut buf = vec![0.0f64; n * 3];
5254 assert_eq!(
5255 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
5256 RKRStatus::RKR_STATUS_SUCCESS
5257 );
5258 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5259 assert_eq!(
5260 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
5261 RKRStatus::RKR_STATUS_SUCCESS
5262 );
5263 assert!(!tensor.is_null());
5264 unsafe { rkr_dlpack_delete(tensor) };
5265 assert_eq!(
5266 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
5267 RKRStatus::RKR_STATUS_SECTION_ABSENT
5268 );
5269 unsafe { free_rkr_frame(handle) };
5270 }
5271 #[test]
5272 fn read_all_frames_c_abi_tiny() {
5273 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5274 let mut n: usize = 0;
5275 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5276 assert!(!arr.is_null() && n >= 1);
5277 let first = unsafe { *arr };
5278 let nat = unsafe { rkr_frame_atom_count(first) };
5279 let mut buf = vec![0.0f64; nat * 3];
5280 assert_eq!(
5281 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
5282 RKRStatus::RKR_STATUS_SUCCESS
5283 );
5284 unsafe { free_rkr_frame_array(arr, n) };
5285 }
5286
5287 #[test]
5288 fn read_all_frames_n_threads_matches_auto() {
5289 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5290 let mut n_auto: usize = 0;
5291 let mut n_one: usize = 0;
5292 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
5293 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
5294 assert!(!auto.is_null() && !one.is_null());
5295 assert_eq!(n_auto, n_one);
5296 assert_eq!(n_auto, 1);
5297 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
5298 let nat_one = unsafe { rkr_frame_atom_count(*one) };
5299 assert_eq!(nat_auto, nat_one);
5300 unsafe { free_rkr_frame_array(auto, n_auto) };
5301 unsafe { free_rkr_frame_array(one, n_one) };
5302 }
5303
5304 #[test]
5305 fn free_rkr_frame_ptr_array_keeps_frames() {
5306 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5307 let mut n: usize = 0;
5308 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5309 assert!(!arr.is_null() && n >= 1);
5310 let first = unsafe { *arr };
5311 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
5313 unsafe { free_rkr_frame_ptr_array(arr, n) };
5314 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
5315 unsafe { free_rkr_frame(first) };
5316 for h in rest {
5317 if !h.is_null() {
5318 unsafe { free_rkr_frame(h) };
5319 }
5320 }
5321 }
5322 fn test_frame_handle() -> *mut RKRConFrame {
5323 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5324 builder
5325 .prebox_header("Generated by test")
5326 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
5327 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
5328 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
5329 }
5330 #[test]
5331 fn header_line_rejects_null_buffer() {
5332 let frame = test_frame_handle();
5333 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
5334 unsafe { free_rkr_frame(frame) };
5335 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5336 }
5337 #[test]
5338 fn header_line_rejects_empty_buffer() {
5339 let frame = test_frame_handle();
5340 let mut buffer = [0 as c_char; 1];
5341 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
5342 unsafe { free_rkr_frame(frame) };
5343 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
5344 }
5345 #[test]
5346 fn header_line_truncates_and_terminates_buffer() {
5347 let frame = test_frame_handle();
5348 let mut buffer = [0 as c_char; 10];
5349 let status =
5350 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
5351 unsafe { free_rkr_frame(frame) };
5352 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5353 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
5354 assert_eq!(copied.to_str().unwrap(), "Generated");
5355 }
5356
5357 #[test]
5358 fn pack_rcso_size_query_then_dest() {
5359 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
5360 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
5361 assert!(!frame.is_null());
5362 let mut need = 0usize;
5363 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
5364 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5365 assert!(need >= 24);
5366 let mut buf = vec![0u8; need];
5367 let mut wrote = 0usize;
5368 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
5369 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5370 assert_eq!(wrote, need);
5371 assert_eq!(&buf[0..4], b"RCSO");
5372 let mut natoms = 0u32;
5373 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
5374 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5375 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
5376 let mut xyz = vec![0.0f64; natoms as usize * 3];
5377 let st =
5378 unsafe { rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms) };
5379 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5380 let mut frc = vec![0.0f64; natoms as usize * 3];
5381 let st =
5382 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
5383 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5384 unsafe { free_rkr_frame(frame) };
5385 }
5386
5387 fn test_builder_handle() -> *mut RKRConFrameBuilder {
5388 let cell = [10.0, 11.0, 12.0];
5389 let angles = [90.0, 91.0, 92.0];
5390 unsafe {
5391 rkr_frame_new(
5392 cell.as_ptr(),
5393 angles.as_ptr(),
5394 ptr::null(),
5395 ptr::null(),
5396 ptr::null(),
5397 ptr::null(),
5398 )
5399 }
5400 }
5401 fn c_string(s: &str) -> CString {
5402 CString::new(s).unwrap()
5403 }
5404 #[test]
5405 fn builder_set_units_c_abi_stores_the_object() {
5406 let builder = test_builder_handle();
5407 let units = c_string(
5408 r#"{"length":"A","energy":"ev","mass":"amu","time":"(amu*angstrom^2/eV)^0.5"}"#,
5409 );
5410 let status = unsafe { rkr_frame_builder_set_units(builder, units.as_ptr()) };
5411 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5412 let symbol = c_string("H");
5413 assert_eq!(
5414 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5415 RKRStatus::RKR_STATUS_SUCCESS
5416 );
5417 let frame = unsafe { rkr_frame_builder_build(builder) };
5418 assert!(!frame.is_null());
5419 let json = unsafe { rkr_frame_metadata_json(frame) };
5420 assert!(!json.is_null());
5421 let text = unsafe { CStr::from_ptr(json) }.to_str().unwrap();
5422 assert!(text.contains(r#""length":"angstrom""#), "{text}");
5423 assert!(text.contains(r#""energy":"eV""#), "{text}");
5424 assert!(
5425 text.contains(r#""time":"(amu*angstrom^2/eV)^0.5""#),
5426 "{text}"
5427 );
5428 unsafe {
5429 rkr_free_string(json);
5430 free_rkr_frame(frame);
5431 }
5432 }
5433 #[test]
5434 fn builder_stamps_con_spec_minor_one_unless_set() {
5435 let builder = test_builder_handle();
5436 let symbol = c_string("H");
5437 assert_eq!(
5438 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5439 RKRStatus::RKR_STATUS_SUCCESS
5440 );
5441 let frame = unsafe { rkr_frame_builder_build(builder) };
5442 let json = unsafe { rkr_frame_metadata_json(frame) };
5443 let text = unsafe { CStr::from_ptr(json) }
5444 .to_str()
5445 .unwrap()
5446 .to_string();
5447 assert!(text.contains(r#""con_spec_minor":1"#), "{text}");
5448 unsafe {
5449 rkr_free_string(json);
5450 free_rkr_frame(frame);
5451 }
5452
5453 let builder = test_builder_handle();
5454 assert_eq!(
5455 unsafe { rkr_frame_builder_set_con_spec_minor(builder, 4) },
5456 RKRStatus::RKR_STATUS_SUCCESS
5457 );
5458 let symbol = c_string("H");
5459 assert_eq!(
5460 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5461 RKRStatus::RKR_STATUS_SUCCESS
5462 );
5463 let frame = unsafe { rkr_frame_builder_build(builder) };
5464 let json = unsafe { rkr_frame_metadata_json(frame) };
5465 let text = unsafe { CStr::from_ptr(json) }
5466 .to_str()
5467 .unwrap()
5468 .to_string();
5469 assert!(text.contains(r#""con_spec_minor":4"#), "{text}");
5470 unsafe {
5471 rkr_free_string(json);
5472 free_rkr_frame(frame);
5473 }
5474 }
5475 unsafe fn assert_single_atom(
5476 frame: *mut RKRConFrame,
5477 fixed: [bool; 3],
5478 velocity: Option<[f64; 3]>,
5479 forces: Option<[f64; 3]>,
5480 ) {
5481 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5482 assert!(!c_frame.is_null());
5483 let c_frame_ref = unsafe { &*c_frame };
5484 assert_eq!(c_frame_ref.num_atoms, 1);
5485 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
5486 assert_eq!(c_frame_ref.has_forces, forces.is_some());
5487 let atom = unsafe { &*c_frame_ref.atoms };
5488 assert_eq!(atom.fixed_x, fixed[0]);
5489 assert_eq!(atom.fixed_y, fixed[1]);
5490 assert_eq!(atom.fixed_z, fixed[2]);
5491 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
5492 assert_eq!(atom.has_velocity, velocity.is_some());
5493 assert_eq!(atom.has_forces, forces.is_some());
5494 if let Some([vx, vy, vz]) = velocity {
5495 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
5496 }
5497 if let Some([fx, fy, fz]) = forces {
5498 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
5499 }
5500 unsafe { free_c_frame(c_frame) };
5501 unsafe { free_rkr_frame(frame) };
5502 }
5503 #[test]
5504 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
5505 let builder = test_builder_handle();
5506 let symbol = c_string("Cu");
5507 let status = unsafe {
5508 rkr_frame_add_atom_with_fixed_mask(
5509 builder,
5510 symbol.as_ptr(),
5511 1.0,
5512 2.0,
5513 3.0,
5514 true,
5515 false,
5516 true,
5517 7,
5518 63.546,
5519 )
5520 };
5521 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5522 let frame = unsafe { rkr_frame_builder_build(builder) };
5523 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
5524 }
5525 #[test]
5526 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
5527 let builder = test_builder_handle();
5528 let symbol = c_string("H");
5529 let status = unsafe {
5530 rkr_frame_add_atom_with_velocity_fixed_mask(
5531 builder,
5532 symbol.as_ptr(),
5533 1.0,
5534 2.0,
5535 3.0,
5536 false,
5537 true,
5538 false,
5539 9,
5540 1.008,
5541 0.1,
5542 0.2,
5543 0.3,
5544 )
5545 };
5546 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5547 let frame = unsafe { rkr_frame_builder_build(builder) };
5548 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
5549 }
5550 #[test]
5551 fn builder_preserves_fixed_mask_for_atom_with_forces() {
5552 let builder = test_builder_handle();
5553 let symbol = c_string("O");
5554 let status = unsafe {
5555 rkr_frame_add_atom_with_forces_fixed_mask(
5556 builder,
5557 symbol.as_ptr(),
5558 1.0,
5559 2.0,
5560 3.0,
5561 true,
5562 true,
5563 false,
5564 11,
5565 15.999,
5566 -0.1,
5567 -0.2,
5568 -0.3,
5569 )
5570 };
5571 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5572 let frame = unsafe { rkr_frame_builder_build(builder) };
5573 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
5574 }
5575 #[test]
5576 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
5577 let builder = test_builder_handle();
5578 let symbol = c_string("N");
5579 let status = unsafe {
5580 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5581 builder,
5582 symbol.as_ptr(),
5583 1.0,
5584 2.0,
5585 3.0,
5586 false,
5587 true,
5588 true,
5589 13,
5590 14.007,
5591 0.4,
5592 0.5,
5593 0.6,
5594 -0.4,
5595 -0.5,
5596 -0.6,
5597 )
5598 };
5599 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5600 let frame = unsafe { rkr_frame_builder_build(builder) };
5601 unsafe {
5602 assert_single_atom(
5603 frame,
5604 [false, true, true],
5605 Some([0.4, 0.5, 0.6]),
5606 Some([-0.4, -0.5, -0.6]),
5607 )
5608 };
5609 }
5610 #[test]
5611 fn builder_bool_fixed_functions_set_all_axes_together() {
5612 let builder = test_builder_handle();
5613 let cu = c_string("Cu");
5614 let h = c_string("H");
5615 let atom_status =
5616 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
5617 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
5618 let velocity_status = unsafe {
5619 rkr_frame_add_atom_with_velocity(
5620 builder,
5621 h.as_ptr(),
5622 4.0,
5623 5.0,
5624 6.0,
5625 false,
5626 2,
5627 1.008,
5628 0.7,
5629 0.8,
5630 0.9,
5631 )
5632 };
5633 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
5634 let frame = unsafe { rkr_frame_builder_build(builder) };
5635 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5636 assert!(!c_frame.is_null());
5637 let c_frame_ref = unsafe { &*c_frame };
5638 assert_eq!(c_frame_ref.num_atoms, 2);
5639 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
5640 assert_eq!(
5641 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
5642 [true, true, true]
5643 );
5644 assert_eq!(
5645 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
5646 [false, false, false]
5647 );
5648 unsafe { free_c_frame(c_frame) };
5649 unsafe { free_rkr_frame(frame) };
5650 }
5651 #[test]
5652 fn status_message_returns_static_strings_for_all_status_values() {
5653 let cases = [
5654 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
5655 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
5656 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
5657 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
5658 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
5659 (
5660 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
5661 "index out of bounds",
5662 ),
5663 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
5664 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
5665 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
5666 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
5667 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
5668 (
5669 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
5670 "feature disabled in this build",
5671 ),
5672 ];
5673 for (status, expected) in cases {
5674 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
5675 assert_eq!(message.to_str().unwrap(), expected);
5676 }
5677 }
5678 unsafe fn assert_dlpack_cpu_float(
5682 t: *mut RKRDLManagedTensorVersioned,
5683 expect_ndim: i32,
5684 expect_shape: &[i64],
5685 expect_bits: u8,
5686 ) {
5687 assert!(!t.is_null());
5688 let dl = unsafe { &(*t).dl_tensor };
5689 assert_eq!(dl.ndim, expect_ndim);
5690 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
5691 assert_eq!(shape, expect_shape);
5692 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5693 assert_eq!(dl.dtype.bits, expect_bits);
5694 assert_eq!(dl.dtype.lanes, 1);
5695 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5696 assert!(!dl.data.is_null());
5697 }
5698
5699 #[test]
5700 fn frame_optional_section_dlpack_present_and_absent() {
5701 let handle = test_frame_handle();
5703 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5704 assert_eq!(
5705 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
5706 RKRStatus::RKR_STATUS_SECTION_ABSENT
5707 );
5708 assert!(t.is_null());
5709 assert_eq!(
5710 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
5711 RKRStatus::RKR_STATUS_SECTION_ABSENT
5712 );
5713 assert_eq!(
5714 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
5715 RKRStatus::RKR_STATUS_SECTION_ABSENT
5716 );
5717 unsafe { free_rkr_frame(handle) };
5718
5719 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
5721 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
5722 assert!(!fr.is_null());
5723 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
5724 assert!(n > 0);
5725 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5726 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
5727 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5728 unsafe {
5729 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
5730 rkr_dlpack_delete(vel);
5731 free_rkr_frame(fr);
5732 }
5733
5734 let cell = [10.0f64; 3];
5736 let ang = [90.0f64; 3];
5737 let b = unsafe {
5738 rkr_frame_new(
5739 cell.as_ptr(),
5740 ang.as_ptr(),
5741 std::ptr::null(),
5742 std::ptr::null(),
5743 std::ptr::null(),
5744 std::ptr::null(),
5745 )
5746 };
5747 assert!(!b.is_null());
5748 let sym = CString::new("H").unwrap();
5749 unsafe {
5750 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5751 b,
5752 sym.as_ptr(),
5753 0.0,
5754 0.0,
5755 0.0,
5756 false,
5757 false,
5758 false,
5759 0,
5760 1.0,
5761 0.1,
5762 0.0,
5763 0.0,
5764 0.0,
5765 0.0,
5766 -1.0,
5767 );
5768 rkr_frame_builder_set_last_energy(b, -0.5);
5769 }
5770 let built = unsafe { rkr_frame_builder_build(b) };
5771 assert!(!built.is_null());
5772 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
5773 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5774 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5775 assert_eq!(
5776 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
5777 RKRStatus::RKR_STATUS_SUCCESS
5778 );
5779 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
5780 assert_eq!(
5781 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
5782 RKRStatus::RKR_STATUS_SUCCESS
5783 );
5784 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
5785 let opts32 = RKRDlpackExportOptions {
5787 dtype: RKRDLDataType {
5788 code: rkr_dl_type_code::RKR_DL_FLOAT,
5789 bits: 32,
5790 lanes: 1,
5791 },
5792 device: RKRDLDevice {
5793 device_type: rkr_dl_device_type::RKR_DL_CPU,
5794 device_id: 0,
5795 },
5796 };
5797 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5798 assert_eq!(
5799 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
5800 RKRStatus::RKR_STATUS_SUCCESS
5801 );
5802 unsafe {
5803 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
5804 rkr_dlpack_delete(pos32);
5805 }
5806 let cuda_dev = RKRDlpackExportOptions {
5809 dtype: RKRDLDataType {
5810 code: rkr_dl_type_code::RKR_DL_FLOAT,
5811 bits: 64,
5812 lanes: 1,
5813 },
5814 device: RKRDLDevice {
5815 device_type: rkr_dl_device_type::RKR_DL_CUDA,
5816 device_id: 0,
5817 },
5818 };
5819 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5820 #[cfg(not(feature = "cuda"))]
5821 assert_eq!(
5822 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5823 RKRStatus::RKR_STATUS_FEATURE_DISABLED
5824 );
5825 #[cfg(feature = "cuda")]
5826 {
5827 assert_eq!(
5828 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5829 RKRStatus::RKR_STATUS_SUCCESS
5830 );
5831 assert!(!junk.is_null());
5832 let dl = unsafe { &(*junk).dl_tensor };
5833 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
5834 assert!(!dl.data.is_null());
5835 unsafe { rkr_dlpack_delete(junk) };
5836 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5838 assert_eq!(
5839 unsafe {
5840 rkr_frame_positions_as_dlpack(
5841 built,
5842 rkr_dl_device_type::RKR_DL_CUDA,
5843 0,
5844 0,
5845 1,
5846 0,
5847 &mut as_cuda,
5848 )
5849 },
5850 RKRStatus::RKR_STATUS_SUCCESS
5851 );
5852 assert!(!as_cuda.is_null());
5853 unsafe {
5854 assert_eq!(
5855 (*as_cuda).dl_tensor.device.device_type,
5856 dlpk::sys::DLDeviceType::kDLCUDA
5857 );
5858 rkr_dlpack_delete(as_cuda);
5859 }
5860 }
5861 let bad_bits = RKRDlpackExportOptions {
5863 dtype: RKRDLDataType {
5864 code: rkr_dl_type_code::RKR_DL_FLOAT,
5865 bits: 16,
5866 lanes: 1,
5867 },
5868 device: RKRDLDevice {
5869 device_type: rkr_dl_device_type::RKR_DL_CPU,
5870 device_id: 0,
5871 },
5872 };
5873 assert_eq!(
5874 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
5875 RKRStatus::RKR_STATUS_VALIDATION_ERROR
5876 );
5877 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5879 assert_eq!(
5880 unsafe {
5881 rkr_frame_positions_as_dlpack(
5882 built,
5883 rkr_dl_device_type::RKR_DL_CPU,
5884 0,
5885 0,
5886 1,
5887 0,
5888 &mut as_t,
5889 )
5890 },
5891 RKRStatus::RKR_STATUS_SUCCESS
5892 );
5893 unsafe {
5894 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
5895 }
5896 {
5898 let fr = unsafe { &mut *(built as *mut ConFrame) };
5899 for a in fr.atom_data.iter_mut() {
5900 a.x = -99.0;
5901 a.y = -99.0;
5902 a.z = -99.0;
5903 }
5904 }
5905 assert_eq!(
5906 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
5907 RKRStatus::RKR_STATUS_SUCCESS
5908 );
5909 {
5910 let fr = unsafe { &*(built as *const ConFrame) };
5911 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
5912 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
5913 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
5914 }
5915 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5917 assert_eq!(
5918 unsafe {
5919 rkr_frame_positions_as_dlpack(
5920 built,
5921 rkr_dl_device_type::RKR_DL_CPU,
5922 0,
5923 0,
5924 1,
5925 0,
5926 &mut again,
5927 )
5928 },
5929 RKRStatus::RKR_STATUS_SUCCESS
5930 );
5931 unsafe {
5932 let dl = &(*again).dl_tensor;
5933 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
5934 assert!((data[0] - 0.0).abs() < 1e-12);
5935 assert!((data[1] - 0.0).abs() < 1e-12);
5936 assert!((data[2] - 0.0).abs() < 1e-12);
5937 rkr_dlpack_delete(again);
5938 rkr_dlpack_delete(as_t);
5939 rkr_dlpack_delete(frc);
5940 rkr_dlpack_delete(eng);
5941 free_rkr_frame(built);
5942 }
5943 }
5944
5945 #[test]
5946 fn ffi_positions_dlpack_round_trip() {
5947 let handle = test_builder_handle();
5948 let sym = c_string("Cu");
5949 unsafe {
5950 rkr_frame_add_atom_full(
5951 handle,
5952 sym.as_ptr(),
5953 1.0,
5954 2.0,
5955 3.0,
5956 false,
5957 false,
5958 false,
5959 7,
5960 63.5,
5961 ptr::null(),
5962 ptr::null(),
5963 )
5964 };
5965 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5966 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
5967 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5968 assert!(!t.is_null());
5969 let dl = unsafe { &(*t).dl_tensor };
5971 assert_eq!(dl.ndim, 2);
5972 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
5973 assert_eq!(shape, &[1, 3]);
5974 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5975 assert_eq!(dl.dtype.bits, 64);
5976 assert_eq!(dl.dtype.lanes, 1);
5977 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5978 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
5979 assert_eq!(data, &[1.0, 2.0, 3.0]);
5980 let deleter = unsafe { (*t).deleter };
5982 if let Some(del) = deleter {
5983 unsafe { del(t) };
5984 }
5985 unsafe { free_rkr_frame_builder(handle) };
5986 }
5987 #[test]
5988 fn ffi_velocities_dlpack_section_absent() {
5989 let handle = test_builder_handle();
5990 let sym = c_string("Cu");
5991 unsafe {
5992 rkr_frame_add_atom_full(
5993 handle,
5994 sym.as_ptr(),
5995 0.0,
5996 0.0,
5997 0.0,
5998 false,
5999 false,
6000 false,
6001 0,
6002 63.5,
6003 ptr::null(),
6004 ptr::null(),
6005 )
6006 };
6007 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
6008 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
6009 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
6010 assert!(t.is_null());
6011 unsafe { free_rkr_frame_builder(handle) };
6012 }
6013 #[test]
6014 fn ffi_dlpack_null_handle_rejects() {
6015 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
6016 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
6017 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
6018 assert!(t.is_null());
6019 }
6020 #[cfg(feature = "chemfiles")]
6021 #[test]
6022 fn rkr_frame_select_finds_oxygen() {
6023 use crate::types::ConFrameBuilder;
6024 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
6025 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
6026 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
6027 let frame = b.build().unwrap();
6028 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
6029 let sel = CString::new("name O").unwrap();
6030 let mut out: *mut RKRSelectionResult = ptr::null_mut();
6031 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
6032 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6033 assert!(!out.is_null());
6034 let n = unsafe { rkr_selection_result_match_count(out) };
6035 assert_eq!(n, 1);
6036 let mut atoms = [u64::MAX; 4];
6037 let mut size = 0u32;
6038 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
6039 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
6040 assert_eq!(size, 1);
6041 assert_eq!(atoms[0], 0);
6042 unsafe {
6043 rkr_selection_result_free(out);
6044 free_rkr_frame(frame_ptr);
6045 }
6046 }
6047 #[cfg(feature = "chemfiles")]
6049 #[test]
6050 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
6051 use crate::types::{Bond, ConFrameBuilder};
6052 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
6054 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
6055 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
6056 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
6057 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
6058 let mut frame = b.build().unwrap();
6059 let id_to = |id: u64| {
6060 frame
6061 .atom_data
6062 .iter()
6063 .position(|a| a.atom_id == id)
6064 .unwrap() as u32
6065 };
6066 frame.header.set_bonds(&[
6067 Bond::new(id_to(0), id_to(1)),
6068 Bond::new(id_to(1), id_to(2)),
6069 Bond::new(id_to(2), id_to(3)),
6070 ]);
6071 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
6072 let run = |sel: &str| -> (u64, u32) {
6073 let csel = CString::new(sel).unwrap();
6074 let mut out: *mut RKRSelectionResult = ptr::null_mut();
6075 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
6076 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
6077 let n = unsafe { rkr_selection_result_match_count(out) };
6078 let ctx = unsafe { rkr_selection_result_context_size(out) };
6079 unsafe { rkr_selection_result_free(out) };
6080 (n, ctx)
6081 };
6082 assert_eq!(run("bonds: all"), (3, 2));
6083 assert_eq!(run("angles: all"), (2, 3));
6084 assert_eq!(run("dihedrals: all"), (1, 4));
6085 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
6086 assert_eq!(
6087 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
6088 run("bonds: type(#1) H and name(#2) O").0
6089 );
6090 unsafe { free_rkr_frame(frame_ptr) };
6091 }
6092 #[cfg(feature = "metatensor")]
6093 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
6094 assert!(!block.is_null());
6095 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
6096 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
6097 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
6098 let shape_fn = array
6099 .shape
6100 .expect("mts_array_t.shape from metatensor C API");
6101 let mut shape_ptr: *const usize = std::ptr::null();
6102 let mut shape_count: usize = 0;
6103 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
6104 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
6105 assert_eq!(shape_count, 2);
6106 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
6107 assert_eq!(shape[0], n);
6108 assert_eq!(shape[1], props);
6109 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
6110 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
6111 assert!(!samples.is_null() && !prop_lab.is_null());
6112 }
6113 #[cfg(feature = "metatensor")]
6114 #[test]
6115 fn metatensor_positions_via_c_abi() {
6116 let handle = test_frame_handle();
6117 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6118 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
6119 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6120 assert_mts_block_shape(out, 1, 3);
6121 unsafe { rkr_mts_block_free(out) };
6122 for (name, export) in [
6123 (
6124 "velocities",
6125 rkr_frame_metatensor_velocities_block
6126 as unsafe extern "C" fn(
6127 *const RKRConFrame,
6128 *mut *mut metatensor::c_api::mts_block_t,
6129 ) -> RKRStatus,
6130 ),
6131 (
6132 "forces",
6133 rkr_frame_metatensor_forces_block
6134 as unsafe extern "C" fn(
6135 *const RKRConFrame,
6136 *mut *mut metatensor::c_api::mts_block_t,
6137 ) -> RKRStatus,
6138 ),
6139 (
6140 "atom_energies",
6141 rkr_frame_metatensor_atom_energies_block
6142 as unsafe extern "C" fn(
6143 *const RKRConFrame,
6144 *mut *mut metatensor::c_api::mts_block_t,
6145 ) -> RKRStatus,
6146 ),
6147 ] {
6148 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6149 let st_abs = unsafe { export(handle, &mut o) };
6150 assert_eq!(
6151 st_abs,
6152 RKRStatus::RKR_STATUS_SECTION_ABSENT,
6153 "{name} must be SECTION_ABSENT on minimal test frame"
6154 );
6155 assert!(o.is_null());
6156 }
6157 unsafe { free_rkr_frame(handle) };
6158 }
6159
6160 #[cfg(feature = "metatensor")]
6161 #[test]
6162 fn metatensor_optional_sections_via_c_abi() {
6163 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
6165 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
6166 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
6167 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
6168 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
6169 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
6170 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
6171 builder.set_atom_energy(0, -0.5).unwrap();
6172 builder.set_atom_energy(1, -1.0).unwrap();
6173 let frame = builder.build().unwrap();
6174 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
6175 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6176 assert_eq!(
6177 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
6178 RKRStatus::RKR_STATUS_SUCCESS
6179 );
6180 assert_mts_block_shape(pos, 2, 3);
6181 unsafe { rkr_mts_block_free(pos) };
6182 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6183 assert_eq!(
6184 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
6185 RKRStatus::RKR_STATUS_SUCCESS
6186 );
6187 assert_mts_block_shape(vel, 2, 3);
6188 unsafe { rkr_mts_block_free(vel) };
6189 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6190 assert_eq!(
6191 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
6192 RKRStatus::RKR_STATUS_SUCCESS
6193 );
6194 assert_mts_block_shape(frc, 2, 3);
6195 unsafe { rkr_mts_block_free(frc) };
6196 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6197 assert_eq!(
6198 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
6199 RKRStatus::RKR_STATUS_SUCCESS
6200 );
6201 assert_mts_block_shape(eng, 2, 1);
6202 unsafe { rkr_mts_block_free(eng) };
6203 unsafe { free_rkr_frame(handle) };
6204 }
6205
6206 #[test]
6207 fn string_iterator_yields_frames_from_buffer() {
6208 let text =
6209 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
6210 let c_text = CString::new(text.as_str()).unwrap();
6211 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
6212 assert!(!it.is_null());
6213 let mut n = 0usize;
6214 loop {
6215 let fr = unsafe { con_frame_iterator_next(it) };
6216 if fr.is_null() {
6217 break;
6218 }
6219 n += 1;
6220 unsafe { free_rkr_frame(fr) };
6221 }
6222 unsafe { free_con_frame_iterator(it) };
6223 assert!(n >= 1, "string iterator should yield >=1 frame");
6224
6225 let bytes = text.as_bytes();
6226 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
6227 assert!(!it2.is_null());
6228 let fr2 = unsafe { con_frame_iterator_next(it2) };
6229 assert!(!fr2.is_null());
6230 unsafe {
6231 free_rkr_frame(fr2);
6232 free_con_frame_iterator(it2);
6233 }
6234 }
6235
6236 #[test]
6237 fn file_iterator_reads_gzip_when_present() {
6238 use flate2::Compression;
6239 use flate2::write::GzEncoder;
6240 use std::io::Write;
6241 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
6242 let dir = tempfile::tempdir().expect("tempdir");
6243 let gz_path = dir.path().join("tiny_cuh2.con.gz");
6244 {
6245 let f = std::fs::File::create(&gz_path).unwrap();
6246 let mut enc = GzEncoder::new(f, Compression::default());
6247 enc.write_all(&plain).unwrap();
6248 enc.finish().unwrap();
6249 }
6250 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
6251 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
6252 assert!(
6253 !it.is_null(),
6254 "path iterator must decompress .con.gz transparently"
6255 );
6256 let fr = unsafe { con_frame_iterator_next(it) };
6257 assert!(!fr.is_null());
6258 let n = unsafe { rkr_frame_atom_count(fr) };
6259 assert!(n > 0);
6260 unsafe {
6261 free_rkr_frame(fr);
6262 free_con_frame_iterator(it);
6263 }
6264 }
6265}