-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Description:
This task represents the final core implementation phase before exposing the public API. It focuses on creating the QEqSolver engine, which orchestrates the entire charge equilibration process. This solver will integrate the atomic parameters from the params module and the shielded Coulomb potential from the math module to iteratively solve for equilibrium charges.
The implementation will reside in a new top-level solver module and will use the faer crate for high-performance, numerically stable linear algebra. The core logic will be a Self-Consistent Field (SCF) loop to handle the charge-dependent hardness of hydrogen, making the solver applicable to all elements described in the Rappe & Goddard paper.
Tasks:
-
Phase 1: Module Scaffolding and Dependencies
- Add the
faercrate (version = "0.17") toCargo.toml. - Create a new directory
src/solver/withmod.rs. - Create
src/solver/options.rsand define theSolverOptionsstruct withtolerance,max_iterations, andlambda_scale, including aDefaultimplementation. - Create
src/solver/solver.rsand define theQEqSolverstruct. It should hold a reference toParametersand an instance ofSolverOptions. - Implement
QEqSolver::new()and a builder-stylewith_options()method.
- Add the
-
Phase 2: Implement the System Builder (
build_system)- In
solver.rs, create the private helper functionbuild_system. - Signature:
fn build_system<A: AtomView>(&self, atoms: &[A], element_data: &[&'p ElementData], current_charges: ColRef<f64>) -> Result<(Mat<f64>, Col<f64>), CheqError>. - Logic:
- Initialize an
(N+1) x (N+1)matrixAand an(N+1)vectorbusingfaer. - Loop
ifrom0toN-1:- Diagonal
A[i, i](HardnessJ_ii):- Check if the atom is Hydrogen (
n=1oratomic_number=1). - If yes, apply the charge-dependent hardness formula:
J_ii = J_ii⁰ * (1 + Q_i * H_CHARGE_FACTOR). - If no, use the hardness
J_ii⁰directly fromelement_data.
- Check if the atom is Hydrogen (
- Off-diagonal
A[i, j](CoulombJ_ij):- Loop
jfromi+1toN-1. - Calculate inter-atomic distance
R_ijin Angstroms. - Perform all necessary unit conversions (Å → Bohr) for inputs to the math module.
- Call
math::shielding::gaussian_coulomb_integral. - Convert the result (Hartree → eV).
- Fill the symmetric elements
A[i, j]andA[j, i].
- Loop
- Right-hand side
b[i](Electronegativityχ_i⁰):- Set
b[i] = -element_data[i].electronegativity.
- Set
- Diagonal
- Constraint Equations:
- Fill the last column of
A(rows0..N) with-1.0. - Fill the last row of
A(cols0..N) with1.0.A[N, N]remains0.0. - Set the last element of the vector
b[N] = total_charge.
- Fill the last column of
- Initialize an
- Return
Ok((A, b)).
- In
-
Phase 3: Implement the SCF Solver Logic (
solve)- Implement the public method
pub fn solve<A: AtomView>(&self, atoms: &[A], total_charge: f64) -> Result<CalculationResult, CheqError>. - Initial Setup:
- Handle the edge case of an empty
atomsslice, returningCheqError::NoAtoms. - Pre-fetch all necessary
ElementDatafor the atoms, handlingCheqError::ParameterNotFound. - Initialize
chargesvector to all zeros. - Determine if the system contains hydrogen to decide if an SCF loop is needed.
- Handle the edge case of an empty
- SCF Loop:
- Loop up to
self.options.max_iterations. - Call
build_systemto get the current matrixAand vectorb. - Solve
Ax = businga.lu().solve(&b). Handle potentialNoneresult by returningCheqError::LinalgError. - Extract new charges
Q_newfrom the firstNelements of the solution vector. - Calculate
max_charge_delta = max(|Q_new - Q_old|). - Update
charges = new_charges. - Check for Convergence: If
!has_hydrogen(exit after one iteration) ormax_charge_delta < self.options.tolerance, break the loop and proceed to success.
- Loop up to
- Finalization:
- If the loop converged, extract the equilibrated potential
χ̄ = -solution[N], createCalculationResult, and returnOk(...). - If the loop terminated due to max iterations, return
Err(CheqError::NotConverged).
- If the loop converged, extract the equilibrated potential
- Implement the public method
-
Phase 4: Integration Testing
- Create a
testssubmodule withinsrc/solver/solver.rs. - Test a linear system (no H): Use a molecule like
COorN₂. Assert that charges are computed in 1 iteration and have the correct signs and rough magnitudes. - Test a non-linear system (with H):
- Use LiH. Check that it takes multiple iterations. Assert the final charge on H is negative and close to the paper's value (~ -0.7).
- Use H₂O. Assert the final charge on H is positive and close to the paper's QEq value (~ 0.35).
- Test symmetry: For H₂, assert charges are exactly zero. For a symmetric molecule like CH₄, assert all H charges are identical.
- Test error handling:
- Write a test that calls
solvewith an element not in the default parameters (e.g., Uranium, if not present) and assert it returnsCheqError::ParameterNotFound. - Write a test with an empty slice of atoms and assert it returns
CheqError::NoAtoms.
- Write a test that calls
- Create a
Metadata
Metadata
Assignees
Labels
Type
Projects
Status