Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,20 +143,19 @@ endif()
#
set(SRC_FILES
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolbng.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolgraphics.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolboxes.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolcmd.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolcomparts.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolfilament.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolgraphics.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolmolec.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolport.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smollattice.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolreact.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolrule.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolsim.cpp
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolsurface.c
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolwall.c
)
${CMAKE_SOURCE_DIR}/source/Smoldyn/smolwall.c)

set(MAIN_FILES ${CMAKE_SOURCE_DIR}/source/Smoldyn/smoldyn.cpp)

Expand Down
3 changes: 3 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[workspace]
resolver = "2"
members = ["libsmoldyn", "smoldyn-rs"]
12 changes: 12 additions & 0 deletions rust/libsmoldyn/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "libsmoldyn"
version = "0.27.1"
edition = "2024"

[dependencies]
cxx = { version = "1.0.202", features = ["c++17"] }
tracing = "0.1.44"

[build-dependencies]
cmake = "0.1.58"
cxx-build = "1.0.202"
23 changes: 23 additions & 0 deletions rust/libsmoldyn/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use cmake::Config;

fn main() {
let dst = Config::new("../..")
.define("OPTION_PYTHON", "OFF")
.define("OPTION_STATIC", "ON")
.define("OPTION_USE_OPENGL", "OFF")
.define("OPTION_USE_LIBTIFF", "OFF")
.build();

cxx_build::bridge("src/lib.rs")
.include("../../source")
.include("include")
.include(format!("{}/build", dst.display()))
.compile("simulation");

println!("cargo:rerun-if-changed=include/smolrs.h");
// cxx_build's rerun-if-changed replaces cargo's default, so list the C/C++ sources too.
println!("cargo:rerun-if-changed=../../source");
println!("cargo:rerun-if-changed=../../CMakeLists.txt");
println!("cargo:rustc-link-search=native={}/build", dst.display());
println!("cargo:rustc-link-lib=static=smoldyn_static");
}
45 changes: 45 additions & 0 deletions rust/libsmoldyn/include/smolrs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Read-only accessors for `simstruct` fields that libsmoldyn.h has no getter for.
//
// Rust sees `simstruct` as an opaque type, so reading fields goes through these
// inline functions; that keeps the struct layout in C++ where it is defined.
#pragma once

#include <cmath>
#include <cstdlib>
#include <cstring>

#include "Smoldyn/smoldyn.h"

inline int smolrs_dim(const simstruct &sim) { return sim.dim; }
inline double smolrs_time(const simstruct &sim) { return sim.time; }
inline double smolrs_time_start(const simstruct &sim) { return sim.tmin; }
inline double smolrs_time_stop(const simstruct &sim) { return sim.tmax; }
inline double smolrs_time_step(const simstruct &sim) { return sim.dt; }

// Position of the low (highside=0) or high (highside=1) wall along dimension d.
// Returns NaN if the walls are not allocated or d/highside are out of range.
inline double smolrs_wall_pos(const simstruct &sim, int d, int highside) {
if (!sim.wlist || d < 0 || d >= sim.dim || highside < 0 || highside > 1)
return NAN;
return sim.wlist[2 * d + highside]->pos;
}

// Length of the configuration file path ("" for simulations not loaded from a file).
inline size_t smolrs_filepath_len(const simstruct &sim) {
return sim.filepath ? strlen(sim.filepath) : 0;
}

// Number of species slots, including the "empty" species at index 0.
inline int smolrs_nspecies(const simstruct &sim) {
return sim.mols ? sim.mols->nspecies : 0;
}

// Name of species i, or nullptr if out of range. Owned by the simulation.
inline const char *smolrs_species_name(const simstruct &sim, int i) {
if (!sim.mols || i < 0 || i >= sim.mols->nspecies)
return nullptr;
return sim.mols->spname[i];
}

// Free an array that libsmoldyn allocated with calloc (smolGetOutputData).
inline void smolrs_free_doubles(double *array) { free(array); }
81 changes: 81 additions & 0 deletions rust/libsmoldyn/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::ffi::{CStr, c_char};
use std::fmt;

use crate::ffi::{self, ErrorCode};

pub(crate) const STRCHARLONG: usize = 4096;

pub type SmolResult<T> = Result<T, SmolError>;

#[derive(Debug, Clone)]
pub enum SmolError {
Smoldyn {
code: ErrorCode,
function: String,
message: String,
},
InvalidArgument(String),
}

impl SmolError {
pub(crate) fn last(fallback: ErrorCode) -> Self {
let mut function = [0 as c_char; STRCHARLONG];
let mut message = [0 as c_char; STRCHARLONG];
let code = unsafe { ffi::smolGetError(function.as_mut_ptr(), message.as_mut_ptr(), 1) };
let code = if code == ErrorCode::Ok {
fallback
} else {
code
};
let to_string = |buf: &[c_char]| {
unsafe { CStr::from_ptr(buf.as_ptr()) }
.to_string_lossy()
.into_owned()
};
SmolError::Smoldyn {
code,
function: to_string(&function),
message: to_string(&message),
}
}
}

impl fmt::Display for SmolError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SmolError::Smoldyn {
code,
function,
message,
} => write!(f, "smoldyn error {code:?} in {function}: {message}"),
SmolError::InvalidArgument(msg) => write!(f, "invalid argument: {msg}"),
}
}
}

impl std::error::Error for SmolError {}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Status {
Ok,
Notify,
Warning,
}

pub(crate) fn check(code: ErrorCode) -> SmolResult<Status> {
match code {
ErrorCode::Ok => Ok(Status::Ok),
ErrorCode::Notify => {
// clear it, otherwise libsmoldyn keeps returning ECnotify from later calls.
let note = SmolError::last(code);
tracing::debug!("{note}");
Ok(Status::Notify)
}
ErrorCode::Warning => {
let err = SmolError::last(code);
tracing::warn!("{err}");
Ok(Status::Warning)
}
_ => Err(SmolError::last(code)),
}
}
Loading
Loading