Skip to content
Snippets Groups Projects
CreateCalFileByNames.cpp 9.62 KiB
Newer Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/CreateCalFileByNames.h"
#include "MantidKernel/ConfigService.h"
#include "MantidAPI/InstrumentDataService.h"
#include "MantidAPI/IInstrument.h"
#include <queue>
#include <fstream>
#include <iomanip>

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/detail/classification.hpp>


namespace Mantid
{
  namespace Algorithms
  {

    // Register the class into the algorithm factory
    DECLARE_ALGORITHM(CreateCalFileByNames)

    using namespace Kernel;
    using API::Progress;

    CreateCalFileByNames::CreateCalFileByNames():API::Algorithm(),group_no(0)
    {
    }

    /** Initialisation method. Declares properties to be used in algorithm.
     *
     */
    void CreateCalFileByNames::init()
    {
      declareProperty("InstrumentName", "",
        "The name of the instrument, needs to be present in the Instrument Data\n"
        "Service" );
      declareProperty(new FileProperty("GroupingFileName","",FileProperty::Save, ".cal"),
        "The name of the output CalFile");
      declareProperty("GroupNames","",
        "A string of the instrument component names to use as separate groups.\n"
        "Use / or , to separate multiple groups");
    }

    /** Executes the algorithm
     *
     *  @throw Exception::FileError If the grouping file cannot be opened or read successfully
     *  @throw runtime_error If unable to run one of the sub-algorithms successfully
     */
    void CreateCalFileByNames::exec()
    {
      std::ostringstream mess;
      // Check that the instrument is in store
      std::string instname=getProperty("InstrumentName");
      // Get only the first 3 letters
      std::string instshort=instname;
      std::transform(instshort.begin(),instshort.end(),instshort.begin(),toupper);
      instshort=instshort+"_Definition.xml";
      // If instrument not in store, insult the user
      if (!API::InstrumentDataService::Instance().doesExist(instshort))
      {
        g_log.error("Instrument "+instshort+" is not present in data store.");
        throw std::runtime_error("Instrument "+instshort+" is not present in data store.");
      }
      // Get the instrument.
      API::IInstrument_sptr inst=API::InstrumentDataService::Instance().retrieve(instshort);

      // Get the names of groups
      std::string groupsname=getProperty("GroupNames");
      groups=groupsname;

      // Split the names of the group and insert in a vector, throw if group empty
      std::vector<std::string> vgroups;
      boost::split( vgroups, groupsname, boost::algorithm::detail::is_any_ofF<char>(",/*"));
      if (vgroups.empty())
      {
        g_log.error("Could not determine group names. Group names should be separated by / or ,");
        throw std::runtime_error("Could not determine group names. Group names should be separated by / or ,");
      }

      // Assign incremental number to each group
      std::map<std::string,int> group_map;
      int index=0;
      for (std::vector<std::string>::const_iterator it=vgroups.begin();it!=vgroups.end();it++)
        group_map[(*it)]=++index;

      // Not needed anymore
      vgroups.clear();

      // Find Detectors that belong to groups
      typedef boost::shared_ptr<Geometry::ICompAssembly> sptr_ICompAss;
      typedef boost::shared_ptr<Geometry::IComponent> sptr_IComp;
      typedef boost::shared_ptr<Geometry::IDetector> sptr_IDet;
      std::queue< std::pair<sptr_ICompAss,int> > assemblies;
      sptr_ICompAss current=boost::dynamic_pointer_cast<Geometry::ICompAssembly>(inst);
      sptr_IDet currentDet;
      sptr_IComp currentIComp;
      sptr_ICompAss currentchild;

      int top_group, child_group;

      if (current.get())
      {
        top_group=group_map[current->getName()]; // Return 0 if not in map
        assemblies.push(std::make_pair<sptr_ICompAss,int>(current,top_group));
      }

      std::string filename=getProperty("GroupingFilename");

      // Check if a template cal file is given
      bool overwrite=groupingFileDoesExist(filename);

      int number=0;
      Progress prog(this,0.0,0.8,assemblies.size());
      while(!assemblies.empty()) //Travel the tree from the instrument point
      {
        current=assemblies.front().first;
        top_group=assemblies.front().second;
        assemblies.pop();
        int nchilds=current->nelements();
        if (nchilds!=0)
        {
          for (int i=0;i<nchilds;++i)
          {
            currentIComp=(*(current.get()))[i]; // Get child
            currentDet=boost::dynamic_pointer_cast<Geometry::IDetector>(currentIComp);
            if (currentDet.get())// Is detector
            {
              if (overwrite) // Map will contains udet as the key
                instrcalib[currentDet->getID()]=std::make_pair<int,int>(number++,top_group);
              else          // Map will contains the entry number as the key
                instrcalib[number++]=std::make_pair<int,int>(currentDet->getID(),top_group);
            }
            else // Is an assembly, push in the queue
            {
              currentchild=boost::dynamic_pointer_cast<Geometry::ICompAssembly>(currentIComp);
              if (currentchild.get())
              {
                child_group=group_map[currentchild->getName()];
                if (child_group==0)
                  child_group=top_group;
                assemblies.push(std::make_pair<sptr_ICompAss,int>(currentchild,child_group));
              }
            }
          }
        }
        prog.report();
      }
      // Write the results in a file
      saveGroupingFile(filename,overwrite);
      progress(0.2);
      return;
    }

