use ndarray::{Array1, Array3};
use num_complex::Complex64;
use refeff_core::{GenfmtSpinChannelCountInput, genfmt_spin_channel_count};
use crate::error::{IoError, Result};
use super::common::invalid_phase_bin;
use super::types::PhaseBinData;
use super::validate::validate_phase_bin;
#[derive(Debug, Clone, PartialEq)]
pub struct RhorrpPhaseBinHandoff {
pub energies_hartree: Array1<Complex64>,
pub chemical_potential_hartree: f64,
pub real_axis_count: usize,
pub xsph_phase_shifts: Array3<Complex64>,
}
impl RhorrpPhaseBinHandoff {
#[must_use]
pub fn energy_count(&self) -> usize {
self.energies_hartree.len()
}
#[must_use]
pub fn angular_momentum_count(&self) -> usize {
self.xsph_phase_shifts.dim().1
}
#[must_use]
pub fn potential_count(&self) -> usize {
self.xsph_phase_shifts.dim().2
}
}
impl PhaseBinData {
pub fn to_rhorrp_handoff(&self, spin_selector: i32) -> Result<RhorrpPhaseBinHandoff> {
rhorrp_phase_handoff_from_phase_bin(self, spin_selector)
}
}
pub fn rhorrp_phase_handoff_from_phase_bin(
phase: &PhaseBinData,
spin_selector: i32,
) -> Result<RhorrpPhaseBinHandoff> {
validate_rhorrp_phase_handoff(phase)?;
let xsph_phase_shifts = rhorrp_phase_table_from_phase_bin(phase, spin_selector)?;
Ok(RhorrpPhaseBinHandoff {
energies_hartree: phase.energy_grid.clone(),
chemical_potential_hartree: phase.scalars.fermi_level,
real_axis_count: phase.main_energy_count,
xsph_phase_shifts,
})
}
pub fn rhorrp_phase_table_from_phase_bin(
phase: &PhaseBinData,
spin_selector: i32,
) -> Result<Array3<Complex64>> {
validate_phase_bin(phase)?;
let angular_count = phase
.potentials
.iter()
.map(|potential| potential.lmax)
.max()
.and_then(|lmax| lmax.checked_add(1))
.ok_or_else(|| invalid_phase_bin("lmax", "phase.bin contains no potentials"))?;
let spin_channel_count = genfmt_spin_channel_count(GenfmtSpinChannelCountInput {
spin_selector,
available_spin_channels: phase.spin_count,
})
.map_err(rhorrp_phase_error)?;
let mut output = Array3::zeros((phase.energy_count, angular_count, phase.potential_count()));
for (potential_index, potential) in phase.potentials.iter().enumerate() {
for energy in 0..phase.energy_count {
for angular in 0..=potential.lmax {
let signed_l_slot = potential
.lmax
.checked_add(angular)
.ok_or_else(|| invalid_phase_bin("lmax", "signed l slot overflowed"))?;
output[(energy, angular, potential_index)] = selected_phase_shift(
phase.spin_count,
spin_channel_count,
potential.phase_shifts[(energy, signed_l_slot, 0)],
potential.phase_shifts[(energy, signed_l_slot, phase.spin_count - 1)],
);
}
}
}
Ok(output)
}
fn selected_phase_shift(
available_spin_channels: usize,
active_spin_channels: usize,
first_spin: Complex64,
last_spin: Complex64,
) -> Complex64 {
if available_spin_channels == 1 || active_spin_channels == 1 {
first_spin
} else {
(first_spin + last_spin) * 0.5
}
}
fn rhorrp_phase_error(source: refeff_core::GenfmtError) -> IoError {
invalid_phase_bin("rhorrp_phase", source.to_string())
}
fn validate_rhorrp_phase_handoff(phase: &PhaseBinData) -> Result<()> {
validate_phase_bin(phase)?;
if phase.main_energy_count < 2 || phase.main_energy_count > phase.energy_count {
return Err(invalid_phase_bin(
"ne1",
format!(
"RHORRP real-axis count {} is outside 2..={}",
phase.main_energy_count, phase.energy_count
),
));
}
let used_energy_count = phase
.main_energy_count
.checked_add(phase.auxiliary_energy_count)
.ok_or_else(|| invalid_phase_bin("ne3", "main plus auxiliary energy count overflowed"))?;
if used_energy_count > phase.energy_count {
return Err(invalid_phase_bin(
"ne3",
format!(
"main plus auxiliary energy counts {} exceed total energy count {}",
used_energy_count, phase.energy_count
),
));
}
Ok(())
}