/*************************************************************************** * GNodeArray.cpp - Array of nodes class * * ----------------------------------------------------------------------- * * copyright (C) 2008-2012 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 GNodeArray.cpp * @brief Node array class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include "GException.hpp" #include "GNodeArray.hpp" #include "GTools.hpp" /* __ Method name definitions ____________________________________________ */ #define G_ACCESS "GNodeArray::operator[](int)" #define G_INTERPOLATE "GNodeArray::interpolate(double&,std::vector&)" #define G_SET_VALUE "GNodeArray::set_value(double&)" #define G_SETUP "GNodeArray::setup()" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /* __ Constants __________________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GNodeArray::GNodeArray(void) { // Initialise class members for clean destruction init_members(); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] array Node array. ***************************************************************************/ GNodeArray::GNodeArray(const GNodeArray& array) { // Initialise class members for clean destruction init_members(); // Copy members copy_members(array); // Return return; } /***********************************************************************//** * @brief Element constructor * * @param[in] num Number of elements. * @param[in] array Array of elements. * * Constructs node array from a number of elements in an array. ***************************************************************************/ GNodeArray::GNodeArray(const int& num, const double* array) { // Initialise class members for clean destruction init_members(); // Use elements to initialise nodes nodes(num, array); // Return return; } /***********************************************************************//** * @brief GVector constructor * * @param[in] vector Vector. * * Constructs node array from the elements in a GVector object. ***************************************************************************/ GNodeArray::GNodeArray(const GVector& vector) { // Initialise class members for clean destruction init_members(); // Use vector to initialise nodes nodes(vector); // Return return; } /***********************************************************************//** * @brief Vector constructor * * @param[in] vector Vector. * * Constructs node array from the elements in a double precision vector. ***************************************************************************/ GNodeArray::GNodeArray(const std::vector& vector) { // Initialise class members for clean destruction init_members(); // Use vector to initialise nodes nodes(vector); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GNodeArray::~GNodeArray(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] array Node array. * @return Node array. ***************************************************************************/ GNodeArray& GNodeArray::operator= (const GNodeArray& array) { // Execute only if object is not identical if (this != &array) { // Free members free_members(); // Initialise private members init_members(); // Copy members copy_members(array); } // endif: object was not identical // Return this object return *this; } /***********************************************************************//** * @brief Node access operator * * @param[in] index Node index [0,...,size()-1]. * @return Node value. * * @exception GException::out_of_range * Node index is out of range. ***************************************************************************/ double& GNodeArray::operator[](const int& index) { // Compile option: raise an exception if index is out of range #if defined(G_RANGE_CHECK) if (index < 0 || index >= size()) { throw GException::out_of_range(G_ACCESS, index, size()-1); } #endif // Signal that setup needs to be called m_need_setup = false; // Return node return m_node[index]; } /***********************************************************************//** * @brief Node access operator (const version) * * @param[in] index Node index [0,...,size()-1]. * @return Node value. * * @exception GException::out_of_range * Node index is out of range. ***************************************************************************/ const double& GNodeArray::operator[](const int& index) const { // Compile option: raise an exception if index is out of range #if defined(G_RANGE_CHECK) if (index < 0 || index >= size()) { throw GException::out_of_range(G_ACCESS, index, size()-1); } #endif // Signal that setup needs to be called m_need_setup = false; // Return node return m_node[index]; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear instance ***************************************************************************/ void GNodeArray::clear(void) { // Free class members free_members(); // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Clone instance * * @return Deep copy of node array. ***************************************************************************/ GNodeArray* GNodeArray::clone(void) const { return new GNodeArray(*this); } /***********************************************************************//** * @brief Set node array * * @param[in] num Number of nodes * @param[in] array Node values \f$x_i\f$. * * Setup node array from an array of double precision values. ***************************************************************************/ void GNodeArray::nodes(const int& num, const double* array) { // Free any existing memory free_members(); // Initialise members init_members(); // Set node values for (int i = 0; i < num; ++i) { m_node.push_back(array[i]); } // Setup node distances and linear array handling setup(); // Return return; } /***********************************************************************//** * @brief Set node array from vector * * @param[in] vector Vector from which node array will be built. * * Setup node array from a vector of values. ***************************************************************************/ void GNodeArray::nodes(const GVector& vector) { // Free any existing memory free_members(); // Initialise members init_members(); // Set node values for (int i = 0; i < vector.size(); ++i) { m_node.push_back(vector[i]); } // Setup node distances and linear array handling setup(); // Return return; } /***********************************************************************//** * @brief Set node array from vector * * @param[in] vector Vector from which node array will be built. * * Setup node array from a vector of double precision values. ***************************************************************************/ void GNodeArray::nodes(const std::vector& vector) { // Free any existing memory free_members(); // Initialise members init_members(); // Set node values m_node = vector; // Setup node distances and linear array handling setup(); // Return return; } /***********************************************************************//** * @brief Append one node to array. * * @param[in] node Node to be appended to array. ***************************************************************************/ void GNodeArray::append(const double& node) { // Add node m_node.push_back(node); // Setup node distances and linear array handling setup(); // Return return; } /***********************************************************************//** * @brief Interpolate value. * * @param[in] value Value \f$x\f$ at which interpolation should be done. * @param[in] vector Vector \f$y_i\f$ that should be interpolated. * * @exception GException::not_enough_nodes * Not enough nodes for interpolation in node array. * @exception GException::vector_mismatch * Size of node vector does not match the size of vector argument. * * This method performs a linear interpolation of values \f$y_i\f$. The * corresponding values \f$x_i\f$ are stored in the node array. ***************************************************************************/ double GNodeArray::interpolate(const double& value, const std::vector& vector) const { // Throw exception if there are not enough nodes if (m_node.size() < 2) { throw GException::not_enough_nodes(G_INTERPOLATE, m_node.size()); } // Throw exception if vectors have not the same size if (m_node.size() != vector.size()) { throw GException::vector_mismatch(G_INTERPOLATE, m_node.size(), vector.size()); } // Set interpolation value (circumvent const correctness) set_value(value); // Interpolate double y = vector[inx_left()] * wgt_left() + vector[inx_right()] * wgt_right(); // Return return y; } /***********************************************************************//** * @brief Set indices and weighting factors for interpolation * * @param[in] value Value for which the interpolation should be done. * * @exception GException::not_enough_nodes * At least two nodes are required for setting up the factors * * Set the indices that bound the specified value and the corresponding * weighting factors for linear interpolation. If the array has a linear * form (i.e. the nodes are equidistant), an analytic formula is used to * determine the boundary indices. If the nodes are not equidistant the * boundary indices are searched by bisection. ***************************************************************************/ void GNodeArray::set_value(const double& value) const { // Get number of nodes int nodes = m_node.size(); // Throw an exception if less than 2 nodes are available if (nodes < 2) { throw GException::not_enough_nodes(G_SET_VALUE, nodes); } // Initialize computation flag bool compute = true; // Update cache if required. If cache was updated, computation is // enforced. Otherwise we check if the value has changed. The value // check is only done when a last value has been recorded. if (m_need_setup) { setup(); } else { if (m_has_last_value && value == m_last_value) { compute = false; } } // Continue only if computation is required if (compute) { // If array is linear then get left index from analytic formula if (m_is_linear) { // Set left index m_inx_left = int(m_linear_slope * value + m_linear_offset); // Keep index in valid range if (m_inx_left < 0) { m_inx_left = 0; } else if (m_inx_left >= nodes-1) { m_inx_left = nodes - 2; } } // endif: array is linear // ... otherwise search the relevant indices by bisection else { // Set left index if value is before first node if (value < m_node[0]) { m_inx_left = 0; } // Set left index if value is after last node else if (value > m_node[nodes-1]) { m_inx_left = nodes - 2; } // Set left index by bisection else { int low = 0; int high = nodes - 1; while ((high - low) > 1) { int mid = (low+high) / 2; if (m_node[mid] > value) { high = mid; } else { low = mid; } } m_inx_left = low; } // endelse: did bisection } // Set right index m_inx_right = m_inx_left + 1; // Set weighting factors m_wgt_right = (value - m_node[m_inx_left]) / m_step[m_inx_left]; m_wgt_left = 1.0 - m_wgt_right; } // endif: computation was required // Store last value and signal availability m_last_value = value; m_has_last_value = true; // Return return; } /***********************************************************************//** * @brief Print nodes ***************************************************************************/ std::string GNodeArray::print(void) const { // Initialise result string std::string result; // Append header result.append("=== GNodeArray ==="); // Append array type result.append("\n"+parformat("Number of nodes in array")+str(size())); if (m_is_linear) { result.append("\n"+parformat("Array type")+"linear"); result.append("\n"+parformat("Linear slope")+str(m_linear_slope)); result.append("\n"+parformat("Linear offset")+str(m_linear_offset)); } else result.append("\n"+parformat("Array type")+"nonlinear"); // Append indices and weights result.append("\n"+parformat("Indices and weights")); result.append("("+str(m_inx_left)+","+str(m_inx_right)+")="); result.append("("+str(m_wgt_left)+","+str(m_wgt_right)+")"); // Append nodes for (int i = 0; i < size(); ++i) { result.append("\n"+parformat("Node "+str(i))); result.append(str(m_node[i])); if (i < m_step.size()) { result.append(" (delta="+str(m_step[i])+")"); } } // Return result return result; } /*========================================================================== = = = GNodeArray private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GNodeArray::init_members(void) { // Initialise members m_node.clear(); m_step.clear(); m_is_linear = false; m_has_last_value = false; m_last_value = 0.0; m_linear_slope = 0.0; m_linear_offset = 0.0; m_inx_left = 0; m_inx_right = 0; m_wgt_left = 0.0; m_wgt_right = 0.0; m_need_setup = false; // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] array Node array to be copied. ***************************************************************************/ void GNodeArray::copy_members(const GNodeArray& array) { // Copy number of bins m_node = array.m_node; m_step = array.m_step; m_is_linear = array.m_is_linear; m_has_last_value = array.m_has_last_value; m_last_value = array.m_last_value; m_linear_slope = array.m_linear_slope; m_linear_offset = array.m_linear_offset; m_inx_left = array.m_inx_left; m_inx_right = array.m_inx_right; m_wgt_left = array.m_wgt_left; m_wgt_right = array.m_wgt_right; m_need_setup = array.m_need_setup; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GNodeArray::free_members(void) { // Return return; } /***********************************************************************//** * @brief Compute distance array and linear slope/offset * * Precomputes values for fast interpolation. The precomputation requires * at least 2 nodes to be present in the node array. If less then two * nodes are present, the distance vector m_step will be empty and no * computation is done. ***************************************************************************/ void GNodeArray::setup(void) const { // Reset distance vector m_step.clear(); // Get number of nodes int nodes = m_node.size(); // Continue only if there are at least 2 nodes if (nodes > 1) { // Setup distance array between subsequent nodes for (int i = 0; i < nodes-1; ++i) { m_step.push_back(m_node[i+1] - m_node[i]); } // Evaluate linear slope and offset m_linear_slope = double(nodes-1) / (m_node[nodes-1] - m_node[0]); m_linear_offset = -m_linear_slope * m_node[0]; // Check if nodes form a linear array m_is_linear = true; for (int i = 0; i < nodes-1; ++i) { double eps = m_linear_slope * m_node[i] + m_linear_offset - double(i); if (std::abs(eps) > 1.0e-6) { m_is_linear = false; break; } } } // endif: there were at least two nodes // Signal that setup has been called m_need_setup = false; // Return return; }