/**
 * Calculate the twoTheta angle from the detector and sample locations.
 * @return: twoTheta
 */
double SpecularReflectionAlgorithm::calculateTwoTheta() const {
  MatrixWorkspace_sptr inWS = this->getProperty("InputWorkspace");

  const std::string analysisMode = this->getProperty("AnalysisMode");

  Instrument_const_sptr instrument = inWS->getInstrument();

  IComponent_const_sptr detector =
      this->getDetectorComponent(inWS, analysisMode == pointDetectorAnalysis);

  IComponent_const_sptr sample = this->getSurfaceSampleComponent(instrument);

  const V3D detSample = detector->getPos() - sample->getPos();

  boost::shared_ptr<const ReferenceFrame> refFrame =
      instrument->getReferenceFrame();

  const double upoffset = refFrame->vecPointingUp().scalar_prod(detSample);
  const double beamoffset =
      refFrame->vecPointingAlongBeam().scalar_prod(detSample);

  const double twoTheta = std::atan2(upoffset, beamoffset) * 180 / M_PI;

  return twoTheta;
}
/** Get instrument geometry setup including L2 for each detector and L1
  */
void CreateLogTimeCorrection::getInstrumentSetup() {
  // 1. Get sample position and source position
  IComponent_const_sptr sample = m_instrument->getSample();
  if (!sample) {
    throw runtime_error("No sample has been set.");
  }
  V3D samplepos = sample->getPos();

  IComponent_const_sptr source = m_instrument->getSource();
  if (!source) {
    throw runtime_error("No source has been set.");
  }
  V3D sourcepos = source->getPos();
  m_L1 = sourcepos.distance(samplepos);

  // 2. Get detector IDs
  std::vector<detid_t> detids = m_instrument->getDetectorIDs(true);
  for (auto &detid : detids) {
    IDetector_const_sptr detector = m_instrument->getDetector(detid);
    V3D detpos = detector->getPos();
    double l2 = detpos.distance(samplepos);
    m_l2map.emplace(detid, l2);
  }

  // 3. Output information
  g_log.information() << "Sample position = " << samplepos << "; "
                      << "Source position = " << sourcepos << ", L1 = " << m_L1
                      << "; "
                      << "Number of detector/pixels = " << detids.size()
                      << ".\n";
}
/**
 * Correct the position of the detectors based on the input theta value.
 * @param toCorrect : Workspace to correct detector posisitions on.
 * @param twoThetaInDeg : 2* Theta in degrees to use in correction calculations.
 * @param sample : Pointer to the sample
 * @param detector : Pointer to a given detector
 */
void SpecularReflectionPositionCorrect::correctPosition(
    API::MatrixWorkspace_sptr toCorrect, const double &twoThetaInDeg,
    IComponent_const_sptr sample, IComponent_const_sptr detector) {

  auto instrument = toCorrect->getInstrument();

  const V3D detectorPosition = detector->getPos();

  const V3D samplePosition = sample->getPos();

  const V3D sampleToDetector = detectorPosition - samplePosition;

  auto referenceFrame = instrument->getReferenceFrame();

  const double twoThetaInRad = twoThetaInDeg * (M_PI / 180.0);

  double acrossOffset = 0;

  double beamOffset = sampleToDetector.scalar_prod(
      referenceFrame
          ->vecPointingAlongBeam()); // We just recalculate beam offset.

  double upOffset =
      (beamOffset *
       std::tan(0.5 * twoThetaInRad)); // We only correct vertical position

  // Apply the movements.
  moveDetectors(toCorrect, detector, sample, upOffset, acrossOffset,
                detector->getPos());
}
/**
 * Execute the MoveInstrumentComponent on all (named) subcomponents
 * @param toCorrect : Workspace to correct
 * @param detector : Detector or DetectorGroup
 * @param sample : Sample Component
 * @param upOffset : Up offset to apply
 * @param acrossOffset : Across offset to apply
 * @param detectorPosition: Actual detector or detector group position.
 */
