Program Listing for File potentialDihedralHarmonics.cpp#

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

#include "potentialDihedralHarmonics.hpp"

real ComputeVertexDihedralEnergy_lambda(const real3 &r0,
                                        const real3 &r1,
                                        const real3 &r2,
                                        const real3 &r3,
                                        const real &kappa,
                                        const real &theta0,
                                        const BoxType &box)
{
    // std::cout<<"test seg"<<std::endl;
    real3 r01, r20, r02, r32;
    r01 = pymemb::vector_subtract(r0, r1, box);
    r02 = pymemb::vector_subtract(r0, r2, box);
    r20 = pymemb::vector_subtract(r2, r0, box);
    r32 = pymemb::vector_subtract(r3, r2, box);

    real3 nk, nl;
    vcross(nk, r01, r02);
    vcross(nl, r32, r02);

    real nksq, nlsq, r02norm;
    nksq = vdot(nk, nk);
    nlsq = vdot(nl, nl);
    r02norm = sqrt(vdot(r02, r02));

    real r02inv, nk2inv, nl2inv, nknlinv;
    r02inv = nk2inv = nl2inv = 0.0;
    if (r02norm > 0.0)
        r02inv = 1.0 / r02norm;
    if (nksq > 0.0)
        nk2inv = 1.0 / nksq;
    if (nlsq > 0.0)
        nl2inv = 1.0 / nlsq;
    nknlinv = sqrt(nk2inv * nl2inv);

    real cos_theta, sin_theta, cos_theta_0, sin_theta_0;
    cos_theta = -vdot(nk, nl) * nknlinv;
    sin_theta = -r02norm * nknlinv * vdot(nk, r32);
    sin_theta_0 = sin(theta0);
    cos_theta_0 = cos(theta0);

    if (cos_theta > 1.0)
        cos_theta = 1.0;
    if (cos_theta < -1.0)
        cos_theta = -1.0;

    // real energy = 0.5 * kappa * (1.0 - cos_theta * cos_theta_0 - sin_theta * sin_theta_0);
    return (kappa * (1.0 - cos_theta * cos_theta_0 - sin_theta * sin_theta_0));
}

