mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
BUG: blockMesh mergePatchPairs fails with edge-shared points (fixes #2589)
- remedy by performing the attach() action sequentially (as per stitchMesh changes). This ensures that the current point addressing is always used and avoids references to the already-merged points (which is what causes the failure). ENH: improve handling of empty patch removal - only remove empty *merged* patches, but leave any other empty patches untouched since they may intentional placeholders for other parts of a workflow. - remove any empty point/face zones created for patch merging
This commit is contained in:
@ -1,30 +1,56 @@
|
|||||||
// Handle merging of patch pairs
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | www.openfoam.com
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Copyright (C) 2009 OpenFOAM Foundation
|
||||||
|
Copyright (C) 2017-2022 OpenCFD Ltd.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
License
|
||||||
|
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
|
||||||
|
|
||||||
|
Description
|
||||||
|
Handle merging of patch pairs
|
||||||
|
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
{
|
{
|
||||||
wordPairList mergePatchPairs;
|
wordPairList mergePatchPairs;
|
||||||
|
|
||||||
// Read in a list of dictionaries for the merge patch pairs
|
// Read in a list of merge patch pairs
|
||||||
if
|
if
|
||||||
(
|
(
|
||||||
meshDict.readIfPresent("mergePatchPairs", mergePatchPairs)
|
meshDict.readIfPresent("mergePatchPairs", mergePatchPairs)
|
||||||
&& mergePatchPairs.size()
|
&& mergePatchPairs.size()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Info<< "Creating merge patch pairs" << nl << endl;
|
Info<< "Merging " << mergePatchPairs.size() << " patch pairs" << nl;
|
||||||
|
|
||||||
Info<< "Adding point and face zones" << endl;
|
// Cleanup
|
||||||
|
wordHashSet cleanupPatches(4*mergePatchPairs.size());
|
||||||
|
wordHashSet cleanupPointZones(2*mergePatchPairs.size());
|
||||||
|
wordHashSet cleanupFaceZones(2*mergePatchPairs.size());
|
||||||
|
|
||||||
|
Info<< " Adding point and face zones" << endl;
|
||||||
{
|
{
|
||||||
auto& pzs = mesh.pointZones();
|
const auto& pbm = mesh.boundaryMesh();
|
||||||
pzs.clearAddressing();
|
|
||||||
auto& fzs = mesh.faceZones();
|
auto& pzs = mesh.pointZones(); pzs.clearAddressing();
|
||||||
fzs.clearAddressing();
|
auto& fzs = mesh.faceZones(); fzs.clearAddressing();
|
||||||
|
|
||||||
forAll(mergePatchPairs, pairi)
|
forAll(mergePatchPairs, pairi)
|
||||||
{
|
{
|
||||||
|
// Patch pairs
|
||||||
|
const polyPatch& patch0 = pbm[mergePatchPairs[pairi].first()];
|
||||||
|
const polyPatch& patch1 = pbm[mergePatchPairs[pairi].second()];
|
||||||
|
|
||||||
const word mergeName
|
const word mergeName
|
||||||
(
|
(
|
||||||
mergePatchPairs[pairi].first()
|
mergePatchPairs[pairi].first()
|
||||||
+ mergePatchPairs[pairi].second()
|
+ mergePatchPairs[pairi].second()
|
||||||
+ name(pairi)
|
+ Foam::name(pairi)
|
||||||
);
|
);
|
||||||
|
|
||||||
// An empty zone for cut points
|
// An empty zone for cut points
|
||||||
@ -37,40 +63,35 @@
|
|||||||
pzs
|
pzs
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
cleanupPointZones.insert(pzs.last().name());
|
||||||
|
|
||||||
// Master patch
|
// Coupling side 0 (master)
|
||||||
const word masterPatchName(mergePatchPairs[pairi].first());
|
|
||||||
const polyPatch& masterPatch =
|
|
||||||
mesh.boundaryMesh()[masterPatchName];
|
|
||||||
|
|
||||||
fzs.append
|
fzs.append
|
||||||
(
|
(
|
||||||
new faceZone
|
new faceZone
|
||||||
(
|
(
|
||||||
mergeName + "MasterZone",
|
mergeName + "Side0Zone",
|
||||||
identity(masterPatch.range()),
|
identity(patch0.range()),
|
||||||
false, // none are flipped
|
false, // none are flipped
|
||||||
fzs.size(),
|
fzs.size(),
|
||||||
fzs
|
fzs
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
cleanupFaceZones.insert(fzs.last().name());
|
||||||
|
|
||||||
// Slave patch
|
// Coupling side 1 (slave)
|
||||||
const word slavePatchName(mergePatchPairs[pairi].second());
|
|
||||||
const polyPatch& slavePatch =
|
|
||||||
mesh.boundaryMesh()[slavePatchName];
|
|
||||||
|
|
||||||
fzs.append
|
fzs.append
|
||||||
(
|
(
|
||||||
new faceZone
|
new faceZone
|
||||||
(
|
(
|
||||||
mergeName + "SlaveZone",
|
mergeName + "Side1Zone",
|
||||||
identity(slavePatch.range()),
|
identity(patch1.range()),
|
||||||
false, // none are flipped
|
false, // none are flipped
|
||||||
fzs.size(),
|
fzs.size(),
|
||||||
fzs
|
fzs
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
cleanupFaceZones.insert(fzs.last().name());
|
||||||
|
|
||||||
// An empty zone for cut faces
|
// An empty zone for cut faces
|
||||||
fzs.append
|
fzs.append
|
||||||
@ -82,35 +103,38 @@
|
|||||||
fzs
|
fzs
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} // end of all merge pairs
|
cleanupFaceZones.insert(fzs.last().name());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Info<< " Merging with attachPolyTopoChanger" << endl;
|
||||||
Info<< "Creating attachPolyTopoChanger" << endl;
|
|
||||||
attachPolyTopoChanger polyMeshAttacher(mesh);
|
attachPolyTopoChanger polyMeshAttacher(mesh);
|
||||||
polyMeshAttacher.setSize(mergePatchPairs.size());
|
polyMeshAttacher.resize(1);
|
||||||
|
|
||||||
forAll(mergePatchPairs, pairi)
|
forAll(mergePatchPairs, pairi)
|
||||||
{
|
{
|
||||||
|
cleanupPatches.insert(mergePatchPairs[pairi].first());
|
||||||
|
cleanupPatches.insert(mergePatchPairs[pairi].second());
|
||||||
|
|
||||||
const word mergeName
|
const word mergeName
|
||||||
(
|
(
|
||||||
mergePatchPairs[pairi].first()
|
mergePatchPairs[pairi].first()
|
||||||
+ mergePatchPairs[pairi].second()
|
+ mergePatchPairs[pairi].second()
|
||||||
+ name(pairi)
|
+ Foam::name(pairi)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add the sliding interface mesh modifier
|
// Add the sliding interface mesh modifier
|
||||||
polyMeshAttacher.set
|
polyMeshAttacher.set
|
||||||
(
|
(
|
||||||
pairi,
|
0,
|
||||||
new slidingInterface
|
new slidingInterface
|
||||||
(
|
(
|
||||||
"couple" + name(pairi),
|
"couple" + Foam::name(pairi),
|
||||||
pairi,
|
pairi,
|
||||||
polyMeshAttacher,
|
polyMeshAttacher,
|
||||||
mergeName + "MasterZone",
|
mergeName + "Side0Zone",
|
||||||
mergeName + "SlaveZone",
|
mergeName + "Side1Zone",
|
||||||
mergeName + "CutPointZone",
|
mergeName + "CutPointZone",
|
||||||
mergeName + "CutFaceZone",
|
mergeName + "CutFaceZone",
|
||||||
mergePatchPairs[pairi].first(),
|
mergePatchPairs[pairi].first(),
|
||||||
@ -120,12 +144,135 @@
|
|||||||
intersection::VISIBLE
|
intersection::VISIBLE
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
polyMeshAttacher.attach(false); // Do not yet remove empty patches
|
||||||
}
|
}
|
||||||
|
|
||||||
polyMeshAttacher.attach(true);
|
// Re-do the boundary patches, removing empty merge patches
|
||||||
|
// but keeping any other empty patches
|
||||||
|
{
|
||||||
|
const polyBoundaryMesh& oldPatches = mesh.boundaryMesh();
|
||||||
|
|
||||||
|
polyPatchList newPatches(oldPatches.size());
|
||||||
|
label nNewPatches = 0;
|
||||||
|
|
||||||
|
wordHashSet removedPatches(cleanupPatches.capacity());
|
||||||
|
|
||||||
|
forAll(oldPatches, patchi)
|
||||||
|
{
|
||||||
|
const word& patchName = oldPatches[patchi].name();
|
||||||
|
|
||||||
|
if
|
||||||
|
(
|
||||||
|
!cleanupPatches.found(patchName)
|
||||||
|
|| returnReduceOr(oldPatches[patchi].size())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
newPatches.set
|
||||||
|
(
|
||||||
|
nNewPatches,
|
||||||
|
oldPatches[patchi].clone
|
||||||
|
(
|
||||||
|
mesh.boundaryMesh(),
|
||||||
|
nNewPatches,
|
||||||
|
oldPatches[patchi].size(),
|
||||||
|
oldPatches[patchi].start()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
++nNewPatches;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
removedPatches.insert(patchName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newPatches.resize(nNewPatches);
|
||||||
|
|
||||||
|
mesh.removeBoundary();
|
||||||
|
mesh.addPatches(newPatches);
|
||||||
|
|
||||||
|
Info<< "Removed " << removedPatches.size()
|
||||||
|
<< " empty merged patches:" << nl
|
||||||
|
<< " " << flatOutput(removedPatches.sortedToc()) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup empty merged point zones
|
||||||
|
{
|
||||||
|
PtrList<pointZone>& zones = mesh.pointZones();
|
||||||
|
mesh.pointZones().clearAddressing();
|
||||||
|
|
||||||
|
wordHashSet removedZones(2*zones.size());
|
||||||
|
|
||||||
|
label nZones = 0;
|
||||||
|
forAll(zones, zonei)
|
||||||
|
{
|
||||||
|
if
|
||||||
|
(
|
||||||
|
!cleanupPointZones.found(zones[zonei].name())
|
||||||
|
|| returnReduceOr(zones[zonei].size())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
zones.set(nZones, zones.release(zonei));
|
||||||
|
zones[nZones].index() = nZones; // re-index
|
||||||
|
++nZones;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
removedZones.insert(zones[zonei].name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zones.resize(nZones);
|
||||||
|
|
||||||
|
if (removedZones.size())
|
||||||
|
{
|
||||||
|
Info<< "Removed " << removedZones.size()
|
||||||
|
<< " empty point zones:" << nl
|
||||||
|
<< " " << flatOutput(removedZones.sortedToc()) << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup empty merged face zones
|
||||||
|
{
|
||||||
|
PtrList<faceZone>& zones = mesh.faceZones();
|
||||||
|
mesh.faceZones().clearAddressing();
|
||||||
|
|
||||||
|
wordHashSet removedZones(2*zones.size());
|
||||||
|
|
||||||
|
label nZones = 0;
|
||||||
|
forAll(zones, zonei)
|
||||||
|
{
|
||||||
|
if
|
||||||
|
(
|
||||||
|
!cleanupFaceZones.found(zones[zonei].name())
|
||||||
|
|| returnReduceOr(zones[zonei].size())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
zones.set(nZones, zones.release(zonei));
|
||||||
|
zones[nZones].index() = nZones; // re-index
|
||||||
|
++nZones;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
removedZones.insert(zones[zonei].name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zones.resize(nZones);
|
||||||
|
|
||||||
|
if (removedZones.size())
|
||||||
|
{
|
||||||
|
Info<< "Removed " << removedZones.size()
|
||||||
|
<< " empty merged face zones:" << nl
|
||||||
|
<< " " << flatOutput(removedZones.sortedToc()) << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Info<< nl << "There are no merge patch pairs" << endl;
|
Info<< "No patch pairs to merge" << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
\\/ M anipulation |
|
\\/ M anipulation |
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
Copyright (C) 2011-2017 OpenFOAM Foundation
|
Copyright (C) 2011-2017 OpenFOAM Foundation
|
||||||
Copyright (C) 2017-2020 OpenCFD Ltd.
|
Copyright (C) 2017-2022 OpenCFD Ltd.
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
License
|
License
|
||||||
This file is part of OpenFOAM.
|
This file is part of OpenFOAM.
|
||||||
@ -564,7 +564,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
mesh.faceZones()
|
mesh.faceZones()
|
||||||
(
|
(
|
||||||
mergePatchName + "MasterZone",
|
mergePatchName + "Side0Zone",
|
||||||
true // verbose
|
true // verbose
|
||||||
).resetAddressing(std::move(faceIds), false);
|
).resetAddressing(std::move(faceIds), false);
|
||||||
|
|
||||||
@ -574,7 +574,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
mesh.faceZones()
|
mesh.faceZones()
|
||||||
(
|
(
|
||||||
mergePatchName + "SlaveZone",
|
mergePatchName + "Side1Zone",
|
||||||
true // verbose
|
true // verbose
|
||||||
).resetAddressing(std::move(faceIds), false);
|
).resetAddressing(std::move(faceIds), false);
|
||||||
|
|
||||||
@ -595,8 +595,8 @@ int main(int argc, char *argv[])
|
|||||||
"couple" + Foam::name(actioni),
|
"couple" + Foam::name(actioni),
|
||||||
0,
|
0,
|
||||||
stitcher,
|
stitcher,
|
||||||
mergePatchName + "MasterZone",
|
mergePatchName + "Side0Zone",
|
||||||
mergePatchName + "SlaveZone",
|
mergePatchName + "Side1Zone",
|
||||||
mergePatchName + "CutPointZone",
|
mergePatchName + "CutPointZone",
|
||||||
cutZoneName,
|
cutZoneName,
|
||||||
masterPatchName,
|
masterPatchName,
|
||||||
|
|||||||
10
tutorials/mesh/blockMesh/mergePairs/Allclean
Executable file
10
tutorials/mesh/blockMesh/mergePairs/Allclean
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
cd "${0%/*}" || exit # Run from this directory
|
||||||
|
. ${WM_PROJECT_DIR:?}/bin/tools/CleanFunctions # Tutorial clean functions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
cleanCase0
|
||||||
|
|
||||||
|
rm -rf constant
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
8
tutorials/mesh/blockMesh/mergePairs/Allrun
Executable file
8
tutorials/mesh/blockMesh/mergePairs/Allrun
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
cd "${0%/*}" || exit # Run from this directory
|
||||||
|
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
runApplication blockMesh
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
119
tutorials/mesh/blockMesh/mergePairs/system/blockMeshDict
Normal file
119
tutorials/mesh/blockMesh/mergePairs/system/blockMeshDict
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*--------------------------------*- C++ -*----------------------------------*\
|
||||||
|
| ========= | |
|
||||||
|
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
|
||||||
|
| \\ / O peration | Version: v2212 |
|
||||||
|
| \\ / A nd | Website: www.openfoam.com |
|
||||||
|
| \\/ M anipulation | |
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
FoamFile
|
||||||
|
{
|
||||||
|
version 2.0;
|
||||||
|
format ascii;
|
||||||
|
class dictionary;
|
||||||
|
object blockMeshDict;
|
||||||
|
}
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
scale 0.001;
|
||||||
|
|
||||||
|
vertices
|
||||||
|
(
|
||||||
|
// Block d
|
||||||
|
( 50 -17 -12.5)
|
||||||
|
(100 -17 -12.5)
|
||||||
|
(100 -2 -12.5)
|
||||||
|
( 50 -2 -12.5)
|
||||||
|
( 50 -17 12.5)
|
||||||
|
(100 -17 12.5)
|
||||||
|
(100 -2 12.5)
|
||||||
|
( 50 -2 12.5)
|
||||||
|
|
||||||
|
// Block e
|
||||||
|
( 0 -17 -12.5)
|
||||||
|
( 50 -17 -12.5)
|
||||||
|
( 50 -2 -12.5)
|
||||||
|
( 0 -2 -12.5)
|
||||||
|
( 0 -17 12.5)
|
||||||
|
( 50 -17 12.5)
|
||||||
|
( 50 -2 12.5)
|
||||||
|
( 0 -2 12.5)
|
||||||
|
|
||||||
|
// Block f
|
||||||
|
( 0 -19 -12.5)
|
||||||
|
( 50 -19 -12.5)
|
||||||
|
( 50 -17 -12.5)
|
||||||
|
( 0 -17 -12.5)
|
||||||
|
( 0 -19 12.5)
|
||||||
|
( 50 -19 12.5)
|
||||||
|
( 50 -17 12.5)
|
||||||
|
( 0 -17 12.5)
|
||||||
|
);
|
||||||
|
|
||||||
|
blocks
|
||||||
|
(
|
||||||
|
// Block d
|
||||||
|
hex (0 1 2 3 4 5 6 7) block-d (4 5 4) grading (1 1 1)
|
||||||
|
|
||||||
|
// Block e
|
||||||
|
hex (8 9 10 11 12 13 14 15) block-e (4 4 4) grading (1 1 1)
|
||||||
|
|
||||||
|
// Block f
|
||||||
|
hex (16 17 18 19 20 21 22 23) block-f (5 4 4) grading (1 1 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
edges
|
||||||
|
(
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
boundary
|
||||||
|
(
|
||||||
|
mid6
|
||||||
|
{
|
||||||
|
type patch;
|
||||||
|
faces
|
||||||
|
(
|
||||||
|
(0 0) // Block d
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mid7
|
||||||
|
{
|
||||||
|
type patch;
|
||||||
|
faces
|
||||||
|
(
|
||||||
|
(1 1) // Block e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mid8
|
||||||
|
{
|
||||||
|
type patch;
|
||||||
|
faces
|
||||||
|
(
|
||||||
|
(1 2) // Block e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mid9
|
||||||
|
{
|
||||||
|
type patch;
|
||||||
|
faces
|
||||||
|
(
|
||||||
|
(2 3) // Block e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
other
|
||||||
|
{
|
||||||
|
type patch;
|
||||||
|
faces ();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
mergePatchPairs
|
||||||
|
(
|
||||||
|
(mid6 mid7)
|
||||||
|
(mid8 mid9)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
48
tutorials/mesh/blockMesh/mergePairs/system/controlDict
Normal file
48
tutorials/mesh/blockMesh/mergePairs/system/controlDict
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*--------------------------------*- C++ -*----------------------------------*\
|
||||||
|
| ========= | |
|
||||||
|
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
|
||||||
|
| \\ / O peration | Version: v2206 |
|
||||||
|
| \\ / A nd | Website: www.openfoam.com |
|
||||||
|
| \\/ M anipulation | |
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
FoamFile
|
||||||
|
{
|
||||||
|
version 2.0;
|
||||||
|
format ascii;
|
||||||
|
class dictionary;
|
||||||
|
object controlDict;
|
||||||
|
}
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
application blockMesh;
|
||||||
|
|
||||||
|
startFrom startTime;
|
||||||
|
|
||||||
|
startTime 0;
|
||||||
|
|
||||||
|
stopAt endTime;
|
||||||
|
|
||||||
|
endTime 0;
|
||||||
|
|
||||||
|
deltaT 0;
|
||||||
|
|
||||||
|
writeControl timeStep;
|
||||||
|
|
||||||
|
writeInterval 1;
|
||||||
|
|
||||||
|
purgeWrite 0;
|
||||||
|
|
||||||
|
writeFormat ascii;
|
||||||
|
|
||||||
|
writePrecision 6;
|
||||||
|
|
||||||
|
writeCompression off;
|
||||||
|
|
||||||
|
timeFormat general;
|
||||||
|
|
||||||
|
timePrecision 6;
|
||||||
|
|
||||||
|
runTimeModifiable true;
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
Reference in New Issue
Block a user