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
3628fn pack_frame_handles(frames: Vec<ConFrame>, num_frames: *mut usize) -> *mut *mut RKRConFrame {
3630 let count = frames.len();
3631 let mut handles: Vec<*mut RKRConFrame> = frames
3632 .into_iter()
3633 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3634 .collect();
3635 handles.shrink_to_fit();
3636 debug_assert_eq!(handles.len(), handles.capacity());
3637 let ptr = handles.as_mut_ptr();
3638 std::mem::forget(handles);
3639 unsafe { *num_frames = count };
3640 ptr
3641}
3642#[unsafe(no_mangle)]
3648pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3649 if frames.is_null() {
3650 return;
3651 }
3652 unsafe {
3653 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3654 for handle in handles {
3655 if !handle.is_null() {
3656 let _ = Box::from_raw(handle as *mut ConFrame);
3657 }
3658 }
3659 }
3660}
3661#[unsafe(no_mangle)]
3667pub unsafe extern "C" fn free_rkr_frame_ptr_array(
3668 frames: *mut *mut RKRConFrame,
3669 num_frames: usize,
3670) {
3671 if frames.is_null() {
3672 return;
3673 }
3674 unsafe {
3675 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3676 }
3677}
3678
3679#[cfg(feature = "metatensor")]
3694#[unsafe(no_mangle)]
3695pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3696 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3697}
3698#[cfg(feature = "metatensor")]
3700#[unsafe(no_mangle)]
3701pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3702 frame_handle: *const RKRConFrame,
3703 out_block: *mut *mut metatensor::c_api::mts_block_t,
3704) -> RKRStatus {
3705 if frame_handle.is_null() || out_block.is_null() {
3706 return RKRStatus::RKR_STATUS_NULL_POINTER;
3707 }
3708 unsafe { *out_block = std::ptr::null_mut() };
3709 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3710 return RKRStatus::RKR_STATUS_NULL_POINTER;
3711 };
3712 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3713 crate::metatensor_export::frame_positions_block(frame)
3714 })) {
3715 Ok(Ok(b)) => {
3716 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3717 RKRStatus::RKR_STATUS_SUCCESS
3718 }
3719 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3720 }
3721}
3722#[cfg(feature = "metatensor")]
3723#[unsafe(no_mangle)]
3724pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3725 frame_handle: *const RKRConFrame,
3726 out_block: *mut *mut metatensor::c_api::mts_block_t,
3727) -> RKRStatus {
3728 if frame_handle.is_null() || out_block.is_null() {
3729 return RKRStatus::RKR_STATUS_NULL_POINTER;
3730 }
3731 unsafe { *out_block = std::ptr::null_mut() };
3732 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3733 return RKRStatus::RKR_STATUS_NULL_POINTER;
3734 };
3735 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3736 crate::metatensor_export::frame_velocities_block(frame)
3737 })) {
3738 Ok(Ok(Some(b))) => {
3739 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3740 RKRStatus::RKR_STATUS_SUCCESS
3741 }
3742 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3743 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3744 }
3745}
3746#[cfg(feature = "metatensor")]
3747#[unsafe(no_mangle)]
3748pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3749 frame_handle: *const RKRConFrame,
3750 out_block: *mut *mut metatensor::c_api::mts_block_t,
3751) -> RKRStatus {
3752 if frame_handle.is_null() || out_block.is_null() {
3753 return RKRStatus::RKR_STATUS_NULL_POINTER;
3754 }
3755 unsafe { *out_block = std::ptr::null_mut() };
3756 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3757 return RKRStatus::RKR_STATUS_NULL_POINTER;
3758 };
3759 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3760 crate::metatensor_export::frame_forces_block(frame)
3761 })) {
3762 Ok(Ok(Some(b))) => {
3763 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3764 RKRStatus::RKR_STATUS_SUCCESS
3765 }
3766 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3767 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3768 }
3769}
3770#[cfg(feature = "metatensor")]
3771#[unsafe(no_mangle)]
3772pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3773 frame_handle: *const RKRConFrame,
3774 out_block: *mut *mut metatensor::c_api::mts_block_t,
3775) -> RKRStatus {
3776 if frame_handle.is_null() || out_block.is_null() {
3777 return RKRStatus::RKR_STATUS_NULL_POINTER;
3778 }
3779 unsafe { *out_block = std::ptr::null_mut() };
3780 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3781 return RKRStatus::RKR_STATUS_NULL_POINTER;
3782 };
3783 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3784 crate::metatensor_export::frame_energies_block(frame)
3785 })) {
3786 Ok(Ok(Some(b))) => {
3787 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3788 RKRStatus::RKR_STATUS_SUCCESS
3789 }
3790 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3791 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3792 }
3793}
3794#[cfg(not(feature = "zstd"))]
3795#[unsafe(no_mangle)]
3796pub unsafe extern "C" fn create_writer_zstd_c(
3797 _filename_c: *const c_char,
3798) -> *mut RKRConFrameWriter {
3799 ptr::null_mut()
3800}
3801#[cfg(not(feature = "zstd"))]
3802#[unsafe(no_mangle)]
3803pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3804 _filename_c: *const c_char,
3805 _precision: u8,
3806) -> *mut RKRConFrameWriter {
3807 ptr::null_mut()
3808}
3809#[cfg(not(feature = "metatensor"))]
3813pub struct mts_block_t {
3816 _private: [u8; 0],
3817}
3818#[cfg(not(feature = "metatensor"))]
3819#[unsafe(no_mangle)]
3820pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3821#[cfg(not(feature = "metatensor"))]
3822#[unsafe(no_mangle)]
3823pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3824 _frame_handle: *const RKRConFrame,
3825 out_block: *mut *mut mts_block_t,
3826) -> RKRStatus {
3827 if !out_block.is_null() {
3828 unsafe { *out_block = std::ptr::null_mut() };
3829 }
3830 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3831}
3832#[cfg(not(feature = "metatensor"))]
3833#[unsafe(no_mangle)]
3834pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3835 _frame_handle: *const RKRConFrame,
3836 out_block: *mut *mut mts_block_t,
3837) -> RKRStatus {
3838 if !out_block.is_null() {
3839 unsafe { *out_block = std::ptr::null_mut() };
3840 }
3841 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3842}
3843#[cfg(not(feature = "metatensor"))]
3844#[unsafe(no_mangle)]
3845pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3846 _frame_handle: *const RKRConFrame,
3847 out_block: *mut *mut mts_block_t,
3848) -> RKRStatus {
3849 if !out_block.is_null() {
3850 unsafe { *out_block = std::ptr::null_mut() };
3851 }
3852 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3853}
3854#[cfg(not(feature = "metatensor"))]
3855#[unsafe(no_mangle)]
3856pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3857 _frame_handle: *const RKRConFrame,
3858 out_block: *mut *mut mts_block_t,
3859) -> RKRStatus {
3860 if !out_block.is_null() {
3861 unsafe { *out_block = std::ptr::null_mut() };
3862 }
3863 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3864}
3865#[unsafe(no_mangle)]
3868pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3869 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3870 return 0;
3871 };
3872 frame.atom_data.len()
3873}
3874#[unsafe(no_mangle)]
3876pub unsafe extern "C" fn rkr_frame_xyz_view(
3877 frame_handle: *const RKRConFrame,
3878 out: *mut RKRArrayView,
3879) -> RKRStatus {
3880 fill_array2_view(frame_handle, out, |f| &f.positions, false)
3881}
3882
3883#[unsafe(no_mangle)]
3885pub unsafe extern "C" fn rkr_frame_velocities_view(
3886 frame_handle: *const RKRConFrame,
3887 out: *mut RKRArrayView,
3888) -> RKRStatus {
3889 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
3890}
3891
3892#[unsafe(no_mangle)]
3894pub unsafe extern "C" fn rkr_frame_forces_view(
3895 frame_handle: *const RKRConFrame,
3896 out: *mut RKRArrayView,
3897) -> RKRStatus {
3898 fill_array2_view(frame_handle, out, |f| &f.forces, true)
3899}
3900
3901#[unsafe(no_mangle)]
3907pub unsafe extern "C" fn rkr_frame_has_displacements(frame_handle: *const RKRConFrame) -> bool {
3908 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
3909 Some(f) => f.has_displacements(),
3910 None => false,
3911 }
3912}
3913
3914#[unsafe(no_mangle)]
3919pub unsafe extern "C" fn rkr_frame_displacements_view(
3920 frame_handle: *const RKRConFrame,
3921 out: *mut RKRArrayView,
3922) -> RKRStatus {
3923 fill_array2_view(frame_handle, out, |f| &f.displacements, true)
3924}
3925
3926#[unsafe(no_mangle)]
3932pub unsafe extern "C" fn rkr_frame_has_spreads(frame_handle: *const RKRConFrame) -> bool {
3933 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
3934 Some(f) => f.has_spreads(),
3935 None => false,
3936 }
3937}
3938
3939#[unsafe(no_mangle)]
3944pub unsafe extern "C" fn rkr_frame_spreads_view(
3945 frame_handle: *const RKRConFrame,
3946 out: *mut RKRArrayView,
3947) -> RKRStatus {
3948 fill_array2_view(frame_handle, out, |f| &f.spreads, true)
3949}
3950
3951#[unsafe(no_mangle)]
3953pub unsafe extern "C" fn rkr_frame_energies_view(
3954 frame_handle: *const RKRConFrame,
3955 out: *mut RKRArrayView,
3956) -> RKRStatus {
3957 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
3958}
3959
3960#[unsafe(no_mangle)]
3962pub unsafe extern "C" fn rkr_frame_masses_view(
3963 frame_handle: *const RKRConFrame,
3964 out: *mut RKRArrayView,
3965) -> RKRStatus {
3966 fill_array1_view(frame_handle, out, |f| &f.masses, false)
3967}
3968
3969#[unsafe(no_mangle)]
3971pub unsafe extern "C" fn rkr_frame_atom_ids_view(
3972 frame_handle: *const RKRConFrame,
3973 out: *mut RKRArrayView,
3974) -> RKRStatus {
3975 if frame_handle.is_null() || out.is_null() {
3976 return RKRStatus::RKR_STATUS_NULL_POINTER;
3977 }
3978 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3979 return RKRStatus::RKR_STATUS_NULL_POINTER;
3980 };
3981 let slice = frame.atom_ids.as_slice_memory_order();
3982 unsafe {
3983 *out = RKRArrayView {
3984 data: slice
3985 .map(|s| s.as_ptr() as *const std::ffi::c_void)
3986 .unwrap_or(std::ptr::null()),
3987 n: frame.atom_ids.len(),
3988 cols: 1,
3989 dtype_code: 1, dtype_bits: 64,
3991 };
3992 }
3993 RKRStatus::RKR_STATUS_SUCCESS
3994}
3995
3996#[unsafe(no_mangle)]
3999pub unsafe extern "C" fn rkr_frame_xyz_f64(
4000 frame_handle: *const RKRConFrame,
4001 n: *mut usize,
4002) -> *const f64 {
4003 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
4004}
4005
4006#[unsafe(no_mangle)]
4008pub unsafe extern "C" fn rkr_frame_velocities_f64(
4009 frame_handle: *const RKRConFrame,
4010 n: *mut usize,
4011) -> *const f64 {
4012 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
4013}
4014
4015#[unsafe(no_mangle)]
4017pub unsafe extern "C" fn rkr_frame_forces_f64(
4018 frame_handle: *const RKRConFrame,
4019 n: *mut usize,
4020) -> *const f64 {
4021 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
4022}
4023
4024#[unsafe(no_mangle)]
4029pub unsafe extern "C" fn rkr_frame_displacements_f64(
4030 frame_handle: *const RKRConFrame,
4031 n: *mut usize,
4032) -> *const f64 {
4033 f64_col_ptr(frame_handle, n, |f| f.displacements.f64_slice())
4034}
4035
4036#[unsafe(no_mangle)]
4041pub unsafe extern "C" fn rkr_frame_spreads_f64(
4042 frame_handle: *const RKRConFrame,
4043 n: *mut usize,
4044) -> *const f64 {
4045 f64_col_ptr(frame_handle, n, |f| f.spreads.f64_slice())
4046}
4047
4048fn fill_array2_view(
4049 frame_handle: *const RKRConFrame,
4050 out: *mut RKRArrayView,
4051 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4052 section: bool,
4053) -> RKRStatus {
4054 if frame_handle.is_null() || out.is_null() {
4055 return RKRStatus::RKR_STATUS_NULL_POINTER;
4056 }
4057 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4058 return RKRStatus::RKR_STATUS_NULL_POINTER;
4059 };
4060 let arr = get(frame);
4061 if section && arr.nrows() == 0 {
4062 unsafe { *out = RKRArrayView::empty() };
4063 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4064 }
4065 unsafe { *out = RKRArrayView::from_array2(arr) };
4066 RKRStatus::RKR_STATUS_SUCCESS
4067}
4068
4069fn fill_array1_view(
4070 frame_handle: *const RKRConFrame,
4071 out: *mut RKRArrayView,
4072 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4073 section: bool,
4074) -> RKRStatus {
4075 if frame_handle.is_null() || out.is_null() {
4076 return RKRStatus::RKR_STATUS_NULL_POINTER;
4077 }
4078 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4079 return RKRStatus::RKR_STATUS_NULL_POINTER;
4080 };
4081 let arr = get(frame);
4082 if section && arr.len() == 0 {
4083 unsafe { *out = RKRArrayView::empty() };
4084 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4085 }
4086 unsafe { *out = RKRArrayView::from_array1(arr) };
4087 RKRStatus::RKR_STATUS_SUCCESS
4088}
4089
4090fn f64_col_ptr(
4091 frame_handle: *const RKRConFrame,
4092 n: *mut usize,
4093 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
4094) -> *const f64 {
4095 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4096 if !n.is_null() {
4097 unsafe { *n = 0 };
4098 }
4099 return std::ptr::null();
4100 };
4101 match get(frame) {
4102 Some(s) if !s.is_empty() => {
4103 if !n.is_null() {
4104 unsafe { *n = s.len() / 3 };
4105 }
4106 s.as_ptr()
4107 }
4108 _ => {
4109 if !n.is_null() {
4110 unsafe { *n = 0 };
4111 }
4112 std::ptr::null()
4113 }
4114 }
4115}
4116
4117#[unsafe(no_mangle)]
4120pub unsafe extern "C" fn rkr_frame_copy_positions(
4121 frame_handle: *const RKRConFrame,
4122 out: *mut f64,
4123 out_len: usize,
4124) -> RKRStatus {
4125 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
4126}
4127fn copy_array2_f64(
4128 frame_handle: *const RKRConFrame,
4129 out: *mut f64,
4130 out_len: usize,
4131 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
4132 section: bool,
4133) -> RKRStatus {
4134 if frame_handle.is_null() || out.is_null() {
4135 return RKRStatus::RKR_STATUS_NULL_POINTER;
4136 }
4137 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4138 return RKRStatus::RKR_STATUS_NULL_POINTER;
4139 };
4140 let arr = get(frame);
4141 let n = if arr.nrows() > 0 {
4142 arr.nrows()
4143 } else {
4144 frame.atom_data.len()
4145 };
4146 if section && arr.nrows() == 0 {
4147 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4148 }
4149 let need = n.saturating_mul(3);
4150 if out_len < need {
4151 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4152 }
4153 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
4154 if arr.nrows() == n {
4155 if let Some(src) = arr.f64_slice() {
4156 dest.copy_from_slice(&src[..need.min(src.len())]);
4157 return RKRStatus::RKR_STATUS_SUCCESS;
4158 }
4159 for i in 0..n {
4160 let row = arr.as_f64_row(i);
4161 dest[i * 3] = row[0];
4162 dest[i * 3 + 1] = row[1];
4163 dest[i * 3 + 2] = row[2];
4164 }
4165 return RKRStatus::RKR_STATUS_SUCCESS;
4166 }
4167 for (i, a) in frame.atom_data.iter().enumerate() {
4168 dest[i * 3] = a.x;
4169 dest[i * 3 + 1] = a.y;
4170 dest[i * 3 + 2] = a.z;
4171 }
4172 RKRStatus::RKR_STATUS_SUCCESS
4173}
4174
4175fn copy_array1_f64(
4176 frame_handle: *const RKRConFrame,
4177 out: *mut f64,
4178 out_len: usize,
4179 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
4180 section: bool,
4181) -> RKRStatus {
4182 if frame_handle.is_null() || out.is_null() {
4183 return RKRStatus::RKR_STATUS_NULL_POINTER;
4184 }
4185 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4186 return RKRStatus::RKR_STATUS_NULL_POINTER;
4187 };
4188 let arr = get(frame);
4189 let n = if arr.len() > 0 {
4190 arr.len()
4191 } else {
4192 frame.atom_data.len()
4193 };
4194 if section && arr.len() == 0 {
4195 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4196 }
4197 if out_len < n {
4198 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4199 }
4200 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4201 if let Some(src) = arr.f64_slice() {
4202 dest.copy_from_slice(&src[..n.min(src.len())]);
4203 return RKRStatus::RKR_STATUS_SUCCESS;
4204 }
4205 for i in 0..n {
4206 dest[i] = arr.get_f64(i);
4207 }
4208 RKRStatus::RKR_STATUS_SUCCESS
4209}
4210
4211#[unsafe(no_mangle)]
4212pub unsafe extern "C" fn rkr_frame_copy_velocities(
4213 frame_handle: *const RKRConFrame,
4214 out: *mut f64,
4215 out_len: usize,
4216) -> RKRStatus {
4217 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
4218}
4219#[unsafe(no_mangle)]
4220pub unsafe extern "C" fn rkr_frame_copy_forces(
4221 frame_handle: *const RKRConFrame,
4222 out: *mut f64,
4223 out_len: usize,
4224) -> RKRStatus {
4225 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
4226}
4227#[unsafe(no_mangle)]
4233pub unsafe extern "C" fn rkr_frame_copy_displacements(
4234 frame_handle: *const RKRConFrame,
4235 out: *mut f64,
4236 out_len: usize,
4237) -> RKRStatus {
4238 copy_array2_f64(frame_handle, out, out_len, |f| &f.displacements, true)
4239}
4240#[unsafe(no_mangle)]
4246pub unsafe extern "C" fn rkr_frame_copy_spreads(
4247 frame_handle: *const RKRConFrame,
4248 out: *mut f64,
4249 out_len: usize,
4250) -> RKRStatus {
4251 copy_array2_f64(frame_handle, out, out_len, |f| &f.spreads, true)
4252}
4253#[unsafe(no_mangle)]
4254pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
4255 frame_handle: *const RKRConFrame,
4256 out: *mut f64,
4257 out_len: usize,
4258) -> RKRStatus {
4259 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
4260}
4261#[unsafe(no_mangle)]
4262pub unsafe extern "C" fn rkr_frame_copy_masses(
4263 frame_handle: *const RKRConFrame,
4264 out: *mut f64,
4265 out_len: usize,
4266) -> RKRStatus {
4267 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
4268}
4269#[unsafe(no_mangle)]
4270pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
4271 frame_handle: *const RKRConFrame,
4272 out: *mut u64,
4273 out_len: usize,
4274) -> RKRStatus {
4275 if frame_handle.is_null() || out.is_null() {
4276 return RKRStatus::RKR_STATUS_NULL_POINTER;
4277 }
4278 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4279 return RKRStatus::RKR_STATUS_NULL_POINTER;
4280 };
4281 let n = frame.atom_ids.len().max(frame.atom_data.len());
4282 if out_len < n {
4283 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4284 }
4285 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
4286 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
4287 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
4288 return RKRStatus::RKR_STATUS_SUCCESS;
4289 }
4290 for (i, a) in frame.atom_data.iter().enumerate() {
4291 dest[i] = a.atom_id;
4292 }
4293 RKRStatus::RKR_STATUS_SUCCESS
4294}
4295fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
4296 let n = frame.atom_data.len();
4297 let mut data = Vec::with_capacity(n * 3);
4298 for a in &frame.atom_data {
4299 data.extend_from_slice(&[a.x, a.y, a.z]);
4300 }
4301 ndarray::ArcArray2::from_shape_vec((n, 3), data)
4302 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
4303}
4304
4305#[unsafe(no_mangle)]
4315pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
4316 frame_handle: *const RKRConFrame,
4317 device_type: i32,
4318 device_id: i32,
4319 _stream: i64,
4320 _max_version_major: u32,
4321 _max_version_minor: u32,
4322 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4323) -> RKRStatus {
4324 if frame_handle.is_null() || out_tensor.is_null() {
4325 return RKRStatus::RKR_STATUS_NULL_POINTER;
4326 }
4327 unsafe { *out_tensor = std::ptr::null_mut() };
4328 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
4329 dlpk::sys::DLDevice::cpu()
4330 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
4331 #[cfg(feature = "cuda")]
4332 {
4333 dlpk::sys::DLDevice::cuda(device_id)
4334 }
4335 #[cfg(not(feature = "cuda"))]
4336 {
4337 let _ = device_id;
4338 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4339 }
4340 } else {
4341 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4342 };
4343 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4344 return RKRStatus::RKR_STATUS_NULL_POINTER;
4345 };
4346 match frame.positions_as_dlpack(dl_device) {
4348 Ok(tensor) => {
4349 let raw = tensor.into_raw();
4350 unsafe {
4351 *out_tensor = raw.as_ptr();
4352 }
4353 RKRStatus::RKR_STATUS_SUCCESS
4354 }
4355 Err(e) => map_dlpack_err(e),
4356 }
4357}
4358
4359#[unsafe(no_mangle)]
4365pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
4366 frame_handle: *mut RKRConFrame,
4367 tensor: *const RKRDLManagedTensorVersioned,
4368) -> RKRStatus {
4369 if frame_handle.is_null() || tensor.is_null() {
4370 return RKRStatus::RKR_STATUS_NULL_POINTER;
4371 }
4372 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
4373 let dl = unsafe { &(*tensor).dl_tensor };
4374 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
4375 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4376 }
4377 let n = frame.atom_data.len();
4378 let need = n.saturating_mul(3);
4379 let ndim = dl.ndim as usize;
4380 let shape = if dl.shape.is_null() {
4381 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4382 } else {
4383 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
4384 };
4385 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
4386 need
4387 } else if ndim == 1 && shape[0] == need as i64 {
4388 need
4389 } else {
4390 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4391 };
4392 let code = dl.dtype.code as u8;
4393 let bits = dl.dtype.bits;
4394 if dl.data.is_null() {
4395 return RKRStatus::RKR_STATUS_NULL_POINTER;
4396 }
4397 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
4398 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
4399 s.to_vec()
4400 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
4401 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
4402 s.iter().map(|&x| x as f64).collect()
4403 } else {
4404 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4405 };
4406 if frame.positions.nrows() != n {
4407 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4408 }
4409 for i in 0..n {
4410 frame
4411 .positions
4412 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
4413 }
4414 frame.sync_atom_data_from_arrays();
4415 RKRStatus::RKR_STATUS_SUCCESS
4416}
4417
4418#[unsafe(no_mangle)]
4421pub unsafe extern "C" fn rkr_frame_positions_dlpack(
4422 frame_handle: *const RKRConFrame,
4423 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4424) -> RKRStatus {
4425 unsafe {
4426 rkr_frame_positions_as_dlpack(
4427 frame_handle,
4428 rkr_dl_device_type::RKR_DL_CPU,
4429 0,
4430 0,
4431 1,
4432 0,
4433 out_tensor,
4434 )
4435 }
4436}
4437
4438#[unsafe(no_mangle)]
4443pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
4444 frame_handle: *const RKRConFrame,
4445 opts: *const RKRDlpackExportOptions,
4446 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4447) -> RKRStatus {
4448 if frame_handle.is_null() || out_tensor.is_null() {
4449 return RKRStatus::RKR_STATUS_NULL_POINTER;
4450 }
4451 unsafe { *out_tensor = std::ptr::null_mut() };
4452 let o = match resolve_dlpack_opts(opts) {
4453 Ok(o) => o,
4454 Err(st) => return st,
4455 };
4456 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4457 return RKRStatus::RKR_STATUS_NULL_POINTER;
4458 };
4459 let arr = frame_positions_arc(frame);
4460 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4461}
4462
4463#[unsafe(no_mangle)]
4465pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
4466 frame_handle: *const RKRConFrame,
4467 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4468) -> RKRStatus {
4469 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4470}
4471
4472#[unsafe(no_mangle)]
4473pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
4474 frame_handle: *const RKRConFrame,
4475 opts: *const RKRDlpackExportOptions,
4476 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4477) -> RKRStatus {
4478 if frame_handle.is_null() || out_tensor.is_null() {
4479 return RKRStatus::RKR_STATUS_NULL_POINTER;
4480 }
4481 unsafe { *out_tensor = std::ptr::null_mut() };
4482 let o = match resolve_dlpack_opts(opts) {
4483 Ok(o) => o,
4484 Err(st) => return st,
4485 };
4486 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4487 return RKRStatus::RKR_STATUS_NULL_POINTER;
4488 };
4489 if !frame.has_velocities() {
4490 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4491 }
4492 let n = frame.atom_data.len();
4493 let mut data = Vec::with_capacity(n * 3);
4494 for a in &frame.atom_data {
4495 let v = a.velocity.unwrap_or([0.0; 3]);
4496 data.extend_from_slice(&v);
4497 }
4498 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4499 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4500 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4501}
4502
4503#[unsafe(no_mangle)]
4505pub unsafe extern "C" fn rkr_frame_forces_dlpack(
4506 frame_handle: *const RKRConFrame,
4507 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4508) -> RKRStatus {
4509 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4510}
4511
4512#[unsafe(no_mangle)]
4513pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
4514 frame_handle: *const RKRConFrame,
4515 opts: *const RKRDlpackExportOptions,
4516 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4517) -> RKRStatus {
4518 if frame_handle.is_null() || out_tensor.is_null() {
4519 return RKRStatus::RKR_STATUS_NULL_POINTER;
4520 }
4521 unsafe { *out_tensor = std::ptr::null_mut() };
4522 let o = match resolve_dlpack_opts(opts) {
4523 Ok(o) => o,
4524 Err(st) => return st,
4525 };
4526 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4527 return RKRStatus::RKR_STATUS_NULL_POINTER;
4528 };
4529 if !frame.has_forces() {
4530 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4531 }
4532 let n = frame.atom_data.len();
4533 let mut data = Vec::with_capacity(n * 3);
4534 for a in &frame.atom_data {
4535 let f = a.force.unwrap_or([0.0; 3]);
4536 data.extend_from_slice(&f);
4537 }
4538 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4539 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4540 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4541}
4542
4543#[unsafe(no_mangle)]
4550pub unsafe extern "C" fn rkr_frame_displacements_dlpack(
4551 frame_handle: *const RKRConFrame,
4552 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4553) -> RKRStatus {
4554 unsafe { rkr_frame_displacements_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4555}
4556
4557#[unsafe(no_mangle)]
4564pub unsafe extern "C" fn rkr_frame_displacements_dlpack_ex(
4565 frame_handle: *const RKRConFrame,
4566 opts: *const RKRDlpackExportOptions,
4567 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4568) -> RKRStatus {
4569 if frame_handle.is_null() || out_tensor.is_null() {
4570 return RKRStatus::RKR_STATUS_NULL_POINTER;
4571 }
4572 unsafe { *out_tensor = std::ptr::null_mut() };
4573 let o = match resolve_dlpack_opts(opts) {
4574 Ok(o) => o,
4575 Err(st) => return st,
4576 };
4577 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4578 return RKRStatus::RKR_STATUS_NULL_POINTER;
4579 };
4580 if !frame.has_displacements() {
4581 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4582 }
4583 let n = frame.atom_data.len();
4584 let mut data = Vec::with_capacity(n * 3);
4585 for a in &frame.atom_data {
4586 let d = a.displacement.unwrap_or([0.0; 3]);
4587 data.extend_from_slice(&d);
4588 }
4589 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4590 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4591 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4592}
4593
4594#[unsafe(no_mangle)]
4601pub unsafe extern "C" fn rkr_frame_spreads_dlpack(
4602 frame_handle: *const RKRConFrame,
4603 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4604) -> RKRStatus {
4605 unsafe { rkr_frame_spreads_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4606}
4607
4608#[unsafe(no_mangle)]
4615pub unsafe extern "C" fn rkr_frame_spreads_dlpack_ex(
4616 frame_handle: *const RKRConFrame,
4617 opts: *const RKRDlpackExportOptions,
4618 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4619) -> RKRStatus {
4620 if frame_handle.is_null() || out_tensor.is_null() {
4621 return RKRStatus::RKR_STATUS_NULL_POINTER;
4622 }
4623 unsafe { *out_tensor = std::ptr::null_mut() };
4624 let o = match resolve_dlpack_opts(opts) {
4625 Ok(o) => o,
4626 Err(st) => return st,
4627 };
4628 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4629 return RKRStatus::RKR_STATUS_NULL_POINTER;
4630 };
4631 if !frame.has_spreads() {
4632 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4633 }
4634 let n = frame.atom_data.len();
4635 let mut data = Vec::with_capacity(n * 3);
4636 for a in &frame.atom_data {
4637 let d = a.spread.unwrap_or([0.0; 3]);
4638 data.extend_from_slice(&d);
4639 }
4640 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4641 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4642 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4643}
4644
4645#[unsafe(no_mangle)]
4647pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
4648 frame_handle: *const RKRConFrame,
4649 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4650) -> RKRStatus {
4651 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4652}
4653
4654#[unsafe(no_mangle)]
4655pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
4656 frame_handle: *const RKRConFrame,
4657 opts: *const RKRDlpackExportOptions,
4658 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4659) -> RKRStatus {
4660 if frame_handle.is_null() || out_tensor.is_null() {
4661 return RKRStatus::RKR_STATUS_NULL_POINTER;
4662 }
4663 unsafe { *out_tensor = std::ptr::null_mut() };
4664 let o = match resolve_dlpack_opts(opts) {
4665 Ok(o) => o,
4666 Err(st) => return st,
4667 };
4668 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4669 return RKRStatus::RKR_STATUS_NULL_POINTER;
4670 };
4671 if !frame.has_energies() {
4672 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4673 }
4674 let data: Vec<f64> = frame
4675 .atom_data
4676 .iter()
4677 .map(|a| a.energy.unwrap_or(0.0))
4678 .collect();
4679 let arr = ndarray::ArcArray1::from_vec(data);
4680 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
4681}
4682
4683pub struct RKRSelectionResult;
4687#[unsafe(no_mangle)]
4698pub unsafe extern "C" fn rkr_frame_select(
4699 frame_handle: *const RKRConFrame,
4700 selection: *const c_char,
4701 out_result: *mut *mut RKRSelectionResult,
4702) -> RKRStatus {
4703 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
4704 return RKRStatus::RKR_STATUS_NULL_POINTER;
4705 }
4706 let frame = unsafe { &*(frame_handle as *const ConFrame) };
4707 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
4708 Ok(s) => s,
4709 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
4710 };
4711 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
4712 Ok(result) => {
4713 let boxed = Box::new(result);
4714 unsafe {
4715 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
4716 }
4717 RKRStatus::RKR_STATUS_SUCCESS
4718 }
4719 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
4720 }
4721}
4722#[unsafe(no_mangle)]
4727pub unsafe extern "C" fn rkr_selection_result_match_count(
4728 result_handle: *const RKRSelectionResult,
4729) -> u64 {
4730 if result_handle.is_null() {
4731 return 0;
4732 }
4733 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4734 result.matches.len() as u64
4735}
4736#[unsafe(no_mangle)]
4741pub unsafe extern "C" fn rkr_selection_result_context_size(
4742 result_handle: *const RKRSelectionResult,
4743) -> u32 {
4744 if result_handle.is_null() {
4745 return 0;
4746 }
4747 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4748 result.context_size as u32
4749}
4750#[unsafe(no_mangle)]
4756pub unsafe extern "C" fn rkr_selection_result_match_at(
4757 result_handle: *const RKRSelectionResult,
4758 match_index: u64,
4759 out_atoms: *mut u64,
4760 out_size: *mut u32,
4761) -> RKRStatus {
4762 if result_handle.is_null() || out_atoms.is_null() {
4763 return RKRStatus::RKR_STATUS_NULL_POINTER;
4764 }
4765 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4766 let idx = match_index as usize;
4767 if idx >= result.matches.len() {
4768 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
4769 }
4770 let m = &result.matches[idx];
4771 unsafe {
4772 for i in 0..4 {
4773 *out_atoms.add(i) = if i < m.size {
4774 m.atoms[i] as u64
4775 } else {
4776 u64::MAX
4777 };
4778 }
4779 if !out_size.is_null() {
4780 *out_size = m.size as u32;
4781 }
4782 }
4783 RKRStatus::RKR_STATUS_SUCCESS
4784}
4785#[unsafe(no_mangle)]
4791pub unsafe extern "C" fn rkr_selection_result_primary_indices(
4792 result_handle: *const RKRSelectionResult,
4793 out_indices: *mut u64,
4794 capacity: u64,
4795 out_written: *mut u64,
4796) -> RKRStatus {
4797 if result_handle.is_null() {
4798 return RKRStatus::RKR_STATUS_NULL_POINTER;
4799 }
4800 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4801 let n = result.matches.len() as u64;
4802 if !out_written.is_null() {
4803 unsafe {
4804 *out_written = n;
4805 }
4806 }
4807 if n == 0 {
4808 return RKRStatus::RKR_STATUS_SUCCESS;
4809 }
4810 if out_indices.is_null() {
4811 return RKRStatus::RKR_STATUS_NULL_POINTER;
4812 }
4813 if capacity < n {
4814 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4815 }
4816 unsafe {
4817 for (i, m) in result.matches.iter().enumerate() {
4818 *out_indices.add(i) = m.atoms[0] as u64;
4819 }
4820 }
4821 RKRStatus::RKR_STATUS_SUCCESS
4822}
4823#[unsafe(no_mangle)]
4828pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
4829 if result_handle.is_null() {
4830 return;
4831 }
4832 unsafe {
4833 drop(Box::from_raw(
4834 result_handle as *mut crate::chemfiles_selection::SelectionResult,
4835 ));
4836 }
4837}
4838#[unsafe(no_mangle)]
4840pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
4841 #[cfg(feature = "chemfiles")]
4842 {
4843 1
4844 }
4845 #[cfg(not(feature = "chemfiles"))]
4846 {
4847 0
4848 }
4849}
4850
4851#[unsafe(no_mangle)]
4853pub extern "C" fn rkr_has_parallel_support() -> u8 {
4854 #[cfg(feature = "parallel")]
4855 {
4856 1
4857 }
4858 #[cfg(not(feature = "parallel"))]
4859 {
4860 0
4861 }
4862}
4863#[unsafe(no_mangle)]
4869pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
4870 if path_c.is_null() {
4871 return std::ptr::null_mut();
4872 }
4873 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4874 return std::ptr::null_mut();
4875 };
4876 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
4877 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4878 Err(_) => std::ptr::null_mut(),
4879 }
4880}
4881
4882#[unsafe(no_mangle)]
4888pub unsafe extern "C" fn rkr_read_chemfiles(
4889 path_c: *const c_char,
4890 num_frames: *mut usize,
4891) -> *mut *mut RKRConFrame {
4892 if path_c.is_null() || num_frames.is_null() {
4893 return std::ptr::null_mut();
4894 }
4895 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4896 return std::ptr::null_mut();
4897 };
4898 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
4899 Ok(frames) => pack_frame_handles(frames, num_frames),
4900 Err(_) => std::ptr::null_mut(),
4901 }
4902}
4903
4904#[unsafe(no_mangle)]
4910pub unsafe extern "C" fn rkr_read_chemfiles_nth(
4911 path_c: *const c_char,
4912 index: usize,
4913) -> *mut RKRConFrame {
4914 if path_c.is_null() {
4915 return std::ptr::null_mut();
4916 }
4917 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4918 return std::ptr::null_mut();
4919 };
4920 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
4921 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4922 Err(_) => std::ptr::null_mut(),
4923 }
4924}
4925
4926#[unsafe(no_mangle)]
4932pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
4933 if path_c.is_null() {
4934 return usize::MAX;
4935 }
4936 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4937 return usize::MAX;
4938 };
4939 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
4940}
4941
4942#[unsafe(no_mangle)]
4950pub unsafe extern "C" fn rkr_read_chemfiles_range(
4951 path_c: *const c_char,
4952 start: usize,
4953 step: usize,
4954 stop: usize,
4955 topology_c: *const c_char,
4956 guess_bonds: u8,
4957 num_frames: *mut usize,
4958) -> *mut *mut RKRConFrame {
4959 if path_c.is_null() || num_frames.is_null() {
4960 return std::ptr::null_mut();
4961 }
4962 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4963 return std::ptr::null_mut();
4964 };
4965 let topology = if topology_c.is_null() {
4966 None
4967 } else {
4968 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
4969 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
4970 _ => return std::ptr::null_mut(),
4971 }
4972 };
4973 let opts = crate::chemfiles_import::ChemfilesReadOpts {
4974 start,
4975 step,
4976 stop: if stop == usize::MAX { None } else { Some(stop) },
4977 format: None,
4978 topology,
4979 topology_format: None,
4980 guess_bonds: guess_bonds != 0,
4981 };
4982 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
4983 Ok(frames) => pack_frame_handles(frames, num_frames),
4984 Err(_) => std::ptr::null_mut(),
4985 }
4986}
4987#[unsafe(no_mangle)]
4994pub unsafe extern "C" fn rkr_read_chemfiles_memory(
4995 data_c: *const c_char,
4996 format_c: *const c_char,
4997 num_frames: *mut usize,
4998) -> *mut *mut RKRConFrame {
4999 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
5000 return std::ptr::null_mut();
5001 }
5002 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
5003 return std::ptr::null_mut();
5004 };
5005 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
5006 return std::ptr::null_mut();
5007 };
5008 match crate::chemfiles_import::con_frames_from_memory(data, format) {
5009 Ok(frames) => {
5010 let n = frames.len();
5011 unsafe { *num_frames = n };
5012 let mut ptrs: Vec<*mut RKRConFrame> = frames
5013 .into_iter()
5014 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
5015 .collect();
5016 let p = ptrs.as_mut_ptr();
5017 std::mem::forget(ptrs);
5018 p
5019 }
5020 Err(_) => std::ptr::null_mut(),
5021 }
5022}
5023#[unsafe(no_mangle)]
5028pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
5029 if tensor.is_null() {
5030 return;
5031 }
5032 unsafe {
5033 let t = &mut *tensor;
5034 if let Some(del) = t.deleter {
5035 del(tensor);
5036 }
5037 }
5038}
5039
5040#[unsafe(no_mangle)]
5050pub unsafe extern "C" fn rkr_pack_rcso(
5051 frame_handle: *const RKRConFrame,
5052 buf: *mut u8,
5053 buflen: usize,
5054 out_len: *mut usize,
5055) -> RKRStatus {
5056 if out_len.is_null() {
5057 return RKRStatus::RKR_STATUS_NULL_POINTER;
5058 }
5059 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
5060 Some(f) => f,
5061 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
5062 };
5063 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
5064 Ok(b) => b,
5065 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5066 };
5067 unsafe { *out_len = bytes.len() };
5068 if buf.is_null() {
5069 return RKRStatus::RKR_STATUS_SUCCESS;
5070 }
5071 if buflen < bytes.len() {
5072 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5073 }
5074 unsafe {
5075 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
5076 }
5077 RKRStatus::RKR_STATUS_SUCCESS
5078}
5079
5080#[unsafe(no_mangle)]
5085pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
5086 buf: *const u8,
5087 buflen: usize,
5088 out_natoms: *mut u32,
5089) -> RKRStatus {
5090 if buf.is_null() || out_natoms.is_null() {
5091 return RKRStatus::RKR_STATUS_NULL_POINTER;
5092 }
5093 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5094 match crate::rcso::Rcso::decode(bytes) {
5095 Ok(s) => {
5096 unsafe { *out_natoms = s.natoms };
5097 RKRStatus::RKR_STATUS_SUCCESS
5098 }
5099 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5100 }
5101}
5102
5103#[unsafe(no_mangle)]
5108pub unsafe extern "C" fn rkr_unpack_rcso_positions(
5109 buf: *const u8,
5110 buflen: usize,
5111 dest: *mut f64,
5112 dest_natoms: u32,
5113) -> RKRStatus {
5114 if buf.is_null() || dest.is_null() {
5115 return RKRStatus::RKR_STATUS_NULL_POINTER;
5116 }
5117 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5118 let soa = match crate::rcso::Rcso::decode(bytes) {
5119 Ok(s) => s,
5120 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5121 };
5122 if soa.natoms != dest_natoms {
5123 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5124 }
5125 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5126 for (i, p) in soa.positions.iter().enumerate() {
5127 out[i * 3] = p[0];
5128 out[i * 3 + 1] = p[1];
5129 out[i * 3 + 2] = p[2];
5130 }
5131 RKRStatus::RKR_STATUS_SUCCESS
5132}
5133
5134#[unsafe(no_mangle)]
5140pub unsafe extern "C" fn rkr_unpack_rcso_forces(
5141 buf: *const u8,
5142 buflen: usize,
5143 dest: *mut f64,
5144 dest_natoms: u32,
5145) -> RKRStatus {
5146 if buf.is_null() || dest.is_null() {
5147 return RKRStatus::RKR_STATUS_NULL_POINTER;
5148 }
5149 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
5150 let soa = match crate::rcso::Rcso::decode(bytes) {
5151 Ok(s) => s,
5152 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
5153 };
5154 let Some(forces) = soa.forces else {
5155 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
5156 };
5157 if soa.natoms != dest_natoms {
5158 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
5159 }
5160 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
5161 for (i, f) in forces.iter().enumerate() {
5162 out[i * 3] = f[0];
5163 out[i * 3 + 1] = f[1];
5164 out[i * 3 + 2] = f[2];
5165 }
5166 RKRStatus::RKR_STATUS_SUCCESS
5167}
5168
5169#[cfg(test)]
5170mod tests {
5171 use super::*;
5172 use std::ffi::{CStr, CString};
5173 #[test]
5174 fn frame_copy_positions_without_cframe() {
5175 let handle = test_frame_handle();
5176 let n = unsafe { rkr_frame_atom_count(handle) };
5177 assert_eq!(n, 1);
5178 let mut buf = vec![0.0f64; n * 3];
5179 assert_eq!(
5180 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
5181 RKRStatus::RKR_STATUS_SUCCESS
5182 );
5183 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5184 assert_eq!(
5185 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
5186 RKRStatus::RKR_STATUS_SUCCESS
5187 );
5188 assert!(!tensor.is_null());
5189 unsafe { rkr_dlpack_delete(tensor) };
5190 assert_eq!(
5191 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
5192 RKRStatus::RKR_STATUS_SECTION_ABSENT
5193 );
5194 unsafe { free_rkr_frame(handle) };
5195 }
5196 #[test]
5197 fn read_all_frames_c_abi_tiny() {
5198 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5199 let mut n: usize = 0;
5200 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5201 assert!(!arr.is_null() && n >= 1);
5202 let first = unsafe { *arr };
5203 let nat = unsafe { rkr_frame_atom_count(first) };
5204 let mut buf = vec![0.0f64; nat * 3];
5205 assert_eq!(
5206 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
5207 RKRStatus::RKR_STATUS_SUCCESS
5208 );
5209 unsafe { free_rkr_frame_array(arr, n) };
5210 }
5211
5212 #[test]
5213 fn read_all_frames_n_threads_matches_auto() {
5214 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5215 let mut n_auto: usize = 0;
5216 let mut n_one: usize = 0;
5217 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
5218 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
5219 assert!(!auto.is_null() && !one.is_null());
5220 assert_eq!(n_auto, n_one);
5221 assert_eq!(n_auto, 1);
5222 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
5223 let nat_one = unsafe { rkr_frame_atom_count(*one) };
5224 assert_eq!(nat_auto, nat_one);
5225 unsafe { free_rkr_frame_array(auto, n_auto) };
5226 unsafe { free_rkr_frame_array(one, n_one) };
5227 }
5228
5229 #[test]
5230 fn free_rkr_frame_ptr_array_keeps_frames() {
5231 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
5232 let mut n: usize = 0;
5233 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
5234 assert!(!arr.is_null() && n >= 1);
5235 let first = unsafe { *arr };
5236 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
5238 unsafe { free_rkr_frame_ptr_array(arr, n) };
5239 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
5240 unsafe { free_rkr_frame(first) };
5241 for h in rest {
5242 if !h.is_null() {
5243 unsafe { free_rkr_frame(h) };
5244 }
5245 }
5246 }
5247 fn test_frame_handle() -> *mut RKRConFrame {
5248 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5249 builder
5250 .prebox_header("Generated by test")
5251 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
5252 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
5253 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
5254 }
5255 #[test]
5256 fn header_line_rejects_null_buffer() {
5257 let frame = test_frame_handle();
5258 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
5259 unsafe { free_rkr_frame(frame) };
5260 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5261 }
5262 #[test]
5263 fn header_line_rejects_empty_buffer() {
5264 let frame = test_frame_handle();
5265 let mut buffer = [0 as c_char; 1];
5266 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
5267 unsafe { free_rkr_frame(frame) };
5268 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
5269 }
5270 #[test]
5271 fn header_line_truncates_and_terminates_buffer() {
5272 let frame = test_frame_handle();
5273 let mut buffer = [0 as c_char; 10];
5274 let status =
5275 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
5276 unsafe { free_rkr_frame(frame) };
5277 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5278 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
5279 assert_eq!(copied.to_str().unwrap(), "Generated");
5280 }
5281
5282 #[test]
5283 fn pack_rcso_size_query_then_dest() {
5284 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
5285 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
5286 assert!(!frame.is_null());
5287 let mut need = 0usize;
5288 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
5289 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5290 assert!(need >= 24);
5291 let mut buf = vec![0u8; need];
5292 let mut wrote = 0usize;
5293 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
5294 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5295 assert_eq!(wrote, need);
5296 assert_eq!(&buf[0..4], b"RCSO");
5297 let mut natoms = 0u32;
5298 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
5299 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5300 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
5301 let mut xyz = vec![0.0f64; natoms as usize * 3];
5302 let st =
5303 unsafe { rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms) };
5304 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5305 let mut frc = vec![0.0f64; natoms as usize * 3];
5306 let st =
5307 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
5308 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5309 unsafe { free_rkr_frame(frame) };
5310 }
5311
5312 fn test_builder_handle() -> *mut RKRConFrameBuilder {
5313 let cell = [10.0, 11.0, 12.0];
5314 let angles = [90.0, 91.0, 92.0];
5315 unsafe {
5316 rkr_frame_new(
5317 cell.as_ptr(),
5318 angles.as_ptr(),
5319 ptr::null(),
5320 ptr::null(),
5321 ptr::null(),
5322 ptr::null(),
5323 )
5324 }
5325 }
5326 fn c_string(s: &str) -> CString {
5327 CString::new(s).unwrap()
5328 }
5329 #[test]
5330 fn builder_set_units_c_abi_stores_the_object() {
5331 let builder = test_builder_handle();
5332 let units = c_string(
5333 r#"{"length":"A","energy":"ev","mass":"amu","time":"(amu*angstrom^2/eV)^0.5"}"#,
5334 );
5335 let status = unsafe { rkr_frame_builder_set_units(builder, units.as_ptr()) };
5336 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5337 let symbol = c_string("H");
5338 assert_eq!(
5339 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5340 RKRStatus::RKR_STATUS_SUCCESS
5341 );
5342 let frame = unsafe { rkr_frame_builder_build(builder) };
5343 assert!(!frame.is_null());
5344 let json = unsafe { rkr_frame_metadata_json(frame) };
5345 assert!(!json.is_null());
5346 let text = unsafe { CStr::from_ptr(json) }.to_str().unwrap();
5347 assert!(text.contains(r#""length":"angstrom""#), "{text}");
5348 assert!(text.contains(r#""energy":"eV""#), "{text}");
5349 assert!(
5350 text.contains(r#""time":"(amu*angstrom^2/eV)^0.5""#),
5351 "{text}"
5352 );
5353 unsafe {
5354 rkr_free_string(json);
5355 free_rkr_frame(frame);
5356 }
5357 }
5358 #[test]
5359 fn builder_stamps_con_spec_minor_one_unless_set() {
5360 let builder = test_builder_handle();
5361 let symbol = c_string("H");
5362 assert_eq!(
5363 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5364 RKRStatus::RKR_STATUS_SUCCESS
5365 );
5366 let frame = unsafe { rkr_frame_builder_build(builder) };
5367 let json = unsafe { rkr_frame_metadata_json(frame) };
5368 let text = unsafe { CStr::from_ptr(json) }
5369 .to_str()
5370 .unwrap()
5371 .to_string();
5372 assert!(text.contains(r#""con_spec_minor":1"#), "{text}");
5373 unsafe {
5374 rkr_free_string(json);
5375 free_rkr_frame(frame);
5376 }
5377
5378 let builder = test_builder_handle();
5379 assert_eq!(
5380 unsafe { rkr_frame_builder_set_con_spec_minor(builder, 4) },
5381 RKRStatus::RKR_STATUS_SUCCESS
5382 );
5383 let symbol = c_string("H");
5384 assert_eq!(
5385 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
5386 RKRStatus::RKR_STATUS_SUCCESS
5387 );
5388 let frame = unsafe { rkr_frame_builder_build(builder) };
5389 let json = unsafe { rkr_frame_metadata_json(frame) };
5390 let text = unsafe { CStr::from_ptr(json) }
5391 .to_str()
5392 .unwrap()
5393 .to_string();
5394 assert!(text.contains(r#""con_spec_minor":4"#), "{text}");
5395 unsafe {
5396 rkr_free_string(json);
5397 free_rkr_frame(frame);
5398 }
5399 }
5400 unsafe fn assert_single_atom(
5401 frame: *mut RKRConFrame,
5402 fixed: [bool; 3],
5403 velocity: Option<[f64; 3]>,
5404 forces: Option<[f64; 3]>,
5405 ) {
5406 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5407 assert!(!c_frame.is_null());
5408 let c_frame_ref = unsafe { &*c_frame };
5409 assert_eq!(c_frame_ref.num_atoms, 1);
5410 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
5411 assert_eq!(c_frame_ref.has_forces, forces.is_some());
5412 let atom = unsafe { &*c_frame_ref.atoms };
5413 assert_eq!(atom.fixed_x, fixed[0]);
5414 assert_eq!(atom.fixed_y, fixed[1]);
5415 assert_eq!(atom.fixed_z, fixed[2]);
5416 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
5417 assert_eq!(atom.has_velocity, velocity.is_some());
5418 assert_eq!(atom.has_forces, forces.is_some());
5419 if let Some([vx, vy, vz]) = velocity {
5420 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
5421 }
5422 if let Some([fx, fy, fz]) = forces {
5423 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
5424 }
5425 unsafe { free_c_frame(c_frame) };
5426 unsafe { free_rkr_frame(frame) };
5427 }
5428 #[test]
5429 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
5430 let builder = test_builder_handle();
5431 let symbol = c_string("Cu");
5432 let status = unsafe {
5433 rkr_frame_add_atom_with_fixed_mask(
5434 builder,
5435 symbol.as_ptr(),
5436 1.0,
5437 2.0,
5438 3.0,
5439 true,
5440 false,
5441 true,
5442 7,
5443 63.546,
5444 )
5445 };
5446 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5447 let frame = unsafe { rkr_frame_builder_build(builder) };
5448 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
5449 }
5450 #[test]
5451 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
5452 let builder = test_builder_handle();
5453 let symbol = c_string("H");
5454 let status = unsafe {
5455 rkr_frame_add_atom_with_velocity_fixed_mask(
5456 builder,
5457 symbol.as_ptr(),
5458 1.0,
5459 2.0,
5460 3.0,
5461 false,
5462 true,
5463 false,
5464 9,
5465 1.008,
5466 0.1,
5467 0.2,
5468 0.3,
5469 )
5470 };
5471 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5472 let frame = unsafe { rkr_frame_builder_build(builder) };
5473 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
5474 }
5475 #[test]
5476 fn builder_preserves_fixed_mask_for_atom_with_forces() {
5477 let builder = test_builder_handle();
5478 let symbol = c_string("O");
5479 let status = unsafe {
5480 rkr_frame_add_atom_with_forces_fixed_mask(
5481 builder,
5482 symbol.as_ptr(),
5483 1.0,
5484 2.0,
5485 3.0,
5486 true,
5487 true,
5488 false,
5489 11,
5490 15.999,
5491 -0.1,
5492 -0.2,
5493 -0.3,
5494 )
5495 };
5496 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5497 let frame = unsafe { rkr_frame_builder_build(builder) };
5498 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
5499 }
5500 #[test]
5501 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
5502 let builder = test_builder_handle();
5503 let symbol = c_string("N");
5504 let status = unsafe {
5505 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5506 builder,
5507 symbol.as_ptr(),
5508 1.0,
5509 2.0,
5510 3.0,
5511 false,
5512 true,
5513 true,
5514 13,
5515 14.007,
5516 0.4,
5517 0.5,
5518 0.6,
5519 -0.4,
5520 -0.5,
5521 -0.6,
5522 )
5523 };
5524 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5525 let frame = unsafe { rkr_frame_builder_build(builder) };
5526 unsafe {
5527 assert_single_atom(
5528 frame,
5529 [false, true, true],
5530 Some([0.4, 0.5, 0.6]),
5531 Some([-0.4, -0.5, -0.6]),
5532 )
5533 };
5534 }
5535 #[test]
5536 fn builder_bool_fixed_functions_set_all_axes_together() {
5537 let builder = test_builder_handle();
5538 let cu = c_string("Cu");
5539 let h = c_string("H");
5540 let atom_status =
5541 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
5542 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
5543 let velocity_status = unsafe {
5544 rkr_frame_add_atom_with_velocity(
5545 builder,
5546 h.as_ptr(),
5547 4.0,
5548 5.0,
5549 6.0,
5550 false,
5551 2,
5552 1.008,
5553 0.7,
5554 0.8,
5555 0.9,
5556 )
5557 };
5558 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
5559 let frame = unsafe { rkr_frame_builder_build(builder) };
5560 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5561 assert!(!c_frame.is_null());
5562 let c_frame_ref = unsafe { &*c_frame };
5563 assert_eq!(c_frame_ref.num_atoms, 2);
5564 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
5565 assert_eq!(
5566 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
5567 [true, true, true]
5568 );
5569 assert_eq!(
5570 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
5571 [false, false, false]
5572 );
5573 unsafe { free_c_frame(c_frame) };
5574 unsafe { free_rkr_frame(frame) };
5575 }
5576 #[test]
5577 fn status_message_returns_static_strings_for_all_status_values() {
5578 let cases = [
5579 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
5580 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
5581 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
5582 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
5583 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
5584 (
5585 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
5586 "index out of bounds",
5587 ),
5588 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
5589 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
5590 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
5591 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
5592 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
5593 (
5594 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
5595 "feature disabled in this build",
5596 ),
5597 ];
5598 for (status, expected) in cases {
5599 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
5600 assert_eq!(message.to_str().unwrap(), expected);
5601 }
5602 }
5603 unsafe fn assert_dlpack_cpu_float(
5607 t: *mut RKRDLManagedTensorVersioned,
5608 expect_ndim: i32,
5609 expect_shape: &[i64],
5610 expect_bits: u8,
5611 ) {
5612 assert!(!t.is_null());
5613 let dl = unsafe { &(*t).dl_tensor };
5614 assert_eq!(dl.ndim, expect_ndim);
5615 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
5616 assert_eq!(shape, expect_shape);
5617 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5618 assert_eq!(dl.dtype.bits, expect_bits);
5619 assert_eq!(dl.dtype.lanes, 1);
5620 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5621 assert!(!dl.data.is_null());
5622 }
5623
5624 #[test]
5625 fn frame_optional_section_dlpack_present_and_absent() {
5626 let handle = test_frame_handle();
5628 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5629 assert_eq!(
5630 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
5631 RKRStatus::RKR_STATUS_SECTION_ABSENT
5632 );
5633 assert!(t.is_null());
5634 assert_eq!(
5635 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
5636 RKRStatus::RKR_STATUS_SECTION_ABSENT
5637 );
5638 assert_eq!(
5639 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
5640 RKRStatus::RKR_STATUS_SECTION_ABSENT
5641 );
5642 unsafe { free_rkr_frame(handle) };
5643
5644 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
5646 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
5647 assert!(!fr.is_null());
5648 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
5649 assert!(n > 0);
5650 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5651 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
5652 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5653 unsafe {
5654 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
5655 rkr_dlpack_delete(vel);
5656 free_rkr_frame(fr);
5657 }
5658
5659 let cell = [10.0f64; 3];
5661 let ang = [90.0f64; 3];
5662 let b = unsafe {
5663 rkr_frame_new(
5664 cell.as_ptr(),
5665 ang.as_ptr(),
5666 std::ptr::null(),
5667 std::ptr::null(),
5668 std::ptr::null(),
5669 std::ptr::null(),
5670 )
5671 };
5672 assert!(!b.is_null());
5673 let sym = CString::new("H").unwrap();
5674 unsafe {
5675 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5676 b,
5677 sym.as_ptr(),
5678 0.0,
5679 0.0,
5680 0.0,
5681 false,
5682 false,
5683 false,
5684 0,
5685 1.0,
5686 0.1,
5687 0.0,
5688 0.0,
5689 0.0,
5690 0.0,
5691 -1.0,
5692 );
5693 rkr_frame_builder_set_last_energy(b, -0.5);
5694 }
5695 let built = unsafe { rkr_frame_builder_build(b) };
5696 assert!(!built.is_null());
5697 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
5698 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5699 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5700 assert_eq!(
5701 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
5702 RKRStatus::RKR_STATUS_SUCCESS
5703 );
5704 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
5705 assert_eq!(
5706 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
5707 RKRStatus::RKR_STATUS_SUCCESS
5708 );
5709 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
5710 let opts32 = RKRDlpackExportOptions {
5712 dtype: RKRDLDataType {
5713 code: rkr_dl_type_code::RKR_DL_FLOAT,
5714 bits: 32,
5715 lanes: 1,
5716 },
5717 device: RKRDLDevice {
5718 device_type: rkr_dl_device_type::RKR_DL_CPU,
5719 device_id: 0,
5720 },
5721 };
5722 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5723 assert_eq!(
5724 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
5725 RKRStatus::RKR_STATUS_SUCCESS
5726 );
5727 unsafe {
5728 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
5729 rkr_dlpack_delete(pos32);
5730 }
5731 let cuda_dev = RKRDlpackExportOptions {
5734 dtype: RKRDLDataType {
5735 code: rkr_dl_type_code::RKR_DL_FLOAT,
5736 bits: 64,
5737 lanes: 1,
5738 },
5739 device: RKRDLDevice {
5740 device_type: rkr_dl_device_type::RKR_DL_CUDA,
5741 device_id: 0,
5742 },
5743 };
5744 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5745 #[cfg(not(feature = "cuda"))]
5746 assert_eq!(
5747 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5748 RKRStatus::RKR_STATUS_FEATURE_DISABLED
5749 );
5750 #[cfg(feature = "cuda")]
5751 {
5752 assert_eq!(
5753 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5754 RKRStatus::RKR_STATUS_SUCCESS
5755 );
5756 assert!(!junk.is_null());
5757 let dl = unsafe { &(*junk).dl_tensor };
5758 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
5759 assert!(!dl.data.is_null());
5760 unsafe { rkr_dlpack_delete(junk) };
5761 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5763 assert_eq!(
5764 unsafe {
5765 rkr_frame_positions_as_dlpack(
5766 built,
5767 rkr_dl_device_type::RKR_DL_CUDA,
5768 0,
5769 0,
5770 1,
5771 0,
5772 &mut as_cuda,
5773 )
5774 },
5775 RKRStatus::RKR_STATUS_SUCCESS
5776 );
5777 assert!(!as_cuda.is_null());
5778 unsafe {
5779 assert_eq!(
5780 (*as_cuda).dl_tensor.device.device_type,
5781 dlpk::sys::DLDeviceType::kDLCUDA
5782 );
5783 rkr_dlpack_delete(as_cuda);
5784 }
5785 }
5786 let bad_bits = RKRDlpackExportOptions {
5788 dtype: RKRDLDataType {
5789 code: rkr_dl_type_code::RKR_DL_FLOAT,
5790 bits: 16,
5791 lanes: 1,
5792 },
5793 device: RKRDLDevice {
5794 device_type: rkr_dl_device_type::RKR_DL_CPU,
5795 device_id: 0,
5796 },
5797 };
5798 assert_eq!(
5799 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
5800 RKRStatus::RKR_STATUS_VALIDATION_ERROR
5801 );
5802 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5804 assert_eq!(
5805 unsafe {
5806 rkr_frame_positions_as_dlpack(
5807 built,
5808 rkr_dl_device_type::RKR_DL_CPU,
5809 0,
5810 0,
5811 1,
5812 0,
5813 &mut as_t,
5814 )
5815 },
5816 RKRStatus::RKR_STATUS_SUCCESS
5817 );
5818 unsafe {
5819 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
5820 }
5821 {
5823 let fr = unsafe { &mut *(built as *mut ConFrame) };
5824 for a in fr.atom_data.iter_mut() {
5825 a.x = -99.0;
5826 a.y = -99.0;
5827 a.z = -99.0;
5828 }
5829 }
5830 assert_eq!(
5831 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
5832 RKRStatus::RKR_STATUS_SUCCESS
5833 );
5834 {
5835 let fr = unsafe { &*(built as *const ConFrame) };
5836 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
5837 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
5838 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
5839 }
5840 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5842 assert_eq!(
5843 unsafe {
5844 rkr_frame_positions_as_dlpack(
5845 built,
5846 rkr_dl_device_type::RKR_DL_CPU,
5847 0,
5848 0,
5849 1,
5850 0,
5851 &mut again,
5852 )
5853 },
5854 RKRStatus::RKR_STATUS_SUCCESS
5855 );
5856 unsafe {
5857 let dl = &(*again).dl_tensor;
5858 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
5859 assert!((data[0] - 0.0).abs() < 1e-12);
5860 assert!((data[1] - 0.0).abs() < 1e-12);
5861 assert!((data[2] - 0.0).abs() < 1e-12);
5862 rkr_dlpack_delete(again);
5863 rkr_dlpack_delete(as_t);
5864 rkr_dlpack_delete(frc);
5865 rkr_dlpack_delete(eng);
5866 free_rkr_frame(built);
5867 }
5868 }
5869
5870 #[test]
5871 fn ffi_positions_dlpack_round_trip() {
5872 let handle = test_builder_handle();
5873 let sym = c_string("Cu");
5874 unsafe {
5875 rkr_frame_add_atom_full(
5876 handle,
5877 sym.as_ptr(),
5878 1.0,
5879 2.0,
5880 3.0,
5881 false,
5882 false,
5883 false,
5884 7,
5885 63.5,
5886 ptr::null(),
5887 ptr::null(),
5888 )
5889 };
5890 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5891 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
5892 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5893 assert!(!t.is_null());
5894 let dl = unsafe { &(*t).dl_tensor };
5896 assert_eq!(dl.ndim, 2);
5897 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
5898 assert_eq!(shape, &[1, 3]);
5899 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5900 assert_eq!(dl.dtype.bits, 64);
5901 assert_eq!(dl.dtype.lanes, 1);
5902 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5903 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
5904 assert_eq!(data, &[1.0, 2.0, 3.0]);
5905 let deleter = unsafe { (*t).deleter };
5907 if let Some(del) = deleter {
5908 unsafe { del(t) };
5909 }
5910 unsafe { free_rkr_frame_builder(handle) };
5911 }
5912 #[test]
5913 fn ffi_velocities_dlpack_section_absent() {
5914 let handle = test_builder_handle();
5915 let sym = c_string("Cu");
5916 unsafe {
5917 rkr_frame_add_atom_full(
5918 handle,
5919 sym.as_ptr(),
5920 0.0,
5921 0.0,
5922 0.0,
5923 false,
5924 false,
5925 false,
5926 0,
5927 63.5,
5928 ptr::null(),
5929 ptr::null(),
5930 )
5931 };
5932 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5933 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
5934 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
5935 assert!(t.is_null());
5936 unsafe { free_rkr_frame_builder(handle) };
5937 }
5938 #[test]
5939 fn ffi_dlpack_null_handle_rejects() {
5940 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5941 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
5942 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5943 assert!(t.is_null());
5944 }
5945 #[cfg(feature = "chemfiles")]
5946 #[test]
5947 fn rkr_frame_select_finds_oxygen() {
5948 use crate::types::ConFrameBuilder;
5949 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5950 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
5951 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
5952 let frame = b.build().unwrap();
5953 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5954 let sel = CString::new("name O").unwrap();
5955 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5956 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
5957 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5958 assert!(!out.is_null());
5959 let n = unsafe { rkr_selection_result_match_count(out) };
5960 assert_eq!(n, 1);
5961 let mut atoms = [u64::MAX; 4];
5962 let mut size = 0u32;
5963 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
5964 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
5965 assert_eq!(size, 1);
5966 assert_eq!(atoms[0], 0);
5967 unsafe {
5968 rkr_selection_result_free(out);
5969 free_rkr_frame(frame_ptr);
5970 }
5971 }
5972 #[cfg(feature = "chemfiles")]
5974 #[test]
5975 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
5976 use crate::types::{Bond, ConFrameBuilder};
5977 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5979 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
5980 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
5981 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
5982 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
5983 let mut frame = b.build().unwrap();
5984 let id_to = |id: u64| {
5985 frame
5986 .atom_data
5987 .iter()
5988 .position(|a| a.atom_id == id)
5989 .unwrap() as u32
5990 };
5991 frame.header.set_bonds(&[
5992 Bond::new(id_to(0), id_to(1)),
5993 Bond::new(id_to(1), id_to(2)),
5994 Bond::new(id_to(2), id_to(3)),
5995 ]);
5996 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5997 let run = |sel: &str| -> (u64, u32) {
5998 let csel = CString::new(sel).unwrap();
5999 let mut out: *mut RKRSelectionResult = ptr::null_mut();
6000 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
6001 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
6002 let n = unsafe { rkr_selection_result_match_count(out) };
6003 let ctx = unsafe { rkr_selection_result_context_size(out) };
6004 unsafe { rkr_selection_result_free(out) };
6005 (n, ctx)
6006 };
6007 assert_eq!(run("bonds: all"), (3, 2));
6008 assert_eq!(run("angles: all"), (2, 3));
6009 assert_eq!(run("dihedrals: all"), (1, 4));
6010 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
6011 assert_eq!(
6012 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
6013 run("bonds: type(#1) H and name(#2) O").0
6014 );
6015 unsafe { free_rkr_frame(frame_ptr) };
6016 }
6017 #[cfg(feature = "metatensor")]
6018 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
6019 assert!(!block.is_null());
6020 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
6021 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
6022 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
6023 let shape_fn = array
6024 .shape
6025 .expect("mts_array_t.shape from metatensor C API");
6026 let mut shape_ptr: *const usize = std::ptr::null();
6027 let mut shape_count: usize = 0;
6028 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
6029 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
6030 assert_eq!(shape_count, 2);
6031 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
6032 assert_eq!(shape[0], n);
6033 assert_eq!(shape[1], props);
6034 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
6035 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
6036 assert!(!samples.is_null() && !prop_lab.is_null());
6037 }
6038 #[cfg(feature = "metatensor")]
6039 #[test]
6040 fn metatensor_positions_via_c_abi() {
6041 let handle = test_frame_handle();
6042 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6043 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
6044 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6045 assert_mts_block_shape(out, 1, 3);
6046 unsafe { rkr_mts_block_free(out) };
6047 for (name, export) in [
6048 (
6049 "velocities",
6050 rkr_frame_metatensor_velocities_block
6051 as unsafe extern "C" fn(
6052 *const RKRConFrame,
6053 *mut *mut metatensor::c_api::mts_block_t,
6054 ) -> RKRStatus,
6055 ),
6056 (
6057 "forces",
6058 rkr_frame_metatensor_forces_block
6059 as unsafe extern "C" fn(
6060 *const RKRConFrame,
6061 *mut *mut metatensor::c_api::mts_block_t,
6062 ) -> RKRStatus,
6063 ),
6064 (
6065 "atom_energies",
6066 rkr_frame_metatensor_atom_energies_block
6067 as unsafe extern "C" fn(
6068 *const RKRConFrame,
6069 *mut *mut metatensor::c_api::mts_block_t,
6070 ) -> RKRStatus,
6071 ),
6072 ] {
6073 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6074 let st_abs = unsafe { export(handle, &mut o) };
6075 assert_eq!(
6076 st_abs,
6077 RKRStatus::RKR_STATUS_SECTION_ABSENT,
6078 "{name} must be SECTION_ABSENT on minimal test frame"
6079 );
6080 assert!(o.is_null());
6081 }
6082 unsafe { free_rkr_frame(handle) };
6083 }
6084
6085 #[cfg(feature = "metatensor")]
6086 #[test]
6087 fn metatensor_optional_sections_via_c_abi() {
6088 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
6090 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
6091 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
6092 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
6093 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
6094 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
6095 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
6096 builder.set_atom_energy(0, -0.5).unwrap();
6097 builder.set_atom_energy(1, -1.0).unwrap();
6098 let frame = builder.build().unwrap();
6099 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
6100 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6101 assert_eq!(
6102 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
6103 RKRStatus::RKR_STATUS_SUCCESS
6104 );
6105 assert_mts_block_shape(pos, 2, 3);
6106 unsafe { rkr_mts_block_free(pos) };
6107 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6108 assert_eq!(
6109 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
6110 RKRStatus::RKR_STATUS_SUCCESS
6111 );
6112 assert_mts_block_shape(vel, 2, 3);
6113 unsafe { rkr_mts_block_free(vel) };
6114 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6115 assert_eq!(
6116 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
6117 RKRStatus::RKR_STATUS_SUCCESS
6118 );
6119 assert_mts_block_shape(frc, 2, 3);
6120 unsafe { rkr_mts_block_free(frc) };
6121 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
6122 assert_eq!(
6123 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
6124 RKRStatus::RKR_STATUS_SUCCESS
6125 );
6126 assert_mts_block_shape(eng, 2, 1);
6127 unsafe { rkr_mts_block_free(eng) };
6128 unsafe { free_rkr_frame(handle) };
6129 }
6130
6131 #[test]
6132 fn string_iterator_yields_frames_from_buffer() {
6133 let text =
6134 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
6135 let c_text = CString::new(text.as_str()).unwrap();
6136 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
6137 assert!(!it.is_null());
6138 let mut n = 0usize;
6139 loop {
6140 let fr = unsafe { con_frame_iterator_next(it) };
6141 if fr.is_null() {
6142 break;
6143 }
6144 n += 1;
6145 unsafe { free_rkr_frame(fr) };
6146 }
6147 unsafe { free_con_frame_iterator(it) };
6148 assert!(n >= 1, "string iterator should yield >=1 frame");
6149
6150 let bytes = text.as_bytes();
6151 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
6152 assert!(!it2.is_null());
6153 let fr2 = unsafe { con_frame_iterator_next(it2) };
6154 assert!(!fr2.is_null());
6155 unsafe {
6156 free_rkr_frame(fr2);
6157 free_con_frame_iterator(it2);
6158 }
6159 }
6160
6161 #[test]
6162 fn file_iterator_reads_gzip_when_present() {
6163 use flate2::Compression;
6164 use flate2::write::GzEncoder;
6165 use std::io::Write;
6166 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
6167 let dir = tempfile::tempdir().expect("tempdir");
6168 let gz_path = dir.path().join("tiny_cuh2.con.gz");
6169 {
6170 let f = std::fs::File::create(&gz_path).unwrap();
6171 let mut enc = GzEncoder::new(f, Compression::default());
6172 enc.write_all(&plain).unwrap();
6173 enc.finish().unwrap();
6174 }
6175 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
6176 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
6177 assert!(
6178 !it.is_null(),
6179 "path iterator must decompress .con.gz transparently"
6180 );
6181 let fr = unsafe { con_frame_iterator_next(it) };
6182 assert!(!fr.is_null());
6183 let n = unsafe { rkr_frame_atom_count(fr) };
6184 assert!(n > 0);
6185 unsafe {
6186 free_rkr_frame(fr);
6187 free_con_frame_iterator(it);
6188 }
6189 }
6190}