From 814c8739c5f46f82782b264d812d9d619b3cbdf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Wed, 18 Dec 2024 15:12:28 +0100 Subject: [PATCH] Collect Statistics on Connection Level Fracturing Process This commit adds a new member function Fracture::assignGeomechWellState(ConnFracStatistics&) which computes statistical measures (minimum value, maximum value, average value/mean, and standard deviation) of fracture pressures, injection flow rate, and fracture width for a single fracture. We also add a new member function FractureModel::assignGeomechWellState(WellState& wellState) which calls the above for each fracture in the model. Collectively, these enable emitting the new summary vectors CFR{MAX,MIN,AVG,STD} for the quantity as 'P' (pressure), 'IR' (injection rate), and 'WD' (width) through the regular mechanism. --- opm/geomech/Fracture.cpp | 32 +++++++++++++++++++++++++++ opm/geomech/Fracture.hpp | 8 +++++++ opm/geomech/FractureModel.cpp | 38 ++++++++++++++++++++++++++++++++ opm/geomech/FractureModel.hpp | 8 +++++++ opm/geomech/eclproblemgeomech.hh | 3 +++ 5 files changed, 89 insertions(+) diff --git a/opm/geomech/Fracture.cpp b/opm/geomech/Fracture.cpp index c49094f..db64782 100644 --- a/opm/geomech/Fracture.cpp +++ b/opm/geomech/Fracture.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -959,6 +960,34 @@ std::vector> Fracture::wellIndices() const return wellIndices; } +template +void Fracture::assignGeomechWellState(ConnFracStatistics& stats) const +{ + using Quantity = typename ConnFracStatistics::Quantity; + + constexpr auto pressIx = static_cast>(Quantity::Pressure); + constexpr auto rateIx = static_cast>(Quantity::FlowRate); + constexpr auto widthIx = static_cast>(Quantity::Width); + + const auto nCells = this->reservoir_cells_.size(); + + stats.reset(); + + for (auto cellIx = 0*nCells; cellIx < nCells; ++cellIx) { + auto samplePoint = typename ConnFracStatistics::SamplePoint{}; + + samplePoint[pressIx] = this->fracture_pressure_[cellIx][0]; + + samplePoint[rateIx] = this->leakof_[cellIx] + * (this->fracture_pressure_[cellIx][0] - + this->reservoir_pressure_[cellIx]); + + samplePoint[widthIx] = this->fracture_width_[cellIx][0]; + + stats.addSamplePoint(samplePoint); + } +} + void Fracture::writePressureSystem() const { if(prm_.get("write_pressure_system")){ @@ -1255,4 +1284,7 @@ template void Fracture::updateReservoirCells(const external::cvf::ref& cellSearchTree, const Dune::PolyhedralGrid<3,3,double>& grid3D); +template void Fracture::assignGeomechWellState(ConnFracStatistics&) const; +template void Fracture::assignGeomechWellState(ConnFracStatistics&) const; + } // namespace Opm diff --git a/opm/geomech/Fracture.hpp b/opm/geomech/Fracture.hpp index afeec53..927c5e1 100644 --- a/opm/geomech/Fracture.hpp +++ b/opm/geomech/Fracture.hpp @@ -53,6 +53,11 @@ #include +namespace Opm { + template + class ConnFracStatistics; +} + namespace Opm { struct WellInfo { @@ -184,6 +189,9 @@ class Fracture Dune::FieldVector strain(const Dune::FieldVector& obs) const; Dune::FieldVector disp(const Dune::FieldVector& obs) const; + template + void assignGeomechWellState(ConnFracStatistics& stats) const; + private: Dune::BlockVector> all_slips() const; void resetWriters(); diff --git a/opm/geomech/FractureModel.cpp b/opm/geomech/FractureModel.cpp index 3481164..a47e357 100644 --- a/opm/geomech/FractureModel.cpp +++ b/opm/geomech/FractureModel.cpp @@ -7,6 +7,9 @@ #include +#include +#include + #include #include #include @@ -254,4 +257,39 @@ namespace Opm{ return wellindices; } + + template + void FractureModel::assignGeomechWellState(WellState& wellState) const + { + const auto nWells = this->wells_.size(); + for (auto i = 0*nWells; i < nWells; ++i) { + const auto wsIx = wellState.index(this->wells_[i].name()); + if (! wsIx.has_value()) { continue; } + + auto& perfData = wellState[*wsIx].perf_data; + + if (perfData.connFracStatistics.size() != perfData.cell_index.size()) { + perfData.connFracStatistics.resize(perfData.cell_index.size()); + } + + for (const auto& fracture : this->well_fractures_[i]) { + auto perfPos = std::find(perfData.cell_index.begin(), + perfData.cell_index.end(), + fracture.wellInfo().well_cell); + if (perfPos == perfData.cell_index.end()) { continue; } + + // Possibly just "fracture.wellInfo().perf" instead. + const auto perfIx = std::distance(perfData.cell_index.begin(), perfPos); + + fracture.assignGeomechWellState(perfData.connFracStatistics[perfIx]); + } + } + } } + +// =========================================================================== +// Explicit specialisations. No other code below separator. +// =========================================================================== + +template void Opm::FractureModel::assignGeomechWellState(WellState&) const; +template void Opm::FractureModel::assignGeomechWellState(WellState&) const; diff --git a/opm/geomech/FractureModel.hpp b/opm/geomech/FractureModel.hpp index 35946e2..7b91d6e 100644 --- a/opm/geomech/FractureModel.hpp +++ b/opm/geomech/FractureModel.hpp @@ -31,6 +31,11 @@ std::vector as_vector(ptree const& pt, ptree::key_type const& key) return r; } +namespace Opm { + template + class WellState; +} + // template // std::vector opm_as_vector(const Opm::PropertyTree& pt, const std::string& key) // { @@ -143,6 +148,9 @@ class FractureModel std::vector> getExtraWellIndices(const std::string& wellname) const; + template + void assignGeomechWellState(WellState& wellState) const; + bool addPertsToSchedule(){return prm_.get("addperfs_to_schedule");} // probably this should be collected in one loop since all do full loop over fracture ++ well diff --git a/opm/geomech/eclproblemgeomech.hh b/opm/geomech/eclproblemgeomech.hh index 16870b0..d7534c3 100644 --- a/opm/geomech/eclproblemgeomech.hh +++ b/opm/geomech/eclproblemgeomech.hh @@ -263,6 +263,9 @@ namespace Opm{ assert(false); this->addConnectionsToWell(); } + + this->geomechModel_.fractureModel() + .assignGeomechWellState(this->wellModel_.wellState()); } }