Program Listing for File potentialHarmonic.cpp#

Return to documentation for file (pymembrane/cppmodule/src/potentials/potentialHarmonic.cpp)

#include "potentialHarmonic.hpp"

inline real ComputeEdgeHarmonicEnergy(const real3 &rij,
                                      const real &k,
                                      const real &l0)
{
    auto dr = sqrt(vdot(rij, rij));
    return (0.5 * k * (dr - l0) * (dr - l0));
}

void ComputeVertexHarmonicEnergy_fn(const int Numedges,
                                    HE_EdgeProp *edges,
                                    const HE_VertexProp *vertices,
                                    const real *__restrict__ _k,
                                    const real *__restrict__ _l0,
                                    const BoxType &_box)
{
    for (int edge_index = 0; edge_index < Numedges; edge_index++)
    {
        int type = edges[edge_index].type;
        int v1 = edges[edge_index].i;
        auto r1 = vertices[v1].r;
        int v2 = edges[edge_index].j;
        auto r2 = vertices[v2].r;
        auto rij = pymemb::vector_subtract(r2, r1, _box);

        auto energy = ComputeEdgeHarmonicEnergy(rij, _k[type], _l0[type]);

        edges[edge_index].energy += energy;
    }
}

double ComputeVertexHarmonicEnergy::compute_edge_energy(int query_edge_index)
{
    int type = _system.edges[query_edge_index].type;
    int v1 = _system.edges[query_edge_index].i;
    auto r1 = _system.vertices[v1].r;
    int v2 = _system.edges[query_edge_index].j;
    auto r2 = _system.vertices[v2].r;
    auto rij = pymemb::vector_subtract(r2, r1, _system.get_box());
    double edge_energy = ComputeEdgeHarmonicEnergy(rij, m_k[type], m_l0[type]);
    return edge_energy;
}

double ComputeVertexHarmonicEnergy::compute_vertex_energy(int query_vertex_index)
{

    double energy = 0.0;
    int he = _system.vertices[query_vertex_index]._hedge;
    int first = he;
    do
    {
        int edge_index = _system.halfedges[he].edge;
        int type = _system.edges[edge_index].type;
        int v1 = _system.edges[edge_index].i;
        auto r1 = _system.vertices[v1].r;
        int v2 = _system.edges[edge_index].j;
        auto r2 = _system.vertices[v2].r;
        auto rij = pymemb::vector_subtract(r2, r1, _system.get_box());
        energy += 0.5 * ComputeEdgeHarmonicEnergy(rij, m_k[type], m_l0[type]);

        int he_pair = _system.halfedges[he].pair;
        he = _system.halfedges[he_pair].next;
    } while ((he != first));
    return energy;
}

void ComputeVertexHarmonicEnergy::compute_energy(void)
{

    ComputeVertexHarmonicEnergy_fn(_system.Numedges,
                                   &_system.edges[0],
                                   &_system.vertices[0],
                                   &m_k[0],
                                   &m_l0[0],
                                   _system.get_box());
}

inline real ComputeVertexHarmonicForce(const real3& rij,
                                  const real& k,
                                  const real& l0)
{
    auto dr = sqrt(vdot(rij, rij));
    auto fval = k * (dr - l0) / dr;
    return fval;
}

void ComputeVertexHarmonicForce_fn(const int Numedges,
                                   HE_VertexProp *vertices,
                                   const HE_EdgeProp *__restrict__ edges,
                                   const real *__restrict__ _k,
                                   const real *__restrict__ _l0,
                                   const BoxType &_box)
{
    for (int edge_index = 0; edge_index < Numedges; edge_index++)
    {
        int type = edges[edge_index].type;
        int v1 = edges[edge_index].i;
        auto r1 = vertices[v1].r;
        int v2 = edges[edge_index].j;
        auto r2 = vertices[v2].r;
        auto rij = pymemb::vector_subtract(r2, r1, _box);

        double fval = ComputeVertexHarmonicForce(rij, _k[type], _l0[type]);

        vertices[v1].forceC.x += fval * rij.x;
        vertices[v1].forceC.y += fval * rij.y;
        vertices[v1].forceC.z += fval * rij.z;

        vertices[v2].forceC.x += -1.0 * fval * rij.x;
        vertices[v2].forceC.y += -1.0 * fval * rij.y;
        vertices[v2].forceC.z += -1.0 * fval * rij.z;
    }
}

void ComputeVertexHarmonicEnergy::compute(void)
{

    ComputeVertexHarmonicForce_fn(_system.Numedges,
                                  &_system.vertices[0],
                                  &_system.edges[0],
                                  &m_k[0],
                                  &m_l0[0],
                                  _system.get_box());
}

