Skip to content
Snippets Groups Projects
CreateCalFileByNames.cpp 8.64 KiB
Newer Older
Laurent Chapon's avatar
Laurent Chapon committed
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/detail/classification.hpp>
#include "MantidKernel/FileValidator.h"
#include "MantidKernel/ConfigService.h"
#include "MantidAPI/InstrumentDataService.h"
#include "MantidAPI/IInstrument.h"
#include <queue>
#include <fstream>
#include "MantidAlgorithms/CreateCalFileByNames.h"


namespace Mantid
{
namespace Algorithms
{

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

using namespace Kernel;
Laurent Chapon's avatar
Laurent Chapon committed

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("GroupingFileName","",
    new FileValidator(std::vector<std::string>(1,"cal"),false),
    "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");
Laurent Chapon's avatar
Laurent Chapon committed
}

/** 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");
Laurent Chapon's avatar
Laurent Chapon committed
	// Get only the first 3 letters
Laurent Chapon's avatar
Laurent Chapon committed
	std::string instshort=instname;
Laurent Chapon's avatar
Laurent Chapon committed
	std::transform(instshort.begin(),instshort.end(),instshort.begin(),toupper);
	instshort=instshort+"_Definition.xml";
Laurent Chapon's avatar
Laurent Chapon committed
	// If instrument not in store, insult the user
Laurent Chapon's avatar
Laurent Chapon committed
	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");
Laurent Chapon's avatar
Laurent Chapon committed
	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())
Laurent Chapon's avatar
Laurent Chapon committed
	{
		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 ,");
	}
Laurent Chapon's avatar
Laurent Chapon committed

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

Laurent Chapon's avatar
Laurent Chapon committed
	// Not needed anymore
	vgroups.clear();

Laurent Chapon's avatar
Laurent Chapon committed
	// 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));
	}
Laurent Chapon's avatar
Laurent Chapon committed

Laurent Chapon's avatar
Laurent Chapon committed
	std::string filename=getProperty("GroupingFilename");

Laurent Chapon's avatar
Laurent Chapon committed
	// Check if a template cal file is given
	bool overwrite=groupingFileDoesExist(filename);

	int number=0;
	Progress prog(this,0.0,0.8,assemblies.size());
Laurent Chapon's avatar
Laurent Chapon committed
	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
				{
Laurent Chapon's avatar
Laurent Chapon committed
					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);
Laurent Chapon's avatar
Laurent Chapon committed
				}
				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));
					}
				}
			}
		}
Laurent Chapon's avatar
Laurent Chapon committed
	}
Laurent Chapon's avatar
Laurent Chapon committed
	// Write the results in a file
	saveGroupingFile(filename,overwrite);
Laurent Chapon's avatar
Laurent Chapon committed
	return;
}

Laurent Chapon's avatar
Laurent Chapon committed
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;
}
Laurent Chapon's avatar
Laurent Chapon committed

/// Creates and saves the output file
Laurent Chapon's avatar
Laurent Chapon committed
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
Laurent Chapon's avatar
Laurent Chapon committed
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
Laurent Chapon's avatar
Laurent Chapon committed
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;
}
Laurent Chapon's avatar
Laurent Chapon committed

} // namespace Algorithm
} // namespace Mantid