1use ndarray::{Array1, Array2};
21use serde::{Deserialize, Serialize};
22use thiserror::Error;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
26pub enum Simulator {
27 Genesis,
28 Xppaut,
29 Auto,
30 ModelDB,
31 Neuron,
32 Brian,
33}
34
35#[derive(Debug, Error)]
37pub enum OldiesError {
38 #[error("Parse error: {0}")]
39 ParseError(String),
40
41 #[error("Simulation error: {0}")]
42 SimulationError(String),
43
44 #[error("Model not found: {0}")]
45 ModelNotFound(String),
46
47 #[error("IO error: {0}")]
48 IoError(#[from] std::io::Error),
49
50 #[error("Numerical error: {0}")]
51 NumericalError(String),
52}
53
54pub type Result<T> = std::result::Result<T, OldiesError>;
55
56pub type Time = f64;
58
59pub type Voltage = f64;
61
62pub type Current = f64;
64
65pub type Conductance = f64;
67
68pub type Concentration = f64;
70
71pub type StateVector = Array1<f64>;
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct TimeSeries {
77 pub time: Vec<Time>,
79 pub values: Vec<f64>,
81 pub name: String,
83 pub units: Option<String>,
85}
86
87impl TimeSeries {
88 pub fn new(name: &str) -> Self {
89 Self {
90 time: Vec::new(),
91 values: Vec::new(),
92 name: name.to_string(),
93 units: None,
94 }
95 }
96
97 pub fn push(&mut self, t: Time, v: f64) {
98 self.time.push(t);
99 self.values.push(v);
100 }
101
102 pub fn len(&self) -> usize {
103 self.time.len()
104 }
105
106 pub fn is_empty(&self) -> bool {
107 self.time.is_empty()
108 }
109}
110
111pub trait OdeSystem {
113 fn dimension(&self) -> usize;
115
116 fn derivatives(&self, t: Time, y: &StateVector) -> StateVector;
118
119 fn jacobian(&self, _t: Time, _y: &StateVector) -> Option<Array2<f64>> {
121 None
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct SimulationParams {
128 pub t_start: Time,
130 pub t_end: Time,
132 pub dt: Time,
134 pub output_dt: Option<Time>,
136 pub tolerance: f64,
138}
139
140impl Default for SimulationParams {
141 fn default() -> Self {
142 Self {
143 t_start: 0.0,
144 t_end: 100.0,
145 dt: 0.01,
146 output_dt: Some(0.1),
147 tolerance: 1e-6,
148 }
149 }
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct IonChannel {
155 pub name: String,
157 pub g_max: Conductance,
159 pub e_rev: Voltage,
161 pub gates: Vec<GateVariable>,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct GateVariable {
168 pub name: String,
170 pub power: u32,
172 pub alpha: RateFunction,
174 pub beta: RateFunction,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180pub enum RateFunction {
181 HodgkinHuxley { a: f64, b: f64, c: f64 },
183 Exponential { a: f64, b: f64, c: f64 },
185 Sigmoid { a: f64, b: f64, c: f64 },
187 Linear { a: f64, b: f64 },
189 Constant(f64),
191}
192
193impl RateFunction {
194 pub fn eval(&self, v: Voltage) -> f64 {
196 match self {
197 Self::HodgkinHuxley { a, b, c } => {
198 let x = (v + b) / c;
199 if x.abs() < 1e-6 {
200 a * c
202 } else {
203 a * (v + b) / (x.exp() - 1.0)
204 }
205 }
206 Self::Exponential { a, b, c } => {
207 a * ((v + b) / c).exp()
208 }
209 Self::Sigmoid { a, b, c } => {
210 a / (1.0 + ((v + b) / c).exp())
211 }
212 Self::Linear { a, b } => {
213 a * (v + b)
214 }
215 Self::Constant(c) => *c,
216 }
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn test_rate_functions() {
226 let hh = RateFunction::HodgkinHuxley { a: 0.1, b: 40.0, c: 10.0 };
227 let rate = hh.eval(-65.0);
228 assert!(rate > 0.0);
229
230 let exp = RateFunction::Exponential { a: 0.1, b: 65.0, c: 80.0 };
231 let rate = exp.eval(-65.0);
232 assert!(rate > 0.0);
233 }
234
235 #[test]
236 fn test_time_series() {
237 let mut ts = TimeSeries::new("voltage");
238 ts.push(0.0, -65.0);
239 ts.push(0.1, -64.0);
240 assert_eq!(ts.len(), 2);
241 }
242}