Program Listing for File potentialBendingHelfrich.cpp#

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

#include "potentialBendingHelfrich.hpp"

// update face global index, normal vector, area and cotangent
void update_face(const real3 *local_vertices,
                 real *local_cot,
                 real *local_area,
                 real3 *local_sigma,
                 real &At,
                 real3 &nf,
                 const BoxType &_box)
{
    nf = pymemb::compute_normal_triangle(local_vertices[0], local_vertices[1], local_vertices[2], _box);
    At = 0.5 * sqrt(vdot(nf, nf));
    for (int i = 0; i < 3; ++i)
    {
        int k = (i + 1) % 3, l = (i + 2) % 3;
        real3 ri = local_vertices[i];
        real3 rk = local_vertices[k];
        real3 rl = local_vertices[l];
        auto ril = pymemb::vector_subtract(ri, rl, _box);
        auto rik = pymemb::vector_subtract(ri, rk, _box);
        auto rkl = pymemb::vector_subtract(rk, rl, _box);
        local_cot[k] = -vdot(rik, rkl) / 2. / At;
        local_cot[l] = vdot(ril, rkl) / 2. / At;
        Xvec2(local_sigma[i], 0.5 * local_cot[k], ril, 0.5 * local_cot[l], rik);
        local_area[i] = 0.125 * local_cot[k] * vdot(ril, ril) + 0.125 * local_cot[l] * vdot(rik, rik);
    }
}

void compute_mean_curvature_operature(int Numvertices,
                                      int Numfaces,
                                      const HE_VertexProp *vertices,
                                      const HE_FaceProp *faces,
                                      real3 *meanH_operator,
                                      real *meanH,
                                      const BoxType &_box)
{
    std::vector<real> global_area(Numvertices);
    std::vector<real3> global_sigma(Numvertices);
    std::vector<real3> global_normal(Numvertices);

    real At;
    real3 nf;

    for (int face_index = 0; face_index < Numfaces; face_index++)
    {
        int local_index[3];
        real3 local_vertices[3];
        real local_cot[3];
        real local_area[3];
        real3 local_sigma[3];

        local_index[0] = faces[face_index].v1;
        local_index[1] = faces[face_index].v2;
        local_index[2] = faces[face_index].v3;

        local_vertices[0] = vertices[local_index[0]].r;
        local_vertices[1] = vertices[local_index[1]].r;
        local_vertices[2] = vertices[local_index[2]].r;

        update_face(local_vertices,
                    local_cot,
                    local_area,
                    local_sigma,
                    At,
                    nf,
                    _box);
        for (int i = 0; i < 3; ++i)
        {
            int vi = local_index[i];

            global_area[vi] += local_area[i];
            global_sigma[vi] = pymemb::vector_sum(global_sigma[vi], local_sigma[i]);
            global_normal[vi] = pymemb::vector_sum(global_normal[vi], nf);
        }
    }
    for (int vertex_index = 0; vertex_index < Numvertices; vertex_index++)
    {
        real vertex_area = global_area[vertex_index];
        real3 nv = global_normal[vertex_index];
        real3 sigma = global_sigma[vertex_index];
        real sign = (vdot(nv, sigma) > 0.) ? 1. : -1.;
        Xvec1(meanH_operator[vertex_index], 1. / vertex_area, sigma);
        meanH[vertex_index] = sign * sqrt(vdot(sigma, sigma)) / vertex_area;
    }
}

