#ifndef GAIA_AMOEBA_CG_SOLVER_HPP #define GAIA_AMOEBA_CG_SOLVER_HPP #include #include #include #include namespace gaia { namespace amoeba { struct Vec3 { double x, y, z; Vec3(double x=0, double y=0, double z=0) : x(x), y(y), z(z) {} Vec3 operator+(const Vec3& o) const { return Vec3(x+o.x, y+o.y, z+o.z); } Vec3 operator-(const Vec3& o) const { return Vec3(x-o.x, y-o.y, z-o.z); } Vec3 operator*(double s) const { return Vec3(x*s, y*s, z*s); } double dot(const Vec3& o) const { return x*o.x + y*o.y + z*o.z; } double norm2() const { return x*x + y*y + z*z; } }; struct Atom { Vec3 pos; Vec3 mu_prev; double alpha; int element; }; class ConjugateGradientSolver { public: ConjugateGradientSolver(double tolerance = 1e-6, int max_iter = 20) : tolerance(tolerance), max_iter(max_iter) {} std::vector solve(const std::vector& atoms, const std::vector& E_field) { int n = (int)atoms.size(); std::vector mu(n); std::vector r(n); std::vector p(n); for (int i = 0; i < n; i++) { mu[i] = atoms[i].mu_prev; r[i] = E_field[i] - compute_T_mu(atoms, mu, i); p[i] = r[i]; } double rho = dot_product(r, r); for (int iter = 0; iter < max_iter; iter++) { std::vector Ap(n); for (int i = 0; i < n; i++) { Ap[i] = compute_T_p(atoms, p, i); } double pAp = dot_product(p, Ap); if (pAp < 1e-12) break; double alpha = rho / pAp; for (int i = 0; i < n; i++) { mu[i] = mu[i] + p[i] * alpha; } for (int i = 0; i < n; i++) { r[i] = r[i] - Ap[i] * alpha; } double rho_new = dot_product(r, r); double beta = (rho_new - rho) / rho; for (int i = 0; i < n; i++) { p[i] = r[i] + p[i] * beta; } rho = rho_new; if (std::sqrt(rho) < tolerance) break; } return mu; } private: Vec3 compute_T_mu(const std::vector& atoms, const std::vector& mu, int i) { Vec3 result; const double damping = 0.39; for (int j = 0; j < (int)atoms.size(); j++) { if (i == j) continue; Vec3 dr = atoms[i].pos - atoms[j].pos; double r2 = dr.norm2(); double r = std::sqrt(r2); if (r < 1e-12) continue; double alpha_i = atoms[i].alpha; double alpha_j = atoms[j].alpha; double u = r / std::pow(alpha_i * alpha_j, 1.0/6.0); double damp = 1.0 - std::exp(-damping * u * u * u); double factor = damp * alpha_j / (r2 * r); result = result + dr * (mu[j].dot(dr) * 3.0 * factor) - mu[j] * factor; } return result; } Vec3 compute_T_p(const std::vector& atoms, const std::vector& p, int i) { Vec3 result; const double damping = 0.39; for (int j = 0; j < (int)atoms.size(); j++) { if (i == j) continue; Vec3 dr = atoms[i].pos - atoms[j].pos; double r2 = dr.norm2(); double r = std::sqrt(r2); if (r < 1e-12) continue; double alpha_i = atoms[i].alpha; double alpha_j = atoms[j].alpha; double u = r / std::pow(alpha_i * alpha_j, 1.0/6.0); double damp = 1.0 - std::exp(-damping * u * u * u); double factor = damp * alpha_j / (r2 * r); result = result + dr * (p[j].dot(dr) * 3.0 * factor) - p[j] * factor; } return result; } double dot_product(const std::vector& a, const std::vector& b) { double sum = 0.0; for (size_t i = 0; i < a.size(); i++) { sum += a[i].dot(b[i]); } return sum; } double tolerance; int max_iter; }; } // namespace amoeba } // namespace gaia #endif