mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
Fault Reactivation updates (#10727)
* Generate reservoir element sets * Add support for materials * Add local coordinate system support for exported model
This commit is contained in:
@@ -5,6 +5,7 @@ set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationModelCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationDataAccess.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationEnums.h
|
||||
)
|
||||
|
||||
set(SOURCE_GROUP_SOURCE_FILES
|
||||
|
||||
@@ -75,18 +75,31 @@ void RimFaultReactivationDataAccess::useCellIndexAdjustment( std::map<size_t, si
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimFaultReactivationDataAccess::porePressureAtPosition( const cvf::Vec3d& position, double defaultPorePressureGradient )
|
||||
size_t RimFaultReactivationDataAccess::findAdjustedCellIndex( const cvf::Vec3d& position,
|
||||
const RigMainGrid* grid,
|
||||
const std::map<size_t, size_t>& cellIndexAdjustmentMap )
|
||||
{
|
||||
CAF_ASSERT( grid != nullptr );
|
||||
|
||||
size_t cellIdx = grid->findReservoirCellIndexFromPoint( position );
|
||||
|
||||
// adjust cell index if present in the map
|
||||
if ( auto search = cellIndexAdjustmentMap.find( cellIdx ); search != cellIndexAdjustmentMap.end() )
|
||||
{
|
||||
cellIdx = search->second;
|
||||
}
|
||||
|
||||
return cellIdx;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimFaultReactivationDataAccess::porePressureAtPosition( const cvf::Vec3d& position, double defaultPorePressureGradient ) const
|
||||
{
|
||||
size_t cellIdx = cvf::UNDEFINED_SIZE_T;
|
||||
if ( ( m_mainGrid != nullptr ) && m_resultAccessor.notNull() )
|
||||
{
|
||||
cellIdx = m_mainGrid->findReservoirCellIndexFromPoint( position );
|
||||
|
||||
// adjust cell index to be on correct side of fault
|
||||
if ( auto search = m_cellIndexAdjustment.find( cellIdx ); search != m_cellIndexAdjustment.end() )
|
||||
{
|
||||
cellIdx = search->second;
|
||||
}
|
||||
auto cellIdx = findAdjustedCellIndex( position, m_mainGrid, m_cellIndexAdjustment );
|
||||
|
||||
if ( ( cellIdx != cvf::UNDEFINED_SIZE_T ) )
|
||||
{
|
||||
@@ -106,7 +119,7 @@ double RimFaultReactivationDataAccess::porePressureAtPosition( const cvf::Vec3d&
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimFaultReactivationDataAccess::calculatePorePressure( double depth, double gradient )
|
||||
{
|
||||
return gradient * 9.81 * depth * 1000;
|
||||
return gradient * 9.81 * depth * 1000.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -116,3 +129,27 @@ size_t RimFaultReactivationDataAccess::timeStepIndex() const
|
||||
{
|
||||
return m_timeStepIndex;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimFaultReactivationDataAccess::elementHasValidData( std::vector<cvf::Vec3d> elementCorners ) const
|
||||
{
|
||||
int nValid = 0;
|
||||
for ( auto& p : elementCorners )
|
||||
{
|
||||
auto cellIdx = findAdjustedCellIndex( p, m_mainGrid, m_cellIndexAdjustment );
|
||||
|
||||
if ( ( cellIdx != cvf::UNDEFINED_SIZE_T ) )
|
||||
{
|
||||
double value = m_resultAccessor->cellScalar( cellIdx );
|
||||
if ( !std::isinf( value ) )
|
||||
{
|
||||
nValid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if more than half of the nodes have valid data, we're ok
|
||||
return nValid > 4;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class RimEclipseCase;
|
||||
class RigEclipseCaseData;
|
||||
@@ -40,12 +41,17 @@ public:
|
||||
|
||||
void useCellIndexAdjustment( std::map<size_t, size_t> adjustments );
|
||||
|
||||
double porePressureAtPosition( const cvf::Vec3d& position, double defaultPorePressureGradient );
|
||||
double porePressureAtPosition( const cvf::Vec3d& position, double defaultPorePressureGradient ) const;
|
||||
|
||||
size_t timeStepIndex() const;
|
||||
|
||||
static size_t
|
||||
findAdjustedCellIndex( const cvf::Vec3d& position, const RigMainGrid* grid, const std::map<size_t, size_t>& cellIndexAdjustmentMap );
|
||||
|
||||
bool elementHasValidData( std::vector<cvf::Vec3d> elementCorners ) const;
|
||||
|
||||
protected:
|
||||
double calculatePorePressure( double depth, double gradient );
|
||||
static double calculatePorePressure( double depth, double gradient );
|
||||
|
||||
private:
|
||||
RimEclipseCase* m_case;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2023 Equinor ASA
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace RimFaultReactivation
|
||||
{
|
||||
|
||||
enum class ModelParts
|
||||
{
|
||||
HiPart1,
|
||||
MidPart1,
|
||||
LowPart1,
|
||||
HiPart2,
|
||||
MidPart2,
|
||||
LowPart2
|
||||
};
|
||||
|
||||
enum class GridPart
|
||||
{
|
||||
PART1,
|
||||
PART2
|
||||
};
|
||||
|
||||
enum class BorderSurface
|
||||
{
|
||||
UpperSurface,
|
||||
FaultSurface,
|
||||
LowerSurface
|
||||
};
|
||||
|
||||
enum class Boundary
|
||||
{
|
||||
FarSide,
|
||||
Bottom
|
||||
};
|
||||
|
||||
enum class ElementSets
|
||||
{
|
||||
OverBurden,
|
||||
UnderBurden,
|
||||
Reservoir,
|
||||
IntraReservoir
|
||||
};
|
||||
|
||||
} // namespace RimFaultReactivation
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "RiaQDateTimeTools.h"
|
||||
|
||||
#include "RifJsonEncodeDecode.h"
|
||||
#include "RifParameterXmlReader.h"
|
||||
|
||||
#include "RigBasicPlane.h"
|
||||
#include "RigFaultReactivationModel.h"
|
||||
@@ -37,12 +38,14 @@
|
||||
#include "RivFaultReactivationModelPartMgr.h"
|
||||
|
||||
#include "Rim3dView.h"
|
||||
#include "RimDoubleParameter.h"
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimEclipseView.h"
|
||||
#include "RimFaultInView.h"
|
||||
#include "RimFaultInViewCollection.h"
|
||||
#include "RimFaultReactivationDataAccess.h"
|
||||
#include "RimFaultReactivationTools.h"
|
||||
#include "RimParameterGroup.h"
|
||||
#include "RimPolylineTarget.h"
|
||||
#include "RimTimeStepFilter.h"
|
||||
#include "RimTools.h"
|
||||
@@ -104,6 +107,8 @@ RimFaultReactivationModel::RimFaultReactivationModel()
|
||||
CAF_PDM_InitField( &m_numberOfCellsVertMid, "NumberOfCellsVertMid", 20, "Vertical Number of Cells, Middle Part" );
|
||||
CAF_PDM_InitField( &m_numberOfCellsVertLow, "NumberOfCellsVertLow", 20, "Vertical Number of Cells, Lower Part" );
|
||||
|
||||
CAF_PDM_InitField( &m_useLocalCoordinates, "UseLocalCoordinates", false, "Export Using Local Coordinates" );
|
||||
|
||||
// Time Step Selection
|
||||
CAF_PDM_InitFieldNoDefault( &m_timeStepFilter, "TimeStepFilter", "Available Time Steps" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_selectedTimeSteps, "TimeSteps", "Select Time Steps" );
|
||||
@@ -113,11 +118,13 @@ RimFaultReactivationModel::RimFaultReactivationModel()
|
||||
CAF_PDM_InitFieldNoDefault( &m_targets, "Targets", "Targets" );
|
||||
m_targets.uiCapability()->setUiEditorTypeName( caf::PdmUiTableViewEditor::uiEditorTypeName() );
|
||||
m_targets.uiCapability()->setUiTreeChildrenHidden( true );
|
||||
m_targets.uiCapability()->setUiTreeHidden( true );
|
||||
m_targets.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );
|
||||
m_targets.uiCapability()->setCustomContextMenuEnabled( false );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_materialParameters, "MaterialParameters", "Materials", ":/Bullet.png" );
|
||||
|
||||
this->setUi3dEditorTypeName( RicPolyline3dEditor::uiEditorTypeName() );
|
||||
this->uiCapability()->setUiTreeChildrenHidden( true );
|
||||
|
||||
setDeletable( true );
|
||||
|
||||
@@ -140,6 +147,23 @@ void RimFaultReactivationModel::initAfterRead()
|
||||
updateVisualization();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimFaultReactivationModel::initSettings( QString& outErrmsg )
|
||||
{
|
||||
RifParameterXmlReader basicreader( RiaPreferencesGeoMech::current()->geomechFRMDefaultXML() );
|
||||
if ( !basicreader.parseFile( outErrmsg ) ) return false;
|
||||
|
||||
m_materialParameters.deleteChildren();
|
||||
for ( auto group : basicreader.parameterGroups() )
|
||||
{
|
||||
m_materialParameters.push_back( group );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -297,7 +321,19 @@ void RimFaultReactivationModel::updateVisualization()
|
||||
m_numberOfCellsVertLow );
|
||||
m_modelPlane->setThickness( m_modelThickness );
|
||||
|
||||
m_modelPlane->updateRects();
|
||||
// set up transform to local coordinate system
|
||||
{
|
||||
auto [xVec, yVec] = localCoordSysNormalsXY();
|
||||
cvf::Mat4d transform = cvf::Mat4d::fromCoordSystemAxes( &xVec, &yVec, &cvf::Vec3d::Z_AXIS );
|
||||
cvf::Vec3d center = m_targets[0]->targetPointXYZ() * -1.0;
|
||||
center.z() = 0.0;
|
||||
center.transformPoint( transform );
|
||||
transform.setTranslation( center );
|
||||
m_modelPlane->setLocalCoordTransformation( transform );
|
||||
m_modelPlane->setUseLocalCoordinates( m_useLocalCoordinates );
|
||||
}
|
||||
|
||||
m_modelPlane->updateGeometry();
|
||||
|
||||
view->scheduleCreateDisplayModelAndRedraw();
|
||||
}
|
||||
@@ -401,6 +437,13 @@ std::pair<cvf::Vec3d, cvf::Vec3d> RimFaultReactivationModel::localCoordSysNormal
|
||||
cvf::Vec3d yNormal = m_modelPlane->normal();
|
||||
cvf::Vec3d xNormal = yNormal ^ cvf::Vec3d::Z_AXIS;
|
||||
|
||||
xNormal.z() = 0.0;
|
||||
yNormal.z() = 0.0;
|
||||
xNormal.normalize();
|
||||
yNormal.normalize();
|
||||
|
||||
yNormal = xNormal ^ cvf::Vec3d::Z_AXIS;
|
||||
|
||||
return std::make_pair( xNormal, yNormal );
|
||||
}
|
||||
|
||||
@@ -438,6 +481,7 @@ void RimFaultReactivationModel::defineUiOrdering( QString uiConfigName, caf::Pdm
|
||||
gridModelGrp->add( &m_numberOfCellsVertUp );
|
||||
gridModelGrp->add( &m_numberOfCellsVertMid );
|
||||
gridModelGrp->add( &m_numberOfCellsVertLow );
|
||||
gridModelGrp->add( &m_useLocalCoordinates );
|
||||
|
||||
auto timeStepGrp = uiOrdering.addNewGroup( "Time Steps" );
|
||||
timeStepGrp->add( &m_timeStepFilter );
|
||||
@@ -560,16 +604,6 @@ std::vector<QDateTime> RimFaultReactivationModel::selectedTimeSteps() const
|
||||
return dates;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimFaultReactivationModel::isFirstTimeStepsSelected() const
|
||||
{
|
||||
if ( m_availableTimeSteps.empty() || selectedTimeSteps().empty() ) return false;
|
||||
|
||||
return m_availableTimeSteps.front() == selectedTimeSteps().front();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -660,6 +694,7 @@ bool RimFaultReactivationModel::extractAndExportModelData()
|
||||
if ( !exportModelSettings() ) return false;
|
||||
|
||||
auto eCase = eclipseCase();
|
||||
if ( eCase == nullptr ) return false;
|
||||
|
||||
// get the selected time step indexes
|
||||
std::vector<size_t> selectedTimeStepIndexes;
|
||||
@@ -671,6 +706,17 @@ bool RimFaultReactivationModel::extractAndExportModelData()
|
||||
selectedTimeStepIndexes.push_back( idx - m_availableTimeSteps.begin() );
|
||||
}
|
||||
|
||||
auto grid = eCase->mainGrid();
|
||||
|
||||
// generate cell index mappings for cells that ends up at the wrong side of the fault
|
||||
model()->generateCellIndexMapping( grid );
|
||||
|
||||
// generate element sets for the various data parts of the model
|
||||
{
|
||||
RimFaultReactivationDataAccess dataAccess( eCase, 0 );
|
||||
model()->generateElementSets( &dataAccess, grid );
|
||||
}
|
||||
|
||||
// extract data for each timestep
|
||||
size_t outputTimeStepIndex = 0;
|
||||
for ( auto timeStepIdx : selectedTimeStepIndexes )
|
||||
@@ -681,3 +727,30 @@ bool RimFaultReactivationModel::extractAndExportModelData()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::array<double, 3> RimFaultReactivationModel::materialParameters( ElementSets elementSet )
|
||||
{
|
||||
std::array<double, 3> retVal = { 0.0, 0.0, 0.0 };
|
||||
static std::map<ElementSets, std::string> groupMap = { { ElementSets::OverBurden, "material_overburden" },
|
||||
{ ElementSets::Reservoir, "material_reservoir" },
|
||||
{ ElementSets::IntraReservoir, "material_intrareservoir" },
|
||||
{ ElementSets::UnderBurden, "material_underburden" } };
|
||||
|
||||
auto keyName = QString::fromStdString( groupMap[elementSet] );
|
||||
|
||||
for ( auto& grp : m_materialParameters )
|
||||
{
|
||||
if ( grp->name() != keyName ) continue;
|
||||
|
||||
retVal[0] = grp->parameterDoubleValue( "youngs_modulus", 0.0 );
|
||||
retVal[1] = grp->parameterDoubleValue( "poissons_number", 0.0 );
|
||||
retVal[2] = grp->parameterDoubleValue( "density", 0.0 );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "RimCheckableNamedObject.h"
|
||||
#include "RimFaultReactivationEnums.h"
|
||||
#include "RimPolylinePickerInterface.h"
|
||||
#include "RimPolylinesDataInterface.h"
|
||||
#include "RimTimeStepFilter.h"
|
||||
@@ -42,6 +43,7 @@
|
||||
class RicPolylineTargetsPickEventHandler;
|
||||
class RimEclipseCase;
|
||||
class RimFaultInView;
|
||||
class RimParameterGroup;
|
||||
class RimPolylineTarget;
|
||||
class RimTimeStepFilter;
|
||||
class RivFaultReactivationModelPartMgr;
|
||||
@@ -59,11 +61,14 @@ class RimFaultReactivationModel : public RimCheckableNamedObject, public RimPoly
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
using TimeStepFilterEnum = caf::AppEnum<RimTimeStepFilter::TimeStepFilterTypeEnum>;
|
||||
using ElementSets = RimFaultReactivation::ElementSets;
|
||||
|
||||
public:
|
||||
RimFaultReactivationModel();
|
||||
~RimFaultReactivationModel() override;
|
||||
|
||||
bool initSettings( QString& outErrmsg );
|
||||
|
||||
QString userDescription();
|
||||
void setUserDescription( QString description );
|
||||
|
||||
@@ -102,7 +107,8 @@ public:
|
||||
void setBaseDir( QString path );
|
||||
|
||||
std::vector<QDateTime> selectedTimeSteps() const;
|
||||
bool isFirstTimeStepsSelected() const;
|
||||
|
||||
std::array<double, 3> materialParameters( ElementSets elementSet );
|
||||
|
||||
QStringList commandParameters() const;
|
||||
|
||||
@@ -157,6 +163,7 @@ private:
|
||||
caf::PdmField<int> m_numberOfCellsVertUp;
|
||||
caf::PdmField<int> m_numberOfCellsVertMid;
|
||||
caf::PdmField<int> m_numberOfCellsVertLow;
|
||||
caf::PdmField<bool> m_useLocalCoordinates;
|
||||
|
||||
cvf::ref<RigBasicPlane> m_faultPlane;
|
||||
cvf::ref<RigFaultReactivationModel> m_modelPlane;
|
||||
@@ -164,5 +171,7 @@ private:
|
||||
caf::PdmField<TimeStepFilterEnum> m_timeStepFilter;
|
||||
caf::PdmField<std::vector<QDateTime>> m_selectedTimeSteps;
|
||||
|
||||
caf::PdmChildArrayField<RimParameterGroup*> m_materialParameters;
|
||||
|
||||
std::vector<QDateTime> m_availableTimeSteps;
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ RimFaultReactivationModelCollection::RimFaultReactivationModelCollection()
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_models, "FaultReactivationModels", "Models" );
|
||||
m_models.uiCapability()->setUiTreeHidden( true );
|
||||
m_models.uiCapability()->setUiHidden( true );
|
||||
|
||||
setName( "Fault Reactivation Models" );
|
||||
}
|
||||
@@ -60,8 +61,11 @@ RimFaultReactivationModelCollection::~RimFaultReactivationModelCollection()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimFaultReactivationModel*
|
||||
RimFaultReactivationModelCollection::addNewModel( RimFaultInView* fault, cvf::Vec3d target1, cvf::Vec3d target2, QString baseDir )
|
||||
RimFaultReactivationModel* RimFaultReactivationModelCollection::addNewModel( RimFaultInView* fault,
|
||||
cvf::Vec3d target1,
|
||||
cvf::Vec3d target2,
|
||||
QString baseDir,
|
||||
QString& outErrMsg )
|
||||
{
|
||||
auto newModel = new RimFaultReactivationModel();
|
||||
newModel->setFault( fault );
|
||||
@@ -69,11 +73,20 @@ RimFaultReactivationModel*
|
||||
newModel->setUserDescription( fault->name() );
|
||||
newModel->setTargets( target1, target2 );
|
||||
|
||||
QString errmsg;
|
||||
if ( !newModel->initSettings( errmsg ) )
|
||||
{
|
||||
delete newModel;
|
||||
outErrMsg = "Unable to load default parameters from the Fault Reactivation Model default parameter XML file:\n" + errmsg;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_models.push_back( newModel );
|
||||
|
||||
updateConnectedEditors();
|
||||
|
||||
newModel->updateVisualization();
|
||||
|
||||
// updateView();
|
||||
return newModel;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
RimFaultReactivationModelCollection();
|
||||
~RimFaultReactivationModelCollection() override;
|
||||
|
||||
RimFaultReactivationModel* addNewModel( RimFaultInView* fault, cvf::Vec3d target1, cvf::Vec3d target2, QString baseDir );
|
||||
RimFaultReactivationModel* addNewModel( RimFaultInView* fault, cvf::Vec3d target1, cvf::Vec3d target2, QString baseDir, QString& errMsg );
|
||||
|
||||
bool empty();
|
||||
int size();
|
||||
|
||||
@@ -345,3 +345,13 @@ QVariant RimParameterGroup::parameterValue( QString name ) const
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimParameterGroup::parameterDoubleValue( QString name, double defaultValue ) const
|
||||
{
|
||||
RimDoubleParameter* p = dynamic_cast<RimDoubleParameter*>( parameter( name ) );
|
||||
if ( p == nullptr ) return defaultValue;
|
||||
return p->value();
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
|
||||
RimGenericParameter* parameter( QString name ) const;
|
||||
QVariant parameterValue( QString name ) const;
|
||||
double parameterDoubleValue( QString name, double defaultValue ) const;
|
||||
|
||||
private:
|
||||
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
|
||||
|
||||
Reference in New Issue
Block a user