1use crate::helpers::symbol_to_atomic_number;
2use crate::iterators::{self, ConFrameIterator};
3use crate::types::{ConFrame, ConFrameBuilder, meta};
4use crate::writer::ConFrameWriter;
5use std::ffi::{CStr, CString, c_char};
6use std::fs::File;
7use std::path::Path;
8use std::ptr;
9pub const RKR_ABI_VERSION_MAJOR: u32 = 1;
14pub const RKR_ABI_VERSION_MINOR: u32 = 1;
16pub const RKR_ABI_LAYOUT_REVISION: u32 = 1;
18
19#[unsafe(no_mangle)]
21pub extern "C" fn rkr_abi_version_major() -> u32 {
22 RKR_ABI_VERSION_MAJOR
23}
24
25#[unsafe(no_mangle)]
27pub extern "C" fn rkr_abi_version_minor() -> u32 {
28 RKR_ABI_VERSION_MINOR
29}
30
31#[unsafe(no_mangle)]
33pub extern "C" fn rkr_abi_layout_revision() -> u32 {
34 RKR_ABI_LAYOUT_REVISION
35}
36
37#[unsafe(no_mangle)]
39pub extern "C" fn rkr_abi_stamp() -> *const c_char {
40 const STAMP: &[u8] = b"readcon-core/abi-1.1/layout-1\0";
41 STAMP.as_ptr() as *const c_char
42}
43
44pub const RKR_CON_SPEC_VERSION: u32 = 3;
52#[unsafe(no_mangle)]
54pub extern "C" fn rkr_con_spec_version() -> u32 {
55 crate::CON_SPEC_VERSION
56}
57#[unsafe(no_mangle)]
60pub extern "C" fn rkr_library_version() -> *const c_char {
61 const VERSION_NUL: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
63 VERSION_NUL.as_ptr() as *const c_char
64}
65#[unsafe(no_mangle)]
77pub unsafe extern "C" fn rkr_frame_atom_index_by_id(
78 frame_handle: *const RKRConFrame,
79 atom_id: u64,
80) -> u64 {
81 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
82 Some(f) => f,
83 None => return u64::MAX,
84 };
85 match frame.atom_index_by_id(atom_id) {
86 Some(idx) => idx as u64,
87 None => u64::MAX,
88 }
89}
90#[unsafe(no_mangle)]
99pub unsafe extern "C" fn rkr_symbol_to_z(symbol: *const c_char) -> u64 {
100 if symbol.is_null() {
101 return 0;
102 }
103 match unsafe { CStr::from_ptr(symbol) }.to_str() {
104 Ok(s) => symbol_to_atomic_number(s),
105 Err(_) => 0,
106 }
107}
108#[unsafe(no_mangle)]
113pub extern "C" fn rkr_z_to_symbol(z: u64) -> *const c_char {
114 macro_rules! cstrs {
119 ($($lit:literal),* $(,)?) => {
120 [$(concat!($lit, "\0").as_bytes()),*]
121 };
122 }
123 const TABLE: [&[u8]; 93] = cstrs![
124 "X", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P",
125 "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
126 "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh",
127 "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
128 "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re",
129 "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
130 "Pa", "U",
131 ];
132 let idx = if (1..=92).contains(&z) { z as usize } else { 0 };
133 TABLE[idx].as_ptr() as *const c_char
134}
135#[unsafe(no_mangle)]
138pub extern "C" fn rkr_frame_spec_version(frame_handle: *const RKRConFrame) -> u32 {
139 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
140 Some(f) => f.header.spec_version,
141 None => 0,
142 }
143}
144#[unsafe(no_mangle)]
151pub unsafe extern "C" fn rkr_frame_metadata_json(frame_handle: *const RKRConFrame) -> *mut c_char {
152 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
153 Some(f) => f,
154 None => return ptr::null_mut(),
155 };
156 let mut obj = serde_json::Map::new();
157 obj.insert(
158 meta::CON_SPEC_VERSION.into(),
159 serde_json::Value::from(frame.header.spec_version),
160 );
161 for (k, v) in &frame.header.metadata {
162 obj.insert(k.clone(), v.clone());
163 }
164 let json_str = serde_json::Value::Object(obj).to_string();
165 match CString::new(json_str) {
166 Ok(cs) => cs.into_raw(),
167 Err(_) => ptr::null_mut(),
168 }
169}
170#[unsafe(no_mangle)]
172pub extern "C" fn rkr_frame_energy(frame_handle: *const RKRConFrame) -> f64 {
173 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
174 Some(f) => f.header.energy().unwrap_or(f64::NAN),
175 None => f64::NAN,
176 }
177}
178
179#[unsafe(no_mangle)]
182pub extern "C" fn rkr_frame_index_energy(frame_handle: *const RKRConFrame) -> f64 {
183 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
184 Some(f) => crate::index_proj::finite_energy(f).unwrap_or(f64::NAN),
185 None => f64::NAN,
186 }
187}
188
189#[unsafe(no_mangle)]
191pub unsafe extern "C" fn rkr_frame_composition_formula(
192 frame_handle: *const RKRConFrame,
193) -> *mut c_char {
194 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
195 Some(f) => f,
196 None => return ptr::null_mut(),
197 };
198 let s = crate::index_proj::frame_composition_formula(frame);
199 match CString::new(s) {
200 Ok(cs) => cs.into_raw(),
201 Err(_) => ptr::null_mut(),
202 }
203}
204
205#[unsafe(no_mangle)]
207pub extern "C" fn rkr_frame_total_mass(frame_handle: *const RKRConFrame) -> f64 {
208 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
209 Some(f) => crate::index_proj::frame_total_mass(f).unwrap_or(f64::NAN),
210 None => f64::NAN,
211 }
212}
213
214#[unsafe(no_mangle)]
216pub extern "C" fn rkr_frame_cell_volume(frame_handle: *const RKRConFrame) -> f64 {
217 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
218 Some(f) => crate::index_proj::frame_cell_volume(f).unwrap_or(f64::NAN),
219 None => f64::NAN,
220 }
221}
222
223#[unsafe(no_mangle)]
225pub extern "C" fn rkr_frame_fmax(frame_handle: *const RKRConFrame) -> f64 {
226 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
227 Some(f) => crate::index_proj::frame_fmax(f).unwrap_or(f64::NAN),
228 None => f64::NAN,
229 }
230}
231
232#[unsafe(no_mangle)]
234pub extern "C" fn rkr_frame_sections_mask(frame_handle: *const RKRConFrame) -> u8 {
235 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
236 Some(f) => crate::index_proj::sections_present_mask(f),
237 None => 0,
238 }
239}
240
241#[unsafe(no_mangle)]
243pub extern "C" fn rkr_frame_index_natoms(frame_handle: *const RKRConFrame) -> u32 {
244 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
245 Some(f) => f.atom_data.len() as u32,
246 None => 0,
247 }
248}
249
250#[unsafe(no_mangle)]
253pub unsafe extern "C" fn rkr_frame_index_projection_json(
254 frame_handle: *const RKRConFrame,
255) -> *mut c_char {
256 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
257 Some(f) => f,
258 None => return ptr::null_mut(),
259 };
260 let p = crate::index_proj::FrameIndexProjection::from_frame(frame);
261 let v = serde_json::json!({
262 "n_atoms": p.n_atoms,
263 "formula": p.formula,
264 "energy": p.energy,
265 "fmax": p.fmax,
266 "total_mass": p.total_mass,
267 "cell_volume": p.cell_volume,
268 "sections_mask": p.sections_mask,
269 "has_forces": p.has_forces,
270 "has_velocities": p.has_velocities,
271 "has_energy": p.has_energy,
272 "symbols": p.symbols,
273 "species_counts": p.species_counts.iter().map(|(s,c)| serde_json::json!([s, c])).collect::<Vec<_>>(),
274 "time": p.time,
275 "timestep": p.timestep,
276 "frame_index": p.frame_index,
277 "neb_bead": p.neb_bead,
278 "neb_band": p.neb_band,
279 "charge": p.charge,
280 "magmom": p.magmom,
281 });
282 match CString::new(v.to_string()) {
283 Ok(cs) => cs.into_raw(),
284 Err(_) => ptr::null_mut(),
285 }
286}
287#[unsafe(no_mangle)]
294pub unsafe extern "C" fn rkr_frame_potential_type(frame_handle: *const RKRConFrame) -> *mut c_char {
295 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
296 Some(f) => f,
297 None => return ptr::null_mut(),
298 };
299 match frame.header.potential_type() {
300 Some(pot_type) => match CString::new(pot_type) {
301 Ok(cs) => cs.into_raw(),
302 Err(_) => ptr::null_mut(),
303 },
304 None => ptr::null_mut(),
305 }
306}
307#[unsafe(no_mangle)]
309pub extern "C" fn rkr_frame_frame_index(frame_handle: *const RKRConFrame) -> u64 {
310 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
311 Some(f) => f.header.frame_index().unwrap_or(u64::MAX),
312 None => u64::MAX,
313 }
314}
315#[unsafe(no_mangle)]
317pub extern "C" fn rkr_frame_time(frame_handle: *const RKRConFrame) -> f64 {
318 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
319 Some(f) => f.header.time().unwrap_or(f64::NAN),
320 None => f64::NAN,
321 }
322}
323#[unsafe(no_mangle)]
325pub extern "C" fn rkr_frame_timestep(frame_handle: *const RKRConFrame) -> f64 {
326 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
327 Some(f) => f.header.timestep().unwrap_or(f64::NAN),
328 None => f64::NAN,
329 }
330}
331#[unsafe(no_mangle)]
333pub extern "C" fn rkr_frame_neb_bead(frame_handle: *const RKRConFrame) -> u64 {
334 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
335 Some(f) => f.header.neb_bead().unwrap_or(u64::MAX),
336 None => u64::MAX,
337 }
338}
339#[unsafe(no_mangle)]
341pub extern "C" fn rkr_frame_neb_band(frame_handle: *const RKRConFrame) -> u64 {
342 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
343 Some(f) => f.header.neb_band().unwrap_or(u64::MAX),
344 None => u64::MAX,
345 }
346}
347#[repr(C)]
352#[allow(non_camel_case_types)]
353#[derive(Debug, PartialEq, Eq)]
354pub enum RKRStatus {
355 RKR_STATUS_SUCCESS = 0,
357 RKR_STATUS_NULL_POINTER = -1,
359 RKR_STATUS_INVALID_UTF8 = -2,
361 RKR_STATUS_INVALID_JSON = -3,
363 RKR_STATUS_IO_ERROR = -4,
365 RKR_STATUS_INDEX_OUT_OF_BOUNDS = -5,
367 RKR_STATUS_BUFFER_TOO_SMALL = -6,
369 RKR_STATUS_INTERNAL_ERROR = -7,
371 RKR_STATUS_SECTION_ABSENT = -8,
374 RKR_STATUS_VALIDATION_ERROR = -9,
376 RKR_STATUS_SELECTION_ERROR = -10,
378 RKR_STATUS_FEATURE_DISABLED = -11,
381 RKR_STATUS_DEVICE_MISMATCH = -12,
383 RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED = -13,
385}
386#[unsafe(no_mangle)]
391pub unsafe extern "C" fn rkr_frame_bond_count(frame_handle: *const RKRConFrame) -> u64 {
392 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
393 Some(f) => f.bonds().len() as u64,
394 None => 0,
395 }
396}
397#[unsafe(no_mangle)]
406pub unsafe extern "C" fn rkr_frame_bond_at(
407 frame_handle: *const RKRConFrame,
408 index: u64,
409 out_i: *mut u32,
410 out_j: *mut u32,
411 out_has_order: *mut u8,
412 out_order: *mut i32,
413) -> RKRStatus {
414 if frame_handle.is_null()
415 || out_i.is_null()
416 || out_j.is_null()
417 || out_has_order.is_null()
418 || out_order.is_null()
419 {
420 return RKRStatus::RKR_STATUS_NULL_POINTER;
421 }
422 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
423 return RKRStatus::RKR_STATUS_NULL_POINTER;
424 };
425 let bonds = frame.bonds();
426 let Some(bond) = bonds.get(index as usize) else {
427 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
428 };
429 unsafe {
430 *out_i = bond.i;
431 *out_j = bond.j;
432 if let Some(order) = bond.order {
433 *out_has_order = 1;
434 *out_order = order;
435 } else {
436 *out_has_order = 0;
437 *out_order = 0;
438 }
439 }
440 RKRStatus::RKR_STATUS_SUCCESS
441}
442#[unsafe(no_mangle)]
445pub extern "C" fn rkr_status_message(status: RKRStatus) -> *const c_char {
446 match status {
447 RKRStatus::RKR_STATUS_SUCCESS => c"success".as_ptr(),
448 RKRStatus::RKR_STATUS_NULL_POINTER => c"null pointer".as_ptr(),
449 RKRStatus::RKR_STATUS_INVALID_UTF8 => c"invalid UTF-8".as_ptr(),
450 RKRStatus::RKR_STATUS_INVALID_JSON => c"invalid JSON".as_ptr(),
451 RKRStatus::RKR_STATUS_IO_ERROR => c"I/O error".as_ptr(),
452 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS => c"index out of bounds".as_ptr(),
453 RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL => c"buffer too small".as_ptr(),
454 RKRStatus::RKR_STATUS_INTERNAL_ERROR => c"internal error".as_ptr(),
455 RKRStatus::RKR_STATUS_SECTION_ABSENT => c"section absent".as_ptr(),
456 RKRStatus::RKR_STATUS_VALIDATION_ERROR => c"validation error".as_ptr(),
457 RKRStatus::RKR_STATUS_SELECTION_ERROR => c"selection error".as_ptr(),
458 RKRStatus::RKR_STATUS_FEATURE_DISABLED => c"feature disabled in this build".as_ptr(),
459 RKRStatus::RKR_STATUS_DEVICE_MISMATCH => c"DLPack device mismatch".as_ptr(),
460 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED => {
461 c"device allocation unsupported in this build".as_ptr()
462 }
463 }
464}
465pub struct RKRConFrame {
470 _private: [u8; 0],
471}
472pub struct RKRConFrameWriter {
477 _private: [u8; 0],
478}
479#[repr(C)]
485pub struct RKRArrayView {
486 pub data: *const std::ffi::c_void,
487 pub n: usize,
488 pub cols: u32,
489 pub dtype_code: u8,
490 pub dtype_bits: u8,
491}
492
493impl RKRArrayView {
494 fn empty() -> Self {
495 Self {
496 data: std::ptr::null(),
497 n: 0,
498 cols: 0,
499 dtype_code: 0,
500 dtype_bits: 0,
501 }
502 }
503
504 fn from_array2(arr: &crate::storage_dtype::FloatArray2) -> Self {
505 let kind = arr.kind();
506 Self {
507 data: arr.data_ptr() as *const std::ffi::c_void,
508 n: arr.nrows(),
509 cols: arr.ncols() as u32,
510 dtype_code: kind.dlpack_code(),
511 dtype_bits: kind.dlpack_bits(),
512 }
513 }
514
515 fn from_array1(arr: &crate::storage_dtype::FloatArray1) -> Self {
516 let kind = arr.kind();
517 Self {
518 data: arr.data_ptr() as *const std::ffi::c_void,
519 n: arr.len(),
520 cols: 1,
521 dtype_code: kind.dlpack_code(),
522 dtype_bits: kind.dlpack_bits(),
523 }
524 }
525}
526
527#[repr(C)]
528pub struct CFrame {
529 pub atoms: *mut CAtom,
530 pub num_atoms: usize,
531 pub cell: [f64; 3],
532 pub angles: [f64; 3],
533 pub has_velocities: bool,
534 pub has_forces: bool,
535 pub has_energies: bool,
536}
537#[repr(C)]
547pub struct CAtom {
548 pub atomic_number: u64,
549 pub x: f64,
550 pub y: f64,
551 pub z: f64,
552 pub atom_id: u64,
553 pub mass: f64,
554 pub is_fixed: bool,
557 pub fixed_x: bool,
558 pub fixed_y: bool,
559 pub fixed_z: bool,
560 pub vx: f64,
561 pub vy: f64,
562 pub vz: f64,
563 pub has_velocity: bool,
564 pub fx: f64,
565 pub fy: f64,
566 pub fz: f64,
567 pub has_forces: bool,
568 pub energy: f64,
571 pub has_energy: bool,
572}
573#[repr(C)]
574pub struct CConFrameIterator {
575 iterator: *mut ConFrameIterator<'static>,
576 file_contents: *mut String,
577}
578
579fn c_iterator_from_owned_string(contents: String) -> *mut CConFrameIterator {
581 let file_contents_box = Box::new(contents);
582 let file_contents_ptr = Box::into_raw(file_contents_box);
583 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
584 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
585 let c_iterator = Box::new(CConFrameIterator {
586 iterator: Box::into_raw(iterator),
587 file_contents: file_contents_ptr,
588 });
589 Box::into_raw(c_iterator)
590}
591
592#[unsafe(no_mangle)]
608pub unsafe extern "C" fn read_con_file_iterator(
609 filename_c: *const c_char,
610) -> *mut CConFrameIterator {
611 if filename_c.is_null() {
612 return ptr::null_mut();
613 }
614 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
615 Ok(s) => s,
616 Err(_) => return ptr::null_mut(),
617 };
618 let owned = match crate::compression::read_file_contents(Path::new(filename)) {
619 Ok(fc) => match fc.as_str() {
620 Ok(s) => s.to_owned(),
621 Err(_) => return ptr::null_mut(),
622 },
623 Err(_) => return ptr::null_mut(),
624 };
625 c_iterator_from_owned_string(owned)
626}
627
628#[unsafe(no_mangle)]
636pub unsafe extern "C" fn read_con_string_iterator(
637 contents_c: *const c_char,
638) -> *mut CConFrameIterator {
639 if contents_c.is_null() {
640 return ptr::null_mut();
641 }
642 let contents = match unsafe { CStr::from_ptr(contents_c).to_str() } {
643 Ok(s) => s.to_owned(),
644 Err(_) => return ptr::null_mut(),
645 };
646 c_iterator_from_owned_string(contents)
647}
648
649#[unsafe(no_mangle)]
656pub unsafe extern "C" fn read_con_buffer_iterator(
657 data: *const u8,
658 len: usize,
659) -> *mut CConFrameIterator {
660 if data.is_null() && len > 0 {
661 return ptr::null_mut();
662 }
663 if len == 0 {
664 return c_iterator_from_owned_string(String::new());
665 }
666 let slice = unsafe { std::slice::from_raw_parts(data, len) };
667 let contents = match std::str::from_utf8(slice) {
668 Ok(s) => s.to_owned(),
669 Err(_) => return ptr::null_mut(),
670 };
671 c_iterator_from_owned_string(contents)
672}
673#[unsafe(no_mangle)]
679pub unsafe extern "C" fn con_frame_iterator_next(
680 iterator: *mut CConFrameIterator,
681) -> *mut RKRConFrame {
682 if iterator.is_null() {
683 return ptr::null_mut();
684 }
685 let iter = unsafe { &mut *(*iterator).iterator };
686 match iter.next() {
687 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
688 _ => ptr::null_mut(),
689 }
690}
691
692#[unsafe(no_mangle)]
695pub unsafe extern "C" fn con_frame_iterator_forward(iterator: *mut CConFrameIterator) -> RKRStatus {
696 if iterator.is_null() {
697 return RKRStatus::RKR_STATUS_NULL_POINTER;
698 }
699 let iter = unsafe { &mut *(*iterator).iterator };
700 match iter.forward() {
701 Some(Ok(())) => RKRStatus::RKR_STATUS_SUCCESS,
702 Some(Err(_)) => RKRStatus::RKR_STATUS_IO_ERROR,
703 None => RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
704 }
705}
706
707#[unsafe(no_mangle)]
710pub unsafe extern "C" fn con_frame_iterator_skip(
711 iterator: *mut CConFrameIterator,
712 n: usize,
713) -> usize {
714 if iterator.is_null() {
715 return usize::MAX;
716 }
717 let iter = unsafe { &mut *(*iterator).iterator };
718 iter.skip_frames(n).unwrap_or(usize::MAX)
719}
720
721#[unsafe(no_mangle)]
723pub unsafe extern "C" fn con_frame_iterator_nth(
724 iterator: *mut CConFrameIterator,
725 index: usize,
726) -> *mut RKRConFrame {
727 if iterator.is_null() {
728 return ptr::null_mut();
729 }
730 let iter = unsafe { &mut *(*iterator).iterator };
731 match iter.skip_frames(index) {
732 Ok(skipped) if skipped == index => {}
733 _ => return ptr::null_mut(),
734 }
735 match iter.next() {
736 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
737 _ => ptr::null_mut(),
738 }
739}
740
741#[unsafe(no_mangle)]
743pub unsafe extern "C" fn rkr_count_frames(filename_c: *const c_char) -> usize {
744 if filename_c.is_null() {
745 return usize::MAX;
746 }
747 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
748 Ok(s) => s,
749 Err(_) => return usize::MAX,
750 };
751 crate::iterators::count_frames(Path::new(filename)).unwrap_or(usize::MAX)
752}
753
754#[unsafe(no_mangle)]
756pub unsafe extern "C" fn rkr_read_nth_frame(
757 filename_c: *const c_char,
758 index: usize,
759) -> *mut RKRConFrame {
760 if filename_c.is_null() {
761 return ptr::null_mut();
762 }
763 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
764 Ok(s) => s,
765 Err(_) => return ptr::null_mut(),
766 };
767 match crate::iterators::read_nth_frame(Path::new(filename), index) {
768 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
769 Err(_) => ptr::null_mut(),
770 }
771}
772#[unsafe(no_mangle)]
777pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
778 if !frame_handle.is_null() {
779 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
780 }
781}
782#[unsafe(no_mangle)]
787pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
788 if iterator.is_null() {
789 return;
790 }
791 unsafe {
792 let c_iterator_box = Box::from_raw(iterator);
793 let _ = Box::from_raw(c_iterator_box.iterator);
794 let _ = Box::from_raw(c_iterator_box.file_contents);
795 }
796}
797#[unsafe(no_mangle)]
806pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
807 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
808 Some(f) => f,
809 None => return ptr::null_mut(),
810 };
811 let masses_iter = frame
812 .header
813 .natms_per_type
814 .iter()
815 .zip(frame.header.masses_per_type.iter())
816 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
817 let has_velocities = frame.has_velocities();
818 let mut c_atoms: Vec<CAtom> = frame
819 .atom_data
820 .iter()
821 .zip(masses_iter)
822 .map(|(atom_datum, mass)| {
823 let [vx, vy, vz] = atom_datum.velocity.unwrap_or([0.0; 3]);
824 let [fx, fy, fz] = atom_datum.force.unwrap_or([0.0; 3]);
825 CAtom {
826 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
827 x: atom_datum.x,
828 y: atom_datum.y,
829 z: atom_datum.z,
830 is_fixed: atom_datum.is_fixed(),
831 fixed_x: atom_datum.fixed[0],
832 fixed_y: atom_datum.fixed[1],
833 fixed_z: atom_datum.fixed[2],
834 atom_id: atom_datum.atom_id,
835 mass,
836 vx,
837 vy,
838 vz,
839 has_velocity: atom_datum.has_velocity(),
840 fx,
841 fy,
842 fz,
843 has_forces: atom_datum.has_forces(),
844 energy: atom_datum.energy.unwrap_or(0.0),
845 has_energy: atom_datum.has_energy(),
846 }
847 })
848 .collect();
849 let atoms_ptr = c_atoms.as_mut_ptr();
850 let num_atoms = c_atoms.len();
851 std::mem::forget(c_atoms);
852 let has_forces = frame.has_forces();
853 let has_energies = frame.has_energies();
854 let c_frame = Box::new(CFrame {
855 atoms: atoms_ptr,
856 num_atoms,
857 cell: frame.header.boxl,
858 angles: frame.header.angles,
859 has_velocities,
860 has_forces,
861 has_energies,
862 });
863 Box::into_raw(c_frame)
864}
865#[unsafe(no_mangle)]
870pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
871 if frame.is_null() {
872 return;
873 }
874 unsafe {
875 let frame_box = Box::from_raw(frame);
876 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
877 }
878}
879#[unsafe(no_mangle)]
898pub unsafe extern "C" fn rkr_frame_get_header_line(
899 frame_handle: *const RKRConFrame,
900 is_prebox: bool,
901 line_index: usize,
902 buffer: *mut c_char,
903 buffer_len: usize,
904) -> RKRStatus {
905 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
906 Some(f) => f,
907 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
908 };
909 if buffer.is_null() {
910 return RKRStatus::RKR_STATUS_NULL_POINTER;
911 }
912 if buffer_len == 0 {
913 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
914 }
915 let line_to_copy: Option<&str> = if is_prebox {
916 match line_index {
917 0 => Some(frame.header.prebox_header.user.as_str()),
918 1 => Some(frame.header.prebox_header.metadata_line()),
919 _ => None,
920 }
921 } else {
922 frame
923 .header
924 .postbox_header
925 .get(line_index)
926 .map(String::as_str)
927 };
928 if let Some(line) = line_to_copy {
929 let bytes = line.as_bytes();
930 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
931 unsafe {
932 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
933 *buffer.add(len_to_copy) = 0;
934 }
935 RKRStatus::RKR_STATUS_SUCCESS
936 } else {
937 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
938 }
939}
940#[unsafe(no_mangle)]
953pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
954 frame_handle: *const RKRConFrame,
955 is_prebox: bool,
956 line_index: usize,
957) -> *mut c_char {
958 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
959 Some(f) => f,
960 None => return ptr::null_mut(),
961 };
962 let line_to_copy: Option<&str> = if is_prebox {
963 match line_index {
964 0 => Some(frame.header.prebox_header.user.as_str()),
965 1 => Some(frame.header.prebox_header.metadata_line()),
966 _ => None,
967 }
968 } else {
969 frame
970 .header
971 .postbox_header
972 .get(line_index)
973 .map(String::as_str)
974 };
975 if let Some(line) = line_to_copy {
976 match CString::new(line) {
978 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
981 } else {
982 ptr::null_mut() }
984}
985#[unsafe(no_mangle)]
993pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
994 if !s.is_null() {
995 let _ = unsafe { CString::from_raw(s) };
997 }
998}
999type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
1013#[inline]
1016fn into_rkr_writer(sink: Box<dyn std::io::Write>, precision: Option<u8>) -> *mut RKRConFrameWriter {
1017 let writer: RkrWriter = match precision {
1018 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
1019 None => ConFrameWriter::new(sink),
1020 };
1021 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1022}
1023#[inline]
1025unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
1026 if filename_c.is_null() {
1027 return None;
1028 }
1029 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
1030}
1031#[unsafe(no_mangle)]
1037pub unsafe extern "C" fn create_writer_from_path_c(
1038 filename_c: *const c_char,
1039) -> *mut RKRConFrameWriter {
1040 let filename = match unsafe { cstr_path(filename_c) } {
1041 Some(s) => s,
1042 None => return ptr::null_mut(),
1043 };
1044 match File::create(filename) {
1045 Ok(file) => into_rkr_writer(Box::new(file), None),
1046 Err(_) => ptr::null_mut(),
1047 }
1048}
1049#[unsafe(no_mangle)]
1055pub unsafe extern "C" fn create_writer_from_path_round_trip_c(
1056 filename_c: *const c_char,
1057) -> *mut RKRConFrameWriter {
1058 let filename = match unsafe { cstr_path(filename_c) } {
1059 Some(s) => s,
1060 None => return ptr::null_mut(),
1061 };
1062 match File::create(filename) {
1063 Ok(file) => {
1064 let writer: RkrWriter = ConFrameWriter::with_float_format(
1065 Box::new(file),
1066 crate::writer::FloatFormat::RoundTrip,
1067 );
1068 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1069 }
1070 Err(_) => ptr::null_mut(),
1071 }
1072}
1073
1074#[unsafe(no_mangle)]
1079pub unsafe extern "C" fn rkr_writer_flush(writer_handle: *mut RKRConFrameWriter) -> RKRStatus {
1080 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1081 Some(w) => w,
1082 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1083 };
1084 match writer.flush() {
1085 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
1086 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
1087 }
1088}
1089
1090#[unsafe(no_mangle)]
1095pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
1096 if !writer_handle.is_null() {
1097 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
1098 }
1099}
1100#[unsafe(no_mangle)]
1106pub unsafe extern "C" fn rkr_writer_extend(
1107 writer_handle: *mut RKRConFrameWriter,
1108 frame_handles: *const *const RKRConFrame,
1109 num_frames: usize,
1110) -> RKRStatus {
1111 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1112 Some(w) => w,
1113 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1114 };
1115 if frame_handles.is_null() {
1116 return RKRStatus::RKR_STATUS_NULL_POINTER;
1117 }
1118 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
1119 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
1120 if handles_slice.iter().any(|&handle| handle.is_null()) {
1121 return RKRStatus::RKR_STATUS_NULL_POINTER;
1124 }
1125 for &handle in handles_slice.iter() {
1126 match unsafe { (handle as *const ConFrame).as_ref() } {
1128 Some(frame) => rust_frames.push(frame),
1129 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1131 }
1132 }
1133 match writer.extend(rust_frames.into_iter()) {
1134 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1135 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
1136 }
1137}
1138
1139#[unsafe(no_mangle)]
1145pub unsafe extern "C" fn rkr_writer_set_canonical(
1146 writer_handle: *mut RKRConFrameWriter,
1147 canonical: u8,
1148) -> RKRStatus {
1149 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1150 Some(w) => w,
1151 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1152 };
1153 writer.set_canonical(canonical != 0);
1154 RKRStatus::RKR_STATUS_SUCCESS
1155}
1156
1157#[unsafe(no_mangle)]
1159pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
1160 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
1161 Some(w) => u8::from(w.is_canonical()),
1162 None => 0,
1163 }
1164}
1165
1166#[cfg(test)]
1167mod index_proj_ffi_tests {
1168 use super::*;
1169 use std::ffi::CStr;
1170 use std::fs;
1171 use std::path::PathBuf;
1172
1173 fn fixture_path() -> PathBuf {
1174 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
1175 }
1176
1177 #[test]
1178 fn ffi_projection_matches_index_proj() {
1179 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1180 let fr = &frames[0];
1181 let handle = fr as *const ConFrame as *const RKRConFrame;
1182 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
1183 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
1185 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
1186 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
1187 assert!(!formula_c.is_null());
1188 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
1189 assert_eq!(formula, proj.formula);
1190 unsafe { rkr_free_string(formula_c) };
1191 let ie = rkr_frame_index_energy(handle);
1192 match proj.energy {
1193 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
1194 None => assert!(ie.is_nan()),
1195 }
1196 let tm = rkr_frame_total_mass(handle);
1197 match proj.total_mass {
1198 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
1199 None => assert!(tm.is_nan()),
1200 }
1201 let cv = rkr_frame_cell_volume(handle);
1202 match proj.cell_volume {
1203 Some(v) => assert!(
1204 (cv - v).abs() < 1e-6 * v.max(1.0),
1205 "cell_volume C={cv} proj={v}"
1206 ),
1207 None => assert!(cv.is_nan()),
1208 }
1209 let fm = rkr_frame_fmax(handle);
1210 match proj.fmax {
1211 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
1212 None => assert!(fm.is_nan()),
1213 }
1214 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
1215 assert!(!json_c.is_null());
1216 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
1217 assert!(json.contains("\"formula\""));
1218 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
1219 assert!(json.contains("\"n_atoms\""));
1220 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
1221 unsafe { rkr_free_string(json_c) };
1222 }
1223
1224 #[test]
1225 fn ffi_canonical_writer_byte_identical() {
1226 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1227 let fr = &frames[0];
1228 let dir = tempfile::tempdir().unwrap();
1229 let p1 = dir.path().join("a.con");
1230 let p2 = dir.path().join("b.con");
1231 for p in [&p1, &p2] {
1232 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
1233 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
1234 assert!(!w.is_null());
1235 assert_eq!(
1236 unsafe { rkr_writer_set_canonical(w, 1) },
1237 RKRStatus::RKR_STATUS_SUCCESS
1238 );
1239 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
1240 let handles = [fr as *const ConFrame as *const RKRConFrame];
1241 assert_eq!(
1242 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
1243 RKRStatus::RKR_STATUS_SUCCESS
1244 );
1245 unsafe { free_rkr_writer(w) };
1246 }
1247 let b1 = fs::read(&p1).unwrap();
1248 let b2 = fs::read(&p2).unwrap();
1249 assert_eq!(b1, b2);
1250 assert!(!b1.is_empty());
1251 }
1252}
1253#[unsafe(no_mangle)]
1262pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
1263 filename_c: *const c_char,
1264 precision: u8,
1265) -> *mut RKRConFrameWriter {
1266 let filename = match unsafe { cstr_path(filename_c) } {
1267 Some(s) => s,
1268 None => return ptr::null_mut(),
1269 };
1270 match File::create(filename) {
1271 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1272 Err(_) => ptr::null_mut(),
1273 }
1274}
1275pub struct RKRConFrameBuilder {
1282 _private: [u8; 0],
1283}
1284#[allow(clippy::too_many_arguments)]
1285unsafe fn add_builder_atom(
1286 builder_handle: *mut RKRConFrameBuilder,
1287 symbol: *const c_char,
1288 x: f64,
1289 y: f64,
1290 z: f64,
1291 fixed: [bool; 3],
1292 atom_id: u64,
1293 mass: f64,
1294 velocity: Option<[f64; 3]>,
1295 forces: Option<[f64; 3]>,
1296) -> RKRStatus {
1297 if builder_handle.is_null() || symbol.is_null() {
1298 return RKRStatus::RKR_STATUS_NULL_POINTER;
1299 }
1300 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1301 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
1302 Ok(s) => s,
1303 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1304 };
1305 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
1306 if let Some(v) = velocity {
1307 builder.with_velocity(v);
1308 }
1309 if let Some(f) = forces {
1310 builder.with_force(f);
1311 }
1312 RKRStatus::RKR_STATUS_SUCCESS
1313}
1314#[unsafe(no_mangle)]
1320pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
1321 builder_handle: *mut RKRConFrameBuilder,
1322 velocity: *const f64,
1323) -> RKRStatus {
1324 if builder_handle.is_null() || velocity.is_null() {
1325 return RKRStatus::RKR_STATUS_NULL_POINTER;
1326 }
1327 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1328 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1329 builder.with_velocity(v);
1330 RKRStatus::RKR_STATUS_SUCCESS
1331}
1332#[unsafe(no_mangle)]
1338pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
1339 builder_handle: *mut RKRConFrameBuilder,
1340 force: *const f64,
1341) -> RKRStatus {
1342 if builder_handle.is_null() || force.is_null() {
1343 return RKRStatus::RKR_STATUS_NULL_POINTER;
1344 }
1345 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1346 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1347 builder.with_force(f);
1348 RKRStatus::RKR_STATUS_SUCCESS
1349}
1350#[unsafe(no_mangle)]
1357pub unsafe extern "C" fn rkr_frame_builder_set_last_displacement(
1358 builder_handle: *mut RKRConFrameBuilder,
1359 displacement: *const f64,
1360) -> RKRStatus {
1361 if builder_handle.is_null() || displacement.is_null() {
1362 return RKRStatus::RKR_STATUS_NULL_POINTER;
1363 }
1364 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1365 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1366 builder.with_displacement(d);
1367 RKRStatus::RKR_STATUS_SUCCESS
1368}
1369#[unsafe(no_mangle)]
1376pub unsafe extern "C" fn rkr_frame_builder_set_last_spread(
1377 builder_handle: *mut RKRConFrameBuilder,
1378 spread: *const f64,
1379) -> RKRStatus {
1380 if builder_handle.is_null() || spread.is_null() {
1381 return RKRStatus::RKR_STATUS_NULL_POINTER;
1382 }
1383 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1384 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
1385 builder.with_spread(d);
1386 RKRStatus::RKR_STATUS_SUCCESS
1387}
1388#[unsafe(no_mangle)]
1398pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1399 builder_handle: *mut RKRConFrameBuilder,
1400 energy: f64,
1401) -> RKRStatus {
1402 if builder_handle.is_null() {
1403 return RKRStatus::RKR_STATUS_NULL_POINTER;
1404 }
1405 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1406 builder.with_energy(energy);
1407 RKRStatus::RKR_STATUS_SUCCESS
1408}
1409fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1423 use crate::error::ParseError;
1424 match e {
1425 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1426 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1427 }
1428 ParseError::MassMismatch { .. } | ParseError::ValidationError(_) => {
1429 RKRStatus::RKR_STATUS_VALIDATION_ERROR
1430 }
1431 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1432 }
1433}
1434#[unsafe(no_mangle)]
1441pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1442 builder_handle: *const RKRConFrameBuilder,
1443) -> usize {
1444 if builder_handle.is_null() {
1445 return 0;
1446 }
1447 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1448 builder.atom_count()
1449}
1450#[unsafe(no_mangle)]
1454pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1455 builder_handle: *mut RKRConFrameBuilder,
1456 index: usize,
1457 x: f64,
1458 y: f64,
1459 z: f64,
1460) -> RKRStatus {
1461 if builder_handle.is_null() {
1462 return RKRStatus::RKR_STATUS_NULL_POINTER;
1463 }
1464 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1465 match builder.set_atom_position(index, x, y, z) {
1466 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1467 Err(e) => map_builder_err(e),
1468 }
1469}
1470#[unsafe(no_mangle)]
1474pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1475 builder_handle: *mut RKRConFrameBuilder,
1476 index: usize,
1477 velocity: *const f64,
1478) -> RKRStatus {
1479 if builder_handle.is_null() || velocity.is_null() {
1480 return RKRStatus::RKR_STATUS_NULL_POINTER;
1481 }
1482 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1483 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1484 match builder.set_atom_velocity(index, v) {
1485 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1486 Err(e) => map_builder_err(e),
1487 }
1488}
1489#[unsafe(no_mangle)]
1493pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1494 builder_handle: *mut RKRConFrameBuilder,
1495 index: usize,
1496 force: *const f64,
1497) -> RKRStatus {
1498 if builder_handle.is_null() || force.is_null() {
1499 return RKRStatus::RKR_STATUS_NULL_POINTER;
1500 }
1501 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1502 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1503 match builder.set_atom_force(index, f) {
1504 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1505 Err(e) => map_builder_err(e),
1506 }
1507}
1508#[unsafe(no_mangle)]
1513pub unsafe extern "C" fn rkr_frame_builder_set_atom_displacement(
1514 builder_handle: *mut RKRConFrameBuilder,
1515 index: usize,
1516 displacement: *const f64,
1517) -> RKRStatus {
1518 if builder_handle.is_null() || displacement.is_null() {
1519 return RKRStatus::RKR_STATUS_NULL_POINTER;
1520 }
1521 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1522 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1523 match builder.set_atom_displacement(index, d) {
1524 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1525 Err(e) => map_builder_err(e),
1526 }
1527}
1528#[unsafe(no_mangle)]
1533pub unsafe extern "C" fn rkr_frame_builder_set_atom_spread(
1534 builder_handle: *mut RKRConFrameBuilder,
1535 index: usize,
1536 spread: *const f64,
1537) -> RKRStatus {
1538 if builder_handle.is_null() || spread.is_null() {
1539 return RKRStatus::RKR_STATUS_NULL_POINTER;
1540 }
1541 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1542 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
1543 match builder.set_atom_spread(index, d) {
1544 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1545 Err(e) => map_builder_err(e),
1546 }
1547}
1548#[unsafe(no_mangle)]
1552pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1553 builder_handle: *mut RKRConFrameBuilder,
1554 index: usize,
1555 energy: f64,
1556) -> RKRStatus {
1557 if builder_handle.is_null() {
1558 return RKRStatus::RKR_STATUS_NULL_POINTER;
1559 }
1560 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1561 match builder.set_atom_energy(index, energy) {
1562 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1563 Err(e) => map_builder_err(e),
1564 }
1565}
1566#[unsafe(no_mangle)]
1570pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1571 builder_handle: *mut RKRConFrameBuilder,
1572 index: usize,
1573 fixed_x: bool,
1574 fixed_y: bool,
1575 fixed_z: bool,
1576) -> RKRStatus {
1577 if builder_handle.is_null() {
1578 return RKRStatus::RKR_STATUS_NULL_POINTER;
1579 }
1580 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1581 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1582 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1583 Err(e) => map_builder_err(e),
1584 }
1585}
1586#[unsafe(no_mangle)]
1590pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1591 builder_handle: *mut RKRConFrameBuilder,
1592 index: usize,
1593 mass: f64,
1594) -> RKRStatus {
1595 if builder_handle.is_null() {
1596 return RKRStatus::RKR_STATUS_NULL_POINTER;
1597 }
1598 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1599 match builder.set_atom_mass(index, mass) {
1600 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1601 Err(e) => map_builder_err(e),
1602 }
1603}
1604#[unsafe(no_mangle)]
1611pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1612 builder_handle: *mut RKRConFrameBuilder,
1613 index: usize,
1614 atom_id: u64,
1615) -> RKRStatus {
1616 if builder_handle.is_null() {
1617 return RKRStatus::RKR_STATUS_NULL_POINTER;
1618 }
1619 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1620 match builder.set_atom_id(index, atom_id) {
1621 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1622 Err(e) => map_builder_err(e),
1623 }
1624}
1625#[unsafe(no_mangle)]
1629pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1630 builder_handle: *mut RKRConFrameBuilder,
1631 index: usize,
1632) -> RKRStatus {
1633 if builder_handle.is_null() {
1634 return RKRStatus::RKR_STATUS_NULL_POINTER;
1635 }
1636 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1637 match builder.clear_atom_velocity(index) {
1638 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1639 Err(e) => map_builder_err(e),
1640 }
1641}
1642#[unsafe(no_mangle)]
1645pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1646 builder_handle: *mut RKRConFrameBuilder,
1647 index: usize,
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.clear_atom_force(index) {
1654 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1655 Err(e) => map_builder_err(e),
1656 }
1657}
1658#[unsafe(no_mangle)]
1661pub unsafe extern "C" fn rkr_frame_builder_clear_atom_displacement(
1662 builder_handle: *mut RKRConFrameBuilder,
1663 index: usize,
1664) -> RKRStatus {
1665 if builder_handle.is_null() {
1666 return RKRStatus::RKR_STATUS_NULL_POINTER;
1667 }
1668 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1669 match builder.clear_atom_displacement(index) {
1670 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1671 Err(e) => map_builder_err(e),
1672 }
1673}
1674#[unsafe(no_mangle)]
1677pub unsafe extern "C" fn rkr_frame_builder_clear_atom_spread(
1678 builder_handle: *mut RKRConFrameBuilder,
1679 index: usize,
1680) -> RKRStatus {
1681 if builder_handle.is_null() {
1682 return RKRStatus::RKR_STATUS_NULL_POINTER;
1683 }
1684 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1685 match builder.clear_atom_spread(index) {
1686 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1687 Err(e) => map_builder_err(e),
1688 }
1689}
1690#[unsafe(no_mangle)]
1693pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1694 builder_handle: *mut RKRConFrameBuilder,
1695 index: usize,
1696) -> RKRStatus {
1697 if builder_handle.is_null() {
1698 return RKRStatus::RKR_STATUS_NULL_POINTER;
1699 }
1700 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1701 match builder.clear_atom_energy(index) {
1702 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1703 Err(e) => map_builder_err(e),
1704 }
1705}
1706#[unsafe(no_mangle)]
1711pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1712 builder_handle: *mut RKRConFrameBuilder,
1713 positions: *const f64,
1714 len: usize,
1715) -> RKRStatus {
1716 if builder_handle.is_null() || positions.is_null() {
1717 return RKRStatus::RKR_STATUS_NULL_POINTER;
1718 }
1719 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1720 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1721 match builder.set_positions_from_flat(slice) {
1722 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1723 Err(e) => map_builder_err(e),
1724 }
1725}
1726#[unsafe(no_mangle)]
1730pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1731 builder_handle: *mut RKRConFrameBuilder,
1732 forces: *const f64,
1733 len: usize,
1734) -> RKRStatus {
1735 if builder_handle.is_null() || forces.is_null() {
1736 return RKRStatus::RKR_STATUS_NULL_POINTER;
1737 }
1738 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1739 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1740 match builder.set_forces_from_flat(slice) {
1741 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1742 Err(e) => map_builder_err(e),
1743 }
1744}
1745#[unsafe(no_mangle)]
1750pub unsafe extern "C" fn rkr_frame_builder_set_displacements_from_flat(
1751 builder_handle: *mut RKRConFrameBuilder,
1752 displacements: *const f64,
1753 len: usize,
1754) -> RKRStatus {
1755 if builder_handle.is_null() || displacements.is_null() {
1756 return RKRStatus::RKR_STATUS_NULL_POINTER;
1757 }
1758 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1759 let slice = unsafe { std::slice::from_raw_parts(displacements, len) };
1760 match builder.set_displacements_from_flat(slice) {
1761 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1762 Err(e) => map_builder_err(e),
1763 }
1764}
1765#[unsafe(no_mangle)]
1770pub unsafe extern "C" fn rkr_frame_builder_set_spreads_from_flat(
1771 builder_handle: *mut RKRConFrameBuilder,
1772 spreads: *const f64,
1773 len: usize,
1774) -> RKRStatus {
1775 if builder_handle.is_null() || spreads.is_null() {
1776 return RKRStatus::RKR_STATUS_NULL_POINTER;
1777 }
1778 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1779 let slice = unsafe { std::slice::from_raw_parts(spreads, len) };
1780 match builder.set_spreads_from_flat(slice) {
1781 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1782 Err(e) => map_builder_err(e),
1783 }
1784}
1785#[unsafe(no_mangle)]
1789pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1790 builder_handle: *mut RKRConFrameBuilder,
1791 energies: *const f64,
1792 len: usize,
1793) -> RKRStatus {
1794 if builder_handle.is_null() || energies.is_null() {
1795 return RKRStatus::RKR_STATUS_NULL_POINTER;
1796 }
1797 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1798 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1799 match builder.set_atom_energies_from_flat(slice) {
1800 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1801 Err(e) => map_builder_err(e),
1802 }
1803}
1804#[unsafe(no_mangle)]
1808pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1809 builder_handle: *const RKRConFrameBuilder,
1810 index: usize,
1811 out_xyz: *mut f64,
1812) -> RKRStatus {
1813 if builder_handle.is_null() || out_xyz.is_null() {
1814 return RKRStatus::RKR_STATUS_NULL_POINTER;
1815 }
1816 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1817 match builder.get_atom_position(index) {
1818 Ok((x, y, z)) => unsafe {
1819 *out_xyz = x;
1820 *out_xyz.add(1) = y;
1821 *out_xyz.add(2) = z;
1822 RKRStatus::RKR_STATUS_SUCCESS
1823 },
1824 Err(e) => map_builder_err(e),
1825 }
1826}
1827#[unsafe(no_mangle)]
1834pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1835 builder_handle: *const RKRConFrameBuilder,
1836 index: usize,
1837 out_xyz: *mut f64,
1838 has_value: *mut bool,
1839) -> RKRStatus {
1840 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1841 return RKRStatus::RKR_STATUS_NULL_POINTER;
1842 }
1843 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1844 match builder.get_atom_velocity(index) {
1845 Ok(Some(v)) => unsafe {
1846 *out_xyz = v[0];
1847 *out_xyz.add(1) = v[1];
1848 *out_xyz.add(2) = v[2];
1849 *has_value = true;
1850 RKRStatus::RKR_STATUS_SUCCESS
1851 },
1852 Ok(None) => unsafe {
1853 *has_value = false;
1854 RKRStatus::RKR_STATUS_SUCCESS
1855 },
1856 Err(e) => map_builder_err(e),
1857 }
1858}
1859#[unsafe(no_mangle)]
1862pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1863 builder_handle: *const RKRConFrameBuilder,
1864 index: usize,
1865 out_xyz: *mut f64,
1866 has_value: *mut bool,
1867) -> RKRStatus {
1868 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1869 return RKRStatus::RKR_STATUS_NULL_POINTER;
1870 }
1871 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1872 match builder.get_atom_force(index) {
1873 Ok(Some(f)) => unsafe {
1874 *out_xyz = f[0];
1875 *out_xyz.add(1) = f[1];
1876 *out_xyz.add(2) = f[2];
1877 *has_value = true;
1878 RKRStatus::RKR_STATUS_SUCCESS
1879 },
1880 Ok(None) => unsafe {
1881 *has_value = false;
1882 RKRStatus::RKR_STATUS_SUCCESS
1883 },
1884 Err(e) => map_builder_err(e),
1885 }
1886}
1887#[unsafe(no_mangle)]
1893pub unsafe extern "C" fn rkr_frame_builder_get_atom_displacement(
1894 builder_handle: *const RKRConFrameBuilder,
1895 index: usize,
1896 out_xyz: *mut f64,
1897 has_value: *mut bool,
1898) -> RKRStatus {
1899 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1900 return RKRStatus::RKR_STATUS_NULL_POINTER;
1901 }
1902 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1903 match builder.get_atom_displacement(index) {
1904 Ok(Some(d)) => unsafe {
1905 *out_xyz = d[0];
1906 *out_xyz.add(1) = d[1];
1907 *out_xyz.add(2) = d[2];
1908 *has_value = true;
1909 RKRStatus::RKR_STATUS_SUCCESS
1910 },
1911 Ok(None) => unsafe {
1912 *has_value = false;
1913 RKRStatus::RKR_STATUS_SUCCESS
1914 },
1915 Err(e) => map_builder_err(e),
1916 }
1917}
1918#[unsafe(no_mangle)]
1924pub unsafe extern "C" fn rkr_frame_builder_get_atom_spread(
1925 builder_handle: *const RKRConFrameBuilder,
1926 index: usize,
1927 out_xyz: *mut f64,
1928 has_value: *mut bool,
1929) -> RKRStatus {
1930 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1931 return RKRStatus::RKR_STATUS_NULL_POINTER;
1932 }
1933 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1934 match builder.get_atom_spread(index) {
1935 Ok(Some(d)) => unsafe {
1936 *out_xyz = d[0];
1937 *out_xyz.add(1) = d[1];
1938 *out_xyz.add(2) = d[2];
1939 *has_value = true;
1940 RKRStatus::RKR_STATUS_SUCCESS
1941 },
1942 Ok(None) => unsafe {
1943 *has_value = false;
1944 RKRStatus::RKR_STATUS_SUCCESS
1945 },
1946 Err(e) => map_builder_err(e),
1947 }
1948}
1949#[unsafe(no_mangle)]
1955pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
1956 builder_handle: *const RKRConFrameBuilder,
1957 index: usize,
1958 out_value: *mut f64,
1959 has_value: *mut bool,
1960) -> RKRStatus {
1961 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
1962 return RKRStatus::RKR_STATUS_NULL_POINTER;
1963 }
1964 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1965 match builder.get_atom_energy(index) {
1966 Ok(Some(e)) => unsafe {
1967 *out_value = e;
1968 *has_value = true;
1969 RKRStatus::RKR_STATUS_SUCCESS
1970 },
1971 Ok(None) => unsafe {
1972 *has_value = false;
1973 RKRStatus::RKR_STATUS_SUCCESS
1974 },
1975 Err(e) => map_builder_err(e),
1976 }
1977}
1978#[unsafe(no_mangle)]
1982pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
1983 builder_handle: *const RKRConFrameBuilder,
1984 index: usize,
1985 out_mass: *mut f64,
1986) -> RKRStatus {
1987 if builder_handle.is_null() || out_mass.is_null() {
1988 return RKRStatus::RKR_STATUS_NULL_POINTER;
1989 }
1990 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1991 match builder.get_atom_mass(index) {
1992 Ok(m) => unsafe {
1993 *out_mass = m;
1994 RKRStatus::RKR_STATUS_SUCCESS
1995 },
1996 Err(e) => map_builder_err(e),
1997 }
1998}
1999pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
2022fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
2023 use crate::error::ParseError;
2024 match e {
2025 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
2026 RKRStatus::RKR_STATUS_DEVICE_MISMATCH
2027 }
2028 ParseError::ValidationError(ref msg)
2029 if msg.contains("no device allocator") || msg.contains("allocator") =>
2030 {
2031 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED
2032 }
2033 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2034 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
2035 }
2036}
2037pub mod rkr_dl_type_code {
2041 pub const RKR_DL_INT: u8 = 0;
2042 pub const RKR_DL_UINT: u8 = 1;
2043 pub const RKR_DL_FLOAT: u8 = 2;
2044 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
2045 pub const RKR_DL_BFLOAT: u8 = 4;
2046 pub const RKR_DL_COMPLEX: u8 = 5;
2047 pub const RKR_DL_BOOL: u8 = 6;
2048}
2049
2050pub mod rkr_dl_device_type {
2052 pub const RKR_DL_CPU: i32 = 1;
2053 pub const RKR_DL_CUDA: i32 = 2;
2054 pub const RKR_DL_CUDA_HOST: i32 = 3;
2055}
2056
2057#[repr(C)]
2061#[derive(Clone, Copy, Debug)]
2062pub struct RKRDLDataType {
2063 pub code: u8,
2065 pub bits: u8,
2067 pub lanes: u16,
2069}
2070
2071#[repr(C)]
2074#[derive(Clone, Copy, Debug)]
2075pub struct RKRDLDevice {
2076 pub device_type: i32,
2078 pub device_id: i32,
2079}
2080
2081#[repr(C)]
2095#[derive(Clone, Copy, Debug)]
2096pub struct RKRDlpackExportOptions {
2097 pub dtype: RKRDLDataType,
2099 pub device: RKRDLDevice,
2101}
2102
2103impl Default for RKRDlpackExportOptions {
2104 fn default() -> Self {
2105 Self {
2106 dtype: RKRDLDataType {
2107 code: rkr_dl_type_code::RKR_DL_FLOAT,
2108 bits: 64,
2109 lanes: 1,
2110 },
2111 device: RKRDLDevice {
2112 device_type: rkr_dl_device_type::RKR_DL_CPU,
2113 device_id: 0,
2114 },
2115 }
2116 }
2117}
2118
2119fn resolve_dlpack_opts(
2122 opts: *const RKRDlpackExportOptions,
2123) -> Result<RKRDlpackExportOptions, RKRStatus> {
2124 let o = if opts.is_null() {
2125 RKRDlpackExportOptions::default()
2126 } else {
2127 unsafe { *opts }
2128 };
2129 let dt = o.device.device_type;
2130 if dt == rkr_dl_device_type::RKR_DL_CPU {
2131 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
2133 #[cfg(not(feature = "cuda"))]
2134 {
2135 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
2136 }
2137 #[cfg(feature = "cuda")]
2138 {
2139 }
2141 } else {
2142 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
2143 }
2144 if o.dtype.lanes != 1 {
2145 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
2146 }
2147 Ok(o)
2148}
2149
2150fn finish_dlpack_tensor<E: std::fmt::Display>(
2151 result: Result<dlpk::DLPackTensor, E>,
2152 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2153) -> RKRStatus {
2154 match result {
2155 Ok(tensor) => {
2156 let raw = tensor.into_raw();
2157 unsafe {
2158 *out_tensor = raw.as_ptr();
2159 }
2160 RKRStatus::RKR_STATUS_SUCCESS
2161 }
2162 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
2163 "DLPack export failed: {e}"
2164 ))),
2165 }
2166}
2167
2168fn export_f64_slice_as_dlpack(
2171 data: &[f64],
2172 shape: &[usize],
2173 dtype: RKRDLDataType,
2174 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2175) -> RKRStatus {
2176 use rkr_dl_type_code::*;
2177 if dtype.lanes != 1 {
2178 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2179 }
2180 macro_rules! arc_export {
2181 ($ty:ty, $map:expr) => {{
2182 let v: Vec<$ty> = data.iter().map($map).collect();
2183 match *shape {
2184 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
2185 Ok(a) => dlpk::DLPackTensor::try_from(a),
2186 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2187 },
2188 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
2189 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2190 }
2191 }};
2192 }
2193 let tensor = match (dtype.code, dtype.bits) {
2194 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
2195 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
2196 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
2197 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
2198 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
2199 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
2200 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
2201 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
2202 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
2203 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
2204 (RKR_DL_BOOL, 8) => {
2205 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
2207 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
2208 }
2209 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2211 };
2212 finish_dlpack_tensor(tensor, out_tensor)
2213}
2214
2215fn export_owned_array2_dlpack_opts(
2216 arr: &ndarray::ArcArray2<f64>,
2217 opts: &RKRDlpackExportOptions,
2218 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2219) -> RKRStatus {
2220 let (r, c) = arr.dim();
2221 let flat: Vec<f64> = arr.iter().copied().collect();
2222 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2223 #[cfg(feature = "cuda")]
2224 {
2225 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2227 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2228 }
2229 return finish_dlpack_tensor(
2230 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2231 &[r, c],
2232 &flat,
2233 opts.device.device_id,
2234 ),
2235 out_tensor,
2236 );
2237 }
2238 #[cfg(not(feature = "cuda"))]
2239 {
2240 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2241 }
2242 }
2243 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
2244}
2245
2246fn export_owned_array1_f64_dlpack_opts(
2247 arr: &ndarray::ArcArray1<f64>,
2248 opts: &RKRDlpackExportOptions,
2249 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2250) -> RKRStatus {
2251 let flat = arr.to_vec();
2252 let n = flat.len();
2253 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2254 #[cfg(feature = "cuda")]
2255 {
2256 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2257 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2258 }
2259 return finish_dlpack_tensor(
2260 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2261 &[n],
2262 &flat,
2263 opts.device.device_id,
2264 ),
2265 out_tensor,
2266 );
2267 }
2268 #[cfg(not(feature = "cuda"))]
2269 {
2270 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2271 }
2272 }
2273 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
2274}
2275
2276fn export_owned_array1_u64_dlpack(
2277 arr: &ndarray::ArcArray1<u64>,
2278 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2279) -> RKRStatus {
2280 finish_dlpack_tensor(dlpk::DLPackTensor::try_from(arr.clone()), out_tensor)
2281}
2282
2283fn export_owned_array1_u64_dlpack_opts(
2285 arr: &ndarray::ArcArray1<u64>,
2286 opts: &RKRDlpackExportOptions,
2287 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2288) -> RKRStatus {
2289 use rkr_dl_type_code::*;
2290 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
2291 return export_owned_array1_u64_dlpack(arr, out_tensor);
2292 }
2293 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
2294 let n = as_f64.len();
2295 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
2296}
2297#[unsafe(no_mangle)]
2308pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
2309 builder_handle: *const RKRConFrameBuilder,
2310 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2311) -> RKRStatus {
2312 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2313}
2314
2315#[unsafe(no_mangle)]
2322pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
2323 builder_handle: *const RKRConFrameBuilder,
2324 opts: *const RKRDlpackExportOptions,
2325 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2326) -> RKRStatus {
2327 if builder_handle.is_null() || out_tensor.is_null() {
2328 return RKRStatus::RKR_STATUS_NULL_POINTER;
2329 }
2330 let o = match resolve_dlpack_opts(opts) {
2331 Ok(o) => o,
2332 Err(st) => return st,
2333 };
2334 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2335 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
2336}
2337
2338#[unsafe(no_mangle)]
2348pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
2349 builder_handle: *const RKRConFrameBuilder,
2350 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2351) -> RKRStatus {
2352 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2353}
2354
2355#[unsafe(no_mangle)]
2356pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
2357 builder_handle: *const RKRConFrameBuilder,
2358 opts: *const RKRDlpackExportOptions,
2359 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2360) -> RKRStatus {
2361 if builder_handle.is_null() || out_tensor.is_null() {
2362 return RKRStatus::RKR_STATUS_NULL_POINTER;
2363 }
2364 let o = match resolve_dlpack_opts(opts) {
2365 Ok(o) => o,
2366 Err(st) => return st,
2367 };
2368 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2369 if !builder.has_velocities_section() {
2370 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2371 }
2372 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
2373}
2374
2375#[unsafe(no_mangle)]
2384pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
2385 builder_handle: *const RKRConFrameBuilder,
2386 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2387) -> RKRStatus {
2388 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2389}
2390
2391#[unsafe(no_mangle)]
2392pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
2393 builder_handle: *const RKRConFrameBuilder,
2394 opts: *const RKRDlpackExportOptions,
2395 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2396) -> RKRStatus {
2397 if builder_handle.is_null() || out_tensor.is_null() {
2398 return RKRStatus::RKR_STATUS_NULL_POINTER;
2399 }
2400 let o = match resolve_dlpack_opts(opts) {
2401 Ok(o) => o,
2402 Err(st) => return st,
2403 };
2404 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2405 if !builder.has_forces_section() {
2406 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2407 }
2408 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
2409}
2410
2411#[unsafe(no_mangle)]
2420pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack(
2421 builder_handle: *const RKRConFrameBuilder,
2422 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2423) -> RKRStatus {
2424 unsafe {
2425 rkr_frame_builder_displacements_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2426 }
2427}
2428
2429#[unsafe(no_mangle)]
2436pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack_ex(
2437 builder_handle: *const RKRConFrameBuilder,
2438 opts: *const RKRDlpackExportOptions,
2439 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2440) -> RKRStatus {
2441 if builder_handle.is_null() || out_tensor.is_null() {
2442 return RKRStatus::RKR_STATUS_NULL_POINTER;
2443 }
2444 let o = match resolve_dlpack_opts(opts) {
2445 Ok(o) => o,
2446 Err(st) => return st,
2447 };
2448 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2449 if !builder.has_displacements_section() {
2450 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2451 }
2452 export_owned_array2_dlpack_opts(builder.displacements_2d_ref(), &o, out_tensor)
2453}
2454
2455#[unsafe(no_mangle)]
2464pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack(
2465 builder_handle: *const RKRConFrameBuilder,
2466 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2467) -> RKRStatus {
2468 unsafe { rkr_frame_builder_spreads_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2469}
2470
2471#[unsafe(no_mangle)]
2478pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack_ex(
2479 builder_handle: *const RKRConFrameBuilder,
2480 opts: *const RKRDlpackExportOptions,
2481 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2482) -> RKRStatus {
2483 if builder_handle.is_null() || out_tensor.is_null() {
2484 return RKRStatus::RKR_STATUS_NULL_POINTER;
2485 }
2486 let o = match resolve_dlpack_opts(opts) {
2487 Ok(o) => o,
2488 Err(st) => return st,
2489 };
2490 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2491 if !builder.has_spreads_section() {
2492 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2493 }
2494 export_owned_array2_dlpack_opts(builder.spreads_2d_ref(), &o, out_tensor)
2495}
2496
2497#[unsafe(no_mangle)]
2506pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
2507 builder_handle: *const RKRConFrameBuilder,
2508 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2509) -> RKRStatus {
2510 unsafe {
2511 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2512 }
2513}
2514
2515#[unsafe(no_mangle)]
2516pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
2517 builder_handle: *const RKRConFrameBuilder,
2518 opts: *const RKRDlpackExportOptions,
2519 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2520) -> RKRStatus {
2521 if builder_handle.is_null() || out_tensor.is_null() {
2522 return RKRStatus::RKR_STATUS_NULL_POINTER;
2523 }
2524 let o = match resolve_dlpack_opts(opts) {
2525 Ok(o) => o,
2526 Err(st) => return st,
2527 };
2528 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2529 if !builder.has_energies_section() {
2530 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2531 }
2532 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
2533}
2534
2535#[unsafe(no_mangle)]
2541pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
2542 builder_handle: *const RKRConFrameBuilder,
2543 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2544) -> RKRStatus {
2545 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2546}
2547
2548#[unsafe(no_mangle)]
2549pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
2550 builder_handle: *const RKRConFrameBuilder,
2551 opts: *const RKRDlpackExportOptions,
2552 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2553) -> RKRStatus {
2554 if builder_handle.is_null() || out_tensor.is_null() {
2555 return RKRStatus::RKR_STATUS_NULL_POINTER;
2556 }
2557 let o = match resolve_dlpack_opts(opts) {
2558 Ok(o) => o,
2559 Err(st) => return st,
2560 };
2561 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2562 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
2563}
2564#[unsafe(no_mangle)]
2570pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
2571 builder_handle: *const RKRConFrameBuilder,
2572 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2573) -> RKRStatus {
2574 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2575}
2576
2577#[unsafe(no_mangle)]
2578pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2579 builder_handle: *const RKRConFrameBuilder,
2580 opts: *const RKRDlpackExportOptions,
2581 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2582) -> RKRStatus {
2583 if builder_handle.is_null() || out_tensor.is_null() {
2584 return RKRStatus::RKR_STATUS_NULL_POINTER;
2585 }
2586 let o = match resolve_dlpack_opts(opts) {
2587 Ok(o) => o,
2588 Err(st) => return st,
2589 };
2590 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2591 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2592}
2593#[unsafe(no_mangle)]
2617pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2618 builder_handle: *mut RKRConFrameBuilder,
2619) -> *mut f64 {
2620 if builder_handle.is_null() {
2621 return std::ptr::null_mut();
2622 }
2623 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2624 builder
2625 .positions_view_mut()
2626 .as_slice_memory_order_mut()
2627 .map(|s| s.as_mut_ptr())
2628 .unwrap_or(std::ptr::null_mut())
2629}
2630#[unsafe(no_mangle)]
2637pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
2638 builder_handle: *mut RKRConFrameBuilder,
2639) -> *mut f64 {
2640 if builder_handle.is_null() {
2641 return std::ptr::null_mut();
2642 }
2643 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2644 if !builder.has_velocities_section() {
2645 return std::ptr::null_mut();
2646 }
2647 let slice = builder.velocities_mut();
2648 if slice.is_empty() {
2649 std::ptr::null_mut()
2650 } else {
2651 slice.as_mut_ptr()
2652 }
2653}
2654#[unsafe(no_mangle)]
2660pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2661 builder_handle: *mut RKRConFrameBuilder,
2662) -> *mut f64 {
2663 if builder_handle.is_null() {
2664 return std::ptr::null_mut();
2665 }
2666 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2667 if !builder.has_forces_section() {
2668 return std::ptr::null_mut();
2669 }
2670 let slice = builder.forces_mut();
2671 if slice.is_empty() {
2672 std::ptr::null_mut()
2673 } else {
2674 slice.as_mut_ptr()
2675 }
2676}
2677#[unsafe(no_mangle)]
2683pub unsafe extern "C" fn rkr_frame_builder_displacements_data(
2684 builder_handle: *mut RKRConFrameBuilder,
2685) -> *mut f64 {
2686 if builder_handle.is_null() {
2687 return std::ptr::null_mut();
2688 }
2689 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2690 if !builder.has_displacements_section() {
2691 return std::ptr::null_mut();
2692 }
2693 let slice = builder.displacements_mut();
2694 if slice.is_empty() {
2695 std::ptr::null_mut()
2696 } else {
2697 slice.as_mut_ptr()
2698 }
2699}
2700#[unsafe(no_mangle)]
2706pub unsafe extern "C" fn rkr_frame_builder_spreads_data(
2707 builder_handle: *mut RKRConFrameBuilder,
2708) -> *mut f64 {
2709 if builder_handle.is_null() {
2710 return std::ptr::null_mut();
2711 }
2712 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2713 if !builder.has_spreads_section() {
2714 return std::ptr::null_mut();
2715 }
2716 let slice = builder.spreads_mut();
2717 if slice.is_empty() {
2718 std::ptr::null_mut()
2719 } else {
2720 slice.as_mut_ptr()
2721 }
2722}
2723#[unsafe(no_mangle)]
2729pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2730 builder_handle: *mut RKRConFrameBuilder,
2731) -> *mut f64 {
2732 if builder_handle.is_null() {
2733 return std::ptr::null_mut();
2734 }
2735 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2736 if !builder.has_energies_section() {
2737 return std::ptr::null_mut();
2738 }
2739 let slice = builder.atom_energies_mut();
2740 if slice.is_empty() {
2741 std::ptr::null_mut()
2742 } else {
2743 slice.as_mut_ptr()
2744 }
2745}
2746#[unsafe(no_mangle)]
2751pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2752 builder_handle: *mut RKRConFrameBuilder,
2753) -> *mut f64 {
2754 if builder_handle.is_null() {
2755 return std::ptr::null_mut();
2756 }
2757 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2758 let slice = builder.masses_mut();
2759 if slice.is_empty() {
2760 std::ptr::null_mut()
2761 } else {
2762 slice.as_mut_ptr()
2763 }
2764}
2765#[unsafe(no_mangle)]
2770pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2771 builder_handle: *const RKRConFrameBuilder,
2772) -> *const u64 {
2773 if builder_handle.is_null() {
2774 return std::ptr::null();
2775 }
2776 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2777 let slice = builder.atom_ids();
2778 if slice.is_empty() {
2779 std::ptr::null()
2780 } else {
2781 slice.as_ptr()
2782 }
2783}
2784#[unsafe(no_mangle)]
2796#[allow(clippy::too_many_arguments)]
2797pub unsafe extern "C" fn rkr_frame_add_atom_full(
2798 builder_handle: *mut RKRConFrameBuilder,
2799 symbol: *const c_char,
2800 x: f64,
2801 y: f64,
2802 z: f64,
2803 fixed_x: bool,
2804 fixed_y: bool,
2805 fixed_z: bool,
2806 atom_id: u64,
2807 mass: f64,
2808 velocity: *const f64,
2809 force: *const f64,
2810) -> RKRStatus {
2811 let velocity = if velocity.is_null() {
2812 None
2813 } else {
2814 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
2815 };
2816 let force = if force.is_null() {
2817 None
2818 } else {
2819 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
2820 };
2821 unsafe {
2822 add_builder_atom(
2823 builder_handle,
2824 symbol,
2825 x,
2826 y,
2827 z,
2828 [fixed_x, fixed_y, fixed_z],
2829 atom_id,
2830 mass,
2831 velocity,
2832 force,
2833 )
2834 }
2835}
2836#[unsafe(no_mangle)]
2852pub unsafe extern "C" fn rkr_frame_new(
2853 cell: *const f64,
2854 angles: *const f64,
2855 prebox0: *const c_char,
2856 prebox1: *const c_char,
2857 postbox0: *const c_char,
2858 postbox1: *const c_char,
2859) -> *mut RKRConFrameBuilder {
2860 if cell.is_null() || angles.is_null() {
2861 return ptr::null_mut();
2862 }
2863 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
2864 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
2865 let get_str = |p: *const c_char| -> String {
2866 if p.is_null() {
2867 String::new()
2868 } else {
2869 unsafe { CStr::from_ptr(p) }
2870 .to_str()
2871 .unwrap_or("")
2872 .to_string()
2873 }
2874 };
2875 let _ = get_str(prebox1);
2878 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
2879 builder
2880 .prebox_header(get_str(prebox0))
2881 .postbox_header([get_str(postbox0), get_str(postbox1)]);
2882 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
2883}
2884#[unsafe(no_mangle)]
2890pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
2891 builder_handle: *mut RKRConFrameBuilder,
2892 metadata_json: *const c_char,
2893) -> RKRStatus {
2894 if builder_handle.is_null() || metadata_json.is_null() {
2895 return RKRStatus::RKR_STATUS_NULL_POINTER;
2896 }
2897 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2898 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
2899 Ok(s) => s,
2900 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2901 };
2902 match builder.set_metadata_json(metadata_json) {
2903 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
2904 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
2905 }
2906}
2907#[unsafe(no_mangle)]
2913pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
2914 builder_handle: *mut RKRConFrameBuilder,
2915 key: *const c_char,
2916 value: f64,
2917) -> RKRStatus {
2918 if builder_handle.is_null() || key.is_null() {
2919 return RKRStatus::RKR_STATUS_NULL_POINTER;
2920 }
2921 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2922 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2923 Ok(s) => s,
2924 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2925 };
2926 builder.set_scalar_metadata(key, value);
2927 RKRStatus::RKR_STATUS_SUCCESS
2928}
2929#[unsafe(no_mangle)]
2935pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
2936 builder_handle: *mut RKRConFrameBuilder,
2937 key: *const c_char,
2938 value: *const c_char,
2939) -> RKRStatus {
2940 if builder_handle.is_null() || key.is_null() || value.is_null() {
2941 return RKRStatus::RKR_STATUS_NULL_POINTER;
2942 }
2943 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2944 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2945 Ok(s) => s,
2946 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2947 };
2948 let value = match unsafe { CStr::from_ptr(value).to_str() } {
2949 Ok(s) => s,
2950 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2951 };
2952 builder.set_string_metadata(key, value);
2953 RKRStatus::RKR_STATUS_SUCCESS
2954}
2955#[unsafe(no_mangle)]
2961pub unsafe extern "C" fn rkr_frame_builder_set_energy(
2962 builder_handle: *mut RKRConFrameBuilder,
2963 energy: f64,
2964) -> RKRStatus {
2965 if builder_handle.is_null() {
2966 return RKRStatus::RKR_STATUS_NULL_POINTER;
2967 }
2968 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2969 builder.set_energy(energy);
2970 RKRStatus::RKR_STATUS_SUCCESS
2971}
2972#[unsafe(no_mangle)]
2978pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
2979 builder_handle: *mut RKRConFrameBuilder,
2980 idx: u64,
2981) -> RKRStatus {
2982 if builder_handle.is_null() {
2983 return RKRStatus::RKR_STATUS_NULL_POINTER;
2984 }
2985 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2986 builder.set_frame_index(idx);
2987 RKRStatus::RKR_STATUS_SUCCESS
2988}
2989#[unsafe(no_mangle)]
2995pub unsafe extern "C" fn rkr_frame_builder_set_time(
2996 builder_handle: *mut RKRConFrameBuilder,
2997 time: f64,
2998) -> RKRStatus {
2999 if builder_handle.is_null() {
3000 return RKRStatus::RKR_STATUS_NULL_POINTER;
3001 }
3002 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3003 builder.set_time(time);
3004 RKRStatus::RKR_STATUS_SUCCESS
3005}
3006#[unsafe(no_mangle)]
3012pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
3013 builder_handle: *mut RKRConFrameBuilder,
3014 dt: f64,
3015) -> RKRStatus {
3016 if builder_handle.is_null() {
3017 return RKRStatus::RKR_STATUS_NULL_POINTER;
3018 }
3019 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3020 builder.set_timestep(dt);
3021 RKRStatus::RKR_STATUS_SUCCESS
3022}
3023#[unsafe(no_mangle)]
3029pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
3030 builder_handle: *mut RKRConFrameBuilder,
3031 bead: u64,
3032) -> RKRStatus {
3033 if builder_handle.is_null() {
3034 return RKRStatus::RKR_STATUS_NULL_POINTER;
3035 }
3036 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3037 builder.set_neb_bead(bead);
3038 RKRStatus::RKR_STATUS_SUCCESS
3039}
3040#[unsafe(no_mangle)]
3046pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
3047 builder_handle: *mut RKRConFrameBuilder,
3048 band: u64,
3049) -> RKRStatus {
3050 if builder_handle.is_null() {
3051 return RKRStatus::RKR_STATUS_NULL_POINTER;
3052 }
3053 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3054 builder.set_neb_band(band);
3055 RKRStatus::RKR_STATUS_SUCCESS
3056}
3057#[unsafe(no_mangle)]
3074pub unsafe extern "C" fn rkr_frame_add_atom(
3075 builder_handle: *mut RKRConFrameBuilder,
3076 symbol: *const c_char,
3077 x: f64,
3078 y: f64,
3079 z: f64,
3080 is_fixed: bool,
3081 atom_id: u64,
3082 mass: f64,
3083) -> RKRStatus {
3084 unsafe {
3085 add_builder_atom(
3086 builder_handle,
3087 symbol,
3088 x,
3089 y,
3090 z,
3091 [is_fixed; 3],
3092 atom_id,
3093 mass,
3094 None,
3095 None,
3096 )
3097 }
3098}
3099#[unsafe(no_mangle)]
3106pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
3107 builder_handle: *mut RKRConFrameBuilder,
3108 symbol: *const c_char,
3109 x: f64,
3110 y: f64,
3111 z: f64,
3112 fixed_x: bool,
3113 fixed_y: bool,
3114 fixed_z: bool,
3115 atom_id: u64,
3116 mass: f64,
3117) -> RKRStatus {
3118 unsafe {
3119 add_builder_atom(
3120 builder_handle,
3121 symbol,
3122 x,
3123 y,
3124 z,
3125 [fixed_x, fixed_y, fixed_z],
3126 atom_id,
3127 mass,
3128 None,
3129 None,
3130 )
3131 }
3132}
3133#[unsafe(no_mangle)]
3140pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
3141 builder_handle: *mut RKRConFrameBuilder,
3142 symbol: *const c_char,
3143 x: f64,
3144 y: f64,
3145 z: f64,
3146 is_fixed: bool,
3147 atom_id: u64,
3148 mass: f64,
3149 vx: f64,
3150 vy: f64,
3151 vz: f64,
3152) -> RKRStatus {
3153 unsafe {
3154 add_builder_atom(
3155 builder_handle,
3156 symbol,
3157 x,
3158 y,
3159 z,
3160 [is_fixed; 3],
3161 atom_id,
3162 mass,
3163 Some([vx, vy, vz]),
3164 None,
3165 )
3166 }
3167}
3168#[unsafe(no_mangle)]
3175pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
3176 builder_handle: *mut RKRConFrameBuilder,
3177 symbol: *const c_char,
3178 x: f64,
3179 y: f64,
3180 z: f64,
3181 fixed_x: bool,
3182 fixed_y: bool,
3183 fixed_z: bool,
3184 atom_id: u64,
3185 mass: f64,
3186 vx: f64,
3187 vy: f64,
3188 vz: f64,
3189) -> RKRStatus {
3190 unsafe {
3191 add_builder_atom(
3192 builder_handle,
3193 symbol,
3194 x,
3195 y,
3196 z,
3197 [fixed_x, fixed_y, fixed_z],
3198 atom_id,
3199 mass,
3200 Some([vx, vy, vz]),
3201 None,
3202 )
3203 }
3204}
3205#[unsafe(no_mangle)]
3212pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
3213 builder_handle: *mut RKRConFrameBuilder,
3214 symbol: *const c_char,
3215 x: f64,
3216 y: f64,
3217 z: f64,
3218 is_fixed: bool,
3219 atom_id: u64,
3220 mass: f64,
3221 fx: f64,
3222 fy: f64,
3223 fz: f64,
3224) -> RKRStatus {
3225 unsafe {
3226 add_builder_atom(
3227 builder_handle,
3228 symbol,
3229 x,
3230 y,
3231 z,
3232 [is_fixed; 3],
3233 atom_id,
3234 mass,
3235 None,
3236 Some([fx, fy, fz]),
3237 )
3238 }
3239}
3240#[unsafe(no_mangle)]
3247pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
3248 builder_handle: *mut RKRConFrameBuilder,
3249 symbol: *const c_char,
3250 x: f64,
3251 y: f64,
3252 z: f64,
3253 fixed_x: bool,
3254 fixed_y: bool,
3255 fixed_z: bool,
3256 atom_id: u64,
3257 mass: f64,
3258 fx: f64,
3259 fy: f64,
3260 fz: f64,
3261) -> RKRStatus {
3262 unsafe {
3263 add_builder_atom(
3264 builder_handle,
3265 symbol,
3266 x,
3267 y,
3268 z,
3269 [fixed_x, fixed_y, fixed_z],
3270 atom_id,
3271 mass,
3272 None,
3273 Some([fx, fy, fz]),
3274 )
3275 }
3276}
3277#[unsafe(no_mangle)]
3284pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
3285 builder_handle: *mut RKRConFrameBuilder,
3286 symbol: *const c_char,
3287 x: f64,
3288 y: f64,
3289 z: f64,
3290 is_fixed: bool,
3291 atom_id: u64,
3292 mass: f64,
3293 vx: f64,
3294 vy: f64,
3295 vz: f64,
3296 fx: f64,
3297 fy: f64,
3298 fz: f64,
3299) -> RKRStatus {
3300 unsafe {
3301 add_builder_atom(
3302 builder_handle,
3303 symbol,
3304 x,
3305 y,
3306 z,
3307 [is_fixed; 3],
3308 atom_id,
3309 mass,
3310 Some([vx, vy, vz]),
3311 Some([fx, fy, fz]),
3312 )
3313 }
3314}
3315#[unsafe(no_mangle)]
3322pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
3323 builder_handle: *mut RKRConFrameBuilder,
3324 symbol: *const c_char,
3325 x: f64,
3326 y: f64,
3327 z: f64,
3328 fixed_x: bool,
3329 fixed_y: bool,
3330 fixed_z: bool,
3331 atom_id: u64,
3332 mass: f64,
3333 vx: f64,
3334 vy: f64,
3335 vz: f64,
3336 fx: f64,
3337 fy: f64,
3338 fz: f64,
3339) -> RKRStatus {
3340 unsafe {
3341 add_builder_atom(
3342 builder_handle,
3343 symbol,
3344 x,
3345 y,
3346 z,
3347 [fixed_x, fixed_y, fixed_z],
3348 atom_id,
3349 mass,
3350 Some([vx, vy, vz]),
3351 Some([fx, fy, fz]),
3352 )
3353 }
3354}
3355#[unsafe(no_mangle)]
3364pub unsafe extern "C" fn rkr_frame_builder_build(
3365 builder_handle: *mut RKRConFrameBuilder,
3366) -> *mut RKRConFrame {
3367 if builder_handle.is_null() {
3368 return ptr::null_mut();
3369 }
3370 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3371 match builder.build() {
3372 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3373 Err(_) => ptr::null_mut(),
3374 }
3375}
3376#[unsafe(no_mangle)]
3381pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
3382 if !builder_handle.is_null() {
3383 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3384 }
3385}
3386#[unsafe(no_mangle)]
3403pub unsafe extern "C" fn rkr_frame_builder_clone(
3404 builder_handle: *const RKRConFrameBuilder,
3405) -> *mut RKRConFrameBuilder {
3406 if builder_handle.is_null() {
3407 return std::ptr::null_mut();
3408 }
3409 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3410 let cloned = builder.clone();
3411 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
3412}
3413#[unsafe(no_mangle)]
3419pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3420 let filename = match unsafe { cstr_path(filename_c) } {
3421 Some(s) => s,
3422 None => return ptr::null_mut(),
3423 };
3424 match crate::compression::gzip_writer(Path::new(filename)) {
3425 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3426 Err(_) => ptr::null_mut(),
3427 }
3428}
3429#[unsafe(no_mangle)]
3436pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
3437 filename_c: *const c_char,
3438 precision: u8,
3439) -> *mut RKRConFrameWriter {
3440 let filename = match unsafe { cstr_path(filename_c) } {
3441 Some(s) => s,
3442 None => return ptr::null_mut(),
3443 };
3444 match crate::compression::gzip_writer(Path::new(filename)) {
3445 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3446 Err(_) => ptr::null_mut(),
3447 }
3448}
3449#[cfg(feature = "zstd")]
3459#[unsafe(no_mangle)]
3460pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3461 let filename = match unsafe { cstr_path(filename_c) } {
3462 Some(s) => s,
3463 None => return ptr::null_mut(),
3464 };
3465 match crate::compression::zstd_writer(Path::new(filename)) {
3466 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3467 Err(_) => ptr::null_mut(),
3468 }
3469}
3470#[cfg(feature = "zstd")]
3481#[unsafe(no_mangle)]
3482pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3483 filename_c: *const c_char,
3484 precision: u8,
3485) -> *mut RKRConFrameWriter {
3486 let filename = match unsafe { cstr_path(filename_c) } {
3487 Some(s) => s,
3488 None => return ptr::null_mut(),
3489 };
3490 match crate::compression::zstd_writer(Path::new(filename)) {
3491 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3492 Err(_) => ptr::null_mut(),
3493 }
3494}
3495#[unsafe(no_mangle)]
3507pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
3508 if filename_c.is_null() {
3509 return ptr::null_mut();
3510 }
3511 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3512 Ok(s) => s,
3513 Err(_) => return ptr::null_mut(),
3514 };
3515 match iterators::read_first_frame(Path::new(filename)) {
3516 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3517 Err(_) => ptr::null_mut(),
3518 }
3519}
3520#[unsafe(no_mangle)]
3529pub unsafe extern "C" fn rkr_read_all_frames(
3530 filename_c: *const c_char,
3531 num_frames: *mut usize,
3532) -> *mut *mut RKRConFrame {
3533 if filename_c.is_null() || num_frames.is_null() {
3534 return ptr::null_mut();
3535 }
3536 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3537 Ok(s) => s,
3538 Err(_) => return ptr::null_mut(),
3539 };
3540 match iterators::read_all_frames(Path::new(filename)) {
3541 Ok(frames) => pack_frame_handles(frames, num_frames),
3542 Err(_) => ptr::null_mut(),
3543 }
3544}
3545
3546#[unsafe(no_mangle)]
3556pub unsafe extern "C" fn rkr_read_all_frames_n_threads(
3557 filename_c: *const c_char,
3558 num_frames: *mut usize,
3559 n_threads: usize,
3560) -> *mut *mut RKRConFrame {
3561 if filename_c.is_null() || num_frames.is_null() {
3562 return ptr::null_mut();
3563 }
3564 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3565 Ok(s) => s,
3566 Err(_) => return ptr::null_mut(),
3567 };
3568 let threads = if n_threads == 0 {
3569 None
3570 } else {
3571 Some(n_threads)
3572 };
3573 match iterators::read_all_frames_with_threads(Path::new(filename), threads) {
3574 Ok(frames) => pack_frame_handles(frames, num_frames),
3575 Err(_) => ptr::null_mut(),
3576 }
3577}
3578
3579fn pack_frame_handles(frames: Vec<ConFrame>, num_frames: *mut usize) -> *mut *mut RKRConFrame {
3581 let count = frames.len();
3582 let mut handles: Vec<*mut RKRConFrame> = frames
3583 .into_iter()
3584 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3585 .collect();
3586 handles.shrink_to_fit();
3587 debug_assert_eq!(handles.len(), handles.capacity());
3588 let ptr = handles.as_mut_ptr();
3589 std::mem::forget(handles);
3590 unsafe { *num_frames = count };
3591 ptr
3592}
3593#[unsafe(no_mangle)]
3599pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3600 if frames.is_null() {
3601 return;
3602 }
3603 unsafe {
3604 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3605 for handle in handles {
3606 if !handle.is_null() {
3607 let _ = Box::from_raw(handle as *mut ConFrame);
3608 }
3609 }
3610 }
3611}
3612#[unsafe(no_mangle)]
3618pub unsafe extern "C" fn free_rkr_frame_ptr_array(
3619 frames: *mut *mut RKRConFrame,
3620 num_frames: usize,
3621) {
3622 if frames.is_null() {
3623 return;
3624 }
3625 unsafe {
3626 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3627 }
3628}
3629
3630#[cfg(feature = "metatensor")]
3645#[unsafe(no_mangle)]
3646pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3647 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3648}
3649#[cfg(feature = "metatensor")]
3651#[unsafe(no_mangle)]
3652pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3653 frame_handle: *const RKRConFrame,
3654 out_block: *mut *mut metatensor::c_api::mts_block_t,
3655) -> RKRStatus {
3656 if frame_handle.is_null() || out_block.is_null() {
3657 return RKRStatus::RKR_STATUS_NULL_POINTER;
3658 }
3659 unsafe { *out_block = std::ptr::null_mut() };
3660 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3661 return RKRStatus::RKR_STATUS_NULL_POINTER;
3662 };
3663 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3664 crate::metatensor_export::frame_positions_block(frame)
3665 })) {
3666 Ok(Ok(b)) => {
3667 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3668 RKRStatus::RKR_STATUS_SUCCESS
3669 }
3670 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3671 }
3672}
3673#[cfg(feature = "metatensor")]
3674#[unsafe(no_mangle)]
3675pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3676 frame_handle: *const RKRConFrame,
3677 out_block: *mut *mut metatensor::c_api::mts_block_t,
3678) -> RKRStatus {
3679 if frame_handle.is_null() || out_block.is_null() {
3680 return RKRStatus::RKR_STATUS_NULL_POINTER;
3681 }
3682 unsafe { *out_block = std::ptr::null_mut() };
3683 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3684 return RKRStatus::RKR_STATUS_NULL_POINTER;
3685 };
3686 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3687 crate::metatensor_export::frame_velocities_block(frame)
3688 })) {
3689 Ok(Ok(Some(b))) => {
3690 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3691 RKRStatus::RKR_STATUS_SUCCESS
3692 }
3693 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3694 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3695 }
3696}
3697#[cfg(feature = "metatensor")]
3698#[unsafe(no_mangle)]
3699pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3700 frame_handle: *const RKRConFrame,
3701 out_block: *mut *mut metatensor::c_api::mts_block_t,
3702) -> RKRStatus {
3703 if frame_handle.is_null() || out_block.is_null() {
3704 return RKRStatus::RKR_STATUS_NULL_POINTER;
3705 }
3706 unsafe { *out_block = std::ptr::null_mut() };
3707 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3708 return RKRStatus::RKR_STATUS_NULL_POINTER;
3709 };
3710 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3711 crate::metatensor_export::frame_forces_block(frame)
3712 })) {
3713 Ok(Ok(Some(b))) => {
3714 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3715 RKRStatus::RKR_STATUS_SUCCESS
3716 }
3717 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3718 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3719 }
3720}
3721#[cfg(feature = "metatensor")]
3722#[unsafe(no_mangle)]
3723pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3724 frame_handle: *const RKRConFrame,
3725 out_block: *mut *mut metatensor::c_api::mts_block_t,
3726) -> RKRStatus {
3727 if frame_handle.is_null() || out_block.is_null() {
3728 return RKRStatus::RKR_STATUS_NULL_POINTER;
3729 }
3730 unsafe { *out_block = std::ptr::null_mut() };
3731 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3732 return RKRStatus::RKR_STATUS_NULL_POINTER;
3733 };
3734 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3735 crate::metatensor_export::frame_energies_block(frame)
3736 })) {
3737 Ok(Ok(Some(b))) => {
3738 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3739 RKRStatus::RKR_STATUS_SUCCESS
3740 }
3741 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3742 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3743 }
3744}
3745#[cfg(not(feature = "zstd"))]
3746#[unsafe(no_mangle)]
3747pub unsafe extern "C" fn create_writer_zstd_c(
3748 _filename_c: *const c_char,
3749) -> *mut RKRConFrameWriter {
3750 ptr::null_mut()
3751}
3752#[cfg(not(feature = "zstd"))]
3753#[unsafe(no_mangle)]
3754pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3755 _filename_c: *const c_char,
3756 _precision: u8,
3757) -> *mut RKRConFrameWriter {
3758 ptr::null_mut()
3759}
3760#[cfg(not(feature = "metatensor"))]
3764pub struct mts_block_t {
3767 _private: [u8; 0],
3768}
3769#[cfg(not(feature = "metatensor"))]
3770#[unsafe(no_mangle)]
3771pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3772#[cfg(not(feature = "metatensor"))]
3773#[unsafe(no_mangle)]
3774pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3775 _frame_handle: *const RKRConFrame,
3776 out_block: *mut *mut mts_block_t,
3777) -> RKRStatus {
3778 if !out_block.is_null() {
3779 unsafe { *out_block = std::ptr::null_mut() };
3780 }
3781 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3782}
3783#[cfg(not(feature = "metatensor"))]
3784#[unsafe(no_mangle)]
3785pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3786 _frame_handle: *const RKRConFrame,
3787 out_block: *mut *mut mts_block_t,
3788) -> RKRStatus {
3789 if !out_block.is_null() {
3790 unsafe { *out_block = std::ptr::null_mut() };
3791 }
3792 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3793}
3794#[cfg(not(feature = "metatensor"))]
3795#[unsafe(no_mangle)]
3796pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3797 _frame_handle: *const RKRConFrame,
3798 out_block: *mut *mut mts_block_t,
3799) -> RKRStatus {
3800 if !out_block.is_null() {
3801 unsafe { *out_block = std::ptr::null_mut() };
3802 }
3803 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3804}
3805#[cfg(not(feature = "metatensor"))]
3806#[unsafe(no_mangle)]
3807pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3808 _frame_handle: *const RKRConFrame,
3809 out_block: *mut *mut mts_block_t,
3810) -> RKRStatus {
3811 if !out_block.is_null() {
3812 unsafe { *out_block = std::ptr::null_mut() };
3813 }
3814 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3815}
3816#[unsafe(no_mangle)]
3819pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3820 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3821 return 0;
3822 };
3823 frame.atom_data.len()
3824}
3825#[unsafe(no_mangle)]
3827pub unsafe extern "C" fn rkr_frame_xyz_view(
3828 frame_handle: *const RKRConFrame,
3829 out: *mut RKRArrayView,
3830) -> RKRStatus {
3831 fill_array2_view(frame_handle, out, |f| &f.positions, false)
3832}
3833
3834#[unsafe(no_mangle)]
3836pub unsafe extern "C" fn rkr_frame_velocities_view(
3837 frame_handle: *const RKRConFrame,
3838 out: *mut RKRArrayView,
3839) -> RKRStatus {
3840 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
3841}
3842
3843#[unsafe(no_mangle)]
3845pub unsafe extern "C" fn rkr_frame_forces_view(
3846 frame_handle: *const RKRConFrame,
3847 out: *mut RKRArrayView,
3848) -> RKRStatus {
3849 fill_array2_view(frame_handle, out, |f| &f.forces, true)
3850}
3851
3852#[unsafe(no_mangle)]
3858pub unsafe extern "C" fn rkr_frame_has_displacements(frame_handle: *const RKRConFrame) -> bool {
3859 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
3860 Some(f) => f.has_displacements(),
3861 None => false,
3862 }
3863}
3864
3865#[unsafe(no_mangle)]
3870pub unsafe extern "C" fn rkr_frame_displacements_view(
3871 frame_handle: *const RKRConFrame,
3872 out: *mut RKRArrayView,
3873) -> RKRStatus {
3874 fill_array2_view(frame_handle, out, |f| &f.displacements, true)
3875}
3876
3877#[unsafe(no_mangle)]
3883pub unsafe extern "C" fn rkr_frame_has_spreads(frame_handle: *const RKRConFrame) -> bool {
3884 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
3885 Some(f) => f.has_spreads(),
3886 None => false,
3887 }
3888}
3889
3890#[unsafe(no_mangle)]
3895pub unsafe extern "C" fn rkr_frame_spreads_view(
3896 frame_handle: *const RKRConFrame,
3897 out: *mut RKRArrayView,
3898) -> RKRStatus {
3899 fill_array2_view(frame_handle, out, |f| &f.spreads, true)
3900}
3901
3902#[unsafe(no_mangle)]
3904pub unsafe extern "C" fn rkr_frame_energies_view(
3905 frame_handle: *const RKRConFrame,
3906 out: *mut RKRArrayView,
3907) -> RKRStatus {
3908 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
3909}
3910
3911#[unsafe(no_mangle)]
3913pub unsafe extern "C" fn rkr_frame_masses_view(
3914 frame_handle: *const RKRConFrame,
3915 out: *mut RKRArrayView,
3916) -> RKRStatus {
3917 fill_array1_view(frame_handle, out, |f| &f.masses, false)
3918}
3919
3920#[unsafe(no_mangle)]
3922pub unsafe extern "C" fn rkr_frame_atom_ids_view(
3923 frame_handle: *const RKRConFrame,
3924 out: *mut RKRArrayView,
3925) -> RKRStatus {
3926 if frame_handle.is_null() || out.is_null() {
3927 return RKRStatus::RKR_STATUS_NULL_POINTER;
3928 }
3929 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3930 return RKRStatus::RKR_STATUS_NULL_POINTER;
3931 };
3932 let slice = frame.atom_ids.as_slice_memory_order();
3933 unsafe {
3934 *out = RKRArrayView {
3935 data: slice
3936 .map(|s| s.as_ptr() as *const std::ffi::c_void)
3937 .unwrap_or(std::ptr::null()),
3938 n: frame.atom_ids.len(),
3939 cols: 1,
3940 dtype_code: 1, dtype_bits: 64,
3942 };
3943 }
3944 RKRStatus::RKR_STATUS_SUCCESS
3945}
3946
3947#[unsafe(no_mangle)]
3950pub unsafe extern "C" fn rkr_frame_xyz_f64(
3951 frame_handle: *const RKRConFrame,
3952 n: *mut usize,
3953) -> *const f64 {
3954 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
3955}
3956
3957#[unsafe(no_mangle)]
3959pub unsafe extern "C" fn rkr_frame_velocities_f64(
3960 frame_handle: *const RKRConFrame,
3961 n: *mut usize,
3962) -> *const f64 {
3963 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
3964}
3965
3966#[unsafe(no_mangle)]
3968pub unsafe extern "C" fn rkr_frame_forces_f64(
3969 frame_handle: *const RKRConFrame,
3970 n: *mut usize,
3971) -> *const f64 {
3972 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
3973}
3974
3975#[unsafe(no_mangle)]
3980pub unsafe extern "C" fn rkr_frame_displacements_f64(
3981 frame_handle: *const RKRConFrame,
3982 n: *mut usize,
3983) -> *const f64 {
3984 f64_col_ptr(frame_handle, n, |f| f.displacements.f64_slice())
3985}
3986
3987#[unsafe(no_mangle)]
3992pub unsafe extern "C" fn rkr_frame_spreads_f64(
3993 frame_handle: *const RKRConFrame,
3994 n: *mut usize,
3995) -> *const f64 {
3996 f64_col_ptr(frame_handle, n, |f| f.spreads.f64_slice())
3997}
3998
3999fn fill_array2_view(
4000 frame_handle: *const RKRConFrame,
4001 out: *mut RKRArrayView,
4002 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4003 section: bool,
4004) -> RKRStatus {
4005 if frame_handle.is_null() || out.is_null() {
4006 return RKRStatus::RKR_STATUS_NULL_POINTER;
4007 }
4008 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4009 return RKRStatus::RKR_STATUS_NULL_POINTER;
4010 };
4011 let arr = get(frame);
4012 if section && arr.nrows() == 0 {
4013 unsafe { *out = RKRArrayView::empty() };
4014 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4015 }
4016 unsafe { *out = RKRArrayView::from_array2(arr) };
4017 RKRStatus::RKR_STATUS_SUCCESS
4018}
4019
4020fn fill_array1_view(
4021 frame_handle: *const RKRConFrame,
4022 out: *mut RKRArrayView,
4023 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4024 section: bool,
4025) -> RKRStatus {
4026 if frame_handle.is_null() || out.is_null() {
4027 return RKRStatus::RKR_STATUS_NULL_POINTER;
4028 }
4029 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4030 return RKRStatus::RKR_STATUS_NULL_POINTER;
4031 };
4032 let arr = get(frame);
4033 if section && arr.len() == 0 {
4034 unsafe { *out = RKRArrayView::empty() };
4035 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4036 }
4037 unsafe { *out = RKRArrayView::from_array1(arr) };
4038 RKRStatus::RKR_STATUS_SUCCESS
4039}
4040
4041fn f64_col_ptr(
4042 frame_handle: *const RKRConFrame,
4043 n: *mut usize,
4044 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
4045) -> *const f64 {
4046 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4047 if !n.is_null() {
4048 unsafe { *n = 0 };
4049 }
4050 return std::ptr::null();
4051 };
4052 match get(frame) {
4053 Some(s) if !s.is_empty() => {
4054 if !n.is_null() {
4055 unsafe { *n = s.len() / 3 };
4056 }
4057 s.as_ptr()
4058 }
4059 _ => {
4060 if !n.is_null() {
4061 unsafe { *n = 0 };
4062 }
4063 std::ptr::null()
4064 }
4065 }
4066}
4067
4068#[unsafe(no_mangle)]
4071pub unsafe extern "C" fn rkr_frame_copy_positions(
4072 frame_handle: *const RKRConFrame,
4073 out: *mut f64,
4074 out_len: usize,
4075) -> RKRStatus {
4076 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
4077}
4078fn copy_array2_f64(
4079 frame_handle: *const RKRConFrame,
4080 out: *mut f64,
4081 out_len: usize,
4082 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4083 section: bool,
4084) -> RKRStatus {
4085 if frame_handle.is_null() || out.is_null() {
4086 return RKRStatus::RKR_STATUS_NULL_POINTER;
4087 }
4088 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4089 return RKRStatus::RKR_STATUS_NULL_POINTER;
4090 };
4091 let arr = get(frame);
4092 let n = if arr.nrows() > 0 {
4093 arr.nrows()
4094 } else {
4095 frame.atom_data.len()
4096 };
4097 if section && arr.nrows() == 0 {
4098 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4099 }
4100 let need = n.saturating_mul(3);
4101 if out_len < need {
4102 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4103 }
4104 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
4105 if arr.nrows() == n {
4106 if let Some(src) = arr.f64_slice() {
4107 dest.copy_from_slice(&src[..need.min(src.len())]);
4108 return RKRStatus::RKR_STATUS_SUCCESS;
4109 }
4110 for i in 0..n {
4111 let row = arr.as_f64_row(i);
4112 dest[i * 3] = row[0];
4113 dest[i * 3 + 1] = row[1];
4114 dest[i * 3 + 2] = row[2];
4115 }
4116 return RKRStatus::RKR_STATUS_SUCCESS;
4117 }
4118 for (i, a) in frame.atom_data.iter().enumerate() {
4119 dest[i * 3] = a.x;
4120 dest[i * 3 + 1] = a.y;
4121 dest[i * 3 + 2] = a.z;
4122 }
4123 RKRStatus::RKR_STATUS_SUCCESS
4124}
4125
4126fn copy_array1_f64(
4127 frame_handle: *const RKRConFrame,
4128 out: *mut f64,
4129 out_len: usize,
4130 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4131 section: bool,
4132) -> RKRStatus {
4133 if frame_handle.is_null() || out.is_null() {
4134 return RKRStatus::RKR_STATUS_NULL_POINTER;
4135 }
4136 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4137 return RKRStatus::RKR_STATUS_NULL_POINTER;
4138 };
4139 let arr = get(frame);
4140 let n = if arr.len() > 0 {
4141 arr.len()
4142 } else {
4143 frame.atom_data.len()
4144 };
4145 if section && arr.len() == 0 {
4146 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4147 }
4148 if out_len < n {
4149 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4150 }
4151 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4152 if let Some(src) = arr.f64_slice() {
4153 dest.copy_from_slice(&src[..n.min(src.len())]);
4154 return RKRStatus::RKR_STATUS_SUCCESS;
4155 }
4156 for i in 0..n {
4157 dest[i] = arr.get_f64(i);
4158 }
4159 RKRStatus::RKR_STATUS_SUCCESS
4160}
4161
4162#[unsafe(no_mangle)]
4163pub unsafe extern "C" fn rkr_frame_copy_velocities(
4164 frame_handle: *const RKRConFrame,
4165 out: *mut f64,
4166 out_len: usize,
4167) -> RKRStatus {
4168 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
4169}
4170#[unsafe(no_mangle)]
4171pub unsafe extern "C" fn rkr_frame_copy_forces(
4172 frame_handle: *const RKRConFrame,
4173 out: *mut f64,
4174 out_len: usize,
4175) -> RKRStatus {
4176 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
4177}
4178#[unsafe(no_mangle)]
4184pub unsafe extern "C" fn rkr_frame_copy_displacements(
4185 frame_handle: *const RKRConFrame,
4186 out: *mut f64,
4187 out_len: usize,
4188) -> RKRStatus {
4189 copy_array2_f64(frame_handle, out, out_len, |f| &f.displacements, true)
4190}
4191#[unsafe(no_mangle)]
4197pub unsafe extern "C" fn rkr_frame_copy_spreads(
4198 frame_handle: *const RKRConFrame,
4199 out: *mut f64,
4200 out_len: usize,
4201) -> RKRStatus {
4202 copy_array2_f64(frame_handle, out, out_len, |f| &f.spreads, true)
4203}
4204#[unsafe(no_mangle)]
4205pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
4206 frame_handle: *const RKRConFrame,
4207 out: *mut f64,
4208 out_len: usize,
4209) -> RKRStatus {
4210 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
4211}
4212#[unsafe(no_mangle)]
4213pub unsafe extern "C" fn rkr_frame_copy_masses(
4214 frame_handle: *const RKRConFrame,
4215 out: *mut f64,
4216 out_len: usize,
4217) -> RKRStatus {
4218 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
4219}
4220#[unsafe(no_mangle)]
4221pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
4222 frame_handle: *const RKRConFrame,
4223 out: *mut u64,
4224 out_len: usize,
4225) -> RKRStatus {
4226 if frame_handle.is_null() || out.is_null() {
4227 return RKRStatus::RKR_STATUS_NULL_POINTER;
4228 }
4229 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4230 return RKRStatus::RKR_STATUS_NULL_POINTER;
4231 };
4232 let n = frame.atom_ids.len().max(frame.atom_data.len());
4233 if out_len < n {
4234 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4235 }
4236 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4237 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
4238 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
4239 return RKRStatus::RKR_STATUS_SUCCESS;
4240 }
4241 for (i, a) in frame.atom_data.iter().enumerate() {
4242 dest[i] = a.atom_id;
4243 }
4244 RKRStatus::RKR_STATUS_SUCCESS
4245}
4246fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
4247 let n = frame.atom_data.len();
4248 let mut data = Vec::with_capacity(n * 3);
4249 for a in &frame.atom_data {
4250 data.extend_from_slice(&[a.x, a.y, a.z]);
4251 }
4252 ndarray::ArcArray2::from_shape_vec((n, 3), data)
4253 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
4254}
4255
4256#[unsafe(no_mangle)]
4266pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
4267 frame_handle: *const RKRConFrame,
4268 device_type: i32,
4269 device_id: i32,
4270 _stream: i64,
4271 _max_version_major: u32,
4272 _max_version_minor: u32,
4273 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4274) -> RKRStatus {
4275 if frame_handle.is_null() || out_tensor.is_null() {
4276 return RKRStatus::RKR_STATUS_NULL_POINTER;
4277 }
4278 unsafe { *out_tensor = std::ptr::null_mut() };
4279 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
4280 dlpk::sys::DLDevice::cpu()
4281 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
4282 #[cfg(feature = "cuda")]
4283 {
4284 dlpk::sys::DLDevice::cuda(device_id)
4285 }
4286 #[cfg(not(feature = "cuda"))]
4287 {
4288 let _ = device_id;
4289 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4290 }
4291 } else {
4292 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4293 };
4294 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4295 return RKRStatus::RKR_STATUS_NULL_POINTER;
4296 };
4297 match frame.positions_as_dlpack(dl_device) {
4299 Ok(tensor) => {
4300 let raw = tensor.into_raw();
4301 unsafe {
4302 *out_tensor = raw.as_ptr();
4303 }
4304 RKRStatus::RKR_STATUS_SUCCESS
4305 }
4306 Err(e) => map_dlpack_err(e),
4307 }
4308}
4309
4310#[unsafe(no_mangle)]
4316pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
4317 frame_handle: *mut RKRConFrame,
4318 tensor: *const RKRDLManagedTensorVersioned,
4319) -> RKRStatus {
4320 if frame_handle.is_null() || tensor.is_null() {
4321 return RKRStatus::RKR_STATUS_NULL_POINTER;
4322 }
4323 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
4324 let dl = unsafe { &(*tensor).dl_tensor };
4325 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
4326 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4327 }
4328 let n = frame.atom_data.len();
4329 let need = n.saturating_mul(3);
4330 let ndim = dl.ndim as usize;
4331 let shape = if dl.shape.is_null() {
4332 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4333 } else {
4334 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
4335 };
4336 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
4337 need
4338 } else if ndim == 1 && shape[0] == need as i64 {
4339 need
4340 } else {
4341 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4342 };
4343 let code = dl.dtype.code as u8;
4344 let bits = dl.dtype.bits;
4345 if dl.data.is_null() {
4346 return RKRStatus::RKR_STATUS_NULL_POINTER;
4347 }
4348 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
4349 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
4350 s.to_vec()
4351 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
4352 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
4353 s.iter().map(|&x| x as f64).collect()
4354 } else {
4355 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4356 };
4357 if frame.positions.nrows() != n {
4358 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4359 }
4360 for i in 0..n {
4361 frame
4362 .positions
4363 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
4364 }
4365 frame.sync_atom_data_from_arrays();
4366 RKRStatus::RKR_STATUS_SUCCESS
4367}
4368
4369#[unsafe(no_mangle)]
4372pub unsafe extern "C" fn rkr_frame_positions_dlpack(
4373 frame_handle: *const RKRConFrame,
4374 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4375) -> RKRStatus {
4376 unsafe {
4377 rkr_frame_positions_as_dlpack(
4378 frame_handle,
4379 rkr_dl_device_type::RKR_DL_CPU,
4380 0,
4381 0,
4382 1,
4383 0,
4384 out_tensor,
4385 )
4386 }
4387}
4388
4389#[unsafe(no_mangle)]
4394pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
4395 frame_handle: *const RKRConFrame,
4396 opts: *const RKRDlpackExportOptions,
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 o = match resolve_dlpack_opts(opts) {
4404 Ok(o) => o,
4405 Err(st) => return st,
4406 };
4407 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4408 return RKRStatus::RKR_STATUS_NULL_POINTER;
4409 };
4410 let arr = frame_positions_arc(frame);
4411 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4412}
4413
4414#[unsafe(no_mangle)]
4416pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
4417 frame_handle: *const RKRConFrame,
4418 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4419) -> RKRStatus {
4420 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4421}
4422
4423#[unsafe(no_mangle)]
4424pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
4425 frame_handle: *const RKRConFrame,
4426 opts: *const RKRDlpackExportOptions,
4427 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4428) -> RKRStatus {
4429 if frame_handle.is_null() || out_tensor.is_null() {
4430 return RKRStatus::RKR_STATUS_NULL_POINTER;
4431 }
4432 unsafe { *out_tensor = std::ptr::null_mut() };
4433 let o = match resolve_dlpack_opts(opts) {
4434 Ok(o) => o,
4435 Err(st) => return st,
4436 };
4437 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4438 return RKRStatus::RKR_STATUS_NULL_POINTER;
4439 };
4440 if !frame.has_velocities() {
4441 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4442 }
4443 let n = frame.atom_data.len();
4444 let mut data = Vec::with_capacity(n * 3);
4445 for a in &frame.atom_data {
4446 let v = a.velocity.unwrap_or([0.0; 3]);
4447 data.extend_from_slice(&v);
4448 }
4449 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4450 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4451 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4452}
4453
4454#[unsafe(no_mangle)]
4456pub unsafe extern "C" fn rkr_frame_forces_dlpack(
4457 frame_handle: *const RKRConFrame,
4458 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4459) -> RKRStatus {
4460 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4461}
4462
4463#[unsafe(no_mangle)]
4464pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
4465 frame_handle: *const RKRConFrame,
4466 opts: *const RKRDlpackExportOptions,
4467 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4468) -> RKRStatus {
4469 if frame_handle.is_null() || out_tensor.is_null() {
4470 return RKRStatus::RKR_STATUS_NULL_POINTER;
4471 }
4472 unsafe { *out_tensor = std::ptr::null_mut() };
4473 let o = match resolve_dlpack_opts(opts) {
4474 Ok(o) => o,
4475 Err(st) => return st,
4476 };
4477 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4478 return RKRStatus::RKR_STATUS_NULL_POINTER;
4479 };
4480 if !frame.has_forces() {
4481 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4482 }
4483 let n = frame.atom_data.len();
4484 let mut data = Vec::with_capacity(n * 3);
4485 for a in &frame.atom_data {
4486 let f = a.force.unwrap_or([0.0; 3]);
4487 data.extend_from_slice(&f);
4488 }
4489 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4490 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4491 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4492}
4493
4494#[unsafe(no_mangle)]
4501pub unsafe extern "C" fn rkr_frame_displacements_dlpack(
4502 frame_handle: *const RKRConFrame,
4503 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4504) -> RKRStatus {
4505 unsafe { rkr_frame_displacements_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4506}
4507
4508#[unsafe(no_mangle)]
4515pub unsafe extern "C" fn rkr_frame_displacements_dlpack_ex(
4516 frame_handle: *const RKRConFrame,
4517 opts: *const RKRDlpackExportOptions,
4518 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4519) -> RKRStatus {
4520 if frame_handle.is_null() || out_tensor.is_null() {
4521 return RKRStatus::RKR_STATUS_NULL_POINTER;
4522 }
4523 unsafe { *out_tensor = std::ptr::null_mut() };
4524 let o = match resolve_dlpack_opts(opts) {
4525 Ok(o) => o,
4526 Err(st) => return st,
4527 };
4528 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4529 return RKRStatus::RKR_STATUS_NULL_POINTER;
4530 };
4531 if !frame.has_displacements() {
4532 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4533 }
4534 let n = frame.atom_data.len();
4535 let mut data = Vec::with_capacity(n * 3);
4536 for a in &frame.atom_data {
4537 let d = a.displacement.unwrap_or([0.0; 3]);
4538 data.extend_from_slice(&d);
4539 }
4540 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4541 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4542 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4543}
4544
4545#[unsafe(no_mangle)]
4552pub unsafe extern "C" fn rkr_frame_spreads_dlpack(
4553 frame_handle: *const RKRConFrame,
4554 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4555) -> RKRStatus {
4556 unsafe { rkr_frame_spreads_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4557}
4558
4559#[unsafe(no_mangle)]
4566pub unsafe extern "C" fn rkr_frame_spreads_dlpack_ex(
4567 frame_handle: *const RKRConFrame,
4568 opts: *const RKRDlpackExportOptions,
4569 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4570) -> RKRStatus {
4571 if frame_handle.is_null() || out_tensor.is_null() {
4572 return RKRStatus::RKR_STATUS_NULL_POINTER;
4573 }
4574 unsafe { *out_tensor = std::ptr::null_mut() };
4575 let o = match resolve_dlpack_opts(opts) {
4576 Ok(o) => o,
4577 Err(st) => return st,
4578 };
4579 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4580 return RKRStatus::RKR_STATUS_NULL_POINTER;
4581 };
4582 if !frame.has_spreads() {
4583 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4584 }
4585 let n = frame.atom_data.len();
4586 let mut data = Vec::with_capacity(n * 3);
4587 for a in &frame.atom_data {
4588 let d = a.spread.unwrap_or([0.0; 3]);
4589 data.extend_from_slice(&d);
4590 }
4591 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4592 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4593 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4594}
4595
4596#[unsafe(no_mangle)]
4598pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
4599 frame_handle: *const RKRConFrame,
4600 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4601) -> RKRStatus {
4602 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4603}
4604
4605#[unsafe(no_mangle)]
4606pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
4607 frame_handle: *const RKRConFrame,
4608 opts: *const RKRDlpackExportOptions,
4609 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4610) -> RKRStatus {
4611 if frame_handle.is_null() || out_tensor.is_null() {
4612 return RKRStatus::RKR_STATUS_NULL_POINTER;
4613 }
4614 unsafe { *out_tensor = std::ptr::null_mut() };
4615 let o = match resolve_dlpack_opts(opts) {
4616 Ok(o) => o,
4617 Err(st) => return st,
4618 };
4619 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4620 return RKRStatus::RKR_STATUS_NULL_POINTER;
4621 };
4622 if !frame.has_energies() {
4623 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4624 }
4625 let data: Vec<f64> = frame
4626 .atom_data
4627 .iter()
4628 .map(|a| a.energy.unwrap_or(0.0))
4629 .collect();
4630 let arr = ndarray::ArcArray1::from_vec(data);
4631 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
4632}
4633
4634pub struct RKRSelectionResult;
4638#[unsafe(no_mangle)]
4649pub unsafe extern "C" fn rkr_frame_select(
4650 frame_handle: *const RKRConFrame,
4651 selection: *const c_char,
4652 out_result: *mut *mut RKRSelectionResult,
4653) -> RKRStatus {
4654 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
4655 return RKRStatus::RKR_STATUS_NULL_POINTER;
4656 }
4657 let frame = unsafe { &*(frame_handle as *const ConFrame) };
4658 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
4659 Ok(s) => s,
4660 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
4661 };
4662 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
4663 Ok(result) => {
4664 let boxed = Box::new(result);
4665 unsafe {
4666 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
4667 }
4668 RKRStatus::RKR_STATUS_SUCCESS
4669 }
4670 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
4671 }
4672}
4673#[unsafe(no_mangle)]
4678pub unsafe extern "C" fn rkr_selection_result_match_count(
4679 result_handle: *const RKRSelectionResult,
4680) -> u64 {
4681 if result_handle.is_null() {
4682 return 0;
4683 }
4684 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4685 result.matches.len() as u64
4686}
4687#[unsafe(no_mangle)]
4692pub unsafe extern "C" fn rkr_selection_result_context_size(
4693 result_handle: *const RKRSelectionResult,
4694) -> u32 {
4695 if result_handle.is_null() {
4696 return 0;
4697 }
4698 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4699 result.context_size as u32
4700}
4701#[unsafe(no_mangle)]
4707pub unsafe extern "C" fn rkr_selection_result_match_at(
4708 result_handle: *const RKRSelectionResult,
4709 match_index: u64,
4710 out_atoms: *mut u64,
4711 out_size: *mut u32,
4712) -> RKRStatus {
4713 if result_handle.is_null() || out_atoms.is_null() {
4714 return RKRStatus::RKR_STATUS_NULL_POINTER;
4715 }
4716 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4717 let idx = match_index as usize;
4718 if idx >= result.matches.len() {
4719 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
4720 }
4721 let m = &result.matches[idx];
4722 unsafe {
4723 for i in 0..4 {
4724 *out_atoms.add(i) = if i < m.size {
4725 m.atoms[i] as u64
4726 } else {
4727 u64::MAX
4728 };
4729 }
4730 if !out_size.is_null() {
4731 *out_size = m.size as u32;
4732 }
4733 }
4734 RKRStatus::RKR_STATUS_SUCCESS
4735}
4736#[unsafe(no_mangle)]
4742pub unsafe extern "C" fn rkr_selection_result_primary_indices(
4743 result_handle: *const RKRSelectionResult,
4744 out_indices: *mut u64,
4745 capacity: u64,
4746 out_written: *mut u64,
4747) -> RKRStatus {
4748 if result_handle.is_null() {
4749 return RKRStatus::RKR_STATUS_NULL_POINTER;
4750 }
4751 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4752 let n = result.matches.len() as u64;
4753 if !out_written.is_null() {
4754 unsafe {
4755 *out_written = n;
4756 }
4757 }
4758 if n == 0 {
4759 return RKRStatus::RKR_STATUS_SUCCESS;
4760 }
4761 if out_indices.is_null() {
4762 return RKRStatus::RKR_STATUS_NULL_POINTER;
4763 }
4764 if capacity < n {
4765 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4766 }
4767 unsafe {
4768 for (i, m) in result.matches.iter().enumerate() {
4769 *out_indices.add(i) = m.atoms[0] as u64;
4770 }
4771 }
4772 RKRStatus::RKR_STATUS_SUCCESS
4773}
4774#[unsafe(no_mangle)]
4779pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
4780 if result_handle.is_null() {
4781 return;
4782 }
4783 unsafe {
4784 drop(Box::from_raw(
4785 result_handle as *mut crate::chemfiles_selection::SelectionResult,
4786 ));
4787 }
4788}
4789#[unsafe(no_mangle)]
4791pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
4792 #[cfg(feature = "chemfiles")]
4793 {
4794 1
4795 }
4796 #[cfg(not(feature = "chemfiles"))]
4797 {
4798 0
4799 }
4800}
4801
4802#[unsafe(no_mangle)]
4804pub extern "C" fn rkr_has_parallel_support() -> u8 {
4805 #[cfg(feature = "parallel")]
4806 {
4807 1
4808 }
4809 #[cfg(not(feature = "parallel"))]
4810 {
4811 0
4812 }
4813}
4814#[unsafe(no_mangle)]
4820pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
4821 if path_c.is_null() {
4822 return std::ptr::null_mut();
4823 }
4824 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4825 return std::ptr::null_mut();
4826 };
4827 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
4828 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4829 Err(_) => std::ptr::null_mut(),
4830 }
4831}
4832
4833#[unsafe(no_mangle)]
4839pub unsafe extern "C" fn rkr_read_chemfiles(
4840 path_c: *const c_char,
4841 num_frames: *mut usize,
4842) -> *mut *mut RKRConFrame {
4843 if path_c.is_null() || num_frames.is_null() {
4844 return std::ptr::null_mut();
4845 }
4846 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4847 return std::ptr::null_mut();
4848 };
4849 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
4850 Ok(frames) => pack_frame_handles(frames, num_frames),
4851 Err(_) => std::ptr::null_mut(),
4852 }
4853}
4854
4855#[unsafe(no_mangle)]
4861pub unsafe extern "C" fn rkr_read_chemfiles_nth(
4862 path_c: *const c_char,
4863 index: usize,
4864) -> *mut RKRConFrame {
4865 if path_c.is_null() {
4866 return std::ptr::null_mut();
4867 }
4868 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4869 return std::ptr::null_mut();
4870 };
4871 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
4872 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4873 Err(_) => std::ptr::null_mut(),
4874 }
4875}
4876
4877#[unsafe(no_mangle)]
4883pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
4884 if path_c.is_null() {
4885 return usize::MAX;
4886 }
4887 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4888 return usize::MAX;
4889 };
4890 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
4891}
4892
4893#[unsafe(no_mangle)]
4901pub unsafe extern "C" fn rkr_read_chemfiles_range(
4902 path_c: *const c_char,
4903 start: usize,
4904 step: usize,
4905 stop: usize,
4906 topology_c: *const c_char,
4907 guess_bonds: u8,
4908 num_frames: *mut usize,
4909) -> *mut *mut RKRConFrame {
4910 if path_c.is_null() || num_frames.is_null() {
4911 return std::ptr::null_mut();
4912 }
4913 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4914 return std::ptr::null_mut();
4915 };
4916 let topology = if topology_c.is_null() {
4917 None
4918 } else {
4919 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
4920 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
4921 _ => return std::ptr::null_mut(),
4922 }
4923 };
4924 let opts = crate::chemfiles_import::ChemfilesReadOpts {
4925 start,
4926 step,
4927 stop: if stop == usize::MAX { None } else { Some(stop) },
4928 format: None,
4929 topology,
4930 topology_format: None,
4931 guess_bonds: guess_bonds != 0,
4932 };
4933 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
4934 Ok(frames) => pack_frame_handles(frames, num_frames),
4935 Err(_) => std::ptr::null_mut(),
4936 }
4937}
4938#[unsafe(no_mangle)]
4945pub unsafe extern "C" fn rkr_read_chemfiles_memory(
4946 data_c: *const c_char,
4947 format_c: *const c_char,
4948 num_frames: *mut usize,
4949) -> *mut *mut RKRConFrame {
4950 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
4951 return std::ptr::null_mut();
4952 }
4953 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
4954 return std::ptr::null_mut();
4955 };
4956 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
4957 return std::ptr::null_mut();
4958 };
4959 match crate::chemfiles_import::con_frames_from_memory(data, format) {
4960 Ok(frames) => {
4961 let n = frames.len();
4962 unsafe { *num_frames = n };
4963 let mut ptrs: Vec<*mut RKRConFrame> = frames
4964 .into_iter()
4965 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
4966 .collect();
4967 let p = ptrs.as_mut_ptr();
4968 std::mem::forget(ptrs);
4969 p
4970 }
4971 Err(_) => std::ptr::null_mut(),
4972 }
4973}
4974#[unsafe(no_mangle)]
4979pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
4980 if tensor.is_null() {
4981 return;
4982 }
4983 unsafe {
4984 let t = &mut *tensor;
4985 if let Some(del) = t.deleter {
4986 del(tensor);
4987 }
4988 }
4989}
4990
4991#[unsafe(no_mangle)]
5001pub unsafe extern "C" fn rkr_pack_rcso(
5002 frame_handle: *const RKRConFrame,
5003 buf: *mut u8,
5004 buflen: usize,
5005 out_len: *mut usize,
5006) -> RKRStatus {
5007 if out_len.is_null() {
5008 return RKRStatus::RKR_STATUS_NULL_POINTER;
5009 }
5010 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
5011 Some(f) => f,
5012 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
5013 };
5014 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
5015 Ok(b) => b,
5016 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5017 };
5018 unsafe { *out_len = bytes.len() };
5019 if buf.is_null() {
5020 return RKRStatus::RKR_STATUS_SUCCESS;
5021 }
5022 if buflen < bytes.len() {
5023 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5024 }
5025 unsafe {
5026 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
5027 }
5028 RKRStatus::RKR_STATUS_SUCCESS
5029}
5030
5031#[unsafe(no_mangle)]
5036pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
5037 buf: *const u8,
5038 buflen: usize,
5039 out_natoms: *mut u32,
5040) -> RKRStatus {
5041 if buf.is_null() || out_natoms.is_null() {
5042 return RKRStatus::RKR_STATUS_NULL_POINTER;
5043 }
5044 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5045 match crate::rcso::Rcso::decode(bytes) {
5046 Ok(s) => {
5047 unsafe { *out_natoms = s.natoms };
5048 RKRStatus::RKR_STATUS_SUCCESS
5049 }
5050 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5051 }
5052}
5053
5054#[unsafe(no_mangle)]
5059pub unsafe extern "C" fn rkr_unpack_rcso_positions(
5060 buf: *const u8,
5061 buflen: usize,
5062 dest: *mut f64,
5063 dest_natoms: u32,
5064) -> RKRStatus {
5065 if buf.is_null() || dest.is_null() {
5066 return RKRStatus::RKR_STATUS_NULL_POINTER;
5067 }
5068 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5069 let soa = match crate::rcso::Rcso::decode(bytes) {
5070 Ok(s) => s,
5071 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5072 };
5073 if soa.natoms != dest_natoms {
5074 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5075 }
5076 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5077 for (i, p) in soa.positions.iter().enumerate() {
5078 out[i * 3] = p[0];
5079 out[i * 3 + 1] = p[1];
5080 out[i * 3 + 2] = p[2];
5081 }
5082 RKRStatus::RKR_STATUS_SUCCESS
5083}
5084
5085#[unsafe(no_mangle)]
5091pub unsafe extern "C" fn rkr_unpack_rcso_forces(
5092 buf: *const u8,
5093 buflen: usize,
5094 dest: *mut f64,
5095 dest_natoms: u32,
5096) -> RKRStatus {
5097 if buf.is_null() || dest.is_null() {
5098 return RKRStatus::RKR_STATUS_NULL_POINTER;
5099 }
5100 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5101 let soa = match crate::rcso::Rcso::decode(bytes) {
5102 Ok(s) => s,
5103 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5104 };
5105 let Some(forces) = soa.forces else {
5106 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
5107 };
5108 if soa.natoms != dest_natoms {
5109 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5110 }
5111 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5112 for (i, f) in forces.iter().enumerate() {
5113 out[i * 3] = f[0];
5114 out[i * 3 + 1] = f[1];
5115 out[i * 3 + 2] = f[2];
5116 }
5117 RKRStatus::RKR_STATUS_SUCCESS
5118}
5119
5120#[cfg(test)]
5121mod tests {
5122 use super::*;
5123 use std::ffi::{CStr, CString};
5124 #[test]
5125 fn frame_copy_positions_without_cframe() {
5126 let handle = test_frame_handle();
5127 let n = unsafe { rkr_frame_atom_count(handle) };
5128 assert_eq!(n, 1);
5129 let mut buf = vec![0.0f64; n * 3];
5130 assert_eq!(
5131 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
5132 RKRStatus::RKR_STATUS_SUCCESS
5133 );
5134 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5135 assert_eq!(
5136 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
5137 RKRStatus::RKR_STATUS_SUCCESS
5138 );
5139 assert!(!tensor.is_null());
5140 unsafe { rkr_dlpack_delete(tensor) };
5141 assert_eq!(
5142 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
5143 RKRStatus::RKR_STATUS_SECTION_ABSENT
5144 );
5145 unsafe { free_rkr_frame(handle) };
5146 }
5147 #[test]
5148 fn read_all_frames_c_abi_tiny() {
5149 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5150 let mut n: usize = 0;
5151 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5152 assert!(!arr.is_null() && n >= 1);
5153 let first = unsafe { *arr };
5154 let nat = unsafe { rkr_frame_atom_count(first) };
5155 let mut buf = vec![0.0f64; nat * 3];
5156 assert_eq!(
5157 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
5158 RKRStatus::RKR_STATUS_SUCCESS
5159 );
5160 unsafe { free_rkr_frame_array(arr, n) };
5161 }
5162
5163 #[test]
5164 fn read_all_frames_n_threads_matches_auto() {
5165 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5166 let mut n_auto: usize = 0;
5167 let mut n_one: usize = 0;
5168 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
5169 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
5170 assert!(!auto.is_null() && !one.is_null());
5171 assert_eq!(n_auto, n_one);
5172 assert_eq!(n_auto, 1);
5173 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
5174 let nat_one = unsafe { rkr_frame_atom_count(*one) };
5175 assert_eq!(nat_auto, nat_one);
5176 unsafe { free_rkr_frame_array(auto, n_auto) };
5177 unsafe { free_rkr_frame_array(one, n_one) };
5178 }
5179
5180 #[test]
5181 fn free_rkr_frame_ptr_array_keeps_frames() {
5182 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5183 let mut n: usize = 0;
5184 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5185 assert!(!arr.is_null() && n >= 1);
5186 let first = unsafe { *arr };
5187 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
5189 unsafe { free_rkr_frame_ptr_array(arr, n) };
5190 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
5191 unsafe { free_rkr_frame(first) };
5192 for h in rest {
5193 if !h.is_null() {
5194 unsafe { free_rkr_frame(h) };
5195 }
5196 }
5197 }
5198 fn test_frame_handle() -> *mut RKRConFrame {
5199 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5200 builder
5201 .prebox_header("Generated by test")
5202 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
5203 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
5204 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
5205 }
5206 #[test]
5207 fn header_line_rejects_null_buffer() {
5208 let frame = test_frame_handle();
5209 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
5210 unsafe { free_rkr_frame(frame) };
5211 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5212 }
5213 #[test]
5214 fn header_line_rejects_empty_buffer() {
5215 let frame = test_frame_handle();
5216 let mut buffer = [0 as c_char; 1];
5217 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
5218 unsafe { free_rkr_frame(frame) };
5219 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
5220 }
5221 #[test]
5222 fn header_line_truncates_and_terminates_buffer() {
5223 let frame = test_frame_handle();
5224 let mut buffer = [0 as c_char; 10];
5225 let status =
5226 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
5227 unsafe { free_rkr_frame(frame) };
5228 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5229 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
5230 assert_eq!(copied.to_str().unwrap(), "Generated");
5231 }
5232
5233 #[test]
5234 fn pack_rcso_size_query_then_dest() {
5235 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
5236 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
5237 assert!(!frame.is_null());
5238 let mut need = 0usize;
5239 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
5240 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5241 assert!(need >= 24);
5242 let mut buf = vec![0u8; need];
5243 let mut wrote = 0usize;
5244 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
5245 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5246 assert_eq!(wrote, need);
5247 assert_eq!(&buf[0..4], b"RCSO");
5248 let mut natoms = 0u32;
5249 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
5250 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5251 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
5252 let mut xyz = vec![0.0f64; natoms as usize * 3];
5253 let st =
5254 unsafe { rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms) };
5255 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5256 let mut frc = vec![0.0f64; natoms as usize * 3];
5257 let st =
5258 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
5259 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5260 unsafe { free_rkr_frame(frame) };
5261 }
5262
5263 fn test_builder_handle() -> *mut RKRConFrameBuilder {
5264 let cell = [10.0, 11.0, 12.0];
5265 let angles = [90.0, 91.0, 92.0];
5266 unsafe {
5267 rkr_frame_new(
5268 cell.as_ptr(),
5269 angles.as_ptr(),
5270 ptr::null(),
5271 ptr::null(),
5272 ptr::null(),
5273 ptr::null(),
5274 )
5275 }
5276 }
5277 fn c_string(s: &str) -> CString {
5278 CString::new(s).unwrap()
5279 }
5280 unsafe fn assert_single_atom(
5281 frame: *mut RKRConFrame,
5282 fixed: [bool; 3],
5283 velocity: Option<[f64; 3]>,
5284 forces: Option<[f64; 3]>,
5285 ) {
5286 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5287 assert!(!c_frame.is_null());
5288 let c_frame_ref = unsafe { &*c_frame };
5289 assert_eq!(c_frame_ref.num_atoms, 1);
5290 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
5291 assert_eq!(c_frame_ref.has_forces, forces.is_some());
5292 let atom = unsafe { &*c_frame_ref.atoms };
5293 assert_eq!(atom.fixed_x, fixed[0]);
5294 assert_eq!(atom.fixed_y, fixed[1]);
5295 assert_eq!(atom.fixed_z, fixed[2]);
5296 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
5297 assert_eq!(atom.has_velocity, velocity.is_some());
5298 assert_eq!(atom.has_forces, forces.is_some());
5299 if let Some([vx, vy, vz]) = velocity {
5300 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
5301 }
5302 if let Some([fx, fy, fz]) = forces {
5303 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
5304 }
5305 unsafe { free_c_frame(c_frame) };
5306 unsafe { free_rkr_frame(frame) };
5307 }
5308 #[test]
5309 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
5310 let builder = test_builder_handle();
5311 let symbol = c_string("Cu");
5312 let status = unsafe {
5313 rkr_frame_add_atom_with_fixed_mask(
5314 builder,
5315 symbol.as_ptr(),
5316 1.0,
5317 2.0,
5318 3.0,
5319 true,
5320 false,
5321 true,
5322 7,
5323 63.546,
5324 )
5325 };
5326 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5327 let frame = unsafe { rkr_frame_builder_build(builder) };
5328 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
5329 }
5330 #[test]
5331 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
5332 let builder = test_builder_handle();
5333 let symbol = c_string("H");
5334 let status = unsafe {
5335 rkr_frame_add_atom_with_velocity_fixed_mask(
5336 builder,
5337 symbol.as_ptr(),
5338 1.0,
5339 2.0,
5340 3.0,
5341 false,
5342 true,
5343 false,
5344 9,
5345 1.008,
5346 0.1,
5347 0.2,
5348 0.3,
5349 )
5350 };
5351 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5352 let frame = unsafe { rkr_frame_builder_build(builder) };
5353 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
5354 }
5355 #[test]
5356 fn builder_preserves_fixed_mask_for_atom_with_forces() {
5357 let builder = test_builder_handle();
5358 let symbol = c_string("O");
5359 let status = unsafe {
5360 rkr_frame_add_atom_with_forces_fixed_mask(
5361 builder,
5362 symbol.as_ptr(),
5363 1.0,
5364 2.0,
5365 3.0,
5366 true,
5367 true,
5368 false,
5369 11,
5370 15.999,
5371 -0.1,
5372 -0.2,
5373 -0.3,
5374 )
5375 };
5376 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5377 let frame = unsafe { rkr_frame_builder_build(builder) };
5378 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
5379 }
5380 #[test]
5381 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
5382 let builder = test_builder_handle();
5383 let symbol = c_string("N");
5384 let status = unsafe {
5385 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5386 builder,
5387 symbol.as_ptr(),
5388 1.0,
5389 2.0,
5390 3.0,
5391 false,
5392 true,
5393 true,
5394 13,
5395 14.007,
5396 0.4,
5397 0.5,
5398 0.6,
5399 -0.4,
5400 -0.5,
5401 -0.6,
5402 )
5403 };
5404 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5405 let frame = unsafe { rkr_frame_builder_build(builder) };
5406 unsafe {
5407 assert_single_atom(
5408 frame,
5409 [false, true, true],
5410 Some([0.4, 0.5, 0.6]),
5411 Some([-0.4, -0.5, -0.6]),
5412 )
5413 };
5414 }
5415 #[test]
5416 fn builder_bool_fixed_functions_set_all_axes_together() {
5417 let builder = test_builder_handle();
5418 let cu = c_string("Cu");
5419 let h = c_string("H");
5420 let atom_status =
5421 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
5422 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
5423 let velocity_status = unsafe {
5424 rkr_frame_add_atom_with_velocity(
5425 builder,
5426 h.as_ptr(),
5427 4.0,
5428 5.0,
5429 6.0,
5430 false,
5431 2,
5432 1.008,
5433 0.7,
5434 0.8,
5435 0.9,
5436 )
5437 };
5438 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
5439 let frame = unsafe { rkr_frame_builder_build(builder) };
5440 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5441 assert!(!c_frame.is_null());
5442 let c_frame_ref = unsafe { &*c_frame };
5443 assert_eq!(c_frame_ref.num_atoms, 2);
5444 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
5445 assert_eq!(
5446 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
5447 [true, true, true]
5448 );
5449 assert_eq!(
5450 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
5451 [false, false, false]
5452 );
5453 unsafe { free_c_frame(c_frame) };
5454 unsafe { free_rkr_frame(frame) };
5455 }
5456 #[test]
5457 fn status_message_returns_static_strings_for_all_status_values() {
5458 let cases = [
5459 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
5460 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
5461 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
5462 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
5463 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
5464 (
5465 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
5466 "index out of bounds",
5467 ),
5468 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
5469 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
5470 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
5471 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
5472 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
5473 (
5474 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
5475 "feature disabled in this build",
5476 ),
5477 ];
5478 for (status, expected) in cases {
5479 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
5480 assert_eq!(message.to_str().unwrap(), expected);
5481 }
5482 }
5483 unsafe fn assert_dlpack_cpu_float(
5487 t: *mut RKRDLManagedTensorVersioned,
5488 expect_ndim: i32,
5489 expect_shape: &[i64],
5490 expect_bits: u8,
5491 ) {
5492 assert!(!t.is_null());
5493 let dl = unsafe { &(*t).dl_tensor };
5494 assert_eq!(dl.ndim, expect_ndim);
5495 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
5496 assert_eq!(shape, expect_shape);
5497 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5498 assert_eq!(dl.dtype.bits, expect_bits);
5499 assert_eq!(dl.dtype.lanes, 1);
5500 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5501 assert!(!dl.data.is_null());
5502 }
5503
5504 #[test]
5505 fn frame_optional_section_dlpack_present_and_absent() {
5506 let handle = test_frame_handle();
5508 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5509 assert_eq!(
5510 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
5511 RKRStatus::RKR_STATUS_SECTION_ABSENT
5512 );
5513 assert!(t.is_null());
5514 assert_eq!(
5515 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
5516 RKRStatus::RKR_STATUS_SECTION_ABSENT
5517 );
5518 assert_eq!(
5519 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
5520 RKRStatus::RKR_STATUS_SECTION_ABSENT
5521 );
5522 unsafe { free_rkr_frame(handle) };
5523
5524 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
5526 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
5527 assert!(!fr.is_null());
5528 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
5529 assert!(n > 0);
5530 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5531 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
5532 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5533 unsafe {
5534 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
5535 rkr_dlpack_delete(vel);
5536 free_rkr_frame(fr);
5537 }
5538
5539 let cell = [10.0f64; 3];
5541 let ang = [90.0f64; 3];
5542 let b = unsafe {
5543 rkr_frame_new(
5544 cell.as_ptr(),
5545 ang.as_ptr(),
5546 std::ptr::null(),
5547 std::ptr::null(),
5548 std::ptr::null(),
5549 std::ptr::null(),
5550 )
5551 };
5552 assert!(!b.is_null());
5553 let sym = CString::new("H").unwrap();
5554 unsafe {
5555 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5556 b,
5557 sym.as_ptr(),
5558 0.0,
5559 0.0,
5560 0.0,
5561 false,
5562 false,
5563 false,
5564 0,
5565 1.0,
5566 0.1,
5567 0.0,
5568 0.0,
5569 0.0,
5570 0.0,
5571 -1.0,
5572 );
5573 rkr_frame_builder_set_last_energy(b, -0.5);
5574 }
5575 let built = unsafe { rkr_frame_builder_build(b) };
5576 assert!(!built.is_null());
5577 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
5578 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5579 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5580 assert_eq!(
5581 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
5582 RKRStatus::RKR_STATUS_SUCCESS
5583 );
5584 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
5585 assert_eq!(
5586 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
5587 RKRStatus::RKR_STATUS_SUCCESS
5588 );
5589 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
5590 let opts32 = RKRDlpackExportOptions {
5592 dtype: RKRDLDataType {
5593 code: rkr_dl_type_code::RKR_DL_FLOAT,
5594 bits: 32,
5595 lanes: 1,
5596 },
5597 device: RKRDLDevice {
5598 device_type: rkr_dl_device_type::RKR_DL_CPU,
5599 device_id: 0,
5600 },
5601 };
5602 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5603 assert_eq!(
5604 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
5605 RKRStatus::RKR_STATUS_SUCCESS
5606 );
5607 unsafe {
5608 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
5609 rkr_dlpack_delete(pos32);
5610 }
5611 let cuda_dev = RKRDlpackExportOptions {
5614 dtype: RKRDLDataType {
5615 code: rkr_dl_type_code::RKR_DL_FLOAT,
5616 bits: 64,
5617 lanes: 1,
5618 },
5619 device: RKRDLDevice {
5620 device_type: rkr_dl_device_type::RKR_DL_CUDA,
5621 device_id: 0,
5622 },
5623 };
5624 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5625 #[cfg(not(feature = "cuda"))]
5626 assert_eq!(
5627 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5628 RKRStatus::RKR_STATUS_FEATURE_DISABLED
5629 );
5630 #[cfg(feature = "cuda")]
5631 {
5632 assert_eq!(
5633 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5634 RKRStatus::RKR_STATUS_SUCCESS
5635 );
5636 assert!(!junk.is_null());
5637 let dl = unsafe { &(*junk).dl_tensor };
5638 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
5639 assert!(!dl.data.is_null());
5640 unsafe { rkr_dlpack_delete(junk) };
5641 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5643 assert_eq!(
5644 unsafe {
5645 rkr_frame_positions_as_dlpack(
5646 built,
5647 rkr_dl_device_type::RKR_DL_CUDA,
5648 0,
5649 0,
5650 1,
5651 0,
5652 &mut as_cuda,
5653 )
5654 },
5655 RKRStatus::RKR_STATUS_SUCCESS
5656 );
5657 assert!(!as_cuda.is_null());
5658 unsafe {
5659 assert_eq!(
5660 (*as_cuda).dl_tensor.device.device_type,
5661 dlpk::sys::DLDeviceType::kDLCUDA
5662 );
5663 rkr_dlpack_delete(as_cuda);
5664 }
5665 }
5666 let bad_bits = RKRDlpackExportOptions {
5668 dtype: RKRDLDataType {
5669 code: rkr_dl_type_code::RKR_DL_FLOAT,
5670 bits: 16,
5671 lanes: 1,
5672 },
5673 device: RKRDLDevice {
5674 device_type: rkr_dl_device_type::RKR_DL_CPU,
5675 device_id: 0,
5676 },
5677 };
5678 assert_eq!(
5679 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
5680 RKRStatus::RKR_STATUS_VALIDATION_ERROR
5681 );
5682 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5684 assert_eq!(
5685 unsafe {
5686 rkr_frame_positions_as_dlpack(
5687 built,
5688 rkr_dl_device_type::RKR_DL_CPU,
5689 0,
5690 0,
5691 1,
5692 0,
5693 &mut as_t,
5694 )
5695 },
5696 RKRStatus::RKR_STATUS_SUCCESS
5697 );
5698 unsafe {
5699 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
5700 }
5701 {
5703 let fr = unsafe { &mut *(built as *mut ConFrame) };
5704 for a in fr.atom_data.iter_mut() {
5705 a.x = -99.0;
5706 a.y = -99.0;
5707 a.z = -99.0;
5708 }
5709 }
5710 assert_eq!(
5711 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
5712 RKRStatus::RKR_STATUS_SUCCESS
5713 );
5714 {
5715 let fr = unsafe { &*(built as *const ConFrame) };
5716 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
5717 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
5718 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
5719 }
5720 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5722 assert_eq!(
5723 unsafe {
5724 rkr_frame_positions_as_dlpack(
5725 built,
5726 rkr_dl_device_type::RKR_DL_CPU,
5727 0,
5728 0,
5729 1,
5730 0,
5731 &mut again,
5732 )
5733 },
5734 RKRStatus::RKR_STATUS_SUCCESS
5735 );
5736 unsafe {
5737 let dl = &(*again).dl_tensor;
5738 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
5739 assert!((data[0] - 0.0).abs() < 1e-12);
5740 assert!((data[1] - 0.0).abs() < 1e-12);
5741 assert!((data[2] - 0.0).abs() < 1e-12);
5742 rkr_dlpack_delete(again);
5743 rkr_dlpack_delete(as_t);
5744 rkr_dlpack_delete(frc);
5745 rkr_dlpack_delete(eng);
5746 free_rkr_frame(built);
5747 }
5748 }
5749
5750 #[test]
5751 fn ffi_positions_dlpack_round_trip() {
5752 let handle = test_builder_handle();
5753 let sym = c_string("Cu");
5754 unsafe {
5755 rkr_frame_add_atom_full(
5756 handle,
5757 sym.as_ptr(),
5758 1.0,
5759 2.0,
5760 3.0,
5761 false,
5762 false,
5763 false,
5764 7,
5765 63.5,
5766 ptr::null(),
5767 ptr::null(),
5768 )
5769 };
5770 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5771 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
5772 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5773 assert!(!t.is_null());
5774 let dl = unsafe { &(*t).dl_tensor };
5776 assert_eq!(dl.ndim, 2);
5777 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
5778 assert_eq!(shape, &[1, 3]);
5779 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5780 assert_eq!(dl.dtype.bits, 64);
5781 assert_eq!(dl.dtype.lanes, 1);
5782 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5783 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
5784 assert_eq!(data, &[1.0, 2.0, 3.0]);
5785 let deleter = unsafe { (*t).deleter };
5787 if let Some(del) = deleter {
5788 unsafe { del(t) };
5789 }
5790 unsafe { free_rkr_frame_builder(handle) };
5791 }
5792 #[test]
5793 fn ffi_velocities_dlpack_section_absent() {
5794 let handle = test_builder_handle();
5795 let sym = c_string("Cu");
5796 unsafe {
5797 rkr_frame_add_atom_full(
5798 handle,
5799 sym.as_ptr(),
5800 0.0,
5801 0.0,
5802 0.0,
5803 false,
5804 false,
5805 false,
5806 0,
5807 63.5,
5808 ptr::null(),
5809 ptr::null(),
5810 )
5811 };
5812 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5813 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
5814 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
5815 assert!(t.is_null());
5816 unsafe { free_rkr_frame_builder(handle) };
5817 }
5818 #[test]
5819 fn ffi_dlpack_null_handle_rejects() {
5820 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5821 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
5822 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5823 assert!(t.is_null());
5824 }
5825 #[cfg(feature = "chemfiles")]
5826 #[test]
5827 fn rkr_frame_select_finds_oxygen() {
5828 use crate::types::ConFrameBuilder;
5829 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5830 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
5831 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
5832 let frame = b.build().unwrap();
5833 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5834 let sel = CString::new("name O").unwrap();
5835 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5836 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
5837 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5838 assert!(!out.is_null());
5839 let n = unsafe { rkr_selection_result_match_count(out) };
5840 assert_eq!(n, 1);
5841 let mut atoms = [u64::MAX; 4];
5842 let mut size = 0u32;
5843 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
5844 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
5845 assert_eq!(size, 1);
5846 assert_eq!(atoms[0], 0);
5847 unsafe {
5848 rkr_selection_result_free(out);
5849 free_rkr_frame(frame_ptr);
5850 }
5851 }
5852 #[cfg(feature = "chemfiles")]
5854 #[test]
5855 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
5856 use crate::types::{Bond, ConFrameBuilder};
5857 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5859 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
5860 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
5861 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
5862 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
5863 let mut frame = b.build().unwrap();
5864 let id_to = |id: u64| {
5865 frame
5866 .atom_data
5867 .iter()
5868 .position(|a| a.atom_id == id)
5869 .unwrap() as u32
5870 };
5871 frame.header.set_bonds(&[
5872 Bond::new(id_to(0), id_to(1)),
5873 Bond::new(id_to(1), id_to(2)),
5874 Bond::new(id_to(2), id_to(3)),
5875 ]);
5876 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5877 let run = |sel: &str| -> (u64, u32) {
5878 let csel = CString::new(sel).unwrap();
5879 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5880 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
5881 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
5882 let n = unsafe { rkr_selection_result_match_count(out) };
5883 let ctx = unsafe { rkr_selection_result_context_size(out) };
5884 unsafe { rkr_selection_result_free(out) };
5885 (n, ctx)
5886 };
5887 assert_eq!(run("bonds: all"), (3, 2));
5888 assert_eq!(run("angles: all"), (2, 3));
5889 assert_eq!(run("dihedrals: all"), (1, 4));
5890 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
5891 assert_eq!(
5892 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
5893 run("bonds: type(#1) H and name(#2) O").0
5894 );
5895 unsafe { free_rkr_frame(frame_ptr) };
5896 }
5897 #[cfg(feature = "metatensor")]
5898 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
5899 assert!(!block.is_null());
5900 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
5901 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
5902 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
5903 let shape_fn = array
5904 .shape
5905 .expect("mts_array_t.shape from metatensor C API");
5906 let mut shape_ptr: *const usize = std::ptr::null();
5907 let mut shape_count: usize = 0;
5908 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
5909 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
5910 assert_eq!(shape_count, 2);
5911 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
5912 assert_eq!(shape[0], n);
5913 assert_eq!(shape[1], props);
5914 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
5915 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
5916 assert!(!samples.is_null() && !prop_lab.is_null());
5917 }
5918 #[cfg(feature = "metatensor")]
5919 #[test]
5920 fn metatensor_positions_via_c_abi() {
5921 let handle = test_frame_handle();
5922 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5923 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
5924 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5925 assert_mts_block_shape(out, 1, 3);
5926 unsafe { rkr_mts_block_free(out) };
5927 for (name, export) in [
5928 (
5929 "velocities",
5930 rkr_frame_metatensor_velocities_block
5931 as unsafe extern "C" fn(
5932 *const RKRConFrame,
5933 *mut *mut metatensor::c_api::mts_block_t,
5934 ) -> RKRStatus,
5935 ),
5936 (
5937 "forces",
5938 rkr_frame_metatensor_forces_block
5939 as unsafe extern "C" fn(
5940 *const RKRConFrame,
5941 *mut *mut metatensor::c_api::mts_block_t,
5942 ) -> RKRStatus,
5943 ),
5944 (
5945 "atom_energies",
5946 rkr_frame_metatensor_atom_energies_block
5947 as unsafe extern "C" fn(
5948 *const RKRConFrame,
5949 *mut *mut metatensor::c_api::mts_block_t,
5950 ) -> RKRStatus,
5951 ),
5952 ] {
5953 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5954 let st_abs = unsafe { export(handle, &mut o) };
5955 assert_eq!(
5956 st_abs,
5957 RKRStatus::RKR_STATUS_SECTION_ABSENT,
5958 "{name} must be SECTION_ABSENT on minimal test frame"
5959 );
5960 assert!(o.is_null());
5961 }
5962 unsafe { free_rkr_frame(handle) };
5963 }
5964
5965 #[cfg(feature = "metatensor")]
5966 #[test]
5967 fn metatensor_optional_sections_via_c_abi() {
5968 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5970 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
5971 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
5972 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
5973 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
5974 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
5975 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
5976 builder.set_atom_energy(0, -0.5).unwrap();
5977 builder.set_atom_energy(1, -1.0).unwrap();
5978 let frame = builder.build().unwrap();
5979 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5980 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5981 assert_eq!(
5982 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
5983 RKRStatus::RKR_STATUS_SUCCESS
5984 );
5985 assert_mts_block_shape(pos, 2, 3);
5986 unsafe { rkr_mts_block_free(pos) };
5987 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5988 assert_eq!(
5989 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
5990 RKRStatus::RKR_STATUS_SUCCESS
5991 );
5992 assert_mts_block_shape(vel, 2, 3);
5993 unsafe { rkr_mts_block_free(vel) };
5994 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5995 assert_eq!(
5996 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
5997 RKRStatus::RKR_STATUS_SUCCESS
5998 );
5999 assert_mts_block_shape(frc, 2, 3);
6000 unsafe { rkr_mts_block_free(frc) };
6001 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6002 assert_eq!(
6003 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
6004 RKRStatus::RKR_STATUS_SUCCESS
6005 );
6006 assert_mts_block_shape(eng, 2, 1);
6007 unsafe { rkr_mts_block_free(eng) };
6008 unsafe { free_rkr_frame(handle) };
6009 }
6010
6011 #[test]
6012 fn string_iterator_yields_frames_from_buffer() {
6013 let text =
6014 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
6015 let c_text = CString::new(text.as_str()).unwrap();
6016 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
6017 assert!(!it.is_null());
6018 let mut n = 0usize;
6019 loop {
6020 let fr = unsafe { con_frame_iterator_next(it) };
6021 if fr.is_null() {
6022 break;
6023 }
6024 n += 1;
6025 unsafe { free_rkr_frame(fr) };
6026 }
6027 unsafe { free_con_frame_iterator(it) };
6028 assert!(n >= 1, "string iterator should yield >=1 frame");
6029
6030 let bytes = text.as_bytes();
6031 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
6032 assert!(!it2.is_null());
6033 let fr2 = unsafe { con_frame_iterator_next(it2) };
6034 assert!(!fr2.is_null());
6035 unsafe {
6036 free_rkr_frame(fr2);
6037 free_con_frame_iterator(it2);
6038 }
6039 }
6040
6041 #[test]
6042 fn file_iterator_reads_gzip_when_present() {
6043 use flate2::Compression;
6044 use flate2::write::GzEncoder;
6045 use std::io::Write;
6046 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
6047 let dir = tempfile::tempdir().expect("tempdir");
6048 let gz_path = dir.path().join("tiny_cuh2.con.gz");
6049 {
6050 let f = std::fs::File::create(&gz_path).unwrap();
6051 let mut enc = GzEncoder::new(f, Compression::default());
6052 enc.write_all(&plain).unwrap();
6053 enc.finish().unwrap();
6054 }
6055 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
6056 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
6057 assert!(
6058 !it.is_null(),
6059 "path iterator must decompress .con.gz transparently"
6060 );
6061 let fr = unsafe { con_frame_iterator_next(it) };
6062 assert!(!fr.is_null());
6063 let n = unsafe { rkr_frame_atom_count(fr) };
6064 assert!(n > 0);
6065 unsafe {
6066 free_rkr_frame(fr);
6067 free_con_frame_iterator(it);
6068 }
6069 }
6070}