void ComputeVertexBendingHelfrichEnergyComputeForce_lambda(const HE_VertexProp *vertices,
                                                           const real3 *meanH_operator,
                                                           const real *meanH,
                                                           const int *local_index,
                                                           const real3 *local_vertices,
                                                           const real *kappaH,
                                                           const real *kappaG,
                                                           const real *H0,
                                                           const BoxType &_box,
                                                           real3 *vertex_force)
{
    vertex_force[0].x = vertex_force[0].y = vertex_force[0].z = 0.0;
    vertex_force[1].x = vertex_force[1].y = vertex_force[1].z = 0.0;
    vertex_force[2].x = vertex_force[2].y = vertex_force[2].z = 0.0;

    real local_cot[3];
    real local_area[3];
    real3 local_sigma[3];
    real At;
    real3 nf;

    update_face(local_vertices,
                local_cot,
                local_area,
                local_sigma,
                At,
                nf,
                _box);

    int vi, vj, vk, vl;
    real a, H, H_0, k_b, k_g, cot_i, cot_j, cot_k, cot_l;
    real3 ri, rj, rk, rl, rij, rjk, ril, rik, rkl;
    real3 Hop, sigmai, sigmaj, dA, force1, force2, forceg;

    for (int i = 0; i < 3; ++i)
    {
        // force on i:
        vi = local_index[i];
        ri = vertices[vi].r;
        sigmai = local_sigma[i];
        for (int j = 0; j < 3; ++j)
        {
            a = local_area[j];
            sigmaj = local_sigma[j];
            Hop = meanH_operator[local_index[j]];
            H = meanH[local_index[j]];
            int type = vertices[local_index[j]].type;
            H_0 = H0[type];
            k_b = kappaH[type];
            k_g = kappaG[type];
            vj = local_index[j];
            cot_j = local_cot[j];
            if (i == j)
            {
                vk = local_index[(i + 1) % 3];
                vl = local_index[(i + 2) % 3];
                rk = vertices[vk].r;
                rl = vertices[vl].r;
                ril = pymemb::vector_subtract(ri, rl, _box);
                rik = pymemb::vector_subtract(ri, rk, _box);
                rkl = pymemb::vector_subtract(rk, rl, _box);
                cot_k = local_cot[(i + 1) % 3];
                cot_l = local_cot[(i + 2) % 3];
                Xvec3(dA,
                      (1. - a / At), sigmai,
                      -0.125 * (cot_k + cot_l), rik,
                      -0.125 * (cot_k + cot_l), ril);
                Xvec1(force1,
                      0.5 * (H - 2. * H_0) * (H + 2. * H_0), dA);
                Xvec3(force2,
                      -1.0 * vdot(Hop, sigmai), sigmai,
                      +0.25 * vdot(rkl, rkl), Hop,
                      -0.25 * vdot(rkl, Hop), rkl);
                if (vdot(force2, force2) > 0.)
                    aXvec(-(H - 2. * H_0) / (H * At), force2);

                Xvec2(forceg,
                      -1.0 * cot_k / vdot(rik, rik), rik,
                      -1.0 * cot_l / vdot(ril, ril), ril);
            }
            else
            {
                vj = local_index[j];
                vk = local_index[3 - i - j];
                rj = vertices[vj].r;
                rk = vertices[vk].r;
                rij = pymemb::vector_subtract(ri, rj, _box);
                rik = pymemb::vector_subtract(ri, rk, _box);
                rjk = pymemb::vector_subtract(rj, rk, _box);
                cot_i = local_cot[i];
                cot_k = local_cot[3 - i - j];
                Xvec3(dA,
                      (0.5 - a / At), sigmai,
                      +0.125 * (2 * cot_k), rik,
                      +0.125 * (cot_i - cot_k), rjk);
                Xvec1(force1,
                      0.5 * (H - 2. * H_0) * (H + 2. * H_0), dA);
                Xvec4(force2,
                      +1.0 * vdot(Hop, sigmaj), sigmai,
                      -0.5 * vdot(Hop, rjk), rik,
                      +0.25 * vdot(Hop, rik), rjk,
                      +0.25 * vdot(rjk, rik), Hop);

                if (vdot(force2, force2) > 0.)
                    aXvec((H - 2. * H_0) / (H * At), force2);

                Xvec2(forceg,
                      0.5 / At, rjk,
                      cot_j / vdot(rij, rij), rij);
            }
            vertex_force[i].x += k_b * (force1.x + force2.x) + k_g * forceg.x;
            vertex_force[i].y += k_b * (force1.y + force2.y) + k_g * forceg.y;
            vertex_force[i].z += k_b * (force1.z + force2.z) + k_g * forceg.z;
        }
    }
}