void SpecularReflectionPositionCorrect::moveDetectors(
    API::MatrixWorkspace_sptr toCorrect, IComponent_const_sptr detector,
    IComponent_const_sptr sample, const double &upOffset,
    const double &acrossOffset, const V3D &detectorPosition) {
  auto instrument = toCorrect->getInstrument();
  const V3D samplePosition = sample->getPos();
  auto referenceFrame = instrument->getReferenceFrame();
  if (auto groupDetector = boost::dynamic_pointer_cast<const DetectorGroup>(
          detector)) // Do we have a group of detectors
  {
    const std::vector<IDetector_const_sptr> detectors =
        groupDetector->getDetectors();
    const bool commonParent = hasCommonParent(detectors);
    if (commonParent) {
      /*
       * Same parent component. So lets move that.
       */
      moveDetectors(toCorrect, detectors[0], sample, upOffset, acrossOffset,
                    detectorPosition); // Recursive call
    } else {
      /*
       * We have to move individual components.
       */
      for (size_t i = 0; i < detectors.size(); ++i) {
        moveDetectors(toCorrect, detectors[i], sample, upOffset, acrossOffset,
                      detectorPosition); // Recursive call
      }
    }
  } else {
    auto moveComponentAlg =
        this->createChildAlgorithm("MoveInstrumentComponent");
    moveComponentAlg->initialize();
    moveComponentAlg->setProperty("Workspace", toCorrect);
    IComponent_const_sptr root = getParentComponent(detector);
    const std::string componentName = root->getName();
    moveComponentAlg->setProperty("ComponentName", componentName);
    moveComponentAlg->setProperty("RelativePosition", false);
    // Movements
    moveComponentAlg->setProperty(
        referenceFrame->pointingAlongBeamAxis(),
        detectorPosition.scalar_prod(referenceFrame->vecPointingAlongBeam()));
    moveComponentAlg->setProperty(referenceFrame->pointingHorizontalAxis(),
                                  acrossOffset);
    const double detectorVerticalPosition =
        detectorPosition.scalar_prod(referenceFrame->vecPointingUp());
    const double rootVerticalPosition =
        root->getPos().scalar_prod(referenceFrame->vecPointingUp());

    const double dm = rootVerticalPosition - detectorVerticalPosition;
    moveComponentAlg->setProperty(
        referenceFrame->pointingUpAxis(),
        samplePosition.scalar_prod(referenceFrame->vecPointingUp()) + upOffset +
            dm);
    // Execute the movement.
    moveComponentAlg->execute();
  }
}
Beispiel #5
0
/** Creates or modifies the parameter map for the specified detector adding
*  pressure and wall thickness information
*  @param params :: these will be written to the detector paraments 3He(atm)=pressure) and wallT(m)=wall thickness
*  @param change :: if the parameters are successfully changed they are stored here
*  @throw NotFoundError if a pointer to the specified detector couldn't be retrieved
*/
void LoadDetectorInfo::setDetectorParams(const detectorInfo &params, detectorInfo &change)
{
  Geometry::IDetector_sptr det;
  try
  {
    det = boost::const_pointer_cast<IDetector>(m_workspace->getInstrument()->baseInstrument()->getDetector(params.detID));
  }
  catch( std::runtime_error &e)
  {
    throw Exception::NotFoundError(e.what(), params.detID);
  }

  Geometry::ParameterMap &pmap = m_workspace->instrumentParameters();
  // Set the detectors pressure.
  pmap.addDouble(det->getComponentID(), "3He(atm)", params.pressure);
  // Set the wall thickness
  pmap.addDouble(det->getComponentID(), "wallT(m)", params.wallThick);

  // If we have a l2, theta and phi. Update the postion if required
  if( m_moveDets && 
      params.l2 != DBL_MAX && params.theta != DBL_MAX && params.phi != DBL_MAX )
  {
    V3D newPos;
    newPos.spherical(params.l2, params.theta, params.phi);
    // The sample position may not be at 0,0,0
    newPos += m_samplePos;

    IComponent_const_sptr parent = det->getParent();
    if (parent)
    {
      newPos -= parent->getPos();
      Quat rot = parent->getRotation();
      rot.inverse();
      rot.rotate(newPos);
    }
    det->setPos(newPos);
  }


  // this operation has been successful if we are here, the following infomation is usefull for logging
  change = params;
}
/**
 * Cache frequently accessed values
 * @param instrument : The instrument for this run
 * @param detID : The det ID for this observation
 */
