/***************************************************************************
* GModel.cpp - Abstract virtual model base class *
* ----------------------------------------------------------------------- *
* copyright (C) 2009-2013 by Juergen Knoedlseder *
* ----------------------------------------------------------------------- *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
***************************************************************************/
/**
* @file GModel.cpp
* @brief Abstract model base class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include "GTools.hpp"
#include "GException.hpp"
#include "GModel.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_ACCESS1 "GModel::operator[](int&)"
#define G_ACCESS2 "GModel::operator[](std::string&)"
#define G_WRITE_SCALES "GModel::write_scales(GXmlElement&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GModel::GModel(void)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief XML constructor
*
* @param[in] xml XML element.
*
* Construct model from XML element. The method extracts the "name",
* "instrument" and "id" attributes from the model node.
***************************************************************************/
GModel::GModel(const GXmlElement& xml)
{
// Initialise members
init_members();
// Set model name
name(xml.attribute("name"));
// Set instruments
instruments(xml.attribute("instrument"));
// Set observation identifiers
ids(xml.attribute("id"));
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] model Model.
***************************************************************************/
GModel::GModel(const GModel& model)
{
// Initialise members
init_members();
// Copy members
copy_members(model);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GModel::~GModel(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] model Model.
* @return Model.
***************************************************************************/
GModel& GModel::operator=(const GModel& model)
{
// Execute only if object is not identical
if (this != &model) {
// Free members
free_members();
// Initialise private members for clean destruction
init_members();
// Copy members
copy_members(model);
} // endif: object was not identical
// Return
return *this;
}
/***********************************************************************//**
* @brief Returns reference to model parameter by index
*
* @param[in] index Parameter index [0,...,size()-1].
* @return Reference to model parameter.
*
* @exception GException::out_of_range
* Parameter index is out of range.
***************************************************************************/
GModelPar& GModel::operator[](const int& index)
{
// Compile option: raise exception if index is out of range
#if defined(G_RANGE_CHECK)
if (index < 0 || index >= size()) {
throw GException::out_of_range(G_ACCESS1, index, 0, size()-1);
}
#endif
// Return reference
return *(m_pars[index]);
}
/***********************************************************************//**
* @brief Returns reference to model parameter by index (const version)
*
* @param[in] index Parameter index [0,...,size()-1].
* @return Const reference to model parameter.
*
* @exception GException::out_of_range
* Parameter index is out of range.
***************************************************************************/
const GModelPar& GModel::operator[](const int& index) const
{
// Compile option: raise exception if index is out of range
#if defined(G_RANGE_CHECK)
if (index < 0 || index >= size()) {
throw GException::out_of_range(G_ACCESS1, index, 0, size()-1);
}
#endif
// Return reference
return *(m_pars[index]);
}
/***********************************************************************//**
* @brief Returns reference to model parameter by name
*
* @param[in] name Parameter name.
* @return Reference to model parameter.
*
* @exception GException::par_not_found
* Parameter with specified name not found in container.
***************************************************************************/
GModelPar& GModel::operator[](const std::string& name)
{
// Get parameter index
int index = 0;
for (; index < size(); ++index) {
if (m_pars[index]->name() == name) {
break;
}
}
// Throw exception if parameter name was not found
if (index >= size()) {
throw GException::par_not_found(G_ACCESS2, name);
}
// Return reference
return *(m_pars[index]);
}
/***********************************************************************//**
* @brief Returns reference to model parameter by name const version)
*
* @param[in] name Parameter name.
* @return Reference to model parameter.
*
* @exception GException::par_not_found
* Parameter with specified name not found in container.
***************************************************************************/
const GModelPar& GModel::operator[](const std::string& name) const
{
// Get parameter index
int index = 0;
for (; index < size(); ++index) {
if (m_pars[index]->name() == name) {
break;
}
}
// Throw exception if parameter name was not found
if (index >= size()) {
throw GException::par_not_found(G_ACCESS2, name);
}
// Return reference
return *(m_pars[index]);
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Returns instruments to which model applies
*
* Returns a comma separated list of instruments to which model applies. If
* no instrument exists then an empty string is returned.
***************************************************************************/
std::string GModel::instruments(void) const
{
// Initialise string
std::string result;
// Attach all instruments
for (int i = 0; i < m_instruments.size(); ++i) {
if (i > 0) {
result += ",";
}
result += m_instruments[i];
}
// Return
return result;
}
/***********************************************************************//**
* @brief Set instruments to which model applies
*
* @param[in] instruments String of instruments.
*
* Sets the instruments to which the model applies from a comma separated
* list of strings. If the instrument string is empty the model is considered
* to apply to all instruments.
***************************************************************************/
void GModel::instruments(const std::string& instruments)
{
// Clear instruments vector
m_instruments.clear();
// Extract instruments
std::vector inst = split(instruments, ",");
// Attach all instruments
for (int i = 0; i < inst.size(); ++i) {
m_instruments.push_back(toupper(strip_whitespace(inst[i])));
}
// Return
return;
}
/***********************************************************************//**
* @brief Returns model scale factor for a given instrument
*
* @param[in] instrument Instrument.
*
* Returns the model scale factor for a given instrument. If the instrument
* name is not found, the method returns a scale factor of unity.
***************************************************************************/
GModelPar GModel::scale(const std::string& instrument) const
{
// Convert instrument to upper case string
std::string uinstrument = toupper(strip_whitespace(instrument));
// Initialise unit scale factor
GModelPar scale;
scale.real_value(1.0);
scale.name(uinstrument);
scale.fix();
// Search for instrument and recover scale factor if the instrument
// has been found.
for (int i = 0; i < m_scales.size(); ++i) {
if (m_scales[i].name() == uinstrument) {
scale = m_scales[i];
break;
}
}
// Return scale factor
return scale;
}
/***********************************************************************//**
* @brief Set model scale factor for a given instrument
*
* @param[in] par Model parameter for scaling.
*
* Sets the model parameter for a given instrument. The instrument name will
* be determined from the model parameter name. The model parameter name
* will be converted to upper case and any leading and trailing whitespace
* will be stripped.
*
* If the instrument is not yet defined it will be appended to the list of
* instruments.
***************************************************************************/
void GModel::scale(const GModelPar& par)
{
// Convert instrument to upper case string
std::string uinstrument = toupper(strip_whitespace(par.name()));
// Search for instrument and copy the model parameter if the instrument
// has been found. Make sure that the instrument name is in upper case.
bool found = false;
for (int i = 0; i < m_scales.size(); ++i) {
if (m_scales[i].name() == uinstrument) {
found = true;
m_scales[i] = par;
m_scales[i].name(uinstrument);
break;
}
}
// If instrument has not been found then append it now to the list
// of instruments.
if (!found) {
m_scales.push_back(par);
m_scales[m_scales.size()-1].name(uinstrument);
}
// Return
return;
}
/***********************************************************************//**
* @brief Returns observation identifiers to which model applies
*
* Returns a comma separated list of observation identifiers to which model
* applies. If no observation identifier exists then an empty string is
* returned.
***************************************************************************/
std::string GModel::ids(void) const
{
// Initialise string
std::string result;
// Attach all observation identifiers
for (int i = 0; i < m_ids.size(); ++i) {
if (i > 0) {
result += ",";
}
result += m_ids[i];
}
// Return
return result;
}
/***********************************************************************//**
* @brief Set observation identifiers to which model applies
*
* @param[in] ids String of observation identifiers.
*
* Sets the observation identifiers to which the model applies from a comma
* separated list of strings. If the observation identifier string is empty,
* the model is considered to apply to all observation identifiers.
***************************************************************************/
void GModel::ids(const std::string& ids)
{
// Clear observation identifier vector
m_ids.clear();
// Extract observation identifiers
std::vector id = split(ids, ",");
// Attach all observation identifiers
for (int i = 0; i < id.size(); ++i) {
m_ids.push_back(toupper(strip_whitespace(id[i])));
}
// Return
return;
}
/***********************************************************************//**
* @brief Verifies if model is valid for a given instrument and identifier
*
* @param[in] instrument Instrument name.
* @param[in] id Observation identifier.
* @return Validity flag
*
* Checks if specified instrument name and observation identifier is in list
* of applicable instruments and identifiers. The check is case insensitive.
*
* If the list of applicable instruments is empty, the model applies to all
* possible instruments. If the list of applicable observation identifiers
* is empty, the model applies to all identifiers.
***************************************************************************/
bool GModel::isvalid(const std::string& instrument,
const std::string& id) const
{
// Initialise validity
bool valid = true;
// Check if model applies to instrument
if (!m_instruments.empty()) {
// Convert instrument name to upper case
std::string uinstrument = toupper(instrument);
// Initialise validity flag
valid = false;
// Check if instrument is in list
for (int i = 0; i < m_instruments.size(); ++i) {
if (uinstrument == m_instruments[i]) {
valid = true;
break;
}
}
}
// Check if model applies to observation identifier
if (valid && !m_ids.empty()) {
// Convert observation identifier to upper case
std::string uid = toupper(id);
// Initialise validity flag
valid = false;
// Check if name is in list
for (int i = 0; i < m_ids.size(); ++i) {
if (uid == m_ids[i]) {
valid = true;
break;
}
}
}
// Return
return valid;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GModel::init_members(void)
{
// Initialise members
m_name.clear();
m_instruments.clear();
m_scales.clear();
m_ids.clear();
m_pars.clear();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] model Model.
***************************************************************************/
void GModel::copy_members(const GModel& model)
{
// Copy members
m_name = model.m_name;
m_instruments = model.m_instruments;
m_scales = model.m_scales;
m_ids = model.m_ids;
m_pars = model.m_pars;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GModel::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Read instrument scales from XML element
*
* @param[in] xml XML source element.
*
* Reads the instrument scale factors from a tag with the following format
*
*
*
*
*
*
* The name of each instrument will be converted to upper case and any
* leading or trailing whitespace will be removed.
*
* If no scaling tag is found, all instrument scale factors will be cleared.
***************************************************************************/
void GModel::read_scales(const GXmlElement& xml)
{
// Clear any existing scales
m_scales.clear();
// Continue only if there is a tag
if (xml.elements("scaling") != 0) {
// Get pointer on first instrument scale factors
GXmlElement* scales = static_cast(xml.element("scaling", 0));
// Determine number of scale factors
int nscales = scales->elements("instrument");
// Read all scale factors
for (int i = 0; i < nscales; ++i) {
GXmlElement* par =
static_cast(scales->element("instrument", i));
GModelPar scale;
scale.read(*par);
scale.name(toupper(strip_whitespace(par->attribute("name"))));
m_scales.push_back(scale);
}
}
// Return
return;
}
/***********************************************************************//**
* @brief Write instrument scales into XML element
*
* @param[in] xml XML source element.
*
* @exception GException::model_invalid_parnum
* Invalid number of instrument tags found in XML element.
*
* If there are instrument scale factors then add a tag with the following
* format to the XML element:
*
*
*
*
*
***************************************************************************/
void GModel::write_scales(GXmlElement& xml) const
{
// Continue only is scale factors are present
if (!m_scales.empty()) {
// Get number of instruments
int num = m_scales.size();
// Initialise scaling tag
GXmlElement* scale = NULL;
// If no tag exists then add one now with the required
// number of instruments ...
if (xml.elements("scaling") == 0) {
scale = new GXmlElement("scaling");
for (int i = 0; i < num; ++i) {
scale->append(new GXmlElement("instrument"));
}
xml.append(scale);
}
// ... otherwise get first tag
else {
scale = static_cast(xml.element("scaling", 0));
}
// Verify that scaling tag has the required number of instruments
if (scale->elements() != num || scale->elements("instrument") != num) {
throw GException::model_invalid_parnum(G_WRITE_SCALES, *scale,
"Instrument scaling needs "+str(num)+" instrument tags.");
}
// Write all instruments
for (int i = 0; i < num; ++i) {
// Get instrument element
GXmlElement* inst = static_cast(scale->element("instrument", i));
// Set instrument name
inst->attribute("name", m_scales[i].name());
// Write instrument scaling factor
m_scales[i].write(*inst);
} // endfor: looped over all instruments
}
// Return
return;
}
/***********************************************************************//**
* @brief Print model attributes
*
* @return Returns string with model attributes.
***************************************************************************/
std::string GModel::print_attributes(void) const
{
// Initialise result string
std::string result;
// Append model name
result.append(parformat("Name")+name());
// Append instruments
result.append("\n"+parformat("Instruments"));
if (!m_instruments.empty()) {
for (int i = 0; i < m_instruments.size(); ++i) {
if (i > 0) {
result.append(", ");
}
result.append(m_instruments[i]);
}
}
else {
result.append("all");
}
// Append instrument scale factors
result.append("\n"+parformat("Instrument scale factors"));
if (!m_scales.empty()) {
for (int i = 0; i < m_scales.size(); ++i) {
if (i > 0) {
result.append(", ");
}
result.append(m_scales[i].name());
result.append("=");
result.append(str(m_scales[i].real_value()));
}
result.append(", others unity");
}
else {
result.append("unity");
}
// Append observation identifiers
result.append("\n"+parformat("Observation identifiers"));
if (!m_ids.empty()) {
for (int i = 0; i < m_ids.size(); ++i) {
if (i > 0) {
result.append(", ");
}
result.append(m_ids[i]);
}
}
else {
result.append("all");
}
// Return result
return result;
}