Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
// #include "Math/GenVector/Rotation3D.h"

#include "FWCore/MessageLogger/interface/MessageLogger.h"
#include "FWCore/Utilities/interface/Parse.h"

#include "Alignment/CommonAlignment/interface/Utilities.h"

align::EulerAngles align::toAngles(const RotationType& rot) {
  const Scalar one = 1;  // to ensure same precison is used in comparison

  EulerAngles angles(3);

  if (std::abs(rot.zx()) > one) {
    edm::LogWarning("Alignment") << "Rounding errors in\n" << rot;
  }

  if (std::abs(rot.zx()) < one) {
    angles(1) = -std::atan2(rot.zy(), rot.zz());
    angles(2) = std::asin(rot.zx());
    angles(3) = -std::atan2(rot.yx(), rot.xx());
  } else if (rot.zx() >= one) {
    angles(1) = std::atan2(rot.xy() + rot.yz(), rot.yy() - rot.xz());
    angles(2) = std::asin(one);
    angles(3) = 0;
  } else if (rot.zx() <= -one) {
    angles(1) = std::atan2(rot.xy() - rot.yz(), rot.yy() + rot.xz());
    angles(2) = std::asin(-one);
    angles(3) = 0;
  }

  return angles;
}

align::RotationType align::toMatrix(const EulerAngles& angles) {
  Scalar s1 = std::sin(angles[0]), c1 = std::cos(angles[0]);
  Scalar s2 = std::sin(angles[1]), c2 = std::cos(angles[1]);
  Scalar s3 = std::sin(angles[2]), c3 = std::cos(angles[2]);

  return RotationType(c2 * c3,
                      c1 * s3 + s1 * s2 * c3,
                      s1 * s3 - c1 * s2 * c3,
                      -c2 * s3,
                      c1 * c3 - s1 * s2 * s3,
                      s1 * c3 + c1 * s2 * s3,
                      s2,
                      -s1 * c2,
                      c1 * c2);
}

align::PositionType align::motherPosition(const std::vector<const PositionType*>& dauPos) {
  unsigned int nDau = dauPos.size();

  Scalar posX(0.), posY(0.), posZ(0.);  // position of mother

  for (unsigned int i = 0; i < nDau; ++i) {
    const PositionType* point = dauPos[i];

    posX += point->x();
    posY += point->y();
    posZ += point->z();
  }

  Scalar inv = 1. / static_cast<Scalar>(nDau);

  return PositionType(posX * inv, posY * inv, posZ * inv);
}

align::RotationType align::diffRot(const GlobalVectors& current, const GlobalVectors& nominal) {
  // Find the matrix needed to rotate the nominal surface to the current one
  // using small angle approximation through the equation:
  //
  //   I * dOmega = dr * r (sum over points)
  //
  // where dOmega is a vector of small rotation angles about (x, y, z)-axes,
  //   and I is the inertia tensor defined as
  //
  //   I_ij = delta_ij * r^2 - r_i * r_j (sum over points)
  //
  // delta_ij is the identity matrix. i, j are indices for (x, y, z).
  //
  // On the rhs of the first eq, r * dr is the cross product of r and dr.
  // In this case, r is the nominal vector and dr is the displacement of the
  // current point from its nominal point (current vector - nominal vector).
  //
  // Since the solution of dOmega (by inverting I) gives angles that are small,
  // we rotate the current surface by -dOmega and repeat the process until the
  // dOmega^2 is less than a certain tolerance value.
  // (In other words, we move the current surface by small angular steps till
  // it matches the nominal surface.)
  // The full rotation is found by adding up the rotations (given by dOmega)
  // in each step. (More precisely, the product of all the matrices.)
  //
  // Note that, in some cases, if the angular displacement between current and
  // nominal is pi, the algorithm can return an identity (no rotation).
  // This is because dr = -r and r * dr is all zero.
  // This is not a problem since we are dealing with small angles in alignment.

  static const double tolerance = 1e-12;

  RotationType rot;  // rotation from nominal to current; init to identity

  // Initial values for dr and I; I is always the same in each step

  AlgebraicSymMatrix I(3);  // inertia tensor

  GlobalVectors rotated = current;  // rotated current vectors in each step

  unsigned int nPoints = nominal.size();

  for (unsigned int j = 0; j < nPoints; ++j) {
    const GlobalVector& r = nominal[j];
    // Inertial tensor: I_ij = delta_ij * r^2 - r_i * r_j (sum over points)

    I.fast(1, 1) += r.y() * r.y() + r.z() * r.z();
    I.fast(2, 2) += r.x() * r.x() + r.z() * r.z();
    I.fast(3, 3) += r.y() * r.y() + r.x() * r.x();
    I.fast(2, 1) -= r.x() * r.y();  // row index must be >= col index
    I.fast(3, 1) -= r.x() * r.z();
    I.fast(3, 2) -= r.y() * r.z();
  }
  int count = 0;
  while (true) {
    AlgebraicVector rhs(3);  // sum of dr * r

    for (unsigned int j = 0; j < nPoints; ++j) {
      const GlobalVector& r = nominal[j];
      const GlobalVector& c = rotated[j];

      // Cross product of dr * r = c * r (sum over points)

      rhs(1) += c.y() * r.z() - c.z() * r.y();
      rhs(2) += c.z() * r.x() - c.x() * r.z();
      rhs(3) += c.x() * r.y() - c.y() * r.x();
    }

    EulerAngles dOmega = CLHEP::solve(I, rhs);

    rot *= toMatrix(dOmega);  // add to rotation

    if (dOmega.normsq() < tolerance)
      break;  // converges, so exit loop
    count++;
    if (count > 100000) {
      std::cout << "diffRot infinite loop: dOmega is " << dOmega.normsq() << "\n";
      break;
    }

    // Not yet converge; move current vectors to new positions and find dr

    for (unsigned int j = 0; j < nPoints; ++j) {
      rotated[j] = GlobalVector(rot.multiplyInverse(current[j].basicVector()));
    }
  }

  return rot;
}