    bool CreateCalFileByNames::groupingFileDoesExist(const std::string& filename) const
    {
      std::ifstream file(filename.c_str());
      // Check if the file already exists
      if (!file)
        return false;
      file.close();
      std::ostringstream mess;
      mess << "Calibration file "<< filename << " already exist. Only grouping will be modified";
      g_log.information(mess.str());
      return true;
    }

    /// Creates and saves the output file
    void CreateCalFileByNames::saveGroupingFile(const std::string& filename,bool overwrite) const
    {
      std::ostringstream message;
      std::fstream outfile;
      std::fstream infile;
      if (!overwrite) // Open the file directly
      {
        outfile.open(filename.c_str(), std::ios::out);
        if (!outfile.is_open())
        {
          message << "Can't open Calibration File: " << filename;
          g_log.error(message.str());
          throw std::runtime_error(message.str());
          message.str("");
        }
      }
      else
      {
        infile.open(filename.c_str(),std::ios::in);
        std::string newfilename=filename+"2";
        outfile.open(newfilename.c_str(), std::ios::out);
        if (!infile.is_open())
        {
          message << "Can't open input Calibration File: " << filename;
          g_log.error(message.str());
          throw std::runtime_error(message.str());
        }
        if (!outfile.is_open())
        {
          message << "Can't open new Calibration File: " << newfilename;
          g_log.error(message.str());
          throw std::runtime_error(message.str());
        }
      }

      // Write the headers
      writeHeaders(outfile,filename,overwrite);

      if (overwrite)
      {
        int number, udet, select, group;
        double offset;

        instrcalmap::const_iterator it;
        std::string str;
        while(getline(infile,str))
        {
          if (str.empty() || str[0]=='#') // Skip the headers
            continue;
          std::istringstream istr(str);
          istr >> number >> udet >> offset >> select >> group;
          it=instrcalib.find(udet);
          if (it==instrcalib.end()) // Not found, don't assign a group
            group=0;
          group=((*it).second).second; // If found then assign new group
          writeCalEntry(outfile,number,udet,offset,select,group);
        }
      }
      else //
      {
        instrcalmap::const_iterator it=instrcalib.begin();
        for (;it!=instrcalib.end();it++)
          writeCalEntry(outfile,(*it).first,((*it).second).first,0.0,1,((*it).second).second);
      }

      // Closing
      outfile.close();
      if (overwrite)
        infile.close();
      return;
    }

    /// Writes a single calibration line to the output file
    void CreateCalFileByNames::writeCalEntry(std::ostream& os, int number, int udet, double offset, int select, int group)
    {
      os << std::fixed << std::setw(9) << number <<
        std::fixed << std::setw(15) << udet <<
        std::fixed << std::setprecision(7) << std::setw(15)<< offset <<
        std::fixed << std::setw(8) << select <<
        std::fixed << std::setw(8) << group  << "\n";
      return;
    }

    /// Writes out the header to the output file
    void CreateCalFileByNames::writeHeaders(std::ostream& os,const std::string& filename,bool overwrite) const
    {
      os << "# Diffraction focusing calibration file created by Mantid" <<  "\n";
      os << "# Detectors have been grouped using assembly names:" << groups <<"\n";
      if (overwrite)
      {
        os << "# Template file " << filename << " has been used" << "\n";
        os << "# Only grouping has been changed, offset from template file have been copied" << "\n";
      }
      else
        os << "# No template file, all offsets set to 0.0 and select to 1" << "\n";

      os << "#  Number           UDET         offset      select  group" << "\n";
      return;
    }

  } // namespace Algorithm
} // namespace Mantid