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 = 0;
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.0/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)]
1054pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
1055 if !writer_handle.is_null() {
1056 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
1057 }
1058}
1059#[unsafe(no_mangle)]
1065pub unsafe extern "C" fn rkr_writer_extend(
1066 writer_handle: *mut RKRConFrameWriter,
1067 frame_handles: *const *const RKRConFrame,
1068 num_frames: usize,
1069) -> RKRStatus {
1070 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1071 Some(w) => w,
1072 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1073 };
1074 if frame_handles.is_null() {
1075 return RKRStatus::RKR_STATUS_NULL_POINTER;
1076 }
1077 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
1078 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
1079 if handles_slice.iter().any(|&handle| handle.is_null()) {
1080 return RKRStatus::RKR_STATUS_NULL_POINTER;
1083 }
1084 for &handle in handles_slice.iter() {
1085 match unsafe { (handle as *const ConFrame).as_ref() } {
1087 Some(frame) => rust_frames.push(frame),
1088 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1090 }
1091 }
1092 match writer.extend(rust_frames.into_iter()) {
1093 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1094 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
1095 }
1096}
1097
1098#[unsafe(no_mangle)]
1104pub unsafe extern "C" fn rkr_writer_set_canonical(
1105 writer_handle: *mut RKRConFrameWriter,
1106 canonical: u8,
1107) -> RKRStatus {
1108 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1109 Some(w) => w,
1110 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1111 };
1112 writer.set_canonical(canonical != 0);
1113 RKRStatus::RKR_STATUS_SUCCESS
1114}
1115
1116#[unsafe(no_mangle)]
1118pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
1119 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
1120 Some(w) => u8::from(w.is_canonical()),
1121 None => 0,
1122 }
1123}
1124
1125#[cfg(test)]
1126mod index_proj_ffi_tests {
1127 use super::*;
1128 use std::ffi::CStr;
1129 use std::fs;
1130 use std::path::PathBuf;
1131
1132 fn fixture_path() -> PathBuf {
1133 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
1134 }
1135
1136 #[test]
1137 fn ffi_projection_matches_index_proj() {
1138 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1139 let fr = &frames[0];
1140 let handle = fr as *const ConFrame as *const RKRConFrame;
1141 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
1142 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
1144 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
1145 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
1146 assert!(!formula_c.is_null());
1147 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
1148 assert_eq!(formula, proj.formula);
1149 unsafe { rkr_free_string(formula_c) };
1150 let ie = rkr_frame_index_energy(handle);
1151 match proj.energy {
1152 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
1153 None => assert!(ie.is_nan()),
1154 }
1155 let tm = rkr_frame_total_mass(handle);
1156 match proj.total_mass {
1157 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
1158 None => assert!(tm.is_nan()),
1159 }
1160 let cv = rkr_frame_cell_volume(handle);
1161 match proj.cell_volume {
1162 Some(v) => assert!(
1163 (cv - v).abs() < 1e-6 * v.max(1.0),
1164 "cell_volume C={cv} proj={v}"
1165 ),
1166 None => assert!(cv.is_nan()),
1167 }
1168 let fm = rkr_frame_fmax(handle);
1169 match proj.fmax {
1170 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
1171 None => assert!(fm.is_nan()),
1172 }
1173 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
1174 assert!(!json_c.is_null());
1175 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
1176 assert!(json.contains("\"formula\""));
1177 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
1178 assert!(json.contains("\"n_atoms\""));
1179 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
1180 unsafe { rkr_free_string(json_c) };
1181 }
1182
1183 #[test]
1184 fn ffi_canonical_writer_byte_identical() {
1185 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1186 let fr = &frames[0];
1187 let dir = tempfile::tempdir().unwrap();
1188 let p1 = dir.path().join("a.con");
1189 let p2 = dir.path().join("b.con");
1190 for p in [&p1, &p2] {
1191 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
1192 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
1193 assert!(!w.is_null());
1194 assert_eq!(
1195 unsafe { rkr_writer_set_canonical(w, 1) },
1196 RKRStatus::RKR_STATUS_SUCCESS
1197 );
1198 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
1199 let handles = [fr as *const ConFrame as *const RKRConFrame];
1200 assert_eq!(
1201 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
1202 RKRStatus::RKR_STATUS_SUCCESS
1203 );
1204 unsafe { free_rkr_writer(w) };
1205 }
1206 let b1 = fs::read(&p1).unwrap();
1207 let b2 = fs::read(&p2).unwrap();
1208 assert_eq!(b1, b2);
1209 assert!(!b1.is_empty());
1210 }
1211}
1212#[unsafe(no_mangle)]
1221pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
1222 filename_c: *const c_char,
1223 precision: u8,
1224) -> *mut RKRConFrameWriter {
1225 let filename = match unsafe { cstr_path(filename_c) } {
1226 Some(s) => s,
1227 None => return ptr::null_mut(),
1228 };
1229 match File::create(filename) {
1230 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1231 Err(_) => ptr::null_mut(),
1232 }
1233}
1234pub struct RKRConFrameBuilder {
1241 _private: [u8; 0],
1242}
1243#[allow(clippy::too_many_arguments)]
1244unsafe fn add_builder_atom(
1245 builder_handle: *mut RKRConFrameBuilder,
1246 symbol: *const c_char,
1247 x: f64,
1248 y: f64,
1249 z: f64,
1250 fixed: [bool; 3],
1251 atom_id: u64,
1252 mass: f64,
1253 velocity: Option<[f64; 3]>,
1254 forces: Option<[f64; 3]>,
1255) -> RKRStatus {
1256 if builder_handle.is_null() || symbol.is_null() {
1257 return RKRStatus::RKR_STATUS_NULL_POINTER;
1258 }
1259 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1260 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
1261 Ok(s) => s,
1262 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1263 };
1264 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
1265 if let Some(v) = velocity {
1266 builder.with_velocity(v);
1267 }
1268 if let Some(f) = forces {
1269 builder.with_force(f);
1270 }
1271 RKRStatus::RKR_STATUS_SUCCESS
1272}
1273#[unsafe(no_mangle)]
1279pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
1280 builder_handle: *mut RKRConFrameBuilder,
1281 velocity: *const f64,
1282) -> RKRStatus {
1283 if builder_handle.is_null() || velocity.is_null() {
1284 return RKRStatus::RKR_STATUS_NULL_POINTER;
1285 }
1286 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1287 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1288 builder.with_velocity(v);
1289 RKRStatus::RKR_STATUS_SUCCESS
1290}
1291#[unsafe(no_mangle)]
1297pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
1298 builder_handle: *mut RKRConFrameBuilder,
1299 force: *const f64,
1300) -> RKRStatus {
1301 if builder_handle.is_null() || force.is_null() {
1302 return RKRStatus::RKR_STATUS_NULL_POINTER;
1303 }
1304 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1305 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1306 builder.with_force(f);
1307 RKRStatus::RKR_STATUS_SUCCESS
1308}
1309#[unsafe(no_mangle)]
1316pub unsafe extern "C" fn rkr_frame_builder_set_last_displacement(
1317 builder_handle: *mut RKRConFrameBuilder,
1318 displacement: *const f64,
1319) -> RKRStatus {
1320 if builder_handle.is_null() || displacement.is_null() {
1321 return RKRStatus::RKR_STATUS_NULL_POINTER;
1322 }
1323 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1324 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1325 builder.with_displacement(d);
1326 RKRStatus::RKR_STATUS_SUCCESS
1327}
1328#[unsafe(no_mangle)]
1335pub unsafe extern "C" fn rkr_frame_builder_set_last_spread(
1336 builder_handle: *mut RKRConFrameBuilder,
1337 spread: *const f64,
1338) -> RKRStatus {
1339 if builder_handle.is_null() || spread.is_null() {
1340 return RKRStatus::RKR_STATUS_NULL_POINTER;
1341 }
1342 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1343 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
1344 builder.with_spread(d);
1345 RKRStatus::RKR_STATUS_SUCCESS
1346}
1347#[unsafe(no_mangle)]
1357pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1358 builder_handle: *mut RKRConFrameBuilder,
1359 energy: f64,
1360) -> RKRStatus {
1361 if builder_handle.is_null() {
1362 return RKRStatus::RKR_STATUS_NULL_POINTER;
1363 }
1364 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1365 builder.with_energy(energy);
1366 RKRStatus::RKR_STATUS_SUCCESS
1367}
1368fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1382 use crate::error::ParseError;
1383 match e {
1384 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1385 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1386 }
1387 ParseError::MassMismatch { .. } | ParseError::ValidationError(_) => {
1388 RKRStatus::RKR_STATUS_VALIDATION_ERROR
1389 }
1390 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1391 }
1392}
1393#[unsafe(no_mangle)]
1400pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1401 builder_handle: *const RKRConFrameBuilder,
1402) -> usize {
1403 if builder_handle.is_null() {
1404 return 0;
1405 }
1406 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1407 builder.atom_count()
1408}
1409#[unsafe(no_mangle)]
1413pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1414 builder_handle: *mut RKRConFrameBuilder,
1415 index: usize,
1416 x: f64,
1417 y: f64,
1418 z: f64,
1419) -> RKRStatus {
1420 if builder_handle.is_null() {
1421 return RKRStatus::RKR_STATUS_NULL_POINTER;
1422 }
1423 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1424 match builder.set_atom_position(index, x, y, z) {
1425 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1426 Err(e) => map_builder_err(e),
1427 }
1428}
1429#[unsafe(no_mangle)]
1433pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1434 builder_handle: *mut RKRConFrameBuilder,
1435 index: usize,
1436 velocity: *const f64,
1437) -> RKRStatus {
1438 if builder_handle.is_null() || velocity.is_null() {
1439 return RKRStatus::RKR_STATUS_NULL_POINTER;
1440 }
1441 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1442 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1443 match builder.set_atom_velocity(index, v) {
1444 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1445 Err(e) => map_builder_err(e),
1446 }
1447}
1448#[unsafe(no_mangle)]
1452pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1453 builder_handle: *mut RKRConFrameBuilder,
1454 index: usize,
1455 force: *const f64,
1456) -> RKRStatus {
1457 if builder_handle.is_null() || force.is_null() {
1458 return RKRStatus::RKR_STATUS_NULL_POINTER;
1459 }
1460 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1461 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1462 match builder.set_atom_force(index, f) {
1463 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1464 Err(e) => map_builder_err(e),
1465 }
1466}
1467#[unsafe(no_mangle)]
1472pub unsafe extern "C" fn rkr_frame_builder_set_atom_displacement(
1473 builder_handle: *mut RKRConFrameBuilder,
1474 index: usize,
1475 displacement: *const f64,
1476) -> RKRStatus {
1477 if builder_handle.is_null() || displacement.is_null() {
1478 return RKRStatus::RKR_STATUS_NULL_POINTER;
1479 }
1480 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1481 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1482 match builder.set_atom_displacement(index, d) {
1483 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1484 Err(e) => map_builder_err(e),
1485 }
1486}
1487#[unsafe(no_mangle)]
1492pub unsafe extern "C" fn rkr_frame_builder_set_atom_spread(
1493 builder_handle: *mut RKRConFrameBuilder,
1494 index: usize,
1495 spread: *const f64,
1496) -> RKRStatus {
1497 if builder_handle.is_null() || spread.is_null() {
1498 return RKRStatus::RKR_STATUS_NULL_POINTER;
1499 }
1500 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1501 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
1502 match builder.set_atom_spread(index, d) {
1503 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1504 Err(e) => map_builder_err(e),
1505 }
1506}
1507#[unsafe(no_mangle)]
1511pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1512 builder_handle: *mut RKRConFrameBuilder,
1513 index: usize,
1514 energy: f64,
1515) -> RKRStatus {
1516 if builder_handle.is_null() {
1517 return RKRStatus::RKR_STATUS_NULL_POINTER;
1518 }
1519 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1520 match builder.set_atom_energy(index, energy) {
1521 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1522 Err(e) => map_builder_err(e),
1523 }
1524}
1525#[unsafe(no_mangle)]
1529pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1530 builder_handle: *mut RKRConFrameBuilder,
1531 index: usize,
1532 fixed_x: bool,
1533 fixed_y: bool,
1534 fixed_z: bool,
1535) -> RKRStatus {
1536 if builder_handle.is_null() {
1537 return RKRStatus::RKR_STATUS_NULL_POINTER;
1538 }
1539 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1540 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1541 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1542 Err(e) => map_builder_err(e),
1543 }
1544}
1545#[unsafe(no_mangle)]
1549pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1550 builder_handle: *mut RKRConFrameBuilder,
1551 index: usize,
1552 mass: f64,
1553) -> RKRStatus {
1554 if builder_handle.is_null() {
1555 return RKRStatus::RKR_STATUS_NULL_POINTER;
1556 }
1557 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1558 match builder.set_atom_mass(index, mass) {
1559 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1560 Err(e) => map_builder_err(e),
1561 }
1562}
1563#[unsafe(no_mangle)]
1570pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1571 builder_handle: *mut RKRConFrameBuilder,
1572 index: usize,
1573 atom_id: u64,
1574) -> RKRStatus {
1575 if builder_handle.is_null() {
1576 return RKRStatus::RKR_STATUS_NULL_POINTER;
1577 }
1578 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1579 match builder.set_atom_id(index, atom_id) {
1580 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1581 Err(e) => map_builder_err(e),
1582 }
1583}
1584#[unsafe(no_mangle)]
1588pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1589 builder_handle: *mut RKRConFrameBuilder,
1590 index: usize,
1591) -> RKRStatus {
1592 if builder_handle.is_null() {
1593 return RKRStatus::RKR_STATUS_NULL_POINTER;
1594 }
1595 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1596 match builder.clear_atom_velocity(index) {
1597 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1598 Err(e) => map_builder_err(e),
1599 }
1600}
1601#[unsafe(no_mangle)]
1604pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1605 builder_handle: *mut RKRConFrameBuilder,
1606 index: usize,
1607) -> RKRStatus {
1608 if builder_handle.is_null() {
1609 return RKRStatus::RKR_STATUS_NULL_POINTER;
1610 }
1611 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1612 match builder.clear_atom_force(index) {
1613 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1614 Err(e) => map_builder_err(e),
1615 }
1616}
1617#[unsafe(no_mangle)]
1620pub unsafe extern "C" fn rkr_frame_builder_clear_atom_displacement(
1621 builder_handle: *mut RKRConFrameBuilder,
1622 index: usize,
1623) -> RKRStatus {
1624 if builder_handle.is_null() {
1625 return RKRStatus::RKR_STATUS_NULL_POINTER;
1626 }
1627 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1628 match builder.clear_atom_displacement(index) {
1629 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1630 Err(e) => map_builder_err(e),
1631 }
1632}
1633#[unsafe(no_mangle)]
1636pub unsafe extern "C" fn rkr_frame_builder_clear_atom_spread(
1637 builder_handle: *mut RKRConFrameBuilder,
1638 index: usize,
1639) -> RKRStatus {
1640 if builder_handle.is_null() {
1641 return RKRStatus::RKR_STATUS_NULL_POINTER;
1642 }
1643 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1644 match builder.clear_atom_spread(index) {
1645 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1646 Err(e) => map_builder_err(e),
1647 }
1648}
1649#[unsafe(no_mangle)]
1652pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1653 builder_handle: *mut RKRConFrameBuilder,
1654 index: usize,
1655) -> RKRStatus {
1656 if builder_handle.is_null() {
1657 return RKRStatus::RKR_STATUS_NULL_POINTER;
1658 }
1659 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1660 match builder.clear_atom_energy(index) {
1661 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1662 Err(e) => map_builder_err(e),
1663 }
1664}
1665#[unsafe(no_mangle)]
1670pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1671 builder_handle: *mut RKRConFrameBuilder,
1672 positions: *const f64,
1673 len: usize,
1674) -> RKRStatus {
1675 if builder_handle.is_null() || positions.is_null() {
1676 return RKRStatus::RKR_STATUS_NULL_POINTER;
1677 }
1678 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1679 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1680 match builder.set_positions_from_flat(slice) {
1681 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1682 Err(e) => map_builder_err(e),
1683 }
1684}
1685#[unsafe(no_mangle)]
1689pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1690 builder_handle: *mut RKRConFrameBuilder,
1691 forces: *const f64,
1692 len: usize,
1693) -> RKRStatus {
1694 if builder_handle.is_null() || forces.is_null() {
1695 return RKRStatus::RKR_STATUS_NULL_POINTER;
1696 }
1697 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1698 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1699 match builder.set_forces_from_flat(slice) {
1700 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1701 Err(e) => map_builder_err(e),
1702 }
1703}
1704#[unsafe(no_mangle)]
1709pub unsafe extern "C" fn rkr_frame_builder_set_displacements_from_flat(
1710 builder_handle: *mut RKRConFrameBuilder,
1711 displacements: *const f64,
1712 len: usize,
1713) -> RKRStatus {
1714 if builder_handle.is_null() || displacements.is_null() {
1715 return RKRStatus::RKR_STATUS_NULL_POINTER;
1716 }
1717 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1718 let slice = unsafe { std::slice::from_raw_parts(displacements, len) };
1719 match builder.set_displacements_from_flat(slice) {
1720 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1721 Err(e) => map_builder_err(e),
1722 }
1723}
1724#[unsafe(no_mangle)]
1729pub unsafe extern "C" fn rkr_frame_builder_set_spreads_from_flat(
1730 builder_handle: *mut RKRConFrameBuilder,
1731 spreads: *const f64,
1732 len: usize,
1733) -> RKRStatus {
1734 if builder_handle.is_null() || spreads.is_null() {
1735 return RKRStatus::RKR_STATUS_NULL_POINTER;
1736 }
1737 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1738 let slice = unsafe { std::slice::from_raw_parts(spreads, len) };
1739 match builder.set_spreads_from_flat(slice) {
1740 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1741 Err(e) => map_builder_err(e),
1742 }
1743}
1744#[unsafe(no_mangle)]
1748pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1749 builder_handle: *mut RKRConFrameBuilder,
1750 energies: *const f64,
1751 len: usize,
1752) -> RKRStatus {
1753 if builder_handle.is_null() || energies.is_null() {
1754 return RKRStatus::RKR_STATUS_NULL_POINTER;
1755 }
1756 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1757 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1758 match builder.set_atom_energies_from_flat(slice) {
1759 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1760 Err(e) => map_builder_err(e),
1761 }
1762}
1763#[unsafe(no_mangle)]
1767pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1768 builder_handle: *const RKRConFrameBuilder,
1769 index: usize,
1770 out_xyz: *mut f64,
1771) -> RKRStatus {
1772 if builder_handle.is_null() || out_xyz.is_null() {
1773 return RKRStatus::RKR_STATUS_NULL_POINTER;
1774 }
1775 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1776 match builder.get_atom_position(index) {
1777 Ok((x, y, z)) => unsafe {
1778 *out_xyz = x;
1779 *out_xyz.add(1) = y;
1780 *out_xyz.add(2) = z;
1781 RKRStatus::RKR_STATUS_SUCCESS
1782 },
1783 Err(e) => map_builder_err(e),
1784 }
1785}
1786#[unsafe(no_mangle)]
1793pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1794 builder_handle: *const RKRConFrameBuilder,
1795 index: usize,
1796 out_xyz: *mut f64,
1797 has_value: *mut bool,
1798) -> RKRStatus {
1799 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1800 return RKRStatus::RKR_STATUS_NULL_POINTER;
1801 }
1802 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1803 match builder.get_atom_velocity(index) {
1804 Ok(Some(v)) => unsafe {
1805 *out_xyz = v[0];
1806 *out_xyz.add(1) = v[1];
1807 *out_xyz.add(2) = v[2];
1808 *has_value = true;
1809 RKRStatus::RKR_STATUS_SUCCESS
1810 },
1811 Ok(None) => unsafe {
1812 *has_value = false;
1813 RKRStatus::RKR_STATUS_SUCCESS
1814 },
1815 Err(e) => map_builder_err(e),
1816 }
1817}
1818#[unsafe(no_mangle)]
1821pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1822 builder_handle: *const RKRConFrameBuilder,
1823 index: usize,
1824 out_xyz: *mut f64,
1825 has_value: *mut bool,
1826) -> RKRStatus {
1827 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1828 return RKRStatus::RKR_STATUS_NULL_POINTER;
1829 }
1830 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1831 match builder.get_atom_force(index) {
1832 Ok(Some(f)) => unsafe {
1833 *out_xyz = f[0];
1834 *out_xyz.add(1) = f[1];
1835 *out_xyz.add(2) = f[2];
1836 *has_value = true;
1837 RKRStatus::RKR_STATUS_SUCCESS
1838 },
1839 Ok(None) => unsafe {
1840 *has_value = false;
1841 RKRStatus::RKR_STATUS_SUCCESS
1842 },
1843 Err(e) => map_builder_err(e),
1844 }
1845}
1846#[unsafe(no_mangle)]
1852pub unsafe extern "C" fn rkr_frame_builder_get_atom_displacement(
1853 builder_handle: *const RKRConFrameBuilder,
1854 index: usize,
1855 out_xyz: *mut f64,
1856 has_value: *mut bool,
1857) -> RKRStatus {
1858 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1859 return RKRStatus::RKR_STATUS_NULL_POINTER;
1860 }
1861 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1862 match builder.get_atom_displacement(index) {
1863 Ok(Some(d)) => unsafe {
1864 *out_xyz = d[0];
1865 *out_xyz.add(1) = d[1];
1866 *out_xyz.add(2) = d[2];
1867 *has_value = true;
1868 RKRStatus::RKR_STATUS_SUCCESS
1869 },
1870 Ok(None) => unsafe {
1871 *has_value = false;
1872 RKRStatus::RKR_STATUS_SUCCESS
1873 },
1874 Err(e) => map_builder_err(e),
1875 }
1876}
1877#[unsafe(no_mangle)]
1883pub unsafe extern "C" fn rkr_frame_builder_get_atom_spread(
1884 builder_handle: *const RKRConFrameBuilder,
1885 index: usize,
1886 out_xyz: *mut f64,
1887 has_value: *mut bool,
1888) -> RKRStatus {
1889 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1890 return RKRStatus::RKR_STATUS_NULL_POINTER;
1891 }
1892 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1893 match builder.get_atom_spread(index) {
1894 Ok(Some(d)) => unsafe {
1895 *out_xyz = d[0];
1896 *out_xyz.add(1) = d[1];
1897 *out_xyz.add(2) = d[2];
1898 *has_value = true;
1899 RKRStatus::RKR_STATUS_SUCCESS
1900 },
1901 Ok(None) => unsafe {
1902 *has_value = false;
1903 RKRStatus::RKR_STATUS_SUCCESS
1904 },
1905 Err(e) => map_builder_err(e),
1906 }
1907}
1908#[unsafe(no_mangle)]
1914pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
1915 builder_handle: *const RKRConFrameBuilder,
1916 index: usize,
1917 out_value: *mut f64,
1918 has_value: *mut bool,
1919) -> RKRStatus {
1920 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
1921 return RKRStatus::RKR_STATUS_NULL_POINTER;
1922 }
1923 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1924 match builder.get_atom_energy(index) {
1925 Ok(Some(e)) => unsafe {
1926 *out_value = e;
1927 *has_value = true;
1928 RKRStatus::RKR_STATUS_SUCCESS
1929 },
1930 Ok(None) => unsafe {
1931 *has_value = false;
1932 RKRStatus::RKR_STATUS_SUCCESS
1933 },
1934 Err(e) => map_builder_err(e),
1935 }
1936}
1937#[unsafe(no_mangle)]
1941pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
1942 builder_handle: *const RKRConFrameBuilder,
1943 index: usize,
1944 out_mass: *mut f64,
1945) -> RKRStatus {
1946 if builder_handle.is_null() || out_mass.is_null() {
1947 return RKRStatus::RKR_STATUS_NULL_POINTER;
1948 }
1949 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1950 match builder.get_atom_mass(index) {
1951 Ok(m) => unsafe {
1952 *out_mass = m;
1953 RKRStatus::RKR_STATUS_SUCCESS
1954 },
1955 Err(e) => map_builder_err(e),
1956 }
1957}
1958pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
1981fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
1982 use crate::error::ParseError;
1983 match e {
1984 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
1985 RKRStatus::RKR_STATUS_DEVICE_MISMATCH
1986 }
1987 ParseError::ValidationError(ref msg)
1988 if msg.contains("no device allocator") || msg.contains("allocator") =>
1989 {
1990 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED
1991 }
1992 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1993 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1994 }
1995}
1996pub mod rkr_dl_type_code {
2000 pub const RKR_DL_INT: u8 = 0;
2001 pub const RKR_DL_UINT: u8 = 1;
2002 pub const RKR_DL_FLOAT: u8 = 2;
2003 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
2004 pub const RKR_DL_BFLOAT: u8 = 4;
2005 pub const RKR_DL_COMPLEX: u8 = 5;
2006 pub const RKR_DL_BOOL: u8 = 6;
2007}
2008
2009pub mod rkr_dl_device_type {
2011 pub const RKR_DL_CPU: i32 = 1;
2012 pub const RKR_DL_CUDA: i32 = 2;
2013 pub const RKR_DL_CUDA_HOST: i32 = 3;
2014}
2015
2016#[repr(C)]
2020#[derive(Clone, Copy, Debug)]
2021pub struct RKRDLDataType {
2022 pub code: u8,
2024 pub bits: u8,
2026 pub lanes: u16,
2028}
2029
2030#[repr(C)]
2033#[derive(Clone, Copy, Debug)]
2034pub struct RKRDLDevice {
2035 pub device_type: i32,
2037 pub device_id: i32,
2038}
2039
2040#[repr(C)]
2054#[derive(Clone, Copy, Debug)]
2055pub struct RKRDlpackExportOptions {
2056 pub dtype: RKRDLDataType,
2058 pub device: RKRDLDevice,
2060}
2061
2062impl Default for RKRDlpackExportOptions {
2063 fn default() -> Self {
2064 Self {
2065 dtype: RKRDLDataType {
2066 code: rkr_dl_type_code::RKR_DL_FLOAT,
2067 bits: 64,
2068 lanes: 1,
2069 },
2070 device: RKRDLDevice {
2071 device_type: rkr_dl_device_type::RKR_DL_CPU,
2072 device_id: 0,
2073 },
2074 }
2075 }
2076}
2077
2078fn resolve_dlpack_opts(
2081 opts: *const RKRDlpackExportOptions,
2082) -> Result<RKRDlpackExportOptions, RKRStatus> {
2083 let o = if opts.is_null() {
2084 RKRDlpackExportOptions::default()
2085 } else {
2086 unsafe { *opts }
2087 };
2088 let dt = o.device.device_type;
2089 if dt == rkr_dl_device_type::RKR_DL_CPU {
2090 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
2092 #[cfg(not(feature = "cuda"))]
2093 {
2094 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
2095 }
2096 #[cfg(feature = "cuda")]
2097 {
2098 }
2100 } else {
2101 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
2102 }
2103 if o.dtype.lanes != 1 {
2104 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
2105 }
2106 Ok(o)
2107}
2108
2109fn finish_dlpack_tensor<E: std::fmt::Display>(
2110 result: Result<dlpk::DLPackTensor, E>,
2111 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2112) -> RKRStatus {
2113 match result {
2114 Ok(tensor) => {
2115 let raw = tensor.into_raw();
2116 unsafe {
2117 *out_tensor = raw.as_ptr();
2118 }
2119 RKRStatus::RKR_STATUS_SUCCESS
2120 }
2121 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
2122 "DLPack export failed: {e}"
2123 ))),
2124 }
2125}
2126
2127fn export_f64_slice_as_dlpack(
2130 data: &[f64],
2131 shape: &[usize],
2132 dtype: RKRDLDataType,
2133 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2134) -> RKRStatus {
2135 use rkr_dl_type_code::*;
2136 if dtype.lanes != 1 {
2137 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2138 }
2139 macro_rules! arc_export {
2140 ($ty:ty, $map:expr) => {{
2141 let v: Vec<$ty> = data.iter().map($map).collect();
2142 match *shape {
2143 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
2144 Ok(a) => dlpk::DLPackTensor::try_from(a),
2145 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2146 },
2147 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
2148 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2149 }
2150 }};
2151 }
2152 let tensor = match (dtype.code, dtype.bits) {
2153 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
2154 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
2155 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
2156 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
2157 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
2158 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
2159 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
2160 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
2161 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
2162 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
2163 (RKR_DL_BOOL, 8) => {
2164 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
2166 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
2167 }
2168 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2170 };
2171 finish_dlpack_tensor(tensor, out_tensor)
2172}
2173
2174fn export_owned_array2_dlpack_opts(
2175 arr: &ndarray::ArcArray2<f64>,
2176 opts: &RKRDlpackExportOptions,
2177 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2178) -> RKRStatus {
2179 let (r, c) = arr.dim();
2180 let flat: Vec<f64> = arr.iter().copied().collect();
2181 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2182 #[cfg(feature = "cuda")]
2183 {
2184 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2186 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2187 }
2188 return finish_dlpack_tensor(
2189 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2190 &[r, c],
2191 &flat,
2192 opts.device.device_id,
2193 ),
2194 out_tensor,
2195 );
2196 }
2197 #[cfg(not(feature = "cuda"))]
2198 {
2199 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2200 }
2201 }
2202 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
2203}
2204
2205fn export_owned_array1_f64_dlpack_opts(
2206 arr: &ndarray::ArcArray1<f64>,
2207 opts: &RKRDlpackExportOptions,
2208 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2209) -> RKRStatus {
2210 let flat = arr.to_vec();
2211 let n = flat.len();
2212 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2213 #[cfg(feature = "cuda")]
2214 {
2215 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2216 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2217 }
2218 return finish_dlpack_tensor(
2219 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2220 &[n],
2221 &flat,
2222 opts.device.device_id,
2223 ),
2224 out_tensor,
2225 );
2226 }
2227 #[cfg(not(feature = "cuda"))]
2228 {
2229 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2230 }
2231 }
2232 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
2233}
2234
2235fn export_owned_array1_u64_dlpack(
2236 arr: &ndarray::ArcArray1<u64>,
2237 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2238) -> RKRStatus {
2239 finish_dlpack_tensor(dlpk::DLPackTensor::try_from(arr.clone()), out_tensor)
2240}
2241
2242fn export_owned_array1_u64_dlpack_opts(
2244 arr: &ndarray::ArcArray1<u64>,
2245 opts: &RKRDlpackExportOptions,
2246 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2247) -> RKRStatus {
2248 use rkr_dl_type_code::*;
2249 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
2250 return export_owned_array1_u64_dlpack(arr, out_tensor);
2251 }
2252 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
2253 let n = as_f64.len();
2254 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
2255}
2256#[unsafe(no_mangle)]
2267pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
2268 builder_handle: *const RKRConFrameBuilder,
2269 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2270) -> RKRStatus {
2271 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2272}
2273
2274#[unsafe(no_mangle)]
2281pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
2282 builder_handle: *const RKRConFrameBuilder,
2283 opts: *const RKRDlpackExportOptions,
2284 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2285) -> RKRStatus {
2286 if builder_handle.is_null() || out_tensor.is_null() {
2287 return RKRStatus::RKR_STATUS_NULL_POINTER;
2288 }
2289 let o = match resolve_dlpack_opts(opts) {
2290 Ok(o) => o,
2291 Err(st) => return st,
2292 };
2293 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2294 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
2295}
2296
2297#[unsafe(no_mangle)]
2307pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
2308 builder_handle: *const RKRConFrameBuilder,
2309 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2310) -> RKRStatus {
2311 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2312}
2313
2314#[unsafe(no_mangle)]
2315pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
2316 builder_handle: *const RKRConFrameBuilder,
2317 opts: *const RKRDlpackExportOptions,
2318 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2319) -> RKRStatus {
2320 if builder_handle.is_null() || out_tensor.is_null() {
2321 return RKRStatus::RKR_STATUS_NULL_POINTER;
2322 }
2323 let o = match resolve_dlpack_opts(opts) {
2324 Ok(o) => o,
2325 Err(st) => return st,
2326 };
2327 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2328 if !builder.has_velocities_section() {
2329 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2330 }
2331 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
2332}
2333
2334#[unsafe(no_mangle)]
2343pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
2344 builder_handle: *const RKRConFrameBuilder,
2345 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2346) -> RKRStatus {
2347 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2348}
2349
2350#[unsafe(no_mangle)]
2351pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
2352 builder_handle: *const RKRConFrameBuilder,
2353 opts: *const RKRDlpackExportOptions,
2354 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2355) -> RKRStatus {
2356 if builder_handle.is_null() || out_tensor.is_null() {
2357 return RKRStatus::RKR_STATUS_NULL_POINTER;
2358 }
2359 let o = match resolve_dlpack_opts(opts) {
2360 Ok(o) => o,
2361 Err(st) => return st,
2362 };
2363 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2364 if !builder.has_forces_section() {
2365 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2366 }
2367 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
2368}
2369
2370#[unsafe(no_mangle)]
2379pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack(
2380 builder_handle: *const RKRConFrameBuilder,
2381 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2382) -> RKRStatus {
2383 unsafe {
2384 rkr_frame_builder_displacements_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2385 }
2386}
2387
2388#[unsafe(no_mangle)]
2395pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack_ex(
2396 builder_handle: *const RKRConFrameBuilder,
2397 opts: *const RKRDlpackExportOptions,
2398 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2399) -> RKRStatus {
2400 if builder_handle.is_null() || out_tensor.is_null() {
2401 return RKRStatus::RKR_STATUS_NULL_POINTER;
2402 }
2403 let o = match resolve_dlpack_opts(opts) {
2404 Ok(o) => o,
2405 Err(st) => return st,
2406 };
2407 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2408 if !builder.has_displacements_section() {
2409 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2410 }
2411 export_owned_array2_dlpack_opts(builder.displacements_2d_ref(), &o, out_tensor)
2412}
2413
2414#[unsafe(no_mangle)]
2423pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack(
2424 builder_handle: *const RKRConFrameBuilder,
2425 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2426) -> RKRStatus {
2427 unsafe { rkr_frame_builder_spreads_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2428}
2429
2430#[unsafe(no_mangle)]
2437pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack_ex(
2438 builder_handle: *const RKRConFrameBuilder,
2439 opts: *const RKRDlpackExportOptions,
2440 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2441) -> RKRStatus {
2442 if builder_handle.is_null() || out_tensor.is_null() {
2443 return RKRStatus::RKR_STATUS_NULL_POINTER;
2444 }
2445 let o = match resolve_dlpack_opts(opts) {
2446 Ok(o) => o,
2447 Err(st) => return st,
2448 };
2449 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2450 if !builder.has_spreads_section() {
2451 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2452 }
2453 export_owned_array2_dlpack_opts(builder.spreads_2d_ref(), &o, out_tensor)
2454}
2455
2456#[unsafe(no_mangle)]
2465pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
2466 builder_handle: *const RKRConFrameBuilder,
2467 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2468) -> RKRStatus {
2469 unsafe {
2470 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2471 }
2472}
2473
2474#[unsafe(no_mangle)]
2475pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
2476 builder_handle: *const RKRConFrameBuilder,
2477 opts: *const RKRDlpackExportOptions,
2478 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2479) -> RKRStatus {
2480 if builder_handle.is_null() || out_tensor.is_null() {
2481 return RKRStatus::RKR_STATUS_NULL_POINTER;
2482 }
2483 let o = match resolve_dlpack_opts(opts) {
2484 Ok(o) => o,
2485 Err(st) => return st,
2486 };
2487 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2488 if !builder.has_energies_section() {
2489 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2490 }
2491 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
2492}
2493
2494#[unsafe(no_mangle)]
2500pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
2501 builder_handle: *const RKRConFrameBuilder,
2502 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2503) -> RKRStatus {
2504 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2505}
2506
2507#[unsafe(no_mangle)]
2508pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
2509 builder_handle: *const RKRConFrameBuilder,
2510 opts: *const RKRDlpackExportOptions,
2511 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2512) -> RKRStatus {
2513 if builder_handle.is_null() || out_tensor.is_null() {
2514 return RKRStatus::RKR_STATUS_NULL_POINTER;
2515 }
2516 let o = match resolve_dlpack_opts(opts) {
2517 Ok(o) => o,
2518 Err(st) => return st,
2519 };
2520 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2521 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
2522}
2523#[unsafe(no_mangle)]
2529pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
2530 builder_handle: *const RKRConFrameBuilder,
2531 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2532) -> RKRStatus {
2533 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2534}
2535
2536#[unsafe(no_mangle)]
2537pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2538 builder_handle: *const RKRConFrameBuilder,
2539 opts: *const RKRDlpackExportOptions,
2540 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2541) -> RKRStatus {
2542 if builder_handle.is_null() || out_tensor.is_null() {
2543 return RKRStatus::RKR_STATUS_NULL_POINTER;
2544 }
2545 let o = match resolve_dlpack_opts(opts) {
2546 Ok(o) => o,
2547 Err(st) => return st,
2548 };
2549 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2550 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2551}
2552#[unsafe(no_mangle)]
2576pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2577 builder_handle: *mut RKRConFrameBuilder,
2578) -> *mut f64 {
2579 if builder_handle.is_null() {
2580 return std::ptr::null_mut();
2581 }
2582 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2583 builder
2584 .positions_view_mut()
2585 .as_slice_memory_order_mut()
2586 .map(|s| s.as_mut_ptr())
2587 .unwrap_or(std::ptr::null_mut())
2588}
2589#[unsafe(no_mangle)]
2596pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
2597 builder_handle: *mut RKRConFrameBuilder,
2598) -> *mut f64 {
2599 if builder_handle.is_null() {
2600 return std::ptr::null_mut();
2601 }
2602 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2603 if !builder.has_velocities_section() {
2604 return std::ptr::null_mut();
2605 }
2606 let slice = builder.velocities_mut();
2607 if slice.is_empty() {
2608 std::ptr::null_mut()
2609 } else {
2610 slice.as_mut_ptr()
2611 }
2612}
2613#[unsafe(no_mangle)]
2619pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2620 builder_handle: *mut RKRConFrameBuilder,
2621) -> *mut f64 {
2622 if builder_handle.is_null() {
2623 return std::ptr::null_mut();
2624 }
2625 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2626 if !builder.has_forces_section() {
2627 return std::ptr::null_mut();
2628 }
2629 let slice = builder.forces_mut();
2630 if slice.is_empty() {
2631 std::ptr::null_mut()
2632 } else {
2633 slice.as_mut_ptr()
2634 }
2635}
2636#[unsafe(no_mangle)]
2642pub unsafe extern "C" fn rkr_frame_builder_displacements_data(
2643 builder_handle: *mut RKRConFrameBuilder,
2644) -> *mut f64 {
2645 if builder_handle.is_null() {
2646 return std::ptr::null_mut();
2647 }
2648 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2649 if !builder.has_displacements_section() {
2650 return std::ptr::null_mut();
2651 }
2652 let slice = builder.displacements_mut();
2653 if slice.is_empty() {
2654 std::ptr::null_mut()
2655 } else {
2656 slice.as_mut_ptr()
2657 }
2658}
2659#[unsafe(no_mangle)]
2665pub unsafe extern "C" fn rkr_frame_builder_spreads_data(
2666 builder_handle: *mut RKRConFrameBuilder,
2667) -> *mut f64 {
2668 if builder_handle.is_null() {
2669 return std::ptr::null_mut();
2670 }
2671 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2672 if !builder.has_spreads_section() {
2673 return std::ptr::null_mut();
2674 }
2675 let slice = builder.spreads_mut();
2676 if slice.is_empty() {
2677 std::ptr::null_mut()
2678 } else {
2679 slice.as_mut_ptr()
2680 }
2681}
2682#[unsafe(no_mangle)]
2688pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2689 builder_handle: *mut RKRConFrameBuilder,
2690) -> *mut f64 {
2691 if builder_handle.is_null() {
2692 return std::ptr::null_mut();
2693 }
2694 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2695 if !builder.has_energies_section() {
2696 return std::ptr::null_mut();
2697 }
2698 let slice = builder.atom_energies_mut();
2699 if slice.is_empty() {
2700 std::ptr::null_mut()
2701 } else {
2702 slice.as_mut_ptr()
2703 }
2704}
2705#[unsafe(no_mangle)]
2710pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2711 builder_handle: *mut RKRConFrameBuilder,
2712) -> *mut f64 {
2713 if builder_handle.is_null() {
2714 return std::ptr::null_mut();
2715 }
2716 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2717 let slice = builder.masses_mut();
2718 if slice.is_empty() {
2719 std::ptr::null_mut()
2720 } else {
2721 slice.as_mut_ptr()
2722 }
2723}
2724#[unsafe(no_mangle)]
2729pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2730 builder_handle: *const RKRConFrameBuilder,
2731) -> *const u64 {
2732 if builder_handle.is_null() {
2733 return std::ptr::null();
2734 }
2735 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2736 let slice = builder.atom_ids();
2737 if slice.is_empty() {
2738 std::ptr::null()
2739 } else {
2740 slice.as_ptr()
2741 }
2742}
2743#[unsafe(no_mangle)]
2755#[allow(clippy::too_many_arguments)]
2756pub unsafe extern "C" fn rkr_frame_add_atom_full(
2757 builder_handle: *mut RKRConFrameBuilder,
2758 symbol: *const c_char,
2759 x: f64,
2760 y: f64,
2761 z: f64,
2762 fixed_x: bool,
2763 fixed_y: bool,
2764 fixed_z: bool,
2765 atom_id: u64,
2766 mass: f64,
2767 velocity: *const f64,
2768 force: *const f64,
2769) -> RKRStatus {
2770 let velocity = if velocity.is_null() {
2771 None
2772 } else {
2773 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
2774 };
2775 let force = if force.is_null() {
2776 None
2777 } else {
2778 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
2779 };
2780 unsafe {
2781 add_builder_atom(
2782 builder_handle,
2783 symbol,
2784 x,
2785 y,
2786 z,
2787 [fixed_x, fixed_y, fixed_z],
2788 atom_id,
2789 mass,
2790 velocity,
2791 force,
2792 )
2793 }
2794}
2795#[unsafe(no_mangle)]
2811pub unsafe extern "C" fn rkr_frame_new(
2812 cell: *const f64,
2813 angles: *const f64,
2814 prebox0: *const c_char,
2815 prebox1: *const c_char,
2816 postbox0: *const c_char,
2817 postbox1: *const c_char,
2818) -> *mut RKRConFrameBuilder {
2819 if cell.is_null() || angles.is_null() {
2820 return ptr::null_mut();
2821 }
2822 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
2823 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
2824 let get_str = |p: *const c_char| -> String {
2825 if p.is_null() {
2826 String::new()
2827 } else {
2828 unsafe { CStr::from_ptr(p) }
2829 .to_str()
2830 .unwrap_or("")
2831 .to_string()
2832 }
2833 };
2834 let _ = get_str(prebox1);
2837 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
2838 builder
2839 .prebox_header(get_str(prebox0))
2840 .postbox_header([get_str(postbox0), get_str(postbox1)]);
2841 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
2842}
2843#[unsafe(no_mangle)]
2849pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
2850 builder_handle: *mut RKRConFrameBuilder,
2851 metadata_json: *const c_char,
2852) -> RKRStatus {
2853 if builder_handle.is_null() || metadata_json.is_null() {
2854 return RKRStatus::RKR_STATUS_NULL_POINTER;
2855 }
2856 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2857 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
2858 Ok(s) => s,
2859 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2860 };
2861 match builder.set_metadata_json(metadata_json) {
2862 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
2863 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
2864 }
2865}
2866#[unsafe(no_mangle)]
2872pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
2873 builder_handle: *mut RKRConFrameBuilder,
2874 key: *const c_char,
2875 value: f64,
2876) -> RKRStatus {
2877 if builder_handle.is_null() || key.is_null() {
2878 return RKRStatus::RKR_STATUS_NULL_POINTER;
2879 }
2880 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2881 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2882 Ok(s) => s,
2883 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2884 };
2885 builder.set_scalar_metadata(key, value);
2886 RKRStatus::RKR_STATUS_SUCCESS
2887}
2888#[unsafe(no_mangle)]
2894pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
2895 builder_handle: *mut RKRConFrameBuilder,
2896 key: *const c_char,
2897 value: *const c_char,
2898) -> RKRStatus {
2899 if builder_handle.is_null() || key.is_null() || value.is_null() {
2900 return RKRStatus::RKR_STATUS_NULL_POINTER;
2901 }
2902 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2903 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2904 Ok(s) => s,
2905 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2906 };
2907 let value = match unsafe { CStr::from_ptr(value).to_str() } {
2908 Ok(s) => s,
2909 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2910 };
2911 builder.set_string_metadata(key, value);
2912 RKRStatus::RKR_STATUS_SUCCESS
2913}
2914#[unsafe(no_mangle)]
2920pub unsafe extern "C" fn rkr_frame_builder_set_energy(
2921 builder_handle: *mut RKRConFrameBuilder,
2922 energy: f64,
2923) -> RKRStatus {
2924 if builder_handle.is_null() {
2925 return RKRStatus::RKR_STATUS_NULL_POINTER;
2926 }
2927 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2928 builder.set_energy(energy);
2929 RKRStatus::RKR_STATUS_SUCCESS
2930}
2931#[unsafe(no_mangle)]
2937pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
2938 builder_handle: *mut RKRConFrameBuilder,
2939 idx: u64,
2940) -> RKRStatus {
2941 if builder_handle.is_null() {
2942 return RKRStatus::RKR_STATUS_NULL_POINTER;
2943 }
2944 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2945 builder.set_frame_index(idx);
2946 RKRStatus::RKR_STATUS_SUCCESS
2947}
2948#[unsafe(no_mangle)]
2954pub unsafe extern "C" fn rkr_frame_builder_set_time(
2955 builder_handle: *mut RKRConFrameBuilder,
2956 time: f64,
2957) -> RKRStatus {
2958 if builder_handle.is_null() {
2959 return RKRStatus::RKR_STATUS_NULL_POINTER;
2960 }
2961 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2962 builder.set_time(time);
2963 RKRStatus::RKR_STATUS_SUCCESS
2964}
2965#[unsafe(no_mangle)]
2971pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
2972 builder_handle: *mut RKRConFrameBuilder,
2973 dt: f64,
2974) -> RKRStatus {
2975 if builder_handle.is_null() {
2976 return RKRStatus::RKR_STATUS_NULL_POINTER;
2977 }
2978 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2979 builder.set_timestep(dt);
2980 RKRStatus::RKR_STATUS_SUCCESS
2981}
2982#[unsafe(no_mangle)]
2988pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
2989 builder_handle: *mut RKRConFrameBuilder,
2990 bead: u64,
2991) -> RKRStatus {
2992 if builder_handle.is_null() {
2993 return RKRStatus::RKR_STATUS_NULL_POINTER;
2994 }
2995 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2996 builder.set_neb_bead(bead);
2997 RKRStatus::RKR_STATUS_SUCCESS
2998}
2999#[unsafe(no_mangle)]
3005pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
3006 builder_handle: *mut RKRConFrameBuilder,
3007 band: u64,
3008) -> RKRStatus {
3009 if builder_handle.is_null() {
3010 return RKRStatus::RKR_STATUS_NULL_POINTER;
3011 }
3012 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3013 builder.set_neb_band(band);
3014 RKRStatus::RKR_STATUS_SUCCESS
3015}
3016#[unsafe(no_mangle)]
3033pub unsafe extern "C" fn rkr_frame_add_atom(
3034 builder_handle: *mut RKRConFrameBuilder,
3035 symbol: *const c_char,
3036 x: f64,
3037 y: f64,
3038 z: f64,
3039 is_fixed: bool,
3040 atom_id: u64,
3041 mass: f64,
3042) -> RKRStatus {
3043 unsafe {
3044 add_builder_atom(
3045 builder_handle,
3046 symbol,
3047 x,
3048 y,
3049 z,
3050 [is_fixed; 3],
3051 atom_id,
3052 mass,
3053 None,
3054 None,
3055 )
3056 }
3057}
3058#[unsafe(no_mangle)]
3065pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
3066 builder_handle: *mut RKRConFrameBuilder,
3067 symbol: *const c_char,
3068 x: f64,
3069 y: f64,
3070 z: f64,
3071 fixed_x: bool,
3072 fixed_y: bool,
3073 fixed_z: bool,
3074 atom_id: u64,
3075 mass: f64,
3076) -> RKRStatus {
3077 unsafe {
3078 add_builder_atom(
3079 builder_handle,
3080 symbol,
3081 x,
3082 y,
3083 z,
3084 [fixed_x, fixed_y, fixed_z],
3085 atom_id,
3086 mass,
3087 None,
3088 None,
3089 )
3090 }
3091}
3092#[unsafe(no_mangle)]
3099pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
3100 builder_handle: *mut RKRConFrameBuilder,
3101 symbol: *const c_char,
3102 x: f64,
3103 y: f64,
3104 z: f64,
3105 is_fixed: bool,
3106 atom_id: u64,
3107 mass: f64,
3108 vx: f64,
3109 vy: f64,
3110 vz: f64,
3111) -> RKRStatus {
3112 unsafe {
3113 add_builder_atom(
3114 builder_handle,
3115 symbol,
3116 x,
3117 y,
3118 z,
3119 [is_fixed; 3],
3120 atom_id,
3121 mass,
3122 Some([vx, vy, vz]),
3123 None,
3124 )
3125 }
3126}
3127#[unsafe(no_mangle)]
3134pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
3135 builder_handle: *mut RKRConFrameBuilder,
3136 symbol: *const c_char,
3137 x: f64,
3138 y: f64,
3139 z: f64,
3140 fixed_x: bool,
3141 fixed_y: bool,
3142 fixed_z: bool,
3143 atom_id: u64,
3144 mass: f64,
3145 vx: f64,
3146 vy: f64,
3147 vz: f64,
3148) -> RKRStatus {
3149 unsafe {
3150 add_builder_atom(
3151 builder_handle,
3152 symbol,
3153 x,
3154 y,
3155 z,
3156 [fixed_x, fixed_y, fixed_z],
3157 atom_id,
3158 mass,
3159 Some([vx, vy, vz]),
3160 None,
3161 )
3162 }
3163}
3164#[unsafe(no_mangle)]
3171pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
3172 builder_handle: *mut RKRConFrameBuilder,
3173 symbol: *const c_char,
3174 x: f64,
3175 y: f64,
3176 z: f64,
3177 is_fixed: bool,
3178 atom_id: u64,
3179 mass: f64,
3180 fx: f64,
3181 fy: f64,
3182 fz: f64,
3183) -> RKRStatus {
3184 unsafe {
3185 add_builder_atom(
3186 builder_handle,
3187 symbol,
3188 x,
3189 y,
3190 z,
3191 [is_fixed; 3],
3192 atom_id,
3193 mass,
3194 None,
3195 Some([fx, fy, fz]),
3196 )
3197 }
3198}
3199#[unsafe(no_mangle)]
3206pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
3207 builder_handle: *mut RKRConFrameBuilder,
3208 symbol: *const c_char,
3209 x: f64,
3210 y: f64,
3211 z: f64,
3212 fixed_x: bool,
3213 fixed_y: bool,
3214 fixed_z: bool,
3215 atom_id: u64,
3216 mass: f64,
3217 fx: f64,
3218 fy: f64,
3219 fz: f64,
3220) -> RKRStatus {
3221 unsafe {
3222 add_builder_atom(
3223 builder_handle,
3224 symbol,
3225 x,
3226 y,
3227 z,
3228 [fixed_x, fixed_y, fixed_z],
3229 atom_id,
3230 mass,
3231 None,
3232 Some([fx, fy, fz]),
3233 )
3234 }
3235}
3236#[unsafe(no_mangle)]
3243pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
3244 builder_handle: *mut RKRConFrameBuilder,
3245 symbol: *const c_char,
3246 x: f64,
3247 y: f64,
3248 z: f64,
3249 is_fixed: bool,
3250 atom_id: u64,
3251 mass: f64,
3252 vx: f64,
3253 vy: f64,
3254 vz: f64,
3255 fx: f64,
3256 fy: f64,
3257 fz: f64,
3258) -> RKRStatus {
3259 unsafe {
3260 add_builder_atom(
3261 builder_handle,
3262 symbol,
3263 x,
3264 y,
3265 z,
3266 [is_fixed; 3],
3267 atom_id,
3268 mass,
3269 Some([vx, vy, vz]),
3270 Some([fx, fy, fz]),
3271 )
3272 }
3273}
3274#[unsafe(no_mangle)]
3281pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
3282 builder_handle: *mut RKRConFrameBuilder,
3283 symbol: *const c_char,
3284 x: f64,
3285 y: f64,
3286 z: f64,
3287 fixed_x: bool,
3288 fixed_y: bool,
3289 fixed_z: bool,
3290 atom_id: u64,
3291 mass: f64,
3292 vx: f64,
3293 vy: f64,
3294 vz: f64,
3295 fx: f64,
3296 fy: f64,
3297 fz: f64,
3298) -> RKRStatus {
3299 unsafe {
3300 add_builder_atom(
3301 builder_handle,
3302 symbol,
3303 x,
3304 y,
3305 z,
3306 [fixed_x, fixed_y, fixed_z],
3307 atom_id,
3308 mass,
3309 Some([vx, vy, vz]),
3310 Some([fx, fy, fz]),
3311 )
3312 }
3313}
3314#[unsafe(no_mangle)]
3323pub unsafe extern "C" fn rkr_frame_builder_build(
3324 builder_handle: *mut RKRConFrameBuilder,
3325) -> *mut RKRConFrame {
3326 if builder_handle.is_null() {
3327 return ptr::null_mut();
3328 }
3329 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3330 match builder.build() {
3331 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3332 Err(_) => ptr::null_mut(),
3333 }
3334}
3335#[unsafe(no_mangle)]
3340pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
3341 if !builder_handle.is_null() {
3342 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3343 }
3344}
3345#[unsafe(no_mangle)]
3362pub unsafe extern "C" fn rkr_frame_builder_clone(
3363 builder_handle: *const RKRConFrameBuilder,
3364) -> *mut RKRConFrameBuilder {
3365 if builder_handle.is_null() {
3366 return std::ptr::null_mut();
3367 }
3368 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3369 let cloned = builder.clone();
3370 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
3371}
3372#[unsafe(no_mangle)]
3378pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3379 let filename = match unsafe { cstr_path(filename_c) } {
3380 Some(s) => s,
3381 None => return ptr::null_mut(),
3382 };
3383 match crate::compression::gzip_writer(Path::new(filename)) {
3384 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3385 Err(_) => ptr::null_mut(),
3386 }
3387}
3388#[unsafe(no_mangle)]
3395pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
3396 filename_c: *const c_char,
3397 precision: u8,
3398) -> *mut RKRConFrameWriter {
3399 let filename = match unsafe { cstr_path(filename_c) } {
3400 Some(s) => s,
3401 None => return ptr::null_mut(),
3402 };
3403 match crate::compression::gzip_writer(Path::new(filename)) {
3404 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3405 Err(_) => ptr::null_mut(),
3406 }
3407}
3408#[cfg(feature = "zstd")]
3418#[unsafe(no_mangle)]
3419pub unsafe extern "C" fn create_writer_zstd_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::zstd_writer(Path::new(filename)) {
3425 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3426 Err(_) => ptr::null_mut(),
3427 }
3428}
3429#[cfg(feature = "zstd")]
3440#[unsafe(no_mangle)]
3441pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3442 filename_c: *const c_char,
3443 precision: u8,
3444) -> *mut RKRConFrameWriter {
3445 let filename = match unsafe { cstr_path(filename_c) } {
3446 Some(s) => s,
3447 None => return ptr::null_mut(),
3448 };
3449 match crate::compression::zstd_writer(Path::new(filename)) {
3450 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3451 Err(_) => ptr::null_mut(),
3452 }
3453}
3454#[unsafe(no_mangle)]
3466pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
3467 if filename_c.is_null() {
3468 return ptr::null_mut();
3469 }
3470 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3471 Ok(s) => s,
3472 Err(_) => return ptr::null_mut(),
3473 };
3474 match iterators::read_first_frame(Path::new(filename)) {
3475 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3476 Err(_) => ptr::null_mut(),
3477 }
3478}
3479#[unsafe(no_mangle)]
3488pub unsafe extern "C" fn rkr_read_all_frames(
3489 filename_c: *const c_char,
3490 num_frames: *mut usize,
3491) -> *mut *mut RKRConFrame {
3492 if filename_c.is_null() || num_frames.is_null() {
3493 return ptr::null_mut();
3494 }
3495 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3496 Ok(s) => s,
3497 Err(_) => return ptr::null_mut(),
3498 };
3499 match iterators::read_all_frames(Path::new(filename)) {
3500 Ok(frames) => pack_frame_handles(frames, num_frames),
3501 Err(_) => ptr::null_mut(),
3502 }
3503}
3504
3505#[unsafe(no_mangle)]
3515pub unsafe extern "C" fn rkr_read_all_frames_n_threads(
3516 filename_c: *const c_char,
3517 num_frames: *mut usize,
3518 n_threads: usize,
3519) -> *mut *mut RKRConFrame {
3520 if filename_c.is_null() || num_frames.is_null() {
3521 return ptr::null_mut();
3522 }
3523 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3524 Ok(s) => s,
3525 Err(_) => return ptr::null_mut(),
3526 };
3527 let threads = if n_threads == 0 {
3528 None
3529 } else {
3530 Some(n_threads)
3531 };
3532 match iterators::read_all_frames_with_threads(Path::new(filename), threads) {
3533 Ok(frames) => pack_frame_handles(frames, num_frames),
3534 Err(_) => ptr::null_mut(),
3535 }
3536}
3537
3538fn pack_frame_handles(frames: Vec<ConFrame>, num_frames: *mut usize) -> *mut *mut RKRConFrame {
3540 let count = frames.len();
3541 let mut handles: Vec<*mut RKRConFrame> = frames
3542 .into_iter()
3543 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3544 .collect();
3545 handles.shrink_to_fit();
3546 debug_assert_eq!(handles.len(), handles.capacity());
3547 let ptr = handles.as_mut_ptr();
3548 std::mem::forget(handles);
3549 unsafe { *num_frames = count };
3550 ptr
3551}
3552#[unsafe(no_mangle)]
3558pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3559 if frames.is_null() {
3560 return;
3561 }
3562 unsafe {
3563 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3564 for handle in handles {
3565 if !handle.is_null() {
3566 let _ = Box::from_raw(handle as *mut ConFrame);
3567 }
3568 }
3569 }
3570}
3571#[unsafe(no_mangle)]
3577pub unsafe extern "C" fn free_rkr_frame_ptr_array(
3578 frames: *mut *mut RKRConFrame,
3579 num_frames: usize,
3580) {
3581 if frames.is_null() {
3582 return;
3583 }
3584 unsafe {
3585 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3586 }
3587}
3588
3589#[cfg(feature = "metatensor")]
3604#[unsafe(no_mangle)]
3605pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3606 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3607}
3608#[cfg(feature = "metatensor")]
3610#[unsafe(no_mangle)]
3611pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3612 frame_handle: *const RKRConFrame,
3613 out_block: *mut *mut metatensor::c_api::mts_block_t,
3614) -> RKRStatus {
3615 if frame_handle.is_null() || out_block.is_null() {
3616 return RKRStatus::RKR_STATUS_NULL_POINTER;
3617 }
3618 unsafe { *out_block = std::ptr::null_mut() };
3619 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3620 return RKRStatus::RKR_STATUS_NULL_POINTER;
3621 };
3622 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3623 crate::metatensor_export::frame_positions_block(frame)
3624 })) {
3625 Ok(Ok(b)) => {
3626 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3627 RKRStatus::RKR_STATUS_SUCCESS
3628 }
3629 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3630 }
3631}
3632#[cfg(feature = "metatensor")]
3633#[unsafe(no_mangle)]
3634pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3635 frame_handle: *const RKRConFrame,
3636 out_block: *mut *mut metatensor::c_api::mts_block_t,
3637) -> RKRStatus {
3638 if frame_handle.is_null() || out_block.is_null() {
3639 return RKRStatus::RKR_STATUS_NULL_POINTER;
3640 }
3641 unsafe { *out_block = std::ptr::null_mut() };
3642 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3643 return RKRStatus::RKR_STATUS_NULL_POINTER;
3644 };
3645 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3646 crate::metatensor_export::frame_velocities_block(frame)
3647 })) {
3648 Ok(Ok(Some(b))) => {
3649 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3650 RKRStatus::RKR_STATUS_SUCCESS
3651 }
3652 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3653 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3654 }
3655}
3656#[cfg(feature = "metatensor")]
3657#[unsafe(no_mangle)]
3658pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3659 frame_handle: *const RKRConFrame,
3660 out_block: *mut *mut metatensor::c_api::mts_block_t,
3661) -> RKRStatus {
3662 if frame_handle.is_null() || out_block.is_null() {
3663 return RKRStatus::RKR_STATUS_NULL_POINTER;
3664 }
3665 unsafe { *out_block = std::ptr::null_mut() };
3666 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3667 return RKRStatus::RKR_STATUS_NULL_POINTER;
3668 };
3669 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3670 crate::metatensor_export::frame_forces_block(frame)
3671 })) {
3672 Ok(Ok(Some(b))) => {
3673 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3674 RKRStatus::RKR_STATUS_SUCCESS
3675 }
3676 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3677 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3678 }
3679}
3680#[cfg(feature = "metatensor")]
3681#[unsafe(no_mangle)]
3682pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3683 frame_handle: *const RKRConFrame,
3684 out_block: *mut *mut metatensor::c_api::mts_block_t,
3685) -> RKRStatus {
3686 if frame_handle.is_null() || out_block.is_null() {
3687 return RKRStatus::RKR_STATUS_NULL_POINTER;
3688 }
3689 unsafe { *out_block = std::ptr::null_mut() };
3690 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3691 return RKRStatus::RKR_STATUS_NULL_POINTER;
3692 };
3693 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3694 crate::metatensor_export::frame_energies_block(frame)
3695 })) {
3696 Ok(Ok(Some(b))) => {
3697 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3698 RKRStatus::RKR_STATUS_SUCCESS
3699 }
3700 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3701 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3702 }
3703}
3704#[cfg(not(feature = "zstd"))]
3705#[unsafe(no_mangle)]
3706pub unsafe extern "C" fn create_writer_zstd_c(
3707 _filename_c: *const c_char,
3708) -> *mut RKRConFrameWriter {
3709 ptr::null_mut()
3710}
3711#[cfg(not(feature = "zstd"))]
3712#[unsafe(no_mangle)]
3713pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3714 _filename_c: *const c_char,
3715 _precision: u8,
3716) -> *mut RKRConFrameWriter {
3717 ptr::null_mut()
3718}
3719#[cfg(not(feature = "metatensor"))]
3723pub struct mts_block_t {
3726 _private: [u8; 0],
3727}
3728#[cfg(not(feature = "metatensor"))]
3729#[unsafe(no_mangle)]
3730pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3731#[cfg(not(feature = "metatensor"))]
3732#[unsafe(no_mangle)]
3733pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3734 _frame_handle: *const RKRConFrame,
3735 out_block: *mut *mut mts_block_t,
3736) -> RKRStatus {
3737 if !out_block.is_null() {
3738 unsafe { *out_block = std::ptr::null_mut() };
3739 }
3740 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3741}
3742#[cfg(not(feature = "metatensor"))]
3743#[unsafe(no_mangle)]
3744pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3745 _frame_handle: *const RKRConFrame,
3746 out_block: *mut *mut mts_block_t,
3747) -> RKRStatus {
3748 if !out_block.is_null() {
3749 unsafe { *out_block = std::ptr::null_mut() };
3750 }
3751 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3752}
3753#[cfg(not(feature = "metatensor"))]
3754#[unsafe(no_mangle)]
3755pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3756 _frame_handle: *const RKRConFrame,
3757 out_block: *mut *mut mts_block_t,
3758) -> RKRStatus {
3759 if !out_block.is_null() {
3760 unsafe { *out_block = std::ptr::null_mut() };
3761 }
3762 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3763}
3764#[cfg(not(feature = "metatensor"))]
3765#[unsafe(no_mangle)]
3766pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3767 _frame_handle: *const RKRConFrame,
3768 out_block: *mut *mut mts_block_t,
3769) -> RKRStatus {
3770 if !out_block.is_null() {
3771 unsafe { *out_block = std::ptr::null_mut() };
3772 }
3773 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3774}
3775#[unsafe(no_mangle)]
3778pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3779 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3780 return 0;
3781 };
3782 frame.atom_data.len()
3783}
3784#[unsafe(no_mangle)]
3786pub unsafe extern "C" fn rkr_frame_xyz_view(
3787 frame_handle: *const RKRConFrame,
3788 out: *mut RKRArrayView,
3789) -> RKRStatus {
3790 fill_array2_view(frame_handle, out, |f| &f.positions, false)
3791}
3792
3793#[unsafe(no_mangle)]
3795pub unsafe extern "C" fn rkr_frame_velocities_view(
3796 frame_handle: *const RKRConFrame,
3797 out: *mut RKRArrayView,
3798) -> RKRStatus {
3799 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
3800}
3801
3802#[unsafe(no_mangle)]
3804pub unsafe extern "C" fn rkr_frame_forces_view(
3805 frame_handle: *const RKRConFrame,
3806 out: *mut RKRArrayView,
3807) -> RKRStatus {
3808 fill_array2_view(frame_handle, out, |f| &f.forces, true)
3809}
3810
3811#[unsafe(no_mangle)]
3817pub unsafe extern "C" fn rkr_frame_has_displacements(frame_handle: *const RKRConFrame) -> bool {
3818 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
3819 Some(f) => f.has_displacements(),
3820 None => false,
3821 }
3822}
3823
3824#[unsafe(no_mangle)]
3829pub unsafe extern "C" fn rkr_frame_displacements_view(
3830 frame_handle: *const RKRConFrame,
3831 out: *mut RKRArrayView,
3832) -> RKRStatus {
3833 fill_array2_view(frame_handle, out, |f| &f.displacements, true)
3834}
3835
3836#[unsafe(no_mangle)]
3842pub unsafe extern "C" fn rkr_frame_has_spreads(frame_handle: *const RKRConFrame) -> bool {
3843 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
3844 Some(f) => f.has_spreads(),
3845 None => false,
3846 }
3847}
3848
3849#[unsafe(no_mangle)]
3854pub unsafe extern "C" fn rkr_frame_spreads_view(
3855 frame_handle: *const RKRConFrame,
3856 out: *mut RKRArrayView,
3857) -> RKRStatus {
3858 fill_array2_view(frame_handle, out, |f| &f.spreads, true)
3859}
3860
3861#[unsafe(no_mangle)]
3863pub unsafe extern "C" fn rkr_frame_energies_view(
3864 frame_handle: *const RKRConFrame,
3865 out: *mut RKRArrayView,
3866) -> RKRStatus {
3867 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
3868}
3869
3870#[unsafe(no_mangle)]
3872pub unsafe extern "C" fn rkr_frame_masses_view(
3873 frame_handle: *const RKRConFrame,
3874 out: *mut RKRArrayView,
3875) -> RKRStatus {
3876 fill_array1_view(frame_handle, out, |f| &f.masses, false)
3877}
3878
3879#[unsafe(no_mangle)]
3881pub unsafe extern "C" fn rkr_frame_atom_ids_view(
3882 frame_handle: *const RKRConFrame,
3883 out: *mut RKRArrayView,
3884) -> RKRStatus {
3885 if frame_handle.is_null() || out.is_null() {
3886 return RKRStatus::RKR_STATUS_NULL_POINTER;
3887 }
3888 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3889 return RKRStatus::RKR_STATUS_NULL_POINTER;
3890 };
3891 let slice = frame.atom_ids.as_slice_memory_order();
3892 unsafe {
3893 *out = RKRArrayView {
3894 data: slice
3895 .map(|s| s.as_ptr() as *const std::ffi::c_void)
3896 .unwrap_or(std::ptr::null()),
3897 n: frame.atom_ids.len(),
3898 cols: 1,
3899 dtype_code: 1, dtype_bits: 64,
3901 };
3902 }
3903 RKRStatus::RKR_STATUS_SUCCESS
3904}
3905
3906#[unsafe(no_mangle)]
3909pub unsafe extern "C" fn rkr_frame_xyz_f64(
3910 frame_handle: *const RKRConFrame,
3911 n: *mut usize,
3912) -> *const f64 {
3913 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
3914}
3915
3916#[unsafe(no_mangle)]
3918pub unsafe extern "C" fn rkr_frame_velocities_f64(
3919 frame_handle: *const RKRConFrame,
3920 n: *mut usize,
3921) -> *const f64 {
3922 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
3923}
3924
3925#[unsafe(no_mangle)]
3927pub unsafe extern "C" fn rkr_frame_forces_f64(
3928 frame_handle: *const RKRConFrame,
3929 n: *mut usize,
3930) -> *const f64 {
3931 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
3932}
3933
3934#[unsafe(no_mangle)]
3939pub unsafe extern "C" fn rkr_frame_displacements_f64(
3940 frame_handle: *const RKRConFrame,
3941 n: *mut usize,
3942) -> *const f64 {
3943 f64_col_ptr(frame_handle, n, |f| f.displacements.f64_slice())
3944}
3945
3946#[unsafe(no_mangle)]
3951pub unsafe extern "C" fn rkr_frame_spreads_f64(
3952 frame_handle: *const RKRConFrame,
3953 n: *mut usize,
3954) -> *const f64 {
3955 f64_col_ptr(frame_handle, n, |f| f.spreads.f64_slice())
3956}
3957
3958fn fill_array2_view(
3959 frame_handle: *const RKRConFrame,
3960 out: *mut RKRArrayView,
3961 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
3962 section: bool,
3963) -> RKRStatus {
3964 if frame_handle.is_null() || out.is_null() {
3965 return RKRStatus::RKR_STATUS_NULL_POINTER;
3966 }
3967 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3968 return RKRStatus::RKR_STATUS_NULL_POINTER;
3969 };
3970 let arr = get(frame);
3971 if section && arr.nrows() == 0 {
3972 unsafe { *out = RKRArrayView::empty() };
3973 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3974 }
3975 unsafe { *out = RKRArrayView::from_array2(arr) };
3976 RKRStatus::RKR_STATUS_SUCCESS
3977}
3978
3979fn fill_array1_view(
3980 frame_handle: *const RKRConFrame,
3981 out: *mut RKRArrayView,
3982 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
3983 section: bool,
3984) -> RKRStatus {
3985 if frame_handle.is_null() || out.is_null() {
3986 return RKRStatus::RKR_STATUS_NULL_POINTER;
3987 }
3988 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3989 return RKRStatus::RKR_STATUS_NULL_POINTER;
3990 };
3991 let arr = get(frame);
3992 if section && arr.len() == 0 {
3993 unsafe { *out = RKRArrayView::empty() };
3994 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3995 }
3996 unsafe { *out = RKRArrayView::from_array1(arr) };
3997 RKRStatus::RKR_STATUS_SUCCESS
3998}
3999
4000fn f64_col_ptr(
4001 frame_handle: *const RKRConFrame,
4002 n: *mut usize,
4003 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
4004) -> *const f64 {
4005 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4006 if !n.is_null() {
4007 unsafe { *n = 0 };
4008 }
4009 return std::ptr::null();
4010 };
4011 match get(frame) {
4012 Some(s) if !s.is_empty() => {
4013 if !n.is_null() {
4014 unsafe { *n = s.len() / 3 };
4015 }
4016 s.as_ptr()
4017 }
4018 _ => {
4019 if !n.is_null() {
4020 unsafe { *n = 0 };
4021 }
4022 std::ptr::null()
4023 }
4024 }
4025}
4026
4027#[unsafe(no_mangle)]
4030pub unsafe extern "C" fn rkr_frame_copy_positions(
4031 frame_handle: *const RKRConFrame,
4032 out: *mut f64,
4033 out_len: usize,
4034) -> RKRStatus {
4035 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
4036}
4037fn copy_array2_f64(
4038 frame_handle: *const RKRConFrame,
4039 out: *mut f64,
4040 out_len: usize,
4041 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4042 section: bool,
4043) -> RKRStatus {
4044 if frame_handle.is_null() || out.is_null() {
4045 return RKRStatus::RKR_STATUS_NULL_POINTER;
4046 }
4047 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4048 return RKRStatus::RKR_STATUS_NULL_POINTER;
4049 };
4050 let arr = get(frame);
4051 let n = if arr.nrows() > 0 {
4052 arr.nrows()
4053 } else {
4054 frame.atom_data.len()
4055 };
4056 if section && arr.nrows() == 0 {
4057 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4058 }
4059 let need = n.saturating_mul(3);
4060 if out_len < need {
4061 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4062 }
4063 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
4064 if arr.nrows() == n {
4065 if let Some(src) = arr.f64_slice() {
4066 dest.copy_from_slice(&src[..need.min(src.len())]);
4067 return RKRStatus::RKR_STATUS_SUCCESS;
4068 }
4069 for i in 0..n {
4070 let row = arr.as_f64_row(i);
4071 dest[i * 3] = row[0];
4072 dest[i * 3 + 1] = row[1];
4073 dest[i * 3 + 2] = row[2];
4074 }
4075 return RKRStatus::RKR_STATUS_SUCCESS;
4076 }
4077 for (i, a) in frame.atom_data.iter().enumerate() {
4078 dest[i * 3] = a.x;
4079 dest[i * 3 + 1] = a.y;
4080 dest[i * 3 + 2] = a.z;
4081 }
4082 RKRStatus::RKR_STATUS_SUCCESS
4083}
4084
4085fn copy_array1_f64(
4086 frame_handle: *const RKRConFrame,
4087 out: *mut f64,
4088 out_len: usize,
4089 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4090 section: bool,
4091) -> RKRStatus {
4092 if frame_handle.is_null() || out.is_null() {
4093 return RKRStatus::RKR_STATUS_NULL_POINTER;
4094 }
4095 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4096 return RKRStatus::RKR_STATUS_NULL_POINTER;
4097 };
4098 let arr = get(frame);
4099 let n = if arr.len() > 0 {
4100 arr.len()
4101 } else {
4102 frame.atom_data.len()
4103 };
4104 if section && arr.len() == 0 {
4105 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4106 }
4107 if out_len < n {
4108 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4109 }
4110 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4111 if let Some(src) = arr.f64_slice() {
4112 dest.copy_from_slice(&src[..n.min(src.len())]);
4113 return RKRStatus::RKR_STATUS_SUCCESS;
4114 }
4115 for i in 0..n {
4116 dest[i] = arr.get_f64(i);
4117 }
4118 RKRStatus::RKR_STATUS_SUCCESS
4119}
4120
4121#[unsafe(no_mangle)]
4122pub unsafe extern "C" fn rkr_frame_copy_velocities(
4123 frame_handle: *const RKRConFrame,
4124 out: *mut f64,
4125 out_len: usize,
4126) -> RKRStatus {
4127 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
4128}
4129#[unsafe(no_mangle)]
4130pub unsafe extern "C" fn rkr_frame_copy_forces(
4131 frame_handle: *const RKRConFrame,
4132 out: *mut f64,
4133 out_len: usize,
4134) -> RKRStatus {
4135 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
4136}
4137#[unsafe(no_mangle)]
4143pub unsafe extern "C" fn rkr_frame_copy_displacements(
4144 frame_handle: *const RKRConFrame,
4145 out: *mut f64,
4146 out_len: usize,
4147) -> RKRStatus {
4148 copy_array2_f64(frame_handle, out, out_len, |f| &f.displacements, true)
4149}
4150#[unsafe(no_mangle)]
4156pub unsafe extern "C" fn rkr_frame_copy_spreads(
4157 frame_handle: *const RKRConFrame,
4158 out: *mut f64,
4159 out_len: usize,
4160) -> RKRStatus {
4161 copy_array2_f64(frame_handle, out, out_len, |f| &f.spreads, true)
4162}
4163#[unsafe(no_mangle)]
4164pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
4165 frame_handle: *const RKRConFrame,
4166 out: *mut f64,
4167 out_len: usize,
4168) -> RKRStatus {
4169 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
4170}
4171#[unsafe(no_mangle)]
4172pub unsafe extern "C" fn rkr_frame_copy_masses(
4173 frame_handle: *const RKRConFrame,
4174 out: *mut f64,
4175 out_len: usize,
4176) -> RKRStatus {
4177 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
4178}
4179#[unsafe(no_mangle)]
4180pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
4181 frame_handle: *const RKRConFrame,
4182 out: *mut u64,
4183 out_len: usize,
4184) -> RKRStatus {
4185 if frame_handle.is_null() || out.is_null() {
4186 return RKRStatus::RKR_STATUS_NULL_POINTER;
4187 }
4188 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4189 return RKRStatus::RKR_STATUS_NULL_POINTER;
4190 };
4191 let n = frame.atom_ids.len().max(frame.atom_data.len());
4192 if out_len < n {
4193 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4194 }
4195 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4196 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
4197 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
4198 return RKRStatus::RKR_STATUS_SUCCESS;
4199 }
4200 for (i, a) in frame.atom_data.iter().enumerate() {
4201 dest[i] = a.atom_id;
4202 }
4203 RKRStatus::RKR_STATUS_SUCCESS
4204}
4205fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
4206 let n = frame.atom_data.len();
4207 let mut data = Vec::with_capacity(n * 3);
4208 for a in &frame.atom_data {
4209 data.extend_from_slice(&[a.x, a.y, a.z]);
4210 }
4211 ndarray::ArcArray2::from_shape_vec((n, 3), data)
4212 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
4213}
4214
4215#[unsafe(no_mangle)]
4225pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
4226 frame_handle: *const RKRConFrame,
4227 device_type: i32,
4228 device_id: i32,
4229 _stream: i64,
4230 _max_version_major: u32,
4231 _max_version_minor: u32,
4232 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4233) -> RKRStatus {
4234 if frame_handle.is_null() || out_tensor.is_null() {
4235 return RKRStatus::RKR_STATUS_NULL_POINTER;
4236 }
4237 unsafe { *out_tensor = std::ptr::null_mut() };
4238 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
4239 dlpk::sys::DLDevice::cpu()
4240 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
4241 #[cfg(feature = "cuda")]
4242 {
4243 dlpk::sys::DLDevice::cuda(device_id)
4244 }
4245 #[cfg(not(feature = "cuda"))]
4246 {
4247 let _ = device_id;
4248 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4249 }
4250 } else {
4251 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4252 };
4253 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4254 return RKRStatus::RKR_STATUS_NULL_POINTER;
4255 };
4256 match frame.positions_as_dlpack(dl_device) {
4258 Ok(tensor) => {
4259 let raw = tensor.into_raw();
4260 unsafe {
4261 *out_tensor = raw.as_ptr();
4262 }
4263 RKRStatus::RKR_STATUS_SUCCESS
4264 }
4265 Err(e) => map_dlpack_err(e),
4266 }
4267}
4268
4269#[unsafe(no_mangle)]
4275pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
4276 frame_handle: *mut RKRConFrame,
4277 tensor: *const RKRDLManagedTensorVersioned,
4278) -> RKRStatus {
4279 if frame_handle.is_null() || tensor.is_null() {
4280 return RKRStatus::RKR_STATUS_NULL_POINTER;
4281 }
4282 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
4283 let dl = unsafe { &(*tensor).dl_tensor };
4284 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
4285 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4286 }
4287 let n = frame.atom_data.len();
4288 let need = n.saturating_mul(3);
4289 let ndim = dl.ndim as usize;
4290 let shape = if dl.shape.is_null() {
4291 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4292 } else {
4293 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
4294 };
4295 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
4296 need
4297 } else if ndim == 1 && shape[0] == need as i64 {
4298 need
4299 } else {
4300 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4301 };
4302 let code = dl.dtype.code as u8;
4303 let bits = dl.dtype.bits;
4304 if dl.data.is_null() {
4305 return RKRStatus::RKR_STATUS_NULL_POINTER;
4306 }
4307 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
4308 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
4309 s.to_vec()
4310 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
4311 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
4312 s.iter().map(|&x| x as f64).collect()
4313 } else {
4314 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4315 };
4316 if frame.positions.nrows() != n {
4317 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4318 }
4319 for i in 0..n {
4320 frame
4321 .positions
4322 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
4323 }
4324 frame.sync_atom_data_from_arrays();
4325 RKRStatus::RKR_STATUS_SUCCESS
4326}
4327
4328#[unsafe(no_mangle)]
4331pub unsafe extern "C" fn rkr_frame_positions_dlpack(
4332 frame_handle: *const RKRConFrame,
4333 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4334) -> RKRStatus {
4335 unsafe {
4336 rkr_frame_positions_as_dlpack(
4337 frame_handle,
4338 rkr_dl_device_type::RKR_DL_CPU,
4339 0,
4340 0,
4341 1,
4342 0,
4343 out_tensor,
4344 )
4345 }
4346}
4347
4348#[unsafe(no_mangle)]
4353pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
4354 frame_handle: *const RKRConFrame,
4355 opts: *const RKRDlpackExportOptions,
4356 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4357) -> RKRStatus {
4358 if frame_handle.is_null() || out_tensor.is_null() {
4359 return RKRStatus::RKR_STATUS_NULL_POINTER;
4360 }
4361 unsafe { *out_tensor = std::ptr::null_mut() };
4362 let o = match resolve_dlpack_opts(opts) {
4363 Ok(o) => o,
4364 Err(st) => return st,
4365 };
4366 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4367 return RKRStatus::RKR_STATUS_NULL_POINTER;
4368 };
4369 let arr = frame_positions_arc(frame);
4370 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4371}
4372
4373#[unsafe(no_mangle)]
4375pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
4376 frame_handle: *const RKRConFrame,
4377 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4378) -> RKRStatus {
4379 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4380}
4381
4382#[unsafe(no_mangle)]
4383pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
4384 frame_handle: *const RKRConFrame,
4385 opts: *const RKRDlpackExportOptions,
4386 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4387) -> RKRStatus {
4388 if frame_handle.is_null() || out_tensor.is_null() {
4389 return RKRStatus::RKR_STATUS_NULL_POINTER;
4390 }
4391 unsafe { *out_tensor = std::ptr::null_mut() };
4392 let o = match resolve_dlpack_opts(opts) {
4393 Ok(o) => o,
4394 Err(st) => return st,
4395 };
4396 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4397 return RKRStatus::RKR_STATUS_NULL_POINTER;
4398 };
4399 if !frame.has_velocities() {
4400 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4401 }
4402 let n = frame.atom_data.len();
4403 let mut data = Vec::with_capacity(n * 3);
4404 for a in &frame.atom_data {
4405 let v = a.velocity.unwrap_or([0.0; 3]);
4406 data.extend_from_slice(&v);
4407 }
4408 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4409 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4410 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4411}
4412
4413#[unsafe(no_mangle)]
4415pub unsafe extern "C" fn rkr_frame_forces_dlpack(
4416 frame_handle: *const RKRConFrame,
4417 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4418) -> RKRStatus {
4419 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4420}
4421
4422#[unsafe(no_mangle)]
4423pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
4424 frame_handle: *const RKRConFrame,
4425 opts: *const RKRDlpackExportOptions,
4426 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4427) -> RKRStatus {
4428 if frame_handle.is_null() || out_tensor.is_null() {
4429 return RKRStatus::RKR_STATUS_NULL_POINTER;
4430 }
4431 unsafe { *out_tensor = std::ptr::null_mut() };
4432 let o = match resolve_dlpack_opts(opts) {
4433 Ok(o) => o,
4434 Err(st) => return st,
4435 };
4436 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4437 return RKRStatus::RKR_STATUS_NULL_POINTER;
4438 };
4439 if !frame.has_forces() {
4440 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4441 }
4442 let n = frame.atom_data.len();
4443 let mut data = Vec::with_capacity(n * 3);
4444 for a in &frame.atom_data {
4445 let f = a.force.unwrap_or([0.0; 3]);
4446 data.extend_from_slice(&f);
4447 }
4448 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4449 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4450 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4451}
4452
4453#[unsafe(no_mangle)]
4460pub unsafe extern "C" fn rkr_frame_displacements_dlpack(
4461 frame_handle: *const RKRConFrame,
4462 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4463) -> RKRStatus {
4464 unsafe { rkr_frame_displacements_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4465}
4466
4467#[unsafe(no_mangle)]
4474pub unsafe extern "C" fn rkr_frame_displacements_dlpack_ex(
4475 frame_handle: *const RKRConFrame,
4476 opts: *const RKRDlpackExportOptions,
4477 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4478) -> RKRStatus {
4479 if frame_handle.is_null() || out_tensor.is_null() {
4480 return RKRStatus::RKR_STATUS_NULL_POINTER;
4481 }
4482 unsafe { *out_tensor = std::ptr::null_mut() };
4483 let o = match resolve_dlpack_opts(opts) {
4484 Ok(o) => o,
4485 Err(st) => return st,
4486 };
4487 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4488 return RKRStatus::RKR_STATUS_NULL_POINTER;
4489 };
4490 if !frame.has_displacements() {
4491 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4492 }
4493 let n = frame.atom_data.len();
4494 let mut data = Vec::with_capacity(n * 3);
4495 for a in &frame.atom_data {
4496 let d = a.displacement.unwrap_or([0.0; 3]);
4497 data.extend_from_slice(&d);
4498 }
4499 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4500 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4501 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4502}
4503
4504#[unsafe(no_mangle)]
4511pub unsafe extern "C" fn rkr_frame_spreads_dlpack(
4512 frame_handle: *const RKRConFrame,
4513 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4514) -> RKRStatus {
4515 unsafe { rkr_frame_spreads_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4516}
4517
4518#[unsafe(no_mangle)]
4525pub unsafe extern "C" fn rkr_frame_spreads_dlpack_ex(
4526 frame_handle: *const RKRConFrame,
4527 opts: *const RKRDlpackExportOptions,
4528 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4529) -> RKRStatus {
4530 if frame_handle.is_null() || out_tensor.is_null() {
4531 return RKRStatus::RKR_STATUS_NULL_POINTER;
4532 }
4533 unsafe { *out_tensor = std::ptr::null_mut() };
4534 let o = match resolve_dlpack_opts(opts) {
4535 Ok(o) => o,
4536 Err(st) => return st,
4537 };
4538 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4539 return RKRStatus::RKR_STATUS_NULL_POINTER;
4540 };
4541 if !frame.has_spreads() {
4542 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4543 }
4544 let n = frame.atom_data.len();
4545 let mut data = Vec::with_capacity(n * 3);
4546 for a in &frame.atom_data {
4547 let d = a.spread.unwrap_or([0.0; 3]);
4548 data.extend_from_slice(&d);
4549 }
4550 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4551 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4552 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4553}
4554
4555#[unsafe(no_mangle)]
4557pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
4558 frame_handle: *const RKRConFrame,
4559 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4560) -> RKRStatus {
4561 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4562}
4563
4564#[unsafe(no_mangle)]
4565pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
4566 frame_handle: *const RKRConFrame,
4567 opts: *const RKRDlpackExportOptions,
4568 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4569) -> RKRStatus {
4570 if frame_handle.is_null() || out_tensor.is_null() {
4571 return RKRStatus::RKR_STATUS_NULL_POINTER;
4572 }
4573 unsafe { *out_tensor = std::ptr::null_mut() };
4574 let o = match resolve_dlpack_opts(opts) {
4575 Ok(o) => o,
4576 Err(st) => return st,
4577 };
4578 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4579 return RKRStatus::RKR_STATUS_NULL_POINTER;
4580 };
4581 if !frame.has_energies() {
4582 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4583 }
4584 let data: Vec<f64> = frame
4585 .atom_data
4586 .iter()
4587 .map(|a| a.energy.unwrap_or(0.0))
4588 .collect();
4589 let arr = ndarray::ArcArray1::from_vec(data);
4590 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
4591}
4592
4593pub struct RKRSelectionResult;
4597#[unsafe(no_mangle)]
4608pub unsafe extern "C" fn rkr_frame_select(
4609 frame_handle: *const RKRConFrame,
4610 selection: *const c_char,
4611 out_result: *mut *mut RKRSelectionResult,
4612) -> RKRStatus {
4613 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
4614 return RKRStatus::RKR_STATUS_NULL_POINTER;
4615 }
4616 let frame = unsafe { &*(frame_handle as *const ConFrame) };
4617 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
4618 Ok(s) => s,
4619 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
4620 };
4621 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
4622 Ok(result) => {
4623 let boxed = Box::new(result);
4624 unsafe {
4625 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
4626 }
4627 RKRStatus::RKR_STATUS_SUCCESS
4628 }
4629 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
4630 }
4631}
4632#[unsafe(no_mangle)]
4637pub unsafe extern "C" fn rkr_selection_result_match_count(
4638 result_handle: *const RKRSelectionResult,
4639) -> u64 {
4640 if result_handle.is_null() {
4641 return 0;
4642 }
4643 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4644 result.matches.len() as u64
4645}
4646#[unsafe(no_mangle)]
4651pub unsafe extern "C" fn rkr_selection_result_context_size(
4652 result_handle: *const RKRSelectionResult,
4653) -> u32 {
4654 if result_handle.is_null() {
4655 return 0;
4656 }
4657 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4658 result.context_size as u32
4659}
4660#[unsafe(no_mangle)]
4666pub unsafe extern "C" fn rkr_selection_result_match_at(
4667 result_handle: *const RKRSelectionResult,
4668 match_index: u64,
4669 out_atoms: *mut u64,
4670 out_size: *mut u32,
4671) -> RKRStatus {
4672 if result_handle.is_null() || out_atoms.is_null() {
4673 return RKRStatus::RKR_STATUS_NULL_POINTER;
4674 }
4675 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4676 let idx = match_index as usize;
4677 if idx >= result.matches.len() {
4678 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
4679 }
4680 let m = &result.matches[idx];
4681 unsafe {
4682 for i in 0..4 {
4683 *out_atoms.add(i) = if i < m.size {
4684 m.atoms[i] as u64
4685 } else {
4686 u64::MAX
4687 };
4688 }
4689 if !out_size.is_null() {
4690 *out_size = m.size as u32;
4691 }
4692 }
4693 RKRStatus::RKR_STATUS_SUCCESS
4694}
4695#[unsafe(no_mangle)]
4701pub unsafe extern "C" fn rkr_selection_result_primary_indices(
4702 result_handle: *const RKRSelectionResult,
4703 out_indices: *mut u64,
4704 capacity: u64,
4705 out_written: *mut u64,
4706) -> RKRStatus {
4707 if result_handle.is_null() {
4708 return RKRStatus::RKR_STATUS_NULL_POINTER;
4709 }
4710 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4711 let n = result.matches.len() as u64;
4712 if !out_written.is_null() {
4713 unsafe {
4714 *out_written = n;
4715 }
4716 }
4717 if n == 0 {
4718 return RKRStatus::RKR_STATUS_SUCCESS;
4719 }
4720 if out_indices.is_null() {
4721 return RKRStatus::RKR_STATUS_NULL_POINTER;
4722 }
4723 if capacity < n {
4724 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4725 }
4726 unsafe {
4727 for (i, m) in result.matches.iter().enumerate() {
4728 *out_indices.add(i) = m.atoms[0] as u64;
4729 }
4730 }
4731 RKRStatus::RKR_STATUS_SUCCESS
4732}
4733#[unsafe(no_mangle)]
4738pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
4739 if result_handle.is_null() {
4740 return;
4741 }
4742 unsafe {
4743 drop(Box::from_raw(
4744 result_handle as *mut crate::chemfiles_selection::SelectionResult,
4745 ));
4746 }
4747}
4748#[unsafe(no_mangle)]
4750pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
4751 #[cfg(feature = "chemfiles")]
4752 {
4753 1
4754 }
4755 #[cfg(not(feature = "chemfiles"))]
4756 {
4757 0
4758 }
4759}
4760
4761#[unsafe(no_mangle)]
4763pub extern "C" fn rkr_has_parallel_support() -> u8 {
4764 #[cfg(feature = "parallel")]
4765 {
4766 1
4767 }
4768 #[cfg(not(feature = "parallel"))]
4769 {
4770 0
4771 }
4772}
4773#[unsafe(no_mangle)]
4779pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
4780 if path_c.is_null() {
4781 return std::ptr::null_mut();
4782 }
4783 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4784 return std::ptr::null_mut();
4785 };
4786 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
4787 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4788 Err(_) => std::ptr::null_mut(),
4789 }
4790}
4791
4792#[unsafe(no_mangle)]
4798pub unsafe extern "C" fn rkr_read_chemfiles(
4799 path_c: *const c_char,
4800 num_frames: *mut usize,
4801) -> *mut *mut RKRConFrame {
4802 if path_c.is_null() || num_frames.is_null() {
4803 return std::ptr::null_mut();
4804 }
4805 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4806 return std::ptr::null_mut();
4807 };
4808 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
4809 Ok(frames) => pack_frame_handles(frames, num_frames),
4810 Err(_) => std::ptr::null_mut(),
4811 }
4812}
4813
4814#[unsafe(no_mangle)]
4820pub unsafe extern "C" fn rkr_read_chemfiles_nth(
4821 path_c: *const c_char,
4822 index: usize,
4823) -> *mut RKRConFrame {
4824 if path_c.is_null() {
4825 return std::ptr::null_mut();
4826 }
4827 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4828 return std::ptr::null_mut();
4829 };
4830 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
4831 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4832 Err(_) => std::ptr::null_mut(),
4833 }
4834}
4835
4836#[unsafe(no_mangle)]
4842pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
4843 if path_c.is_null() {
4844 return usize::MAX;
4845 }
4846 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4847 return usize::MAX;
4848 };
4849 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
4850}
4851
4852#[unsafe(no_mangle)]
4860pub unsafe extern "C" fn rkr_read_chemfiles_range(
4861 path_c: *const c_char,
4862 start: usize,
4863 step: usize,
4864 stop: usize,
4865 topology_c: *const c_char,
4866 guess_bonds: u8,
4867 num_frames: *mut usize,
4868) -> *mut *mut RKRConFrame {
4869 if path_c.is_null() || num_frames.is_null() {
4870 return std::ptr::null_mut();
4871 }
4872 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4873 return std::ptr::null_mut();
4874 };
4875 let topology = if topology_c.is_null() {
4876 None
4877 } else {
4878 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
4879 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
4880 _ => return std::ptr::null_mut(),
4881 }
4882 };
4883 let opts = crate::chemfiles_import::ChemfilesReadOpts {
4884 start,
4885 step,
4886 stop: if stop == usize::MAX { None } else { Some(stop) },
4887 format: None,
4888 topology,
4889 topology_format: None,
4890 guess_bonds: guess_bonds != 0,
4891 };
4892 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
4893 Ok(frames) => pack_frame_handles(frames, num_frames),
4894 Err(_) => std::ptr::null_mut(),
4895 }
4896}
4897#[unsafe(no_mangle)]
4904pub unsafe extern "C" fn rkr_read_chemfiles_memory(
4905 data_c: *const c_char,
4906 format_c: *const c_char,
4907 num_frames: *mut usize,
4908) -> *mut *mut RKRConFrame {
4909 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
4910 return std::ptr::null_mut();
4911 }
4912 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
4913 return std::ptr::null_mut();
4914 };
4915 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
4916 return std::ptr::null_mut();
4917 };
4918 match crate::chemfiles_import::con_frames_from_memory(data, format) {
4919 Ok(frames) => {
4920 let n = frames.len();
4921 unsafe { *num_frames = n };
4922 let mut ptrs: Vec<*mut RKRConFrame> = frames
4923 .into_iter()
4924 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
4925 .collect();
4926 let p = ptrs.as_mut_ptr();
4927 std::mem::forget(ptrs);
4928 p
4929 }
4930 Err(_) => std::ptr::null_mut(),
4931 }
4932}
4933#[unsafe(no_mangle)]
4938pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
4939 if tensor.is_null() {
4940 return;
4941 }
4942 unsafe {
4943 let t = &mut *tensor;
4944 if let Some(del) = t.deleter {
4945 del(tensor);
4946 }
4947 }
4948}
4949
4950#[unsafe(no_mangle)]
4960pub unsafe extern "C" fn rkr_pack_rcso(
4961 frame_handle: *const RKRConFrame,
4962 buf: *mut u8,
4963 buflen: usize,
4964 out_len: *mut usize,
4965) -> RKRStatus {
4966 if out_len.is_null() {
4967 return RKRStatus::RKR_STATUS_NULL_POINTER;
4968 }
4969 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
4970 Some(f) => f,
4971 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
4972 };
4973 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
4974 Ok(b) => b,
4975 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4976 };
4977 unsafe { *out_len = bytes.len() };
4978 if buf.is_null() {
4979 return RKRStatus::RKR_STATUS_SUCCESS;
4980 }
4981 if buflen < bytes.len() {
4982 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4983 }
4984 unsafe {
4985 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
4986 }
4987 RKRStatus::RKR_STATUS_SUCCESS
4988}
4989
4990#[unsafe(no_mangle)]
4995pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
4996 buf: *const u8,
4997 buflen: usize,
4998 out_natoms: *mut u32,
4999) -> RKRStatus {
5000 if buf.is_null() || out_natoms.is_null() {
5001 return RKRStatus::RKR_STATUS_NULL_POINTER;
5002 }
5003 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5004 match crate::rcso::Rcso::decode(bytes) {
5005 Ok(s) => {
5006 unsafe { *out_natoms = s.natoms };
5007 RKRStatus::RKR_STATUS_SUCCESS
5008 }
5009 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5010 }
5011}
5012
5013#[unsafe(no_mangle)]
5018pub unsafe extern "C" fn rkr_unpack_rcso_positions(
5019 buf: *const u8,
5020 buflen: usize,
5021 dest: *mut f64,
5022 dest_natoms: u32,
5023) -> RKRStatus {
5024 if buf.is_null() || dest.is_null() {
5025 return RKRStatus::RKR_STATUS_NULL_POINTER;
5026 }
5027 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5028 let soa = match crate::rcso::Rcso::decode(bytes) {
5029 Ok(s) => s,
5030 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5031 };
5032 if soa.natoms != dest_natoms {
5033 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5034 }
5035 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5036 for (i, p) in soa.positions.iter().enumerate() {
5037 out[i * 3] = p[0];
5038 out[i * 3 + 1] = p[1];
5039 out[i * 3 + 2] = p[2];
5040 }
5041 RKRStatus::RKR_STATUS_SUCCESS
5042}
5043
5044#[unsafe(no_mangle)]
5050pub unsafe extern "C" fn rkr_unpack_rcso_forces(
5051 buf: *const u8,
5052 buflen: usize,
5053 dest: *mut f64,
5054 dest_natoms: u32,
5055) -> RKRStatus {
5056 if buf.is_null() || dest.is_null() {
5057 return RKRStatus::RKR_STATUS_NULL_POINTER;
5058 }
5059 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5060 let soa = match crate::rcso::Rcso::decode(bytes) {
5061 Ok(s) => s,
5062 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5063 };
5064 let Some(forces) = soa.forces else {
5065 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
5066 };
5067 if soa.natoms != dest_natoms {
5068 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5069 }
5070 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5071 for (i, f) in forces.iter().enumerate() {
5072 out[i * 3] = f[0];
5073 out[i * 3 + 1] = f[1];
5074 out[i * 3 + 2] = f[2];
5075 }
5076 RKRStatus::RKR_STATUS_SUCCESS
5077}
5078
5079#[cfg(test)]
5080mod tests {
5081 use super::*;
5082 use std::ffi::{CStr, CString};
5083 #[test]
5084 fn frame_copy_positions_without_cframe() {
5085 let handle = test_frame_handle();
5086 let n = unsafe { rkr_frame_atom_count(handle) };
5087 assert_eq!(n, 1);
5088 let mut buf = vec![0.0f64; n * 3];
5089 assert_eq!(
5090 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
5091 RKRStatus::RKR_STATUS_SUCCESS
5092 );
5093 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5094 assert_eq!(
5095 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
5096 RKRStatus::RKR_STATUS_SUCCESS
5097 );
5098 assert!(!tensor.is_null());
5099 unsafe { rkr_dlpack_delete(tensor) };
5100 assert_eq!(
5101 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
5102 RKRStatus::RKR_STATUS_SECTION_ABSENT
5103 );
5104 unsafe { free_rkr_frame(handle) };
5105 }
5106 #[test]
5107 fn read_all_frames_c_abi_tiny() {
5108 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5109 let mut n: usize = 0;
5110 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5111 assert!(!arr.is_null() && n >= 1);
5112 let first = unsafe { *arr };
5113 let nat = unsafe { rkr_frame_atom_count(first) };
5114 let mut buf = vec![0.0f64; nat * 3];
5115 assert_eq!(
5116 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
5117 RKRStatus::RKR_STATUS_SUCCESS
5118 );
5119 unsafe { free_rkr_frame_array(arr, n) };
5120 }
5121
5122 #[test]
5123 fn read_all_frames_n_threads_matches_auto() {
5124 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5125 let mut n_auto: usize = 0;
5126 let mut n_one: usize = 0;
5127 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
5128 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
5129 assert!(!auto.is_null() && !one.is_null());
5130 assert_eq!(n_auto, n_one);
5131 assert_eq!(n_auto, 1);
5132 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
5133 let nat_one = unsafe { rkr_frame_atom_count(*one) };
5134 assert_eq!(nat_auto, nat_one);
5135 unsafe { free_rkr_frame_array(auto, n_auto) };
5136 unsafe { free_rkr_frame_array(one, n_one) };
5137 }
5138
5139 #[test]
5140 fn free_rkr_frame_ptr_array_keeps_frames() {
5141 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5142 let mut n: usize = 0;
5143 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5144 assert!(!arr.is_null() && n >= 1);
5145 let first = unsafe { *arr };
5146 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
5148 unsafe { free_rkr_frame_ptr_array(arr, n) };
5149 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
5150 unsafe { free_rkr_frame(first) };
5151 for h in rest {
5152 if !h.is_null() {
5153 unsafe { free_rkr_frame(h) };
5154 }
5155 }
5156 }
5157 fn test_frame_handle() -> *mut RKRConFrame {
5158 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5159 builder
5160 .prebox_header("Generated by test")
5161 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
5162 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
5163 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
5164 }
5165 #[test]
5166 fn header_line_rejects_null_buffer() {
5167 let frame = test_frame_handle();
5168 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
5169 unsafe { free_rkr_frame(frame) };
5170 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5171 }
5172 #[test]
5173 fn header_line_rejects_empty_buffer() {
5174 let frame = test_frame_handle();
5175 let mut buffer = [0 as c_char; 1];
5176 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
5177 unsafe { free_rkr_frame(frame) };
5178 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
5179 }
5180 #[test]
5181 fn header_line_truncates_and_terminates_buffer() {
5182 let frame = test_frame_handle();
5183 let mut buffer = [0 as c_char; 10];
5184 let status =
5185 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
5186 unsafe { free_rkr_frame(frame) };
5187 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5188 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
5189 assert_eq!(copied.to_str().unwrap(), "Generated");
5190 }
5191
5192 #[test]
5193 fn pack_rcso_size_query_then_dest() {
5194 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
5195 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
5196 assert!(!frame.is_null());
5197 let mut need = 0usize;
5198 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
5199 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5200 assert!(need >= 24);
5201 let mut buf = vec![0u8; need];
5202 let mut wrote = 0usize;
5203 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
5204 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5205 assert_eq!(wrote, need);
5206 assert_eq!(&buf[0..4], b"RCSO");
5207 let mut natoms = 0u32;
5208 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
5209 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5210 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
5211 let mut xyz = vec![0.0f64; natoms as usize * 3];
5212 let st =
5213 unsafe { rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms) };
5214 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5215 let mut frc = vec![0.0f64; natoms as usize * 3];
5216 let st =
5217 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
5218 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5219 unsafe { free_rkr_frame(frame) };
5220 }
5221
5222 fn test_builder_handle() -> *mut RKRConFrameBuilder {
5223 let cell = [10.0, 11.0, 12.0];
5224 let angles = [90.0, 91.0, 92.0];
5225 unsafe {
5226 rkr_frame_new(
5227 cell.as_ptr(),
5228 angles.as_ptr(),
5229 ptr::null(),
5230 ptr::null(),
5231 ptr::null(),
5232 ptr::null(),
5233 )
5234 }
5235 }
5236 fn c_string(s: &str) -> CString {
5237 CString::new(s).unwrap()
5238 }
5239 unsafe fn assert_single_atom(
5240 frame: *mut RKRConFrame,
5241 fixed: [bool; 3],
5242 velocity: Option<[f64; 3]>,
5243 forces: Option<[f64; 3]>,
5244 ) {
5245 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5246 assert!(!c_frame.is_null());
5247 let c_frame_ref = unsafe { &*c_frame };
5248 assert_eq!(c_frame_ref.num_atoms, 1);
5249 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
5250 assert_eq!(c_frame_ref.has_forces, forces.is_some());
5251 let atom = unsafe { &*c_frame_ref.atoms };
5252 assert_eq!(atom.fixed_x, fixed[0]);
5253 assert_eq!(atom.fixed_y, fixed[1]);
5254 assert_eq!(atom.fixed_z, fixed[2]);
5255 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
5256 assert_eq!(atom.has_velocity, velocity.is_some());
5257 assert_eq!(atom.has_forces, forces.is_some());
5258 if let Some([vx, vy, vz]) = velocity {
5259 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
5260 }
5261 if let Some([fx, fy, fz]) = forces {
5262 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
5263 }
5264 unsafe { free_c_frame(c_frame) };
5265 unsafe { free_rkr_frame(frame) };
5266 }
5267 #[test]
5268 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
5269 let builder = test_builder_handle();
5270 let symbol = c_string("Cu");
5271 let status = unsafe {
5272 rkr_frame_add_atom_with_fixed_mask(
5273 builder,
5274 symbol.as_ptr(),
5275 1.0,
5276 2.0,
5277 3.0,
5278 true,
5279 false,
5280 true,
5281 7,
5282 63.546,
5283 )
5284 };
5285 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5286 let frame = unsafe { rkr_frame_builder_build(builder) };
5287 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
5288 }
5289 #[test]
5290 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
5291 let builder = test_builder_handle();
5292 let symbol = c_string("H");
5293 let status = unsafe {
5294 rkr_frame_add_atom_with_velocity_fixed_mask(
5295 builder,
5296 symbol.as_ptr(),
5297 1.0,
5298 2.0,
5299 3.0,
5300 false,
5301 true,
5302 false,
5303 9,
5304 1.008,
5305 0.1,
5306 0.2,
5307 0.3,
5308 )
5309 };
5310 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5311 let frame = unsafe { rkr_frame_builder_build(builder) };
5312 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
5313 }
5314 #[test]
5315 fn builder_preserves_fixed_mask_for_atom_with_forces() {
5316 let builder = test_builder_handle();
5317 let symbol = c_string("O");
5318 let status = unsafe {
5319 rkr_frame_add_atom_with_forces_fixed_mask(
5320 builder,
5321 symbol.as_ptr(),
5322 1.0,
5323 2.0,
5324 3.0,
5325 true,
5326 true,
5327 false,
5328 11,
5329 15.999,
5330 -0.1,
5331 -0.2,
5332 -0.3,
5333 )
5334 };
5335 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5336 let frame = unsafe { rkr_frame_builder_build(builder) };
5337 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
5338 }
5339 #[test]
5340 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
5341 let builder = test_builder_handle();
5342 let symbol = c_string("N");
5343 let status = unsafe {
5344 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5345 builder,
5346 symbol.as_ptr(),
5347 1.0,
5348 2.0,
5349 3.0,
5350 false,
5351 true,
5352 true,
5353 13,
5354 14.007,
5355 0.4,
5356 0.5,
5357 0.6,
5358 -0.4,
5359 -0.5,
5360 -0.6,
5361 )
5362 };
5363 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5364 let frame = unsafe { rkr_frame_builder_build(builder) };
5365 unsafe {
5366 assert_single_atom(
5367 frame,
5368 [false, true, true],
5369 Some([0.4, 0.5, 0.6]),
5370 Some([-0.4, -0.5, -0.6]),
5371 )
5372 };
5373 }
5374 #[test]
5375 fn builder_bool_fixed_functions_set_all_axes_together() {
5376 let builder = test_builder_handle();
5377 let cu = c_string("Cu");
5378 let h = c_string("H");
5379 let atom_status =
5380 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
5381 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
5382 let velocity_status = unsafe {
5383 rkr_frame_add_atom_with_velocity(
5384 builder,
5385 h.as_ptr(),
5386 4.0,
5387 5.0,
5388 6.0,
5389 false,
5390 2,
5391 1.008,
5392 0.7,
5393 0.8,
5394 0.9,
5395 )
5396 };
5397 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
5398 let frame = unsafe { rkr_frame_builder_build(builder) };
5399 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5400 assert!(!c_frame.is_null());
5401 let c_frame_ref = unsafe { &*c_frame };
5402 assert_eq!(c_frame_ref.num_atoms, 2);
5403 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
5404 assert_eq!(
5405 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
5406 [true, true, true]
5407 );
5408 assert_eq!(
5409 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
5410 [false, false, false]
5411 );
5412 unsafe { free_c_frame(c_frame) };
5413 unsafe { free_rkr_frame(frame) };
5414 }
5415 #[test]
5416 fn status_message_returns_static_strings_for_all_status_values() {
5417 let cases = [
5418 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
5419 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
5420 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
5421 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
5422 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
5423 (
5424 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
5425 "index out of bounds",
5426 ),
5427 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
5428 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
5429 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
5430 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
5431 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
5432 (
5433 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
5434 "feature disabled in this build",
5435 ),
5436 ];
5437 for (status, expected) in cases {
5438 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
5439 assert_eq!(message.to_str().unwrap(), expected);
5440 }
5441 }
5442 unsafe fn assert_dlpack_cpu_float(
5446 t: *mut RKRDLManagedTensorVersioned,
5447 expect_ndim: i32,
5448 expect_shape: &[i64],
5449 expect_bits: u8,
5450 ) {
5451 assert!(!t.is_null());
5452 let dl = unsafe { &(*t).dl_tensor };
5453 assert_eq!(dl.ndim, expect_ndim);
5454 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
5455 assert_eq!(shape, expect_shape);
5456 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5457 assert_eq!(dl.dtype.bits, expect_bits);
5458 assert_eq!(dl.dtype.lanes, 1);
5459 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5460 assert!(!dl.data.is_null());
5461 }
5462
5463 #[test]
5464 fn frame_optional_section_dlpack_present_and_absent() {
5465 let handle = test_frame_handle();
5467 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5468 assert_eq!(
5469 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
5470 RKRStatus::RKR_STATUS_SECTION_ABSENT
5471 );
5472 assert!(t.is_null());
5473 assert_eq!(
5474 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
5475 RKRStatus::RKR_STATUS_SECTION_ABSENT
5476 );
5477 assert_eq!(
5478 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
5479 RKRStatus::RKR_STATUS_SECTION_ABSENT
5480 );
5481 unsafe { free_rkr_frame(handle) };
5482
5483 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
5485 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
5486 assert!(!fr.is_null());
5487 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
5488 assert!(n > 0);
5489 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5490 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
5491 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5492 unsafe {
5493 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
5494 rkr_dlpack_delete(vel);
5495 free_rkr_frame(fr);
5496 }
5497
5498 let cell = [10.0f64; 3];
5500 let ang = [90.0f64; 3];
5501 let b = unsafe {
5502 rkr_frame_new(
5503 cell.as_ptr(),
5504 ang.as_ptr(),
5505 std::ptr::null(),
5506 std::ptr::null(),
5507 std::ptr::null(),
5508 std::ptr::null(),
5509 )
5510 };
5511 assert!(!b.is_null());
5512 let sym = CString::new("H").unwrap();
5513 unsafe {
5514 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5515 b,
5516 sym.as_ptr(),
5517 0.0,
5518 0.0,
5519 0.0,
5520 false,
5521 false,
5522 false,
5523 0,
5524 1.0,
5525 0.1,
5526 0.0,
5527 0.0,
5528 0.0,
5529 0.0,
5530 -1.0,
5531 );
5532 rkr_frame_builder_set_last_energy(b, -0.5);
5533 }
5534 let built = unsafe { rkr_frame_builder_build(b) };
5535 assert!(!built.is_null());
5536 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
5537 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5538 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5539 assert_eq!(
5540 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
5541 RKRStatus::RKR_STATUS_SUCCESS
5542 );
5543 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
5544 assert_eq!(
5545 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
5546 RKRStatus::RKR_STATUS_SUCCESS
5547 );
5548 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
5549 let opts32 = RKRDlpackExportOptions {
5551 dtype: RKRDLDataType {
5552 code: rkr_dl_type_code::RKR_DL_FLOAT,
5553 bits: 32,
5554 lanes: 1,
5555 },
5556 device: RKRDLDevice {
5557 device_type: rkr_dl_device_type::RKR_DL_CPU,
5558 device_id: 0,
5559 },
5560 };
5561 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5562 assert_eq!(
5563 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
5564 RKRStatus::RKR_STATUS_SUCCESS
5565 );
5566 unsafe {
5567 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
5568 rkr_dlpack_delete(pos32);
5569 }
5570 let cuda_dev = RKRDlpackExportOptions {
5573 dtype: RKRDLDataType {
5574 code: rkr_dl_type_code::RKR_DL_FLOAT,
5575 bits: 64,
5576 lanes: 1,
5577 },
5578 device: RKRDLDevice {
5579 device_type: rkr_dl_device_type::RKR_DL_CUDA,
5580 device_id: 0,
5581 },
5582 };
5583 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5584 #[cfg(not(feature = "cuda"))]
5585 assert_eq!(
5586 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5587 RKRStatus::RKR_STATUS_FEATURE_DISABLED
5588 );
5589 #[cfg(feature = "cuda")]
5590 {
5591 assert_eq!(
5592 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5593 RKRStatus::RKR_STATUS_SUCCESS
5594 );
5595 assert!(!junk.is_null());
5596 let dl = unsafe { &(*junk).dl_tensor };
5597 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
5598 assert!(!dl.data.is_null());
5599 unsafe { rkr_dlpack_delete(junk) };
5600 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5602 assert_eq!(
5603 unsafe {
5604 rkr_frame_positions_as_dlpack(
5605 built,
5606 rkr_dl_device_type::RKR_DL_CUDA,
5607 0,
5608 0,
5609 1,
5610 0,
5611 &mut as_cuda,
5612 )
5613 },
5614 RKRStatus::RKR_STATUS_SUCCESS
5615 );
5616 assert!(!as_cuda.is_null());
5617 unsafe {
5618 assert_eq!(
5619 (*as_cuda).dl_tensor.device.device_type,
5620 dlpk::sys::DLDeviceType::kDLCUDA
5621 );
5622 rkr_dlpack_delete(as_cuda);
5623 }
5624 }
5625 let bad_bits = RKRDlpackExportOptions {
5627 dtype: RKRDLDataType {
5628 code: rkr_dl_type_code::RKR_DL_FLOAT,
5629 bits: 16,
5630 lanes: 1,
5631 },
5632 device: RKRDLDevice {
5633 device_type: rkr_dl_device_type::RKR_DL_CPU,
5634 device_id: 0,
5635 },
5636 };
5637 assert_eq!(
5638 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
5639 RKRStatus::RKR_STATUS_VALIDATION_ERROR
5640 );
5641 let mut as_t: *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_CPU,
5648 0,
5649 0,
5650 1,
5651 0,
5652 &mut as_t,
5653 )
5654 },
5655 RKRStatus::RKR_STATUS_SUCCESS
5656 );
5657 unsafe {
5658 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
5659 }
5660 {
5662 let fr = unsafe { &mut *(built as *mut ConFrame) };
5663 for a in fr.atom_data.iter_mut() {
5664 a.x = -99.0;
5665 a.y = -99.0;
5666 a.z = -99.0;
5667 }
5668 }
5669 assert_eq!(
5670 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
5671 RKRStatus::RKR_STATUS_SUCCESS
5672 );
5673 {
5674 let fr = unsafe { &*(built as *const ConFrame) };
5675 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
5676 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
5677 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
5678 }
5679 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5681 assert_eq!(
5682 unsafe {
5683 rkr_frame_positions_as_dlpack(
5684 built,
5685 rkr_dl_device_type::RKR_DL_CPU,
5686 0,
5687 0,
5688 1,
5689 0,
5690 &mut again,
5691 )
5692 },
5693 RKRStatus::RKR_STATUS_SUCCESS
5694 );
5695 unsafe {
5696 let dl = &(*again).dl_tensor;
5697 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
5698 assert!((data[0] - 0.0).abs() < 1e-12);
5699 assert!((data[1] - 0.0).abs() < 1e-12);
5700 assert!((data[2] - 0.0).abs() < 1e-12);
5701 rkr_dlpack_delete(again);
5702 rkr_dlpack_delete(as_t);
5703 rkr_dlpack_delete(frc);
5704 rkr_dlpack_delete(eng);
5705 free_rkr_frame(built);
5706 }
5707 }
5708
5709 #[test]
5710 fn ffi_positions_dlpack_round_trip() {
5711 let handle = test_builder_handle();
5712 let sym = c_string("Cu");
5713 unsafe {
5714 rkr_frame_add_atom_full(
5715 handle,
5716 sym.as_ptr(),
5717 1.0,
5718 2.0,
5719 3.0,
5720 false,
5721 false,
5722 false,
5723 7,
5724 63.5,
5725 ptr::null(),
5726 ptr::null(),
5727 )
5728 };
5729 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5730 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
5731 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5732 assert!(!t.is_null());
5733 let dl = unsafe { &(*t).dl_tensor };
5735 assert_eq!(dl.ndim, 2);
5736 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
5737 assert_eq!(shape, &[1, 3]);
5738 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5739 assert_eq!(dl.dtype.bits, 64);
5740 assert_eq!(dl.dtype.lanes, 1);
5741 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5742 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
5743 assert_eq!(data, &[1.0, 2.0, 3.0]);
5744 let deleter = unsafe { (*t).deleter };
5746 if let Some(del) = deleter {
5747 unsafe { del(t) };
5748 }
5749 unsafe { free_rkr_frame_builder(handle) };
5750 }
5751 #[test]
5752 fn ffi_velocities_dlpack_section_absent() {
5753 let handle = test_builder_handle();
5754 let sym = c_string("Cu");
5755 unsafe {
5756 rkr_frame_add_atom_full(
5757 handle,
5758 sym.as_ptr(),
5759 0.0,
5760 0.0,
5761 0.0,
5762 false,
5763 false,
5764 false,
5765 0,
5766 63.5,
5767 ptr::null(),
5768 ptr::null(),
5769 )
5770 };
5771 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5772 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
5773 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
5774 assert!(t.is_null());
5775 unsafe { free_rkr_frame_builder(handle) };
5776 }
5777 #[test]
5778 fn ffi_dlpack_null_handle_rejects() {
5779 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5780 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
5781 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5782 assert!(t.is_null());
5783 }
5784 #[cfg(feature = "chemfiles")]
5785 #[test]
5786 fn rkr_frame_select_finds_oxygen() {
5787 use crate::types::ConFrameBuilder;
5788 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5789 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
5790 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
5791 let frame = b.build().unwrap();
5792 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5793 let sel = CString::new("name O").unwrap();
5794 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5795 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
5796 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5797 assert!(!out.is_null());
5798 let n = unsafe { rkr_selection_result_match_count(out) };
5799 assert_eq!(n, 1);
5800 let mut atoms = [u64::MAX; 4];
5801 let mut size = 0u32;
5802 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
5803 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
5804 assert_eq!(size, 1);
5805 assert_eq!(atoms[0], 0);
5806 unsafe {
5807 rkr_selection_result_free(out);
5808 free_rkr_frame(frame_ptr);
5809 }
5810 }
5811 #[cfg(feature = "chemfiles")]
5813 #[test]
5814 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
5815 use crate::types::{Bond, ConFrameBuilder};
5816 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5818 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
5819 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
5820 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
5821 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
5822 let mut frame = b.build().unwrap();
5823 let id_to = |id: u64| {
5824 frame
5825 .atom_data
5826 .iter()
5827 .position(|a| a.atom_id == id)
5828 .unwrap() as u32
5829 };
5830 frame.header.set_bonds(&[
5831 Bond::new(id_to(0), id_to(1)),
5832 Bond::new(id_to(1), id_to(2)),
5833 Bond::new(id_to(2), id_to(3)),
5834 ]);
5835 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5836 let run = |sel: &str| -> (u64, u32) {
5837 let csel = CString::new(sel).unwrap();
5838 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5839 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
5840 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
5841 let n = unsafe { rkr_selection_result_match_count(out) };
5842 let ctx = unsafe { rkr_selection_result_context_size(out) };
5843 unsafe { rkr_selection_result_free(out) };
5844 (n, ctx)
5845 };
5846 assert_eq!(run("bonds: all"), (3, 2));
5847 assert_eq!(run("angles: all"), (2, 3));
5848 assert_eq!(run("dihedrals: all"), (1, 4));
5849 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
5850 assert_eq!(
5851 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
5852 run("bonds: type(#1) H and name(#2) O").0
5853 );
5854 unsafe { free_rkr_frame(frame_ptr) };
5855 }
5856 #[cfg(feature = "metatensor")]
5857 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
5858 assert!(!block.is_null());
5859 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
5860 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
5861 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
5862 let shape_fn = array
5863 .shape
5864 .expect("mts_array_t.shape from metatensor C API");
5865 let mut shape_ptr: *const usize = std::ptr::null();
5866 let mut shape_count: usize = 0;
5867 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
5868 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
5869 assert_eq!(shape_count, 2);
5870 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
5871 assert_eq!(shape[0], n);
5872 assert_eq!(shape[1], props);
5873 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
5874 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
5875 assert!(!samples.is_null() && !prop_lab.is_null());
5876 }
5877 #[cfg(feature = "metatensor")]
5878 #[test]
5879 fn metatensor_positions_via_c_abi() {
5880 let handle = test_frame_handle();
5881 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5882 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
5883 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5884 assert_mts_block_shape(out, 1, 3);
5885 unsafe { rkr_mts_block_free(out) };
5886 for (name, export) in [
5887 (
5888 "velocities",
5889 rkr_frame_metatensor_velocities_block
5890 as unsafe extern "C" fn(
5891 *const RKRConFrame,
5892 *mut *mut metatensor::c_api::mts_block_t,
5893 ) -> RKRStatus,
5894 ),
5895 (
5896 "forces",
5897 rkr_frame_metatensor_forces_block
5898 as unsafe extern "C" fn(
5899 *const RKRConFrame,
5900 *mut *mut metatensor::c_api::mts_block_t,
5901 ) -> RKRStatus,
5902 ),
5903 (
5904 "atom_energies",
5905 rkr_frame_metatensor_atom_energies_block
5906 as unsafe extern "C" fn(
5907 *const RKRConFrame,
5908 *mut *mut metatensor::c_api::mts_block_t,
5909 ) -> RKRStatus,
5910 ),
5911 ] {
5912 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5913 let st_abs = unsafe { export(handle, &mut o) };
5914 assert_eq!(
5915 st_abs,
5916 RKRStatus::RKR_STATUS_SECTION_ABSENT,
5917 "{name} must be SECTION_ABSENT on minimal test frame"
5918 );
5919 assert!(o.is_null());
5920 }
5921 unsafe { free_rkr_frame(handle) };
5922 }
5923
5924 #[cfg(feature = "metatensor")]
5925 #[test]
5926 fn metatensor_optional_sections_via_c_abi() {
5927 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5929 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
5930 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
5931 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
5932 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
5933 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
5934 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
5935 builder.set_atom_energy(0, -0.5).unwrap();
5936 builder.set_atom_energy(1, -1.0).unwrap();
5937 let frame = builder.build().unwrap();
5938 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5939 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5940 assert_eq!(
5941 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
5942 RKRStatus::RKR_STATUS_SUCCESS
5943 );
5944 assert_mts_block_shape(pos, 2, 3);
5945 unsafe { rkr_mts_block_free(pos) };
5946 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5947 assert_eq!(
5948 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
5949 RKRStatus::RKR_STATUS_SUCCESS
5950 );
5951 assert_mts_block_shape(vel, 2, 3);
5952 unsafe { rkr_mts_block_free(vel) };
5953 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5954 assert_eq!(
5955 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
5956 RKRStatus::RKR_STATUS_SUCCESS
5957 );
5958 assert_mts_block_shape(frc, 2, 3);
5959 unsafe { rkr_mts_block_free(frc) };
5960 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5961 assert_eq!(
5962 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
5963 RKRStatus::RKR_STATUS_SUCCESS
5964 );
5965 assert_mts_block_shape(eng, 2, 1);
5966 unsafe { rkr_mts_block_free(eng) };
5967 unsafe { free_rkr_frame(handle) };
5968 }
5969
5970 #[test]
5971 fn string_iterator_yields_frames_from_buffer() {
5972 let text =
5973 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
5974 let c_text = CString::new(text.as_str()).unwrap();
5975 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
5976 assert!(!it.is_null());
5977 let mut n = 0usize;
5978 loop {
5979 let fr = unsafe { con_frame_iterator_next(it) };
5980 if fr.is_null() {
5981 break;
5982 }
5983 n += 1;
5984 unsafe { free_rkr_frame(fr) };
5985 }
5986 unsafe { free_con_frame_iterator(it) };
5987 assert!(n >= 1, "string iterator should yield >=1 frame");
5988
5989 let bytes = text.as_bytes();
5990 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
5991 assert!(!it2.is_null());
5992 let fr2 = unsafe { con_frame_iterator_next(it2) };
5993 assert!(!fr2.is_null());
5994 unsafe {
5995 free_rkr_frame(fr2);
5996 free_con_frame_iterator(it2);
5997 }
5998 }
5999
6000 #[test]
6001 fn file_iterator_reads_gzip_when_present() {
6002 use flate2::Compression;
6003 use flate2::write::GzEncoder;
6004 use std::io::Write;
6005 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
6006 let dir = tempfile::tempdir().expect("tempdir");
6007 let gz_path = dir.path().join("tiny_cuh2.con.gz");
6008 {
6009 let f = std::fs::File::create(&gz_path).unwrap();
6010 let mut enc = GzEncoder::new(f, Compression::default());
6011 enc.write_all(&plain).unwrap();
6012 enc.finish().unwrap();
6013 }
6014 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
6015 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
6016 assert!(
6017 !it.is_null(),
6018 "path iterator must decompress .con.gz transparently"
6019 );
6020 let fr = unsafe { con_frame_iterator_next(it) };
6021 assert!(!fr.is_null());
6022 let n = unsafe { rkr_frame_atom_count(fr) };
6023 assert!(n > 0);
6024 unsafe {
6025 free_rkr_frame(fr);
6026 free_con_frame_iterator(it);
6027 }
6028 }
6029}