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