Beispiel #1
0
 void ParserKeyword::applyUnitsToDeck( Deck& deck, DeckKeyword& deckKeyword) const {
     for (size_t index = 0; index < deckKeyword.size(); index++) {
         const auto& parserRecord = this->getRecord( index );
         auto& deckRecord = deckKeyword.getRecord( index );
         parserRecord.applyUnitsToDeck( deck, deckRecord );
     }
 }
Beispiel #2
0
    void TimeMap::addFromDATESKeyword( const DeckKeyword& DATESKeyword ) {
        if (DATESKeyword.name() != "DATES")
            throw std::invalid_argument("Method requires DATES keyword input.");

        for (size_t recordIndex = 0; recordIndex < DATESKeyword.size(); recordIndex++) {
            const auto& record = DATESKeyword.getRecord( recordIndex );
            boost::posix_time::ptime nextTime = TimeMap::timeFromEclipse( record );
            addTime( nextTime );
        }
    }
Beispiel #3
0
    bool DeckKeyword::equal_data(const DeckKeyword& other, bool cmp_default, bool cmp_numeric) const {
        if (this->size() != other.size())
            return false;

        for (size_t index = 0; index < this->size(); index++) {
            const auto& this_record = this->getRecord( index );
            const auto& other_record = other.getRecord( index );
            if (!this_record.equal( other_record , cmp_default, cmp_numeric))
                return false;
        }
        return true;
    }
Beispiel #4
0
 std::vector<std::pair<size_t , size_t> > PvtxTable::recordRanges( const DeckKeyword& keyword ) {
     std::vector<std::pair<size_t,size_t> > ranges;
     size_t startRecord = 0;
     size_t recordIndex = 0;
     while (recordIndex < keyword.size()) {
         const auto& item = keyword.getRecord(recordIndex).getItem(0);
         if (item.size( ) == 0) {
             ranges.push_back( std::make_pair( startRecord , recordIndex ) );
             startRecord = recordIndex + 1;
         }
         recordIndex++;
     }
     ranges.push_back( std::make_pair( startRecord , recordIndex ) );
     return ranges;
 }
Beispiel #5
0
    void PvtxTable::init( const DeckKeyword& keyword, size_t tableIdx) {
        auto ranges = recordRanges( keyword );
        if (tableIdx >= ranges.size())
            throw std::invalid_argument("Asked for table: " + std::to_string( tableIdx ) + " in keyword + " + keyword.name() + " which only has " + std::to_string( ranges.size() ) + " tables");

        {
            auto range = ranges[ tableIdx ];
            for (size_t  rowIdx = range.first; rowIdx < range.second; rowIdx++) {
                const auto& deckRecord = keyword.getRecord(rowIdx);
                m_outerColumn.addValue( deckRecord.getItem( 0 ).getSIDouble( 0 ));

                const auto& dataItem = deckRecord.getItem(1);
                std::shared_ptr<SimpleTable> underSaturatedTable = std::make_shared<SimpleTable>(m_underSaturatedSchema , dataItem);
                m_underSaturatedTables.push_back( underSaturatedTable );
            }


            m_saturatedTable = std::make_shared<SimpleTable>(m_saturatedSchema);
            for (size_t sat_index = 0; sat_index < size(); sat_index++) {
                const auto& underSaturatedTable = getUnderSaturatedTable( sat_index );
                std::vector<double> row(4);
                row[0] = m_outerColumn[sat_index];
                for (size_t col_index = 0; col_index < m_underSaturatedSchema->size(); col_index++)
                    row[col_index + 1] = underSaturatedTable.get( col_index , 0 );

                m_saturatedTable->addRow( row );
            }
        }
    }