void ComputeVertexDihedralEnergy_fn(const int Numedges,
                                    HE_EdgeProp *edges,
                                    const HE_FaceProp *faces,
                                    const HE_VertexProp *vertices,
                                    const real *_kappa,
                                    const real *_theta0,
                                    const BoxType _box)
{
    for (int edge_index = 0; edge_index < Numedges; edge_index++)
    {

        if (edges[edge_index].boundary == false)
        {
            int type = edges[edge_index].type;

            int v0 = edges[edge_index].v0;
            int v1 = edges[edge_index].v1;
            int v2 = edges[edge_index].v2;
            int v3 = edges[edge_index].v3;
            real3 r0 = vertices[v0].r;
            real3 r1 = vertices[v1].r;
            real3 r2 = vertices[v2].r;
            real3 r3 = vertices[v3].r;

            real energy = ComputeVertexDihedralEnergy_lambda(r0, r1, r2, r3, _kappa[type], _theta0[type], _box);

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

void ComputeVertexDihedralEnergy::compute_energy(void)
{

    ComputeVertexDihedralEnergy_fn(_system.Numedges,
                                   &_system.edges[0],
                                   &_system.faces[0],
                                   &_system.vertices[0],
                                   &m_kappa[0],
                                   &m_theta0[0],
                                   _system.get_box());
}

real ComputeVertexDihedralEnergy::compute_edge_energy(int query_edge_index)
{
    // we need to loop the 4 edges that are connected to the edge_index
    auto edge_index_vec = _system.get_edge_neighbours_host(query_edge_index);
    // reset energy
    real edge_energy = 0.0;
    for (auto edge_index : edge_index_vec)
    {
        if (_system.edges[edge_index].boundary == false)
        {
            int type = _system.edges[edge_index].type;

            int v0 = _system.edges[edge_index].v0;
            int v1 = _system.edges[edge_index].v1;
            int v2 = _system.edges[edge_index].v2;
            int v3 = _system.edges[edge_index].v3;
            real3 r0 = _system.vertices[v0].r;
            real3 r1 = _system.vertices[v1].r;
            real3 r2 = _system.vertices[v2].r;
            real3 r3 = _system.vertices[v3].r;

            edge_energy += ComputeVertexDihedralEnergy_lambda(r0, r1, r2, r3, m_kappa[type], m_theta0[type], _system.get_box());
        }
    }
    return edge_energy;
}

real ComputeVertexDihedralEnergy::compute_vertex_energy(int query_vertex_index)
{
    real energy = 0.0;
    int he = _system.vertices[query_vertex_index]._hedge;
    int first = he;
    int he_vec[2];
    do
    {
        he_vec[0] = he;
        he_vec[1] = _system.halfedges[he].next;
        for (auto he_index : he_vec)
        {
            int edge_index = _system.halfedges[he_index].edge;
            if (_system.edges[edge_index].boundary == false)
            {
                int type = _system.edges[edge_index].type;

                int v0 = _system.edges[edge_index].v0;
                int v1 = _system.edges[edge_index].v1;
                int v2 = _system.edges[edge_index].v2;
                int v3 = _system.edges[edge_index].v3;
                real3 r0 = _system.vertices[v0].r;
                real3 r1 = _system.vertices[v1].r;
                real3 r2 = _system.vertices[v2].r;
                real3 r3 = _system.vertices[v3].r;

                energy += ComputeVertexDihedralEnergy_lambda(r0, r1, r2, r3, m_kappa[type], m_theta0[type], _system.get_box());
            }
        }
        int he_prev = _system.halfedges[he].prev;
        he = _system.halfedges[he_prev].pair;
    } while (he != first);
    return energy;
}

void ComputeVertexBendingForce_fn(int Numedges,
                                  const HE_EdgeProp *edges,
                                  const HE_FaceProp *faces,
                                  HE_VertexProp *vertices,
                                  const real *_kappa,
                                  const real *_theta0,
                                  const BoxType _box)
{
    for (int edge_index = 0; edge_index < Numedges; edge_index++)
    {

        if (edges[edge_index].boundary == false)
        {
            int type = edges[edge_index].type;

            int v0 = edges[edge_index].v0;
            int v1 = edges[edge_index].v1;
            int v2 = edges[edge_index].v2;
            int v3 = edges[edge_index].v3;
            real3 r0 = vertices[v0].r;
            real3 r1 = vertices[v1].r;
            real3 r2 = vertices[v2].r;
            real3 r3 = vertices[v3].r;

            real3 r01 = pymemb::vector_subtract(r0, r1, _box);
            real3 r02 = pymemb::vector_subtract(r0, r2, _box);
            // real3 r20 = pymemb::vector_subtract(r2, r0, _box);
            real3 r32 = pymemb::vector_subtract(r3, r2, _box);

            real3 nk, nl;
            vcross(nk, r01, r02);
            vcross(nl, r32, r02);

            real nksq, nlsq, r02norm;
            nksq = vdot(nk, nk);
            nlsq = vdot(nl, nl);
            r02norm = sqrt(vdot(r02, r02));

            real r02inv = 0.0, nk2inv = 0.0, nl2inv = 0.0, nknlinv;
            if (r02norm > 0.0)
                r02inv = 1.0 / r02norm;
            if (nksq > 0.0)
                nk2inv = 1.0 / nksq;
            if (nlsq > 0.0)
                nl2inv = 1.0 / nlsq;
            nknlinv = sqrt(nk2inv * nl2inv);

            real cos_theta, sin_theta, cos_theta_0, sin_theta_0;
            cos_theta = -vdot(nk, nl) * nknlinv;
            sin_theta = -r02norm * nknlinv * vdot(nk, r32);
            sin_theta_0 = sin(_theta0[type]);
            cos_theta_0 = cos(_theta0[type]);

            if (cos_theta > 1.0)
                cos_theta = 1.0;
            if (cos_theta < -1.0)
                cos_theta = -1.0;

            real df = -_kappa[type] * (sin_theta * cos_theta_0 - cos_theta * sin_theta_0);

            real dE0k = vdot(r01, r02) * nk2inv * r02inv - r02norm * nk2inv;
            real dE0l = -vdot(r32, r02) * nl2inv * r02inv;
            real dE1k = nk2inv * r02norm;
            real dE2k = -vdot(r01, r02) * nk2inv * r02inv;
            real dE2l = vdot(r32, r02) * nl2inv * r02inv - r02norm * nl2inv;
            real dE3l = nl2inv * r02norm;

            real3 F0, F1, F2, F3;
            Xvec2(F0, df * dE0k, nk, df * dE0l, nl);
            Xvec1(F1, df * dE1k, nk);
            Xvec2(F2, df * dE2k, nk, df * dE2l, nl);
            Xvec1(F3, df * dE3l, nl);

            vertices[v0].forceC.x += F0.x;
            vertices[v0].forceC.y += F0.y;
            vertices[v0].forceC.z += F0.z;

            vertices[v1].forceC.x += F1.x;
            vertices[v1].forceC.y += F1.y;
            vertices[v1].forceC.z += F1.z;

            vertices[v2].forceC.x += F2.x;
            vertices[v2].forceC.y += F2.y;
            vertices[v2].forceC.z += F2.z;

            vertices[v3].forceC.x += F3.x;
            vertices[v3].forceC.y += F3.y;
            vertices[v3].forceC.z += F3.z;
        }
    }
}

void ComputeVertexDihedralEnergy::compute(void)
{

    ComputeVertexBendingForce_fn(_system.Numedges,
                                 &_system.edges[0],
                                 &_system.faces[0],
                                 &_system.vertices[0],
                                 &m_kappa[0],
                                 &m_theta0[0],
                                 _system.get_box());
}

void ComputeVertexBendingStress_fn(int Numedges,
                                   const HE_EdgeProp *edges,
                                   const HE_FaceProp *faces,
                                   HE_VertexProp *vertices,
                                   const real *_kappa,
                                   const real *_theta0,
                                   realTensor *stress_group_edges,
                                   const BoxType _box)
{
    for (int edge_index = 0; edge_index < Numedges; edge_index++)
    {

        if (edges[edge_index].boundary == false)
        {
            int type = edges[edge_index].type;

            int v0 = edges[edge_index].v0;
            int v1 = edges[edge_index].v1;
            int v2 = edges[edge_index].v2;
            int v3 = edges[edge_index].v3;
            real3 r0 = vertices[v0].r;
            real3 r1 = vertices[v1].r;
            real3 r2 = vertices[v2].r;
            real3 r3 = vertices[v3].r;

            real3 r01, r20, r02, r32;
            r01 = pymemb::vector_subtract(r0, r1, _box);
            r02 = pymemb::vector_subtract(r0, r2, _box);
            r20 = pymemb::vector_subtract(r2, r0, _box);
            r32 = pymemb::vector_subtract(r3, r2, _box);

            real3 nk, nl;
            vcross(nk, r01, r02);
            vcross(nl, r32, r02);

            real nksq, nlsq, r02norm;
            nksq = vdot(nk, nk);
            nlsq = vdot(nl, nl);
            r02norm = sqrt(vdot(r02, r02));

            real r02inv, nk2inv, nl2inv, nknlinv;
            r02inv = nk2inv = nl2inv = 0.0;
            if (r02norm > 0.0)
                r02inv = 1.0 / r02norm;
            if (nksq > 0.0)
                nk2inv = 1.0 / nksq;
            if (nlsq > 0.0)
                nl2inv = 1.0 / nlsq;
            nknlinv = sqrt(nk2inv * nl2inv);

            real cos_theta, sin_theta, cos_theta_0, sin_theta_0;
            cos_theta = -vdot(nk, nl) * nknlinv;
            sin_theta = -r02norm * nknlinv * vdot(nk, r32);
            sin_theta_0 = sin(_theta0[type]);
            cos_theta_0 = cos(_theta0[type]);

            if (cos_theta > 1.0)
                cos_theta = 1.0;
            if (cos_theta < -1.0)
                cos_theta = -1.0;

            real df = -_kappa[type] * (sin_theta * cos_theta_0 - cos_theta * sin_theta_0);

            real dE0k = vdot(r01, r02) * nk2inv * r02inv - r02norm * nk2inv;
            real dE0l = -vdot(r32, r02) * nl2inv * r02inv;
            real dE1k = nk2inv * r02norm;
            real dE2k = -vdot(r01, r02) * nk2inv * r02inv;
            real dE2l = vdot(r32, r02) * nl2inv * r02inv - r02norm * nl2inv;
            real dE3l = nl2inv * r02norm;

            real3 F0, F1, F2, F3;
            Xvec2(F0, df * dE0k, nk, df * dE0l, nl);
            Xvec1(F1, df * dE1k, nk);
            Xvec2(F2, df * dE2k, nk, df * dE2l, nl);
            Xvec1(F3, df * dE3l, nl);

            // This might be wrong so have to be checked
            // real check J. Chem. Phys. 131, 154107 (2009) page 4 Eq. 21
            // Asume that v0 is in the local replica then contruct the r1, r2, r3 based on it
            // real3 r01, r02,
            real3 r03;
            r01.x *= -1.0;
            r01.y *= -1.0;
            r01.z *= -1.0; // r01 = pymemb::vector_subtract(r1, r0, _box);
            r02.x *= -1.0;
            r02.y *= -1.0;
            r02.z *= -1.0; // r02 = pymemb::vector_subtract(r2, r0, _box);
            r03 = pymemb::vector_subtract(r3, r0, _box);
            real3 uw_r3, uw_r2, uw_r1 /*,uw_r0,*/;
            // uw_r0 = r0;
            uw_r1 = pymemb::vector_sum(r0, r01);
            uw_r2 = pymemb::vector_sum(r0, r02);
            uw_r3 = pymemb::vector_sum(r0, r03);

            stress_group_edges[edge_index].xx += r0.x * F0.x + uw_r1.x * F1.x + uw_r2.x * F2.x + uw_r3.x * F3.x;
            stress_group_edges[edge_index].xy += r0.x * F0.y + uw_r1.x * F1.y + uw_r2.x * F2.y + uw_r3.x * F3.y;
            stress_group_edges[edge_index].xz += r0.x * F0.z + uw_r1.x * F1.z + uw_r2.x * F2.z + uw_r3.x * F3.z;

            stress_group_edges[edge_index].yx += r0.y * F0.x + uw_r1.y * F1.x + uw_r2.y * F2.x + uw_r3.y * F3.x;
            stress_group_edges[edge_index].yy += r0.y * F0.y + uw_r1.y * F1.y + uw_r2.y * F2.y + uw_r3.y * F3.y;
            stress_group_edges[edge_index].yz += r0.y * F0.z + uw_r1.y * F1.z + uw_r2.y * F2.z + uw_r3.y * F3.z;

            stress_group_edges[edge_index].zx += r0.z * F0.x + uw_r1.z * F1.x + uw_r2.z * F2.x + uw_r3.z * F3.x;
            stress_group_edges[edge_index].zy += r0.z * F0.y + uw_r1.z * F1.y + uw_r2.z * F2.y + uw_r3.z * F3.y;
            stress_group_edges[edge_index].zz += r0.z * F0.z + uw_r1.z * F1.z + uw_r2.z * F2.z + uw_r3.z * F3.z;
        }
    }
}

void ComputeVertexDihedralEnergy::compute_stress(void)
{
    ComputeVertexBendingStress_fn(_system.Numedges,
                                  &_system.edges[0],
                                  &_system.faces[0],
                                  &_system.vertices[0],
                                  &m_kappa[0],
                                  &m_theta0[0],
                                  &_system.stress_group_edges[0],
                                  _system.get_box());
}

void ComputeVertexBendingStressAtom_fn(int Numedges,
                                       const HE_EdgeProp *edges,
                                       const HE_FaceProp *faces,
                                       HE_VertexProp *vertices,
                                       const real *_kappa,
                                       const real *_theta0,
                                       realTensor *stress_virial_atom,
                                       const BoxType _box)
{
    for (int edge_index = 0; edge_index < Numedges; edge_index++)
    {

        if (edges[edge_index].boundary == false)
        {
            int type = edges[edge_index].type;

            int v0 = edges[edge_index].v0;
            int v1 = edges[edge_index].v1;
            int v2 = edges[edge_index].v2;
            int v3 = edges[edge_index].v3;
            real3 r0 = vertices[v0].r;
            real3 r1 = vertices[v1].r;
            real3 r2 = vertices[v2].r;
            real3 r3 = vertices[v3].r;

            real3 r01, r20, r02, r32;
            r01 = pymemb::vector_subtract(r0, r1, _box);
            r02 = pymemb::vector_subtract(r0, r2, _box);
            r20 = pymemb::vector_subtract(r2, r0, _box);
            r32 = pymemb::vector_subtract(r3, r2, _box);

            real3 nk, nl;
            vcross(nk, r01, r02);
            vcross(nl, r32, r02);

            real nksq, nlsq, r02norm;
            nksq = vdot(nk, nk);
            nlsq = vdot(nl, nl);
            r02norm = sqrt(vdot(r02, r02));

            real r02inv, nk2inv, nl2inv, nknlinv;
            r02inv = nk2inv = nl2inv = 0.0;
            if (r02norm > 0.0)
                r02inv = 1.0 / r02norm;
            if (nksq > 0.0)
                nk2inv = 1.0 / nksq;
            if (nlsq > 0.0)
                nl2inv = 1.0 / nlsq;
            nknlinv = sqrt(nk2inv * nl2inv);

            real cos_theta, sin_theta, cos_theta_0, sin_theta_0;
            cos_theta = -vdot(nk, nl) * nknlinv;
            sin_theta = -r02norm * nknlinv * vdot(nk, r32);
            sin_theta_0 = sin(_theta0[type]);
            cos_theta_0 = cos(_theta0[type]);

            if (cos_theta > 1.0)
                cos_theta = 1.0;
            if (cos_theta < -1.0)
                cos_theta = -1.0;

            real df = -_kappa[type] * (sin_theta * cos_theta_0 - cos_theta * sin_theta_0);

            real dE0k = vdot(r01, r02) * nk2inv * r02inv - r02norm * nk2inv;
            real dE0l = -vdot(r32, r02) * nl2inv * r02inv;
            real dE1k = nk2inv * r02norm;
            real dE2k = -vdot(r01, r02) * nk2inv * r02inv;
            real dE2l = vdot(r32, r02) * nl2inv * r02inv - r02norm * nl2inv;
            real dE3l = nl2inv * r02norm;

            real3 F0, F1, F2, F3;
            Xvec2(F0, df * dE0k, nk, df * dE0l, nl);
            Xvec1(F1, df * dE1k, nk);
            Xvec2(F2, df * dE2k, nk, df * dE2l, nl);
            Xvec1(F3, df * dE3l, nl);

            /*
            FOR NOW USE THE GROUP METHOD FOR AN EDGE AND DEVIDE BY 4 IN EACH ATOM
            */
            // This might be wrong so have to be checked
            // real check J. Chem. Phys. 131, 154107 (2009) page 4 Eq. 21
            // Asume that v0 is in the local replica then contruct the r1, r2, r3 based on it
            // real3 r01, r02,
            real3 r03;
            r01.x *= -1.0;
            r01.y *= -1.0;
            r01.z *= -1.0; // r01 = pymemb::vector_subtract(r1, r0, _box);
            r02.x *= -1.0;
            r02.y *= -1.0;
            r02.z *= -1.0; // r02 = pymemb::vector_subtract(r2, r0, _box);
            r03 = pymemb::vector_subtract(r3, r0, _box);
            real3 uw_r3, uw_r2, uw_r1 /*,uw_r0,*/;
            // uw_r0 = r0;
            uw_r1 = pymemb::vector_sum(r0, r01);
            uw_r2 = pymemb::vector_sum(r0, r02);
            uw_r3 = pymemb::vector_sum(r0, r03);

            realTensor stress_group_edge;
            stress_group_edge.xx = r0.x * F0.x + uw_r1.x * F1.x + uw_r2.x * F2.x + uw_r3.x * F3.x;
            stress_group_edge.xy = r0.x * F0.y + uw_r1.x * F1.y + uw_r2.x * F2.y + uw_r3.x * F3.y;
            stress_group_edge.xz = r0.x * F0.z + uw_r1.x * F1.z + uw_r2.x * F2.z + uw_r3.x * F3.z;

            stress_group_edge.yx = r0.y * F0.x + uw_r1.y * F1.x + uw_r2.y * F2.x + uw_r3.y * F3.x;
            stress_group_edge.yy = r0.y * F0.y + uw_r1.y * F1.y + uw_r2.y * F2.y + uw_r3.y * F3.y;
            stress_group_edge.yz = r0.y * F0.z + uw_r1.y * F1.z + uw_r2.y * F2.z + uw_r3.y * F3.z;

            stress_group_edge.zx = r0.z * F0.x + uw_r1.z * F1.x + uw_r2.z * F2.x + uw_r3.z * F3.x;
            stress_group_edge.zy = r0.z * F0.y + uw_r1.z * F1.y + uw_r2.z * F2.y + uw_r3.z * F3.y;
            stress_group_edge.zz = r0.z * F0.z + uw_r1.z * F1.z + uw_r2.z * F2.z + uw_r3.z * F3.z;

            int vvec[4] = {v0, v1, v2, v3};
            for (auto v : vvec)
            {
                stress_virial_atom[v].xx += 0.25 * stress_group_edge.xx;
                stress_virial_atom[v].xy += 0.25 * stress_group_edge.xy;
                stress_virial_atom[v].xz += 0.25 * stress_group_edge.xz;

                stress_virial_atom[v].yx += 0.25 * stress_group_edge.yx;
                stress_virial_atom[v].yy += 0.25 * stress_group_edge.yy;
                stress_virial_atom[v].yz += 0.25 * stress_group_edge.yz;

                stress_virial_atom[v].zx += 0.25 * stress_group_edge.zx;
                stress_virial_atom[v].zy += 0.25 * stress_group_edge.zy;
                stress_virial_atom[v].zz += 0.25 * stress_group_edge.zz;
            }
        }
    }
}

void ComputeVertexDihedralEnergy::compute_atomic_stress(void)
{
    ComputeVertexBendingStressAtom_fn(_system.Numedges,
                                      &_system.edges[0],
                                      &_system.faces[0],
                                      &_system.vertices[0],
                                      &m_kappa[0],
                                      &m_theta0[0],
                                      &_system.stress_virial_atom[0],
                                      _system.get_box());
}