H2+ Molecular Orbital (C++)

Quantum Chemistry

This page presents a C++ implementation for computing the H2+ molecular orbital (gerade/ungerade) and related matrix elements using numerical integration on a 3D grid.

H2+ Molecular Orbital

For H2+, the bonding (gerade) orbital is constructed as a linear combination of atomic orbitals:

where is a 1s hydrogen orbital and the normalization constant is:

with overlap integral .

Matrix Elements

The Hamiltonian matrix elements are computed numerically on a 3D grid:

C++ Implementation

The following code implements the H2+ orbital and matrix element calculations with all helper functions included:

#include <vector>
#include <functional>
#include <cmath>
#include <iostream>
#include <numbers>
#include <algorithm>

// Helper function: linspace
std::vector<double> linspace(double start, double end, size_t num) {
    std::vector<double> result(num);
    double dx = (end - start) / (num - 1);
    for (size_t i = 0; i < num; ++i) {
        result[i] = start + i * dx;
    }
    return result;
}

// Euclidean distance
double distance(const std::vector<double>& x, const std::vector<double>& xp) {
    double result = 0.0;
    for (size_t i = 0; i < x.size(); i++) {
        result += std::pow(x[i] - xp[i], 2);
    }
    return std::sqrt(result);
}

// Kahan summation for numerical stability
enum class SumType {
    Naive,
    Kahan,
    Neumaier
};

class Summation {
public:
    explicit Summation(SumType t) : type(t), sum(0.0), c(0.0) {}

    void add(double x) {
        if (type == SumType::Kahan) {
            double y = x - c;
            double t = sum + y;
            c = (t - sum) - y;
            sum = t;
        } else {
            sum += x;
        }
    }

    double value() const {
        return sum + c;
    }

private:
    SumType type;
    double sum;
    double c;
};

// Hydrogen 1s orbital
double hydrogenOrbital(double r) {
    double a0 = 1.0;  // Bohr radius in atomic units
    return std::exp(-r/a0);
}

// H2+ gerade orbital class
struct h2orbital {
    const std::vector<double>& a;  // Position of nucleus A
    const std::vector<double>& b;  // Position of nucleus B

    h2orbital(const std::vector<double>& a_,
              const std::vector<double>& b_)
        : a(a_), b(b_) {}

    double operator()(double x, double y, double z) {
        std::vector<double> r = {x, y, z};
        double pi = std::numbers::pi_v<double>; 
        double R = distance(a, b);
        
        // Overlap integral (analytical form for 1s orbitals)
        double S = (1.0 + R + (1.0/3.0)*R*R) * std::exp(-R);
        
        // Normalization constant for gerade orbital
        double N_g = 1.0 / std::sqrt(2.0 * (1.0 + S));
        
        // 1s orbital normalization
        double oneSNorm = 1.0 / std::sqrt(pi);
        
        // Construct gerade orbital
        double result = oneSNorm * N_g * 
                       (hydrogenOrbital(distance(r, a)) + 
                        hydrogenOrbital(distance(r, b)));
        return result;
    }
};

// 3D Laplacian using finite differences
double templaplace3dcart(double x, double y, double z, 
                         std::function<double(double, double, double)> f) {
    double h = 0.001;
    double result = f(x + h, y, z) + f(x - h, y, z) 
                  + f(x, y + h, z) + f(x, y - h, z) 
                  + f(x, y, z + h) + f(x, y, z - h) 
                  - 6.0 * f(x, y, z);
    result = result / (h * h);
    return result;
}

// H2+ Hamiltonian calculation
void computeH2Hamiltonian(double R, double box, int points) {
    std::vector<double> R_a = {0, 0, 0.5*R};
    std::vector<double> R_b = {0, 0, -0.5*R};
    
    auto xs = linspace(-box, box, points);
    auto ys = linspace(-box, box, points);
    auto zs = linspace(-box, box, points);
    
    double dx = std::abs(xs[1] - xs[0]); 
    double dy = std::abs(ys[1] - ys[0]); 
    double dz = std::abs(zs[1] - zs[0]); 
    
    h2orbital orbital(R_a, R_b);
    std::vector<double> r = {0, 0, 0};
    
    Summation kahan_k(SumType::Kahan);
    Summation kahan_v(SumType::Kahan);
    
    for (size_t i = 0; i < xs.size(); i++) {
        for (size_t j = 0; j < ys.size(); j++) {
            for (size_t k = 0; k < zs.size(); k++) {
                r[0] = xs[i];
                r[1] = ys[j];
                r[2] = zs[k];
                
                double ra = distance(r, R_a);
                double rb = distance(r, R_b);
                
                double orb = orbital(xs[i], ys[j], zs[k]);
                double laplace = templaplace3dcart(xs[i], ys[j], zs[k], 
                                                   [&](double x, double y, double z) {
                                                       return orbital(x, y, z);
                                                   });
                
                // Kinetic energy density
                double T_density = (-0.5 * laplace) * orb * dx * dy * dz;
                kahan_k.add(T_density);
                
                // Potential energy density
                double V_density = (-1.0/std::max(rb, 1e-3) + 
                                   -1.0/std::max(ra, 1e-3) + 
                                   1.0/R) * orb * orb * dx * dy * dz;
                kahan_v.add(V_density);
            }
        }
    }
    
    double kinetic = kahan_k.value();
    double potential = kahan_v.value();
    double total_energy = kinetic + potential;
    
    std::cout << "Kinetic energy: " << kinetic << std::endl;
    std::cout << "Potential energy: " << potential << std::endl;
    std::cout << "Total energy: " << total_energy << std::endl;
}

// Example usage
int main() {
    double R = 2.0;  // Bond length in atomic units
    double box = 4.0;
    int points = 50;  // Reduced for faster execution
    
    computeH2Hamiltonian(R, box, points);
    
    return 0;
}

Key Features

Numerical Considerations