use crate::error::Result;
pub mod cpu;
#[cfg(feature = "cuda")]
pub mod cuda;
pub use cpu::CpuDevice;
#[cfg(feature = "cuda")]
pub use cuda::CudaDevice;
pub trait Device: Send + Sync {
fn name(&self) -> &'static str;
fn max_spins(&self) -> usize {
usize::MAX
}
fn is_available(&self) -> bool;
fn step_llg_rk4(
&self,
spins: &mut [[f64; 3]],
h_eff: &[[f64; 3]],
alpha: f64,
dt: f64,
) -> Result<()>;
fn step_llg_rk4_multi(
&self,
spins: &mut [[f64; 3]],
h_eff: &[[f64; 3]],
alpha: f64,
dt: f64,
n_steps: usize,
) -> Result<()>;
fn zeeman_energy(&self, spins: &[[f64; 3]], h: &[[f64; 3]], ms: f64) -> Result<f64>;
}
pub fn available_devices() -> Vec<Box<dyn Device>> {
#[cfg(feature = "cuda")]
{
let mut devices: Vec<Box<dyn Device>> = vec![Box::new(CpuDevice::new())];
if let Ok(cuda) = CudaDevice::new() {
devices.push(Box::new(cuda));
}
devices
}
#[cfg(not(feature = "cuda"))]
{
let devices: Vec<Box<dyn Device>> = vec![Box::new(CpuDevice::new())];
devices
}
}
pub fn select_best_device() -> Box<dyn Device> {
#[cfg(feature = "cuda")]
{
if let Ok(cuda) = CudaDevice::new() {
if cuda.is_available() {
return Box::new(cuda);
}
}
}
Box::new(CpuDevice::new())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_device_trait_object_construction() {
let cpu: Box<dyn Device> = Box::new(CpuDevice::new());
assert_eq!(cpu.name(), "cpu");
}
#[test]
fn test_cpu_device_new_works() {
let cpu = CpuDevice::new();
assert_eq!(cpu.n_threads, 0);
}
#[test]
fn test_cpu_device_name() {
let cpu = CpuDevice::new();
assert_eq!(cpu.name(), "cpu");
}
#[test]
fn test_cpu_device_is_available_true() {
let cpu = CpuDevice::new();
assert!(cpu.is_available());
}
#[test]
fn test_available_devices_contains_at_least_one() {
let devs = available_devices();
assert!(!devs.is_empty());
assert_eq!(devs[0].name(), "cpu");
}
#[test]
fn test_select_best_device_returns_valid_device() {
let dev = select_best_device();
assert!(dev.is_available());
let name = dev.name();
assert!(name == "cpu" || name == "cuda");
}
}