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}
465#[repr(C)]
468pub struct RKRConFrame {
469 _private: [u8; 0],
470}
471#[repr(C)]
474pub struct RKRConFrameWriter {
475 _private: [u8; 0],
476}
477#[repr(C)]
483pub struct RKRArrayView {
484 pub data: *const std::ffi::c_void,
485 pub n: usize,
486 pub cols: u32,
487 pub dtype_code: u8,
488 pub dtype_bits: u8,
489}
490
491impl RKRArrayView {
492 fn empty() -> Self {
493 Self {
494 data: std::ptr::null(),
495 n: 0,
496 cols: 0,
497 dtype_code: 0,
498 dtype_bits: 0,
499 }
500 }
501
502 fn from_array2(arr: &crate::storage_dtype::FloatArray2) -> Self {
503 let kind = arr.kind();
504 Self {
505 data: arr.data_ptr() as *const std::ffi::c_void,
506 n: arr.nrows(),
507 cols: arr.ncols() as u32,
508 dtype_code: kind.dlpack_code(),
509 dtype_bits: kind.dlpack_bits(),
510 }
511 }
512
513 fn from_array1(arr: &crate::storage_dtype::FloatArray1) -> Self {
514 let kind = arr.kind();
515 Self {
516 data: arr.data_ptr() as *const std::ffi::c_void,
517 n: arr.len(),
518 cols: 1,
519 dtype_code: kind.dlpack_code(),
520 dtype_bits: kind.dlpack_bits(),
521 }
522 }
523}
524
525#[repr(C)]
526pub struct CFrame {
527 pub atoms: *mut CAtom,
528 pub num_atoms: usize,
529 pub cell: [f64; 3],
530 pub angles: [f64; 3],
531 pub has_velocities: bool,
532 pub has_forces: bool,
533 pub has_energies: bool,
534}
535#[repr(C)]
545pub struct CAtom {
546 pub atomic_number: u64,
547 pub x: f64,
548 pub y: f64,
549 pub z: f64,
550 pub atom_id: u64,
551 pub mass: f64,
552 pub is_fixed: bool,
555 pub fixed_x: bool,
556 pub fixed_y: bool,
557 pub fixed_z: bool,
558 pub vx: f64,
559 pub vy: f64,
560 pub vz: f64,
561 pub has_velocity: bool,
562 pub fx: f64,
563 pub fy: f64,
564 pub fz: f64,
565 pub has_forces: bool,
566 pub energy: f64,
569 pub has_energy: bool,
570}
571#[repr(C)]
572pub struct CConFrameIterator {
573 iterator: *mut ConFrameIterator<'static>,
574 file_contents: *mut String,
575}
576
577fn c_iterator_from_owned_string(contents: String) -> *mut CConFrameIterator {
579 let file_contents_box = Box::new(contents);
580 let file_contents_ptr = Box::into_raw(file_contents_box);
581 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
582 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
583 let c_iterator = Box::new(CConFrameIterator {
584 iterator: Box::into_raw(iterator),
585 file_contents: file_contents_ptr,
586 });
587 Box::into_raw(c_iterator)
588}
589
590#[unsafe(no_mangle)]
606pub unsafe extern "C" fn read_con_file_iterator(
607 filename_c: *const c_char,
608) -> *mut CConFrameIterator {
609 if filename_c.is_null() {
610 return ptr::null_mut();
611 }
612 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
613 Ok(s) => s,
614 Err(_) => return ptr::null_mut(),
615 };
616 let owned = match crate::compression::read_file_contents(Path::new(filename)) {
617 Ok(fc) => match fc.as_str() {
618 Ok(s) => s.to_owned(),
619 Err(_) => return ptr::null_mut(),
620 },
621 Err(_) => return ptr::null_mut(),
622 };
623 c_iterator_from_owned_string(owned)
624}
625
626#[unsafe(no_mangle)]
634pub unsafe extern "C" fn read_con_string_iterator(
635 contents_c: *const c_char,
636) -> *mut CConFrameIterator {
637 if contents_c.is_null() {
638 return ptr::null_mut();
639 }
640 let contents = match unsafe { CStr::from_ptr(contents_c).to_str() } {
641 Ok(s) => s.to_owned(),
642 Err(_) => return ptr::null_mut(),
643 };
644 c_iterator_from_owned_string(contents)
645}
646
647#[unsafe(no_mangle)]
654pub unsafe extern "C" fn read_con_buffer_iterator(
655 data: *const u8,
656 len: usize,
657) -> *mut CConFrameIterator {
658 if data.is_null() && len > 0 {
659 return ptr::null_mut();
660 }
661 if len == 0 {
662 return c_iterator_from_owned_string(String::new());
663 }
664 let slice = unsafe { std::slice::from_raw_parts(data, len) };
665 let contents = match std::str::from_utf8(slice) {
666 Ok(s) => s.to_owned(),
667 Err(_) => return ptr::null_mut(),
668 };
669 c_iterator_from_owned_string(contents)
670}
671#[unsafe(no_mangle)]
677pub unsafe extern "C" fn con_frame_iterator_next(
678 iterator: *mut CConFrameIterator,
679) -> *mut RKRConFrame {
680 if iterator.is_null() {
681 return ptr::null_mut();
682 }
683 let iter = unsafe { &mut *(*iterator).iterator };
684 match iter.next() {
685 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
686 _ => ptr::null_mut(),
687 }
688}
689
690#[unsafe(no_mangle)]
693pub unsafe extern "C" fn con_frame_iterator_forward(iterator: *mut CConFrameIterator) -> RKRStatus {
694 if iterator.is_null() {
695 return RKRStatus::RKR_STATUS_NULL_POINTER;
696 }
697 let iter = unsafe { &mut *(*iterator).iterator };
698 match iter.forward() {
699 Some(Ok(())) => RKRStatus::RKR_STATUS_SUCCESS,
700 Some(Err(_)) => RKRStatus::RKR_STATUS_IO_ERROR,
701 None => RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
702 }
703}
704
705#[unsafe(no_mangle)]
708pub unsafe extern "C" fn con_frame_iterator_skip(
709 iterator: *mut CConFrameIterator,
710 n: usize,
711) -> usize {
712 if iterator.is_null() {
713 return usize::MAX;
714 }
715 let iter = unsafe { &mut *(*iterator).iterator };
716 iter.skip_frames(n).unwrap_or(usize::MAX)
717}
718
719#[unsafe(no_mangle)]
721pub unsafe extern "C" fn con_frame_iterator_nth(
722 iterator: *mut CConFrameIterator,
723 index: usize,
724) -> *mut RKRConFrame {
725 if iterator.is_null() {
726 return ptr::null_mut();
727 }
728 let iter = unsafe { &mut *(*iterator).iterator };
729 match iter.skip_frames(index) {
730 Ok(skipped) if skipped == index => {}
731 _ => return ptr::null_mut(),
732 }
733 match iter.next() {
734 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
735 _ => ptr::null_mut(),
736 }
737}
738
739#[unsafe(no_mangle)]
741pub unsafe extern "C" fn rkr_count_frames(filename_c: *const c_char) -> usize {
742 if filename_c.is_null() {
743 return usize::MAX;
744 }
745 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
746 Ok(s) => s,
747 Err(_) => return usize::MAX,
748 };
749 crate::iterators::count_frames(Path::new(filename)).unwrap_or(usize::MAX)
750}
751
752#[unsafe(no_mangle)]
754pub unsafe extern "C" fn rkr_read_nth_frame(
755 filename_c: *const c_char,
756 index: usize,
757) -> *mut RKRConFrame {
758 if filename_c.is_null() {
759 return ptr::null_mut();
760 }
761 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
762 Ok(s) => s,
763 Err(_) => return ptr::null_mut(),
764 };
765 match crate::iterators::read_nth_frame(Path::new(filename), index) {
766 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
767 Err(_) => ptr::null_mut(),
768 }
769}
770#[unsafe(no_mangle)]
775pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
776 if !frame_handle.is_null() {
777 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
778 }
779}
780#[unsafe(no_mangle)]
785pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
786 if iterator.is_null() {
787 return;
788 }
789 unsafe {
790 let c_iterator_box = Box::from_raw(iterator);
791 let _ = Box::from_raw(c_iterator_box.iterator);
792 let _ = Box::from_raw(c_iterator_box.file_contents);
793 }
794}
795#[unsafe(no_mangle)]
804pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
805 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
806 Some(f) => f,
807 None => return ptr::null_mut(),
808 };
809 let masses_iter = frame
810 .header
811 .natms_per_type
812 .iter()
813 .zip(frame.header.masses_per_type.iter())
814 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
815 let has_velocities = frame.has_velocities();
816 let mut c_atoms: Vec<CAtom> = frame
817 .atom_data
818 .iter()
819 .zip(masses_iter)
820 .map(|(atom_datum, mass)| {
821 let [vx, vy, vz] = atom_datum.velocity.unwrap_or([0.0; 3]);
822 let [fx, fy, fz] = atom_datum.force.unwrap_or([0.0; 3]);
823 CAtom {
824 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
825 x: atom_datum.x,
826 y: atom_datum.y,
827 z: atom_datum.z,
828 is_fixed: atom_datum.is_fixed(),
829 fixed_x: atom_datum.fixed[0],
830 fixed_y: atom_datum.fixed[1],
831 fixed_z: atom_datum.fixed[2],
832 atom_id: atom_datum.atom_id,
833 mass,
834 vx,
835 vy,
836 vz,
837 has_velocity: atom_datum.has_velocity(),
838 fx,
839 fy,
840 fz,
841 has_forces: atom_datum.has_forces(),
842 energy: atom_datum.energy.unwrap_or(0.0),
843 has_energy: atom_datum.has_energy(),
844 }
845 })
846 .collect();
847 let atoms_ptr = c_atoms.as_mut_ptr();
848 let num_atoms = c_atoms.len();
849 std::mem::forget(c_atoms);
850 let has_forces = frame.has_forces();
851 let has_energies = frame.has_energies();
852 let c_frame = Box::new(CFrame {
853 atoms: atoms_ptr,
854 num_atoms,
855 cell: frame.header.boxl,
856 angles: frame.header.angles,
857 has_velocities,
858 has_forces,
859 has_energies,
860 });
861 Box::into_raw(c_frame)
862}
863#[unsafe(no_mangle)]
868pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
869 if frame.is_null() {
870 return;
871 }
872 unsafe {
873 let frame_box = Box::from_raw(frame);
874 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
875 }
876}
877#[unsafe(no_mangle)]
896pub unsafe extern "C" fn rkr_frame_get_header_line(
897 frame_handle: *const RKRConFrame,
898 is_prebox: bool,
899 line_index: usize,
900 buffer: *mut c_char,
901 buffer_len: usize,
902) -> RKRStatus {
903 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
904 Some(f) => f,
905 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
906 };
907 if buffer.is_null() {
908 return RKRStatus::RKR_STATUS_NULL_POINTER;
909 }
910 if buffer_len == 0 {
911 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
912 }
913 let line_to_copy: Option<&str> = if is_prebox {
914 match line_index {
915 0 => Some(frame.header.prebox_header.user.as_str()),
916 1 => Some(frame.header.prebox_header.metadata_line()),
917 _ => None,
918 }
919 } else {
920 frame
921 .header
922 .postbox_header
923 .get(line_index)
924 .map(String::as_str)
925 };
926 if let Some(line) = line_to_copy {
927 let bytes = line.as_bytes();
928 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
929 unsafe {
930 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
931 *buffer.add(len_to_copy) = 0;
932 }
933 RKRStatus::RKR_STATUS_SUCCESS
934 } else {
935 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
936 }
937}
938#[unsafe(no_mangle)]
951pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
952 frame_handle: *const RKRConFrame,
953 is_prebox: bool,
954 line_index: usize,
955) -> *mut c_char {
956 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
957 Some(f) => f,
958 None => return ptr::null_mut(),
959 };
960 let line_to_copy: Option<&str> = if is_prebox {
961 match line_index {
962 0 => Some(frame.header.prebox_header.user.as_str()),
963 1 => Some(frame.header.prebox_header.metadata_line()),
964 _ => None,
965 }
966 } else {
967 frame
968 .header
969 .postbox_header
970 .get(line_index)
971 .map(String::as_str)
972 };
973 if let Some(line) = line_to_copy {
974 match CString::new(line) {
976 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
979 } else {
980 ptr::null_mut() }
982}
983#[unsafe(no_mangle)]
991pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
992 if !s.is_null() {
993 let _ = unsafe { CString::from_raw(s) };
995 }
996}
997type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
1011#[inline]
1014fn into_rkr_writer(sink: Box<dyn std::io::Write>, precision: Option<u8>) -> *mut RKRConFrameWriter {
1015 let writer: RkrWriter = match precision {
1016 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
1017 None => ConFrameWriter::new(sink),
1018 };
1019 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1020}
1021#[inline]
1023unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
1024 if filename_c.is_null() {
1025 return None;
1026 }
1027 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
1028}
1029#[unsafe(no_mangle)]
1035pub unsafe extern "C" fn create_writer_from_path_c(
1036 filename_c: *const c_char,
1037) -> *mut RKRConFrameWriter {
1038 let filename = match unsafe { cstr_path(filename_c) } {
1039 Some(s) => s,
1040 None => return ptr::null_mut(),
1041 };
1042 match File::create(filename) {
1043 Ok(file) => into_rkr_writer(Box::new(file), None),
1044 Err(_) => ptr::null_mut(),
1045 }
1046}
1047#[unsafe(no_mangle)]
1052pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
1053 if !writer_handle.is_null() {
1054 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
1055 }
1056}
1057#[unsafe(no_mangle)]
1063pub unsafe extern "C" fn rkr_writer_extend(
1064 writer_handle: *mut RKRConFrameWriter,
1065 frame_handles: *const *const RKRConFrame,
1066 num_frames: usize,
1067) -> RKRStatus {
1068 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1069 Some(w) => w,
1070 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1071 };
1072 if frame_handles.is_null() {
1073 return RKRStatus::RKR_STATUS_NULL_POINTER;
1074 }
1075 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
1076 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
1077 if handles_slice.iter().any(|&handle| handle.is_null()) {
1078 return RKRStatus::RKR_STATUS_NULL_POINTER;
1081 }
1082 for &handle in handles_slice.iter() {
1083 match unsafe { (handle as *const ConFrame).as_ref() } {
1085 Some(frame) => rust_frames.push(frame),
1086 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1088 }
1089 }
1090 match writer.extend(rust_frames.into_iter()) {
1091 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1092 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
1093 }
1094}
1095
1096#[unsafe(no_mangle)]
1102pub unsafe extern "C" fn rkr_writer_set_canonical(
1103 writer_handle: *mut RKRConFrameWriter,
1104 canonical: u8,
1105) -> RKRStatus {
1106 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
1107 Some(w) => w,
1108 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
1109 };
1110 writer.set_canonical(canonical != 0);
1111 RKRStatus::RKR_STATUS_SUCCESS
1112}
1113
1114#[unsafe(no_mangle)]
1116pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
1117 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
1118 Some(w) => u8::from(w.is_canonical()),
1119 None => 0,
1120 }
1121}
1122
1123#[cfg(test)]
1124mod index_proj_ffi_tests {
1125 use super::*;
1126 use std::ffi::CStr;
1127 use std::fs;
1128 use std::path::PathBuf;
1129
1130 fn fixture_path() -> PathBuf {
1131 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
1132 }
1133
1134 #[test]
1135 fn ffi_projection_matches_index_proj() {
1136 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1137 let fr = &frames[0];
1138 let handle = fr as *const ConFrame as *const RKRConFrame;
1139 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
1140 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
1142 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
1143 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
1144 assert!(!formula_c.is_null());
1145 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
1146 assert_eq!(formula, proj.formula);
1147 unsafe { rkr_free_string(formula_c) };
1148 let ie = rkr_frame_index_energy(handle);
1149 match proj.energy {
1150 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
1151 None => assert!(ie.is_nan()),
1152 }
1153 let tm = rkr_frame_total_mass(handle);
1154 match proj.total_mass {
1155 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
1156 None => assert!(tm.is_nan()),
1157 }
1158 let cv = rkr_frame_cell_volume(handle);
1159 match proj.cell_volume {
1160 Some(v) => assert!(
1161 (cv - v).abs() < 1e-6 * v.max(1.0),
1162 "cell_volume C={cv} proj={v}"
1163 ),
1164 None => assert!(cv.is_nan()),
1165 }
1166 let fm = rkr_frame_fmax(handle);
1167 match proj.fmax {
1168 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
1169 None => assert!(fm.is_nan()),
1170 }
1171 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
1172 assert!(!json_c.is_null());
1173 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
1174 assert!(json.contains("\"formula\""));
1175 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
1176 assert!(json.contains("\"n_atoms\""));
1177 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
1178 unsafe { rkr_free_string(json_c) };
1179 }
1180
1181 #[test]
1182 fn ffi_canonical_writer_byte_identical() {
1183 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
1184 let fr = &frames[0];
1185 let dir = tempfile::tempdir().unwrap();
1186 let p1 = dir.path().join("a.con");
1187 let p2 = dir.path().join("b.con");
1188 for p in [&p1, &p2] {
1189 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
1190 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
1191 assert!(!w.is_null());
1192 assert_eq!(
1193 unsafe { rkr_writer_set_canonical(w, 1) },
1194 RKRStatus::RKR_STATUS_SUCCESS
1195 );
1196 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
1197 let handles = [fr as *const ConFrame as *const RKRConFrame];
1198 assert_eq!(
1199 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
1200 RKRStatus::RKR_STATUS_SUCCESS
1201 );
1202 unsafe { free_rkr_writer(w) };
1203 }
1204 let b1 = fs::read(&p1).unwrap();
1205 let b2 = fs::read(&p2).unwrap();
1206 assert_eq!(b1, b2);
1207 assert!(!b1.is_empty());
1208 }
1209}
1210#[unsafe(no_mangle)]
1219pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
1220 filename_c: *const c_char,
1221 precision: u8,
1222) -> *mut RKRConFrameWriter {
1223 let filename = match unsafe { cstr_path(filename_c) } {
1224 Some(s) => s,
1225 None => return ptr::null_mut(),
1226 };
1227 match File::create(filename) {
1228 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1229 Err(_) => ptr::null_mut(),
1230 }
1231}
1232#[repr(C)]
1237pub struct RKRConFrameBuilder {
1238 _private: [u8; 0],
1239}
1240#[allow(clippy::too_many_arguments)]
1241unsafe fn add_builder_atom(
1242 builder_handle: *mut RKRConFrameBuilder,
1243 symbol: *const c_char,
1244 x: f64,
1245 y: f64,
1246 z: f64,
1247 fixed: [bool; 3],
1248 atom_id: u64,
1249 mass: f64,
1250 velocity: Option<[f64; 3]>,
1251 forces: Option<[f64; 3]>,
1252) -> RKRStatus {
1253 if builder_handle.is_null() || symbol.is_null() {
1254 return RKRStatus::RKR_STATUS_NULL_POINTER;
1255 }
1256 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1257 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
1258 Ok(s) => s,
1259 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1260 };
1261 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
1262 if let Some(v) = velocity {
1263 builder.with_velocity(v);
1264 }
1265 if let Some(f) = forces {
1266 builder.with_force(f);
1267 }
1268 RKRStatus::RKR_STATUS_SUCCESS
1269}
1270#[unsafe(no_mangle)]
1276pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
1277 builder_handle: *mut RKRConFrameBuilder,
1278 velocity: *const f64,
1279) -> RKRStatus {
1280 if builder_handle.is_null() || velocity.is_null() {
1281 return RKRStatus::RKR_STATUS_NULL_POINTER;
1282 }
1283 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1284 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1285 builder.with_velocity(v);
1286 RKRStatus::RKR_STATUS_SUCCESS
1287}
1288#[unsafe(no_mangle)]
1294pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
1295 builder_handle: *mut RKRConFrameBuilder,
1296 force: *const f64,
1297) -> RKRStatus {
1298 if builder_handle.is_null() || force.is_null() {
1299 return RKRStatus::RKR_STATUS_NULL_POINTER;
1300 }
1301 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1302 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1303 builder.with_force(f);
1304 RKRStatus::RKR_STATUS_SUCCESS
1305}
1306#[unsafe(no_mangle)]
1316pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
1317 builder_handle: *mut RKRConFrameBuilder,
1318 energy: f64,
1319) -> RKRStatus {
1320 if builder_handle.is_null() {
1321 return RKRStatus::RKR_STATUS_NULL_POINTER;
1322 }
1323 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1324 builder.with_energy(energy);
1325 RKRStatus::RKR_STATUS_SUCCESS
1326}
1327fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
1341 use crate::error::ParseError;
1342 match e {
1343 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
1344 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
1345 }
1346 ParseError::MassMismatch { .. } | ParseError::ValidationError(_) => {
1347 RKRStatus::RKR_STATUS_VALIDATION_ERROR
1348 }
1349 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1350 }
1351}
1352#[unsafe(no_mangle)]
1359pub unsafe extern "C" fn rkr_frame_builder_atom_count(
1360 builder_handle: *const RKRConFrameBuilder,
1361) -> usize {
1362 if builder_handle.is_null() {
1363 return 0;
1364 }
1365 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1366 builder.atom_count()
1367}
1368#[unsafe(no_mangle)]
1372pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
1373 builder_handle: *mut RKRConFrameBuilder,
1374 index: usize,
1375 x: f64,
1376 y: f64,
1377 z: f64,
1378) -> RKRStatus {
1379 if builder_handle.is_null() {
1380 return RKRStatus::RKR_STATUS_NULL_POINTER;
1381 }
1382 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1383 match builder.set_atom_position(index, x, y, z) {
1384 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1385 Err(e) => map_builder_err(e),
1386 }
1387}
1388#[unsafe(no_mangle)]
1392pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1393 builder_handle: *mut RKRConFrameBuilder,
1394 index: usize,
1395 velocity: *const f64,
1396) -> RKRStatus {
1397 if builder_handle.is_null() || velocity.is_null() {
1398 return RKRStatus::RKR_STATUS_NULL_POINTER;
1399 }
1400 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1401 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1402 match builder.set_atom_velocity(index, v) {
1403 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1404 Err(e) => map_builder_err(e),
1405 }
1406}
1407#[unsafe(no_mangle)]
1411pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1412 builder_handle: *mut RKRConFrameBuilder,
1413 index: usize,
1414 force: *const f64,
1415) -> RKRStatus {
1416 if builder_handle.is_null() || force.is_null() {
1417 return RKRStatus::RKR_STATUS_NULL_POINTER;
1418 }
1419 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1420 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1421 match builder.set_atom_force(index, f) {
1422 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1423 Err(e) => map_builder_err(e),
1424 }
1425}
1426#[unsafe(no_mangle)]
1430pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1431 builder_handle: *mut RKRConFrameBuilder,
1432 index: usize,
1433 energy: f64,
1434) -> RKRStatus {
1435 if builder_handle.is_null() {
1436 return RKRStatus::RKR_STATUS_NULL_POINTER;
1437 }
1438 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1439 match builder.set_atom_energy(index, energy) {
1440 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1441 Err(e) => map_builder_err(e),
1442 }
1443}
1444#[unsafe(no_mangle)]
1448pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1449 builder_handle: *mut RKRConFrameBuilder,
1450 index: usize,
1451 fixed_x: bool,
1452 fixed_y: bool,
1453 fixed_z: bool,
1454) -> RKRStatus {
1455 if builder_handle.is_null() {
1456 return RKRStatus::RKR_STATUS_NULL_POINTER;
1457 }
1458 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1459 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1460 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1461 Err(e) => map_builder_err(e),
1462 }
1463}
1464#[unsafe(no_mangle)]
1468pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1469 builder_handle: *mut RKRConFrameBuilder,
1470 index: usize,
1471 mass: f64,
1472) -> RKRStatus {
1473 if builder_handle.is_null() {
1474 return RKRStatus::RKR_STATUS_NULL_POINTER;
1475 }
1476 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1477 match builder.set_atom_mass(index, mass) {
1478 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1479 Err(e) => map_builder_err(e),
1480 }
1481}
1482#[unsafe(no_mangle)]
1489pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1490 builder_handle: *mut RKRConFrameBuilder,
1491 index: usize,
1492 atom_id: u64,
1493) -> RKRStatus {
1494 if builder_handle.is_null() {
1495 return RKRStatus::RKR_STATUS_NULL_POINTER;
1496 }
1497 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1498 match builder.set_atom_id(index, atom_id) {
1499 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1500 Err(e) => map_builder_err(e),
1501 }
1502}
1503#[unsafe(no_mangle)]
1507pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1508 builder_handle: *mut RKRConFrameBuilder,
1509 index: usize,
1510) -> RKRStatus {
1511 if builder_handle.is_null() {
1512 return RKRStatus::RKR_STATUS_NULL_POINTER;
1513 }
1514 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1515 match builder.clear_atom_velocity(index) {
1516 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1517 Err(e) => map_builder_err(e),
1518 }
1519}
1520#[unsafe(no_mangle)]
1523pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1524 builder_handle: *mut RKRConFrameBuilder,
1525 index: usize,
1526) -> RKRStatus {
1527 if builder_handle.is_null() {
1528 return RKRStatus::RKR_STATUS_NULL_POINTER;
1529 }
1530 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1531 match builder.clear_atom_force(index) {
1532 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1533 Err(e) => map_builder_err(e),
1534 }
1535}
1536#[unsafe(no_mangle)]
1539pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1540 builder_handle: *mut RKRConFrameBuilder,
1541 index: usize,
1542) -> RKRStatus {
1543 if builder_handle.is_null() {
1544 return RKRStatus::RKR_STATUS_NULL_POINTER;
1545 }
1546 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1547 match builder.clear_atom_energy(index) {
1548 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1549 Err(e) => map_builder_err(e),
1550 }
1551}
1552#[unsafe(no_mangle)]
1557pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1558 builder_handle: *mut RKRConFrameBuilder,
1559 positions: *const f64,
1560 len: usize,
1561) -> RKRStatus {
1562 if builder_handle.is_null() || positions.is_null() {
1563 return RKRStatus::RKR_STATUS_NULL_POINTER;
1564 }
1565 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1566 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1567 match builder.set_positions_from_flat(slice) {
1568 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1569 Err(e) => map_builder_err(e),
1570 }
1571}
1572#[unsafe(no_mangle)]
1576pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1577 builder_handle: *mut RKRConFrameBuilder,
1578 forces: *const f64,
1579 len: usize,
1580) -> RKRStatus {
1581 if builder_handle.is_null() || forces.is_null() {
1582 return RKRStatus::RKR_STATUS_NULL_POINTER;
1583 }
1584 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1585 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1586 match builder.set_forces_from_flat(slice) {
1587 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1588 Err(e) => map_builder_err(e),
1589 }
1590}
1591#[unsafe(no_mangle)]
1595pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1596 builder_handle: *mut RKRConFrameBuilder,
1597 energies: *const f64,
1598 len: usize,
1599) -> RKRStatus {
1600 if builder_handle.is_null() || energies.is_null() {
1601 return RKRStatus::RKR_STATUS_NULL_POINTER;
1602 }
1603 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1604 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1605 match builder.set_atom_energies_from_flat(slice) {
1606 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1607 Err(e) => map_builder_err(e),
1608 }
1609}
1610#[unsafe(no_mangle)]
1614pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1615 builder_handle: *const RKRConFrameBuilder,
1616 index: usize,
1617 out_xyz: *mut f64,
1618) -> RKRStatus {
1619 if builder_handle.is_null() || out_xyz.is_null() {
1620 return RKRStatus::RKR_STATUS_NULL_POINTER;
1621 }
1622 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1623 match builder.get_atom_position(index) {
1624 Ok((x, y, z)) => unsafe {
1625 *out_xyz = x;
1626 *out_xyz.add(1) = y;
1627 *out_xyz.add(2) = z;
1628 RKRStatus::RKR_STATUS_SUCCESS
1629 },
1630 Err(e) => map_builder_err(e),
1631 }
1632}
1633#[unsafe(no_mangle)]
1640pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1641 builder_handle: *const RKRConFrameBuilder,
1642 index: usize,
1643 out_xyz: *mut f64,
1644 has_value: *mut bool,
1645) -> RKRStatus {
1646 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1647 return RKRStatus::RKR_STATUS_NULL_POINTER;
1648 }
1649 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1650 match builder.get_atom_velocity(index) {
1651 Ok(Some(v)) => unsafe {
1652 *out_xyz = v[0];
1653 *out_xyz.add(1) = v[1];
1654 *out_xyz.add(2) = v[2];
1655 *has_value = true;
1656 RKRStatus::RKR_STATUS_SUCCESS
1657 },
1658 Ok(None) => unsafe {
1659 *has_value = false;
1660 RKRStatus::RKR_STATUS_SUCCESS
1661 },
1662 Err(e) => map_builder_err(e),
1663 }
1664}
1665#[unsafe(no_mangle)]
1668pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1669 builder_handle: *const RKRConFrameBuilder,
1670 index: usize,
1671 out_xyz: *mut f64,
1672 has_value: *mut bool,
1673) -> RKRStatus {
1674 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1675 return RKRStatus::RKR_STATUS_NULL_POINTER;
1676 }
1677 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1678 match builder.get_atom_force(index) {
1679 Ok(Some(f)) => unsafe {
1680 *out_xyz = f[0];
1681 *out_xyz.add(1) = f[1];
1682 *out_xyz.add(2) = f[2];
1683 *has_value = true;
1684 RKRStatus::RKR_STATUS_SUCCESS
1685 },
1686 Ok(None) => unsafe {
1687 *has_value = false;
1688 RKRStatus::RKR_STATUS_SUCCESS
1689 },
1690 Err(e) => map_builder_err(e),
1691 }
1692}
1693#[unsafe(no_mangle)]
1699pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
1700 builder_handle: *const RKRConFrameBuilder,
1701 index: usize,
1702 out_value: *mut f64,
1703 has_value: *mut bool,
1704) -> RKRStatus {
1705 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
1706 return RKRStatus::RKR_STATUS_NULL_POINTER;
1707 }
1708 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1709 match builder.get_atom_energy(index) {
1710 Ok(Some(e)) => unsafe {
1711 *out_value = e;
1712 *has_value = true;
1713 RKRStatus::RKR_STATUS_SUCCESS
1714 },
1715 Ok(None) => unsafe {
1716 *has_value = false;
1717 RKRStatus::RKR_STATUS_SUCCESS
1718 },
1719 Err(e) => map_builder_err(e),
1720 }
1721}
1722#[unsafe(no_mangle)]
1726pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
1727 builder_handle: *const RKRConFrameBuilder,
1728 index: usize,
1729 out_mass: *mut f64,
1730) -> RKRStatus {
1731 if builder_handle.is_null() || out_mass.is_null() {
1732 return RKRStatus::RKR_STATUS_NULL_POINTER;
1733 }
1734 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1735 match builder.get_atom_mass(index) {
1736 Ok(m) => unsafe {
1737 *out_mass = m;
1738 RKRStatus::RKR_STATUS_SUCCESS
1739 },
1740 Err(e) => map_builder_err(e),
1741 }
1742}
1743pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
1766fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
1767 use crate::error::ParseError;
1768 match e {
1769 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
1770 RKRStatus::RKR_STATUS_DEVICE_MISMATCH
1771 }
1772 ParseError::ValidationError(ref msg)
1773 if msg.contains("no device allocator") || msg.contains("allocator") =>
1774 {
1775 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED
1776 }
1777 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1778 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1779 }
1780}
1781pub mod rkr_dl_type_code {
1785 pub const RKR_DL_INT: u8 = 0;
1786 pub const RKR_DL_UINT: u8 = 1;
1787 pub const RKR_DL_FLOAT: u8 = 2;
1788 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
1789 pub const RKR_DL_BFLOAT: u8 = 4;
1790 pub const RKR_DL_COMPLEX: u8 = 5;
1791 pub const RKR_DL_BOOL: u8 = 6;
1792}
1793
1794pub mod rkr_dl_device_type {
1796 pub const RKR_DL_CPU: i32 = 1;
1797 pub const RKR_DL_CUDA: i32 = 2;
1798 pub const RKR_DL_CUDA_HOST: i32 = 3;
1799}
1800
1801#[repr(C)]
1805#[derive(Clone, Copy, Debug)]
1806pub struct RKRDLDataType {
1807 pub code: u8,
1809 pub bits: u8,
1811 pub lanes: u16,
1813}
1814
1815#[repr(C)]
1818#[derive(Clone, Copy, Debug)]
1819pub struct RKRDLDevice {
1820 pub device_type: i32,
1822 pub device_id: i32,
1823}
1824
1825#[repr(C)]
1839#[derive(Clone, Copy, Debug)]
1840pub struct RKRDlpackExportOptions {
1841 pub dtype: RKRDLDataType,
1843 pub device: RKRDLDevice,
1845}
1846
1847impl Default for RKRDlpackExportOptions {
1848 fn default() -> Self {
1849 Self {
1850 dtype: RKRDLDataType {
1851 code: rkr_dl_type_code::RKR_DL_FLOAT,
1852 bits: 64,
1853 lanes: 1,
1854 },
1855 device: RKRDLDevice {
1856 device_type: rkr_dl_device_type::RKR_DL_CPU,
1857 device_id: 0,
1858 },
1859 }
1860 }
1861}
1862
1863fn resolve_dlpack_opts(
1866 opts: *const RKRDlpackExportOptions,
1867) -> Result<RKRDlpackExportOptions, RKRStatus> {
1868 let o = if opts.is_null() {
1869 RKRDlpackExportOptions::default()
1870 } else {
1871 unsafe { *opts }
1872 };
1873 let dt = o.device.device_type;
1874 if dt == rkr_dl_device_type::RKR_DL_CPU {
1875 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
1877 #[cfg(not(feature = "cuda"))]
1878 {
1879 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1880 }
1881 #[cfg(feature = "cuda")]
1882 {
1883 }
1885 } else {
1886 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
1887 }
1888 if o.dtype.lanes != 1 {
1889 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
1890 }
1891 Ok(o)
1892}
1893
1894fn finish_dlpack_tensor<E: std::fmt::Display>(
1895 result: Result<dlpk::DLPackTensor, E>,
1896 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1897) -> RKRStatus {
1898 match result {
1899 Ok(tensor) => {
1900 let raw = tensor.into_raw();
1901 unsafe {
1902 *out_tensor = raw.as_ptr();
1903 }
1904 RKRStatus::RKR_STATUS_SUCCESS
1905 }
1906 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
1907 "DLPack export failed: {e}"
1908 ))),
1909 }
1910}
1911
1912fn export_f64_slice_as_dlpack(
1915 data: &[f64],
1916 shape: &[usize],
1917 dtype: RKRDLDataType,
1918 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1919) -> RKRStatus {
1920 use rkr_dl_type_code::*;
1921 if dtype.lanes != 1 {
1922 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
1923 }
1924 macro_rules! arc_export {
1925 ($ty:ty, $map:expr) => {{
1926 let v: Vec<$ty> = data.iter().map($map).collect();
1927 match *shape {
1928 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
1929 Ok(a) => dlpk::DLPackTensor::try_from(a),
1930 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1931 },
1932 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
1933 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1934 }
1935 }};
1936 }
1937 let tensor = match (dtype.code, dtype.bits) {
1938 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
1939 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
1940 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
1941 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
1942 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
1943 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
1944 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
1945 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
1946 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
1947 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
1948 (RKR_DL_BOOL, 8) => {
1949 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
1951 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
1952 }
1953 _ => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1955 };
1956 finish_dlpack_tensor(tensor, out_tensor)
1957}
1958
1959fn export_owned_array2_dlpack_opts(
1960 arr: &ndarray::ArcArray2<f64>,
1961 opts: &RKRDlpackExportOptions,
1962 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1963) -> RKRStatus {
1964 let (r, c) = arr.dim();
1965 let flat: Vec<f64> = arr.iter().copied().collect();
1966 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
1967 #[cfg(feature = "cuda")]
1968 {
1969 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
1971 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
1972 }
1973 return finish_dlpack_tensor(
1974 crate::cuda_array::export_host_f64_as_cuda_dlpack(
1975 &[r, c],
1976 &flat,
1977 opts.device.device_id,
1978 ),
1979 out_tensor,
1980 );
1981 }
1982 #[cfg(not(feature = "cuda"))]
1983 {
1984 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
1985 }
1986 }
1987 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
1988}
1989
1990fn export_owned_array1_f64_dlpack_opts(
1991 arr: &ndarray::ArcArray1<f64>,
1992 opts: &RKRDlpackExportOptions,
1993 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1994) -> RKRStatus {
1995 let flat = arr.to_vec();
1996 let n = flat.len();
1997 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
1998 #[cfg(feature = "cuda")]
1999 {
2000 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
2001 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
2002 }
2003 return finish_dlpack_tensor(
2004 crate::cuda_array::export_host_f64_as_cuda_dlpack(
2005 &[n],
2006 &flat,
2007 opts.device.device_id,
2008 ),
2009 out_tensor,
2010 );
2011 }
2012 #[cfg(not(feature = "cuda"))]
2013 {
2014 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
2015 }
2016 }
2017 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
2018}
2019
2020fn export_owned_array1_u64_dlpack(
2021 arr: &ndarray::ArcArray1<u64>,
2022 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2023) -> RKRStatus {
2024 finish_dlpack_tensor(dlpk::DLPackTensor::try_from(arr.clone()), out_tensor)
2025}
2026
2027fn export_owned_array1_u64_dlpack_opts(
2029 arr: &ndarray::ArcArray1<u64>,
2030 opts: &RKRDlpackExportOptions,
2031 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2032) -> RKRStatus {
2033 use rkr_dl_type_code::*;
2034 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
2035 return export_owned_array1_u64_dlpack(arr, out_tensor);
2036 }
2037 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
2038 let n = as_f64.len();
2039 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
2040}
2041#[unsafe(no_mangle)]
2052pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
2053 builder_handle: *const RKRConFrameBuilder,
2054 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2055) -> RKRStatus {
2056 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2057}
2058
2059#[unsafe(no_mangle)]
2066pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
2067 builder_handle: *const RKRConFrameBuilder,
2068 opts: *const RKRDlpackExportOptions,
2069 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2070) -> RKRStatus {
2071 if builder_handle.is_null() || out_tensor.is_null() {
2072 return RKRStatus::RKR_STATUS_NULL_POINTER;
2073 }
2074 let o = match resolve_dlpack_opts(opts) {
2075 Ok(o) => o,
2076 Err(st) => return st,
2077 };
2078 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2079 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
2080}
2081
2082#[unsafe(no_mangle)]
2092pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
2093 builder_handle: *const RKRConFrameBuilder,
2094 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2095) -> RKRStatus {
2096 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2097}
2098
2099#[unsafe(no_mangle)]
2100pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
2101 builder_handle: *const RKRConFrameBuilder,
2102 opts: *const RKRDlpackExportOptions,
2103 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2104) -> RKRStatus {
2105 if builder_handle.is_null() || out_tensor.is_null() {
2106 return RKRStatus::RKR_STATUS_NULL_POINTER;
2107 }
2108 let o = match resolve_dlpack_opts(opts) {
2109 Ok(o) => o,
2110 Err(st) => return st,
2111 };
2112 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2113 if !builder.has_velocities_section() {
2114 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2115 }
2116 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
2117}
2118
2119#[unsafe(no_mangle)]
2128pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
2129 builder_handle: *const RKRConFrameBuilder,
2130 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2131) -> RKRStatus {
2132 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2133}
2134
2135#[unsafe(no_mangle)]
2136pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
2137 builder_handle: *const RKRConFrameBuilder,
2138 opts: *const RKRDlpackExportOptions,
2139 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2140) -> RKRStatus {
2141 if builder_handle.is_null() || out_tensor.is_null() {
2142 return RKRStatus::RKR_STATUS_NULL_POINTER;
2143 }
2144 let o = match resolve_dlpack_opts(opts) {
2145 Ok(o) => o,
2146 Err(st) => return st,
2147 };
2148 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2149 if !builder.has_forces_section() {
2150 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2151 }
2152 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
2153}
2154
2155#[unsafe(no_mangle)]
2164pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
2165 builder_handle: *const RKRConFrameBuilder,
2166 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2167) -> RKRStatus {
2168 unsafe {
2169 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
2170 }
2171}
2172
2173#[unsafe(no_mangle)]
2174pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
2175 builder_handle: *const RKRConFrameBuilder,
2176 opts: *const RKRDlpackExportOptions,
2177 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2178) -> RKRStatus {
2179 if builder_handle.is_null() || out_tensor.is_null() {
2180 return RKRStatus::RKR_STATUS_NULL_POINTER;
2181 }
2182 let o = match resolve_dlpack_opts(opts) {
2183 Ok(o) => o,
2184 Err(st) => return st,
2185 };
2186 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2187 if !builder.has_energies_section() {
2188 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
2189 }
2190 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
2191}
2192
2193#[unsafe(no_mangle)]
2199pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
2200 builder_handle: *const RKRConFrameBuilder,
2201 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2202) -> RKRStatus {
2203 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2204}
2205
2206#[unsafe(no_mangle)]
2207pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
2208 builder_handle: *const RKRConFrameBuilder,
2209 opts: *const RKRDlpackExportOptions,
2210 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2211) -> RKRStatus {
2212 if builder_handle.is_null() || out_tensor.is_null() {
2213 return RKRStatus::RKR_STATUS_NULL_POINTER;
2214 }
2215 let o = match resolve_dlpack_opts(opts) {
2216 Ok(o) => o,
2217 Err(st) => return st,
2218 };
2219 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2220 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
2221}
2222#[unsafe(no_mangle)]
2228pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
2229 builder_handle: *const RKRConFrameBuilder,
2230 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2231) -> RKRStatus {
2232 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
2233}
2234
2235#[unsafe(no_mangle)]
2236pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
2237 builder_handle: *const RKRConFrameBuilder,
2238 opts: *const RKRDlpackExportOptions,
2239 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
2240) -> RKRStatus {
2241 if builder_handle.is_null() || out_tensor.is_null() {
2242 return RKRStatus::RKR_STATUS_NULL_POINTER;
2243 }
2244 let o = match resolve_dlpack_opts(opts) {
2245 Ok(o) => o,
2246 Err(st) => return st,
2247 };
2248 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2249 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
2250}
2251#[unsafe(no_mangle)]
2275pub unsafe extern "C" fn rkr_frame_builder_positions_data(
2276 builder_handle: *mut RKRConFrameBuilder,
2277) -> *mut f64 {
2278 if builder_handle.is_null() {
2279 return std::ptr::null_mut();
2280 }
2281 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2282 builder
2283 .positions_view_mut()
2284 .as_slice_memory_order_mut()
2285 .map(|s| s.as_mut_ptr())
2286 .unwrap_or(std::ptr::null_mut())
2287}
2288#[unsafe(no_mangle)]
2295pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
2296 builder_handle: *mut RKRConFrameBuilder,
2297) -> *mut f64 {
2298 if builder_handle.is_null() {
2299 return std::ptr::null_mut();
2300 }
2301 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2302 if !builder.has_velocities_section() {
2303 return std::ptr::null_mut();
2304 }
2305 let slice = builder.velocities_mut();
2306 if slice.is_empty() {
2307 std::ptr::null_mut()
2308 } else {
2309 slice.as_mut_ptr()
2310 }
2311}
2312#[unsafe(no_mangle)]
2318pub unsafe extern "C" fn rkr_frame_builder_forces_data(
2319 builder_handle: *mut RKRConFrameBuilder,
2320) -> *mut f64 {
2321 if builder_handle.is_null() {
2322 return std::ptr::null_mut();
2323 }
2324 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2325 if !builder.has_forces_section() {
2326 return std::ptr::null_mut();
2327 }
2328 let slice = builder.forces_mut();
2329 if slice.is_empty() {
2330 std::ptr::null_mut()
2331 } else {
2332 slice.as_mut_ptr()
2333 }
2334}
2335#[unsafe(no_mangle)]
2341pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
2342 builder_handle: *mut RKRConFrameBuilder,
2343) -> *mut f64 {
2344 if builder_handle.is_null() {
2345 return std::ptr::null_mut();
2346 }
2347 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2348 if !builder.has_energies_section() {
2349 return std::ptr::null_mut();
2350 }
2351 let slice = builder.atom_energies_mut();
2352 if slice.is_empty() {
2353 std::ptr::null_mut()
2354 } else {
2355 slice.as_mut_ptr()
2356 }
2357}
2358#[unsafe(no_mangle)]
2363pub unsafe extern "C" fn rkr_frame_builder_masses_data(
2364 builder_handle: *mut RKRConFrameBuilder,
2365) -> *mut f64 {
2366 if builder_handle.is_null() {
2367 return std::ptr::null_mut();
2368 }
2369 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2370 let slice = builder.masses_mut();
2371 if slice.is_empty() {
2372 std::ptr::null_mut()
2373 } else {
2374 slice.as_mut_ptr()
2375 }
2376}
2377#[unsafe(no_mangle)]
2382pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
2383 builder_handle: *const RKRConFrameBuilder,
2384) -> *const u64 {
2385 if builder_handle.is_null() {
2386 return std::ptr::null();
2387 }
2388 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2389 let slice = builder.atom_ids();
2390 if slice.is_empty() {
2391 std::ptr::null()
2392 } else {
2393 slice.as_ptr()
2394 }
2395}
2396#[unsafe(no_mangle)]
2408#[allow(clippy::too_many_arguments)]
2409pub unsafe extern "C" fn rkr_frame_add_atom_full(
2410 builder_handle: *mut RKRConFrameBuilder,
2411 symbol: *const c_char,
2412 x: f64,
2413 y: f64,
2414 z: f64,
2415 fixed_x: bool,
2416 fixed_y: bool,
2417 fixed_z: bool,
2418 atom_id: u64,
2419 mass: f64,
2420 velocity: *const f64,
2421 force: *const f64,
2422) -> RKRStatus {
2423 let velocity = if velocity.is_null() {
2424 None
2425 } else {
2426 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
2427 };
2428 let force = if force.is_null() {
2429 None
2430 } else {
2431 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
2432 };
2433 unsafe {
2434 add_builder_atom(
2435 builder_handle,
2436 symbol,
2437 x,
2438 y,
2439 z,
2440 [fixed_x, fixed_y, fixed_z],
2441 atom_id,
2442 mass,
2443 velocity,
2444 force,
2445 )
2446 }
2447}
2448#[unsafe(no_mangle)]
2464pub unsafe extern "C" fn rkr_frame_new(
2465 cell: *const f64,
2466 angles: *const f64,
2467 prebox0: *const c_char,
2468 prebox1: *const c_char,
2469 postbox0: *const c_char,
2470 postbox1: *const c_char,
2471) -> *mut RKRConFrameBuilder {
2472 if cell.is_null() || angles.is_null() {
2473 return ptr::null_mut();
2474 }
2475 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
2476 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
2477 let get_str = |p: *const c_char| -> String {
2478 if p.is_null() {
2479 String::new()
2480 } else {
2481 unsafe { CStr::from_ptr(p) }
2482 .to_str()
2483 .unwrap_or("")
2484 .to_string()
2485 }
2486 };
2487 let _ = get_str(prebox1);
2490 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
2491 builder
2492 .prebox_header(get_str(prebox0))
2493 .postbox_header([get_str(postbox0), get_str(postbox1)]);
2494 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
2495}
2496#[unsafe(no_mangle)]
2502pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
2503 builder_handle: *mut RKRConFrameBuilder,
2504 metadata_json: *const c_char,
2505) -> RKRStatus {
2506 if builder_handle.is_null() || metadata_json.is_null() {
2507 return RKRStatus::RKR_STATUS_NULL_POINTER;
2508 }
2509 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2510 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
2511 Ok(s) => s,
2512 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2513 };
2514 match builder.set_metadata_json(metadata_json) {
2515 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
2516 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
2517 }
2518}
2519#[unsafe(no_mangle)]
2525pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
2526 builder_handle: *mut RKRConFrameBuilder,
2527 key: *const c_char,
2528 value: f64,
2529) -> RKRStatus {
2530 if builder_handle.is_null() || key.is_null() {
2531 return RKRStatus::RKR_STATUS_NULL_POINTER;
2532 }
2533 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2534 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2535 Ok(s) => s,
2536 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2537 };
2538 builder.set_scalar_metadata(key, value);
2539 RKRStatus::RKR_STATUS_SUCCESS
2540}
2541#[unsafe(no_mangle)]
2547pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
2548 builder_handle: *mut RKRConFrameBuilder,
2549 key: *const c_char,
2550 value: *const c_char,
2551) -> RKRStatus {
2552 if builder_handle.is_null() || key.is_null() || value.is_null() {
2553 return RKRStatus::RKR_STATUS_NULL_POINTER;
2554 }
2555 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2556 let key = match unsafe { CStr::from_ptr(key).to_str() } {
2557 Ok(s) => s,
2558 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2559 };
2560 let value = match unsafe { CStr::from_ptr(value).to_str() } {
2561 Ok(s) => s,
2562 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2563 };
2564 builder.set_string_metadata(key, value);
2565 RKRStatus::RKR_STATUS_SUCCESS
2566}
2567#[unsafe(no_mangle)]
2573pub unsafe extern "C" fn rkr_frame_builder_set_energy(
2574 builder_handle: *mut RKRConFrameBuilder,
2575 energy: f64,
2576) -> RKRStatus {
2577 if builder_handle.is_null() {
2578 return RKRStatus::RKR_STATUS_NULL_POINTER;
2579 }
2580 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2581 builder.set_energy(energy);
2582 RKRStatus::RKR_STATUS_SUCCESS
2583}
2584#[unsafe(no_mangle)]
2590pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
2591 builder_handle: *mut RKRConFrameBuilder,
2592 idx: u64,
2593) -> RKRStatus {
2594 if builder_handle.is_null() {
2595 return RKRStatus::RKR_STATUS_NULL_POINTER;
2596 }
2597 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2598 builder.set_frame_index(idx);
2599 RKRStatus::RKR_STATUS_SUCCESS
2600}
2601#[unsafe(no_mangle)]
2607pub unsafe extern "C" fn rkr_frame_builder_set_time(
2608 builder_handle: *mut RKRConFrameBuilder,
2609 time: f64,
2610) -> RKRStatus {
2611 if builder_handle.is_null() {
2612 return RKRStatus::RKR_STATUS_NULL_POINTER;
2613 }
2614 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2615 builder.set_time(time);
2616 RKRStatus::RKR_STATUS_SUCCESS
2617}
2618#[unsafe(no_mangle)]
2624pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
2625 builder_handle: *mut RKRConFrameBuilder,
2626 dt: f64,
2627) -> RKRStatus {
2628 if builder_handle.is_null() {
2629 return RKRStatus::RKR_STATUS_NULL_POINTER;
2630 }
2631 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2632 builder.set_timestep(dt);
2633 RKRStatus::RKR_STATUS_SUCCESS
2634}
2635#[unsafe(no_mangle)]
2641pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
2642 builder_handle: *mut RKRConFrameBuilder,
2643 bead: u64,
2644) -> RKRStatus {
2645 if builder_handle.is_null() {
2646 return RKRStatus::RKR_STATUS_NULL_POINTER;
2647 }
2648 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2649 builder.set_neb_bead(bead);
2650 RKRStatus::RKR_STATUS_SUCCESS
2651}
2652#[unsafe(no_mangle)]
2658pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
2659 builder_handle: *mut RKRConFrameBuilder,
2660 band: u64,
2661) -> RKRStatus {
2662 if builder_handle.is_null() {
2663 return RKRStatus::RKR_STATUS_NULL_POINTER;
2664 }
2665 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2666 builder.set_neb_band(band);
2667 RKRStatus::RKR_STATUS_SUCCESS
2668}
2669#[unsafe(no_mangle)]
2686pub unsafe extern "C" fn rkr_frame_add_atom(
2687 builder_handle: *mut RKRConFrameBuilder,
2688 symbol: *const c_char,
2689 x: f64,
2690 y: f64,
2691 z: f64,
2692 is_fixed: bool,
2693 atom_id: u64,
2694 mass: f64,
2695) -> RKRStatus {
2696 unsafe {
2697 add_builder_atom(
2698 builder_handle,
2699 symbol,
2700 x,
2701 y,
2702 z,
2703 [is_fixed; 3],
2704 atom_id,
2705 mass,
2706 None,
2707 None,
2708 )
2709 }
2710}
2711#[unsafe(no_mangle)]
2718pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
2719 builder_handle: *mut RKRConFrameBuilder,
2720 symbol: *const c_char,
2721 x: f64,
2722 y: f64,
2723 z: f64,
2724 fixed_x: bool,
2725 fixed_y: bool,
2726 fixed_z: bool,
2727 atom_id: u64,
2728 mass: f64,
2729) -> RKRStatus {
2730 unsafe {
2731 add_builder_atom(
2732 builder_handle,
2733 symbol,
2734 x,
2735 y,
2736 z,
2737 [fixed_x, fixed_y, fixed_z],
2738 atom_id,
2739 mass,
2740 None,
2741 None,
2742 )
2743 }
2744}
2745#[unsafe(no_mangle)]
2752pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
2753 builder_handle: *mut RKRConFrameBuilder,
2754 symbol: *const c_char,
2755 x: f64,
2756 y: f64,
2757 z: f64,
2758 is_fixed: bool,
2759 atom_id: u64,
2760 mass: f64,
2761 vx: f64,
2762 vy: f64,
2763 vz: f64,
2764) -> RKRStatus {
2765 unsafe {
2766 add_builder_atom(
2767 builder_handle,
2768 symbol,
2769 x,
2770 y,
2771 z,
2772 [is_fixed; 3],
2773 atom_id,
2774 mass,
2775 Some([vx, vy, vz]),
2776 None,
2777 )
2778 }
2779}
2780#[unsafe(no_mangle)]
2787pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
2788 builder_handle: *mut RKRConFrameBuilder,
2789 symbol: *const c_char,
2790 x: f64,
2791 y: f64,
2792 z: f64,
2793 fixed_x: bool,
2794 fixed_y: bool,
2795 fixed_z: bool,
2796 atom_id: u64,
2797 mass: f64,
2798 vx: f64,
2799 vy: f64,
2800 vz: f64,
2801) -> RKRStatus {
2802 unsafe {
2803 add_builder_atom(
2804 builder_handle,
2805 symbol,
2806 x,
2807 y,
2808 z,
2809 [fixed_x, fixed_y, fixed_z],
2810 atom_id,
2811 mass,
2812 Some([vx, vy, vz]),
2813 None,
2814 )
2815 }
2816}
2817#[unsafe(no_mangle)]
2824pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
2825 builder_handle: *mut RKRConFrameBuilder,
2826 symbol: *const c_char,
2827 x: f64,
2828 y: f64,
2829 z: f64,
2830 is_fixed: bool,
2831 atom_id: u64,
2832 mass: f64,
2833 fx: f64,
2834 fy: f64,
2835 fz: f64,
2836) -> RKRStatus {
2837 unsafe {
2838 add_builder_atom(
2839 builder_handle,
2840 symbol,
2841 x,
2842 y,
2843 z,
2844 [is_fixed; 3],
2845 atom_id,
2846 mass,
2847 None,
2848 Some([fx, fy, fz]),
2849 )
2850 }
2851}
2852#[unsafe(no_mangle)]
2859pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
2860 builder_handle: *mut RKRConFrameBuilder,
2861 symbol: *const c_char,
2862 x: f64,
2863 y: f64,
2864 z: f64,
2865 fixed_x: bool,
2866 fixed_y: bool,
2867 fixed_z: bool,
2868 atom_id: u64,
2869 mass: f64,
2870 fx: f64,
2871 fy: f64,
2872 fz: f64,
2873) -> RKRStatus {
2874 unsafe {
2875 add_builder_atom(
2876 builder_handle,
2877 symbol,
2878 x,
2879 y,
2880 z,
2881 [fixed_x, fixed_y, fixed_z],
2882 atom_id,
2883 mass,
2884 None,
2885 Some([fx, fy, fz]),
2886 )
2887 }
2888}
2889#[unsafe(no_mangle)]
2896pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
2897 builder_handle: *mut RKRConFrameBuilder,
2898 symbol: *const c_char,
2899 x: f64,
2900 y: f64,
2901 z: f64,
2902 is_fixed: bool,
2903 atom_id: u64,
2904 mass: f64,
2905 vx: f64,
2906 vy: f64,
2907 vz: f64,
2908 fx: f64,
2909 fy: f64,
2910 fz: f64,
2911) -> RKRStatus {
2912 unsafe {
2913 add_builder_atom(
2914 builder_handle,
2915 symbol,
2916 x,
2917 y,
2918 z,
2919 [is_fixed; 3],
2920 atom_id,
2921 mass,
2922 Some([vx, vy, vz]),
2923 Some([fx, fy, fz]),
2924 )
2925 }
2926}
2927#[unsafe(no_mangle)]
2934pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
2935 builder_handle: *mut RKRConFrameBuilder,
2936 symbol: *const c_char,
2937 x: f64,
2938 y: f64,
2939 z: f64,
2940 fixed_x: bool,
2941 fixed_y: bool,
2942 fixed_z: bool,
2943 atom_id: u64,
2944 mass: f64,
2945 vx: f64,
2946 vy: f64,
2947 vz: f64,
2948 fx: f64,
2949 fy: f64,
2950 fz: f64,
2951) -> RKRStatus {
2952 unsafe {
2953 add_builder_atom(
2954 builder_handle,
2955 symbol,
2956 x,
2957 y,
2958 z,
2959 [fixed_x, fixed_y, fixed_z],
2960 atom_id,
2961 mass,
2962 Some([vx, vy, vz]),
2963 Some([fx, fy, fz]),
2964 )
2965 }
2966}
2967#[unsafe(no_mangle)]
2976pub unsafe extern "C" fn rkr_frame_builder_build(
2977 builder_handle: *mut RKRConFrameBuilder,
2978) -> *mut RKRConFrame {
2979 if builder_handle.is_null() {
2980 return ptr::null_mut();
2981 }
2982 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2983 match builder.build() {
2984 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
2985 Err(_) => ptr::null_mut(),
2986 }
2987}
2988#[unsafe(no_mangle)]
2993pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
2994 if !builder_handle.is_null() {
2995 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2996 }
2997}
2998#[unsafe(no_mangle)]
3015pub unsafe extern "C" fn rkr_frame_builder_clone(
3016 builder_handle: *const RKRConFrameBuilder,
3017) -> *mut RKRConFrameBuilder {
3018 if builder_handle.is_null() {
3019 return std::ptr::null_mut();
3020 }
3021 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3022 let cloned = builder.clone();
3023 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
3024}
3025#[unsafe(no_mangle)]
3031pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3032 let filename = match unsafe { cstr_path(filename_c) } {
3033 Some(s) => s,
3034 None => return ptr::null_mut(),
3035 };
3036 match crate::compression::gzip_writer(Path::new(filename)) {
3037 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3038 Err(_) => ptr::null_mut(),
3039 }
3040}
3041#[unsafe(no_mangle)]
3048pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
3049 filename_c: *const c_char,
3050 precision: u8,
3051) -> *mut RKRConFrameWriter {
3052 let filename = match unsafe { cstr_path(filename_c) } {
3053 Some(s) => s,
3054 None => return ptr::null_mut(),
3055 };
3056 match crate::compression::gzip_writer(Path::new(filename)) {
3057 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3058 Err(_) => ptr::null_mut(),
3059 }
3060}
3061#[cfg(feature = "zstd")]
3071#[unsafe(no_mangle)]
3072pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
3073 let filename = match unsafe { cstr_path(filename_c) } {
3074 Some(s) => s,
3075 None => return ptr::null_mut(),
3076 };
3077 match crate::compression::zstd_writer(Path::new(filename)) {
3078 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
3079 Err(_) => ptr::null_mut(),
3080 }
3081}
3082#[cfg(feature = "zstd")]
3093#[unsafe(no_mangle)]
3094pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3095 filename_c: *const c_char,
3096 precision: u8,
3097) -> *mut RKRConFrameWriter {
3098 let filename = match unsafe { cstr_path(filename_c) } {
3099 Some(s) => s,
3100 None => return ptr::null_mut(),
3101 };
3102 match crate::compression::zstd_writer(Path::new(filename)) {
3103 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
3104 Err(_) => ptr::null_mut(),
3105 }
3106}
3107#[unsafe(no_mangle)]
3119pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
3120 if filename_c.is_null() {
3121 return ptr::null_mut();
3122 }
3123 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3124 Ok(s) => s,
3125 Err(_) => return ptr::null_mut(),
3126 };
3127 match iterators::read_first_frame(Path::new(filename)) {
3128 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
3129 Err(_) => ptr::null_mut(),
3130 }
3131}
3132#[unsafe(no_mangle)]
3141pub unsafe extern "C" fn rkr_read_all_frames(
3142 filename_c: *const c_char,
3143 num_frames: *mut usize,
3144) -> *mut *mut RKRConFrame {
3145 if filename_c.is_null() || num_frames.is_null() {
3146 return ptr::null_mut();
3147 }
3148 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3149 Ok(s) => s,
3150 Err(_) => return ptr::null_mut(),
3151 };
3152 match iterators::read_all_frames(Path::new(filename)) {
3153 Ok(frames) => pack_frame_handles(frames, num_frames),
3154 Err(_) => ptr::null_mut(),
3155 }
3156}
3157
3158#[unsafe(no_mangle)]
3168pub unsafe extern "C" fn rkr_read_all_frames_n_threads(
3169 filename_c: *const c_char,
3170 num_frames: *mut usize,
3171 n_threads: usize,
3172) -> *mut *mut RKRConFrame {
3173 if filename_c.is_null() || num_frames.is_null() {
3174 return ptr::null_mut();
3175 }
3176 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
3177 Ok(s) => s,
3178 Err(_) => return ptr::null_mut(),
3179 };
3180 let threads = if n_threads == 0 {
3181 None
3182 } else {
3183 Some(n_threads)
3184 };
3185 match iterators::read_all_frames_with_threads(Path::new(filename), threads) {
3186 Ok(frames) => pack_frame_handles(frames, num_frames),
3187 Err(_) => ptr::null_mut(),
3188 }
3189}
3190
3191fn pack_frame_handles(frames: Vec<ConFrame>, num_frames: *mut usize) -> *mut *mut RKRConFrame {
3193 let count = frames.len();
3194 let mut handles: Vec<*mut RKRConFrame> = frames
3195 .into_iter()
3196 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
3197 .collect();
3198 handles.shrink_to_fit();
3199 debug_assert_eq!(handles.len(), handles.capacity());
3200 let ptr = handles.as_mut_ptr();
3201 std::mem::forget(handles);
3202 unsafe { *num_frames = count };
3203 ptr
3204}
3205#[unsafe(no_mangle)]
3211pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
3212 if frames.is_null() {
3213 return;
3214 }
3215 unsafe {
3216 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
3217 for handle in handles {
3218 if !handle.is_null() {
3219 let _ = Box::from_raw(handle as *mut ConFrame);
3220 }
3221 }
3222 }
3223}
3224#[unsafe(no_mangle)]
3230pub unsafe extern "C" fn free_rkr_frame_ptr_array(
3231 frames: *mut *mut RKRConFrame,
3232 num_frames: usize,
3233) {
3234 if frames.is_null() {
3235 return;
3236 }
3237 unsafe {
3238 let _ptrs = Vec::from_raw_parts(frames, num_frames, num_frames);
3239 }
3240}
3241
3242#[cfg(feature = "metatensor")]
3257#[unsafe(no_mangle)]
3258pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
3259 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
3260}
3261#[cfg(feature = "metatensor")]
3263#[unsafe(no_mangle)]
3264pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3265 frame_handle: *const RKRConFrame,
3266 out_block: *mut *mut metatensor::c_api::mts_block_t,
3267) -> RKRStatus {
3268 if frame_handle.is_null() || out_block.is_null() {
3269 return RKRStatus::RKR_STATUS_NULL_POINTER;
3270 }
3271 unsafe { *out_block = std::ptr::null_mut() };
3272 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3273 return RKRStatus::RKR_STATUS_NULL_POINTER;
3274 };
3275 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3276 crate::metatensor_export::frame_positions_block(frame)
3277 })) {
3278 Ok(Ok(b)) => {
3279 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3280 RKRStatus::RKR_STATUS_SUCCESS
3281 }
3282 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3283 }
3284}
3285#[cfg(feature = "metatensor")]
3286#[unsafe(no_mangle)]
3287pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3288 frame_handle: *const RKRConFrame,
3289 out_block: *mut *mut metatensor::c_api::mts_block_t,
3290) -> RKRStatus {
3291 if frame_handle.is_null() || out_block.is_null() {
3292 return RKRStatus::RKR_STATUS_NULL_POINTER;
3293 }
3294 unsafe { *out_block = std::ptr::null_mut() };
3295 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3296 return RKRStatus::RKR_STATUS_NULL_POINTER;
3297 };
3298 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3299 crate::metatensor_export::frame_velocities_block(frame)
3300 })) {
3301 Ok(Ok(Some(b))) => {
3302 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3303 RKRStatus::RKR_STATUS_SUCCESS
3304 }
3305 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3306 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3307 }
3308}
3309#[cfg(feature = "metatensor")]
3310#[unsafe(no_mangle)]
3311pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3312 frame_handle: *const RKRConFrame,
3313 out_block: *mut *mut metatensor::c_api::mts_block_t,
3314) -> RKRStatus {
3315 if frame_handle.is_null() || out_block.is_null() {
3316 return RKRStatus::RKR_STATUS_NULL_POINTER;
3317 }
3318 unsafe { *out_block = std::ptr::null_mut() };
3319 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3320 return RKRStatus::RKR_STATUS_NULL_POINTER;
3321 };
3322 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3323 crate::metatensor_export::frame_forces_block(frame)
3324 })) {
3325 Ok(Ok(Some(b))) => {
3326 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3327 RKRStatus::RKR_STATUS_SUCCESS
3328 }
3329 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3330 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3331 }
3332}
3333#[cfg(feature = "metatensor")]
3334#[unsafe(no_mangle)]
3335pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3336 frame_handle: *const RKRConFrame,
3337 out_block: *mut *mut metatensor::c_api::mts_block_t,
3338) -> RKRStatus {
3339 if frame_handle.is_null() || out_block.is_null() {
3340 return RKRStatus::RKR_STATUS_NULL_POINTER;
3341 }
3342 unsafe { *out_block = std::ptr::null_mut() };
3343 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3344 return RKRStatus::RKR_STATUS_NULL_POINTER;
3345 };
3346 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3347 crate::metatensor_export::frame_energies_block(frame)
3348 })) {
3349 Ok(Ok(Some(b))) => {
3350 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
3351 RKRStatus::RKR_STATUS_SUCCESS
3352 }
3353 Ok(Ok(None)) => RKRStatus::RKR_STATUS_SECTION_ABSENT,
3354 Ok(Err(_)) | Err(_) => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
3355 }
3356}
3357#[cfg(not(feature = "zstd"))]
3358#[unsafe(no_mangle)]
3359pub unsafe extern "C" fn create_writer_zstd_c(
3360 _filename_c: *const c_char,
3361) -> *mut RKRConFrameWriter {
3362 ptr::null_mut()
3363}
3364#[cfg(not(feature = "zstd"))]
3365#[unsafe(no_mangle)]
3366pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
3367 _filename_c: *const c_char,
3368 _precision: u8,
3369) -> *mut RKRConFrameWriter {
3370 ptr::null_mut()
3371}
3372#[cfg(not(feature = "metatensor"))]
3376#[repr(C)]
3377pub struct mts_block_t {
3378 _private: [u8; 0],
3379}
3380#[cfg(not(feature = "metatensor"))]
3381#[unsafe(no_mangle)]
3382pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
3383#[cfg(not(feature = "metatensor"))]
3384#[unsafe(no_mangle)]
3385pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
3386 _frame_handle: *const RKRConFrame,
3387 out_block: *mut *mut mts_block_t,
3388) -> RKRStatus {
3389 if !out_block.is_null() {
3390 unsafe { *out_block = std::ptr::null_mut() };
3391 }
3392 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3393}
3394#[cfg(not(feature = "metatensor"))]
3395#[unsafe(no_mangle)]
3396pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
3397 _frame_handle: *const RKRConFrame,
3398 out_block: *mut *mut mts_block_t,
3399) -> RKRStatus {
3400 if !out_block.is_null() {
3401 unsafe { *out_block = std::ptr::null_mut() };
3402 }
3403 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3404}
3405#[cfg(not(feature = "metatensor"))]
3406#[unsafe(no_mangle)]
3407pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
3408 _frame_handle: *const RKRConFrame,
3409 out_block: *mut *mut mts_block_t,
3410) -> RKRStatus {
3411 if !out_block.is_null() {
3412 unsafe { *out_block = std::ptr::null_mut() };
3413 }
3414 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3415}
3416#[cfg(not(feature = "metatensor"))]
3417#[unsafe(no_mangle)]
3418pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
3419 _frame_handle: *const RKRConFrame,
3420 out_block: *mut *mut mts_block_t,
3421) -> RKRStatus {
3422 if !out_block.is_null() {
3423 unsafe { *out_block = std::ptr::null_mut() };
3424 }
3425 RKRStatus::RKR_STATUS_FEATURE_DISABLED
3426}
3427#[unsafe(no_mangle)]
3430pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
3431 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3432 return 0;
3433 };
3434 frame.atom_data.len()
3435}
3436#[unsafe(no_mangle)]
3438pub unsafe extern "C" fn rkr_frame_xyz_view(
3439 frame_handle: *const RKRConFrame,
3440 out: *mut RKRArrayView,
3441) -> RKRStatus {
3442 fill_array2_view(frame_handle, out, |f| &f.positions, false)
3443}
3444
3445#[unsafe(no_mangle)]
3447pub unsafe extern "C" fn rkr_frame_velocities_view(
3448 frame_handle: *const RKRConFrame,
3449 out: *mut RKRArrayView,
3450) -> RKRStatus {
3451 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
3452}
3453
3454#[unsafe(no_mangle)]
3456pub unsafe extern "C" fn rkr_frame_forces_view(
3457 frame_handle: *const RKRConFrame,
3458 out: *mut RKRArrayView,
3459) -> RKRStatus {
3460 fill_array2_view(frame_handle, out, |f| &f.forces, true)
3461}
3462
3463#[unsafe(no_mangle)]
3465pub unsafe extern "C" fn rkr_frame_energies_view(
3466 frame_handle: *const RKRConFrame,
3467 out: *mut RKRArrayView,
3468) -> RKRStatus {
3469 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
3470}
3471
3472#[unsafe(no_mangle)]
3474pub unsafe extern "C" fn rkr_frame_masses_view(
3475 frame_handle: *const RKRConFrame,
3476 out: *mut RKRArrayView,
3477) -> RKRStatus {
3478 fill_array1_view(frame_handle, out, |f| &f.masses, false)
3479}
3480
3481#[unsafe(no_mangle)]
3483pub unsafe extern "C" fn rkr_frame_atom_ids_view(
3484 frame_handle: *const RKRConFrame,
3485 out: *mut RKRArrayView,
3486) -> RKRStatus {
3487 if frame_handle.is_null() || out.is_null() {
3488 return RKRStatus::RKR_STATUS_NULL_POINTER;
3489 }
3490 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3491 return RKRStatus::RKR_STATUS_NULL_POINTER;
3492 };
3493 let slice = frame.atom_ids.as_slice_memory_order();
3494 unsafe {
3495 *out = RKRArrayView {
3496 data: slice
3497 .map(|s| s.as_ptr() as *const std::ffi::c_void)
3498 .unwrap_or(std::ptr::null()),
3499 n: frame.atom_ids.len(),
3500 cols: 1,
3501 dtype_code: 1, dtype_bits: 64,
3503 };
3504 }
3505 RKRStatus::RKR_STATUS_SUCCESS
3506}
3507
3508#[unsafe(no_mangle)]
3511pub unsafe extern "C" fn rkr_frame_xyz_f64(
3512 frame_handle: *const RKRConFrame,
3513 n: *mut usize,
3514) -> *const f64 {
3515 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
3516}
3517
3518#[unsafe(no_mangle)]
3520pub unsafe extern "C" fn rkr_frame_velocities_f64(
3521 frame_handle: *const RKRConFrame,
3522 n: *mut usize,
3523) -> *const f64 {
3524 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
3525}
3526
3527#[unsafe(no_mangle)]
3529pub unsafe extern "C" fn rkr_frame_forces_f64(
3530 frame_handle: *const RKRConFrame,
3531 n: *mut usize,
3532) -> *const f64 {
3533 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
3534}
3535
3536fn fill_array2_view(
3537 frame_handle: *const RKRConFrame,
3538 out: *mut RKRArrayView,
3539 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
3540 section: bool,
3541) -> RKRStatus {
3542 if frame_handle.is_null() || out.is_null() {
3543 return RKRStatus::RKR_STATUS_NULL_POINTER;
3544 }
3545 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3546 return RKRStatus::RKR_STATUS_NULL_POINTER;
3547 };
3548 let arr = get(frame);
3549 if section && arr.nrows() == 0 {
3550 unsafe { *out = RKRArrayView::empty() };
3551 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3552 }
3553 unsafe { *out = RKRArrayView::from_array2(arr) };
3554 RKRStatus::RKR_STATUS_SUCCESS
3555}
3556
3557fn fill_array1_view(
3558 frame_handle: *const RKRConFrame,
3559 out: *mut RKRArrayView,
3560 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
3561 section: bool,
3562) -> RKRStatus {
3563 if frame_handle.is_null() || out.is_null() {
3564 return RKRStatus::RKR_STATUS_NULL_POINTER;
3565 }
3566 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3567 return RKRStatus::RKR_STATUS_NULL_POINTER;
3568 };
3569 let arr = get(frame);
3570 if section && arr.len() == 0 {
3571 unsafe { *out = RKRArrayView::empty() };
3572 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3573 }
3574 unsafe { *out = RKRArrayView::from_array1(arr) };
3575 RKRStatus::RKR_STATUS_SUCCESS
3576}
3577
3578fn f64_col_ptr(
3579 frame_handle: *const RKRConFrame,
3580 n: *mut usize,
3581 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
3582) -> *const f64 {
3583 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3584 if !n.is_null() {
3585 unsafe { *n = 0 };
3586 }
3587 return std::ptr::null();
3588 };
3589 match get(frame) {
3590 Some(s) if !s.is_empty() => {
3591 if !n.is_null() {
3592 unsafe { *n = s.len() / 3 };
3593 }
3594 s.as_ptr()
3595 }
3596 _ => {
3597 if !n.is_null() {
3598 unsafe { *n = 0 };
3599 }
3600 std::ptr::null()
3601 }
3602 }
3603}
3604
3605#[unsafe(no_mangle)]
3608pub unsafe extern "C" fn rkr_frame_copy_positions(
3609 frame_handle: *const RKRConFrame,
3610 out: *mut f64,
3611 out_len: usize,
3612) -> RKRStatus {
3613 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
3614}
3615fn copy_array2_f64(
3616 frame_handle: *const RKRConFrame,
3617 out: *mut f64,
3618 out_len: usize,
3619 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
3620 section: bool,
3621) -> RKRStatus {
3622 if frame_handle.is_null() || out.is_null() {
3623 return RKRStatus::RKR_STATUS_NULL_POINTER;
3624 }
3625 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3626 return RKRStatus::RKR_STATUS_NULL_POINTER;
3627 };
3628 let arr = get(frame);
3629 let n = if arr.nrows() > 0 {
3630 arr.nrows()
3631 } else {
3632 frame.atom_data.len()
3633 };
3634 if section && arr.nrows() == 0 {
3635 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3636 }
3637 let need = n.saturating_mul(3);
3638 if out_len < need {
3639 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3640 }
3641 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
3642 if arr.nrows() == n {
3643 if let Some(src) = arr.f64_slice() {
3644 dest.copy_from_slice(&src[..need.min(src.len())]);
3645 return RKRStatus::RKR_STATUS_SUCCESS;
3646 }
3647 for i in 0..n {
3648 let row = arr.as_f64_row(i);
3649 dest[i * 3] = row[0];
3650 dest[i * 3 + 1] = row[1];
3651 dest[i * 3 + 2] = row[2];
3652 }
3653 return RKRStatus::RKR_STATUS_SUCCESS;
3654 }
3655 for (i, a) in frame.atom_data.iter().enumerate() {
3656 dest[i * 3] = a.x;
3657 dest[i * 3 + 1] = a.y;
3658 dest[i * 3 + 2] = a.z;
3659 }
3660 RKRStatus::RKR_STATUS_SUCCESS
3661}
3662
3663fn copy_array1_f64(
3664 frame_handle: *const RKRConFrame,
3665 out: *mut f64,
3666 out_len: usize,
3667 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
3668 section: bool,
3669) -> RKRStatus {
3670 if frame_handle.is_null() || out.is_null() {
3671 return RKRStatus::RKR_STATUS_NULL_POINTER;
3672 }
3673 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3674 return RKRStatus::RKR_STATUS_NULL_POINTER;
3675 };
3676 let arr = get(frame);
3677 let n = if arr.len() > 0 {
3678 arr.len()
3679 } else {
3680 frame.atom_data.len()
3681 };
3682 if section && arr.len() == 0 {
3683 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3684 }
3685 if out_len < n {
3686 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3687 }
3688 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
3689 if let Some(src) = arr.f64_slice() {
3690 dest.copy_from_slice(&src[..n.min(src.len())]);
3691 return RKRStatus::RKR_STATUS_SUCCESS;
3692 }
3693 for i in 0..n {
3694 dest[i] = arr.get_f64(i);
3695 }
3696 RKRStatus::RKR_STATUS_SUCCESS
3697}
3698
3699#[unsafe(no_mangle)]
3700pub unsafe extern "C" fn rkr_frame_copy_velocities(
3701 frame_handle: *const RKRConFrame,
3702 out: *mut f64,
3703 out_len: usize,
3704) -> RKRStatus {
3705 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
3706}
3707#[unsafe(no_mangle)]
3708pub unsafe extern "C" fn rkr_frame_copy_forces(
3709 frame_handle: *const RKRConFrame,
3710 out: *mut f64,
3711 out_len: usize,
3712) -> RKRStatus {
3713 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
3714}
3715#[unsafe(no_mangle)]
3716pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
3717 frame_handle: *const RKRConFrame,
3718 out: *mut f64,
3719 out_len: usize,
3720) -> RKRStatus {
3721 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
3722}
3723#[unsafe(no_mangle)]
3724pub unsafe extern "C" fn rkr_frame_copy_masses(
3725 frame_handle: *const RKRConFrame,
3726 out: *mut f64,
3727 out_len: usize,
3728) -> RKRStatus {
3729 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
3730}
3731#[unsafe(no_mangle)]
3732pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
3733 frame_handle: *const RKRConFrame,
3734 out: *mut u64,
3735 out_len: usize,
3736) -> RKRStatus {
3737 if frame_handle.is_null() || out.is_null() {
3738 return RKRStatus::RKR_STATUS_NULL_POINTER;
3739 }
3740 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3741 return RKRStatus::RKR_STATUS_NULL_POINTER;
3742 };
3743 let n = frame.atom_ids.len().max(frame.atom_data.len());
3744 if out_len < n {
3745 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
3746 }
3747 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
3748 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
3749 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
3750 return RKRStatus::RKR_STATUS_SUCCESS;
3751 }
3752 for (i, a) in frame.atom_data.iter().enumerate() {
3753 dest[i] = a.atom_id;
3754 }
3755 RKRStatus::RKR_STATUS_SUCCESS
3756}
3757fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
3758 let n = frame.atom_data.len();
3759 let mut data = Vec::with_capacity(n * 3);
3760 for a in &frame.atom_data {
3761 data.extend_from_slice(&[a.x, a.y, a.z]);
3762 }
3763 ndarray::ArcArray2::from_shape_vec((n, 3), data)
3764 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
3765}
3766
3767#[unsafe(no_mangle)]
3777pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
3778 frame_handle: *const RKRConFrame,
3779 device_type: i32,
3780 device_id: i32,
3781 _stream: i64,
3782 _max_version_major: u32,
3783 _max_version_minor: u32,
3784 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3785) -> RKRStatus {
3786 if frame_handle.is_null() || out_tensor.is_null() {
3787 return RKRStatus::RKR_STATUS_NULL_POINTER;
3788 }
3789 unsafe { *out_tensor = std::ptr::null_mut() };
3790 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
3791 dlpk::sys::DLDevice::cpu()
3792 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
3793 #[cfg(feature = "cuda")]
3794 {
3795 dlpk::sys::DLDevice::cuda(device_id)
3796 }
3797 #[cfg(not(feature = "cuda"))]
3798 {
3799 let _ = device_id;
3800 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3801 }
3802 } else {
3803 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3804 };
3805 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3806 return RKRStatus::RKR_STATUS_NULL_POINTER;
3807 };
3808 match frame.positions_as_dlpack(dl_device) {
3810 Ok(tensor) => {
3811 let raw = tensor.into_raw();
3812 unsafe {
3813 *out_tensor = raw.as_ptr();
3814 }
3815 RKRStatus::RKR_STATUS_SUCCESS
3816 }
3817 Err(e) => map_dlpack_err(e),
3818 }
3819}
3820
3821#[unsafe(no_mangle)]
3827pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
3828 frame_handle: *mut RKRConFrame,
3829 tensor: *const RKRDLManagedTensorVersioned,
3830) -> RKRStatus {
3831 if frame_handle.is_null() || tensor.is_null() {
3832 return RKRStatus::RKR_STATUS_NULL_POINTER;
3833 }
3834 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
3835 let dl = unsafe { &(*tensor).dl_tensor };
3836 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
3837 return RKRStatus::RKR_STATUS_FEATURE_DISABLED;
3838 }
3839 let n = frame.atom_data.len();
3840 let need = n.saturating_mul(3);
3841 let ndim = dl.ndim as usize;
3842 let shape = if dl.shape.is_null() {
3843 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3844 } else {
3845 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
3846 };
3847 let nelem = if ndim == 2 && shape[0] == n as i64 && shape[1] == 3 {
3848 need
3849 } else if ndim == 1 && shape[0] == need as i64 {
3850 need
3851 } else {
3852 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3853 };
3854 let code = dl.dtype.code as u8;
3855 let bits = dl.dtype.bits;
3856 if dl.data.is_null() {
3857 return RKRStatus::RKR_STATUS_NULL_POINTER;
3858 }
3859 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
3860 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
3861 s.to_vec()
3862 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
3863 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
3864 s.iter().map(|&x| x as f64).collect()
3865 } else {
3866 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3867 };
3868 if frame.positions.nrows() != n {
3869 return RKRStatus::RKR_STATUS_VALIDATION_ERROR;
3870 }
3871 for i in 0..n {
3872 frame
3873 .positions
3874 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
3875 }
3876 frame.sync_atom_data_from_arrays();
3877 RKRStatus::RKR_STATUS_SUCCESS
3878}
3879
3880#[unsafe(no_mangle)]
3883pub unsafe extern "C" fn rkr_frame_positions_dlpack(
3884 frame_handle: *const RKRConFrame,
3885 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3886) -> RKRStatus {
3887 unsafe {
3888 rkr_frame_positions_as_dlpack(
3889 frame_handle,
3890 rkr_dl_device_type::RKR_DL_CPU,
3891 0,
3892 0,
3893 1,
3894 0,
3895 out_tensor,
3896 )
3897 }
3898}
3899
3900#[unsafe(no_mangle)]
3905pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
3906 frame_handle: *const RKRConFrame,
3907 opts: *const RKRDlpackExportOptions,
3908 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3909) -> RKRStatus {
3910 if frame_handle.is_null() || out_tensor.is_null() {
3911 return RKRStatus::RKR_STATUS_NULL_POINTER;
3912 }
3913 unsafe { *out_tensor = std::ptr::null_mut() };
3914 let o = match resolve_dlpack_opts(opts) {
3915 Ok(o) => o,
3916 Err(st) => return st,
3917 };
3918 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3919 return RKRStatus::RKR_STATUS_NULL_POINTER;
3920 };
3921 let arr = frame_positions_arc(frame);
3922 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3923}
3924
3925#[unsafe(no_mangle)]
3927pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
3928 frame_handle: *const RKRConFrame,
3929 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3930) -> RKRStatus {
3931 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3932}
3933
3934#[unsafe(no_mangle)]
3935pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
3936 frame_handle: *const RKRConFrame,
3937 opts: *const RKRDlpackExportOptions,
3938 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3939) -> RKRStatus {
3940 if frame_handle.is_null() || out_tensor.is_null() {
3941 return RKRStatus::RKR_STATUS_NULL_POINTER;
3942 }
3943 unsafe { *out_tensor = std::ptr::null_mut() };
3944 let o = match resolve_dlpack_opts(opts) {
3945 Ok(o) => o,
3946 Err(st) => return st,
3947 };
3948 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3949 return RKRStatus::RKR_STATUS_NULL_POINTER;
3950 };
3951 if !frame.has_velocities() {
3952 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3953 }
3954 let n = frame.atom_data.len();
3955 let mut data = Vec::with_capacity(n * 3);
3956 for a in &frame.atom_data {
3957 let v = a.velocity.unwrap_or([0.0; 3]);
3958 data.extend_from_slice(&v);
3959 }
3960 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
3961 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
3962 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
3963}
3964
3965#[unsafe(no_mangle)]
3967pub unsafe extern "C" fn rkr_frame_forces_dlpack(
3968 frame_handle: *const RKRConFrame,
3969 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3970) -> RKRStatus {
3971 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
3972}
3973
3974#[unsafe(no_mangle)]
3975pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
3976 frame_handle: *const RKRConFrame,
3977 opts: *const RKRDlpackExportOptions,
3978 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3979) -> RKRStatus {
3980 if frame_handle.is_null() || out_tensor.is_null() {
3981 return RKRStatus::RKR_STATUS_NULL_POINTER;
3982 }
3983 unsafe { *out_tensor = std::ptr::null_mut() };
3984 let o = match resolve_dlpack_opts(opts) {
3985 Ok(o) => o,
3986 Err(st) => return st,
3987 };
3988 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
3989 return RKRStatus::RKR_STATUS_NULL_POINTER;
3990 };
3991 if !frame.has_forces() {
3992 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
3993 }
3994 let n = frame.atom_data.len();
3995 let mut data = Vec::with_capacity(n * 3);
3996 for a in &frame.atom_data {
3997 let f = a.force.unwrap_or([0.0; 3]);
3998 data.extend_from_slice(&f);
3999 }
4000 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
4001 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
4002 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
4003}
4004
4005#[unsafe(no_mangle)]
4007pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
4008 frame_handle: *const RKRConFrame,
4009 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4010) -> RKRStatus {
4011 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
4012}
4013
4014#[unsafe(no_mangle)]
4015pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
4016 frame_handle: *const RKRConFrame,
4017 opts: *const RKRDlpackExportOptions,
4018 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
4019) -> RKRStatus {
4020 if frame_handle.is_null() || out_tensor.is_null() {
4021 return RKRStatus::RKR_STATUS_NULL_POINTER;
4022 }
4023 unsafe { *out_tensor = std::ptr::null_mut() };
4024 let o = match resolve_dlpack_opts(opts) {
4025 Ok(o) => o,
4026 Err(st) => return st,
4027 };
4028 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4029 return RKRStatus::RKR_STATUS_NULL_POINTER;
4030 };
4031 if !frame.has_energies() {
4032 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4033 }
4034 let data: Vec<f64> = frame
4035 .atom_data
4036 .iter()
4037 .map(|a| a.energy.unwrap_or(0.0))
4038 .collect();
4039 let arr = ndarray::ArcArray1::from_vec(data);
4040 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
4041}
4042
4043pub struct RKRSelectionResult;
4047#[unsafe(no_mangle)]
4058pub unsafe extern "C" fn rkr_frame_select(
4059 frame_handle: *const RKRConFrame,
4060 selection: *const c_char,
4061 out_result: *mut *mut RKRSelectionResult,
4062) -> RKRStatus {
4063 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
4064 return RKRStatus::RKR_STATUS_NULL_POINTER;
4065 }
4066 let frame = unsafe { &*(frame_handle as *const ConFrame) };
4067 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
4068 Ok(s) => s,
4069 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
4070 };
4071 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
4072 Ok(result) => {
4073 let boxed = Box::new(result);
4074 unsafe {
4075 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
4076 }
4077 RKRStatus::RKR_STATUS_SUCCESS
4078 }
4079 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
4080 }
4081}
4082#[unsafe(no_mangle)]
4087pub unsafe extern "C" fn rkr_selection_result_match_count(
4088 result_handle: *const RKRSelectionResult,
4089) -> u64 {
4090 if result_handle.is_null() {
4091 return 0;
4092 }
4093 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4094 result.matches.len() as u64
4095}
4096#[unsafe(no_mangle)]
4101pub unsafe extern "C" fn rkr_selection_result_context_size(
4102 result_handle: *const RKRSelectionResult,
4103) -> u32 {
4104 if result_handle.is_null() {
4105 return 0;
4106 }
4107 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4108 result.context_size as u32
4109}
4110#[unsafe(no_mangle)]
4116pub unsafe extern "C" fn rkr_selection_result_match_at(
4117 result_handle: *const RKRSelectionResult,
4118 match_index: u64,
4119 out_atoms: *mut u64,
4120 out_size: *mut u32,
4121) -> RKRStatus {
4122 if result_handle.is_null() || out_atoms.is_null() {
4123 return RKRStatus::RKR_STATUS_NULL_POINTER;
4124 }
4125 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4126 let idx = match_index as usize;
4127 if idx >= result.matches.len() {
4128 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
4129 }
4130 let m = &result.matches[idx];
4131 unsafe {
4132 for i in 0..4 {
4133 *out_atoms.add(i) = if i < m.size {
4134 m.atoms[i] as u64
4135 } else {
4136 u64::MAX
4137 };
4138 }
4139 if !out_size.is_null() {
4140 *out_size = m.size as u32;
4141 }
4142 }
4143 RKRStatus::RKR_STATUS_SUCCESS
4144}
4145#[unsafe(no_mangle)]
4151pub unsafe extern "C" fn rkr_selection_result_primary_indices(
4152 result_handle: *const RKRSelectionResult,
4153 out_indices: *mut u64,
4154 capacity: u64,
4155 out_written: *mut u64,
4156) -> RKRStatus {
4157 if result_handle.is_null() {
4158 return RKRStatus::RKR_STATUS_NULL_POINTER;
4159 }
4160 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
4161 let n = result.matches.len() as u64;
4162 if !out_written.is_null() {
4163 unsafe {
4164 *out_written = n;
4165 }
4166 }
4167 if n == 0 {
4168 return RKRStatus::RKR_STATUS_SUCCESS;
4169 }
4170 if out_indices.is_null() {
4171 return RKRStatus::RKR_STATUS_NULL_POINTER;
4172 }
4173 if capacity < n {
4174 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4175 }
4176 unsafe {
4177 for (i, m) in result.matches.iter().enumerate() {
4178 *out_indices.add(i) = m.atoms[0] as u64;
4179 }
4180 }
4181 RKRStatus::RKR_STATUS_SUCCESS
4182}
4183#[unsafe(no_mangle)]
4188pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
4189 if result_handle.is_null() {
4190 return;
4191 }
4192 unsafe {
4193 drop(Box::from_raw(
4194 result_handle as *mut crate::chemfiles_selection::SelectionResult,
4195 ));
4196 }
4197}
4198#[unsafe(no_mangle)]
4200pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
4201 #[cfg(feature = "chemfiles")]
4202 {
4203 1
4204 }
4205 #[cfg(not(feature = "chemfiles"))]
4206 {
4207 0
4208 }
4209}
4210
4211#[unsafe(no_mangle)]
4213pub extern "C" fn rkr_has_parallel_support() -> u8 {
4214 #[cfg(feature = "parallel")]
4215 {
4216 1
4217 }
4218 #[cfg(not(feature = "parallel"))]
4219 {
4220 0
4221 }
4222}
4223#[unsafe(no_mangle)]
4229pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
4230 if path_c.is_null() {
4231 return std::ptr::null_mut();
4232 }
4233 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4234 return std::ptr::null_mut();
4235 };
4236 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
4237 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4238 Err(_) => std::ptr::null_mut(),
4239 }
4240}
4241
4242#[unsafe(no_mangle)]
4248pub unsafe extern "C" fn rkr_read_chemfiles(
4249 path_c: *const c_char,
4250 num_frames: *mut usize,
4251) -> *mut *mut RKRConFrame {
4252 if path_c.is_null() || num_frames.is_null() {
4253 return std::ptr::null_mut();
4254 }
4255 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4256 return std::ptr::null_mut();
4257 };
4258 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
4259 Ok(frames) => pack_frame_handles(frames, num_frames),
4260 Err(_) => std::ptr::null_mut(),
4261 }
4262}
4263
4264#[unsafe(no_mangle)]
4270pub unsafe extern "C" fn rkr_read_chemfiles_nth(
4271 path_c: *const c_char,
4272 index: usize,
4273) -> *mut RKRConFrame {
4274 if path_c.is_null() {
4275 return std::ptr::null_mut();
4276 }
4277 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4278 return std::ptr::null_mut();
4279 };
4280 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
4281 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4282 Err(_) => std::ptr::null_mut(),
4283 }
4284}
4285
4286#[unsafe(no_mangle)]
4292pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
4293 if path_c.is_null() {
4294 return usize::MAX;
4295 }
4296 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4297 return usize::MAX;
4298 };
4299 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
4300}
4301
4302#[unsafe(no_mangle)]
4310pub unsafe extern "C" fn rkr_read_chemfiles_range(
4311 path_c: *const c_char,
4312 start: usize,
4313 step: usize,
4314 stop: usize,
4315 topology_c: *const c_char,
4316 guess_bonds: u8,
4317 num_frames: *mut usize,
4318) -> *mut *mut RKRConFrame {
4319 if path_c.is_null() || num_frames.is_null() {
4320 return std::ptr::null_mut();
4321 }
4322 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
4323 return std::ptr::null_mut();
4324 };
4325 let topology = if topology_c.is_null() {
4326 None
4327 } else {
4328 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
4329 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
4330 _ => return std::ptr::null_mut(),
4331 }
4332 };
4333 let opts = crate::chemfiles_import::ChemfilesReadOpts {
4334 start,
4335 step,
4336 stop: if stop == usize::MAX { None } else { Some(stop) },
4337 format: None,
4338 topology,
4339 topology_format: None,
4340 guess_bonds: guess_bonds != 0,
4341 };
4342 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
4343 Ok(frames) => pack_frame_handles(frames, num_frames),
4344 Err(_) => std::ptr::null_mut(),
4345 }
4346}
4347#[unsafe(no_mangle)]
4354pub unsafe extern "C" fn rkr_read_chemfiles_memory(
4355 data_c: *const c_char,
4356 format_c: *const c_char,
4357 num_frames: *mut usize,
4358) -> *mut *mut RKRConFrame {
4359 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
4360 return std::ptr::null_mut();
4361 }
4362 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
4363 return std::ptr::null_mut();
4364 };
4365 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
4366 return std::ptr::null_mut();
4367 };
4368 match crate::chemfiles_import::con_frames_from_memory(data, format) {
4369 Ok(frames) => {
4370 let n = frames.len();
4371 unsafe { *num_frames = n };
4372 let mut ptrs: Vec<*mut RKRConFrame> = frames
4373 .into_iter()
4374 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
4375 .collect();
4376 let p = ptrs.as_mut_ptr();
4377 std::mem::forget(ptrs);
4378 p
4379 }
4380 Err(_) => std::ptr::null_mut(),
4381 }
4382}
4383#[unsafe(no_mangle)]
4388pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
4389 if tensor.is_null() {
4390 return;
4391 }
4392 unsafe {
4393 let t = &mut *tensor;
4394 if let Some(del) = t.deleter {
4395 del(tensor);
4396 }
4397 }
4398}
4399
4400#[unsafe(no_mangle)]
4410pub unsafe extern "C" fn rkr_pack_rcso(
4411 frame_handle: *const RKRConFrame,
4412 buf: *mut u8,
4413 buflen: usize,
4414 out_len: *mut usize,
4415) -> RKRStatus {
4416 if out_len.is_null() {
4417 return RKRStatus::RKR_STATUS_NULL_POINTER;
4418 }
4419 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
4420 Some(f) => f,
4421 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
4422 };
4423 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
4424 Ok(b) => b,
4425 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4426 };
4427 unsafe { *out_len = bytes.len() };
4428 if buf.is_null() {
4429 return RKRStatus::RKR_STATUS_SUCCESS;
4430 }
4431 if buflen < bytes.len() {
4432 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4433 }
4434 unsafe {
4435 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
4436 }
4437 RKRStatus::RKR_STATUS_SUCCESS
4438}
4439
4440#[unsafe(no_mangle)]
4445pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
4446 buf: *const u8,
4447 buflen: usize,
4448 out_natoms: *mut u32,
4449) -> RKRStatus {
4450 if buf.is_null() || out_natoms.is_null() {
4451 return RKRStatus::RKR_STATUS_NULL_POINTER;
4452 }
4453 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4454 match crate::rcso::Rcso::decode(bytes) {
4455 Ok(s) => {
4456 unsafe { *out_natoms = s.natoms };
4457 RKRStatus::RKR_STATUS_SUCCESS
4458 }
4459 Err(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4460 }
4461}
4462
4463#[unsafe(no_mangle)]
4468pub unsafe extern "C" fn rkr_unpack_rcso_positions(
4469 buf: *const u8,
4470 buflen: usize,
4471 dest: *mut f64,
4472 dest_natoms: u32,
4473) -> RKRStatus {
4474 if buf.is_null() || dest.is_null() {
4475 return RKRStatus::RKR_STATUS_NULL_POINTER;
4476 }
4477 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4478 let soa = match crate::rcso::Rcso::decode(bytes) {
4479 Ok(s) => s,
4480 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4481 };
4482 if soa.natoms != dest_natoms {
4483 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4484 }
4485 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
4486 for (i, p) in soa.positions.iter().enumerate() {
4487 out[i * 3] = p[0];
4488 out[i * 3 + 1] = p[1];
4489 out[i * 3 + 2] = p[2];
4490 }
4491 RKRStatus::RKR_STATUS_SUCCESS
4492}
4493
4494#[unsafe(no_mangle)]
4500pub unsafe extern "C" fn rkr_unpack_rcso_forces(
4501 buf: *const u8,
4502 buflen: usize,
4503 dest: *mut f64,
4504 dest_natoms: u32,
4505) -> RKRStatus {
4506 if buf.is_null() || dest.is_null() {
4507 return RKRStatus::RKR_STATUS_NULL_POINTER;
4508 }
4509 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
4510 let soa = match crate::rcso::Rcso::decode(bytes) {
4511 Ok(s) => s,
4512 Err(_) => return RKRStatus::RKR_STATUS_VALIDATION_ERROR,
4513 };
4514 let Some(forces) = soa.forces else {
4515 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
4516 };
4517 if soa.natoms != dest_natoms {
4518 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
4519 }
4520 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
4521 for (i, f) in forces.iter().enumerate() {
4522 out[i * 3] = f[0];
4523 out[i * 3 + 1] = f[1];
4524 out[i * 3 + 2] = f[2];
4525 }
4526 RKRStatus::RKR_STATUS_SUCCESS
4527}
4528
4529#[cfg(test)]
4530mod tests {
4531 use super::*;
4532 use std::ffi::{CStr, CString};
4533 #[test]
4534 fn frame_copy_positions_without_cframe() {
4535 let handle = test_frame_handle();
4536 let n = unsafe { rkr_frame_atom_count(handle) };
4537 assert_eq!(n, 1);
4538 let mut buf = vec![0.0f64; n * 3];
4539 assert_eq!(
4540 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
4541 RKRStatus::RKR_STATUS_SUCCESS
4542 );
4543 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4544 assert_eq!(
4545 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
4546 RKRStatus::RKR_STATUS_SUCCESS
4547 );
4548 assert!(!tensor.is_null());
4549 unsafe { rkr_dlpack_delete(tensor) };
4550 assert_eq!(
4551 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
4552 RKRStatus::RKR_STATUS_SECTION_ABSENT
4553 );
4554 unsafe { free_rkr_frame(handle) };
4555 }
4556 #[test]
4557 fn read_all_frames_c_abi_tiny() {
4558 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4559 let mut n: usize = 0;
4560 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
4561 assert!(!arr.is_null() && n >= 1);
4562 let first = unsafe { *arr };
4563 let nat = unsafe { rkr_frame_atom_count(first) };
4564 let mut buf = vec![0.0f64; nat * 3];
4565 assert_eq!(
4566 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
4567 RKRStatus::RKR_STATUS_SUCCESS
4568 );
4569 unsafe { free_rkr_frame_array(arr, n) };
4570 }
4571
4572 #[test]
4573 fn read_all_frames_n_threads_matches_auto() {
4574 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4575 let mut n_auto: usize = 0;
4576 let mut n_one: usize = 0;
4577 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
4578 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
4579 assert!(!auto.is_null() && !one.is_null());
4580 assert_eq!(n_auto, n_one);
4581 assert_eq!(n_auto, 1);
4582 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
4583 let nat_one = unsafe { rkr_frame_atom_count(*one) };
4584 assert_eq!(nat_auto, nat_one);
4585 unsafe { free_rkr_frame_array(auto, n_auto) };
4586 unsafe { free_rkr_frame_array(one, n_one) };
4587 }
4588
4589 #[test]
4590 fn free_rkr_frame_ptr_array_keeps_frames() {
4591 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
4592 let mut n: usize = 0;
4593 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
4594 assert!(!arr.is_null() && n >= 1);
4595 let first = unsafe { *arr };
4596 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
4598 unsafe { free_rkr_frame_ptr_array(arr, n) };
4599 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
4600 unsafe { free_rkr_frame(first) };
4601 for h in rest {
4602 if !h.is_null() {
4603 unsafe { free_rkr_frame(h) };
4604 }
4605 }
4606 }
4607 fn test_frame_handle() -> *mut RKRConFrame {
4608 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
4609 builder
4610 .prebox_header("Generated by test")
4611 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
4612 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
4613 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
4614 }
4615 #[test]
4616 fn header_line_rejects_null_buffer() {
4617 let frame = test_frame_handle();
4618 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
4619 unsafe { free_rkr_frame(frame) };
4620 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
4621 }
4622 #[test]
4623 fn header_line_rejects_empty_buffer() {
4624 let frame = test_frame_handle();
4625 let mut buffer = [0 as c_char; 1];
4626 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
4627 unsafe { free_rkr_frame(frame) };
4628 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
4629 }
4630 #[test]
4631 fn header_line_truncates_and_terminates_buffer() {
4632 let frame = test_frame_handle();
4633 let mut buffer = [0 as c_char; 10];
4634 let status =
4635 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
4636 unsafe { free_rkr_frame(frame) };
4637 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4638 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
4639 assert_eq!(copied.to_str().unwrap(), "Generated");
4640 }
4641
4642 #[test]
4643 fn pack_rcso_size_query_then_dest() {
4644 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
4645 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
4646 assert!(!frame.is_null());
4647 let mut need = 0usize;
4648 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
4649 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4650 assert!(need >= 24);
4651 let mut buf = vec![0u8; need];
4652 let mut wrote = 0usize;
4653 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
4654 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4655 assert_eq!(wrote, need);
4656 assert_eq!(&buf[0..4], b"RCSO");
4657 let mut natoms = 0u32;
4658 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
4659 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4660 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
4661 let mut xyz = vec![0.0f64; natoms as usize * 3];
4662 let st = unsafe {
4663 rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms)
4664 };
4665 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4666 let mut frc = vec![0.0f64; natoms as usize * 3];
4667 let st =
4668 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
4669 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4670 unsafe { free_rkr_frame(frame) };
4671 }
4672
4673 fn test_builder_handle() -> *mut RKRConFrameBuilder {
4674 let cell = [10.0, 11.0, 12.0];
4675 let angles = [90.0, 91.0, 92.0];
4676 unsafe {
4677 rkr_frame_new(
4678 cell.as_ptr(),
4679 angles.as_ptr(),
4680 ptr::null(),
4681 ptr::null(),
4682 ptr::null(),
4683 ptr::null(),
4684 )
4685 }
4686 }
4687 fn c_string(s: &str) -> CString {
4688 CString::new(s).unwrap()
4689 }
4690 unsafe fn assert_single_atom(
4691 frame: *mut RKRConFrame,
4692 fixed: [bool; 3],
4693 velocity: Option<[f64; 3]>,
4694 forces: Option<[f64; 3]>,
4695 ) {
4696 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
4697 assert!(!c_frame.is_null());
4698 let c_frame_ref = unsafe { &*c_frame };
4699 assert_eq!(c_frame_ref.num_atoms, 1);
4700 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
4701 assert_eq!(c_frame_ref.has_forces, forces.is_some());
4702 let atom = unsafe { &*c_frame_ref.atoms };
4703 assert_eq!(atom.fixed_x, fixed[0]);
4704 assert_eq!(atom.fixed_y, fixed[1]);
4705 assert_eq!(atom.fixed_z, fixed[2]);
4706 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
4707 assert_eq!(atom.has_velocity, velocity.is_some());
4708 assert_eq!(atom.has_forces, forces.is_some());
4709 if let Some([vx, vy, vz]) = velocity {
4710 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
4711 }
4712 if let Some([fx, fy, fz]) = forces {
4713 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
4714 }
4715 unsafe { free_c_frame(c_frame) };
4716 unsafe { free_rkr_frame(frame) };
4717 }
4718 #[test]
4719 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
4720 let builder = test_builder_handle();
4721 let symbol = c_string("Cu");
4722 let status = unsafe {
4723 rkr_frame_add_atom_with_fixed_mask(
4724 builder,
4725 symbol.as_ptr(),
4726 1.0,
4727 2.0,
4728 3.0,
4729 true,
4730 false,
4731 true,
4732 7,
4733 63.546,
4734 )
4735 };
4736 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4737 let frame = unsafe { rkr_frame_builder_build(builder) };
4738 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
4739 }
4740 #[test]
4741 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
4742 let builder = test_builder_handle();
4743 let symbol = c_string("H");
4744 let status = unsafe {
4745 rkr_frame_add_atom_with_velocity_fixed_mask(
4746 builder,
4747 symbol.as_ptr(),
4748 1.0,
4749 2.0,
4750 3.0,
4751 false,
4752 true,
4753 false,
4754 9,
4755 1.008,
4756 0.1,
4757 0.2,
4758 0.3,
4759 )
4760 };
4761 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4762 let frame = unsafe { rkr_frame_builder_build(builder) };
4763 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
4764 }
4765 #[test]
4766 fn builder_preserves_fixed_mask_for_atom_with_forces() {
4767 let builder = test_builder_handle();
4768 let symbol = c_string("O");
4769 let status = unsafe {
4770 rkr_frame_add_atom_with_forces_fixed_mask(
4771 builder,
4772 symbol.as_ptr(),
4773 1.0,
4774 2.0,
4775 3.0,
4776 true,
4777 true,
4778 false,
4779 11,
4780 15.999,
4781 -0.1,
4782 -0.2,
4783 -0.3,
4784 )
4785 };
4786 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4787 let frame = unsafe { rkr_frame_builder_build(builder) };
4788 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
4789 }
4790 #[test]
4791 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
4792 let builder = test_builder_handle();
4793 let symbol = c_string("N");
4794 let status = unsafe {
4795 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4796 builder,
4797 symbol.as_ptr(),
4798 1.0,
4799 2.0,
4800 3.0,
4801 false,
4802 true,
4803 true,
4804 13,
4805 14.007,
4806 0.4,
4807 0.5,
4808 0.6,
4809 -0.4,
4810 -0.5,
4811 -0.6,
4812 )
4813 };
4814 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
4815 let frame = unsafe { rkr_frame_builder_build(builder) };
4816 unsafe {
4817 assert_single_atom(
4818 frame,
4819 [false, true, true],
4820 Some([0.4, 0.5, 0.6]),
4821 Some([-0.4, -0.5, -0.6]),
4822 )
4823 };
4824 }
4825 #[test]
4826 fn builder_bool_fixed_functions_set_all_axes_together() {
4827 let builder = test_builder_handle();
4828 let cu = c_string("Cu");
4829 let h = c_string("H");
4830 let atom_status =
4831 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
4832 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
4833 let velocity_status = unsafe {
4834 rkr_frame_add_atom_with_velocity(
4835 builder,
4836 h.as_ptr(),
4837 4.0,
4838 5.0,
4839 6.0,
4840 false,
4841 2,
4842 1.008,
4843 0.7,
4844 0.8,
4845 0.9,
4846 )
4847 };
4848 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
4849 let frame = unsafe { rkr_frame_builder_build(builder) };
4850 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
4851 assert!(!c_frame.is_null());
4852 let c_frame_ref = unsafe { &*c_frame };
4853 assert_eq!(c_frame_ref.num_atoms, 2);
4854 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
4855 assert_eq!(
4856 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
4857 [true, true, true]
4858 );
4859 assert_eq!(
4860 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
4861 [false, false, false]
4862 );
4863 unsafe { free_c_frame(c_frame) };
4864 unsafe { free_rkr_frame(frame) };
4865 }
4866 #[test]
4867 fn status_message_returns_static_strings_for_all_status_values() {
4868 let cases = [
4869 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
4870 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
4871 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
4872 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
4873 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
4874 (
4875 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
4876 "index out of bounds",
4877 ),
4878 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
4879 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
4880 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
4881 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
4882 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
4883 (
4884 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
4885 "feature disabled in this build",
4886 ),
4887 ];
4888 for (status, expected) in cases {
4889 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
4890 assert_eq!(message.to_str().unwrap(), expected);
4891 }
4892 }
4893 unsafe fn assert_dlpack_cpu_float(
4897 t: *mut RKRDLManagedTensorVersioned,
4898 expect_ndim: i32,
4899 expect_shape: &[i64],
4900 expect_bits: u8,
4901 ) {
4902 assert!(!t.is_null());
4903 let dl = unsafe { &(*t).dl_tensor };
4904 assert_eq!(dl.ndim, expect_ndim);
4905 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
4906 assert_eq!(shape, expect_shape);
4907 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
4908 assert_eq!(dl.dtype.bits, expect_bits);
4909 assert_eq!(dl.dtype.lanes, 1);
4910 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
4911 assert!(!dl.data.is_null());
4912 }
4913
4914 #[test]
4915 fn frame_optional_section_dlpack_present_and_absent() {
4916 let handle = test_frame_handle();
4918 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4919 assert_eq!(
4920 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
4921 RKRStatus::RKR_STATUS_SECTION_ABSENT
4922 );
4923 assert!(t.is_null());
4924 assert_eq!(
4925 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
4926 RKRStatus::RKR_STATUS_SECTION_ABSENT
4927 );
4928 assert_eq!(
4929 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
4930 RKRStatus::RKR_STATUS_SECTION_ABSENT
4931 );
4932 unsafe { free_rkr_frame(handle) };
4933
4934 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
4936 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
4937 assert!(!fr.is_null());
4938 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
4939 assert!(n > 0);
4940 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4941 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
4942 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
4943 unsafe {
4944 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
4945 rkr_dlpack_delete(vel);
4946 free_rkr_frame(fr);
4947 }
4948
4949 let cell = [10.0f64; 3];
4951 let ang = [90.0f64; 3];
4952 let b = unsafe {
4953 rkr_frame_new(
4954 cell.as_ptr(),
4955 ang.as_ptr(),
4956 std::ptr::null(),
4957 std::ptr::null(),
4958 std::ptr::null(),
4959 std::ptr::null(),
4960 )
4961 };
4962 assert!(!b.is_null());
4963 let sym = CString::new("H").unwrap();
4964 unsafe {
4965 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4966 b,
4967 sym.as_ptr(),
4968 0.0,
4969 0.0,
4970 0.0,
4971 false,
4972 false,
4973 false,
4974 0,
4975 1.0,
4976 0.1,
4977 0.0,
4978 0.0,
4979 0.0,
4980 0.0,
4981 -1.0,
4982 );
4983 rkr_frame_builder_set_last_energy(b, -0.5);
4984 }
4985 let built = unsafe { rkr_frame_builder_build(b) };
4986 assert!(!built.is_null());
4987 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
4988 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4989 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
4990 assert_eq!(
4991 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
4992 RKRStatus::RKR_STATUS_SUCCESS
4993 );
4994 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
4995 assert_eq!(
4996 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
4997 RKRStatus::RKR_STATUS_SUCCESS
4998 );
4999 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
5000 let opts32 = RKRDlpackExportOptions {
5002 dtype: RKRDLDataType {
5003 code: rkr_dl_type_code::RKR_DL_FLOAT,
5004 bits: 32,
5005 lanes: 1,
5006 },
5007 device: RKRDLDevice {
5008 device_type: rkr_dl_device_type::RKR_DL_CPU,
5009 device_id: 0,
5010 },
5011 };
5012 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5013 assert_eq!(
5014 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
5015 RKRStatus::RKR_STATUS_SUCCESS
5016 );
5017 unsafe {
5018 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
5019 rkr_dlpack_delete(pos32);
5020 }
5021 let cuda_dev = RKRDlpackExportOptions {
5024 dtype: RKRDLDataType {
5025 code: rkr_dl_type_code::RKR_DL_FLOAT,
5026 bits: 64,
5027 lanes: 1,
5028 },
5029 device: RKRDLDevice {
5030 device_type: rkr_dl_device_type::RKR_DL_CUDA,
5031 device_id: 0,
5032 },
5033 };
5034 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5035 #[cfg(not(feature = "cuda"))]
5036 assert_eq!(
5037 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5038 RKRStatus::RKR_STATUS_FEATURE_DISABLED
5039 );
5040 #[cfg(feature = "cuda")]
5041 {
5042 assert_eq!(
5043 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
5044 RKRStatus::RKR_STATUS_SUCCESS
5045 );
5046 assert!(!junk.is_null());
5047 let dl = unsafe { &(*junk).dl_tensor };
5048 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
5049 assert!(!dl.data.is_null());
5050 unsafe { rkr_dlpack_delete(junk) };
5051 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5053 assert_eq!(
5054 unsafe {
5055 rkr_frame_positions_as_dlpack(
5056 built,
5057 rkr_dl_device_type::RKR_DL_CUDA,
5058 0,
5059 0,
5060 1,
5061 0,
5062 &mut as_cuda,
5063 )
5064 },
5065 RKRStatus::RKR_STATUS_SUCCESS
5066 );
5067 assert!(!as_cuda.is_null());
5068 unsafe {
5069 assert_eq!(
5070 (*as_cuda).dl_tensor.device.device_type,
5071 dlpk::sys::DLDeviceType::kDLCUDA
5072 );
5073 rkr_dlpack_delete(as_cuda);
5074 }
5075 }
5076 let bad_bits = RKRDlpackExportOptions {
5078 dtype: RKRDLDataType {
5079 code: rkr_dl_type_code::RKR_DL_FLOAT,
5080 bits: 16,
5081 lanes: 1,
5082 },
5083 device: RKRDLDevice {
5084 device_type: rkr_dl_device_type::RKR_DL_CPU,
5085 device_id: 0,
5086 },
5087 };
5088 assert_eq!(
5089 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
5090 RKRStatus::RKR_STATUS_VALIDATION_ERROR
5091 );
5092 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5094 assert_eq!(
5095 unsafe {
5096 rkr_frame_positions_as_dlpack(
5097 built,
5098 rkr_dl_device_type::RKR_DL_CPU,
5099 0,
5100 0,
5101 1,
5102 0,
5103 &mut as_t,
5104 )
5105 },
5106 RKRStatus::RKR_STATUS_SUCCESS
5107 );
5108 unsafe {
5109 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
5110 }
5111 {
5113 let fr = unsafe { &mut *(built as *mut ConFrame) };
5114 for a in fr.atom_data.iter_mut() {
5115 a.x = -99.0;
5116 a.y = -99.0;
5117 a.z = -99.0;
5118 }
5119 }
5120 assert_eq!(
5121 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
5122 RKRStatus::RKR_STATUS_SUCCESS
5123 );
5124 {
5125 let fr = unsafe { &*(built as *const ConFrame) };
5126 assert!((fr.atom_data[0].x - 0.0).abs() < 1e-12);
5127 assert!((fr.atom_data[0].y - 0.0).abs() < 1e-12);
5128 assert!((fr.atom_data[0].z - 0.0).abs() < 1e-12);
5129 }
5130 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
5132 assert_eq!(
5133 unsafe {
5134 rkr_frame_positions_as_dlpack(
5135 built,
5136 rkr_dl_device_type::RKR_DL_CPU,
5137 0,
5138 0,
5139 1,
5140 0,
5141 &mut again,
5142 )
5143 },
5144 RKRStatus::RKR_STATUS_SUCCESS
5145 );
5146 unsafe {
5147 let dl = &(*again).dl_tensor;
5148 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
5149 assert!((data[0] - 0.0).abs() < 1e-12);
5150 assert!((data[1] - 0.0).abs() < 1e-12);
5151 assert!((data[2] - 0.0).abs() < 1e-12);
5152 rkr_dlpack_delete(again);
5153 rkr_dlpack_delete(as_t);
5154 rkr_dlpack_delete(frc);
5155 rkr_dlpack_delete(eng);
5156 free_rkr_frame(built);
5157 }
5158 }
5159
5160 #[test]
5161 fn ffi_positions_dlpack_round_trip() {
5162 let handle = test_builder_handle();
5163 let sym = c_string("Cu");
5164 unsafe {
5165 rkr_frame_add_atom_full(
5166 handle,
5167 sym.as_ptr(),
5168 1.0,
5169 2.0,
5170 3.0,
5171 false,
5172 false,
5173 false,
5174 7,
5175 63.5,
5176 ptr::null(),
5177 ptr::null(),
5178 )
5179 };
5180 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5181 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
5182 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
5183 assert!(!t.is_null());
5184 let dl = unsafe { &(*t).dl_tensor };
5186 assert_eq!(dl.ndim, 2);
5187 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
5188 assert_eq!(shape, &[1, 3]);
5189 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
5190 assert_eq!(dl.dtype.bits, 64);
5191 assert_eq!(dl.dtype.lanes, 1);
5192 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
5193 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
5194 assert_eq!(data, &[1.0, 2.0, 3.0]);
5195 let deleter = unsafe { (*t).deleter };
5197 if let Some(del) = deleter {
5198 unsafe { del(t) };
5199 }
5200 unsafe { free_rkr_frame_builder(handle) };
5201 }
5202 #[test]
5203 fn ffi_velocities_dlpack_section_absent() {
5204 let handle = test_builder_handle();
5205 let sym = c_string("Cu");
5206 unsafe {
5207 rkr_frame_add_atom_full(
5208 handle,
5209 sym.as_ptr(),
5210 0.0,
5211 0.0,
5212 0.0,
5213 false,
5214 false,
5215 false,
5216 0,
5217 63.5,
5218 ptr::null(),
5219 ptr::null(),
5220 )
5221 };
5222 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5223 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
5224 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
5225 assert!(t.is_null());
5226 unsafe { free_rkr_frame_builder(handle) };
5227 }
5228 #[test]
5229 fn ffi_dlpack_null_handle_rejects() {
5230 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
5231 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
5232 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
5233 assert!(t.is_null());
5234 }
5235 #[cfg(feature = "chemfiles")]
5236 #[test]
5237 fn rkr_frame_select_finds_oxygen() {
5238 use crate::types::ConFrameBuilder;
5239 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5240 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
5241 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
5242 let frame = b.build().unwrap();
5243 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5244 let sel = CString::new("name O").unwrap();
5245 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5246 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
5247 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5248 assert!(!out.is_null());
5249 let n = unsafe { rkr_selection_result_match_count(out) };
5250 assert_eq!(n, 1);
5251 let mut atoms = [u64::MAX; 4];
5252 let mut size = 0u32;
5253 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
5254 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
5255 assert_eq!(size, 1);
5256 assert_eq!(atoms[0], 0);
5257 unsafe {
5258 rkr_selection_result_free(out);
5259 free_rkr_frame(frame_ptr);
5260 }
5261 }
5262 #[cfg(feature = "chemfiles")]
5264 #[test]
5265 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
5266 use crate::types::{Bond, ConFrameBuilder};
5267 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
5269 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
5270 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
5271 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
5272 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
5273 let mut frame = b.build().unwrap();
5274 let id_to = |id: u64| {
5275 frame
5276 .atom_data
5277 .iter()
5278 .position(|a| a.atom_id == id)
5279 .unwrap() as u32
5280 };
5281 frame.header.set_bonds(&[
5282 Bond::new(id_to(0), id_to(1)),
5283 Bond::new(id_to(1), id_to(2)),
5284 Bond::new(id_to(2), id_to(3)),
5285 ]);
5286 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5287 let run = |sel: &str| -> (u64, u32) {
5288 let csel = CString::new(sel).unwrap();
5289 let mut out: *mut RKRSelectionResult = ptr::null_mut();
5290 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
5291 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
5292 let n = unsafe { rkr_selection_result_match_count(out) };
5293 let ctx = unsafe { rkr_selection_result_context_size(out) };
5294 unsafe { rkr_selection_result_free(out) };
5295 (n, ctx)
5296 };
5297 assert_eq!(run("bonds: all"), (3, 2));
5298 assert_eq!(run("angles: all"), (2, 3));
5299 assert_eq!(run("dihedrals: all"), (1, 4));
5300 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
5301 assert_eq!(
5302 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
5303 run("bonds: type(#1) H and name(#2) O").0
5304 );
5305 unsafe { free_rkr_frame(frame_ptr) };
5306 }
5307 #[cfg(feature = "metatensor")]
5308 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
5309 assert!(!block.is_null());
5310 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
5311 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
5312 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
5313 let shape_fn = array
5314 .shape
5315 .expect("mts_array_t.shape from metatensor C API");
5316 let mut shape_ptr: *const usize = std::ptr::null();
5317 let mut shape_count: usize = 0;
5318 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
5319 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
5320 assert_eq!(shape_count, 2);
5321 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
5322 assert_eq!(shape[0], n);
5323 assert_eq!(shape[1], props);
5324 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
5325 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
5326 assert!(!samples.is_null() && !prop_lab.is_null());
5327 }
5328 #[cfg(feature = "metatensor")]
5329 #[test]
5330 fn metatensor_positions_via_c_abi() {
5331 let handle = test_frame_handle();
5332 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5333 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
5334 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
5335 assert_mts_block_shape(out, 1, 3);
5336 unsafe { rkr_mts_block_free(out) };
5337 for (name, export) in [
5338 (
5339 "velocities",
5340 rkr_frame_metatensor_velocities_block
5341 as unsafe extern "C" fn(
5342 *const RKRConFrame,
5343 *mut *mut metatensor::c_api::mts_block_t,
5344 ) -> RKRStatus,
5345 ),
5346 (
5347 "forces",
5348 rkr_frame_metatensor_forces_block
5349 as unsafe extern "C" fn(
5350 *const RKRConFrame,
5351 *mut *mut metatensor::c_api::mts_block_t,
5352 ) -> RKRStatus,
5353 ),
5354 (
5355 "atom_energies",
5356 rkr_frame_metatensor_atom_energies_block
5357 as unsafe extern "C" fn(
5358 *const RKRConFrame,
5359 *mut *mut metatensor::c_api::mts_block_t,
5360 ) -> RKRStatus,
5361 ),
5362 ] {
5363 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5364 let st_abs = unsafe { export(handle, &mut o) };
5365 assert_eq!(
5366 st_abs,
5367 RKRStatus::RKR_STATUS_SECTION_ABSENT,
5368 "{name} must be SECTION_ABSENT on minimal test frame"
5369 );
5370 assert!(o.is_null());
5371 }
5372 unsafe { free_rkr_frame(handle) };
5373 }
5374
5375 #[cfg(feature = "metatensor")]
5376 #[test]
5377 fn metatensor_optional_sections_via_c_abi() {
5378 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
5380 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
5381 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
5382 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
5383 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
5384 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
5385 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
5386 builder.set_atom_energy(0, -0.5).unwrap();
5387 builder.set_atom_energy(1, -1.0).unwrap();
5388 let frame = builder.build().unwrap();
5389 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
5390 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5391 assert_eq!(
5392 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
5393 RKRStatus::RKR_STATUS_SUCCESS
5394 );
5395 assert_mts_block_shape(pos, 2, 3);
5396 unsafe { rkr_mts_block_free(pos) };
5397 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5398 assert_eq!(
5399 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
5400 RKRStatus::RKR_STATUS_SUCCESS
5401 );
5402 assert_mts_block_shape(vel, 2, 3);
5403 unsafe { rkr_mts_block_free(vel) };
5404 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5405 assert_eq!(
5406 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
5407 RKRStatus::RKR_STATUS_SUCCESS
5408 );
5409 assert_mts_block_shape(frc, 2, 3);
5410 unsafe { rkr_mts_block_free(frc) };
5411 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
5412 assert_eq!(
5413 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
5414 RKRStatus::RKR_STATUS_SUCCESS
5415 );
5416 assert_mts_block_shape(eng, 2, 1);
5417 unsafe { rkr_mts_block_free(eng) };
5418 unsafe { free_rkr_frame(handle) };
5419 }
5420
5421 #[test]
5422 fn string_iterator_yields_frames_from_buffer() {
5423 let text =
5424 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
5425 let c_text = CString::new(text.as_str()).unwrap();
5426 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
5427 assert!(!it.is_null());
5428 let mut n = 0usize;
5429 loop {
5430 let fr = unsafe { con_frame_iterator_next(it) };
5431 if fr.is_null() {
5432 break;
5433 }
5434 n += 1;
5435 unsafe { free_rkr_frame(fr) };
5436 }
5437 unsafe { free_con_frame_iterator(it) };
5438 assert!(n >= 1, "string iterator should yield >=1 frame");
5439
5440 let bytes = text.as_bytes();
5441 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
5442 assert!(!it2.is_null());
5443 let fr2 = unsafe { con_frame_iterator_next(it2) };
5444 assert!(!fr2.is_null());
5445 unsafe {
5446 free_rkr_frame(fr2);
5447 free_con_frame_iterator(it2);
5448 }
5449 }
5450
5451 #[test]
5452 fn file_iterator_reads_gzip_when_present() {
5453 use flate2::Compression;
5454 use flate2::write::GzEncoder;
5455 use std::io::Write;
5456 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
5457 let dir = tempfile::tempdir().expect("tempdir");
5458 let gz_path = dir.path().join("tiny_cuh2.con.gz");
5459 {
5460 let f = std::fs::File::create(&gz_path).unwrap();
5461 let mut enc = GzEncoder::new(f, Compression::default());
5462 enc.write_all(&plain).unwrap();
5463 enc.finish().unwrap();
5464 }
5465 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
5466 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
5467 assert!(
5468 !it.is_null(),
5469 "path iterator must decompress .con.gz transparently"
5470 );
5471 let fr = unsafe { con_frame_iterator_next(it) };
5472 assert!(!fr.is_null());
5473 let n = unsafe { rkr_frame_atom_count(fr) };
5474 assert!(n > 0);
5475 unsafe {
5476 free_rkr_frame(fr);
5477 free_con_frame_iterator(it);
5478 }
5479 }
5480}