mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Minor refactoring of VFPProperties to prepare for support for VFPINJ tables
This commit is contained in:
@@ -33,40 +33,63 @@ namespace Opm {
|
|||||||
VFPProperties::VFPProperties() {
|
VFPProperties::VFPProperties() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VFPProperties::init(const VFPProdTable* table) {
|
VFPProperties::VFPProperties(const VFPInjTable* inj_table, const VFPProdTable* prod_table) {
|
||||||
m_tables[table->getTableNum()] = table;
|
if (inj_table != NULL) {
|
||||||
|
m_inj_tables[inj_table->getTableNum()] = inj_table;
|
||||||
|
}
|
||||||
|
if (prod_table != NULL) {
|
||||||
|
m_prod_tables[prod_table->getTableNum()] = prod_table;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VFPProperties::init(const std::vector<VFPProdTable>& tables) {
|
VFPProperties::VFPProperties(const std::map<int, VFPInjTable>& inj_tables,
|
||||||
//Loop through all tables, and add to our map
|
const std::map<int, VFPProdTable>& prod_tables) {
|
||||||
for (unsigned int i=0; i<tables.size(); ++i) {
|
init(inj_tables);
|
||||||
int table_number = tables[i].getTableNum();
|
init(prod_tables);
|
||||||
if (m_tables.find(table_number) != m_tables.end()) {
|
}
|
||||||
OPM_THROW(std::invalid_argument, "Duplicate table numbers found for VFPPROD");
|
|
||||||
}
|
|
||||||
else {
|
VFPProperties::VFPProperties(const std::map<int, VFPInjTable>& inj_tables) {
|
||||||
m_tables[table_number] = &tables[i];
|
init(inj_tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VFPProperties::VFPProperties(const std::map<int, VFPProdTable>& prod_tables) {
|
||||||
|
init(prod_tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VFPProperties::init(const std::map<int, VFPInjTable>& inj_tables) {
|
||||||
|
//Populate injection table pointers.
|
||||||
|
for (const auto& table : inj_tables) {
|
||||||
|
m_inj_tables[table.first] = &table.second;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VFPProperties::init(const std::map<int, VFPProdTable>& prod_tables) {
|
||||||
|
//Populate production table pointers
|
||||||
|
for (const auto& table : prod_tables) {
|
||||||
|
m_prod_tables[table.first] = &table.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double VFPProperties::bhp(int table, const double& flo, const double& thp, const double& wfr, const double& gfr, const double& alq) {
|
double VFPProperties::prod_bhp(int table, const double& flo, const double& thp, const double& wfr, const double& gfr, const double& alq) {
|
||||||
if (m_tables.find(table) == m_tables.end()) {
|
if (m_prod_tables.find(table) == m_prod_tables.end()) {
|
||||||
OPM_THROW(std::invalid_argument, "Nonexistant table " << table << " referenced.");
|
OPM_THROW(std::invalid_argument, "Nonexistent table " << table << " referenced.");
|
||||||
}
|
}
|
||||||
|
const auto* tab = m_prod_tables[table];
|
||||||
//First, find the values to interpolate between
|
//First, find the values to interpolate between
|
||||||
auto flo_i = find_interp_data(flo, m_tables[table]->getFloAxis());
|
auto flo_i = find_interp_data(flo, tab->getFloAxis());
|
||||||
auto thp_i = find_interp_data(thp, m_tables[table]->getTHPAxis());
|
auto thp_i = find_interp_data(thp, tab->getTHPAxis());
|
||||||
auto wfr_i = find_interp_data(wfr, m_tables[table]->getWFRAxis());
|
auto wfr_i = find_interp_data(wfr, tab->getWFRAxis());
|
||||||
auto gfr_i = find_interp_data(gfr, m_tables[table]->getGFRAxis());
|
auto gfr_i = find_interp_data(gfr, tab->getGFRAxis());
|
||||||
auto alq_i = find_interp_data(alq, m_tables[table]->getALQAxis());
|
auto alq_i = find_interp_data(alq, tab->getALQAxis());
|
||||||
|
|
||||||
//Then perform the interpolation itself
|
//Then perform the interpolation itself
|
||||||
return interpolate(m_tables[table]->getTable(), flo_i, thp_i, wfr_i, gfr_i, alq_i);
|
return interpolate(tab->getTable(), flo_i, thp_i, wfr_i, gfr_i, alq_i);
|
||||||
}
|
}
|
||||||
|
|
||||||
VFPProperties::ADB VFPProperties::bhp(int table, const ADB& flo, const ADB& thp, const ADB& wfr, const ADB& gfr, const ADB& alq) {
|
VFPProperties::ADB VFPProperties::prod_bhp(int table, const ADB& flo, const ADB& thp, const ADB& wfr, const ADB& gfr, const ADB& alq) {
|
||||||
const ADB::V& f_v = flo.value();
|
const ADB::V& f_v = flo.value();
|
||||||
const ADB::V& t_v = thp.value();
|
const ADB::V& t_v = thp.value();
|
||||||
const ADB::V& w_v = wfr.value();
|
const ADB::V& w_v = wfr.value();
|
||||||
@@ -79,7 +102,7 @@ VFPProperties::ADB VFPProperties::bhp(int table, const ADB& flo, const ADB& thp,
|
|||||||
ADB::V bhp_vals;
|
ADB::V bhp_vals;
|
||||||
bhp_vals.resize(nw);
|
bhp_vals.resize(nw);
|
||||||
for (int i=0; i<nw; ++i) {
|
for (int i=0; i<nw; ++i) {
|
||||||
bhp_vals[i] = bhp(table, f_v[i], t_v[i], w_v[i], g_v[i], a_v[i]);
|
bhp_vals[i] = prod_bhp(table, f_v[i], t_v[i], w_v[i], g_v[i], a_v[i]);
|
||||||
}
|
}
|
||||||
//Create an ADB constant value.
|
//Create an ADB constant value.
|
||||||
return ADB::constant(bhp_vals);
|
return ADB::constant(bhp_vals);
|
||||||
@@ -87,8 +110,8 @@ VFPProperties::ADB VFPProperties::bhp(int table, const ADB& flo, const ADB& thp,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
VFPProperties::ADB VFPProperties::bhp(int table, const Wells& wells, const ADB& qs, const ADB& thp, const ADB& alq) {
|
VFPProperties::ADB VFPProperties::prod_bhp(int table, const Wells& wells, const ADB& qs, const ADB& thp, const ADB& alq) {
|
||||||
if (m_tables.find(table) == m_tables.end()) {
|
if (m_prod_tables.find(table) == m_prod_tables.end()) {
|
||||||
OPM_THROW(std::invalid_argument, "Nonexistant table " << table << " referenced.");
|
OPM_THROW(std::invalid_argument, "Nonexistant table " << table << " referenced.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,13 +125,14 @@ VFPProperties::ADB VFPProperties::bhp(int table, const Wells& wells, const ADB&
|
|||||||
const ADB& o = subset(qs, Span(nw, 1, BlackoilPhases::Liquid*nw));
|
const ADB& o = subset(qs, Span(nw, 1, BlackoilPhases::Liquid*nw));
|
||||||
const ADB& g = subset(qs, Span(nw, 1, BlackoilPhases::Vapour*nw));
|
const ADB& g = subset(qs, Span(nw, 1, BlackoilPhases::Vapour*nw));
|
||||||
|
|
||||||
ADB flo = getFlo(w, o, g, m_tables[table]->getFloType());
|
const auto* tab = m_prod_tables[table];
|
||||||
ADB wfr = getWFR(w, o, g, m_tables[table]->getWFRType());
|
ADB flo = getFlo(w, o, g, tab->getFloType());
|
||||||
ADB gfr = getGFR(w, o, g, m_tables[table]->getGFRType());
|
ADB wfr = getWFR(w, o, g, tab->getWFRType());
|
||||||
|
ADB gfr = getGFR(w, o, g, tab->getGFRType());
|
||||||
|
|
||||||
//TODO: Check ALQ type here?
|
//TODO: Check ALQ type here?
|
||||||
|
|
||||||
return bhp(table, flo, thp, wfr, gfr, alq);
|
return prod_bhp(table, flo, thp, wfr, gfr, alq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,38 +23,57 @@
|
|||||||
#include <opm/core/wells.h>
|
#include <opm/core/wells.h>
|
||||||
#include <opm/autodiff/AutoDiffBlock.hpp>
|
#include <opm/autodiff/AutoDiffBlock.hpp>
|
||||||
#include <opm/parser/eclipse/EclipseState/Tables/VFPProdTable.hpp>
|
#include <opm/parser/eclipse/EclipseState/Tables/VFPProdTable.hpp>
|
||||||
|
#include <opm/parser/eclipse/EclipseState/Tables/VFPInjTable.hpp>
|
||||||
#include <boost/multi_array.hpp>
|
#include <boost/multi_array.hpp>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace Opm {
|
namespace Opm {
|
||||||
|
|
||||||
class VFPProdTable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class which linearly interpolates BHP as a function of rate type, tubing head pressure,
|
* Class which linearly interpolates BHP as a function of rate, tubing head pressure,
|
||||||
* water fraction, gas fraction, and artificial lift.
|
* water fraction, gas fraction, and artificial lift for production VFP tables, and similarly
|
||||||
|
* the BHP as a function of the rate and tubing head pressure.
|
||||||
*/
|
*/
|
||||||
class VFPProperties {
|
class VFPProperties {
|
||||||
public:
|
public:
|
||||||
typedef AutoDiffBlock<double> ADB;
|
typedef AutoDiffBlock<double> ADB;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Empty constructor
|
||||||
*/
|
*/
|
||||||
VFPProperties();
|
VFPProperties();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialization routine for a single production table, takes *no* ownership of data.
|
* Constructor
|
||||||
* @param table A single VFPPROD table
|
* Takes *no* ownership of data.
|
||||||
|
* @param inj_table A *single* VFPINJ table or NULL (no table)
|
||||||
|
* @param prod_table A *single* VFPPROD table or NULL (no table)
|
||||||
*/
|
*/
|
||||||
void init(const VFPProdTable* table);
|
VFPProperties(const VFPInjTable* inj_table, const VFPProdTable* prod_table);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialization routine, takes *no* ownership of data.
|
* Constructor
|
||||||
* @param tables A vector of different VFPPROD tables. Must have unique table numbers
|
* Takes *no* ownership of data.
|
||||||
|
* @param inj_tables A map of different VFPINJ tables.
|
||||||
|
* @param prod_tables A map of different VFPPROD tables.
|
||||||
*/
|
*/
|
||||||
void init(const std::vector<VFPProdTable>& tables);
|
VFPProperties(const std::map<int, VFPInjTable>& inj_tables,
|
||||||
|
const std::map<int, VFPProdTable>& prod_tables);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* Takes *no* ownership of data.
|
||||||
|
* @param inj_tables A map of different VFPINJ tables.
|
||||||
|
*/
|
||||||
|
VFPProperties(const std::map<int, VFPInjTable>& inj_tables);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* Takes *no* ownership of data.
|
||||||
|
* @param prod_tables A map of different VFPPROD tables.
|
||||||
|
*/
|
||||||
|
VFPProperties(const std::map<int, VFPProdTable>& prod_tables);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Linear interpolation of bhp as function of the input parameters.
|
* Linear interpolation of bhp as function of the input parameters.
|
||||||
@@ -67,7 +86,7 @@ public:
|
|||||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||||
* the above parameters from the values in the input table.
|
* the above parameters from the values in the input table.
|
||||||
*/
|
*/
|
||||||
ADB bhp(int table,
|
ADB prod_bhp(int table,
|
||||||
const Wells& wells,
|
const Wells& wells,
|
||||||
const ADB& qs,
|
const ADB& qs,
|
||||||
const ADB& thp,
|
const ADB& thp,
|
||||||
@@ -85,7 +104,7 @@ public:
|
|||||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||||
* the above parameters from the values in the input table.
|
* the above parameters from the values in the input table.
|
||||||
*/
|
*/
|
||||||
double bhp(int table,
|
double prod_bhp(int table,
|
||||||
const double& flo,
|
const double& flo,
|
||||||
const double& thp,
|
const double& thp,
|
||||||
const double& wfr,
|
const double& wfr,
|
||||||
@@ -105,13 +124,15 @@ public:
|
|||||||
* the above parameters from the values in the input table, for each entry in the
|
* the above parameters from the values in the input table, for each entry in the
|
||||||
* input ADB objects.
|
* input ADB objects.
|
||||||
*/
|
*/
|
||||||
ADB bhp(int table,
|
ADB prod_bhp(int table,
|
||||||
const ADB& flo,
|
const ADB& flo,
|
||||||
const ADB& thp,
|
const ADB& thp,
|
||||||
const ADB& wfr,
|
const ADB& wfr,
|
||||||
const ADB& gfr,
|
const ADB& gfr,
|
||||||
const ADB& alq);
|
const ADB& alq);
|
||||||
|
|
||||||
|
//FIXME: ARB: Implement inj_bhp to match the prod_bhp's, but for injection wells.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the flo parameter according to the flo_type_
|
* Computes the flo parameter according to the flo_type_
|
||||||
* @return Production rate of oil, gas or liquid.
|
* @return Production rate of oil, gas or liquid.
|
||||||
@@ -135,7 +156,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Map which connects the table number with the table itself
|
// Map which connects the table number with the table itself
|
||||||
std::map<int, const VFPProdTable*> m_tables;
|
std::map<int, const VFPProdTable*> m_prod_tables;
|
||||||
|
std::map<int, const VFPInjTable*> m_inj_tables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper struct for linear interpolation
|
* Helper struct for linear interpolation
|
||||||
@@ -161,6 +183,11 @@ private:
|
|||||||
const InterpData& gfr_i,
|
const InterpData& gfr_i,
|
||||||
const InterpData& alq_i);
|
const InterpData& alq_i);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization routines
|
||||||
|
*/
|
||||||
|
void init(const std::map<int, VFPInjTable>& inj_tables);
|
||||||
|
void init(const std::map<int, VFPProdTable>& prod_tables);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
@@ -136,8 +136,7 @@ struct TrivialFixture {
|
|||||||
data);
|
data);
|
||||||
|
|
||||||
//Initialize properties that use the table
|
//Initialize properties that use the table
|
||||||
properties.reset(new Opm::VFPProperties());
|
properties.reset(new Opm::VFPProperties(NULL, &table));
|
||||||
properties->init(&table);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Opm::VFPProperties> properties;
|
std::shared_ptr<Opm::VFPProperties> properties;
|
||||||
@@ -171,10 +170,10 @@ BOOST_AUTO_TEST_CASE(GetTable)
|
|||||||
initProperties();
|
initProperties();
|
||||||
|
|
||||||
//Table 1 has been initialized
|
//Table 1 has been initialized
|
||||||
properties->bhp(1, 0.0, 0.0, 0.0, 0.0, 0.0);
|
properties->prod_bhp(1, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
//Table 2 does not exist.
|
//Table 2 does not exist.
|
||||||
BOOST_CHECK_THROW(properties->bhp(2, 0.0, 0.0, 0.0, 0.0, 0.0), std::invalid_argument);
|
BOOST_CHECK_THROW(properties->prod_bhp(2, 0.0, 0.0, 0.0, 0.0, 0.0), std::invalid_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,7 +200,7 @@ BOOST_AUTO_TEST_CASE(InterpolateZero)
|
|||||||
const double v = m / static_cast<double>(n-1);
|
const double v = m / static_cast<double>(n-1);
|
||||||
|
|
||||||
//Note order of arguments!
|
//Note order of arguments!
|
||||||
sum += properties->bhp(1, v, x, y, z, u);
|
sum += properties->prod_bhp(1, v, x, y, z, u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +235,7 @@ BOOST_AUTO_TEST_CASE(InterpolateOne)
|
|||||||
const double v = m / static_cast<double>(n-1);
|
const double v = m / static_cast<double>(n-1);
|
||||||
|
|
||||||
//Note order of arguments!
|
//Note order of arguments!
|
||||||
sum += properties->bhp(1, v, x, y, z, u);
|
sum += properties->prod_bhp(1, v, x, y, z, u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,7 +276,7 @@ BOOST_AUTO_TEST_CASE(InterpolatePlane)
|
|||||||
reference_sum += reference;
|
reference_sum += reference;
|
||||||
|
|
||||||
//Note order of arguments!
|
//Note order of arguments!
|
||||||
double value = properties->bhp(1, v, x, y, z, u);
|
double value = properties->prod_bhp(1, v, x, y, z, u);
|
||||||
sum += value;
|
sum += value;
|
||||||
|
|
||||||
double abs_diff = std::abs(value - reference);
|
double abs_diff = std::abs(value - reference);
|
||||||
@@ -326,7 +325,7 @@ BOOST_AUTO_TEST_CASE(ExtrapolatePlane)
|
|||||||
reference_sum += reference;
|
reference_sum += reference;
|
||||||
|
|
||||||
//Note order of arguments!
|
//Note order of arguments!
|
||||||
double value = properties->bhp(1, v, x, y, z, u);
|
double value = properties->prod_bhp(1, v, x, y, z, u);
|
||||||
sum += value;
|
sum += value;
|
||||||
|
|
||||||
double abs_diff = std::abs(value - reference);
|
double abs_diff = std::abs(value - reference);
|
||||||
@@ -394,7 +393,7 @@ BOOST_AUTO_TEST_CASE(ExtrapolatePlaneADB)
|
|||||||
ADB gfr = ADB::constant(zz);
|
ADB gfr = ADB::constant(zz);
|
||||||
ADB alq = ADB::constant(uu);
|
ADB alq = ADB::constant(uu);
|
||||||
|
|
||||||
ADB bhp_val = properties->bhp(1, flo, thp, wfr, gfr, alq);
|
ADB bhp_val = properties->prod_bhp(1, flo, thp, wfr, gfr, alq);
|
||||||
|
|
||||||
double value = 0.0;
|
double value = 0.0;
|
||||||
double reference = 0.0;
|
double reference = 0.0;
|
||||||
@@ -474,7 +473,7 @@ BOOST_AUTO_TEST_CASE(InterpolateADBAndQs)
|
|||||||
ADB alq = ADB::constant(alq_vals);
|
ADB alq = ADB::constant(alq_vals);
|
||||||
|
|
||||||
//Call the bhp function
|
//Call the bhp function
|
||||||
ADB bhp = properties->bhp(1, *wells, qs, thp, alq);
|
ADB bhp = properties->prod_bhp(1, *wells, qs, thp, alq);
|
||||||
|
|
||||||
//Calculate reference
|
//Calculate reference
|
||||||
//First, find the three phases
|
//First, find the three phases
|
||||||
@@ -722,17 +721,10 @@ BOOST_AUTO_TEST_CASE(ParseInterpolateRealisticVFPPROD)
|
|||||||
BOOST_REQUIRE(deck->hasKeyword("VFPPROD"));
|
BOOST_REQUIRE(deck->hasKeyword("VFPPROD"));
|
||||||
BOOST_CHECK_EQUAL(deck->numKeywords("VFPPROD"), 1);
|
BOOST_CHECK_EQUAL(deck->numKeywords("VFPPROD"), 1);
|
||||||
|
|
||||||
std::vector<Opm::VFPProdTable> tables;
|
Opm::VFPProdTable table;
|
||||||
|
table.init(deck->getKeyword("VFPPROD", 1), units);
|
||||||
|
|
||||||
int num_tables = deck->numKeywords("VFPPROD");
|
Opm::VFPProperties properties(NULL, &table);
|
||||||
for (int i=0; i<num_tables; ++i) {
|
|
||||||
Opm::DeckKeywordConstPtr keyword = deck->getKeyword("VFPPROD", i);
|
|
||||||
tables.push_back(Opm::VFPProdTable());
|
|
||||||
tables[i].init(keyword, units);
|
|
||||||
}
|
|
||||||
|
|
||||||
Opm::VFPProperties properties;
|
|
||||||
properties.init(tables);
|
|
||||||
|
|
||||||
//Do some rudimentary testing
|
//Do some rudimentary testing
|
||||||
//Get the BHP as a function of rate, thp, wfr, gfr, alq
|
//Get the BHP as a function of rate, thp, wfr, gfr, alq
|
||||||
@@ -771,7 +763,7 @@ BOOST_AUTO_TEST_CASE(ParseInterpolateRealisticVFPPROD)
|
|||||||
double a_i = 0.0;
|
double a_i = 0.0;
|
||||||
|
|
||||||
//Value given as pascal, convert to barsa for comparison with reference
|
//Value given as pascal, convert to barsa for comparison with reference
|
||||||
double value_i = properties.bhp(32, f_i, t_i, w_i, g_i, a_i) * 10.0e-6;
|
double value_i = properties.prod_bhp(32, f_i, t_i, w_i, g_i, a_i) * 10.0e-6;
|
||||||
|
|
||||||
double abs_diff = std::abs(value_i - reference[i]);
|
double abs_diff = std::abs(value_i - reference[i]);
|
||||||
sad += abs_diff;
|
sad += abs_diff;
|
||||||
@@ -792,8 +784,8 @@ BOOST_AUTO_TEST_CASE(ParseInterpolateRealisticVFPPROD)
|
|||||||
std::cout << "];" << std::endl;
|
std::cout << "];" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
BOOST_CHECK_SMALL(max_d, 1.0e-12);
|
BOOST_CHECK_SMALL(max_d, max_d_tol);
|
||||||
BOOST_CHECK_SMALL(sad, 1.0e-9);
|
BOOST_CHECK_SMALL(sad, sad_tol);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user