use std::{fs::File, io::BufReader, path::Path};
use gmt_dos_clients_crseo::calibration::{Calib, MixedMirrorMode, algebra::CalibProps};
use nalgebra::DMatrix;
use osqp::{CscMatrix, Problem, Settings};
use serde::Deserialize;
use super::{active_optics::ActiveOptics, QpError};
#[derive(Deserialize)]
struct QPData {
#[serde(rename = "D")]
dmat: Vec<f64>,
#[serde(rename = "W2")]
w2: Vec<f64>,
#[serde(rename = "W3")]
w3: Vec<f64>,
#[serde(rename = "K")]
k: f64,
#[serde(rename = "_Tu")]
tu: Vec<f64>,
rho_3: f64,
end2end_ordering: bool,
}
#[derive(Deserialize)]
pub struct QP<const M1_RBM: usize, const M2_RBM: usize, const M1_BM: usize, const N_MODE: usize> {
#[serde(rename = "SHAcO_qp")]
data: QPData,
#[serde(skip)]
verbose: bool,
#[serde(skip)]
#[allow(unused)]
m1_actuator_forces_outputs: bool,
#[serde(skip)]
convergence_tolerances: (f64, f64),
#[serde(skip)]
calib: Option<Calib<MixedMirrorMode>>,
}
impl<const M1_RBM: usize, const M2_RBM: usize, const M1_BM: usize, const N_MODE: usize>
QP<M1_RBM, M2_RBM, M1_BM, N_MODE>
{
pub fn new(
qp_filename: impl AsRef<Path>,
) -> Result<Self, QpError> {
assert!(
M1_RBM + M2_RBM + 7 * M1_BM == N_MODE,
"The number of modes {} do not match the expected value {} (M1_RBM + M2_RBM + 7 * M1_BM)x",
N_MODE,
M1_RBM + M2_RBM + 7 * M1_BM
);
let file = File::open(&qp_filename).map_err(|e| QpError::Open {
filename: qp_filename.as_ref().to_str().unwrap().to_string(),
source: e,
})?;
let rdr = BufReader::with_capacity(10_000, file);
let this: Self = serde_pickle::from_reader(rdr, Default::default())?;
Ok(Self {
verbose: false,
m1_actuator_forces_outputs: false,
convergence_tolerances: (1.0e-8, 1.0e-6),
..this
})
}
pub fn update_dmat(mut self, path: impl AsRef<Path>) -> Result<Self, QpError> {
let file = File::open(&path).map_err(|e| QpError::Open {
filename: path.as_ref().to_str().unwrap().to_string(),
source: e,
})?;
let buffer = BufReader::new(file);
self.data.dmat = serde_pickle::from_reader(buffer, Default::default())?;
Ok(self)
}
pub fn update_calib(mut self, path: impl AsRef<Path>) -> Result<Self, QpError> {
let file = File::open(&path).map_err(|e| QpError::Open {
filename: path.as_ref().to_str().unwrap().to_string(),
source: e,
})?;
let buffer = BufReader::new(file);
let calib: Calib<MixedMirrorMode> = serde_pickle::from_reader(buffer, Default::default())?;
self.data.dmat = calib
.mat_ref()
.col_iter()
.flat_map(|c| c.iter().cloned().collect::<Vec<_>>())
.collect();
self.calib = Some(calib);
Ok(self)
}
pub fn verbose(self) -> Self {
Self {
verbose: true,
..self
}
}
pub fn as_m1_actuator_forces(self) -> Self {
Self {
m1_actuator_forces_outputs: true,
..self
}
}
pub fn convergence_tolerances(self, convergence_tolerances: (f64, f64)) -> Self {
Self {
convergence_tolerances,
..self
}
}
pub fn build(self) -> Result<ActiveOptics<M1_RBM, M2_RBM, M1_BM, N_MODE>, QpError> {
let dmat = self.data.dmat;
let w2 = DMatrix::from_column_slice(N_MODE, N_MODE, &self.data.w2);
let w3 = DMatrix::<f64>::from_column_slice(N_MODE, N_MODE, &self.data.w3);
let d_wfs = DMatrix::from_column_slice(dmat.len() / N_MODE, N_MODE, &dmat);
let d_t_w1_d =
d_wfs.tr_mul(&d_wfs);
let p_utri = {
let p = &d_t_w1_d + &w2 + w3.scale(self.data.rho_3 * self.data.k * self.data.k);
CscMatrix::from_column_iter_dense(
p.nrows(),
p.ncols(),
p.as_slice().to_vec().into_iter(),
)
.into_upper_tri()
};
let i_m1_s7_rz: u8 = if self.data.end2end_ordering {
41
} else {
(((12 + M1_BM) * 6) + 5).try_into().unwrap()
};
let i_m2_s7_rz: u8 = if self.data.end2end_ordering {
82 + 1
} else {
(((12 + M1_BM) * 6) + 10 + 1).try_into().unwrap()
};
let tu = DMatrix::<f64>::from_vec(273, 1228, self.data.tu)
.transpose()
.remove_columns_at(&[i_m1_s7_rz.into(), i_m2_s7_rz.into()]);
let a_in = {
let tus = tu.scale(self.data.k);
CscMatrix::from(
&tus.row_iter()
.map(|x| x.clone_owned().as_slice().to_vec())
.collect::<Vec<Vec<f64>>>(),
)
};
let q: Vec<f64> = vec![0f64; N_MODE];
let umin = vec![f64::NEG_INFINITY; tu.nrows()];
let umax = vec![f64::INFINITY; tu.nrows()];
let settings = Settings::default()
.eps_abs(self.convergence_tolerances.0)
.eps_rel(self.convergence_tolerances.1)
.max_iter((500 * N_MODE).try_into().unwrap())
.warm_start(true)
.verbose(self.verbose);
let prob = Problem::new(p_utri, &q, a_in, &umin, &umax, &settings)?;
Ok(ActiveOptics {
prob,
u: vec![0f64; 84 + 7 * M1_BM],
y_valid: Vec::with_capacity(d_wfs.nrows()),
d_wfs,
u_ant: DMatrix::zeros(N_MODE,1),
d_t_w1_d,
w2,
w3,
rho_3: self.data.rho_3,
k: self.data.k,
umin: vec![f64::NEG_INFINITY; tu.nrows()],
umax: vec![f64::INFINITY; tu.nrows()],
tu,
calib: self.calib,
})
}
}