void ComputeVertexBendingHelfrichEnergy::compute(void)
{

    // std::vector<real3> force(3);
    std::vector<real3> meanH_operator(_system.Numvertices);
    std::vector<real> meanH(_system.Numvertices);
    // std::vector<real3> forces(_system.Numvertices);

    compute_mean_curvature_operature(_system.Numvertices,
                                     _system.Numfaces,
                                     &_system.vertices[0],
                                     &_system.faces[0],
                                     &meanH_operator[0],
                                     &meanH[0],
                                     _system.get_box());

    for (int face_index = 0; face_index < _system.Numfaces; face_index++)
    {
        int local_index[3];
        local_index[0] = _system.faces[face_index].v1;
        local_index[1] = _system.faces[face_index].v2;
        local_index[2] = _system.faces[face_index].v3;

        real3 local_vertices[3];
        local_vertices[0] = _system.vertices[local_index[0]].r;
        local_vertices[1] = _system.vertices[local_index[1]].r;
        local_vertices[2] = _system.vertices[local_index[2]].r;

        // Force over the vertices of the face
        real3 vertex_force[3];
        ComputeVertexBendingHelfrichEnergyComputeForce_lambda(&_system.vertices[0],
                                                              &meanH_operator[0],
                                                              &meanH[0],
                                                              &local_index[0],
                                                              &local_vertices[0],
                                                              &m_kappaH[0],
                                                              &m_kappaG[0],
                                                              &m_H0[0],
                                                              _system.get_box(),
                                                              &vertex_force[0]);

        _system.vertices[local_index[0]].forceC.x += vertex_force[0].x;
        _system.vertices[local_index[0]].forceC.y += vertex_force[0].y;
        _system.vertices[local_index[0]].forceC.z += vertex_force[0].z;

        _system.vertices[local_index[1]].forceC.x += vertex_force[1].x;
        _system.vertices[local_index[1]].forceC.y += vertex_force[1].y;
        _system.vertices[local_index[1]].forceC.z += vertex_force[1].z;

        _system.vertices[local_index[2]].forceC.x += vertex_force[2].x;
        _system.vertices[local_index[2]].forceC.y += vertex_force[2].y;
        _system.vertices[local_index[2]].forceC.z += vertex_force[2].z;
    }
}

