///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2025- Equinor ASA // // ResInsight 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. // // ResInsight 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 at // for more details. // ///////////////////////////////////////////////////////////////////////////////// //================================================================================================== // // RifVfpInjTable - VFP Injection Table Reader Implementation // // Based on: ThirdParty/custom-opm-common/opm-common/opm/input/eclipse/Schedule/VFPInjTable.cpp // // This implementation preserves the original Eclipse deck keyword values without unit conversions, // unlike the OPM version which automatically converts all values to SI units. // //================================================================================================== #include "RifVfpInjTable.h" #include "cafAssert.h" #include #include #include #include #include #include #include namespace { template inline const Opm::DeckItem& getNonEmptyItem( const Opm::DeckRecord& record ) { const auto& retval = record.getItem(); if ( !retval.hasValue( 0 ) ) { throw std::invalid_argument( "Missing data" ); } return retval; } } // namespace //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifVfpInjTable::FLO_TYPE getFloType( const std::string& floString ) { if ( floString == "OIL" ) return RifVfpInjTable::FLO_TYPE::FLO_OIL; if ( floString == "WAT" ) return RifVfpInjTable::FLO_TYPE::FLO_WAT; if ( floString == "GAS" ) return RifVfpInjTable::FLO_TYPE::FLO_GAS; throw std::invalid_argument( "Invalid RATE_TYPE string" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifVfpInjTable::checkAxis( const std::vector& axis ) { if ( axis.size() == 0 ) throw std::invalid_argument( "Empty axis" ); if ( !std::is_sorted( axis.begin(), axis.end() ) ) throw std::invalid_argument( "Axis is not sorted" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RifVfpInjTable::tableNum() const { return m_tableNum; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RifVfpInjTable::datumDepth() const { return m_datumDepth; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifVfpInjTable::FLO_TYPE RifVfpInjTable::floType() const { return m_floType; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::string& RifVfpInjTable::units() const { return m_units; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::string& RifVfpInjTable::bodyDef() const { return m_bodyDef; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& RifVfpInjTable::floAxis() const { return m_floData; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& RifVfpInjTable::thpAxis() const { return m_thpData; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& RifVfpInjTable::table() const { return m_data; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifVfpInjTable::RifVfpInjTable() { m_tableNum = -1; m_datumDepth = 0.0; m_floType = FLO_TYPE::FLO_OIL; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifVfpInjTable::RifVfpInjTable( const Opm::DeckKeyword& table ) { using Opm::ParserKeywords::VFPINJ; if ( table.size() < 4 ) { throw std::invalid_argument( "VFPINJ table does not appear to have enough records to be valid" ); } const auto& header = table.getRecord( 0 ); m_tableNum = getNonEmptyItem( header ).get( 0 ); m_datumDepth = getNonEmptyItem( header ).get( 0 ); m_floType = ::getFloType( getNonEmptyItem( header ).get( 0 ) ); std::string quantity_string = getNonEmptyItem( header ).get( 0 ); if ( quantity_string != "THP" ) { throw std::invalid_argument( "PRESSURE_DEF is required to be THP" ); } if ( header.getItem().hasValue( 0 ) ) { m_units = header.getItem().get( 0 ); } m_bodyDef = getNonEmptyItem( header ).get( 0 ); if ( m_bodyDef != "BHP" ) { throw std::invalid_argument( "Invalid BODY_DEF string" ); } m_floData = getNonEmptyItem( table.getRecord( 1 ) ).getData(); m_thpData = getNonEmptyItem( table.getRecord( 2 ) ).getData(); size_t nt = m_thpData.size(); size_t nf = m_floData.size(); m_data.resize( nt * nf ); std::fill_n( m_data.data(), m_data.size(), std::nan( "0" ) ); if ( table.size() != nt + 3 ) { throw std::invalid_argument( "VFPINJ table does not contain enough records." ); } for ( size_t i = 3; i < table.size(); ++i ) { const auto& record = table.getRecord( i ); int t = getNonEmptyItem( record ).get( 0 ) - 1; const std::vector& bhp_values = getNonEmptyItem( record ).getData(); if ( bhp_values.size() != nf ) { throw std::invalid_argument( "VFPINJ table does not contain enough FLO values." ); } for ( size_t f = 0; f < bhp_values.size(); ++f ) { ( *this )( t, f ) = bhp_values[f]; } } check(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::array RifVfpInjTable::shape() const { return { m_thpData.size(), m_floData.size() }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RifVfpInjTable::operator()( size_t thpIdx, size_t floIdx ) const { size_t nt = m_thpData.size(); size_t nf = m_floData.size(); CAF_ASSERT( thpIdx < nt ); CAF_ASSERT( floIdx < nf ); size_t index = thpIdx + nt * floIdx; return m_data[index]; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double& RifVfpInjTable::operator()( size_t thpIdx, size_t floIdx ) { size_t nt = m_thpData.size(); size_t nf = m_floData.size(); CAF_ASSERT( thpIdx < nt ); CAF_ASSERT( floIdx < nf ); size_t index = thpIdx + nt * floIdx; return m_data[index]; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifVfpInjTable::check() { if ( m_tableNum <= 0 ) throw std::invalid_argument( "Invalid table number" ); checkAxis( m_floData ); checkAxis( m_thpData ); if ( m_data.size() != m_thpData.size() * m_floData.size() ) throw std::invalid_argument( "Wrong data size" ); for ( size_t t = 0; t < m_thpData.size(); ++t ) { for ( size_t f = 0; f < m_floData.size(); ++f ) { if ( std::isnan( ( *this )( t, f ) ) ) { throw std::invalid_argument( "VFPINJ table element not initialized" ); } } } }