/* Copyright 2015 SINTEF ICT, Applied Mathematics. This file is part of the Open Porous Media project (OPM). OPM 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. OPM 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 for more details. You should have received a copy of the GNU General Public License along with OPM. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Opm { namespace { VFPProdTable::FLO_TYPE getFloType( const DeckItem& item) { const std::string& flo_string = item.getTrimmedString(0); if (flo_string == "OIL") { return VFPProdTable::FLO_OIL; } else if (flo_string == "LIQ") { return VFPProdTable::FLO_LIQ; } else if (flo_string == "GAS") { return VFPProdTable::FLO_GAS; } else { throw std::invalid_argument("Invalid RATE_TYPE string: " + flo_string); } return VFPProdTable::FLO_INVALID; } VFPProdTable::WFR_TYPE getWFRType( const DeckItem& item) { const std::string& wfr_string = item.getTrimmedString(0); if (wfr_string == "WOR") { return VFPProdTable::WFR_WOR; } else if (wfr_string == "WCT") { return VFPProdTable::WFR_WCT; } else if (wfr_string == "WGR") { return VFPProdTable::WFR_WGR; } else { throw std::invalid_argument("Invalid WFR string"); } return VFPProdTable::WFR_INVALID; } VFPProdTable::GFR_TYPE getGFRType( const DeckItem& item) {; const std::string& gfr_string = item.getTrimmedString(0); if (gfr_string == "GOR") { return VFPProdTable::GFR_GOR; } else if (gfr_string == "GLR") { return VFPProdTable::GFR_GLR; } else if (gfr_string == "OGR") { return VFPProdTable::GFR_OGR; } else { throw std::invalid_argument("Invalid GFR string"); } return VFPProdTable::GFR_INVALID; } VFPProdTable::ALQ_TYPE getALQType( const DeckItem& item) { if (item.defaultApplied(0)) { return VFPProdTable::ALQ_UNDEF; } else { const std::string& alq_string = item.getTrimmedString(0); if (alq_string == "GRAT") { return VFPProdTable::ALQ_GRAT; } else if (alq_string == "IGLR") { return VFPProdTable::ALQ_IGLR; } else if (alq_string == "TGLR") { return VFPProdTable::ALQ_TGLR; } else if (alq_string == "PUMP") { return VFPProdTable::ALQ_PUMP; } else if (alq_string == "COMP") { return VFPProdTable::ALQ_COMP; } else if (alq_string == "BEAN") { return VFPProdTable::ALQ_BEAN; } else if (alq_string == "") { return VFPProdTable::ALQ_UNDEF; } else { throw std::invalid_argument("Invalid ALQ_DEF string: " + alq_string); } return VFPProdTable::ALQ_INVALID; } } } VFPProdTable::VFPProdTable() { m_table_num = -1; m_datum_depth = 0.0; m_flo_type = FLO_INVALID; m_wfr_type = WFR_INVALID; m_gfr_type = GFR_INVALID; m_alq_type = ALQ_INVALID; } VFPProdTable::VFPProdTable(int table_num, double datum_depth, FLO_TYPE flo_type, WFR_TYPE wfr_type, GFR_TYPE gfr_type, ALQ_TYPE alq_type, const std::vector& flo_data, const std::vector& thp_data, const std::vector& wfr_data, const std::vector& gfr_data, const std::vector& alq_data, const array_type& data) { m_table_num = table_num; m_datum_depth = datum_depth; m_flo_type = flo_type; m_wfr_type = wfr_type; m_gfr_type = gfr_type; m_alq_type = alq_type; m_flo_data = flo_data; m_thp_data = thp_data; m_wfr_data = wfr_data; m_gfr_data = gfr_data; m_alq_data = alq_data; m_data = data; //check(); } VFPProdTable VFPProdTable::serializeObject() { VFPProdTable result; result.m_table_num = 1; result.m_datum_depth = 2; result.m_flo_type = FLO_OIL; result.m_wfr_type = WFR_WOR; result.m_gfr_type = GFR_GLR; result.m_alq_type = ALQ_TGLR; result.m_flo_data = {1.0, 2.0, 3.0, 4.0, 5.0}; result.m_thp_data = {6.0}; result.m_wfr_data = {7.0}; result.m_gfr_data = {8.0}; result.m_alq_data = {9.0, 10.0, 11.0}; result.m_data = {12.0, 13.0, 14.0, 15.0, 16.0}; return result; } 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().get< int >(0); m_datum_depth = header.getItem().getSIDouble(0); m_flo_type = Opm::getFloType(header.getItem()); m_wfr_type = Opm::getWFRType(header.getItem()); m_gfr_type = Opm::getGFRType(header.getItem()); //Not used, but check that PRESSURE_DEF is indeed THP std::string quantity_string = header.getItem().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()); //Check units used for this table std::string units_string = ""; if (header.getItem().hasValue(0)) { units_string = header.getItem().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().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().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().getData< double >(); convertTHPToSI(m_thp_data, deck_unit_system); //Get actual water fraction values m_wfr_data = table.getRecord(3).getItem().getData< double >(); convertWFRToSI(m_wfr_type, m_wfr_data, deck_unit_system); //Get actual gas fraction values m_gfr_data = table.getRecord(4).getItem().getData< double >(); convertGFRToSI(m_gfr_type, m_gfr_data, deck_unit_system); //Get actual gas fraction values m_alq_data = table.getRecord(5).getItem().getData< double >(); convertAlqToSI(m_alq_type, m_alq_data, deck_unit_system); //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(); m_data.resize(nt*nw*ng*na*nf); std::fill_n(m_data.data(), m_data.size(), 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().get< int >(0) - 1; int w = record.getItem().get< int >(0) - 1; int g = record.getItem().get< int >(0) - 1; int a = record.getItem().get< int >(0) - 1; //Rest of values (bottom hole pressure or tubing head temperature) have index of flo value const std::vector& bhp_tht = record.getItem().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 1.0e10) { //TODO: Replace with proper log message std::cerr << "VFPPROD element [" << t << "," << w << "," << g << "," << a << "," << f << "]=" << bhp_tht[f] << " too large" << std::endl; } (*this)(t,w,g,a,f) = table_scaling_factor*bhp_tht[f]; } } check(table, table_scaling_factor); } 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())); 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(); //Check data size matches axes assert(m_data.size() == nt*nw*ng*na*nf); //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 typedef array_type::size_type size_type; std::string points; for (size_type w = 0; w < nw; ++w) { for (size_type g = 0; g < ng; ++g) { for (size_type a = 0; a < na; ++a) { for (size_type f = 0; f < nf; ++f) { double bhp_last = (*this)(0,w,g,a,f); for (size_type t = 0; t < nt; ++t) { //Check that all elements have been set if (std::isnan((*this)(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"); } if ((*this)(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((*this)(t,w,g,a,f) / table_scaling_factor) + "\n"; } bhp_last = (*this)(t,w,g,a,f); } } } } } if (!points.empty()) { const auto& location = keyword.location(); 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 " + location.filename + ", line " + std::to_string(location.lineno) + "\n"); OpmLog::note(points); } } void VFPProdTable::scaleValues(std::vector& values, const double& scaling_factor) { if (scaling_factor == 1.0) { return; } else { for (size_t i=0; i& values, const UnitSystem& unit_system) { double scaling_factor = 1.0; switch (type) { case FLO_OIL: case FLO_LIQ: scaling_factor = unit_system.parse("LiquidSurfaceVolume/Time").getSIScaling(); break; case FLO_GAS: scaling_factor = unit_system.parse("GasSurfaceVolume/Time").getSIScaling(); break; default: throw std::logic_error("Invalid FLO type"); } scaleValues(values, scaling_factor); } void VFPProdTable::convertTHPToSI(std::vector& values, const UnitSystem& unit_system) { double scaling_factor = unit_system.parse("Pressure").getSIScaling(); scaleValues(values, scaling_factor); } void VFPProdTable::convertWFRToSI(const WFR_TYPE& type, std::vector& values, const UnitSystem& unit_system) { double scaling_factor = 1.0; switch (type) { case WFR_WOR: case WFR_WCT: scaling_factor = unit_system.parse("LiquidSurfaceVolume/LiquidSurfaceVolume").getSIScaling(); break; case WFR_WGR: scaling_factor = unit_system.parse("LiquidSurfaceVolume/GasSurfaceVolume").getSIScaling(); break; default: throw std::logic_error("Invalid FLO type"); } scaleValues(values, scaling_factor); } void VFPProdTable::convertGFRToSI(const GFR_TYPE& type, std::vector& values, const UnitSystem& unit_system) { double scaling_factor = 1.0; switch (type) { case GFR_GOR: case GFR_GLR: scaling_factor = unit_system.parse("GasSurfaceVolume/LiquidSurfaceVolume").getSIScaling(); break; case GFR_OGR: scaling_factor = unit_system.parse("LiquidSurfaceVolume/GasSurfaceVolume").getSIScaling(); break; default: throw std::logic_error("Invalid FLO type"); } scaleValues(values, scaling_factor); } void VFPProdTable::convertAlqToSI(const ALQ_TYPE& type, std::vector& values, const UnitSystem& unit_system) { double scaling_factor = 1.0; switch (type) { case ALQ_IGLR: scaling_factor = unit_system.parse("GasSurfaceVolume/LiquidSurfaceVolume * Time").getSIScaling(); break; case ALQ_TGLR: scaling_factor = unit_system.parse("GasSurfaceVolume/LiquidSurfaceVolume").getSIScaling(); break; case ALQ_GRAT: scaling_factor = unit_system.parse("GasSurfaceVolume/Time").getSIScaling(); break; case ALQ_UNDEF: break; case ALQ_PUMP: case ALQ_COMP: case ALQ_INVALID: case ALQ_BEAN: std::logic_error("scaling of the given ALQ type, not implemented "); break; default: throw std::logic_error("Invalid ALQ type"); } scaleValues(values, scaling_factor); } bool VFPProdTable::operator==(const VFPProdTable& data) const { return this->getTableNum() == data.getTableNum() && this->getDatumDepth () == data.getDatumDepth() && this->getFloType() == data.getFloType() && this->getWFRType() == data.getWFRType() && this->getGFRType() == data.getGFRType() && this->getALQType() == data.getALQType() && this->getFloAxis() == data.getFloAxis() && this->getTHPAxis() == data.getTHPAxis() && this->getWFRAxis() == data.getWFRAxis() && this->getGFRAxis() == data.getGFRAxis() && this->getALQAxis() == data.getALQAxis() && this->getTable() == data.getTable(); } double VFPProdTable::operator()(size_t thp_idx, size_t wfr_idx, size_t gfr_idx, size_t alq_idx, size_t flo_idx) const { 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(); return m_data[thp_idx*nw*ng*na*nf + wfr_idx*ng*na*nf + gfr_idx*na*nf + alq_idx*nf + flo_idx]; } double& VFPProdTable::operator()(size_t thp_idx, size_t wfr_idx, size_t gfr_idx, size_t alq_idx, size_t flo_idx) { 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(); return m_data[thp_idx*nw*ng*na*nf + wfr_idx*ng*na*nf + gfr_idx*na*nf + alq_idx*nf + flo_idx]; } std::array VFPProdTable::shape() const { 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(); return {nt, nw, ng, na, nf}; } } //Namespace opm