real compute_vertex_energy_fn(int query_vertex_index,
                              const HE_VertexProp *vertices,
                              const HE_HalfEdgeProp *halfedges,
                              const real *_kappaH,
                              const real *_kappaG,
                              const real *_H0,
                              const BoxType &_box)
{
    real3 sigma, nv, nf;
    nv.x = nv.y = nv.z = 0.0;
    sigma.x = sigma.y = sigma.z = 0.0;
    real gaussian_curv = 2.0 * defPI;
    real vertex_area = 0.0;
    int v0 = query_vertex_index, v1, v2;
    int he = vertices[query_vertex_index]._hedge;
    int first = he;
    do
    {
        int edge_index = halfedges[he].edge;
        if (halfedges[he].boundary == false)
        {
            v1 = halfedges[he].vert_to;
            int he_next = halfedges[he].next;
            v2 = halfedges[he_next].vert_to;
            nf = pymemb::compute_normal_triangle(vertices[v0].r, vertices[v1].r, vertices[v2].r, _box);
            nv.x += nf.x;
            nv.y += nf.y;
            nv.z += nf.z;

            real3 r0 = vertices[v0].r;
            real3 r1 = vertices[v1].r;
            real3 r2 = vertices[v2].r;
            auto r01 = pymemb::vector_subtract(r0, r1, _box);
            auto r02 = pymemb::vector_subtract(r0, r2, _box);
            auto r10 = pymemb::vector_subtract(r1, r0, _box);
            auto r12 = pymemb::vector_subtract(r1, r2, _box);
            auto r20 = pymemb::vector_subtract(r2, r0, _box);
            auto r21 = pymemb::vector_subtract(r2, r1, _box);
            real r01_dot_r02 = vdot(r01, r02);
            real r10_dot_r12 = vdot(r10, r12);
            real r20_dot_r21 = vdot(r20, r21);
            real3 r01_cross_r02, r10_cross_r12, r20_cross_r21;
            vcross(r01_cross_r02, r01, r02);
            vcross(r10_cross_r12, r10, r12);
            vcross(r20_cross_r21, r21, r20);
            real r01_cross_r02n = sqrt(vdot(r01_cross_r02, r01_cross_r02));
            real r10_cross_r12n = sqrt(vdot(r10_cross_r12, r10_cross_r12));
            real r20_cross_r21n = sqrt(vdot(r20_cross_r21, r20_cross_r21));
            real cot_alpha = r10_dot_r12 / r10_cross_r12n;
            real cot_beta = r20_dot_r21 / r20_cross_r21n;
            real cot_weight = 0.5 * (cot_alpha + cot_beta);
            sigma.x += 0.5 * cot_alpha * r02.x + 0.5 * cot_beta * r01.x;
            sigma.y += 0.5 * cot_alpha * r02.y + 0.5 * cot_beta * r01.y;
            sigma.z += 0.5 * cot_alpha * r02.z + 0.5 * cot_beta * r01.z;

            // vertex_area += 0.125 * vdot(r02, r02) * cot_alpha + 0.125 * vdot(r01, r01) * cot_beta;
            if (r01_dot_r02 < 0 || r10_dot_r12 < 0 || r20_dot_r21 < 0)
            {
                if (r01_dot_r02 < 0)
                    vertex_area += 0.5 * r01_cross_r02n;
                else
                    vertex_area += 0.25 * r01_cross_r02n;
            }
            else
                vertex_area += 0.125 * vdot(r02, r02) * cot_alpha + 0.125 * vdot(r01, r01) * cot_beta;

            gaussian_curv -= acos(r01_dot_r02 / sqrt(vdot(r01, r01) * vdot(r02, r02)));
        }
        int he_pair = halfedges[he].pair;
        he = halfedges[he_pair].next;
    } while ((he != first));

    int type = vertices[query_vertex_index].type;
    real sign = (vdot(nv, sigma) > 0.) ? 1. : -1.;
    real H = sign * sqrt(vdot(sigma, sigma)) / vertex_area;
    real delH = H - 2.0 * _H0[type];
    return (0.5 * _kappaH[type] * delH * delH * vertex_area + _kappaG[type] * gaussian_curv);
}

void ComputeVertexBendingHelfrichEnergy::compute_energy(void)
{
    for (int vertex_index = 0; vertex_index < _system.Numvertices; vertex_index++)
    {
        real energy = compute_vertex_energy_fn(vertex_index,
                                               &_system.vertices[0],
                                               &_system.halfedges[0],
                                               &m_kappaH[0],
                                               &m_kappaG[0],
                                               &m_H0[0],
                                               _system.get_box());
        _system.vertices[vertex_index].energy += energy;
    }
}

real ComputeVertexBendingHelfrichEnergy::compute_edge_energy(int query_edge_index)
{
    pymemb::vector<int> v_index_vec(4);
    v_index_vec[0] = _system.edges[query_edge_index].v0;
    v_index_vec[1] = _system.edges[query_edge_index].v1;
    v_index_vec[2] = _system.edges[query_edge_index].v2;
    v_index_vec[3] = _system.edges[query_edge_index].v3;
    real edge_energy = 0.0;
    for (auto v_index : v_index_vec)
    {
        edge_energy += compute_vertex_energy_fn(v_index,
                                                &_system.vertices[0],
                                                &_system.halfedges[0],
                                                &m_kappaH[0],
                                                &m_kappaG[0],
                                                &m_H0[0],
                                                _system.get_box());
    }

    return (edge_energy);
}

// energy of query vertex and its neighbor vertices
real ComputeVertexBendingHelfrichEnergy::compute_vertex_energy(int query_vertex_index)
{
    real energy = compute_vertex_energy_fn(query_vertex_index,
                                           &_system.vertices[0],
                                           &_system.halfedges[0],
                                           &m_kappaH[0],
                                           &m_kappaG[0],
                                           &m_H0[0],
                                           _system.get_box());
    int he = _system.vertices[query_vertex_index]._hedge;
    int first = he;
    do
    {
        energy += compute_vertex_energy_fn(_system.halfedges[he].vert_to,
                                           &_system.vertices[0],
                                           &_system.halfedges[0],
                                           &m_kappaH[0],
                                           &m_kappaG[0],
                                           &m_H0[0],
                                           _system.get_box());
        int he_pair = _system.halfedges[he].pair;
        he = _system.halfedges[he_pair].next;
    } while ((he != first));
    return energy;
}

