/** method calculates fake detectors positions in the situation when real detector information has been lost  */
    void PreprocessDetectorsToMD::buildFakeDetectorsPositions(const API::MatrixWorkspace_const_sptr &inputWS,DataObjects::TableWorkspace_sptr &targWS)
    {
      UNUSED_ARG(inputWS);
      // set sample-detector position equal to 1;
      targWS->logs()->addProperty<double>("L1",1.,true);
      // 
      targWS->logs()->addProperty<std::string>("InstrumentName","FakeInstrument",true);    
      targWS->logs()->addProperty<bool>("FakeDetectors",true,true);



      // get access to the workspace memory
      auto &sp2detMap  = targWS->getColVector<size_t>("spec2detMap");
      auto &detId      = targWS->getColVector<int32_t>("DetectorID");
      auto &detIDMap   = targWS->getColVector<size_t>("detIDMap");
      auto &L2         = targWS->getColVector<double>("L2");
      auto &TwoTheta   = targWS->getColVector<double>("TwoTheta");
      auto &Azimuthal  = targWS->getColVector<double>("Azimuthal");
      auto &detDir     = targWS->getColVector<Kernel::V3D>("DetDirections"); 
  //    auto &detMask    = targWS->getColVector<bool>("detMask");

      //// progress message appearance  
      size_t nHist = targWS->rowCount();
      targWS->logs()->addProperty<uint32_t>("ActualDetectorsNum",uint32_t(nHist),true);

      double polar(0);
      // Loop over the spectra
      for (size_t i = 0; i < nHist; i++)
      {
        sp2detMap[i]= i;
        detId[i]    = (detid_t)i;
        detIDMap[i] = i;
        L2[i]       = 1;

        TwoTheta[i] =  polar;
        Azimuthal[i] = 0;
        //this->SinThetaSq[i]= 0;

        double ez = 1.;
        double ex = 0.;
        double ey = 0.;

        detDir[i].setX(ex);
        detDir[i].setY(ey);
        detDir[i].setZ(ez);

      }
      // 
    }
