diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index f24ae5d6a6a..4b9fb081622 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -217,6 +217,7 @@ list (APPEND MAIN_SOURCE_FILES opm/simulators/wells/BlackoilWellModelWBP.cpp opm/simulators/wells/ConnFiltrateData.cpp opm/simulators/wells/GuideRateHandler.cpp + opm/simulators/wells/ConnFractureData.cpp opm/simulators/wells/FractionCalculator.cpp opm/simulators/wells/GasLiftCommon.cpp opm/simulators/wells/GasLiftGroupInfo.cpp diff --git a/opm/simulators/wells/ConnFiltrateData.cpp b/opm/simulators/wells/ConnFiltrateData.cpp index 2bb829211a3..4f433663da1 100644 --- a/opm/simulators/wells/ConnFiltrateData.cpp +++ b/opm/simulators/wells/ConnFiltrateData.cpp @@ -37,6 +37,10 @@ void ConnFiltrateData::resize(std::size_t num_perf) this->poro.resize(num_perf); this->radius.resize(num_perf); this->area_of_flow.resize(num_perf); + // Share of the connection flow entering the matrix: 1 unless a + // fracture model registers a competing fracture contribution. + this->flow_factor.resize(num_perf, Scalar{1}); + this->fracture_rate.resize(num_perf); } template @@ -52,6 +56,8 @@ ConnFiltrateData::serializationTestObject() result.poro = {0.3}; result.radius = {0.05}; result.area_of_flow = {0.7}; + result.flow_factor = {0.1}; + result.fracture_rate = {7.}; return result; } @@ -65,7 +71,9 @@ bool ConnFiltrateData::operator==(const ConnFiltrateData& rhs) const this->perm == rhs.perm && this->poro == rhs.poro && this->radius == rhs.radius && - this->area_of_flow == rhs.area_of_flow; + this->area_of_flow == rhs.area_of_flow && + this->flow_factor == rhs.flow_factor && + this->fracture_rate == rhs.fracture_rate; } template struct ConnFiltrateData; diff --git a/opm/simulators/wells/ConnFiltrateData.hpp b/opm/simulators/wells/ConnFiltrateData.hpp index 028953838ef..95c14243d5f 100644 --- a/opm/simulators/wells/ConnFiltrateData.hpp +++ b/opm/simulators/wells/ConnFiltrateData.hpp @@ -40,6 +40,8 @@ struct ConnFiltrateData { serializer(poro); serializer(radius); serializer(area_of_flow); + serializer(flow_factor); + serializer(fracture_rate); } static ConnFiltrateData serializationTestObject(); @@ -54,8 +56,10 @@ struct ConnFiltrateData { std::vector poro; std::vector radius; std::vector area_of_flow; + std::vector flow_factor; + std::vector fracture_rate; }; -} +} // namespace Opm #endif // OPM_CONNFILTRATEDATA_HPP diff --git a/opm/simulators/wells/ConnFractureData.cpp b/opm/simulators/wells/ConnFractureData.cpp new file mode 100644 index 00000000000..82a91777f78 --- /dev/null +++ b/opm/simulators/wells/ConnFractureData.cpp @@ -0,0 +1,92 @@ +/* + Copyright 2023 Equinor ASA. + + 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 . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#include + +namespace Opm { + +template +void ConnFractureData::resize(std::size_t num_perf) +{ + this->area.resize(num_perf); + this->flux.resize(num_perf); + this->height.resize(num_perf); + this->length.resize(num_perf); + this->WI.resize(num_perf); + this->volume.resize(num_perf); + this->filter_volume.resize(num_perf); + this->avg_width.resize(num_perf); + this->avg_filter_width.resize(num_perf); + this->inj_pressure.resize(num_perf); + this->inj_bhp.resize(num_perf); + this->inj_wellrate.resize(num_perf); + this->water_rate.resize(num_perf); +} + +template +ConnFractureData +ConnFractureData::serializationTestObject() +{ + ConnFractureData result; + result.area = {8.}; + result.flux = {100.}; + result.height = {0.5}; + result.length = {0.05}; + result.WI = {10.0}; + result.volume = {4.0}; + result.filter_volume = {0.4}; + result.avg_width = {0.5}; + result.avg_filter_width = {0.05}; + result.inj_pressure = {250.0}; + result.inj_bhp = {200.0}; + result.inj_wellrate = {150.0}; + result.water_rate = {123.4}; + return result; +} + +template +bool ConnFractureData::operator==(const ConnFractureData& rhs) const +{ + return (this->area == rhs.area) + && (this->flux == rhs.flux) + && (this->height == rhs.height) + && (this->length == rhs.length) + && (this->WI == rhs.WI) + && (this->volume == rhs.volume) + && (this->filter_volume == rhs.filter_volume) + && (this->avg_width == rhs.avg_width) + && (this->avg_filter_width == rhs.avg_filter_width) + && (this->inj_pressure == rhs.inj_pressure) + && (this->inj_bhp == rhs.inj_bhp) + && (this->inj_wellrate == rhs.inj_wellrate) + && (this->water_rate == rhs.water_rate) + ; +} + +template struct ConnFractureData; + +#if FLOW_INSTANTIATE_FLOAT +template struct ConnFractureData; +#endif + +} diff --git a/opm/simulators/wells/ConnFractureData.hpp b/opm/simulators/wells/ConnFractureData.hpp new file mode 100644 index 00000000000..04fd476b8fc --- /dev/null +++ b/opm/simulators/wells/ConnFractureData.hpp @@ -0,0 +1,70 @@ +/* + Copyright 2023 Equinor ASA. + + 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 . +*/ + +#ifndef OPM_CONNFRACTUREDATA_HPP +#define OPM_CONNFRACTUREDATA_HPP + +#include + +namespace Opm { + +template +struct ConnFractureData { + + void resize(std::size_t num_perf); + + template + void serializeOp(Serializer& serializer) { + serializer(area); + serializer(flux); + serializer(height); + serializer(length); + serializer(WI); + serializer(volume); + serializer(filter_volume); + serializer(avg_width); + serializer(avg_filter_width); + serializer(inj_pressure); + serializer(inj_bhp); + serializer(inj_wellrate); + serializer(water_rate); + } + + static ConnFractureData serializationTestObject(); + + bool operator==(const ConnFractureData& rhs) const; + + std::vector area; + std::vector flux; + std::vector height; + std::vector length; + std::vector WI; + std::vector volume; + std::vector filter_volume; + std::vector avg_width; + std::vector avg_filter_width; + std::vector inj_pressure; + std::vector inj_bhp; + std::vector inj_wellrate; + std::vector water_rate; +}; + +} + +#endif // OPM_CONNFRACTUREDATA_HPP diff --git a/opm/simulators/wells/PerfData.cpp b/opm/simulators/wells/PerfData.cpp index 7c64d576596..ad1e054a9aa 100644 --- a/opm/simulators/wells/PerfData.cpp +++ b/opm/simulators/wells/PerfData.cpp @@ -68,6 +68,9 @@ void PerfData::prepareInjectorContainers() this->skin_pressure.resize(num_perf); this->water_velocity.resize(num_perf); this->filtrate_data.resize(num_perf); + // fracture_data is deliberately not sized here: a fracture model calls + // resize() when it has something to store, so a run without one pays + // nothing. Everything reading it must tolerate the empty state. } template @@ -99,6 +102,7 @@ PerfData PerfData::serializationTestObject() result.skin_pressure = {27.0, 28.0}; result.water_velocity = {29.0, 30.0}; result.filtrate_data = ConnFiltrateData::serializationTestObject(); + result.fracture_data = ConnFractureData::serializationTestObject(); result.connFracStatistics.assign(3, ConnFracStatistics::serializationTestObject()); result.gas_mass_rates = {31.0}; result.wat_mass_rates = {32.0}; @@ -145,6 +149,7 @@ bool PerfData::try_assign(const PerfData& other) this->skin_pressure = other.skin_pressure; this->water_velocity = other.water_velocity; this->filtrate_data = other.filtrate_data; + this->fracture_data = other.fracture_data; this->connFracStatistics = other.connFracStatistics; this->gas_mass_rates = other.gas_mass_rates; this->wat_mass_rates = other.wat_mass_rates; @@ -180,6 +185,7 @@ bool PerfData::operator==(const PerfData& rhs) const && (this->skin_pressure == rhs.skin_pressure) && (this->water_velocity == rhs.water_velocity) && (this->filtrate_data == rhs.filtrate_data) + && (this->fracture_data == rhs.fracture_data) && (this->connFracStatistics == rhs.connFracStatistics) && (this->gas_mass_rates == rhs.gas_mass_rates) && (this->wat_mass_rates == rhs.wat_mass_rates) diff --git a/opm/simulators/wells/PerfData.hpp b/opm/simulators/wells/PerfData.hpp index 86eae37d707..23e90670c8c 100644 --- a/opm/simulators/wells/PerfData.hpp +++ b/opm/simulators/wells/PerfData.hpp @@ -21,6 +21,7 @@ #define OPM_PERFDATA_HEADER_INCLUDED #include +#include #include #include @@ -80,6 +81,7 @@ class PerfData serializer(skin_pressure); serializer(water_velocity); serializer(filtrate_data); + serializer(fracture_data); serializer(connFracStatistics); serializer(gas_mass_rates); serializer(wat_mass_rates); @@ -123,6 +125,7 @@ class PerfData std::vector water_velocity{}; ConnFiltrateData filtrate_data{}; + ConnFractureData fracture_data{}; std::vector> connFracStatistics{}; }; diff --git a/opm/simulators/wells/SingleWellState.cpp b/opm/simulators/wells/SingleWellState.cpp index 9afbb77f576..cd8173ae785 100644 --- a/opm/simulators/wells/SingleWellState.cpp +++ b/opm/simulators/wells/SingleWellState.cpp @@ -404,6 +404,7 @@ bool SingleWellState::operator==(const SingleWellState& rhs this->surface_rates == rhs.surface_rates && this->reservoir_rates == rhs.reservoir_rates && this->prev_surface_rates == rhs.prev_surface_rates && + this->frac_rate == rhs.frac_rate && this->perf_data == rhs.perf_data && this->filtrate_conc == rhs.filtrate_conc && this->trivial_group_target == rhs.trivial_group_target && diff --git a/opm/simulators/wells/SingleWellState.hpp b/opm/simulators/wells/SingleWellState.hpp index c177ec0acb9..8bb940edfe4 100644 --- a/opm/simulators/wells/SingleWellState.hpp +++ b/opm/simulators/wells/SingleWellState.hpp @@ -77,6 +77,7 @@ class SingleWellState { serializer(surface_rates); serializer(reservoir_rates); serializer(prev_surface_rates); + serializer(frac_rate); serializer(trivial_group_target); serializer(segments); serializer(events); @@ -153,6 +154,7 @@ class SingleWellState { std::vector surface_rates; std::vector reservoir_rates; std::vector prev_surface_rates; + Scalar frac_rate{0.0}; PerfData perf_data; bool trivial_group_target; std::optional group_target; diff --git a/opm/simulators/wells/WellState.cpp b/opm/simulators/wells/WellState.cpp index c805a0e8155..7eba794ad8c 100644 --- a/opm/simulators/wells/WellState.cpp +++ b/opm/simulators/wells/WellState.cpp @@ -547,6 +547,7 @@ report(const int* globalCellIdxMap, const auto& well_potentials = ws.well_potentials; const auto& wpi = ws.productivity_index; const auto& wv = ws.surface_rates; + const auto frac_rate = ws.frac_rate; const auto& wname = this->name(well_index); auto dummyWell = data::Well{}; @@ -565,6 +566,7 @@ report(const int* globalCellIdxMap, if (pu.phaseIsActive(waterPhaseIdx)) { const int phase_pos = pu.canonicalToActivePhaseIdx(waterPhaseIdx); well.rates.set(rt::wat, wv[phase_pos]); + well.rates.set(rt::wat_frac, frac_rate); well.rates.set(rt::reservoir_water, reservoir_rates[phase_pos]); well.rates.set(rt::productivity_index_water, wpi[phase_pos]); well.rates.set(rt::well_potential_water, well_potentials[phase_pos]); @@ -702,6 +704,7 @@ reportConnections(std::vector& connections, if (! ws.producer) { this->reportConnectionFilterCake(well_index, connections); + this->reportConnectionFracture(well_index, connections); } if (! perf_data.connFracStatistics.empty()) { @@ -1318,6 +1321,45 @@ reportConnectionFilterCake(const std::size_t well_index, filtrate.perm = filtrate_data.perm[i]; filtrate.radius = filtrate_data.radius[i]; filtrate.area_of_flow = filtrate_data.area_of_flow[i]; + filtrate.flow_factor = filtrate_data.flow_factor[i]; + filtrate.fracture_rate = filtrate_data.fracture_rate[i]; + } +} + + +template +void WellState:: +reportConnectionFracture(const std::size_t well_index, + std::vector& connections) const +{ + const auto& perf_data = this->well(well_index).perf_data; + const auto num_perf_well = perf_data.size(); + + const auto& data = perf_data.fracture_data; + if (data.area.size() != num_perf_well) { + // No fracture model has claimed this well, so there is nothing to + // report and the containers were never sized. + return; + } + + for (auto i = 0*num_perf_well; i < num_perf_well; ++i) { + auto& fracture = connections[i].fracture; + + fracture.area = data.area[i]; + fracture.flux = data.flux[i]; + fracture.height = data.height[i]; + fracture.length = data.length[i]; + fracture.WI = data.WI[i]; + fracture.volume = data.volume[i]; + fracture.filter_volume = data.filter_volume[i]; + fracture.avg_width = data.avg_width[i]; + fracture.avg_filter_width = data.avg_filter_width[i]; + fracture.inj_pressure = data.inj_pressure[i]; + fracture.inj_bhp = data.inj_bhp[i]; + fracture.inj_wellrate = data.inj_wellrate[i]; + + connections[i].rates.set(data::Rates::opt::wat_frac, + data.water_rate[i]); } } diff --git a/opm/simulators/wells/WellState.hpp b/opm/simulators/wells/WellState.hpp index b0769ccdd33..c7349b1fc4a 100644 --- a/opm/simulators/wells/WellState.hpp +++ b/opm/simulators/wells/WellState.hpp @@ -442,6 +442,9 @@ class WellState void reportConnectionFilterCake(const std::size_t well_index, std::vector& connections) const; + void reportConnectionFracture(const std::size_t well_index, + std::vector& connections) const; + void reportFractureStatistics(const std::vector>& stats, std::vector& connections) const; };