mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
Merge remote-tracking branch 'upstream/develop' into wp3-directional-refinement
This commit is contained in:
@ -75,11 +75,10 @@ PtrList<SLList<label>> slPatchCellFaces;
|
||||
Map<word> cellTypes;
|
||||
label currentTypei = -1;
|
||||
|
||||
|
||||
// Dummy yywrap to keep yylex happy at compile time.
|
||||
// It is called by yylex but is not used as the mechanism to change file.
|
||||
// See <<EOF>>
|
||||
#if YY_FLEX_MINOR_VERSION < 6 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
extern "C" int yywrap()
|
||||
#else
|
||||
int yyFlexLexer::yywrap()
|
||||
|
||||
@ -126,7 +126,7 @@ void uniquify(word& name, HashSet<word>& patchNames)
|
||||
// Dummy yywrap to keep yylex happy at compile time.
|
||||
// It is called by yylex but is not used as the mechanism to change file.
|
||||
// See <<EOF>>
|
||||
#if YY_FLEX_MINOR_VERSION < 6 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
extern "C" int yywrap()
|
||||
#else
|
||||
int yyFlexLexer::yywrap()
|
||||
|
||||
@ -103,7 +103,7 @@ wordList patchNameIDs(maxZoneID);
|
||||
// Dummy yywrap to keep yylex happy at compile time.
|
||||
// It is called by yylex but is not used as the mechanism to change file.
|
||||
// See <<EOF>>
|
||||
#if YY_FLEX_MINOR_VERSION < 6 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
extern "C" int yywrap()
|
||||
#else
|
||||
int yyFlexLexer::yywrap()
|
||||
|
||||
@ -82,7 +82,7 @@ label nValuesForPatchFaces = 0;
|
||||
// Dummy yywrap to keep yylex happy at compile time.
|
||||
// It is called by yylex but is not used as the mechanism to change file.
|
||||
// See <<EOF>>
|
||||
#if YY_FLEX_MINOR_VERSION < 6 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
extern "C" int yywrap()
|
||||
#else
|
||||
int yyFlexLexer::yywrap()
|
||||
|
||||
@ -178,16 +178,16 @@ Foam::label Foam::checkTopology
|
||||
{
|
||||
cells.insert(celli);
|
||||
}
|
||||
forAll(cFaces, i)
|
||||
for (const label facei : cFaces)
|
||||
{
|
||||
if (cFaces[i] < 0 || cFaces[i] >= mesh.nFaces())
|
||||
if (facei < 0 || facei >= mesh.nFaces())
|
||||
{
|
||||
cells.insert(celli);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
const label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
|
||||
if (nCells > 0)
|
||||
{
|
||||
@ -203,7 +203,6 @@ Foam::label Foam::checkTopology
|
||||
{
|
||||
mergeAndWrite(surfWriter(), cells);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -238,7 +237,7 @@ Foam::label Foam::checkTopology
|
||||
noFailedChecks++;
|
||||
}
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
const label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
@ -259,7 +258,7 @@ Foam::label Foam::checkTopology
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
const label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with out-of-range or duplicate vertices to set "
|
||||
@ -280,7 +279,7 @@ Foam::label Foam::checkTopology
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
const label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nCells
|
||||
<< " cells with over used edges to set " << cells.name()
|
||||
@ -303,7 +302,7 @@ Foam::label Foam::checkTopology
|
||||
noFailedChecks++;
|
||||
}
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
const label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
@ -334,9 +333,9 @@ Foam::label Foam::checkTopology
|
||||
{
|
||||
const labelUList& owners = patches[patchi].faceCells();
|
||||
|
||||
forAll(owners, i)
|
||||
for (const label facei : owners)
|
||||
{
|
||||
nInternalFaces[owners[i]]++;
|
||||
nInternalFaces[facei]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -444,14 +443,14 @@ Foam::label Foam::checkTopology
|
||||
(
|
||||
label facei = mesh.nInternalFaces();
|
||||
facei < mesh.nFaces();
|
||||
facei++
|
||||
++facei
|
||||
)
|
||||
{
|
||||
label regioni = rs[mesh.faceOwner()[facei]];
|
||||
const label regioni = rs[mesh.faceOwner()[facei]];
|
||||
const face& f = mesh.faces()[facei];
|
||||
forAll(f, fp)
|
||||
for (const label verti : f)
|
||||
{
|
||||
label& pRegion = pointToRegion[f[fp]];
|
||||
label& pRegion = pointToRegion[verti];
|
||||
if (pRegion == -1)
|
||||
{
|
||||
pRegion = regioni;
|
||||
@ -467,7 +466,7 @@ Foam::label Foam::checkTopology
|
||||
regionDisconnected[regioni] = false;
|
||||
regionDisconnected[pRegion] = false;
|
||||
pRegion = -2;
|
||||
points.insert(f[fp]);
|
||||
points.insert(verti);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -618,9 +617,8 @@ Foam::label Foam::checkTopology
|
||||
}
|
||||
Info<< endl;
|
||||
|
||||
forAll(faceZones, zoneI)
|
||||
for (const faceZone& fz : faceZones)
|
||||
{
|
||||
const faceZone& fz = faceZones[zoneI];
|
||||
checkPatch(allGeometry, fz.name(), fz(), points);
|
||||
Info<< endl;
|
||||
}
|
||||
@ -631,7 +629,7 @@ Foam::label Foam::checkTopology
|
||||
}
|
||||
}
|
||||
|
||||
label nPoints = returnReduce(points.size(), sumOp<label>());
|
||||
const label nPoints = returnReduce(points.size(), sumOp<label>());
|
||||
|
||||
if (nPoints)
|
||||
{
|
||||
@ -654,45 +652,44 @@ Foam::label Foam::checkTopology
|
||||
|
||||
if (cellZones.size())
|
||||
{
|
||||
|
||||
Info<< " "
|
||||
<< setw(20) << "CellZone"
|
||||
<< setw(9) << "Cells"
|
||||
<< setw(9) << "Points"
|
||||
<< setw(13) << "BoundingBox" <<endl;
|
||||
<< ' ' << "BoundingBox" <<endl;
|
||||
|
||||
const cellList& cells = mesh.cells();
|
||||
const faceList& faces = mesh.faces();
|
||||
treeBoundBox bb(boundBox::invertedBox);
|
||||
PackedBoolList isZonePoint(mesh.nPoints());
|
||||
|
||||
forAll(cellZones, zoneI)
|
||||
for (const cellZone& cZone : cellZones)
|
||||
{
|
||||
const cellZone& cZone = cellZones[zoneI];
|
||||
boundBox bb;
|
||||
isZonePoint.reset(); // clears all bits (reset count)
|
||||
|
||||
forAll(cZone, i)
|
||||
for (const label celli : cZone)
|
||||
{
|
||||
const label cellI = cZone[i];
|
||||
const cell& cFaces = cells[cellI];
|
||||
forAll(cFaces, cFacei)
|
||||
for (const label facei : cells[celli])
|
||||
{
|
||||
const face& f = faces[cFaces[cFacei]];
|
||||
forAll(f, fp)
|
||||
const face& f = faces[facei];
|
||||
for (const label verti : f)
|
||||
{
|
||||
if (isZonePoint.set(f[fp]))
|
||||
if (isZonePoint.set(verti))
|
||||
{
|
||||
bb.add(mesh.points()[f[fp]]);
|
||||
bb.add(mesh.points()[verti]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb.reduce(); // Global min/max
|
||||
|
||||
Info<< " "
|
||||
<< setw(20) << cZone.name()
|
||||
<< setw(9) << returnReduce(cZone.size(), sumOp<label>())
|
||||
<< setw(9)
|
||||
<< returnReduce(isZonePoint.count(), sumOp<label>())
|
||||
<< setw(3) << bb << endl;
|
||||
<< ' ' << bb << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -48,19 +48,19 @@ void RotateFields
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
const IOobjectList& objects,
|
||||
const tensor& T
|
||||
const tensor& rotT
|
||||
)
|
||||
{
|
||||
// Search list of objects for volScalarFields
|
||||
// Objects of field type
|
||||
IOobjectList fields(objects.lookupClass(GeometricField::typeName));
|
||||
|
||||
forAllIter(IOobjectList, fields, fieldIter)
|
||||
{
|
||||
Info<< " Rotating " << fieldIter()->name() << endl;
|
||||
|
||||
GeometricField theta(*fieldIter(), mesh);
|
||||
transform(theta, dimensionedTensor(T), theta);
|
||||
theta.write();
|
||||
GeometricField fld(*fieldIter(), mesh);
|
||||
transform(fld, dimensionedTensor(rotT), fld);
|
||||
fld.write();
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +69,12 @@ void RotateFields
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
argList::addNote
|
||||
(
|
||||
"Rotate mesh points and vector/tensor fields\n"
|
||||
"Rotation from the <n1> vector to the <n2> vector"
|
||||
);
|
||||
|
||||
timeSelector::addOptions();
|
||||
|
||||
argList::addArgument("n1");
|
||||
@ -83,7 +89,7 @@ int main(int argc, char *argv[])
|
||||
vector n2(args.argRead<vector>(2));
|
||||
n2 /= mag(n2);
|
||||
|
||||
tensor T(rotationTensor(n1, n2));
|
||||
const tensor rotT(rotationTensor(n1, n2));
|
||||
|
||||
{
|
||||
pointIOField points
|
||||
@ -100,7 +106,7 @@ int main(int argc, char *argv[])
|
||||
)
|
||||
);
|
||||
|
||||
points = transform(T, points);
|
||||
points = transform(rotT, points);
|
||||
|
||||
// Set the precision of the points data to 10
|
||||
IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision()));
|
||||
@ -123,15 +129,15 @@ int main(int argc, char *argv[])
|
||||
// Search for list of objects for this time
|
||||
IOobjectList objects(mesh, runTime.timeName());
|
||||
|
||||
RotateFields<volVectorField>(mesh, objects, T);
|
||||
RotateFields<volSphericalTensorField>(mesh, objects, T);
|
||||
RotateFields<volSymmTensorField>(mesh, objects, T);
|
||||
RotateFields<volTensorField>(mesh, objects, T);
|
||||
RotateFields<volVectorField>(mesh, objects, rotT);
|
||||
RotateFields<volSphericalTensorField>(mesh, objects, rotT);
|
||||
RotateFields<volSymmTensorField>(mesh, objects, rotT);
|
||||
RotateFields<volTensorField>(mesh, objects, rotT);
|
||||
|
||||
RotateFields<surfaceVectorField>(mesh, objects, T);
|
||||
RotateFields<surfaceSphericalTensorField>(mesh, objects, T);
|
||||
RotateFields<surfaceSymmTensorField>(mesh, objects, T);
|
||||
RotateFields<surfaceTensorField>(mesh, objects, T);
|
||||
RotateFields<surfaceVectorField>(mesh, objects, rotT);
|
||||
RotateFields<surfaceSphericalTensorField>(mesh, objects, rotT);
|
||||
RotateFields<surfaceSymmTensorField>(mesh, objects, rotT);
|
||||
RotateFields<surfaceTensorField>(mesh, objects, rotT);
|
||||
}
|
||||
|
||||
Info<< "End\n" << endl;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
\\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -40,6 +40,9 @@ Usage
|
||||
-rotate (vector vector)
|
||||
Rotates the points from the first vector to the second,
|
||||
|
||||
-rotate-angle (vector angle)
|
||||
Rotate angle degrees about vector axis.
|
||||
|
||||
or -yawPitchRoll (yawdegrees pitchdegrees rolldegrees)
|
||||
or -rollPitchYaw (rolldegrees pitchdegrees yawdegrees)
|
||||
|
||||
@ -68,7 +71,6 @@ Usage
|
||||
#include "pointFields.H"
|
||||
#include "transformField.H"
|
||||
#include "transformGeometricField.H"
|
||||
#include "StringStream.H"
|
||||
#include "mathematicalConstants.H"
|
||||
|
||||
using namespace Foam;
|
||||
@ -81,7 +83,7 @@ void readAndRotateFields
|
||||
(
|
||||
PtrList<GeoField>& flds,
|
||||
const fvMesh& mesh,
|
||||
const tensor& T,
|
||||
const tensor& rotT,
|
||||
const IOobjectList& objects
|
||||
)
|
||||
{
|
||||
@ -89,7 +91,7 @@ void readAndRotateFields
|
||||
forAll(flds, i)
|
||||
{
|
||||
Info<< "Transforming " << flds[i].name() << endl;
|
||||
dimensionedTensor dimT("t", flds[i].dimensions(), T);
|
||||
const dimensionedTensor dimT("t", flds[i].dimensions(), rotT);
|
||||
transform(flds[i], dimT, flds[i]);
|
||||
}
|
||||
}
|
||||
@ -155,23 +157,34 @@ int main(int argc, char *argv[])
|
||||
"Translate by specified <vector> - eg, '(1 0 0)' before rotations"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"origin",
|
||||
"point",
|
||||
"Use specified <point> as origin for rotations"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"rotate",
|
||||
"(vectorA vectorB)",
|
||||
"Transform as a rotation between <vectorA> and <vectorB> "
|
||||
"- eg, '( (1 0 0) (0 0 1) )'"
|
||||
"Rotate from <vectorA> to <vectorB> - eg, '((1 0 0) (0 0 1))'"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"rotate-angle",
|
||||
"(vector scalar)",
|
||||
"Rotate <angle> degrees about <vector> - eg, '((1 0 0) 45)'"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"rollPitchYaw",
|
||||
"vector",
|
||||
"Rotate by '(roll pitch yaw)' in degrees"
|
||||
"Rotate by '(roll pitch yaw)' degrees"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"yawPitchRoll",
|
||||
"vector",
|
||||
"Rotate by '(yaw pitch roll)' in degrees"
|
||||
"Rotate by '(yaw pitch roll)' degrees"
|
||||
);
|
||||
argList::addBoolOption
|
||||
(
|
||||
@ -182,25 +195,54 @@ int main(int argc, char *argv[])
|
||||
(
|
||||
"scale",
|
||||
"scalar | vector",
|
||||
"Scale by the specified amount - eg, for a uniform [mm] to [m] scaling "
|
||||
"use either (0.001 0.001 0.001)' or simply '0.001'"
|
||||
"Scale by the specified amount - Eg, for uniform [mm] to [m] scaling "
|
||||
"use either '(0.001 0.001 0.001)' or simply '0.001'"
|
||||
);
|
||||
|
||||
#include "addRegionOption.H"
|
||||
#include "setRootCase.H"
|
||||
|
||||
const bool doRotateFields = args.optionFound("rotateFields");
|
||||
|
||||
// Verify that an operation has been specified
|
||||
{
|
||||
const List<word> operationNames
|
||||
{
|
||||
"translate",
|
||||
"rotate",
|
||||
"rotate-angle",
|
||||
"rollPitchYaw",
|
||||
"yawPitchRoll",
|
||||
"scale"
|
||||
};
|
||||
|
||||
if (!args.optionCount(operationNames))
|
||||
{
|
||||
FatalError
|
||||
<< "No operation supplied, "
|
||||
<< "use least one of the following:" << nl
|
||||
<< " ";
|
||||
|
||||
for (const auto& opName : operationNames)
|
||||
{
|
||||
FatalError
|
||||
<< " -" << opName;
|
||||
}
|
||||
|
||||
FatalError
|
||||
<< nl << exit(FatalError);
|
||||
}
|
||||
}
|
||||
|
||||
#include "createTime.H"
|
||||
|
||||
word regionName = polyMesh::defaultRegion;
|
||||
fileName meshDir;
|
||||
fileName meshDir = polyMesh::meshSubDir;
|
||||
|
||||
if (args.optionReadIfPresent("region", regionName))
|
||||
{
|
||||
meshDir = regionName/polyMesh::meshSubDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshDir = polyMesh::meshSubDir;
|
||||
}
|
||||
|
||||
pointIOField points
|
||||
(
|
||||
@ -216,17 +258,6 @@ int main(int argc, char *argv[])
|
||||
)
|
||||
);
|
||||
|
||||
const bool doRotateFields = args.optionFound("rotateFields");
|
||||
|
||||
// this is not actually stringent enough:
|
||||
if (args.options().empty())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "No options supplied, please use one or more of "
|
||||
"-translate, -rotate or -scale options."
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
vector v;
|
||||
if (args.optionReadIfPresent("translate", v))
|
||||
{
|
||||
@ -235,6 +266,14 @@ int main(int argc, char *argv[])
|
||||
points += v;
|
||||
}
|
||||
|
||||
vector origin;
|
||||
const bool useOrigin = args.optionReadIfPresent("origin", origin);
|
||||
if (useOrigin)
|
||||
{
|
||||
Info<< "Set origin for rotations to " << origin << endl;
|
||||
points -= origin;
|
||||
}
|
||||
|
||||
if (args.optionFound("rotate"))
|
||||
{
|
||||
Pair<vector> n1n2
|
||||
@ -243,15 +282,41 @@ int main(int argc, char *argv[])
|
||||
);
|
||||
n1n2[0] /= mag(n1n2[0]);
|
||||
n1n2[1] /= mag(n1n2[1]);
|
||||
tensor T = rotationTensor(n1n2[0], n1n2[1]);
|
||||
|
||||
Info<< "Rotating points by " << T << endl;
|
||||
const tensor rotT = rotationTensor(n1n2[0], n1n2[1]);
|
||||
|
||||
points = transform(T, points);
|
||||
Info<< "Rotating points by " << rotT << endl;
|
||||
|
||||
points = transform(rotT, points);
|
||||
|
||||
if (doRotateFields)
|
||||
{
|
||||
rotateFields(args, runTime, T);
|
||||
rotateFields(args, runTime, rotT);
|
||||
}
|
||||
}
|
||||
else if (args.optionFound("rotate-angle"))
|
||||
{
|
||||
const Tuple2<vector, scalar> axisAngle
|
||||
(
|
||||
args.optionLookup("rotate-angle")()
|
||||
);
|
||||
|
||||
Info<< "Rotating points " << nl
|
||||
<< " about " << axisAngle.first() << nl
|
||||
<< " angle " << axisAngle.second() << nl;
|
||||
|
||||
const quaternion quat
|
||||
(
|
||||
axisAngle.first(),
|
||||
axisAngle.second() * pi/180.0 // degToRad
|
||||
);
|
||||
|
||||
Info<< "Rotating points by quaternion " << quat << endl;
|
||||
points = transform(quat, points);
|
||||
|
||||
if (doRotateFields)
|
||||
{
|
||||
rotateFields(args, runTime, quat.R());
|
||||
}
|
||||
}
|
||||
else if (args.optionReadIfPresent("rollPitchYaw", v))
|
||||
@ -264,14 +329,14 @@ int main(int argc, char *argv[])
|
||||
// degToRad
|
||||
v *= pi/180.0;
|
||||
|
||||
const quaternion R(quaternion::rotationSequence::XYZ, v);
|
||||
const quaternion quat(quaternion::rotationSequence::XYZ, v);
|
||||
|
||||
Info<< "Rotating points by quaternion " << R << endl;
|
||||
points = transform(R, points);
|
||||
Info<< "Rotating points by quaternion " << quat << endl;
|
||||
points = transform(quat, points);
|
||||
|
||||
if (doRotateFields)
|
||||
{
|
||||
rotateFields(args, runTime, R.R());
|
||||
rotateFields(args, runTime, quat.R());
|
||||
}
|
||||
}
|
||||
else if (args.optionReadIfPresent("yawPitchRoll", v))
|
||||
@ -284,14 +349,14 @@ int main(int argc, char *argv[])
|
||||
// degToRad
|
||||
v *= pi/180.0;
|
||||
|
||||
const quaternion R(quaternion::rotationSequence::ZYX, v);
|
||||
const quaternion quat(quaternion::rotationSequence::ZYX, v);
|
||||
|
||||
Info<< "Rotating points by quaternion " << R << endl;
|
||||
points = transform(R, points);
|
||||
Info<< "Rotating points by quaternion " << quat << endl;
|
||||
points = transform(quat, points);
|
||||
|
||||
if (doRotateFields)
|
||||
{
|
||||
rotateFields(args, runTime, R.R());
|
||||
rotateFields(args, runTime, quat.R());
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,6 +390,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (useOrigin)
|
||||
{
|
||||
Info<< "Unset origin for rotations from " << origin << endl;
|
||||
points += origin;
|
||||
}
|
||||
|
||||
|
||||
// Set the precision of the points data to 10
|
||||
IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision()));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user