/** method does preliminary calculations of the detectors positions to convert
results into k-dE space ;
and places the results into static cash to be used in subsequent calls to this
algorithm */
void PreprocessDetectorsToMD::processDetectorsPositions(
    const API::MatrixWorkspace_const_sptr &inputWS,
    DataObjects::TableWorkspace_sptr &targWS) {
  g_log.information()
      << "Preprocessing detector locations in a target reciprocal space\n";
  //
  Geometry::Instrument_const_sptr instrument = inputWS->getInstrument();
  // this->pBaseInstr                = instrument->baseInstrument();
  //
  Geometry::IComponent_const_sptr source = instrument->getSource();
  Geometry::IComponent_const_sptr sample = instrument->getSample();
  if ((!source) || (!sample)) {
    g_log.error() << " Instrument is not fully defined. Can not identify "
                     "source or sample\n";
    throw Kernel::Exception::InstrumentDefinitionError(
        "Instrument not sufficiently defined: failed to get source and/or "
        "sample");
  }

  // L1
  try {
    double L1 = source->getDistance(*sample);
    targWS->logs()->addProperty<double>("L1", L1, true);
    g_log.debug() << "Source-sample distance: " << L1 << '\n';
  } catch (Kernel::Exception::NotFoundError &) {
    throw Kernel::Exception::InstrumentDefinitionError(
        "Unable to calculate source-sample distance for workspace",
        inputWS->getTitle());
  }
  // Instrument name
  std::string InstrName = instrument->getName();
  targWS->logs()->addProperty<std::string>(
      "InstrumentName", InstrName,
      true); // "The name which should unique identify current instrument");
  targWS->logs()->addProperty<bool>("FakeDetectors", false, true);

  // get access to the workspace memory
  auto &sp2detMap = targWS->getColVector<size_t>("spec2detMap");
  auto &detId = targWS->getColVector<int32_t>("DetectorID");
  auto &detIDMap = targWS->getColVector<size_t>("detIDMap");
  auto &L2 = targWS->getColVector<double>("L2");
  auto &TwoTheta = targWS->getColVector<double>("TwoTheta");
  auto &Azimuthal = targWS->getColVector<double>("Azimuthal");
  auto &detDir = targWS->getColVector<Kernel::V3D>("DetDirections");

  // Efixed; do we need one and does one exist?
  double Efi = targWS->getLogs()->getPropertyValueAsType<double>("Ei");
  float *pEfixedArray(nullptr);
  const Geometry::ParameterMap &pmap = inputWS->constInstrumentParameters();
  if (m_getEFixed)
    pEfixedArray = targWS->getColDataArray<float>("eFixed");

  // check if one needs to generate masked detectors column.
  int *pMasksArray(nullptr);
  if (m_getIsMasked)
    pMasksArray = targWS->getColDataArray<int>("detMask");

  //// progress message appearance
  size_t div = 100;
  size_t nHist = targWS->rowCount();
  Mantid::API::Progress theProgress(this, 0, 1, nHist);
  //// Loop over the spectra
  uint32_t liveDetectorsCount(0);
  const auto &spectrumInfo = inputWS->spectrumInfo();
  for (size_t i = 0; i < nHist; i++) {
    sp2detMap[i] = std::numeric_limits<uint64_t>::quiet_NaN();
    detId[i] = std::numeric_limits<int32_t>::quiet_NaN();
    detIDMap[i] = std::numeric_limits<uint64_t>::quiet_NaN();
    L2[i] = std::numeric_limits<double>::quiet_NaN();
    TwoTheta[i] = std::numeric_limits<double>::quiet_NaN();
    Azimuthal[i] = std::numeric_limits<double>::quiet_NaN();
    //     detMask[i]  = true;

    if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i))
      continue;

    // if masked detectors state is not used, masked detectors just ignored;
    bool maskDetector = spectrumInfo.isMasked(i);
    if (m_getIsMasked)
      *(pMasksArray + liveDetectorsCount) = maskDetector ? 1 : 0;
    else if (maskDetector)
      continue;

    const auto &spDet = spectrumInfo.detector(i);

    // calculate the requested values;
    sp2detMap[i] = liveDetectorsCount;
    detId[liveDetectorsCount] = int32_t(spDet.getID());
    detIDMap[liveDetectorsCount] = i;
    L2[liveDetectorsCount] = spectrumInfo.l2(i);

    double polar = spectrumInfo.twoTheta(i);
    double azim = spDet.getPhi();
    TwoTheta[liveDetectorsCount] = polar;
    Azimuthal[liveDetectorsCount] = azim;

    double sPhi = sin(polar);
    double ez = cos(polar);
    double ex = sPhi * cos(azim);
    double ey = sPhi * sin(azim);

    detDir[liveDetectorsCount].setX(ex);
    detDir[liveDetectorsCount].setY(ey);
    detDir[liveDetectorsCount].setZ(ez);

    // double sinTheta=sin(0.5*polar);
    // this->SinThetaSq[liveDetectorsCount]  = sinTheta*sinTheta;

    // specific code which should work and makes sense
    // for indirect instrument but may be deployed on any code with Ei property
    // defined;
    if (pEfixedArray) {
      try {
        Geometry::Parameter_sptr par = pmap.getRecursive(&spDet, "eFixed");
        if (par)
          Efi = par->value<double>();
      } catch (std::runtime_error &) {
      }
      // set efixed for each existing detector
      *(pEfixedArray + liveDetectorsCount) = static_cast<float>(Efi);
    }

    liveDetectorsCount++;
    if (i % div == 0)
      theProgress.report(i, "Preprocessing detectors");
  }
  targWS->logs()->addProperty<uint32_t>("ActualDetectorsNum",
                                        liveDetectorsCount, true);

  theProgress.report();
  g_log.information() << "Finished preprocessing detector locations. Found: "
                      << liveDetectorsCount << " detectors out of: " << nHist
                      << " histograms\n";
}
/**The method responsible for analyzing input workspace parameters and
*preprocessing detectors positions into reciprocal space
*
* @param InWS2D -- input Matrix workspace with defined instrument
* @param dEModeRequested -- energy conversion mode (direct/indirect/elastic)
* @param updateMasks  --  if full detector positions calculations or just update
*masking requested
* @param OutWSName    -- the name for the preprocessed detectors workspace to
*have in the analysis data service
*
* @return          shared pointer to the workspace with preprocessed detectors
*information.
*/
DataObjects::TableWorkspace_const_sptr
ConvertToMDParent::preprocessDetectorsPositions(
    const Mantid::API::MatrixWorkspace_const_sptr &InWS2D,
    const std::string &dEModeRequested, bool updateMasks,
    const std::string &OutWSName) {

  DataObjects::TableWorkspace_sptr TargTableWS;
  Kernel::DeltaEMode::Type Emode;

  // Do we need to reuse output workspace
  bool storeInDataService(true);
  std::string tOutWSName(OutWSName);
  if (tOutWSName == "-" ||
      tOutWSName.empty()) // TargTableWS is recalculated each time;
  {
    storeInDataService = false;
    tOutWSName = "ServiceTableWS"; // TODO: should be hidden?
  } else {
    storeInDataService = true;
  }

  // if output workspace exists in dataservice, we may try to use it
  if (storeInDataService &&
      API::AnalysisDataService::Instance().doesExist(tOutWSName)) {
    TargTableWS = API::AnalysisDataService::Instance()
                      .retrieveWS<DataObjects::TableWorkspace>(tOutWSName);
    // get number of all histograms (may be masked or invalid)
    size_t nHist = InWS2D->getNumberHistograms();
    size_t nDetMap = TargTableWS->rowCount();
    if (nHist == nDetMap) {
      // let's take at least some precaution to ensure that instrument have not
      // changed
      std::string currentWSInstrumentName = InWS2D->getInstrument()->getName();
      std::string oldInstrName =
          TargTableWS->getLogs()->getPropertyValueAsType<std::string>(
              "InstrumentName");

      if (oldInstrName == currentWSInstrumentName) {
        // a direct mode instrument can be unchanged but incident energy can be
        // different.
        // It is cheap operation so we should always replace incident energy on
        // the target workspace
        bool hasEi = InWS2D->run().hasProperty("Ei");
        bool hasEfix = InWS2D->run().hasProperty("eFixed");
        if (hasEi || hasEfix) {

          double Ei;
          if (hasEi)
            Ei = InWS2D->run().getPropertyValueAsType<double>("Ei");
          if (hasEfix)
            Ei = InWS2D->run().getPropertyValueAsType<double>("eFixed");

          TargTableWS->logs()->addProperty<double>("Ei", Ei, true);
        } else {
          Emode = Kernel::DeltaEMode::fromString(dEModeRequested);
          if (Emode == Kernel::DeltaEMode::Direct)
            throw(std::invalid_argument(
                "Input neutron's energy has to be present at the workspace as "
                "Ei or eFixed number log in Direct inelastic mode"));
          // if(Emode==Kernel::DeltaEMode::Indirect && !hasEfix)
          //    throw(std::invalid_argument("Input neutron's energy has to be
          //    present at the workspace as eFixed number log in Indirect
          //    inelastic mode"));
        }

        if (!updateMasks)
          return TargTableWS;
        // Target workspace with preprocessed detectors exists and seems is
        // correct one.
        // We still need to update masked detectors information
        TargTableWS = this->runPreprocessDetectorsToMDChildUpdatingMasks(
            InWS2D, tOutWSName, dEModeRequested, Emode);
        return TargTableWS;
      }
    } else // there is a workspace in the data service with the same name but
           // this ws is not suitable as target for this algorithm.
    {      // Should delete this WS from the dataservice
      API::AnalysisDataService::Instance().remove(tOutWSName);
    }
  }
  // No result found in analysis data service or the result is unsatisfactory.
  // Try to calculate target workspace.

  TargTableWS = this->runPreprocessDetectorsToMDChildUpdatingMasks(
      InWS2D, tOutWSName, dEModeRequested, Emode);

  if (storeInDataService)
    API::AnalysisDataService::Instance().addOrReplace(tOutWSName, TargTableWS);
  //    else
  //      TargTableWS->setName(OutWSName);

  // check if we got what we wanted:

  // in direct or indirect mode input ws has to have input energy
  if (Emode == Kernel::DeltaEMode::Direct ||
      Emode == Kernel::DeltaEMode::Indirect) {
    double m_Ei = TargTableWS->getLogs()->getPropertyValueAsType<double>("Ei");
    if (isNaN(m_Ei)) {
      // Direct mode needs Ei
      if (Emode == Kernel::DeltaEMode::Direct)
        throw(std::invalid_argument(
            "Input neutron's energy has to be defined in inelastic mode "));

      // Do we have at least something for Indirect?
      float *eFixed = TargTableWS->getColDataArray<float>("eFixed");
      if (!eFixed)
        throw(std::invalid_argument(
            "Input neutron's energy has to be defined in inelastic mode "));

      uint32_t NDetectors =
          TargTableWS->getLogs()->getPropertyValueAsType<uint32_t>(
              "ActualDetectorsNum");
      for (uint32_t i = 0; i < NDetectors; i++)
        if (isNaN(*(eFixed + i)))
          throw(
              std::invalid_argument("Undefined eFixed energy for detector N: " +
                                    boost::lexical_cast<std::string>(i)));
    }
  }

  return TargTableWS;
}