align::GlobalVector align::diffR(const GlobalVectors& current, const GlobalVectors& nominal) {
  GlobalVector nCM(0, 0, 0);
  GlobalVector cCM(0, 0, 0);

  unsigned int nPoints = nominal.size();

  for (unsigned int j = 0; j < nPoints; ++j) {
    nCM += nominal[j];
    cCM += current[j];
  }

  nCM -= cCM;

  return nCM /= static_cast<Scalar>(nPoints);
}

align::GlobalVector align::centerOfMass(const GlobalVectors& theVs) {
  unsigned int nPoints = theVs.size();

  GlobalVector CM(0, 0, 0);

  for (unsigned int j = 0; j < nPoints; ++j)
    CM += theVs[j];

  return CM /= static_cast<Scalar>(nPoints);
}

void align::rectify(RotationType& rot) {
  // Use ROOT for better numerical precision but slower.

  //   ROOT::Math::Rotation3D temp( rot.xx(), rot.xy(), rot.xz(),
  //                                rot.yx(), rot.yy(), rot.yz(),
  //                                rot.zx(), rot.zy(), rot.zz() );
  //
  //   temp.Rectify();
  //
  //   Scalar elems[9];
  //
  //   temp.GetComponents(elems);
  //   rot = RotationType(elems);

  rot = toMatrix(toAngles(rot));  // fast rectification but less precise
}

align::RunRanges align::makeNonOverlappingRunRanges(const edm::VParameterSet& runRanges, const RunNumber& defaultRun) {
  const auto beginValue = cond::timeTypeSpecs[cond::runnumber].beginValue;
  const auto endValue = cond::timeTypeSpecs[cond::runnumber].endValue;
  RunRanges uniqueRunRanges;
  if (!runRanges.empty()) {
    std::map<RunNumber, RunNumber> uniqueFirstRunNumbers;
    for (const auto& ipset : runRanges) {
      const auto RunRangeStrings = ipset.getParameter<std::vector<std::string> >("RunRanges");
      for (const auto& irange : RunRangeStrings) {
        if (irange.find(':') == std::string::npos) {
          long int temp{strtol(irange.c_str(), nullptr, 0)};
          auto first = (temp != -1) ? temp : beginValue;
          uniqueFirstRunNumbers[first] = first;
        } else {
          edm::LogWarning("BadConfig") << "@SUB=align::makeNonOverlappingRunRanges"
                                       << "Config file contains old format for 'RunRangeSelection'. Only "
                                       << "the start run number is used internally. The number of the last"
                                       << " run is ignored and can besafely removed from the config file.";
          auto tokens = edm::tokenize(irange, ":");
          long int temp{strtol(tokens[0].c_str(), nullptr, 0)};
          auto first = (temp != -1) ? temp : beginValue;
          uniqueFirstRunNumbers[first] = first;
        }
      }
    }

    for (const auto& iFirst : uniqueFirstRunNumbers) {
      uniqueRunRanges.push_back(std::make_pair(iFirst.first, endValue));
    }
    for (unsigned int i = 0; i < uniqueRunRanges.size() - 1; ++i) {
      uniqueRunRanges[i].second = uniqueRunRanges[i + 1].first - 1;
    }
  } else {
    uniqueRunRanges.push_back(std::make_pair(defaultRun, endValue));
  }

  return uniqueRunRanges;
}

align::RunRanges align::makeUniqueRunRanges(const edm::VParameterSet& runRanges, const RunNumber& defaultRun) {
  auto uniqueRunRanges = align::makeNonOverlappingRunRanges(runRanges, defaultRun);
  if (uniqueRunRanges.empty()) {  // create dummy IOV
    const RunRange runRange(defaultRun, cond::timeTypeSpecs[cond::runnumber].endValue);
    uniqueRunRanges.push_back(runRange);
  }
  return uniqueRunRanges;
}