Beispiel #6
0
 void checkOptions(const DeckKeyword& keyword, std::multimap<std::string , PartiallySupported<T> >& map, const ParseContext& parseContext, ErrorGuard& errorGuard)
 {
     // check for partially supported keywords.
     typename std::multimap<std::string, PartiallySupported<T> >::iterator it, itlow, itup;
     itlow = map.lower_bound(keyword.name());
     itup  = map.upper_bound(keyword.name());
     for (it = itlow; it != itup; ++it) {
         const auto& record = keyword.getRecord(0);
         if (record.getItem(it->second.item).template get<T>(0) != it->second.item_value) {
             std::string msg = "For keyword '" + it->first + "' only value " + boost::lexical_cast<std::string>(it->second.item_value)
                 + " in item " + it->second.item + " is supported by flow.\n"
                 + "In file " + keyword.getFileName() + ", line " + std::to_string(keyword.getLineNumber()) + "\n";
             parseContext.handleError(ParseContext::SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED, msg, errorGuard);
         }
     }
 }
Beispiel #7
0
    static bool isSectionDelimiter( const DeckKeyword& keyword ) {
        const auto& name = keyword.name();
        for( const auto& x : { "RUNSPEC", "GRID", "EDIT", "PROPS",
                               "REGIONS", "SOLUTION", "SUMMARY", "SCHEDULE" } )
            if( name == x ) return true;

        return false;
    }
Beispiel #8
0
    void TimeMap::addFromTSTEPKeyword( const DeckKeyword& TSTEPKeyword ) {
        if (TSTEPKeyword.name() != "TSTEP")
            throw std::invalid_argument("Method requires TSTEP keyword input.");
        {
            const auto& item = TSTEPKeyword.getRecord( 0 ).getItem( 0 );

            for (size_t itemIndex = 0; itemIndex < item.size(); itemIndex++) {
                double days = item.get< double >( itemIndex );
                long int wholeSeconds = static_cast<long int>(days * 24*60*60);
                long int milliSeconds = static_cast<long int>((days * 24*60*60 - wholeSeconds)*1000);
                boost::posix_time::time_duration step =
                    boost::posix_time::seconds(wholeSeconds) +
                    boost::posix_time::milliseconds(milliSeconds);
                addTStep( step );
            }
        }
    }
    void MULTREGTScanner::assertKeywordSupported( const DeckKeyword& deckKeyword, const std::string& defaultRegion) {
        for (auto iter = deckKeyword.begin(); iter != deckKeyword.end(); ++iter) {
            MULTREGTRecord record( *iter , defaultRegion);

            if (record.m_nncBehaviour == MULTREGT::NOAQUNNC)
                throw std::invalid_argument("Sorry - currently we do not support \'NOAQUNNC\' for MULTREGT.");

            if (!record.m_srcRegion.hasValue())
                throw std::invalid_argument("Sorry - currently it is not supported with a defaulted source region value.");

            if (!record.m_targetRegion.hasValue())
                throw std::invalid_argument("Sorry - currently it is not supported with a defaulted target region value.");

            if (record.m_srcRegion.getValue() == record.m_targetRegion.getValue())
                throw std::invalid_argument("Sorry - multregt applied internally to a region is not yet supported");

        }
    }
Beispiel #10
0
    bool DeckView::hasKeyword( const DeckKeyword& keyword ) const {
        auto key = this->keywordMap.find( keyword.name() );

        if( key == this->keywordMap.end() ) return false;

        for( auto index : key->second )
            if( &this->getKeyword( index ) == &keyword ) return true;

        return false;
    }
Beispiel #11
0
    void MULTREGTScanner::addKeyword( const DeckKeyword& deckKeyword , const std::string& defaultRegion) {
        assertKeywordSupported( deckKeyword , defaultRegion );

        for (auto iter = deckKeyword.begin(); iter != deckKeyword.end(); ++iter) {
            MULTREGTRecord record( *iter , defaultRegion );
            /*
              The default value for the region item is to use the
              region item on the previous record, or alternatively
              'MULTNUM' for the first record.
            */
            if (!record.m_region.hasValue()) {
                if (m_records.size() > 0) {
                    auto previousRecord = m_records.back();
                    record.m_region.setValue( previousRecord.m_region.getValue() );
                } else
                    record.m_region.setValue( "MULTNUM" );
            }
            m_records.push_back( record );
        }

    }
