mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
#1901 Use the more robust WellLogExtraction system for perforation interval intersection calculations.
This commit is contained in:
@@ -179,7 +179,8 @@ void RicFishbonesTransmissibilityCalculationFeatureImp::findFishboneImportedLate
|
|||||||
for (const RimFishboneWellPath* fishbonesPath : wellPath->fishbonesCollection()->wellPathCollection()->wellPaths())
|
for (const RimFishboneWellPath* fishbonesPath : wellPath->fishbonesCollection()->wellPathCollection()->wellPaths())
|
||||||
{
|
{
|
||||||
std::vector<WellPathCellIntersectionInfo> intersectedCells = RigWellPathIntersectionTools::findCellsIntersectedByPath(settings.caseToApply->eclipseCaseData(),
|
std::vector<WellPathCellIntersectionInfo> intersectedCells = RigWellPathIntersectionTools::findCellsIntersectedByPath(settings.caseToApply->eclipseCaseData(),
|
||||||
fishbonesPath->coordinates());
|
fishbonesPath->coordinates(),
|
||||||
|
fishbonesPath->measuredDepths());
|
||||||
for (auto& cell : intersectedCells)
|
for (auto& cell : intersectedCells)
|
||||||
{
|
{
|
||||||
if (std::find(wellPathCells.begin(), wellPathCells.end(), cell.cellIndex) != wellPathCells.end()) continue;
|
if (std::find(wellPathCells.begin(), wellPathCells.end(), cell.cellIndex) != wellPathCells.end()) continue;
|
||||||
@@ -211,11 +212,12 @@ void RicFishbonesTransmissibilityCalculationFeatureImp::findMainWellBoreParts(st
|
|||||||
double wellPathEndMD = 0.0;
|
double wellPathEndMD = 0.0;
|
||||||
if (wellPathMD.size() > 1) wellPathEndMD = wellPathMD.back();
|
if (wellPathMD.size() > 1) wellPathEndMD = wellPathMD.back();
|
||||||
|
|
||||||
std::vector<cvf::Vec3d> fishbonePerfWellPathCoords = wellPath->wellPathGeometry()->clippedPointSubset(wellPath->fishbonesCollection()->startMD(),
|
std::pair< std::vector<cvf::Vec3d>, std::vector<double> > fishbonePerfWellPathCoords = wellPath->wellPathGeometry()->clippedPointSubset(wellPath->fishbonesCollection()->startMD(),
|
||||||
wellPathEndMD);
|
wellPathEndMD);
|
||||||
|
|
||||||
std::vector<WellPathCellIntersectionInfo> intersectedCellsIntersectionInfo = RigWellPathIntersectionTools::findCellsIntersectedByPath(settings.caseToApply->eclipseCaseData(),
|
std::vector<WellPathCellIntersectionInfo> intersectedCellsIntersectionInfo = RigWellPathIntersectionTools::findCellsIntersectedByPath(settings.caseToApply->eclipseCaseData(),
|
||||||
fishbonePerfWellPathCoords);
|
fishbonePerfWellPathCoords.first,
|
||||||
|
fishbonePerfWellPathCoords.second);
|
||||||
|
|
||||||
for (auto& cell : intersectedCellsIntersectionInfo)
|
for (auto& cell : intersectedCellsIntersectionInfo)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -716,9 +716,11 @@ std::vector<RigCompletionData> RicWellPathExportCompletionDataFeature::generateP
|
|||||||
{
|
{
|
||||||
if (!interval->isActiveOnDate(settings.caseToApply->timeStepDates()[settings.timeStep])) continue;
|
if (!interval->isActiveOnDate(settings.caseToApply->timeStepDates()[settings.timeStep])) continue;
|
||||||
|
|
||||||
std::vector<cvf::Vec3d> perforationPoints = wellPath->wellPathGeometry()->clippedPointSubset(interval->startMD(), interval->endMD());
|
using namespace std;
|
||||||
|
pair<vector<cvf::Vec3d>, vector<double> > perforationPointsAndMD = wellPath->wellPathGeometry()->clippedPointSubset(interval->startMD(), interval->endMD());
|
||||||
std::vector<WellPathCellIntersectionInfo> intersectedCells = RigWellPathIntersectionTools::findCellsIntersectedByPath(settings.caseToApply->eclipseCaseData(),
|
std::vector<WellPathCellIntersectionInfo> intersectedCells = RigWellPathIntersectionTools::findCellsIntersectedByPath(settings.caseToApply->eclipseCaseData(),
|
||||||
perforationPoints);
|
perforationPointsAndMD.first,
|
||||||
|
perforationPointsAndMD.second);
|
||||||
for (auto& cell : intersectedCells)
|
for (auto& cell : intersectedCells)
|
||||||
{
|
{
|
||||||
bool cellIsActive = activeCellInfo->isActive(cell.cellIndex);
|
bool cellIsActive = activeCellInfo->isActive(cell.cellIndex);
|
||||||
@@ -861,9 +863,24 @@ void RicWellPathExportCompletionDataFeature::calculateLateralIntersections(const
|
|||||||
for (WellSegmentLateral& lateral : location->laterals)
|
for (WellSegmentLateral& lateral : location->laterals)
|
||||||
{
|
{
|
||||||
lateral.branchNumber = ++(*branchNum);
|
lateral.branchNumber = ++(*branchNum);
|
||||||
std::vector<cvf::Vec3d> lateralCoords = location->fishbonesSubs->coordsForLateral(location->subIndex, lateral.lateralIndex);
|
|
||||||
|
std::vector<std::pair<cvf::Vec3d, double> > lateralCoordMDPairs = location->fishbonesSubs->coordsAndMDForLateral(location->subIndex, lateral.lateralIndex);
|
||||||
|
|
||||||
|
std::vector<cvf::Vec3d> lateralCoords;
|
||||||
|
std::vector<double> lateralMDs;
|
||||||
|
|
||||||
|
lateralCoords.reserve(lateralCoordMDPairs.size());
|
||||||
|
lateralMDs.reserve(lateralCoordMDPairs.size());
|
||||||
|
|
||||||
|
for (auto& coordMD : lateralCoordMDPairs)
|
||||||
|
{
|
||||||
|
lateralCoords.push_back(coordMD.first);
|
||||||
|
lateralMDs.push_back(coordMD.second);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<WellPathCellIntersectionInfo> intersections = RigWellPathIntersectionTools::findCellsIntersectedByPath(caseToApply->eclipseCaseData(),
|
std::vector<WellPathCellIntersectionInfo> intersections = RigWellPathIntersectionTools::findCellsIntersectedByPath(caseToApply->eclipseCaseData(),
|
||||||
lateralCoords);
|
lateralCoords,
|
||||||
|
lateralMDs);
|
||||||
|
|
||||||
auto intersection = intersections.cbegin();
|
auto intersection = intersections.cbegin();
|
||||||
double length = 0;
|
double length = 0;
|
||||||
|
|||||||
@@ -200,16 +200,18 @@ void RivWellPathPartMgr::appendPerforationsToModel(const QDateTime& currentViewD
|
|||||||
|
|
||||||
if (currentViewDate.isValid() && !perforation->isActiveOnDate(currentViewDate)) continue;
|
if (currentViewDate.isValid() && !perforation->isActiveOnDate(currentViewDate)) continue;
|
||||||
|
|
||||||
std::vector<cvf::Vec3d> displayCoords = wellPathGeometry->clippedPointSubset(perforation->startMD(), perforation->endMD());
|
using namespace std;
|
||||||
|
pair<vector<cvf::Vec3d>, vector<double> > displayCoordsAndMD = wellPathGeometry->clippedPointSubset(perforation->startMD(),
|
||||||
|
perforation->endMD());
|
||||||
|
|
||||||
if (displayCoords.size() < 2) continue;
|
if (displayCoordsAndMD.first.size() < 2) continue;
|
||||||
|
|
||||||
for (cvf::Vec3d& point : displayCoords) point = displayCoordTransform->transformToDisplayCoord(point);
|
for (cvf::Vec3d& point : displayCoordsAndMD.first) point = displayCoordTransform->transformToDisplayCoord(point);
|
||||||
|
|
||||||
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo(perforation);
|
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo(perforation);
|
||||||
|
|
||||||
cvf::Collection<cvf::Part> parts;
|
cvf::Collection<cvf::Part> parts;
|
||||||
geoGenerator.cylinderWithCenterLineParts(&parts, displayCoords, cvf::Color3f::GREEN, perforationRadius);
|
geoGenerator.cylinderWithCenterLineParts(&parts, displayCoordsAndMD.first, cvf::Color3f::GREEN, perforationRadius);
|
||||||
for (auto part : parts)
|
for (auto part : parts)
|
||||||
{
|
{
|
||||||
part->setSourceInfo(objectSourceInfo.p());
|
part->setSourceInfo(objectSourceInfo.p());
|
||||||
|
|||||||
@@ -148,9 +148,17 @@ void RimCompletionCellIntersectionCalc::calculateFishbonesIntersections(const Ri
|
|||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
///
|
///
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
void RimCompletionCellIntersectionCalc::calculatePerforationIntersections(const RimWellPath* wellPath, const RimPerforationInterval* perforationInterval, const RigMainGrid* grid, std::vector<double>& values)
|
void RimCompletionCellIntersectionCalc::calculatePerforationIntersections(const RimWellPath* wellPath,
|
||||||
|
const RimPerforationInterval* perforationInterval,
|
||||||
|
const RigMainGrid* grid,
|
||||||
|
std::vector<double>& values)
|
||||||
{
|
{
|
||||||
std::vector<HexIntersectionInfo> intersections = RigWellPathIntersectionTools::getIntersectedCells(grid, wellPath->wellPathGeometry()->clippedPointSubset(perforationInterval->startMD(), perforationInterval->endMD()));
|
using namespace std;
|
||||||
|
pair<vector<cvf::Vec3d>, vector<double> > clippedWellPathData = wellPath->wellPathGeometry()->clippedPointSubset(perforationInterval->startMD(),
|
||||||
|
perforationInterval->endMD());
|
||||||
|
|
||||||
|
std::vector<HexIntersectionInfo> intersections = RigWellPathIntersectionTools::getIntersectedCells(grid,
|
||||||
|
clippedWellPathData.first);
|
||||||
for (auto& intersection : intersections)
|
for (auto& intersection : intersections)
|
||||||
{
|
{
|
||||||
values[intersection.m_hexIndex] = RiaDefines::PERFORATION_INTERVAL;
|
values[intersection.m_hexIndex] = RiaDefines::PERFORATION_INTERVAL;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "RigWellLogExtractionTools.h"
|
#include "RigWellLogExtractionTools.h"
|
||||||
#include "RigMainGrid.h"
|
#include "RigMainGrid.h"
|
||||||
|
#include "RigWellPathIntersectionTools.h"
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
///
|
///
|
||||||
@@ -131,6 +132,31 @@ void RigEclipseWellLogExtractor::curveData(const RigResultAccessor* resultAccess
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
///
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
std::vector<WellPathCellIntersectionInfo> RigEclipseWellLogExtractor::cellIntersectionInfo()
|
||||||
|
{
|
||||||
|
std::vector<WellPathCellIntersectionInfo> cellIntersectionInfos;
|
||||||
|
cellIntersectionInfos.reserve(m_intersections.size()-1);
|
||||||
|
|
||||||
|
for (size_t cpIdx = 0; cpIdx < m_intersections.size()-1; ++cpIdx)
|
||||||
|
{
|
||||||
|
size_t cellIdx1 = m_intersectedCells[cpIdx];
|
||||||
|
size_t cellIdx2 = m_intersectedCells[cpIdx+1];
|
||||||
|
|
||||||
|
if (cellIdx1 == cellIdx2)
|
||||||
|
{
|
||||||
|
cvf::Vec3d internalCellLengths;
|
||||||
|
internalCellLengths = RigWellPathIntersectionTools::calculateLengthInCell( m_caseData->mainGrid(), cellIdx1, m_intersections[cpIdx], m_intersections[cpIdx+1] );
|
||||||
|
|
||||||
|
cellIntersectionInfos.push_back(WellPathCellIntersectionInfo(cellIdx1, m_intersections[cpIdx], m_intersections[cpIdx+1], internalCellLengths));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cellIntersectionInfos;
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
///
|
///
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "cvfStructGrid.h"
|
#include "cvfStructGrid.h"
|
||||||
|
#include "RigWellPathIntersectionTools.h"
|
||||||
|
|
||||||
class RigEclipseCaseData;
|
class RigEclipseCaseData;
|
||||||
class RigWellPath;
|
class RigWellPath;
|
||||||
@@ -48,6 +49,8 @@ public:
|
|||||||
void curveData(const RigResultAccessor* resultAccessor, std::vector<double>* values );
|
void curveData(const RigResultAccessor* resultAccessor, std::vector<double>* values );
|
||||||
const RigEclipseCaseData* caseData() { return m_caseData.p();}
|
const RigEclipseCaseData* caseData() { return m_caseData.p();}
|
||||||
|
|
||||||
|
std::vector<WellPathCellIntersectionInfo> cellIntersectionInfo();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void calculateIntersection();
|
void calculateIntersection();
|
||||||
std::vector<size_t> findCloseCells(const cvf::BoundingBox& bb);
|
std::vector<size_t> findCloseCells(const cvf::BoundingBox& bb);
|
||||||
|
|||||||
@@ -192,25 +192,29 @@ void RigWellPath::twoClosestPoints(const cvf::Vec3d& position, cvf::Vec3d* p1, c
|
|||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
///
|
///
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
std::vector<cvf::Vec3d> RigWellPath::clippedPointSubset(double startMD, double endMD) const
|
std::pair<std::vector<cvf::Vec3d>, std::vector<double> > RigWellPath::clippedPointSubset(double startMD, double endMD) const
|
||||||
{
|
{
|
||||||
std::vector<cvf::Vec3d> points;
|
std::pair<std::vector<cvf::Vec3d>, std::vector<double> > pointsAndMDs;
|
||||||
if (m_measuredDepths.empty()) return points;
|
if (m_measuredDepths.empty()) return pointsAndMDs;
|
||||||
if (startMD > endMD) return points;
|
if (startMD > endMD) return pointsAndMDs;
|
||||||
|
|
||||||
|
pointsAndMDs.first.push_back(interpolatedPointAlongWellPath(startMD));
|
||||||
|
pointsAndMDs.second.push_back(startMD);
|
||||||
|
|
||||||
points.push_back(interpolatedPointAlongWellPath(startMD));
|
|
||||||
for (size_t i = 0; i < m_measuredDepths.size(); ++i)
|
for (size_t i = 0; i < m_measuredDepths.size(); ++i)
|
||||||
{
|
{
|
||||||
double measuredDepth = m_measuredDepths[i];
|
double measuredDepth = m_measuredDepths[i];
|
||||||
if (measuredDepth > startMD && measuredDepth < endMD)
|
if (measuredDepth > startMD && measuredDepth < endMD)
|
||||||
{
|
{
|
||||||
points.push_back(m_wellPathPoints[i]);
|
pointsAndMDs.first.push_back(m_wellPathPoints[i]);
|
||||||
|
pointsAndMDs.second.push_back(measuredDepth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
points.push_back(interpolatedPointAlongWellPath(endMD));
|
pointsAndMDs.first.push_back(interpolatedPointAlongWellPath(endMD));
|
||||||
|
pointsAndMDs.second.push_back(endMD);
|
||||||
|
|
||||||
|
|
||||||
return points;
|
return pointsAndMDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ public:
|
|||||||
cvf::Vec3d interpolatedPointAlongWellPath(double measuredDepth) const;
|
cvf::Vec3d interpolatedPointAlongWellPath(double measuredDepth) const;
|
||||||
double wellPathAzimuthAngle(const cvf::Vec3d& position) const;
|
double wellPathAzimuthAngle(const cvf::Vec3d& position) const;
|
||||||
void twoClosestPoints(const cvf::Vec3d& position, cvf::Vec3d* p1, cvf::Vec3d* p2) const;
|
void twoClosestPoints(const cvf::Vec3d& position, cvf::Vec3d* p1, cvf::Vec3d* p2) const;
|
||||||
std::vector<cvf::Vec3d> clippedPointSubset(double startMD, double endMD) const;
|
|
||||||
|
std::pair<std::vector<cvf::Vec3d>, std::vector<double> >
|
||||||
|
clippedPointSubset(double startMD, double endMD) const;
|
||||||
|
|
||||||
std::vector<cvf::Vec3d> wellPathPointsIncludingFractureIntersection(double fractureIntersectionMD) const;
|
std::vector<cvf::Vec3d> wellPathPointsIncludingFractureIntersection(double fractureIntersectionMD) const;
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -28,68 +28,29 @@
|
|||||||
|
|
||||||
#include "cvfGeometryTools.h"
|
#include "cvfGeometryTools.h"
|
||||||
#include "cvfMatrix3.h"
|
#include "cvfMatrix3.h"
|
||||||
|
#include "RigEclipseWellLogExtractor.h"
|
||||||
|
#include "RimEclipseCase.h"
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
///
|
///
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
std::vector<WellPathCellIntersectionInfo> RigWellPathIntersectionTools::findCellsIntersectedByPath(const RigEclipseCaseData* caseData,
|
std::vector<WellPathCellIntersectionInfo> RigWellPathIntersectionTools::findCellsIntersectedByPath(const RigEclipseCaseData* caseData,
|
||||||
const std::vector<cvf::Vec3d>& pathCoords)
|
const std::vector<cvf::Vec3d>& pathCoords,
|
||||||
|
const std::vector<double>& pathMds)
|
||||||
{
|
{
|
||||||
std::vector<WellPathCellIntersectionInfo> intersectionInfos;
|
std::vector<WellPathCellIntersectionInfo> intersectionInfos;
|
||||||
const RigMainGrid* grid = caseData->mainGrid();
|
const RigMainGrid* grid = caseData->mainGrid();
|
||||||
|
|
||||||
if (pathCoords.size() < 2) return intersectionInfos;
|
if (pathCoords.size() < 2) return intersectionInfos;
|
||||||
|
cvf::ref<RigWellPath> dummyWellPath = new RigWellPath;
|
||||||
|
dummyWellPath->m_wellPathPoints = pathCoords;
|
||||||
|
dummyWellPath->m_measuredDepths = pathMds;
|
||||||
|
|
||||||
std::vector<HexIntersectionInfo> intersections = getIntersectedCells(grid, pathCoords);
|
cvf::ref<RigEclipseWellLogExtractor> extractor = new RigEclipseWellLogExtractor(caseData, dummyWellPath.p(), caseData->ownerCase()->caseUserDescription().toStdString());
|
||||||
removeEnteringIntersections(&intersections);
|
|
||||||
|
|
||||||
if (intersections.empty()) return intersectionInfos;
|
return extractor->cellIntersectionInfo();
|
||||||
|
|
||||||
cvf::Vec3d startPoint;
|
|
||||||
cvf::Vec3d endPoint;
|
|
||||||
size_t cellIndex;
|
|
||||||
cvf::Vec3d internalCellLengths;
|
|
||||||
|
|
||||||
auto intersection = intersections.cbegin();
|
|
||||||
|
|
||||||
//start cell
|
|
||||||
bool foundCell;
|
|
||||||
startPoint = pathCoords[0];
|
|
||||||
cellIndex = findCellFromCoords(grid, startPoint, &foundCell);
|
|
||||||
if (foundCell)
|
|
||||||
{
|
|
||||||
endPoint = intersection->m_intersectionPoint;
|
|
||||||
internalCellLengths = calculateLengthInCell(grid, cellIndex, startPoint, endPoint);
|
|
||||||
intersectionInfos.push_back(WellPathCellIntersectionInfo(cellIndex, startPoint, endPoint, internalCellLengths));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RiaLogging::debug("Path starts outside valid cell");
|
|
||||||
}
|
|
||||||
|
|
||||||
//center cells
|
|
||||||
startPoint = intersection->m_intersectionPoint;
|
|
||||||
cellIndex = intersection->m_hexIndex;
|
|
||||||
|
|
||||||
++intersection;
|
|
||||||
|
|
||||||
while (intersection != intersections.cend())
|
|
||||||
{
|
|
||||||
endPoint = intersection->m_intersectionPoint;
|
|
||||||
internalCellLengths = calculateLengthInCell(grid, cellIndex, startPoint, endPoint);
|
|
||||||
intersectionInfos.push_back(WellPathCellIntersectionInfo(cellIndex, startPoint, endPoint, internalCellLengths));
|
|
||||||
|
|
||||||
startPoint = endPoint;
|
|
||||||
cellIndex = intersection->m_hexIndex;
|
|
||||||
++intersection;
|
|
||||||
}
|
|
||||||
|
|
||||||
//end cell
|
|
||||||
endPoint = pathCoords[pathCoords.size() - 1];
|
|
||||||
internalCellLengths = calculateLengthInCell(grid, cellIndex, startPoint, endPoint);
|
|
||||||
intersectionInfos.push_back(WellPathCellIntersectionInfo(cellIndex, startPoint, endPoint, internalCellLengths));
|
|
||||||
|
|
||||||
return intersectionInfos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ struct WellPathCellIntersectionInfo {
|
|||||||
class RigWellPathIntersectionTools
|
class RigWellPathIntersectionTools
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::vector<WellPathCellIntersectionInfo> findCellsIntersectedByPath(const RigEclipseCaseData* caseData, const std::vector<cvf::Vec3d>& pathCoords);
|
static std::vector<WellPathCellIntersectionInfo> findCellsIntersectedByPath(const RigEclipseCaseData* caseData,
|
||||||
|
const std::vector<cvf::Vec3d>& pathCoords,
|
||||||
|
const std::vector<double>& pathMds);
|
||||||
|
|
||||||
static std::vector<HexIntersectionInfo> getIntersectedCells(const RigMainGrid* grid, const std::vector<cvf::Vec3d>& coords);
|
static std::vector<HexIntersectionInfo> getIntersectedCells(const RigMainGrid* grid, const std::vector<cvf::Vec3d>& coords);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user