1use crate::helpers::symbol_to_atomic_number;
2use crate::iterators::{self, ConFrameIterator};
3use crate::types::{ConFrame, ConFrameBuilder, meta};
4use crate::writer::ConFrameWriter;
5use std::ffi::{CStr, CString, c_char};
6use std::fs::File;
7use std::path::Path;
8use std::ptr;
9pub const RKR_ABI_VERSION_MAJOR: u32 = 1;
14pub const RKR_ABI_VERSION_MINOR: u32 = 0;
16pub const RKR_ABI_LAYOUT_REVISION: u32 = 1;
18
19#[unsafe(no_mangle)]
21pub extern "C" fn rkr_abi_version_major() -> u32 {
22 RKR_ABI_VERSION_MAJOR
23}
24
25#[unsafe(no_mangle)]
27pub extern "C" fn rkr_abi_version_minor() -> u32 {
28 RKR_ABI_VERSION_MINOR
29}
30
31#[unsafe(no_mangle)]
33pub extern "C" fn rkr_abi_layout_revision() -> u32 {
34 RKR_ABI_LAYOUT_REVISION
35}
36
37#[unsafe(no_mangle)]
39pub extern "C" fn rkr_abi_stamp() -> *const c_char {
40 const STAMP: &[u8] = b"readcon-core/abi-1.0/layout-1\0";
41 STAMP.as_ptr() as *const c_char
42}
43
44pub const RKR_CON_SPEC_VERSION: u32 = 3;
52#[unsafe(no_mangle)]
54pub extern "C" fn rkr_con_spec_version() -> u32 {
55 crate::CON_SPEC_VERSION
56}
57#[unsafe(no_mangle)]
60pub extern "C" fn rkr_library_version() -> *const c_char {
61 const VERSION_NUL: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
63 VERSION_NUL.as_ptr() as *const c_char
64}
65#[unsafe(no_mangle)]
77pub unsafe extern "C" fn rkr_frame_atom_index_by_id(
78 frame_handle: *const RKRConFrame,
79 atom_id: u64,
80) -> u64 {
81 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
82 Some(f) => f,
83 None => return u64::MAX,
84 };
85 match frame.atom_index_by_id(atom_id) {
86 Some(idx) => idx as u64,
87 None => u64::MAX,
88 }
89}
90#[unsafe(no_mangle)]
99pub unsafe extern "C" fn rkr_symbol_to_z(symbol: *const c_char) -> u64 {
100 if symbol.is_null() {
101 return 0;
102 }
103 match unsafe { CStr::from_ptr(symbol) }.to_str() {
104 Ok(s) => symbol_to_atomic_number(s),
105 Err(_) => 0,
106 }
107}
108#[unsafe(no_mangle)]
113pub extern "C" fn rkr_z_to_symbol(z: u64) -> *const c_char {
114 macro_rules! cstrs {
119 ($($lit:literal),* $(,)?) => {
120 [$(concat!($lit, "\0").as_bytes()),*]
121 };
122 }
123 const TABLE: [&[u8]; 93] = cstrs![
124 "X", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P",
125 "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
126 "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh",
127 "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
128 "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re",
129 "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
130 "Pa", "U",
131 ];
132 let idx = if (1..=92).contains(&z) { z as usize } else { 0 };
133 TABLE[idx].as_ptr() as *const c_char
134}
135#[unsafe(no_mangle)]
138pub extern "C" fn rkr_frame_spec_version(frame_handle: *const RKRConFrame) -> u32 {
139 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
140 Some(f) => f.header.spec_version,
141 None => 0,
142 }
143}
144#[unsafe(no_mangle)]
151pub unsafe extern "C" fn rkr_frame_metadata_json(frame_handle: *const RKRConFrame) -> *mut c_char {
152 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
153 Some(f) => f,
154 None => return ptr::null_mut(),
155 };
156 let mut obj = serde_json::Map::new();
157 obj.insert(
158 meta::CON_SPEC_VERSION.into(),
159 serde_json::Value::from(frame.header.spec_version),
160 );
161 for (k, v) in &frame.header.metadata {
162 obj.insert(k.clone(), v.clone());
163 }
164 let json_str = serde_json::Value::Object(obj).to_string();
165 match CString::new(json_str) {
166 Ok(cs) => cs.into_raw(),
167 Err(_) => ptr::null_mut(),
168 }
169}
170#[unsafe(no_mangle)]
172pub extern "C" fn rkr_frame_energy(frame_handle: *const RKRConFrame) -> f64 {
173 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
174 Some(f) => f.header.energy().unwrap_or(f64::NAN),
175 None => f64::NAN,
176 }
177}
178
179#[unsafe(no_mangle)]
182pub extern "C" fn rkr_frame_index_energy(frame_handle: *const RKRConFrame) -> f64 {
183 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
184 Some(f) => crate::index_proj::finite_energy(f).unwrap_or(f64::NAN),
185 None => f64::NAN,
186 }
187}
188
189#[unsafe(no_mangle)]
191pub unsafe extern "C" fn rkr_frame_composition_formula(
192 frame_handle: *const RKRConFrame,
193) -> *mut c_char {
194 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
195 Some(f) => f,
196 None => return ptr::null_mut(),
197 };
198 let s = crate::index_proj::frame_composition_formula(frame);
199 match CString::new(s) {
200 Ok(cs) => cs.into_raw(),
201 Err(_) => ptr::null_mut(),
202 }
203}
204
205#[unsafe(no_mangle)]
207pub extern "C" fn rkr_frame_total_mass(frame_handle: *const RKRConFrame) -> f64 {
208 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
209 Some(f) => crate::index_proj::frame_total_mass(f).unwrap_or(f64::NAN),
210 None => f64::NAN,
211 }
212}
213
214#[unsafe(no_mangle)]
216pub extern "C" fn rkr_frame_cell_volume(frame_handle: *const RKRConFrame) -> f64 {
217 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
218 Some(f) => crate::index_proj::frame_cell_volume(f).unwrap_or(f64::NAN),
219 None => f64::NAN,
220 }
221}
222
223#[unsafe(no_mangle)]
225pub extern "C" fn rkr_frame_fmax(frame_handle: *const RKRConFrame) -> f64 {
226 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
227 Some(f) => crate::index_proj::frame_fmax(f).unwrap_or(f64::NAN),
228 None => f64::NAN,
229 }
230}
231
232#[unsafe(no_mangle)]
234pub extern "C" fn rkr_frame_sections_mask(frame_handle: *const RKRConFrame) -> u8 {
235 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
236 Some(f) => crate::index_proj::sections_present_mask(f),
237 None => 0,
238 }
239}
240
241#[unsafe(no_mangle)]
243pub extern "C" fn rkr_frame_index_natoms(frame_handle: *const RKRConFrame) -> u32 {
244 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
245 Some(f) => f.atom_data.len() as u32,
246 None => 0,
247 }
248}
249
250#[unsafe(no_mangle)]
253pub unsafe extern "C" fn rkr_frame_index_projection_json(
254 frame_handle: *const RKRConFrame,
255) -> *mut c_char {
256 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
257 Some(f) => f,
258 None => return ptr::null_mut(),
259 };
260 let p = crate::index_proj::FrameIndexProjection::from_frame(frame);
261 let v = serde_json::json!({
262 "n_atoms": p.n_atoms,
263 "formula": p.formula,
264 "energy": p.energy,
265 "fmax": p.fmax,
266 "total_mass": p.total_mass,
267 "cell_volume": p.cell_volume,
268 "sections_mask": p.sections_mask,
269 "has_forces": p.has_forces,
270 "has_velocities": p.has_velocities,
271 "has_energy": p.has_energy,
272 "symbols": p.symbols,
273 "species_counts": p.species_counts.iter().map(|(s,c)| serde_json::json!([s, c])).collect::<Vec<_>>(),
274 "time": p.time,
275 "timestep": p.timestep,
276 "frame_index": p.frame_index,
277 "neb_bead": p.neb_bead,
278 "neb_band": p.neb_band,
279 "charge": p.charge,
280 "magmom": p.magmom,
281 });
282 match CString::new(v.to_string()) {
283 Ok(cs) => cs.into_raw(),
284 Err(_) => ptr::null_mut(),
285 }
286}
287#[unsafe(no_mangle)]
294pub unsafe extern "C" fn rkr_frame_potential_type(frame_handle: *const RKRConFrame) -> *mut c_char {
295 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
296 Some(f) => f,
297 None => return ptr::null_mut(),
298 };
299 match frame.header.potential_type() {
300 Some(pot_type) => match CString::new(pot_type) {
301 Ok(cs) => cs.into_raw(),
302 Err(_) => ptr::null_mut(),
303 },
304 None => ptr::null_mut(),
305 }
306}
307#[unsafe(no_mangle)]
309pub extern "C" fn rkr_frame_frame_index(frame_handle: *const RKRConFrame) -> u64 {
310 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
311 Some(f) => f.header.frame_index().unwrap_or(u64::MAX),
312 None => u64::MAX,
313 }
314}
315#[unsafe(no_mangle)]
317pub extern "C" fn rkr_frame_time(frame_handle: *const RKRConFrame) -> f64 {
318 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
319 Some(f) => f.header.time().unwrap_or(f64::NAN),
320 None => f64::NAN,
321 }
322}
323#[unsafe(no_mangle)]
325pub extern "C" fn rkr_frame_timestep(frame_handle: *const RKRConFrame) -> f64 {
326 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
327 Some(f) => f.header.timestep().unwrap_or(f64::NAN),
328 None => f64::NAN,
329 }
330}
331#[unsafe(no_mangle)]
333pub extern "C" fn rkr_frame_neb_bead(frame_handle: *const RKRConFrame) -> u64 {
334 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
335 Some(f) => f.header.neb_bead().unwrap_or(u64::MAX),
336 None => u64::MAX,
337 }
338}
339#[unsafe(no_mangle)]
341pub extern "C" fn rkr_frame_neb_band(frame_handle: *const RKRConFrame) -> u64 {
342 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
343 Some(f) => f.header.neb_band().unwrap_or(u64::MAX),
344 None => u64::MAX,
345 }
346}
347#[repr(C)]
352#[allow(non_camel_case_types)]
353#[derive(Debug, PartialEq, Eq)]
354pub enum RKRStatus {
355 RKR_STATUS_SUCCESS = 0,
357 RKR_STATUS_NULL_POINTER = -1,
359 RKR_STATUS_INVALID_UTF8 = -2,
361 RKR_STATUS_INVALID_JSON = -3,
363 RKR_STATUS_IO_ERROR = -4,
365 RKR_STATUS_INDEX_OUT_OF_BOUNDS = -5,
367 RKR_STATUS_BUFFER_TOO_SMALL = -6,
369 RKR_STATUS_INTERNAL_ERROR = -7,
371 RKR_STATUS_SECTION_ABSENT = -8,
374 RKR_STATUS_VALIDATION_ERROR = -9,
376 RKR_STATUS_SELECTION_ERROR = -10,
378 RKR_STATUS_FEATURE_DISABLED = -11,
381 RKR_STATUS_DEVICE_MISMATCH = -12,
383 RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED = -13,
385}
386#[unsafe(no_mangle)]
391pub unsafe extern "C" fn rkr_frame_bond_count(frame_handle: *const RKRConFrame) -> u64 {
392 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
393 Some(f) => f.bonds().len() as u64,
394 None => 0,
395 }
396}
397#[unsafe(no_mangle)]
406pub unsafe extern "C" fn rkr_frame_bond_at(
407 frame_handle: *const RKRConFrame,
408 index: u64,
409 out_i: *mut u32,
410 out_j: *mut u32,
411 out_has_order: *mut u8,
412 out_order: *mut i32,
413) -> RKRStatus {
414 if frame_handle.is_null()
415 || out_i.is_null()
416 || out_j.is_null()
417 || out_has_order.is_null()
418 || out_order.is_null()
419 {
420 return RKRStatus::RKR_STATUS_NULL_POINTER;
421 }
422 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
423 return RKRStatus::RKR_STATUS_NULL_POINTER;
424 };
425 let bonds = frame.bonds();
426 let Some(bond) = bonds.get(index as usize) else {
427 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
428 };
429 unsafe {
430 *out_i = bond.i;
431 *out_j = bond.j;
432 if let Some(order) = bond.order {
433 *out_has_order = 1;
434 *out_order = order;
435 } else {
436 *out_has_order = 0;
437 *out_order = 0;
438 }
439 }
440 RKRStatus::RKR_STATUS_SUCCESS
441}
442#[unsafe(no_mangle)]
445pub extern "C" fn rkr_status_message(status: RKRStatus) -> *const c_char {
446 match status {
447 RKRStatus::RKR_STATUS_SUCCESS => c"success".as_ptr(),
448 RKRStatus::RKR_STATUS_NULL_POINTER => c"null pointer".as_ptr(),
449 RKRStatus::RKR_STATUS_INVALID_UTF8 => c"invalid UTF-8".as_ptr(),
450 RKRStatus::RKR_STATUS_INVALID_JSON => c"invalid JSON".as_ptr(),
451 RKRStatus::RKR_STATUS_IO_ERROR => c"I/O error".as_ptr(),
452 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS => c"index out of bounds".as_ptr(),
453 RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL => c"buffer too small".as_ptr(),
454 RKRStatus::RKR_STATUS_INTERNAL_ERROR => c"internal error".as_ptr(),
455 RKRStatus::RKR_STATUS_SECTION_ABSENT => c"section absent".as_ptr(),
456 RKRStatus::RKR_STATUS_VALIDATION_ERROR => c"validation error".as_ptr(),
457 RKRStatus::RKR_STATUS_SELECTION_ERROR => c"selection error".as_ptr(),
458 RKRStatus::RKR_STATUS_FEATURE_DISABLED => c"feature disabled in this build".as_ptr(),
459 RKRStatus::RKR_STATUS_DEVICE_MISMATCH => c"DLPack device mismatch".as_ptr(),
460 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED => {
461 c"device allocation unsupported in this build".as_ptr()
462 }
463 }
464}
465pub struct RKRConFrame {
470 _private: [u8; 0],
471}
472pub struct RKRConFrameWriter {
477 _private: [u8; 0],
478}
479#[repr(C)]
485pub struct RKRArrayView {
486 pub data: *const std::ffi::c_void,
487 pub n: usize,
488 pub cols: u32,
489 pub dtype_code: u8,
490 pub dtype_bits: u8,
491}
492
493impl RKRArrayView {
494 fn empty() -> Self {
495 Self {
496 data: std::ptr::null(),
497 n: 0,
498 cols: 0,
499 dtype_code: 0,
500 dtype_bits: 0,
501 }
502 }
503
504 fn from_array2(arr: &crate::storage_dtype::FloatArray2) -> Self {
505 let kind = arr.kind();
506 Self {
507 data: arr.data_ptr() as *const std::ffi::c_void,
508 n: arr.nrows(),
509 cols: arr.ncols() as u32,
510 dtype_code: kind.dlpack_code(),
511 dtype_bits: kind.dlpack_bits(),
512 }
513 }
514
515 fn from_array1(arr: &crate::storage_dtype::FloatArray1) -> Self {
516 let kind = arr.kind();
517 Self {
518 data: arr.data_ptr() as *const std::ffi::c_void,
519 n: arr.len(),
520 cols: 1,
521 dtype_code: kind.dlpack_code(),
522 dtype_bits: kind.dlpack_bits(),
523 }
524 }
525}
526
527#[repr(C)]
528pub struct CFrame {
529 pub atoms: *mut CAtom,
530 pub num_atoms: usize,
531 pub cell: [f64; 3],
532 pub angles: [f64; 3],
533 pub has_velocities: bool,
534 pub has_forces: bool,
535 pub has_energies: bool,
536}
537#[repr(C)]
547pub struct CAtom {
548 pub atomic_number: u64,
549 pub x: f64,
550 pub y: f64,
551 pub z: f64,
552 pub atom_id: u64,
553 pub mass: f64,
554 pub is_fixed: bool,
557 pub fixed_x: bool,
558 pub fixed_y: bool,
559 pub fixed_z: bool,
560 pub vx: f64,
561 pub vy: f64,
562 pub vz: f64,
563 pub has_velocity: bool,
564 pub fx: f64,
565 pub fy: f64,
566 pub fz: f64,
567 pub has_forces: bool,
568 pub energy: f64,
571 pub has_energy: bool,
572}
573#[repr(C)]
574pub struct CConFrameIterator {
575 iterator: *mut ConFrameIterator<'static>,
576 file_contents: *mut String,
577}
578
579fn c_iterator_from_owned_string(contents: String) -> *mut CConFrameIterator {
581 let file_contents_box = Box::new(contents);
582 let file_contents_ptr = Box::into_raw(file_contents_box);
583 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
584 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
585 let c_iterator = Box::new(CConFrameIterator {
586 iterator: Box::into_raw(iterator),
587 file_contents: file_contents_ptr,
588 });
589 Box::into_raw(c_iterator)
590}
591
592#[unsafe(no_mangle)]
608pub unsafe extern "C" fn read_con_file_iterator(
609 filename_c: *const c_char,
610) -> *mut CConFrameIterator {
611 if filename_c.is_null() {
612 return ptr::null_mut();
613 }
614 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
615 Ok(s) => s,
616 Err(_) => return ptr::null_mut(),
617 };
618 let owned = match crate::compression::read_file_contents(Path::new(filename)) {
619 Ok(fc) => match fc.as_str() {
620 Ok(s) => s.to_owned(),
621 Err(_) => return ptr::null_mut(),
622 },
623 Err(_) => return ptr::null_mut(),
624 };
625 c_iterator_from_owned_string(owned)
626}
627
628#[unsafe(no_mangle)]
636pub unsafe extern "C" fn read_con_string_iterator(
637 contents_c: *const c_char,
638) -> *mut CConFrameIterator {
639 if contents_c.is_null() {
640 return ptr::null_mut();
641 }
642 let contents = match unsafe { CStr::from_ptr(contents_c).to_str() } {
643 Ok(s) => s.to_owned(),
644 Err(_) => return ptr::null_mut(),
645 };
646 c_iterator_from_owned_string(contents)
647}
648
649#[unsafe(no_mangle)]
656pub unsafe extern "C" fn read_con_buffer_iterator(
657 data: *const u8,
658 len: usize,
659) -> *mut CConFrameIterator {
660 if data.is_null() && len > 0 {
661 return ptr::null_mut();
662 }
663 if len == 0 {
664 return c_iterator_from_owned_string(String::new());
665 }
666 let slice = unsafe { std::slice::from_raw_parts(data, len) };
667 let contents = match std::str::from_utf8(slice) {
668 Ok(s) => s.to_owned(),
669 Err(_) => return ptr::null_mut(),
670 };
671 c_iterator_from_owned_string(contents)
672}
673#[unsafe(no_mangle)]
679pub unsafe extern "C" fn con_frame_iterator_next(
680 iterator: *mut CConFrameIterator,
681) -> *mut RKRConFrame {
682 if iterator.is_null() {
683 return ptr::null_mut();
684 }
685 let iter = unsafe { &mut *(*iterator).iterator };
686 match iter.next() {
687 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
688 _ => ptr::null_mut(),
689 }
690}
691
692#[unsafe(no_mangle)]
695pub unsafe extern "C" fn con_frame_iterator_forward(iterator: *mut CConFrameIterator) -> RKRStatus {
696 if iterator.is_null() {
697 return RKRStatus::RKR_STATUS_NULL_POINTER;
698 }
699 let iter = unsafe { &mut *(*iterator).iterator };
700 match iter.forward() {
701 Some(Ok(())) => RKRStatus::RKR_STATUS_SUCCESS,
702 Some(Err(_)) => RKRStatus::RKR_STATUS_IO_ERROR,
703 None => RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
704 }
705}
706
707#[unsafe(no_mangle)]
710pub unsafe extern "C" fn con_frame_iterator_skip(
711 iterator: *mut CConFrameIterator,
712 n: usize,
713) -> usize {
714 if iterator.is_null() {
715 return usize::MAX;
716 }
717 let iter = unsafe { &mut *(*iterator).iterator };
718 iter.skip_frames(n).unwrap_or(usize::MAX)
719}
720
721#[unsafe(no_mangle)]
723pub unsafe extern "C" fn con_frame_iterator_nth(
724 iterator: *mut CConFrameIterator,
725 index: usize,
726) -> *mut RKRConFrame {
727 if iterator.is_null() {
728 return ptr::null_mut();
729 }
730 let iter = unsafe { &mut *(*iterator).iterator };
731 match iter.skip_frames(index) {
732 Ok(skipped) if skipped == index => {}
733 _ => return ptr::null_mut(),
734 }
735 match iter.next() {
736 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
737 _ => ptr::null_mut(),
738 }
739}
740
741#[unsafe(no_mangle)]
743pub unsafe extern "C" fn rkr_count_frames(filename_c: *const c_char) -> usize {
744 if filename_c.is_null() {
745 return usize::MAX;
746 }
747 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
748 Ok(s) => s,
749 Err(_) => return usize::MAX,
750 };
751 crate::iterators::count_frames(Path::new(filename)).unwrap_or(usize::MAX)
752}
753
754#[unsafe(no_mangle)]
756pub unsafe extern "C" fn rkr_read_nth_frame(
757 filename_c: *const c_char,
758 index: usize,
759) -> *mut RKRConFrame {
760 if filename_c.is_null() {
761 return ptr::null_mut();
762 }
763 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
764 Ok(s) => s,
765 Err(_) => return ptr::null_mut(),
766 };
767 match crate::iterators::read_nth_frame(Path::new(filename), index) {
768 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
769 Err(_) => ptr::null_mut(),
770 }
771}
772#[unsafe(no_mangle)]
777pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
778 if !frame_handle.is_null() {
779 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
780 }
781}
782#[unsafe(no_mangle)]
787pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
788 if iterator.is_null() {
789 return;
790 }
791 unsafe {
792 let c_iterator_box = Box::from_raw(iterator);
793 let _ = Box::from_raw(c_iterator_box.iterator);
794 let _ = Box::from_raw(c_iterator_box.file_contents);
795 }
796}
797#[unsafe(no_mangle)]
806pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
807 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
808 Some(f) => f,
809 None => return ptr::null_mut(),
810 };
811 let masses_iter = frame
812 .header
813 .natms_per_type
814 .iter()
815 .zip(frame.header.masses_per_type.iter())
816 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
817 let has_velocities = frame.has_velocities();
818 let mut c_atoms: Vec<CAtom> = frame
819 .atom_data
820 .iter()
821 .zip(masses_iter)
822 .map(|(atom_datum, mass)| {
823 let [vx, vy, vz] = atom_datum.velocity.unwrap_or([0.0; 3]);
824 let [fx, fy, fz] = atom_datum.force.unwrap_or([0.0; 3]);
825 CAtom {
826 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
827 x: atom_datum.x,
828 y: atom_datum.y,
829 z: atom_datum.z,
830 is_fixed: atom_datum.is_fixed(),
831 fixed_x: atom_datum.fixed[0],
832 fixed_y: atom_datum.fixed[1],
833 fixed_z: atom_datum.fixed[2],
834 atom_id: atom_datum.atom_id,
835 mass,
836 vx,
837 vy,
838 vz,
839 has_velocity: atom_datum.has_velocity(),
840 fx,
841 fy,
842 fz,
843 has_forces: atom_datum.has_forces(),
844 energy: atom_datum.energy.unwrap_or(0.0),
845 has_energy: atom_datum.has_energy(),
846 }
847 })
848 .collect();
849 let atoms_ptr = c_atoms.as_mut_ptr();
850 let num_atoms = c_atoms.len();
851 std::mem::forget(c_atoms);
852 let has_forces = frame.has_forces();
853 let has_energies = frame.has_energies();
854 let c_frame = Box::new(CFrame {
855 atoms: atoms_ptr,
856 num_atoms,
857 cell: frame.header.boxl,
858 angles: frame.header.angles,
859 has_velocities,
860 has_forces,
861 has_energies,
862 });
863 Box::into_raw(c_frame)
864}
865#[unsafe(no_mangle)]
870pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
871 if frame.is_null() {
872 return;
873 }
874 unsafe {
875 let frame_box = Box::from_raw(frame);
876 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
877 }
878}
879#[unsafe(no_mangle)]
898pub unsafe extern "C" fn rkr_frame_get_header_line(
899 frame_handle: *const RKRConFrame,
900 is_prebox: bool,
901 line_index: usize,
902 buffer: *mut c_char,
903 buffer_len: usize,
904) -> RKRStatus {
905 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
906 Some(f) => f,
907 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
908 };
909 if buffer.is_null() {
910 return RKRStatus::RKR_STATUS_NULL_POINTER;
911 }
912 if buffer_len == 0 {
913 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
914 }
915 let line_to_copy: Option<&str> = if is_prebox {
916 match line_index {
917 0 => Some(frame.header.prebox_header.user.as_str()),
918 1 => Some(frame.header.prebox_header.metadata_line()),
919 _ => None,
920 }
921 } else {
922 frame
923 .header
924 .postbox_header
925 .get(line_index)
926 .map(String::as_str)
927 };
928 if let Some(line) = line_to_copy {
929 let bytes = line.as_bytes();
930 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
931 unsafe {
932 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
933 *buffer.add(len_to_copy) = 0;
934 }
935 RKRStatus::RKR_STATUS_SUCCESS
936 } else {
937 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
938 }
939}
940#[unsafe(no_mangle)]
953pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
954 frame_handle: *const RKRConFrame,
955 is_prebox: bool,
956 line_index: usize,
957) -> *mut c_char {
958 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
959 Some(f) => f,
960 None => return ptr::null_mut(),
961 };
962 let line_to_copy: Option<&str> = if is_prebox {
963 match line_index {
964 0 => Some(frame.header.prebox_header.user.as_str()),
965 1 => Some(frame.header.prebox_header.metadata_line()),
966 _ => None,
967 }
968 } else {
969 frame
970 .header
971 .postbox_header
972 .get(line_index)
973 .map(String::as_str)
974 };
975 if let Some(line) = line_to_copy {
976 match CString::new(line) {
978 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
981 } else {
982 ptr::null_mut() }
984}
985#[unsafe(no_mangle)]
993pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
994 if !s.is_null() {
995 let _ = unsafe { CString::from_raw(s) };
997 }
998}
999type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
1013#[inline]
1016fn into_rkr_writer(sink: Box<dyn std::io::Write>, precision: Option<u8>) -> *mut RKRConFrameWriter {
1017 let writer: RkrWriter = match precision {
1018 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
1019 None => ConFrameWriter::new(sink),
1020 };
1021 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1022}
1023#[inline]
1025unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
1026 if filename_c.is_null() {
1027 return None;
1028 }
1029 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
1030}
1031#[unsafe(no_mangle)]
1037pub unsafe extern "C" fn create_writer_from_path_c(
1038 filename_c: *const c_char,
1039) -> *mut RKRConFrameWriter {
1040 let filename = match unsafe { cstr_path(filename_c) } {
1041 Some(s) => s,
1042 None => return ptr::null_mut(),
1043 };
1044 match File::create(filename) {
1045 Ok(file) => into_rkr_writer(Box::new(file), None),
1046 Err(_) => ptr::null_mut(),
1047 }
1048}
1049#[unsafe(no_mangle)]
1054pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
1055 if !writer_handle.is_null() {
1056 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
1057 }
1058}
1059#[unsafe(no_mangle)]
1065pub unsafe extern "C" fn rkr_writer_extend(
1066 writer_handle: *mut RKRConFrameWriter,
1067 frame_handles: *const *const RKRConFrame,
1068 num_frames: usize,
1069) -> RKRStatus {
1070 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1071 Some(w) => w,
1072 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1073 };
1074 if frame_handles.is_null() {
1075 return RKRStatus::RKR_STATUS_NULL_POINTER;
1076 }
1077 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
1078 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
1079 if handles_slice.iter().any(|&handle| handle.is_null()) {
1080 return RKRStatus::RKR_STATUS_NULL_POINTER;
1083 }
1084 for &handle in handles_slice.iter() {
1085 match unsafe { (handle as *const ConFrame).as_ref() } {
1087 Some(frame) => rust_frames.push(frame),
1088 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1090 }
1091 }
1092 match writer.extend(rust_frames.into_iter()) {
1093 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1094 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
1095 }
1096}
1097
1098#[unsafe(no_mangle)]
1104pub unsafe extern "C" fn rkr_writer_set_canonical(
1105 writer_handle: *mut RKRConFrameWriter,
1106 canonical: u8,
1107) -> RKRStatus {
1108 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1109 Some(w) => w,
1110 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1111 };
1112 writer.set_canonical(canonical != 0);
1113 RKRStatus::RKR_STATUS_SUCCESS
1114}
1115
1116#[unsafe(no_mangle)]
1118pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
1119 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
1120 Some(w) => u8::from(w.is_canonical()),
1121 None => 0,
1122 }
1123}
1124
1125#[cfg(test)]
1126mod index_proj_ffi_tests {
1127 use super::*;
1128 use std::ffi::CStr;
1129 use std::fs;
1130 use std::path::PathBuf;
1131
1132 fn fixture_path() -> PathBuf {
1133 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
1134 }
1135
1136 #[test]
1137 fn ffi_projection_matches_index_proj() {
1138 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1139 let fr = &frames[0];
1140 let handle = fr as *const ConFrame as *const RKRConFrame;
1141 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
1142 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
1144 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
1145 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
1146 assert!(!formula_c.is_null());
1147 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
1148 assert_eq!(formula, proj.formula);
1149 unsafe { rkr_free_string(formula_c) };
1150 let ie = rkr_frame_index_energy(handle);
1151 match proj.energy {
1152 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
1153 None => assert!(ie.is_nan()),
1154 }
1155 let tm = rkr_frame_total_mass(handle);
1156 match proj.total_mass {
1157 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
1158 None => assert!(tm.is_nan()),
1159 }
1160 let cv = rkr_frame_cell_volume(handle);
1161 match proj.cell_volume {
1162 Some(v) => assert!(
1163 (cv - v).abs() < 1e-6 * v.max(1.0),
1164 "cell_volume C={cv} proj={v}"
1165 ),
1166 None => assert!(cv.is_nan()),
1167 }
1168 let fm = rkr_frame_fmax(handle);
1169 match proj.fmax {
1170 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
1171 None => assert!(fm.is_nan()),
1172 }
1173 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
1174 assert!(!json_c.is_null());
1175 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
1176 assert!(json.contains("\"formula\""));
1177 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
1178 assert!(json.contains("\"n_atoms\""));
1179 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
1180 unsafe { rkr_free_string(json_c) };
1181 }
1182
1183 #[test]
1184 fn ffi_canonical_writer_byte_identical() {
1185 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1186 let fr = &frames[0];
1187 let dir = tempfile::tempdir().unwrap();
1188 let p1 = dir.path().join("a.con");
1189 let p2 = dir.path().join("b.con");
1190 for p in [&p1, &p2] {
1191 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
1192 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
1193 assert!(!w.is_null());
1194 assert_eq!(
1195 unsafe { rkr_writer_set_canonical(w, 1) },
1196 RKRStatus::RKR_STATUS_SUCCESS
1197 );
1198 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
1199 let handles = [fr as *const ConFrame as *const RKRConFrame];
1200 assert_eq!(
1201 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
1202 RKRStatus::RKR_STATUS_SUCCESS
1203 );
1204 unsafe { free_rkr_writer(w) };
1205 }
1206 let b1 = fs::read(&p1).unwrap();
1207 let b2 = fs::read(&p2).unwrap();
1208 assert_eq!(b1, b2);
1209 assert!(!b1.is_empty());
1210 }
1211}
1212#[unsafe(no_mangle)]
1221pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
1222 filename_c: *const c_char,
1223 precision: u8,
1224) -> *mut RKRConFrameWriter {
1225 let filename = match unsafe { cstr_path(filename_c) } {
1226 Some(s) => s,
1227 None => return ptr::null_mut(),
1228 };
1229 match File::create(filename) {
1230 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1231 Err(_) => ptr::null_mut(),
1232 }
1233}
1234pub struct RKRConFrameBuilder {
1241 _private: [u8; 0],
1242}
1243#[allow(clippy::too_many_arguments)]
1244unsafe fn add_builder_atom(
1245 builder_handle: *mut RKRConFrameBuilder,
1246 symbol: *const c_char,
1247 x: f64,
1248 y: f64,
1249 z: f64,
1250 fixed: [bool; 3],
1251 atom_id: u64,
1252 mass: f64,
1253 velocity: Option<[f64; 3]>,
1254 forces: Option<[f64; 3]>,
1255) -> RKRStatus {
1256 if builder_handle.is_null() || symbol.is_null() {
1257 return RKRStatus::RKR_STATUS_NULL_POINTER;
1258 }
1259 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1260 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
1261 Ok(s) => s,
1262 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1263 };
1264 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
1265 if let Some(v) = velocity {
1266 builder.with_velocity(v);
1267 }
1268 if let Some(f) = forces {
1269 builder.with_force(f);
1270 }
1271 RKRStatus::RKR_STATUS_SUCCESS
1272}
1273#[unsafe(no_mangle)]
1279pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
1280 builder_handle: *mut RKRConFrameBuilder,
1281 velocity: *const f64,
1282) -> RKRStatus {
1283 if builder_handle.is_null() || velocity.is_null() {
1284 return RKRStatus::RKR_STATUS_NULL_POINTER;
1285 }
1286 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1287 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1288 builder.with_velocity(v);
1289 RKRStatus::RKR_STATUS_SUCCESS
1290}
1291#[unsafe(no_mangle)]
1297pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
1298 builder_handle: *mut RKRConFrameBuilder,
1299 force: *const f64,
1300) -> RKRStatus {
1301 if builder_handle.is_null() || force.is_null() {
1302 return RKRStatus::RKR_STATUS_NULL_POINTER;
1303 }
1304 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1305 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1306 builder.with_force(f);
1307 RKRStatus::RKR_STATUS_SUCCESS
1308}
1309#[unsafe(no_mangle)]
1316pub unsafe extern "C" fn rkr_frame_builder_set_last_displacement(
1317 builder_handle: *mut RKRConFrameBuilder,
1318 displacement: *const f64,
1319) -> RKRStatus {
1320 if builder_handle.is_null() || displacement.is_null() {
1321 return RKRStatus::RKR_STATUS_NULL_POINTER;
1322 }
1323 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1324 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1325 builder.with_displacement(d);
1326 RKRStatus::RKR_STATUS_SUCCESS
1327}
1328#[unsafe(no_mangle)]
1338pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1339 builder_handle: *mut RKRConFrameBuilder,
1340 energy: f64,
1341) -> RKRStatus {
1342 if builder_handle.is_null() {
1343 return RKRStatus::RKR_STATUS_NULL_POINTER;
1344 }
1345 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1346 builder.with_energy(energy);
1347 RKRStatus::RKR_STATUS_SUCCESS
1348}
1349fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1363 use crate::error::ParseError;
1364 match e {
1365 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1366 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1367 }
1368 ParseError::MassMismatch { .. } | ParseError::ValidationError(_) => {
1369 RKRStatus::RKR_STATUS_VALIDATION_ERROR
1370 }
1371 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1372 }
1373}
1374#[unsafe(no_mangle)]
1381pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1382 builder_handle: *const RKRConFrameBuilder,
1383) -> usize {
1384 if builder_handle.is_null() {
1385 return 0;
1386 }
1387 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1388 builder.atom_count()
1389}
1390#[unsafe(no_mangle)]
1394pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1395 builder_handle: *mut RKRConFrameBuilder,
1396 index: usize,
1397 x: f64,
1398 y: f64,
1399 z: f64,
1400) -> RKRStatus {
1401 if builder_handle.is_null() {
1402 return RKRStatus::RKR_STATUS_NULL_POINTER;
1403 }
1404 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1405 match builder.set_atom_position(index, x, y, z) {
1406 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1407 Err(e) => map_builder_err(e),
1408 }
1409}
1410#[unsafe(no_mangle)]
1414pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1415 builder_handle: *mut RKRConFrameBuilder,
1416 index: usize,
1417 velocity: *const f64,
1418) -> RKRStatus {
1419 if builder_handle.is_null() || velocity.is_null() {
1420 return RKRStatus::RKR_STATUS_NULL_POINTER;
1421 }
1422 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1423 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1424 match builder.set_atom_velocity(index, v) {
1425 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1426 Err(e) => map_builder_err(e),
1427 }
1428}
1429#[unsafe(no_mangle)]
1433pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1434 builder_handle: *mut RKRConFrameBuilder,
1435 index: usize,
1436 force: *const f64,
1437) -> RKRStatus {
1438 if builder_handle.is_null() || force.is_null() {
1439 return RKRStatus::RKR_STATUS_NULL_POINTER;
1440 }
1441 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1442 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1443 match builder.set_atom_force(index, f) {
1444 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1445 Err(e) => map_builder_err(e),
1446 }
1447}
1448#[unsafe(no_mangle)]
1453pub unsafe extern "C" fn rkr_frame_builder_set_atom_displacement(
1454 builder_handle: *mut RKRConFrameBuilder,
1455 index: usize,
1456 displacement: *const f64,
1457) -> RKRStatus {
1458 if builder_handle.is_null() || displacement.is_null() {
1459 return RKRStatus::RKR_STATUS_NULL_POINTER;
1460 }
1461 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1462 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
1463 match builder.set_atom_displacement(index, d) {
1464 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1465 Err(e) => map_builder_err(e),
1466 }
1467}
1468#[unsafe(no_mangle)]
1472pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1473 builder_handle: *mut RKRConFrameBuilder,
1474 index: usize,
1475 energy: f64,
1476) -> RKRStatus {
1477 if builder_handle.is_null() {
1478 return RKRStatus::RKR_STATUS_NULL_POINTER;
1479 }
1480 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1481 match builder.set_atom_energy(index, energy) {
1482 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1483 Err(e) => map_builder_err(e),
1484 }
1485}
1486#[unsafe(no_mangle)]
1490pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1491 builder_handle: *mut RKRConFrameBuilder,
1492 index: usize,
1493 fixed_x: bool,
1494 fixed_y: bool,
1495 fixed_z: bool,
1496) -> RKRStatus {
1497 if builder_handle.is_null() {
1498 return RKRStatus::RKR_STATUS_NULL_POINTER;
1499 }
1500 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1501 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1502 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1503 Err(e) => map_builder_err(e),
1504 }
1505}
1506#[unsafe(no_mangle)]
1510pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1511 builder_handle: *mut RKRConFrameBuilder,
1512 index: usize,
1513 mass: f64,
1514) -> RKRStatus {
1515 if builder_handle.is_null() {
1516 return RKRStatus::RKR_STATUS_NULL_POINTER;
1517 }
1518 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1519 match builder.set_atom_mass(index, mass) {
1520 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1521 Err(e) => map_builder_err(e),
1522 }
1523}
1524#[unsafe(no_mangle)]
1531pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1532 builder_handle: *mut RKRConFrameBuilder,
1533 index: usize,
1534 atom_id: u64,
1535) -> RKRStatus {
1536 if builder_handle.is_null() {
1537 return RKRStatus::RKR_STATUS_NULL_POINTER;
1538 }
1539 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1540 match builder.set_atom_id(index, atom_id) {
1541 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1542 Err(e) => map_builder_err(e),
1543 }
1544}
1545#[unsafe(no_mangle)]
1549pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1550 builder_handle: *mut RKRConFrameBuilder,
1551 index: usize,
1552) -> RKRStatus {
1553 if builder_handle.is_null() {
1554 return RKRStatus::RKR_STATUS_NULL_POINTER;
1555 }
1556 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1557 match builder.clear_atom_velocity(index) {
1558 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1559 Err(e) => map_builder_err(e),
1560 }
1561}
1562#[unsafe(no_mangle)]
1565pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1566 builder_handle: *mut RKRConFrameBuilder,
1567 index: usize,
1568) -> RKRStatus {
1569 if builder_handle.is_null() {
1570 return RKRStatus::RKR_STATUS_NULL_POINTER;
1571 }
1572 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1573 match builder.clear_atom_force(index) {
1574 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1575 Err(e) => map_builder_err(e),
1576 }
1577}
1578#[unsafe(no_mangle)]
1581pub unsafe extern "C" fn rkr_frame_builder_clear_atom_displacement(
1582 builder_handle: *mut RKRConFrameBuilder,
1583 index: usize,
1584) -> RKRStatus {
1585 if builder_handle.is_null() {
1586 return RKRStatus::RKR_STATUS_NULL_POINTER;
1587 }
1588 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1589 match builder.clear_atom_displacement(index) {
1590 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1591 Err(e) => map_builder_err(e),
1592 }
1593}
1594#[unsafe(no_mangle)]
1597pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1598 builder_handle: *mut RKRConFrameBuilder,
1599 index: usize,
1600) -> RKRStatus {
1601 if builder_handle.is_null() {
1602 return RKRStatus::RKR_STATUS_NULL_POINTER;
1603 }
1604 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1605 match builder.clear_atom_energy(index) {
1606 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1607 Err(e) => map_builder_err(e),
1608 }
1609}
1610#[unsafe(no_mangle)]
1615pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1616 builder_handle: *mut RKRConFrameBuilder,
1617 positions: *const f64,
1618 len: usize,
1619) -> RKRStatus {
1620 if builder_handle.is_null() || positions.is_null() {
1621 return RKRStatus::RKR_STATUS_NULL_POINTER;
1622 }
1623 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1624 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1625 match builder.set_positions_from_flat(slice) {
1626 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1627 Err(e) => map_builder_err(e),
1628 }
1629}
1630#[unsafe(no_mangle)]
1634pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1635 builder_handle: *mut RKRConFrameBuilder,
1636 forces: *const f64,
1637 len: usize,
1638) -> RKRStatus {
1639 if builder_handle.is_null() || forces.is_null() {
1640 return RKRStatus::RKR_STATUS_NULL_POINTER;
1641 }
1642 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1643 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1644 match builder.set_forces_from_flat(slice) {
1645 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1646 Err(e) => map_builder_err(e),
1647 }
1648}
1649#[unsafe(no_mangle)]
1654pub unsafe extern "C" fn rkr_frame_builder_set_displacements_from_flat(
1655 builder_handle: *mut RKRConFrameBuilder,
1656 displacements: *const f64,
1657 len: usize,
1658) -> RKRStatus {
1659 if builder_handle.is_null() || displacements.is_null() {
1660 return RKRStatus::RKR_STATUS_NULL_POINTER;
1661 }
1662 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1663 let slice = unsafe { std::slice::from_raw_parts(displacements, len) };
1664 match builder.set_displacements_from_flat(slice) {
1665 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1666 Err(e) => map_builder_err(e),
1667 }
1668}
1669#[unsafe(no_mangle)]
1673pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1674 builder_handle: *mut RKRConFrameBuilder,
1675 energies: *const f64,
1676 len: usize,
1677) -> RKRStatus {
1678 if builder_handle.is_null() || energies.is_null() {
1679 return RKRStatus::RKR_STATUS_NULL_POINTER;
1680 }
1681 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1682 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1683 match builder.set_atom_energies_from_flat(slice) {
1684 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1685 Err(e) => map_builder_err(e),
1686 }
1687}
1688#[unsafe(no_mangle)]
1692pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1693 builder_handle: *const RKRConFrameBuilder,
1694 index: usize,
1695 out_xyz: *mut f64,
1696) -> RKRStatus {
1697 if builder_handle.is_null() || out_xyz.is_null() {
1698 return RKRStatus::RKR_STATUS_NULL_POINTER;
1699 }
1700 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1701 match builder.get_atom_position(index) {
1702 Ok((x, y, z)) => unsafe {
1703 *out_xyz = x;
1704 *out_xyz.add(1) = y;
1705 *out_xyz.add(2) = z;
1706 RKRStatus::RKR_STATUS_SUCCESS
1707 },
1708 Err(e) => map_builder_err(e),
1709 }
1710}
1711#[unsafe(no_mangle)]
1718pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1719 builder_handle: *const RKRConFrameBuilder,
1720 index: usize,
1721 out_xyz: *mut f64,
1722 has_value: *mut bool,
1723) -> RKRStatus {
1724 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1725 return RKRStatus::RKR_STATUS_NULL_POINTER;
1726 }
1727 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1728 match builder.get_atom_velocity(index) {
1729 Ok(Some(v)) => unsafe {
1730 *out_xyz = v[0];
1731 *out_xyz.add(1) = v[1];
1732 *out_xyz.add(2) = v[2];
1733 *has_value = true;
1734 RKRStatus::RKR_STATUS_SUCCESS
1735 },
1736 Ok(None) => unsafe {
1737 *has_value = false;
1738 RKRStatus::RKR_STATUS_SUCCESS
1739 },
1740 Err(e) => map_builder_err(e),
1741 }
1742}
1743#[unsafe(no_mangle)]
1746pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1747 builder_handle: *const RKRConFrameBuilder,
1748 index: usize,
1749 out_xyz: *mut f64,
1750 has_value: *mut bool,
1751) -> RKRStatus {
1752 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1753 return RKRStatus::RKR_STATUS_NULL_POINTER;
1754 }
1755 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1756 match builder.get_atom_force(index) {
1757 Ok(Some(f)) => unsafe {
1758 *out_xyz = f[0];
1759 *out_xyz.add(1) = f[1];
1760 *out_xyz.add(2) = f[2];
1761 *has_value = true;
1762 RKRStatus::RKR_STATUS_SUCCESS
1763 },
1764 Ok(None) => unsafe {
1765 *has_value = false;
1766 RKRStatus::RKR_STATUS_SUCCESS
1767 },
1768 Err(e) => map_builder_err(e),
1769 }
1770}
1771#[unsafe(no_mangle)]
1777pub unsafe extern "C" fn rkr_frame_builder_get_atom_displacement(
1778 builder_handle: *const RKRConFrameBuilder,
1779 index: usize,
1780 out_xyz: *mut f64,
1781 has_value: *mut bool,
1782) -> RKRStatus {
1783 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1784 return RKRStatus::RKR_STATUS_NULL_POINTER;
1785 }
1786 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1787 match builder.get_atom_displacement(index) {
1788 Ok(Some(d)) => unsafe {
1789 *out_xyz = d[0];
1790 *out_xyz.add(1) = d[1];
1791 *out_xyz.add(2) = d[2];
1792 *has_value = true;
1793 RKRStatus::RKR_STATUS_SUCCESS
1794 },
1795 Ok(None) => unsafe {
1796 *has_value = false;
1797 RKRStatus::RKR_STATUS_SUCCESS
1798 },
1799 Err(e) => map_builder_err(e),
1800 }
1801}
1802#[unsafe(no_mangle)]
1808pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
1809 builder_handle: *const RKRConFrameBuilder,
1810 index: usize,
1811 out_value: *mut f64,
1812 has_value: *mut bool,
1813) -> RKRStatus {
1814 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
1815 return RKRStatus::RKR_STATUS_NULL_POINTER;
1816 }
1817 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1818 match builder.get_atom_energy(index) {
1819 Ok(Some(e)) => unsafe {
1820 *out_value = e;
1821 *has_value = true;
1822 RKRStatus::RKR_STATUS_SUCCESS
1823 },
1824 Ok(None) => unsafe {
1825 *has_value = false;
1826 RKRStatus::RKR_STATUS_SUCCESS
1827 },
1828 Err(e) => map_builder_err(e),
1829 }
1830}
1831#[unsafe(no_mangle)]
1835pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
1836 builder_handle: *const RKRConFrameBuilder,
1837 index: usize,
1838 out_mass: *mut f64,
1839) -> RKRStatus {
1840 if builder_handle.is_null() || out_mass.is_null() {
1841 return RKRStatus::RKR_STATUS_NULL_POINTER;
1842 }
1843 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1844 match builder.get_atom_mass(index) {
1845 Ok(m) => unsafe {
1846 *out_mass = m;
1847 RKRStatus::RKR_STATUS_SUCCESS
1848 },
1849 Err(e) => map_builder_err(e),
1850 }
1851}
1852pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
1875fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
1876 use crate::error::ParseError;
1877 match e {
1878 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
1879 RKRStatus::RKR_STATUS_DEVICE_MISMATCH
1880 }
1881 ParseError::ValidationError(ref msg)
1882 if msg.contains("no device allocator") || msg.contains("allocator") =>
1883 {
1884 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED
1885 }
1886 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1887 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1888 }
1889}
1890pub mod rkr_dl_type_code {
1894 pub const RKR_DL_INT: u8 = 0;
1895 pub const RKR_DL_UINT: u8 = 1;
1896 pub const RKR_DL_FLOAT: u8 = 2;
1897 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
1898 pub const RKR_DL_BFLOAT: u8 = 4;
1899 pub const RKR_DL_COMPLEX: u8 = 5;
1900 pub const RKR_DL_BOOL: u8 = 6;
1901}
1902
1903pub mod rkr_dl_device_type {
1905 pub const RKR_DL_CPU: i32 = 1;
1906 pub const RKR_DL_CUDA: i32 = 2;
1907 pub const RKR_DL_CUDA_HOST: i32 = 3;
1908}
1909
1910#[repr(C)]
1914#[derive(Clone, Copy, Debug)]
1915pub struct RKRDLDataType {
1916 pub code: u8,
1918 pub bits: u8,
1920 pub lanes: u16,
1922}
1923
1924#[repr(C)]
1927#[derive(Clone, Copy, Debug)]
1928pub struct RKRDLDevice {
1929 pub device_type: i32,
1931 pub device_id: i32,
1932}
1933
1934#[repr(C)]
1948#[derive(Clone, Copy, Debug)]
1949pub struct RKRDlpackExportOptions {
1950 pub dtype: RKRDLDataType,
1952 pub device: RKRDLDevice,
1954}
1955
1956impl Default for RKRDlpackExportOptions {
1957 fn default() -> Self {
1958 Self {
1959 dtype: RKRDLDataType {
1960 code: rkr_dl_type_code::RKR_DL_FLOAT,
1961 bits: 64,
1962 lanes: 1,
1963 },
1964 device: RKRDLDevice {
1965 device_type: rkr_dl_device_type::RKR_DL_CPU,
1966 device_id: 0,
1967 },
1968 }
1969 }
1970}
1971
1972fn resolve_dlpack_opts(
1975 opts: *const RKRDlpackExportOptions,
1976) -> Result<RKRDlpackExportOptions, RKRStatus> {
1977 let o = if opts.is_null() {
1978 RKRDlpackExportOptions::default()
1979 } else {
1980 unsafe { *opts }
1981 };
1982 let dt = o.device.device_type;
1983 if dt == rkr_dl_device_type::RKR_DL_CPU {
1984 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
1986 #[cfg(not(feature = "cuda"))]
1987 {
1988 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1989 }
1990 #[cfg(feature = "cuda")]
1991 {
1992 }
1994 } else {
1995 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1996 }
1997 if o.dtype.lanes != 1 {
1998 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
1999 }
2000 Ok(o)
2001}
2002
2003fn finish_dlpack_tensor<E: std::fmt::Display>(
2004 result: Result<dlpk::DLPackTensor, E>,
2005 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2006) -> RKRStatus {
2007 match result {
2008 Ok(tensor) => {
2009 let raw = tensor.into_raw();
2010 unsafe {
2011 *out_tensor = raw.as_ptr();
2012 }
2013 RKRStatus::RKR_STATUS_SUCCESS
2014 }
2015 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
2016 "DLPack export failed: {e}"
2017 ))),
2018 }
2019}
2020
2021fn export_f64_slice_as_dlpack(
2024 data: &[f64],
2025 shape: &[usize],
2026 dtype: RKRDLDataType,
2027 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2028) -> RKRStatus {
2029 use rkr_dl_type_code::*;
2030 if dtype.lanes != 1 {
2031 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2032 }
2033 macro_rules! arc_export {
2034 ($ty:ty, $map:expr) => {{
2035 let v: Vec<$ty> = data.iter().map($map).collect();
2036 match *shape {
2037 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
2038 Ok(a) => dlpk::DLPackTensor::try_from(a),
2039 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2040 },
2041 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
2042 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2043 }
2044 }};
2045 }
2046 let tensor = match (dtype.code, dtype.bits) {
2047 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
2048 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
2049 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
2050 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
2051 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
2052 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
2053 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
2054 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
2055 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
2056 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
2057 (RKR_DL_BOOL, 8) => {
2058 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
2060 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
2061 }
2062 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
2064 };
2065 finish_dlpack_tensor(tensor, out_tensor)
2066}
2067
2068fn export_owned_array2_dlpack_opts(
2069 arr: &ndarray::ArcArray2<f64>,
2070 opts: &RKRDlpackExportOptions,
2071 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2072) -> RKRStatus {
2073 let (r, c) = arr.dim();
2074 let flat: Vec<f64> = arr.iter().copied().collect();
2075 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2076 #[cfg(feature = "cuda")]
2077 {
2078 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2080 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2081 }
2082 return finish_dlpack_tensor(
2083 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2084 &[r, c],
2085 &flat,
2086 opts.device.device_id,
2087 ),
2088 out_tensor,
2089 );
2090 }
2091 #[cfg(not(feature = "cuda"))]
2092 {
2093 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2094 }
2095 }
2096 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
2097}
2098
2099fn export_owned_array1_f64_dlpack_opts(
2100 arr: &ndarray::ArcArray1<f64>,
2101 opts: &RKRDlpackExportOptions,
2102 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2103) -> RKRStatus {
2104 let flat = arr.to_vec();
2105 let n = flat.len();
2106 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2107 #[cfg(feature = "cuda")]
2108 {
2109 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2110 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2111 }
2112 return finish_dlpack_tensor(
2113 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2114 &[n],
2115 &flat,
2116 opts.device.device_id,
2117 ),
2118 out_tensor,
2119 );
2120 }
2121 #[cfg(not(feature = "cuda"))]
2122 {
2123 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2124 }
2125 }
2126 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
2127}
2128
2129fn export_owned_array1_u64_dlpack(
2130 arr: &ndarray::ArcArray1<u64>,
2131 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2132) -> RKRStatus {
2133 finish_dlpack_tensor(dlpk::DLPackTensor::try_from(arr.clone()), out_tensor)
2134}
2135
2136fn export_owned_array1_u64_dlpack_opts(
2138 arr: &ndarray::ArcArray1<u64>,
2139 opts: &RKRDlpackExportOptions,
2140 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2141) -> RKRStatus {
2142 use rkr_dl_type_code::*;
2143 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
2144 return export_owned_array1_u64_dlpack(arr, out_tensor);
2145 }
2146 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
2147 let n = as_f64.len();
2148 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
2149}
2150#[unsafe(no_mangle)]
2161pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
2162 builder_handle: *const RKRConFrameBuilder,
2163 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2164) -> RKRStatus {
2165 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2166}
2167
2168#[unsafe(no_mangle)]
2175pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
2176 builder_handle: *const RKRConFrameBuilder,
2177 opts: *const RKRDlpackExportOptions,
2178 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2179) -> RKRStatus {
2180 if builder_handle.is_null() || out_tensor.is_null() {
2181 return RKRStatus::RKR_STATUS_NULL_POINTER;
2182 }
2183 let o = match resolve_dlpack_opts(opts) {
2184 Ok(o) => o,
2185 Err(st) => return st,
2186 };
2187 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2188 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
2189}
2190
2191#[unsafe(no_mangle)]
2201pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
2202 builder_handle: *const RKRConFrameBuilder,
2203 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2204) -> RKRStatus {
2205 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2206}
2207
2208#[unsafe(no_mangle)]
2209pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
2210 builder_handle: *const RKRConFrameBuilder,
2211 opts: *const RKRDlpackExportOptions,
2212 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2213) -> RKRStatus {
2214 if builder_handle.is_null() || out_tensor.is_null() {
2215 return RKRStatus::RKR_STATUS_NULL_POINTER;
2216 }
2217 let o = match resolve_dlpack_opts(opts) {
2218 Ok(o) => o,
2219 Err(st) => return st,
2220 };
2221 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2222 if !builder.has_velocities_section() {
2223 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2224 }
2225 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
2226}
2227
2228#[unsafe(no_mangle)]
2237pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
2238 builder_handle: *const RKRConFrameBuilder,
2239 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2240) -> RKRStatus {
2241 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2242}
2243
2244#[unsafe(no_mangle)]
2245pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
2246 builder_handle: *const RKRConFrameBuilder,
2247 opts: *const RKRDlpackExportOptions,
2248 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2249) -> RKRStatus {
2250 if builder_handle.is_null() || out_tensor.is_null() {
2251 return RKRStatus::RKR_STATUS_NULL_POINTER;
2252 }
2253 let o = match resolve_dlpack_opts(opts) {
2254 Ok(o) => o,
2255 Err(st) => return st,
2256 };
2257 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2258 if !builder.has_forces_section() {
2259 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2260 }
2261 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
2262}
2263
2264#[unsafe(no_mangle)]
2273pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack(
2274 builder_handle: *const RKRConFrameBuilder,
2275 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2276) -> RKRStatus {
2277 unsafe {
2278 rkr_frame_builder_displacements_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2279 }
2280}
2281
2282#[unsafe(no_mangle)]
2289pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack_ex(
2290 builder_handle: *const RKRConFrameBuilder,
2291 opts: *const RKRDlpackExportOptions,
2292 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2293) -> RKRStatus {
2294 if builder_handle.is_null() || out_tensor.is_null() {
2295 return RKRStatus::RKR_STATUS_NULL_POINTER;
2296 }
2297 let o = match resolve_dlpack_opts(opts) {
2298 Ok(o) => o,
2299 Err(st) => return st,
2300 };
2301 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2302 if !builder.has_displacements_section() {
2303 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2304 }
2305 export_owned_array2_dlpack_opts(builder.displacements_2d_ref(), &o, out_tensor)
2306}
2307
2308#[unsafe(no_mangle)]
2317pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
2318 builder_handle: *const RKRConFrameBuilder,
2319 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2320) -> RKRStatus {
2321 unsafe {
2322 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2323 }
2324}
2325
2326#[unsafe(no_mangle)]
2327pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
2328 builder_handle: *const RKRConFrameBuilder,
2329 opts: *const RKRDlpackExportOptions,
2330 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2331) -> RKRStatus {
2332 if builder_handle.is_null() || out_tensor.is_null() {
2333 return RKRStatus::RKR_STATUS_NULL_POINTER;
2334 }
2335 let o = match resolve_dlpack_opts(opts) {
2336 Ok(o) => o,
2337 Err(st) => return st,
2338 };
2339 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2340 if !builder.has_energies_section() {
2341 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2342 }
2343 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
2344}
2345
2346#[unsafe(no_mangle)]
2352pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
2353 builder_handle: *const RKRConFrameBuilder,
2354 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2355) -> RKRStatus {
2356 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2357}
2358
2359#[unsafe(no_mangle)]
2360pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
2361 builder_handle: *const RKRConFrameBuilder,
2362 opts: *const RKRDlpackExportOptions,
2363 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2364) -> RKRStatus {
2365 if builder_handle.is_null() || out_tensor.is_null() {
2366 return RKRStatus::RKR_STATUS_NULL_POINTER;
2367 }
2368 let o = match resolve_dlpack_opts(opts) {
2369 Ok(o) => o,
2370 Err(st) => return st,
2371 };
2372 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2373 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
2374}
2375#[unsafe(no_mangle)]
2381pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
2382 builder_handle: *const RKRConFrameBuilder,
2383 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2384) -> RKRStatus {
2385 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2386}
2387
2388#[unsafe(no_mangle)]
2389pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2390 builder_handle: *const RKRConFrameBuilder,
2391 opts: *const RKRDlpackExportOptions,
2392 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2393) -> RKRStatus {
2394 if builder_handle.is_null() || out_tensor.is_null() {
2395 return RKRStatus::RKR_STATUS_NULL_POINTER;
2396 }
2397 let o = match resolve_dlpack_opts(opts) {
2398 Ok(o) => o,
2399 Err(st) => return st,
2400 };
2401 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2402 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2403}
2404#[unsafe(no_mangle)]
2428pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2429 builder_handle: *mut RKRConFrameBuilder,
2430) -> *mut f64 {
2431 if builder_handle.is_null() {
2432 return std::ptr::null_mut();
2433 }
2434 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2435 builder
2436 .positions_view_mut()
2437 .as_slice_memory_order_mut()
2438 .map(|s| s.as_mut_ptr())
2439 .unwrap_or(std::ptr::null_mut())
2440}
2441#[unsafe(no_mangle)]
2448pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
2449 builder_handle: *mut RKRConFrameBuilder,
2450) -> *mut f64 {
2451 if builder_handle.is_null() {
2452 return std::ptr::null_mut();
2453 }
2454 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2455 if !builder.has_velocities_section() {
2456 return std::ptr::null_mut();
2457 }
2458 let slice = builder.velocities_mut();
2459 if slice.is_empty() {
2460 std::ptr::null_mut()
2461 } else {
2462 slice.as_mut_ptr()
2463 }
2464}
2465#[unsafe(no_mangle)]
2471pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2472 builder_handle: *mut RKRConFrameBuilder,
2473) -> *mut f64 {
2474 if builder_handle.is_null() {
2475 return std::ptr::null_mut();
2476 }
2477 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2478 if !builder.has_forces_section() {
2479 return std::ptr::null_mut();
2480 }
2481 let slice = builder.forces_mut();
2482 if slice.is_empty() {
2483 std::ptr::null_mut()
2484 } else {
2485 slice.as_mut_ptr()
2486 }
2487}
2488#[unsafe(no_mangle)]
2494pub unsafe extern "C" fn rkr_frame_builder_displacements_data(
2495 builder_handle: *mut RKRConFrameBuilder,
2496) -> *mut f64 {
2497 if builder_handle.is_null() {
2498 return std::ptr::null_mut();
2499 }
2500 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2501 if !builder.has_displacements_section() {
2502 return std::ptr::null_mut();
2503 }
2504 let slice = builder.displacements_mut();
2505 if slice.is_empty() {
2506 std::ptr::null_mut()
2507 } else {
2508 slice.as_mut_ptr()
2509 }
2510}
2511#[unsafe(no_mangle)]
2517pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2518 builder_handle: *mut RKRConFrameBuilder,
2519) -> *mut f64 {
2520 if builder_handle.is_null() {
2521 return std::ptr::null_mut();
2522 }
2523 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2524 if !builder.has_energies_section() {
2525 return std::ptr::null_mut();
2526 }
2527 let slice = builder.atom_energies_mut();
2528 if slice.is_empty() {
2529 std::ptr::null_mut()
2530 } else {
2531 slice.as_mut_ptr()
2532 }
2533}
2534#[unsafe(no_mangle)]
2539pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2540 builder_handle: *mut RKRConFrameBuilder,
2541) -> *mut f64 {
2542 if builder_handle.is_null() {
2543 return std::ptr::null_mut();
2544 }
2545 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2546 let slice = builder.masses_mut();
2547 if slice.is_empty() {
2548 std::ptr::null_mut()
2549 } else {
2550 slice.as_mut_ptr()
2551 }
2552}
2553#[unsafe(no_mangle)]
2558pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2559 builder_handle: *const RKRConFrameBuilder,
2560) -> *const u64 {
2561 if builder_handle.is_null() {
2562 return std::ptr::null();
2563 }
2564 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2565 let slice = builder.atom_ids();
2566 if slice.is_empty() {
2567 std::ptr::null()
2568 } else {
2569 slice.as_ptr()
2570 }
2571}
2572#[unsafe(no_mangle)]
2584#[allow(clippy::too_many_arguments)]
2585pub unsafe extern "C" fn rkr_frame_add_atom_full(
2586 builder_handle: *mut RKRConFrameBuilder,
2587 symbol: *const c_char,
2588 x: f64,
2589 y: f64,
2590 z: f64,
2591 fixed_x: bool,
2592 fixed_y: bool,
2593 fixed_z: bool,
2594 atom_id: u64,
2595 mass: f64,
2596 velocity: *const f64,
2597 force: *const f64,
2598) -> RKRStatus {
2599 let velocity = if velocity.is_null() {
2600 None
2601 } else {
2602 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
2603 };
2604 let force = if force.is_null() {
2605 None
2606 } else {
2607 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
2608 };
2609 unsafe {
2610 add_builder_atom(
2611 builder_handle,
2612 symbol,
2613 x,
2614 y,
2615 z,
2616 [fixed_x, fixed_y, fixed_z],
2617 atom_id,
2618 mass,
2619 velocity,
2620 force,
2621 )
2622 }
2623}
2624#[unsafe(no_mangle)]
2640pub unsafe extern "C" fn rkr_frame_new(
2641 cell: *const f64,
2642 angles: *const f64,
2643 prebox0: *const c_char,
2644 prebox1: *const c_char,
2645 postbox0: *const c_char,
2646 postbox1: *const c_char,
2647) -> *mut RKRConFrameBuilder {
2648 if cell.is_null() || angles.is_null() {
2649 return ptr::null_mut();
2650 }
2651 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
2652 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
2653 let get_str = |p: *const c_char| -> String {
2654 if p.is_null() {
2655 String::new()
2656 } else {
2657 unsafe { CStr::from_ptr(p) }
2658 .to_str()
2659 .unwrap_or("")
2660 .to_string()
2661 }
2662 };
2663 let _ = get_str(prebox1);
2666 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
2667 builder
2668 .prebox_header(get_str(prebox0))
2669 .postbox_header([get_str(postbox0), get_str(postbox1)]);
2670 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
2671}
2672#[unsafe(no_mangle)]
2678pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
2679 builder_handle: *mut RKRConFrameBuilder,
2680 metadata_json: *const c_char,
2681) -> RKRStatus {
2682 if builder_handle.is_null() || metadata_json.is_null() {
2683 return RKRStatus::RKR_STATUS_NULL_POINTER;
2684 }
2685 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2686 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
2687 Ok(s) => s,
2688 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2689 };
2690 match builder.set_metadata_json(metadata_json) {
2691 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
2692 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
2693 }
2694}
2695#[unsafe(no_mangle)]
2701pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
2702 builder_handle: *mut RKRConFrameBuilder,
2703 key: *const c_char,
2704 value: f64,
2705) -> RKRStatus {
2706 if builder_handle.is_null() || key.is_null() {
2707 return RKRStatus::RKR_STATUS_NULL_POINTER;
2708 }
2709 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2710 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2711 Ok(s) => s,
2712 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2713 };
2714 builder.set_scalar_metadata(key, value);
2715 RKRStatus::RKR_STATUS_SUCCESS
2716}
2717#[unsafe(no_mangle)]
2723pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
2724 builder_handle: *mut RKRConFrameBuilder,
2725 key: *const c_char,
2726 value: *const c_char,
2727) -> RKRStatus {
2728 if builder_handle.is_null() || key.is_null() || value.is_null() {
2729 return RKRStatus::RKR_STATUS_NULL_POINTER;
2730 }
2731 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2732 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2733 Ok(s) => s,
2734 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2735 };
2736 let value = match unsafe { CStr::from_ptr(value).to_str() } {
2737 Ok(s) => s,
2738 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2739 };
2740 builder.set_string_metadata(key, value);
2741 RKRStatus::RKR_STATUS_SUCCESS
2742}
2743#[unsafe(no_mangle)]
2749pub unsafe extern "C" fn rkr_frame_builder_set_energy(
2750 builder_handle: *mut RKRConFrameBuilder,
2751 energy: f64,
2752) -> RKRStatus {
2753 if builder_handle.is_null() {
2754 return RKRStatus::RKR_STATUS_NULL_POINTER;
2755 }
2756 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2757 builder.set_energy(energy);
2758 RKRStatus::RKR_STATUS_SUCCESS
2759}
2760#[unsafe(no_mangle)]
2766pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
2767 builder_handle: *mut RKRConFrameBuilder,
2768 idx: u64,
2769) -> RKRStatus {
2770 if builder_handle.is_null() {
2771 return RKRStatus::RKR_STATUS_NULL_POINTER;
2772 }
2773 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2774 builder.set_frame_index(idx);
2775 RKRStatus::RKR_STATUS_SUCCESS
2776}
2777#[unsafe(no_mangle)]
2783pub unsafe extern "C" fn rkr_frame_builder_set_time(
2784 builder_handle: *mut RKRConFrameBuilder,
2785 time: f64,
2786) -> RKRStatus {
2787 if builder_handle.is_null() {
2788 return RKRStatus::RKR_STATUS_NULL_POINTER;
2789 }
2790 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2791 builder.set_time(time);
2792 RKRStatus::RKR_STATUS_SUCCESS
2793}
2794#[unsafe(no_mangle)]
2800pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
2801 builder_handle: *mut RKRConFrameBuilder,
2802 dt: f64,
2803) -> RKRStatus {
2804 if builder_handle.is_null() {
2805 return RKRStatus::RKR_STATUS_NULL_POINTER;
2806 }
2807 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2808 builder.set_timestep(dt);
2809 RKRStatus::RKR_STATUS_SUCCESS
2810}
2811#[unsafe(no_mangle)]
2817pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
2818 builder_handle: *mut RKRConFrameBuilder,
2819 bead: u64,
2820) -> RKRStatus {
2821 if builder_handle.is_null() {
2822 return RKRStatus::RKR_STATUS_NULL_POINTER;
2823 }
2824 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2825 builder.set_neb_bead(bead);
2826 RKRStatus::RKR_STATUS_SUCCESS
2827}
2828#[unsafe(no_mangle)]
2834pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
2835 builder_handle: *mut RKRConFrameBuilder,
2836 band: u64,
2837) -> RKRStatus {
2838 if builder_handle.is_null() {
2839 return RKRStatus::RKR_STATUS_NULL_POINTER;
2840 }
2841 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2842 builder.set_neb_band(band);
2843 RKRStatus::RKR_STATUS_SUCCESS
2844}
2845#[unsafe(no_mangle)]
2862pub unsafe extern "C" fn rkr_frame_add_atom(
2863 builder_handle: *mut RKRConFrameBuilder,
2864 symbol: *const c_char,
2865 x: f64,
2866 y: f64,
2867 z: f64,
2868 is_fixed: bool,
2869 atom_id: u64,
2870 mass: f64,
2871) -> RKRStatus {
2872 unsafe {
2873 add_builder_atom(
2874 builder_handle,
2875 symbol,
2876 x,
2877 y,
2878 z,
2879 [is_fixed; 3],
2880 atom_id,
2881 mass,
2882 None,
2883 None,
2884 )
2885 }
2886}
2887#[unsafe(no_mangle)]
2894pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
2895 builder_handle: *mut RKRConFrameBuilder,
2896 symbol: *const c_char,
2897 x: f64,
2898 y: f64,
2899 z: f64,
2900 fixed_x: bool,
2901 fixed_y: bool,
2902 fixed_z: bool,
2903 atom_id: u64,
2904 mass: f64,
2905) -> RKRStatus {
2906 unsafe {
2907 add_builder_atom(
2908 builder_handle,
2909 symbol,
2910 x,
2911 y,
2912 z,
2913 [fixed_x, fixed_y, fixed_z],
2914 atom_id,
2915 mass,
2916 None,
2917 None,
2918 )
2919 }
2920}
2921#[unsafe(no_mangle)]
2928pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
2929 builder_handle: *mut RKRConFrameBuilder,
2930 symbol: *const c_char,
2931 x: f64,
2932 y: f64,
2933 z: f64,
2934 is_fixed: bool,
2935 atom_id: u64,
2936 mass: f64,
2937 vx: f64,
2938 vy: f64,
2939 vz: f64,
2940) -> RKRStatus {
2941 unsafe {
2942 add_builder_atom(
2943 builder_handle,
2944 symbol,
2945 x,
2946 y,
2947 z,
2948 [is_fixed; 3],
2949 atom_id,
2950 mass,
2951 Some([vx, vy, vz]),
2952 None,
2953 )
2954 }
2955}
2956#[unsafe(no_mangle)]
2963pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
2964 builder_handle: *mut RKRConFrameBuilder,
2965 symbol: *const c_char,
2966 x: f64,
2967 y: f64,
2968 z: f64,
2969 fixed_x: bool,
2970 fixed_y: bool,
2971 fixed_z: bool,
2972 atom_id: u64,
2973 mass: f64,
2974 vx: f64,
2975 vy: f64,
2976 vz: f64,
2977) -> RKRStatus {
2978 unsafe {
2979 add_builder_atom(
2980 builder_handle,
2981 symbol,
2982 x,
2983 y,
2984 z,
2985 [fixed_x, fixed_y, fixed_z],
2986 atom_id,
2987 mass,
2988 Some([vx, vy, vz]),
2989 None,
2990 )
2991 }
2992}
2993#[unsafe(no_mangle)]
3000pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
3001 builder_handle: *mut RKRConFrameBuilder,
3002 symbol: *const c_char,
3003 x: f64,
3004 y: f64,
3005 z: f64,
3006 is_fixed: bool,
3007 atom_id: u64,
3008 mass: f64,
3009 fx: f64,
3010 fy: f64,
3011 fz: f64,
3012) -> RKRStatus {
3013 unsafe {
3014 add_builder_atom(
3015 builder_handle,
3016 symbol,
3017 x,
3018 y,
3019 z,
3020 [is_fixed; 3],
3021 atom_id,
3022 mass,
3023 None,
3024 Some([fx, fy, fz]),
3025 )
3026 }
3027}
3028#[unsafe(no_mangle)]
3035pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
3036 builder_handle: *mut RKRConFrameBuilder,
3037 symbol: *const c_char,
3038 x: f64,
3039 y: f64,
3040 z: f64,
3041 fixed_x: bool,
3042 fixed_y: bool,
3043 fixed_z: bool,
3044 atom_id: u64,
3045 mass: f64,
3046 fx: f64,
3047 fy: f64,
3048 fz: f64,
3049) -> RKRStatus {
3050 unsafe {
3051 add_builder_atom(
3052 builder_handle,
3053 symbol,
3054 x,
3055 y,
3056 z,
3057 [fixed_x, fixed_y, fixed_z],
3058 atom_id,
3059 mass,
3060 None,
3061 Some([fx, fy, fz]),
3062 )
3063 }
3064}
3065#[unsafe(no_mangle)]
3072pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
3073 builder_handle: *mut RKRConFrameBuilder,
3074 symbol: *const c_char,
3075 x: f64,
3076 y: f64,
3077 z: f64,
3078 is_fixed: bool,
3079 atom_id: u64,
3080 mass: f64,
3081 vx: f64,
3082 vy: f64,
3083 vz: f64,
3084 fx: f64,
3085 fy: f64,
3086 fz: f64,
3087) -> RKRStatus {
3088 unsafe {
3089 add_builder_atom(
3090 builder_handle,
3091 symbol,
3092 x,
3093 y,
3094 z,
3095 [is_fixed; 3],
3096 atom_id,
3097 mass,
3098 Some([vx, vy, vz]),
3099 Some([fx, fy, fz]),
3100 )
3101 }
3102}
3103#[unsafe(no_mangle)]
3110pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
3111 builder_handle: *mut RKRConFrameBuilder,
3112 symbol: *const c_char,
3113 x: f64,
3114 y: f64,
3115 z: f64,
3116 fixed_x: bool,
3117 fixed_y: bool,
3118 fixed_z: bool,
3119 atom_id: u64,
3120 mass: f64,
3121 vx: f64,
3122 vy: f64,
3123 vz: f64,
3124 fx: f64,
3125 fy: f64,
3126 fz: f64,
3127) -> RKRStatus {
3128 unsafe {
3129 add_builder_atom(
3130 builder_handle,
3131 symbol,
3132 x,
3133 y,
3134 z,
3135 [fixed_x, fixed_y, fixed_z],
3136 atom_id,
3137 mass,
3138 Some([vx, vy, vz]),
3139 Some([fx, fy, fz]),
3140 )
3141 }
3142}
3143#[unsafe(no_mangle)]
3152pub unsafe extern "C" fn rkr_frame_builder_build(
3153 builder_handle: *mut RKRConFrameBuilder,
3154) -> *mut RKRConFrame {
3155 if builder_handle.is_null() {
3156 return ptr::null_mut();
3157 }
3158 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3159 match builder.build() {
3160 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3161 Err(_) => ptr::null_mut(),
3162 }
3163}
3164#[unsafe(no_mangle)]
3169pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
3170 if !builder_handle.is_null() {
3171 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
3172 }
3173}
3174#[unsafe(no_mangle)]
3191pub unsafe extern "C" fn rkr_frame_builder_clone(
3192 builder_handle: *const RKRConFrameBuilder,
3193) -> *mut RKRConFrameBuilder {
3194 if builder_handle.is_null() {
3195 return std::ptr::null_mut();
3196 }
3197 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3198 let cloned = builder.clone();
3199 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
3200}
3201#[unsafe(no_mangle)]
3207pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3208 let filename = match unsafe { cstr_path(filename_c) } {
3209 Some(s) => s,
3210 None => return ptr::null_mut(),
3211 };
3212 match crate::compression::gzip_writer(Path::new(filename)) {
3213 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3214 Err(_) => ptr::null_mut(),
3215 }
3216}
3217#[unsafe(no_mangle)]
3224pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
3225 filename_c: *const c_char,
3226 precision: u8,
3227) -> *mut RKRConFrameWriter {
3228 let filename = match unsafe { cstr_path(filename_c) } {
3229 Some(s) => s,
3230 None => return ptr::null_mut(),
3231 };
3232 match crate::compression::gzip_writer(Path::new(filename)) {
3233 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3234 Err(_) => ptr::null_mut(),
3235 }
3236}
3237#[cfg(feature = "zstd")]
3247#[unsafe(no_mangle)]
3248pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3249 let filename = match unsafe { cstr_path(filename_c) } {
3250 Some(s) => s,
3251 None => return ptr::null_mut(),
3252 };
3253 match crate::compression::zstd_writer(Path::new(filename)) {
3254 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3255 Err(_) => ptr::null_mut(),
3256 }
3257}
3258#[cfg(feature = "zstd")]
3269#[unsafe(no_mangle)]
3270pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3271 filename_c: *const c_char,
3272 precision: u8,
3273) -> *mut RKRConFrameWriter {
3274 let filename = match unsafe { cstr_path(filename_c) } {
3275 Some(s) => s,
3276 None => return ptr::null_mut(),
3277 };
3278 match crate::compression::zstd_writer(Path::new(filename)) {
3279 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3280 Err(_) => ptr::null_mut(),
3281 }
3282}
3283#[unsafe(no_mangle)]
3295pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
3296 if filename_c.is_null() {
3297 return ptr::null_mut();
3298 }
3299 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3300 Ok(s) => s,
3301 Err(_) => return ptr::null_mut(),
3302 };
3303 match iterators::read_first_frame(Path::new(filename)) {
3304 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3305 Err(_) => ptr::null_mut(),
3306 }
3307}
3308#[unsafe(no_mangle)]
3317pub unsafe extern "C" fn rkr_read_all_frames(
3318 filename_c: *const c_char,
3319 num_frames: *mut usize,
3320) -> *mut *mut RKRConFrame {
3321 if filename_c.is_null() || num_frames.is_null() {
3322 return ptr::null_mut();
3323 }
3324 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3325 Ok(s) => s,
3326 Err(_) => return ptr::null_mut(),
3327 };
3328 match iterators::read_all_frames(Path::new(filename)) {
3329 Ok(frames) => pack_frame_handles(frames, num_frames),
3330 Err(_) => ptr::null_mut(),
3331 }
3332}
3333
3334#[unsafe(no_mangle)]
3344pub unsafe extern "C" fn rkr_read_all_frames_n_threads(
3345 filename_c: *const c_char,
3346 num_frames: *mut usize,
3347 n_threads: usize,
3348) -> *mut *mut RKRConFrame {
3349 if filename_c.is_null() || num_frames.is_null() {
3350 return ptr::null_mut();
3351 }
3352 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3353 Ok(s) => s,
3354 Err(_) => return ptr::null_mut(),
3355 };
3356 let threads = if n_threads == 0 {
3357 None
3358 } else {
3359 Some(n_threads)
3360 };
3361 match iterators::read_all_frames_with_threads(Path::new(filename), threads) {
3362 Ok(frames) => pack_frame_handles(frames, num_frames),
3363 Err(_) => ptr::null_mut(),
3364 }
3365}
3366
3367fn pack_frame_handles(frames: Vec<ConFrame>, num_frames: *mut usize) -> *mut *mut RKRConFrame {
3369 let count = frames.len();
3370 let mut handles: Vec<*mut RKRConFrame> = frames
3371 .into_iter()
3372 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3373 .collect();
3374 handles.shrink_to_fit();
3375 debug_assert_eq!(handles.len(), handles.capacity());
3376 let ptr = handles.as_mut_ptr();
3377 std::mem::forget(handles);
3378 unsafe { *num_frames = count };
3379 ptr
3380}
3381#[unsafe(no_mangle)]
3387pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3388 if frames.is_null() {
3389 return;
3390 }
3391 unsafe {
3392 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3393 for handle in handles {
3394 if !handle.is_null() {
3395 let _ = Box::from_raw(handle as *mut ConFrame);
3396 }
3397 }
3398 }
3399}
3400#[unsafe(no_mangle)]
3406pub unsafe extern "C" fn free_rkr_frame_ptr_array(
3407 frames: *mut *mut RKRConFrame,
3408 num_frames: usize,
3409) {
3410 if frames.is_null() {
3411 return;
3412 }
3413 unsafe {
3414 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3415 }
3416}
3417
3418#[cfg(feature = "metatensor")]
3433#[unsafe(no_mangle)]
3434pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3435 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3436}
3437#[cfg(feature = "metatensor")]
3439#[unsafe(no_mangle)]
3440pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3441 frame_handle: *const RKRConFrame,
3442 out_block: *mut *mut metatensor::c_api::mts_block_t,
3443) -> RKRStatus {
3444 if frame_handle.is_null() || out_block.is_null() {
3445 return RKRStatus::RKR_STATUS_NULL_POINTER;
3446 }
3447 unsafe { *out_block = std::ptr::null_mut() };
3448 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3449 return RKRStatus::RKR_STATUS_NULL_POINTER;
3450 };
3451 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3452 crate::metatensor_export::frame_positions_block(frame)
3453 })) {
3454 Ok(Ok(b)) => {
3455 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3456 RKRStatus::RKR_STATUS_SUCCESS
3457 }
3458 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3459 }
3460}
3461#[cfg(feature = "metatensor")]
3462#[unsafe(no_mangle)]
3463pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3464 frame_handle: *const RKRConFrame,
3465 out_block: *mut *mut metatensor::c_api::mts_block_t,
3466) -> RKRStatus {
3467 if frame_handle.is_null() || out_block.is_null() {
3468 return RKRStatus::RKR_STATUS_NULL_POINTER;
3469 }
3470 unsafe { *out_block = std::ptr::null_mut() };
3471 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3472 return RKRStatus::RKR_STATUS_NULL_POINTER;
3473 };
3474 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3475 crate::metatensor_export::frame_velocities_block(frame)
3476 })) {
3477 Ok(Ok(Some(b))) => {
3478 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3479 RKRStatus::RKR_STATUS_SUCCESS
3480 }
3481 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3482 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3483 }
3484}
3485#[cfg(feature = "metatensor")]
3486#[unsafe(no_mangle)]
3487pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3488 frame_handle: *const RKRConFrame,
3489 out_block: *mut *mut metatensor::c_api::mts_block_t,
3490) -> RKRStatus {
3491 if frame_handle.is_null() || out_block.is_null() {
3492 return RKRStatus::RKR_STATUS_NULL_POINTER;
3493 }
3494 unsafe { *out_block = std::ptr::null_mut() };
3495 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3496 return RKRStatus::RKR_STATUS_NULL_POINTER;
3497 };
3498 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3499 crate::metatensor_export::frame_forces_block(frame)
3500 })) {
3501 Ok(Ok(Some(b))) => {
3502 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3503 RKRStatus::RKR_STATUS_SUCCESS
3504 }
3505 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3506 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3507 }
3508}
3509#[cfg(feature = "metatensor")]
3510#[unsafe(no_mangle)]
3511pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3512 frame_handle: *const RKRConFrame,
3513 out_block: *mut *mut metatensor::c_api::mts_block_t,
3514) -> RKRStatus {
3515 if frame_handle.is_null() || out_block.is_null() {
3516 return RKRStatus::RKR_STATUS_NULL_POINTER;
3517 }
3518 unsafe { *out_block = std::ptr::null_mut() };
3519 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3520 return RKRStatus::RKR_STATUS_NULL_POINTER;
3521 };
3522 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3523 crate::metatensor_export::frame_energies_block(frame)
3524 })) {
3525 Ok(Ok(Some(b))) => {
3526 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3527 RKRStatus::RKR_STATUS_SUCCESS
3528 }
3529 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3530 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3531 }
3532}
3533#[cfg(not(feature = "zstd"))]
3534#[unsafe(no_mangle)]
3535pub unsafe extern "C" fn create_writer_zstd_c(
3536 _filename_c: *const c_char,
3537) -> *mut RKRConFrameWriter {
3538 ptr::null_mut()
3539}
3540#[cfg(not(feature = "zstd"))]
3541#[unsafe(no_mangle)]
3542pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3543 _filename_c: *const c_char,
3544 _precision: u8,
3545) -> *mut RKRConFrameWriter {
3546 ptr::null_mut()
3547}
3548#[cfg(not(feature = "metatensor"))]
3552pub struct mts_block_t {
3555 _private: [u8; 0],
3556}
3557#[cfg(not(feature = "metatensor"))]
3558#[unsafe(no_mangle)]
3559pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3560#[cfg(not(feature = "metatensor"))]
3561#[unsafe(no_mangle)]
3562pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3563 _frame_handle: *const RKRConFrame,
3564 out_block: *mut *mut mts_block_t,
3565) -> RKRStatus {
3566 if !out_block.is_null() {
3567 unsafe { *out_block = std::ptr::null_mut() };
3568 }
3569 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3570}
3571#[cfg(not(feature = "metatensor"))]
3572#[unsafe(no_mangle)]
3573pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3574 _frame_handle: *const RKRConFrame,
3575 out_block: *mut *mut mts_block_t,
3576) -> RKRStatus {
3577 if !out_block.is_null() {
3578 unsafe { *out_block = std::ptr::null_mut() };
3579 }
3580 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3581}
3582#[cfg(not(feature = "metatensor"))]
3583#[unsafe(no_mangle)]
3584pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3585 _frame_handle: *const RKRConFrame,
3586 out_block: *mut *mut mts_block_t,
3587) -> RKRStatus {
3588 if !out_block.is_null() {
3589 unsafe { *out_block = std::ptr::null_mut() };
3590 }
3591 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3592}
3593#[cfg(not(feature = "metatensor"))]
3594#[unsafe(no_mangle)]
3595pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3596 _frame_handle: *const RKRConFrame,
3597 out_block: *mut *mut mts_block_t,
3598) -> RKRStatus {
3599 if !out_block.is_null() {
3600 unsafe { *out_block = std::ptr::null_mut() };
3601 }
3602 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3603}
3604#[unsafe(no_mangle)]
3607pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3608 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3609 return 0;
3610 };
3611 frame.atom_data.len()
3612}
3613#[unsafe(no_mangle)]
3615pub unsafe extern "C" fn rkr_frame_xyz_view(
3616 frame_handle: *const RKRConFrame,
3617 out: *mut RKRArrayView,
3618) -> RKRStatus {
3619 fill_array2_view(frame_handle, out, |f| &f.positions, false)
3620}
3621
3622#[unsafe(no_mangle)]
3624pub unsafe extern "C" fn rkr_frame_velocities_view(
3625 frame_handle: *const RKRConFrame,
3626 out: *mut RKRArrayView,
3627) -> RKRStatus {
3628 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
3629}
3630
3631#[unsafe(no_mangle)]
3633pub unsafe extern "C" fn rkr_frame_forces_view(
3634 frame_handle: *const RKRConFrame,
3635 out: *mut RKRArrayView,
3636) -> RKRStatus {
3637 fill_array2_view(frame_handle, out, |f| &f.forces, true)
3638}
3639
3640#[unsafe(no_mangle)]
3646pub unsafe extern "C" fn rkr_frame_has_displacements(frame_handle: *const RKRConFrame) -> bool {
3647 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
3648 Some(f) => f.has_displacements(),
3649 None => false,
3650 }
3651}
3652
3653#[unsafe(no_mangle)]
3658pub unsafe extern "C" fn rkr_frame_displacements_view(
3659 frame_handle: *const RKRConFrame,
3660 out: *mut RKRArrayView,
3661) -> RKRStatus {
3662 fill_array2_view(frame_handle, out, |f| &f.displacements, true)
3663}
3664
3665#[unsafe(no_mangle)]
3667pub unsafe extern "C" fn rkr_frame_energies_view(
3668 frame_handle: *const RKRConFrame,
3669 out: *mut RKRArrayView,
3670) -> RKRStatus {
3671 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
3672}
3673
3674#[unsafe(no_mangle)]
3676pub unsafe extern "C" fn rkr_frame_masses_view(
3677 frame_handle: *const RKRConFrame,
3678 out: *mut RKRArrayView,
3679) -> RKRStatus {
3680 fill_array1_view(frame_handle, out, |f| &f.masses, false)
3681}
3682
3683#[unsafe(no_mangle)]
3685pub unsafe extern "C" fn rkr_frame_atom_ids_view(
3686 frame_handle: *const RKRConFrame,
3687 out: *mut RKRArrayView,
3688) -> RKRStatus {
3689 if frame_handle.is_null() || out.is_null() {
3690 return RKRStatus::RKR_STATUS_NULL_POINTER;
3691 }
3692 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3693 return RKRStatus::RKR_STATUS_NULL_POINTER;
3694 };
3695 let slice = frame.atom_ids.as_slice_memory_order();
3696 unsafe {
3697 *out = RKRArrayView {
3698 data: slice
3699 .map(|s| s.as_ptr() as *const std::ffi::c_void)
3700 .unwrap_or(std::ptr::null()),
3701 n: frame.atom_ids.len(),
3702 cols: 1,
3703 dtype_code: 1, dtype_bits: 64,
3705 };
3706 }
3707 RKRStatus::RKR_STATUS_SUCCESS
3708}
3709
3710#[unsafe(no_mangle)]
3713pub unsafe extern "C" fn rkr_frame_xyz_f64(
3714 frame_handle: *const RKRConFrame,
3715 n: *mut usize,
3716) -> *const f64 {
3717 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
3718}
3719
3720#[unsafe(no_mangle)]
3722pub unsafe extern "C" fn rkr_frame_velocities_f64(
3723 frame_handle: *const RKRConFrame,
3724 n: *mut usize,
3725) -> *const f64 {
3726 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
3727}
3728
3729#[unsafe(no_mangle)]
3731pub unsafe extern "C" fn rkr_frame_forces_f64(
3732 frame_handle: *const RKRConFrame,
3733 n: *mut usize,
3734) -> *const f64 {
3735 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
3736}
3737
3738#[unsafe(no_mangle)]
3743pub unsafe extern "C" fn rkr_frame_displacements_f64(
3744 frame_handle: *const RKRConFrame,
3745 n: *mut usize,
3746) -> *const f64 {
3747 f64_col_ptr(frame_handle, n, |f| f.displacements.f64_slice())
3748}
3749
3750fn fill_array2_view(
3751 frame_handle: *const RKRConFrame,
3752 out: *mut RKRArrayView,
3753 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
3754 section: bool,
3755) -> RKRStatus {
3756 if frame_handle.is_null() || out.is_null() {
3757 return RKRStatus::RKR_STATUS_NULL_POINTER;
3758 }
3759 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3760 return RKRStatus::RKR_STATUS_NULL_POINTER;
3761 };
3762 let arr = get(frame);
3763 if section && arr.nrows() == 0 {
3764 unsafe { *out = RKRArrayView::empty() };
3765 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3766 }
3767 unsafe { *out = RKRArrayView::from_array2(arr) };
3768 RKRStatus::RKR_STATUS_SUCCESS
3769}
3770
3771fn fill_array1_view(
3772 frame_handle: *const RKRConFrame,
3773 out: *mut RKRArrayView,
3774 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
3775 section: bool,
3776) -> RKRStatus {
3777 if frame_handle.is_null() || out.is_null() {
3778 return RKRStatus::RKR_STATUS_NULL_POINTER;
3779 }
3780 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3781 return RKRStatus::RKR_STATUS_NULL_POINTER;
3782 };
3783 let arr = get(frame);
3784 if section && arr.len() == 0 {
3785 unsafe { *out = RKRArrayView::empty() };
3786 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3787 }
3788 unsafe { *out = RKRArrayView::from_array1(arr) };
3789 RKRStatus::RKR_STATUS_SUCCESS
3790}
3791
3792fn f64_col_ptr(
3793 frame_handle: *const RKRConFrame,
3794 n: *mut usize,
3795 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
3796) -> *const f64 {
3797 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3798 if !n.is_null() {
3799 unsafe { *n = 0 };
3800 }
3801 return std::ptr::null();
3802 };
3803 match get(frame) {
3804 Some(s) if !s.is_empty() => {
3805 if !n.is_null() {
3806 unsafe { *n = s.len() / 3 };
3807 }
3808 s.as_ptr()
3809 }
3810 _ => {
3811 if !n.is_null() {
3812 unsafe { *n = 0 };
3813 }
3814 std::ptr::null()
3815 }
3816 }
3817}
3818
3819#[unsafe(no_mangle)]
3822pub unsafe extern "C" fn rkr_frame_copy_positions(
3823 frame_handle: *const RKRConFrame,
3824 out: *mut f64,
3825 out_len: usize,
3826) -> RKRStatus {
3827 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
3828}
3829fn copy_array2_f64(
3830 frame_handle: *const RKRConFrame,
3831 out: *mut f64,
3832 out_len: usize,
3833 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
3834 section: bool,
3835) -> RKRStatus {
3836 if frame_handle.is_null() || out.is_null() {
3837 return RKRStatus::RKR_STATUS_NULL_POINTER;
3838 }
3839 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3840 return RKRStatus::RKR_STATUS_NULL_POINTER;
3841 };
3842 let arr = get(frame);
3843 let n = if arr.nrows() > 0 {
3844 arr.nrows()
3845 } else {
3846 frame.atom_data.len()
3847 };
3848 if section && arr.nrows() == 0 {
3849 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3850 }
3851 let need = n.saturating_mul(3);
3852 if out_len < need {
3853 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3854 }
3855 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
3856 if arr.nrows() == n {
3857 if let Some(src) = arr.f64_slice() {
3858 dest.copy_from_slice(&src[..need.min(src.len())]);
3859 return RKRStatus::RKR_STATUS_SUCCESS;
3860 }
3861 for i in 0..n {
3862 let row = arr.as_f64_row(i);
3863 dest[i * 3] = row[0];
3864 dest[i * 3 + 1] = row[1];
3865 dest[i * 3 + 2] = row[2];
3866 }
3867 return RKRStatus::RKR_STATUS_SUCCESS;
3868 }
3869 for (i, a) in frame.atom_data.iter().enumerate() {
3870 dest[i * 3] = a.x;
3871 dest[i * 3 + 1] = a.y;
3872 dest[i * 3 + 2] = a.z;
3873 }
3874 RKRStatus::RKR_STATUS_SUCCESS
3875}
3876
3877fn copy_array1_f64(
3878 frame_handle: *const RKRConFrame,
3879 out: *mut f64,
3880 out_len: usize,
3881 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
3882 section: bool,
3883) -> RKRStatus {
3884 if frame_handle.is_null() || out.is_null() {
3885 return RKRStatus::RKR_STATUS_NULL_POINTER;
3886 }
3887 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3888 return RKRStatus::RKR_STATUS_NULL_POINTER;
3889 };
3890 let arr = get(frame);
3891 let n = if arr.len() > 0 {
3892 arr.len()
3893 } else {
3894 frame.atom_data.len()
3895 };
3896 if section && arr.len() == 0 {
3897 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3898 }
3899 if out_len < n {
3900 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3901 }
3902 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
3903 if let Some(src) = arr.f64_slice() {
3904 dest.copy_from_slice(&src[..n.min(src.len())]);
3905 return RKRStatus::RKR_STATUS_SUCCESS;
3906 }
3907 for i in 0..n {
3908 dest[i] = arr.get_f64(i);
3909 }
3910 RKRStatus::RKR_STATUS_SUCCESS
3911}
3912
3913#[unsafe(no_mangle)]
3914pub unsafe extern "C" fn rkr_frame_copy_velocities(
3915 frame_handle: *const RKRConFrame,
3916 out: *mut f64,
3917 out_len: usize,
3918) -> RKRStatus {
3919 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
3920}
3921#[unsafe(no_mangle)]
3922pub unsafe extern "C" fn rkr_frame_copy_forces(
3923 frame_handle: *const RKRConFrame,
3924 out: *mut f64,
3925 out_len: usize,
3926) -> RKRStatus {
3927 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
3928}
3929#[unsafe(no_mangle)]
3935pub unsafe extern "C" fn rkr_frame_copy_displacements(
3936 frame_handle: *const RKRConFrame,
3937 out: *mut f64,
3938 out_len: usize,
3939) -> RKRStatus {
3940 copy_array2_f64(frame_handle, out, out_len, |f| &f.displacements, true)
3941}
3942#[unsafe(no_mangle)]
3943pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
3944 frame_handle: *const RKRConFrame,
3945 out: *mut f64,
3946 out_len: usize,
3947) -> RKRStatus {
3948 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
3949}
3950#[unsafe(no_mangle)]
3951pub unsafe extern "C" fn rkr_frame_copy_masses(
3952 frame_handle: *const RKRConFrame,
3953 out: *mut f64,
3954 out_len: usize,
3955) -> RKRStatus {
3956 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
3957}
3958#[unsafe(no_mangle)]
3959pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
3960 frame_handle: *const RKRConFrame,
3961 out: *mut u64,
3962 out_len: usize,
3963) -> RKRStatus {
3964 if frame_handle.is_null() || out.is_null() {
3965 return RKRStatus::RKR_STATUS_NULL_POINTER;
3966 }
3967 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3968 return RKRStatus::RKR_STATUS_NULL_POINTER;
3969 };
3970 let n = frame.atom_ids.len().max(frame.atom_data.len());
3971 if out_len < n {
3972 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3973 }
3974 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
3975 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
3976 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
3977 return RKRStatus::RKR_STATUS_SUCCESS;
3978 }
3979 for (i, a) in frame.atom_data.iter().enumerate() {
3980 dest[i] = a.atom_id;
3981 }
3982 RKRStatus::RKR_STATUS_SUCCESS
3983}
3984fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
3985 let n = frame.atom_data.len();
3986 let mut data = Vec::with_capacity(n * 3);
3987 for a in &frame.atom_data {
3988 data.extend_from_slice(&[a.x, a.y, a.z]);
3989 }
3990 ndarray::ArcArray2::from_shape_vec((n, 3), data)
3991 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
3992}
3993
3994#[unsafe(no_mangle)]
4004pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
4005 frame_handle: *const RKRConFrame,
4006 device_type: i32,
4007 device_id: i32,
4008 _stream: i64,
4009 _max_version_major: u32,
4010 _max_version_minor: u32,
4011 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4012) -> RKRStatus {
4013 if frame_handle.is_null() || out_tensor.is_null() {
4014 return RKRStatus::RKR_STATUS_NULL_POINTER;
4015 }
4016 unsafe { *out_tensor = std::ptr::null_mut() };
4017 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
4018 dlpk::sys::DLDevice::cpu()
4019 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
4020 #[cfg(feature = "cuda")]
4021 {
4022 dlpk::sys::DLDevice::cuda(device_id)
4023 }
4024 #[cfg(not(feature = "cuda"))]
4025 {
4026 let _ = device_id;
4027 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4028 }
4029 } else {
4030 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4031 };
4032 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4033 return RKRStatus::RKR_STATUS_NULL_POINTER;
4034 };
4035 match frame.positions_as_dlpack(dl_device) {
4037 Ok(tensor) => {
4038 let raw = tensor.into_raw();
4039 unsafe {
4040 *out_tensor = raw.as_ptr();
4041 }
4042 RKRStatus::RKR_STATUS_SUCCESS
4043 }
4044 Err(e) => map_dlpack_err(e),
4045 }
4046}
4047
4048#[unsafe(no_mangle)]
4054pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
4055 frame_handle: *mut RKRConFrame,
4056 tensor: *const RKRDLManagedTensorVersioned,
4057) -> RKRStatus {
4058 if frame_handle.is_null() || tensor.is_null() {
4059 return RKRStatus::RKR_STATUS_NULL_POINTER;
4060 }
4061 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
4062 let dl = unsafe { &(*tensor).dl_tensor };
4063 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
4064 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
4065 }
4066 let n = frame.atom_data.len();
4067 let need = n.saturating_mul(3);
4068 let ndim = dl.ndim as usize;
4069 let shape = if dl.shape.is_null() {
4070 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4071 } else {
4072 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
4073 };
4074 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
4075 need
4076 } else if ndim == 1 && shape[0] == need as i64 {
4077 need
4078 } else {
4079 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4080 };
4081 let code = dl.dtype.code as u8;
4082 let bits = dl.dtype.bits;
4083 if dl.data.is_null() {
4084 return RKRStatus::RKR_STATUS_NULL_POINTER;
4085 }
4086 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
4087 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
4088 s.to_vec()
4089 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
4090 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
4091 s.iter().map(|&x| x as f64).collect()
4092 } else {
4093 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4094 };
4095 if frame.positions.nrows() != n {
4096 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
4097 }
4098 for i in 0..n {
4099 frame
4100 .positions
4101 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
4102 }
4103 frame.sync_atom_data_from_arrays();
4104 RKRStatus::RKR_STATUS_SUCCESS
4105}
4106
4107#[unsafe(no_mangle)]
4110pub unsafe extern "C" fn rkr_frame_positions_dlpack(
4111 frame_handle: *const RKRConFrame,
4112 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4113) -> RKRStatus {
4114 unsafe {
4115 rkr_frame_positions_as_dlpack(
4116 frame_handle,
4117 rkr_dl_device_type::RKR_DL_CPU,
4118 0,
4119 0,
4120 1,
4121 0,
4122 out_tensor,
4123 )
4124 }
4125}
4126
4127#[unsafe(no_mangle)]
4132pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
4133 frame_handle: *const RKRConFrame,
4134 opts: *const RKRDlpackExportOptions,
4135 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4136) -> RKRStatus {
4137 if frame_handle.is_null() || out_tensor.is_null() {
4138 return RKRStatus::RKR_STATUS_NULL_POINTER;
4139 }
4140 unsafe { *out_tensor = std::ptr::null_mut() };
4141 let o = match resolve_dlpack_opts(opts) {
4142 Ok(o) => o,
4143 Err(st) => return st,
4144 };
4145 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4146 return RKRStatus::RKR_STATUS_NULL_POINTER;
4147 };
4148 let arr = frame_positions_arc(frame);
4149 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4150}
4151
4152#[unsafe(no_mangle)]
4154pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
4155 frame_handle: *const RKRConFrame,
4156 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4157) -> RKRStatus {
4158 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4159}
4160
4161#[unsafe(no_mangle)]
4162pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
4163 frame_handle: *const RKRConFrame,
4164 opts: *const RKRDlpackExportOptions,
4165 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4166) -> RKRStatus {
4167 if frame_handle.is_null() || out_tensor.is_null() {
4168 return RKRStatus::RKR_STATUS_NULL_POINTER;
4169 }
4170 unsafe { *out_tensor = std::ptr::null_mut() };
4171 let o = match resolve_dlpack_opts(opts) {
4172 Ok(o) => o,
4173 Err(st) => return st,
4174 };
4175 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4176 return RKRStatus::RKR_STATUS_NULL_POINTER;
4177 };
4178 if !frame.has_velocities() {
4179 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4180 }
4181 let n = frame.atom_data.len();
4182 let mut data = Vec::with_capacity(n * 3);
4183 for a in &frame.atom_data {
4184 let v = a.velocity.unwrap_or([0.0; 3]);
4185 data.extend_from_slice(&v);
4186 }
4187 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4188 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4189 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4190}
4191
4192#[unsafe(no_mangle)]
4194pub unsafe extern "C" fn rkr_frame_forces_dlpack(
4195 frame_handle: *const RKRConFrame,
4196 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4197) -> RKRStatus {
4198 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4199}
4200
4201#[unsafe(no_mangle)]
4202pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
4203 frame_handle: *const RKRConFrame,
4204 opts: *const RKRDlpackExportOptions,
4205 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4206) -> RKRStatus {
4207 if frame_handle.is_null() || out_tensor.is_null() {
4208 return RKRStatus::RKR_STATUS_NULL_POINTER;
4209 }
4210 unsafe { *out_tensor = std::ptr::null_mut() };
4211 let o = match resolve_dlpack_opts(opts) {
4212 Ok(o) => o,
4213 Err(st) => return st,
4214 };
4215 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4216 return RKRStatus::RKR_STATUS_NULL_POINTER;
4217 };
4218 if !frame.has_forces() {
4219 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4220 }
4221 let n = frame.atom_data.len();
4222 let mut data = Vec::with_capacity(n * 3);
4223 for a in &frame.atom_data {
4224 let f = a.force.unwrap_or([0.0; 3]);
4225 data.extend_from_slice(&f);
4226 }
4227 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4228 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4229 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4230}
4231
4232#[unsafe(no_mangle)]
4239pub unsafe extern "C" fn rkr_frame_displacements_dlpack(
4240 frame_handle: *const RKRConFrame,
4241 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4242) -> RKRStatus {
4243 unsafe { rkr_frame_displacements_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4244}
4245
4246#[unsafe(no_mangle)]
4253pub unsafe extern "C" fn rkr_frame_displacements_dlpack_ex(
4254 frame_handle: *const RKRConFrame,
4255 opts: *const RKRDlpackExportOptions,
4256 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4257) -> RKRStatus {
4258 if frame_handle.is_null() || out_tensor.is_null() {
4259 return RKRStatus::RKR_STATUS_NULL_POINTER;
4260 }
4261 unsafe { *out_tensor = std::ptr::null_mut() };
4262 let o = match resolve_dlpack_opts(opts) {
4263 Ok(o) => o,
4264 Err(st) => return st,
4265 };
4266 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4267 return RKRStatus::RKR_STATUS_NULL_POINTER;
4268 };
4269 if !frame.has_displacements() {
4270 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4271 }
4272 let n = frame.atom_data.len();
4273 let mut data = Vec::with_capacity(n * 3);
4274 for a in &frame.atom_data {
4275 let d = a.displacement.unwrap_or([0.0; 3]);
4276 data.extend_from_slice(&d);
4277 }
4278 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4279 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4280 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4281}
4282
4283#[unsafe(no_mangle)]
4285pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
4286 frame_handle: *const RKRConFrame,
4287 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4288) -> RKRStatus {
4289 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4290}
4291
4292#[unsafe(no_mangle)]
4293pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
4294 frame_handle: *const RKRConFrame,
4295 opts: *const RKRDlpackExportOptions,
4296 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4297) -> RKRStatus {
4298 if frame_handle.is_null() || out_tensor.is_null() {
4299 return RKRStatus::RKR_STATUS_NULL_POINTER;
4300 }
4301 unsafe { *out_tensor = std::ptr::null_mut() };
4302 let o = match resolve_dlpack_opts(opts) {
4303 Ok(o) => o,
4304 Err(st) => return st,
4305 };
4306 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4307 return RKRStatus::RKR_STATUS_NULL_POINTER;
4308 };
4309 if !frame.has_energies() {
4310 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4311 }
4312 let data: Vec<f64> = frame
4313 .atom_data
4314 .iter()
4315 .map(|a| a.energy.unwrap_or(0.0))
4316 .collect();
4317 let arr = ndarray::ArcArray1::from_vec(data);
4318 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
4319}
4320
4321pub struct RKRSelectionResult;
4325#[unsafe(no_mangle)]
4336pub unsafe extern "C" fn rkr_frame_select(
4337 frame_handle: *const RKRConFrame,
4338 selection: *const c_char,
4339 out_result: *mut *mut RKRSelectionResult,
4340) -> RKRStatus {
4341 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
4342 return RKRStatus::RKR_STATUS_NULL_POINTER;
4343 }
4344 let frame = unsafe { &*(frame_handle as *const ConFrame) };
4345 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
4346 Ok(s) => s,
4347 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
4348 };
4349 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
4350 Ok(result) => {
4351 let boxed = Box::new(result);
4352 unsafe {
4353 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
4354 }
4355 RKRStatus::RKR_STATUS_SUCCESS
4356 }
4357 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
4358 }
4359}
4360#[unsafe(no_mangle)]
4365pub unsafe extern "C" fn rkr_selection_result_match_count(
4366 result_handle: *const RKRSelectionResult,
4367) -> u64 {
4368 if result_handle.is_null() {
4369 return 0;
4370 }
4371 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4372 result.matches.len() as u64
4373}
4374#[unsafe(no_mangle)]
4379pub unsafe extern "C" fn rkr_selection_result_context_size(
4380 result_handle: *const RKRSelectionResult,
4381) -> u32 {
4382 if result_handle.is_null() {
4383 return 0;
4384 }
4385 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4386 result.context_size as u32
4387}
4388#[unsafe(no_mangle)]
4394pub unsafe extern "C" fn rkr_selection_result_match_at(
4395 result_handle: *const RKRSelectionResult,
4396 match_index: u64,
4397 out_atoms: *mut u64,
4398 out_size: *mut u32,
4399) -> RKRStatus {
4400 if result_handle.is_null() || out_atoms.is_null() {
4401 return RKRStatus::RKR_STATUS_NULL_POINTER;
4402 }
4403 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4404 let idx = match_index as usize;
4405 if idx >= result.matches.len() {
4406 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
4407 }
4408 let m = &result.matches[idx];
4409 unsafe {
4410 for i in 0..4 {
4411 *out_atoms.add(i) = if i < m.size {
4412 m.atoms[i] as u64
4413 } else {
4414 u64::MAX
4415 };
4416 }
4417 if !out_size.is_null() {
4418 *out_size = m.size as u32;
4419 }
4420 }
4421 RKRStatus::RKR_STATUS_SUCCESS
4422}
4423#[unsafe(no_mangle)]
4429pub unsafe extern "C" fn rkr_selection_result_primary_indices(
4430 result_handle: *const RKRSelectionResult,
4431 out_indices: *mut u64,
4432 capacity: u64,
4433 out_written: *mut u64,
4434) -> RKRStatus {
4435 if result_handle.is_null() {
4436 return RKRStatus::RKR_STATUS_NULL_POINTER;
4437 }
4438 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4439 let n = result.matches.len() as u64;
4440 if !out_written.is_null() {
4441 unsafe {
4442 *out_written = n;
4443 }
4444 }
4445 if n == 0 {
4446 return RKRStatus::RKR_STATUS_SUCCESS;
4447 }
4448 if out_indices.is_null() {
4449 return RKRStatus::RKR_STATUS_NULL_POINTER;
4450 }
4451 if capacity < n {
4452 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4453 }
4454 unsafe {
4455 for (i, m) in result.matches.iter().enumerate() {
4456 *out_indices.add(i) = m.atoms[0] as u64;
4457 }
4458 }
4459 RKRStatus::RKR_STATUS_SUCCESS
4460}
4461#[unsafe(no_mangle)]
4466pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
4467 if result_handle.is_null() {
4468 return;
4469 }
4470 unsafe {
4471 drop(Box::from_raw(
4472 result_handle as *mut crate::chemfiles_selection::SelectionResult,
4473 ));
4474 }
4475}
4476#[unsafe(no_mangle)]
4478pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
4479 #[cfg(feature = "chemfiles")]
4480 {
4481 1
4482 }
4483 #[cfg(not(feature = "chemfiles"))]
4484 {
4485 0
4486 }
4487}
4488
4489#[unsafe(no_mangle)]
4491pub extern "C" fn rkr_has_parallel_support() -> u8 {
4492 #[cfg(feature = "parallel")]
4493 {
4494 1
4495 }
4496 #[cfg(not(feature = "parallel"))]
4497 {
4498 0
4499 }
4500}
4501#[unsafe(no_mangle)]
4507pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
4508 if path_c.is_null() {
4509 return std::ptr::null_mut();
4510 }
4511 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4512 return std::ptr::null_mut();
4513 };
4514 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
4515 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4516 Err(_) => std::ptr::null_mut(),
4517 }
4518}
4519
4520#[unsafe(no_mangle)]
4526pub unsafe extern "C" fn rkr_read_chemfiles(
4527 path_c: *const c_char,
4528 num_frames: *mut usize,
4529) -> *mut *mut RKRConFrame {
4530 if path_c.is_null() || num_frames.is_null() {
4531 return std::ptr::null_mut();
4532 }
4533 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4534 return std::ptr::null_mut();
4535 };
4536 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
4537 Ok(frames) => pack_frame_handles(frames, num_frames),
4538 Err(_) => std::ptr::null_mut(),
4539 }
4540}
4541
4542#[unsafe(no_mangle)]
4548pub unsafe extern "C" fn rkr_read_chemfiles_nth(
4549 path_c: *const c_char,
4550 index: usize,
4551) -> *mut RKRConFrame {
4552 if path_c.is_null() {
4553 return std::ptr::null_mut();
4554 }
4555 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4556 return std::ptr::null_mut();
4557 };
4558 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
4559 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4560 Err(_) => std::ptr::null_mut(),
4561 }
4562}
4563
4564#[unsafe(no_mangle)]
4570pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
4571 if path_c.is_null() {
4572 return usize::MAX;
4573 }
4574 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4575 return usize::MAX;
4576 };
4577 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
4578}
4579
4580#[unsafe(no_mangle)]
4588pub unsafe extern "C" fn rkr_read_chemfiles_range(
4589 path_c: *const c_char,
4590 start: usize,
4591 step: usize,
4592 stop: usize,
4593 topology_c: *const c_char,
4594 guess_bonds: u8,
4595 num_frames: *mut usize,
4596) -> *mut *mut RKRConFrame {
4597 if path_c.is_null() || num_frames.is_null() {
4598 return std::ptr::null_mut();
4599 }
4600 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4601 return std::ptr::null_mut();
4602 };
4603 let topology = if topology_c.is_null() {
4604 None
4605 } else {
4606 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
4607 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
4608 _ => return std::ptr::null_mut(),
4609 }
4610 };
4611 let opts = crate::chemfiles_import::ChemfilesReadOpts {
4612 start,
4613 step,
4614 stop: if stop == usize::MAX { None } else { Some(stop) },
4615 format: None,
4616 topology,
4617 topology_format: None,
4618 guess_bonds: guess_bonds != 0,
4619 };
4620 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
4621 Ok(frames) => pack_frame_handles(frames, num_frames),
4622 Err(_) => std::ptr::null_mut(),
4623 }
4624}
4625#[unsafe(no_mangle)]
4632pub unsafe extern "C" fn rkr_read_chemfiles_memory(
4633 data_c: *const c_char,
4634 format_c: *const c_char,
4635 num_frames: *mut usize,
4636) -> *mut *mut RKRConFrame {
4637 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
4638 return std::ptr::null_mut();
4639 }
4640 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
4641 return std::ptr::null_mut();
4642 };
4643 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
4644 return std::ptr::null_mut();
4645 };
4646 match crate::chemfiles_import::con_frames_from_memory(data, format) {
4647 Ok(frames) => {
4648 let n = frames.len();
4649 unsafe { *num_frames = n };
4650 let mut ptrs: Vec<*mut RKRConFrame> = frames
4651 .into_iter()
4652 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
4653 .collect();
4654 let p = ptrs.as_mut_ptr();
4655 std::mem::forget(ptrs);
4656 p
4657 }
4658 Err(_) => std::ptr::null_mut(),
4659 }
4660}
4661#[unsafe(no_mangle)]
4666pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
4667 if tensor.is_null() {
4668 return;
4669 }
4670 unsafe {
4671 let t = &mut *tensor;
4672 if let Some(del) = t.deleter {
4673 del(tensor);
4674 }
4675 }
4676}
4677
4678#[unsafe(no_mangle)]
4688pub unsafe extern "C" fn rkr_pack_rcso(
4689 frame_handle: *const RKRConFrame,
4690 buf: *mut u8,
4691 buflen: usize,
4692 out_len: *mut usize,
4693) -> RKRStatus {
4694 if out_len.is_null() {
4695 return RKRStatus::RKR_STATUS_NULL_POINTER;
4696 }
4697 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
4698 Some(f) => f,
4699 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
4700 };
4701 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
4702 Ok(b) => b,
4703 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4704 };
4705 unsafe { *out_len = bytes.len() };
4706 if buf.is_null() {
4707 return RKRStatus::RKR_STATUS_SUCCESS;
4708 }
4709 if buflen < bytes.len() {
4710 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4711 }
4712 unsafe {
4713 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
4714 }
4715 RKRStatus::RKR_STATUS_SUCCESS
4716}
4717
4718#[unsafe(no_mangle)]
4723pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
4724 buf: *const u8,
4725 buflen: usize,
4726 out_natoms: *mut u32,
4727) -> RKRStatus {
4728 if buf.is_null() || out_natoms.is_null() {
4729 return RKRStatus::RKR_STATUS_NULL_POINTER;
4730 }
4731 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4732 match crate::rcso::Rcso::decode(bytes) {
4733 Ok(s) => {
4734 unsafe { *out_natoms = s.natoms };
4735 RKRStatus::RKR_STATUS_SUCCESS
4736 }
4737 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4738 }
4739}
4740
4741#[unsafe(no_mangle)]
4746pub unsafe extern "C" fn rkr_unpack_rcso_positions(
4747 buf: *const u8,
4748 buflen: usize,
4749 dest: *mut f64,
4750 dest_natoms: u32,
4751) -> RKRStatus {
4752 if buf.is_null() || dest.is_null() {
4753 return RKRStatus::RKR_STATUS_NULL_POINTER;
4754 }
4755 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4756 let soa = match crate::rcso::Rcso::decode(bytes) {
4757 Ok(s) => s,
4758 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4759 };
4760 if soa.natoms != dest_natoms {
4761 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4762 }
4763 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
4764 for (i, p) in soa.positions.iter().enumerate() {
4765 out[i * 3] = p[0];
4766 out[i * 3 + 1] = p[1];
4767 out[i * 3 + 2] = p[2];
4768 }
4769 RKRStatus::RKR_STATUS_SUCCESS
4770}
4771
4772#[unsafe(no_mangle)]
4778pub unsafe extern "C" fn rkr_unpack_rcso_forces(
4779 buf: *const u8,
4780 buflen: usize,
4781 dest: *mut f64,
4782 dest_natoms: u32,
4783) -> RKRStatus {
4784 if buf.is_null() || dest.is_null() {
4785 return RKRStatus::RKR_STATUS_NULL_POINTER;
4786 }
4787 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4788 let soa = match crate::rcso::Rcso::decode(bytes) {
4789 Ok(s) => s,
4790 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4791 };
4792 let Some(forces) = soa.forces else {
4793 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4794 };
4795 if soa.natoms != dest_natoms {
4796 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4797 }
4798 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
4799 for (i, f) in forces.iter().enumerate() {
4800 out[i * 3] = f[0];
4801 out[i * 3 + 1] = f[1];
4802 out[i * 3 + 2] = f[2];
4803 }
4804 RKRStatus::RKR_STATUS_SUCCESS
4805}
4806
4807#[cfg(test)]
4808mod tests {
4809 use super::*;
4810 use std::ffi::{CStr, CString};
4811 #[test]
4812 fn frame_copy_positions_without_cframe() {
4813 let handle = test_frame_handle();
4814 let n = unsafe { rkr_frame_atom_count(handle) };
4815 assert_eq!(n, 1);
4816 let mut buf = vec![0.0f64; n * 3];
4817 assert_eq!(
4818 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
4819 RKRStatus::RKR_STATUS_SUCCESS
4820 );
4821 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4822 assert_eq!(
4823 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
4824 RKRStatus::RKR_STATUS_SUCCESS
4825 );
4826 assert!(!tensor.is_null());
4827 unsafe { rkr_dlpack_delete(tensor) };
4828 assert_eq!(
4829 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
4830 RKRStatus::RKR_STATUS_SECTION_ABSENT
4831 );
4832 unsafe { free_rkr_frame(handle) };
4833 }
4834 #[test]
4835 fn read_all_frames_c_abi_tiny() {
4836 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4837 let mut n: usize = 0;
4838 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
4839 assert!(!arr.is_null() && n >= 1);
4840 let first = unsafe { *arr };
4841 let nat = unsafe { rkr_frame_atom_count(first) };
4842 let mut buf = vec![0.0f64; nat * 3];
4843 assert_eq!(
4844 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
4845 RKRStatus::RKR_STATUS_SUCCESS
4846 );
4847 unsafe { free_rkr_frame_array(arr, n) };
4848 }
4849
4850 #[test]
4851 fn read_all_frames_n_threads_matches_auto() {
4852 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4853 let mut n_auto: usize = 0;
4854 let mut n_one: usize = 0;
4855 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
4856 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
4857 assert!(!auto.is_null() && !one.is_null());
4858 assert_eq!(n_auto, n_one);
4859 assert_eq!(n_auto, 1);
4860 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
4861 let nat_one = unsafe { rkr_frame_atom_count(*one) };
4862 assert_eq!(nat_auto, nat_one);
4863 unsafe { free_rkr_frame_array(auto, n_auto) };
4864 unsafe { free_rkr_frame_array(one, n_one) };
4865 }
4866
4867 #[test]
4868 fn free_rkr_frame_ptr_array_keeps_frames() {
4869 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4870 let mut n: usize = 0;
4871 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
4872 assert!(!arr.is_null() && n >= 1);
4873 let first = unsafe { *arr };
4874 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
4876 unsafe { free_rkr_frame_ptr_array(arr, n) };
4877 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
4878 unsafe { free_rkr_frame(first) };
4879 for h in rest {
4880 if !h.is_null() {
4881 unsafe { free_rkr_frame(h) };
4882 }
4883 }
4884 }
4885 fn test_frame_handle() -> *mut RKRConFrame {
4886 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
4887 builder
4888 .prebox_header("Generated by test")
4889 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
4890 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
4891 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
4892 }
4893 #[test]
4894 fn header_line_rejects_null_buffer() {
4895 let frame = test_frame_handle();
4896 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
4897 unsafe { free_rkr_frame(frame) };
4898 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
4899 }
4900 #[test]
4901 fn header_line_rejects_empty_buffer() {
4902 let frame = test_frame_handle();
4903 let mut buffer = [0 as c_char; 1];
4904 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
4905 unsafe { free_rkr_frame(frame) };
4906 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
4907 }
4908 #[test]
4909 fn header_line_truncates_and_terminates_buffer() {
4910 let frame = test_frame_handle();
4911 let mut buffer = [0 as c_char; 10];
4912 let status =
4913 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
4914 unsafe { free_rkr_frame(frame) };
4915 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4916 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
4917 assert_eq!(copied.to_str().unwrap(), "Generated");
4918 }
4919
4920 #[test]
4921 fn pack_rcso_size_query_then_dest() {
4922 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
4923 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
4924 assert!(!frame.is_null());
4925 let mut need = 0usize;
4926 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
4927 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4928 assert!(need >= 24);
4929 let mut buf = vec![0u8; need];
4930 let mut wrote = 0usize;
4931 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
4932 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4933 assert_eq!(wrote, need);
4934 assert_eq!(&buf[0..4], b"RCSO");
4935 let mut natoms = 0u32;
4936 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
4937 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4938 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
4939 let mut xyz = vec![0.0f64; natoms as usize * 3];
4940 let st =
4941 unsafe { rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms) };
4942 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4943 let mut frc = vec![0.0f64; natoms as usize * 3];
4944 let st =
4945 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
4946 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4947 unsafe { free_rkr_frame(frame) };
4948 }
4949
4950 fn test_builder_handle() -> *mut RKRConFrameBuilder {
4951 let cell = [10.0, 11.0, 12.0];
4952 let angles = [90.0, 91.0, 92.0];
4953 unsafe {
4954 rkr_frame_new(
4955 cell.as_ptr(),
4956 angles.as_ptr(),
4957 ptr::null(),
4958 ptr::null(),
4959 ptr::null(),
4960 ptr::null(),
4961 )
4962 }
4963 }
4964 fn c_string(s: &str) -> CString {
4965 CString::new(s).unwrap()
4966 }
4967 unsafe fn assert_single_atom(
4968 frame: *mut RKRConFrame,
4969 fixed: [bool; 3],
4970 velocity: Option<[f64; 3]>,
4971 forces: Option<[f64; 3]>,
4972 ) {
4973 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
4974 assert!(!c_frame.is_null());
4975 let c_frame_ref = unsafe { &*c_frame };
4976 assert_eq!(c_frame_ref.num_atoms, 1);
4977 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
4978 assert_eq!(c_frame_ref.has_forces, forces.is_some());
4979 let atom = unsafe { &*c_frame_ref.atoms };
4980 assert_eq!(atom.fixed_x, fixed[0]);
4981 assert_eq!(atom.fixed_y, fixed[1]);
4982 assert_eq!(atom.fixed_z, fixed[2]);
4983 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
4984 assert_eq!(atom.has_velocity, velocity.is_some());
4985 assert_eq!(atom.has_forces, forces.is_some());
4986 if let Some([vx, vy, vz]) = velocity {
4987 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
4988 }
4989 if let Some([fx, fy, fz]) = forces {
4990 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
4991 }
4992 unsafe { free_c_frame(c_frame) };
4993 unsafe { free_rkr_frame(frame) };
4994 }
4995 #[test]
4996 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
4997 let builder = test_builder_handle();
4998 let symbol = c_string("Cu");
4999 let status = unsafe {
5000 rkr_frame_add_atom_with_fixed_mask(
5001 builder,
5002 symbol.as_ptr(),
5003 1.0,
5004 2.0,
5005 3.0,
5006 true,
5007 false,
5008 true,
5009 7,
5010 63.546,
5011 )
5012 };
5013 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5014 let frame = unsafe { rkr_frame_builder_build(builder) };
5015 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
5016 }
5017 #[test]
5018 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
5019 let builder = test_builder_handle();
5020 let symbol = c_string("H");
5021 let status = unsafe {
5022 rkr_frame_add_atom_with_velocity_fixed_mask(
5023 builder,
5024 symbol.as_ptr(),
5025 1.0,
5026 2.0,
5027 3.0,
5028 false,
5029 true,
5030 false,
5031 9,
5032 1.008,
5033 0.1,
5034 0.2,
5035 0.3,
5036 )
5037 };
5038 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5039 let frame = unsafe { rkr_frame_builder_build(builder) };
5040 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
5041 }
5042 #[test]
5043 fn builder_preserves_fixed_mask_for_atom_with_forces() {
5044 let builder = test_builder_handle();
5045 let symbol = c_string("O");
5046 let status = unsafe {
5047 rkr_frame_add_atom_with_forces_fixed_mask(
5048 builder,
5049 symbol.as_ptr(),
5050 1.0,
5051 2.0,
5052 3.0,
5053 true,
5054 true,
5055 false,
5056 11,
5057 15.999,
5058 -0.1,
5059 -0.2,
5060 -0.3,
5061 )
5062 };
5063 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5064 let frame = unsafe { rkr_frame_builder_build(builder) };
5065 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
5066 }
5067 #[test]
5068 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
5069 let builder = test_builder_handle();
5070 let symbol = c_string("N");
5071 let status = unsafe {
5072 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5073 builder,
5074 symbol.as_ptr(),
5075 1.0,
5076 2.0,
5077 3.0,
5078 false,
5079 true,
5080 true,
5081 13,
5082 14.007,
5083 0.4,
5084 0.5,
5085 0.6,
5086 -0.4,
5087 -0.5,
5088 -0.6,
5089 )
5090 };
5091 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5092 let frame = unsafe { rkr_frame_builder_build(builder) };
5093 unsafe {
5094 assert_single_atom(
5095 frame,
5096 [false, true, true],
5097 Some([0.4, 0.5, 0.6]),
5098 Some([-0.4, -0.5, -0.6]),
5099 )
5100 };
5101 }
5102 #[test]
5103 fn builder_bool_fixed_functions_set_all_axes_together() {
5104 let builder = test_builder_handle();
5105 let cu = c_string("Cu");
5106 let h = c_string("H");
5107 let atom_status =
5108 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
5109 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
5110 let velocity_status = unsafe {
5111 rkr_frame_add_atom_with_velocity(
5112 builder,
5113 h.as_ptr(),
5114 4.0,
5115 5.0,
5116 6.0,
5117 false,
5118 2,
5119 1.008,
5120 0.7,
5121 0.8,
5122 0.9,
5123 )
5124 };
5125 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
5126 let frame = unsafe { rkr_frame_builder_build(builder) };
5127 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
5128 assert!(!c_frame.is_null());
5129 let c_frame_ref = unsafe { &*c_frame };
5130 assert_eq!(c_frame_ref.num_atoms, 2);
5131 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
5132 assert_eq!(
5133 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
5134 [true, true, true]
5135 );
5136 assert_eq!(
5137 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
5138 [false, false, false]
5139 );
5140 unsafe { free_c_frame(c_frame) };
5141 unsafe { free_rkr_frame(frame) };
5142 }
5143 #[test]
5144 fn status_message_returns_static_strings_for_all_status_values() {
5145 let cases = [
5146 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
5147 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
5148 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
5149 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
5150 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
5151 (
5152 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
5153 "index out of bounds",
5154 ),
5155 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
5156 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
5157 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
5158 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
5159 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
5160 (
5161 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
5162 "feature disabled in this build",
5163 ),
5164 ];
5165 for (status, expected) in cases {
5166 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
5167 assert_eq!(message.to_str().unwrap(), expected);
5168 }
5169 }
5170 unsafe fn assert_dlpack_cpu_float(
5174 t: *mut RKRDLManagedTensorVersioned,
5175 expect_ndim: i32,
5176 expect_shape: &[i64],
5177 expect_bits: u8,
5178 ) {
5179 assert!(!t.is_null());
5180 let dl = unsafe { &(*t).dl_tensor };
5181 assert_eq!(dl.ndim, expect_ndim);
5182 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
5183 assert_eq!(shape, expect_shape);
5184 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5185 assert_eq!(dl.dtype.bits, expect_bits);
5186 assert_eq!(dl.dtype.lanes, 1);
5187 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5188 assert!(!dl.data.is_null());
5189 }
5190
5191 #[test]
5192 fn frame_optional_section_dlpack_present_and_absent() {
5193 let handle = test_frame_handle();
5195 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5196 assert_eq!(
5197 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
5198 RKRStatus::RKR_STATUS_SECTION_ABSENT
5199 );
5200 assert!(t.is_null());
5201 assert_eq!(
5202 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
5203 RKRStatus::RKR_STATUS_SECTION_ABSENT
5204 );
5205 assert_eq!(
5206 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
5207 RKRStatus::RKR_STATUS_SECTION_ABSENT
5208 );
5209 unsafe { free_rkr_frame(handle) };
5210
5211 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
5213 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
5214 assert!(!fr.is_null());
5215 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
5216 assert!(n > 0);
5217 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5218 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
5219 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5220 unsafe {
5221 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
5222 rkr_dlpack_delete(vel);
5223 free_rkr_frame(fr);
5224 }
5225
5226 let cell = [10.0f64; 3];
5228 let ang = [90.0f64; 3];
5229 let b = unsafe {
5230 rkr_frame_new(
5231 cell.as_ptr(),
5232 ang.as_ptr(),
5233 std::ptr::null(),
5234 std::ptr::null(),
5235 std::ptr::null(),
5236 std::ptr::null(),
5237 )
5238 };
5239 assert!(!b.is_null());
5240 let sym = CString::new("H").unwrap();
5241 unsafe {
5242 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
5243 b,
5244 sym.as_ptr(),
5245 0.0,
5246 0.0,
5247 0.0,
5248 false,
5249 false,
5250 false,
5251 0,
5252 1.0,
5253 0.1,
5254 0.0,
5255 0.0,
5256 0.0,
5257 0.0,
5258 -1.0,
5259 );
5260 rkr_frame_builder_set_last_energy(b, -0.5);
5261 }
5262 let built = unsafe { rkr_frame_builder_build(b) };
5263 assert!(!built.is_null());
5264 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
5265 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5266 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5267 assert_eq!(
5268 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
5269 RKRStatus::RKR_STATUS_SUCCESS
5270 );
5271 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
5272 assert_eq!(
5273 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
5274 RKRStatus::RKR_STATUS_SUCCESS
5275 );
5276 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
5277 let opts32 = RKRDlpackExportOptions {
5279 dtype: RKRDLDataType {
5280 code: rkr_dl_type_code::RKR_DL_FLOAT,
5281 bits: 32,
5282 lanes: 1,
5283 },
5284 device: RKRDLDevice {
5285 device_type: rkr_dl_device_type::RKR_DL_CPU,
5286 device_id: 0,
5287 },
5288 };
5289 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5290 assert_eq!(
5291 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
5292 RKRStatus::RKR_STATUS_SUCCESS
5293 );
5294 unsafe {
5295 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
5296 rkr_dlpack_delete(pos32);
5297 }
5298 let cuda_dev = RKRDlpackExportOptions {
5301 dtype: RKRDLDataType {
5302 code: rkr_dl_type_code::RKR_DL_FLOAT,
5303 bits: 64,
5304 lanes: 1,
5305 },
5306 device: RKRDLDevice {
5307 device_type: rkr_dl_device_type::RKR_DL_CUDA,
5308 device_id: 0,
5309 },
5310 };
5311 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5312 #[cfg(not(feature = "cuda"))]
5313 assert_eq!(
5314 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5315 RKRStatus::RKR_STATUS_FEATURE_DISABLED
5316 );
5317 #[cfg(feature = "cuda")]
5318 {
5319 assert_eq!(
5320 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5321 RKRStatus::RKR_STATUS_SUCCESS
5322 );
5323 assert!(!junk.is_null());
5324 let dl = unsafe { &(*junk).dl_tensor };
5325 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
5326 assert!(!dl.data.is_null());
5327 unsafe { rkr_dlpack_delete(junk) };
5328 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5330 assert_eq!(
5331 unsafe {
5332 rkr_frame_positions_as_dlpack(
5333 built,
5334 rkr_dl_device_type::RKR_DL_CUDA,
5335 0,
5336 0,
5337 1,
5338 0,
5339 &mut as_cuda,
5340 )
5341 },
5342 RKRStatus::RKR_STATUS_SUCCESS
5343 );
5344 assert!(!as_cuda.is_null());
5345 unsafe {
5346 assert_eq!(
5347 (*as_cuda).dl_tensor.device.device_type,
5348 dlpk::sys::DLDeviceType::kDLCUDA
5349 );
5350 rkr_dlpack_delete(as_cuda);
5351 }
5352 }
5353 let bad_bits = RKRDlpackExportOptions {
5355 dtype: RKRDLDataType {
5356 code: rkr_dl_type_code::RKR_DL_FLOAT,
5357 bits: 16,
5358 lanes: 1,
5359 },
5360 device: RKRDLDevice {
5361 device_type: rkr_dl_device_type::RKR_DL_CPU,
5362 device_id: 0,
5363 },
5364 };
5365 assert_eq!(
5366 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
5367 RKRStatus::RKR_STATUS_VALIDATION_ERROR
5368 );
5369 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5371 assert_eq!(
5372 unsafe {
5373 rkr_frame_positions_as_dlpack(
5374 built,
5375 rkr_dl_device_type::RKR_DL_CPU,
5376 0,
5377 0,
5378 1,
5379 0,
5380 &mut as_t,
5381 )
5382 },
5383 RKRStatus::RKR_STATUS_SUCCESS
5384 );
5385 unsafe {
5386 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
5387 }
5388 {
5390 let fr = unsafe { &mut *(built as *mut ConFrame) };
5391 for a in fr.atom_data.iter_mut() {
5392 a.x = -99.0;
5393 a.y = -99.0;
5394 a.z = -99.0;
5395 }
5396 }
5397 assert_eq!(
5398 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
5399 RKRStatus::RKR_STATUS_SUCCESS
5400 );
5401 {
5402 let fr = unsafe { &*(built as *const ConFrame) };
5403 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
5404 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
5405 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
5406 }
5407 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5409 assert_eq!(
5410 unsafe {
5411 rkr_frame_positions_as_dlpack(
5412 built,
5413 rkr_dl_device_type::RKR_DL_CPU,
5414 0,
5415 0,
5416 1,
5417 0,
5418 &mut again,
5419 )
5420 },
5421 RKRStatus::RKR_STATUS_SUCCESS
5422 );
5423 unsafe {
5424 let dl = &(*again).dl_tensor;
5425 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
5426 assert!((data[0] - 0.0).abs() < 1e-12);
5427 assert!((data[1] - 0.0).abs() < 1e-12);
5428 assert!((data[2] - 0.0).abs() < 1e-12);
5429 rkr_dlpack_delete(again);
5430 rkr_dlpack_delete(as_t);
5431 rkr_dlpack_delete(frc);
5432 rkr_dlpack_delete(eng);
5433 free_rkr_frame(built);
5434 }
5435 }
5436
5437 #[test]
5438 fn ffi_positions_dlpack_round_trip() {
5439 let handle = test_builder_handle();
5440 let sym = c_string("Cu");
5441 unsafe {
5442 rkr_frame_add_atom_full(
5443 handle,
5444 sym.as_ptr(),
5445 1.0,
5446 2.0,
5447 3.0,
5448 false,
5449 false,
5450 false,
5451 7,
5452 63.5,
5453 ptr::null(),
5454 ptr::null(),
5455 )
5456 };
5457 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5458 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
5459 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5460 assert!(!t.is_null());
5461 let dl = unsafe { &(*t).dl_tensor };
5463 assert_eq!(dl.ndim, 2);
5464 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
5465 assert_eq!(shape, &[1, 3]);
5466 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5467 assert_eq!(dl.dtype.bits, 64);
5468 assert_eq!(dl.dtype.lanes, 1);
5469 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5470 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
5471 assert_eq!(data, &[1.0, 2.0, 3.0]);
5472 let deleter = unsafe { (*t).deleter };
5474 if let Some(del) = deleter {
5475 unsafe { del(t) };
5476 }
5477 unsafe { free_rkr_frame_builder(handle) };
5478 }
5479 #[test]
5480 fn ffi_velocities_dlpack_section_absent() {
5481 let handle = test_builder_handle();
5482 let sym = c_string("Cu");
5483 unsafe {
5484 rkr_frame_add_atom_full(
5485 handle,
5486 sym.as_ptr(),
5487 0.0,
5488 0.0,
5489 0.0,
5490 false,
5491 false,
5492 false,
5493 0,
5494 63.5,
5495 ptr::null(),
5496 ptr::null(),
5497 )
5498 };
5499 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5500 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
5501 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
5502 assert!(t.is_null());
5503 unsafe { free_rkr_frame_builder(handle) };
5504 }
5505 #[test]
5506 fn ffi_dlpack_null_handle_rejects() {
5507 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5508 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
5509 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5510 assert!(t.is_null());
5511 }
5512 #[cfg(feature = "chemfiles")]
5513 #[test]
5514 fn rkr_frame_select_finds_oxygen() {
5515 use crate::types::ConFrameBuilder;
5516 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5517 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
5518 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
5519 let frame = b.build().unwrap();
5520 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5521 let sel = CString::new("name O").unwrap();
5522 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5523 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
5524 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5525 assert!(!out.is_null());
5526 let n = unsafe { rkr_selection_result_match_count(out) };
5527 assert_eq!(n, 1);
5528 let mut atoms = [u64::MAX; 4];
5529 let mut size = 0u32;
5530 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
5531 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
5532 assert_eq!(size, 1);
5533 assert_eq!(atoms[0], 0);
5534 unsafe {
5535 rkr_selection_result_free(out);
5536 free_rkr_frame(frame_ptr);
5537 }
5538 }
5539 #[cfg(feature = "chemfiles")]
5541 #[test]
5542 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
5543 use crate::types::{Bond, ConFrameBuilder};
5544 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5546 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
5547 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
5548 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
5549 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
5550 let mut frame = b.build().unwrap();
5551 let id_to = |id: u64| {
5552 frame
5553 .atom_data
5554 .iter()
5555 .position(|a| a.atom_id == id)
5556 .unwrap() as u32
5557 };
5558 frame.header.set_bonds(&[
5559 Bond::new(id_to(0), id_to(1)),
5560 Bond::new(id_to(1), id_to(2)),
5561 Bond::new(id_to(2), id_to(3)),
5562 ]);
5563 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5564 let run = |sel: &str| -> (u64, u32) {
5565 let csel = CString::new(sel).unwrap();
5566 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5567 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
5568 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
5569 let n = unsafe { rkr_selection_result_match_count(out) };
5570 let ctx = unsafe { rkr_selection_result_context_size(out) };
5571 unsafe { rkr_selection_result_free(out) };
5572 (n, ctx)
5573 };
5574 assert_eq!(run("bonds: all"), (3, 2));
5575 assert_eq!(run("angles: all"), (2, 3));
5576 assert_eq!(run("dihedrals: all"), (1, 4));
5577 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
5578 assert_eq!(
5579 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
5580 run("bonds: type(#1) H and name(#2) O").0
5581 );
5582 unsafe { free_rkr_frame(frame_ptr) };
5583 }
5584 #[cfg(feature = "metatensor")]
5585 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
5586 assert!(!block.is_null());
5587 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
5588 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
5589 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
5590 let shape_fn = array
5591 .shape
5592 .expect("mts_array_t.shape from metatensor C API");
5593 let mut shape_ptr: *const usize = std::ptr::null();
5594 let mut shape_count: usize = 0;
5595 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
5596 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
5597 assert_eq!(shape_count, 2);
5598 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
5599 assert_eq!(shape[0], n);
5600 assert_eq!(shape[1], props);
5601 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
5602 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
5603 assert!(!samples.is_null() && !prop_lab.is_null());
5604 }
5605 #[cfg(feature = "metatensor")]
5606 #[test]
5607 fn metatensor_positions_via_c_abi() {
5608 let handle = test_frame_handle();
5609 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5610 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
5611 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5612 assert_mts_block_shape(out, 1, 3);
5613 unsafe { rkr_mts_block_free(out) };
5614 for (name, export) in [
5615 (
5616 "velocities",
5617 rkr_frame_metatensor_velocities_block
5618 as unsafe extern "C" fn(
5619 *const RKRConFrame,
5620 *mut *mut metatensor::c_api::mts_block_t,
5621 ) -> RKRStatus,
5622 ),
5623 (
5624 "forces",
5625 rkr_frame_metatensor_forces_block
5626 as unsafe extern "C" fn(
5627 *const RKRConFrame,
5628 *mut *mut metatensor::c_api::mts_block_t,
5629 ) -> RKRStatus,
5630 ),
5631 (
5632 "atom_energies",
5633 rkr_frame_metatensor_atom_energies_block
5634 as unsafe extern "C" fn(
5635 *const RKRConFrame,
5636 *mut *mut metatensor::c_api::mts_block_t,
5637 ) -> RKRStatus,
5638 ),
5639 ] {
5640 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5641 let st_abs = unsafe { export(handle, &mut o) };
5642 assert_eq!(
5643 st_abs,
5644 RKRStatus::RKR_STATUS_SECTION_ABSENT,
5645 "{name} must be SECTION_ABSENT on minimal test frame"
5646 );
5647 assert!(o.is_null());
5648 }
5649 unsafe { free_rkr_frame(handle) };
5650 }
5651
5652 #[cfg(feature = "metatensor")]
5653 #[test]
5654 fn metatensor_optional_sections_via_c_abi() {
5655 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5657 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
5658 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
5659 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
5660 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
5661 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
5662 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
5663 builder.set_atom_energy(0, -0.5).unwrap();
5664 builder.set_atom_energy(1, -1.0).unwrap();
5665 let frame = builder.build().unwrap();
5666 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5667 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5668 assert_eq!(
5669 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
5670 RKRStatus::RKR_STATUS_SUCCESS
5671 );
5672 assert_mts_block_shape(pos, 2, 3);
5673 unsafe { rkr_mts_block_free(pos) };
5674 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5675 assert_eq!(
5676 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
5677 RKRStatus::RKR_STATUS_SUCCESS
5678 );
5679 assert_mts_block_shape(vel, 2, 3);
5680 unsafe { rkr_mts_block_free(vel) };
5681 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5682 assert_eq!(
5683 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
5684 RKRStatus::RKR_STATUS_SUCCESS
5685 );
5686 assert_mts_block_shape(frc, 2, 3);
5687 unsafe { rkr_mts_block_free(frc) };
5688 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5689 assert_eq!(
5690 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
5691 RKRStatus::RKR_STATUS_SUCCESS
5692 );
5693 assert_mts_block_shape(eng, 2, 1);
5694 unsafe { rkr_mts_block_free(eng) };
5695 unsafe { free_rkr_frame(handle) };
5696 }
5697
5698 #[test]
5699 fn string_iterator_yields_frames_from_buffer() {
5700 let text =
5701 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
5702 let c_text = CString::new(text.as_str()).unwrap();
5703 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
5704 assert!(!it.is_null());
5705 let mut n = 0usize;
5706 loop {
5707 let fr = unsafe { con_frame_iterator_next(it) };
5708 if fr.is_null() {
5709 break;
5710 }
5711 n += 1;
5712 unsafe { free_rkr_frame(fr) };
5713 }
5714 unsafe { free_con_frame_iterator(it) };
5715 assert!(n >= 1, "string iterator should yield >=1 frame");
5716
5717 let bytes = text.as_bytes();
5718 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
5719 assert!(!it2.is_null());
5720 let fr2 = unsafe { con_frame_iterator_next(it2) };
5721 assert!(!fr2.is_null());
5722 unsafe {
5723 free_rkr_frame(fr2);
5724 free_con_frame_iterator(it2);
5725 }
5726 }
5727
5728 #[test]
5729 fn file_iterator_reads_gzip_when_present() {
5730 use flate2::Compression;
5731 use flate2::write::GzEncoder;
5732 use std::io::Write;
5733 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
5734 let dir = tempfile::tempdir().expect("tempdir");
5735 let gz_path = dir.path().join("tiny_cuh2.con.gz");
5736 {
5737 let f = std::fs::File::create(&gz_path).unwrap();
5738 let mut enc = GzEncoder::new(f, Compression::default());
5739 enc.write_all(&plain).unwrap();
5740 enc.finish().unwrap();
5741 }
5742 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
5743 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
5744 assert!(
5745 !it.is_null(),
5746 "path iterator must decompress .con.gz transparently"
5747 );
5748 let fr = unsafe { con_frame_iterator_next(it) };
5749 assert!(!fr.is_null());
5750 let n = unsafe { rkr_frame_atom_count(fr) };
5751 assert!(n > 0);
5752 unsafe {
5753 free_rkr_frame(fr);
5754 free_con_frame_iterator(it);
5755 }
5756 }
5757}