Program Listing for File aatopology.cpp#

↰ Return to documentation for file (source/aatopology.cpp)

#include "pele/aatopology.hpp"

#include "pele/rotations.hpp"
#include "pele/vecn.hpp"

namespace pele {

pele::Array<double> pele::RigidFragment::to_atomistic(
    pele::Array<double> const com, pele::VecN<3> const &p) {
  assert(com.size() == _ndim);
  assert(p.size() == 3);
  auto rmat = pele::aa_to_rot_mat(p);
  Array<double> pos(_atom_positions.size());
  MatrixAdapter<double> mpos(pos, _ndim);

  // in python this is:
  //      return com + np.dot(R, np.transpose(self.atom_positions)).transpose()
  for (size_t atom = 0; atom < _natoms; ++atom) {
    for (size_t j = 0; j < _ndim; ++j) {
      double val = com[j];
      for (size_t k = 0; k < _ndim; ++k) {
        val += rmat(j, k) * _atom_positions_matrix(atom, k);
      }
      mpos(atom, j) = val;
    }
  }
  return pos;
}

void pele::RigidFragment::transform_grad(pele::VecN<3> const &p,
                                         pele::Array<double> const g,
                                         pele::VecN<3> &g_com,
                                         pele::VecN<3> &g_rot) {
  assert(g.size() == natoms() * 3);
  // view the array as a matrix
  MatrixAdapter<double> gmat(g, 3);

  // compute the rotation matrix and derivatives
  pele::MatrixNM<3, 3> rmat;
  pele::MatrixNM<3, 3> drm1;
  pele::MatrixNM<3, 3> drm2;
  pele::MatrixNM<3, 3> drm3;
  rot_mat_derivatives(p, rmat, drm1, drm2, drm3);

  // do the center of mass coordinates
  for (size_t k = 0; k < 3; ++k) {
    double val = 0;
    for (size_t atom = 0; atom < _natoms; ++atom) {
      val += gmat(atom, k);
    }
    g_com[k] = val;
  }

  // now do the rotations
  g_rot.assign(0.0);
  for (size_t atom = 0; atom < _natoms; ++atom) {
    double val1 = 0;
    double val2 = 0;
    double val3 = 0;
    for (size_t i = 0; i < 3; ++i) {
      for (size_t j = 0; j < 3; ++j) {
        val1 += gmat(atom, i) * drm1(i, j) * _atom_positions_matrix(atom, j);
        val2 += gmat(atom, i) * drm2(i, j) * _atom_positions_matrix(atom, j);
        val3 += gmat(atom, i) * drm3(i, j) * _atom_positions_matrix(atom, j);
      }
    }
    g_rot[0] += val1;
    g_rot[1] += val2;
    g_rot[2] += val3;
  }
}

double pele::RigidFragment::distance_squared(pele::VecN<3> const &com1,
                                             pele::VecN<3> const &p1,
                                             pele::VecN<3> const &com2,
                                             pele::VecN<3> const &p2) const {
  VecN<3> drij = get_smallest_rij(com1, com2);
  pele::MatrixNM<3, 3> R1 = pele::aa_to_rot_mat(p1);
  pele::MatrixNM<3, 3> R2 = pele::aa_to_rot_mat(p2);

  MatrixNM<3, 3> dR = R2 - R1;

  double d_M = m_W * dot<3>(drij, drij);
  // we only need the trace, so this can be sped up
  double d_P = dot<3, 3, 3>(dR, dot<3, 3, 3>(m_S, transpose<3>(dR))).trace();
  double d_mix = 2. * m_W * dot<3>(drij, dot<3, 3>(dR, m_cog));

  double dist2 = d_M + d_P + d_mix;
  return dist2;
}

void pele::RigidFragment::distance_squared_grad(pele::VecN<3> const &com1,
                                                pele::VecN<3> const &p1,
                                                pele::VecN<3> const &com2,
                                                pele::VecN<3> const &p2,
                                                VecN<3> &g_M,
                                                VecN<3> &g_P) const {
  VecN<3> drij = get_smallest_rij(com1, com2);
  auto R2 = pele::aa_to_rot_mat(p2);
  MatrixNM<3, 3> R1, R11, R12, R13;
  pele::rot_mat_derivatives(p1, R1, R11, R12, R13);

  auto dR = R2 - R1;

  g_M = drij;
  g_M *= -2. * m_W;

  // this linear algebra can be done more efficiently
  auto dRT = pele::transpose(dR);
  g_P[0] = -2. * dot<3, 3, 3>(R11, dot<3, 3, 3>(m_S, dRT)).trace();
  g_P[1] = -2. * dot<3, 3, 3>(R12, dot<3, 3, 3>(m_S, dRT)).trace();
  g_P[2] = -2. * dot<3, 3, 3>(R13, dot<3, 3, 3>(m_S, dRT)).trace();

  // this can also be done more efficiently
  auto temp = dot<3, 3>(dR, m_cog);
  temp *= 2. * m_W;
  g_M -= temp;
  g_P[0] -= 2. * m_W * dot<3>(drij, dot<3, 3>(R11, m_cog));
  g_P[1] -= 2. * m_W * dot<3>(drij, dot<3, 3>(R12, m_cog));
  g_P[2] -= 2. * m_W * dot<3>(drij, dot<3, 3>(R13, m_cog));
}

void pele::MeasureAngleAxisCluster::align(pele::Array<double> const x1,
                                          pele::Array<double> x2) {
  auto c1 = m_topology->get_coords_adaptor(x1);
  auto c2 = m_topology->get_coords_adaptor(x2);

  // now account for the symmetries
  for (size_t isite = 0; isite < m_topology->nrigid(); ++isite) {
    auto const &rotations =
        m_topology->get_sites()[isite].get_symmetry_rotations();
    auto p1 = c1.get_rb_rotation(isite);
    auto p2 = c2.get_rb_rotation(isite);
    auto mx2 = pele::aa_to_rot_mat(p2);
    auto mx1 = pele::transpose(pele::aa_to_rot_mat(p1));
    auto mx = pele::dot(mx1, mx2);
    double theta_min = 10.;
    MatrixNM<3, 3> rot_best = pele::identity<3>();
    for (auto const &rot : rotations) {
      auto mx_diff = dot(mx, rot);
      double theta = norm<3>(rot_mat_to_aa(mx_diff));
      theta -= int(theta / (2. * M_PI)) * 2. * M_PI;
      if (theta < theta_min) {
        theta_min = theta;
        rot_best = rot;
      }
    }
    auto newp2 = rotate_aa(rot_mat_to_aa(rot_best), p2);
    std::copy(newp2.begin(), newp2.end(), p2.begin());
  }
}

Array<double> pele::RBTopology::to_atomistic(Array<double> rbcoords) {
  if (rbcoords.size() != nrigid() * 6) {
    throw std::invalid_argument("rbcoords has the wrong size");
  }

  size_t const nrigid = _sites.size();
  auto ca = get_coords_adaptor(rbcoords);
  Array<double> atomistic(3 * natoms_total());
  // view the atomistic coords as a matrix
  //    size_t istart = 0;
  for (size_t isite = 0; isite < nrigid; ++isite) {
    VecN<3> psite = ca.get_rb_rotation(isite);
    auto const &atom_indices = _sites[isite].get_atom_indices();
    if (atom_indices.size() != _sites[isite].natoms()) {
      throw std::runtime_error(
          "atom_indices is the wrong size.  Did you forget to set it?");
    }
    auto site_atom_positions =
        _sites[isite].to_atomistic(ca.get_rb_position(isite), psite);
    for (size_t i = 0; i < atom_indices.size(); ++i) {
      size_t const atom_i = atom_indices[i];
      for (size_t j = 0; j < 3; ++j) {
        atomistic[3 * atom_i + j] = site_atom_positions[3 * i + j];
      }
    }
    //        Array<double> atomistic_view(atomistic.view(istart, istart +
    //        site_atom_positions.size()));
    //        atomistic_view.assign(site_atom_positions);

    //        istart += site_atom_positions.size();
  }
  //    assert(istart == natoms_total() * 3);
  return atomistic;
}

void pele::RBTopology::transform_gradient(pele::Array<double> rbcoords,
                                          pele::Array<double> grad,
                                          pele::Array<double> rbgrad) {
  if (rbcoords.size() != nrigid() * 6) {
    throw std::invalid_argument("rbcoords has the wrong size");
  }
  if (grad.size() != natoms_total() * 3) {
    throw std::invalid_argument("grad has the wrong size");
  }
  if (rbgrad.size() != rbcoords.size()) {
    throw std::invalid_argument("rbgrad has the wrong size");
  }

  CoordsAdaptor ca(nrigid(), 0, rbcoords);
  pele::Array<double> coords_rot(ca.get_rb_rotations());
  CoordsAdaptor rbgrad_ca(nrigid(), 0, rbgrad);

  for (size_t isite = 0; isite < nrigid(); ++isite) {
    size_t const site_ndof = _sites[isite].natoms() * 3;
    Array<double> grad_permuted(site_ndof);
    auto const &atom_indices = _sites[isite].get_atom_indices();
    for (size_t i = 0; i < atom_indices.size(); ++i) {
      size_t const atom_i = atom_indices[i];
      std::copy(grad.begin() + 3 * atom_i, grad.begin() + 3 * atom_i + 3,
                grad_permuted.begin() + 3 * i);
    }
    _sites[isite].transform_grad(ca.get_rb_rotation(isite), grad_permuted,
                                 rbgrad_ca.get_rb_position(isite),
                                 rbgrad_ca.get_rb_rotation(isite));
  }
}

pele::VecN<3> pele::RBTopology::align_angle_axis_vectors(
    pele::VecN<3> const &p1, pele::VecN<3> const &p2in) {
  pele::VecN<3> p2 = p2in;
  pele::VecN<3> n2, p2n;
  if (norm<3>(p2) < 1e-6) {
    if (norm<3>(p1) < 1e-6) {
      return p2;
    }
    n2 = p1;
    n2 *= 2. * M_PI / norm<3>(p1);
  } else {
    n2 = p2;
    n2 *= 2. * M_PI / norm<3>(p2);
  }

  while (true) {
    p2n = p2;
    p2n += n2;
    if (norm<3>(p2n - p1) > norm<3>(p2 - p1)) {
      break;
    }
    p2 = p2n;
  }

  while (true) {
    p2n = p2;
    p2n -= n2;
    if (norm<3>(p2n - p1) > norm<3>(p2 - p1)) {
      break;
    }
    p2 = p2n;
  }
  return p2;
}

void pele::RBTopology::align_all_angle_axis_vectors(pele::Array<double> x1,
                                                    pele::Array<double> x2) {
  auto c1 = get_coords_adaptor(x1);
  auto c2 = get_coords_adaptor(x2);
  for (size_t isite = 0; isite < nrigid(); ++isite) {
    VecN<3> p1 = c1.get_rb_rotation(isite);
    pele::Array<double> p2 = c2.get_rb_rotation(isite);
    auto p2new = align_angle_axis_vectors(p1, p2);
    std::copy(p2new.begin(), p2new.end(), p2.begin());
  }
}

void pele::RBTopology::align_path(std::list<pele::Array<double>> path) {
  auto iter1 = path.begin();
  auto iter2 = path.begin();
  iter2++;
  while (iter2 != path.end()) {
    align_all_angle_axis_vectors(*iter1, *iter2);
    ++iter1;
    ++iter2;
  }
}

void pele::TransformAACluster::rotate(pele::Array<double> x,
                                      pele::MatrixNM<3, 3> const &mx) {
  auto ca = m_topology->get_coords_adaptor(x);
  if (m_topology->nrigid() > 0) {
    // rotate the center of mass positions by mx
    pele::MatrixAdapter<double> rb_pos(ca.get_rb_positions(), 3);
    // make a MatrixAdapter view of the transposed rotation matrix
    auto mxT = pele::transpose(mx);
    pele::MatrixAdapter<double> mxT_view(mxT.data(), 3, 3);
    assert(mxT_view(0, 1) == mx(1, 0));
    // do the multiplication
    auto result = hacky_mat_mul(rb_pos, mxT_view);
    // copy the results back into the coordinates array
    //        std::cout << "result " << result << std::endl;
    rb_pos.assign(result);

    // rotate each aa rotation by mx
    VecN<3> dp = pele::rot_mat_to_aa(mx);
    for (size_t isite = 0; isite < m_topology->nrigid(); ++isite) {
      pele::Array<double> pview = ca.get_rb_rotation(isite);
      VecN<3> p = pele::rotate_aa(pview, dp);
      // copy the vector back into pview
      std::copy(p.begin(), p.end(), pview.begin());
    }
  }
  if (m_topology->number_of_non_rigid_atoms() > 0) {
    throw std::runtime_error("non-rigid atoms is not yet supported");
    //            ca.posAtom[:] = np.dot(mx, ca.posAtom.transpose()).transpose()
  }
}

}  // namespace pele