use ndarray::ArrayView1;
use refeff_core::{
Complex, Real, RhorrpPreparedWavefunctionTablesInput, RhorrpWavefunctionGridPreparation,
RhorrpWavefunctionGridPreparationInput, RhorrpWavefunctionTables,
rhorrp_density_reference_energy_hartree, rhorrp_prepare_wavefunction_grids,
rhorrp_prepared_wavefunction_tables,
};
use crate::config_dat::{
ConfigDatData, RhorrpConfigOrbitalTables, rhorrp_orbital_tables_from_config_dat,
};
use crate::error::Result;
use crate::{RhorrpFmsInputHandoff, RhorrpPhaseBinHandoff, RhorrpPotInputControls};
use super::common::invalid_pot_bin;
use super::types::PotBinData;
use super::validate::validate_pot_bin;
pub const RHORRP_POT_BIN_RADIAL_DX: Real = 0.05;
pub const RHORRP_WAVEFUNCTION_RADIAL_X0: Real = 8.8;
pub const RHORRP_WAVEFUNCTION_RADIAL_COUNT: usize = 251;
#[derive(Debug, Clone, Copy)]
pub struct RhorrpWavefunctionTablesHandoffInput<'a> {
pub pot: &'a PotBinData,
pub config: &'a ConfigDatData,
pub phase: &'a RhorrpPhaseBinHandoff,
pub fms: RhorrpFmsInputHandoff,
pub controls: RhorrpPotInputControls,
pub radial_count: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RhorrpWavefunctionTablesHandoff {
pub prepared: RhorrpWavefunctionGridPreparation,
pub reference_energy_hartree: Complex,
pub wavefunctions: RhorrpWavefunctionTables,
pub radial_x0: Real,
pub radial_dx: Real,
}
#[derive(Debug, Clone)]
pub struct RhorrpPotBinWavefunctionHandoff<'a> {
pot: &'a PotBinData,
muffin_tin_radii: &'a [Real],
norman_radii: &'a [Real],
atomic_numbers: Vec<Real>,
}
impl RhorrpPotBinWavefunctionHandoff<'_> {
#[must_use]
pub fn potential_count(&self) -> usize {
self.pot.potential_count()
}
#[must_use]
pub fn muffin_tin_radii(&self) -> &[Real] {
self.muffin_tin_radii
}
#[must_use]
pub fn norman_radii(&self) -> &[Real] {
self.norman_radii
}
#[must_use]
pub fn atomic_numbers(&self) -> &[Real] {
&self.atomic_numbers
}
#[must_use]
pub fn grid_preparation_input(
&self,
target_radial_dx: Real,
exchange_index: i32,
radial_count: usize,
) -> RhorrpWavefunctionGridPreparationInput<'_> {
RhorrpWavefunctionGridPreparationInput {
muffin_tin_radii: self.muffin_tin_radii,
electron_density: self.pot.electron_density.view(),
total_potential: self.pot.total_potential.view(),
valence_density: self.pot.valence_density.view(),
valence_potential: self.pot.valence_potential.view(),
magnetization: self.pot.magnetization_density.view(),
bound_large_components: self.pot.large_components.view(),
bound_small_components: self.pot.small_components.view(),
interstitial_potential: self.pot.scalars.interstitial_potential,
interstitial_density: self.pot.scalars.interstitial_density,
original_radial_dx: RHORRP_POT_BIN_RADIAL_DX,
target_radial_dx,
jump_mode: self.pot.jump_mode,
potential_jump: 0.0,
exchange_index,
radial_count,
}
}
pub fn prepared_wavefunction_tables_input<'a>(
&'a self,
prepared: &'a RhorrpWavefunctionGridPreparation,
orbital_tables: &'a RhorrpConfigOrbitalTables,
energies_hartree: ArrayView1<'a, Complex>,
exchange_index: i32,
angular_momentum_count: usize,
) -> Result<RhorrpPreparedWavefunctionTablesInput<'a>> {
validate_prepared_table_sources(self, prepared, orbital_tables)?;
Ok(RhorrpPreparedWavefunctionTablesInput {
prepared,
energies_hartree,
muffin_tin_radii: self.muffin_tin_radii,
norman_radii: self.norman_radii,
bound_large_coefficients_by_potential: self.pot.large_coefficients.view(),
bound_small_coefficients_by_potential: self.pot.small_coefficients.view(),
electron_counts_by_potential: orbital_tables.electron_counts_by_potential.view(),
valence_counts_by_potential: self.pot.orbital_occupancy.view(),
kappa_by_potential: orbital_tables.kappa_by_potential.view(),
atomic_numbers: &self.atomic_numbers,
exchange_index,
angular_momentum_count,
bound_orbital_counts: &orbital_tables.bound_orbital_counts,
})
}
}
pub fn rhorrp_wavefunction_handoff_from_pot_bin(
pot: &PotBinData,
) -> Result<RhorrpPotBinWavefunctionHandoff<'_>> {
validate_pot_bin(pot)?;
let muffin_tin_radii = contiguous_potential_slice("rmt", &pot.muffin_tin_radii)?;
let norman_radii = contiguous_potential_slice("rnrm", &pot.norman_radii)?;
let atomic_numbers = pot
.atomic_numbers
.iter()
.map(|&atomic_number| atomic_number as Real)
.collect::<Vec<_>>();
Ok(RhorrpPotBinWavefunctionHandoff {
pot,
muffin_tin_radii,
norman_radii,
atomic_numbers,
})
}
pub fn rhorrp_wavefunction_tables_from_handoffs(
input: RhorrpWavefunctionTablesHandoffInput<'_>,
) -> Result<RhorrpWavefunctionTablesHandoff> {
let pot = rhorrp_wavefunction_handoff_from_pot_bin(input.pot)?;
validate_rhorrp_wavefunction_handoff_counts(&pot, input.config, input.phase, input.fms)?;
let orbital_tables = rhorrp_orbital_tables_from_config_dat(input.config)?;
let prepared = rhorrp_prepare_wavefunction_grids(pot.grid_preparation_input(
input.controls.target_radial_dx,
input.controls.exchange_index,
input.radial_count,
))
.map_err(|source| invalid_pot_bin("rhorrp_wavefunction_grids", source.to_string()))?;
let tables_input = pot.prepared_wavefunction_tables_input(
&prepared,
&orbital_tables,
input.phase.energies_hartree.view(),
input.controls.exchange_index,
input.fms.angular_momentum_count,
)?;
let wavefunctions = rhorrp_prepared_wavefunction_tables(tables_input)
.map_err(|source| invalid_pot_bin("rhorrp_wavefunction_tables", source.to_string()))?;
let reference_energy_hartree = rhorrp_density_reference_energy_hartree(&prepared)
.map_err(|source| invalid_pot_bin("rhorrp_eref0", source.to_string()))?;
let radial_dx = prepared.radial_dx;
Ok(RhorrpWavefunctionTablesHandoff {
prepared,
reference_energy_hartree,
wavefunctions,
radial_x0: RHORRP_WAVEFUNCTION_RADIAL_X0,
radial_dx,
})
}
fn contiguous_potential_slice<'a>(
field: &'static str,
values: &'a ndarray::Array1<Real>,
) -> Result<&'a [Real]> {
values
.as_slice()
.ok_or_else(|| invalid_pot_bin(field, "potential vector must be contiguous"))
}
fn validate_rhorrp_wavefunction_handoff_counts(
pot: &RhorrpPotBinWavefunctionHandoff<'_>,
config: &ConfigDatData,
phase: &RhorrpPhaseBinHandoff,
fms: RhorrpFmsInputHandoff,
) -> Result<()> {
let potential_count = pot.potential_count();
validate_rhorrp_potential_axis("config.dat", potential_count, config.potential_count())?;
validate_rhorrp_potential_axis("phase.bin", potential_count, phase.potential_count())?;
validate_rhorrp_potential_axis("fms.inp", potential_count, fms.potential_count)?;
Ok(())
}
fn validate_prepared_table_sources(
handoff: &RhorrpPotBinWavefunctionHandoff<'_>,
prepared: &RhorrpWavefunctionGridPreparation,
orbital_tables: &RhorrpConfigOrbitalTables,
) -> Result<()> {
let potential_count = handoff.potential_count();
if prepared.potential_count() != potential_count {
return Err(invalid_pot_bin(
"rhorrp_prepared",
format!(
"prepared wavefunctions have {} potential(s), expected {potential_count}",
prepared.potential_count()
),
));
}
validate_rhorrp_potential_axis(
"config electron_counts",
potential_count,
orbital_tables.electron_counts_by_potential.dim().1,
)?;
validate_rhorrp_potential_axis(
"config kappa",
potential_count,
orbital_tables.kappa_by_potential.dim().1,
)?;
validate_rhorrp_potential_axis(
"config bound_orbital_counts",
potential_count,
orbital_tables.bound_orbital_counts.len(),
)?;
let valence_shape = handoff.pot.orbital_occupancy.dim();
if valence_shape.1 != potential_count {
return Err(invalid_pot_bin(
"xnval",
format!(
"valence occupation table has {} potential(s), expected {potential_count}",
valence_shape.1
),
));
}
let max_bound_orbitals = orbital_tables
.bound_orbital_counts
.iter()
.copied()
.max()
.unwrap_or(0);
if valence_shape.0 < max_bound_orbitals {
return Err(invalid_pot_bin(
"xnval",
format!(
"valence occupation table has {} orbital row(s), expected at least {max_bound_orbitals}",
valence_shape.0
),
));
}
Ok(())
}
fn validate_rhorrp_potential_axis(
field: &'static str,
expected: usize,
actual: usize,
) -> Result<()> {
if actual == expected {
Ok(())
} else {
Err(invalid_pot_bin(
"rhorrp_config",
format!("{field} has {actual} potential(s), expected {expected}"),
))
}
}