Beispiel #12
0
    void IOConfig::handleRPTSOL( const DeckKeyword& keyword) {
        const auto& record = keyword.getRecord(0);

        size_t restart = 0;
        size_t found_mnemonic_RESTART = 0;
        bool handle_RPTSOL_RESTART = false;

        const auto& item = record.getItem(0);


        for (size_t index = 0; index < item.size(); ++index) {
            const std::string& mnemonic = item.get< std::string >(index);

            found_mnemonic_RESTART = mnemonic.find("RESTART=");
            if (found_mnemonic_RESTART != std::string::npos) {
                std::string restart_no = mnemonic.substr(found_mnemonic_RESTART+8, mnemonic.size());
                restart = boost::lexical_cast<size_t>(restart_no);
                handle_RPTSOL_RESTART = true;
            }
        }


        /* If no RESTART mnemonic is found, either it is not present or we might
           have an old data set containing integer controls instead of mnemonics.
           Restart integer switch is integer control nr 7 */

        if (found_mnemonic_RESTART == std::string::npos) {
            if (item.size() >= 7)  {
                const std::string& integer_control = item.get< std::string >(6);
                try {
                    restart = boost::lexical_cast<size_t>(integer_control);
                    handle_RPTSOL_RESTART = true;
                } catch (boost::bad_lexical_cast &) {
                    //do nothing
                }
            }
        }

        if (handle_RPTSOL_RESTART) {
            if (restart > 1) {
                setWriteInitialRestartFile(true);
            } else {
                setWriteInitialRestartFile(false);
            }
        }
    }
Beispiel #13
0
    std::map<std::string , std::vector< CompletionPtr> > Completion::completionsFromCOMPDATKeyword( const DeckKeyword& compdatKeyword ) {
        std::map<std::string , std::vector< CompletionPtr> > completionMapList;
        for (size_t recordIndex = 0; recordIndex < compdatKeyword.size(); recordIndex++) {
            std::pair<std::string , std::vector< CompletionPtr> > wellCompletionsPair = completionsFromCOMPDATRecord( compdatKeyword.getRecord( recordIndex ));
            std::string well = wellCompletionsPair.first;
            std::vector<CompletionPtr>& newCompletions = wellCompletionsPair.second;

            if (completionMapList.find(well) == completionMapList.end())
                 completionMapList[well] = std::vector<CompletionPtr>();

            {
                std::vector<CompletionPtr>& currentCompletions = completionMapList.find(well)->second;

                for (size_t ic = 0; ic < newCompletions.size(); ic++)
                    currentCompletions.push_back( newCompletions[ic] );
            }
        }
        return completionMapList;
    }
Beispiel #14
0
inline std::map< std::string, int > RPT( const DeckKeyword& keyword,
                                         F is_mnemonic,
                                         G integer_mnemonic ) {

    const auto& items = keyword.getStringData();
    const auto ints = std::any_of( items.begin(), items.end(), is_int );
    const auto strs = !std::all_of( items.begin(), items.end(), is_int );

    /* if any of the values are pure integers we assume this is meant to be
     * the slash-terminated list of integers way of configuring. If
     * integers and non-integers are mixed, this is an error.
     */
    if( ints && strs ) throw std::runtime_error(
            "RPTRST does not support mixed mnemonics and integer list."
            );

    auto stoi = []( const std::string& str ) { return std::stoi( str ); };
    if( ints )
        return integer_mnemonic( fun::map( stoi, items ) );

    std::map< std::string, int > mnemonics;

    for( const auto& mnemonic : items ) {
        const auto pos = mnemonic.find( '=' );

        std::string base = mnemonic.substr( 0, pos );
        if( !is_mnemonic( base ) ) continue;

        const int val = pos != std::string::npos
                      ? std::stoi( mnemonic.substr( pos + 1 ) )
                      : 1;

        mnemonics.emplace( base, val );
    }

    return std::move( mnemonics );
}
Beispiel #15
0
 Equil::Equil( const DeckKeyword& keyword ) :
     records( keyword.begin(), keyword.end() )
 {}
