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)]
1319pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1320 builder_handle: *mut RKRConFrameBuilder,
1321 energy: f64,
1322) -> RKRStatus {
1323 if builder_handle.is_null() {
1324 return RKRStatus::RKR_STATUS_NULL_POINTER;
1325 }
1326 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1327 builder.with_energy(energy);
1328 RKRStatus::RKR_STATUS_SUCCESS
1329}
1330fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1344 use crate::error::ParseError;
1345 match e {
1346 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1347 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1348 }
1349 ParseError::MassMismatch { .. } | ParseError::ValidationError(_) => {
1350 RKRStatus::RKR_STATUS_VALIDATION_ERROR
1351 }
1352 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1353 }
1354}
1355#[unsafe(no_mangle)]
1362pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1363 builder_handle: *const RKRConFrameBuilder,
1364) -> usize {
1365 if builder_handle.is_null() {
1366 return 0;
1367 }
1368 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1369 builder.atom_count()
1370}
1371#[unsafe(no_mangle)]
1375pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1376 builder_handle: *mut RKRConFrameBuilder,
1377 index: usize,
1378 x: f64,
1379 y: f64,
1380 z: f64,
1381) -> RKRStatus {
1382 if builder_handle.is_null() {
1383 return RKRStatus::RKR_STATUS_NULL_POINTER;
1384 }
1385 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1386 match builder.set_atom_position(index, x, y, z) {
1387 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1388 Err(e) => map_builder_err(e),
1389 }
1390}
1391#[unsafe(no_mangle)]
1395pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1396 builder_handle: *mut RKRConFrameBuilder,
1397 index: usize,
1398 velocity: *const f64,
1399) -> RKRStatus {
1400 if builder_handle.is_null() || velocity.is_null() {
1401 return RKRStatus::RKR_STATUS_NULL_POINTER;
1402 }
1403 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1404 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1405 match builder.set_atom_velocity(index, v) {
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_force(
1415 builder_handle: *mut RKRConFrameBuilder,
1416 index: usize,
1417 force: *const f64,
1418) -> RKRStatus {
1419 if builder_handle.is_null() || force.is_null() {
1420 return RKRStatus::RKR_STATUS_NULL_POINTER;
1421 }
1422 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1423 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1424 match builder.set_atom_force(index, f) {
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_energy(
1434 builder_handle: *mut RKRConFrameBuilder,
1435 index: usize,
1436 energy: f64,
1437) -> RKRStatus {
1438 if builder_handle.is_null() {
1439 return RKRStatus::RKR_STATUS_NULL_POINTER;
1440 }
1441 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1442 match builder.set_atom_energy(index, energy) {
1443 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1444 Err(e) => map_builder_err(e),
1445 }
1446}
1447#[unsafe(no_mangle)]
1451pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1452 builder_handle: *mut RKRConFrameBuilder,
1453 index: usize,
1454 fixed_x: bool,
1455 fixed_y: bool,
1456 fixed_z: bool,
1457) -> RKRStatus {
1458 if builder_handle.is_null() {
1459 return RKRStatus::RKR_STATUS_NULL_POINTER;
1460 }
1461 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1462 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1463 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1464 Err(e) => map_builder_err(e),
1465 }
1466}
1467#[unsafe(no_mangle)]
1471pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1472 builder_handle: *mut RKRConFrameBuilder,
1473 index: usize,
1474 mass: f64,
1475) -> RKRStatus {
1476 if builder_handle.is_null() {
1477 return RKRStatus::RKR_STATUS_NULL_POINTER;
1478 }
1479 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1480 match builder.set_atom_mass(index, mass) {
1481 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1482 Err(e) => map_builder_err(e),
1483 }
1484}
1485#[unsafe(no_mangle)]
1492pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1493 builder_handle: *mut RKRConFrameBuilder,
1494 index: usize,
1495 atom_id: u64,
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_id(index, atom_id) {
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_clear_atom_velocity(
1511 builder_handle: *mut RKRConFrameBuilder,
1512 index: usize,
1513) -> RKRStatus {
1514 if builder_handle.is_null() {
1515 return RKRStatus::RKR_STATUS_NULL_POINTER;
1516 }
1517 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1518 match builder.clear_atom_velocity(index) {
1519 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1520 Err(e) => map_builder_err(e),
1521 }
1522}
1523#[unsafe(no_mangle)]
1526pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1527 builder_handle: *mut RKRConFrameBuilder,
1528 index: usize,
1529) -> RKRStatus {
1530 if builder_handle.is_null() {
1531 return RKRStatus::RKR_STATUS_NULL_POINTER;
1532 }
1533 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1534 match builder.clear_atom_force(index) {
1535 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1536 Err(e) => map_builder_err(e),
1537 }
1538}
1539#[unsafe(no_mangle)]
1542pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1543 builder_handle: *mut RKRConFrameBuilder,
1544 index: usize,
1545) -> RKRStatus {
1546 if builder_handle.is_null() {
1547 return RKRStatus::RKR_STATUS_NULL_POINTER;
1548 }
1549 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1550 match builder.clear_atom_energy(index) {
1551 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1552 Err(e) => map_builder_err(e),
1553 }
1554}
1555#[unsafe(no_mangle)]
1560pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1561 builder_handle: *mut RKRConFrameBuilder,
1562 positions: *const f64,
1563 len: usize,
1564) -> RKRStatus {
1565 if builder_handle.is_null() || positions.is_null() {
1566 return RKRStatus::RKR_STATUS_NULL_POINTER;
1567 }
1568 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1569 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1570 match builder.set_positions_from_flat(slice) {
1571 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1572 Err(e) => map_builder_err(e),
1573 }
1574}
1575#[unsafe(no_mangle)]
1579pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1580 builder_handle: *mut RKRConFrameBuilder,
1581 forces: *const f64,
1582 len: usize,
1583) -> RKRStatus {
1584 if builder_handle.is_null() || forces.is_null() {
1585 return RKRStatus::RKR_STATUS_NULL_POINTER;
1586 }
1587 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1588 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1589 match builder.set_forces_from_flat(slice) {
1590 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1591 Err(e) => map_builder_err(e),
1592 }
1593}
1594#[unsafe(no_mangle)]
1598pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1599 builder_handle: *mut RKRConFrameBuilder,
1600 energies: *const f64,
1601 len: usize,
1602) -> RKRStatus {
1603 if builder_handle.is_null() || energies.is_null() {
1604 return RKRStatus::RKR_STATUS_NULL_POINTER;
1605 }
1606 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1607 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1608 match builder.set_atom_energies_from_flat(slice) {
1609 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1610 Err(e) => map_builder_err(e),
1611 }
1612}
1613#[unsafe(no_mangle)]
1617pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1618 builder_handle: *const RKRConFrameBuilder,
1619 index: usize,
1620 out_xyz: *mut f64,
1621) -> RKRStatus {
1622 if builder_handle.is_null() || out_xyz.is_null() {
1623 return RKRStatus::RKR_STATUS_NULL_POINTER;
1624 }
1625 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1626 match builder.get_atom_position(index) {
1627 Ok((x, y, z)) => unsafe {
1628 *out_xyz = x;
1629 *out_xyz.add(1) = y;
1630 *out_xyz.add(2) = z;
1631 RKRStatus::RKR_STATUS_SUCCESS
1632 },
1633 Err(e) => map_builder_err(e),
1634 }
1635}
1636#[unsafe(no_mangle)]
1643pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1644 builder_handle: *const RKRConFrameBuilder,
1645 index: usize,
1646 out_xyz: *mut f64,
1647 has_value: *mut bool,
1648) -> RKRStatus {
1649 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1650 return RKRStatus::RKR_STATUS_NULL_POINTER;
1651 }
1652 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1653 match builder.get_atom_velocity(index) {
1654 Ok(Some(v)) => unsafe {
1655 *out_xyz = v[0];
1656 *out_xyz.add(1) = v[1];
1657 *out_xyz.add(2) = v[2];
1658 *has_value = true;
1659 RKRStatus::RKR_STATUS_SUCCESS
1660 },
1661 Ok(None) => unsafe {
1662 *has_value = false;
1663 RKRStatus::RKR_STATUS_SUCCESS
1664 },
1665 Err(e) => map_builder_err(e),
1666 }
1667}
1668#[unsafe(no_mangle)]
1671pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1672 builder_handle: *const RKRConFrameBuilder,
1673 index: usize,
1674 out_xyz: *mut f64,
1675 has_value: *mut bool,
1676) -> RKRStatus {
1677 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1678 return RKRStatus::RKR_STATUS_NULL_POINTER;
1679 }
1680 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1681 match builder.get_atom_force(index) {
1682 Ok(Some(f)) => unsafe {
1683 *out_xyz = f[0];
1684 *out_xyz.add(1) = f[1];
1685 *out_xyz.add(2) = f[2];
1686 *has_value = true;
1687 RKRStatus::RKR_STATUS_SUCCESS
1688 },
1689 Ok(None) => unsafe {
1690 *has_value = false;
1691 RKRStatus::RKR_STATUS_SUCCESS
1692 },
1693 Err(e) => map_builder_err(e),
1694 }
1695}
1696#[unsafe(no_mangle)]
1702pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
1703 builder_handle: *const RKRConFrameBuilder,
1704 index: usize,
1705 out_value: *mut f64,
1706 has_value: *mut bool,
1707) -> RKRStatus {
1708 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
1709 return RKRStatus::RKR_STATUS_NULL_POINTER;
1710 }
1711 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1712 match builder.get_atom_energy(index) {
1713 Ok(Some(e)) => unsafe {
1714 *out_value = e;
1715 *has_value = true;
1716 RKRStatus::RKR_STATUS_SUCCESS
1717 },
1718 Ok(None) => unsafe {
1719 *has_value = false;
1720 RKRStatus::RKR_STATUS_SUCCESS
1721 },
1722 Err(e) => map_builder_err(e),
1723 }
1724}
1725#[unsafe(no_mangle)]
1729pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
1730 builder_handle: *const RKRConFrameBuilder,
1731 index: usize,
1732 out_mass: *mut f64,
1733) -> RKRStatus {
1734 if builder_handle.is_null() || out_mass.is_null() {
1735 return RKRStatus::RKR_STATUS_NULL_POINTER;
1736 }
1737 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1738 match builder.get_atom_mass(index) {
1739 Ok(m) => unsafe {
1740 *out_mass = m;
1741 RKRStatus::RKR_STATUS_SUCCESS
1742 },
1743 Err(e) => map_builder_err(e),
1744 }
1745}
1746pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
1769fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
1770 use crate::error::ParseError;
1771 match e {
1772 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
1773 RKRStatus::RKR_STATUS_DEVICE_MISMATCH
1774 }
1775 ParseError::ValidationError(ref msg)
1776 if msg.contains("no device allocator") || msg.contains("allocator") =>
1777 {
1778 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED
1779 }
1780 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1781 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1782 }
1783}
1784pub mod rkr_dl_type_code {
1788 pub const RKR_DL_INT: u8 = 0;
1789 pub const RKR_DL_UINT: u8 = 1;
1790 pub const RKR_DL_FLOAT: u8 = 2;
1791 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
1792 pub const RKR_DL_BFLOAT: u8 = 4;
1793 pub const RKR_DL_COMPLEX: u8 = 5;
1794 pub const RKR_DL_BOOL: u8 = 6;
1795}
1796
1797pub mod rkr_dl_device_type {
1799 pub const RKR_DL_CPU: i32 = 1;
1800 pub const RKR_DL_CUDA: i32 = 2;
1801 pub const RKR_DL_CUDA_HOST: i32 = 3;
1802}
1803
1804#[repr(C)]
1808#[derive(Clone, Copy, Debug)]
1809pub struct RKRDLDataType {
1810 pub code: u8,
1812 pub bits: u8,
1814 pub lanes: u16,
1816}
1817
1818#[repr(C)]
1821#[derive(Clone, Copy, Debug)]
1822pub struct RKRDLDevice {
1823 pub device_type: i32,
1825 pub device_id: i32,
1826}
1827
1828#[repr(C)]
1842#[derive(Clone, Copy, Debug)]
1843pub struct RKRDlpackExportOptions {
1844 pub dtype: RKRDLDataType,
1846 pub device: RKRDLDevice,
1848}
1849
1850impl Default for RKRDlpackExportOptions {
1851 fn default() -> Self {
1852 Self {
1853 dtype: RKRDLDataType {
1854 code: rkr_dl_type_code::RKR_DL_FLOAT,
1855 bits: 64,
1856 lanes: 1,
1857 },
1858 device: RKRDLDevice {
1859 device_type: rkr_dl_device_type::RKR_DL_CPU,
1860 device_id: 0,
1861 },
1862 }
1863 }
1864}
1865
1866fn resolve_dlpack_opts(
1869 opts: *const RKRDlpackExportOptions,
1870) -> Result<RKRDlpackExportOptions, RKRStatus> {
1871 let o = if opts.is_null() {
1872 RKRDlpackExportOptions::default()
1873 } else {
1874 unsafe { *opts }
1875 };
1876 let dt = o.device.device_type;
1877 if dt == rkr_dl_device_type::RKR_DL_CPU {
1878 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
1880 #[cfg(not(feature = "cuda"))]
1881 {
1882 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1883 }
1884 #[cfg(feature = "cuda")]
1885 {
1886 }
1888 } else {
1889 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1890 }
1891 if o.dtype.lanes != 1 {
1892 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
1893 }
1894 Ok(o)
1895}
1896
1897fn finish_dlpack_tensor<E: std::fmt::Display>(
1898 result: Result<dlpk::DLPackTensor, E>,
1899 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1900) -> RKRStatus {
1901 match result {
1902 Ok(tensor) => {
1903 let raw = tensor.into_raw();
1904 unsafe {
1905 *out_tensor = raw.as_ptr();
1906 }
1907 RKRStatus::RKR_STATUS_SUCCESS
1908 }
1909 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
1910 "DLPack export failed: {e}"
1911 ))),
1912 }
1913}
1914
1915fn export_f64_slice_as_dlpack(
1918 data: &[f64],
1919 shape: &[usize],
1920 dtype: RKRDLDataType,
1921 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1922) -> RKRStatus {
1923 use rkr_dl_type_code::*;
1924 if dtype.lanes != 1 {
1925 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
1926 }
1927 macro_rules! arc_export {
1928 ($ty:ty, $map:expr) => {{
1929 let v: Vec<$ty> = data.iter().map($map).collect();
1930 match *shape {
1931 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
1932 Ok(a) => dlpk::DLPackTensor::try_from(a),
1933 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1934 },
1935 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
1936 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1937 }
1938 }};
1939 }
1940 let tensor = match (dtype.code, dtype.bits) {
1941 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
1942 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
1943 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
1944 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
1945 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
1946 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
1947 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
1948 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
1949 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
1950 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
1951 (RKR_DL_BOOL, 8) => {
1952 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
1954 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
1955 }
1956 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1958 };
1959 finish_dlpack_tensor(tensor, out_tensor)
1960}
1961
1962fn export_owned_array2_dlpack_opts(
1963 arr: &ndarray::ArcArray2<f64>,
1964 opts: &RKRDlpackExportOptions,
1965 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1966) -> RKRStatus {
1967 let (r, c) = arr.dim();
1968 let flat: Vec<f64> = arr.iter().copied().collect();
1969 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
1970 #[cfg(feature = "cuda")]
1971 {
1972 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
1974 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
1975 }
1976 return finish_dlpack_tensor(
1977 crate::cuda_array::export_host_f64_as_cuda_dlpack(
1978 &[r, c],
1979 &flat,
1980 opts.device.device_id,
1981 ),
1982 out_tensor,
1983 );
1984 }
1985 #[cfg(not(feature = "cuda"))]
1986 {
1987 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
1988 }
1989 }
1990 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
1991}
1992
1993fn export_owned_array1_f64_dlpack_opts(
1994 arr: &ndarray::ArcArray1<f64>,
1995 opts: &RKRDlpackExportOptions,
1996 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1997) -> RKRStatus {
1998 let flat = arr.to_vec();
1999 let n = flat.len();
2000 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
2001 #[cfg(feature = "cuda")]
2002 {
2003 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2004 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2005 }
2006 return finish_dlpack_tensor(
2007 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2008 &[n],
2009 &flat,
2010 opts.device.device_id,
2011 ),
2012 out_tensor,
2013 );
2014 }
2015 #[cfg(not(feature = "cuda"))]
2016 {
2017 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2018 }
2019 }
2020 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
2021}
2022
2023fn export_owned_array1_u64_dlpack(
2024 arr: &ndarray::ArcArray1<u64>,
2025 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2026) -> RKRStatus {
2027 finish_dlpack_tensor(dlpk::DLPackTensor::try_from(arr.clone()), out_tensor)
2028}
2029
2030fn export_owned_array1_u64_dlpack_opts(
2032 arr: &ndarray::ArcArray1<u64>,
2033 opts: &RKRDlpackExportOptions,
2034 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2035) -> RKRStatus {
2036 use rkr_dl_type_code::*;
2037 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
2038 return export_owned_array1_u64_dlpack(arr, out_tensor);
2039 }
2040 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
2041 let n = as_f64.len();
2042 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
2043}
2044#[unsafe(no_mangle)]
2055pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
2056 builder_handle: *const RKRConFrameBuilder,
2057 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2058) -> RKRStatus {
2059 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2060}
2061
2062#[unsafe(no_mangle)]
2069pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
2070 builder_handle: *const RKRConFrameBuilder,
2071 opts: *const RKRDlpackExportOptions,
2072 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2073) -> RKRStatus {
2074 if builder_handle.is_null() || out_tensor.is_null() {
2075 return RKRStatus::RKR_STATUS_NULL_POINTER;
2076 }
2077 let o = match resolve_dlpack_opts(opts) {
2078 Ok(o) => o,
2079 Err(st) => return st,
2080 };
2081 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2082 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
2083}
2084
2085#[unsafe(no_mangle)]
2095pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
2096 builder_handle: *const RKRConFrameBuilder,
2097 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2098) -> RKRStatus {
2099 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2100}
2101
2102#[unsafe(no_mangle)]
2103pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
2104 builder_handle: *const RKRConFrameBuilder,
2105 opts: *const RKRDlpackExportOptions,
2106 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2107) -> RKRStatus {
2108 if builder_handle.is_null() || out_tensor.is_null() {
2109 return RKRStatus::RKR_STATUS_NULL_POINTER;
2110 }
2111 let o = match resolve_dlpack_opts(opts) {
2112 Ok(o) => o,
2113 Err(st) => return st,
2114 };
2115 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2116 if !builder.has_velocities_section() {
2117 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2118 }
2119 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
2120}
2121
2122#[unsafe(no_mangle)]
2131pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
2132 builder_handle: *const RKRConFrameBuilder,
2133 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2134) -> RKRStatus {
2135 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2136}
2137
2138#[unsafe(no_mangle)]
2139pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
2140 builder_handle: *const RKRConFrameBuilder,
2141 opts: *const RKRDlpackExportOptions,
2142 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2143) -> RKRStatus {
2144 if builder_handle.is_null() || out_tensor.is_null() {
2145 return RKRStatus::RKR_STATUS_NULL_POINTER;
2146 }
2147 let o = match resolve_dlpack_opts(opts) {
2148 Ok(o) => o,
2149 Err(st) => return st,
2150 };
2151 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2152 if !builder.has_forces_section() {
2153 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2154 }
2155 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
2156}
2157
2158#[unsafe(no_mangle)]
2167pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
2168 builder_handle: *const RKRConFrameBuilder,
2169 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2170) -> RKRStatus {
2171 unsafe {
2172 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2173 }
2174}
2175
2176#[unsafe(no_mangle)]
2177pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
2178 builder_handle: *const RKRConFrameBuilder,
2179 opts: *const RKRDlpackExportOptions,
2180 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2181) -> RKRStatus {
2182 if builder_handle.is_null() || out_tensor.is_null() {
2183 return RKRStatus::RKR_STATUS_NULL_POINTER;
2184 }
2185 let o = match resolve_dlpack_opts(opts) {
2186 Ok(o) => o,
2187 Err(st) => return st,
2188 };
2189 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2190 if !builder.has_energies_section() {
2191 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2192 }
2193 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
2194}
2195
2196#[unsafe(no_mangle)]
2202pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
2203 builder_handle: *const RKRConFrameBuilder,
2204 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2205) -> RKRStatus {
2206 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2207}
2208
2209#[unsafe(no_mangle)]
2210pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
2211 builder_handle: *const RKRConFrameBuilder,
2212 opts: *const RKRDlpackExportOptions,
2213 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2214) -> RKRStatus {
2215 if builder_handle.is_null() || out_tensor.is_null() {
2216 return RKRStatus::RKR_STATUS_NULL_POINTER;
2217 }
2218 let o = match resolve_dlpack_opts(opts) {
2219 Ok(o) => o,
2220 Err(st) => return st,
2221 };
2222 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2223 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
2224}
2225#[unsafe(no_mangle)]
2231pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
2232 builder_handle: *const RKRConFrameBuilder,
2233 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2234) -> RKRStatus {
2235 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2236}
2237
2238#[unsafe(no_mangle)]
2239pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2240 builder_handle: *const RKRConFrameBuilder,
2241 opts: *const RKRDlpackExportOptions,
2242 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2243) -> RKRStatus {
2244 if builder_handle.is_null() || out_tensor.is_null() {
2245 return RKRStatus::RKR_STATUS_NULL_POINTER;
2246 }
2247 let o = match resolve_dlpack_opts(opts) {
2248 Ok(o) => o,
2249 Err(st) => return st,
2250 };
2251 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2252 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2253}
2254#[unsafe(no_mangle)]
2278pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2279 builder_handle: *mut RKRConFrameBuilder,
2280) -> *mut f64 {
2281 if builder_handle.is_null() {
2282 return std::ptr::null_mut();
2283 }
2284 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2285 builder
2286 .positions_view_mut()
2287 .as_slice_memory_order_mut()
2288 .map(|s| s.as_mut_ptr())
2289 .unwrap_or(std::ptr::null_mut())
2290}
2291#[unsafe(no_mangle)]
2298pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
2299 builder_handle: *mut RKRConFrameBuilder,
2300) -> *mut f64 {
2301 if builder_handle.is_null() {
2302 return std::ptr::null_mut();
2303 }
2304 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2305 if !builder.has_velocities_section() {
2306 return std::ptr::null_mut();
2307 }
2308 let slice = builder.velocities_mut();
2309 if slice.is_empty() {
2310 std::ptr::null_mut()
2311 } else {
2312 slice.as_mut_ptr()
2313 }
2314}
2315#[unsafe(no_mangle)]
2321pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2322 builder_handle: *mut RKRConFrameBuilder,
2323) -> *mut f64 {
2324 if builder_handle.is_null() {
2325 return std::ptr::null_mut();
2326 }
2327 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2328 if !builder.has_forces_section() {
2329 return std::ptr::null_mut();
2330 }
2331 let slice = builder.forces_mut();
2332 if slice.is_empty() {
2333 std::ptr::null_mut()
2334 } else {
2335 slice.as_mut_ptr()
2336 }
2337}
2338#[unsafe(no_mangle)]
2344pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2345 builder_handle: *mut RKRConFrameBuilder,
2346) -> *mut f64 {
2347 if builder_handle.is_null() {
2348 return std::ptr::null_mut();
2349 }
2350 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2351 if !builder.has_energies_section() {
2352 return std::ptr::null_mut();
2353 }
2354 let slice = builder.atom_energies_mut();
2355 if slice.is_empty() {
2356 std::ptr::null_mut()
2357 } else {
2358 slice.as_mut_ptr()
2359 }
2360}
2361#[unsafe(no_mangle)]
2366pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2367 builder_handle: *mut RKRConFrameBuilder,
2368) -> *mut f64 {
2369 if builder_handle.is_null() {
2370 return std::ptr::null_mut();
2371 }
2372 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2373 let slice = builder.masses_mut();
2374 if slice.is_empty() {
2375 std::ptr::null_mut()
2376 } else {
2377 slice.as_mut_ptr()
2378 }
2379}
2380#[unsafe(no_mangle)]
2385pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2386 builder_handle: *const RKRConFrameBuilder,
2387) -> *const u64 {
2388 if builder_handle.is_null() {
2389 return std::ptr::null();
2390 }
2391 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2392 let slice = builder.atom_ids();
2393 if slice.is_empty() {
2394 std::ptr::null()
2395 } else {
2396 slice.as_ptr()
2397 }
2398}
2399#[unsafe(no_mangle)]
2411#[allow(clippy::too_many_arguments)]
2412pub unsafe extern "C" fn rkr_frame_add_atom_full(
2413 builder_handle: *mut RKRConFrameBuilder,
2414 symbol: *const c_char,
2415 x: f64,
2416 y: f64,
2417 z: f64,
2418 fixed_x: bool,
2419 fixed_y: bool,
2420 fixed_z: bool,
2421 atom_id: u64,
2422 mass: f64,
2423 velocity: *const f64,
2424 force: *const f64,
2425) -> RKRStatus {
2426 let velocity = if velocity.is_null() {
2427 None
2428 } else {
2429 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
2430 };
2431 let force = if force.is_null() {
2432 None
2433 } else {
2434 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
2435 };
2436 unsafe {
2437 add_builder_atom(
2438 builder_handle,
2439 symbol,
2440 x,
2441 y,
2442 z,
2443 [fixed_x, fixed_y, fixed_z],
2444 atom_id,
2445 mass,
2446 velocity,
2447 force,
2448 )
2449 }
2450}
2451#[unsafe(no_mangle)]
2467pub unsafe extern "C" fn rkr_frame_new(
2468 cell: *const f64,
2469 angles: *const f64,
2470 prebox0: *const c_char,
2471 prebox1: *const c_char,
2472 postbox0: *const c_char,
2473 postbox1: *const c_char,
2474) -> *mut RKRConFrameBuilder {
2475 if cell.is_null() || angles.is_null() {
2476 return ptr::null_mut();
2477 }
2478 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
2479 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
2480 let get_str = |p: *const c_char| -> String {
2481 if p.is_null() {
2482 String::new()
2483 } else {
2484 unsafe { CStr::from_ptr(p) }
2485 .to_str()
2486 .unwrap_or("")
2487 .to_string()
2488 }
2489 };
2490 let _ = get_str(prebox1);
2493 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
2494 builder
2495 .prebox_header(get_str(prebox0))
2496 .postbox_header([get_str(postbox0), get_str(postbox1)]);
2497 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
2498}
2499#[unsafe(no_mangle)]
2505pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
2506 builder_handle: *mut RKRConFrameBuilder,
2507 metadata_json: *const c_char,
2508) -> RKRStatus {
2509 if builder_handle.is_null() || metadata_json.is_null() {
2510 return RKRStatus::RKR_STATUS_NULL_POINTER;
2511 }
2512 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2513 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
2514 Ok(s) => s,
2515 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2516 };
2517 match builder.set_metadata_json(metadata_json) {
2518 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
2519 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
2520 }
2521}
2522#[unsafe(no_mangle)]
2528pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
2529 builder_handle: *mut RKRConFrameBuilder,
2530 key: *const c_char,
2531 value: f64,
2532) -> RKRStatus {
2533 if builder_handle.is_null() || key.is_null() {
2534 return RKRStatus::RKR_STATUS_NULL_POINTER;
2535 }
2536 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2537 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2538 Ok(s) => s,
2539 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2540 };
2541 builder.set_scalar_metadata(key, value);
2542 RKRStatus::RKR_STATUS_SUCCESS
2543}
2544#[unsafe(no_mangle)]
2550pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
2551 builder_handle: *mut RKRConFrameBuilder,
2552 key: *const c_char,
2553 value: *const c_char,
2554) -> RKRStatus {
2555 if builder_handle.is_null() || key.is_null() || value.is_null() {
2556 return RKRStatus::RKR_STATUS_NULL_POINTER;
2557 }
2558 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2559 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2560 Ok(s) => s,
2561 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2562 };
2563 let value = match unsafe { CStr::from_ptr(value).to_str() } {
2564 Ok(s) => s,
2565 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2566 };
2567 builder.set_string_metadata(key, value);
2568 RKRStatus::RKR_STATUS_SUCCESS
2569}
2570#[unsafe(no_mangle)]
2576pub unsafe extern "C" fn rkr_frame_builder_set_energy(
2577 builder_handle: *mut RKRConFrameBuilder,
2578 energy: f64,
2579) -> RKRStatus {
2580 if builder_handle.is_null() {
2581 return RKRStatus::RKR_STATUS_NULL_POINTER;
2582 }
2583 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2584 builder.set_energy(energy);
2585 RKRStatus::RKR_STATUS_SUCCESS
2586}
2587#[unsafe(no_mangle)]
2593pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
2594 builder_handle: *mut RKRConFrameBuilder,
2595 idx: u64,
2596) -> RKRStatus {
2597 if builder_handle.is_null() {
2598 return RKRStatus::RKR_STATUS_NULL_POINTER;
2599 }
2600 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2601 builder.set_frame_index(idx);
2602 RKRStatus::RKR_STATUS_SUCCESS
2603}
2604#[unsafe(no_mangle)]
2610pub unsafe extern "C" fn rkr_frame_builder_set_time(
2611 builder_handle: *mut RKRConFrameBuilder,
2612 time: f64,
2613) -> RKRStatus {
2614 if builder_handle.is_null() {
2615 return RKRStatus::RKR_STATUS_NULL_POINTER;
2616 }
2617 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2618 builder.set_time(time);
2619 RKRStatus::RKR_STATUS_SUCCESS
2620}
2621#[unsafe(no_mangle)]
2627pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
2628 builder_handle: *mut RKRConFrameBuilder,
2629 dt: f64,
2630) -> RKRStatus {
2631 if builder_handle.is_null() {
2632 return RKRStatus::RKR_STATUS_NULL_POINTER;
2633 }
2634 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2635 builder.set_timestep(dt);
2636 RKRStatus::RKR_STATUS_SUCCESS
2637}
2638#[unsafe(no_mangle)]
2644pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
2645 builder_handle: *mut RKRConFrameBuilder,
2646 bead: u64,
2647) -> RKRStatus {
2648 if builder_handle.is_null() {
2649 return RKRStatus::RKR_STATUS_NULL_POINTER;
2650 }
2651 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2652 builder.set_neb_bead(bead);
2653 RKRStatus::RKR_STATUS_SUCCESS
2654}
2655#[unsafe(no_mangle)]
2661pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
2662 builder_handle: *mut RKRConFrameBuilder,
2663 band: u64,
2664) -> RKRStatus {
2665 if builder_handle.is_null() {
2666 return RKRStatus::RKR_STATUS_NULL_POINTER;
2667 }
2668 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2669 builder.set_neb_band(band);
2670 RKRStatus::RKR_STATUS_SUCCESS
2671}
2672#[unsafe(no_mangle)]
2689pub unsafe extern "C" fn rkr_frame_add_atom(
2690 builder_handle: *mut RKRConFrameBuilder,
2691 symbol: *const c_char,
2692 x: f64,
2693 y: f64,
2694 z: f64,
2695 is_fixed: bool,
2696 atom_id: u64,
2697 mass: f64,
2698) -> RKRStatus {
2699 unsafe {
2700 add_builder_atom(
2701 builder_handle,
2702 symbol,
2703 x,
2704 y,
2705 z,
2706 [is_fixed; 3],
2707 atom_id,
2708 mass,
2709 None,
2710 None,
2711 )
2712 }
2713}
2714#[unsafe(no_mangle)]
2721pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
2722 builder_handle: *mut RKRConFrameBuilder,
2723 symbol: *const c_char,
2724 x: f64,
2725 y: f64,
2726 z: f64,
2727 fixed_x: bool,
2728 fixed_y: bool,
2729 fixed_z: bool,
2730 atom_id: u64,
2731 mass: f64,
2732) -> RKRStatus {
2733 unsafe {
2734 add_builder_atom(
2735 builder_handle,
2736 symbol,
2737 x,
2738 y,
2739 z,
2740 [fixed_x, fixed_y, fixed_z],
2741 atom_id,
2742 mass,
2743 None,
2744 None,
2745 )
2746 }
2747}
2748#[unsafe(no_mangle)]
2755pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
2756 builder_handle: *mut RKRConFrameBuilder,
2757 symbol: *const c_char,
2758 x: f64,
2759 y: f64,
2760 z: f64,
2761 is_fixed: bool,
2762 atom_id: u64,
2763 mass: f64,
2764 vx: f64,
2765 vy: f64,
2766 vz: f64,
2767) -> RKRStatus {
2768 unsafe {
2769 add_builder_atom(
2770 builder_handle,
2771 symbol,
2772 x,
2773 y,
2774 z,
2775 [is_fixed; 3],
2776 atom_id,
2777 mass,
2778 Some([vx, vy, vz]),
2779 None,
2780 )
2781 }
2782}
2783#[unsafe(no_mangle)]
2790pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
2791 builder_handle: *mut RKRConFrameBuilder,
2792 symbol: *const c_char,
2793 x: f64,
2794 y: f64,
2795 z: f64,
2796 fixed_x: bool,
2797 fixed_y: bool,
2798 fixed_z: bool,
2799 atom_id: u64,
2800 mass: f64,
2801 vx: f64,
2802 vy: f64,
2803 vz: f64,
2804) -> RKRStatus {
2805 unsafe {
2806 add_builder_atom(
2807 builder_handle,
2808 symbol,
2809 x,
2810 y,
2811 z,
2812 [fixed_x, fixed_y, fixed_z],
2813 atom_id,
2814 mass,
2815 Some([vx, vy, vz]),
2816 None,
2817 )
2818 }
2819}
2820#[unsafe(no_mangle)]
2827pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
2828 builder_handle: *mut RKRConFrameBuilder,
2829 symbol: *const c_char,
2830 x: f64,
2831 y: f64,
2832 z: f64,
2833 is_fixed: bool,
2834 atom_id: u64,
2835 mass: f64,
2836 fx: f64,
2837 fy: f64,
2838 fz: f64,
2839) -> RKRStatus {
2840 unsafe {
2841 add_builder_atom(
2842 builder_handle,
2843 symbol,
2844 x,
2845 y,
2846 z,
2847 [is_fixed; 3],
2848 atom_id,
2849 mass,
2850 None,
2851 Some([fx, fy, fz]),
2852 )
2853 }
2854}
2855#[unsafe(no_mangle)]
2862pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
2863 builder_handle: *mut RKRConFrameBuilder,
2864 symbol: *const c_char,
2865 x: f64,
2866 y: f64,
2867 z: f64,
2868 fixed_x: bool,
2869 fixed_y: bool,
2870 fixed_z: bool,
2871 atom_id: u64,
2872 mass: f64,
2873 fx: f64,
2874 fy: f64,
2875 fz: f64,
2876) -> RKRStatus {
2877 unsafe {
2878 add_builder_atom(
2879 builder_handle,
2880 symbol,
2881 x,
2882 y,
2883 z,
2884 [fixed_x, fixed_y, fixed_z],
2885 atom_id,
2886 mass,
2887 None,
2888 Some([fx, fy, fz]),
2889 )
2890 }
2891}
2892#[unsafe(no_mangle)]
2899pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
2900 builder_handle: *mut RKRConFrameBuilder,
2901 symbol: *const c_char,
2902 x: f64,
2903 y: f64,
2904 z: f64,
2905 is_fixed: bool,
2906 atom_id: u64,
2907 mass: f64,
2908 vx: f64,
2909 vy: f64,
2910 vz: f64,
2911 fx: f64,
2912 fy: f64,
2913 fz: f64,
2914) -> RKRStatus {
2915 unsafe {
2916 add_builder_atom(
2917 builder_handle,
2918 symbol,
2919 x,
2920 y,
2921 z,
2922 [is_fixed; 3],
2923 atom_id,
2924 mass,
2925 Some([vx, vy, vz]),
2926 Some([fx, fy, fz]),
2927 )
2928 }
2929}
2930#[unsafe(no_mangle)]
2937pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
2938 builder_handle: *mut RKRConFrameBuilder,
2939 symbol: *const c_char,
2940 x: f64,
2941 y: f64,
2942 z: f64,
2943 fixed_x: bool,
2944 fixed_y: bool,
2945 fixed_z: bool,
2946 atom_id: u64,
2947 mass: f64,
2948 vx: f64,
2949 vy: f64,
2950 vz: f64,
2951 fx: f64,
2952 fy: f64,
2953 fz: f64,
2954) -> RKRStatus {
2955 unsafe {
2956 add_builder_atom(
2957 builder_handle,
2958 symbol,
2959 x,
2960 y,
2961 z,
2962 [fixed_x, fixed_y, fixed_z],
2963 atom_id,
2964 mass,
2965 Some([vx, vy, vz]),
2966 Some([fx, fy, fz]),
2967 )
2968 }
2969}
2970#[unsafe(no_mangle)]
2979pub unsafe extern "C" fn rkr_frame_builder_build(
2980 builder_handle: *mut RKRConFrameBuilder,
2981) -> *mut RKRConFrame {
2982 if builder_handle.is_null() {
2983 return ptr::null_mut();
2984 }
2985 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2986 match builder.build() {
2987 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
2988 Err(_) => ptr::null_mut(),
2989 }
2990}
2991#[unsafe(no_mangle)]
2996pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
2997 if !builder_handle.is_null() {
2998 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2999 }
3000}
3001#[unsafe(no_mangle)]
3018pub unsafe extern "C" fn rkr_frame_builder_clone(
3019 builder_handle: *const RKRConFrameBuilder,
3020) -> *mut RKRConFrameBuilder {
3021 if builder_handle.is_null() {
3022 return std::ptr::null_mut();
3023 }
3024 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3025 let cloned = builder.clone();
3026 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
3027}
3028#[unsafe(no_mangle)]
3034pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3035 let filename = match unsafe { cstr_path(filename_c) } {
3036 Some(s) => s,
3037 None => return ptr::null_mut(),
3038 };
3039 match crate::compression::gzip_writer(Path::new(filename)) {
3040 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3041 Err(_) => ptr::null_mut(),
3042 }
3043}
3044#[unsafe(no_mangle)]
3051pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
3052 filename_c: *const c_char,
3053 precision: u8,
3054) -> *mut RKRConFrameWriter {
3055 let filename = match unsafe { cstr_path(filename_c) } {
3056 Some(s) => s,
3057 None => return ptr::null_mut(),
3058 };
3059 match crate::compression::gzip_writer(Path::new(filename)) {
3060 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3061 Err(_) => ptr::null_mut(),
3062 }
3063}
3064#[cfg(feature = "zstd")]
3074#[unsafe(no_mangle)]
3075pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3076 let filename = match unsafe { cstr_path(filename_c) } {
3077 Some(s) => s,
3078 None => return ptr::null_mut(),
3079 };
3080 match crate::compression::zstd_writer(Path::new(filename)) {
3081 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3082 Err(_) => ptr::null_mut(),
3083 }
3084}
3085#[cfg(feature = "zstd")]
3096#[unsafe(no_mangle)]
3097pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3098 filename_c: *const c_char,
3099 precision: u8,
3100) -> *mut RKRConFrameWriter {
3101 let filename = match unsafe { cstr_path(filename_c) } {
3102 Some(s) => s,
3103 None => return ptr::null_mut(),
3104 };
3105 match crate::compression::zstd_writer(Path::new(filename)) {
3106 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3107 Err(_) => ptr::null_mut(),
3108 }
3109}
3110#[unsafe(no_mangle)]
3122pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
3123 if filename_c.is_null() {
3124 return ptr::null_mut();
3125 }
3126 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3127 Ok(s) => s,
3128 Err(_) => return ptr::null_mut(),
3129 };
3130 match iterators::read_first_frame(Path::new(filename)) {
3131 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3132 Err(_) => ptr::null_mut(),
3133 }
3134}
3135#[unsafe(no_mangle)]
3144pub unsafe extern "C" fn rkr_read_all_frames(
3145 filename_c: *const c_char,
3146 num_frames: *mut usize,
3147) -> *mut *mut RKRConFrame {
3148 if filename_c.is_null() || num_frames.is_null() {
3149 return ptr::null_mut();
3150 }
3151 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3152 Ok(s) => s,
3153 Err(_) => return ptr::null_mut(),
3154 };
3155 match iterators::read_all_frames(Path::new(filename)) {
3156 Ok(frames) => pack_frame_handles(frames, num_frames),
3157 Err(_) => ptr::null_mut(),
3158 }
3159}
3160
3161#[unsafe(no_mangle)]
3171pub unsafe extern "C" fn rkr_read_all_frames_n_threads(
3172 filename_c: *const c_char,
3173 num_frames: *mut usize,
3174 n_threads: usize,
3175) -> *mut *mut RKRConFrame {
3176 if filename_c.is_null() || num_frames.is_null() {
3177 return ptr::null_mut();
3178 }
3179 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3180 Ok(s) => s,
3181 Err(_) => return ptr::null_mut(),
3182 };
3183 let threads = if n_threads == 0 {
3184 None
3185 } else {
3186 Some(n_threads)
3187 };
3188 match iterators::read_all_frames_with_threads(Path::new(filename), threads) {
3189 Ok(frames) => pack_frame_handles(frames, num_frames),
3190 Err(_) => ptr::null_mut(),
3191 }
3192}
3193
3194fn pack_frame_handles(frames: Vec<ConFrame>, num_frames: *mut usize) -> *mut *mut RKRConFrame {
3196 let count = frames.len();
3197 let mut handles: Vec<*mut RKRConFrame> = frames
3198 .into_iter()
3199 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3200 .collect();
3201 handles.shrink_to_fit();
3202 debug_assert_eq!(handles.len(), handles.capacity());
3203 let ptr = handles.as_mut_ptr();
3204 std::mem::forget(handles);
3205 unsafe { *num_frames = count };
3206 ptr
3207}
3208#[unsafe(no_mangle)]
3214pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3215 if frames.is_null() {
3216 return;
3217 }
3218 unsafe {
3219 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3220 for handle in handles {
3221 if !handle.is_null() {
3222 let _ = Box::from_raw(handle as *mut ConFrame);
3223 }
3224 }
3225 }
3226}
3227#[unsafe(no_mangle)]
3233pub unsafe extern "C" fn free_rkr_frame_ptr_array(
3234 frames: *mut *mut RKRConFrame,
3235 num_frames: usize,
3236) {
3237 if frames.is_null() {
3238 return;
3239 }
3240 unsafe {
3241 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3242 }
3243}
3244
3245#[cfg(feature = "metatensor")]
3260#[unsafe(no_mangle)]
3261pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3262 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3263}
3264#[cfg(feature = "metatensor")]
3266#[unsafe(no_mangle)]
3267pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3268 frame_handle: *const RKRConFrame,
3269 out_block: *mut *mut metatensor::c_api::mts_block_t,
3270) -> RKRStatus {
3271 if frame_handle.is_null() || out_block.is_null() {
3272 return RKRStatus::RKR_STATUS_NULL_POINTER;
3273 }
3274 unsafe { *out_block = std::ptr::null_mut() };
3275 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3276 return RKRStatus::RKR_STATUS_NULL_POINTER;
3277 };
3278 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3279 crate::metatensor_export::frame_positions_block(frame)
3280 })) {
3281 Ok(Ok(b)) => {
3282 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3283 RKRStatus::RKR_STATUS_SUCCESS
3284 }
3285 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3286 }
3287}
3288#[cfg(feature = "metatensor")]
3289#[unsafe(no_mangle)]
3290pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3291 frame_handle: *const RKRConFrame,
3292 out_block: *mut *mut metatensor::c_api::mts_block_t,
3293) -> RKRStatus {
3294 if frame_handle.is_null() || out_block.is_null() {
3295 return RKRStatus::RKR_STATUS_NULL_POINTER;
3296 }
3297 unsafe { *out_block = std::ptr::null_mut() };
3298 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3299 return RKRStatus::RKR_STATUS_NULL_POINTER;
3300 };
3301 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3302 crate::metatensor_export::frame_velocities_block(frame)
3303 })) {
3304 Ok(Ok(Some(b))) => {
3305 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3306 RKRStatus::RKR_STATUS_SUCCESS
3307 }
3308 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3309 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3310 }
3311}
3312#[cfg(feature = "metatensor")]
3313#[unsafe(no_mangle)]
3314pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3315 frame_handle: *const RKRConFrame,
3316 out_block: *mut *mut metatensor::c_api::mts_block_t,
3317) -> RKRStatus {
3318 if frame_handle.is_null() || out_block.is_null() {
3319 return RKRStatus::RKR_STATUS_NULL_POINTER;
3320 }
3321 unsafe { *out_block = std::ptr::null_mut() };
3322 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3323 return RKRStatus::RKR_STATUS_NULL_POINTER;
3324 };
3325 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3326 crate::metatensor_export::frame_forces_block(frame)
3327 })) {
3328 Ok(Ok(Some(b))) => {
3329 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3330 RKRStatus::RKR_STATUS_SUCCESS
3331 }
3332 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3333 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3334 }
3335}
3336#[cfg(feature = "metatensor")]
3337#[unsafe(no_mangle)]
3338pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3339 frame_handle: *const RKRConFrame,
3340 out_block: *mut *mut metatensor::c_api::mts_block_t,
3341) -> RKRStatus {
3342 if frame_handle.is_null() || out_block.is_null() {
3343 return RKRStatus::RKR_STATUS_NULL_POINTER;
3344 }
3345 unsafe { *out_block = std::ptr::null_mut() };
3346 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3347 return RKRStatus::RKR_STATUS_NULL_POINTER;
3348 };
3349 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3350 crate::metatensor_export::frame_energies_block(frame)
3351 })) {
3352 Ok(Ok(Some(b))) => {
3353 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3354 RKRStatus::RKR_STATUS_SUCCESS
3355 }
3356 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3357 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3358 }
3359}
3360#[cfg(not(feature = "zstd"))]
3361#[unsafe(no_mangle)]
3362pub unsafe extern "C" fn create_writer_zstd_c(
3363 _filename_c: *const c_char,
3364) -> *mut RKRConFrameWriter {
3365 ptr::null_mut()
3366}
3367#[cfg(not(feature = "zstd"))]
3368#[unsafe(no_mangle)]
3369pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3370 _filename_c: *const c_char,
3371 _precision: u8,
3372) -> *mut RKRConFrameWriter {
3373 ptr::null_mut()
3374}
3375#[cfg(not(feature = "metatensor"))]
3379pub struct mts_block_t {
3382 _private: [u8; 0],
3383}
3384#[cfg(not(feature = "metatensor"))]
3385#[unsafe(no_mangle)]
3386pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3387#[cfg(not(feature = "metatensor"))]
3388#[unsafe(no_mangle)]
3389pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3390 _frame_handle: *const RKRConFrame,
3391 out_block: *mut *mut mts_block_t,
3392) -> RKRStatus {
3393 if !out_block.is_null() {
3394 unsafe { *out_block = std::ptr::null_mut() };
3395 }
3396 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3397}
3398#[cfg(not(feature = "metatensor"))]
3399#[unsafe(no_mangle)]
3400pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3401 _frame_handle: *const RKRConFrame,
3402 out_block: *mut *mut mts_block_t,
3403) -> RKRStatus {
3404 if !out_block.is_null() {
3405 unsafe { *out_block = std::ptr::null_mut() };
3406 }
3407 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3408}
3409#[cfg(not(feature = "metatensor"))]
3410#[unsafe(no_mangle)]
3411pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3412 _frame_handle: *const RKRConFrame,
3413 out_block: *mut *mut mts_block_t,
3414) -> RKRStatus {
3415 if !out_block.is_null() {
3416 unsafe { *out_block = std::ptr::null_mut() };
3417 }
3418 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3419}
3420#[cfg(not(feature = "metatensor"))]
3421#[unsafe(no_mangle)]
3422pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3423 _frame_handle: *const RKRConFrame,
3424 out_block: *mut *mut mts_block_t,
3425) -> RKRStatus {
3426 if !out_block.is_null() {
3427 unsafe { *out_block = std::ptr::null_mut() };
3428 }
3429 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3430}
3431#[unsafe(no_mangle)]
3434pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3435 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3436 return 0;
3437 };
3438 frame.atom_data.len()
3439}
3440#[unsafe(no_mangle)]
3442pub unsafe extern "C" fn rkr_frame_xyz_view(
3443 frame_handle: *const RKRConFrame,
3444 out: *mut RKRArrayView,
3445) -> RKRStatus {
3446 fill_array2_view(frame_handle, out, |f| &f.positions, false)
3447}
3448
3449#[unsafe(no_mangle)]
3451pub unsafe extern "C" fn rkr_frame_velocities_view(
3452 frame_handle: *const RKRConFrame,
3453 out: *mut RKRArrayView,
3454) -> RKRStatus {
3455 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
3456}
3457
3458#[unsafe(no_mangle)]
3460pub unsafe extern "C" fn rkr_frame_forces_view(
3461 frame_handle: *const RKRConFrame,
3462 out: *mut RKRArrayView,
3463) -> RKRStatus {
3464 fill_array2_view(frame_handle, out, |f| &f.forces, true)
3465}
3466
3467#[unsafe(no_mangle)]
3469pub unsafe extern "C" fn rkr_frame_energies_view(
3470 frame_handle: *const RKRConFrame,
3471 out: *mut RKRArrayView,
3472) -> RKRStatus {
3473 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
3474}
3475
3476#[unsafe(no_mangle)]
3478pub unsafe extern "C" fn rkr_frame_masses_view(
3479 frame_handle: *const RKRConFrame,
3480 out: *mut RKRArrayView,
3481) -> RKRStatus {
3482 fill_array1_view(frame_handle, out, |f| &f.masses, false)
3483}
3484
3485#[unsafe(no_mangle)]
3487pub unsafe extern "C" fn rkr_frame_atom_ids_view(
3488 frame_handle: *const RKRConFrame,
3489 out: *mut RKRArrayView,
3490) -> RKRStatus {
3491 if frame_handle.is_null() || out.is_null() {
3492 return RKRStatus::RKR_STATUS_NULL_POINTER;
3493 }
3494 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3495 return RKRStatus::RKR_STATUS_NULL_POINTER;
3496 };
3497 let slice = frame.atom_ids.as_slice_memory_order();
3498 unsafe {
3499 *out = RKRArrayView {
3500 data: slice
3501 .map(|s| s.as_ptr() as *const std::ffi::c_void)
3502 .unwrap_or(std::ptr::null()),
3503 n: frame.atom_ids.len(),
3504 cols: 1,
3505 dtype_code: 1, dtype_bits: 64,
3507 };
3508 }
3509 RKRStatus::RKR_STATUS_SUCCESS
3510}
3511
3512#[unsafe(no_mangle)]
3515pub unsafe extern "C" fn rkr_frame_xyz_f64(
3516 frame_handle: *const RKRConFrame,
3517 n: *mut usize,
3518) -> *const f64 {
3519 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
3520}
3521
3522#[unsafe(no_mangle)]
3524pub unsafe extern "C" fn rkr_frame_velocities_f64(
3525 frame_handle: *const RKRConFrame,
3526 n: *mut usize,
3527) -> *const f64 {
3528 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
3529}
3530
3531#[unsafe(no_mangle)]
3533pub unsafe extern "C" fn rkr_frame_forces_f64(
3534 frame_handle: *const RKRConFrame,
3535 n: *mut usize,
3536) -> *const f64 {
3537 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
3538}
3539
3540fn fill_array2_view(
3541 frame_handle: *const RKRConFrame,
3542 out: *mut RKRArrayView,
3543 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
3544 section: bool,
3545) -> RKRStatus {
3546 if frame_handle.is_null() || out.is_null() {
3547 return RKRStatus::RKR_STATUS_NULL_POINTER;
3548 }
3549 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3550 return RKRStatus::RKR_STATUS_NULL_POINTER;
3551 };
3552 let arr = get(frame);
3553 if section && arr.nrows() == 0 {
3554 unsafe { *out = RKRArrayView::empty() };
3555 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3556 }
3557 unsafe { *out = RKRArrayView::from_array2(arr) };
3558 RKRStatus::RKR_STATUS_SUCCESS
3559}
3560
3561fn fill_array1_view(
3562 frame_handle: *const RKRConFrame,
3563 out: *mut RKRArrayView,
3564 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
3565 section: bool,
3566) -> RKRStatus {
3567 if frame_handle.is_null() || out.is_null() {
3568 return RKRStatus::RKR_STATUS_NULL_POINTER;
3569 }
3570 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3571 return RKRStatus::RKR_STATUS_NULL_POINTER;
3572 };
3573 let arr = get(frame);
3574 if section && arr.len() == 0 {
3575 unsafe { *out = RKRArrayView::empty() };
3576 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3577 }
3578 unsafe { *out = RKRArrayView::from_array1(arr) };
3579 RKRStatus::RKR_STATUS_SUCCESS
3580}
3581
3582fn f64_col_ptr(
3583 frame_handle: *const RKRConFrame,
3584 n: *mut usize,
3585 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
3586) -> *const f64 {
3587 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3588 if !n.is_null() {
3589 unsafe { *n = 0 };
3590 }
3591 return std::ptr::null();
3592 };
3593 match get(frame) {
3594 Some(s) if !s.is_empty() => {
3595 if !n.is_null() {
3596 unsafe { *n = s.len() / 3 };
3597 }
3598 s.as_ptr()
3599 }
3600 _ => {
3601 if !n.is_null() {
3602 unsafe { *n = 0 };
3603 }
3604 std::ptr::null()
3605 }
3606 }
3607}
3608
3609#[unsafe(no_mangle)]
3612pub unsafe extern "C" fn rkr_frame_copy_positions(
3613 frame_handle: *const RKRConFrame,
3614 out: *mut f64,
3615 out_len: usize,
3616) -> RKRStatus {
3617 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
3618}
3619fn copy_array2_f64(
3620 frame_handle: *const RKRConFrame,
3621 out: *mut f64,
3622 out_len: usize,
3623 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
3624 section: bool,
3625) -> RKRStatus {
3626 if frame_handle.is_null() || out.is_null() {
3627 return RKRStatus::RKR_STATUS_NULL_POINTER;
3628 }
3629 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3630 return RKRStatus::RKR_STATUS_NULL_POINTER;
3631 };
3632 let arr = get(frame);
3633 let n = if arr.nrows() > 0 {
3634 arr.nrows()
3635 } else {
3636 frame.atom_data.len()
3637 };
3638 if section && arr.nrows() == 0 {
3639 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3640 }
3641 let need = n.saturating_mul(3);
3642 if out_len < need {
3643 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3644 }
3645 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
3646 if arr.nrows() == n {
3647 if let Some(src) = arr.f64_slice() {
3648 dest.copy_from_slice(&src[..need.min(src.len())]);
3649 return RKRStatus::RKR_STATUS_SUCCESS;
3650 }
3651 for i in 0..n {
3652 let row = arr.as_f64_row(i);
3653 dest[i * 3] = row[0];
3654 dest[i * 3 + 1] = row[1];
3655 dest[i * 3 + 2] = row[2];
3656 }
3657 return RKRStatus::RKR_STATUS_SUCCESS;
3658 }
3659 for (i, a) in frame.atom_data.iter().enumerate() {
3660 dest[i * 3] = a.x;
3661 dest[i * 3 + 1] = a.y;
3662 dest[i * 3 + 2] = a.z;
3663 }
3664 RKRStatus::RKR_STATUS_SUCCESS
3665}
3666
3667fn copy_array1_f64(
3668 frame_handle: *const RKRConFrame,
3669 out: *mut f64,
3670 out_len: usize,
3671 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
3672 section: bool,
3673) -> RKRStatus {
3674 if frame_handle.is_null() || out.is_null() {
3675 return RKRStatus::RKR_STATUS_NULL_POINTER;
3676 }
3677 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3678 return RKRStatus::RKR_STATUS_NULL_POINTER;
3679 };
3680 let arr = get(frame);
3681 let n = if arr.len() > 0 {
3682 arr.len()
3683 } else {
3684 frame.atom_data.len()
3685 };
3686 if section && arr.len() == 0 {
3687 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3688 }
3689 if out_len < n {
3690 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3691 }
3692 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
3693 if let Some(src) = arr.f64_slice() {
3694 dest.copy_from_slice(&src[..n.min(src.len())]);
3695 return RKRStatus::RKR_STATUS_SUCCESS;
3696 }
3697 for i in 0..n {
3698 dest[i] = arr.get_f64(i);
3699 }
3700 RKRStatus::RKR_STATUS_SUCCESS
3701}
3702
3703#[unsafe(no_mangle)]
3704pub unsafe extern "C" fn rkr_frame_copy_velocities(
3705 frame_handle: *const RKRConFrame,
3706 out: *mut f64,
3707 out_len: usize,
3708) -> RKRStatus {
3709 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
3710}
3711#[unsafe(no_mangle)]
3712pub unsafe extern "C" fn rkr_frame_copy_forces(
3713 frame_handle: *const RKRConFrame,
3714 out: *mut f64,
3715 out_len: usize,
3716) -> RKRStatus {
3717 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
3718}
3719#[unsafe(no_mangle)]
3720pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
3721 frame_handle: *const RKRConFrame,
3722 out: *mut f64,
3723 out_len: usize,
3724) -> RKRStatus {
3725 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
3726}
3727#[unsafe(no_mangle)]
3728pub unsafe extern "C" fn rkr_frame_copy_masses(
3729 frame_handle: *const RKRConFrame,
3730 out: *mut f64,
3731 out_len: usize,
3732) -> RKRStatus {
3733 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
3734}
3735#[unsafe(no_mangle)]
3736pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
3737 frame_handle: *const RKRConFrame,
3738 out: *mut u64,
3739 out_len: usize,
3740) -> RKRStatus {
3741 if frame_handle.is_null() || out.is_null() {
3742 return RKRStatus::RKR_STATUS_NULL_POINTER;
3743 }
3744 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3745 return RKRStatus::RKR_STATUS_NULL_POINTER;
3746 };
3747 let n = frame.atom_ids.len().max(frame.atom_data.len());
3748 if out_len < n {
3749 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3750 }
3751 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
3752 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
3753 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
3754 return RKRStatus::RKR_STATUS_SUCCESS;
3755 }
3756 for (i, a) in frame.atom_data.iter().enumerate() {
3757 dest[i] = a.atom_id;
3758 }
3759 RKRStatus::RKR_STATUS_SUCCESS
3760}
3761fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
3762 let n = frame.atom_data.len();
3763 let mut data = Vec::with_capacity(n * 3);
3764 for a in &frame.atom_data {
3765 data.extend_from_slice(&[a.x, a.y, a.z]);
3766 }
3767 ndarray::ArcArray2::from_shape_vec((n, 3), data)
3768 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
3769}
3770
3771#[unsafe(no_mangle)]
3781pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
3782 frame_handle: *const RKRConFrame,
3783 device_type: i32,
3784 device_id: i32,
3785 _stream: i64,
3786 _max_version_major: u32,
3787 _max_version_minor: u32,
3788 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3789) -> RKRStatus {
3790 if frame_handle.is_null() || out_tensor.is_null() {
3791 return RKRStatus::RKR_STATUS_NULL_POINTER;
3792 }
3793 unsafe { *out_tensor = std::ptr::null_mut() };
3794 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
3795 dlpk::sys::DLDevice::cpu()
3796 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
3797 #[cfg(feature = "cuda")]
3798 {
3799 dlpk::sys::DLDevice::cuda(device_id)
3800 }
3801 #[cfg(not(feature = "cuda"))]
3802 {
3803 let _ = device_id;
3804 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3805 }
3806 } else {
3807 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3808 };
3809 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3810 return RKRStatus::RKR_STATUS_NULL_POINTER;
3811 };
3812 match frame.positions_as_dlpack(dl_device) {
3814 Ok(tensor) => {
3815 let raw = tensor.into_raw();
3816 unsafe {
3817 *out_tensor = raw.as_ptr();
3818 }
3819 RKRStatus::RKR_STATUS_SUCCESS
3820 }
3821 Err(e) => map_dlpack_err(e),
3822 }
3823}
3824
3825#[unsafe(no_mangle)]
3831pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
3832 frame_handle: *mut RKRConFrame,
3833 tensor: *const RKRDLManagedTensorVersioned,
3834) -> RKRStatus {
3835 if frame_handle.is_null() || tensor.is_null() {
3836 return RKRStatus::RKR_STATUS_NULL_POINTER;
3837 }
3838 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
3839 let dl = unsafe { &(*tensor).dl_tensor };
3840 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
3841 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3842 }
3843 let n = frame.atom_data.len();
3844 let need = n.saturating_mul(3);
3845 let ndim = dl.ndim as usize;
3846 let shape = if dl.shape.is_null() {
3847 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3848 } else {
3849 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
3850 };
3851 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
3852 need
3853 } else if ndim == 1 && shape[0] == need as i64 {
3854 need
3855 } else {
3856 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3857 };
3858 let code = dl.dtype.code as u8;
3859 let bits = dl.dtype.bits;
3860 if dl.data.is_null() {
3861 return RKRStatus::RKR_STATUS_NULL_POINTER;
3862 }
3863 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
3864 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
3865 s.to_vec()
3866 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
3867 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
3868 s.iter().map(|&x| x as f64).collect()
3869 } else {
3870 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3871 };
3872 if frame.positions.nrows() != n {
3873 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3874 }
3875 for i in 0..n {
3876 frame
3877 .positions
3878 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
3879 }
3880 frame.sync_atom_data_from_arrays();
3881 RKRStatus::RKR_STATUS_SUCCESS
3882}
3883
3884#[unsafe(no_mangle)]
3887pub unsafe extern "C" fn rkr_frame_positions_dlpack(
3888 frame_handle: *const RKRConFrame,
3889 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3890) -> RKRStatus {
3891 unsafe {
3892 rkr_frame_positions_as_dlpack(
3893 frame_handle,
3894 rkr_dl_device_type::RKR_DL_CPU,
3895 0,
3896 0,
3897 1,
3898 0,
3899 out_tensor,
3900 )
3901 }
3902}
3903
3904#[unsafe(no_mangle)]
3909pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
3910 frame_handle: *const RKRConFrame,
3911 opts: *const RKRDlpackExportOptions,
3912 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3913) -> RKRStatus {
3914 if frame_handle.is_null() || out_tensor.is_null() {
3915 return RKRStatus::RKR_STATUS_NULL_POINTER;
3916 }
3917 unsafe { *out_tensor = std::ptr::null_mut() };
3918 let o = match resolve_dlpack_opts(opts) {
3919 Ok(o) => o,
3920 Err(st) => return st,
3921 };
3922 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3923 return RKRStatus::RKR_STATUS_NULL_POINTER;
3924 };
3925 let arr = frame_positions_arc(frame);
3926 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3927}
3928
3929#[unsafe(no_mangle)]
3931pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
3932 frame_handle: *const RKRConFrame,
3933 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3934) -> RKRStatus {
3935 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3936}
3937
3938#[unsafe(no_mangle)]
3939pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
3940 frame_handle: *const RKRConFrame,
3941 opts: *const RKRDlpackExportOptions,
3942 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3943) -> RKRStatus {
3944 if frame_handle.is_null() || out_tensor.is_null() {
3945 return RKRStatus::RKR_STATUS_NULL_POINTER;
3946 }
3947 unsafe { *out_tensor = std::ptr::null_mut() };
3948 let o = match resolve_dlpack_opts(opts) {
3949 Ok(o) => o,
3950 Err(st) => return st,
3951 };
3952 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3953 return RKRStatus::RKR_STATUS_NULL_POINTER;
3954 };
3955 if !frame.has_velocities() {
3956 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3957 }
3958 let n = frame.atom_data.len();
3959 let mut data = Vec::with_capacity(n * 3);
3960 for a in &frame.atom_data {
3961 let v = a.velocity.unwrap_or([0.0; 3]);
3962 data.extend_from_slice(&v);
3963 }
3964 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
3965 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
3966 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3967}
3968
3969#[unsafe(no_mangle)]
3971pub unsafe extern "C" fn rkr_frame_forces_dlpack(
3972 frame_handle: *const RKRConFrame,
3973 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3974) -> RKRStatus {
3975 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3976}
3977
3978#[unsafe(no_mangle)]
3979pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
3980 frame_handle: *const RKRConFrame,
3981 opts: *const RKRDlpackExportOptions,
3982 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3983) -> RKRStatus {
3984 if frame_handle.is_null() || out_tensor.is_null() {
3985 return RKRStatus::RKR_STATUS_NULL_POINTER;
3986 }
3987 unsafe { *out_tensor = std::ptr::null_mut() };
3988 let o = match resolve_dlpack_opts(opts) {
3989 Ok(o) => o,
3990 Err(st) => return st,
3991 };
3992 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3993 return RKRStatus::RKR_STATUS_NULL_POINTER;
3994 };
3995 if !frame.has_forces() {
3996 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3997 }
3998 let n = frame.atom_data.len();
3999 let mut data = Vec::with_capacity(n * 3);
4000 for a in &frame.atom_data {
4001 let f = a.force.unwrap_or([0.0; 3]);
4002 data.extend_from_slice(&f);
4003 }
4004 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4005 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4006 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4007}
4008
4009#[unsafe(no_mangle)]
4011pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
4012 frame_handle: *const RKRConFrame,
4013 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4014) -> RKRStatus {
4015 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4016}
4017
4018#[unsafe(no_mangle)]
4019pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
4020 frame_handle: *const RKRConFrame,
4021 opts: *const RKRDlpackExportOptions,
4022 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4023) -> RKRStatus {
4024 if frame_handle.is_null() || out_tensor.is_null() {
4025 return RKRStatus::RKR_STATUS_NULL_POINTER;
4026 }
4027 unsafe { *out_tensor = std::ptr::null_mut() };
4028 let o = match resolve_dlpack_opts(opts) {
4029 Ok(o) => o,
4030 Err(st) => return st,
4031 };
4032 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4033 return RKRStatus::RKR_STATUS_NULL_POINTER;
4034 };
4035 if !frame.has_energies() {
4036 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4037 }
4038 let data: Vec<f64> = frame
4039 .atom_data
4040 .iter()
4041 .map(|a| a.energy.unwrap_or(0.0))
4042 .collect();
4043 let arr = ndarray::ArcArray1::from_vec(data);
4044 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
4045}
4046
4047pub struct RKRSelectionResult;
4051#[unsafe(no_mangle)]
4062pub unsafe extern "C" fn rkr_frame_select(
4063 frame_handle: *const RKRConFrame,
4064 selection: *const c_char,
4065 out_result: *mut *mut RKRSelectionResult,
4066) -> RKRStatus {
4067 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
4068 return RKRStatus::RKR_STATUS_NULL_POINTER;
4069 }
4070 let frame = unsafe { &*(frame_handle as *const ConFrame) };
4071 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
4072 Ok(s) => s,
4073 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
4074 };
4075 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
4076 Ok(result) => {
4077 let boxed = Box::new(result);
4078 unsafe {
4079 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
4080 }
4081 RKRStatus::RKR_STATUS_SUCCESS
4082 }
4083 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
4084 }
4085}
4086#[unsafe(no_mangle)]
4091pub unsafe extern "C" fn rkr_selection_result_match_count(
4092 result_handle: *const RKRSelectionResult,
4093) -> u64 {
4094 if result_handle.is_null() {
4095 return 0;
4096 }
4097 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4098 result.matches.len() as u64
4099}
4100#[unsafe(no_mangle)]
4105pub unsafe extern "C" fn rkr_selection_result_context_size(
4106 result_handle: *const RKRSelectionResult,
4107) -> u32 {
4108 if result_handle.is_null() {
4109 return 0;
4110 }
4111 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4112 result.context_size as u32
4113}
4114#[unsafe(no_mangle)]
4120pub unsafe extern "C" fn rkr_selection_result_match_at(
4121 result_handle: *const RKRSelectionResult,
4122 match_index: u64,
4123 out_atoms: *mut u64,
4124 out_size: *mut u32,
4125) -> RKRStatus {
4126 if result_handle.is_null() || out_atoms.is_null() {
4127 return RKRStatus::RKR_STATUS_NULL_POINTER;
4128 }
4129 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4130 let idx = match_index as usize;
4131 if idx >= result.matches.len() {
4132 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
4133 }
4134 let m = &result.matches[idx];
4135 unsafe {
4136 for i in 0..4 {
4137 *out_atoms.add(i) = if i < m.size {
4138 m.atoms[i] as u64
4139 } else {
4140 u64::MAX
4141 };
4142 }
4143 if !out_size.is_null() {
4144 *out_size = m.size as u32;
4145 }
4146 }
4147 RKRStatus::RKR_STATUS_SUCCESS
4148}
4149#[unsafe(no_mangle)]
4155pub unsafe extern "C" fn rkr_selection_result_primary_indices(
4156 result_handle: *const RKRSelectionResult,
4157 out_indices: *mut u64,
4158 capacity: u64,
4159 out_written: *mut u64,
4160) -> RKRStatus {
4161 if result_handle.is_null() {
4162 return RKRStatus::RKR_STATUS_NULL_POINTER;
4163 }
4164 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4165 let n = result.matches.len() as u64;
4166 if !out_written.is_null() {
4167 unsafe {
4168 *out_written = n;
4169 }
4170 }
4171 if n == 0 {
4172 return RKRStatus::RKR_STATUS_SUCCESS;
4173 }
4174 if out_indices.is_null() {
4175 return RKRStatus::RKR_STATUS_NULL_POINTER;
4176 }
4177 if capacity < n {
4178 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4179 }
4180 unsafe {
4181 for (i, m) in result.matches.iter().enumerate() {
4182 *out_indices.add(i) = m.atoms[0] as u64;
4183 }
4184 }
4185 RKRStatus::RKR_STATUS_SUCCESS
4186}
4187#[unsafe(no_mangle)]
4192pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
4193 if result_handle.is_null() {
4194 return;
4195 }
4196 unsafe {
4197 drop(Box::from_raw(
4198 result_handle as *mut crate::chemfiles_selection::SelectionResult,
4199 ));
4200 }
4201}
4202#[unsafe(no_mangle)]
4204pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
4205 #[cfg(feature = "chemfiles")]
4206 {
4207 1
4208 }
4209 #[cfg(not(feature = "chemfiles"))]
4210 {
4211 0
4212 }
4213}
4214
4215#[unsafe(no_mangle)]
4217pub extern "C" fn rkr_has_parallel_support() -> u8 {
4218 #[cfg(feature = "parallel")]
4219 {
4220 1
4221 }
4222 #[cfg(not(feature = "parallel"))]
4223 {
4224 0
4225 }
4226}
4227#[unsafe(no_mangle)]
4233pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
4234 if path_c.is_null() {
4235 return std::ptr::null_mut();
4236 }
4237 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4238 return std::ptr::null_mut();
4239 };
4240 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
4241 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4242 Err(_) => std::ptr::null_mut(),
4243 }
4244}
4245
4246#[unsafe(no_mangle)]
4252pub unsafe extern "C" fn rkr_read_chemfiles(
4253 path_c: *const c_char,
4254 num_frames: *mut usize,
4255) -> *mut *mut RKRConFrame {
4256 if path_c.is_null() || num_frames.is_null() {
4257 return std::ptr::null_mut();
4258 }
4259 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4260 return std::ptr::null_mut();
4261 };
4262 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
4263 Ok(frames) => pack_frame_handles(frames, num_frames),
4264 Err(_) => std::ptr::null_mut(),
4265 }
4266}
4267
4268#[unsafe(no_mangle)]
4274pub unsafe extern "C" fn rkr_read_chemfiles_nth(
4275 path_c: *const c_char,
4276 index: usize,
4277) -> *mut RKRConFrame {
4278 if path_c.is_null() {
4279 return std::ptr::null_mut();
4280 }
4281 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4282 return std::ptr::null_mut();
4283 };
4284 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
4285 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4286 Err(_) => std::ptr::null_mut(),
4287 }
4288}
4289
4290#[unsafe(no_mangle)]
4296pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
4297 if path_c.is_null() {
4298 return usize::MAX;
4299 }
4300 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4301 return usize::MAX;
4302 };
4303 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
4304}
4305
4306#[unsafe(no_mangle)]
4314pub unsafe extern "C" fn rkr_read_chemfiles_range(
4315 path_c: *const c_char,
4316 start: usize,
4317 step: usize,
4318 stop: usize,
4319 topology_c: *const c_char,
4320 guess_bonds: u8,
4321 num_frames: *mut usize,
4322) -> *mut *mut RKRConFrame {
4323 if path_c.is_null() || num_frames.is_null() {
4324 return std::ptr::null_mut();
4325 }
4326 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4327 return std::ptr::null_mut();
4328 };
4329 let topology = if topology_c.is_null() {
4330 None
4331 } else {
4332 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
4333 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
4334 _ => return std::ptr::null_mut(),
4335 }
4336 };
4337 let opts = crate::chemfiles_import::ChemfilesReadOpts {
4338 start,
4339 step,
4340 stop: if stop == usize::MAX { None } else { Some(stop) },
4341 format: None,
4342 topology,
4343 topology_format: None,
4344 guess_bonds: guess_bonds != 0,
4345 };
4346 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
4347 Ok(frames) => pack_frame_handles(frames, num_frames),
4348 Err(_) => std::ptr::null_mut(),
4349 }
4350}
4351#[unsafe(no_mangle)]
4358pub unsafe extern "C" fn rkr_read_chemfiles_memory(
4359 data_c: *const c_char,
4360 format_c: *const c_char,
4361 num_frames: *mut usize,
4362) -> *mut *mut RKRConFrame {
4363 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
4364 return std::ptr::null_mut();
4365 }
4366 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
4367 return std::ptr::null_mut();
4368 };
4369 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
4370 return std::ptr::null_mut();
4371 };
4372 match crate::chemfiles_import::con_frames_from_memory(data, format) {
4373 Ok(frames) => {
4374 let n = frames.len();
4375 unsafe { *num_frames = n };
4376 let mut ptrs: Vec<*mut RKRConFrame> = frames
4377 .into_iter()
4378 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
4379 .collect();
4380 let p = ptrs.as_mut_ptr();
4381 std::mem::forget(ptrs);
4382 p
4383 }
4384 Err(_) => std::ptr::null_mut(),
4385 }
4386}
4387#[unsafe(no_mangle)]
4392pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
4393 if tensor.is_null() {
4394 return;
4395 }
4396 unsafe {
4397 let t = &mut *tensor;
4398 if let Some(del) = t.deleter {
4399 del(tensor);
4400 }
4401 }
4402}
4403
4404#[unsafe(no_mangle)]
4414pub unsafe extern "C" fn rkr_pack_rcso(
4415 frame_handle: *const RKRConFrame,
4416 buf: *mut u8,
4417 buflen: usize,
4418 out_len: *mut usize,
4419) -> RKRStatus {
4420 if out_len.is_null() {
4421 return RKRStatus::RKR_STATUS_NULL_POINTER;
4422 }
4423 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
4424 Some(f) => f,
4425 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
4426 };
4427 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
4428 Ok(b) => b,
4429 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4430 };
4431 unsafe { *out_len = bytes.len() };
4432 if buf.is_null() {
4433 return RKRStatus::RKR_STATUS_SUCCESS;
4434 }
4435 if buflen < bytes.len() {
4436 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4437 }
4438 unsafe {
4439 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
4440 }
4441 RKRStatus::RKR_STATUS_SUCCESS
4442}
4443
4444#[unsafe(no_mangle)]
4449pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
4450 buf: *const u8,
4451 buflen: usize,
4452 out_natoms: *mut u32,
4453) -> RKRStatus {
4454 if buf.is_null() || out_natoms.is_null() {
4455 return RKRStatus::RKR_STATUS_NULL_POINTER;
4456 }
4457 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4458 match crate::rcso::Rcso::decode(bytes) {
4459 Ok(s) => {
4460 unsafe { *out_natoms = s.natoms };
4461 RKRStatus::RKR_STATUS_SUCCESS
4462 }
4463 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4464 }
4465}
4466
4467#[unsafe(no_mangle)]
4472pub unsafe extern "C" fn rkr_unpack_rcso_positions(
4473 buf: *const u8,
4474 buflen: usize,
4475 dest: *mut f64,
4476 dest_natoms: u32,
4477) -> RKRStatus {
4478 if buf.is_null() || dest.is_null() {
4479 return RKRStatus::RKR_STATUS_NULL_POINTER;
4480 }
4481 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4482 let soa = match crate::rcso::Rcso::decode(bytes) {
4483 Ok(s) => s,
4484 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4485 };
4486 if soa.natoms != dest_natoms {
4487 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4488 }
4489 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
4490 for (i, p) in soa.positions.iter().enumerate() {
4491 out[i * 3] = p[0];
4492 out[i * 3 + 1] = p[1];
4493 out[i * 3 + 2] = p[2];
4494 }
4495 RKRStatus::RKR_STATUS_SUCCESS
4496}
4497
4498#[unsafe(no_mangle)]
4504pub unsafe extern "C" fn rkr_unpack_rcso_forces(
4505 buf: *const u8,
4506 buflen: usize,
4507 dest: *mut f64,
4508 dest_natoms: u32,
4509) -> RKRStatus {
4510 if buf.is_null() || dest.is_null() {
4511 return RKRStatus::RKR_STATUS_NULL_POINTER;
4512 }
4513 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4514 let soa = match crate::rcso::Rcso::decode(bytes) {
4515 Ok(s) => s,
4516 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4517 };
4518 let Some(forces) = soa.forces else {
4519 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4520 };
4521 if soa.natoms != dest_natoms {
4522 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4523 }
4524 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
4525 for (i, f) in forces.iter().enumerate() {
4526 out[i * 3] = f[0];
4527 out[i * 3 + 1] = f[1];
4528 out[i * 3 + 2] = f[2];
4529 }
4530 RKRStatus::RKR_STATUS_SUCCESS
4531}
4532
4533#[cfg(test)]
4534mod tests {
4535 use super::*;
4536 use std::ffi::{CStr, CString};
4537 #[test]
4538 fn frame_copy_positions_without_cframe() {
4539 let handle = test_frame_handle();
4540 let n = unsafe { rkr_frame_atom_count(handle) };
4541 assert_eq!(n, 1);
4542 let mut buf = vec![0.0f64; n * 3];
4543 assert_eq!(
4544 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
4545 RKRStatus::RKR_STATUS_SUCCESS
4546 );
4547 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4548 assert_eq!(
4549 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
4550 RKRStatus::RKR_STATUS_SUCCESS
4551 );
4552 assert!(!tensor.is_null());
4553 unsafe { rkr_dlpack_delete(tensor) };
4554 assert_eq!(
4555 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
4556 RKRStatus::RKR_STATUS_SECTION_ABSENT
4557 );
4558 unsafe { free_rkr_frame(handle) };
4559 }
4560 #[test]
4561 fn read_all_frames_c_abi_tiny() {
4562 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4563 let mut n: usize = 0;
4564 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
4565 assert!(!arr.is_null() && n >= 1);
4566 let first = unsafe { *arr };
4567 let nat = unsafe { rkr_frame_atom_count(first) };
4568 let mut buf = vec![0.0f64; nat * 3];
4569 assert_eq!(
4570 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
4571 RKRStatus::RKR_STATUS_SUCCESS
4572 );
4573 unsafe { free_rkr_frame_array(arr, n) };
4574 }
4575
4576 #[test]
4577 fn read_all_frames_n_threads_matches_auto() {
4578 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4579 let mut n_auto: usize = 0;
4580 let mut n_one: usize = 0;
4581 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
4582 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
4583 assert!(!auto.is_null() && !one.is_null());
4584 assert_eq!(n_auto, n_one);
4585 assert_eq!(n_auto, 1);
4586 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
4587 let nat_one = unsafe { rkr_frame_atom_count(*one) };
4588 assert_eq!(nat_auto, nat_one);
4589 unsafe { free_rkr_frame_array(auto, n_auto) };
4590 unsafe { free_rkr_frame_array(one, n_one) };
4591 }
4592
4593 #[test]
4594 fn free_rkr_frame_ptr_array_keeps_frames() {
4595 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4596 let mut n: usize = 0;
4597 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
4598 assert!(!arr.is_null() && n >= 1);
4599 let first = unsafe { *arr };
4600 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
4602 unsafe { free_rkr_frame_ptr_array(arr, n) };
4603 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
4604 unsafe { free_rkr_frame(first) };
4605 for h in rest {
4606 if !h.is_null() {
4607 unsafe { free_rkr_frame(h) };
4608 }
4609 }
4610 }
4611 fn test_frame_handle() -> *mut RKRConFrame {
4612 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
4613 builder
4614 .prebox_header("Generated by test")
4615 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
4616 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
4617 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
4618 }
4619 #[test]
4620 fn header_line_rejects_null_buffer() {
4621 let frame = test_frame_handle();
4622 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
4623 unsafe { free_rkr_frame(frame) };
4624 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
4625 }
4626 #[test]
4627 fn header_line_rejects_empty_buffer() {
4628 let frame = test_frame_handle();
4629 let mut buffer = [0 as c_char; 1];
4630 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
4631 unsafe { free_rkr_frame(frame) };
4632 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
4633 }
4634 #[test]
4635 fn header_line_truncates_and_terminates_buffer() {
4636 let frame = test_frame_handle();
4637 let mut buffer = [0 as c_char; 10];
4638 let status =
4639 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
4640 unsafe { free_rkr_frame(frame) };
4641 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4642 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
4643 assert_eq!(copied.to_str().unwrap(), "Generated");
4644 }
4645
4646 #[test]
4647 fn pack_rcso_size_query_then_dest() {
4648 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
4649 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
4650 assert!(!frame.is_null());
4651 let mut need = 0usize;
4652 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
4653 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4654 assert!(need >= 24);
4655 let mut buf = vec![0u8; need];
4656 let mut wrote = 0usize;
4657 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
4658 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4659 assert_eq!(wrote, need);
4660 assert_eq!(&buf[0..4], b"RCSO");
4661 let mut natoms = 0u32;
4662 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
4663 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4664 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
4665 let mut xyz = vec![0.0f64; natoms as usize * 3];
4666 let st = unsafe {
4667 rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms)
4668 };
4669 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4670 let mut frc = vec![0.0f64; natoms as usize * 3];
4671 let st =
4672 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
4673 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4674 unsafe { free_rkr_frame(frame) };
4675 }
4676
4677 fn test_builder_handle() -> *mut RKRConFrameBuilder {
4678 let cell = [10.0, 11.0, 12.0];
4679 let angles = [90.0, 91.0, 92.0];
4680 unsafe {
4681 rkr_frame_new(
4682 cell.as_ptr(),
4683 angles.as_ptr(),
4684 ptr::null(),
4685 ptr::null(),
4686 ptr::null(),
4687 ptr::null(),
4688 )
4689 }
4690 }
4691 fn c_string(s: &str) -> CString {
4692 CString::new(s).unwrap()
4693 }
4694 unsafe fn assert_single_atom(
4695 frame: *mut RKRConFrame,
4696 fixed: [bool; 3],
4697 velocity: Option<[f64; 3]>,
4698 forces: Option<[f64; 3]>,
4699 ) {
4700 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
4701 assert!(!c_frame.is_null());
4702 let c_frame_ref = unsafe { &*c_frame };
4703 assert_eq!(c_frame_ref.num_atoms, 1);
4704 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
4705 assert_eq!(c_frame_ref.has_forces, forces.is_some());
4706 let atom = unsafe { &*c_frame_ref.atoms };
4707 assert_eq!(atom.fixed_x, fixed[0]);
4708 assert_eq!(atom.fixed_y, fixed[1]);
4709 assert_eq!(atom.fixed_z, fixed[2]);
4710 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
4711 assert_eq!(atom.has_velocity, velocity.is_some());
4712 assert_eq!(atom.has_forces, forces.is_some());
4713 if let Some([vx, vy, vz]) = velocity {
4714 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
4715 }
4716 if let Some([fx, fy, fz]) = forces {
4717 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
4718 }
4719 unsafe { free_c_frame(c_frame) };
4720 unsafe { free_rkr_frame(frame) };
4721 }
4722 #[test]
4723 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
4724 let builder = test_builder_handle();
4725 let symbol = c_string("Cu");
4726 let status = unsafe {
4727 rkr_frame_add_atom_with_fixed_mask(
4728 builder,
4729 symbol.as_ptr(),
4730 1.0,
4731 2.0,
4732 3.0,
4733 true,
4734 false,
4735 true,
4736 7,
4737 63.546,
4738 )
4739 };
4740 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4741 let frame = unsafe { rkr_frame_builder_build(builder) };
4742 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
4743 }
4744 #[test]
4745 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
4746 let builder = test_builder_handle();
4747 let symbol = c_string("H");
4748 let status = unsafe {
4749 rkr_frame_add_atom_with_velocity_fixed_mask(
4750 builder,
4751 symbol.as_ptr(),
4752 1.0,
4753 2.0,
4754 3.0,
4755 false,
4756 true,
4757 false,
4758 9,
4759 1.008,
4760 0.1,
4761 0.2,
4762 0.3,
4763 )
4764 };
4765 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4766 let frame = unsafe { rkr_frame_builder_build(builder) };
4767 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
4768 }
4769 #[test]
4770 fn builder_preserves_fixed_mask_for_atom_with_forces() {
4771 let builder = test_builder_handle();
4772 let symbol = c_string("O");
4773 let status = unsafe {
4774 rkr_frame_add_atom_with_forces_fixed_mask(
4775 builder,
4776 symbol.as_ptr(),
4777 1.0,
4778 2.0,
4779 3.0,
4780 true,
4781 true,
4782 false,
4783 11,
4784 15.999,
4785 -0.1,
4786 -0.2,
4787 -0.3,
4788 )
4789 };
4790 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4791 let frame = unsafe { rkr_frame_builder_build(builder) };
4792 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
4793 }
4794 #[test]
4795 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
4796 let builder = test_builder_handle();
4797 let symbol = c_string("N");
4798 let status = unsafe {
4799 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4800 builder,
4801 symbol.as_ptr(),
4802 1.0,
4803 2.0,
4804 3.0,
4805 false,
4806 true,
4807 true,
4808 13,
4809 14.007,
4810 0.4,
4811 0.5,
4812 0.6,
4813 -0.4,
4814 -0.5,
4815 -0.6,
4816 )
4817 };
4818 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4819 let frame = unsafe { rkr_frame_builder_build(builder) };
4820 unsafe {
4821 assert_single_atom(
4822 frame,
4823 [false, true, true],
4824 Some([0.4, 0.5, 0.6]),
4825 Some([-0.4, -0.5, -0.6]),
4826 )
4827 };
4828 }
4829 #[test]
4830 fn builder_bool_fixed_functions_set_all_axes_together() {
4831 let builder = test_builder_handle();
4832 let cu = c_string("Cu");
4833 let h = c_string("H");
4834 let atom_status =
4835 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
4836 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
4837 let velocity_status = unsafe {
4838 rkr_frame_add_atom_with_velocity(
4839 builder,
4840 h.as_ptr(),
4841 4.0,
4842 5.0,
4843 6.0,
4844 false,
4845 2,
4846 1.008,
4847 0.7,
4848 0.8,
4849 0.9,
4850 )
4851 };
4852 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
4853 let frame = unsafe { rkr_frame_builder_build(builder) };
4854 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
4855 assert!(!c_frame.is_null());
4856 let c_frame_ref = unsafe { &*c_frame };
4857 assert_eq!(c_frame_ref.num_atoms, 2);
4858 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
4859 assert_eq!(
4860 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
4861 [true, true, true]
4862 );
4863 assert_eq!(
4864 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
4865 [false, false, false]
4866 );
4867 unsafe { free_c_frame(c_frame) };
4868 unsafe { free_rkr_frame(frame) };
4869 }
4870 #[test]
4871 fn status_message_returns_static_strings_for_all_status_values() {
4872 let cases = [
4873 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
4874 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
4875 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
4876 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
4877 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
4878 (
4879 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
4880 "index out of bounds",
4881 ),
4882 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
4883 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
4884 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
4885 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
4886 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
4887 (
4888 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
4889 "feature disabled in this build",
4890 ),
4891 ];
4892 for (status, expected) in cases {
4893 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
4894 assert_eq!(message.to_str().unwrap(), expected);
4895 }
4896 }
4897 unsafe fn assert_dlpack_cpu_float(
4901 t: *mut RKRDLManagedTensorVersioned,
4902 expect_ndim: i32,
4903 expect_shape: &[i64],
4904 expect_bits: u8,
4905 ) {
4906 assert!(!t.is_null());
4907 let dl = unsafe { &(*t).dl_tensor };
4908 assert_eq!(dl.ndim, expect_ndim);
4909 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
4910 assert_eq!(shape, expect_shape);
4911 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
4912 assert_eq!(dl.dtype.bits, expect_bits);
4913 assert_eq!(dl.dtype.lanes, 1);
4914 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
4915 assert!(!dl.data.is_null());
4916 }
4917
4918 #[test]
4919 fn frame_optional_section_dlpack_present_and_absent() {
4920 let handle = test_frame_handle();
4922 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4923 assert_eq!(
4924 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
4925 RKRStatus::RKR_STATUS_SECTION_ABSENT
4926 );
4927 assert!(t.is_null());
4928 assert_eq!(
4929 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
4930 RKRStatus::RKR_STATUS_SECTION_ABSENT
4931 );
4932 assert_eq!(
4933 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
4934 RKRStatus::RKR_STATUS_SECTION_ABSENT
4935 );
4936 unsafe { free_rkr_frame(handle) };
4937
4938 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
4940 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
4941 assert!(!fr.is_null());
4942 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
4943 assert!(n > 0);
4944 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4945 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
4946 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4947 unsafe {
4948 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
4949 rkr_dlpack_delete(vel);
4950 free_rkr_frame(fr);
4951 }
4952
4953 let cell = [10.0f64; 3];
4955 let ang = [90.0f64; 3];
4956 let b = unsafe {
4957 rkr_frame_new(
4958 cell.as_ptr(),
4959 ang.as_ptr(),
4960 std::ptr::null(),
4961 std::ptr::null(),
4962 std::ptr::null(),
4963 std::ptr::null(),
4964 )
4965 };
4966 assert!(!b.is_null());
4967 let sym = CString::new("H").unwrap();
4968 unsafe {
4969 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4970 b,
4971 sym.as_ptr(),
4972 0.0,
4973 0.0,
4974 0.0,
4975 false,
4976 false,
4977 false,
4978 0,
4979 1.0,
4980 0.1,
4981 0.0,
4982 0.0,
4983 0.0,
4984 0.0,
4985 -1.0,
4986 );
4987 rkr_frame_builder_set_last_energy(b, -0.5);
4988 }
4989 let built = unsafe { rkr_frame_builder_build(b) };
4990 assert!(!built.is_null());
4991 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
4992 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4993 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4994 assert_eq!(
4995 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
4996 RKRStatus::RKR_STATUS_SUCCESS
4997 );
4998 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
4999 assert_eq!(
5000 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
5001 RKRStatus::RKR_STATUS_SUCCESS
5002 );
5003 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
5004 let opts32 = RKRDlpackExportOptions {
5006 dtype: RKRDLDataType {
5007 code: rkr_dl_type_code::RKR_DL_FLOAT,
5008 bits: 32,
5009 lanes: 1,
5010 },
5011 device: RKRDLDevice {
5012 device_type: rkr_dl_device_type::RKR_DL_CPU,
5013 device_id: 0,
5014 },
5015 };
5016 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5017 assert_eq!(
5018 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
5019 RKRStatus::RKR_STATUS_SUCCESS
5020 );
5021 unsafe {
5022 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
5023 rkr_dlpack_delete(pos32);
5024 }
5025 let cuda_dev = RKRDlpackExportOptions {
5028 dtype: RKRDLDataType {
5029 code: rkr_dl_type_code::RKR_DL_FLOAT,
5030 bits: 64,
5031 lanes: 1,
5032 },
5033 device: RKRDLDevice {
5034 device_type: rkr_dl_device_type::RKR_DL_CUDA,
5035 device_id: 0,
5036 },
5037 };
5038 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5039 #[cfg(not(feature = "cuda"))]
5040 assert_eq!(
5041 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5042 RKRStatus::RKR_STATUS_FEATURE_DISABLED
5043 );
5044 #[cfg(feature = "cuda")]
5045 {
5046 assert_eq!(
5047 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5048 RKRStatus::RKR_STATUS_SUCCESS
5049 );
5050 assert!(!junk.is_null());
5051 let dl = unsafe { &(*junk).dl_tensor };
5052 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
5053 assert!(!dl.data.is_null());
5054 unsafe { rkr_dlpack_delete(junk) };
5055 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5057 assert_eq!(
5058 unsafe {
5059 rkr_frame_positions_as_dlpack(
5060 built,
5061 rkr_dl_device_type::RKR_DL_CUDA,
5062 0,
5063 0,
5064 1,
5065 0,
5066 &mut as_cuda,
5067 )
5068 },
5069 RKRStatus::RKR_STATUS_SUCCESS
5070 );
5071 assert!(!as_cuda.is_null());
5072 unsafe {
5073 assert_eq!(
5074 (*as_cuda).dl_tensor.device.device_type,
5075 dlpk::sys::DLDeviceType::kDLCUDA
5076 );
5077 rkr_dlpack_delete(as_cuda);
5078 }
5079 }
5080 let bad_bits = RKRDlpackExportOptions {
5082 dtype: RKRDLDataType {
5083 code: rkr_dl_type_code::RKR_DL_FLOAT,
5084 bits: 16,
5085 lanes: 1,
5086 },
5087 device: RKRDLDevice {
5088 device_type: rkr_dl_device_type::RKR_DL_CPU,
5089 device_id: 0,
5090 },
5091 };
5092 assert_eq!(
5093 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
5094 RKRStatus::RKR_STATUS_VALIDATION_ERROR
5095 );
5096 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5098 assert_eq!(
5099 unsafe {
5100 rkr_frame_positions_as_dlpack(
5101 built,
5102 rkr_dl_device_type::RKR_DL_CPU,
5103 0,
5104 0,
5105 1,
5106 0,
5107 &mut as_t,
5108 )
5109 },
5110 RKRStatus::RKR_STATUS_SUCCESS
5111 );
5112 unsafe {
5113 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
5114 }
5115 {
5117 let fr = unsafe { &mut *(built as *mut ConFrame) };
5118 for a in fr.atom_data.iter_mut() {
5119 a.x = -99.0;
5120 a.y = -99.0;
5121 a.z = -99.0;
5122 }
5123 }
5124 assert_eq!(
5125 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
5126 RKRStatus::RKR_STATUS_SUCCESS
5127 );
5128 {
5129 let fr = unsafe { &*(built as *const ConFrame) };
5130 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
5131 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
5132 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
5133 }
5134 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5136 assert_eq!(
5137 unsafe {
5138 rkr_frame_positions_as_dlpack(
5139 built,
5140 rkr_dl_device_type::RKR_DL_CPU,
5141 0,
5142 0,
5143 1,
5144 0,
5145 &mut again,
5146 )
5147 },
5148 RKRStatus::RKR_STATUS_SUCCESS
5149 );
5150 unsafe {
5151 let dl = &(*again).dl_tensor;
5152 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
5153 assert!((data[0] - 0.0).abs() < 1e-12);
5154 assert!((data[1] - 0.0).abs() < 1e-12);
5155 assert!((data[2] - 0.0).abs() < 1e-12);
5156 rkr_dlpack_delete(again);
5157 rkr_dlpack_delete(as_t);
5158 rkr_dlpack_delete(frc);
5159 rkr_dlpack_delete(eng);
5160 free_rkr_frame(built);
5161 }
5162 }
5163
5164 #[test]
5165 fn ffi_positions_dlpack_round_trip() {
5166 let handle = test_builder_handle();
5167 let sym = c_string("Cu");
5168 unsafe {
5169 rkr_frame_add_atom_full(
5170 handle,
5171 sym.as_ptr(),
5172 1.0,
5173 2.0,
5174 3.0,
5175 false,
5176 false,
5177 false,
5178 7,
5179 63.5,
5180 ptr::null(),
5181 ptr::null(),
5182 )
5183 };
5184 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5185 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
5186 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5187 assert!(!t.is_null());
5188 let dl = unsafe { &(*t).dl_tensor };
5190 assert_eq!(dl.ndim, 2);
5191 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
5192 assert_eq!(shape, &[1, 3]);
5193 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5194 assert_eq!(dl.dtype.bits, 64);
5195 assert_eq!(dl.dtype.lanes, 1);
5196 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5197 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
5198 assert_eq!(data, &[1.0, 2.0, 3.0]);
5199 let deleter = unsafe { (*t).deleter };
5201 if let Some(del) = deleter {
5202 unsafe { del(t) };
5203 }
5204 unsafe { free_rkr_frame_builder(handle) };
5205 }
5206 #[test]
5207 fn ffi_velocities_dlpack_section_absent() {
5208 let handle = test_builder_handle();
5209 let sym = c_string("Cu");
5210 unsafe {
5211 rkr_frame_add_atom_full(
5212 handle,
5213 sym.as_ptr(),
5214 0.0,
5215 0.0,
5216 0.0,
5217 false,
5218 false,
5219 false,
5220 0,
5221 63.5,
5222 ptr::null(),
5223 ptr::null(),
5224 )
5225 };
5226 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5227 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
5228 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
5229 assert!(t.is_null());
5230 unsafe { free_rkr_frame_builder(handle) };
5231 }
5232 #[test]
5233 fn ffi_dlpack_null_handle_rejects() {
5234 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5235 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
5236 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5237 assert!(t.is_null());
5238 }
5239 #[cfg(feature = "chemfiles")]
5240 #[test]
5241 fn rkr_frame_select_finds_oxygen() {
5242 use crate::types::ConFrameBuilder;
5243 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5244 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
5245 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
5246 let frame = b.build().unwrap();
5247 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5248 let sel = CString::new("name O").unwrap();
5249 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5250 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
5251 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5252 assert!(!out.is_null());
5253 let n = unsafe { rkr_selection_result_match_count(out) };
5254 assert_eq!(n, 1);
5255 let mut atoms = [u64::MAX; 4];
5256 let mut size = 0u32;
5257 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
5258 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
5259 assert_eq!(size, 1);
5260 assert_eq!(atoms[0], 0);
5261 unsafe {
5262 rkr_selection_result_free(out);
5263 free_rkr_frame(frame_ptr);
5264 }
5265 }
5266 #[cfg(feature = "chemfiles")]
5268 #[test]
5269 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
5270 use crate::types::{Bond, ConFrameBuilder};
5271 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5273 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
5274 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
5275 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
5276 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
5277 let mut frame = b.build().unwrap();
5278 let id_to = |id: u64| {
5279 frame
5280 .atom_data
5281 .iter()
5282 .position(|a| a.atom_id == id)
5283 .unwrap() as u32
5284 };
5285 frame.header.set_bonds(&[
5286 Bond::new(id_to(0), id_to(1)),
5287 Bond::new(id_to(1), id_to(2)),
5288 Bond::new(id_to(2), id_to(3)),
5289 ]);
5290 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5291 let run = |sel: &str| -> (u64, u32) {
5292 let csel = CString::new(sel).unwrap();
5293 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5294 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
5295 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
5296 let n = unsafe { rkr_selection_result_match_count(out) };
5297 let ctx = unsafe { rkr_selection_result_context_size(out) };
5298 unsafe { rkr_selection_result_free(out) };
5299 (n, ctx)
5300 };
5301 assert_eq!(run("bonds: all"), (3, 2));
5302 assert_eq!(run("angles: all"), (2, 3));
5303 assert_eq!(run("dihedrals: all"), (1, 4));
5304 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
5305 assert_eq!(
5306 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
5307 run("bonds: type(#1) H and name(#2) O").0
5308 );
5309 unsafe { free_rkr_frame(frame_ptr) };
5310 }
5311 #[cfg(feature = "metatensor")]
5312 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
5313 assert!(!block.is_null());
5314 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
5315 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
5316 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
5317 let shape_fn = array
5318 .shape
5319 .expect("mts_array_t.shape from metatensor C API");
5320 let mut shape_ptr: *const usize = std::ptr::null();
5321 let mut shape_count: usize = 0;
5322 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
5323 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
5324 assert_eq!(shape_count, 2);
5325 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
5326 assert_eq!(shape[0], n);
5327 assert_eq!(shape[1], props);
5328 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
5329 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
5330 assert!(!samples.is_null() && !prop_lab.is_null());
5331 }
5332 #[cfg(feature = "metatensor")]
5333 #[test]
5334 fn metatensor_positions_via_c_abi() {
5335 let handle = test_frame_handle();
5336 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5337 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
5338 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5339 assert_mts_block_shape(out, 1, 3);
5340 unsafe { rkr_mts_block_free(out) };
5341 for (name, export) in [
5342 (
5343 "velocities",
5344 rkr_frame_metatensor_velocities_block
5345 as unsafe extern "C" fn(
5346 *const RKRConFrame,
5347 *mut *mut metatensor::c_api::mts_block_t,
5348 ) -> RKRStatus,
5349 ),
5350 (
5351 "forces",
5352 rkr_frame_metatensor_forces_block
5353 as unsafe extern "C" fn(
5354 *const RKRConFrame,
5355 *mut *mut metatensor::c_api::mts_block_t,
5356 ) -> RKRStatus,
5357 ),
5358 (
5359 "atom_energies",
5360 rkr_frame_metatensor_atom_energies_block
5361 as unsafe extern "C" fn(
5362 *const RKRConFrame,
5363 *mut *mut metatensor::c_api::mts_block_t,
5364 ) -> RKRStatus,
5365 ),
5366 ] {
5367 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5368 let st_abs = unsafe { export(handle, &mut o) };
5369 assert_eq!(
5370 st_abs,
5371 RKRStatus::RKR_STATUS_SECTION_ABSENT,
5372 "{name} must be SECTION_ABSENT on minimal test frame"
5373 );
5374 assert!(o.is_null());
5375 }
5376 unsafe { free_rkr_frame(handle) };
5377 }
5378
5379 #[cfg(feature = "metatensor")]
5380 #[test]
5381 fn metatensor_optional_sections_via_c_abi() {
5382 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5384 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
5385 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
5386 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
5387 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
5388 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
5389 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
5390 builder.set_atom_energy(0, -0.5).unwrap();
5391 builder.set_atom_energy(1, -1.0).unwrap();
5392 let frame = builder.build().unwrap();
5393 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5394 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5395 assert_eq!(
5396 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
5397 RKRStatus::RKR_STATUS_SUCCESS
5398 );
5399 assert_mts_block_shape(pos, 2, 3);
5400 unsafe { rkr_mts_block_free(pos) };
5401 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5402 assert_eq!(
5403 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
5404 RKRStatus::RKR_STATUS_SUCCESS
5405 );
5406 assert_mts_block_shape(vel, 2, 3);
5407 unsafe { rkr_mts_block_free(vel) };
5408 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5409 assert_eq!(
5410 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
5411 RKRStatus::RKR_STATUS_SUCCESS
5412 );
5413 assert_mts_block_shape(frc, 2, 3);
5414 unsafe { rkr_mts_block_free(frc) };
5415 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5416 assert_eq!(
5417 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
5418 RKRStatus::RKR_STATUS_SUCCESS
5419 );
5420 assert_mts_block_shape(eng, 2, 1);
5421 unsafe { rkr_mts_block_free(eng) };
5422 unsafe { free_rkr_frame(handle) };
5423 }
5424
5425 #[test]
5426 fn string_iterator_yields_frames_from_buffer() {
5427 let text =
5428 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
5429 let c_text = CString::new(text.as_str()).unwrap();
5430 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
5431 assert!(!it.is_null());
5432 let mut n = 0usize;
5433 loop {
5434 let fr = unsafe { con_frame_iterator_next(it) };
5435 if fr.is_null() {
5436 break;
5437 }
5438 n += 1;
5439 unsafe { free_rkr_frame(fr) };
5440 }
5441 unsafe { free_con_frame_iterator(it) };
5442 assert!(n >= 1, "string iterator should yield >=1 frame");
5443
5444 let bytes = text.as_bytes();
5445 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
5446 assert!(!it2.is_null());
5447 let fr2 = unsafe { con_frame_iterator_next(it2) };
5448 assert!(!fr2.is_null());
5449 unsafe {
5450 free_rkr_frame(fr2);
5451 free_con_frame_iterator(it2);
5452 }
5453 }
5454
5455 #[test]
5456 fn file_iterator_reads_gzip_when_present() {
5457 use flate2::Compression;
5458 use flate2::write::GzEncoder;
5459 use std::io::Write;
5460 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
5461 let dir = tempfile::tempdir().expect("tempdir");
5462 let gz_path = dir.path().join("tiny_cuh2.con.gz");
5463 {
5464 let f = std::fs::File::create(&gz_path).unwrap();
5465 let mut enc = GzEncoder::new(f, Compression::default());
5466 enc.write_all(&plain).unwrap();
5467 enc.finish().unwrap();
5468 }
5469 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
5470 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
5471 assert!(
5472 !it.is_null(),
5473 "path iterator must decompress .con.gz transparently"
5474 );
5475 let fr = unsafe { con_frame_iterator_next(it) };
5476 assert!(!fr.is_null());
5477 let n = unsafe { rkr_frame_atom_count(fr) };
5478 assert!(n > 0);
5479 unsafe {
5480 free_rkr_frame(fr);
5481 free_con_frame_iterator(it);
5482 }
5483 }
5484}