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 ); } }
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 ); } }
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; }
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; }
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 ); } } }
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); } } }
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; }
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"); } }
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; }
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 ); } }
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); } } }
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; }
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 ); }
Equil::Equil( const DeckKeyword& keyword ) : records( keyword.begin(), keyword.end() ) {}
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); }
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; }
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); }
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); } }
// 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) } }; }