Beispiel #16
0
VFPProdTable::VFPProdTable( const DeckKeyword& table, const UnitSystem& deck_unit_system) {
    using ParserKeywords::VFPPROD;

    //Check that the table has enough records
    if (table.size() < 7) {
        throw std::invalid_argument("VFPPROD table does not appear to have enough records to be valid");
    }

    //Get record 1, the metadata for the table
    const auto& header = table.getRecord(0);

    //Get the different header items
    m_table_num   = header.getItem<VFPPROD::TABLE>().get< int >(0);
    m_datum_depth = header.getItem<VFPPROD::DATUM_DEPTH>().getSIDouble(0);

    m_flo_type = Opm::getFloType(header.getItem<VFPPROD::RATE_TYPE>());
    m_wfr_type = Opm::getWFRType(header.getItem<VFPPROD::WFR>());
    m_gfr_type = Opm::getGFRType(header.getItem<VFPPROD::GFR>());

    //Not used, but check that PRESSURE_DEF is indeed THP
    std::string quantity_string = header.getItem<VFPPROD::PRESSURE_DEF>().get< std::string >(0);
    if (quantity_string != "THP") {
        throw std::invalid_argument("PRESSURE_DEF is required to be THP");
    }

    m_alq_type = Opm::getALQType(header.getItem<VFPPROD::ALQ_DEF>());

    //Check units used for this table
    std::string units_string = "";
    if (header.getItem<VFPPROD::UNITS>().hasValue(0)) {
        units_string = header.getItem<VFPPROD::UNITS>().get< std::string >(0);
    }
    else {
        //If units does not exist in record, the default value is the
        //unit system of the deck itself: do nothing...
    }

    if (units_string != "") {
        UnitSystem::UnitType table_unit_type;

        //FIXME: Only metric and field supported at the moment.
        //Need to change all of the convertToSI functions to support LAB/PVT-M

        if (units_string == "METRIC") {
            table_unit_type = UnitSystem::UnitType::UNIT_TYPE_METRIC;
        }
        else if (units_string == "FIELD") {
            table_unit_type = UnitSystem::UnitType::UNIT_TYPE_FIELD;
        }
        else if (units_string == "LAB") {
            table_unit_type = UnitSystem::UnitType::UNIT_TYPE_LAB;
        }
        else if (units_string == "PVT-M") {
            throw std::invalid_argument("Unsupported UNITS string: 'PVT-M'");
        }
        else {
            throw std::invalid_argument("Invalid UNITS string");
        }

        //Sanity check
        if(table_unit_type != deck_unit_system.getType()) {
            throw std::invalid_argument("Deck units are not equal VFPPROD table units.");
        }
    }

    //Quantity in the body of the table
    std::string body_string = header.getItem<VFPPROD::BODY_DEF>().get< std::string >(0);
    if (body_string == "TEMP") {
        throw std::invalid_argument("Invalid BODY_DEF string: TEMP not supported");
    }
    else if (body_string == "BHP") {

    }
    else {
        throw std::invalid_argument("Invalid BODY_DEF string");
    }


    //Get actual rate / flow values
    m_flo_data = table.getRecord(1).getItem<VFPPROD::FLOW_VALUES>().getData< double >();
    convertFloToSI(m_flo_type, m_flo_data, deck_unit_system);

    //Get actual tubing head pressure values
    m_thp_data = table.getRecord(2).getItem<VFPPROD::THP_VALUES>().getData< double >();
    convertTHPToSI(m_thp_data, deck_unit_system);

    //Get actual water fraction values
    m_wfr_data = table.getRecord(3).getItem<VFPPROD::WFR_VALUES>().getData< double >();
    convertWFRToSI(m_wfr_type, m_wfr_data, deck_unit_system);

    //Get actual gas fraction values
    m_gfr_data = table.getRecord(4).getItem<VFPPROD::GFR_VALUES>().getData< double >();
    convertGFRToSI(m_gfr_type, m_gfr_data, deck_unit_system);

    //Get actual gas fraction values
    m_alq_data = table.getRecord(5).getItem<VFPPROD::ALQ_VALUES>().getData< double >();

    //Finally, read the actual table itself.
    size_t nt = m_thp_data.size();
    size_t nw = m_wfr_data.size();
    size_t ng = m_gfr_data.size();
    size_t na = m_alq_data.size();
    size_t nf = m_flo_data.size();
    extents shape;
    shape[0] = nt;
    shape[1] = nw;
    shape[2] = ng;
    shape[3] = na;
    shape[4] = nf;
    m_data.resize(shape);
    std::fill_n(m_data.data(), m_data.num_elements(), std::nan("0"));

    //Check that size of table matches size of axis:
    if (table.size() != nt*nw*ng*na + 6) {
        throw std::invalid_argument("VFPPROD table does not contain enough records.");
    }

    //FIXME: Unit for TEMP=Tubing head temperature is not Pressure, see BODY_DEF
    const double table_scaling_factor = deck_unit_system.parse("Pressure").getSIScaling();
    for (size_t i=6; i<table.size(); ++i) {
        const auto& record = table.getRecord(i);
        //Get indices (subtract 1 to get 0-based index)
        int t = record.getItem<VFPPROD::THP_INDEX>().get< int >(0) - 1;
        int w = record.getItem<VFPPROD::WFR_INDEX>().get< int >(0) - 1;
        int g = record.getItem<VFPPROD::GFR_INDEX>().get< int >(0) - 1;
        int a = record.getItem<VFPPROD::ALQ_INDEX>().get< int >(0) - 1;

        //Rest of values (bottom hole pressure or tubing head temperature) have index of flo value
        const std::vector<double>& bhp_tht = record.getItem<VFPPROD::VALUES>().getData< double >();

        if (bhp_tht.size() != nf) {
            throw std::invalid_argument("VFPPROD table does not contain enough FLO values.");
        }

        for (size_t f=0; f<bhp_tht.size(); ++f) {
            //Check that all data is within reasonable ranges, defined to be up-to 1.0e10...
            if (bhp_tht[f] > 1.0e10) {
                //TODO: Replace with proper log message
                std::cerr << "VFPPROD element ["
                        << t << "," << w << "," << g << "," << a << "," << f
                        << "]=" << bhp_tht[f] << " too large" << std::endl;
            }
            m_data[t][w][g][a][f] = table_scaling_factor*bhp_tht[f];
        }
    }

    check(table, table_scaling_factor);
}
Beispiel #17
0
    std::vector< Compsegs > Compsegs::compsegsFromCOMPSEGSKeyword( const DeckKeyword& compsegsKeyword ) {

        // only handle the second record here
        // The first record here only contains the well name
        std::vector< Compsegs > compsegs;

        for (size_t recordIndex = 1; recordIndex < compsegsKeyword.size(); ++recordIndex) {
            const auto& record = compsegsKeyword.getRecord(recordIndex);
            // following the coordinate rule for completions
            const int I = record.getItem<ParserKeywords::COMPSEGS::I>().get< int >(0) - 1;
            const int J = record.getItem<ParserKeywords::COMPSEGS::J>().get< int >(0) - 1;
            const int K = record.getItem<ParserKeywords::COMPSEGS::K>().get< int >(0) - 1;
            const int branch = record.getItem<ParserKeywords::COMPSEGS::BRANCH>().get< int >(0);

            double distance_start;
            double distance_end;
            if (record.getItem<ParserKeywords::COMPSEGS::DISTANCE_START>().hasValue(0)) {
                distance_start = record.getItem<ParserKeywords::COMPSEGS::DISTANCE_START>().getSIDouble(0);
            } else if (recordIndex == 1) {
                distance_start = 0.;
            } else {
                // TODO: the end of the previous connection or range
                // 'previous' should be in term of the input order
                // since basically no specific order for the completions
                throw std::runtime_error("this way to obtain DISTANCE_START not implemented yet!");
            }
            if (record.getItem<ParserKeywords::COMPSEGS::DISTANCE_END>().hasValue(0)) {
                distance_end = record.getItem<ParserKeywords::COMPSEGS::DISTANCE_END>().getSIDouble(0);
            } else {
                // TODO: the distance_start plus the thickness of the grid block
                throw std::runtime_error("this way to obtain DISTANCE_END not implemented yet!");
            }

            if( !record.getItem< ParserKeywords::COMPSEGS::DIRECTION >().hasValue( 0 ) &&
                !record.getItem< ParserKeywords::COMPSEGS::DISTANCE_END >().hasValue( 0 ) ) {
                throw std::runtime_error("the direction has to be specified when DISTANCE_END in the record is not specified");
            }

            if( record.getItem< ParserKeywords::COMPSEGS::END_IJK >().hasValue( 0 ) &&
               !record.getItem< ParserKeywords::COMPSEGS::DIRECTION >().hasValue( 0 ) ) {
                throw std::runtime_error("the direction has to be specified when END_IJK in the record is specified");
            }

            /*
             * Defaulted well completion. Must be non-defaulted if DISTANCE_END
             * is set or a range is specified. If not this is effectively ignored.
             */
            WellCompletion::DirectionEnum direction = WellCompletion::X;
            if( record.getItem< ParserKeywords::COMPSEGS::DIRECTION >().hasValue( 0 ) ) {
                direction = WellCompletion::DirectionEnumFromString(record.getItem<ParserKeywords::COMPSEGS::DIRECTION>().get< std::string >(0));
            }

            double center_depth;
            if (!record.getItem<ParserKeywords::COMPSEGS::CENTER_DEPTH>().defaultApplied(0)) {
                center_depth = record.getItem<ParserKeywords::COMPSEGS::CENTER_DEPTH>().getSIDouble(0);
            } else {
                // 0.0 is also the defaulted value
                // which is used to indicate to obtain the final value through related segment
                center_depth = 0.;
            }

            if (center_depth < 0.) {
                //TODO: get the depth from COMPDAT data.
                throw std::runtime_error("this way to obtain CENTER_DISTANCE not implemented yet either!");
            }

            int segment_number;
            if (record.getItem<ParserKeywords::COMPSEGS::SEGMENT_NUMBER>().hasValue(0)) {
                segment_number = record.getItem<ParserKeywords::COMPSEGS::SEGMENT_NUMBER>().get< int >(0);
            } else {
                segment_number = 0;
                // will decide the segment number based on the distance in a process later.
            }

            if (!record.getItem<ParserKeywords::COMPSEGS::END_IJK>().hasValue(0)) { // only one compsegs
                compsegs.emplace_back( I, J, K,
                                       branch,
                                       distance_start, distance_end,
                                       direction,
                                       center_depth,
                                       segment_number );
            } else { // a range is defined. genrate a range of Compsegs
                throw std::runtime_error("entering COMPSEGS entries with a range is not supported yet!");
            }
        }

        return compsegs;
    }
