#[derive(Debug, Clone)]
pub struct Compartment {
membrane: f32,
calcium: f32,
tau_membrane: f32,
tau_calcium: f32,
resting: f32,
}
impl Compartment {
pub fn new() -> Self {
Self {
membrane: 0.0,
calcium: 0.0,
tau_membrane: 20.0,
tau_calcium: 100.0,
resting: 0.0,
}
}
pub fn with_time_constants(tau_membrane: f32, tau_calcium: f32) -> Self {
Self {
membrane: 0.0,
calcium: 0.0,
tau_membrane,
tau_calcium,
resting: 0.0,
}
}
pub fn step(&mut self, input_current: f32, dt: f32) {
let membrane_decay = (self.resting - self.membrane) / self.tau_membrane;
self.membrane += (membrane_decay + input_current) * dt;
self.membrane = self.membrane.clamp(0.0, 1.0);
let calcium_decay = -self.calcium / self.tau_calcium;
self.calcium += calcium_decay * dt;
if self.membrane > 0.5 {
self.calcium += (self.membrane - 0.5) * 0.01 * dt;
}
self.calcium = self.calcium.clamp(0.0, 1.0);
}
pub fn is_active(&self, threshold: f32) -> bool {
self.membrane > threshold
}
pub fn membrane(&self) -> f32 {
self.membrane
}
pub fn calcium(&self) -> f32 {
self.calcium
}
pub fn reset(&mut self) {
self.membrane = self.resting;
self.calcium = 0.0;
}
pub fn inject_spike(&mut self, amplitude: f32) {
self.membrane += amplitude;
self.membrane = self.membrane.clamp(0.0, 1.0);
}
}
impl Default for Compartment {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compartment_creation() {
let comp = Compartment::new();
assert_eq!(comp.membrane(), 0.0);
assert_eq!(comp.calcium(), 0.0);
}
#[test]
fn test_compartment_step() {
let mut comp = Compartment::new();
comp.step(0.1, 1.0);
assert!(comp.membrane() > 0.0);
}
#[test]
fn test_membrane_decay() {
let mut comp = Compartment::new();
comp.inject_spike(0.8);
let initial = comp.membrane();
for _ in 0..100 {
comp.step(0.0, 1.0);
}
assert!(comp.membrane() < initial);
}
#[test]
fn test_calcium_accumulation() {
let mut comp = Compartment::new();
comp.inject_spike(0.9);
for _ in 0..10 {
comp.step(0.0, 1.0);
}
assert!(comp.calcium() > 0.0);
}
#[test]
fn test_threshold_detection() {
let mut comp = Compartment::new();
assert!(!comp.is_active(0.5));
comp.inject_spike(0.6);
assert!(comp.is_active(0.5));
}
#[test]
fn test_reset() {
let mut comp = Compartment::new();
comp.inject_spike(0.8);
comp.step(0.0, 1.0);
comp.reset();
assert_eq!(comp.membrane(), 0.0);
assert_eq!(comp.calcium(), 0.0);
}
}