Giter VIP home page Giter VIP logo

Comments (5)

chemphys avatar chemphys commented on August 16, 2024

Hey @dgasmith ,
I am trying to come up with a good way to loop over everything in the electrostatic routine. I have two options:

  • Loop over SITES <i,j>. This will be easy to go through and fast to implement. The only problem is that then we need to check if a pair <i,j> is excluded, by finding the pair in a set that contains all the excluded pairs of the system. Also, the screening parameter a that the screening functions need depend on the monomer type. This would add some conditionals in the loop that probably will destroy any chance of vectorization.
  • Loop over MONOMER TYPES <i,j>. I already have a vector of pairs with monomer types and number of monomer of each type. Implementing this will require more loops (looping over monomer types, and then over atoms) but will be (I think) easier to parallelize and ensure vectorization if I code it right. We will still need information about excluded pairs, but this time will be a way shorter set, that will be directly accessible through indexes i and j. Also, the excluded pairs are only needed when accounting for electrostatic interaction of two sites of the same monomer.

Unless you have another idea, I would try to go for the second choice. I will see how is the outcome of this and then we can talk about it. But if you have any other idea we can try let me know!

See you!

from mbx.

chemphys avatar chemphys commented on August 16, 2024

Hello @dgasmith and @fpaesani ,
A couple of updates about the vectorization of the electrostatics. I think I can divide the obtainment of the different components in two parts:

  • Interactions due to sites in the same monomer (for now, later will have to be molecule). This part is practically impossible to vectorize for a couple reasons. A) we need to find if the pair we are looking at is excluded, and if so, provide with the proper thole damping, since for water is 0.626 and 0.055 for 1-2 and 1-3 distances respectively, and for the rest of the molecules is 0.3 and 0.3 respectively. I think I can bypass this by performing the search outside the loop I am vectorizing (see TODOs in the loop in the function below). The main and big problem is B) the gamma function. This function is a sum of infinite terms, and is done by performing undefined loops until the convergence is reached. We could also bypass this by precomputing the values, or setting a maximum number of iterations. The algorithm implemented in our old C++ code is the one shown in numerical recipes, chapter 6.2. HOWEVER, most of the pairs that actually need the gamma function (only for the charge charge interactions) will be excluded. This means that this part of the electrostatics should not be a big amount of time in comparison with all the rest. This is just to obtain the permament electric field, which is only computed once. Thus, unless you think it might be important, I would allow this not to be vectorized and use the functions we already have coded.
  • Interactions between sites in different molecules. Here there are not excluded pairs. All the pairs will be computed, and the thole damping is the same for all (0.055). This should be the big cost of the electrostatics calculation. With all the system information I store in the setup of the system, I exactly know how many monomers of each type I have, and how many types are in my system. This allows to easily separate these two sets of calculations, in both the permanent electric field and the dipole electric field.

For your information, since is not pushed yet, I paste the function here that I have for now, which only computes the contribution to the permanent electric field of sites inside the same molecule.

namespace elec {