Beispiel #18
0
    bool DeckKeyword::equal(const DeckKeyword& other, bool cmp_default, bool cmp_numeric) const {
        if (this->name() != other.name())
            return false;

        return this->equal_data(other, cmp_default, cmp_numeric);
    }
Beispiel #19
0
void VFPProdTable::check(const DeckKeyword& keyword, const double table_scaling_factor) {
    //Table number
    assert(m_table_num > 0);

    //Misc types
    assert(m_flo_type >= FLO_OIL && m_flo_type < FLO_INVALID);
    assert(m_wfr_type >= WFR_WOR && m_wfr_type < WFR_INVALID);
    assert(m_gfr_type >= GFR_GOR && m_gfr_type < GFR_INVALID);
    assert(m_alq_type >= ALQ_GRAT && m_alq_type < ALQ_INVALID);

    //Data axis size
    assert(m_flo_data.size() > 0);
    assert(m_thp_data.size() > 0);
    assert(m_wfr_data.size() > 0);
    assert(m_gfr_data.size() > 0);
    assert(m_alq_data.size() > 0);

    //Data axis sorted?
    assert(std::is_sorted(m_flo_data.begin(), m_flo_data.end()));
    assert(std::is_sorted(m_thp_data.begin(), m_thp_data.end()));
    assert(std::is_sorted(m_wfr_data.begin(), m_wfr_data.end()));
    assert(std::is_sorted(m_gfr_data.begin(), m_gfr_data.end()));
    assert(std::is_sorted(m_alq_data.begin(), m_alq_data.end()));

    //Check data size matches axes
    assert(m_data.num_dimensions() == 5);
    assert(m_data.shape()[0] == m_thp_data.size());
    assert(m_data.shape()[1] == m_wfr_data.size());
    assert(m_data.shape()[2] == m_gfr_data.size());
    assert(m_data.shape()[3] == m_alq_data.size());
    assert(m_data.shape()[4] == m_flo_data.size());


    //Check that all elements have been set
    typedef array_type::size_type size_type;
    for (size_type t=0; t<m_data.shape()[0]; ++t) {
        for (size_type w=0; w<m_data.shape()[1]; ++w) {
            for (size_type g=0; g<m_data.shape()[2]; ++g) {
                for (size_type a=0; a<m_data.shape()[3]; ++a) {
                    for (size_type f=0; f<m_data.shape()[4]; ++f) {
                        if (std::isnan(m_data[t][w][g][a][f])) {
                            //TODO: Replace with proper log message
                            std::cerr << "VFPPROD element ["
                                    << t << "," << w << "," << g << "," << a << "," << f
                                    << "] not set!" << std::endl;
                            throw std::invalid_argument("Missing VFPPROD value");
                        }
                    }
                }
            }
        }
    }


    //Check that bhp(thp) is a monotonic increasing function.
    //If this is not the case, we might not be able to determine
    //the thp from the bhp easily
    std::string points = "";
    for (size_type w=0; w<m_data.shape()[1]; ++w) {
        for (size_type g=0; g<m_data.shape()[2]; ++g) {
            for (size_type a=0; a<m_data.shape()[3]; ++a) {
                for (size_type f=0; f<m_data.shape()[4]; ++f) {
                    double bhp_last = m_data[0][w][g][a][f];
                    for (size_type t=0; t<m_data.shape()[0]; ++t) {
                        if (m_data[t][w][g][a][f] < bhp_last) {
                            points += "At point (FLOW, THP, WFR, GFR, ALQ) = "
                                    + std::to_string(f) + " " + std::to_string(t) + " "
                                    + std::to_string(w) + " " + std::to_string(g) + " "
                                    + std::to_string(a) + " at BHP = "
                                    + std::to_string(m_data[t][w][g][a][f] / table_scaling_factor) + "\n";
                        }
                        bhp_last = m_data[t][w][g][a][f];
                    }
                }
            }
        }
    }

    if (!points.empty()) {
        OpmLog::warning("VFP table for production wells has BHP versus THP not " 
                           + std::string("monotonically increasing.\nThis may cause convergence ")
                           + "issues due to switching between BHP and THP control mode."
                           + std::string("\nIn keyword VFPPROD table number ") 
                           + std::to_string(m_table_num)
                           + ", file " + keyword.getFileName()
                           + ", line " + std::to_string(keyword.getLineNumber())
                           + "\n");
        OpmLog::note(points);
    }
}
Beispiel #20
0
 // keyword must be DIMENS or SPECGRID
 inline std::array< int, 3 > readDims(const DeckKeyword& keyword) {
     const auto& record = keyword.getRecord(0);
     return { { record.getItem("NX").get<int>(0),
                record.getItem("NY").get<int>(0),
                record.getItem("NZ").get<int>(0) } };
 }