/* # <> # Copyright 2019, Lawrence Livermore National Security, LLC. # This file is part of the gidiplus package (https://github.com/LLNL/gidiplus). # gidiplus is licensed under the MIT license (see https://opensource.org/licenses/MIT). # SPDX-License-Identifier: MIT # <> */ #include "GIDI.hpp" #include namespace GIDI { namespace Functions { /* *********************************************************************************************************//** * * * @param a_x [in] The * @param a_epsilon [in] The fractional amount to move the x-value. * * @return double. ***********************************************************************************************************/ static double getAdjustedX( double a_x, double a_epsilon ) { double xp = a_epsilon; if( a_x != 0.0 ) { if( a_x < 0 ) { xp = a_x * ( 1.0 - a_epsilon ); } else { xp = a_x * ( 1.0 + a_epsilon ); } } return( xp ); } /*! \class Regions1d * Class for the GNDS <**regions1d**> node. */ /* *********************************************************************************************************//** * @param a_construction [in] Used to pass user options to the constructor. * @param a_node [in] The **HAPI::Node** to be parsed and used to construct the XYs2d. * @param a_setupInfo [in] Information create my the Protare constructor to help in parsing. * @param a_parent [in] The parent GIDI::Suite. ***********************************************************************************************************/ Regions1d::Regions1d( Construction::Settings const &a_construction, HAPI::Node const &a_node, SetupInfo &a_setupInfo, Suite *a_parent ) : Function1dForm( a_construction, a_node, a_setupInfo, FormType::regions1d, a_parent ) { if( a_setupInfo.m_formatVersion.format( ) != GNDS_formatVersion_1_10Chars ) { data1dListParse( a_construction, a_node.child( GIDI_function1dsChars ), a_setupInfo, m_function1ds ); checkSequentialDomainLimits1d( m_function1ds, m_Xs ); return; // Need to add uncertainty parsing. } for( HAPI::Node child = a_node.first_child( ); !child.empty( ); child.to_next_sibling( ) ) { std::string name( child.name( ) ); if( name == GIDI_axesChars ) continue; if( name == GIDI_uncertaintyChars ) continue; Function1dForm *_form = data1dParse( a_construction, child, a_setupInfo, nullptr ); if( _form == nullptr ) throw Exception( "Regions1d::Regions1d: data1dParse returned nullptr." ); append( _form ); } } /* *********************************************************************************************************//** ***********************************************************************************************************/ Regions1d::~Regions1d( ) { for( std::vector::iterator iter = m_function1ds.begin( ); iter < m_function1ds.end( ); ++iter ) delete *iter; } /* *********************************************************************************************************//** * Returns the domain minimum for the instance. * * @return The domain minimum for the instance. ***********************************************************************************************************/ double Regions1d::domainMin( ) const { if( m_Xs.size( ) == 0 ) throw Exception( "Regions1d::domainMin: Regions1d has no regions" ); return( m_Xs[0] ); } /* *********************************************************************************************************//** * Returns the domain maximum for the instance. * * @return The domain maximum for the instance. ***********************************************************************************************************/ double Regions1d::domainMax( ) const { if( m_Xs.size( ) == 0 ) throw Exception( "Regions1d::domainMax: Regions1d has not regions" ); return( m_Xs[m_Xs.size( )-1] ); } /* *********************************************************************************************************//** * Appends the 1d function *a_function* to the region. * * @param a_function [in] The 1d function (i.e., 1d region) to append to the Regions1d. ***********************************************************************************************************/ void Regions1d::append( Function1dForm *a_function ) { if( dimension( ) != a_function->dimension( ) ) throw Exception( "Regions1d::append: dimensions differ." ); double _domainMin = a_function->domainMin( ), _domainMax = a_function->domainMax( ); if( m_Xs.size( ) == 0 ) { m_Xs.push_back( _domainMin ); } else { if( m_Xs.back( ) != _domainMin ) throw Exception( "Regions1d::append: regions do not abut." ); } m_Xs.push_back( _domainMax ); m_function1ds.push_back( a_function ); } /* *********************************************************************************************************//** * The value of *y(x1)* at the point *a_x1*. * * @param a_x1 [in] Domain value to evaluate this at. * @return The value of this at the point *a_x1*. ***********************************************************************************************************/ double Regions1d::evaluate( double a_x1 ) const { if( m_Xs.size( ) == 0 ) throw Exception( "Regions1d::evaluate: Regions1d has not regions" ); long iX1 = binarySearchVector( a_x1, m_Xs ); if( iX1 < 0 ) { if( iX1 == -1 ) { /* x1 > last value of Xs. */ return( m_function1ds.back( )->evaluate( a_x1 ) ); } iX1 = 0; /* x1 < last value of Xs. */ } return( m_function1ds[static_cast(iX1)]->evaluate( a_x1 ) ); } /* *********************************************************************************************************//** * Evaluates *this* at the X-values in *a_Xs*[*a_offset*:] and adds the results to *a_results*[*a_offset*:]. * *a_Xs* and *a_results* must be the same size otherwise a throw is executed. * * @param a_offset [in] The offset in *a_Xs* to start. * @param a_Xs [in] The list of domain values to evaluate *this* at. * @param a_results [in] The list whose values are added to by the Y-values of *this*. * @param a_scaleFactor [in] A factor applied to each evaluation before it is added to *a_results*. ***********************************************************************************************************/ void Regions1d::mapToXsAndAdd( std::size_t a_offset, std::vector const &a_Xs, std::vector &a_results, double a_scaleFactor ) const { for( auto iter = m_function1ds.begin( ); iter < m_function1ds.end( ); ++iter ) { (*iter)->mapToXsAndAdd( a_offset, a_Xs, a_results, a_scaleFactor ); } } /* *********************************************************************************************************//** * This methods returns an XYs1d representation of *this*. The calling function owns the created instance and is responible * for freeing it. * * @param a_asLinlin [in] If **true**, the inpolatation of the returned XYs1d instance will always be lin-lin. Otherwise, * the interpolation depends on the child 1d functions. * @param a_accuracy [in] The accuracy use to convert the data to lin=lin interpolation if needed. * @param a_lowerEps [in] The relative domain ammount to put a point below a boundary between two regions. * @param a_upperEps [in] The relative domain ammount to put a point above a boundary between two regions. * * @return A pointer to an XYs1d instance that must be freed by the calling function. ***********************************************************************************************************/ XYs1d *Regions1d::asXYs1d( bool a_asLinlin, double a_accuracy, double a_lowerEps, double a_upperEps ) const { XYs1d *xys1d1 = nullptr, *xys1d2 = nullptr; if( a_lowerEps < 1e-14 ) a_lowerEps = 0.0; // 1e-14 should be a global parameter. if( a_upperEps < 1e-14 ) a_upperEps = 0.0; if( !a_asLinlin ) { bool firstRegion = true; ptwXY_interpolation interpolation2 = ptwXY_interpolationLinLin; for( auto regionIter = m_function1ds.begin( ); regionIter != m_function1ds.end( ); ++regionIter ) { if( (*regionIter)->moniker( ) == GIDI_XYs1dChars ) { xys1d2 = static_cast( *regionIter ); if( firstRegion ) interpolation2 = ptwXY_getInterpolation( xys1d2->ptwXY( ) ); if( interpolation2 != ptwXY_getInterpolation( xys1d2->ptwXY( ) ) ) { a_asLinlin = true; break; } } else { a_asLinlin = true; break; } } } std::vector xs; std::vector ys; for( auto regionIter = m_function1ds.begin( ); regionIter != m_function1ds.end( ); ++regionIter ) { xys1d2 = (*regionIter)->asXYs1d( a_asLinlin, a_accuracy, a_lowerEps, a_upperEps ); if( xys1d2 == nullptr ) { delete xys1d1; return( nullptr ); } std::vector xs2( xys1d2->xs( ) ); if( xs2.size( ) < 2 ) continue; std::size_t xySize = xs.size( ); std::vector ys2( xys1d2->ys( ) ); if( xySize > 0 ) { double y1u = ys.back( ); double y2l = ys2[0]; if( y1u == y2l ) { // Simple, just remove last point in xs and ys. xs.resize( xySize - 1 ); ys.resize( xySize - 1 ); } else { bool addLower = false, addUpper = false; double x1l = xs[xySize-2]; double x1u = xs.back( ); double x2l = xs2[0]; double x2u = xs2[1]; double x1up = 0.0, x2lp= 0.0; if( a_lowerEps != 0.0 ) { double x1lp = getAdjustedX( x1l, 0.8 * a_lowerEps ); x1up = getAdjustedX( x1u, -a_lowerEps ); // Point to add in xs below xs.back() if room. addLower = x1lp < x1up; // Only add if relative spacing between existing point is greater than 1.8 * a_lowerEps. } if( a_upperEps > 0 ) { x2lp = getAdjustedX( x2l, a_upperEps ); // Point to add in xs2 above xs2[0] if room. double x2up = getAdjustedX( x2u, 0.8 * -a_upperEps ); addUpper = x2lp < x2up; // Only add if relative spacing between existing point is greater than 1.8 * a_upperEps. } if( addLower ) { if( addUpper ) { xs.back( ) = x1up; ys.back( ) = xys1d1->evaluate( x1up ); xs.push_back( x1u ); ys.push_back( 0.5 * ( y1u + y2l ) ); xs2[0] = x2lp; ys2[0] = xys1d2->evaluate( x2lp ); } else { xs.back( ) = x1up; ys.back( ) = xys1d1->evaluate( x1up ); } } else if( addUpper ) { xs2[0] = x2lp; ys2[0] = xys1d2->evaluate( x2lp ); } else { ys2[0] = 0.5 * ( y1u + y2l ); xs.resize( xySize - 1 ); ys.resize( xySize - 1 ); } } } xs.insert( xs.end( ), xs2.begin( ), xs2.end( ) ); ys.insert( ys.end( ), ys2.begin( ), ys2.end( ) ); xys1d1 = xys1d2; } delete xys1d1; return( new XYs1d( axes( ), ptwXY_interpolationLinLin, xs, ys ) ); } /* *********************************************************************************************************//** * Fills the argument *a_writeInfo* with the XML lines that represent *this*. Recursively enters each sub-node. * * @param a_writeInfo [in/out] Instance containing incremental indentation and other information and stores the appended lines. * @param a_indent [in] The amount to indent *this* node. * @param a_embedded [in] If *true*, *this* function is embedded in a higher dimensional function. * @param a_inRegions [in] If *true*, *this* is in a Regions1d container. ***********************************************************************************************************/ void Regions1d::toXMLList_func( GUPI::WriteInfo &a_writeInfo, std::string const &a_indent, bool a_embedded, bool a_inRegions ) const { std::string indent2 = a_writeInfo.incrementalIndent( a_indent ); std::string indent3 = a_writeInfo.incrementalIndent( indent2 ); std::string attributes; if( a_embedded ) { attributes += a_writeInfo.addAttribute( GIDI_outerDomainValueChars, LUPI::Misc::doubleToShortestString( outerDomainValue( ) ) ); } else { if( a_inRegions ) { attributes = a_writeInfo.addAttribute( GIDI_indexChars, intToString( index( ) ) ); } else { if( label( ) != "" ) attributes = a_writeInfo.addAttribute( GIDI_labelChars, label( ) ); } } a_writeInfo.addNodeStarter( a_indent, moniker( ), attributes ); axes( ).toXMLList( a_writeInfo, indent2 ); a_writeInfo.push_back( indent2 + "" ); for( std::vector::const_iterator iter = m_function1ds.begin( ); iter != m_function1ds.end( ); ++iter ) (*iter)->toXMLList_func( a_writeInfo, indent3, false, true ); a_writeInfo.push_back( "" ); a_writeInfo.addNodeEnder( moniker( ) ); } /* *********************************************************************************************************//** * This method calls the write method for each region of *this*. * * @param a_file [in] The C FILE instance to write the data to. * @param a_format [in] The format string passed to each region's write method. ***********************************************************************************************************/ void Regions1d::write( FILE *a_file, std::string const &a_format ) const { for( auto regionIter = m_function1ds.begin( ); regionIter != m_function1ds.end( ); ++regionIter ) { (*regionIter)->write( a_file, a_format ); } } } // End namespace Functions. } // End namespace GIDI.