  double electrostatics(const std::vector<double> chg,
    const std::vector<double> polfac,
    const std::vector<double> pol,
    const std::vector<double> orig_xyz,
    const std::vector<std::string> mon_id,
    const std::vector<size_t> sites,
    const std::vector<size_t> first_ind,
    const excluded_set_type& excluded12,
    const excluded_set_type& excluded13,
    const excluded_set_type& excluded14,
    const std::vector<std::pair<std::string,size_t>> mon_type_count,
    const bool do_grads,
    std::vector<double> &grad) {

    // Damping declarations
    const double aCC = 0.4;
    const double aCD = 0.4;
    const double aDD = 0.055;

    // Constants that will be used later
    const double g34 = std::exp(gammln(3.0/4.0));

    // System properties and sizes
    const size_t nsites = chg.size();
    std::vector<double> sqrtpol(3*nsites, 0.0);

    // Electric fields and potential
    std::vector<double> Efq(3*nsites,0.0);
    std::vector<double> Efd(3*nsites,0.0);
    std::vector<double> phi(nsites,0.0);

    // Squareroot all pols
    for (size_t j = 0; j < nsites; j++) {
      const size_t j3 = 3*j;
      const double sq = std::sqrt(pol[j]);
      for (size_t i = 0; i < 3; i++) {
        sqrtpol[j3 + i] = sq;
      }
    }

    // Organize xyz so we have x1_1 x1_2 ... y1_1 y1_2...
    // where xN_M is read as coordinate x of site N of monomer M
    // for the first monomer type. Then follows the second, and so on.
    std::vector<double> xyz(nsites,0.0);
    size_t fi_mon = 0;
    size_t fi_crd = 0;
    for (size_t mt = 0; mt < mon_type_count.size(); mt++) {
      size_t ns = sites[fi_mon];
      size_t nmon = mon_type_count[mt].second;
      size_t nmon2 = nmon*2;
      for (size_t m = 0; m < nmon; m++) {
        size_t mns3 = m*ns*3;
        for (size_t i = 0; i < ns; i++) {
          size_t inmon3 = 3*i*nmon;
          xyz[inmon3 + m + fi_crd] =
                 orig_xyz[fi_crd + mns3 + 3*i];
          xyz[inmon3 + m + fi_crd + nmon] =
                 orig_xyz[fi_crd + mns3 + 3*i + 1];
          xyz[inmon3 + m + fi_crd + nmon2] =
                 orig_xyz[fi_crd + mns3 + 3*i + 2];
        }
      }
      fi_mon += nmon;
      fi_crd += nmon*ns*3;
    }

    // Obtain permanent electric field
    // Monomers alone
    fi_mon = 0;
    size_t fi_sites = 0;
    for (size_t mt = 0; mt < mon_type_count.size(); mt++) {
      size_t ns = sites[fi_mon];
      size_t nmon = mon_type_count[mt].second;
      size_t nmon2 = nmon*2;
      for (size_t i = 0; i < ns -1; i++) {
        size_t inmon  = i*nmon;
        size_t inmon3  = 3*inmon;
        size_t i3 = 3*i;
        for (size_t j = i + 1; j < ns; j++) {
          size_t jnmon  = j*nmon;
          size_t jnmon3  = 3*jnmon;
          size_t j3 = 3*j;
          // TODO For now, assuming A > std::numeric_limits<double>::epsilon();
          // TODO COntinue only if i and j are not bonded
          double A = polfac[fi_sites + i] * polfac[fi_sites + j];
          double Ai = 1/A;
          double Asqsq = A*A*A*A;
          std::vector<double> phii(nmon,0.0);
          std::vector<double> phij(nmon,0.0);
          std::vector<double> Efqix(nmon,0.0);
          std::vector<double> Efqjx(nmon,0.0);
          std::vector<double> Efqiy(nmon,0.0);
          std::vector<double> Efqjy(nmon,0.0);
          std::vector<double> Efqiz(nmon,0.0);
          std::vector<double> Efqjz(nmon,0.0);
          for (size_t m = 0; m < nmon; m++) {
            //  Distances and values that will be reused
            const double rijx = xyz[inmon3 + m] - xyz[jnmon3 + m];
            const double rijy = xyz[inmon3 + m + nmon] - xyz[jnmon3 + m + nmon];
            const double rijz = xyz[inmon3 + m + nmon2] - xyz[jnmon3 + m + nmon2];
            const double rsq = rijx*rijx + rijy*rijy + rijz*rijz;
            const double r = std::sqrt(rsq);
            const double ri = 1.0/r;
            const double risq = ri*ri;
            const double rsqsq = rsq * rsq;

            // Some values that will be used in the screening functions
            const double rA4 = rsqsq/Asqsq;
            const double arA4 = aCC*rA4;
            // TODO look at the exponential function intel vec
            const double exp1 = std::exp(-arA4);
            const double gq = gammq(3.0/4.0, arA4);
            //const double gq = 1.0;
            const double a_mrt = std::pow(aCC, 1.0/4.0);
            //const double a4 = aCC * 4.0;

            // Get screening functions
            const double s1r = ri - exp1*ri;
            const double s0r = s1r + a_mrt * Ai * g34 * gq;
            const double s1r3 = s1r * risq;

            // Update the field
            const size_t shift = first_ind[fi_mon + m];
            const size_t shift3 = 3*shift;
            const size_t spi = shift + i;
            const size_t spj = shift + j;

            phii[m] = s0r *chg[spj];
            phij[m] = s0r *chg[spi];

            // Update Electric field
            Efqix[m] = s1r3 * chg[spj] * rijx;
            Efqjx[m] = s1r3 * chg[spi] * rijx;
            Efqiy[m] = s1r3 * chg[spj] * rijy;
            Efqjy[m] = s1r3 * chg[spi] * rijy;
            Efqiz[m] = s1r3 * chg[spj] * rijz;
            Efqjz[m] = s1r3 * chg[spi] * rijz;
          }
          for (size_t m = 0; m < nmon; m++) {
            const size_t shift = first_ind[fi_mon + m];
            const size_t shift3 = 3*shift;
            phi[i] += phii[m];
            phi[j] += phij[m];
            Efq[shift3 + i3] += Efqix[m];
            Efq[shift3 + j3] -= Efqjx[m];
            Efq[shift3 + i3 + 1] += Efqiy[m];
            Efq[shift3 + j3 + 1] -= Efqjy[m];
            Efq[shift3 + i3 + 2] += Efqiz[m];
            Efq[shift3 + j3 + 2] -= Efqjz[m];
          }
        }
      }

      // Update first indexes
      fi_mon += nmon;
      fi_sites += nmon * ns;
    }



    return 0.0;

  }

from mbx.

chemphys avatar chemphys commented on August 16, 2024

Electrostatics is implemented with gradients, and working. I will proceed now to clean up the code, reduce the number of vector declarations, and try to compact similar operations.

from mbx.

chemphys avatar chemphys commented on August 16, 2024

Hey @dgasmith , (@fpaesani to keep you in the loop)
I pushed to my branch the new version of the electrostatics. These are the new modifications:

  • Vectorized all the field and gradient calculations. To add this, I needed to add the pragma ivdep since the compiler was confused about some dependencies that I know for sure they don't exist. With that pragma, loops are vectorized (all except the gammq function, which is not vectorizable at all)
  • The code in electrostatics.cpp is cleaner and easier to read. It declares a Field object at the beginning that is used to call the functions that calculate permanent electric field, dipole electric field and gradients.
  • The payback is that when I loop over the intramonomer contributions in the permanent electric field, the dipole field and the gradients, that has to call the function N times, where N is the number of monomers. Since the only one that will be called several times is the dipole one, I can generate a special function for the intramonomer case, and vectorize that one too.
  • Tests are passing, and the time for the same 256 molecules has been reduced from 2.1 seconds (the first version of the electrostatics code I implemented) to 1.2 seconds using gradients.

I think this is it. Maybe we can discuss today what can we do to make it a little bit faster. I think I added all your suggestions, but I might have forgotten some at some place. I will also have a look at intel sqrt and exp. I did not put that yet.

Thanks for all the tips and advice!!

from mbx.

chemphys avatar chemphys commented on August 16, 2024

Seems to work well. Closing issue.

from mbx.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.