void CachedExperimentInfo::initCaches(
    const Geometry::Instrument_const_sptr &instrument, const detid_t detID) {
  // Throws if detector does not exist
  // Takes into account possible detector mapping
  IDetector_const_sptr det = m_exptInfo.getDetectorByID(detID);

  // Instrument distances
  boost::shared_ptr<const ReferenceFrame> refFrame =
      instrument->getReferenceFrame();
  m_beam = refFrame->pointingAlongBeam();
  m_up = refFrame->pointingUp();
  m_horiz = refFrame->pointingHorizontal();

  IComponent_const_sptr source = instrument->getSource();
  IComponent_const_sptr sample = instrument->getSample();
  IComponent_const_sptr aperture =
      instrument->getComponentByName("aperture", 1);
  if (!aperture) {
    throw std::invalid_argument(
        "No component named \"aperture\" found in instrument.");
  }
  IObjComponent_const_sptr firstChopper = instrument->getChopperPoint(0);
  const Kernel::V3D samplePos = sample->getPos();
  const Kernel::V3D beamDir = samplePos - source->getPos();

  // Cache
  m_twoTheta = det->getTwoTheta(samplePos, beamDir);
  m_phi = det->getPhi();
  m_modToChop = firstChopper->getDistance(*source);
  m_apertureToChop = firstChopper->getDistance(*aperture);
  m_chopToSample = sample->getDistance(*firstChopper);
  m_sampleToDet = det->getDistance(*sample);

  // Aperture
  Geometry::BoundingBox apertureBox;
  aperture->getBoundingBox(apertureBox);
  if (apertureBox.isNull()) {
    throw std::invalid_argument("CachedExperimentInfo::initCaches - Aperture "
                                "has no bounding box, cannot sample from it");
  }
  m_apertureSize.first = apertureBox.maxPoint()[0] - apertureBox.minPoint()[0];
  m_apertureSize.second = apertureBox.maxPoint()[1] - apertureBox.minPoint()[1];

  // Sample volume
  const API::Sample &sampleDescription = m_exptInfo.sample();
  const Geometry::Object &shape = sampleDescription.getShape();
  m_sampleWidths = shape.getBoundingBox().width();

  // Detector volume
  // Make sure it encompasses all possible detectors
  det->getBoundingBox(m_detBox);
  if (m_detBox.isNull()) {
    throw std::invalid_argument("CachedExperimentInfo::initCaches - Detector "
                                "has no bounding box, cannot sample from it. "
                                "ID:" +
                                boost::lexical_cast<std::string>(det->getID()));
  }

  const double rad2deg = 180. / M_PI;
  const double thetaInDegs = twoTheta() * rad2deg;
  const double phiInDegs = phi() * rad2deg;

  m_gonimeter = new Goniometer;
  m_gonimeter->makeUniversalGoniometer();
  m_gonimeter->setRotationAngle("phi", thetaInDegs);
  m_gonimeter->setRotationAngle("chi", phiInDegs);
  m_sampleToDetMatrix =
      m_exptInfo.sample().getOrientedLattice().getU() * m_gonimeter->getR();

  // EFixed
  m_efixed = m_exptInfo.getEFixed(det);
}
Beispiel #7
0
void SofQWCentre::exec() {
  using namespace Geometry;

  MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace");

  // Do the full check for common binning
  if (!WorkspaceHelpers::commonBoundaries(*inputWorkspace)) {
    g_log.error(
        "The input workspace must have common binning across all spectra");
    throw std::invalid_argument(
        "The input workspace must have common binning across all spectra");
  }

  std::vector<double> verticalAxis;
  MatrixWorkspace_sptr outputWorkspace = setUpOutputWorkspace(
      inputWorkspace, getProperty("QAxisBinning"), verticalAxis);
  setProperty("OutputWorkspace", outputWorkspace);

  // Holds the spectrum-detector mapping
  std::vector<specnum_t> specNumberMapping;
  std::vector<detid_t> detIDMapping;

  m_EmodeProperties.initCachedValues(*inputWorkspace, this);
  int emode = m_EmodeProperties.m_emode;

  // Get a pointer to the instrument contained in the workspace
  Instrument_const_sptr instrument = inputWorkspace->getInstrument();

  // Get the distance between the source and the sample (assume in metres)
  IComponent_const_sptr source = instrument->getSource();
  IComponent_const_sptr sample = instrument->getSample();
  V3D beamDir = sample->getPos() - source->getPos();
  beamDir.normalize();

  try {
    double l1 = source->getDistance(*sample);
    g_log.debug() << "Source-sample distance: " << l1 << '\n';
  } catch (Exception::NotFoundError &) {
    g_log.error("Unable to calculate source-sample distance");
    throw Exception::InstrumentDefinitionError(
        "Unable to calculate source-sample distance",
        inputWorkspace->getTitle());
  }

  // Conversion constant for E->k. k(A^-1) = sqrt(energyToK*E(meV))
  const double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass *
                           PhysicalConstants::meV * 1e-20 /
                           (PhysicalConstants::h * PhysicalConstants::h);

  // Loop over input workspace bins, reassigning data to correct bin in output
  // qw workspace
  const size_t numHists = inputWorkspace->getNumberHistograms();
  const size_t numBins = inputWorkspace->blocksize();
  Progress prog(this, 0.0, 1.0, numHists);
  for (int64_t i = 0; i < int64_t(numHists); ++i) {
    try {
      // Now get the detector object for this histogram
      IDetector_const_sptr spectrumDet = inputWorkspace->getDetector(i);
      if (spectrumDet->isMonitor())
        continue;

      const double efixed = m_EmodeProperties.getEFixed(*spectrumDet);

      // For inelastic scattering the simple relationship q=4*pi*sinTheta/lambda
      // does not hold. In order to
      // be completely general we must calculate the momentum transfer by
      // calculating the incident and final
      // wave vectors and then use |q| = sqrt[(ki - kf)*(ki - kf)]
      DetectorGroup_const_sptr detGroup =
          boost::dynamic_pointer_cast<const DetectorGroup>(spectrumDet);
      std::vector<IDetector_const_sptr> detectors;
      if (detGroup) {
        detectors = detGroup->getDetectors();
      } else {
        detectors.push_back(spectrumDet);
      }

      const size_t numDets = detectors.size();
      // cache to reduce number of static casts
      const double numDets_d = static_cast<double>(numDets);
      const auto &Y = inputWorkspace->y(i);
      const auto &E = inputWorkspace->e(i);
      const auto &X = inputWorkspace->x(i);

      // Loop over the detectors and for each bin calculate Q
      for (size_t idet = 0; idet < numDets; ++idet) {
        IDetector_const_sptr det = detectors[idet];
        // Calculate kf vector direction and then Q for each energy bin
        V3D scatterDir = (det->getPos() - sample->getPos());
        scatterDir.normalize();
        for (size_t j = 0; j < numBins; ++j) {
          const double deltaE = 0.5 * (X[j] + X[j + 1]);
          // Compute ki and kf wave vectors and therefore q = ki - kf
          double ei(0.0), ef(0.0);
          if (emode == 1) {
            ei = efixed;
            ef = efixed - deltaE;
            if (ef < 0) {
              std::string mess =
                  "Energy transfer requested in Direct mode exceeds incident "
                  "energy.\n Found for det ID: " +
                  std::to_string(idet) + " bin No " + std::to_string(j) +
                  " with Ei=" + boost::lexical_cast<std::string>(efixed) +
                  " and energy transfer: " +
                  boost::lexical_cast<std::string>(deltaE);
              throw std::runtime_error(mess);
            }
          } else {
            ei = efixed + deltaE;
            ef = efixed;
            if (ef < 0) {
              std::string mess =
                  "Incident energy of a neutron is negative. Are you trying to "
                  "process Direct data in Indirect mode?\n Found for det ID: " +
                  std::to_string(idet) + " bin No " + std::to_string(j) +
                  " with efied=" + boost::lexical_cast<std::string>(efixed) +
                  " and energy transfer: " +
                  boost::lexical_cast<std::string>(deltaE);
              throw std::runtime_error(mess);
            }
          }

          if (ei < 0)
            throw std::runtime_error(
                "Negative incident energy. Check binning.");

          const V3D ki = beamDir * sqrt(energyToK * ei);
          const V3D kf = scatterDir * (sqrt(energyToK * (ef)));
          const double q = (ki - kf).norm();

          // Test whether it's in range of the Q axis
          if (q < verticalAxis.front() || q > verticalAxis.back())
            continue;
          // Find which q bin this point lies in
          const MantidVec::difference_type qIndex =
              std::upper_bound(verticalAxis.begin(), verticalAxis.end(), q) -
              verticalAxis.begin() - 1;

          // Add this spectra-detector pair to the mapping
          specNumberMapping.push_back(
              outputWorkspace->getSpectrum(qIndex).getSpectrumNo());
          detIDMapping.push_back(det->getID());

          // And add the data and it's error to that bin, taking into account
          // the number of detectors contributing to this bin
          outputWorkspace->mutableY(qIndex)[j] += Y[j] / numDets_d;
          // Standard error on the average
          outputWorkspace->mutableE(qIndex)[j] =
              sqrt((pow(outputWorkspace->e(qIndex)[j], 2) + pow(E[j], 2)) /
                   numDets_d);
        }
      }

    } catch (Exception::NotFoundError &) {
      // Get to here if exception thrown when calculating distance to detector
      // Presumably, if we get to here the spectrum will be all zeroes anyway
      // (from conversion to E)
      continue;
    }
    prog.report();
  }

  // If the input workspace was a distribution, need to divide by q bin width
  if (inputWorkspace->isDistribution())
    this->makeDistribution(outputWorkspace, verticalAxis);

  // Set the output spectrum-detector mapping
  SpectrumDetectorMapping outputDetectorMap(specNumberMapping, detIDMapping);
  outputWorkspace->updateSpectraUsing(outputDetectorMap);

  // Replace any NaNs in outputWorkspace with zeroes
  if (this->getProperty("ReplaceNaNs")) {
    auto replaceNans = this->createChildAlgorithm("ReplaceSpecialValues");
    replaceNans->setChild(true);
    replaceNans->initialize();
    replaceNans->setProperty("InputWorkspace", outputWorkspace);
    replaceNans->setProperty("OutputWorkspace", outputWorkspace);
    replaceNans->setProperty("NaNValue", 0.0);
    replaceNans->setProperty("InfinityValue", 0.0);
    replaceNans->setProperty("BigNumberThreshold", DBL_MAX);
    replaceNans->execute();
  }
}