void ComputeVertexHarmonicStress_fn(const int Numedges,
                                    HE_VertexProp *vertices,
                                    const HE_EdgeProp *__restrict__ edges,
                                    const real *__restrict__ _k,
                                    const real *__restrict__ _l0,
                                    realTensor *stress_group_edges,
                                    const BoxType &_box)
{
    for (int edge_index = 0; edge_index < Numedges; edge_index++)
    {

        int type = edges[edge_index].type;
        int v1 = edges[edge_index].i;
        auto r1 = vertices[v1].r;
        int v2 = edges[edge_index].j;
        auto r2 = vertices[v2].r;
        auto r12 = pymemb::vector_subtract(r2, r1, _box);

        double fval = ComputeVertexHarmonicForce(r12, _k[type], _l0[type]);


        // J. Chem. Phys. 131, 154107 (2009) page 4 Eq. 21
        // Asume that v1 is in the local replica then construct the r1, r2 based on it
        real3 uw_r2;
        uw_r2 = pymemb::vector_sum(r1, r12);

        real3 F2, F1;
        F1.x = fval * r12.x;
        F1.y = fval * r12.y;
        F1.z = fval * r12.z;

        F2.x = -fval * r12.x;
        F2.y = -fval * r12.y;
        F2.z = -fval * r12.z;

        stress_group_edges[edge_index].xx += r1.x * F1.x + uw_r2.x * F2.x;
        stress_group_edges[edge_index].xy += r1.x * F1.y + uw_r2.x * F2.y;
        stress_group_edges[edge_index].xz += r1.x * F1.z + uw_r2.x * F2.z;

        stress_group_edges[edge_index].yx += r1.y * F1.x + uw_r2.y * F2.x;
        stress_group_edges[edge_index].yy += r1.y * F1.y + uw_r2.y * F2.y;
        stress_group_edges[edge_index].yz += r1.y * F1.z + uw_r2.y * F2.z;

        stress_group_edges[edge_index].zx += r1.z * F1.x + uw_r2.z * F2.x;
        stress_group_edges[edge_index].zy += r1.z * F1.y + uw_r2.z * F2.y;
        stress_group_edges[edge_index].zz += r1.z * F1.z + uw_r2.z * F2.z;
    }
}

void ComputeVertexHarmonicEnergy::compute_stress(void)
{
    ComputeVertexHarmonicStress_fn(_system.Numedges,
                                   &_system.vertices[0],
                                   &_system.edges[0],
                                   &m_k[0],
                                   &m_l0[0],
                                   &_system.stress_group_edges[0],
                                   _system.get_box());
}

void ComputeVertexHarmonicStressAtom_fn(const int Numedges,
                                        HE_VertexProp *vertices,
                                        const HE_EdgeProp *__restrict__ edges,
                                        const real *__restrict__ _k,
                                        const real *__restrict__ _l0,
                                        realTensor *stress_virial_atom,
                                        const BoxType &_box)
{
    for (int edge_index = 0; edge_index < Numedges; edge_index++)
    {
        int type = edges[edge_index].type;
        int v1 = edges[edge_index].i;
        auto r1 = vertices[v1].r;
        int v2 = edges[edge_index].j;
        auto r2 = vertices[v2].r;
        auto r12 = pymemb::vector_subtract(r2, r1, _box);

        double fval = ComputeVertexHarmonicForce(r12, _k[type], _l0[type]);

        // J. Chem. Phys. 131, 154107 (2009) page 4 Eq. 21
        // Asume that v1 is in the local replica then contruct the r1, r2 based on it
        real3 uw_r2;
        uw_r2 = pymemb::vector_sum(r1, r12);

        real3 F2, F1;
        F1.x = fval * r12.x;
        F1.y = fval * r12.y;
        F1.z = fval * r12.z;

        F2.x = -fval * r12.x;
        F2.y = -fval * r12.y;
        F2.z = -fval * r12.z;

        // virial as we know it with PBC
        stress_virial_atom[v1].xx += 0.5 * r12.x * F1.x;
        stress_virial_atom[v1].xy += 0.5 * r12.x * F1.y;
        stress_virial_atom[v1].xz += 0.5 * r12.x * F1.z;
        stress_virial_atom[v1].yx += 0.5 * r12.y * F1.x;
        stress_virial_atom[v1].yy += 0.5 * r12.y * F1.y;
        stress_virial_atom[v1].yz += 0.5 * r12.y * F1.z;
        stress_virial_atom[v1].zx += 0.5 * r12.z * F1.x;
        stress_virial_atom[v1].zy += 0.5 * r12.z * F1.y;
        stress_virial_atom[v1].zz += 0.5 * r12.z * F1.z;

        stress_virial_atom[v2].xx += -0.5 * r12.x * F2.x;
        stress_virial_atom[v2].xy += -0.5 * r12.x * F2.y;
        stress_virial_atom[v2].xz += -0.5 * r12.x * F2.z;
        stress_virial_atom[v2].yx += -0.5 * r12.y * F2.x;
        stress_virial_atom[v2].yy += -0.5 * r12.y * F2.y;
        stress_virial_atom[v2].yz += -0.5 * r12.y * F2.z;
        stress_virial_atom[v2].zx += -0.5 * r12.z * F2.x;
        stress_virial_atom[v2].zy += -0.5 * r12.z * F2.y;
        stress_virial_atom[v2].zz += -0.5 * r12.z * F2.z;
    }
}

void ComputeVertexHarmonicEnergy::compute_atomic_stress(void)
{
    ComputeVertexHarmonicStressAtom_fn(_system.Numedges,
                                       &_system.vertices[0],
                                       &_system.edges[0],
                                       &m_k[0],
                                       &m_l0[0],
                                       &_system.stress_virial_atom[0],
                                       _system.get_box());
}