Files
geant4/source/processes/hadronic/models/lend/src/GIDI_regions1d.cc
T
2025-12-05 08:54:02 +01:00

331 lines
15 KiB
C++

/*
# <<BEGIN-copyright>>
# 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
# <<END-copyright>>
*/
#include "GIDI.hpp"
#include <HAPI.hpp>
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<Function1dForm *>::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<std::size_t>(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<double> const &a_Xs, std::vector<double> &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<XYs1d *>( *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<double> xs;
std::vector<double> 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<double> xs2( xys1d2->xs( ) );
if( xs2.size( ) < 2 ) continue;
std::size_t xySize = xs.size( );
std::vector<double> 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 + "<function1ds>" );
for( std::vector<Function1dForm *>::const_iterator iter = m_function1ds.begin( ); iter != m_function1ds.end( ); ++iter ) (*iter)->toXMLList_func( a_writeInfo, indent3, false, true );
a_writeInfo.push_back( "</function1ds>" );
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.