1use crate::correspondence::{self, IdentityCheck};
2use crate::helpers::symbol_to_atomic_number;
3use crate::iterators::{self, ConFrameIterator};
4use crate::types::{ConFrame, ConFrameBuilder, meta};
5use crate::writer::{ConFrameWriter, binding_rewrite_frame};
6use std::cell::RefCell;
7use std::ffi::{CStr, CString, c_char};
8use std::fs::File;
9use std::path::Path;
10use std::ptr;
11pub const RKR_ABI_VERSION_MAJOR: u32 = 1;
16pub const RKR_ABI_VERSION_MINOR: u32 = 1;
18pub const RKR_ABI_LAYOUT_REVISION: u32 = 1;
20
21#[unsafe(no_mangle)]
23pub extern "C" fn rkr_abi_version_major() -> u32 {
24 RKR_ABI_VERSION_MAJOR
25}
26
27#[unsafe(no_mangle)]
29pub extern "C" fn rkr_abi_version_minor() -> u32 {
30 RKR_ABI_VERSION_MINOR
31}
32
33#[unsafe(no_mangle)]
35pub extern "C" fn rkr_abi_layout_revision() -> u32 {
36 RKR_ABI_LAYOUT_REVISION
37}
38
39#[unsafe(no_mangle)]
41pub extern "C" fn rkr_abi_stamp() -> *const c_char {
42 const STAMP: &[u8] = b"readcon-core/abi-1.1/layout-1\0";
43 STAMP.as_ptr() as *const c_char
44}
45
46pub const RKR_CON_SPEC_VERSION: u32 = 3;
54#[unsafe(no_mangle)]
56pub extern "C" fn rkr_con_spec_version() -> u32 {
57 crate::CON_SPEC_VERSION
58}
59#[unsafe(no_mangle)]
62pub extern "C" fn rkr_library_version() -> *const c_char {
63 const VERSION_NUL: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
65 VERSION_NUL.as_ptr() as *const c_char
66}
67#[unsafe(no_mangle)]
79pub unsafe extern "C" fn rkr_frame_atom_index_by_id(
80 frame_handle: *const RKRConFrame,
81 atom_id: u64,
82) -> u64 {
83 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
84 Some(f) => f,
85 None => return u64::MAX,
86 };
87 match frame.atom_index_by_id(atom_id) {
88 Some(idx) => idx as u64,
89 None => u64::MAX,
90 }
91}
92
93fn identity_check(strict: u8) -> IdentityCheck {
94 if strict == 0 {
95 IdentityCheck::Lenient
96 } else {
97 IdentityCheck::Strict
98 }
99}
100
101fn correspondence_fail(err: &correspondence::CorrespondenceError) -> RKRStatus {
102 let frame = err.frame_index().unwrap_or(0) as u64;
103 set_last_error(
104 RKRStatus::RKR_STATUS_VALIDATION_ERROR as i32,
105 &err.to_string(),
106 0,
107 frame,
108 );
109 RKRStatus::RKR_STATUS_VALIDATION_ERROR
110}
111
112fn frame_ref<'a>(handle: *const RKRConFrame) -> Option<&'a ConFrame> {
113 unsafe { (handle as *const ConFrame).as_ref() }
114}
115
116#[unsafe(no_mangle)]
125pub unsafe extern "C" fn rkr_frame_atom_id_permutation(
126 frame_a: *const RKRConFrame,
127 frame_b: *const RKRConFrame,
128 strict: u8,
129 out_perm: *mut u64,
130 out_len: usize,
131) -> RKRStatus {
132 let (Some(reference), Some(other)) = (frame_ref(frame_a), frame_ref(frame_b)) else {
133 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
134 };
135 let perm = match correspondence::atom_id_permutation(reference, other, identity_check(strict)) {
136 Ok(perm) => perm,
137 Err(err) => return correspondence_fail(&err),
138 };
139 if perm.is_empty() {
140 return RKRStatus::RKR_STATUS_SUCCESS;
141 }
142 if out_perm.is_null() {
143 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
144 }
145 if out_len < perm.len() {
146 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
147 }
148 for (i, index) in perm.iter().enumerate() {
149 unsafe { *out_perm.add(i) = *index as u64 };
150 }
151 RKRStatus::RKR_STATUS_SUCCESS
152}
153
154#[unsafe(no_mangle)]
161pub unsafe extern "C" fn rkr_frame_reorder_like(
162 frame_a: *const RKRConFrame,
163 frame_b: *const RKRConFrame,
164 strict: u8,
165) -> *mut RKRConFrame {
166 let (Some(reference), Some(other)) = (frame_ref(frame_a), frame_ref(frame_b)) else {
167 return null_err(RKRStatus::RKR_STATUS_NULL_POINTER);
168 };
169 match correspondence::reorder_like(reference, other, identity_check(strict)) {
170 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
171 Err(err) => {
172 correspondence_fail(&err);
173 ptr::null_mut()
174 }
175 }
176}
177
178#[unsafe(no_mangle)]
187pub unsafe extern "C" fn rkr_frames_check_atom_ids(
188 frames: *const *const RKRConFrame,
189 nframes: usize,
190 strict: u8,
191 out_frame_index: *mut u64,
192 out_atom_id: *mut u64,
193) -> RKRStatus {
194 if nframes == 0 {
195 write_correspondence_outs(
196 &correspondence::CorrespondenceError::EmptySequence,
197 out_frame_index,
198 out_atom_id,
199 );
200 return correspondence_fail(&correspondence::CorrespondenceError::EmptySequence);
201 }
202 if frames.is_null() {
203 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
204 }
205 let mut refs = Vec::with_capacity(nframes);
206 for i in 0..nframes {
207 let handle = unsafe { *frames.add(i) };
208 let Some(frame) = frame_ref(handle) else {
209 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
210 };
211 refs.push(frame);
212 }
213 match correspondence::check_atom_id_consistency_refs(&refs, identity_check(strict)) {
214 Ok(()) => {
215 if !out_frame_index.is_null() {
216 unsafe { *out_frame_index = u64::MAX };
217 }
218 if !out_atom_id.is_null() {
219 unsafe { *out_atom_id = u64::MAX };
220 }
221 RKRStatus::RKR_STATUS_SUCCESS
222 }
223 Err(err) => {
224 write_correspondence_outs(&err, out_frame_index, out_atom_id);
225 correspondence_fail(&err)
226 }
227 }
228}
229
230fn write_correspondence_outs(
231 err: &correspondence::CorrespondenceError,
232 out_frame_index: *mut u64,
233 out_atom_id: *mut u64,
234) {
235 if !out_frame_index.is_null() {
236 unsafe { *out_frame_index = err.frame_index().unwrap_or(0) as u64 };
237 }
238 if !out_atom_id.is_null() {
239 unsafe { *out_atom_id = err.atom_id().unwrap_or(u64::MAX) };
240 }
241}
242
243#[unsafe(no_mangle)]
251pub unsafe extern "C" fn rkr_frame_minimum_image_displacements(
252 frame_a: *const RKRConFrame,
253 frame_b: *const RKRConFrame,
254 strict: u8,
255 out_disp: *mut f64,
256 out_len: usize,
257) -> RKRStatus {
258 let (Some(reference), Some(other)) = (frame_ref(frame_a), frame_ref(frame_b)) else {
259 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
260 };
261 let rows =
262 match correspondence::minimum_image_displacements(reference, other, identity_check(strict))
263 {
264 Ok(rows) => rows,
265 Err(err) => return correspondence_fail(&err),
266 };
267 if rows.is_empty() {
268 return RKRStatus::RKR_STATUS_SUCCESS;
269 }
270 if out_disp.is_null() {
271 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
272 }
273 if out_len < rows.len().saturating_mul(3) {
274 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
275 }
276 for (i, row) in rows.iter().enumerate() {
277 unsafe {
278 *out_disp.add(i * 3) = row[0];
279 *out_disp.add(i * 3 + 1) = row[1];
280 *out_disp.add(i * 3 + 2) = row[2];
281 }
282 }
283 RKRStatus::RKR_STATUS_SUCCESS
284}
285#[unsafe(no_mangle)]
294pub unsafe extern "C" fn rkr_symbol_to_z(symbol: *const c_char) -> u64 {
295 if symbol.is_null() {
296 return 0;
297 }
298 match unsafe { CStr::from_ptr(symbol) }.to_str() {
299 Ok(s) => symbol_to_atomic_number(s),
300 Err(_) => 0,
301 }
302}
303#[unsafe(no_mangle)]
308pub extern "C" fn rkr_z_to_symbol(z: u64) -> *const c_char {
309 macro_rules! cstrs {
314 ($($lit:literal),* $(,)?) => {
315 [$(concat!($lit, "\0").as_bytes()),*]
316 };
317 }
318 const TABLE: [&[u8]; 93] = cstrs![
319 "X", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P",
320 "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
321 "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh",
322 "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
323 "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re",
324 "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
325 "Pa", "U",
326 ];
327 let idx = if (1..=92).contains(&z) { z as usize } else { 0 };
328 TABLE[idx].as_ptr() as *const c_char
329}
330#[unsafe(no_mangle)]
333pub extern "C" fn rkr_frame_spec_version(frame_handle: *const RKRConFrame) -> u32 {
334 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
335 Some(f) => f.header.spec_version,
336 None => 0,
337 }
338}
339#[unsafe(no_mangle)]
346pub unsafe extern "C" fn rkr_frame_metadata_json(frame_handle: *const RKRConFrame) -> *mut c_char {
347 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
348 Some(f) => f,
349 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
350 };
351 let json_str = metadata_json_text(frame);
352 match CString::new(json_str) {
353 Ok(cs) => cs.into_raw(),
354 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
355 }
356}
357#[unsafe(no_mangle)]
359pub extern "C" fn rkr_frame_energy(frame_handle: *const RKRConFrame) -> f64 {
360 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
361 Some(f) => f.header.energy().unwrap_or(f64::NAN),
362 None => f64::NAN,
363 }
364}
365
366#[unsafe(no_mangle)]
369pub extern "C" fn rkr_frame_index_energy(frame_handle: *const RKRConFrame) -> f64 {
370 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
371 Some(f) => crate::index_proj::finite_energy(f).unwrap_or(f64::NAN),
372 None => f64::NAN,
373 }
374}
375
376#[unsafe(no_mangle)]
382pub unsafe extern "C" fn rkr_frame_composition_formula(
383 frame_handle: *const RKRConFrame,
384) -> *mut c_char {
385 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
386 Some(f) => f,
387 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
388 };
389 let s = crate::index_proj::frame_composition_formula(frame);
390 match CString::new(s) {
391 Ok(cs) => cs.into_raw(),
392 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
393 }
394}
395
396#[unsafe(no_mangle)]
398pub extern "C" fn rkr_frame_total_mass(frame_handle: *const RKRConFrame) -> f64 {
399 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
400 Some(f) => crate::index_proj::frame_total_mass(f).unwrap_or(f64::NAN),
401 None => f64::NAN,
402 }
403}
404
405#[unsafe(no_mangle)]
407pub extern "C" fn rkr_frame_cell_volume(frame_handle: *const RKRConFrame) -> f64 {
408 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
409 Some(f) => crate::index_proj::frame_cell_volume(f).unwrap_or(f64::NAN),
410 None => f64::NAN,
411 }
412}
413
414#[unsafe(no_mangle)]
416pub extern "C" fn rkr_frame_fmax(frame_handle: *const RKRConFrame) -> f64 {
417 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
418 Some(f) => crate::index_proj::frame_fmax(f).unwrap_or(f64::NAN),
419 None => f64::NAN,
420 }
421}
422
423#[unsafe(no_mangle)]
425pub extern "C" fn rkr_frame_sections_mask(frame_handle: *const RKRConFrame) -> u8 {
426 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
427 Some(f) => crate::index_proj::sections_present_mask(f),
428 None => 0,
429 }
430}
431
432#[unsafe(no_mangle)]
434pub extern "C" fn rkr_frame_index_natoms(frame_handle: *const RKRConFrame) -> u32 {
435 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
436 Some(f) => f.atom_data.len() as u32,
437 None => 0,
438 }
439}
440
441#[unsafe(no_mangle)]
448pub unsafe extern "C" fn rkr_frame_index_projection_json(
449 frame_handle: *const RKRConFrame,
450) -> *mut c_char {
451 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
452 Some(f) => f,
453 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
454 };
455 let p = crate::index_proj::FrameIndexProjection::from_frame(frame);
456 let v = serde_json::json!({
457 "n_atoms": p.n_atoms,
458 "formula": p.formula,
459 "energy": p.energy,
460 "fmax": p.fmax,
461 "total_mass": p.total_mass,
462 "cell_volume": p.cell_volume,
463 "sections_mask": p.sections_mask,
464 "has_forces": p.has_forces,
465 "has_velocities": p.has_velocities,
466 "has_energy": p.has_energy,
467 "symbols": p.symbols,
468 "species_counts": p.species_counts.iter().map(|(s,c)| serde_json::json!([s, c])).collect::<Vec<_>>(),
469 "time": p.time,
470 "timestep": p.timestep,
471 "frame_index": p.frame_index,
472 "neb_bead": p.neb_bead,
473 "neb_band": p.neb_band,
474 "charge": p.charge,
475 "magmom": p.magmom,
476 });
477 match CString::new(v.to_string()) {
478 Ok(cs) => cs.into_raw(),
479 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
480 }
481}
482#[unsafe(no_mangle)]
489pub unsafe extern "C" fn rkr_frame_potential_type(frame_handle: *const RKRConFrame) -> *mut c_char {
490 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
491 Some(f) => f,
492 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
493 };
494 match frame.header.potential_type() {
495 Some(pot_type) => match CString::new(pot_type) {
496 Ok(cs) => cs.into_raw(),
497 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
498 },
499 None => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
500 }
501}
502#[unsafe(no_mangle)]
504pub extern "C" fn rkr_frame_frame_index(frame_handle: *const RKRConFrame) -> u64 {
505 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
506 Some(f) => f.header.frame_index().unwrap_or(u64::MAX),
507 None => u64::MAX,
508 }
509}
510#[unsafe(no_mangle)]
512pub extern "C" fn rkr_frame_time(frame_handle: *const RKRConFrame) -> f64 {
513 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
514 Some(f) => f.header.time().unwrap_or(f64::NAN),
515 None => f64::NAN,
516 }
517}
518#[unsafe(no_mangle)]
520pub extern "C" fn rkr_frame_timestep(frame_handle: *const RKRConFrame) -> f64 {
521 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
522 Some(f) => f.header.timestep().unwrap_or(f64::NAN),
523 None => f64::NAN,
524 }
525}
526#[unsafe(no_mangle)]
528pub extern "C" fn rkr_frame_neb_bead(frame_handle: *const RKRConFrame) -> u64 {
529 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
530 Some(f) => f.header.neb_bead().unwrap_or(u64::MAX),
531 None => u64::MAX,
532 }
533}
534#[unsafe(no_mangle)]
536pub extern "C" fn rkr_frame_neb_band(frame_handle: *const RKRConFrame) -> u64 {
537 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
538 Some(f) => f.header.neb_band().unwrap_or(u64::MAX),
539 None => u64::MAX,
540 }
541}
542#[repr(C)]
547#[allow(non_camel_case_types)]
548#[derive(Debug, Clone, Copy, PartialEq, Eq)]
549pub enum RKRStatus {
550 RKR_STATUS_SUCCESS = 0,
552 RKR_STATUS_NULL_POINTER = -1,
554 RKR_STATUS_INVALID_UTF8 = -2,
556 RKR_STATUS_INVALID_JSON = -3,
558 RKR_STATUS_IO_ERROR = -4,
560 RKR_STATUS_INDEX_OUT_OF_BOUNDS = -5,
562 RKR_STATUS_BUFFER_TOO_SMALL = -6,
564 RKR_STATUS_INTERNAL_ERROR = -7,
566 RKR_STATUS_SECTION_ABSENT = -8,
569 RKR_STATUS_VALIDATION_ERROR = -9,
571 RKR_STATUS_SELECTION_ERROR = -10,
573 RKR_STATUS_FEATURE_DISABLED = -11,
576 RKR_STATUS_DEVICE_MISMATCH = -12,
578 RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED = -13,
580 RKR_STATUS_PARSE_ERROR = -14,
583}
584#[unsafe(no_mangle)]
589pub unsafe extern "C" fn rkr_frame_bond_count(frame_handle: *const RKRConFrame) -> u64 {
590 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
591 Some(f) => f.bonds().len() as u64,
592 None => 0,
593 }
594}
595#[unsafe(no_mangle)]
604pub unsafe extern "C" fn rkr_frame_bond_at(
605 frame_handle: *const RKRConFrame,
606 index: u64,
607 out_i: *mut u32,
608 out_j: *mut u32,
609 out_has_order: *mut u8,
610 out_order: *mut i32,
611) -> RKRStatus {
612 if frame_handle.is_null()
613 || out_i.is_null()
614 || out_j.is_null()
615 || out_has_order.is_null()
616 || out_order.is_null()
617 {
618 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
619 }
620 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
621 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
622 };
623 let bonds = frame.bonds();
624 let Some(bond) = bonds.get(index as usize) else {
625 return note(RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS);
626 };
627 unsafe {
628 *out_i = bond.i;
629 *out_j = bond.j;
630 if let Some(order) = bond.order {
631 *out_has_order = 1;
632 *out_order = order;
633 } else {
634 *out_has_order = 0;
635 *out_order = 0;
636 }
637 }
638 note(RKRStatus::RKR_STATUS_SUCCESS)
639}
640#[unsafe(no_mangle)]
643pub extern "C" fn rkr_status_message(status: RKRStatus) -> *const c_char {
644 match status {
645 RKRStatus::RKR_STATUS_SUCCESS => c"success".as_ptr(),
646 RKRStatus::RKR_STATUS_NULL_POINTER => c"null pointer".as_ptr(),
647 RKRStatus::RKR_STATUS_INVALID_UTF8 => c"invalid UTF-8".as_ptr(),
648 RKRStatus::RKR_STATUS_INVALID_JSON => c"invalid JSON".as_ptr(),
649 RKRStatus::RKR_STATUS_IO_ERROR => c"I/O error".as_ptr(),
650 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS => c"index out of bounds".as_ptr(),
651 RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL => c"buffer too small".as_ptr(),
652 RKRStatus::RKR_STATUS_INTERNAL_ERROR => c"internal error".as_ptr(),
653 RKRStatus::RKR_STATUS_SECTION_ABSENT => c"section absent".as_ptr(),
654 RKRStatus::RKR_STATUS_VALIDATION_ERROR => c"validation error".as_ptr(),
655 RKRStatus::RKR_STATUS_SELECTION_ERROR => c"selection error".as_ptr(),
656 RKRStatus::RKR_STATUS_FEATURE_DISABLED => c"feature disabled in this build".as_ptr(),
657 RKRStatus::RKR_STATUS_DEVICE_MISMATCH => c"DLPack device mismatch".as_ptr(),
658 RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED => {
659 c"device allocation unsupported in this build".as_ptr()
660 }
661 RKRStatus::RKR_STATUS_PARSE_ERROR => c"parse error".as_ptr(),
662 }
663}
664
665#[repr(C)]
668pub struct rkr_error {
669 pub code: i32,
670 pub message: *const c_char,
671 pub line: u32,
672 pub frame_index: u64,
673}
674
675struct LastErrorSlot {
676 message: CString,
677 view: rkr_error,
678}
679
680thread_local! {
681 static LAST_ERROR: RefCell<Option<LastErrorSlot>> = const { RefCell::new(None) };
682}
683
684fn set_last_error(code: i32, message: &str, line: u32, frame_index: u64) {
685 let cleaned = message.replace('\0', "");
686 let message = CString::new(cleaned).unwrap_or_else(|_| CString::new("error").unwrap());
687 LAST_ERROR.with(|cell| {
688 let mut slot = cell.borrow_mut();
689 *slot = Some(LastErrorSlot {
690 message,
691 view: rkr_error {
692 code,
693 message: std::ptr::null(),
694 line,
695 frame_index,
696 },
697 });
698 if let Some(stored) = slot.as_mut() {
699 stored.view.message = stored.message.as_ptr();
700 }
701 });
702}
703
704fn clear_last_error() {
705 LAST_ERROR.with(|cell| {
706 *cell.borrow_mut() = None;
707 });
708}
709
710fn static_status_message(status: RKRStatus) -> String {
711 let ptr = rkr_status_message(status);
712 if ptr.is_null() {
713 return "error".to_string();
714 }
715 unsafe { CStr::from_ptr(ptr) }
716 .to_string_lossy()
717 .into_owned()
718}
719
720fn note(status: RKRStatus) -> RKRStatus {
722 let code = status as i32;
723 if code < 0 {
724 let message = static_status_message(status);
725 set_last_error(code, &message, 0, 0);
726 }
727 status
728}
729
730fn null_err<T>(status: RKRStatus) -> *mut T {
731 note(status);
732 ptr::null_mut()
733}
734
735fn status_for_parse(err: &crate::error::ParseError) -> RKRStatus {
736 use crate::error::ParseError;
737 match err {
738 ParseError::IndexOutOfBounds { .. } => note(RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS),
739 ParseError::InvalidMetadataJson(_) | ParseError::MissingSpecVersion => {
740 note(RKRStatus::RKR_STATUS_INVALID_JSON)
741 }
742 _ => note(RKRStatus::RKR_STATUS_PARSE_ERROR),
743 }
744}
745
746fn note_located(err: &crate::error::LocatedParseError) -> RKRStatus {
747 let status = status_for_parse(&err.error);
748 let message = format!("{}: {}", err.error.class_name(), err.error);
749 set_last_error(status as i32, &message, err.line, err.frame_index);
750 status
751}
752
753#[cfg(any(test, feature = "mpi"))]
758pub(crate) fn record_mpi_failure(err: &(dyn std::error::Error + 'static)) {
759 if let Some(located) = err.downcast_ref::<crate::error::LocatedParseError>() {
760 note_located(located);
761 return;
762 }
763 set_last_error(
764 RKRStatus::RKR_STATUS_IO_ERROR as i32,
765 &err.to_string(),
766 0,
767 0,
768 );
769}
770
771#[cfg(feature = "mpi")]
773pub(crate) fn record_status_message(status: RKRStatus, message: &str) {
774 set_last_error(status as i32, message, 0, 0);
775}
776
777fn null_located<T>(err: &crate::error::LocatedParseError) -> *mut T {
778 note_located(err);
779 ptr::null_mut()
780}
781
782fn note_load(err: crate::iterators::LoadError) {
783 match err {
784 crate::iterators::LoadError::Io(e) => {
785 set_last_error(RKRStatus::RKR_STATUS_IO_ERROR as i32, &e.to_string(), 0, 0);
786 }
787 crate::iterators::LoadError::Utf8(e) => {
788 set_last_error(
789 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
790 &e.to_string(),
791 0,
792 0,
793 );
794 }
795 crate::iterators::LoadError::Parse(located) => {
796 note_located(&located);
797 }
798 crate::iterators::LoadError::Empty => {
799 let located = crate::error::LocatedParseError::new(
800 crate::error::ParseError::IncompleteHeader,
801 1,
802 0,
803 );
804 note_located(&located);
805 }
806 }
807}
808
809fn null_load<T>(err: crate::iterators::LoadError) -> *mut T {
810 note_load(err);
811 ptr::null_mut()
812}
813
814fn note_boxed(err: Box<dyn std::error::Error>) {
815 if let Some(io) = err.downcast_ref::<std::io::Error>() {
816 set_last_error(RKRStatus::RKR_STATUS_IO_ERROR as i32, &io.to_string(), 0, 0);
817 return;
818 }
819 let err = match err.downcast::<crate::iterators::LoadError>() {
820 Ok(load) => {
821 note_load(*load);
822 return;
823 }
824 Err(err) => err,
825 };
826 let err = match err.downcast::<crate::error::LocatedParseError>() {
827 Ok(located) => {
828 note_located(&located);
829 return;
830 }
831 Err(err) => err,
832 };
833 if let Some(parse) = err.downcast_ref::<crate::error::ParseError>() {
834 let located = crate::error::LocatedParseError::new(parse.clone(), parse.line(), 0);
835 note_located(&located);
836 return;
837 }
838 set_last_error(
839 RKRStatus::RKR_STATUS_INTERNAL_ERROR as i32,
840 &err.to_string(),
841 0,
842 0,
843 );
844}
845
846#[unsafe(no_mangle)]
856pub extern "C" fn rkr_last_error_message() -> *const c_char {
857 LAST_ERROR.with(|cell| {
858 cell.borrow()
859 .as_ref()
860 .map(|slot| slot.view.message)
861 .unwrap_or(std::ptr::null())
862 })
863}
864
865#[unsafe(no_mangle)]
870pub extern "C" fn rkr_last_error() -> *const rkr_error {
871 LAST_ERROR.with(|cell| {
872 cell.borrow()
873 .as_ref()
874 .map(|slot| &slot.view as *const rkr_error)
875 .unwrap_or(std::ptr::null())
876 })
877}
878
879#[unsafe(no_mangle)]
881pub extern "C" fn rkr_clear_error() {
882 clear_last_error();
883}
884pub struct RKRConFrame {
889 _private: [u8; 0],
890}
891pub struct RKRConFrameWriter {
896 _private: [u8; 0],
897}
898#[repr(C)]
904pub struct RKRArrayView {
905 pub data: *const std::ffi::c_void,
906 pub n: usize,
907 pub cols: u32,
908 pub dtype_code: u8,
909 pub dtype_bits: u8,
910}
911
912impl RKRArrayView {
913 fn empty() -> Self {
914 Self {
915 data: std::ptr::null(),
916 n: 0,
917 cols: 0,
918 dtype_code: 0,
919 dtype_bits: 0,
920 }
921 }
922
923 fn from_array2(arr: &crate::storage_dtype::FloatArray2) -> Self {
924 let kind = arr.kind();
925 Self {
926 data: arr.data_ptr() as *const std::ffi::c_void,
927 n: arr.nrows(),
928 cols: arr.ncols() as u32,
929 dtype_code: kind.dlpack_code(),
930 dtype_bits: kind.dlpack_bits(),
931 }
932 }
933
934 fn from_array1(arr: &crate::storage_dtype::FloatArray1) -> Self {
935 let kind = arr.kind();
936 Self {
937 data: arr.data_ptr() as *const std::ffi::c_void,
938 n: arr.len(),
939 cols: 1,
940 dtype_code: kind.dlpack_code(),
941 dtype_bits: kind.dlpack_bits(),
942 }
943 }
944}
945
946#[repr(C)]
947pub struct CFrame {
948 pub atoms: *mut CAtom,
949 pub num_atoms: usize,
950 pub cell: [f64; 3],
951 pub angles: [f64; 3],
952 pub has_velocities: bool,
953 pub has_forces: bool,
954 pub has_energies: bool,
955}
956#[repr(C)]
966pub struct CAtom {
967 pub atomic_number: u64,
968 pub x: f64,
969 pub y: f64,
970 pub z: f64,
971 pub atom_id: u64,
972 pub mass: f64,
973 pub is_fixed: bool,
976 pub fixed_x: bool,
977 pub fixed_y: bool,
978 pub fixed_z: bool,
979 pub vx: f64,
980 pub vy: f64,
981 pub vz: f64,
982 pub has_velocity: bool,
983 pub fx: f64,
984 pub fy: f64,
985 pub fz: f64,
986 pub has_forces: bool,
987 pub energy: f64,
990 pub has_energy: bool,
991}
992#[repr(C)]
993pub struct CConFrameIterator {
994 iterator: *mut ConFrameIterator<'static>,
995 file_contents: *mut crate::compression::ByteSource,
996}
997
998fn c_iterator_from_source(
1004 source: crate::compression::ByteSource,
1005 utf8_checked: bool,
1006) -> *mut CConFrameIterator {
1007 let source = Box::new(source);
1008 let source_ptr = Box::into_raw(source);
1009 let bytes = unsafe { (*source_ptr).as_slice() };
1010 if !utf8_checked && std::str::from_utf8(bytes).is_err() {
1011 unsafe {
1012 drop(Box::from_raw(source_ptr));
1013 }
1014 return ptr::null_mut();
1015 }
1016 let text: &'static str = unsafe { std::str::from_utf8_unchecked(bytes) };
1019 let iterator = Box::new(ConFrameIterator::new(text));
1020 let c_iterator = Box::new(CConFrameIterator {
1021 iterator: Box::into_raw(iterator),
1022 file_contents: source_ptr,
1023 });
1024 Box::into_raw(c_iterator)
1025}
1026
1027fn c_iterator_from_owned_string(contents: String) -> *mut CConFrameIterator {
1029 c_iterator_from_source(
1030 crate::compression::ByteSource::Buf(contents.into_bytes()),
1031 true,
1032 )
1033}
1034
1035#[unsafe(no_mangle)]
1051pub unsafe extern "C" fn read_con_file_iterator(
1052 filename_c: *const c_char,
1053) -> *mut CConFrameIterator {
1054 if filename_c.is_null() {
1055 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
1056 }
1057 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
1058 Ok(s) => s,
1059 Err(e) => {
1060 set_last_error(
1061 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
1062 &e.to_string(),
1063 0,
1064 0,
1065 );
1066 return ptr::null_mut();
1067 }
1068 };
1069 let owned = match crate::compression::read_bytes_owned(Path::new(filename)) {
1070 Ok(bytes) => bytes,
1071 Err(e) => {
1072 note_boxed(e);
1073 return ptr::null_mut();
1074 }
1075 };
1076 if let Err(e) = std::str::from_utf8(owned.as_slice()) {
1077 set_last_error(
1078 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
1079 &e.to_string(),
1080 0,
1081 0,
1082 );
1083 return ptr::null_mut();
1084 }
1085 c_iterator_from_source(owned, true)
1086}
1087
1088#[unsafe(no_mangle)]
1096pub unsafe extern "C" fn read_con_string_iterator(
1097 contents_c: *const c_char,
1098) -> *mut CConFrameIterator {
1099 if contents_c.is_null() {
1100 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
1101 }
1102 let contents = match unsafe { CStr::from_ptr(contents_c).to_str() } {
1103 Ok(s) => s.to_owned(),
1104 Err(e) => {
1105 set_last_error(
1106 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
1107 &e.to_string(),
1108 0,
1109 0,
1110 );
1111 return ptr::null_mut();
1112 }
1113 };
1114 c_iterator_from_owned_string(contents)
1115}
1116
1117#[unsafe(no_mangle)]
1124pub unsafe extern "C" fn read_con_buffer_iterator(
1125 data: *const u8,
1126 len: usize,
1127) -> *mut CConFrameIterator {
1128 if data.is_null() && len > 0 {
1129 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
1130 }
1131 if len == 0 {
1132 return c_iterator_from_owned_string(String::new());
1133 }
1134 let slice = unsafe { std::slice::from_raw_parts(data, len) };
1135 let contents = match std::str::from_utf8(slice) {
1136 Ok(s) => s.to_owned(),
1137 Err(_) => return null_err::<_>(RKRStatus::RKR_STATUS_INVALID_UTF8),
1138 };
1139 c_iterator_from_owned_string(contents)
1140}
1141#[unsafe(no_mangle)]
1147pub unsafe extern "C" fn con_frame_iterator_next(
1148 iterator: *mut CConFrameIterator,
1149) -> *mut RKRConFrame {
1150 if iterator.is_null() {
1151 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
1152 }
1153 let iter = unsafe { &mut *(*iterator).iterator };
1154 match iter.next() {
1155 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
1156 Some(Err(e)) => {
1157 let located =
1158 crate::error::LocatedParseError::new(e, iter.lines.line_no(), iter.frame_index());
1159 null_located(&located)
1160 }
1161 None => ptr::null_mut(),
1162 }
1163}
1164
1165#[unsafe(no_mangle)]
1172pub unsafe extern "C" fn con_frame_iterator_forward(iterator: *mut CConFrameIterator) -> RKRStatus {
1173 if iterator.is_null() {
1174 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
1175 }
1176 let iter = unsafe { &mut *(*iterator).iterator };
1177 match iter.forward() {
1178 Some(Ok(())) => RKRStatus::RKR_STATUS_SUCCESS,
1179 Some(Err(e)) => {
1180 let located =
1181 crate::error::LocatedParseError::new(e, iter.lines.line_no(), iter.frame_index());
1182 note_located(&located)
1183 }
1184 None => note(RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS),
1185 }
1186}
1187
1188#[unsafe(no_mangle)]
1195pub unsafe extern "C" fn con_frame_iterator_skip(
1196 iterator: *mut CConFrameIterator,
1197 n: usize,
1198) -> usize {
1199 if iterator.is_null() {
1200 note(RKRStatus::RKR_STATUS_NULL_POINTER);
1201 return usize::MAX;
1202 }
1203 let iter = unsafe { &mut *(*iterator).iterator };
1204 match iter.skip_frames(n) {
1205 Ok(skipped) => skipped,
1206 Err(e) => {
1207 let located =
1208 crate::error::LocatedParseError::new(e, iter.lines.line_no(), iter.frame_index());
1209 note_located(&located);
1210 usize::MAX
1211 }
1212 }
1213}
1214
1215#[unsafe(no_mangle)]
1221pub unsafe extern "C" fn con_frame_iterator_nth(
1222 iterator: *mut CConFrameIterator,
1223 index: usize,
1224) -> *mut RKRConFrame {
1225 if iterator.is_null() {
1226 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
1227 }
1228 let iter = unsafe { &mut *(*iterator).iterator };
1229 match iter.skip_frames_counted(index) {
1230 Ok(skipped) if skipped == index => {}
1231 Ok(_) => return null_err::<_>(RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS),
1232 Err((_failed_at, e)) => {
1233 let located =
1234 crate::error::LocatedParseError::new(e, iter.lines.line_no(), iter.frame_index());
1235 return null_located(&located);
1236 }
1237 }
1238 match iter.next() {
1239 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
1240 Some(Err(e)) => {
1241 let located =
1242 crate::error::LocatedParseError::new(e, iter.lines.line_no(), iter.frame_index());
1243 null_located(&located)
1244 }
1245 None => null_err::<_>(RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS),
1246 }
1247}
1248
1249#[unsafe(no_mangle)]
1255pub unsafe extern "C" fn rkr_count_frames(filename_c: *const c_char) -> usize {
1256 if filename_c.is_null() {
1257 note(RKRStatus::RKR_STATUS_NULL_POINTER);
1258 return usize::MAX;
1259 }
1260 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
1261 Ok(s) => s,
1262 Err(e) => {
1263 set_last_error(
1264 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
1265 &e.to_string(),
1266 0,
1267 0,
1268 );
1269 return usize::MAX;
1270 }
1271 };
1272 match crate::iterators::count_frames(Path::new(filename)) {
1273 Ok(n) => n,
1274 Err(e) => {
1275 note_load(e);
1276 usize::MAX
1277 }
1278 }
1279}
1280
1281#[unsafe(no_mangle)]
1287pub unsafe extern "C" fn rkr_read_nth_frame(
1288 filename_c: *const c_char,
1289 index: usize,
1290) -> *mut RKRConFrame {
1291 if filename_c.is_null() {
1292 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
1293 }
1294 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
1295 Ok(s) => s,
1296 Err(e) => {
1297 set_last_error(
1298 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
1299 &e.to_string(),
1300 0,
1301 0,
1302 );
1303 return ptr::null_mut();
1304 }
1305 };
1306 match crate::iterators::read_nth_frame_located(Path::new(filename), index) {
1307 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
1308 Err(e) => null_load(e),
1309 }
1310}
1311
1312pub struct RKRTrajectory {
1316 _private: [u8; 0],
1317}
1318
1319#[unsafe(no_mangle)]
1329pub unsafe extern "C" fn rkr_trajectory_open(
1330 filename_c: *const c_char,
1331 write_index: i32,
1332) -> *mut RKRTrajectory {
1333 if filename_c.is_null() {
1334 set_last_error(
1335 RKRStatus::RKR_STATUS_NULL_POINTER as i32,
1336 "filename is NULL",
1337 0,
1338 0,
1339 );
1340 return ptr::null_mut();
1341 }
1342 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
1343 Ok(s) => s,
1344 Err(_) => {
1345 set_last_error(
1346 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
1347 "filename is not valid UTF-8",
1348 0,
1349 0,
1350 );
1351 return ptr::null_mut();
1352 }
1353 };
1354 let opened = std::panic::catch_unwind(|| {
1355 crate::trajectory::ConTrajectory::open(Path::new(filename), write_index != 0)
1356 .map_err(|e| e.to_string())
1357 })
1358 .unwrap_or_else(|_| Err("internal error while opening the file".to_owned()));
1359 match opened {
1360 Ok(traj) => {
1361 clear_last_error();
1362 Box::into_raw(Box::new(traj)) as *mut RKRTrajectory
1363 }
1364 Err(msg) => {
1365 set_last_error(RKRStatus::RKR_STATUS_IO_ERROR as i32, &msg, 0, 0);
1366 ptr::null_mut()
1367 }
1368 }
1369}
1370
1371#[unsafe(no_mangle)]
1379pub unsafe extern "C" fn rkr_trajectory_len(handle: *const RKRTrajectory) -> usize {
1380 match unsafe { (handle as *const crate::trajectory::ConTrajectory).as_ref() } {
1381 Some(traj) => traj.len(),
1382 None => {
1383 set_last_error(
1384 RKRStatus::RKR_STATUS_NULL_POINTER as i32,
1385 "null argument",
1386 0,
1387 0,
1388 );
1389 usize::MAX
1390 }
1391 }
1392}
1393
1394#[unsafe(no_mangle)]
1407pub unsafe extern "C" fn rkr_trajectory_read_frame(
1408 handle: *const RKRTrajectory,
1409 index: usize,
1410 out_frame: *mut *mut RKRConFrame,
1411) -> RKRStatus {
1412 if !out_frame.is_null() {
1413 unsafe { *out_frame = ptr::null_mut() };
1414 }
1415 if handle.is_null() || out_frame.is_null() {
1416 set_last_error(
1417 RKRStatus::RKR_STATUS_NULL_POINTER as i32,
1418 "null argument",
1419 0,
1420 0,
1421 );
1422 return RKRStatus::RKR_STATUS_NULL_POINTER;
1423 }
1424 let traj = unsafe { &*(handle as *const crate::trajectory::ConTrajectory) };
1425 let parsed = std::panic::catch_unwind(|| traj.frame(index));
1426 let (status, msg) = match parsed {
1427 Ok(Ok(frame)) => {
1428 clear_last_error();
1429 unsafe { *out_frame = Box::into_raw(Box::new(frame)) as *mut RKRConFrame };
1430 return RKRStatus::RKR_STATUS_SUCCESS;
1431 }
1432 Ok(Err(crate::error::ParseError::IndexOutOfBounds { index, len })) => (
1433 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
1434 format!("frame index {index} is out of range for a trajectory of {len} frames"),
1435 ),
1436 Ok(Err(e)) => (
1437 RKRStatus::RKR_STATUS_IO_ERROR,
1438 format!("frame {index}: {e}"),
1439 ),
1440 Err(_) => (
1441 RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1442 format!("internal error while reading frame {index}"),
1443 ),
1444 };
1445 set_last_error(status as i32, &msg, 0, 0);
1446 status
1447}
1448
1449#[unsafe(no_mangle)]
1454pub unsafe extern "C" fn rkr_trajectory_free(handle: *mut RKRTrajectory) {
1455 if !handle.is_null() {
1456 let _ = unsafe { Box::from_raw(handle as *mut crate::trajectory::ConTrajectory) };
1457 }
1458}
1459#[unsafe(no_mangle)]
1464pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
1465 if !frame_handle.is_null() {
1466 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
1467 }
1468}
1469#[unsafe(no_mangle)]
1474pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
1475 if iterator.is_null() {
1476 return;
1477 }
1478 unsafe {
1479 let c_iterator_box = Box::from_raw(iterator);
1480 let _ = Box::from_raw(c_iterator_box.iterator);
1481 let _ = Box::from_raw(c_iterator_box.file_contents);
1482 }
1483}
1484fn export_catoms(frame: &ConFrame) -> Vec<CAtom> {
1494 let atoms = &frame.atom_data;
1495 let mut out = Vec::with_capacity(atoms.len());
1496 let has_vel = frame.has_velocities();
1497 let has_force = frame.has_forces();
1498 let mut mass_slot = 0usize;
1499 let mut mass_left = 0usize;
1500 let mut mass = 0.0f64;
1501 let mut last_symbol: *const u8 = std::ptr::null();
1502 let mut z = 0u64;
1503 let mut i = 0usize;
1504 while i < atoms.len() {
1505 if mass_left == 0 {
1506 if mass_slot >= frame.header.natms_per_type.len() {
1507 break;
1508 }
1509 mass_left = frame.header.natms_per_type[mass_slot];
1510 mass = frame
1511 .header
1512 .masses_per_type
1513 .get(mass_slot)
1514 .copied()
1515 .unwrap_or(0.0);
1516 mass_slot += 1;
1517 if mass_left == 0 {
1518 continue;
1519 }
1520 }
1521 let symbol = atoms[i].symbol.as_ptr();
1522 if symbol != last_symbol {
1523 z = symbol_to_atomic_number(atoms[i].symbol.as_ref());
1524 last_symbol = symbol;
1525 }
1526 out.push(catom_from_datum(&atoms[i], z, mass, has_vel, has_force));
1527 i += 1;
1528 mass_left -= 1;
1529 }
1530 out
1531}
1532
1533fn catom_from_datum(
1534 atom: &crate::types::AtomDatum,
1535 atomic_number: u64,
1536 mass: f64,
1537 has_vel: bool,
1538 has_force: bool,
1539) -> CAtom {
1540 let [vx, vy, vz] = if has_vel {
1541 atom.velocity().unwrap_or([0.0; 3])
1542 } else {
1543 [0.0; 3]
1544 };
1545 let [fx, fy, fz] = if has_force {
1546 atom.force().unwrap_or([0.0; 3])
1547 } else {
1548 [0.0; 3]
1549 };
1550 let fixed = atom.fixed();
1551 CAtom {
1552 atomic_number,
1553 x: atom.x(),
1554 y: atom.y(),
1555 z: atom.z(),
1556 is_fixed: atom.is_fixed(),
1557 fixed_x: fixed[0],
1558 fixed_y: fixed[1],
1559 fixed_z: fixed[2],
1560 atom_id: atom.atom_id,
1561 mass,
1562 vx,
1563 vy,
1564 vz,
1565 has_velocity: atom.has_velocity(),
1566 fx,
1567 fy,
1568 fz,
1569 has_forces: atom.has_forces(),
1570 energy: atom.energy().unwrap_or(0.0),
1571 has_energy: atom.has_energy(),
1572 }
1573}
1574
1575fn metadata_json_text(frame: &ConFrame) -> String {
1581 if let Some(raw) = frame.header.metadata.unedited_line() {
1582 thread_local! {
1583 static LAST: RefCell<(std::sync::Arc<str>, String)> =
1584 RefCell::new((std::sync::Arc::from(""), String::new()));
1585 }
1586 return LAST.with(|slot| {
1587 let mut slot = slot.borrow_mut();
1588 if !slot.1.is_empty() && std::sync::Arc::ptr_eq(&slot.0, raw) {
1589 return slot.1.clone();
1590 }
1591 if slot.0.as_ref() == raw.as_ref() && !slot.1.is_empty() {
1592 return slot.1.clone();
1593 }
1594 let json = metadata_json_rebuilt(frame);
1595 slot.0 = std::sync::Arc::clone(raw);
1596 slot.1.clone_from(&json);
1597 json
1598 });
1599 }
1600 metadata_json_rebuilt(frame)
1601}
1602
1603fn metadata_json_rebuilt(frame: &ConFrame) -> String {
1604 let mut obj = serde_json::Map::new();
1605 obj.insert(
1606 meta::CON_SPEC_VERSION.into(),
1607 serde_json::Value::from(frame.header.spec_version),
1608 );
1609 for (k, v) in &frame.header.metadata {
1610 obj.insert(k.clone(), v.clone());
1611 }
1612 serde_json::Value::Object(obj).to_string()
1613}
1614
1615#[unsafe(no_mangle)]
1626pub unsafe extern "C" fn rkr_frame_plain_geometry(
1627 frame_handle: *const RKRConFrame,
1628 atomic_numbers: *mut u64,
1629 masses: *mut f64,
1630 counts: *mut u64,
1631 n_types: *mut usize,
1632 cell: *mut f64,
1633 angles: *mut f64,
1634) -> i32 {
1635 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
1636 Some(frame) => frame,
1637 None => return 0,
1638 };
1639 if !frame.sequential_plain
1640 || frame.has_velocities()
1641 || frame.has_forces()
1642 || frame.has_energies()
1643 {
1644 return 0;
1645 }
1646 let xyz = match frame.positions.f64_slice() {
1647 Some(xyz) => xyz,
1648 None => return 0,
1649 };
1650 let atoms = &frame.atom_data;
1651 if xyz.len() != atoms.len().saturating_mul(3) {
1652 return 0;
1653 }
1654 let cap = unsafe { *n_types };
1655 let n_species = frame.header.natms_per_type.len();
1656 if n_species > cap
1657 || n_species != frame.header.masses_per_type.len()
1658 || atomic_numbers.is_null()
1659 || masses.is_null()
1660 || counts.is_null()
1661 || cell.is_null()
1662 || angles.is_null()
1663 {
1664 return 0;
1665 }
1666 let mut cursor = 0usize;
1667 for (slot, count) in frame.header.natms_per_type.iter().copied().enumerate() {
1668 if count == 0 || cursor >= atoms.len() {
1669 return 0;
1670 }
1671 let z = symbol_to_atomic_number(atoms[cursor].symbol.as_ref());
1672 unsafe {
1673 *atomic_numbers.add(slot) = z;
1674 *masses.add(slot) = frame.header.masses_per_type[slot];
1675 *counts.add(slot) = count as u64;
1676 }
1677 cursor += count;
1678 }
1679 if cursor != atoms.len() {
1680 return 0;
1681 }
1682 unsafe {
1683 *n_types = n_species;
1684 std::ptr::copy_nonoverlapping(frame.header.boxl.as_ptr(), cell, 3);
1685 std::ptr::copy_nonoverlapping(frame.header.angles.as_ptr(), angles, 3);
1686 }
1687 1
1688}
1689
1690#[unsafe(no_mangle)]
1696pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
1697 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
1698 Some(f) => f,
1699 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
1700 };
1701 let c_atoms = export_catoms(frame);
1702 let has_velocities = frame.has_velocities();
1703 let boxed = c_atoms.into_boxed_slice();
1707 let num_atoms = boxed.len();
1708 let atoms_ptr = Box::into_raw(boxed).cast::<CAtom>();
1709 let has_forces = frame.has_forces();
1710 let has_energies = frame.has_energies();
1711 let c_frame = Box::new(CFrame {
1712 atoms: atoms_ptr,
1713 num_atoms,
1714 cell: frame.header.boxl,
1715 angles: frame.header.angles,
1716 has_velocities,
1717 has_forces,
1718 has_energies,
1719 });
1720 Box::into_raw(c_frame)
1721}
1722#[unsafe(no_mangle)]
1727pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
1728 if frame.is_null() {
1729 return;
1730 }
1731 unsafe {
1732 let frame_box = Box::from_raw(frame);
1733 if frame_box.num_atoms > 0 {
1734 drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(
1735 frame_box.atoms,
1736 frame_box.num_atoms,
1737 )));
1738 }
1739 }
1740}
1741#[unsafe(no_mangle)]
1760pub unsafe extern "C" fn rkr_frame_get_header_line(
1761 frame_handle: *const RKRConFrame,
1762 is_prebox: bool,
1763 line_index: usize,
1764 buffer: *mut c_char,
1765 buffer_len: usize,
1766) -> RKRStatus {
1767 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
1768 Some(f) => f,
1769 None => return note(RKRStatus::RKR_STATUS_NULL_POINTER),
1770 };
1771 if buffer.is_null() {
1772 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
1773 }
1774 if buffer_len == 0 {
1775 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
1776 }
1777 let line_to_copy: Option<&str> = if is_prebox {
1778 match line_index {
1779 0 => Some(frame.header.prebox_header.user.as_str()),
1780 1 => Some(frame.header.prebox_header.metadata_line()),
1781 _ => None,
1782 }
1783 } else {
1784 frame
1785 .header
1786 .postbox_header
1787 .get(line_index)
1788 .map(String::as_str)
1789 };
1790 if let Some(line) = line_to_copy {
1791 let bytes = line.as_bytes();
1792 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
1793 unsafe {
1794 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
1795 *buffer.add(len_to_copy) = 0;
1796 }
1797 note(RKRStatus::RKR_STATUS_SUCCESS)
1798 } else {
1799 note(RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS)
1800 }
1801}
1802#[unsafe(no_mangle)]
1815pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
1816 frame_handle: *const RKRConFrame,
1817 is_prebox: bool,
1818 line_index: usize,
1819) -> *mut c_char {
1820 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
1821 Some(f) => f,
1822 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
1823 };
1824 let line_to_copy: Option<&str> = if is_prebox {
1825 match line_index {
1826 0 => Some(frame.header.prebox_header.user.as_str()),
1827 1 => Some(frame.header.prebox_header.metadata_line()),
1828 _ => None,
1829 }
1830 } else {
1831 frame
1832 .header
1833 .postbox_header
1834 .get(line_index)
1835 .map(String::as_str)
1836 };
1837 if let Some(line) = line_to_copy {
1838 match CString::new(line) {
1840 Ok(c_string) => c_string.into_raw(), Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR), }
1843 } else {
1844 ptr::null_mut() }
1846}
1847#[unsafe(no_mangle)]
1855pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
1856 if !s.is_null() {
1857 let _ = unsafe { CString::from_raw(s) };
1859 }
1860}
1861type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
1875#[inline]
1878fn into_rkr_writer(sink: Box<dyn std::io::Write>, precision: Option<u8>) -> *mut RKRConFrameWriter {
1879 let writer: RkrWriter = match precision {
1880 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
1881 None => ConFrameWriter::new(sink),
1882 };
1883 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1884}
1885#[inline]
1887unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
1888 if filename_c.is_null() {
1889 return None;
1890 }
1891 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
1892}
1893#[unsafe(no_mangle)]
1899pub unsafe extern "C" fn create_writer_from_path_c(
1900 filename_c: *const c_char,
1901) -> *mut RKRConFrameWriter {
1902 let filename = match unsafe { cstr_path(filename_c) } {
1903 Some(s) => s,
1904 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
1905 };
1906 match File::create(filename) {
1907 Ok(file) => into_rkr_writer(Box::new(file), None),
1908 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_IO_ERROR),
1909 }
1910}
1911#[unsafe(no_mangle)]
1917pub unsafe extern "C" fn create_writer_from_path_round_trip_c(
1918 filename_c: *const c_char,
1919) -> *mut RKRConFrameWriter {
1920 let filename = match unsafe { cstr_path(filename_c) } {
1921 Some(s) => s,
1922 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
1923 };
1924 match File::create(filename) {
1925 Ok(file) => {
1926 let writer: RkrWriter = ConFrameWriter::with_float_format(
1927 Box::new(file),
1928 crate::writer::FloatFormat::RoundTrip,
1929 );
1930 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
1931 }
1932 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_IO_ERROR),
1933 }
1934}
1935
1936#[unsafe(no_mangle)]
1946pub unsafe extern "C" fn create_writer_from_path_append_c(
1947 filename_c: *const c_char,
1948) -> *mut RKRConFrameWriter {
1949 let Some(filename) = (unsafe { cstr_path(filename_c) }) else {
1950 return ptr::null_mut();
1951 };
1952 match crate::writer::open_uncompressed_append(Path::new(filename)) {
1953 Ok(file) => into_rkr_writer(Box::new(file), None),
1954 Err(err) => {
1955 set_last_error(
1956 RKRStatus::RKR_STATUS_IO_ERROR as i32,
1957 &err.to_string(),
1958 0,
1959 0,
1960 );
1961 ptr::null_mut()
1962 }
1963 }
1964}
1965
1966#[unsafe(no_mangle)]
1973pub unsafe extern "C" fn create_writer_from_path_append_with_precision_c(
1974 filename_c: *const c_char,
1975 precision: u8,
1976) -> *mut RKRConFrameWriter {
1977 let Some(filename) = (unsafe { cstr_path(filename_c) }) else {
1978 return ptr::null_mut();
1979 };
1980 match crate::writer::open_uncompressed_append(Path::new(filename)) {
1981 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
1982 Err(err) => {
1983 set_last_error(
1984 RKRStatus::RKR_STATUS_IO_ERROR as i32,
1985 &err.to_string(),
1986 0,
1987 0,
1988 );
1989 ptr::null_mut()
1990 }
1991 }
1992}
1993
1994#[unsafe(no_mangle)]
2002pub unsafe extern "C" fn rkr_writer_flush(writer_handle: *mut RKRConFrameWriter) -> RKRStatus {
2003 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
2004 Some(w) => w,
2005 None => return note(RKRStatus::RKR_STATUS_NULL_POINTER),
2006 };
2007 match writer.flush() {
2008 Ok(()) => {
2009 clear_last_error();
2010 RKRStatus::RKR_STATUS_SUCCESS
2011 }
2012 Err(err) => {
2013 set_last_error(
2014 RKRStatus::RKR_STATUS_IO_ERROR as i32,
2015 &err.to_string(),
2016 0,
2017 0,
2018 );
2019 RKRStatus::RKR_STATUS_IO_ERROR
2020 }
2021 }
2022}
2023
2024#[unsafe(no_mangle)]
2029pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
2030 if !writer_handle.is_null() {
2031 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
2032 }
2033}
2034#[unsafe(no_mangle)]
2043pub unsafe extern "C" fn rkr_frame_binding_rewrite(
2044 frame_handle: *const RKRConFrame,
2045) -> *mut RKRConFrame {
2046 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
2047 return ptr::null_mut();
2048 };
2049 match binding_rewrite_frame(frame) {
2050 Ok(rewritten) => Box::into_raw(Box::new(rewritten)) as *mut RKRConFrame,
2051 Err(_) => ptr::null_mut(),
2052 }
2053}
2054
2055#[unsafe(no_mangle)]
2066pub unsafe extern "C" fn rkr_writer_extend_binding(
2067 writer_handle: *mut RKRConFrameWriter,
2068 frame_handles: *const *mut RKRConFrame,
2069 num_frames: usize,
2070) -> RKRStatus {
2071 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
2072 Some(w) => w,
2073 None => return note(RKRStatus::RKR_STATUS_NULL_POINTER),
2074 };
2075 if frame_handles.is_null() && num_frames > 0 {
2076 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2077 }
2078 for i in 0..num_frames {
2079 let handle = unsafe { *frame_handles.add(i) };
2080 let Some(frame) = (unsafe { (handle as *mut ConFrame).as_mut() }) else {
2081 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2082 };
2083 if let Err(err) = writer.write_frame_binding(frame) {
2084 set_last_error(
2085 RKRStatus::RKR_STATUS_IO_ERROR as i32,
2086 &err.to_string(),
2087 0,
2088 0,
2089 );
2090 return RKRStatus::RKR_STATUS_IO_ERROR;
2091 }
2092 }
2093 clear_last_error();
2094 RKRStatus::RKR_STATUS_SUCCESS
2095}
2096
2097#[unsafe(no_mangle)]
2107pub unsafe extern "C" fn rkr_writer_extend(
2108 writer_handle: *mut RKRConFrameWriter,
2109 frame_handles: *const *const RKRConFrame,
2110 num_frames: usize,
2111) -> RKRStatus {
2112 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
2113 Some(w) => w,
2114 None => return note(RKRStatus::RKR_STATUS_NULL_POINTER),
2115 };
2116 if frame_handles.is_null() {
2117 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2118 }
2119 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
2120 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
2121 if handles_slice.iter().any(|&handle| handle.is_null()) {
2122 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2125 }
2126 for &handle in handles_slice.iter() {
2127 match unsafe { (handle as *const ConFrame).as_ref() } {
2129 Some(frame) => rust_frames.push(frame),
2130 None => return note(RKRStatus::RKR_STATUS_NULL_POINTER),
2132 }
2133 }
2134 match writer.extend(rust_frames.into_iter()) {
2135 Ok(_) => {
2136 clear_last_error();
2137 RKRStatus::RKR_STATUS_SUCCESS
2138 }
2139 Err(err) => {
2140 set_last_error(
2141 RKRStatus::RKR_STATUS_IO_ERROR as i32,
2142 &err.to_string(),
2143 0,
2144 0,
2145 );
2146 RKRStatus::RKR_STATUS_IO_ERROR
2147 }
2148 }
2149}
2150
2151#[unsafe(no_mangle)]
2163pub unsafe extern "C" fn rkr_write_frames_to_string(
2164 frame_handles: *const *const RKRConFrame,
2165 num_frames: usize,
2166 out_text: *mut *mut c_char,
2167 out_len: *mut usize,
2168) -> RKRStatus {
2169 if out_text.is_null() || out_len.is_null() {
2170 return RKRStatus::RKR_STATUS_NULL_POINTER;
2171 }
2172 unsafe {
2173 *out_text = ptr::null_mut();
2174 *out_len = 0;
2175 }
2176 if num_frames > 0 && frame_handles.is_null() {
2177 return RKRStatus::RKR_STATUS_NULL_POINTER;
2178 }
2179 let mut buffer = Vec::new();
2180 {
2181 let mut writer = ConFrameWriter::new(&mut buffer);
2182 for i in 0..num_frames {
2183 let handle = unsafe { *frame_handles.add(i) };
2184 let Some(frame) = (unsafe { (handle as *const ConFrame).as_ref() }) else {
2185 return RKRStatus::RKR_STATUS_NULL_POINTER;
2186 };
2187 if let Err(err) = writer.write_frame(frame) {
2188 set_last_error(
2189 RKRStatus::RKR_STATUS_IO_ERROR as i32,
2190 &err.to_string(),
2191 0,
2192 0,
2193 );
2194 return RKRStatus::RKR_STATUS_IO_ERROR;
2195 }
2196 }
2197 }
2198 let len = buffer.len();
2199 match CString::new(buffer) {
2200 Ok(text) => {
2201 unsafe {
2202 *out_text = text.into_raw();
2203 *out_len = len;
2204 }
2205 clear_last_error();
2206 RKRStatus::RKR_STATUS_SUCCESS
2207 }
2208 Err(_) => {
2209 set_last_error(
2210 RKRStatus::RKR_STATUS_IO_ERROR as i32,
2211 "serialized CON contains an interior NUL",
2212 0,
2213 0,
2214 );
2215 RKRStatus::RKR_STATUS_IO_ERROR
2216 }
2217 }
2218}
2219
2220#[unsafe(no_mangle)]
2226pub unsafe extern "C" fn rkr_writer_set_canonical(
2227 writer_handle: *mut RKRConFrameWriter,
2228 canonical: u8,
2229) -> RKRStatus {
2230 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
2231 Some(w) => w,
2232 None => return note(RKRStatus::RKR_STATUS_NULL_POINTER),
2233 };
2234 writer.set_canonical(canonical != 0);
2235 note(RKRStatus::RKR_STATUS_SUCCESS)
2236}
2237
2238#[unsafe(no_mangle)]
2244pub unsafe extern "C" fn rkr_writer_is_canonical(writer_handle: *const RKRConFrameWriter) -> u8 {
2245 match unsafe { (writer_handle as *const RkrWriter).as_ref() } {
2246 Some(w) => u8::from(w.is_canonical()),
2247 None => 0,
2248 }
2249}
2250
2251#[cfg(test)]
2252mod index_proj_ffi_tests {
2253 use super::*;
2254 use std::ffi::CStr;
2255 use std::fs;
2256 use std::path::PathBuf;
2257
2258 fn fixture_path() -> PathBuf {
2259 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/tiny_cuh2.con")
2260 }
2261
2262 #[test]
2263 fn ffi_projection_matches_index_proj() {
2264 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
2265 let fr = &frames[0];
2266 let handle = fr as *const ConFrame as *const RKRConFrame;
2267 let proj = crate::index_proj::FrameIndexProjection::from_frame(fr);
2268 assert_eq!(rkr_frame_index_natoms(handle), proj.n_atoms);
2270 assert_eq!(rkr_frame_sections_mask(handle), proj.sections_mask);
2271 let formula_c = unsafe { rkr_frame_composition_formula(handle) };
2272 assert!(!formula_c.is_null());
2273 let formula = unsafe { CStr::from_ptr(formula_c) }.to_str().unwrap();
2274 assert_eq!(formula, proj.formula);
2275 unsafe { rkr_free_string(formula_c) };
2276 let ie = rkr_frame_index_energy(handle);
2277 match proj.energy {
2278 Some(e) => assert!((ie - e).abs() < 1e-12 || (ie.is_nan() && e.is_nan())),
2279 None => assert!(ie.is_nan()),
2280 }
2281 let tm = rkr_frame_total_mass(handle);
2282 match proj.total_mass {
2283 Some(m) => assert!((tm - m).abs() < 1e-9, "total_mass C={tm} proj={m}"),
2284 None => assert!(tm.is_nan()),
2285 }
2286 let cv = rkr_frame_cell_volume(handle);
2287 match proj.cell_volume {
2288 Some(v) => assert!(
2289 (cv - v).abs() < 1e-6 * v.max(1.0),
2290 "cell_volume C={cv} proj={v}"
2291 ),
2292 None => assert!(cv.is_nan()),
2293 }
2294 let fm = rkr_frame_fmax(handle);
2295 match proj.fmax {
2296 Some(f) => assert!((fm - f).abs() < 1e-12 || (fm.is_nan() && f.is_nan())),
2297 None => assert!(fm.is_nan()),
2298 }
2299 let json_c = unsafe { rkr_frame_index_projection_json(handle) };
2300 assert!(!json_c.is_null());
2301 let json = unsafe { CStr::from_ptr(json_c) }.to_str().unwrap();
2302 assert!(json.contains("\"formula\""));
2303 assert!(json.contains(&proj.formula) || proj.formula.is_empty());
2304 assert!(json.contains("\"n_atoms\""));
2305 assert!(json.contains("\"total_mass\"") || json.contains("\"cell_volume\""));
2306 unsafe { rkr_free_string(json_c) };
2307 }
2308
2309 #[test]
2310 fn ffi_canonical_writer_byte_identical() {
2311 let frames = crate::iterators::read_all_frames(&fixture_path()).unwrap();
2312 let fr = &frames[0];
2313 let dir = tempfile::tempdir().unwrap();
2314 let p1 = dir.path().join("a.con");
2315 let p2 = dir.path().join("b.con");
2316 for p in [&p1, &p2] {
2317 let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
2318 let w = unsafe { create_writer_from_path_c(path_c.as_ptr()) };
2319 assert!(!w.is_null());
2320 assert_eq!(
2321 unsafe { rkr_writer_set_canonical(w, 1) },
2322 note(RKRStatus::RKR_STATUS_SUCCESS)
2323 );
2324 assert_eq!(unsafe { rkr_writer_is_canonical(w) }, 1);
2325 let handles = [fr as *const ConFrame as *const RKRConFrame];
2326 assert_eq!(
2327 unsafe { rkr_writer_extend(w, handles.as_ptr(), 1) },
2328 note(RKRStatus::RKR_STATUS_SUCCESS)
2329 );
2330 unsafe { free_rkr_writer(w) };
2331 }
2332 let b1 = fs::read(&p1).unwrap();
2333 let b2 = fs::read(&p2).unwrap();
2334 assert_eq!(b1, b2);
2335 assert!(!b1.is_empty());
2336 }
2337}
2338#[unsafe(no_mangle)]
2347pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
2348 filename_c: *const c_char,
2349 precision: u8,
2350) -> *mut RKRConFrameWriter {
2351 let filename = match unsafe { cstr_path(filename_c) } {
2352 Some(s) => s,
2353 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
2354 };
2355 match File::create(filename) {
2356 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
2357 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_IO_ERROR),
2358 }
2359}
2360pub struct RKRConFrameBuilder {
2367 _private: [u8; 0],
2368}
2369#[allow(clippy::too_many_arguments)]
2370unsafe fn add_builder_atom(
2371 builder_handle: *mut RKRConFrameBuilder,
2372 symbol: *const c_char,
2373 x: f64,
2374 y: f64,
2375 z: f64,
2376 fixed: [bool; 3],
2377 atom_id: u64,
2378 mass: f64,
2379 velocity: Option<[f64; 3]>,
2380 forces: Option<[f64; 3]>,
2381) -> RKRStatus {
2382 if builder_handle.is_null() || symbol.is_null() {
2383 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2384 }
2385 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2386 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
2387 Ok(s) => s,
2388 Err(_) => return note(RKRStatus::RKR_STATUS_INVALID_UTF8),
2389 };
2390 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
2391 if let Some(v) = velocity {
2392 builder.with_velocity(v);
2393 }
2394 if let Some(f) = forces {
2395 builder.with_force(f);
2396 }
2397 note(RKRStatus::RKR_STATUS_SUCCESS)
2398}
2399#[unsafe(no_mangle)]
2405pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
2406 builder_handle: *mut RKRConFrameBuilder,
2407 velocity: *const f64,
2408) -> RKRStatus {
2409 if builder_handle.is_null() || velocity.is_null() {
2410 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2411 }
2412 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2413 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
2414 builder.with_velocity(v);
2415 note(RKRStatus::RKR_STATUS_SUCCESS)
2416}
2417#[unsafe(no_mangle)]
2423pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
2424 builder_handle: *mut RKRConFrameBuilder,
2425 force: *const f64,
2426) -> RKRStatus {
2427 if builder_handle.is_null() || force.is_null() {
2428 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2429 }
2430 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2431 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
2432 builder.with_force(f);
2433 note(RKRStatus::RKR_STATUS_SUCCESS)
2434}
2435#[unsafe(no_mangle)]
2442pub unsafe extern "C" fn rkr_frame_builder_set_last_displacement(
2443 builder_handle: *mut RKRConFrameBuilder,
2444 displacement: *const f64,
2445) -> RKRStatus {
2446 if builder_handle.is_null() || displacement.is_null() {
2447 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2448 }
2449 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2450 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
2451 builder.with_displacement(d);
2452 note(RKRStatus::RKR_STATUS_SUCCESS)
2453}
2454#[unsafe(no_mangle)]
2461pub unsafe extern "C" fn rkr_frame_builder_set_last_spread(
2462 builder_handle: *mut RKRConFrameBuilder,
2463 spread: *const f64,
2464) -> RKRStatus {
2465 if builder_handle.is_null() || spread.is_null() {
2466 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2467 }
2468 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2469 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
2470 builder.with_spread(d);
2471 note(RKRStatus::RKR_STATUS_SUCCESS)
2472}
2473#[unsafe(no_mangle)]
2483pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
2484 builder_handle: *mut RKRConFrameBuilder,
2485 energy: f64,
2486) -> RKRStatus {
2487 if builder_handle.is_null() {
2488 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2489 }
2490 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2491 builder.with_energy(energy);
2492 note(RKRStatus::RKR_STATUS_SUCCESS)
2493}
2494fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
2508 use crate::error::ParseError;
2509 match e {
2510 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
2511 note(RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS)
2512 }
2513 ParseError::MassMismatch { .. } | ParseError::ValidationError(_) => {
2514 note(RKRStatus::RKR_STATUS_VALIDATION_ERROR)
2515 }
2516 _ => note(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
2517 }
2518}
2519#[unsafe(no_mangle)]
2526pub unsafe extern "C" fn rkr_frame_builder_atom_count(
2527 builder_handle: *const RKRConFrameBuilder,
2528) -> usize {
2529 if builder_handle.is_null() {
2530 return 0;
2531 }
2532 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2533 builder.atom_count()
2534}
2535#[unsafe(no_mangle)]
2539pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
2540 builder_handle: *mut RKRConFrameBuilder,
2541 index: usize,
2542 x: f64,
2543 y: f64,
2544 z: f64,
2545) -> RKRStatus {
2546 if builder_handle.is_null() {
2547 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2548 }
2549 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2550 match builder.set_atom_position(index, x, y, z) {
2551 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2552 Err(e) => map_builder_err(e),
2553 }
2554}
2555#[unsafe(no_mangle)]
2559pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
2560 builder_handle: *mut RKRConFrameBuilder,
2561 index: usize,
2562 velocity: *const f64,
2563) -> RKRStatus {
2564 if builder_handle.is_null() || velocity.is_null() {
2565 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2566 }
2567 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2568 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
2569 match builder.set_atom_velocity(index, v) {
2570 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2571 Err(e) => map_builder_err(e),
2572 }
2573}
2574#[unsafe(no_mangle)]
2578pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
2579 builder_handle: *mut RKRConFrameBuilder,
2580 index: usize,
2581 force: *const f64,
2582) -> RKRStatus {
2583 if builder_handle.is_null() || force.is_null() {
2584 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2585 }
2586 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2587 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
2588 match builder.set_atom_force(index, f) {
2589 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2590 Err(e) => map_builder_err(e),
2591 }
2592}
2593#[unsafe(no_mangle)]
2598pub unsafe extern "C" fn rkr_frame_builder_set_atom_displacement(
2599 builder_handle: *mut RKRConFrameBuilder,
2600 index: usize,
2601 displacement: *const f64,
2602) -> RKRStatus {
2603 if builder_handle.is_null() || displacement.is_null() {
2604 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2605 }
2606 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2607 let d = unsafe { [*displacement, *displacement.add(1), *displacement.add(2)] };
2608 match builder.set_atom_displacement(index, d) {
2609 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2610 Err(e) => map_builder_err(e),
2611 }
2612}
2613#[unsafe(no_mangle)]
2618pub unsafe extern "C" fn rkr_frame_builder_set_atom_spread(
2619 builder_handle: *mut RKRConFrameBuilder,
2620 index: usize,
2621 spread: *const f64,
2622) -> RKRStatus {
2623 if builder_handle.is_null() || spread.is_null() {
2624 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2625 }
2626 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2627 let d = unsafe { [*spread, *spread.add(1), *spread.add(2)] };
2628 match builder.set_atom_spread(index, d) {
2629 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2630 Err(e) => map_builder_err(e),
2631 }
2632}
2633#[unsafe(no_mangle)]
2637pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
2638 builder_handle: *mut RKRConFrameBuilder,
2639 index: usize,
2640 energy: f64,
2641) -> RKRStatus {
2642 if builder_handle.is_null() {
2643 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2644 }
2645 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2646 match builder.set_atom_energy(index, energy) {
2647 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2648 Err(e) => map_builder_err(e),
2649 }
2650}
2651#[unsafe(no_mangle)]
2655pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
2656 builder_handle: *mut RKRConFrameBuilder,
2657 index: usize,
2658 fixed_x: bool,
2659 fixed_y: bool,
2660 fixed_z: bool,
2661) -> RKRStatus {
2662 if builder_handle.is_null() {
2663 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2664 }
2665 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2666 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
2667 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2668 Err(e) => map_builder_err(e),
2669 }
2670}
2671#[unsafe(no_mangle)]
2675pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
2676 builder_handle: *mut RKRConFrameBuilder,
2677 index: usize,
2678 mass: f64,
2679) -> RKRStatus {
2680 if builder_handle.is_null() {
2681 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2682 }
2683 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2684 match builder.set_atom_mass(index, mass) {
2685 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2686 Err(e) => map_builder_err(e),
2687 }
2688}
2689#[unsafe(no_mangle)]
2696pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
2697 builder_handle: *mut RKRConFrameBuilder,
2698 index: usize,
2699 atom_id: u64,
2700) -> RKRStatus {
2701 if builder_handle.is_null() {
2702 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2703 }
2704 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2705 match builder.set_atom_id(index, atom_id) {
2706 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2707 Err(e) => map_builder_err(e),
2708 }
2709}
2710#[unsafe(no_mangle)]
2714pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
2715 builder_handle: *mut RKRConFrameBuilder,
2716 index: usize,
2717) -> RKRStatus {
2718 if builder_handle.is_null() {
2719 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2720 }
2721 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2722 match builder.clear_atom_velocity(index) {
2723 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2724 Err(e) => map_builder_err(e),
2725 }
2726}
2727#[unsafe(no_mangle)]
2730pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
2731 builder_handle: *mut RKRConFrameBuilder,
2732 index: usize,
2733) -> RKRStatus {
2734 if builder_handle.is_null() {
2735 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2736 }
2737 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2738 match builder.clear_atom_force(index) {
2739 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2740 Err(e) => map_builder_err(e),
2741 }
2742}
2743#[unsafe(no_mangle)]
2746pub unsafe extern "C" fn rkr_frame_builder_clear_atom_displacement(
2747 builder_handle: *mut RKRConFrameBuilder,
2748 index: usize,
2749) -> RKRStatus {
2750 if builder_handle.is_null() {
2751 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2752 }
2753 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2754 match builder.clear_atom_displacement(index) {
2755 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2756 Err(e) => map_builder_err(e),
2757 }
2758}
2759#[unsafe(no_mangle)]
2762pub unsafe extern "C" fn rkr_frame_builder_clear_atom_spread(
2763 builder_handle: *mut RKRConFrameBuilder,
2764 index: usize,
2765) -> RKRStatus {
2766 if builder_handle.is_null() {
2767 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2768 }
2769 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2770 match builder.clear_atom_spread(index) {
2771 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2772 Err(e) => map_builder_err(e),
2773 }
2774}
2775#[unsafe(no_mangle)]
2778pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
2779 builder_handle: *mut RKRConFrameBuilder,
2780 index: usize,
2781) -> RKRStatus {
2782 if builder_handle.is_null() {
2783 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2784 }
2785 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2786 match builder.clear_atom_energy(index) {
2787 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2788 Err(e) => map_builder_err(e),
2789 }
2790}
2791#[unsafe(no_mangle)]
2796pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
2797 builder_handle: *mut RKRConFrameBuilder,
2798 positions: *const f64,
2799 len: usize,
2800) -> RKRStatus {
2801 if builder_handle.is_null() || positions.is_null() {
2802 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2803 }
2804 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2805 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
2806 match builder.set_positions_from_flat(slice) {
2807 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2808 Err(e) => map_builder_err(e),
2809 }
2810}
2811#[unsafe(no_mangle)]
2815pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
2816 builder_handle: *mut RKRConFrameBuilder,
2817 forces: *const f64,
2818 len: usize,
2819) -> RKRStatus {
2820 if builder_handle.is_null() || forces.is_null() {
2821 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2822 }
2823 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2824 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
2825 match builder.set_forces_from_flat(slice) {
2826 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2827 Err(e) => map_builder_err(e),
2828 }
2829}
2830#[unsafe(no_mangle)]
2835pub unsafe extern "C" fn rkr_frame_builder_set_displacements_from_flat(
2836 builder_handle: *mut RKRConFrameBuilder,
2837 displacements: *const f64,
2838 len: usize,
2839) -> RKRStatus {
2840 if builder_handle.is_null() || displacements.is_null() {
2841 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2842 }
2843 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2844 let slice = unsafe { std::slice::from_raw_parts(displacements, len) };
2845 match builder.set_displacements_from_flat(slice) {
2846 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2847 Err(e) => map_builder_err(e),
2848 }
2849}
2850#[unsafe(no_mangle)]
2855pub unsafe extern "C" fn rkr_frame_builder_set_spreads_from_flat(
2856 builder_handle: *mut RKRConFrameBuilder,
2857 spreads: *const f64,
2858 len: usize,
2859) -> RKRStatus {
2860 if builder_handle.is_null() || spreads.is_null() {
2861 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2862 }
2863 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2864 let slice = unsafe { std::slice::from_raw_parts(spreads, len) };
2865 match builder.set_spreads_from_flat(slice) {
2866 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2867 Err(e) => map_builder_err(e),
2868 }
2869}
2870#[unsafe(no_mangle)]
2874pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
2875 builder_handle: *mut RKRConFrameBuilder,
2876 energies: *const f64,
2877 len: usize,
2878) -> RKRStatus {
2879 if builder_handle.is_null() || energies.is_null() {
2880 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2881 }
2882 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2883 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
2884 match builder.set_atom_energies_from_flat(slice) {
2885 Ok(_) => note(RKRStatus::RKR_STATUS_SUCCESS),
2886 Err(e) => map_builder_err(e),
2887 }
2888}
2889#[unsafe(no_mangle)]
2893pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
2894 builder_handle: *const RKRConFrameBuilder,
2895 index: usize,
2896 out_xyz: *mut f64,
2897) -> RKRStatus {
2898 if builder_handle.is_null() || out_xyz.is_null() {
2899 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2900 }
2901 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2902 match builder.get_atom_position(index) {
2903 Ok((x, y, z)) => unsafe {
2904 *out_xyz = x;
2905 *out_xyz.add(1) = y;
2906 *out_xyz.add(2) = z;
2907 note(RKRStatus::RKR_STATUS_SUCCESS)
2908 },
2909 Err(e) => map_builder_err(e),
2910 }
2911}
2912#[unsafe(no_mangle)]
2919pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
2920 builder_handle: *const RKRConFrameBuilder,
2921 index: usize,
2922 out_xyz: *mut f64,
2923 has_value: *mut bool,
2924) -> RKRStatus {
2925 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
2926 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2927 }
2928 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2929 match builder.get_atom_velocity(index) {
2930 Ok(Some(v)) => unsafe {
2931 *out_xyz = v[0];
2932 *out_xyz.add(1) = v[1];
2933 *out_xyz.add(2) = v[2];
2934 *has_value = true;
2935 note(RKRStatus::RKR_STATUS_SUCCESS)
2936 },
2937 Ok(None) => unsafe {
2938 *has_value = false;
2939 note(RKRStatus::RKR_STATUS_SUCCESS)
2940 },
2941 Err(e) => map_builder_err(e),
2942 }
2943}
2944#[unsafe(no_mangle)]
2947pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
2948 builder_handle: *const RKRConFrameBuilder,
2949 index: usize,
2950 out_xyz: *mut f64,
2951 has_value: *mut bool,
2952) -> RKRStatus {
2953 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
2954 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2955 }
2956 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2957 match builder.get_atom_force(index) {
2958 Ok(Some(f)) => unsafe {
2959 *out_xyz = f[0];
2960 *out_xyz.add(1) = f[1];
2961 *out_xyz.add(2) = f[2];
2962 *has_value = true;
2963 note(RKRStatus::RKR_STATUS_SUCCESS)
2964 },
2965 Ok(None) => unsafe {
2966 *has_value = false;
2967 note(RKRStatus::RKR_STATUS_SUCCESS)
2968 },
2969 Err(e) => map_builder_err(e),
2970 }
2971}
2972#[unsafe(no_mangle)]
2978pub unsafe extern "C" fn rkr_frame_builder_get_atom_displacement(
2979 builder_handle: *const RKRConFrameBuilder,
2980 index: usize,
2981 out_xyz: *mut f64,
2982 has_value: *mut bool,
2983) -> RKRStatus {
2984 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
2985 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
2986 }
2987 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2988 match builder.get_atom_displacement(index) {
2989 Ok(Some(d)) => unsafe {
2990 *out_xyz = d[0];
2991 *out_xyz.add(1) = d[1];
2992 *out_xyz.add(2) = d[2];
2993 *has_value = true;
2994 note(RKRStatus::RKR_STATUS_SUCCESS)
2995 },
2996 Ok(None) => unsafe {
2997 *has_value = false;
2998 note(RKRStatus::RKR_STATUS_SUCCESS)
2999 },
3000 Err(e) => map_builder_err(e),
3001 }
3002}
3003#[unsafe(no_mangle)]
3009pub unsafe extern "C" fn rkr_frame_builder_get_atom_spread(
3010 builder_handle: *const RKRConFrameBuilder,
3011 index: usize,
3012 out_xyz: *mut f64,
3013 has_value: *mut bool,
3014) -> RKRStatus {
3015 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
3016 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3017 }
3018 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3019 match builder.get_atom_spread(index) {
3020 Ok(Some(d)) => unsafe {
3021 *out_xyz = d[0];
3022 *out_xyz.add(1) = d[1];
3023 *out_xyz.add(2) = d[2];
3024 *has_value = true;
3025 note(RKRStatus::RKR_STATUS_SUCCESS)
3026 },
3027 Ok(None) => unsafe {
3028 *has_value = false;
3029 note(RKRStatus::RKR_STATUS_SUCCESS)
3030 },
3031 Err(e) => map_builder_err(e),
3032 }
3033}
3034#[unsafe(no_mangle)]
3040pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
3041 builder_handle: *const RKRConFrameBuilder,
3042 index: usize,
3043 out_value: *mut f64,
3044 has_value: *mut bool,
3045) -> RKRStatus {
3046 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
3047 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3048 }
3049 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3050 match builder.get_atom_energy(index) {
3051 Ok(Some(e)) => unsafe {
3052 *out_value = e;
3053 *has_value = true;
3054 note(RKRStatus::RKR_STATUS_SUCCESS)
3055 },
3056 Ok(None) => unsafe {
3057 *has_value = false;
3058 note(RKRStatus::RKR_STATUS_SUCCESS)
3059 },
3060 Err(e) => map_builder_err(e),
3061 }
3062}
3063#[unsafe(no_mangle)]
3067pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
3068 builder_handle: *const RKRConFrameBuilder,
3069 index: usize,
3070 out_mass: *mut f64,
3071) -> RKRStatus {
3072 if builder_handle.is_null() || out_mass.is_null() {
3073 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3074 }
3075 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3076 match builder.get_atom_mass(index) {
3077 Ok(m) => unsafe {
3078 *out_mass = m;
3079 note(RKRStatus::RKR_STATUS_SUCCESS)
3080 },
3081 Err(e) => map_builder_err(e),
3082 }
3083}
3084pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
3107fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
3108 use crate::error::ParseError;
3109 match e {
3110 ParseError::ValidationError(ref msg) if msg.contains("device mismatch") => {
3111 note(RKRStatus::RKR_STATUS_DEVICE_MISMATCH)
3112 }
3113 ParseError::ValidationError(ref msg)
3114 if msg.contains("no device allocator") || msg.contains("allocator") =>
3115 {
3116 note(RKRStatus::RKR_STATUS_DEVICE_ALLOC_UNSUPPORTED)
3117 }
3118 ParseError::ValidationError(_) => note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
3119 _ => note(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
3120 }
3121}
3122pub mod rkr_dl_type_code {
3126 pub const RKR_DL_INT: u8 = 0;
3127 pub const RKR_DL_UINT: u8 = 1;
3128 pub const RKR_DL_FLOAT: u8 = 2;
3129 pub const RKR_DL_OPAQUE_HANDLE: u8 = 3;
3130 pub const RKR_DL_BFLOAT: u8 = 4;
3131 pub const RKR_DL_COMPLEX: u8 = 5;
3132 pub const RKR_DL_BOOL: u8 = 6;
3133}
3134
3135pub mod rkr_dl_device_type {
3137 pub const RKR_DL_CPU: i32 = 1;
3138 pub const RKR_DL_CUDA: i32 = 2;
3139 pub const RKR_DL_CUDA_HOST: i32 = 3;
3140}
3141
3142#[repr(C)]
3146#[derive(Clone, Copy, Debug)]
3147pub struct RKRDLDataType {
3148 pub code: u8,
3150 pub bits: u8,
3152 pub lanes: u16,
3154}
3155
3156#[repr(C)]
3159#[derive(Clone, Copy, Debug)]
3160pub struct RKRDLDevice {
3161 pub device_type: i32,
3163 pub device_id: i32,
3164}
3165
3166#[repr(C)]
3180#[derive(Clone, Copy, Debug)]
3181pub struct RKRDlpackExportOptions {
3182 pub dtype: RKRDLDataType,
3184 pub device: RKRDLDevice,
3186}
3187
3188impl Default for RKRDlpackExportOptions {
3189 fn default() -> Self {
3190 Self {
3191 dtype: RKRDLDataType {
3192 code: rkr_dl_type_code::RKR_DL_FLOAT,
3193 bits: 64,
3194 lanes: 1,
3195 },
3196 device: RKRDLDevice {
3197 device_type: rkr_dl_device_type::RKR_DL_CPU,
3198 device_id: 0,
3199 },
3200 }
3201 }
3202}
3203
3204fn resolve_dlpack_opts(
3207 opts: *const RKRDlpackExportOptions,
3208) -> Result<RKRDlpackExportOptions, RKRStatus> {
3209 let o = if opts.is_null() {
3210 RKRDlpackExportOptions::default()
3211 } else {
3212 unsafe { *opts }
3213 };
3214 let dt = o.device.device_type;
3215 if dt == rkr_dl_device_type::RKR_DL_CPU {
3216 } else if dt == rkr_dl_device_type::RKR_DL_CUDA {
3218 #[cfg(not(feature = "cuda"))]
3219 {
3220 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
3221 }
3222 #[cfg(feature = "cuda")]
3223 {
3224 }
3226 } else {
3227 return Err(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
3228 }
3229 if o.dtype.lanes != 1 {
3230 return Err(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
3231 }
3232 Ok(o)
3233}
3234
3235fn finish_dlpack_tensor<E: std::fmt::Display>(
3236 result: Result<dlpk::DLPackTensor, E>,
3237 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3238) -> RKRStatus {
3239 match result {
3240 Ok(tensor) => {
3241 let raw = tensor.into_raw();
3242 unsafe {
3243 *out_tensor = raw.as_ptr();
3244 }
3245 note(RKRStatus::RKR_STATUS_SUCCESS)
3246 }
3247 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
3248 "DLPack export failed: {e}"
3249 ))),
3250 }
3251}
3252
3253fn export_f64_slice_as_dlpack(
3256 data: &[f64],
3257 shape: &[usize],
3258 dtype: RKRDLDataType,
3259 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3260) -> RKRStatus {
3261 use rkr_dl_type_code::*;
3262 if dtype.lanes != 1 {
3263 return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
3264 }
3265 macro_rules! arc_export {
3266 ($ty:ty, $map:expr) => {{
3267 let v: Vec<$ty> = data.iter().map($map).collect();
3268 match *shape {
3269 [r, c] => match ndarray::ArcArray2::from_shape_vec((r, c), v) {
3270 Ok(a) => dlpk::DLPackTensor::try_from(a),
3271 Err(_) => return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
3272 },
3273 [_] => dlpk::DLPackTensor::try_from(ndarray::ArcArray1::from_vec(v)),
3274 _ => return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
3275 }
3276 }};
3277 }
3278 let tensor = match (dtype.code, dtype.bits) {
3279 (RKR_DL_FLOAT, 64) => arc_export!(f64, |&x| x),
3280 (RKR_DL_FLOAT, 32) => arc_export!(f32, |&x| x as f32),
3281 (RKR_DL_INT, 8) => arc_export!(i8, |&x| x as i8),
3282 (RKR_DL_INT, 16) => arc_export!(i16, |&x| x as i16),
3283 (RKR_DL_INT, 32) => arc_export!(i32, |&x| x as i32),
3284 (RKR_DL_INT, 64) => arc_export!(i64, |&x| x as i64),
3285 (RKR_DL_UINT, 8) => arc_export!(u8, |&x| x as u8),
3286 (RKR_DL_UINT, 16) => arc_export!(u16, |&x| x as u16),
3287 (RKR_DL_UINT, 32) => arc_export!(u32, |&x| x as u32),
3288 (RKR_DL_UINT, 64) => arc_export!(u64, |&x| x as u64),
3289 (RKR_DL_BOOL, 8) => {
3290 let v: Vec<bool> = data.iter().map(|&x| x != 0.0).collect();
3292 return finish_dlpack_tensor(dlpk::DLPackTensor::try_from(v), out_tensor);
3293 }
3294 _ => return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
3296 };
3297 finish_dlpack_tensor(tensor, out_tensor)
3298}
3299
3300fn export_owned_array2_dlpack_opts(
3301 arr: &ndarray::ArcArray2<f64>,
3302 opts: &RKRDlpackExportOptions,
3303 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3304) -> RKRStatus {
3305 let (r, c) = arr.dim();
3306 let flat: Vec<f64> = arr.iter().copied().collect();
3307 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
3308 #[cfg(feature = "cuda")]
3309 {
3310 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
3312 return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
3313 }
3314 return finish_dlpack_tensor(
3315 crate::cuda_array::export_host_f64_as_cuda_dlpack(
3316 &[r, c],
3317 &flat,
3318 opts.device.device_id,
3319 ),
3320 out_tensor,
3321 );
3322 }
3323 #[cfg(not(feature = "cuda"))]
3324 {
3325 return note(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
3326 }
3327 }
3328 export_f64_slice_as_dlpack(&flat, &[r, c], opts.dtype, out_tensor)
3329}
3330
3331fn export_owned_array1_f64_dlpack_opts(
3332 arr: &ndarray::ArcArray1<f64>,
3333 opts: &RKRDlpackExportOptions,
3334 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3335) -> RKRStatus {
3336 let flat = arr.to_vec();
3337 let n = flat.len();
3338 if opts.device.device_type == rkr_dl_device_type::RKR_DL_CUDA {
3339 #[cfg(feature = "cuda")]
3340 {
3341 if opts.dtype.code != rkr_dl_type_code::RKR_DL_FLOAT || opts.dtype.bits != 64 {
3342 return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
3343 }
3344 return finish_dlpack_tensor(
3345 crate::cuda_array::export_host_f64_as_cuda_dlpack(
3346 &[n],
3347 &flat,
3348 opts.device.device_id,
3349 ),
3350 out_tensor,
3351 );
3352 }
3353 #[cfg(not(feature = "cuda"))]
3354 {
3355 return note(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
3356 }
3357 }
3358 export_f64_slice_as_dlpack(&flat, &[n], opts.dtype, out_tensor)
3359}
3360
3361fn export_owned_array1_u64_dlpack(
3362 arr: &ndarray::ArcArray1<u64>,
3363 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3364) -> RKRStatus {
3365 finish_dlpack_tensor(dlpk::DLPackTensor::try_from(arr.clone()), out_tensor)
3366}
3367
3368fn export_owned_array1_u64_dlpack_opts(
3370 arr: &ndarray::ArcArray1<u64>,
3371 opts: &RKRDlpackExportOptions,
3372 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3373) -> RKRStatus {
3374 use rkr_dl_type_code::*;
3375 if opts.dtype.code == RKR_DL_UINT && opts.dtype.bits == 64 && opts.dtype.lanes == 1 {
3376 return export_owned_array1_u64_dlpack(arr, out_tensor);
3377 }
3378 let as_f64: Vec<f64> = arr.iter().map(|&x| x as f64).collect();
3379 let n = as_f64.len();
3380 export_f64_slice_as_dlpack(&as_f64, &[n], opts.dtype, out_tensor)
3381}
3382#[unsafe(no_mangle)]
3393pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
3394 builder_handle: *const RKRConFrameBuilder,
3395 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3396) -> RKRStatus {
3397 unsafe { rkr_frame_builder_positions_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
3398}
3399
3400#[unsafe(no_mangle)]
3407pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack_ex(
3408 builder_handle: *const RKRConFrameBuilder,
3409 opts: *const RKRDlpackExportOptions,
3410 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3411) -> RKRStatus {
3412 if builder_handle.is_null() || out_tensor.is_null() {
3413 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3414 }
3415 let o = match resolve_dlpack_opts(opts) {
3416 Ok(o) => o,
3417 Err(st) => return st,
3418 };
3419 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3420 export_owned_array2_dlpack_opts(builder.positions_2d_ref(), &o, out_tensor)
3421}
3422
3423#[unsafe(no_mangle)]
3433pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
3434 builder_handle: *const RKRConFrameBuilder,
3435 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3436) -> RKRStatus {
3437 unsafe { rkr_frame_builder_velocities_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
3438}
3439
3440#[unsafe(no_mangle)]
3445pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack_ex(
3446 builder_handle: *const RKRConFrameBuilder,
3447 opts: *const RKRDlpackExportOptions,
3448 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3449) -> RKRStatus {
3450 if builder_handle.is_null() || out_tensor.is_null() {
3451 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3452 }
3453 let o = match resolve_dlpack_opts(opts) {
3454 Ok(o) => o,
3455 Err(st) => return st,
3456 };
3457 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3458 if !builder.has_velocities_section() {
3459 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
3460 }
3461 export_owned_array2_dlpack_opts(builder.velocities_2d_ref(), &o, out_tensor)
3462}
3463
3464#[unsafe(no_mangle)]
3473pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
3474 builder_handle: *const RKRConFrameBuilder,
3475 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3476) -> RKRStatus {
3477 unsafe { rkr_frame_builder_forces_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
3478}
3479
3480#[unsafe(no_mangle)]
3485pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack_ex(
3486 builder_handle: *const RKRConFrameBuilder,
3487 opts: *const RKRDlpackExportOptions,
3488 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3489) -> RKRStatus {
3490 if builder_handle.is_null() || out_tensor.is_null() {
3491 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3492 }
3493 let o = match resolve_dlpack_opts(opts) {
3494 Ok(o) => o,
3495 Err(st) => return st,
3496 };
3497 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3498 if !builder.has_forces_section() {
3499 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
3500 }
3501 export_owned_array2_dlpack_opts(builder.forces_2d_ref(), &o, out_tensor)
3502}
3503
3504#[unsafe(no_mangle)]
3513pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack(
3514 builder_handle: *const RKRConFrameBuilder,
3515 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3516) -> RKRStatus {
3517 unsafe {
3518 rkr_frame_builder_displacements_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
3519 }
3520}
3521
3522#[unsafe(no_mangle)]
3529pub unsafe extern "C" fn rkr_frame_builder_displacements_dlpack_ex(
3530 builder_handle: *const RKRConFrameBuilder,
3531 opts: *const RKRDlpackExportOptions,
3532 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3533) -> RKRStatus {
3534 if builder_handle.is_null() || out_tensor.is_null() {
3535 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3536 }
3537 let o = match resolve_dlpack_opts(opts) {
3538 Ok(o) => o,
3539 Err(st) => return st,
3540 };
3541 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3542 if !builder.has_displacements_section() {
3543 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
3544 }
3545 export_owned_array2_dlpack_opts(builder.displacements_2d_ref(), &o, out_tensor)
3546}
3547
3548#[unsafe(no_mangle)]
3557pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack(
3558 builder_handle: *const RKRConFrameBuilder,
3559 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3560) -> RKRStatus {
3561 unsafe { rkr_frame_builder_spreads_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
3562}
3563
3564#[unsafe(no_mangle)]
3571pub unsafe extern "C" fn rkr_frame_builder_spreads_dlpack_ex(
3572 builder_handle: *const RKRConFrameBuilder,
3573 opts: *const RKRDlpackExportOptions,
3574 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3575) -> RKRStatus {
3576 if builder_handle.is_null() || out_tensor.is_null() {
3577 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3578 }
3579 let o = match resolve_dlpack_opts(opts) {
3580 Ok(o) => o,
3581 Err(st) => return st,
3582 };
3583 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3584 if !builder.has_spreads_section() {
3585 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
3586 }
3587 export_owned_array2_dlpack_opts(builder.spreads_2d_ref(), &o, out_tensor)
3588}
3589
3590#[unsafe(no_mangle)]
3599pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
3600 builder_handle: *const RKRConFrameBuilder,
3601 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3602) -> RKRStatus {
3603 unsafe {
3604 rkr_frame_builder_atom_energies_dlpack_ex(builder_handle, std::ptr::null(), out_tensor)
3605 }
3606}
3607
3608#[unsafe(no_mangle)]
3613pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack_ex(
3614 builder_handle: *const RKRConFrameBuilder,
3615 opts: *const RKRDlpackExportOptions,
3616 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3617) -> RKRStatus {
3618 if builder_handle.is_null() || out_tensor.is_null() {
3619 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3620 }
3621 let o = match resolve_dlpack_opts(opts) {
3622 Ok(o) => o,
3623 Err(st) => return st,
3624 };
3625 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3626 if !builder.has_energies_section() {
3627 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
3628 }
3629 export_owned_array1_f64_dlpack_opts(builder.atom_energies_1d_ref(), &o, out_tensor)
3630}
3631
3632#[unsafe(no_mangle)]
3638pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
3639 builder_handle: *const RKRConFrameBuilder,
3640 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3641) -> RKRStatus {
3642 unsafe { rkr_frame_builder_masses_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
3643}
3644
3645#[unsafe(no_mangle)]
3650pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack_ex(
3651 builder_handle: *const RKRConFrameBuilder,
3652 opts: *const RKRDlpackExportOptions,
3653 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3654) -> RKRStatus {
3655 if builder_handle.is_null() || out_tensor.is_null() {
3656 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3657 }
3658 let o = match resolve_dlpack_opts(opts) {
3659 Ok(o) => o,
3660 Err(st) => return st,
3661 };
3662 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3663 export_owned_array1_f64_dlpack_opts(builder.masses_1d_ref(), &o, out_tensor)
3664}
3665#[unsafe(no_mangle)]
3671pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
3672 builder_handle: *const RKRConFrameBuilder,
3673 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3674) -> RKRStatus {
3675 unsafe { rkr_frame_builder_atom_ids_dlpack_ex(builder_handle, std::ptr::null(), out_tensor) }
3676}
3677
3678#[unsafe(no_mangle)]
3683pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack_ex(
3684 builder_handle: *const RKRConFrameBuilder,
3685 opts: *const RKRDlpackExportOptions,
3686 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
3687) -> RKRStatus {
3688 if builder_handle.is_null() || out_tensor.is_null() {
3689 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
3690 }
3691 let o = match resolve_dlpack_opts(opts) {
3692 Ok(o) => o,
3693 Err(st) => return st,
3694 };
3695 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3696 export_owned_array1_u64_dlpack_opts(builder.atom_ids_1d_ref(), &o, out_tensor)
3697}
3698#[unsafe(no_mangle)]
3722pub unsafe extern "C" fn rkr_frame_builder_positions_data(
3723 builder_handle: *mut RKRConFrameBuilder,
3724) -> *mut f64 {
3725 if builder_handle.is_null() {
3726 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3727 }
3728 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3729 builder
3730 .positions_view_mut()
3731 .as_slice_memory_order_mut()
3732 .map(|s| s.as_mut_ptr())
3733 .unwrap_or(std::ptr::null_mut())
3734}
3735#[unsafe(no_mangle)]
3742pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
3743 builder_handle: *mut RKRConFrameBuilder,
3744) -> *mut f64 {
3745 if builder_handle.is_null() {
3746 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3747 }
3748 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3749 if !builder.has_velocities_section() {
3750 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3751 }
3752 let slice = builder.velocities_mut();
3753 if slice.is_empty() {
3754 std::ptr::null_mut()
3755 } else {
3756 slice.as_mut_ptr()
3757 }
3758}
3759#[unsafe(no_mangle)]
3765pub unsafe extern "C" fn rkr_frame_builder_forces_data(
3766 builder_handle: *mut RKRConFrameBuilder,
3767) -> *mut f64 {
3768 if builder_handle.is_null() {
3769 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3770 }
3771 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3772 if !builder.has_forces_section() {
3773 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3774 }
3775 let slice = builder.forces_mut();
3776 if slice.is_empty() {
3777 std::ptr::null_mut()
3778 } else {
3779 slice.as_mut_ptr()
3780 }
3781}
3782#[unsafe(no_mangle)]
3788pub unsafe extern "C" fn rkr_frame_builder_displacements_data(
3789 builder_handle: *mut RKRConFrameBuilder,
3790) -> *mut f64 {
3791 if builder_handle.is_null() {
3792 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3793 }
3794 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3795 if !builder.has_displacements_section() {
3796 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3797 }
3798 let slice = builder.displacements_mut();
3799 if slice.is_empty() {
3800 std::ptr::null_mut()
3801 } else {
3802 slice.as_mut_ptr()
3803 }
3804}
3805#[unsafe(no_mangle)]
3811pub unsafe extern "C" fn rkr_frame_builder_spreads_data(
3812 builder_handle: *mut RKRConFrameBuilder,
3813) -> *mut f64 {
3814 if builder_handle.is_null() {
3815 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3816 }
3817 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3818 if !builder.has_spreads_section() {
3819 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3820 }
3821 let slice = builder.spreads_mut();
3822 if slice.is_empty() {
3823 std::ptr::null_mut()
3824 } else {
3825 slice.as_mut_ptr()
3826 }
3827}
3828#[unsafe(no_mangle)]
3834pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
3835 builder_handle: *mut RKRConFrameBuilder,
3836) -> *mut f64 {
3837 if builder_handle.is_null() {
3838 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3839 }
3840 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3841 if !builder.has_energies_section() {
3842 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3843 }
3844 let slice = builder.atom_energies_mut();
3845 if slice.is_empty() {
3846 std::ptr::null_mut()
3847 } else {
3848 slice.as_mut_ptr()
3849 }
3850}
3851#[unsafe(no_mangle)]
3856pub unsafe extern "C" fn rkr_frame_builder_masses_data(
3857 builder_handle: *mut RKRConFrameBuilder,
3858) -> *mut f64 {
3859 if builder_handle.is_null() {
3860 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3861 }
3862 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
3863 let slice = builder.masses_mut();
3864 if slice.is_empty() {
3865 std::ptr::null_mut()
3866 } else {
3867 slice.as_mut_ptr()
3868 }
3869}
3870#[unsafe(no_mangle)]
3875pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
3876 builder_handle: *const RKRConFrameBuilder,
3877) -> *const u64 {
3878 if builder_handle.is_null() {
3879 return std::ptr::null();
3880 }
3881 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
3882 let slice = builder.atom_ids();
3883 if slice.is_empty() {
3884 std::ptr::null()
3885 } else {
3886 slice.as_ptr()
3887 }
3888}
3889#[unsafe(no_mangle)]
3901#[allow(clippy::too_many_arguments)]
3902pub unsafe extern "C" fn rkr_frame_add_atom_full(
3903 builder_handle: *mut RKRConFrameBuilder,
3904 symbol: *const c_char,
3905 x: f64,
3906 y: f64,
3907 z: f64,
3908 fixed_x: bool,
3909 fixed_y: bool,
3910 fixed_z: bool,
3911 atom_id: u64,
3912 mass: f64,
3913 velocity: *const f64,
3914 force: *const f64,
3915) -> RKRStatus {
3916 let velocity = if velocity.is_null() {
3917 None
3918 } else {
3919 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
3920 };
3921 let force = if force.is_null() {
3922 None
3923 } else {
3924 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
3925 };
3926 unsafe {
3927 add_builder_atom(
3928 builder_handle,
3929 symbol,
3930 x,
3931 y,
3932 z,
3933 [fixed_x, fixed_y, fixed_z],
3934 atom_id,
3935 mass,
3936 velocity,
3937 force,
3938 )
3939 }
3940}
3941#[unsafe(no_mangle)]
3957pub unsafe extern "C" fn rkr_frame_new(
3958 cell: *const f64,
3959 angles: *const f64,
3960 prebox0: *const c_char,
3961 prebox1: *const c_char,
3962 postbox0: *const c_char,
3963 postbox1: *const c_char,
3964) -> *mut RKRConFrameBuilder {
3965 if cell.is_null() || angles.is_null() {
3966 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
3967 }
3968 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
3969 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
3970 let get_str = |p: *const c_char| -> String {
3971 if p.is_null() {
3972 String::new()
3973 } else {
3974 unsafe { CStr::from_ptr(p) }
3975 .to_str()
3976 .unwrap_or("")
3977 .to_string()
3978 }
3979 };
3980 let _ = get_str(prebox1);
3983 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
3984 builder
3985 .prebox_header(get_str(prebox0))
3986 .postbox_header([get_str(postbox0), get_str(postbox1)]);
3987 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
3988}
3989#[unsafe(no_mangle)]
3995pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
3996 builder_handle: *mut RKRConFrameBuilder,
3997 metadata_json: *const c_char,
3998) -> RKRStatus {
3999 if builder_handle.is_null() || metadata_json.is_null() {
4000 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4001 }
4002 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4003 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
4004 Ok(s) => s,
4005 Err(_) => return note(RKRStatus::RKR_STATUS_INVALID_UTF8),
4006 };
4007 match builder.set_metadata_json(metadata_json) {
4008 Ok(()) => note(RKRStatus::RKR_STATUS_SUCCESS),
4009 Err(_) => note(RKRStatus::RKR_STATUS_INVALID_JSON),
4010 }
4011}
4012#[unsafe(no_mangle)]
4022pub unsafe extern "C" fn rkr_frame_builder_set_units(
4023 builder_handle: *mut RKRConFrameBuilder,
4024 units_json: *const c_char,
4025) -> RKRStatus {
4026 if builder_handle.is_null() || units_json.is_null() {
4027 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4028 }
4029 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4030 let units_json = match unsafe { CStr::from_ptr(units_json).to_str() } {
4031 Ok(s) => s,
4032 Err(_) => return note(RKRStatus::RKR_STATUS_INVALID_UTF8),
4033 };
4034 let units: serde_json::Value = match serde_json::from_str(units_json) {
4035 Ok(value) => value,
4036 Err(_) => return note(RKRStatus::RKR_STATUS_INVALID_JSON),
4037 };
4038 match builder.set_units(units) {
4039 Ok(()) => note(RKRStatus::RKR_STATUS_SUCCESS),
4040 Err(_) => note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
4041 }
4042}
4043#[unsafe(no_mangle)]
4050pub unsafe extern "C" fn rkr_frame_builder_set_con_spec_minor(
4051 builder_handle: *mut RKRConFrameBuilder,
4052 minor: u32,
4053) -> RKRStatus {
4054 if builder_handle.is_null() {
4055 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4056 }
4057 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4058 builder.set_con_spec_minor(minor);
4059 note(RKRStatus::RKR_STATUS_SUCCESS)
4060}
4061#[unsafe(no_mangle)]
4067pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
4068 builder_handle: *mut RKRConFrameBuilder,
4069 key: *const c_char,
4070 value: f64,
4071) -> RKRStatus {
4072 if builder_handle.is_null() || key.is_null() {
4073 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4074 }
4075 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4076 let key = match unsafe { CStr::from_ptr(key).to_str() } {
4077 Ok(s) => s,
4078 Err(_) => return note(RKRStatus::RKR_STATUS_INVALID_UTF8),
4079 };
4080 builder.set_scalar_metadata(key, value);
4081 note(RKRStatus::RKR_STATUS_SUCCESS)
4082}
4083#[unsafe(no_mangle)]
4089pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
4090 builder_handle: *mut RKRConFrameBuilder,
4091 key: *const c_char,
4092 value: *const c_char,
4093) -> RKRStatus {
4094 if builder_handle.is_null() || key.is_null() || value.is_null() {
4095 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4096 }
4097 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4098 let key = match unsafe { CStr::from_ptr(key).to_str() } {
4099 Ok(s) => s,
4100 Err(_) => return note(RKRStatus::RKR_STATUS_INVALID_UTF8),
4101 };
4102 let value = match unsafe { CStr::from_ptr(value).to_str() } {
4103 Ok(s) => s,
4104 Err(_) => return note(RKRStatus::RKR_STATUS_INVALID_UTF8),
4105 };
4106 builder.set_string_metadata(key, value);
4107 note(RKRStatus::RKR_STATUS_SUCCESS)
4108}
4109#[unsafe(no_mangle)]
4115pub unsafe extern "C" fn rkr_frame_builder_set_energy(
4116 builder_handle: *mut RKRConFrameBuilder,
4117 energy: f64,
4118) -> RKRStatus {
4119 if builder_handle.is_null() {
4120 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4121 }
4122 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4123 builder.set_energy(energy);
4124 note(RKRStatus::RKR_STATUS_SUCCESS)
4125}
4126#[unsafe(no_mangle)]
4132pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
4133 builder_handle: *mut RKRConFrameBuilder,
4134 idx: u64,
4135) -> RKRStatus {
4136 if builder_handle.is_null() {
4137 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4138 }
4139 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4140 builder.set_frame_index(idx);
4141 note(RKRStatus::RKR_STATUS_SUCCESS)
4142}
4143#[unsafe(no_mangle)]
4149pub unsafe extern "C" fn rkr_frame_builder_set_time(
4150 builder_handle: *mut RKRConFrameBuilder,
4151 time: f64,
4152) -> RKRStatus {
4153 if builder_handle.is_null() {
4154 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4155 }
4156 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4157 builder.set_time(time);
4158 note(RKRStatus::RKR_STATUS_SUCCESS)
4159}
4160#[unsafe(no_mangle)]
4166pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
4167 builder_handle: *mut RKRConFrameBuilder,
4168 dt: f64,
4169) -> RKRStatus {
4170 if builder_handle.is_null() {
4171 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4172 }
4173 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4174 builder.set_timestep(dt);
4175 note(RKRStatus::RKR_STATUS_SUCCESS)
4176}
4177#[unsafe(no_mangle)]
4183pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
4184 builder_handle: *mut RKRConFrameBuilder,
4185 bead: u64,
4186) -> RKRStatus {
4187 if builder_handle.is_null() {
4188 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4189 }
4190 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4191 builder.set_neb_bead(bead);
4192 note(RKRStatus::RKR_STATUS_SUCCESS)
4193}
4194#[unsafe(no_mangle)]
4200pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
4201 builder_handle: *mut RKRConFrameBuilder,
4202 band: u64,
4203) -> RKRStatus {
4204 if builder_handle.is_null() {
4205 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4206 }
4207 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
4208 builder.set_neb_band(band);
4209 note(RKRStatus::RKR_STATUS_SUCCESS)
4210}
4211#[unsafe(no_mangle)]
4228pub unsafe extern "C" fn rkr_frame_add_atom(
4229 builder_handle: *mut RKRConFrameBuilder,
4230 symbol: *const c_char,
4231 x: f64,
4232 y: f64,
4233 z: f64,
4234 is_fixed: bool,
4235 atom_id: u64,
4236 mass: f64,
4237) -> RKRStatus {
4238 unsafe {
4239 add_builder_atom(
4240 builder_handle,
4241 symbol,
4242 x,
4243 y,
4244 z,
4245 [is_fixed; 3],
4246 atom_id,
4247 mass,
4248 None,
4249 None,
4250 )
4251 }
4252}
4253#[unsafe(no_mangle)]
4260pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
4261 builder_handle: *mut RKRConFrameBuilder,
4262 symbol: *const c_char,
4263 x: f64,
4264 y: f64,
4265 z: f64,
4266 fixed_x: bool,
4267 fixed_y: bool,
4268 fixed_z: bool,
4269 atom_id: u64,
4270 mass: f64,
4271) -> RKRStatus {
4272 unsafe {
4273 add_builder_atom(
4274 builder_handle,
4275 symbol,
4276 x,
4277 y,
4278 z,
4279 [fixed_x, fixed_y, fixed_z],
4280 atom_id,
4281 mass,
4282 None,
4283 None,
4284 )
4285 }
4286}
4287#[unsafe(no_mangle)]
4294pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
4295 builder_handle: *mut RKRConFrameBuilder,
4296 symbol: *const c_char,
4297 x: f64,
4298 y: f64,
4299 z: f64,
4300 is_fixed: bool,
4301 atom_id: u64,
4302 mass: f64,
4303 vx: f64,
4304 vy: f64,
4305 vz: f64,
4306) -> RKRStatus {
4307 unsafe {
4308 add_builder_atom(
4309 builder_handle,
4310 symbol,
4311 x,
4312 y,
4313 z,
4314 [is_fixed; 3],
4315 atom_id,
4316 mass,
4317 Some([vx, vy, vz]),
4318 None,
4319 )
4320 }
4321}
4322#[unsafe(no_mangle)]
4329pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
4330 builder_handle: *mut RKRConFrameBuilder,
4331 symbol: *const c_char,
4332 x: f64,
4333 y: f64,
4334 z: f64,
4335 fixed_x: bool,
4336 fixed_y: bool,
4337 fixed_z: bool,
4338 atom_id: u64,
4339 mass: f64,
4340 vx: f64,
4341 vy: f64,
4342 vz: f64,
4343) -> RKRStatus {
4344 unsafe {
4345 add_builder_atom(
4346 builder_handle,
4347 symbol,
4348 x,
4349 y,
4350 z,
4351 [fixed_x, fixed_y, fixed_z],
4352 atom_id,
4353 mass,
4354 Some([vx, vy, vz]),
4355 None,
4356 )
4357 }
4358}
4359#[unsafe(no_mangle)]
4366pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
4367 builder_handle: *mut RKRConFrameBuilder,
4368 symbol: *const c_char,
4369 x: f64,
4370 y: f64,
4371 z: f64,
4372 is_fixed: bool,
4373 atom_id: u64,
4374 mass: f64,
4375 fx: f64,
4376 fy: f64,
4377 fz: f64,
4378) -> RKRStatus {
4379 unsafe {
4380 add_builder_atom(
4381 builder_handle,
4382 symbol,
4383 x,
4384 y,
4385 z,
4386 [is_fixed; 3],
4387 atom_id,
4388 mass,
4389 None,
4390 Some([fx, fy, fz]),
4391 )
4392 }
4393}
4394#[unsafe(no_mangle)]
4401pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
4402 builder_handle: *mut RKRConFrameBuilder,
4403 symbol: *const c_char,
4404 x: f64,
4405 y: f64,
4406 z: f64,
4407 fixed_x: bool,
4408 fixed_y: bool,
4409 fixed_z: bool,
4410 atom_id: u64,
4411 mass: f64,
4412 fx: f64,
4413 fy: f64,
4414 fz: f64,
4415) -> RKRStatus {
4416 unsafe {
4417 add_builder_atom(
4418 builder_handle,
4419 symbol,
4420 x,
4421 y,
4422 z,
4423 [fixed_x, fixed_y, fixed_z],
4424 atom_id,
4425 mass,
4426 None,
4427 Some([fx, fy, fz]),
4428 )
4429 }
4430}
4431#[unsafe(no_mangle)]
4438pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
4439 builder_handle: *mut RKRConFrameBuilder,
4440 symbol: *const c_char,
4441 x: f64,
4442 y: f64,
4443 z: f64,
4444 is_fixed: bool,
4445 atom_id: u64,
4446 mass: f64,
4447 vx: f64,
4448 vy: f64,
4449 vz: f64,
4450 fx: f64,
4451 fy: f64,
4452 fz: f64,
4453) -> RKRStatus {
4454 unsafe {
4455 add_builder_atom(
4456 builder_handle,
4457 symbol,
4458 x,
4459 y,
4460 z,
4461 [is_fixed; 3],
4462 atom_id,
4463 mass,
4464 Some([vx, vy, vz]),
4465 Some([fx, fy, fz]),
4466 )
4467 }
4468}
4469#[unsafe(no_mangle)]
4476pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
4477 builder_handle: *mut RKRConFrameBuilder,
4478 symbol: *const c_char,
4479 x: f64,
4480 y: f64,
4481 z: f64,
4482 fixed_x: bool,
4483 fixed_y: bool,
4484 fixed_z: bool,
4485 atom_id: u64,
4486 mass: f64,
4487 vx: f64,
4488 vy: f64,
4489 vz: f64,
4490 fx: f64,
4491 fy: f64,
4492 fz: f64,
4493) -> RKRStatus {
4494 unsafe {
4495 add_builder_atom(
4496 builder_handle,
4497 symbol,
4498 x,
4499 y,
4500 z,
4501 [fixed_x, fixed_y, fixed_z],
4502 atom_id,
4503 mass,
4504 Some([vx, vy, vz]),
4505 Some([fx, fy, fz]),
4506 )
4507 }
4508}
4509#[unsafe(no_mangle)]
4518pub unsafe extern "C" fn rkr_frame_builder_build(
4519 builder_handle: *mut RKRConFrameBuilder,
4520) -> *mut RKRConFrame {
4521 if builder_handle.is_null() {
4522 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
4523 }
4524 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
4525 match builder.build() {
4526 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4527 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER),
4528 }
4529}
4530#[unsafe(no_mangle)]
4535pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
4536 if !builder_handle.is_null() {
4537 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
4538 }
4539}
4540#[unsafe(no_mangle)]
4557pub unsafe extern "C" fn rkr_frame_builder_clone(
4558 builder_handle: *const RKRConFrameBuilder,
4559) -> *mut RKRConFrameBuilder {
4560 if builder_handle.is_null() {
4561 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
4562 }
4563 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
4564 let cloned = builder.clone();
4565 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
4566}
4567#[unsafe(no_mangle)]
4573pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
4574 let filename = match unsafe { cstr_path(filename_c) } {
4575 Some(s) => s,
4576 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4577 };
4578 match crate::compression::gzip_writer(Path::new(filename)) {
4579 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
4580 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4581 }
4582}
4583#[unsafe(no_mangle)]
4590pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
4591 filename_c: *const c_char,
4592 precision: u8,
4593) -> *mut RKRConFrameWriter {
4594 let filename = match unsafe { cstr_path(filename_c) } {
4595 Some(s) => s,
4596 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4597 };
4598 match crate::compression::gzip_writer(Path::new(filename)) {
4599 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
4600 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4601 }
4602}
4603#[cfg(feature = "zstd")]
4613#[unsafe(no_mangle)]
4614pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
4615 let filename = match unsafe { cstr_path(filename_c) } {
4616 Some(s) => s,
4617 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4618 };
4619 match crate::compression::zstd_writer(Path::new(filename)) {
4620 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
4621 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4622 }
4623}
4624#[cfg(feature = "zstd")]
4635#[unsafe(no_mangle)]
4636pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
4637 filename_c: *const c_char,
4638 precision: u8,
4639) -> *mut RKRConFrameWriter {
4640 let filename = match unsafe { cstr_path(filename_c) } {
4641 Some(s) => s,
4642 None => return null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4643 };
4644 match crate::compression::zstd_writer(Path::new(filename)) {
4645 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
4646 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4647 }
4648}
4649#[unsafe(no_mangle)]
4661pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
4662 if filename_c.is_null() {
4663 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
4664 }
4665 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
4666 Ok(s) => s,
4667 Err(e) => {
4668 set_last_error(
4669 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
4670 &e.to_string(),
4671 0,
4672 0,
4673 );
4674 return ptr::null_mut();
4675 }
4676 };
4677 match iterators::read_first_frame_located(Path::new(filename)) {
4678 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
4679 Err(e) => null_load(e),
4680 }
4681}
4682#[unsafe(no_mangle)]
4691pub unsafe extern "C" fn rkr_read_all_frames(
4692 filename_c: *const c_char,
4693 num_frames: *mut usize,
4694) -> *mut *mut RKRConFrame {
4695 if filename_c.is_null() || num_frames.is_null() {
4696 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
4697 }
4698 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
4699 Ok(s) => s,
4700 Err(e) => {
4701 set_last_error(
4702 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
4703 &e.to_string(),
4704 0,
4705 0,
4706 );
4707 return ptr::null_mut();
4708 }
4709 };
4710 match iterators::read_all_frames_located(Path::new(filename), None) {
4711 Ok(frames) => pack_frame_handles(frames, num_frames),
4712 Err(e) => null_load(e),
4713 }
4714}
4715
4716#[unsafe(no_mangle)]
4725pub unsafe extern "C" fn rkr_read_all_frames_n_threads(
4726 filename_c: *const c_char,
4727 num_frames: *mut usize,
4728 n_threads: usize,
4729) -> *mut *mut RKRConFrame {
4730 if filename_c.is_null() || num_frames.is_null() {
4731 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
4732 }
4733 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
4734 Ok(s) => s,
4735 Err(e) => {
4736 set_last_error(
4737 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
4738 &e.to_string(),
4739 0,
4740 0,
4741 );
4742 return ptr::null_mut();
4743 }
4744 };
4745 let threads = if n_threads == 0 {
4746 None
4747 } else {
4748 Some(n_threads)
4749 };
4750 match iterators::read_all_frames_located(Path::new(filename), threads) {
4751 Ok(frames) => pack_frame_handles(frames, num_frames),
4752 Err(e) => null_load(e),
4753 }
4754}
4755
4756pub struct RKRPositionBlock {
4758 _private: [u8; 0],
4759}
4760
4761struct PositionOwned {
4762 values: Vec<f64>,
4763 n_frames: usize,
4764 n_atoms: usize,
4765}
4766
4767#[unsafe(no_mangle)]
4784pub unsafe extern "C" fn rkr_read_positions_n_threads(
4785 filename_c: *const c_char,
4786 n_frames: *mut usize,
4787 n_atoms: *mut usize,
4788 n_threads: usize,
4789) -> *mut RKRPositionBlock {
4790 if filename_c.is_null() || n_frames.is_null() || n_atoms.is_null() {
4791 return null_err::<RKRPositionBlock>(RKRStatus::RKR_STATUS_NULL_POINTER);
4792 }
4793 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
4794 Ok(s) => s,
4795 Err(e) => {
4796 set_last_error(
4797 RKRStatus::RKR_STATUS_INVALID_UTF8 as i32,
4798 &e.to_string(),
4799 0,
4800 0,
4801 );
4802 return ptr::null_mut();
4803 }
4804 };
4805 let threads = if n_threads == 0 {
4806 None
4807 } else {
4808 Some(n_threads)
4809 };
4810 match iterators::read_positions_with_threads(Path::new(filename), threads) {
4811 Ok(block) => {
4812 unsafe {
4813 *n_frames = block.n_frames;
4814 *n_atoms = block.n_atoms;
4815 }
4816 Box::into_raw(Box::new(PositionOwned {
4817 n_frames: block.n_frames,
4818 n_atoms: block.n_atoms,
4819 values: block.values,
4820 })) as *mut RKRPositionBlock
4821 }
4822 Err(err) => {
4823 note_boxed(err);
4824 ptr::null_mut()
4825 }
4826 }
4827}
4828
4829#[unsafe(no_mangle)]
4834pub unsafe extern "C" fn rkr_read_positions(
4835 filename_c: *const c_char,
4836 n_frames: *mut usize,
4837 n_atoms: *mut usize,
4838) -> *mut RKRPositionBlock {
4839 unsafe { rkr_read_positions_n_threads(filename_c, n_frames, n_atoms, 1) }
4840}
4841
4842#[unsafe(no_mangle)]
4850pub unsafe extern "C" fn rkr_position_block_values(block: *const RKRPositionBlock) -> *const f64 {
4851 if block.is_null() {
4852 return ptr::null();
4853 }
4854 let owned = unsafe { &*(block as *const PositionOwned) };
4855 debug_assert_eq!(
4856 owned.values.len(),
4857 owned
4858 .n_frames
4859 .saturating_mul(owned.n_atoms)
4860 .saturating_mul(3)
4861 );
4862 owned.values.as_ptr()
4863}
4864
4865#[unsafe(no_mangle)]
4870pub unsafe extern "C" fn rkr_free_position_block(block: *mut RKRPositionBlock) {
4871 if block.is_null() {
4872 return;
4873 }
4874 unsafe {
4875 drop(Box::from_raw(block as *mut PositionOwned));
4876 }
4877}
4878
4879pub(crate) fn pack_frame_handles(
4881 frames: Vec<ConFrame>,
4882 num_frames: *mut usize,
4883) -> *mut *mut RKRConFrame {
4884 let count = frames.len();
4885 let handles: Vec<*mut RKRConFrame> = frames
4886 .into_iter()
4887 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
4888 .collect();
4889 let boxed = handles.into_boxed_slice();
4890 let ptr = Box::into_raw(boxed).cast::<*mut RKRConFrame>();
4891 unsafe { *num_frames = count };
4892 ptr
4893}
4894#[unsafe(no_mangle)]
4900pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
4901 if frames.is_null() {
4902 return;
4903 }
4904 unsafe {
4905 let handles = Box::from_raw(std::ptr::slice_from_raw_parts_mut(frames, num_frames));
4906 for handle in handles.iter().copied() {
4907 if !handle.is_null() {
4908 let _ = Box::from_raw(handle as *mut ConFrame);
4909 }
4910 }
4911 }
4912}
4913#[unsafe(no_mangle)]
4919pub unsafe extern "C" fn free_rkr_frame_ptr_array(
4920 frames: *mut *mut RKRConFrame,
4921 num_frames: usize,
4922) {
4923 if frames.is_null() {
4924 return;
4925 }
4926 unsafe {
4927 drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(
4928 frames, num_frames,
4929 )));
4930 }
4931}
4932
4933#[cfg(feature = "metatensor")]
4948#[unsafe(no_mangle)]
4949pub unsafe extern "C" fn rkr_mts_block_free(block: *mut metatensor::c_api::mts_block_t) {
4950 unsafe { crate::metatensor_export::mts_block_free_sys(block) };
4951}
4952#[cfg(feature = "metatensor")]
4958#[unsafe(no_mangle)]
4959pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
4960 frame_handle: *const RKRConFrame,
4961 out_block: *mut *mut metatensor::c_api::mts_block_t,
4962) -> RKRStatus {
4963 if frame_handle.is_null() || out_block.is_null() {
4964 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4965 }
4966 unsafe { *out_block = std::ptr::null_mut() };
4967 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4968 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4969 };
4970 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4971 crate::metatensor_export::frame_positions_block(frame)
4972 })) {
4973 Ok(Ok(b)) => {
4974 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
4975 note(RKRStatus::RKR_STATUS_SUCCESS)
4976 }
4977 Ok(Err(_)) | Err(_) => note(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
4978 }
4979}
4980#[cfg(feature = "metatensor")]
4985#[unsafe(no_mangle)]
4986pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
4987 frame_handle: *const RKRConFrame,
4988 out_block: *mut *mut metatensor::c_api::mts_block_t,
4989) -> RKRStatus {
4990 if frame_handle.is_null() || out_block.is_null() {
4991 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4992 }
4993 unsafe { *out_block = std::ptr::null_mut() };
4994 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
4995 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
4996 };
4997 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4998 crate::metatensor_export::frame_velocities_block(frame)
4999 })) {
5000 Ok(Ok(Some(b))) => {
5001 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
5002 note(RKRStatus::RKR_STATUS_SUCCESS)
5003 }
5004 Ok(Ok(None)) => note(RKRStatus::RKR_STATUS_SECTION_ABSENT),
5005 Ok(Err(_)) | Err(_) => note(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
5006 }
5007}
5008#[cfg(feature = "metatensor")]
5013#[unsafe(no_mangle)]
5014pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
5015 frame_handle: *const RKRConFrame,
5016 out_block: *mut *mut metatensor::c_api::mts_block_t,
5017) -> RKRStatus {
5018 if frame_handle.is_null() || out_block.is_null() {
5019 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5020 }
5021 unsafe { *out_block = std::ptr::null_mut() };
5022 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5023 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5024 };
5025 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5026 crate::metatensor_export::frame_forces_block(frame)
5027 })) {
5028 Ok(Ok(Some(b))) => {
5029 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
5030 note(RKRStatus::RKR_STATUS_SUCCESS)
5031 }
5032 Ok(Ok(None)) => note(RKRStatus::RKR_STATUS_SECTION_ABSENT),
5033 Ok(Err(_)) | Err(_) => note(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
5034 }
5035}
5036#[cfg(feature = "metatensor")]
5041#[unsafe(no_mangle)]
5042pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
5043 frame_handle: *const RKRConFrame,
5044 out_block: *mut *mut metatensor::c_api::mts_block_t,
5045) -> RKRStatus {
5046 if frame_handle.is_null() || out_block.is_null() {
5047 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5048 }
5049 unsafe { *out_block = std::ptr::null_mut() };
5050 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5051 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5052 };
5053 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5054 crate::metatensor_export::frame_energies_block(frame)
5055 })) {
5056 Ok(Ok(Some(b))) => {
5057 unsafe { *out_block = crate::metatensor_export::tensor_block_into_raw_mts(b) };
5058 note(RKRStatus::RKR_STATUS_SUCCESS)
5059 }
5060 Ok(Ok(None)) => note(RKRStatus::RKR_STATUS_SECTION_ABSENT),
5061 Ok(Err(_)) | Err(_) => note(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
5062 }
5063}
5064#[cfg(not(feature = "zstd"))]
5069#[unsafe(no_mangle)]
5070pub unsafe extern "C" fn create_writer_zstd_c(
5071 _filename_c: *const c_char,
5072) -> *mut RKRConFrameWriter {
5073 ptr::null_mut()
5074}
5075#[cfg(not(feature = "zstd"))]
5080#[unsafe(no_mangle)]
5081pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
5082 _filename_c: *const c_char,
5083 _precision: u8,
5084) -> *mut RKRConFrameWriter {
5085 ptr::null_mut()
5086}
5087#[cfg(not(feature = "metatensor"))]
5091#[allow(non_camel_case_types)]
5095pub struct mts_block_t {
5096 _private: [u8; 0],
5097}
5098#[cfg(not(feature = "metatensor"))]
5103#[unsafe(no_mangle)]
5104pub unsafe extern "C" fn rkr_mts_block_free(_block: *mut mts_block_t) {}
5105#[cfg(not(feature = "metatensor"))]
5110#[unsafe(no_mangle)]
5111pub unsafe extern "C" fn rkr_frame_metatensor_positions_block(
5112 _frame_handle: *const RKRConFrame,
5113 out_block: *mut *mut mts_block_t,
5114) -> RKRStatus {
5115 if !out_block.is_null() {
5116 unsafe { *out_block = std::ptr::null_mut() };
5117 }
5118 note(RKRStatus::RKR_STATUS_FEATURE_DISABLED)
5119}
5120#[cfg(not(feature = "metatensor"))]
5125#[unsafe(no_mangle)]
5126pub unsafe extern "C" fn rkr_frame_metatensor_velocities_block(
5127 _frame_handle: *const RKRConFrame,
5128 out_block: *mut *mut mts_block_t,
5129) -> RKRStatus {
5130 if !out_block.is_null() {
5131 unsafe { *out_block = std::ptr::null_mut() };
5132 }
5133 note(RKRStatus::RKR_STATUS_FEATURE_DISABLED)
5134}
5135#[cfg(not(feature = "metatensor"))]
5140#[unsafe(no_mangle)]
5141pub unsafe extern "C" fn rkr_frame_metatensor_forces_block(
5142 _frame_handle: *const RKRConFrame,
5143 out_block: *mut *mut mts_block_t,
5144) -> RKRStatus {
5145 if !out_block.is_null() {
5146 unsafe { *out_block = std::ptr::null_mut() };
5147 }
5148 note(RKRStatus::RKR_STATUS_FEATURE_DISABLED)
5149}
5150#[cfg(not(feature = "metatensor"))]
5155#[unsafe(no_mangle)]
5156pub unsafe extern "C" fn rkr_frame_metatensor_atom_energies_block(
5157 _frame_handle: *const RKRConFrame,
5158 out_block: *mut *mut mts_block_t,
5159) -> RKRStatus {
5160 if !out_block.is_null() {
5161 unsafe { *out_block = std::ptr::null_mut() };
5162 }
5163 note(RKRStatus::RKR_STATUS_FEATURE_DISABLED)
5164}
5165#[unsafe(no_mangle)]
5172pub unsafe extern "C" fn rkr_frame_atom_count(frame_handle: *const RKRConFrame) -> usize {
5173 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5174 return 0;
5175 };
5176 frame.atom_data.len()
5177}
5178#[unsafe(no_mangle)]
5184pub unsafe extern "C" fn rkr_frame_xyz_view(
5185 frame_handle: *const RKRConFrame,
5186 out: *mut RKRArrayView,
5187) -> RKRStatus {
5188 fill_array2_view(frame_handle, out, |f| &f.positions, false)
5189}
5190
5191#[unsafe(no_mangle)]
5197pub unsafe extern "C" fn rkr_frame_velocities_view(
5198 frame_handle: *const RKRConFrame,
5199 out: *mut RKRArrayView,
5200) -> RKRStatus {
5201 fill_array2_view(frame_handle, out, |f| &f.velocities, true)
5202}
5203
5204#[unsafe(no_mangle)]
5210pub unsafe extern "C" fn rkr_frame_forces_view(
5211 frame_handle: *const RKRConFrame,
5212 out: *mut RKRArrayView,
5213) -> RKRStatus {
5214 fill_array2_view(frame_handle, out, |f| &f.forces, true)
5215}
5216
5217#[unsafe(no_mangle)]
5223pub unsafe extern "C" fn rkr_frame_has_displacements(frame_handle: *const RKRConFrame) -> bool {
5224 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
5225 Some(f) => f.has_displacements(),
5226 None => false,
5227 }
5228}
5229
5230#[unsafe(no_mangle)]
5235pub unsafe extern "C" fn rkr_frame_displacements_view(
5236 frame_handle: *const RKRConFrame,
5237 out: *mut RKRArrayView,
5238) -> RKRStatus {
5239 fill_array2_view(frame_handle, out, |f| &f.displacements, true)
5240}
5241
5242#[unsafe(no_mangle)]
5248pub unsafe extern "C" fn rkr_frame_has_spreads(frame_handle: *const RKRConFrame) -> bool {
5249 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
5250 Some(f) => f.has_spreads(),
5251 None => false,
5252 }
5253}
5254
5255#[unsafe(no_mangle)]
5260pub unsafe extern "C" fn rkr_frame_spreads_view(
5261 frame_handle: *const RKRConFrame,
5262 out: *mut RKRArrayView,
5263) -> RKRStatus {
5264 fill_array2_view(frame_handle, out, |f| &f.spreads, true)
5265}
5266
5267#[unsafe(no_mangle)]
5273pub unsafe extern "C" fn rkr_frame_energies_view(
5274 frame_handle: *const RKRConFrame,
5275 out: *mut RKRArrayView,
5276) -> RKRStatus {
5277 fill_array1_view(frame_handle, out, |f| &f.atom_energies, true)
5278}
5279
5280#[unsafe(no_mangle)]
5286pub unsafe extern "C" fn rkr_frame_masses_view(
5287 frame_handle: *const RKRConFrame,
5288 out: *mut RKRArrayView,
5289) -> RKRStatus {
5290 fill_array1_view(frame_handle, out, |f| &f.masses, false)
5291}
5292
5293#[unsafe(no_mangle)]
5299pub unsafe extern "C" fn rkr_frame_atom_ids_view(
5300 frame_handle: *const RKRConFrame,
5301 out: *mut RKRArrayView,
5302) -> RKRStatus {
5303 if frame_handle.is_null() || out.is_null() {
5304 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5305 }
5306 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5307 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5308 };
5309 let slice = frame.atom_ids.as_slice_memory_order();
5310 unsafe {
5311 *out = RKRArrayView {
5312 data: slice
5313 .map(|s| s.as_ptr() as *const std::ffi::c_void)
5314 .unwrap_or(std::ptr::null()),
5315 n: frame.atom_ids.len(),
5316 cols: 1,
5317 dtype_code: 1, dtype_bits: 64,
5319 };
5320 }
5321 note(RKRStatus::RKR_STATUS_SUCCESS)
5322}
5323
5324#[unsafe(no_mangle)]
5331pub unsafe extern "C" fn rkr_frame_xyz_f64(
5332 frame_handle: *const RKRConFrame,
5333 n: *mut usize,
5334) -> *const f64 {
5335 f64_col_ptr(frame_handle, n, |f| f.positions.f64_slice())
5336}
5337
5338#[unsafe(no_mangle)]
5344pub unsafe extern "C" fn rkr_frame_velocities_f64(
5345 frame_handle: *const RKRConFrame,
5346 n: *mut usize,
5347) -> *const f64 {
5348 f64_col_ptr(frame_handle, n, |f| f.velocities.f64_slice())
5349}
5350
5351#[unsafe(no_mangle)]
5357pub unsafe extern "C" fn rkr_frame_forces_f64(
5358 frame_handle: *const RKRConFrame,
5359 n: *mut usize,
5360) -> *const f64 {
5361 f64_col_ptr(frame_handle, n, |f| f.forces.f64_slice())
5362}
5363
5364#[unsafe(no_mangle)]
5369pub unsafe extern "C" fn rkr_frame_displacements_f64(
5370 frame_handle: *const RKRConFrame,
5371 n: *mut usize,
5372) -> *const f64 {
5373 f64_col_ptr(frame_handle, n, |f| f.displacements.f64_slice())
5374}
5375
5376#[unsafe(no_mangle)]
5381pub unsafe extern "C" fn rkr_frame_spreads_f64(
5382 frame_handle: *const RKRConFrame,
5383 n: *mut usize,
5384) -> *const f64 {
5385 f64_col_ptr(frame_handle, n, |f| f.spreads.f64_slice())
5386}
5387
5388fn fill_array2_view(
5389 frame_handle: *const RKRConFrame,
5390 out: *mut RKRArrayView,
5391 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
5392 section: bool,
5393) -> RKRStatus {
5394 if frame_handle.is_null() || out.is_null() {
5395 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5396 }
5397 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5398 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5399 };
5400 let arr = get(frame);
5401 if section && arr.nrows() == 0 {
5402 unsafe { *out = RKRArrayView::empty() };
5403 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
5404 }
5405 unsafe { *out = RKRArrayView::from_array2(arr) };
5406 note(RKRStatus::RKR_STATUS_SUCCESS)
5407}
5408
5409fn fill_array1_view(
5410 frame_handle: *const RKRConFrame,
5411 out: *mut RKRArrayView,
5412 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
5413 section: bool,
5414) -> RKRStatus {
5415 if frame_handle.is_null() || out.is_null() {
5416 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5417 }
5418 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5419 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5420 };
5421 let arr = get(frame);
5422 if section && arr.is_empty() {
5423 unsafe { *out = RKRArrayView::empty() };
5424 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
5425 }
5426 unsafe { *out = RKRArrayView::from_array1(arr) };
5427 note(RKRStatus::RKR_STATUS_SUCCESS)
5428}
5429
5430fn f64_col_ptr(
5431 frame_handle: *const RKRConFrame,
5432 n: *mut usize,
5433 get: impl FnOnce(&ConFrame) -> Option<&[f64]>,
5434) -> *const f64 {
5435 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5436 if !n.is_null() {
5437 unsafe { *n = 0 };
5438 }
5439 return std::ptr::null();
5440 };
5441 match get(frame) {
5442 Some(s) if !s.is_empty() => {
5443 if !n.is_null() {
5444 unsafe { *n = s.len() / 3 };
5445 }
5446 s.as_ptr()
5447 }
5448 _ => {
5449 if !n.is_null() {
5450 unsafe { *n = 0 };
5451 }
5452 std::ptr::null()
5453 }
5454 }
5455}
5456
5457#[unsafe(no_mangle)]
5464pub unsafe extern "C" fn rkr_frame_copy_positions(
5465 frame_handle: *const RKRConFrame,
5466 out: *mut f64,
5467 out_len: usize,
5468) -> RKRStatus {
5469 copy_array2_f64(frame_handle, out, out_len, |f| &f.positions, false)
5470}
5471fn copy_array2_f64(
5472 frame_handle: *const RKRConFrame,
5473 out: *mut f64,
5474 out_len: usize,
5475 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray2,
5476 section: bool,
5477) -> RKRStatus {
5478 if frame_handle.is_null() || out.is_null() {
5479 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5480 }
5481 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5482 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5483 };
5484 let arr = get(frame);
5485 let n = if arr.nrows() > 0 {
5486 arr.nrows()
5487 } else {
5488 frame.atom_data.len()
5489 };
5490 if section && arr.nrows() == 0 {
5491 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
5492 }
5493 let need = n.saturating_mul(3);
5494 if out_len < need {
5495 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
5496 }
5497 let dest = unsafe { std::slice::from_raw_parts_mut(out, need) };
5498 if arr.nrows() == n {
5499 if let Some(src) = arr.f64_slice() {
5500 dest.copy_from_slice(&src[..need.min(src.len())]);
5501 return note(RKRStatus::RKR_STATUS_SUCCESS);
5502 }
5503 for i in 0..n {
5504 let row = arr.as_f64_row(i);
5505 dest[i * 3] = row[0];
5506 dest[i * 3 + 1] = row[1];
5507 dest[i * 3 + 2] = row[2];
5508 }
5509 return note(RKRStatus::RKR_STATUS_SUCCESS);
5510 }
5511 for (i, a) in frame.atom_data.iter().enumerate() {
5512 dest[i * 3] = a.x();
5513 dest[i * 3 + 1] = a.y();
5514 dest[i * 3 + 2] = a.z();
5515 }
5516 note(RKRStatus::RKR_STATUS_SUCCESS)
5517}
5518
5519fn copy_array1_f64(
5520 frame_handle: *const RKRConFrame,
5521 out: *mut f64,
5522 out_len: usize,
5523 get: impl FnOnce(&ConFrame) -> &crate::storage_dtype::FloatArray1,
5524 section: bool,
5525) -> RKRStatus {
5526 if frame_handle.is_null() || out.is_null() {
5527 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5528 }
5529 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5530 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5531 };
5532 let arr = get(frame);
5533 let n = if !arr.is_empty() {
5534 arr.len()
5535 } else {
5536 frame.atom_data.len()
5537 };
5538 if section && arr.is_empty() {
5539 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
5540 }
5541 if out_len < n {
5542 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
5543 }
5544 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
5545 if let Some(src) = arr.f64_slice() {
5546 dest.copy_from_slice(&src[..n.min(src.len())]);
5547 return note(RKRStatus::RKR_STATUS_SUCCESS);
5548 }
5549 for (slot, dest_i) in dest.iter_mut().enumerate() {
5550 *dest_i = arr.get_f64(slot);
5551 }
5552 note(RKRStatus::RKR_STATUS_SUCCESS)
5553}
5554
5555#[unsafe(no_mangle)]
5560pub unsafe extern "C" fn rkr_frame_copy_velocities(
5561 frame_handle: *const RKRConFrame,
5562 out: *mut f64,
5563 out_len: usize,
5564) -> RKRStatus {
5565 copy_array2_f64(frame_handle, out, out_len, |f| &f.velocities, true)
5566}
5567#[unsafe(no_mangle)]
5572pub unsafe extern "C" fn rkr_frame_copy_forces(
5573 frame_handle: *const RKRConFrame,
5574 out: *mut f64,
5575 out_len: usize,
5576) -> RKRStatus {
5577 copy_array2_f64(frame_handle, out, out_len, |f| &f.forces, true)
5578}
5579#[unsafe(no_mangle)]
5585pub unsafe extern "C" fn rkr_frame_copy_displacements(
5586 frame_handle: *const RKRConFrame,
5587 out: *mut f64,
5588 out_len: usize,
5589) -> RKRStatus {
5590 copy_array2_f64(frame_handle, out, out_len, |f| &f.displacements, true)
5591}
5592#[unsafe(no_mangle)]
5598pub unsafe extern "C" fn rkr_frame_copy_spreads(
5599 frame_handle: *const RKRConFrame,
5600 out: *mut f64,
5601 out_len: usize,
5602) -> RKRStatus {
5603 copy_array2_f64(frame_handle, out, out_len, |f| &f.spreads, true)
5604}
5605#[unsafe(no_mangle)]
5610pub unsafe extern "C" fn rkr_frame_copy_atom_energies(
5611 frame_handle: *const RKRConFrame,
5612 out: *mut f64,
5613 out_len: usize,
5614) -> RKRStatus {
5615 copy_array1_f64(frame_handle, out, out_len, |f| &f.atom_energies, true)
5616}
5617#[unsafe(no_mangle)]
5622pub unsafe extern "C" fn rkr_frame_copy_masses(
5623 frame_handle: *const RKRConFrame,
5624 out: *mut f64,
5625 out_len: usize,
5626) -> RKRStatus {
5627 copy_array1_f64(frame_handle, out, out_len, |f| &f.masses, false)
5628}
5629#[unsafe(no_mangle)]
5634pub unsafe extern "C" fn rkr_frame_copy_atom_ids(
5635 frame_handle: *const RKRConFrame,
5636 out: *mut u64,
5637 out_len: usize,
5638) -> RKRStatus {
5639 if frame_handle.is_null() || out.is_null() {
5640 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5641 }
5642 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5643 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5644 };
5645 let n = frame.atom_ids.len().max(frame.atom_data.len());
5646 if out_len < n {
5647 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
5648 }
5649 let dest = unsafe { std::slice::from_raw_parts_mut(out, n) };
5650 if let Some(src) = frame.atom_ids.as_slice_memory_order() {
5651 dest[..src.len().min(n)].copy_from_slice(&src[..src.len().min(n)]);
5652 return note(RKRStatus::RKR_STATUS_SUCCESS);
5653 }
5654 for (i, a) in frame.atom_data.iter().enumerate() {
5655 dest[i] = a.atom_id;
5656 }
5657 note(RKRStatus::RKR_STATUS_SUCCESS)
5658}
5659fn frame_positions_arc(frame: &ConFrame) -> ndarray::ArcArray2<f64> {
5660 let n = frame.atom_data.len();
5661 let mut data = Vec::with_capacity(n * 3);
5662 for a in &frame.atom_data {
5663 data.extend_from_slice(&[a.x(), a.y(), a.z()]);
5664 }
5665 ndarray::ArcArray2::from_shape_vec((n, 3), data)
5666 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)))
5667}
5668
5669#[unsafe(no_mangle)]
5679pub unsafe extern "C" fn rkr_frame_positions_as_dlpack(
5680 frame_handle: *const RKRConFrame,
5681 device_type: i32,
5682 device_id: i32,
5683 _stream: i64,
5684 _max_version_major: u32,
5685 _max_version_minor: u32,
5686 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5687) -> RKRStatus {
5688 if frame_handle.is_null() || out_tensor.is_null() {
5689 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5690 }
5691 unsafe { *out_tensor = std::ptr::null_mut() };
5692 let dl_device = if device_type == rkr_dl_device_type::RKR_DL_CPU {
5693 dlpk::sys::DLDevice::cpu()
5694 } else if device_type == rkr_dl_device_type::RKR_DL_CUDA {
5695 #[cfg(feature = "cuda")]
5696 {
5697 dlpk::sys::DLDevice::cuda(device_id)
5698 }
5699 #[cfg(not(feature = "cuda"))]
5700 {
5701 let _ = device_id;
5702 return note(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
5703 }
5704 } else {
5705 return note(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
5706 };
5707 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5708 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5709 };
5710 match frame.positions_as_dlpack(dl_device) {
5712 Ok(tensor) => {
5713 let raw = tensor.into_raw();
5714 unsafe {
5715 *out_tensor = raw.as_ptr();
5716 }
5717 note(RKRStatus::RKR_STATUS_SUCCESS)
5718 }
5719 Err(e) => map_dlpack_err(e),
5720 }
5721}
5722
5723#[unsafe(no_mangle)]
5729pub unsafe extern "C" fn rkr_frame_positions_from_dlpack(
5730 frame_handle: *mut RKRConFrame,
5731 tensor: *const RKRDLManagedTensorVersioned,
5732) -> RKRStatus {
5733 if frame_handle.is_null() || tensor.is_null() {
5734 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5735 }
5736 let frame = unsafe { &mut *(frame_handle as *mut ConFrame) };
5737 let dl = unsafe { &(*tensor).dl_tensor };
5738 if dl.device.device_type != dlpk::sys::DLDeviceType::kDLCPU {
5739 return note(RKRStatus::RKR_STATUS_FEATURE_DISABLED);
5740 }
5741 let n = frame.atom_data.len();
5742 let need = n.saturating_mul(3);
5743 let ndim = dl.ndim as usize;
5744 let shape = if dl.shape.is_null() {
5745 return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
5746 } else {
5747 unsafe { std::slice::from_raw_parts(dl.shape, ndim) }
5748 };
5749 let shape_ok = (ndim == 2 && shape[0] == n as i64 && shape[1] == 3)
5750 || (ndim == 1 && shape[0] == need as i64);
5751 if !shape_ok {
5752 return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
5753 }
5754 let nelem = need;
5755 let code = dl.dtype.code as u8;
5756 let bits = dl.dtype.bits;
5757 if dl.data.is_null() {
5758 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5759 }
5760 let vals: Vec<f64> = if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 64 {
5761 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f64, nelem) };
5762 s.to_vec()
5763 } else if code == rkr_dl_type_code::RKR_DL_FLOAT && bits == 32 {
5764 let s = unsafe { std::slice::from_raw_parts(dl.data as *const f32, nelem) };
5765 s.iter().map(|&x| x as f64).collect()
5766 } else {
5767 return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
5768 };
5769 if frame.positions.nrows() != n {
5770 return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR);
5771 }
5772 for i in 0..n {
5773 frame
5774 .positions
5775 .set_f64_row(i, [vals[i * 3], vals[i * 3 + 1], vals[i * 3 + 2]]);
5776 }
5777 frame.sync_atom_data_from_arrays();
5778 note(RKRStatus::RKR_STATUS_SUCCESS)
5779}
5780
5781#[unsafe(no_mangle)]
5788pub unsafe extern "C" fn rkr_frame_positions_dlpack(
5789 frame_handle: *const RKRConFrame,
5790 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5791) -> RKRStatus {
5792 unsafe {
5793 rkr_frame_positions_as_dlpack(
5794 frame_handle,
5795 rkr_dl_device_type::RKR_DL_CPU,
5796 0,
5797 0,
5798 1,
5799 0,
5800 out_tensor,
5801 )
5802 }
5803}
5804
5805#[unsafe(no_mangle)]
5810pub unsafe extern "C" fn rkr_frame_positions_dlpack_ex(
5811 frame_handle: *const RKRConFrame,
5812 opts: *const RKRDlpackExportOptions,
5813 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5814) -> RKRStatus {
5815 if frame_handle.is_null() || out_tensor.is_null() {
5816 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5817 }
5818 unsafe { *out_tensor = std::ptr::null_mut() };
5819 let o = match resolve_dlpack_opts(opts) {
5820 Ok(o) => o,
5821 Err(st) => return st,
5822 };
5823 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5824 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5825 };
5826 let arr = frame_positions_arc(frame);
5827 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
5828}
5829
5830#[unsafe(no_mangle)]
5836pub unsafe extern "C" fn rkr_frame_velocities_dlpack(
5837 frame_handle: *const RKRConFrame,
5838 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5839) -> RKRStatus {
5840 unsafe { rkr_frame_velocities_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
5841}
5842
5843#[unsafe(no_mangle)]
5848pub unsafe extern "C" fn rkr_frame_velocities_dlpack_ex(
5849 frame_handle: *const RKRConFrame,
5850 opts: *const RKRDlpackExportOptions,
5851 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5852) -> RKRStatus {
5853 if frame_handle.is_null() || out_tensor.is_null() {
5854 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5855 }
5856 unsafe { *out_tensor = std::ptr::null_mut() };
5857 let o = match resolve_dlpack_opts(opts) {
5858 Ok(o) => o,
5859 Err(st) => return st,
5860 };
5861 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5862 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5863 };
5864 if !frame.has_velocities() {
5865 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
5866 }
5867 let n = frame.atom_data.len();
5868 let mut data = Vec::with_capacity(n * 3);
5869 for a in &frame.atom_data {
5870 let v = a.velocity().unwrap_or([0.0; 3]);
5871 data.extend_from_slice(&v);
5872 }
5873 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
5874 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
5875 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
5876}
5877
5878#[unsafe(no_mangle)]
5884pub unsafe extern "C" fn rkr_frame_forces_dlpack(
5885 frame_handle: *const RKRConFrame,
5886 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5887) -> RKRStatus {
5888 unsafe { rkr_frame_forces_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
5889}
5890
5891#[unsafe(no_mangle)]
5896pub unsafe extern "C" fn rkr_frame_forces_dlpack_ex(
5897 frame_handle: *const RKRConFrame,
5898 opts: *const RKRDlpackExportOptions,
5899 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5900) -> RKRStatus {
5901 if frame_handle.is_null() || out_tensor.is_null() {
5902 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5903 }
5904 unsafe { *out_tensor = std::ptr::null_mut() };
5905 let o = match resolve_dlpack_opts(opts) {
5906 Ok(o) => o,
5907 Err(st) => return st,
5908 };
5909 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5910 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5911 };
5912 if !frame.has_forces() {
5913 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
5914 }
5915 let n = frame.atom_data.len();
5916 let mut data = Vec::with_capacity(n * 3);
5917 for a in &frame.atom_data {
5918 let f = a.force().unwrap_or([0.0; 3]);
5919 data.extend_from_slice(&f);
5920 }
5921 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
5922 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
5923 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
5924}
5925
5926#[unsafe(no_mangle)]
5933pub unsafe extern "C" fn rkr_frame_displacements_dlpack(
5934 frame_handle: *const RKRConFrame,
5935 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5936) -> RKRStatus {
5937 unsafe { rkr_frame_displacements_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
5938}
5939
5940#[unsafe(no_mangle)]
5947pub unsafe extern "C" fn rkr_frame_displacements_dlpack_ex(
5948 frame_handle: *const RKRConFrame,
5949 opts: *const RKRDlpackExportOptions,
5950 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5951) -> RKRStatus {
5952 if frame_handle.is_null() || out_tensor.is_null() {
5953 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5954 }
5955 unsafe { *out_tensor = std::ptr::null_mut() };
5956 let o = match resolve_dlpack_opts(opts) {
5957 Ok(o) => o,
5958 Err(st) => return st,
5959 };
5960 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
5961 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
5962 };
5963 if !frame.has_displacements() {
5964 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
5965 }
5966 let n = frame.atom_data.len();
5967 let mut data = Vec::with_capacity(n * 3);
5968 for a in &frame.atom_data {
5969 let d = a.displacement().unwrap_or([0.0; 3]);
5970 data.extend_from_slice(&d);
5971 }
5972 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
5973 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
5974 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
5975}
5976
5977#[unsafe(no_mangle)]
5984pub unsafe extern "C" fn rkr_frame_spreads_dlpack(
5985 frame_handle: *const RKRConFrame,
5986 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
5987) -> RKRStatus {
5988 unsafe { rkr_frame_spreads_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
5989}
5990
5991#[unsafe(no_mangle)]
5998pub unsafe extern "C" fn rkr_frame_spreads_dlpack_ex(
5999 frame_handle: *const RKRConFrame,
6000 opts: *const RKRDlpackExportOptions,
6001 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
6002) -> RKRStatus {
6003 if frame_handle.is_null() || out_tensor.is_null() {
6004 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6005 }
6006 unsafe { *out_tensor = std::ptr::null_mut() };
6007 let o = match resolve_dlpack_opts(opts) {
6008 Ok(o) => o,
6009 Err(st) => return st,
6010 };
6011 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
6012 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6013 };
6014 if !frame.has_spreads() {
6015 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
6016 }
6017 let n = frame.atom_data.len();
6018 let mut data = Vec::with_capacity(n * 3);
6019 for a in &frame.atom_data {
6020 let d = a.spread().unwrap_or([0.0; 3]);
6021 data.extend_from_slice(&d);
6022 }
6023 let arr = ndarray::ArcArray2::from_shape_vec((n, 3), data)
6024 .unwrap_or_else(|_| ndarray::ArcArray2::zeros((0, 3)));
6025 export_owned_array2_dlpack_opts(&arr, &o, out_tensor)
6026}
6027
6028#[unsafe(no_mangle)]
6034pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack(
6035 frame_handle: *const RKRConFrame,
6036 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
6037) -> RKRStatus {
6038 unsafe { rkr_frame_atom_energies_dlpack_ex(frame_handle, std::ptr::null(), out_tensor) }
6039}
6040
6041#[unsafe(no_mangle)]
6046pub unsafe extern "C" fn rkr_frame_atom_energies_dlpack_ex(
6047 frame_handle: *const RKRConFrame,
6048 opts: *const RKRDlpackExportOptions,
6049 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
6050) -> RKRStatus {
6051 if frame_handle.is_null() || out_tensor.is_null() {
6052 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6053 }
6054 unsafe { *out_tensor = std::ptr::null_mut() };
6055 let o = match resolve_dlpack_opts(opts) {
6056 Ok(o) => o,
6057 Err(st) => return st,
6058 };
6059 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
6060 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6061 };
6062 if !frame.has_energies() {
6063 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
6064 }
6065 let data: Vec<f64> = frame
6066 .atom_data
6067 .iter()
6068 .map(|a| a.energy().unwrap_or(0.0))
6069 .collect();
6070 let arr = ndarray::ArcArray1::from_vec(data);
6071 export_owned_array1_f64_dlpack_opts(&arr, &o, out_tensor)
6072}
6073
6074pub struct RKRSelectionResult;
6078#[unsafe(no_mangle)]
6089pub unsafe extern "C" fn rkr_frame_select(
6090 frame_handle: *const RKRConFrame,
6091 selection: *const c_char,
6092 out_result: *mut *mut RKRSelectionResult,
6093) -> RKRStatus {
6094 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
6095 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6096 }
6097 let frame = unsafe { &*(frame_handle as *const ConFrame) };
6098 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
6099 Ok(s) => s,
6100 Err(_) => return note(RKRStatus::RKR_STATUS_INVALID_UTF8),
6101 };
6102 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
6103 Ok(result) => {
6104 let boxed = Box::new(result);
6105 unsafe {
6106 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
6107 }
6108 note(RKRStatus::RKR_STATUS_SUCCESS)
6109 }
6110 Err(_) => note(RKRStatus::RKR_STATUS_SELECTION_ERROR),
6111 }
6112}
6113#[unsafe(no_mangle)]
6118pub unsafe extern "C" fn rkr_selection_result_match_count(
6119 result_handle: *const RKRSelectionResult,
6120) -> u64 {
6121 if result_handle.is_null() {
6122 return 0;
6123 }
6124 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
6125 result.matches.len() as u64
6126}
6127#[unsafe(no_mangle)]
6132pub unsafe extern "C" fn rkr_selection_result_context_size(
6133 result_handle: *const RKRSelectionResult,
6134) -> u32 {
6135 if result_handle.is_null() {
6136 return 0;
6137 }
6138 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
6139 result.context_size as u32
6140}
6141#[unsafe(no_mangle)]
6147pub unsafe extern "C" fn rkr_selection_result_match_at(
6148 result_handle: *const RKRSelectionResult,
6149 match_index: u64,
6150 out_atoms: *mut u64,
6151 out_size: *mut u32,
6152) -> RKRStatus {
6153 if result_handle.is_null() || out_atoms.is_null() {
6154 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6155 }
6156 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
6157 let idx = match_index as usize;
6158 if idx >= result.matches.len() {
6159 return note(RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS);
6160 }
6161 let m = &result.matches[idx];
6162 unsafe {
6163 for i in 0..4 {
6164 *out_atoms.add(i) = if i < m.size {
6165 m.atoms[i] as u64
6166 } else {
6167 u64::MAX
6168 };
6169 }
6170 if !out_size.is_null() {
6171 *out_size = m.size as u32;
6172 }
6173 }
6174 note(RKRStatus::RKR_STATUS_SUCCESS)
6175}
6176#[unsafe(no_mangle)]
6182pub unsafe extern "C" fn rkr_selection_result_primary_indices(
6183 result_handle: *const RKRSelectionResult,
6184 out_indices: *mut u64,
6185 capacity: u64,
6186 out_written: *mut u64,
6187) -> RKRStatus {
6188 if result_handle.is_null() {
6189 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6190 }
6191 let result = unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
6192 let n = result.matches.len() as u64;
6193 if !out_written.is_null() {
6194 unsafe {
6195 *out_written = n;
6196 }
6197 }
6198 if n == 0 {
6199 return note(RKRStatus::RKR_STATUS_SUCCESS);
6200 }
6201 if out_indices.is_null() {
6202 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6203 }
6204 if capacity < n {
6205 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
6206 }
6207 unsafe {
6208 for (i, m) in result.matches.iter().enumerate() {
6209 *out_indices.add(i) = m.atoms[0] as u64;
6210 }
6211 }
6212 note(RKRStatus::RKR_STATUS_SUCCESS)
6213}
6214#[unsafe(no_mangle)]
6219pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
6220 if result_handle.is_null() {
6221 return;
6222 }
6223 unsafe {
6224 drop(Box::from_raw(
6225 result_handle as *mut crate::chemfiles_selection::SelectionResult,
6226 ));
6227 }
6228}
6229#[unsafe(no_mangle)]
6231pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
6232 #[cfg(feature = "chemfiles")]
6233 {
6234 1
6235 }
6236 #[cfg(not(feature = "chemfiles"))]
6237 {
6238 0
6239 }
6240}
6241
6242#[unsafe(no_mangle)]
6244pub extern "C" fn rkr_has_parallel_support() -> u8 {
6245 #[cfg(feature = "parallel")]
6246 {
6247 1
6248 }
6249 #[cfg(not(feature = "parallel"))]
6250 {
6251 0
6252 }
6253}
6254#[unsafe(no_mangle)]
6260pub unsafe extern "C" fn rkr_read_chemfiles_first(path_c: *const c_char) -> *mut RKRConFrame {
6261 if path_c.is_null() {
6262 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6263 }
6264 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
6265 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6266 };
6267 match crate::chemfiles_import::con_frame_from_trajectory_path(path_str) {
6268 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
6269 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER),
6270 }
6271}
6272
6273#[unsafe(no_mangle)]
6279pub unsafe extern "C" fn rkr_read_chemfiles(
6280 path_c: *const c_char,
6281 num_frames: *mut usize,
6282) -> *mut *mut RKRConFrame {
6283 if path_c.is_null() || num_frames.is_null() {
6284 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6285 }
6286 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
6287 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6288 };
6289 match crate::chemfiles_import::con_frames_from_trajectory_path(path_str) {
6290 Ok(frames) => pack_frame_handles(frames, num_frames),
6291 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER),
6292 }
6293}
6294
6295#[unsafe(no_mangle)]
6301pub unsafe extern "C" fn rkr_read_chemfiles_nth(
6302 path_c: *const c_char,
6303 index: usize,
6304) -> *mut RKRConFrame {
6305 if path_c.is_null() {
6306 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6307 }
6308 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
6309 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6310 };
6311 match crate::chemfiles_import::con_frame_from_trajectory_path_nth(path_str, index) {
6312 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
6313 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER),
6314 }
6315}
6316
6317#[unsafe(no_mangle)]
6323pub unsafe extern "C" fn rkr_chemfiles_nsteps(path_c: *const c_char) -> usize {
6324 if path_c.is_null() {
6325 return usize::MAX;
6326 }
6327 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
6328 return usize::MAX;
6329 };
6330 crate::chemfiles_import::nsteps_from_trajectory_path(path_str).unwrap_or(usize::MAX)
6331}
6332
6333#[unsafe(no_mangle)]
6341pub unsafe extern "C" fn rkr_read_chemfiles_range(
6342 path_c: *const c_char,
6343 start: usize,
6344 step: usize,
6345 stop: usize,
6346 topology_c: *const c_char,
6347 guess_bonds: u8,
6348 num_frames: *mut usize,
6349) -> *mut *mut RKRConFrame {
6350 if path_c.is_null() || num_frames.is_null() {
6351 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6352 }
6353 let Ok(path_str) = unsafe { CStr::from_ptr(path_c) }.to_str() else {
6354 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6355 };
6356 let topology = if topology_c.is_null() {
6357 None
6358 } else {
6359 match unsafe { CStr::from_ptr(topology_c) }.to_str() {
6360 Ok(s) if !s.is_empty() => Some(std::path::PathBuf::from(s)),
6361 _ => return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER),
6362 }
6363 };
6364 let opts = crate::chemfiles_import::ChemfilesReadOpts {
6365 start,
6366 step,
6367 stop: if stop == usize::MAX { None } else { Some(stop) },
6368 format: None,
6369 topology,
6370 topology_format: None,
6371 guess_bonds: guess_bonds != 0,
6372 };
6373 match crate::chemfiles_import::con_frames_from_trajectory_path_with(path_str, &opts) {
6374 Ok(frames) => pack_frame_handles(frames, num_frames),
6375 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
6376 }
6377}
6378#[unsafe(no_mangle)]
6385pub unsafe extern "C" fn rkr_read_chemfiles_memory(
6386 data_c: *const c_char,
6387 format_c: *const c_char,
6388 num_frames: *mut usize,
6389) -> *mut *mut RKRConFrame {
6390 if data_c.is_null() || format_c.is_null() || num_frames.is_null() {
6391 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6392 }
6393 let Ok(data) = unsafe { CStr::from_ptr(data_c) }.to_str() else {
6394 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6395 };
6396 let Ok(format) = unsafe { CStr::from_ptr(format_c) }.to_str() else {
6397 return null_err::<_>(RKRStatus::RKR_STATUS_NULL_POINTER);
6398 };
6399 match crate::chemfiles_import::con_frames_from_memory(data, format) {
6400 Ok(frames) => {
6401 let n = frames.len();
6402 unsafe { *num_frames = n };
6403 let mut ptrs: Vec<*mut RKRConFrame> = frames
6404 .into_iter()
6405 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
6406 .collect();
6407 let p = ptrs.as_mut_ptr();
6408 std::mem::forget(ptrs);
6409 p
6410 }
6411 Err(_) => null_err::<_>(RKRStatus::RKR_STATUS_INTERNAL_ERROR),
6412 }
6413}
6414#[unsafe(no_mangle)]
6419pub unsafe extern "C" fn rkr_dlpack_delete(tensor: *mut RKRDLManagedTensorVersioned) {
6420 if tensor.is_null() {
6421 return;
6422 }
6423 unsafe {
6424 let t = &mut *tensor;
6425 if let Some(del) = t.deleter {
6426 del(tensor);
6427 }
6428 }
6429}
6430
6431#[unsafe(no_mangle)]
6441pub unsafe extern "C" fn rkr_pack_rcso(
6442 frame_handle: *const RKRConFrame,
6443 buf: *mut u8,
6444 buflen: usize,
6445 out_len: *mut usize,
6446) -> RKRStatus {
6447 if out_len.is_null() {
6448 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6449 }
6450 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
6451 Some(f) => f,
6452 None => return note(RKRStatus::RKR_STATUS_NULL_POINTER),
6453 };
6454 let bytes = match crate::rcso::Rcso::encode_frame(frame) {
6455 Ok(b) => b,
6456 Err(_) => return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
6457 };
6458 unsafe { *out_len = bytes.len() };
6459 if buf.is_null() {
6460 return note(RKRStatus::RKR_STATUS_SUCCESS);
6461 }
6462 if buflen < bytes.len() {
6463 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
6464 }
6465 unsafe {
6466 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
6467 }
6468 note(RKRStatus::RKR_STATUS_SUCCESS)
6469}
6470
6471#[unsafe(no_mangle)]
6476pub unsafe extern "C" fn rkr_unpack_rcso_natoms(
6477 buf: *const u8,
6478 buflen: usize,
6479 out_natoms: *mut u32,
6480) -> RKRStatus {
6481 if buf.is_null() || out_natoms.is_null() {
6482 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6483 }
6484 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
6485 match crate::rcso::Rcso::decode(bytes) {
6486 Ok(s) => {
6487 unsafe { *out_natoms = s.natoms };
6488 note(RKRStatus::RKR_STATUS_SUCCESS)
6489 }
6490 Err(_) => note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
6491 }
6492}
6493
6494#[unsafe(no_mangle)]
6499pub unsafe extern "C" fn rkr_unpack_rcso_positions(
6500 buf: *const u8,
6501 buflen: usize,
6502 dest: *mut f64,
6503 dest_natoms: u32,
6504) -> RKRStatus {
6505 if buf.is_null() || dest.is_null() {
6506 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6507 }
6508 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
6509 let soa = match crate::rcso::Rcso::decode(bytes) {
6510 Ok(s) => s,
6511 Err(_) => return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
6512 };
6513 if soa.natoms != dest_natoms {
6514 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
6515 }
6516 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
6517 for (i, p) in soa.positions.iter().enumerate() {
6518 out[i * 3] = p[0];
6519 out[i * 3 + 1] = p[1];
6520 out[i * 3 + 2] = p[2];
6521 }
6522 note(RKRStatus::RKR_STATUS_SUCCESS)
6523}
6524
6525#[unsafe(no_mangle)]
6531pub unsafe extern "C" fn rkr_unpack_rcso_forces(
6532 buf: *const u8,
6533 buflen: usize,
6534 dest: *mut f64,
6535 dest_natoms: u32,
6536) -> RKRStatus {
6537 if buf.is_null() || dest.is_null() {
6538 return note(RKRStatus::RKR_STATUS_NULL_POINTER);
6539 }
6540 let bytes = unsafe { std::slice::from_raw_parts(buf, buflen) };
6541 let soa = match crate::rcso::Rcso::decode(bytes) {
6542 Ok(s) => s,
6543 Err(_) => return note(RKRStatus::RKR_STATUS_VALIDATION_ERROR),
6544 };
6545 let Some(forces) = soa.forces else {
6546 return note(RKRStatus::RKR_STATUS_SECTION_ABSENT);
6547 };
6548 if soa.natoms != dest_natoms {
6549 return note(RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
6550 }
6551 let out = unsafe { std::slice::from_raw_parts_mut(dest, dest_natoms as usize * 3) };
6552 for (i, f) in forces.iter().enumerate() {
6553 out[i * 3] = f[0];
6554 out[i * 3 + 1] = f[1];
6555 out[i * 3 + 2] = f[2];
6556 }
6557 note(RKRStatus::RKR_STATUS_SUCCESS)
6558}
6559
6560#[cfg(test)]
6561mod tests {
6562 use super::*;
6563 use std::ffi::{CStr, CString};
6564 #[test]
6565 fn eon_export_matches_the_frame_and_reuses_metadata_json() {
6566 let text = "\
6567v3
6568{\"con_spec_version\":3,\"con_spec_minor\":1,\"units\":{\"length\":\"angstrom\",\"energy\":\"eV\"}}
656910 10 10
657090 90 90
6571
6572
65732
65741 1
657563.546 1.0
6576Cu
6577Coordinates of Component 1
65781.50000000 2.50000000 3.50000000 0 0
6579H
6580Coordinates of Component 2
65810.25000000 0.50000000 0.75000000 0 1
6582v3
6583{\"con_spec_version\":3,\"con_spec_minor\":1,\"units\":{\"length\":\"angstrom\",\"energy\":\"eV\"}}
658410 10 10
658590 90 90
6586
6587
65882
65891 1
659063.546 1.0
6591Cu
6592Coordinates of Component 1
65934.00000000 5.00000000 6.00000000 0 0
6594H
6595Coordinates of Component 2
65967.00000000 8.00000000 9.00000000 0 1
6597";
6598 let frames = crate::iterators::frames_from_text(text, Some(1)).expect("parse");
6599 assert_eq!(frames.len(), 2);
6600 let json0 = metadata_json_text(&frames[0]);
6601 let json1 = metadata_json_text(&frames[1]);
6602 assert_eq!(json0, metadata_json_rebuilt(&frames[0]));
6603 assert_eq!(json0, json1);
6604 assert!(json0.contains("\"con_spec_version\":3"));
6605 let raw = &frames[0] as *const ConFrame as *const RKRConFrame;
6606 let c = unsafe { rkr_frame_to_c_frame(raw) };
6607 assert!(!c.is_null());
6608 let view = unsafe { &*c };
6609 assert_eq!(view.num_atoms, 2);
6610 let atoms = unsafe { std::slice::from_raw_parts(view.atoms, view.num_atoms) };
6611 assert_eq!(atoms[0].atomic_number, 29);
6612 assert_eq!(atoms[0].mass, 63.546);
6613 assert!((atoms[0].x - 1.5).abs() < 1e-12);
6614 assert!((atoms[0].y - 2.5).abs() < 1e-12);
6615 assert!((atoms[0].z - 3.5).abs() < 1e-12);
6616 assert_eq!(atoms[1].atomic_number, 1);
6617 assert_eq!(atoms[1].mass, 1.0);
6618 assert!((atoms[1].x - 0.25).abs() < 1e-12);
6619 unsafe { free_c_frame(c) };
6620 let mut n_types = 16usize;
6621 let mut atomic_numbers = [0u64; 16];
6622 let mut species_mass = [0.0f64; 16];
6623 let mut species_count = [0u64; 16];
6624 let mut cell = [0.0f64; 3];
6625 let mut angles = [0.0f64; 3];
6626 let plain = unsafe {
6627 rkr_frame_plain_geometry(
6628 raw,
6629 atomic_numbers.as_mut_ptr(),
6630 species_mass.as_mut_ptr(),
6631 species_count.as_mut_ptr(),
6632 &mut n_types,
6633 cell.as_mut_ptr(),
6634 angles.as_mut_ptr(),
6635 )
6636 };
6637 assert_eq!(plain, 1, "stock frame should use the plain geometry fill");
6638 assert_eq!(n_types, 2);
6639 assert_eq!(atomic_numbers[0], 29);
6640 assert_eq!(species_count[0], 1);
6641 assert_eq!(atomic_numbers[1], 1);
6642 }
6643 #[test]
6644 fn frame_copy_positions_without_cframe() {
6645 let handle = test_frame_handle();
6646 let n = unsafe { rkr_frame_atom_count(handle) };
6647 assert_eq!(n, 1);
6648 let mut buf = vec![0.0f64; n * 3];
6649 assert_eq!(
6650 unsafe { rkr_frame_copy_positions(handle, buf.as_mut_ptr(), buf.len()) },
6651 note(RKRStatus::RKR_STATUS_SUCCESS)
6652 );
6653 let mut tensor: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
6654 assert_eq!(
6655 unsafe { rkr_frame_positions_dlpack(handle, &mut tensor) },
6656 note(RKRStatus::RKR_STATUS_SUCCESS)
6657 );
6658 assert!(!tensor.is_null());
6659 unsafe { rkr_dlpack_delete(tensor) };
6660 assert_eq!(
6661 unsafe { rkr_frame_copy_velocities(handle, buf.as_mut_ptr(), buf.len()) },
6662 note(RKRStatus::RKR_STATUS_SECTION_ABSENT)
6663 );
6664 unsafe { free_rkr_frame(handle) };
6665 }
6666 #[test]
6667 fn c_read_uses_the_cached_unit_table() {
6668 let text = "\
6669v3
6670{\"con_spec_version\":3,\"con_spec_minor\":1,\"units\":{\"length\":\"angstrom\",\"energy\":\"eV\"}}
667110 10 10
667290 90 90
6673
6674
66751
66761
66771.0
6678H
6679Coordinates of Component 1
66800.5 0.25 0.125 0 7
6681";
6682 let dir = tempfile::tempdir().unwrap();
6683 let path = dir.path().join("v3.con");
6684 std::fs::write(&path, text).unwrap();
6685 let serial = crate::iterators::read_all_frames(&path).unwrap();
6686 let addr = crate::units::cached_unit_table_addr();
6687 let c_path = std::ffi::CString::new(path.to_str().unwrap()).unwrap();
6688 let mut n: usize = 0;
6689 let arr = unsafe { rkr_read_all_frames(c_path.as_ptr(), &mut n) };
6690 assert!(!arr.is_null() && n == 1);
6691 assert_eq!(crate::units::cached_unit_table_addr(), addr);
6692 let nat = unsafe { rkr_frame_atom_count(*arr) };
6693 assert_eq!(nat, serial[0].atom_data.len());
6694 let mut buf = vec![0.0f64; nat * 3];
6695 assert_eq!(
6696 unsafe { rkr_frame_copy_positions(*arr, buf.as_mut_ptr(), buf.len()) },
6697 RKRStatus::RKR_STATUS_SUCCESS
6698 );
6699 assert_eq!(
6700 buf[0].to_bits(),
6701 serial[0].positions.as_f64_row(0)[0].to_bits()
6702 );
6703 unsafe { free_rkr_frame_array(arr, n) };
6704 let bad = dir.path().join("bad.con");
6705 std::fs::write(&bad, text.replace("angstrom", "not-a-unit")).unwrap();
6706 let bad_c = std::ffi::CString::new(bad.to_str().unwrap()).unwrap();
6707 let mut n_bad = 0usize;
6708 assert!(unsafe { rkr_read_all_frames(bad_c.as_ptr(), &mut n_bad) }.is_null());
6709 assert_eq!(crate::units::cached_unit_table_addr(), addr);
6710 }
6711
6712 #[test]
6713 fn read_all_frames_c_abi_tiny() {
6714 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
6715 let mut n: usize = 0;
6716 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
6717 assert!(!arr.is_null() && n >= 1);
6718 let first = unsafe { *arr };
6719 let nat = unsafe { rkr_frame_atom_count(first) };
6720 let mut buf = vec![0.0f64; nat * 3];
6721 assert_eq!(
6722 unsafe { rkr_frame_copy_positions(first, buf.as_mut_ptr(), buf.len()) },
6723 note(RKRStatus::RKR_STATUS_SUCCESS)
6724 );
6725 unsafe { free_rkr_frame_array(arr, n) };
6726 }
6727
6728 #[test]
6729 fn read_all_frames_n_threads_matches_auto() {
6730 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
6731 let mut n_auto: usize = 0;
6732 let mut n_one: usize = 0;
6733 let auto = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_auto, 0) };
6734 let one = unsafe { rkr_read_all_frames_n_threads(path.as_ptr(), &mut n_one, 1) };
6735 assert!(!auto.is_null() && !one.is_null());
6736 assert_eq!(n_auto, n_one);
6737 assert_eq!(n_auto, 1);
6738 let nat_auto = unsafe { rkr_frame_atom_count(*auto) };
6739 let nat_one = unsafe { rkr_frame_atom_count(*one) };
6740 assert_eq!(nat_auto, nat_one);
6741 unsafe { free_rkr_frame_array(auto, n_auto) };
6742 unsafe { free_rkr_frame_array(one, n_one) };
6743 }
6744
6745 #[test]
6746 fn free_rkr_frame_ptr_array_keeps_frames() {
6747 let path = std::ffi::CString::new("resources/test/tiny_cuh2.con").unwrap();
6748 let mut n: usize = 0;
6749 let arr = unsafe { rkr_read_all_frames(path.as_ptr(), &mut n) };
6750 assert!(!arr.is_null() && n >= 1);
6751 let first = unsafe { *arr };
6752 let rest: Vec<*mut RKRConFrame> = (1..n).map(|i| unsafe { *arr.add(i) }).collect();
6754 unsafe { free_rkr_frame_ptr_array(arr, n) };
6755 assert!(unsafe { rkr_frame_atom_count(first) } >= 1);
6756 unsafe { free_rkr_frame(first) };
6757 for h in rest {
6758 if !h.is_null() {
6759 unsafe { free_rkr_frame(h) };
6760 }
6761 }
6762 }
6763 fn test_frame_handle() -> *mut RKRConFrame {
6764 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
6765 builder
6766 .prebox_header("Generated by test")
6767 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
6768 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
6769 Box::into_raw(Box::new(builder.build().unwrap())) as *mut RKRConFrame
6770 }
6771 #[test]
6772 fn header_line_rejects_null_buffer() {
6773 let frame = test_frame_handle();
6774 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
6775 unsafe { free_rkr_frame(frame) };
6776 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
6777 }
6778 #[test]
6779 fn header_line_rejects_empty_buffer() {
6780 let frame = test_frame_handle();
6781 let mut buffer = [0 as c_char; 1];
6782 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
6783 unsafe { free_rkr_frame(frame) };
6784 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
6785 }
6786 #[test]
6787 fn header_line_truncates_and_terminates_buffer() {
6788 let frame = test_frame_handle();
6789 let mut buffer = [0 as c_char; 10];
6790 let status =
6791 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
6792 unsafe { free_rkr_frame(frame) };
6793 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
6794 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
6795 assert_eq!(copied.to_str().unwrap(), "Generated");
6796 }
6797
6798 #[test]
6799 fn pack_rcso_size_query_then_dest() {
6800 let path = std::ffi::CString::new("resources/test/tiny_cuh2_forces.con").unwrap();
6801 let frame = unsafe { rkr_read_nth_frame(path.as_ptr(), 0) };
6802 assert!(!frame.is_null());
6803 let mut need = 0usize;
6804 let st = unsafe { rkr_pack_rcso(frame, std::ptr::null_mut(), 0, &mut need) };
6805 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6806 assert!(need >= 24);
6807 let mut buf = vec![0u8; need];
6808 let mut wrote = 0usize;
6809 let st = unsafe { rkr_pack_rcso(frame, buf.as_mut_ptr(), buf.len(), &mut wrote) };
6810 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6811 assert_eq!(wrote, need);
6812 assert_eq!(&buf[0..4], b"RCSO");
6813 let mut natoms = 0u32;
6814 let st = unsafe { rkr_unpack_rcso_natoms(buf.as_ptr(), buf.len(), &mut natoms) };
6815 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6816 assert_eq!(natoms, unsafe { rkr_frame_atom_count(frame) } as u32);
6817 let mut xyz = vec![0.0f64; natoms as usize * 3];
6818 let st =
6819 unsafe { rkr_unpack_rcso_positions(buf.as_ptr(), buf.len(), xyz.as_mut_ptr(), natoms) };
6820 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6821 let mut frc = vec![0.0f64; natoms as usize * 3];
6822 let st =
6823 unsafe { rkr_unpack_rcso_forces(buf.as_ptr(), buf.len(), frc.as_mut_ptr(), natoms) };
6824 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
6825 unsafe { free_rkr_frame(frame) };
6826 }
6827
6828 fn test_builder_handle() -> *mut RKRConFrameBuilder {
6829 let cell = [10.0, 11.0, 12.0];
6830 let angles = [90.0, 91.0, 92.0];
6831 unsafe {
6832 rkr_frame_new(
6833 cell.as_ptr(),
6834 angles.as_ptr(),
6835 ptr::null(),
6836 ptr::null(),
6837 ptr::null(),
6838 ptr::null(),
6839 )
6840 }
6841 }
6842 fn c_string(s: &str) -> CString {
6843 CString::new(s).unwrap()
6844 }
6845 #[test]
6846 fn builder_set_units_c_abi_stores_the_object() {
6847 let builder = test_builder_handle();
6848 let units = c_string(
6849 r#"{"length":"A","energy":"ev","mass":"amu","time":"(amu*angstrom^2/eV)^0.5"}"#,
6850 );
6851 let status = unsafe { rkr_frame_builder_set_units(builder, units.as_ptr()) };
6852 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
6853 let symbol = c_string("H");
6854 assert_eq!(
6855 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
6856 RKRStatus::RKR_STATUS_SUCCESS
6857 );
6858 let frame = unsafe { rkr_frame_builder_build(builder) };
6859 assert!(!frame.is_null());
6860 let json = unsafe { rkr_frame_metadata_json(frame) };
6861 assert!(!json.is_null());
6862 let text = unsafe { CStr::from_ptr(json) }.to_str().unwrap();
6863 assert!(text.contains(r#""length":"angstrom""#), "{text}");
6864 assert!(text.contains(r#""energy":"eV""#), "{text}");
6865 assert!(
6866 text.contains(r#""time":"(amu*angstrom^2/eV)^0.5""#),
6867 "{text}"
6868 );
6869 unsafe {
6870 rkr_free_string(json);
6871 free_rkr_frame(frame);
6872 }
6873 }
6874 #[test]
6875 fn builder_stamps_con_spec_minor_one_unless_set() {
6876 let builder = test_builder_handle();
6877 let symbol = c_string("H");
6878 assert_eq!(
6879 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
6880 RKRStatus::RKR_STATUS_SUCCESS
6881 );
6882 let frame = unsafe { rkr_frame_builder_build(builder) };
6883 let json = unsafe { rkr_frame_metadata_json(frame) };
6884 let text = unsafe { CStr::from_ptr(json) }
6885 .to_str()
6886 .unwrap()
6887 .to_string();
6888 assert!(text.contains(r#""con_spec_minor":1"#), "{text}");
6889 unsafe {
6890 rkr_free_string(json);
6891 free_rkr_frame(frame);
6892 }
6893
6894 let builder = test_builder_handle();
6895 assert_eq!(
6896 unsafe { rkr_frame_builder_set_con_spec_minor(builder, 4) },
6897 RKRStatus::RKR_STATUS_SUCCESS
6898 );
6899 let symbol = c_string("H");
6900 assert_eq!(
6901 unsafe { rkr_frame_add_atom(builder, symbol.as_ptr(), 0.0, 0.0, 0.0, false, 0, 1.0) },
6902 RKRStatus::RKR_STATUS_SUCCESS
6903 );
6904 let frame = unsafe { rkr_frame_builder_build(builder) };
6905 let json = unsafe { rkr_frame_metadata_json(frame) };
6906 let text = unsafe { CStr::from_ptr(json) }
6907 .to_str()
6908 .unwrap()
6909 .to_string();
6910 assert!(text.contains(r#""con_spec_minor":4"#), "{text}");
6911 unsafe {
6912 rkr_free_string(json);
6913 free_rkr_frame(frame);
6914 }
6915 }
6916 unsafe fn assert_single_atom(
6917 frame: *mut RKRConFrame,
6918 fixed: [bool; 3],
6919 velocity: Option<[f64; 3]>,
6920 forces: Option<[f64; 3]>,
6921 ) {
6922 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
6923 assert!(!c_frame.is_null());
6924 let c_frame_ref = unsafe { &*c_frame };
6925 assert_eq!(c_frame_ref.num_atoms, 1);
6926 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
6927 assert_eq!(c_frame_ref.has_forces, forces.is_some());
6928 let atom = unsafe { &*c_frame_ref.atoms };
6929 assert_eq!(atom.fixed_x, fixed[0]);
6930 assert_eq!(atom.fixed_y, fixed[1]);
6931 assert_eq!(atom.fixed_z, fixed[2]);
6932 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
6933 assert_eq!(atom.has_velocity, velocity.is_some());
6934 assert_eq!(atom.has_forces, forces.is_some());
6935 if let Some([vx, vy, vz]) = velocity {
6936 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
6937 }
6938 if let Some([fx, fy, fz]) = forces {
6939 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
6940 }
6941 unsafe { free_c_frame(c_frame) };
6942 unsafe { free_rkr_frame(frame) };
6943 }
6944 #[test]
6945 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
6946 let builder = test_builder_handle();
6947 let symbol = c_string("Cu");
6948 let status = unsafe {
6949 rkr_frame_add_atom_with_fixed_mask(
6950 builder,
6951 symbol.as_ptr(),
6952 1.0,
6953 2.0,
6954 3.0,
6955 true,
6956 false,
6957 true,
6958 7,
6959 63.546,
6960 )
6961 };
6962 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
6963 let frame = unsafe { rkr_frame_builder_build(builder) };
6964 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
6965 }
6966 #[test]
6967 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
6968 let builder = test_builder_handle();
6969 let symbol = c_string("H");
6970 let status = unsafe {
6971 rkr_frame_add_atom_with_velocity_fixed_mask(
6972 builder,
6973 symbol.as_ptr(),
6974 1.0,
6975 2.0,
6976 3.0,
6977 false,
6978 true,
6979 false,
6980 9,
6981 1.008,
6982 0.1,
6983 0.2,
6984 0.3,
6985 )
6986 };
6987 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
6988 let frame = unsafe { rkr_frame_builder_build(builder) };
6989 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
6990 }
6991 #[test]
6992 fn builder_preserves_fixed_mask_for_atom_with_forces() {
6993 let builder = test_builder_handle();
6994 let symbol = c_string("O");
6995 let status = unsafe {
6996 rkr_frame_add_atom_with_forces_fixed_mask(
6997 builder,
6998 symbol.as_ptr(),
6999 1.0,
7000 2.0,
7001 3.0,
7002 true,
7003 true,
7004 false,
7005 11,
7006 15.999,
7007 -0.1,
7008 -0.2,
7009 -0.3,
7010 )
7011 };
7012 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
7013 let frame = unsafe { rkr_frame_builder_build(builder) };
7014 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
7015 }
7016 #[test]
7017 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
7018 let builder = test_builder_handle();
7019 let symbol = c_string("N");
7020 let status = unsafe {
7021 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
7022 builder,
7023 symbol.as_ptr(),
7024 1.0,
7025 2.0,
7026 3.0,
7027 false,
7028 true,
7029 true,
7030 13,
7031 14.007,
7032 0.4,
7033 0.5,
7034 0.6,
7035 -0.4,
7036 -0.5,
7037 -0.6,
7038 )
7039 };
7040 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
7041 let frame = unsafe { rkr_frame_builder_build(builder) };
7042 unsafe {
7043 assert_single_atom(
7044 frame,
7045 [false, true, true],
7046 Some([0.4, 0.5, 0.6]),
7047 Some([-0.4, -0.5, -0.6]),
7048 )
7049 };
7050 }
7051 #[test]
7052 fn builder_bool_fixed_functions_set_all_axes_together() {
7053 let builder = test_builder_handle();
7054 let cu = c_string("Cu");
7055 let h = c_string("H");
7056 let atom_status =
7057 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
7058 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
7059 let velocity_status = unsafe {
7060 rkr_frame_add_atom_with_velocity(
7061 builder,
7062 h.as_ptr(),
7063 4.0,
7064 5.0,
7065 6.0,
7066 false,
7067 2,
7068 1.008,
7069 0.7,
7070 0.8,
7071 0.9,
7072 )
7073 };
7074 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
7075 let frame = unsafe { rkr_frame_builder_build(builder) };
7076 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
7077 assert!(!c_frame.is_null());
7078 let c_frame_ref = unsafe { &*c_frame };
7079 assert_eq!(c_frame_ref.num_atoms, 2);
7080 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
7081 assert_eq!(
7082 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
7083 [true, true, true]
7084 );
7085 assert_eq!(
7086 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
7087 [false, false, false]
7088 );
7089 unsafe { free_c_frame(c_frame) };
7090 unsafe { free_rkr_frame(frame) };
7091 }
7092 #[test]
7093 fn status_message_returns_static_strings_for_all_status_values() {
7094 let cases = [
7095 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
7096 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
7097 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
7098 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
7099 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
7100 (
7101 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
7102 "index out of bounds",
7103 ),
7104 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
7105 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
7106 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
7107 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
7108 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
7109 (
7110 RKRStatus::RKR_STATUS_FEATURE_DISABLED,
7111 "feature disabled in this build",
7112 ),
7113 ];
7114 for (status, expected) in cases {
7115 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
7116 assert_eq!(message.to_str().unwrap(), expected);
7117 }
7118 }
7119 unsafe fn assert_dlpack_cpu_float(
7123 t: *mut RKRDLManagedTensorVersioned,
7124 expect_ndim: i32,
7125 expect_shape: &[i64],
7126 expect_bits: u8,
7127 ) {
7128 assert!(!t.is_null());
7129 let dl = unsafe { &(*t).dl_tensor };
7130 assert_eq!(dl.ndim, expect_ndim);
7131 let shape = unsafe { std::slice::from_raw_parts(dl.shape, expect_ndim as usize) };
7132 assert_eq!(shape, expect_shape);
7133 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
7134 assert_eq!(dl.dtype.bits, expect_bits);
7135 assert_eq!(dl.dtype.lanes, 1);
7136 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
7137 assert!(!dl.data.is_null());
7138 }
7139
7140 #[test]
7141 fn frame_optional_section_dlpack_present_and_absent() {
7142 let handle = test_frame_handle();
7144 let mut t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7145 assert_eq!(
7146 unsafe { rkr_frame_velocities_dlpack(handle, &mut t) },
7147 note(RKRStatus::RKR_STATUS_SECTION_ABSENT)
7148 );
7149 assert!(t.is_null());
7150 assert_eq!(
7151 unsafe { rkr_frame_forces_dlpack(handle, &mut t) },
7152 note(RKRStatus::RKR_STATUS_SECTION_ABSENT)
7153 );
7154 assert_eq!(
7155 unsafe { rkr_frame_atom_energies_dlpack(handle, &mut t) },
7156 note(RKRStatus::RKR_STATUS_SECTION_ABSENT)
7157 );
7158 unsafe { free_rkr_frame(handle) };
7159
7160 let path = CString::new("resources/test/tiny_cuh2.convel").unwrap();
7162 let fr = unsafe { rkr_read_first_frame(path.as_ptr()) };
7163 assert!(!fr.is_null());
7164 let n = unsafe { rkr_frame_atom_count(fr) } as i64;
7165 assert!(n > 0);
7166 let mut vel: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7167 let st = unsafe { rkr_frame_velocities_dlpack(fr, &mut vel) };
7168 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
7169 unsafe {
7170 assert_dlpack_cpu_float(vel, 2, &[n, 3], 64);
7171 rkr_dlpack_delete(vel);
7172 free_rkr_frame(fr);
7173 }
7174
7175 let cell = [10.0f64; 3];
7177 let ang = [90.0f64; 3];
7178 let b = unsafe {
7179 rkr_frame_new(
7180 cell.as_ptr(),
7181 ang.as_ptr(),
7182 std::ptr::null(),
7183 std::ptr::null(),
7184 std::ptr::null(),
7185 std::ptr::null(),
7186 )
7187 };
7188 assert!(!b.is_null());
7189 let sym = CString::new("H").unwrap();
7190 unsafe {
7191 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
7192 b,
7193 sym.as_ptr(),
7194 0.0,
7195 0.0,
7196 0.0,
7197 false,
7198 false,
7199 false,
7200 0,
7201 1.0,
7202 0.1,
7203 0.0,
7204 0.0,
7205 0.0,
7206 0.0,
7207 -1.0,
7208 );
7209 rkr_frame_builder_set_last_energy(b, -0.5);
7210 }
7211 let built = unsafe { rkr_frame_builder_build(b) };
7212 assert!(!built.is_null());
7213 let n_built = unsafe { rkr_frame_atom_count(built) } as i64;
7214 let mut frc: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7215 let mut eng: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7216 assert_eq!(
7217 unsafe { rkr_frame_forces_dlpack(built, &mut frc) },
7218 note(RKRStatus::RKR_STATUS_SUCCESS)
7219 );
7220 unsafe { assert_dlpack_cpu_float(frc, 2, &[n_built, 3], 64) };
7221 assert_eq!(
7222 unsafe { rkr_frame_atom_energies_dlpack(built, &mut eng) },
7223 note(RKRStatus::RKR_STATUS_SUCCESS)
7224 );
7225 unsafe { assert_dlpack_cpu_float(eng, 1, &[n_built], 64) };
7226 let opts32 = RKRDlpackExportOptions {
7228 dtype: RKRDLDataType {
7229 code: rkr_dl_type_code::RKR_DL_FLOAT,
7230 bits: 32,
7231 lanes: 1,
7232 },
7233 device: RKRDLDevice {
7234 device_type: rkr_dl_device_type::RKR_DL_CPU,
7235 device_id: 0,
7236 },
7237 };
7238 let mut pos32: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7239 assert_eq!(
7240 unsafe { rkr_frame_positions_dlpack_ex(built, &opts32, &mut pos32) },
7241 note(RKRStatus::RKR_STATUS_SUCCESS)
7242 );
7243 unsafe {
7244 assert_dlpack_cpu_float(pos32, 2, &[n_built, 3], 32);
7245 rkr_dlpack_delete(pos32);
7246 }
7247 let cuda_dev = RKRDlpackExportOptions {
7250 dtype: RKRDLDataType {
7251 code: rkr_dl_type_code::RKR_DL_FLOAT,
7252 bits: 64,
7253 lanes: 1,
7254 },
7255 device: RKRDLDevice {
7256 device_type: rkr_dl_device_type::RKR_DL_CUDA,
7257 device_id: 0,
7258 },
7259 };
7260 let mut junk: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7261 #[cfg(not(feature = "cuda"))]
7262 assert_eq!(
7263 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
7264 note(RKRStatus::RKR_STATUS_FEATURE_DISABLED)
7265 );
7266 #[cfg(feature = "cuda")]
7267 {
7268 assert_eq!(
7269 unsafe { rkr_frame_positions_dlpack_ex(built, &cuda_dev, &mut junk) },
7270 note(RKRStatus::RKR_STATUS_SUCCESS)
7271 );
7272 assert!(!junk.is_null());
7273 let dl = unsafe { &(*junk).dl_tensor };
7274 assert_eq!(dl.device.device_type, dlpk::sys::DLDeviceType::kDLCUDA);
7275 assert!(!dl.data.is_null());
7276 unsafe { rkr_dlpack_delete(junk) };
7277 let mut as_cuda: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7279 assert_eq!(
7280 unsafe {
7281 rkr_frame_positions_as_dlpack(
7282 built,
7283 rkr_dl_device_type::RKR_DL_CUDA,
7284 0,
7285 0,
7286 1,
7287 0,
7288 &mut as_cuda,
7289 )
7290 },
7291 note(RKRStatus::RKR_STATUS_SUCCESS)
7292 );
7293 assert!(!as_cuda.is_null());
7294 unsafe {
7295 assert_eq!(
7296 (*as_cuda).dl_tensor.device.device_type,
7297 dlpk::sys::DLDeviceType::kDLCUDA
7298 );
7299 rkr_dlpack_delete(as_cuda);
7300 }
7301 }
7302 let bad_bits = RKRDlpackExportOptions {
7304 dtype: RKRDLDataType {
7305 code: rkr_dl_type_code::RKR_DL_FLOAT,
7306 bits: 16,
7307 lanes: 1,
7308 },
7309 device: RKRDLDevice {
7310 device_type: rkr_dl_device_type::RKR_DL_CPU,
7311 device_id: 0,
7312 },
7313 };
7314 assert_eq!(
7315 unsafe { rkr_frame_positions_dlpack_ex(built, &bad_bits, &mut junk) },
7316 note(RKRStatus::RKR_STATUS_VALIDATION_ERROR)
7317 );
7318 let mut as_t: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7320 assert_eq!(
7321 unsafe {
7322 rkr_frame_positions_as_dlpack(
7323 built,
7324 rkr_dl_device_type::RKR_DL_CPU,
7325 0,
7326 0,
7327 1,
7328 0,
7329 &mut as_t,
7330 )
7331 },
7332 note(RKRStatus::RKR_STATUS_SUCCESS)
7333 );
7334 unsafe {
7335 assert_dlpack_cpu_float(as_t, 2, &[n_built, 3], 64);
7336 }
7337 {
7339 let fr = unsafe { &mut *(built as *mut ConFrame) };
7340 for a in fr.atom_data.iter_mut() {
7341 a.set_x(-99.0);
7342 a.set_y(-99.0);
7343 a.set_z(-99.0);
7344 }
7345 }
7346 assert_eq!(
7347 unsafe { rkr_frame_positions_from_dlpack(built, as_t) },
7348 note(RKRStatus::RKR_STATUS_SUCCESS)
7349 );
7350 {
7351 let fr = unsafe { &*(built as *const ConFrame) };
7352 assert!((fr.atom_data[0].x() - 0.0).abs() < 1e-12);
7353 assert!((fr.atom_data[0].y() - 0.0).abs() < 1e-12);
7354 assert!((fr.atom_data[0].z() - 0.0).abs() < 1e-12);
7355 }
7356 let mut again: *mut RKRDLManagedTensorVersioned = std::ptr::null_mut();
7358 assert_eq!(
7359 unsafe {
7360 rkr_frame_positions_as_dlpack(
7361 built,
7362 rkr_dl_device_type::RKR_DL_CPU,
7363 0,
7364 0,
7365 1,
7366 0,
7367 &mut again,
7368 )
7369 },
7370 note(RKRStatus::RKR_STATUS_SUCCESS)
7371 );
7372 unsafe {
7373 let dl = &(*again).dl_tensor;
7374 let data = std::slice::from_raw_parts(dl.data as *const f64, 3);
7375 assert!((data[0] - 0.0).abs() < 1e-12);
7376 assert!((data[1] - 0.0).abs() < 1e-12);
7377 assert!((data[2] - 0.0).abs() < 1e-12);
7378 rkr_dlpack_delete(again);
7379 rkr_dlpack_delete(as_t);
7380 rkr_dlpack_delete(frc);
7381 rkr_dlpack_delete(eng);
7382 free_rkr_frame(built);
7383 }
7384 }
7385
7386 #[test]
7387 fn ffi_positions_dlpack_round_trip() {
7388 let handle = test_builder_handle();
7389 let sym = c_string("Cu");
7390 unsafe {
7391 rkr_frame_add_atom_full(
7392 handle,
7393 sym.as_ptr(),
7394 1.0,
7395 2.0,
7396 3.0,
7397 false,
7398 false,
7399 false,
7400 7,
7401 63.5,
7402 ptr::null(),
7403 ptr::null(),
7404 )
7405 };
7406 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
7407 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
7408 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
7409 assert!(!t.is_null());
7410 let dl = unsafe { &(*t).dl_tensor };
7412 assert_eq!(dl.ndim, 2);
7413 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
7414 assert_eq!(shape, &[1, 3]);
7415 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
7416 assert_eq!(dl.dtype.bits, 64);
7417 assert_eq!(dl.dtype.lanes, 1);
7418 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
7419 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
7420 assert_eq!(data, &[1.0, 2.0, 3.0]);
7421 let deleter = unsafe { (*t).deleter };
7423 if let Some(del) = deleter {
7424 unsafe { del(t) };
7425 }
7426 unsafe { free_rkr_frame_builder(handle) };
7427 }
7428 #[test]
7429 fn ffi_velocities_dlpack_section_absent() {
7430 let handle = test_builder_handle();
7431 let sym = c_string("Cu");
7432 unsafe {
7433 rkr_frame_add_atom_full(
7434 handle,
7435 sym.as_ptr(),
7436 0.0,
7437 0.0,
7438 0.0,
7439 false,
7440 false,
7441 false,
7442 0,
7443 63.5,
7444 ptr::null(),
7445 ptr::null(),
7446 )
7447 };
7448 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
7449 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
7450 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
7451 assert!(t.is_null());
7452 unsafe { free_rkr_frame_builder(handle) };
7453 }
7454 #[test]
7455 fn ffi_dlpack_null_handle_rejects() {
7456 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
7457 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
7458 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
7459 assert!(t.is_null());
7460 }
7461 #[cfg(feature = "chemfiles")]
7462 #[test]
7463 fn rkr_frame_select_finds_oxygen() {
7464 use crate::types::ConFrameBuilder;
7465 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
7466 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
7467 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
7468 let frame = b.build().unwrap();
7469 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
7470 let sel = CString::new("name O").unwrap();
7471 let mut out: *mut RKRSelectionResult = ptr::null_mut();
7472 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
7473 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
7474 assert!(!out.is_null());
7475 let n = unsafe { rkr_selection_result_match_count(out) };
7476 assert_eq!(n, 1);
7477 let mut atoms = [u64::MAX; 4];
7478 let mut size = 0u32;
7479 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
7480 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
7481 assert_eq!(size, 1);
7482 assert_eq!(atoms[0], 0);
7483 unsafe {
7484 rkr_selection_result_free(out);
7485 free_rkr_frame(frame_ptr);
7486 }
7487 }
7488 #[cfg(feature = "chemfiles")]
7490 #[test]
7491 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
7492 use crate::types::{Bond, ConFrameBuilder};
7493 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
7495 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
7496 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
7497 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
7498 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
7499 let mut frame = b.build().unwrap();
7500 let id_to = |id: u64| {
7501 frame
7502 .atom_data
7503 .iter()
7504 .position(|a| a.atom_id == id)
7505 .unwrap() as u32
7506 };
7507 frame.header.set_bonds(&[
7508 Bond::new(id_to(0), id_to(1)),
7509 Bond::new(id_to(1), id_to(2)),
7510 Bond::new(id_to(2), id_to(3)),
7511 ]);
7512 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
7513 let run = |sel: &str| -> (u64, u32) {
7514 let csel = CString::new(sel).unwrap();
7515 let mut out: *mut RKRSelectionResult = ptr::null_mut();
7516 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
7517 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
7518 let n = unsafe { rkr_selection_result_match_count(out) };
7519 let ctx = unsafe { rkr_selection_result_context_size(out) };
7520 unsafe { rkr_selection_result_free(out) };
7521 (n, ctx)
7522 };
7523 assert_eq!(run("bonds: all"), (3, 2));
7524 assert_eq!(run("angles: all"), (2, 3));
7525 assert_eq!(run("dihedrals: all"), (1, 4));
7526 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
7527 assert_eq!(
7528 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
7529 run("bonds: type(#1) H and name(#2) O").0
7530 );
7531 unsafe { free_rkr_frame(frame_ptr) };
7532 }
7533 #[cfg(feature = "metatensor")]
7534 fn assert_mts_block_shape(block: *mut metatensor::c_api::mts_block_t, n: usize, props: usize) {
7535 assert!(!block.is_null());
7536 let mut array = unsafe { std::mem::zeroed::<metatensor::c_api::mts_array_t>() };
7537 let status = unsafe { metatensor::c_api::mts_block_data(block, &mut array) };
7538 assert_eq!(status, metatensor::c_api::MTS_SUCCESS);
7539 let shape_fn = array
7540 .shape
7541 .expect("mts_array_t.shape from metatensor C API");
7542 let mut shape_ptr: *const usize = std::ptr::null();
7543 let mut shape_count: usize = 0;
7544 let st_shape = unsafe { shape_fn(array.ptr, &mut shape_ptr, &mut shape_count) };
7545 assert_eq!(st_shape, metatensor::c_api::MTS_SUCCESS);
7546 assert_eq!(shape_count, 2);
7547 let shape = unsafe { std::slice::from_raw_parts(shape_ptr, shape_count) };
7548 assert_eq!(shape[0], n);
7549 assert_eq!(shape[1], props);
7550 let samples = unsafe { metatensor::c_api::mts_block_labels(block, 0) };
7551 let prop_lab = unsafe { metatensor::c_api::mts_block_labels(block, 1) };
7552 assert!(!samples.is_null() && !prop_lab.is_null());
7553 }
7554 #[cfg(feature = "metatensor")]
7555 #[test]
7556 fn metatensor_positions_via_c_abi() {
7557 let handle = test_frame_handle();
7558 let mut out: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
7559 let st = unsafe { rkr_frame_metatensor_positions_block(handle, &mut out) };
7560 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
7561 assert_mts_block_shape(out, 1, 3);
7562 unsafe { rkr_mts_block_free(out) };
7563 for (name, export) in [
7564 (
7565 "velocities",
7566 rkr_frame_metatensor_velocities_block
7567 as unsafe extern "C" fn(
7568 *const RKRConFrame,
7569 *mut *mut metatensor::c_api::mts_block_t,
7570 ) -> RKRStatus,
7571 ),
7572 (
7573 "forces",
7574 rkr_frame_metatensor_forces_block
7575 as unsafe extern "C" fn(
7576 *const RKRConFrame,
7577 *mut *mut metatensor::c_api::mts_block_t,
7578 ) -> RKRStatus,
7579 ),
7580 (
7581 "atom_energies",
7582 rkr_frame_metatensor_atom_energies_block
7583 as unsafe extern "C" fn(
7584 *const RKRConFrame,
7585 *mut *mut metatensor::c_api::mts_block_t,
7586 ) -> RKRStatus,
7587 ),
7588 ] {
7589 let mut o: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
7590 let st_abs = unsafe { export(handle, &mut o) };
7591 assert_eq!(
7592 st_abs,
7593 RKRStatus::RKR_STATUS_SECTION_ABSENT,
7594 "{name} must be SECTION_ABSENT on minimal test frame"
7595 );
7596 assert!(o.is_null());
7597 }
7598 unsafe { free_rkr_frame(handle) };
7599 }
7600
7601 #[cfg(feature = "metatensor")]
7602 #[test]
7603 fn metatensor_optional_sections_via_c_abi() {
7604 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
7606 builder.add_atom("H", 0.0, 0.0, 0.0, [false; 3], 1, 1.0);
7607 builder.add_atom("O", 1.0, 0.0, 0.0, [false; 3], 2, 16.0);
7608 builder.set_atom_velocity(0, [0.1, 0.2, 0.3]).unwrap();
7609 builder.set_atom_velocity(1, [0.0, 0.1, 0.0]).unwrap();
7610 builder.set_atom_force(0, [1.0, 0.0, 0.0]).unwrap();
7611 builder.set_atom_force(1, [0.0, 1.0, 0.0]).unwrap();
7612 builder.set_atom_energy(0, -0.5).unwrap();
7613 builder.set_atom_energy(1, -1.0).unwrap();
7614 let frame = builder.build().unwrap();
7615 let handle = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
7616 let mut pos: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
7617 assert_eq!(
7618 unsafe { rkr_frame_metatensor_positions_block(handle, &mut pos) },
7619 note(RKRStatus::RKR_STATUS_SUCCESS)
7620 );
7621 assert_mts_block_shape(pos, 2, 3);
7622 unsafe { rkr_mts_block_free(pos) };
7623 let mut vel: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
7624 assert_eq!(
7625 unsafe { rkr_frame_metatensor_velocities_block(handle, &mut vel) },
7626 note(RKRStatus::RKR_STATUS_SUCCESS)
7627 );
7628 assert_mts_block_shape(vel, 2, 3);
7629 unsafe { rkr_mts_block_free(vel) };
7630 let mut frc: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
7631 assert_eq!(
7632 unsafe { rkr_frame_metatensor_forces_block(handle, &mut frc) },
7633 note(RKRStatus::RKR_STATUS_SUCCESS)
7634 );
7635 assert_mts_block_shape(frc, 2, 3);
7636 unsafe { rkr_mts_block_free(frc) };
7637 let mut eng: *mut metatensor::c_api::mts_block_t = std::ptr::null_mut();
7638 assert_eq!(
7639 unsafe { rkr_frame_metatensor_atom_energies_block(handle, &mut eng) },
7640 note(RKRStatus::RKR_STATUS_SUCCESS)
7641 );
7642 assert_mts_block_shape(eng, 2, 1);
7643 unsafe { rkr_mts_block_free(eng) };
7644 unsafe { free_rkr_frame(handle) };
7645 }
7646
7647 #[test]
7648 fn string_iterator_yields_frames_from_buffer() {
7649 let text =
7650 std::fs::read_to_string("resources/test/tiny_cuh2.con").expect("fixture tiny_cuh2.con");
7651 let c_text = CString::new(text.as_str()).unwrap();
7652 let it = unsafe { read_con_string_iterator(c_text.as_ptr()) };
7653 assert!(!it.is_null());
7654 let mut n = 0usize;
7655 loop {
7656 let fr = unsafe { con_frame_iterator_next(it) };
7657 if fr.is_null() {
7658 break;
7659 }
7660 n += 1;
7661 unsafe { free_rkr_frame(fr) };
7662 }
7663 unsafe { free_con_frame_iterator(it) };
7664 assert!(n >= 1, "string iterator should yield >=1 frame");
7665
7666 let bytes = text.as_bytes();
7667 let it2 = unsafe { read_con_buffer_iterator(bytes.as_ptr(), bytes.len()) };
7668 assert!(!it2.is_null());
7669 let fr2 = unsafe { con_frame_iterator_next(it2) };
7670 assert!(!fr2.is_null());
7671 unsafe {
7672 free_rkr_frame(fr2);
7673 free_con_frame_iterator(it2);
7674 }
7675 }
7676
7677 #[test]
7678 fn file_iterator_reads_gzip_when_present() {
7679 use flate2::Compression;
7680 use flate2::write::GzEncoder;
7681 use std::io::Write;
7682 let plain = std::fs::read("resources/test/tiny_cuh2.con").expect("fixture");
7683 let dir = tempfile::tempdir().expect("tempdir");
7684 let gz_path = dir.path().join("tiny_cuh2.con.gz");
7685 {
7686 let f = std::fs::File::create(&gz_path).unwrap();
7687 let mut enc = GzEncoder::new(f, Compression::default());
7688 enc.write_all(&plain).unwrap();
7689 enc.finish().unwrap();
7690 }
7691 let c_path = CString::new(gz_path.to_str().unwrap()).unwrap();
7692 let it = unsafe { read_con_file_iterator(c_path.as_ptr()) };
7693 assert!(
7694 !it.is_null(),
7695 "path iterator must decompress .con.gz transparently"
7696 );
7697 let fr = unsafe { con_frame_iterator_next(it) };
7698 assert!(!fr.is_null());
7699 let n = unsafe { rkr_frame_atom_count(fr) };
7700 assert!(n > 0);
7701 unsafe {
7702 free_rkr_frame(fr);
7703 free_con_frame_iterator(it);
7704 }
7705 }
7706
7707 #[test]
7708 fn mpi_read_failure_keeps_the_file_line_and_frame() {
7709 let located = crate::error::LocatedParseError::new(
7710 crate::error::ParseError::InvalidMetadataJson("{not-json".into()),
7711 14,
7712 1,
7713 );
7714 rkr_clear_error();
7715 record_mpi_failure(&located);
7716 let err = rkr_last_error();
7717 assert!(!err.is_null());
7718 let err = unsafe { &*err };
7719 assert_eq!(err.line, 14);
7720 assert_eq!(err.frame_index, 1);
7721 assert_eq!(err.code, RKRStatus::RKR_STATUS_INVALID_JSON as i32);
7722 let message = unsafe { CStr::from_ptr(err.message) }.to_string_lossy();
7723 assert!(message.contains("InvalidMetadataJson"), "{message}");
7724
7725 let io = std::io::Error::new(std::io::ErrorKind::NotFound, "missing file");
7726 record_mpi_failure(&io);
7727 let err = unsafe { &*rkr_last_error() };
7728 assert_eq!(err.code, RKRStatus::RKR_STATUS_IO_ERROR as i32);
7729 assert_eq!(err.line, 0);
7730 assert_eq!(err.frame_index, 0);
7731 let message = unsafe { CStr::from_ptr(err.message) }.to_string_lossy();
7732 assert!(message.contains("missing file"), "{message}");
7733 }
7734}