void ComputeVertexBendingHelfrichEnergy::compute_stress(void)
{
    // std::vector<real3> force(3);
    std::vector<real3> meanH_operator(_system.Numvertices);
    std::vector<real> meanH(_system.Numvertices);
    // std::vector<real3> forces(_system.Numvertices);

    compute_mean_curvature_operature(_system.Numvertices,
                                     _system.Numfaces,
                                     &_system.vertices[0],
                                     &_system.faces[0],
                                     &meanH_operator[0],
                                     &meanH[0],
                                     _system.get_box());

    for (int face_index = 0; face_index < _system.Numfaces; face_index++)
    {
        int local_index[3];
        local_index[0] = _system.faces[face_index].v1;
        local_index[1] = _system.faces[face_index].v2;
        local_index[2] = _system.faces[face_index].v3;

        real3 local_vertices[3];
        local_vertices[0] = _system.vertices[local_index[0]].r;
        local_vertices[1] = _system.vertices[local_index[1]].r;
        local_vertices[2] = _system.vertices[local_index[2]].r;

        // Force over the vertices of the face
        real3 vertex_force[3];
        ComputeVertexBendingHelfrichEnergyComputeForce_lambda(&_system.vertices[0],
                                                              &meanH_operator[0],
                                                              &meanH[0],
                                                              &local_index[0],
                                                              &local_vertices[0],
                                                              &m_kappaH[0],
                                                              &m_kappaG[0],
                                                              &m_H0[0],
                                                              _system.get_box(),
                                                              &vertex_force[0]);

        // J. Chem. Phys. 131, 154107 (2009) page 4 Eq. 21
        // Assume that v1 is in the local replica then construct the r2, r3 based on it
        real3 r1 = local_vertices[0];
        real3 r2 = local_vertices[1];
        real3 r3 = local_vertices[2];

        real3 r12, r13;
        r12 = pymemb::vector_subtract(r2, r1, _system.get_box());
        r13 = pymemb::vector_subtract(r3, r1, _system.get_box());
        real3 uw_r3, uw_r2 /*,uw_r1*/;
        // uw_r1 = r1;
        uw_r2 = pymemb::vector_sum(r1, r12);
        uw_r3 = pymemb::vector_sum(r1, r13);

        real3 F3, F2, F1;

        F1 = vertex_force[0];
        F2 = vertex_force[1];
        F3 = vertex_force[2];

        _system.stress_group_faces[face_index].xx += r1.x * F1.x + uw_r2.x * F2.x + uw_r3.x * F3.x;
        _system.stress_group_faces[face_index].xy += r1.x * F1.y + uw_r2.x * F2.y + uw_r3.x * F3.y;
        _system.stress_group_faces[face_index].xz += r1.x * F1.z + uw_r2.x * F2.z + uw_r3.x * F3.z;

        _system.stress_group_faces[face_index].yx += r1.y * F1.x + uw_r2.y * F2.x + uw_r3.y * F3.x;
        _system.stress_group_faces[face_index].yy += r1.y * F1.y + uw_r2.y * F2.y + uw_r3.y * F3.y;
        _system.stress_group_faces[face_index].yz += r1.y * F1.z + uw_r2.y * F2.z + uw_r3.y * F3.z;

        _system.stress_group_faces[face_index].zx += r1.z * F1.x + uw_r2.z * F2.x + uw_r3.z * F3.x;
        _system.stress_group_faces[face_index].zy += r1.z * F1.y + uw_r2.z * F2.y + uw_r3.z * F3.y;
        _system.stress_group_faces[face_index].zz += r1.z * F1.z + uw_r2.z * F2.z + uw_r3.z * F3.z;
    }
}
void ComputeVertexBendingHelfrichEnergy::compute_atomic_stress(void)
{
    // std::vector<real3> force(3);
    std::vector<real3> meanH_operator(_system.Numvertices);
    std::vector<real> meanH(_system.Numvertices);
    // std::vector<real3> forces(_system.Numvertices);

    compute_mean_curvature_operature(_system.Numvertices,
                                     _system.Numfaces,
                                     &_system.vertices[0],
                                     &_system.faces[0],
                                     &meanH_operator[0],
                                     &meanH[0],
                                     _system.get_box());

    for (int face_index = 0; face_index < _system.Numfaces; face_index++)
    {
        int local_index[3];
        local_index[0] = _system.faces[face_index].v1;
        local_index[1] = _system.faces[face_index].v2;
        local_index[2] = _system.faces[face_index].v3;

        real3 local_vertices[3];
        local_vertices[0] = _system.vertices[local_index[0]].r;
        local_vertices[1] = _system.vertices[local_index[1]].r;
        local_vertices[2] = _system.vertices[local_index[2]].r;

        // Force over the vertices of the face
        real3 vertex_force[3];
        ComputeVertexBendingHelfrichEnergyComputeForce_lambda(&_system.vertices[0],
                                                              &meanH_operator[0],
                                                              &meanH[0],
                                                              &local_index[0],
                                                              &local_vertices[0],
                                                              &m_kappaH[0],
                                                              &m_kappaG[0],
                                                              &m_H0[0],
                                                              _system.get_box(),
                                                              &vertex_force[0]);

        // J. Chem. Phys. 131, 154107 (2009) page 4 Eq. 21
        // Assume that v1 is in the local replica then construct the r2, r3 based on it
        real3 r1 = local_vertices[0];
        real3 r2 = local_vertices[1];
        real3 r3 = local_vertices[2];

        real3 r12, r13;
        r12 = pymemb::vector_subtract(r2, r1, _system.get_box());
        r13 = pymemb::vector_subtract(r3, r1, _system.get_box());
        real3 uw_r3, uw_r2 /*,uw_r1*/;
        // uw_r1 = r1;
        uw_r2 = pymemb::vector_sum(r1, r12);
        uw_r3 = pymemb::vector_sum(r1, r13);

        real3 F3, F2, F1;

        F1 = vertex_force[0];
        F2 = vertex_force[1];
        F3 = vertex_force[2];

        realTensor stress_group_face;
        stress_group_face.xx = r1.x * F1.x + uw_r2.x * F2.x + uw_r3.x * F3.x;
        stress_group_face.xy = r1.x * F1.y + uw_r2.x * F2.y + uw_r3.x * F3.y;
        stress_group_face.xz = r1.x * F1.z + uw_r2.x * F2.z + uw_r3.x * F3.z;

        stress_group_face.yx = r1.y * F1.x + uw_r2.y * F2.x + uw_r3.y * F3.x;
        stress_group_face.yy = r1.y * F1.y + uw_r2.y * F2.y + uw_r3.y * F3.y;
        stress_group_face.yz = r1.y * F1.z + uw_r2.y * F2.z + uw_r3.y * F3.z;

        stress_group_face.zx = r1.z * F1.x + uw_r2.z * F2.x + uw_r3.z * F3.x;
        stress_group_face.zy = r1.z * F1.y + uw_r2.z * F2.y + uw_r3.z * F3.y;
        stress_group_face.zz = r1.z * F1.z + uw_r2.z * F2.z + uw_r3.z * F3.z;

        for (auto v : local_index)
        {
            _system.stress_virial_atom[v].xx += stress_group_face.xx / 3.0;
            _system.stress_virial_atom[v].xy += stress_group_face.xy / 3.0;
            _system.stress_virial_atom[v].xz += stress_group_face.xz / 3.0;
            _system.stress_virial_atom[v].yx += stress_group_face.yx / 3.0;
            _system.stress_virial_atom[v].yy += stress_group_face.yy / 3.0;
            _system.stress_virial_atom[v].yz += stress_group_face.yz / 3.0;
            _system.stress_virial_atom[v].zx += stress_group_face.zx / 3.0;
            _system.stress_virial_atom[v].zy += stress_group_face.zy / 3.0;
            _system.stress_virial_atom[v].zz += stress_group_face.zz / 3.0;
        }
    }
}