mirror of
https://git.salome-platform.org/gitpub/modules/smesh.git
synced 2024-12-25 08:50:35 +05:00
Add “Grading” parameter to Adaptive 1D hypothesis
This commit is contained in:
parent
1c3a10f810
commit
80fe1ddefc
@ -19,12 +19,14 @@ shape = geompy.MakeCut( shape, tool, theName="shape" )
|
|||||||
# Parameters of Adaptive hypothesis. minSize and maxSize are such that they do not limit
|
# Parameters of Adaptive hypothesis. minSize and maxSize are such that they do not limit
|
||||||
# size of segments because size of geometrical features lies within [2.-100.] range, hence
|
# size of segments because size of geometrical features lies within [2.-100.] range, hence
|
||||||
# size of segments is defined by deflection parameter and size of geometrical features only.
|
# size of segments is defined by deflection parameter and size of geometrical features only.
|
||||||
|
# grading is defined how much size of adjacent elements can differ.
|
||||||
minSize = 0.1
|
minSize = 0.1
|
||||||
maxSize = 200
|
maxSize = 200
|
||||||
deflection = 0.05
|
deflection = 0.05
|
||||||
|
grading = 0.7
|
||||||
|
|
||||||
mesh = smesh.Mesh( shape )
|
mesh = smesh.Mesh( shape )
|
||||||
mesh.Segment().Adaptive( minSize, maxSize, deflection )
|
mesh.Segment().Adaptive( minSize, maxSize, deflection, grading )
|
||||||
mesh.Triangle().MaxElementArea( 300 )
|
mesh.Triangle().MaxElementArea( 300 )
|
||||||
mesh.Compute()
|
mesh.Compute()
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 14 KiB |
@ -31,8 +31,8 @@ creation of narrow 2D elements.
|
|||||||
|
|
||||||
- <b>Min size</b> parameter limits the minimal segment size.
|
- <b>Min size</b> parameter limits the minimal segment size.
|
||||||
- <b>Max size</b> parameter defines the length of segments on straight edges.
|
- <b>Max size</b> parameter defines the length of segments on straight edges.
|
||||||
- \b Deflection parameter gives maximal distance of a segment from a curved edge.
|
- <b>Deflection</b> parameter gives maximal distance of a segment from a curved edge.
|
||||||
|
- <b>Grading</b> parameter defines how much size of adjacent elements can differ.
|
||||||
\image html adaptive1d_sample_mesh.png "Adaptive hypothesis and Netgen 2D algorithm - the size of mesh segments reflects the size of geometrical features"
|
\image html adaptive1d_sample_mesh.png "Adaptive hypothesis and Netgen 2D algorithm - the size of mesh segments reflects the size of geometrical features"
|
||||||
|
|
||||||
<b>See Also</b> a \ref tui_1d_adaptive "sample TUI Script" that uses Adaptive hypothesis.
|
<b>See Also</b> a \ref tui_1d_adaptive "sample TUI Script" that uses Adaptive hypothesis.
|
||||||
|
@ -433,6 +433,13 @@ module StdMeshers
|
|||||||
*/
|
*/
|
||||||
void SetDeflection(in double deflection) raises (SALOME::SALOME_Exception);
|
void SetDeflection(in double deflection) raises (SALOME::SALOME_Exception);
|
||||||
double GetDeflection();
|
double GetDeflection();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Sets <grading> parameter value,
|
||||||
|
* i.e. how much size of adjacent elements can differ
|
||||||
|
*/
|
||||||
|
void SetGrading(in double grading) raises (SALOME::SALOME_Exception);
|
||||||
|
double GetGrading();
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -236,7 +236,7 @@
|
|||||||
<hypo>GeometricProgression=GeometricProgression(SetStartLength(),SetCommonRatio(),SetReversedEdges())</hypo>
|
<hypo>GeometricProgression=GeometricProgression(SetStartLength(),SetCommonRatio(),SetReversedEdges())</hypo>
|
||||||
<hypo>StartEndLength=StartEndLength(SetStartLength(),SetEndLength(),SetReversedEdges())</hypo>
|
<hypo>StartEndLength=StartEndLength(SetStartLength(),SetEndLength(),SetReversedEdges())</hypo>
|
||||||
<hypo>Deflection1D=Deflection1D(SetDeflection())</hypo>
|
<hypo>Deflection1D=Deflection1D(SetDeflection())</hypo>
|
||||||
<hypo>Adaptive1D=Adaptive(SetMinSize(),SetMaxSize(),SetDeflection())</hypo>
|
<hypo>Adaptive1D=Adaptive(SetMinSize(),SetMaxSize(),SetDeflection(),SetGrading())</hypo>
|
||||||
<hypo>AutomaticLength=AutomaticLength(SetFineness())</hypo>
|
<hypo>AutomaticLength=AutomaticLength(SetFineness())</hypo>
|
||||||
<hypo>FixedPoints1D=FixedPoints1D(SetPoints(),SetNbSegments(),SetReversedEdges())</hypo>
|
<hypo>FixedPoints1D=FixedPoints1D(SetPoints(),SetNbSegments(),SetReversedEdges())</hypo>
|
||||||
<hypo>Propagation=Propagation()</hypo>
|
<hypo>Propagation=Propagation()</hypo>
|
||||||
@ -262,7 +262,7 @@
|
|||||||
<hypo>GeometricProgression=GeometricProgression(SetStartLength(),SetCommonRatio(),SetReversedEdges())</hypo>
|
<hypo>GeometricProgression=GeometricProgression(SetStartLength(),SetCommonRatio(),SetReversedEdges())</hypo>
|
||||||
<hypo>StartEndLength=StartEndLength(SetStartLength(),SetEndLength(),SetReversedEdges())</hypo>
|
<hypo>StartEndLength=StartEndLength(SetStartLength(),SetEndLength(),SetReversedEdges())</hypo>
|
||||||
<hypo>Deflection1D=Deflection1D(SetDeflection())</hypo>
|
<hypo>Deflection1D=Deflection1D(SetDeflection())</hypo>
|
||||||
<hypo>Adaptive1D=Adaptive(SetMinSize(),SetMaxSize(),SetDeflection())</hypo>
|
<hypo>Adaptive1D=Adaptive(SetMinSize(),SetMaxSize(),SetDeflection(),SetGrading())</hypo>
|
||||||
<hypo>AutomaticLength=AutomaticLength(SetFineness())</hypo>
|
<hypo>AutomaticLength=AutomaticLength(SetFineness())</hypo>
|
||||||
<hypo>FixedPoints1D=FixedPoints1D(SetPoints(),SetNbSegments(),SetReversedEdges())</hypo>
|
<hypo>FixedPoints1D=FixedPoints1D(SetPoints(),SetNbSegments(),SetReversedEdges())</hypo>
|
||||||
<hypo>Propagation=Propagation()</hypo>
|
<hypo>Propagation=Propagation()</hypo>
|
||||||
|
@ -183,20 +183,23 @@ class StdMeshersBuilder_Segment(Mesh_Algorithm):
|
|||||||
# @param minSize defines the minimal allowed segment length
|
# @param minSize defines the minimal allowed segment length
|
||||||
# @param maxSize defines the maximal allowed segment length
|
# @param maxSize defines the maximal allowed segment length
|
||||||
# @param deflection defines the maximal allowed distance from a segment to an edge
|
# @param deflection defines the maximal allowed distance from a segment to an edge
|
||||||
|
# @param grading defines how much size of adjacent elements can differ
|
||||||
# @param UseExisting if ==true - searches for an existing hypothesis created with
|
# @param UseExisting if ==true - searches for an existing hypothesis created with
|
||||||
# the same parameters, else (default) - creates a new one
|
# the same parameters, else (default) - creates a new one
|
||||||
# @return an instance of StdMeshers_Adaptive1D hypothesis
|
# @return an instance of StdMeshers_Adaptive1D hypothesis
|
||||||
# @ingroup l3_hypos_1dhyps
|
# @ingroup l3_hypos_1dhyps
|
||||||
def Adaptive(self, minSize, maxSize, deflection, UseExisting=False):
|
def Adaptive(self, minSize, maxSize, deflection, grading, UseExisting=False):
|
||||||
from salome.smesh.smeshBuilder import IsEqual
|
from salome.smesh.smeshBuilder import IsEqual
|
||||||
compFun = lambda hyp, args: ( IsEqual(hyp.GetMinSize(), args[0]) and \
|
compFun = lambda hyp, args: ( IsEqual(hyp.GetMinSize(), args[0]) and \
|
||||||
IsEqual(hyp.GetMaxSize(), args[1]) and \
|
IsEqual(hyp.GetMaxSize(), args[1]) and \
|
||||||
IsEqual(hyp.GetDeflection(), args[2]))
|
IsEqual(hyp.GetDeflection(), args[2]) and \
|
||||||
hyp = self.Hypothesis("Adaptive1D", [minSize, maxSize, deflection],
|
IsEqual(hyp.GetGrading(), args[3]))
|
||||||
|
hyp = self.Hypothesis("Adaptive1D", [minSize, maxSize, deflection, grading],
|
||||||
UseExisting=UseExisting, CompareMethod=compFun)
|
UseExisting=UseExisting, CompareMethod=compFun)
|
||||||
hyp.SetMinSize(minSize)
|
hyp.SetMinSize(minSize)
|
||||||
hyp.SetMaxSize(maxSize)
|
hyp.SetMaxSize(maxSize)
|
||||||
hyp.SetDeflection(deflection)
|
hyp.SetDeflection(deflection)
|
||||||
|
hyp.SetGrading(grading)
|
||||||
return hyp
|
return hyp
|
||||||
|
|
||||||
## Defines "Arithmetic1D" hypothesis to cut an edge in several segments with a length
|
## Defines "Arithmetic1D" hypothesis to cut an edge in several segments with a length
|
||||||
|
@ -942,6 +942,7 @@ StdMeshers_Adaptive1D::StdMeshers_Adaptive1D(int hypId,
|
|||||||
myMinSize = 1e-10;
|
myMinSize = 1e-10;
|
||||||
myMaxSize = 1e+10;
|
myMaxSize = 1e+10;
|
||||||
myDeflection = 1e-2;
|
myDeflection = 1e-2;
|
||||||
|
myGrading = 1e-2;
|
||||||
myAlgo = NULL;
|
myAlgo = NULL;
|
||||||
_name = "Adaptive1D";
|
_name = "Adaptive1D";
|
||||||
_param_algo_dim = 1; // is used by SMESH_Regular_1D
|
_param_algo_dim = 1; // is used by SMESH_Regular_1D
|
||||||
@ -968,6 +969,20 @@ void StdMeshers_Adaptive1D::SetDeflection(double value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//=======================================================================
|
//=======================================================================
|
||||||
|
//function : SetGrading
|
||||||
|
//purpose :
|
||||||
|
void StdMeshers_Adaptive1D::SetGrading(double value)
|
||||||
|
throw(SALOME_Exception)
|
||||||
|
{
|
||||||
|
if (value <= std::numeric_limits<double>::min() )
|
||||||
|
throw SALOME_Exception("Grading must be greater that zero");
|
||||||
|
if (myGrading != value)
|
||||||
|
{
|
||||||
|
myGrading = value;
|
||||||
|
NotifySubMeshesHypothesisModification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//=======================================================================
|
||||||
//function : SetMinSize
|
//function : SetMinSize
|
||||||
//purpose : Sets minimal allowed segment length
|
//purpose : Sets minimal allowed segment length
|
||||||
void StdMeshers_Adaptive1D::SetMinSize(double minSize)
|
void StdMeshers_Adaptive1D::SetMinSize(double minSize)
|
||||||
@ -1002,7 +1017,7 @@ void StdMeshers_Adaptive1D::SetMaxSize(double maxSize)
|
|||||||
//purpose : Persistence
|
//purpose : Persistence
|
||||||
ostream & StdMeshers_Adaptive1D::SaveTo(ostream & save)
|
ostream & StdMeshers_Adaptive1D::SaveTo(ostream & save)
|
||||||
{
|
{
|
||||||
save << myMinSize << " " << myMaxSize << " " << myDeflection;
|
save << myMinSize << " " << myMaxSize << " " << myDeflection << " " << myGrading;
|
||||||
save << " " << -1 << " " << -1; // preview addition of parameters
|
save << " " << -1 << " " << -1; // preview addition of parameters
|
||||||
return save;
|
return save;
|
||||||
}
|
}
|
||||||
@ -1012,7 +1027,7 @@ ostream & StdMeshers_Adaptive1D::SaveTo(ostream & save)
|
|||||||
istream & StdMeshers_Adaptive1D::LoadFrom(istream & load)
|
istream & StdMeshers_Adaptive1D::LoadFrom(istream & load)
|
||||||
{
|
{
|
||||||
int dummyParam;
|
int dummyParam;
|
||||||
bool isOK = (load >> myMinSize >> myMaxSize >> myDeflection >> dummyParam >> dummyParam);
|
bool isOK = (load >> myMinSize >> myMaxSize >> myDeflection >> myGrading >> dummyParam >> dummyParam);
|
||||||
if (!isOK)
|
if (!isOK)
|
||||||
load.clear(ios::badbit | load.rdstate());
|
load.clear(ios::badbit | load.rdstate());
|
||||||
return load;
|
return load;
|
||||||
@ -1082,6 +1097,7 @@ bool StdMeshers_Adaptive1D::SetParametersByDefaults(const TDefaults& dflts,
|
|||||||
myMinSize = dflts._elemLength / 10;
|
myMinSize = dflts._elemLength / 10;
|
||||||
myMaxSize = dflts._elemLength * 2;
|
myMaxSize = dflts._elemLength * 2;
|
||||||
myDeflection = myMinSize / 7;
|
myDeflection = myMinSize / 7;
|
||||||
|
myGrading = 0.7;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1145,7 +1161,7 @@ bool AdaptiveAlgo::Compute(SMESH_Mesh & theMesh,
|
|||||||
|
|
||||||
myMesh = &theMesh;
|
myMesh = &theMesh;
|
||||||
SMESH_MesherHelper helper( theMesh );
|
SMESH_MesherHelper helper( theMesh );
|
||||||
const double grading = 0.7;
|
const double grading = myHyp->GetGrading();
|
||||||
|
|
||||||
TopTools_IndexedMapOfShape edgeMap, faceMap;
|
TopTools_IndexedMapOfShape edgeMap, faceMap;
|
||||||
TopExp::MapShapes( theShape, TopAbs_EDGE, edgeMap );
|
TopExp::MapShapes( theShape, TopAbs_EDGE, edgeMap );
|
||||||
|
@ -59,6 +59,13 @@ class STDMESHERS_EXPORT StdMeshers_Adaptive1D : public SMESH_Hypothesis
|
|||||||
void SetDeflection(double value) throw(SALOME_Exception);
|
void SetDeflection(double value) throw(SALOME_Exception);
|
||||||
double GetDeflection() const { return myDeflection; }
|
double GetDeflection() const { return myDeflection; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Sets <grading> parameter value,
|
||||||
|
* i.e. how much size of adjacent elements can differ
|
||||||
|
*/
|
||||||
|
void SetGrading(double value) throw(SALOME_Exception);
|
||||||
|
double GetGrading() const { return myGrading; }
|
||||||
|
|
||||||
virtual std::ostream & SaveTo(std::ostream & save);
|
virtual std::ostream & SaveTo(std::ostream & save);
|
||||||
virtual std::istream & LoadFrom(std::istream & load);
|
virtual std::istream & LoadFrom(std::istream & load);
|
||||||
|
|
||||||
@ -83,7 +90,7 @@ class STDMESHERS_EXPORT StdMeshers_Adaptive1D : public SMESH_Hypothesis
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
double myMinSize, myMaxSize, myDeflection;
|
double myMinSize, myMaxSize, myDeflection, myGrading;
|
||||||
SMESH_Algo* myAlgo; // StdMeshers_AdaptiveAlgo_1D implemented in cxx file
|
SMESH_Algo* myAlgo; // StdMeshers_AdaptiveAlgo_1D implemented in cxx file
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -609,6 +609,8 @@ QString StdMeshersGUI_StdHypothesisCreator::storeParams() const
|
|||||||
h->SetMaxSize( params[1].myValue.toDouble() );
|
h->SetMaxSize( params[1].myValue.toDouble() );
|
||||||
h->SetVarParameter( params[0].text(), "SetDeflection" );
|
h->SetVarParameter( params[0].text(), "SetDeflection" );
|
||||||
h->SetDeflection( params[2].myValue.toDouble() );
|
h->SetDeflection( params[2].myValue.toDouble() );
|
||||||
|
h->SetVarParameter( params[0].text(), "SetGrading" );
|
||||||
|
h->SetGrading( params[3].myValue.toDouble() );
|
||||||
}
|
}
|
||||||
else if( hypType()=="AutomaticLength" )
|
else if( hypType()=="AutomaticLength" )
|
||||||
{
|
{
|
||||||
@ -1048,6 +1050,11 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const
|
|||||||
if(!initVariableName( hyp, item, "SetDeflection" ))
|
if(!initVariableName( hyp, item, "SetDeflection" ))
|
||||||
item.myValue = h->GetDeflection();
|
item.myValue = h->GetDeflection();
|
||||||
p.append( item );
|
p.append( item );
|
||||||
|
|
||||||
|
item.myName = tr( "SMESH_GRADING1D_PARAM" );
|
||||||
|
if(!initVariableName( hyp, item, "SetGrading" ))
|
||||||
|
item.myValue = h->GetGrading();
|
||||||
|
p.append( item );
|
||||||
}
|
}
|
||||||
else if( hypType()=="AutomaticLength" )
|
else if( hypType()=="AutomaticLength" )
|
||||||
{
|
{
|
||||||
@ -1421,7 +1428,10 @@ void StdMeshersGUI_StdHypothesisCreator::attuneStdWidget (QWidget* w, const int)
|
|||||||
}
|
}
|
||||||
else if( hypType()=="Adaptive1D" )
|
else if( hypType()=="Adaptive1D" )
|
||||||
{
|
{
|
||||||
sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
|
if (sb->objectName() == tr("SMESH_GRADING1D_PARAM"))
|
||||||
|
sb->RangeStepAndValidator( 0.0, 2.0, 0.1, "length_precision" );
|
||||||
|
else
|
||||||
|
sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
|
||||||
}
|
}
|
||||||
else if( hypType().startsWith( "ViscousLayers" ))
|
else if( hypType().startsWith( "ViscousLayers" ))
|
||||||
{
|
{
|
||||||
|
@ -150,6 +150,10 @@
|
|||||||
<source>SMESH_INVALID_FUNCTION</source>
|
<source>SMESH_INVALID_FUNCTION</source>
|
||||||
<translation>Function is invalid</translation>
|
<translation>Function is invalid</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>SMESH_GRADING1D_PARAM</source>
|
||||||
|
<translation>Grading</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>SMESH_LAYERS_DISTRIBUTION</source>
|
<source>SMESH_LAYERS_DISTRIBUTION</source>
|
||||||
<translation>1D Hypothesis</translation>
|
<translation>1D Hypothesis</translation>
|
||||||
|
@ -135,6 +135,10 @@
|
|||||||
<source>SMESH_INVALID_FUNCTION</source>
|
<source>SMESH_INVALID_FUNCTION</source>
|
||||||
<translation>La fonction n'est pas valide</translation>
|
<translation>La fonction n'est pas valide</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>SMESH_GRADING1D_PARAM</source>
|
||||||
|
<translation type="unfinished">Grading</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>SMESH_LAYERS_DISTRIBUTION</source>
|
<source>SMESH_LAYERS_DISTRIBUTION</source>
|
||||||
<translation>Hypothèse 1D </translation>
|
<translation>Hypothèse 1D </translation>
|
||||||
|
@ -150,6 +150,10 @@
|
|||||||
<source>SMESH_INVALID_FUNCTION</source>
|
<source>SMESH_INVALID_FUNCTION</source>
|
||||||
<translation>関数が無効です。</translation>
|
<translation>関数が無効です。</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>SMESH_GRADING1D_PARAM</source>
|
||||||
|
<translation type="unfinished">Grading</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>SMESH_LAYERS_DISTRIBUTION</source>
|
<source>SMESH_LAYERS_DISTRIBUTION</source>
|
||||||
<translation>仮説 1 d</translation>
|
<translation>仮説 1 d</translation>
|
||||||
|
@ -152,6 +152,37 @@ CORBA::Double StdMeshers_Adaptive1D_i::GetDeflection()
|
|||||||
return this->GetImpl()->GetDeflection();
|
return this->GetImpl()->GetDeflection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//=======================================================================
|
||||||
|
//function : SetGrading
|
||||||
|
//purpose : Sets how much size of adjacent elements can differ.
|
||||||
|
//=======================================================================
|
||||||
|
|
||||||
|
void StdMeshers_Adaptive1D_i::SetGrading( CORBA::Double theValue )
|
||||||
|
throw ( SALOME::SALOME_Exception )
|
||||||
|
{
|
||||||
|
ASSERT( myBaseImpl );
|
||||||
|
try {
|
||||||
|
this->GetImpl()->SetGrading( theValue );
|
||||||
|
}
|
||||||
|
catch ( SALOME_Exception& S_ex ) {
|
||||||
|
THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Python script
|
||||||
|
SMESH::TPythonDump() << _this() << ".SetGrading( " << SMESH::TVar(theValue) << " )";
|
||||||
|
}
|
||||||
|
|
||||||
|
//=======================================================================
|
||||||
|
//function : GetGrading
|
||||||
|
//purpose : Returns grading
|
||||||
|
//=======================================================================
|
||||||
|
|
||||||
|
CORBA::Double StdMeshers_Adaptive1D_i::GetGrading()
|
||||||
|
{
|
||||||
|
ASSERT( myBaseImpl );
|
||||||
|
return this->GetImpl()->GetGrading();
|
||||||
|
}
|
||||||
|
|
||||||
//=======================================================================
|
//=======================================================================
|
||||||
//function : GetImpl
|
//function : GetImpl
|
||||||
//purpose : Get implementation
|
//purpose : Get implementation
|
||||||
|
@ -70,6 +70,12 @@ class STDMESHERS_I_EXPORT StdMeshers_Adaptive1D_i:
|
|||||||
void SetDeflection( CORBA::Double theLength ) throw (SALOME::SALOME_Exception);
|
void SetDeflection( CORBA::Double theLength ) throw (SALOME::SALOME_Exception);
|
||||||
CORBA::Double GetDeflection();
|
CORBA::Double GetDeflection();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Sets <grading> parameter value,
|
||||||
|
// * i.e. a maximal allowed distance between a segment and an edge.
|
||||||
|
*/
|
||||||
|
void SetGrading( CORBA::Double theLength ) throw (SALOME::SALOME_Exception);
|
||||||
|
CORBA::Double GetGrading();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Returns implementation
|
* Returns implementation
|
||||||
|
Loading…
Reference in New Issue
Block a user