“Know how to solve every problem that has been solved.” “What I cannot create, I do not understand.” — Richard Feynman
cpp 107 lines · 2.4 KB
#include "qc/basis.hpp"

#include <cmath>
#include <stdexcept>
#include <vector>

namespace qc {

Vec3 operator+(Vec3 a, Vec3 b) {
  return {a.x + b.x, a.y + b.y, a.z + b.z};
}

Vec3 operator-(Vec3 a, Vec3 b) {
  return {a.x - b.x, a.y - b.y, a.z - b.z};
}

Vec3 operator*(double s, Vec3 v) {
  return {s * v.x, s * v.y, s * v.z};
}

double dot(Vec3 a, Vec3 b) {
  return a.x * b.x + a.y * b.y + a.z * b.z;
}

double norm(Vec3 v) { return std::sqrt(dot(v, v)); }

double dist_sq(Vec3 a, Vec3 b) {
  const double dx = a.x - b.x;
  const double dy = a.y - b.y;
  const double dz = a.z - b.z;
  return dx * dx + dy * dy + dz * dz;
}

namespace {
constexpr double kPi = 3.14159265358979323846;

double norm_cart(int lx, int ly, int lz, double a) {
  const double s_part = std::pow(2.0 * a / kPi, 0.75);
  const int L = lx + ly + lz;
  if (L == 0) {
    return s_part;
  }
  auto odd_fact_prod = [](int n) {
    double v = 1.0;
    for (int k = n; k > 0; k -= 2) {
      v *= static_cast<double>(k);
    }
    return v;
  };
  double den = 1.0;
  if (lx > 0) {
    den *= odd_fact_prod(2 * lx - 1);
  }
  if (ly > 0) {
    den *= odd_fact_prod(2 * ly - 1);
  }
  if (lz > 0) {
    den *= odd_fact_prod(2 * lz - 1);
  }
  const double num = std::pow(4.0 * a, static_cast<double>(L));
  return s_part * std::sqrt(num / den);
}

using PrimS = std::vector<std::pair<double, double>>;

void push_s_shell(Basis& b, Vec3 R, const PrimS& shell) {
  AOFunction ao;
  for (const auto& pr : shell) {
    const double e = pr.first;
    const double d = pr.second;
    CartPrimitive p;
    p.alpha = e;
    p.r = R;
    p.lx = p.ly = p.lz = 0;
    p.coeff = d * norm_cart(0, 0, 0, e);
    ao.prims.push_back(p);
  }
  b.aos.push_back(std::move(ao));
}

}  // namespace

Basis sto3g_hydrogen_dimer(double bond_bohr) {
  static const PrimS h_s = {{3.42525091, 0.1543289673},
                            {0.62391373, 0.5353282813},
                            {0.16885540, 0.4446345420}};
  Basis b;
  push_s_shell(b, {-0.5 * bond_bohr, 0.0, 0.0}, h_s);
  push_s_shell(b, {0.5 * bond_bohr, 0.0, 0.0}, h_s);
  return b;
}

double nuclear_repulsion_energy(const std::vector<Atom>& atoms) {
  double e = 0.0;
  for (std::size_t i = 0; i < atoms.size(); ++i) {
    for (std::size_t j = i + 1; j < atoms.size(); ++j) {
      const double r = std::sqrt(dist_sq(atoms[i].r, atoms[j].r));
      if (r < 1e-14) {
        continue;
      }
      e += atoms[i].Z * atoms[j].Z / r;
    }
  }
  return e;
}

}  // namespace qc