#![allow(dead_code)]
use crate::tanh_levien_f64;
#[inline]
pub fn safe_asinh(x: f64) -> f64 {
const AH_LN2: f64 = 6.93147180559945286227e-01;
let x_abs = x.abs();
let w = if x_abs < 2.0_f64.powi(28) {
(x_abs + ((x * x) + 1.0).sqrt()).ln()
}
else {
x_abs.ln() + AH_LN2
};
w.copysign(x)
}
#[derive(Debug, Clone)]
pub struct DKSolver<const N_N: usize, const N_P: usize, const P_LEN: usize> {
pub z: [f64; N_N],
pub last_z: [f64; N_N],
pub last_p: [f64; N_P],
pub p_full: [f64; P_LEN],
factors: [[f64; N_N]; N_N],
ipiv: [usize; N_N],
pub j: [[f64; N_N]; N_N],
pub jp: [[f64; N_P]; N_N],
pub jq: [[f64; P_LEN]; N_N],
pub residue: [f64; N_N],
pub resmaxabs: f64,
}
impl<const N_N: usize, const N_P: usize, const P_LEN: usize> DKSolver<N_N, N_P, P_LEN> {
pub fn new() -> Self {
Self {
z: [0.; N_N],
last_z: [0.; N_N],
last_p: [0.; N_P],
factors: [[0.; N_N]; N_N],
ipiv: [0; N_N],
j: [[0.; N_N]; N_N],
jp: [[0.; N_P]; N_N],
p_full: [0.; P_LEN],
jq: [[0.; P_LEN]; N_N],
residue: [0.; N_N],
resmaxabs: 0.,
}
}
pub fn set_p(&mut self, p: [f64; N_P], pexps: &[[f64; N_P]; P_LEN]) {
self.p_full = [0.; P_LEN];
for i in 0..P_LEN {
for j in 0..N_P {
self.p_full[i] += p[j] * pexps[i][j];
}
}
}
pub fn set_jp(&mut self, pexps: &[[f64; N_P]; P_LEN]) {
for i in 0..N_N {
for j in 0..N_P {
self.jp[i][j] = 0.;
for k in 0..P_LEN {
self.jp[i][j] += self.jq[i][k] * pexps[k][j];
}
}
}
}
pub fn set_extrapolation_origin(&mut self, p: [f64; N_P], z: [f64; N_N]) {
self.last_p = p;
self.last_z = z;
}
pub fn set_lin_solver(&mut self, new_jacobian: [[f64; N_N]; N_N]) -> bool {
self.factors = new_jacobian;
for k in 0..N_N {
let mut kp = k;
let mut amax = 0.0;
for i in k..N_N {
let absi = self.factors[i][k].abs();
if absi > amax {
kp = i;
amax = absi;
}
}
self.ipiv[k] = kp;
if self.factors[kp][k] != 0.0 {
if k != kp {
for i in 0..N_N {
let tmp = self.factors[k][i];
self.factors[k][i] = self.factors[kp][i];
self.factors[kp][i] = tmp;
}
}
self.factors[k][k] = 1. / self.factors[k][k];
for i in k + 1..N_N {
self.factors[i][k] *= self.factors[k][k];
}
} else {
return false;
}
for j in k + 1..N_N {
for i in k + 1..N_N {
self.factors[i][j] -= self.factors[i][k] * self.factors[k][j];
}
}
}
true
}
pub fn solve_linear_equations(&self, b: [f64; N_N]) -> [f64; N_N] {
let mut x_temp = b;
for i in 0..N_N {
x_temp.swap(i, self.ipiv[i]);
}
for j in 0..N_N {
let xj = x_temp[j];
for i in j + 1..N_N {
x_temp[i] -= self.factors[i][j] * xj;
}
}
for j in (0..N_N).rev() {
x_temp[j] = self.factors[j][j] * x_temp[j];
for i in 0..j {
x_temp[i] -= self.factors[i][j] * x_temp[j];
}
}
x_temp
}
#[inline(always)]
pub fn eval_opamp(&self, v_in: f64, v_out: f64) -> (f64, [f64; 2]) {
let tanh_vin = tanh_levien_f64(v_in);
let residue = tanh_vin - v_out;
let mut jacobian = [(1. - tanh_vin * tanh_vin), -1.0];
if jacobian[0] == 0.0 {
jacobian[0] = v_in.signum() * 1e-9;
}
(residue, jacobian)
}
#[inline]
pub fn eval_ota(&self, q: &[f64]) -> (f64, [f64; 2]) {
let v_in = q[0];
let i_out = q[1];
let tanh_vin = tanh_levien_f64(v_in);
let residue = tanh_vin + i_out;
let mut jacobian = [(1. - tanh_vin * tanh_vin), 1.0];
if jacobian[0] == 0.0 {
jacobian[0] = v_in.signum() * 1e-9;
}
(residue, jacobian)
}
pub fn eval_diodepair(&self, v_in: f64, i_out: f64, i_s: f64, eta: f64) -> (f64, [f64; 2]) {
const V_T: f64 = 25e-3;
let v_t_inv = 1.0 / (V_T * eta);
let x = v_in * v_t_inv;
let ex1 = (x).exp();
let ex2 = (-x).exp();
let sinh_vin = i_s * (ex1 - ex2);
let cosh_vin = i_s * (ex1 + ex2);
const LIM: f64 = 1e34;
let residue = sinh_vin.clamp(-LIM, LIM) - i_out;
let jacobian = cosh_vin.clamp(-LIM, LIM) * v_t_inv;
(residue, [jacobian, -1.])
}
pub fn eval_diode(&self, q: &[f64], i_s: f64, eta: f64) -> (f64, [f64; 2]) {
const V_T: f64 = 25e-3;
let v_in = q[0];
let i_out = q[1];
let ex = (v_in / (V_T * eta)).exp();
let residue = i_s * (ex - 1.) - i_out;
let jacobian = [i_s / (V_T * eta) * ex, -1.0];
(residue, jacobian)
}
pub fn eval_diode_clipper(&self, q: &[f64]) -> (f64, [f64; 2]) {
const ETA: f64 = 1.68;
const V_T: f64 = 25e-3 * ETA;
const I_S: f64 = 1e-15;
const I_S_2: f64 = I_S * I_S;
let v_in = q[0];
let i_out = q[1];
let residue = V_T * safe_asinh(0.5 * i_out / I_S) - v_in;
let jacobian = [-1.0, V_T / (I_S * ((i_out * i_out) / I_S_2 + 4.0).sqrt())];
(residue, jacobian)
}
}