mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: PDRsetFields utility (#1216)
- the PDRsetFields utility processes a set of geometrical obstructions
to determine the equivalent blockage effects.
These fields are necessary inputs for PDRFoam calculations.
After setting up the geometries, the -dry-run option can be used to
generate a VTK file for diagnosis and post-processing purposes.
- this is an initial release, with improvements slated for the future.
NOTE
- the field results may be less than fully reliable when run in
single-precision. This howver does not represent a realistic
restriction since the prepared fields target a combustion
application which will invariably be double-precision.
This commit is contained in:
19
applications/utilities/preProcessing/PDRsetFields/Make/files
Normal file
19
applications/utilities/preProcessing/PDRsetFields/Make/files
Normal file
@ -0,0 +1,19 @@
|
||||
PDRsetFields.C
|
||||
|
||||
PDRarrays.C
|
||||
PDRarraysAnalyse.C
|
||||
PDRarraysCalc.C
|
||||
PDRmeshArrays.C
|
||||
PDRparams.C
|
||||
PDRpatchDef.C
|
||||
PDRlegacyMeshSpec.C
|
||||
PDRutilsIntersect.C
|
||||
PDRutilsOverlap.C
|
||||
|
||||
obstacles/PDRobstacle.C
|
||||
obstacles/PDRobstacleIO.C
|
||||
obstacles/PDRobstacleTypes.C
|
||||
obstacles/PDRobstacleLegacyIO.C
|
||||
obstacles/PDRobstacleLegacyRead.C
|
||||
|
||||
EXE = $(FOAM_APPBIN)/PDRsetFields
|
||||
@ -0,0 +1,14 @@
|
||||
EXE_INC = \
|
||||
-Iobstacles \
|
||||
-I$(LIB_SRC)/finiteVolume/lnInclude \
|
||||
-I$(LIB_SRC)/fileFormats/lnInclude \
|
||||
-I$(LIB_SRC)/surfMesh/lnInclude \
|
||||
-I$(LIB_SRC)/meshTools/lnInclude \
|
||||
-I$(LIB_SRC)/mesh/blockMesh/lnInclude
|
||||
|
||||
EXE_LIBS = \
|
||||
-lfiniteVolume \
|
||||
-lfileFormats \
|
||||
-lsurfMesh \
|
||||
-lmeshTools \
|
||||
-lblockMesh
|
||||
141
applications/utilities/preProcessing/PDRsetFields/PDRarrays.C
Normal file
141
applications/utilities/preProcessing/PDRsetFields/PDRarrays.C
Normal file
@ -0,0 +1,141 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRarrays.H"
|
||||
#include "PDRblock.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Smaller helper to resize matrix and assign to Zero
|
||||
template<class T>
|
||||
inline void resizeMatrix(SquareMatrix<T>& mat, const label n)
|
||||
{
|
||||
mat.setSize(n);
|
||||
mat = Zero;
|
||||
}
|
||||
|
||||
|
||||
// Smaller helper to resize i-j-k field and assign to uniform value,
|
||||
// normally Zero
|
||||
template<class T>
|
||||
inline void resizeField
|
||||
(
|
||||
IjkField<T>& fld,
|
||||
const labelVector& ijk,
|
||||
const T& val = T(Zero)
|
||||
)
|
||||
{
|
||||
fld.resize(ijk);
|
||||
fld = val;
|
||||
}
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::PDRarrays::PDRarrays()
|
||||
:
|
||||
pdrBlock_(std::cref<PDRblock>(PDRblock::null()))
|
||||
{}
|
||||
|
||||
|
||||
Foam::PDRarrays::PDRarrays(const PDRblock& pdrBlock)
|
||||
:
|
||||
PDRarrays()
|
||||
{
|
||||
reset(pdrBlock);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
void Foam::PDRarrays::reset(const PDRblock& pdrBlock)
|
||||
{
|
||||
pdrBlock_ = std::cref<PDRblock>(pdrBlock);
|
||||
|
||||
// Resize all the major arrays, which are grouped in the structure arrp
|
||||
// All the relevant dimensions are in PDRblock
|
||||
|
||||
// Cell-based addressing
|
||||
const labelVector cellDims = pdrBlock.sizes();
|
||||
|
||||
// Face or point-based addressing
|
||||
const labelVector faceDims(cellDims + labelVector::one);
|
||||
|
||||
// Max addressing dimensions for 2D arrays, with some extra space
|
||||
// These will be used for any combination of x,y,z,
|
||||
// so need to be dimensioned to the maximum size in both directions
|
||||
const label maxDim = cmptMax(pdrBlock.sizes()) + 2;
|
||||
|
||||
resizeField(v_block, cellDims);
|
||||
resizeField(surf, cellDims);
|
||||
|
||||
resizeField(area_block_s, cellDims);
|
||||
resizeField(area_block_r, cellDims);
|
||||
resizeField(dirn_block, cellDims);
|
||||
|
||||
resizeField(face_block, faceDims);
|
||||
|
||||
resizeField(along_block, cellDims);
|
||||
|
||||
resizeField(betai_inv1, cellDims);
|
||||
|
||||
resizeField(obs_count, cellDims);
|
||||
resizeField(sub_count, cellDims);
|
||||
resizeField(grating_count, cellDims);
|
||||
|
||||
resizeField(drag_s, cellDims);
|
||||
resizeField(drag_r, cellDims);
|
||||
|
||||
resizeField(obs_size, cellDims);
|
||||
|
||||
for (auto& list : overlap_1d)
|
||||
{
|
||||
list.resize(maxDim);
|
||||
list = Zero;
|
||||
}
|
||||
|
||||
resizeMatrix(aboverlap, maxDim);
|
||||
resizeMatrix(abperim, maxDim);
|
||||
resizeMatrix(a_lblock, maxDim);
|
||||
resizeMatrix(b_lblock, maxDim);
|
||||
resizeMatrix(ac_lblock, maxDim);
|
||||
resizeMatrix(bc_lblock, maxDim);
|
||||
resizeMatrix(c_count, maxDim);
|
||||
resizeMatrix(c_drag, maxDim);
|
||||
|
||||
resizeField(face_patch, faceDims, labelVector::uniform(-1));
|
||||
resizeField(hole_in_face, faceDims);
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
215
applications/utilities/preProcessing/PDRsetFields/PDRarrays.H
Normal file
215
applications/utilities/preProcessing/PDRsetFields/PDRarrays.H
Normal file
@ -0,0 +1,215 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::PDRarrays
|
||||
|
||||
Description
|
||||
Work array definitions for PDR fields
|
||||
|
||||
SourceFiles
|
||||
PDRarrays.C
|
||||
PDRarraysCalc.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRarrays_H
|
||||
#define PDRarrays_H
|
||||
|
||||
#include "symmTensor.H"
|
||||
#include "symmTensor2D.H"
|
||||
#include "SquareMatrix.H"
|
||||
#include "IjkField.H"
|
||||
#include <functional>
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Forward Declarations
|
||||
class PDRblock;
|
||||
class PDRmeshArrays;
|
||||
class PDRobstacle;
|
||||
class PDRpatchDef;
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class PDRarrays Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class PDRarrays
|
||||
{
|
||||
//- Reference to PDRblock
|
||||
std::reference_wrapper<const PDRblock> pdrBlock_;
|
||||
|
||||
public:
|
||||
|
||||
// Data Members
|
||||
// Entries used for analysis and when writing fields
|
||||
|
||||
//- Volume blockage
|
||||
IjkField<scalar> v_block;
|
||||
|
||||
//- Surface area in cell
|
||||
IjkField<scalar> surf;
|
||||
|
||||
//- Obstacle size in cell
|
||||
IjkField<scalar> obs_size;
|
||||
|
||||
//- Summed area blockage (directional) from sharp obstacles
|
||||
IjkField<vector> area_block_s;
|
||||
|
||||
//- Summed area blockage (directional) from round obstacles
|
||||
IjkField<vector> area_block_r;
|
||||
|
||||
//- A total directional blockage in the cell
|
||||
IjkField<Vector<bool>> dirn_block;
|
||||
|
||||
//- Face area blockage for face,
|
||||
//- summed from cell centre-plane to cell centre-plane
|
||||
IjkField<vector> face_block;
|
||||
|
||||
//- Longitudinal area blockage from obstacles that extend all the way
|
||||
//- through the cell in a given direction.
|
||||
IjkField<vector> along_block;
|
||||
|
||||
IjkField<vector> betai_inv1;
|
||||
|
||||
//- Number of obstacles in cell.
|
||||
// Can be non-integer if an obstacle does not pass all way through cell
|
||||
IjkField<scalar> obs_count;
|
||||
|
||||
//- Number of obstacles parallel to specified direction
|
||||
IjkField<vector> sub_count;
|
||||
|
||||
//- Addition to count to account for grating comprises many bars
|
||||
//- (to get Lobs right)
|
||||
IjkField<vector> grating_count;
|
||||
|
||||
//- Tensorial drag from sharp obstacles
|
||||
IjkField<symmTensor> drag_s;
|
||||
|
||||
//- Directional drag from round obstacles
|
||||
IjkField<vector> drag_r;
|
||||
|
||||
|
||||
// Next arrays are for 2D calculations of intersection
|
||||
|
||||
// One-dimensional scratch areas for cell overlaps
|
||||
Vector<List<scalar>> overlap_1d;
|
||||
|
||||
// In two dimensions, area of cell covered by circle
|
||||
SquareMatrix<scalar> aboverlap;
|
||||
|
||||
// In two dimensions, length of perimeter of circle witthin cell
|
||||
SquareMatrix<scalar> abperim;
|
||||
|
||||
// For offset cells, i.e. face blockage
|
||||
SquareMatrix<scalar> a_lblock, b_lblock;
|
||||
|
||||
// For centred cells
|
||||
SquareMatrix<scalar> ac_lblock, bc_lblock;
|
||||
|
||||
// The count in the cells
|
||||
SquareMatrix<scalar> c_count;
|
||||
|
||||
//- Cell-centred drag
|
||||
SquareMatrix<symmTensor2D> c_drag;
|
||||
|
||||
//- Face field for (directional) for patch Id
|
||||
IjkField<labelVector> face_patch;
|
||||
|
||||
//- Face field for (directional) hole in face
|
||||
IjkField<Vector<bool>> hole_in_face;
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct null
|
||||
PDRarrays();
|
||||
|
||||
//- Construct and reset
|
||||
explicit PDRarrays(const PDRblock& pdrBlock);
|
||||
|
||||
|
||||
//- Destructor
|
||||
~PDRarrays() = default;
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- Reset PDRblock reference, resize and zero arrays
|
||||
void reset(const PDRblock& pdrBlock);
|
||||
|
||||
//- Reference to PDRblock
|
||||
const PDRblock& block() const
|
||||
{
|
||||
return pdrBlock_.get();
|
||||
}
|
||||
|
||||
//- Summary of the blockages
|
||||
// For diagnostics and general overview
|
||||
void blockageSummary() const;
|
||||
|
||||
//- Add cylinder blockage
|
||||
void addCylinder(const PDRobstacle& obs);
|
||||
|
||||
//- Add general (non-cylinder) blockage
|
||||
void addBlockage
|
||||
(
|
||||
const PDRobstacle& obs,
|
||||
DynamicList<PDRpatchDef>& patches,
|
||||
const int volumeSign
|
||||
);
|
||||
|
||||
|
||||
static void calculateAndWrite
|
||||
(
|
||||
PDRarrays& arr,
|
||||
const PDRmeshArrays& meshIndexing,
|
||||
const fileName& casepath,
|
||||
const UList<PDRpatchDef>& patches
|
||||
);
|
||||
|
||||
void calculateAndWrite
|
||||
(
|
||||
const fileName& casepath,
|
||||
const PDRmeshArrays& meshIndexing,
|
||||
const UList<PDRpatchDef>& patches
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
1099
applications/utilities/preProcessing/PDRsetFields/PDRarraysAnalyse.C
Normal file
1099
applications/utilities/preProcessing/PDRsetFields/PDRarraysAnalyse.C
Normal file
File diff suppressed because it is too large
Load Diff
1984
applications/utilities/preProcessing/PDRsetFields/PDRarraysCalc.C
Normal file
1984
applications/utilities/preProcessing/PDRsetFields/PDRarraysCalc.C
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,75 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Namespace
|
||||
Foam::PDRlegacy
|
||||
|
||||
Description
|
||||
Legacy and transitional routines
|
||||
|
||||
SourceFiles
|
||||
PDRlegacyMeshSpec.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRlegacy_H
|
||||
#define PDRlegacy_H
|
||||
|
||||
#include "PDRblock.H"
|
||||
#include "fileName.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Forward Declarations
|
||||
class ISstream;
|
||||
class PDRblock;
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Namespace PDRlegacy
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
namespace PDRlegacy
|
||||
{
|
||||
void print_info(const PDRblock& block);
|
||||
|
||||
void read_mesh_spec(const fileName& casepath, PDRblock& pdrBlock);
|
||||
|
||||
void read_mesh_spec(ISstream& is, PDRblock& pdrBlock);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,340 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
// Read spec that resemble this:
|
||||
//
|
||||
// xmesh
|
||||
// (
|
||||
// ( -8.18 14 0.83 )
|
||||
// ( -0.69 11 1.00 )
|
||||
// ( 0.69 14 1.20 )
|
||||
// ( 8.18 0 )
|
||||
// )
|
||||
//
|
||||
// ymesh
|
||||
// (
|
||||
// ...
|
||||
// )
|
||||
|
||||
#include "PDRlegacy.H"
|
||||
|
||||
// OpenFOAM includes
|
||||
#include "error.H"
|
||||
#include "IFstream.H"
|
||||
#include "stringOps.H"
|
||||
#include "OSspecific.H"
|
||||
|
||||
#define XMESH_TAG "xmesh"
|
||||
#define YMESH_TAG "ymesh"
|
||||
#define ZMESH_TAG "zmesh"
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace PDRlegacy
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
|
||||
//- Simple structure for processing a mesh spec entry
|
||||
// Handles an entry with (float), (float int), or (float int float)
|
||||
//
|
||||
// This is for handling a sub-spec that resembles this:
|
||||
//
|
||||
// (
|
||||
// ( -8.18 14 0.83 )
|
||||
// ( -0.69 11 1.00 )
|
||||
// ( 0.69 14 1.20 )
|
||||
// ( 8.18 0 )
|
||||
// )
|
||||
struct pdrMeshSpecLine
|
||||
{
|
||||
scalar knot;
|
||||
label ndiv;
|
||||
scalar factor;
|
||||
|
||||
pdrMeshSpecLine() : knot(0), ndiv(0), factor(0) {}
|
||||
|
||||
//- Cheap means to avoid uniform list output
|
||||
bool operator!=(const pdrMeshSpecLine&) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Read mesh-spec entry
|
||||
Istream& operator>>(Istream& is, pdrMeshSpecLine& spec)
|
||||
{
|
||||
spec.knot = 0;
|
||||
spec.ndiv = 0;
|
||||
spec.factor = 0;
|
||||
|
||||
is.readBegin("pdrMeshSpecLine");
|
||||
|
||||
// Must have a point
|
||||
is >> spec.knot;
|
||||
|
||||
token tok(is);
|
||||
if (tok.isLabel())
|
||||
{
|
||||
spec.ndiv = tok.labelToken();
|
||||
|
||||
if (spec.ndiv)
|
||||
{
|
||||
is >> spec.factor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
is.putBack(tok);
|
||||
}
|
||||
|
||||
is.readEnd("pdrMeshSpecLine");
|
||||
|
||||
is.check(FUNCTION_NAME);
|
||||
return is;
|
||||
}
|
||||
|
||||
#ifdef FULLDEBUG
|
||||
// Write mesh-spec entry
|
||||
Ostream& operator<<(Ostream& os, const pdrMeshSpecLine& spec)
|
||||
{
|
||||
os << token::BEGIN_LIST << spec.knot;
|
||||
|
||||
if (spec.ndiv)
|
||||
{
|
||||
os << token::SPACE << spec.ndiv
|
||||
<< token::SPACE << spec.factor;
|
||||
}
|
||||
|
||||
os << token::END_LIST;
|
||||
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void read_spec(ISstream& is, const direction cmpt, List<scalar>& gridPoint)
|
||||
{
|
||||
if (!gridPoint.empty())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Duplicate specification of "
|
||||
<< vector::componentNames[cmpt]
|
||||
<< " grid"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
List<pdrMeshSpecLine> specs(is);
|
||||
|
||||
if (specs.size() < 2)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Grid specification for " << vector::componentNames[cmpt]
|
||||
<< " is too small. Need at least two points!" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
specs.last().ndiv = 0; // safety
|
||||
|
||||
|
||||
DynamicList<scalar> knots;
|
||||
DynamicList<label> divisions;
|
||||
DynamicList<scalar> factors;
|
||||
|
||||
for (const auto& spec : specs)
|
||||
{
|
||||
knots.append(spec.knot);
|
||||
|
||||
if (spec.ndiv < 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
divisions.append(spec.ndiv);
|
||||
factors.append(spec.factor);
|
||||
}
|
||||
|
||||
label nPoints = 1;
|
||||
for (const label nDiv : divisions)
|
||||
{
|
||||
nPoints += nDiv;
|
||||
}
|
||||
|
||||
if (nPoints < 2)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "No cells defined for direction "
|
||||
<< vector::componentNames[cmpt] << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
|
||||
// Define the grid points
|
||||
gridPoint.resize(nPoints);
|
||||
|
||||
const label nSegments = divisions.size();
|
||||
|
||||
label start = 0;
|
||||
|
||||
for (label segmenti=0; segmenti < nSegments; ++segmenti)
|
||||
{
|
||||
const label nDiv = divisions[segmenti];
|
||||
const scalar factor = factors[segmenti];
|
||||
|
||||
SubList<scalar> subPoint(gridPoint, nDiv+1, start);
|
||||
start += nDiv;
|
||||
|
||||
subPoint[0] = knots[segmenti];
|
||||
subPoint[nDiv] = knots[segmenti+1];
|
||||
|
||||
const scalar dist = (subPoint.last() - subPoint.first());
|
||||
|
||||
if (equal(factor, scalar(1)))
|
||||
{
|
||||
for (label i=1; i < nDiv; ++i)
|
||||
{
|
||||
subPoint[i] = (subPoint[0] + (dist * i)/nDiv);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scalar delta = dist * (1.0 - factor) / (1.0 - ::pow(factor, nDiv));
|
||||
|
||||
scalar xyz = subPoint[0];
|
||||
|
||||
for (label i=0; i < nDiv; ++i)
|
||||
{
|
||||
subPoint[i] = xyz;
|
||||
xyz += delta;
|
||||
delta *= factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // End namespace Detail
|
||||
} // End namespace PDRlegacy
|
||||
} // End namespace Foam
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
|
||||
|
||||
void Foam::PDRlegacy::print_info(const PDRblock& block)
|
||||
{
|
||||
Info<< "PDRblock" << nl
|
||||
<< " nCells: " << block.sizes() << nl
|
||||
<< " Box: " << block.bounds() << nl
|
||||
<< "x " << flatOutput(block.grid().x()) << nl
|
||||
<< "y " << flatOutput(block.grid().y()) << nl
|
||||
<< "z " << flatOutput(block.grid().z()) << nl
|
||||
<< endl;
|
||||
}
|
||||
|
||||
|
||||
void Foam::PDRlegacy::read_mesh_spec(const fileName& casepath, PDRblock& block)
|
||||
{
|
||||
Info<< "Reading pdrMeshSpec (legacy format)" << nl;
|
||||
|
||||
bool processed = false;
|
||||
|
||||
for (const fileName dirName : { "system", "constant/polyMesh" })
|
||||
{
|
||||
fileName path
|
||||
(
|
||||
casepath / dirName / "pdrMeshSpec"
|
||||
);
|
||||
|
||||
if (Foam::isFile(path))
|
||||
{
|
||||
IFstream is(path);
|
||||
|
||||
read_mesh_spec(is, block);
|
||||
processed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Did not process pdrMeshSpec" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::PDRlegacy::read_mesh_spec(ISstream& is, PDRblock& block)
|
||||
{
|
||||
Vector<scalarList> grid;
|
||||
|
||||
string line;
|
||||
|
||||
while (is.good())
|
||||
{
|
||||
is.getLine(line);
|
||||
stringOps::inplaceTrim(line);
|
||||
|
||||
if (line == XMESH_TAG)
|
||||
{
|
||||
Detail::read_spec(is, vector::X, grid.x());
|
||||
}
|
||||
else if (line == YMESH_TAG)
|
||||
{
|
||||
Detail::read_spec(is, vector::Y, grid.y());
|
||||
}
|
||||
else if (line == ZMESH_TAG)
|
||||
{
|
||||
Detail::read_spec(is, vector::Z, grid.z());
|
||||
}
|
||||
}
|
||||
|
||||
for (direction cmpt=0; cmpt < vector::nComponents; ++cmpt)
|
||||
{
|
||||
if (grid[cmpt].empty())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "No specification for "
|
||||
<< vector::componentNames[cmpt]
|
||||
<< " grid" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
|
||||
block.reset(grid.x(), grid.y(), grid.z());
|
||||
|
||||
#ifdef FULLDEBUG
|
||||
print_info(block);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,262 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRmeshArrays.H"
|
||||
#include "PDRblock.H"
|
||||
#include "polyMesh.H"
|
||||
#include "Time.H"
|
||||
#include "IjkField.H"
|
||||
|
||||
// Notes
|
||||
//
|
||||
// Determines the face and cell numbers of all faces and cells in the
|
||||
// central rectangular region where CAD_PDR operates. First,
|
||||
// "points" is read and the coordinates (by which I mean here the
|
||||
// indices in the x, y and z coordinate arrays) are determined. Then
|
||||
// "faces" is read and for each the coordinates of the lower- x,y,z
|
||||
// corner are determioned, also the orientation (X, Y or Z).
|
||||
// (Orientation in the sense of e.g. + or -x is not noted.) The files
|
||||
// "owner" and "neighbour" specify the six faces around each cell, so
|
||||
// from these the coordinates of the cells are determined.
|
||||
//
|
||||
// Full checks are made that the mesh in the central region is consistent
|
||||
// with CAD_PDR's mesh specified by the PDRmeshSpec file.
|
||||
//
|
||||
// Eventually, when writing out results, we shall work through the
|
||||
// full list of cells, writing default values for any cells that are
|
||||
// not in the central regtion.
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
Foam::scalar Foam::PDRmeshArrays::gridPointRelTol = 0.02;
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
void Foam::PDRmeshArrays::classify
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const PDRblock& pdrBlock
|
||||
)
|
||||
{
|
||||
// Additional copy of i-j-k addressing
|
||||
cellDims = pdrBlock.sizes();
|
||||
faceDims = (cellDims + labelVector::one);
|
||||
|
||||
const label maxPointId = cmptMax(pdrBlock.sizes())+1;
|
||||
|
||||
Info<< "Mesh" << nl
|
||||
<< " nPoints:" << mesh.nPoints()
|
||||
<< " nCells:" << mesh.nCells()
|
||||
<< " nFaces:" << mesh.nFaces() << nl;
|
||||
|
||||
Info<< "PDRblock" << nl
|
||||
<< " minEdgeLen:" << pdrBlock.minEdgeLen() << nl;
|
||||
|
||||
|
||||
// Bin points into i-j-k locations
|
||||
List<labelVector> pointIndex(mesh.nPoints());
|
||||
|
||||
for (label pointi=0; pointi < mesh.nPoints(); ++pointi)
|
||||
{
|
||||
const point& pt = mesh.points()[pointi];
|
||||
pointIndex[pointi] = pdrBlock.gridIndex(pt, gridPointRelTol);
|
||||
}
|
||||
|
||||
// Min x,y,z index
|
||||
const labelMinMax invertedLimits(maxPointId, -maxPointId);
|
||||
Vector<labelMinMax> faceLimits;
|
||||
|
||||
const Vector<direction> faceBits
|
||||
(
|
||||
boundBox::XDIR,
|
||||
boundBox::YDIR,
|
||||
boundBox::ZDIR
|
||||
);
|
||||
|
||||
faceIndex.resize(mesh.nFaces());
|
||||
faceOrient.resize(mesh.nFaces());
|
||||
|
||||
for (label facei=0; facei < mesh.nFaces(); ++facei)
|
||||
{
|
||||
faceLimits.x() = faceLimits.y() = faceLimits.z() = invertedLimits;
|
||||
|
||||
for (const label pointi : mesh.faces()[facei])
|
||||
{
|
||||
for (direction cmpt=0; cmpt < labelVector::nComponents; ++cmpt)
|
||||
{
|
||||
faceLimits[cmpt].add(pointIndex[pointi][cmpt]);
|
||||
}
|
||||
}
|
||||
|
||||
direction inPlane(0u);
|
||||
|
||||
for (direction cmpt=0; cmpt < labelVector::nComponents; ++cmpt)
|
||||
{
|
||||
const auto& limits = faceLimits[cmpt];
|
||||
|
||||
if (!limits.valid())
|
||||
{
|
||||
// This should be impossible
|
||||
FatalErrorInFunction
|
||||
<< "Unexpected search failure for " << facei << " in "
|
||||
<< vector::componentNames[cmpt] << "-direction" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
if (limits.min() < 0)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Face " << facei << " contains non-grid point in "
|
||||
<< vector::componentNames[cmpt] << "-direction" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
else if (limits.min() == limits.max())
|
||||
{
|
||||
// In plane
|
||||
inPlane |= faceBits[cmpt];
|
||||
}
|
||||
else if (limits.min() + 1 != limits.max())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Face " << facei
|
||||
<< " not in " << vector::componentNames[cmpt] << "-plane" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
|
||||
switch (inPlane)
|
||||
{
|
||||
case boundBox::XDIR:
|
||||
faceOrient[facei] = vector::X;
|
||||
break;
|
||||
|
||||
case boundBox::YDIR:
|
||||
faceOrient[facei] = vector::Y;
|
||||
break;
|
||||
|
||||
case boundBox::ZDIR:
|
||||
faceOrient[facei] = vector::Z;
|
||||
break;
|
||||
|
||||
default:
|
||||
FatalErrorInFunction
|
||||
<< "Face " << facei << " not in an x/y/z plane?" << nl
|
||||
<< exit(FatalError);
|
||||
break;
|
||||
}
|
||||
|
||||
faceIndex[facei] =
|
||||
labelVector
|
||||
(
|
||||
faceLimits.x().min(),
|
||||
faceLimits.y().min(),
|
||||
faceLimits.z().min()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Bin cells into i-j-k locations
|
||||
cellIndex = std::move(pointIndex);
|
||||
cellIndex = labelVector::uniform(maxPointId);
|
||||
cellIndex.resize(mesh.nCells(), labelVector::uniform(maxPointId));
|
||||
|
||||
// Option 1: use PDRblock.findCell() method
|
||||
if (true)
|
||||
{
|
||||
const pointField& cc = mesh.cellCentres();
|
||||
|
||||
for (label celli=0; celli < mesh.nCells(); ++celli)
|
||||
{
|
||||
cellIndex[celli] = pdrBlock.findCell(cc[celli]);
|
||||
}
|
||||
}
|
||||
|
||||
// Option 2: walk cell faces and use faceIndex information
|
||||
if (false)
|
||||
{
|
||||
for (label celli=0; celli < mesh.nCells(); ++celli)
|
||||
{
|
||||
labelVector& cellIdx = cellIndex[celli];
|
||||
|
||||
for (const label facei : mesh.cells()[celli])
|
||||
{
|
||||
cellIdx.x() = min(cellIdx.x(), faceIndex[facei].x());
|
||||
cellIdx.y() = min(cellIdx.y(), faceIndex[facei].y());
|
||||
cellIdx.z() = min(cellIdx.z(), faceIndex[facei].z());
|
||||
}
|
||||
|
||||
if (cmptMin(cellIdx) < 0)
|
||||
{
|
||||
cellIdx = labelVector(-1,-1,-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Verify that all i-j-k cells were found
|
||||
{
|
||||
// This could be more efficient - but we want to be picky
|
||||
IjkField<bool> cellFound(pdrBlock.sizes(), false);
|
||||
|
||||
for (label celli=0; celli < cellIndex.size(); ++celli)
|
||||
{
|
||||
const labelVector& cellIdx = cellIndex[celli];
|
||||
|
||||
if (cmptMin(cellIdx) >= 0)
|
||||
{
|
||||
cellFound(cellIdx) = true;
|
||||
}
|
||||
}
|
||||
|
||||
label firstMissing = cellFound.find(false);
|
||||
|
||||
if (firstMissing >= 0)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "No cell found for " << pdrBlock.index(firstMissing)
|
||||
<< " indexing"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::PDRmeshArrays::read
|
||||
(
|
||||
const Time& runTime,
|
||||
const PDRblock& pdrBlock
|
||||
)
|
||||
{
|
||||
#include "createPolyMesh.H"
|
||||
classify(mesh, pdrBlock);
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,131 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::PDRmeshArrays
|
||||
|
||||
Description
|
||||
OpenFOAM/PDRblock addressing information
|
||||
|
||||
Provides mapping for a rectilinear OpenFOAM mesh in terms of
|
||||
i-j-k indices for faces and cells.
|
||||
|
||||
The mesh points are first binned according to their i-j-k locations.
|
||||
Next the faces are classified according to their lowest x/y/z
|
||||
coordinates and the face orientation as x/y/z.
|
||||
Orientation in the sense +x or -x is not noted.
|
||||
The cell faces are then examined to determine the appropriate i-j-k
|
||||
location.
|
||||
|
||||
SourceFiles
|
||||
PDRmeshmeshArraysIO.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRmeshArrays_H
|
||||
#define PDRmeshArrays_H
|
||||
|
||||
#include "labelVector.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Forward Declarations
|
||||
class PDRblock;
|
||||
class polyMesh;
|
||||
class Time;
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class PDRmeshArrays Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class PDRmeshArrays
|
||||
{
|
||||
public:
|
||||
|
||||
//- Relative tolerance when matching grid points. Default = 0.02
|
||||
static scalar gridPointRelTol;
|
||||
|
||||
//- The cell i-j-k addressing range
|
||||
labelVector cellDims;
|
||||
|
||||
//- The face i-j-k addressing range
|
||||
labelVector faceDims;
|
||||
|
||||
//- For each cell, the corresponding i-j-k address.
|
||||
List<labelVector> cellIndex;
|
||||
|
||||
//- For each face, the corresponding i-j-k address.
|
||||
List<labelVector> faceIndex;
|
||||
|
||||
//- For each face, the x/y/z orientation
|
||||
List<direction> faceOrient;
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct null
|
||||
PDRmeshArrays() = default;
|
||||
|
||||
|
||||
//- Destructor
|
||||
~PDRmeshArrays() = default;
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- The number of cells
|
||||
label nCells() const
|
||||
{
|
||||
return cellIndex.size();
|
||||
}
|
||||
|
||||
//- The number of faces
|
||||
label nFaces() const
|
||||
{
|
||||
return faceIndex.size();
|
||||
}
|
||||
|
||||
|
||||
//- Determine i-j-k indices for faces/cells
|
||||
void classify(const polyMesh& mesh, const PDRblock& pdrBlock);
|
||||
|
||||
//- Read OpenFOAM mesh and determine i-j-k indices for faces/cells
|
||||
void read(const Time& runTime, const PDRblock& pdrBlock);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
111
applications/utilities/preProcessing/PDRsetFields/PDRparams.C
Normal file
111
applications/utilities/preProcessing/PDRsetFields/PDRparams.C
Normal file
@ -0,0 +1,111 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRparams.H"
|
||||
#include "stringOps.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * Global Data * * * * * * * * * * * * * * * //
|
||||
|
||||
// Global parameter settings
|
||||
Foam::PDRparams Foam::pars;
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
void Foam::PDRparams::readDefaults(const dictionary& dict)
|
||||
{
|
||||
dict.readIfPresent("legacyMeshSpec", legacyMeshSpec);
|
||||
dict.readIfPresent("legacyObsSpec", legacyObsSpec);
|
||||
|
||||
dict.readIfPresent("two_d", two_d);
|
||||
dict.readIfPresent("yCyclic", yCyclic);
|
||||
dict.readIfPresent("ySymmetry", ySymmetry);
|
||||
dict.readIfPresent("deluge", deluge);
|
||||
|
||||
dict.readIfPresent("newFields", new_fields);
|
||||
dict.readIfPresent("noIntersectN", noIntersectN);
|
||||
|
||||
dict.readIfPresent("blockedFacesWallFn", blockedFacesWallFn);
|
||||
dict.readIfPresent("ignoreGratings", ignoreGratings);
|
||||
|
||||
outer_orthog = dict.found("outer_orthog");
|
||||
|
||||
dict.readIfPresent("debugLevel", debugLevel);
|
||||
dict.readIfPresent("nFacesToBlockC", nFacesToBlockC);
|
||||
dict.readIfPresent("nPairsToBlockC", nPairsToBlockC);
|
||||
dict.readIfPresent("overlaps", overlaps);
|
||||
|
||||
dict.readIfPresent("gridPointTol", gridPointTol);
|
||||
|
||||
dict.readIfPresent("Cb_r", cb_r);
|
||||
dict.readIfPresent("Cb_s", cb_s);
|
||||
|
||||
dict.readIfPresent("Cd_r", cd_r);
|
||||
dict.readIfPresent("Cd_s", cd_s);
|
||||
|
||||
dict.readIfPresent("congRegionMaxBetav", cong_max_betav);
|
||||
|
||||
dict.readIfPresent("min_overlap_vol", min_overlap_vol);
|
||||
dict.readIfPresent("min_overlap_area", min_overlap_area);
|
||||
dict.readIfPresent("min_width", min_width);
|
||||
dict.readIfPresent("empty_lobs_fac", empty_lobs_fac);
|
||||
dict.readIfPresent("outerCombFac", outerCombFac);
|
||||
dict.readIfPresent("obs_expand", obs_expand);
|
||||
|
||||
dict.readIfPresent("def_grating_slat_w", def_grating_slat_w);
|
||||
dict.readIfPresent("blockedCellPoros", blockedCellPoros);
|
||||
dict.readIfPresent("blockedFacePar", blockedFacePar);
|
||||
dict.readIfPresent("maxCR", maxCR);
|
||||
|
||||
dict.readIfPresent("blockageNoCT", blockageNoCT);
|
||||
dict.readIfPresent("scale", scale);
|
||||
|
||||
UPatchBc = "fixedValue;value uniform (0 0 0)";
|
||||
if (dict.readIfPresent("UPatchBc", UPatchBc))
|
||||
{
|
||||
stringOps::inplaceTrim(UPatchBc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::PDRparams::read(const dictionary& dict)
|
||||
{
|
||||
readDefaults(dict);
|
||||
|
||||
dict.readEntry("obsFileDir", obsfile_dir);
|
||||
dict.readEntry("obsFileNames", obsfile_names);
|
||||
|
||||
stringOps::inplaceExpand(obsfile_dir);
|
||||
|
||||
for (auto& f : obsfile_names)
|
||||
{
|
||||
stringOps::inplaceExpand(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
165
applications/utilities/preProcessing/PDRsetFields/PDRparams.H
Normal file
165
applications/utilities/preProcessing/PDRsetFields/PDRparams.H
Normal file
@ -0,0 +1,165 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2018-2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::PDRparams
|
||||
|
||||
Description
|
||||
Parameters for PDRsetFields
|
||||
|
||||
SourceFiles
|
||||
PDRparams.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRparams_H
|
||||
#define PDRparams_H
|
||||
|
||||
#include "labelList.H"
|
||||
#include "scalarList.H"
|
||||
#include "wordList.H"
|
||||
#include "fileNameList.H"
|
||||
#include "dictionary.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class PDRparams Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class PDRparams
|
||||
{
|
||||
public:
|
||||
|
||||
// Data Members
|
||||
|
||||
fileName obsfile_dir;
|
||||
wordList obsfile_names;
|
||||
word timeName;
|
||||
string UPatchBc; //!< "fixedValue;value uniform (0 0 0)"
|
||||
|
||||
bool legacyMeshSpec{false};
|
||||
bool legacyObsSpec{false};
|
||||
|
||||
bool two_d{false};
|
||||
bool yCyclic{false};
|
||||
bool ySymmetry{false};
|
||||
bool deluge{false};
|
||||
bool new_fields{true};
|
||||
bool noIntersectN{true};
|
||||
bool blockedFacesWallFn{false};
|
||||
bool ignoreGratings{false};
|
||||
bool outer_orthog{false};
|
||||
|
||||
int debugLevel{0};
|
||||
|
||||
//- Min number of blocked cell faces
|
||||
//- for a cell to be marked as blocked
|
||||
int nFacesToBlockC{6};
|
||||
|
||||
//- Min number of blocked cell face pairs (on opposite faces of a cell)
|
||||
//- for a cell to be marked as blocked
|
||||
int nPairsToBlockC{3};
|
||||
|
||||
//- Flag to control which overlap calculations are performed
|
||||
int overlaps{0x7};
|
||||
|
||||
scalar gridPointTol{0.02};
|
||||
|
||||
scalar cb_r{0.035};
|
||||
scalar cb_s{0.08};
|
||||
|
||||
scalar cd_r{1.2};
|
||||
scalar cd_s{2.0};
|
||||
|
||||
scalar cong_max_betav{1.0};
|
||||
|
||||
scalar min_overlap_vol{0};
|
||||
scalar min_overlap_area{0};
|
||||
|
||||
//- Ignore obstacles with second dimension (or diameter) less than this
|
||||
scalar min_width{0.001};
|
||||
|
||||
//- Lobs in empty cell is this * cube root of cell volume
|
||||
scalar empty_lobs_fac{1.0};
|
||||
|
||||
//- Value for outer region
|
||||
scalar outerCombFac{1.0};
|
||||
|
||||
scalar obs_expand{0};
|
||||
|
||||
//- Default slat thickness grating
|
||||
scalar def_grating_slat_w{0.005};
|
||||
|
||||
//- Cells with porosity less than this are blocked
|
||||
scalar blockedCellPoros{0.05};
|
||||
|
||||
//- Faces with area blockage greater than this are blocked
|
||||
scalar blockedFacePar{0.95};
|
||||
|
||||
//- Upper limit on CR (CT also gets limited)
|
||||
scalar maxCR{1e30};
|
||||
|
||||
//- If a single obstacle blocks a cell by more than this,
|
||||
//- then no CT in that direction
|
||||
scalar blockageNoCT{0.95};
|
||||
|
||||
//- Overall scale factor
|
||||
scalar scale{1.0};
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct null
|
||||
PDRparams() = default;
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- Set or read defaults from dictionary.
|
||||
// Can also be used with an empty dictionary
|
||||
void readDefaults(const dictionary& dict);
|
||||
|
||||
//- Read program parameters from dictionary
|
||||
void read(const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
extern Foam::PDRparams pars; //!< Globals for program parameters (ugly hack)
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,52 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRpatchDef.H"
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
const Foam::Enum
|
||||
<
|
||||
Foam::PDRpatchDef::predefined
|
||||
>
|
||||
Foam::PDRpatchDef::names
|
||||
({
|
||||
{ predefined::BLOCKED_FACE, "blockedFaces" },
|
||||
{ predefined::MERGING_PATCH, "mergingFaces" },
|
||||
{ predefined::WALL_PATCH, "wallFaces" },
|
||||
});
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
|
||||
|
||||
void Foam::PDRpatchDef::operator=(const std::string& newName)
|
||||
{
|
||||
patchName = word::validate(newName);
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
123
applications/utilities/preProcessing/PDRsetFields/PDRpatchDef.H
Normal file
123
applications/utilities/preProcessing/PDRsetFields/PDRpatchDef.H
Normal file
@ -0,0 +1,123 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::PDRpatchDef
|
||||
|
||||
Description
|
||||
Bookkeeping for patch definitions
|
||||
|
||||
SourceFiles
|
||||
PDRpatchDef.H
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRpatchDef_H
|
||||
#define PDRpatchDef_H
|
||||
|
||||
#include "string.H"
|
||||
#include "scalar.H"
|
||||
#include "Enum.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class PDRpatchDef Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class PDRpatchDef
|
||||
{
|
||||
public:
|
||||
|
||||
//- Patch predefines
|
||||
enum predefined
|
||||
{
|
||||
BLOCKED_FACE = 0,
|
||||
MERGING_PATCH = 1,
|
||||
WALL_PATCH = 2,
|
||||
LAST_PREDEFINED = 2, // First user patch number will be 1 more
|
||||
NUM_PREDEFINED = 3
|
||||
};
|
||||
|
||||
//- Names for predefined types
|
||||
static const Enum<predefined> names;
|
||||
|
||||
|
||||
// Data Members
|
||||
|
||||
word patchName;
|
||||
|
||||
label patchType;
|
||||
|
||||
scalar blowoffPress;
|
||||
|
||||
scalar blowoffTime;
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct null
|
||||
PDRpatchDef()
|
||||
:
|
||||
patchName(),
|
||||
patchType(0),
|
||||
blowoffPress(0),
|
||||
blowoffTime(0)
|
||||
{}
|
||||
|
||||
//- Construct with given patch name
|
||||
explicit PDRpatchDef(const word& name)
|
||||
:
|
||||
patchName(name),
|
||||
patchType(0),
|
||||
blowoffPress(0),
|
||||
blowoffTime(0)
|
||||
{}
|
||||
|
||||
|
||||
//- Construct with given patch name
|
||||
PDRpatchDef& operator=(const PDRpatchDef&) = default;
|
||||
PDRpatchDef& operator=(PDRpatchDef&&) = default;
|
||||
|
||||
//- Assign new patch name
|
||||
void operator=(const std::string& newName);
|
||||
};
|
||||
|
||||
|
||||
typedef PDRpatchDef PATCH;
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
351
applications/utilities/preProcessing/PDRsetFields/PDRsetFields.C
Normal file
351
applications/utilities/preProcessing/PDRsetFields/PDRsetFields.C
Normal file
@ -0,0 +1,351 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Applications
|
||||
PDRsetFields
|
||||
|
||||
Description
|
||||
Preparation of fields for PDRFoam
|
||||
|
||||
SourceFiles
|
||||
PDRsetFields.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "argList.H"
|
||||
#include "Time.H"
|
||||
#include "IOdictionary.H"
|
||||
|
||||
#include "PDRsetFields.H"
|
||||
#include "PDRlegacy.H"
|
||||
#include "PDRutils.H"
|
||||
#include "IOmanip.H"
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
// Main program:
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
argList::addNote
|
||||
(
|
||||
"Processes a set of geometrical obstructions to determine the"
|
||||
" equivalent blockage effects when setting cases for PDRFoam"
|
||||
);
|
||||
argList::noParallel();
|
||||
argList::noFunctionObjects();
|
||||
|
||||
argList::addOption
|
||||
(
|
||||
"time",
|
||||
"time",
|
||||
"Specify a time"
|
||||
);
|
||||
|
||||
argList::addOption("dict", "file", "Use alternative PDRsetFieldsDict");
|
||||
|
||||
argList::addBoolOption
|
||||
(
|
||||
"legacy",
|
||||
"Force use of legacy obstacles table"
|
||||
);
|
||||
|
||||
argList::addBoolOption
|
||||
(
|
||||
"dry-run",
|
||||
"Read obstacles and write VTK only"
|
||||
);
|
||||
|
||||
#include "setRootCase.H"
|
||||
#include "createTime.H"
|
||||
|
||||
const word dictName("PDRsetFieldsDict");
|
||||
#include "setSystemRunTimeDictionaryIO.H"
|
||||
|
||||
Info<< "Reading " << dictName << "\n" << endl;
|
||||
|
||||
IOdictionary setFieldsDict(dictIO);
|
||||
|
||||
const bool dryrun = args.found("dry-run");
|
||||
|
||||
const fileName& casepath = runTime.globalPath();
|
||||
|
||||
pars.timeName = "0";
|
||||
args.readIfPresent("time", pars.timeName);
|
||||
|
||||
// Program parameters (globals)
|
||||
pars.read(setFieldsDict);
|
||||
|
||||
if (args.found("legacy"))
|
||||
{
|
||||
pars.legacyObsSpec = true;
|
||||
}
|
||||
|
||||
// Always have the following:
|
||||
// 0 = blockedFaces patch (no wall functions)
|
||||
// 1 = mergingFaces patch
|
||||
// 2 = wallFaces patch
|
||||
|
||||
DynamicList<PDRpatchDef> patches;
|
||||
patches.resize(PDRpatchDef::NUM_PREDEFINED);
|
||||
|
||||
for
|
||||
(
|
||||
PDRpatchDef::predefined predef :
|
||||
{
|
||||
PDRpatchDef::BLOCKED_FACE,
|
||||
PDRpatchDef::MERGING_PATCH,
|
||||
PDRpatchDef::WALL_PATCH,
|
||||
}
|
||||
)
|
||||
{
|
||||
patches[predef] = PDRpatchDef::names[predef];
|
||||
}
|
||||
|
||||
|
||||
// Dimensions and grid points for i-j-k domain
|
||||
PDRblock pdrBlock;
|
||||
|
||||
if (pars.legacyMeshSpec)
|
||||
{
|
||||
PDRlegacy::read_mesh_spec(casepath, pdrBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
IOdictionary iodict
|
||||
(
|
||||
IOobject
|
||||
(
|
||||
"PDRblockMeshDict",
|
||||
runTime.system(),
|
||||
runTime,
|
||||
IOobject::MUST_READ_IF_MODIFIED,
|
||||
IOobject::NO_WRITE
|
||||
)
|
||||
);
|
||||
|
||||
pdrBlock.read(iodict);
|
||||
|
||||
#ifdef FULLDEBUG
|
||||
PDRlegacy::print_info(pdrBlock);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Storage for obstacles and cylinder-like obstacles
|
||||
DynamicList<PDRobstacle> obstacles, cylinders;
|
||||
|
||||
// Read in obstacles
|
||||
const scalar volObstacles =
|
||||
(
|
||||
pars.legacyObsSpec
|
||||
? PDRobstacle::legacyReadFiles
|
||||
(
|
||||
pars.obsfile_dir, pars.obsfile_names,
|
||||
pdrBlock.bounds(),
|
||||
obstacles,
|
||||
cylinders
|
||||
)
|
||||
: PDRobstacle::readFiles
|
||||
(
|
||||
pars.obsfile_dir, pars.obsfile_names,
|
||||
pdrBlock.bounds(),
|
||||
obstacles,
|
||||
cylinders
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
PDRobstacle::generateVtk(casepath/"VTK", obstacles, cylinders);
|
||||
|
||||
if (dryrun)
|
||||
{
|
||||
Info<< nl
|
||||
<< "dry-run: stopping after reading/writing obstacles" << nl
|
||||
<< "\nEnd\n" << nl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Bookkeeping of the ranges within the obstacle list
|
||||
|
||||
// Original blockage at the start
|
||||
const labelRange origBlocks(0, obstacles.size());
|
||||
|
||||
// Intersection blockage
|
||||
labelRange interBlocks(origBlocks.after(), 0);
|
||||
|
||||
scalar volSubtract = 0;
|
||||
|
||||
// Do binary intersections between blocks and cylinders (or diag-beam)
|
||||
// by creating -ve blocks at the overlap
|
||||
|
||||
labelRange int1Blocks(origBlocks.after(), 0);
|
||||
|
||||
if (pars.overlaps % 2 > 0)
|
||||
{
|
||||
Info<< " block/cylinder intersections" << endl;
|
||||
|
||||
label nblocked = obstacles.size();
|
||||
|
||||
volSubtract += block_cylinder_overlap(obstacles, origBlocks, cylinders);
|
||||
|
||||
nblocked = (obstacles.size() - nblocked);
|
||||
|
||||
interBlocks += nblocked;
|
||||
int1Blocks += nblocked;
|
||||
}
|
||||
|
||||
// Do binary intersections between blocks
|
||||
// by creating -ve blocks at the overlap
|
||||
|
||||
labelRange int2Blocks(int1Blocks.after(), 0);
|
||||
if (pars.overlaps % 4 > 1)
|
||||
{
|
||||
Info<< " block/block intersections" << endl;
|
||||
|
||||
label nblocked = obstacles.size();
|
||||
|
||||
volSubtract += block_overlap(obstacles, origBlocks, 1.0);
|
||||
|
||||
nblocked = (obstacles.size() - nblocked);
|
||||
|
||||
interBlocks += nblocked;
|
||||
int2Blocks += nblocked;
|
||||
}
|
||||
|
||||
// Correct for triple intersections
|
||||
// by looking for overlaps between the -ve blocks just created
|
||||
|
||||
labelRange int3Blocks(int2Blocks.after(), 0);
|
||||
if (pars.overlaps % 8 > 3)
|
||||
{
|
||||
Info<< " triple intersections" << endl;
|
||||
|
||||
label nblocked = obstacles.size();
|
||||
|
||||
volSubtract += block_overlap(obstacles, interBlocks, 1.0/3.0);
|
||||
|
||||
nblocked = (obstacles.size() - nblocked);
|
||||
|
||||
interBlocks += nblocked;
|
||||
int3Blocks += nblocked;
|
||||
}
|
||||
|
||||
|
||||
// The field arrays, in one structure pass around easily
|
||||
PDRarrays arr(pdrBlock);
|
||||
|
||||
Info<< "Apply blockage" << endl;
|
||||
|
||||
// Create blockage and count arrays by working through
|
||||
// real and extra blocks and cylinders
|
||||
|
||||
// User-defined negative blocks. Use "sign" to distinguish
|
||||
if (origBlocks.size())
|
||||
{
|
||||
Info<< " negative blocks: " << origBlocks.size() << nl;
|
||||
|
||||
for (const PDRobstacle& obs : obstacles[origBlocks])
|
||||
{
|
||||
arr.addBlockage(obs, patches, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// Do the intersection blocks positive and negative
|
||||
// These are done first so that negative area blockage cancels positive
|
||||
|
||||
if (interBlocks.size())
|
||||
{
|
||||
Info<< " blocks " << interBlocks.size() << nl;
|
||||
|
||||
for (const PDRobstacle& obs : obstacles[interBlocks])
|
||||
{
|
||||
arr.addBlockage(obs, patches, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// The positive real bocks
|
||||
if (origBlocks.size())
|
||||
{
|
||||
Info<< " positive blocks: " << origBlocks.size() << nl;
|
||||
|
||||
for (const PDRobstacle& obs : obstacles[origBlocks])
|
||||
{
|
||||
arr.addBlockage(obs, patches, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// The cylinders
|
||||
if (cylinders.size())
|
||||
{
|
||||
Info<< " cylinders: " << cylinders.size() << nl;
|
||||
|
||||
for (const PDRobstacle& obs : cylinders)
|
||||
{
|
||||
arr.addCylinder(obs);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculation of the fields of drag, turbulence
|
||||
// generation and combustion enhancement
|
||||
|
||||
arr.blockageSummary();
|
||||
|
||||
// Mapping of OpenFOAM cells/faces to i-j-k indices
|
||||
PDRmeshArrays meshIdx;
|
||||
meshIdx.gridPointRelTol = pars.gridPointTol;
|
||||
|
||||
meshIdx.read(runTime, pdrBlock);
|
||||
|
||||
PDRarrays::calculateAndWrite(arr, meshIdx, casepath, patches);
|
||||
|
||||
Info<< nl
|
||||
<< setw(6) << origBlocks.size() << " blocks and "
|
||||
<< cylinders.size() << " cylinders/diagonal blocks" << nl;
|
||||
|
||||
Info<< setw(6) << int2Blocks.size()
|
||||
<< " intersections amongst blocks" << nl;
|
||||
|
||||
Info<< setw(6) << int1Blocks.size()
|
||||
<< " intersections between blocks and cyl/beams" << nl;
|
||||
|
||||
Info<< setw(6) << int1Blocks.size()
|
||||
<< "/3 triple intersections" << nl;
|
||||
|
||||
Info<< "Volume of obstacles read in: " << volObstacles
|
||||
<< ", volume of intersections: " << volSubtract << nl;
|
||||
|
||||
Info<< nl << "After corrections:" << nl;
|
||||
arr.blockageSummary();
|
||||
|
||||
Info<< nl << "\nEnd\n" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
142
applications/utilities/preProcessing/PDRsetFields/PDRsetFields.H
Normal file
142
applications/utilities/preProcessing/PDRsetFields/PDRsetFields.H
Normal file
@ -0,0 +1,142 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Description
|
||||
Preparation of fields for PDRFoam
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRsetFields_H
|
||||
#define PDRsetFields_H
|
||||
|
||||
#include "PDRarrays.H"
|
||||
#include "PDRblock.H"
|
||||
#include "PDRmeshArrays.H"
|
||||
#include "PDRobstacle.H"
|
||||
#include "PDRpatchDef.H"
|
||||
#include "PDRparams.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
|
||||
//YCYCLIC is set to 1 for running the test cases with a cyclic boundry condition in the y direction
|
||||
//TWO_D is set to 1 for running the 2D test cases (no z direction) - usually same case as YCYCLIC
|
||||
//Now specified in CAD_PDRDict and read in as globals.
|
||||
|
||||
// The program also labels intersection obstacles as types 96 and 86, but not then use these values
|
||||
// Obstacles in group definitions have a multiple of 100 added to the type number
|
||||
|
||||
#define floatSMALL 1.e-10
|
||||
|
||||
// Default initial values of field variables, used outside congested area,
|
||||
//and everywhere for uniform fields. They atre strings because same routines
|
||||
//are used to create b.c.s for scalars and tensors.
|
||||
#define DEFAULT_K 0.00015
|
||||
#define DEFAULT_EPS 1e-5
|
||||
#define DEFAULT_T 300
|
||||
#define DEFAULT_P 100000
|
||||
#define DEFAULT_SU 0.5
|
||||
#define DEFAULT_LOBS 0.1 // Does not matter what it is outside congestion
|
||||
// but zero would cause problems with Ep
|
||||
#define DEFAULT_EP 0.01 // Gives length scale 0.1, calc. as (Xp-0.999)/Ep with Xp=1
|
||||
|
||||
// Boundary conditions on walls for all variables where it is not "zero_gradient"
|
||||
#define K_WALL_FN "kqRWallFunction"
|
||||
#define EPS_WALL_FN "epsilonWallFunction"
|
||||
#define ALPHAT_WALL "nutkWallFunction"
|
||||
#define MUT_WALL_FN "mutkWallFunction"
|
||||
#define NUT_WALL_FN "nutkWallFunction"
|
||||
|
||||
#define K_WALL_FN_LEGACY "compressible::kqRWallFunction"
|
||||
#define EPS_WALL_FN_LEGACY "compressible::epsilonWallFunction"
|
||||
#define ALPHAT_WALL_FN_LEGACY "alphatWallFunction;\n\t\tPrt\t0.85"
|
||||
|
||||
|
||||
// The following parameters are used to decide when there arMAX_Ne sufficient (parts of)
|
||||
// obstacles ina cell for them to define the length scale of the generated turbulence.
|
||||
#define MIN_AB_FOR_SIZE 0.002
|
||||
#define MAX_VB_FOR_SIZE 0.9
|
||||
#define COUNT_FOR_SIZE 0.1
|
||||
#define MIN_COUNT_FOR_SIZE 0.05
|
||||
|
||||
// These define how blocked a face or cell has to be for removal from the mesh
|
||||
//#define BLOCKED_CELL_PAR 0.05 //<- Now pars.blockedCellPoros
|
||||
//#define BLOCKED_FACE_PAR 0.95 //<- Now pars.blockedFacePar
|
||||
|
||||
|
||||
//- Calculate block/block overlaps
|
||||
//
|
||||
// Binary self-intersections are to be checked for blocks.
|
||||
// Resulting negative blocks are appended to blocks.
|
||||
// These new blocks have the opposite sign from input blocks, and
|
||||
// blockage multiplied by multiplier.
|
||||
//
|
||||
// If the number of newly generated blocks is required, check the size
|
||||
// of blocks on output vs input to see how many have been added.
|
||||
//
|
||||
// \param[in,out] blocks
|
||||
// \param[in] range - the range within blocks to be examined
|
||||
//
|
||||
// \return overlap volume
|
||||
scalar block_overlap
|
||||
(
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
const labelRange& range,
|
||||
const scalar multiplier = 1.0
|
||||
);
|
||||
|
||||
|
||||
//- Calculate block/cylinder overlaps
|
||||
//
|
||||
// Binary intersections are to be checked for blocks and cylinders.
|
||||
// Resulting negative blocks are appended to blocks.
|
||||
// These new blocks have the opposite sign from input blocks, and
|
||||
// blockage multiplied by multiplier.
|
||||
//
|
||||
// If the number of newly generated blocks is required, check the size
|
||||
// of blocks on output vs input to see how many have been added.
|
||||
//
|
||||
// \param[in,out] arrp
|
||||
// \param[in,out] blocks
|
||||
// \param[in] range - the range within blocks to be examined
|
||||
// \param[in] cylinders - the cylinders to be examined
|
||||
//
|
||||
// \return overlap volume
|
||||
scalar block_cylinder_overlap
|
||||
(
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
const labelRange& range,
|
||||
const UList<PDRobstacle>& cylinders
|
||||
);
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
62
applications/utilities/preProcessing/PDRsetFields/PDRutils.H
Normal file
62
applications/utilities/preProcessing/PDRsetFields/PDRutils.H
Normal file
@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Namespace
|
||||
Foam::PDRutils
|
||||
|
||||
Description
|
||||
Utilities for PDR (eg, for setFields)
|
||||
|
||||
SourceFiles
|
||||
PDRUtils.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRutils_H
|
||||
#define PDRutils_H
|
||||
|
||||
#include "PDRarrays.H"
|
||||
#include "PDRblock.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Forward Declarations
|
||||
class PDRobstacle;
|
||||
|
||||
namespace PDRutils
|
||||
{
|
||||
|
||||
|
||||
} // End namespace PDRutils
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,221 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Namespace
|
||||
Foam::PDRutils
|
||||
|
||||
Description
|
||||
Utilities for PDR (eg, for setFields). Internal usage only.
|
||||
|
||||
The C lineage of the original code is still evident in the use of
|
||||
pointers instead of references.
|
||||
This will be addressed in later versions of the code (2019-12).
|
||||
|
||||
SourceFiles
|
||||
PDRUtils.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRutilsInternal_H
|
||||
#define PDRutilsInternal_H
|
||||
|
||||
#include "PDRutils.H"
|
||||
#include "PDRarrays.H"
|
||||
#include "PDRblock.H"
|
||||
#include "symmTensor2D.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace PDRutils
|
||||
{
|
||||
|
||||
//- Determine 1-D overlap locations for a geometric entity
|
||||
//
|
||||
// \param[in] xmin - min position of the geometric entity
|
||||
// \param[in] xmax - max position of the geometric entity
|
||||
// \param[in] grid - grid point information
|
||||
// \param[out] olap - Fraction of cell-width with overlap
|
||||
// 0 for no overlap, 1 for full overlap.
|
||||
// \param[out] cmin - first cell index (inclusive) with overlap,
|
||||
// values in the range \c [0,nCells]
|
||||
// \param[out] cmax - last cell index (inclusive) with overlap,
|
||||
// values in the range \c [0,nCells]
|
||||
// \param[out] cfmin - first cell index (inclusive) with staggered face,
|
||||
// values in the range \c [0,nCells]
|
||||
// \param[out] cfmax - last cell index (inclusive) with staggered face,
|
||||
// values in the range \c [0,nCells]
|
||||
void one_d_overlap
|
||||
(
|
||||
scalar xmin,
|
||||
scalar xmax,
|
||||
const PDRblock::location& grid,
|
||||
List<scalar>& olap,
|
||||
int *cmin, int *cmax,
|
||||
int *cfmin, int *cfmax
|
||||
);
|
||||
|
||||
|
||||
//- Combine two 1D overlaps.
|
||||
// Multiplying the two 1-d overlaps yields the proportion of each (2D) cell
|
||||
// that is covered.
|
||||
//
|
||||
// \note We go one over the relevant min/max limits since these
|
||||
// values might be used.
|
||||
// The 1D arrays will have bee initially zeroed throughout.
|
||||
void two_d_overlap
|
||||
(
|
||||
const UList<scalar>& a_olap, label amin, label amax,
|
||||
const UList<scalar>& b_olap, label bmin, label bmax,
|
||||
SquareMatrix<scalar>& ab_olap
|
||||
);
|
||||
|
||||
|
||||
//- Calculate the proportion of each (two-dimensional) grid cell
|
||||
//- overlapped by the circle or angled rectangle.
|
||||
//
|
||||
// Coordinates are labelled a and b.
|
||||
//
|
||||
// \param[in] ac, bc coordinates of centre of circle or rectangle
|
||||
// \param[in] dia diameter of circle (zero for rectangle)
|
||||
// \param[in] theta, wa, wb parameters for rectangle
|
||||
// \param[in] amin, amax first and last cells in a-grid overlapped by object
|
||||
// \param[in] agrid locations of grid lines of a-grid
|
||||
// \param[in] amin, amax first and last cells in b-grid overlapped by object
|
||||
// \param[in] bgrid locations of grid lines of b-grid
|
||||
//
|
||||
// \param[out] abolap
|
||||
// 2-D array of (proportionate) area blockage by grid cell
|
||||
// \param[out] a_lblock
|
||||
// 2-D array of (proportionate) blockage to a-direction flow
|
||||
// (This will be area blockage when extruded in the third
|
||||
// coordinate).
|
||||
//
|
||||
// \param[out] a_count
|
||||
// 2-D array The contribution of this object to the count of
|
||||
// obstacles blocking a-direction flow. This is only non-zero if the
|
||||
// object is inside the lateral boundaries of the cell. It is large
|
||||
// negative if the cell is totally blocked in this direction.
|
||||
//
|
||||
//
|
||||
// \param[out] c_drag
|
||||
//
|
||||
// 2-D array of tensor that will give tensor drag in each cell (when
|
||||
// multiplied Cd, cylinder length, and 0.5 rho*U^2) Dimension: L.
|
||||
//
|
||||
// \note this routine does not zero array elements outside the amin
|
||||
// to amax, bmin to bmax area.
|
||||
void circle_overlap
|
||||
(
|
||||
scalar ac, scalar bc, scalar dia,
|
||||
scalar theta, scalar wa, scalar wb,
|
||||
const PDRblock::location& agrid, label amin, label amax,
|
||||
const PDRblock::location& bgrid, label bmin, label bmax,
|
||||
SquareMatrix<scalar>& ab_olap,
|
||||
SquareMatrix<scalar>& ab_perim,
|
||||
SquareMatrix<scalar>& a_lblock,
|
||||
SquareMatrix<scalar>& ac_lblock,
|
||||
SquareMatrix<scalar>& c_count,
|
||||
SquareMatrix<symmTensor2D>& c_drag,
|
||||
SquareMatrix<scalar>& b_lblock,
|
||||
SquareMatrix<scalar>& bc_lblock
|
||||
);
|
||||
|
||||
|
||||
//- Area of intersection between circle and rectangle.
|
||||
//
|
||||
// Calculates the area of intersection between the circle, centre (xc, yc), radius rad,
|
||||
// and the rectangle with sides at x = x1 & x2, and y = y1 and y2.
|
||||
//
|
||||
// The return value is the fraction of the rectangle's area covered by the circle.
|
||||
double inters_cy
|
||||
(
|
||||
double xc, //!< circle centre (x)
|
||||
double yc, //!< circle centre (y)
|
||||
double rad, //!< circle radius
|
||||
double x1, double x2,
|
||||
double y1, double y2,
|
||||
scalar* perim_p,
|
||||
scalar* x_proj_edge_p,
|
||||
scalar* y_proj_edge_p,
|
||||
scalar* x_overlap_p,
|
||||
scalar* y_overlap_p
|
||||
);
|
||||
|
||||
|
||||
//- The area overlap in the plane of a diagonal block and a cell.
|
||||
//
|
||||
// Calculates the overlap, in the plane of a diagonal block and a cell,
|
||||
// plus blockage and drag parameters.
|
||||
// Note that x and y herein may be any two of the three coordinates - would have been better not to label them x and y.
|
||||
//
|
||||
// On entry:
|
||||
// xc, yc Coordinates of axis of d.b.
|
||||
// theta, wa, wb Angle and widths
|
||||
//
|
||||
// The returned parameters will be multipled by the length of the obstacle's intersection with
|
||||
// the third dimension of the 3-D cell to give this obstacle's contribution to the count, drag
|
||||
// and area blockages.
|
||||
// The return value is the area of intersection, which will multiply to volume blockage.
|
||||
//
|
||||
double inters_db
|
||||
(
|
||||
double xc, double yc, double theta,
|
||||
double wa, double wb,
|
||||
double x1, double x2,
|
||||
double y1, double y2,
|
||||
scalar* count_p,
|
||||
symmTensor2D& vdrag, scalar* perim_p,
|
||||
scalar* x_lblk, scalar* y_lblk,
|
||||
scalar* x_centre_p, scalar* y_centre_p
|
||||
);
|
||||
|
||||
|
||||
/* Calculates the blockage to x-direction flow presented by the specified circle on
|
||||
the specified rectangle.
|
||||
Becomes the area blockage when extruded to in the third dimension.
|
||||
In other words, it is the projection on the y axis of the intersection between the
|
||||
circle and the rectangle.
|
||||
Returns fraction blocked
|
||||
Note that x and y in this routine may in fact be any two of the three dimensions.
|
||||
*/
|
||||
double l_blockage
|
||||
(
|
||||
double xc, double yc, double rad,
|
||||
double x1, double x2,
|
||||
double y1, double y2,
|
||||
scalar* count_p, scalar* drag_p, scalar* centre_p
|
||||
);
|
||||
|
||||
|
||||
} // End namespace PDRutils
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,712 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRsetFields.H"
|
||||
#include "PDRutilsInternal.H"
|
||||
#include "mathematicalConstants.H"
|
||||
|
||||
#ifndef FULLDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#include <cassert>
|
||||
|
||||
using namespace Foam::constant;
|
||||
|
||||
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Calculate the area of the sector of a circle whose ends are at
|
||||
// (dxa, dya) and (dxb, dyb) relative to the centre. radsqu is radius
|
||||
// squared.
|
||||
//
|
||||
// (We trust that this is consistent with the other parameters..)
|
||||
inline static scalar sector
|
||||
(
|
||||
scalar dxa, scalar dya,
|
||||
scalar dxb, scalar dyb
|
||||
)
|
||||
{
|
||||
scalar angle = (::atan2(dyb, dxb) - ::atan2(dya, dxa));
|
||||
|
||||
if (angle < -1.0E-10)
|
||||
{
|
||||
angle += mathematical::twoPi;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
double Foam::PDRutils::inters_cy
|
||||
(
|
||||
double xc, double yc, double rad,
|
||||
double x1, double x2,
|
||||
double y1, double y2,
|
||||
scalar* perim_p,
|
||||
scalar* x_proj_edge_p, scalar* y_proj_edge_p,
|
||||
scalar* x_overlap_p, scalar* y_overlap_p
|
||||
)
|
||||
{
|
||||
double angle, area, del;
|
||||
double x_int[6][2], y_int[6][2]; // Coordinates of intersections between the circle and sides of the rectangle.
|
||||
double x_arc[6][2], y_arc[6][2]; // Coordinates of end orc (within cell) for each quadrant
|
||||
double dx[6], dy[6];
|
||||
|
||||
double x_olap_min = GREAT;
|
||||
double x_olap_max = -GREAT;
|
||||
double y_olap_min = GREAT;
|
||||
double y_olap_max = -GREAT;
|
||||
int n_vert, n_oppv;
|
||||
int no_intersection;
|
||||
|
||||
const double dx1 = (x1 - xc);
|
||||
const double dx2 = (x2 - xc);
|
||||
const double dy1 = (y1 - yc);
|
||||
const double dy2 = (y2 - yc);
|
||||
|
||||
const double dx1squ = dx1 * dx1;
|
||||
const double dx2squ = dx2 * dx2;
|
||||
const double dy1squ = dy1 * dy1;
|
||||
const double dy2squ = dy2 * dy2;
|
||||
const double radsqu = rad * rad;
|
||||
|
||||
|
||||
/* Going clockwise from (x1, y1), vertices are labelled 1,2,3,4, with 0 the same as 4
|
||||
and 5 the same as 1 (so that we can find the vertices on either side of any of 1 to 4).*/
|
||||
|
||||
dx[1] = dx1; dy[1] = dy1;
|
||||
dx[2] = dx1; dy[2] = dy2;
|
||||
dx[3] = dx2; dy[3] = dy2;
|
||||
dx[4] = dx2; dy[4] = dy1;
|
||||
dx[0] = dx2; dy[0] = dy1;
|
||||
dx[5] = dx1; dy[5] = dy1;
|
||||
|
||||
// The positions of the ends of the arcs, if these points are
|
||||
// inside the cell, they will be changed, if necessary, below.
|
||||
|
||||
x_arc[2][0] = x_arc[1][1] = -rad; y_arc[2][0] = y_arc[1][1] = 0.0;
|
||||
x_arc[3][0] = x_arc[2][1] = 0.0; y_arc[3][0] = y_arc[2][1] = rad;
|
||||
x_arc[4][0] = x_arc[3][1] = rad; y_arc[4][0] = y_arc[3][1] = 0.0;
|
||||
x_arc[1][0] = x_arc[4][1] = 0.0; y_arc[1][0] = y_arc[4][1] = -rad;
|
||||
|
||||
// We catch arcs that are entirely inside the rectangle
|
||||
// Note: this is wrong for a circle completely outside, but that
|
||||
// will be dealt with separately
|
||||
|
||||
int arc_in[6] = { /* zero-initialied */ };
|
||||
arc_in[1] = (dx1 < -rad && dy1 < -rad) ? 1 : 0;
|
||||
arc_in[2] = (dx1 < -rad && dy2 > rad) ? 1 : 0;
|
||||
arc_in[3] = (dx2 > rad && dy2 > rad) ? 1 : 0;
|
||||
arc_in[4] = (dx2 > rad && dy1 < -rad) ? 1 : 0;
|
||||
|
||||
// Work out which vertices are in the circle
|
||||
|
||||
int vert_in[6];
|
||||
vert_in[1] = (dx1squ + dy1squ <= radsqu);
|
||||
vert_in[2] = (dx1squ + dy2squ <= radsqu);
|
||||
vert_in[3] = (dx2squ + dy2squ <= radsqu);
|
||||
vert_in[4] = (dx2squ + dy1squ <= radsqu);
|
||||
vert_in[0] = vert_in[4];
|
||||
vert_in[5] = vert_in[1];
|
||||
|
||||
int n_in = 0;
|
||||
if (vert_in[1]) ++n_in;
|
||||
if (vert_in[2]) ++n_in;
|
||||
if (vert_in[3]) ++n_in;
|
||||
if (vert_in[4]) ++n_in;
|
||||
|
||||
|
||||
/* We now calculate the points of intersection of the circle with, successively,
|
||||
x=x1, y=y2, x=x2. y=y1.
|
||||
|
||||
Where there are two intersections with one side, need to be careful about
|
||||
the order of the two points (i.e. clockwise round the rectangle) so that
|
||||
later on we get the right sector (short or long way round the circumference) */
|
||||
|
||||
int n_int[6] = { /* zero-initialied */ };
|
||||
n_int[1] = 0;
|
||||
if ( dx1squ <= radsqu)
|
||||
{
|
||||
del = std::sqrt( radsqu - dx1squ);
|
||||
if ( ( ( -del ) <= dy2 ) && ( del >= dy1 ) )
|
||||
{
|
||||
x_int[1][0] = x_int[1][1] = dx1;
|
||||
if ( (-del ) > dy1 )
|
||||
{
|
||||
y_int[1][0] = -del;
|
||||
n_int[1]++;
|
||||
// This intersection will be an end of the 3rd- or 4th-quadrant arc
|
||||
if ( dx1 > 0.0 ) { x_arc[4][1] = dx1; y_arc[4][1] = -del; arc_in[4] = 1; }
|
||||
else { x_arc[1][1] = dx1; y_arc[1][1] = -del; arc_in[1] = 1; }
|
||||
}
|
||||
if ( ( del ) < dy2 )
|
||||
{
|
||||
y_int[1][n_int[1]] = del;
|
||||
n_int[1]++;
|
||||
if ( dx1 > 0.0 ) { x_arc[3][0] = dx1; y_arc[3][0] = del; arc_in[3] = 1; }
|
||||
else { x_arc[2][0] = dx1; y_arc[2][0] = del; arc_in[2] = 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n_int[2] = 0;
|
||||
if ( dy2squ <= radsqu)
|
||||
{
|
||||
del = std::sqrt( radsqu - dy2squ);
|
||||
if ( ( ( -del ) <= dx2 ) && ( del >= dx1 ) )
|
||||
{
|
||||
y_int[2][0] = y_int[2][1] = dy2;
|
||||
if ( (-del ) > dx1 )
|
||||
{
|
||||
x_int[2][0] = -del;
|
||||
n_int[2]++;
|
||||
if ( dy2 > 0.0 ) { x_arc[2][1] = -del; y_arc[2][1] = dy2; arc_in[2] = 1; }
|
||||
else { x_arc[1][1] = -del; y_arc[1][1] = dy2; arc_in[1] = 1; }
|
||||
}
|
||||
if ( ( del ) < dx2 )
|
||||
{
|
||||
x_int[2][n_int[2]] = del;
|
||||
n_int[2]++;
|
||||
if ( dy2 > 0.0 ) { x_arc[3][0] = del; y_arc[3][0] = dy2; arc_in[3] = 1; }
|
||||
else { x_arc[4][0] = del; y_arc[4][0] = dy2; arc_in[4] = 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n_int[3] = 0;
|
||||
if ( dx2squ <= radsqu)
|
||||
{
|
||||
del = std::sqrt( radsqu - dx2squ);
|
||||
if ( ( ( -del ) <= dy2 ) && ( del >= dy1 ) )
|
||||
{
|
||||
x_int[3][0] = x_int[3][1] = dx2;
|
||||
if ( ( del ) < dy2 )
|
||||
{
|
||||
y_int[3][0] = del;
|
||||
n_int[3]++;
|
||||
if ( dx2 > 0.0 ) { x_arc[3][1] = dx2; y_arc[3][1] = del; arc_in[3] = 1; }
|
||||
else { x_arc[2][1] = dx2; y_arc[2][1] = del; arc_in[2] = 1; }
|
||||
}
|
||||
if ( (-del ) > dy1 )
|
||||
{
|
||||
y_int[3][n_int[3]] = -del;
|
||||
n_int[3]++;
|
||||
if ( dx2 > 0.0 ) { x_arc[4][0] = dx2; y_arc[4][0] = -del; arc_in[4] = 1; }
|
||||
else { x_arc[1][0] = dx2; y_arc[1][0] = -del; arc_in[1] = 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n_int[4] = 0;
|
||||
if ( dy1squ <= radsqu)
|
||||
{
|
||||
del = std::sqrt( radsqu - dy1squ);
|
||||
if ( ( ( -del ) <= dx2 ) && ( del >= dx1 ) )
|
||||
{
|
||||
y_int[4][0] = y_int[4][1] = dy1;
|
||||
if ( ( del ) < dx2 )
|
||||
{
|
||||
x_int[4][0] = del;
|
||||
n_int[4]++;
|
||||
if ( dy1 > 0.0 ) { x_arc[3][1] = del; y_arc[3][1] = dy1; arc_in[3] = 1; }
|
||||
else { x_arc[4][1] = del; y_arc[4][1] = dy1; arc_in[4] = 1; }
|
||||
}
|
||||
if ( (-del ) > dx1 )
|
||||
{
|
||||
x_int[4][n_int[4]] = -del;
|
||||
n_int[4]++;
|
||||
if ( dy1 > 0.0 ) { x_arc[2][0] = -del; y_arc[2][0] = dy1; arc_in[2] = 1; }
|
||||
else { x_arc[1][0] = -del; y_arc[1][0] = dy1; arc_in[1] = 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n_int[0] = n_int[4];
|
||||
n_int[5] = n_int[1];
|
||||
|
||||
y_int[0][0] = y_int[0][1] = dy1;
|
||||
x_int[0][0] = x_int[4][0];
|
||||
x_int[0][1] = x_int[4][1];
|
||||
x_int[5][0] = x_int[5][1] = dx1;
|
||||
y_int[5][0] = y_int[1][0];
|
||||
y_int[5][1] = y_int[1][1];
|
||||
|
||||
/* There are five separate cases, depending of the number of vertices inside the circle */
|
||||
switch ( n_in )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
/* We start with the whole area of the circle, and then subtract any bits that stick out. */
|
||||
area = radsqu * mathematical::pi;
|
||||
*perim_p = mathematical::twoPi * rad;
|
||||
no_intersection = true;
|
||||
for (n_vert = 1; n_vert < 5; n_vert++)
|
||||
{
|
||||
assert(n_int[n_vert] != 1);
|
||||
if (n_int[n_vert] == 2)
|
||||
{
|
||||
/* The area of the bit to be subtracted is a sector minus a triangle. */
|
||||
no_intersection = false;
|
||||
angle = sector( x_int[n_vert][1], y_int[n_vert][1], x_int[n_vert][0], y_int[n_vert][0]);
|
||||
area -= angle * radsqu * 0.5;
|
||||
*perim_p -= angle * rad;
|
||||
/* Two trinagles specified here, but one has zero area. */
|
||||
area += ( - ( y_int[n_vert][1] - y_int[n_vert][0] ) * x_int[n_vert][0]
|
||||
+ ( x_int[n_vert][1] - x_int[n_vert][0] ) * y_int[n_vert][0] ) / 2.0;
|
||||
}
|
||||
}
|
||||
/* Need to allow for when the circle is completely out side the rectanglle
|
||||
by checking if the centre is outside the rectangle */
|
||||
if ( no_intersection )
|
||||
{
|
||||
if ( (dx1>0) ||(dx2<0) || (dy1>0) || (dy2<0) )
|
||||
{
|
||||
*perim_p = *x_proj_edge_p = *y_proj_edge_p = 0.0;
|
||||
area = *x_overlap_p = *y_overlap_p = 0.0;
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
/* Find which vertex is inside */
|
||||
n_vert = 1;
|
||||
while ( !vert_in[n_vert] ) { n_vert++; assert( n_vert < 5 ); }
|
||||
assert( n_int[n_vert-1] == 1 );
|
||||
if ( n_int[n_vert] != 1 )
|
||||
{
|
||||
assert( n_int[n_vert] == 1 );
|
||||
}
|
||||
angle = sector( x_int[n_vert-1][0], y_int[n_vert-1][0], x_int[n_vert][0], y_int[n_vert][0]);
|
||||
area = angle * radsqu * 0.5;
|
||||
*perim_p = angle * rad;
|
||||
/* We subtract (or add) two triangles; the other two evaluate to zero */
|
||||
area -= ( - ( x_int[n_vert][0] - dx[n_vert] ) * dy[n_vert]
|
||||
+ ( x_int[n_vert-1][0] - dx[n_vert] ) * dy[n_vert]
|
||||
+ ( y_int[n_vert][0] - dy[n_vert] ) * dx[n_vert]
|
||||
- ( y_int[n_vert-1][0] - dy[n_vert] ) * dx[n_vert] ) / 2.0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
/* This time n_vert is the number of the side which is completely inside the circle */
|
||||
n_vert = 1;
|
||||
while ( !(vert_in[n_vert] && vert_in[n_vert+1]) ) { n_vert++; assert( n_vert < 5 ); }
|
||||
assert( n_int[n_vert-1] == 1 );
|
||||
assert( n_int[n_vert+1] == 1 );
|
||||
angle = sector( x_int[n_vert-1][0], y_int[n_vert-1][0], x_int[n_vert+1][0], y_int[n_vert+1][0]);
|
||||
area = angle * radsqu * 0.5;
|
||||
*perim_p = angle * rad;
|
||||
/* We subtract (or add) three triangles; the other three evaluate to zero */
|
||||
area += ( ( x_int[n_vert+1][0] - dx[n_vert+1] ) * dy[n_vert+1]
|
||||
- ( x_int[n_vert-1][0] - dx[n_vert] ) * dy[n_vert]
|
||||
- ( y_int[n_vert+1][0] - dy[n_vert+1] ) * dx[n_vert+1]
|
||||
+ ( y_int[n_vert-1][0] - dy[n_vert] ) * dx[n_vert]
|
||||
+ ( dx[n_vert+1] -dx[n_vert] ) * dy[n_vert]
|
||||
- ( dy[n_vert+1] -dy[n_vert] ) * dx[n_vert] ) / 2.0;
|
||||
|
||||
switch ( n_vert )
|
||||
{
|
||||
case 1: x_olap_min = dx1; break;
|
||||
case 2: y_olap_max = dy2; break;
|
||||
case 3: x_olap_max = dx2; break;
|
||||
case 4: y_olap_min = dy1; break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
/* Find which vertex is NOT inside */
|
||||
n_vert = 1;
|
||||
while ( vert_in[n_vert] ) { n_vert++; assert( n_vert < 5 ); }
|
||||
assert( n_int[n_vert-1] == 1 );
|
||||
assert( n_int[n_vert] == 1 );
|
||||
n_oppv = (n_vert + 2) % 4;
|
||||
angle = sector( x_int[n_vert][0], y_int[n_vert][0], x_int[n_vert-1][0], y_int[n_vert-1][0]);
|
||||
area = angle * radsqu * 0.5;
|
||||
*perim_p = angle * rad;
|
||||
/* We subtract (or add) four triangles; the other four evaluate to zero */
|
||||
area += ( - ( x_int[n_vert][0] - dx[n_vert+1] ) * dy[n_vert+1]
|
||||
+ ( x_int[n_vert-1][0] - dx[n_vert-1] ) * dy[n_vert-1]
|
||||
+ ( y_int[n_vert][0] - dy[n_vert+1] ) * dx[n_vert+1]
|
||||
- ( y_int[n_vert-1][0] - dy[n_vert-1] ) * dx[n_vert-1]
|
||||
+ ( dx[n_oppv] -dx[n_vert+1] ) * dy[n_oppv]
|
||||
- ( dx[n_oppv] -dx[n_vert-1] ) * dy[n_oppv]
|
||||
- ( dy[n_oppv] -dy[n_vert+1] ) * dx[n_oppv]
|
||||
+ ( dy[n_oppv] -dy[n_vert-1] ) * dx[n_oppv] ) / 2.0;
|
||||
|
||||
x_olap_min = dx1;
|
||||
y_olap_max = dy2;
|
||||
x_olap_max = dx2;
|
||||
y_olap_min = dy1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
/* Easy! We have the whole rectangle. */
|
||||
area = *x_overlap_p = *y_overlap_p = 1.0; // Normalised
|
||||
*perim_p = *x_proj_edge_p = *y_proj_edge_p = 0.0;
|
||||
return area;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The area may be very small negative by rounding errors
|
||||
assert(area >=-1.0E-4);
|
||||
if (area < 0.0) area = 0.0;
|
||||
/* Return the overlap as a fraction of the rectangle's area. */
|
||||
area /= ( (x2 - x1 ) * ( y2 - y1 ) );
|
||||
|
||||
// Sum the parts of the circumference that are inside the circle, projected onto axes
|
||||
*x_proj_edge_p =
|
||||
(
|
||||
(y_arc[1][1] - y_arc[1][0]) * arc_in[1]
|
||||
+ (y_arc[2][1] - y_arc[2][0]) * arc_in[2]
|
||||
+ (y_arc[3][0] - y_arc[3][1]) * arc_in[3]
|
||||
+ (y_arc[4][0] - y_arc[4][1]) * arc_in[4]
|
||||
);
|
||||
|
||||
*y_proj_edge_p =
|
||||
(
|
||||
(x_arc[1][0] - x_arc[1][1]) * arc_in[1]
|
||||
+ (x_arc[2][1] - x_arc[2][0]) * arc_in[2]
|
||||
+ (x_arc[3][1] - x_arc[3][0]) * arc_in[3]
|
||||
+ (x_arc[4][0] - x_arc[4][1]) * arc_in[4]
|
||||
);
|
||||
|
||||
if (arc_in[1])
|
||||
{
|
||||
x_olap_min = min(x_olap_min, x_arc[1][1]);
|
||||
x_olap_max = max(x_olap_max, x_arc[1][0]);
|
||||
y_olap_min = min(y_olap_min, y_arc[1][0]);
|
||||
y_olap_max = max(y_olap_max, y_arc[1][1]);
|
||||
}
|
||||
if (arc_in[2])
|
||||
{
|
||||
x_olap_min = min(x_olap_min, x_arc[2][0]);
|
||||
x_olap_max = max(x_olap_max, x_arc[2][1]);
|
||||
y_olap_min = min(y_olap_min, y_arc[2][0]);
|
||||
y_olap_max = max(y_olap_max, y_arc[2][1]);
|
||||
}
|
||||
if (arc_in[3])
|
||||
{
|
||||
x_olap_min = min(x_olap_min, x_arc[3][0]);
|
||||
x_olap_max = max(x_olap_max, x_arc[3][1]);
|
||||
y_olap_min = min(y_olap_min, y_arc[3][1]);
|
||||
y_olap_max = max(y_olap_max, y_arc[3][0]);
|
||||
}
|
||||
if (arc_in[4])
|
||||
{
|
||||
x_olap_min = min(x_olap_min, x_arc[4][1]);
|
||||
x_olap_max = max(x_olap_max, x_arc[4][0]);
|
||||
y_olap_min = min(y_olap_min, y_arc[4][1]);
|
||||
y_olap_max = max(y_olap_max, y_arc[4][0]);
|
||||
}
|
||||
|
||||
*x_overlap_p = ( x_olap_max - x_olap_min ) / ( x2 - x1 );
|
||||
*y_overlap_p = ( y_olap_max - y_olap_min ) / ( y2 - y1 );
|
||||
assert ( *x_overlap_p >= -floatSMALL );
|
||||
assert ( *y_overlap_p >= -floatSMALL );
|
||||
|
||||
return area;
|
||||
} // End intersect
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
|
||||
double Foam::PDRutils::l_blockage
|
||||
(
|
||||
double xc, double yc, double rad,
|
||||
double x1, double x2,
|
||||
double y1, double y2,
|
||||
scalar* count_p, scalar* drag_p, scalar* centre_p
|
||||
)
|
||||
{
|
||||
double xi = 0.0, lb, lb1, lb2, del;
|
||||
bool within = true; // Indicates that the the intersection does not overlap the ends of the line
|
||||
|
||||
/* xi is the side we need to calc. intersections with */
|
||||
if ( xc < x1 ) { xi = x1; }
|
||||
else if ( xc > x2 ) { xi = x2; }
|
||||
|
||||
if ( xi == 0.0 )
|
||||
{
|
||||
del = rad; // The relevant lowest ( or highest) point is at end of vertical radius
|
||||
}
|
||||
else // The relevant lowest ( or highest) point at intersection with x = xi
|
||||
{
|
||||
del = rad*rad - ( xi - xc ) * ( xi - xc );
|
||||
if ( del < 0.0 ) { del = 0.0; } // No intersection
|
||||
else { del = std::sqrt(del); }
|
||||
}
|
||||
|
||||
if ( ( yc + del ) > y2 ) { lb2 = y2; within = false; } else { lb2 = yc + del; }
|
||||
if ( ( yc - del ) < y1 ) { lb1 = y1; within = false; } else { lb1 = yc - del; }
|
||||
|
||||
lb = (lb2 - lb1) / (y2 - y1);
|
||||
*centre_p = (lb2 + lb1) * 0.5;
|
||||
|
||||
if ( lb < 0.0 ) lb = 0.0;
|
||||
|
||||
/* *count_p is 0 if the circle overlaps either y-side of the rectangle,
|
||||
1 if the circle is entirely inside the rectangle
|
||||
reduced if it overlaps x-sides.
|
||||
A negative value indicates total blockage*/
|
||||
if ( within && (lb > 0.0) )
|
||||
{
|
||||
*count_p = 1.0;
|
||||
if ( ( xc - rad ) < x1 ) *count_p -= 0.5;
|
||||
if ( ( xc + rad ) > x2 ) *count_p -= 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
*count_p = 0.0;
|
||||
}
|
||||
*drag_p = lb * 1.2; //*drag_p = lb * CD_ROUND;
|
||||
if ( lb > 0.99 ) { *count_p = -1000.0; *drag_p = 1000.0; }
|
||||
assert(lb >-100.0);
|
||||
return lb;
|
||||
}// End l_blockage
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
|
||||
double Foam::PDRutils::inters_db
|
||||
(
|
||||
double xc, double yc, double theta,
|
||||
double wa, double wb,
|
||||
double x1, double x2,
|
||||
double y1, double y2,
|
||||
scalar* count_p,
|
||||
symmTensor2D& vdrag, scalar* perim_p,
|
||||
scalar* x_lblk_p, scalar* y_lblk_p,
|
||||
scalar* x_centre_p, scalar* y_centre_p
|
||||
)
|
||||
{
|
||||
double x_int[6][2], y_int[6][2]; // Coordinates of intersections between the circle and sides of the rectangle.
|
||||
double area, lpa, lpb, len;
|
||||
|
||||
double m = ::tan( theta );
|
||||
double cth = ::cos( theta );
|
||||
double sth = ::sin( theta );
|
||||
|
||||
double was = wa * sth * 0.5;
|
||||
double wac = wa * cth * 0.5;
|
||||
double wbs = wb * sth * 0.5;
|
||||
double wbc = wb * cth * 0.5;
|
||||
double waos = wa / sth * 0.5;
|
||||
double waoc = wa / cth * 0.5;
|
||||
double wbos = wb / sth * 0.5;
|
||||
double wboc = wb / cth * 0.5;
|
||||
|
||||
double xb[6], yb[6], xp1, xp2, yp1, yp2;
|
||||
|
||||
double dx1 = (x1 - xc);
|
||||
double dx2 = (x2 - xc);
|
||||
double dy1 = (y1 - yc);
|
||||
double dy2 = (y2 - yc);
|
||||
|
||||
*count_p = 0;
|
||||
|
||||
// The vertices of the rectangle (all coordinates relative to centre of rectangle)
|
||||
xb[1] = -wac - wbs;
|
||||
yb[1] = -was + wbc;
|
||||
xb[3] = wac + wbs;
|
||||
yb[3] = was - wbc;
|
||||
xb[2] = wac - wbs;
|
||||
yb[2] = was + wbc;
|
||||
xb[4] = -wac + wbs;
|
||||
yb[4] = -was - wbc;
|
||||
|
||||
// First parameter of x_int or y_int determines which side of the cell we intersecting with
|
||||
// Second parameter 0 is first intersection, 1 is second, going clockwise
|
||||
|
||||
if ( xb[1] < dx1 ) // i.e. if left corner of block is to the left of x1
|
||||
{
|
||||
// Where one of lower sides of block intersects with x=x1
|
||||
// Innermost max determines which intersection is the genuine one
|
||||
// (not if whole block is to left of x1)
|
||||
y_int[1][0] = min(max(max(dx1 * m - wboc, -dx1 / m - waos), dy1), dy2);
|
||||
// Upper intersection
|
||||
y_int[1][1] = min(max(min(dx1 * m + wboc, -dx1 / m + waos), dy1), dy2);
|
||||
}
|
||||
else
|
||||
{
|
||||
y_int[1][1] = dy1;
|
||||
y_int[1][0] = dy2;
|
||||
// We add a quarter to count for each vertex inside the cell
|
||||
if ( (yb[1] > dy1) && (yb[1] < dy2) ) // ?? Seems inefficient ??
|
||||
{ *count_p += 0.25; }
|
||||
}
|
||||
if ( xb[3] > dx2 )
|
||||
{
|
||||
y_int[3][1] = min(max(max(dx2 * m - wboc, -dx2 / m - waos), dy1), dy2);
|
||||
y_int[3][0] = min(max(min(dx2 * m + wboc, -dx2 / m + waos), dy1), dy2);
|
||||
}
|
||||
else
|
||||
{
|
||||
y_int[3][0] = dy1;
|
||||
y_int[3][1] = dy2;
|
||||
if (yb[3] > dy1 && yb[3] < dy2)
|
||||
{
|
||||
*count_p += 0.25;
|
||||
}
|
||||
}
|
||||
if (yb[2] > dy2)
|
||||
{
|
||||
x_int[2][0] = min(max(max(dy2 / m - wbos, -dy2 * m - waoc), dx1), dx2);
|
||||
x_int[2][1] = min(max(min(dy2 / m + wbos, -dy2 * m + waoc), dx1), dx2);
|
||||
}
|
||||
else
|
||||
{
|
||||
x_int[2][0] = dx2;
|
||||
x_int[2][1] = dx1;
|
||||
if ( (xb[2] > dx1) && (xb[2] < dx2) )
|
||||
{ *count_p += 0.25; }
|
||||
}
|
||||
if ( yb[4] < dy1 )
|
||||
{
|
||||
x_int[4][1] = min(max(max(dy1 / m - wbos, -dy1 * m - waoc ), dx1), dx2);
|
||||
x_int[4][0] = min(max(min(dy1 / m + wbos, -dy1 * m + waoc ), dx1), dx2);
|
||||
}
|
||||
else
|
||||
{
|
||||
x_int[4][1] = dx2;
|
||||
x_int[4][0] = dx1;
|
||||
if ( (xb[4] > dx1) && (xb[4] < dx2) )
|
||||
{ *count_p += 0.25; }
|
||||
}
|
||||
|
||||
y_int[0][0] = y_int[0][1] = dy1;
|
||||
x_int[0][0] = x_int[4][0];
|
||||
x_int[0][1] = x_int[4][1];
|
||||
x_int[5][0] = x_int[5][1] = dx1;
|
||||
y_int[5][0] = y_int[1][0];
|
||||
y_int[5][1] = y_int[1][1];
|
||||
|
||||
|
||||
// We can now define a smaller enclosing rectangle
|
||||
|
||||
xp1 = min(x_int[2][0], x_int[4][1]); // Leftmost of the intersections with top and bottom of cell
|
||||
if ( yb[1] > dy1 && yb[1] < dy2 ) xp1 = min(xp1, xb[1] ); // left corner of block
|
||||
xp1 = max(xp1, dx1); // Make sure it is not to the left of x1
|
||||
|
||||
yp2 = max(y_int[1][1], y_int[3][0] );
|
||||
if ( xb[2] > dx1 && xb[2] < dx2 ) yp2 = max(yp2, yb[2] );
|
||||
yp2 = min(yp2, dy2);
|
||||
|
||||
xp2 = max(x_int[2][1], x_int[4][0] );
|
||||
if ( yb[3] > dy1 && yb[3] < dy2 ) xp2 = max(xp2, xb[3] );
|
||||
xp2 = min(xp2, dx2);
|
||||
|
||||
yp1 = min(y_int[1][0], y_int[3][1]);
|
||||
if ( xb[4] > dx1 && xb[4] < dx2 ) yp1 = min(yp1, yb[4] );
|
||||
yp1 = max(yp1, dy1 );
|
||||
|
||||
// Conveniently, the dimensions of the enclosing rectangle give us the line blockages
|
||||
*x_lblk_p = (xp2 - xp1 ) / (x2 - x1 );
|
||||
if ( *x_lblk_p < 0.0 ) { *x_lblk_p = 0.0; *count_p = 0.0; }; // ?? Better to trap no intersection earlier??
|
||||
*y_lblk_p = (yp2 - yp1 ) / (y2 - y1 );
|
||||
if ( *y_lblk_p < 0.0 ) { *y_lblk_p = 0.0; *count_p = 0.0; };
|
||||
|
||||
*x_centre_p = xc + (xp2 + xp1 ) * 0.5;
|
||||
*y_centre_p = yc + (yp2 + yp1 ) * 0.5;
|
||||
|
||||
*perim_p = lpa = lpb = 0.0;;
|
||||
area = (xp2 - xp1 ) * ( yp2 - yp1 );
|
||||
{
|
||||
double dxx, dyy;
|
||||
// Lower left
|
||||
dyy = max(0.0, min(yb[1], y_int[1][0]) - yp1);
|
||||
dxx = min(xb[4], x_int[0][1] ) - xp1;
|
||||
|
||||
if ( ( dxx * dyy) > 0.0 )
|
||||
{
|
||||
area -= dxx * dyy * 0.5;
|
||||
len = std::hypot(dxx, dyy);
|
||||
lpa += len * 0.5;
|
||||
*perim_p += len;
|
||||
}
|
||||
// Upper left
|
||||
dxx = max(0.0, min(xb[2], x_int[2][0]) - xp1);
|
||||
dyy = yp2 - max(yb[1], y_int[1][1] );
|
||||
if ( ( dxx * dyy) > 0.0 )
|
||||
{
|
||||
area -= dxx * dyy * 0.5;
|
||||
len = std::hypot(dxx, dyy);
|
||||
lpb += len * 0.5;
|
||||
*perim_p += len;
|
||||
}
|
||||
// Upper right
|
||||
dyy = max(0.0, yp2 - max(yb[3], y_int[3][0]));
|
||||
dxx = xp2 - max(xb[2], x_int[2][1] );
|
||||
if ( ( dxx * dyy) > 0.0 )
|
||||
{
|
||||
area -= dxx * dyy * 0.5;
|
||||
len = std::hypot(dxx, dyy);
|
||||
lpa += len * 0.5;
|
||||
*perim_p += len;
|
||||
}
|
||||
// Lower right
|
||||
dxx = max(0.0, xp2 - max(xb[4], x_int[4][0]));
|
||||
dyy = min(yb[3], y_int[3][1] ) - yp1;
|
||||
if ( ( dxx * dyy) > 0.0 )
|
||||
{
|
||||
area -= dxx * dyy * 0.5;
|
||||
len = std::hypot(dxx, dyy);
|
||||
lpb += len * 0.5;
|
||||
*perim_p += len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vdrag.xx() = lpa * cth * cth + lpb * sth * sth;
|
||||
vdrag.xy() = lpa * cth * sth - lpb * sth * cth;
|
||||
vdrag.yy() = lpa * sth * sth + lpb * cth * cth;
|
||||
|
||||
return area / ( (x2 - x1 ) * ( y2 - y1 ) );
|
||||
} // End inters_db
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,767 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRsetFields.H"
|
||||
#include "PDRutilsInternal.H"
|
||||
#include "mathematicalConstants.H"
|
||||
|
||||
#ifndef FULLDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#include <cassert>
|
||||
|
||||
using namespace Foam::constant;
|
||||
|
||||
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
// A sign-corrected multiply
|
||||
// This is used for porosity of obstacle intersections
|
||||
inline static scalar COMBLK(const scalar a, const scalar b)
|
||||
{
|
||||
if (a < 0)
|
||||
{
|
||||
return -a * b;
|
||||
}
|
||||
|
||||
return a * b;
|
||||
}
|
||||
|
||||
|
||||
// Obstacle satisfies some minimum size checks.
|
||||
// A volume check misses thin plates, so use area.
|
||||
// Thin sheet overlaps can be produced by touching objects
|
||||
// if the obs_extend parameter is > 0.
|
||||
inline static bool obsHasMinSize(const vector& span, const PDRparams& tol)
|
||||
{
|
||||
return
|
||||
(
|
||||
(cmptProduct(span) > tol.min_overlap_vol)
|
||||
&&
|
||||
(
|
||||
(span.x() * span.y() > tol.min_overlap_area)
|
||||
|| (span.y() * span.z() > tol.min_overlap_area)
|
||||
|| (span.z() * span.x() > tol.min_overlap_area)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
|
||||
void Foam::PDRutils::one_d_overlap
|
||||
(
|
||||
scalar xmin,
|
||||
scalar xmax,
|
||||
const PDRblock::location& grid,
|
||||
List<scalar>& olap,
|
||||
int *cmin, int *cmax,
|
||||
int *cfmin, int *cfmax
|
||||
)
|
||||
{
|
||||
// Looking at one coordinate direction, called x here, for something
|
||||
// that extends from xmin to xmax, calculate how much it overlaps
|
||||
// each cell in this direction. Result returned in 'olap' List is
|
||||
// the proportion of the grid step overlapped, i.e dimensionless.
|
||||
// First and last steps overlapped given by *cmin, *cmax
|
||||
// Ditto for shifted grid given by *cfmin, *cfmax.
|
||||
|
||||
// Initially zero everwhere
|
||||
olap = Zero;
|
||||
|
||||
if (olap.size() < grid.nPoints())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "The overlap scratch array is too small, has "
|
||||
<< olap.size() << " but needs " << grid.nPoints() << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
|
||||
// No intersection with the box
|
||||
if (xmax <= grid.first() || grid.last() <= xmin)
|
||||
{
|
||||
// Mark as bad range, cannot iterate
|
||||
*cmin = 0;
|
||||
*cmax = -1;
|
||||
|
||||
// Another bad range (cannot iterate) but for extra safety ensure
|
||||
// that (cfmin -> cmin) and (cmax -> cfmax) cannot iterate either
|
||||
*cfmin = 1;
|
||||
*cfmax = -2;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure search is within the (point) bounds
|
||||
xmin = grid.clip(xmin);
|
||||
xmax = grid.clip(xmax);
|
||||
|
||||
// The begin/end of the obstacle
|
||||
*cmin = grid.findCell(xmin);
|
||||
*cmax = grid.findCell(xmax);
|
||||
|
||||
for (label ix = *cmin; ix <= *cmax; ++ix)
|
||||
{
|
||||
olap[ix] = 1.0;
|
||||
}
|
||||
|
||||
// Fixup ends
|
||||
if (*cmax == *cmin)
|
||||
{
|
||||
olap[*cmax] = (xmax - xmin) / grid.width(*cmax);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (grid[*cmin] < xmin)
|
||||
{
|
||||
olap[*cmin] = (grid[*cmin+1] - xmin) / grid.width(*cmin);
|
||||
}
|
||||
|
||||
if (xmax < grid[*cmax+1])
|
||||
{
|
||||
olap[*cmax] = (xmax - grid[*cmax]) / grid.width(*cmax);
|
||||
}
|
||||
}
|
||||
assert(olap[*cmax] >= 0.0);
|
||||
|
||||
|
||||
// Is xmin below/above the cell-centre (for virtual staggered-grid) ?
|
||||
*cfmin =
|
||||
(
|
||||
xmin < grid.C(*cmin)
|
||||
? *cmin
|
||||
: Foam::min(*cmin+1, grid.nCells()-1)
|
||||
);
|
||||
|
||||
// Is xmax below/above the cell-centre (for virtual staggered-grid) ?
|
||||
*cfmax =
|
||||
(
|
||||
xmax < grid.C(*cmax)
|
||||
? *cmax
|
||||
: Foam::min(*cmax+1, grid.nCells()-1)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
void Foam::PDRutils::two_d_overlap
|
||||
(
|
||||
const UList<scalar>& a_olap, label amin, label amax,
|
||||
const UList<scalar>& b_olap, label bmin, label bmax,
|
||||
SquareMatrix<scalar>& ab_olap
|
||||
)
|
||||
{
|
||||
// We go one over the relevant min/max limits since these values might be
|
||||
// used. If not, they would have been zeroed in one_d_overlap
|
||||
|
||||
amin = Foam::max(0, amin-1);
|
||||
bmin = Foam::max(0, bmin-1);
|
||||
amax = Foam::min(a_olap.size()-1, amax+1);
|
||||
bmax = Foam::min(b_olap.size()-1, bmax+1);
|
||||
|
||||
for (label ia = amin; ia <= amax; ++ia)
|
||||
{
|
||||
for (label ib = bmin; ib <= bmax; ++ib)
|
||||
{
|
||||
ab_olap(ia,ib) = a_olap[ia] * b_olap[ib];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
void Foam::PDRutils::circle_overlap
|
||||
(
|
||||
scalar ac, scalar bc, scalar dia,
|
||||
scalar theta, scalar wa, scalar wb,
|
||||
const PDRblock::location& agrid, label amin, label amax,
|
||||
const PDRblock::location& bgrid, label bmin, label bmax,
|
||||
SquareMatrix<scalar>& ab_olap,
|
||||
SquareMatrix<scalar>& ab_perim,
|
||||
SquareMatrix<scalar>& a_lblock,
|
||||
SquareMatrix<scalar>& ac_lblock,
|
||||
SquareMatrix<scalar>& c_count,
|
||||
SquareMatrix<symmTensor2D>& c_drag,
|
||||
SquareMatrix<scalar>& b_lblock,
|
||||
SquareMatrix<scalar>& bc_lblock
|
||||
)
|
||||
{
|
||||
/* This routine calculates the proportion of each (two-dimensional) grid cell
|
||||
overlapped by the circle or angled rectangle. Coordinates are labelled a and b.
|
||||
On entry:
|
||||
ac, bc coordinates of centre of circle or rectangle
|
||||
dia diameter of circle (zero for rectangle)
|
||||
theta, wa, wb parameters for rectangle
|
||||
agrid[] locations of grid lines of a-grid
|
||||
amin, amax first and last cells in a-grid overlapped by object
|
||||
(similarly for b)
|
||||
On exit:
|
||||
abolap 2-D array of (proportionate) area blockage by grid cell
|
||||
a_lblock 2-D array of (proportionate) blockage to a-direction flow
|
||||
(This will be area blockage when extruded in the third coordinate).
|
||||
a_count (2-D array)The contribution of this object to the count of obstacles blocking
|
||||
a-direction flow. This is only non-zero if the object is inside the
|
||||
lateral boundaries of the cell. It is large negative if the cell is
|
||||
totally blocked in this direction.
|
||||
(similarly for b)
|
||||
c_drag 2-D array of tensor that will give tensor drag in each cell (when multiplied
|
||||
Cd, cylinder length, and 0.5 rho*U^2) Dimension: L.
|
||||
|
||||
Note that this routine does not zero array elements outside the amin to amax, bmin to bmax area.
|
||||
*/
|
||||
|
||||
scalar count, a_lblk, b_lblk, perim, dummy;
|
||||
|
||||
symmTensor2D vdrag(Zero);
|
||||
|
||||
// Prevent stepping outside of the array when the obstacle is on the
|
||||
// upper boundary
|
||||
|
||||
// Upper limit of inclusive range is nCells-1
|
||||
amin = Foam::max(0, amin);
|
||||
bmin = Foam::max(0, bmin);
|
||||
amax = Foam::min(amax, agrid.nCells()-1);
|
||||
bmax = Foam::min(bmax, bgrid.nCells()-1);
|
||||
|
||||
for (label ia = amin; ia <= amax; ++ia)
|
||||
{
|
||||
// Cell-centred grid
|
||||
const scalar a1 = agrid[ia];
|
||||
const scalar a2 = agrid[ia+1];
|
||||
|
||||
// Left-shifted staggered face grid (-1 addressing is OK)
|
||||
const scalar af1 = agrid.C(ia-1);
|
||||
const scalar af2 = agrid.C(ia);
|
||||
|
||||
for (label ib = bmin; ib <= bmax; ++ib)
|
||||
{
|
||||
// Cell-centred grid
|
||||
const scalar b1 = bgrid[ib];
|
||||
const scalar b2 = bgrid[ib+1];
|
||||
|
||||
// Left-shifted staggered face grid (-1 addressing is OK)
|
||||
const scalar bf1 = bgrid.C(ib-1);
|
||||
const scalar bf2 = bgrid.C(ib);
|
||||
|
||||
// Do the centred cell
|
||||
if ( dia > 0.0 )
|
||||
{
|
||||
ab_olap(ia,ib) = inters_cy
|
||||
(
|
||||
ac, bc, 0.5*dia, a1, a2, b1, b2, &perim,
|
||||
&dummy, &dummy, &b_lblk, &a_lblk
|
||||
);
|
||||
/* The last two arguments of the above call appear to be reversed, but the inters_cy routine returns
|
||||
the amount of overlap in the a and b direcvtions, which are the blockage to the b and a directions. */
|
||||
|
||||
/* abolap * cell area is area of cylinder in this cell. Divide by PI%D^2/4 to get proportion of cylinder in cell
|
||||
For whole cylinger c_drag should be = D, so multiply by D. */
|
||||
|
||||
c_drag(ia,ib).xx() = c_drag(ia,ib).yy() = 4.0 * ab_olap(ia,ib) * (a2 - a1) * (b2 - b1) / dia / mathematical::pi;
|
||||
c_drag(ia,ib).xy() = Zero;
|
||||
c_count(ia,ib) = perim / (mathematical::pi * dia);
|
||||
|
||||
//******?????
|
||||
scalar area = (a2 - a1) * (b2 - b1);
|
||||
scalar rat = dia * dia / area - 1.5;
|
||||
if (rat > 0.0)
|
||||
{
|
||||
scalar da = ac - 0.5 * (a1 + a2);
|
||||
scalar db = bc - 0.5 * (b1 + b2);
|
||||
scalar dc = std::hypot(da, db);
|
||||
scalar rat1 = min(max((dc / sqrt(area) - 0.3) * 1.4, 0), 1);
|
||||
scalar drg0 = c_drag(ia,ib).xx();
|
||||
scalar drg1 = c_drag(ia,ib).yy();
|
||||
scalar drg = std::hypot(drg0, drg1);
|
||||
c_drag(ia,ib).xx() = drg * ( 1.0 - rat1 ) + drg * da*da/dc/dc * rat1;
|
||||
c_drag(ia,ib).yy() = drg * ( 1.0 - rat1 ) + drg * db*db/dc/dc * rat1;
|
||||
c_drag(ia,ib).xy() = drg * da*db/dc/dc *rat1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ab_olap(ia,ib) = inters_db( ac, bc, theta, wa, wb, a1, a2, b1, b2, &count, c_drag(ia,ib), &perim, &a_lblk, &b_lblk, &dummy, &dummy );
|
||||
c_count(ia,ib) = perim / ( wa + wb ) * 0.5;
|
||||
}
|
||||
ac_lblock(ia,ib) = a_lblk;
|
||||
bc_lblock(ia,ib) = b_lblk;
|
||||
ab_perim(ia,ib) = perim;
|
||||
|
||||
// Do the a-shifted cell
|
||||
if ( dia > 0.0 ) // I.e. a cylinder, not a d.b.
|
||||
{
|
||||
if (ac >= af1 && ac < af2)
|
||||
{
|
||||
// Only want to block one layer of faces
|
||||
a_lblock(ia,ib) = l_blockage
|
||||
(
|
||||
ac, bc, 0.5*dia,
|
||||
af1, af2, b1, b2, &count, &dummy, &dummy
|
||||
);
|
||||
}
|
||||
inters_cy
|
||||
(
|
||||
ac, bc, 0.5*dia,
|
||||
af1, af2, b1, b2,
|
||||
&perim, &count, &dummy, &dummy, &dummy
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
inters_db
|
||||
(
|
||||
ac, bc, theta, wa, wb, af1, af2, b1, b2,
|
||||
&count, vdrag, &dummy, &a_lblk, &b_lblk, &dummy, &dummy
|
||||
);
|
||||
a_lblock(ia,ib) = a_lblk;
|
||||
}
|
||||
|
||||
// Do the b-shifted cell
|
||||
if ( dia > 0.0 )
|
||||
{
|
||||
if (bc >= bf1 && bc < bf2)
|
||||
{
|
||||
// Only want to block one layer of faces
|
||||
b_lblock(ia,ib) = l_blockage
|
||||
(
|
||||
bc, ac, 0.5*dia, bf1, bf2, a1, a2,
|
||||
&count, &(vdrag.yy()), &dummy
|
||||
);
|
||||
}
|
||||
|
||||
inters_cy
|
||||
(
|
||||
ac, bc, 0.5*dia,
|
||||
a1, a2, bf1, bf2,
|
||||
&perim, &dummy, &count, &dummy, &dummy
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
inters_db
|
||||
(
|
||||
ac, bc, theta, wa, wb,
|
||||
a1, a2, bf1, bf2,
|
||||
&count, vdrag, &dummy, &a_lblk, &b_lblk, &dummy, &dummy
|
||||
);
|
||||
b_lblock(ia,ib) = b_lblk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End circle_overlap
|
||||
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
scalar block_overlap
|
||||
(
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
const labelRange& range,
|
||||
const scalar multiplier
|
||||
)
|
||||
{
|
||||
// Size information
|
||||
const label nBlock = range.size();
|
||||
|
||||
// The return value
|
||||
scalar totVolume = 0;
|
||||
|
||||
if (nBlock < 2) return 0;
|
||||
|
||||
|
||||
// Sort blocks by their x-position (with sortBias)
|
||||
labelList blkOrder;
|
||||
sortedOrder(blocks[range], blkOrder);
|
||||
|
||||
DynamicList<PDRobstacle> newBlocks;
|
||||
|
||||
// Work through the sorted blocks
|
||||
for (label i1 = 0; i1 < nBlock-1; ++i1)
|
||||
{
|
||||
const PDRobstacle& blk1 = blocks[range[blkOrder[i1]]];
|
||||
|
||||
// Upper coordinates
|
||||
const vector max1 = blk1.pt + blk1.span;
|
||||
|
||||
// For second block start with the next one on the list, and
|
||||
// stop when we find the first one whose biased x-position
|
||||
// is beyond the end of the block1
|
||||
|
||||
for (label i2 = i1 + 1; i2 < nBlock; ++i2)
|
||||
{
|
||||
const PDRobstacle& blk2 = blocks[range[blkOrder[i2]]];
|
||||
|
||||
// Upper coordinates
|
||||
const vector max2 = blk2.pt + blk2.span;
|
||||
|
||||
if (max1.x() <= blk2.x())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if
|
||||
(
|
||||
max1.y() <= blk2.y()
|
||||
|| max1.z() <= blk2.z()
|
||||
|| max2.y() <= blk1.y()
|
||||
|| max2.z() <= blk1.z()
|
||||
|| (blk1.vbkge * blk2.vbkge <= 0)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
PDRobstacle over;
|
||||
|
||||
over.pt = max(blk1.pt, blk2.pt);
|
||||
over.span = min(max1, max2) - over.pt;
|
||||
|
||||
assert(cmptProduct(over.span) > 0.0);
|
||||
|
||||
// This routine should only have been called for all +ve o r all -ve obstacles
|
||||
assert(blk1.vbkge * blk2.vbkge > 0);
|
||||
/* At the first level of intersection, we create an obstacle of blockage -1 (if both objects solid)
|
||||
to cancel out the double counting. (multiplier is 1).
|
||||
?? COMBLK does a (sign corrected) multiply; is this corrrect for porous obstacles?
|
||||
Depends on how blockages were summed in the first place. In fact this -ve obstacle
|
||||
concept only works if the blockages are summed??*/
|
||||
over.vbkge = - COMBLK( blk1.vbkge, blk2.vbkge ) * multiplier;
|
||||
over.xbkge = - COMBLK( blk1.xbkge, blk2.xbkge ) * multiplier;
|
||||
over.ybkge = - COMBLK( blk1.ybkge, blk2.ybkge ) * multiplier;
|
||||
over.zbkge = - COMBLK( blk1.zbkge, blk2.zbkge ) * multiplier;
|
||||
over.typeId = 81 + int(15 * multiplier); // Not subsequently used
|
||||
|
||||
if (obsHasMinSize(over.span, pars))
|
||||
{
|
||||
// Obstacle satisfies some minimum size checks
|
||||
totVolume -= over.volume();
|
||||
|
||||
newBlocks.append(over);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks.append(std::move(newBlocks));
|
||||
|
||||
return totVolume;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
using namespace Foam::PDRutils;
|
||||
|
||||
scalar block_cylinder_overlap
|
||||
(
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
const labelRange& range,
|
||||
const UList<PDRobstacle>& cylinders
|
||||
)
|
||||
{
|
||||
// Size information
|
||||
const label nBlock = range.size();
|
||||
const label nCyl = cylinders.size();
|
||||
|
||||
// The return value
|
||||
scalar totVolume = 0;
|
||||
|
||||
if (!nBlock || !nCyl) return 0;
|
||||
|
||||
scalar area, a_lblk, b_lblk, dummy, a_centre, b_centre;
|
||||
symmTensor2D dum2;
|
||||
|
||||
|
||||
// Sort blocks and cylinders by their x-position (with sortBias)
|
||||
labelList blkOrder;
|
||||
sortedOrder(blocks[range], blkOrder);
|
||||
|
||||
labelList cylOrder;
|
||||
sortedOrder(cylinders, cylOrder);
|
||||
|
||||
DynamicList<PDRobstacle> newBlocks;
|
||||
|
||||
// Work through the sorted blocks
|
||||
for (label i1 = 0; i1 < nBlock; i1++)
|
||||
{
|
||||
const PDRobstacle& blk1 = blocks[range[blkOrder[i1]]];
|
||||
|
||||
// Upper coordinates
|
||||
const vector max1 = blk1.pt + blk1.span;
|
||||
|
||||
// Cyls whose end is before start of this block no longer
|
||||
// need to be considered
|
||||
|
||||
label i2 = 0;
|
||||
while (i2 < nCyl-1 && cylinders[cylOrder[i2]] < blk1)
|
||||
{
|
||||
++i2;
|
||||
}
|
||||
|
||||
for (/*nil*/; i2 < nCyl; ++i2)
|
||||
{
|
||||
const PDRobstacle& cyl2 = cylinders[cylOrder[i2]];
|
||||
|
||||
// Calculate overlap in axis direction; if zero continue.
|
||||
// Calculate 2-d overlap and c 0f g; if area zero continue.
|
||||
|
||||
PDRobstacle over;
|
||||
|
||||
|
||||
switch (cyl2.orient)
|
||||
{
|
||||
case vector::Z:
|
||||
{
|
||||
const scalar zm2 = cyl2.z() + cyl2.len();
|
||||
if (blk1.z() > zm2 || cyl2.z() > max1.z()) continue;
|
||||
|
||||
if ( cyl2.dia() == 0.0 )
|
||||
{
|
||||
area = inters_db
|
||||
(
|
||||
cyl2.x(), cyl2.y(), cyl2.theta(), cyl2.wa, cyl2.wb,
|
||||
blk1.x(), max1.x(),
|
||||
blk1.y(), max1.y(),
|
||||
&dummy, dum2, &dummy, &a_lblk, &b_lblk,
|
||||
&a_centre, &b_centre
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
area = inters_cy
|
||||
(
|
||||
cyl2.x(), cyl2.y(), 0.5*cyl2.dia(),
|
||||
blk1.x(), max1.x(),
|
||||
blk1.y(), max1.y(),
|
||||
&dummy, &dummy, &dummy, &dummy, &dummy
|
||||
);
|
||||
b_lblk = l_blockage
|
||||
(
|
||||
cyl2.x(), cyl2.y(), 0.5*cyl2.dia(),
|
||||
blk1.x(), max1.x(),
|
||||
blk1.y(), max1.y(),
|
||||
&dummy, &dummy, &b_centre
|
||||
);
|
||||
a_lblk = l_blockage
|
||||
(
|
||||
cyl2.y(), cyl2.x(), 0.5*cyl2.dia(),
|
||||
blk1.y(), max1.y(),
|
||||
blk1.x(), max1.x(),
|
||||
&dummy, &dummy, &a_centre
|
||||
);
|
||||
}
|
||||
if (equal(area, 0)) continue;
|
||||
assert(a_lblk >0.0);
|
||||
assert(b_lblk >0.0);
|
||||
|
||||
// The intersection between a circle and a rectangle can be an odd shape.
|
||||
// We have its area. a_lblk and b_lblk are dimensions of enclosing rectangle
|
||||
// and a_centre and b_centre its centre. We scale this rectangle down to
|
||||
// the corect areacorrect area, as a rectangular approximation to the intersection.
|
||||
const scalar ratio = std::sqrt( area / a_lblk / b_lblk );
|
||||
|
||||
a_lblk *= blk1.span.x() * ratio;
|
||||
b_lblk *= blk1.span.y() * ratio;
|
||||
assert(b_lblk >0.0);
|
||||
assert(a_lblk >0.0);
|
||||
|
||||
over.x() = a_centre - 0.5 * a_lblk;
|
||||
over.y() = b_centre - 0.5 * b_lblk;
|
||||
over.z() = max(blk1.z(), cyl2.z());
|
||||
|
||||
over.span.x() = a_lblk;
|
||||
over.span.y() = b_lblk;
|
||||
over.span.z() = min(max1.z(), cyl2.z() + cyl2.len()) - over.z();
|
||||
assert(over.x() > -200.0);
|
||||
assert(over.x() < 2000.0);
|
||||
}
|
||||
break;
|
||||
|
||||
case vector::Y:
|
||||
{
|
||||
const scalar ym2 = cyl2.y() + cyl2.len();
|
||||
if (blk1.y() > ym2 || cyl2.y() > max1.y()) continue;
|
||||
|
||||
if ( cyl2.dia() == 0.0 )
|
||||
{
|
||||
area = inters_db
|
||||
(
|
||||
cyl2.z(), cyl2.x(), cyl2.theta(), cyl2.wa, cyl2.wb,
|
||||
blk1.z(), max1.z(),
|
||||
blk1.x(), max1.x(),
|
||||
&dummy, dum2, &dummy, &a_lblk, &b_lblk,
|
||||
&a_centre, &b_centre
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
area = inters_cy
|
||||
(
|
||||
cyl2.z(), cyl2.x(), 0.5*cyl2.dia(),
|
||||
blk1.z(), max1.z(),
|
||||
blk1.x(), max1.x(),
|
||||
&dummy, &dummy, &dummy, &dummy, &dummy
|
||||
);
|
||||
|
||||
b_lblk = l_blockage
|
||||
(
|
||||
cyl2.z(), cyl2.x(), 0.5*cyl2.dia(),
|
||||
blk1.z(), max1.z(),
|
||||
blk1.x(), max1.x(),
|
||||
&dummy, &dummy, &b_centre
|
||||
);
|
||||
|
||||
a_lblk = l_blockage
|
||||
(
|
||||
cyl2.x(), cyl2.z(), 0.5*cyl2.dia(),
|
||||
blk1.x(), max1.x(),
|
||||
blk1.z(), max1.z(),
|
||||
&dummy, &dummy, &a_centre
|
||||
);
|
||||
}
|
||||
|
||||
if (equal(area, 0)) continue;
|
||||
assert(a_lblk >0.0);
|
||||
assert(b_lblk >0.0);
|
||||
|
||||
// a_lblk and b_lblk are dimensions of enclosing rectangle.
|
||||
// Need to scale to correct area
|
||||
const scalar ratio = std::sqrt( area / a_lblk / b_lblk );
|
||||
a_lblk *= blk1.span.z() * ratio;
|
||||
b_lblk *= blk1.span.x() * ratio;
|
||||
|
||||
over.z() = a_centre - a_lblk * 0.5;
|
||||
over.x() = b_centre - b_lblk * 0.5;
|
||||
over.y() = max(blk1.y(), cyl2.y());
|
||||
|
||||
over.span.z() = a_lblk;
|
||||
over.span.x() = b_lblk;
|
||||
over.span.y() = min(max1.y(), cyl2.y() + cyl2.len()) - over.y();
|
||||
}
|
||||
break;
|
||||
|
||||
case vector::X:
|
||||
{
|
||||
const scalar xm2 = cyl2.x() + cyl2.len();
|
||||
if (blk1.x() > xm2 || cyl2.x() > max1.x()) continue;
|
||||
|
||||
if ( cyl2.dia() == 0.0 )
|
||||
{
|
||||
area = inters_db
|
||||
(
|
||||
cyl2.y(), cyl2.z(), cyl2.theta(), cyl2.wa, cyl2.wb,
|
||||
blk1.y(), max1.y(),
|
||||
blk1.z(), max1.z(),
|
||||
&dummy, dum2, &dummy, &a_lblk, &b_lblk,
|
||||
&a_centre, &b_centre
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
area = inters_cy
|
||||
(
|
||||
cyl2.y(), cyl2.z(), 0.5*cyl2.dia(),
|
||||
blk1.y(), max1.y(),
|
||||
blk1.z(), max1.z(),
|
||||
&dummy, &dummy, &dummy, &dummy, &dummy
|
||||
);
|
||||
|
||||
b_lblk = l_blockage
|
||||
(
|
||||
cyl2.y(), cyl2.z(), 0.5*cyl2.dia(),
|
||||
blk1.y(), max1.y(),
|
||||
blk1.z(), max1.z(),
|
||||
&dummy, &dummy, &b_centre
|
||||
);
|
||||
|
||||
a_lblk = l_blockage
|
||||
(
|
||||
cyl2.z(), cyl2.y(), 0.5*cyl2.dia(),
|
||||
blk1.z(), max1.z(),
|
||||
blk1.y(), max1.y(),
|
||||
&dummy, &dummy, &a_centre
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if (equal(area, 0)) continue;
|
||||
assert(a_lblk >0.0);
|
||||
assert(b_lblk >0.0);
|
||||
|
||||
// a_lblk and b_lblk are dimensions of enclosing rectangle.
|
||||
// Need to scale to correct area
|
||||
const scalar ratio = std::sqrt( area / a_lblk / b_lblk );
|
||||
assert(ratio >-10000.0);
|
||||
assert(ratio <10000.0);
|
||||
a_lblk *= blk1.span.y() * ratio;
|
||||
b_lblk *= blk1.span.z() * ratio;
|
||||
|
||||
over.y() = a_centre - a_lblk * 0.5;
|
||||
over.z() = b_centre - b_lblk * 0.5;
|
||||
over.x() = max(blk1.x(), cyl2.x());
|
||||
|
||||
over.span.y() = a_lblk;
|
||||
over.span.z() = b_lblk;
|
||||
over.span.x() = min(max1.x(), cyl2.x() + cyl2.len()) - over.x();
|
||||
}
|
||||
break;
|
||||
}
|
||||
over.vbkge = over.xbkge = over.ybkge = over.zbkge = -1.0;
|
||||
over.typeId = PDRobstacle::IGNORE;
|
||||
|
||||
assert(cmptProduct(over.span) > 0.0);
|
||||
assert(b_lblk >0.0);
|
||||
assert(a_lblk >0.0);
|
||||
assert(over.x() > -10000.0);
|
||||
|
||||
if (obsHasMinSize(over.span, pars))
|
||||
{
|
||||
// Obstacle satisfies some minimum size checks
|
||||
totVolume -= over.volume();
|
||||
|
||||
newBlocks.append(over);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks.append(std::move(newBlocks));
|
||||
|
||||
return totVolume;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,179 @@
|
||||
/*--------------------------------*- C++ -*----------------------------------*\
|
||||
| ========= | |
|
||||
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
|
||||
| \\ / O peration | Version: v1812 |
|
||||
| \\ / A nd | Web: www.OpenFOAM.com |
|
||||
| \\/ M anipulation | |
|
||||
\*---------------------------------------------------------------------------*/
|
||||
FoamFile
|
||||
{
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
object obstaclesDict;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
group1
|
||||
{
|
||||
positions
|
||||
(
|
||||
(0 0 0)
|
||||
);
|
||||
|
||||
obstacles
|
||||
(
|
||||
box { point (0 0 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (1 0 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (1 0 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (2 0 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (3 0 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (0 1 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (1 1 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (2 1 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (3 1 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (0 2 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (1 2 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (2 2 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (3 2 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (0 3 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (1 3 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (2 3 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (3 3 0); span (0.05 0.05 2); porosity 0; }
|
||||
box { point (0 0 0); span (0.05 3.05 0.05); }
|
||||
box { point (1 0 0); span (0.05 3.05 0.05); }
|
||||
box { point (2 0 0); span (0.05 3.05 0.05); }
|
||||
box { point (3 0 0); span (0.05 3.05 0.05); }
|
||||
box { point (0 0 1); span (0.05 3.05 0.05); }
|
||||
box { point (1 0 1); span (0.05 3.05 0.05); }
|
||||
box { point (2 0 1); span (0.05 3.05 0.05); }
|
||||
box { point (3 0 1); span (0.05 3.05 0.05); }
|
||||
box { point (0 0 2); span (0.05 3.05 0.05); }
|
||||
box { point (1 0 2); span (0.05 3.05 0.05); }
|
||||
box { point (2 0 2); span (0.05 3.05 0.05); }
|
||||
box { point (3 0 2); span (0.05 3.05 0.05); }
|
||||
box { point (0 0 0); span (3.05 0.05 0.05); }
|
||||
box { point (0 1 0); span (3.05 0.05 0.05); }
|
||||
box { point (0 2 0); span (3.05 0.05 0.05); }
|
||||
box { point (0 3 0); span (3.05 0.05 0.05); }
|
||||
box { point (0 0 1); span (3.05 0.05 0.05); }
|
||||
box { point (0 1 1); span (3.05 0.05 0.05); }
|
||||
box { point (0 2 1); span (3.05 0.05 0.05); }
|
||||
box { point (0 3 1); span (3.05 0.05 0.05); }
|
||||
box { point (0 0 2); span (3.05 0.05 0.05); }
|
||||
box { point (0 1 2); span (3.05 0.05 0.05); }
|
||||
box { point (0 2 2); span (3.05 0.05 0.05); }
|
||||
box { point (0 3 2); span (3.05 0.05 0.05); }
|
||||
|
||||
patch { point (1 1 1); span (2 2 2); inlet x; outlet y; name xpatch; }
|
||||
);
|
||||
}
|
||||
|
||||
group11
|
||||
{
|
||||
positions
|
||||
(
|
||||
(0 0 0)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
group14
|
||||
{
|
||||
positions
|
||||
(
|
||||
(0 0 0.15)
|
||||
(0 0 0.45)
|
||||
);
|
||||
|
||||
obstacles
|
||||
(
|
||||
box { point (0.05 0 1.05); span(0.006 3.05 0.05); }
|
||||
box { point (0.997 0 1.05); span(0.006 3.05 0.05); }
|
||||
box { point (1.05 0 1.05); span(0.006 3.05 0.05); }
|
||||
box { point (1.997 0 1.05); span(0.006 3.05 0.05); }
|
||||
box { point (2.05 0 1.05); span(0.006 3.05 0.05); }
|
||||
box { point (2.997 0 1.05); span(0.006 3.05 0.05); }
|
||||
|
||||
cyl { point (0.05 0.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 0.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 0.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 0.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 1.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 1.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 1.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 1.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 2.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 2.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 2.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 2.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (0.05 3.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 0.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 0.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 0.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 0.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 1.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 1.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 1.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 1.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 2.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 2.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 2.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 2.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (1.05 3.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 0.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 0.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 0.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 0.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 1.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 1.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 1.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 1.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 2.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 2.275 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 2.525 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 2.775 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
cyl { point (2.05 3.025 1.075); length 0.947; diameter 0.026; direction x; }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
group17
|
||||
{
|
||||
positions
|
||||
(
|
||||
(0 0 0)
|
||||
(0 0 0.30)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
group21
|
||||
{
|
||||
positions
|
||||
(
|
||||
(0 0 0)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
group31
|
||||
{
|
||||
positions
|
||||
(
|
||||
(0 0 0)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
group41
|
||||
{
|
||||
positions
|
||||
(
|
||||
(0 0 0)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,737 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRobstacle.H"
|
||||
#include "boundBox.H"
|
||||
#include "meshedSurf.H"
|
||||
#include "axisAngleRotation.H"
|
||||
#include "coordinateSystem.H"
|
||||
#include "foamVtkSurfaceWriter.H"
|
||||
#include "unitConversion.H"
|
||||
#include "addToMemberFunctionSelectionTable.H"
|
||||
|
||||
using namespace Foam::constant;
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
defineMemberFunctionSelectionTable(PDRobstacle, read, dictRead);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::PDRobstacle::PDRobstacle()
|
||||
:
|
||||
groupId(0),
|
||||
typeId(0),
|
||||
orient(vector::X),
|
||||
sortBias(0),
|
||||
pt(Zero),
|
||||
span(Zero),
|
||||
wa(0),
|
||||
wb(0),
|
||||
vbkge(0),
|
||||
xbkge(0),
|
||||
ybkge(0),
|
||||
zbkge(0),
|
||||
blowoff_type(0),
|
||||
identifier()
|
||||
{}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
void Foam::PDRobstacle::clear()
|
||||
{
|
||||
groupId = 0;
|
||||
typeId = 0;
|
||||
orient = vector::X;
|
||||
sortBias = 0;
|
||||
pt = Zero;
|
||||
span = Zero;
|
||||
wa = 0;
|
||||
wb = 0;
|
||||
vbkge = 0;
|
||||
xbkge = 0;
|
||||
ybkge = 0;
|
||||
zbkge = 0;
|
||||
blowoff_type = 0;
|
||||
identifier.clear();
|
||||
}
|
||||
|
||||
|
||||
void Foam::PDRobstacle::readProperties(const dictionary& dict)
|
||||
{
|
||||
PDRobstacle::clear();
|
||||
|
||||
// Read as word, which handles quoted or unquoted entries
|
||||
word obsName;
|
||||
|
||||
if (dict.readIfPresent("name", obsName))
|
||||
{
|
||||
identifier = std::move(obsName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::PDRobstacle::scale(const scalar factor)
|
||||
{
|
||||
if (factor <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sortBias *= factor;
|
||||
|
||||
switch (typeId)
|
||||
{
|
||||
case PDRobstacle::CYLINDER:
|
||||
{
|
||||
pt *= factor;
|
||||
|
||||
dia() *= factor;
|
||||
len() *= factor;
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::DIAG_BEAM:
|
||||
{
|
||||
pt *= factor;
|
||||
|
||||
len() *= factor;
|
||||
wa *= factor;
|
||||
wb *= factor;
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::CUBOID_1:
|
||||
case PDRobstacle::LOUVRE_BLOWOFF:
|
||||
case PDRobstacle::CUBOID:
|
||||
case PDRobstacle::WALL_BEAM:
|
||||
case PDRobstacle::GRATING:
|
||||
case PDRobstacle::RECT_PATCH:
|
||||
{
|
||||
pt *= factor;
|
||||
span *= factor;
|
||||
|
||||
if (typeId == PDRobstacle::GRATING)
|
||||
{
|
||||
slat_width *= factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::PDRobstacle::volume() const
|
||||
{
|
||||
scalar vol = 0;
|
||||
|
||||
switch (typeId)
|
||||
{
|
||||
case PDRobstacle::CYLINDER:
|
||||
vol = 0.25 * mathematical::pi * sqr(dia()) * len();
|
||||
break;
|
||||
|
||||
case PDRobstacle::DIAG_BEAM:
|
||||
vol = wa * wb * len();
|
||||
break;
|
||||
|
||||
case PDRobstacle::CUBOID_1:
|
||||
case PDRobstacle::LOUVRE_BLOWOFF:
|
||||
case PDRobstacle::CUBOID:
|
||||
case PDRobstacle::WALL_BEAM:
|
||||
case PDRobstacle::GRATING:
|
||||
vol = cmptProduct(span) * vbkge;
|
||||
break;
|
||||
}
|
||||
|
||||
return vol;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::PDRobstacle::tooSmall(const scalar minWidth) const
|
||||
{
|
||||
if (minWidth <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (typeId)
|
||||
{
|
||||
case PDRobstacle::CYLINDER:
|
||||
{
|
||||
// The effective half-width
|
||||
if ((0.25 * dia() * sqrt(mathematical::pi)) <= minWidth)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::DIAG_BEAM:
|
||||
{
|
||||
if
|
||||
(
|
||||
(len() <= minWidth && wa <= minWidth)
|
||||
|| (len() <= minWidth && wb <= minWidth)
|
||||
|| (wa <= minWidth && wb <= minWidth)
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::CUBOID_1:
|
||||
case PDRobstacle::LOUVRE_BLOWOFF:
|
||||
case PDRobstacle::CUBOID:
|
||||
case PDRobstacle::WALL_BEAM:
|
||||
case PDRobstacle::GRATING:
|
||||
case PDRobstacle::RECT_PATCH:
|
||||
{
|
||||
if
|
||||
(
|
||||
(span.x() <= minWidth && span.y() <= minWidth)
|
||||
|| (span.y() <= minWidth && span.z() <= minWidth)
|
||||
|| (span.z() <= minWidth && span.x() <= minWidth)
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Foam::volumeType Foam::PDRobstacle::trim(const boundBox& bb)
|
||||
{
|
||||
volumeType::type vt = volumeType::UNKNOWN;
|
||||
|
||||
if (!bb.valid() || !typeId)
|
||||
{
|
||||
return vt;
|
||||
}
|
||||
|
||||
switch (typeId)
|
||||
{
|
||||
case PDRobstacle::CYLINDER:
|
||||
{
|
||||
const scalar rad = 0.5*dia();
|
||||
|
||||
direction e1 = vector::X;
|
||||
direction e2 = vector::Y;
|
||||
direction e3 = vector::Z;
|
||||
|
||||
if (orient == vector::X)
|
||||
{
|
||||
e1 = vector::Y;
|
||||
e2 = vector::Z;
|
||||
e3 = vector::X;
|
||||
}
|
||||
else if (orient == vector::Y)
|
||||
{
|
||||
e1 = vector::Z;
|
||||
e2 = vector::X;
|
||||
e3 = vector::Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
orient = vector::Z; // extra safety?
|
||||
}
|
||||
|
||||
if
|
||||
(
|
||||
(pt[e1] + rad <= bb.min()[e1])
|
||||
|| (pt[e2] + rad <= bb.min()[e2])
|
||||
|| (pt[e3] + len() <= bb.min()[e3])
|
||||
|| (pt[e1] - rad >= bb.max()[e1])
|
||||
|| (pt[e2] - rad >= bb.max()[e2])
|
||||
|| (pt[e3] >= bb.max()[e3])
|
||||
)
|
||||
{
|
||||
// No overlap
|
||||
return volumeType::OUTSIDE;
|
||||
}
|
||||
|
||||
vt = volumeType::INSIDE;
|
||||
|
||||
// Trim obstacle length as required
|
||||
if (pt[e3] < bb.min()[e3])
|
||||
{
|
||||
vt = volumeType::MIXED;
|
||||
len() -= bb.min()[e3] - pt[e3];
|
||||
pt[e3] = bb.min()[e3];
|
||||
}
|
||||
|
||||
if (pt[e3] + len() > bb.max()[e3])
|
||||
{
|
||||
vt = volumeType::MIXED;
|
||||
len() = bb.max()[e3] - pt[e3];
|
||||
}
|
||||
|
||||
// Cannot trim diameter very well, so just mark as protruding
|
||||
if
|
||||
(
|
||||
(pt[e1] - rad < bb.min()[e1]) || (pt[e1] + rad > bb.max()[e1])
|
||||
|| (pt[e2] - rad < bb.min()[e2]) || (pt[e2] + rad > bb.max()[e2])
|
||||
)
|
||||
{
|
||||
vt = volumeType::MIXED;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case PDRobstacle::DIAG_BEAM:
|
||||
{
|
||||
// Not implemented
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case PDRobstacle::CUBOID_1:
|
||||
case PDRobstacle::LOUVRE_BLOWOFF:
|
||||
case PDRobstacle::CUBOID:
|
||||
case PDRobstacle::WALL_BEAM:
|
||||
case PDRobstacle::GRATING:
|
||||
case PDRobstacle::RECT_PATCH:
|
||||
{
|
||||
for (direction cmpt=0; cmpt < vector::nComponents; ++cmpt)
|
||||
{
|
||||
if
|
||||
(
|
||||
((pt[cmpt] + span[cmpt]) < bb.min()[cmpt])
|
||||
|| (pt[cmpt] > bb.max()[cmpt])
|
||||
)
|
||||
{
|
||||
// No overlap
|
||||
return volumeType::OUTSIDE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vt = volumeType::INSIDE;
|
||||
|
||||
// Trim obstacle as required
|
||||
|
||||
for (direction cmpt=0; cmpt < vector::nComponents; ++cmpt)
|
||||
{
|
||||
if (pt[cmpt] < bb.min()[cmpt])
|
||||
{
|
||||
vt = volumeType::MIXED;
|
||||
if (span[cmpt] > 0)
|
||||
{
|
||||
span[cmpt] -= bb.min()[cmpt] - pt[cmpt];
|
||||
}
|
||||
pt[cmpt] = bb.min()[cmpt];
|
||||
}
|
||||
|
||||
|
||||
if (pt[cmpt] + span[cmpt] > bb.max()[cmpt])
|
||||
{
|
||||
vt = volumeType::MIXED;
|
||||
span[cmpt] -= bb.max()[cmpt] - pt[cmpt];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return vt;
|
||||
}
|
||||
|
||||
|
||||
Foam::meshedSurface Foam::PDRobstacle::surface() const
|
||||
{
|
||||
meshedSurface surf;
|
||||
|
||||
const PDRobstacle& obs = *this;
|
||||
|
||||
switch (obs.typeId)
|
||||
{
|
||||
case PDRobstacle::CUBOID_1 :
|
||||
case PDRobstacle::CUBOID :
|
||||
{
|
||||
boundBox box(obs.pt, obs.pt + obs.span);
|
||||
|
||||
pointField pts(box.points());
|
||||
faceList fcs(boundBox::faces);
|
||||
|
||||
surf.transfer(pts, fcs);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::DIAG_BEAM :
|
||||
{
|
||||
boundBox box(Zero);
|
||||
|
||||
switch (orient)
|
||||
{
|
||||
case vector::X:
|
||||
{
|
||||
box.min() = vector(0, -0.5*obs.wa, -0.5*obs.wb);
|
||||
box.max() = vector(obs.len(), 0.5*obs.wa, 0.5*obs.wb);
|
||||
break;
|
||||
}
|
||||
|
||||
case vector::Y:
|
||||
{
|
||||
box.min() = vector(-0.5*obs.wb, 0, -0.5*obs.wa);
|
||||
box.max() = vector(0.5*obs.wb, obs.len(), 0.5*obs.wa);
|
||||
break;
|
||||
}
|
||||
|
||||
case vector::Z:
|
||||
{
|
||||
box.min() = vector(-0.5*obs.wa, -0.5*obs.wb, 0);
|
||||
box.max() = vector(0.5*obs.wa, 0.5*obs.wb, obs.len());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
coordinateSystem cs
|
||||
(
|
||||
obs.pt,
|
||||
coordinateRotations::axisAngle
|
||||
(
|
||||
vector::components(obs.orient),
|
||||
obs.theta(),
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
pointField pts0(box.points());
|
||||
faceList fcs(boundBox::faces);
|
||||
|
||||
pointField pts(cs.globalPosition(pts0));
|
||||
|
||||
surf.transfer(pts, fcs);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::CYLINDER :
|
||||
{
|
||||
// Tessellation 12 looks fairly reasonable
|
||||
|
||||
constexpr int nDiv = 12;
|
||||
|
||||
point org(obs.pt);
|
||||
|
||||
direction e1 = vector::X;
|
||||
direction e2 = vector::Y;
|
||||
direction e3 = vector::Z;
|
||||
|
||||
if (obs.orient == vector::X)
|
||||
{
|
||||
e1 = vector::Y;
|
||||
e2 = vector::Z;
|
||||
e3 = vector::X;
|
||||
}
|
||||
else if (obs.orient == vector::Y)
|
||||
{
|
||||
e1 = vector::Z;
|
||||
e2 = vector::X;
|
||||
e3 = vector::Y;
|
||||
}
|
||||
|
||||
pointField pts(2*nDiv, org);
|
||||
faceList fcs(2 + nDiv);
|
||||
|
||||
// Origin for back
|
||||
org[e3] += obs.len();
|
||||
SubList<point>(pts, nDiv, nDiv) = org;
|
||||
|
||||
const scalar radius = 0.5*obs.dia();
|
||||
|
||||
for (label i=0; i < nDiv; ++i)
|
||||
{
|
||||
const scalar angle = (i * mathematical::twoPi) / nDiv;
|
||||
const scalar s = radius * sin(angle);
|
||||
const scalar c = radius * cos(angle);
|
||||
|
||||
pts[i][e1] += s;
|
||||
pts[i][e2] += c;
|
||||
|
||||
pts[nDiv+i][e1] += s;
|
||||
pts[nDiv+i][e2] += c;
|
||||
}
|
||||
|
||||
// Side-faces
|
||||
for (label facei=0; facei < nDiv; ++facei)
|
||||
{
|
||||
face& f = fcs[facei];
|
||||
f.resize(4);
|
||||
|
||||
f[0] = facei;
|
||||
f[3] = (facei + 1) % nDiv;
|
||||
f[1] = f[0] + nDiv;
|
||||
f[2] = f[3] + nDiv;
|
||||
}
|
||||
|
||||
{
|
||||
// Front face
|
||||
face& f1 = fcs[nDiv];
|
||||
f1.resize(nDiv);
|
||||
|
||||
f1[0] = 0;
|
||||
for (label pti=1; pti < nDiv; ++pti)
|
||||
{
|
||||
f1[pti] = nDiv-pti;
|
||||
}
|
||||
|
||||
// Back face
|
||||
labelList& f2 = fcs[nDiv+1];
|
||||
f2 = identity(nDiv, nDiv);
|
||||
}
|
||||
|
||||
surf.transfer(pts, fcs);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::RECT_PATCH :
|
||||
{
|
||||
pointField pts(4, obs.span);
|
||||
pts[0] = Zero;
|
||||
|
||||
switch (obs.inlet_dirn)
|
||||
{
|
||||
case -1:
|
||||
case 1:
|
||||
{
|
||||
for (auto& p : pts)
|
||||
{
|
||||
p.x() = 0;
|
||||
}
|
||||
|
||||
pts[1].z() = 0;
|
||||
pts[3].y() = 0;
|
||||
break;
|
||||
}
|
||||
case -2:
|
||||
case 2:
|
||||
{
|
||||
for (auto& p : pts)
|
||||
{
|
||||
p.y() = 0;
|
||||
}
|
||||
|
||||
pts[1].x() = 0;
|
||||
pts[3].z() = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
for (auto& p : pts)
|
||||
{
|
||||
p.z() = 0;
|
||||
}
|
||||
|
||||
pts[1].y() = 0;
|
||||
pts[3].x() = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// pts += obs.pt;
|
||||
|
||||
faceList fcs(one(), face(identity(4)));
|
||||
|
||||
surf.transfer(pts, fcs);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
// LOUVRE_BLOWOFF = 5,
|
||||
// WALL_BEAM = 7,
|
||||
// GRATING = 8,
|
||||
// CIRC_PATCH = 12,
|
||||
// MESH_PLANE = 46,
|
||||
}
|
||||
|
||||
return surf;
|
||||
}
|
||||
|
||||
|
||||
Foam::label Foam::PDRobstacle::addPieces
|
||||
(
|
||||
vtk::surfaceWriter& surfWriter,
|
||||
const UList<PDRobstacle>& list,
|
||||
label pieceId
|
||||
)
|
||||
{
|
||||
for (const PDRobstacle& obs : list)
|
||||
{
|
||||
meshedSurface surf(obs.surface());
|
||||
|
||||
if (!surf.empty())
|
||||
{
|
||||
surfWriter.piece(surf.points(), surf.surfFaces());
|
||||
|
||||
surfWriter.writeGeometry();
|
||||
surfWriter.beginCellData(2);
|
||||
surfWriter.writeUniform("group", label(obs.groupId));
|
||||
surfWriter.writeUniform("type", label(obs.typeId));
|
||||
surfWriter.writeUniform("obstacle", pieceId);
|
||||
++pieceId;
|
||||
}
|
||||
}
|
||||
|
||||
return pieceId;
|
||||
}
|
||||
|
||||
|
||||
void Foam::PDRobstacle::generateVtk
|
||||
(
|
||||
const fileName& outputDir,
|
||||
const UList<PDRobstacle>& obslist,
|
||||
const UList<PDRobstacle>& cyllist
|
||||
)
|
||||
{
|
||||
label pieceId = 0;
|
||||
|
||||
meshedSurf::emptySurface dummy;
|
||||
|
||||
vtk::surfaceWriter surfWriter
|
||||
(
|
||||
dummy.points(),
|
||||
dummy.faces(),
|
||||
// vtk::formatType::INLINE_ASCII,
|
||||
(outputDir / "Obstacles"),
|
||||
false // serial only
|
||||
);
|
||||
|
||||
pieceId = addPieces(surfWriter, obslist, pieceId);
|
||||
pieceId = addPieces(surfWriter, cyllist, pieceId);
|
||||
|
||||
Info<< "Wrote " << pieceId << " obstacles (VTK) to "
|
||||
<< outputDir/"Obstacles" << nl;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
|
||||
|
||||
Foam::Ostream& Foam::operator<<
|
||||
(
|
||||
Ostream& os,
|
||||
const InfoProxy<PDRobstacle>& iproxy
|
||||
)
|
||||
{
|
||||
const PDRobstacle& obs = iproxy.t_;
|
||||
|
||||
switch (obs.typeId)
|
||||
{
|
||||
case PDRobstacle::CUBOID_1 :
|
||||
case PDRobstacle::CUBOID :
|
||||
os << "box { point " << obs.pt
|
||||
<< "; size " << obs.span
|
||||
<< "; }";
|
||||
break;
|
||||
|
||||
case PDRobstacle::CYLINDER :
|
||||
os << "cyl { point " << obs.pt
|
||||
<< "; length " << obs.len() << "; diameter " << obs.dia()
|
||||
<< "; direction " << vector::componentNames[obs.orient]
|
||||
<< "; }";
|
||||
break;
|
||||
|
||||
case PDRobstacle::DIAG_BEAM :
|
||||
os << "diag { point " << obs.pt
|
||||
<< "; length " << obs.len()
|
||||
<< "; width (" << obs.wa << ' ' << obs.wb << ')'
|
||||
<< "; angle " << radToDeg(obs.theta())
|
||||
<< "; direction " << vector::componentNames[obs.orient]
|
||||
<< "; }";
|
||||
break;
|
||||
|
||||
case PDRobstacle::WALL_BEAM :
|
||||
os << "wallbeam { point " << obs.pt
|
||||
<< " size " << obs.span
|
||||
<< "; }";
|
||||
break;
|
||||
|
||||
case PDRobstacle::GRATING :
|
||||
os << "grate { point " << obs.pt
|
||||
<< "; size " << obs.span
|
||||
<< "; slats " << obs.slat_width
|
||||
<< "; }";
|
||||
break;
|
||||
|
||||
case PDRobstacle::LOUVER_BLOWOFF :
|
||||
os << "louver { point " << obs.pt
|
||||
<< "; size " << obs.span
|
||||
<< "; pressure " << paToBar(obs.blowoff_press)
|
||||
<< "; }";
|
||||
break;
|
||||
|
||||
case PDRobstacle::RECT_PATCH :
|
||||
os << "patch { " << obs.pt
|
||||
<< "; size " << obs.span
|
||||
<< "; name " << obs.identifier
|
||||
<< "; }";
|
||||
break;
|
||||
|
||||
case PDRobstacle::OLD_INLET :
|
||||
case PDRobstacle::OLD_BLOWOFF :
|
||||
case PDRobstacle::IGNITION :
|
||||
os << "/* ignored: " << obs.typeId << " */";
|
||||
break;
|
||||
|
||||
default:
|
||||
os << "/* unknown: " << obs.typeId << " */";
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
|
||||
|
||||
bool Foam::operator<(const PDRobstacle& a, const PDRobstacle& b)
|
||||
{
|
||||
return (a.pt.x() + a.sortBias) < (b.pt.x() + b.sortBias);
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,477 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::PDRobstacle
|
||||
|
||||
Description
|
||||
Obstacle definitions for PDR
|
||||
|
||||
SourceFiles
|
||||
PDRobstacle.C
|
||||
PDRobstacleIO.C
|
||||
PDRobstacleRead.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRobstacle_H
|
||||
#define PDRobstacle_H
|
||||
|
||||
#include "InfoProxy.H"
|
||||
#include "labelPair.H"
|
||||
#include "MeshedSurface.H"
|
||||
#include "MeshedSurfacesFwd.H"
|
||||
#include "boundBox.H"
|
||||
#include "DynamicList.H"
|
||||
#include "pointField.H"
|
||||
#include "volumeType.H"
|
||||
#include "memberFunctionSelectionTables.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Forward Declarations
|
||||
class boundBox;
|
||||
class PDRobstacle;
|
||||
|
||||
Istream& operator>>(Istream& is, PDRobstacle& obs);
|
||||
Ostream& operator<<(Ostream& os, const InfoProxy<PDRobstacle>& info);
|
||||
|
||||
namespace vtk
|
||||
{
|
||||
class surfaceWriter;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class PDRobstacle Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class PDRobstacle
|
||||
{
|
||||
public:
|
||||
|
||||
//- Obstacle types (legacy numbering)
|
||||
enum legacyTypes
|
||||
{
|
||||
NONE = 0, //!< Placeholder
|
||||
CUBOID_1 = 1,
|
||||
CYLINDER = 2,
|
||||
LOUVER_BLOWOFF = 5,
|
||||
LOUVRE_BLOWOFF = 5,
|
||||
CUBOID = 6,
|
||||
WALL_BEAM = 7,
|
||||
GRATING = 8,
|
||||
OLD_INLET = 9, //!< ignored (old)
|
||||
OLD_BLOWOFF = 10, //!< ignored (old)
|
||||
CIRC_PATCH = 12,
|
||||
RECT_PATCH = 16,
|
||||
DIAG_BEAM = 22,
|
||||
IGNITION = 41, //!< ignored (old)
|
||||
MESH_PLANE = 46,
|
||||
IGNORE = 200
|
||||
};
|
||||
|
||||
|
||||
// Static Data Members
|
||||
|
||||
//- The max blowoff pressure [bar]
|
||||
// Primarily to catch accidental input in Pa or mbar
|
||||
static constexpr int maxBlowoffPressure = 10;
|
||||
|
||||
|
||||
// Data Members
|
||||
|
||||
//- The group-id
|
||||
label groupId;
|
||||
|
||||
//- The obstacle type-id
|
||||
int typeId;
|
||||
|
||||
//- The x/y/z orientation (0,1,2)
|
||||
direction orient;
|
||||
|
||||
//- Bias for position sorting
|
||||
scalar sortBias;
|
||||
|
||||
//- The obstacle location.
|
||||
// Lower corner for boxes, end-centre for cylinders
|
||||
point pt;
|
||||
|
||||
//- The obstacle dimensions (for boxes)
|
||||
vector span;
|
||||
|
||||
// Accessors for cylinders and diagonal blocks
|
||||
|
||||
inline scalar dia() const { return span[vector::X]; }
|
||||
inline scalar theta() const { return span[vector::Y]; }
|
||||
inline scalar len() const { return span[vector::Z]; }
|
||||
|
||||
inline scalar& dia() { return span[vector::X]; }
|
||||
inline scalar& theta() { return span[vector::Y]; }
|
||||
inline scalar& len() { return span[vector::Z]; }
|
||||
|
||||
union
|
||||
{
|
||||
scalar wa;
|
||||
scalar slat_width;
|
||||
scalar blowoff_press;
|
||||
};
|
||||
union
|
||||
{
|
||||
scalar wb;
|
||||
scalar blowoff_time;
|
||||
};
|
||||
scalar vbkge;
|
||||
scalar xbkge;
|
||||
scalar ybkge;
|
||||
scalar zbkge;
|
||||
|
||||
union
|
||||
{
|
||||
int blowoff_type;
|
||||
int inlet_dirn;
|
||||
};
|
||||
|
||||
string identifier;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct zero-initialized
|
||||
PDRobstacle();
|
||||
|
||||
//- Read construct as named dictionary
|
||||
explicit PDRobstacle(Istream& is);
|
||||
|
||||
|
||||
// Member Function Selectors
|
||||
|
||||
declareMemberFunctionSelectionTable
|
||||
(
|
||||
void,
|
||||
PDRobstacle,
|
||||
read,
|
||||
dictRead,
|
||||
(
|
||||
PDRobstacle& obs,
|
||||
const dictionary& dict
|
||||
),
|
||||
(obs, dict)
|
||||
);
|
||||
|
||||
|
||||
// Static Member Functions
|
||||
|
||||
//- Read obstacle files and add to the lists
|
||||
// \return the total volume
|
||||
static scalar legacyReadFiles
|
||||
(
|
||||
const fileName& obsFileDir,
|
||||
const wordList& obsFileNames,
|
||||
const boundBox& meshBb,
|
||||
// output
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
DynamicList<PDRobstacle>& cylinders
|
||||
);
|
||||
|
||||
//- Read obstacle files and set the lists
|
||||
// \return the total volume
|
||||
static scalar readFiles
|
||||
(
|
||||
const fileName& obsFileDir,
|
||||
const wordList& obsFileNames,
|
||||
const boundBox& meshBb,
|
||||
// output
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
DynamicList<PDRobstacle>& cylinders
|
||||
);
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- Read name / dictionary
|
||||
bool read(Istream& is);
|
||||
|
||||
//- Read the 'name' identifier if present
|
||||
void readProperties(const dictionary& dict);
|
||||
|
||||
//- Obstacle position accessors
|
||||
inline scalar x() const { return pt.x(); }
|
||||
inline scalar y() const { return pt.y(); }
|
||||
inline scalar z() const { return pt.z(); }
|
||||
inline scalar& x() { return pt.x(); }
|
||||
inline scalar& y() { return pt.y(); }
|
||||
inline scalar& z() { return pt.z(); }
|
||||
|
||||
|
||||
//- Is obstacle type id cylinder-like?
|
||||
inline static bool isCylinder(const label id);
|
||||
|
||||
//- Is obstacle cylinder-like?
|
||||
inline bool isCylinder() const;
|
||||
|
||||
//- Reset to a zero obstacle
|
||||
void clear();
|
||||
|
||||
//- Scale obstacle dimensions by specified scaling factor
|
||||
// Zero and negative factors are ignored
|
||||
void scale(const scalar factor);
|
||||
|
||||
//- Volume of the obstacle
|
||||
scalar volume() const;
|
||||
|
||||
//- True if the obstacle is considered to be too small
|
||||
bool tooSmall(const scalar minWidth) const;
|
||||
|
||||
//- Set values from single-line, multi-column format.
|
||||
// The only input format, but termed 'legacy' since it may
|
||||
// be replaced in the near future.
|
||||
// \return false if the scanning failed or if the obstacle type
|
||||
// is not supported (or no longer supported)
|
||||
bool setFromLegacy
|
||||
(
|
||||
const int groupTypeId,
|
||||
const string& buffer,
|
||||
const label lineNo = -1,
|
||||
const word& inputFile = word::null
|
||||
);
|
||||
|
||||
//- Trim obstacle to ensure it is within the specified bounding box
|
||||
volumeType trim(const boundBox& bb);
|
||||
|
||||
//- Surface (points, faces) representation
|
||||
meshedSurface surface() const;
|
||||
|
||||
//- Add pieces to vtp output
|
||||
static label addPieces
|
||||
(
|
||||
vtk::surfaceWriter& surfWriter,
|
||||
const UList<PDRobstacle>& list,
|
||||
label pieceId = 0
|
||||
);
|
||||
|
||||
//- Generate multi-piece VTK (vtp) file of obstacles
|
||||
static void generateVtk
|
||||
(
|
||||
const fileName& outputDir,
|
||||
const UList<PDRobstacle>& obslist,
|
||||
const UList<PDRobstacle>& cyllist
|
||||
);
|
||||
|
||||
|
||||
// IOstream Operators
|
||||
|
||||
//- Return info proxy.
|
||||
InfoProxy<PDRobstacle> info() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend Istream& operator>>(Istream& is, PDRobstacle& obs);
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
// Global Operators
|
||||
|
||||
//- Compare according to x0 position
|
||||
bool operator<(const PDRobstacle& a, const PDRobstacle& b);
|
||||
|
||||
//- For list output, assert that no obstacles are identical
|
||||
inline bool operator!=(const PDRobstacle& a, const PDRobstacle& b)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace PDRlegacy
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class obstacleGrouping Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
//- Locations for each instance of an obstacle group.
|
||||
class obstacleGrouping
|
||||
:
|
||||
public DynamicList<point>
|
||||
{
|
||||
//- Number of obstacles counted
|
||||
label nObstacle_;
|
||||
|
||||
//- Number of cylinder-like obstacles counted
|
||||
label nCylinder_;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- Construct null
|
||||
obstacleGrouping()
|
||||
:
|
||||
nObstacle_(0),
|
||||
nCylinder_(0)
|
||||
{}
|
||||
|
||||
//- Construct with one location (instance)
|
||||
explicit obstacleGrouping(const vector& origin)
|
||||
:
|
||||
obstacleGrouping()
|
||||
{
|
||||
append(origin);
|
||||
}
|
||||
|
||||
//- Clear obstacle count and locations
|
||||
void clear()
|
||||
{
|
||||
nObstacle_ = 0;
|
||||
nCylinder_ = 0;
|
||||
DynamicList<point>::clear();
|
||||
}
|
||||
|
||||
//- Increment the number of obstacles
|
||||
void addObstacle()
|
||||
{
|
||||
++nObstacle_;
|
||||
}
|
||||
|
||||
//- Increment the number of cylinder-like obstacles
|
||||
void addCylinder()
|
||||
{
|
||||
++nCylinder_;
|
||||
}
|
||||
|
||||
//- The number of obstacles
|
||||
label nObstacle() const
|
||||
{
|
||||
return nObstacle_;
|
||||
}
|
||||
|
||||
//- The number of cylinder-like obstacles
|
||||
label nCylinder() const
|
||||
{
|
||||
return nCylinder_;
|
||||
}
|
||||
|
||||
//- The number of locations x number of obstacles
|
||||
label nTotalObstacle() const
|
||||
{
|
||||
return size() * nObstacle_;
|
||||
}
|
||||
|
||||
//- The number of locations x number of cylinder-like obstacles
|
||||
label nTotalCylinder() const
|
||||
{
|
||||
return size() * nCylinder_;
|
||||
}
|
||||
|
||||
//- The number of locations x number of obstacles
|
||||
label nTotal() const
|
||||
{
|
||||
return size() * (nObstacle_ + nCylinder_);
|
||||
}
|
||||
|
||||
//- Add a location
|
||||
using DynamicList<point>::append;
|
||||
|
||||
//- Add a location
|
||||
void append(const scalar x, const scalar y, const scalar z)
|
||||
{
|
||||
append(point(x, y, z));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Service Functions
|
||||
|
||||
//- Read obstacle files, do counting only.
|
||||
// \return nObstacle, nCylinder read
|
||||
labelPair readObstacleFiles
|
||||
(
|
||||
const fileName& obsFileDir,
|
||||
const wordList& obsFileNames,
|
||||
Map<obstacleGrouping>& groups
|
||||
);
|
||||
|
||||
|
||||
//- Read obstacle files and add to the lists
|
||||
// \return the total volume
|
||||
scalar readObstacleFiles
|
||||
(
|
||||
const fileName& obsFileDir,
|
||||
const wordList& obsFileNames,
|
||||
const Map<obstacleGrouping>& groups,
|
||||
const boundBox& meshBb,
|
||||
// output
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
DynamicList<PDRobstacle>& cylinders
|
||||
);
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace PDRlegacy
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
// Global Operators
|
||||
|
||||
//- Locations for each instance of an obstacle group.
|
||||
inline Ostream& operator<<
|
||||
(
|
||||
Ostream& os,
|
||||
const PDRlegacy::obstacleGrouping& group
|
||||
)
|
||||
{
|
||||
os << token::BEGIN_LIST
|
||||
<< group.size() << token::SPACE
|
||||
<< group.nObstacle() << token::SPACE
|
||||
<< group.nCylinder() << token::END_LIST;
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#include "PDRobstacleI.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
inline bool Foam::PDRobstacle::isCylinder(const label id)
|
||||
{
|
||||
return
|
||||
(
|
||||
id == PDRobstacle::CYLINDER
|
||||
|| id == PDRobstacle::DIAG_BEAM
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
inline bool Foam::PDRobstacle::isCylinder() const
|
||||
{
|
||||
return isCylinder(typeId);
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,353 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRsetFields.H"
|
||||
#include "PDRobstacle.H"
|
||||
#include "volumeType.H"
|
||||
|
||||
using namespace Foam;
|
||||
using namespace Foam::constant;
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
bool Foam::PDRobstacle::read(Istream& is)
|
||||
{
|
||||
this->clear();
|
||||
|
||||
const word obsType(is);
|
||||
const dictionary dict(is);
|
||||
|
||||
const auto mfIter = readdictReadMemberFunctionTablePtr_->cfind(obsType);
|
||||
|
||||
if (!mfIter.good())
|
||||
{
|
||||
FatalIOErrorInFunction(is)
|
||||
<< "Unknown obstacle type: " << obsType << nl
|
||||
<< "Valid types:" << nl
|
||||
<< readdictReadMemberFunctionTablePtr_->sortedToc() << nl
|
||||
<< exit(FatalIOError);
|
||||
}
|
||||
|
||||
mfIter()(*this, dict);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::PDRobstacle::readFiles
|
||||
(
|
||||
const fileName& obsFileDir,
|
||||
const wordList& obsFileNames,
|
||||
const boundBox& meshBb,
|
||||
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
DynamicList<PDRobstacle>& cylinders
|
||||
)
|
||||
{
|
||||
blocks.clear();
|
||||
cylinders.clear();
|
||||
|
||||
scalar totVolume = 0;
|
||||
label nOutside = 0;
|
||||
label nProtruding = 0;
|
||||
|
||||
scalar shift = pars.obs_expand;
|
||||
|
||||
if (!obsFileNames.empty())
|
||||
{
|
||||
Info<< "Reading obstacle files" << nl;
|
||||
}
|
||||
|
||||
label maxGroup = -1;
|
||||
|
||||
for (const word& inputFile : obsFileNames)
|
||||
{
|
||||
Info<< " file: " << inputFile << nl;
|
||||
|
||||
fileName path = (obsFileDir / inputFile);
|
||||
|
||||
IFstream is(path);
|
||||
dictionary inputDict(is);
|
||||
|
||||
const scalar scaleFactor = inputDict.getOrDefault<scalar>("scale", 0);
|
||||
|
||||
const label verbose = inputDict.getOrDefault<label>("verbose", 0);
|
||||
|
||||
for (const entry& dEntry : inputDict)
|
||||
{
|
||||
if (!dEntry.isDict())
|
||||
{
|
||||
// ignore non-dictionary entry
|
||||
continue;
|
||||
}
|
||||
|
||||
const dictionary& dict = dEntry.dict();
|
||||
|
||||
if (!dict.getOrDefault("enabled", true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
label obsGroupId = 0;
|
||||
if (dict.readIfPresent("groupId", obsGroupId))
|
||||
{
|
||||
maxGroup = max(maxGroup, obsGroupId);
|
||||
}
|
||||
else
|
||||
{
|
||||
obsGroupId = ++maxGroup;
|
||||
}
|
||||
|
||||
|
||||
pointField pts;
|
||||
dict.readIfPresent("locations", pts);
|
||||
if (pts.empty())
|
||||
{
|
||||
pts.resize(1, Zero);
|
||||
}
|
||||
|
||||
List<PDRobstacle> obsInput;
|
||||
dict.readEntry("obstacles", obsInput);
|
||||
|
||||
label nCyl = 0; // The number of cylinders vs blocks
|
||||
|
||||
for (PDRobstacle& obs : obsInput)
|
||||
{
|
||||
obs.groupId = obsGroupId;
|
||||
obs.scale(scaleFactor);
|
||||
|
||||
if (obs.isCylinder())
|
||||
{
|
||||
++nCyl;
|
||||
}
|
||||
}
|
||||
|
||||
const label nBlock = (obsInput.size() - nCyl);
|
||||
|
||||
blocks.reserve(blocks.size() + nBlock*pts.size());
|
||||
cylinders.reserve(cylinders.size() + nCyl*pts.size());
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
Info<< "Read " << obsInput.size() << " obstacles ("
|
||||
<< nCyl << " cylinders) with "
|
||||
<< pts.size() << " locations" << nl;
|
||||
|
||||
if (verbose > 1)
|
||||
{
|
||||
Info<< "locations " << pts << nl
|
||||
<< "obstacles " << obsInput << nl;
|
||||
}
|
||||
}
|
||||
|
||||
for (const PDRobstacle& scanObs : obsInput)
|
||||
{
|
||||
// Reject anything below minimum width
|
||||
if (scanObs.tooSmall(pars.min_width))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const point& origin : pts)
|
||||
{
|
||||
// A different (very small) shift for each obstacle
|
||||
// so that faces cannot be coincident
|
||||
|
||||
shift += floatSMALL;
|
||||
const scalar shift2 = shift * 2.0;
|
||||
|
||||
|
||||
switch (scanObs.typeId)
|
||||
{
|
||||
case PDRobstacle::CYLINDER:
|
||||
{
|
||||
// Make a copy
|
||||
PDRobstacle obs(scanObs);
|
||||
|
||||
// Offset for the group position
|
||||
obs.pt += origin;
|
||||
|
||||
// Shift the end outwards so, if exactly on
|
||||
// cell boundary, now overlap cell.
|
||||
// So included in Aw.
|
||||
obs.pt -= point::uniform(shift);
|
||||
obs.len() += shift2;
|
||||
obs.dia() -= floatSMALL;
|
||||
|
||||
|
||||
// Trim against the mesh bounds
|
||||
// - ignore if it doesn't overlap
|
||||
const volumeType vt = obs.trim(meshBb);
|
||||
|
||||
switch (vt)
|
||||
{
|
||||
case volumeType::OUTSIDE:
|
||||
++nOutside;
|
||||
continue; // Can ignore the rest
|
||||
break;
|
||||
|
||||
case volumeType::MIXED:
|
||||
++nProtruding;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Later for position sorting
|
||||
switch (obs.orient)
|
||||
{
|
||||
case vector::X:
|
||||
obs.sortBias = obs.len();
|
||||
break;
|
||||
case vector::Y:
|
||||
obs.sortBias = 0.5*obs.dia();
|
||||
break;
|
||||
case vector::Z:
|
||||
obs.sortBias = 0.5*obs.dia();
|
||||
break;
|
||||
}
|
||||
|
||||
totVolume += obs.volume();
|
||||
cylinders.append(obs);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::DIAG_BEAM:
|
||||
{
|
||||
// Make a copy
|
||||
PDRobstacle obs(scanObs);
|
||||
|
||||
// Offset for the group position
|
||||
obs.pt += origin;
|
||||
|
||||
// Shift the end outwards so, if exactly on
|
||||
// cell boundary, now overlap cell.
|
||||
// So included in Aw.
|
||||
obs.pt -= point::uniform(shift);
|
||||
obs.len() += shift2;
|
||||
obs.wa += shift2;
|
||||
obs.wb += shift2;
|
||||
|
||||
totVolume += obs.volume();
|
||||
cylinders.append(obs);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::CUBOID_1:
|
||||
case PDRobstacle::LOUVRE_BLOWOFF:
|
||||
case PDRobstacle::CUBOID:
|
||||
case PDRobstacle::WALL_BEAM:
|
||||
case PDRobstacle::GRATING:
|
||||
case PDRobstacle::RECT_PATCH:
|
||||
{
|
||||
// Make a copy
|
||||
PDRobstacle obs(scanObs);
|
||||
|
||||
// Offset for the position of the group
|
||||
obs.pt += origin;
|
||||
|
||||
if (obs.typeId == PDRobstacle::GRATING)
|
||||
{
|
||||
if (obs.slat_width <= 0)
|
||||
{
|
||||
obs.slat_width = pars.def_grating_slat_w;
|
||||
}
|
||||
}
|
||||
|
||||
// Shift the end outwards so, if exactly on
|
||||
// cell boundary, now overlap cell.
|
||||
// So included in Aw.
|
||||
obs.pt -= point::uniform(shift);
|
||||
obs.span += point::uniform(shift2);
|
||||
|
||||
|
||||
// Trim against the mesh bounds
|
||||
// - ignore if it doesn't overlap
|
||||
const volumeType vt = obs.trim(meshBb);
|
||||
|
||||
switch (vt)
|
||||
{
|
||||
case volumeType::OUTSIDE:
|
||||
++nOutside;
|
||||
continue; // Can ignore the rest
|
||||
break;
|
||||
|
||||
case volumeType::MIXED:
|
||||
++nProtruding;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
totVolume += obs.volume();
|
||||
|
||||
blocks.append(obs);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Info<< "Cylinders: " << cylinders << nl;
|
||||
}
|
||||
|
||||
if (nOutside || nProtruding)
|
||||
{
|
||||
Info<< "Warning: " << nOutside << " obstacles outside domain, "
|
||||
<< nProtruding << " obstacles partly outside domain" << nl;
|
||||
}
|
||||
}
|
||||
|
||||
// #ifdef FULLDEBUG
|
||||
// Info<< blocks << nl << cylinders << nl;
|
||||
// #endif
|
||||
|
||||
|
||||
Info<< "Number of obstacles: "
|
||||
<< (blocks.size() + cylinders.size()) << " ("
|
||||
<< cylinders.size() << " cylinders)" << nl;
|
||||
|
||||
return totVolume;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
|
||||
|
||||
Foam::Istream& Foam::operator>>(Istream& is, PDRobstacle& obs)
|
||||
{
|
||||
obs.read(is);
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,475 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRobstacle.H"
|
||||
#include "vector.H"
|
||||
#include "doubleVector.H"
|
||||
#include "stringOps.H"
|
||||
#include "unitConversion.H"
|
||||
#include <cmath>
|
||||
|
||||
#define ReportLineInfo(line, file) \
|
||||
if (line >= 0 && !file.empty()) \
|
||||
{ \
|
||||
Info<< " Line " << line << " of file '" << file << '\''; \
|
||||
} \
|
||||
Info<< nl;
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
bool Foam::PDRobstacle::setFromLegacy
|
||||
(
|
||||
const int groupTypeId,
|
||||
const string& buffer,
|
||||
const label lineNo,
|
||||
const word& inputFile
|
||||
)
|
||||
{
|
||||
// Handling the optional identifier string can be a pain.
|
||||
// Generally can only say that it exists if there are 15 or
|
||||
// more columns.
|
||||
//
|
||||
// Cylinder has 8 normal entries
|
||||
// Cuboid, diagonal beam etc have 14 normal entries
|
||||
// However, reject anything that looks like a slipped numeric
|
||||
|
||||
double dummy1;
|
||||
|
||||
string in_ident;
|
||||
|
||||
const auto columns = stringOps::splitSpace(buffer);
|
||||
|
||||
for (std::size_t coli = 14; coli < columns.size(); ++coli)
|
||||
{
|
||||
// See if it can parse into a numerical value
|
||||
if (!readDouble(columns[coli].str(), dummy1))
|
||||
{
|
||||
// Not a numeric value. This must be our identifier
|
||||
in_ident = buffer.substr(columns[coli].first - buffer.begin());
|
||||
|
||||
#ifdef FULLDEBUG
|
||||
Info<< "Identifier: " << in_ident << nl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Strip off group number
|
||||
groupId = groupTypeId / 100;
|
||||
typeId = groupTypeId % 100;
|
||||
|
||||
// This is a safe value
|
||||
orient = vector::X;
|
||||
|
||||
switch (typeId)
|
||||
{
|
||||
case PDRobstacle::CYLINDER:
|
||||
{
|
||||
// 8 Tokens
|
||||
// "%d %lf %lf %lf %lf %lf %d %lf"
|
||||
// USP 13/8/14 Read vbkge in case a negative cyl to punch a circular hole
|
||||
|
||||
int in_typeId;
|
||||
double in_x, in_y, in_z;
|
||||
double in_len, in_dia;
|
||||
int in_orient;
|
||||
double in_poro;
|
||||
|
||||
int nread =
|
||||
sscanf
|
||||
(
|
||||
buffer.c_str(),
|
||||
"%d %lf %lf %lf %lf %lf %d %lf",
|
||||
&in_typeId, &in_x, &in_y, &in_z,
|
||||
&in_len, &in_dia, &in_orient,
|
||||
&in_poro
|
||||
);
|
||||
|
||||
if (nread < 8)
|
||||
{
|
||||
Info<< "Expected 8 items, but read in " << nread;
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
}
|
||||
|
||||
identifier = in_ident;
|
||||
pt = point(in_x, in_y, in_z);
|
||||
|
||||
len() = in_len;
|
||||
dia() = in_dia;
|
||||
|
||||
orient = vector::X; // Check again later
|
||||
|
||||
// Read porosity. Convert to blockage.
|
||||
vbkge = 1.0 - in_poro;
|
||||
|
||||
// Orientation (1,2,3) on input -> (0,1,2)
|
||||
// - sortBias for later position sorting
|
||||
switch (in_orient)
|
||||
{
|
||||
case 1:
|
||||
orient = vector::X;
|
||||
sortBias = len();
|
||||
break;
|
||||
case 2:
|
||||
orient = vector::Y;
|
||||
sortBias = 0.5*dia();
|
||||
break;
|
||||
case 3:
|
||||
orient = vector::Z;
|
||||
sortBias = 0.5*dia();
|
||||
break;
|
||||
default:
|
||||
sortBias = len();
|
||||
Info<< "Unexpected orientation " << in_orient;
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PDRobstacle::DIAG_BEAM:
|
||||
{
|
||||
// A diagonal block
|
||||
|
||||
// 14 columns + identifier
|
||||
// "%d %lf %lf %lf %lf %lf %d %lf %lf %lf %lf %lf %d %lf %s"
|
||||
// vbkge (porosity at this stage) should be 0. Not used (yet)
|
||||
|
||||
int in_typeId;
|
||||
double in_x, in_y, in_z;
|
||||
double in_len, in_theta;
|
||||
int in_orient;
|
||||
double in_wa, in_wb, in_poro;
|
||||
double col_11, col_12, col_14;
|
||||
int col_13;
|
||||
|
||||
int nread =
|
||||
sscanf
|
||||
(
|
||||
buffer.c_str(),
|
||||
"%d %lf %lf %lf %lf %lf %d %lf %lf %lf %lf %lf %d %lf",
|
||||
&in_typeId, &in_x, &in_y, &in_z,
|
||||
&in_len, &in_theta, &in_orient,
|
||||
&in_wa, &in_wb, &in_poro,
|
||||
&col_11, &col_12, &col_13, &col_14
|
||||
);
|
||||
|
||||
if (nread < 14)
|
||||
{
|
||||
Info<< "Expected min 10 items, but read in " << nread;
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
}
|
||||
|
||||
identifier = in_ident;
|
||||
pt = point(in_x, in_y, in_z);
|
||||
|
||||
len() = in_len;
|
||||
dia() = 0;
|
||||
theta() = 0; // Fix later on
|
||||
|
||||
orient = vector::X; // Check again later
|
||||
|
||||
wa = in_wa;
|
||||
wb = in_wb;
|
||||
|
||||
// Degrees on input, limit to range [0, PI]
|
||||
while (in_theta > 180) in_theta -= 180;
|
||||
while (in_theta < 0) in_theta += 180;
|
||||
|
||||
// Swap axes when theta > PI/2
|
||||
// For 89-90 degrees it becomes -ve, which is picked up
|
||||
// in next section
|
||||
if (in_theta > 89)
|
||||
{
|
||||
in_theta -= 90;
|
||||
// Swap wa <-> wb
|
||||
wa = in_wb;
|
||||
wb = in_wa;
|
||||
}
|
||||
|
||||
theta() = degToRad(in_theta);
|
||||
|
||||
// Orientation (1,2,3) on input -> (0,1,2)
|
||||
// - sortBias for later position sorting
|
||||
switch (in_orient)
|
||||
{
|
||||
case 1:
|
||||
orient = vector::X;
|
||||
sortBias = len();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
orient = vector::Y;
|
||||
sortBias = 0.5*(wa * sin(theta()) + wb * cos(theta()));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
orient = vector::Z;
|
||||
sortBias = 0.5*(wa * cos(theta()) + wb * sin(theta()));
|
||||
break;
|
||||
|
||||
default:
|
||||
sortBias = len();
|
||||
Info<< "Unexpected orientation " << in_orient;
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// If very nearly aligned with axis, turn it into normal block,
|
||||
// to avoid 1/tan(theta) blowing up
|
||||
if (in_theta < 1)
|
||||
{
|
||||
switch (orient)
|
||||
{
|
||||
case vector::X:
|
||||
span = vector(len(), wa, wb);
|
||||
// Was end center, now lower corner
|
||||
pt.y() = pt.y() - 0.5 * span.y();
|
||||
pt.z() = pt.z() - 0.5 * span.z();
|
||||
break;
|
||||
|
||||
case vector::Y:
|
||||
span = vector(wb, len(), wa);
|
||||
// Was end center, now lower corner
|
||||
pt.z() = pt.z() - 0.5 * span.z();
|
||||
pt.x() = pt.x() - 0.5 * span.x();
|
||||
break;
|
||||
|
||||
case vector::Z:
|
||||
span = vector(wa, wb, len());
|
||||
// Was end center, now lower corner
|
||||
pt.x() = pt.x() - 0.5 * span.x();
|
||||
pt.y() = pt.y() - 0.5 * span.y();
|
||||
break;
|
||||
}
|
||||
|
||||
typeId = PDRobstacle::CUBOID;
|
||||
sortBias = 0;
|
||||
xbkge = ybkge = zbkge = vbkge = 1.0;
|
||||
blowoff_type = 0;
|
||||
|
||||
Info<< "... changed to type cuboid" << nl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PDRobstacle::CUBOID_1: // Cuboid "Type 1"
|
||||
case PDRobstacle::LOUVRE_BLOWOFF: // Louvred wall or blow-off panel
|
||||
case PDRobstacle::CUBOID: // Cuboid
|
||||
case PDRobstacle::WALL_BEAM: // Beam against wall (treated here as normal cuboid)
|
||||
case PDRobstacle::GRATING: // Grating
|
||||
case PDRobstacle::RECT_PATCH: // Inlet, outlet, other b.c. (rectangular)
|
||||
{
|
||||
// 14 columns + identifier
|
||||
// "%d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %d %lf"
|
||||
|
||||
int in_typeId;
|
||||
double in_x, in_y, in_z;
|
||||
double in_delx, in_dely, in_delz;
|
||||
double in_poro, in_porox, in_poroy, in_poroz;
|
||||
double col_12;
|
||||
int col_13;
|
||||
double in_blowoff_time = 0;
|
||||
|
||||
int nread =
|
||||
sscanf
|
||||
(
|
||||
buffer.c_str(),
|
||||
"%d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %d %lf",
|
||||
&in_typeId, &in_x, &in_y, &in_z,
|
||||
&in_delx, &in_dely, &in_delz,
|
||||
&in_poro, &in_porox, &in_poroy, &in_poroz,
|
||||
&col_12, &col_13, &in_blowoff_time
|
||||
);
|
||||
|
||||
blowoff_time = scalar(in_blowoff_time);
|
||||
|
||||
if (nread < 14)
|
||||
{
|
||||
Info<< "Expected 14 items, but read in " << nread;
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
}
|
||||
|
||||
identifier = in_ident;
|
||||
pt = point(in_x, in_y, in_z);
|
||||
|
||||
span = vector(in_delx, in_dely, in_delz);
|
||||
|
||||
// Read porosity. Convert to blockage.
|
||||
vbkge = 1.0 - in_poro;
|
||||
xbkge = 1.0 - in_porox;
|
||||
ybkge = 1.0 - in_poroy;
|
||||
zbkge = 1.0 - in_poroz;
|
||||
|
||||
if
|
||||
(
|
||||
typeId == PDRobstacle::CUBOID_1
|
||||
|| typeId == PDRobstacle::WALL_BEAM
|
||||
|| typeId == PDRobstacle::RECT_PATCH
|
||||
)
|
||||
{
|
||||
// Check for invalid input
|
||||
|
||||
if (vbkge != 1.0 || xbkge != 1.0 || ybkge != 1.0 || zbkge != 1.0)
|
||||
{
|
||||
Info<< "Type " << typeId << " is porous (setting to blockage).";
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
|
||||
vbkge = 1;
|
||||
xbkge = 1;
|
||||
ybkge = 1;
|
||||
zbkge = 1;
|
||||
}
|
||||
if (typeId == PDRobstacle::RECT_PATCH)
|
||||
{
|
||||
// Correct interpretation of column 13
|
||||
inlet_dirn = col_13;
|
||||
|
||||
if (identifier.empty())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "RECT_PATCH without a patch name"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (typeId == PDRobstacle::CUBOID)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!equal(cmptProduct(span), 0))
|
||||
{
|
||||
Info<< "Type " << typeId << " has non-zero thickness.";
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeId == PDRobstacle::LOUVRE_BLOWOFF)
|
||||
{
|
||||
// Blowoff panel
|
||||
blowoff_press = barToPa(col_12);
|
||||
blowoff_type = col_13;
|
||||
|
||||
if (blowoff_type == 1)
|
||||
{
|
||||
Info<< "Type " << typeId
|
||||
<< ": blowoff-type 1 not yet implemented.";
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
|
||||
if (blowoff_time != 0)
|
||||
{
|
||||
Info<< "Type " << typeId << " has blowoff time set,"
|
||||
<< " not set to blow off cell-by-cell";
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if
|
||||
(
|
||||
(blowoff_type == 1 || blowoff_type == 2)
|
||||
&& (col_12 > 0)
|
||||
)
|
||||
{
|
||||
if (col_12 > maxBlowoffPressure)
|
||||
{
|
||||
Info<< "Blowoff pressure (" << col_12
|
||||
<< ") too high for blowoff type "
|
||||
<< blowoff_type;
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< "Problem with blowoff parameters";
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
Info<< "Col12 " << col_12
|
||||
<< " Blowoff type " << blowoff_type
|
||||
<< ", blowoff pressure " << blowoff_press << nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (typeId == PDRobstacle::WALL_BEAM)
|
||||
{
|
||||
// WALL_BEAM against walls only contribute half to drag
|
||||
// if ((col_12 == 1) || (col_12 == -1)) { against_wall_fac = 0.5; }
|
||||
}
|
||||
else if (typeId == PDRobstacle::GRATING)
|
||||
{
|
||||
if (col_12 > 0)
|
||||
{
|
||||
slat_width = col_12;
|
||||
}
|
||||
else
|
||||
{
|
||||
slat_width = 0;
|
||||
}
|
||||
|
||||
// Set orientation
|
||||
if (equal(span.x(), 0))
|
||||
{
|
||||
orient = vector::X;
|
||||
}
|
||||
else if (equal(span.y(), 0))
|
||||
{
|
||||
orient = vector::Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
orient = vector::Z;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0: // Group location
|
||||
case PDRobstacle::OLD_INLET: // Ventilation source only
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PDRobstacle::IGNITION: // Ignition (now ignored. 2019-04)
|
||||
Info<< "Ignition cell type ignored";
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
Info<< "Unexpected type " << typeId;
|
||||
ReportLineInfo(lineNo, inputFile);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,567 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 Shell Research Ltd.
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRsetFields.H"
|
||||
#include "PDRobstacle.H"
|
||||
#include "volumeType.H"
|
||||
|
||||
using namespace Foam;
|
||||
using namespace Foam::constant;
|
||||
|
||||
#undef USE_ZERO_INSTANCE_GROUPS
|
||||
// #define USE_ZERO_INSTANCE_GROUPS
|
||||
|
||||
|
||||
// Counting
|
||||
Foam::labelPair Foam::PDRlegacy::readObstacleFiles
|
||||
(
|
||||
const fileName& obsFileDir,
|
||||
const wordList& obsFileNames,
|
||||
Map<obstacleGrouping>& groups
|
||||
)
|
||||
{
|
||||
// Default group with single instance and position (0,0,0)
|
||||
groups(0).clear();
|
||||
groups(0).append(point::zero);
|
||||
|
||||
string buffer;
|
||||
|
||||
if (!obsFileNames.empty())
|
||||
{
|
||||
Info<< "Counting groups in obstacle files" << nl;
|
||||
}
|
||||
for (const word& inputFile : obsFileNames)
|
||||
{
|
||||
Info<< " file: " << inputFile << nl;
|
||||
|
||||
fileName path = (obsFileDir / inputFile);
|
||||
|
||||
IFstream is(path);
|
||||
|
||||
while (is.good())
|
||||
{
|
||||
// Process each line of obstacle files
|
||||
is.getLine(buffer);
|
||||
|
||||
const auto firstCh = buffer.find_first_not_of(" \t\n\v\f\r");
|
||||
|
||||
if
|
||||
(
|
||||
firstCh == std::string::npos
|
||||
|| buffer[firstCh] == '#'
|
||||
)
|
||||
{
|
||||
// Empty line or comment line
|
||||
continue;
|
||||
}
|
||||
|
||||
int typeId;
|
||||
double x, y, z; // Double (not scalar) to match sscanf spec
|
||||
|
||||
if
|
||||
(
|
||||
sscanf(buffer.c_str(), "%d %lf %lf %lf", &typeId, &x, &y, &z)<4
|
||||
|| typeId == 0
|
||||
|| typeId == PDRobstacle::MESH_PLANE
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
x *= pars.scale;
|
||||
y *= pars.scale;
|
||||
z *= pars.scale;
|
||||
|
||||
const label groupId = typeId / 100;
|
||||
typeId %= 100;
|
||||
|
||||
if (typeId == PDRobstacle::OLD_INLET)
|
||||
{
|
||||
Info<< "Ignored old-inlet type" << nl;
|
||||
continue;
|
||||
}
|
||||
else if (typeId == PDRobstacle::GRATING && pars.ignoreGratings)
|
||||
{
|
||||
Info<< "Ignored grating" << nl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeId == 0)
|
||||
{
|
||||
// Defining a group location
|
||||
groups(groupId).append(x, y, z);
|
||||
}
|
||||
else if (PDRobstacle::isCylinder(typeId))
|
||||
{
|
||||
// Increment cylinder count for the group
|
||||
groups(groupId).addCylinder();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Increment obstacle count for the group
|
||||
groups(groupId).addObstacle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
label nTotalObs = 0;
|
||||
label nTotalCyl = 0;
|
||||
|
||||
label nMissedObs = 0;
|
||||
label nMissedCyl = 0;
|
||||
|
||||
forAllConstIters(groups, iter)
|
||||
{
|
||||
const auto& group = iter.val();
|
||||
|
||||
nTotalObs += group.nTotalObstacle();
|
||||
nTotalCyl += group.nTotalCylinder();
|
||||
|
||||
if (group.empty())
|
||||
{
|
||||
nMissedObs += group.nObstacle();
|
||||
nMissedCyl += group.nCylinder();
|
||||
}
|
||||
}
|
||||
|
||||
for (const label groupId : groups.sortedToc())
|
||||
{
|
||||
const auto& group = groups[groupId];
|
||||
|
||||
if (groupId)
|
||||
{
|
||||
if (group.size())
|
||||
{
|
||||
Info<< "Found " << group.size()
|
||||
<< " instances of group " << groupId << " ("
|
||||
<< group.nObstacle() << " obstacles "
|
||||
<< group.nCylinder() << " cylinders)"
|
||||
<< nl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The group 0 is for ungrouped obstacles
|
||||
Info<< "Found "
|
||||
<< group.nObstacle() << " obstacles "
|
||||
<< group.nCylinder() << " cylinders not in groups" << nl;
|
||||
}
|
||||
}
|
||||
|
||||
Info<< "Number of obstacles: "
|
||||
<< (nTotalObs + nTotalCyl) << " ("
|
||||
<< nTotalCyl << " cylinders)" << nl;
|
||||
|
||||
if (nMissedObs + nMissedCyl)
|
||||
{
|
||||
#ifdef USE_ZERO_INSTANCE_GROUPS
|
||||
|
||||
nTotalObs += nMissedObs;
|
||||
nTotalCyl += nMissedCyl;
|
||||
Info<< "Adding " << (nMissedObs + nMissedCyl)
|
||||
<< " obstacles in groups without instances to default group" << nl;
|
||||
|
||||
#else
|
||||
|
||||
Warning
|
||||
<< nl << "Found " << (nMissedObs + nMissedCyl)
|
||||
<< " obstacles in groups without instances" << nl << nl;
|
||||
|
||||
if (pars.debugLevel > 1)
|
||||
{
|
||||
for (const label groupId : groups.sortedToc())
|
||||
{
|
||||
const auto& group = groups[groupId];
|
||||
|
||||
if
|
||||
(
|
||||
groupId && group.empty()
|
||||
&& (group.nObstacle() || group.nCylinder())
|
||||
)
|
||||
{
|
||||
Info<< " Group " << groupId << " ("
|
||||
<< group.nObstacle() << " obstacles "
|
||||
<< group.nCylinder() << " cylinders)"
|
||||
<< nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return labelPair(nTotalObs, nTotalCyl);
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::PDRlegacy::readObstacleFiles
|
||||
(
|
||||
const fileName& obsFileDir,
|
||||
const wordList& obsFileNames,
|
||||
const Map<obstacleGrouping>& groups,
|
||||
const boundBox& meshBb,
|
||||
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
DynamicList<PDRobstacle>& cylinders
|
||||
)
|
||||
{
|
||||
// Catch programming errors
|
||||
if (!groups.found(0))
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "No default group 0 defined!" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
scalar totVolume = 0;
|
||||
label nOutside = 0;
|
||||
label nProtruding = 0;
|
||||
|
||||
scalar shift = pars.obs_expand;
|
||||
|
||||
string buffer;
|
||||
|
||||
if (!obsFileNames.empty())
|
||||
{
|
||||
Info<< "Reading obstacle files" << nl;
|
||||
}
|
||||
|
||||
for (const word& inputFile : obsFileNames)
|
||||
{
|
||||
Info<< " file: " << inputFile << nl;
|
||||
|
||||
fileName path = (obsFileDir / inputFile);
|
||||
|
||||
IFstream is(path);
|
||||
|
||||
label lineNo = 0;
|
||||
while (is.good())
|
||||
{
|
||||
// Process each line of obstacle files
|
||||
++lineNo;
|
||||
is.getLine(buffer);
|
||||
|
||||
const auto firstCh = buffer.find_first_not_of(" \t\n\v\f\r");
|
||||
|
||||
if
|
||||
(
|
||||
firstCh == std::string::npos
|
||||
|| buffer[firstCh] == '#'
|
||||
)
|
||||
{
|
||||
// Empty line or comment line
|
||||
continue;
|
||||
}
|
||||
|
||||
// Quick reject
|
||||
|
||||
int typeId; // Int (not label) to match sscanf spec
|
||||
double x, y, z; // Double (not scalar) to match sscanf spec
|
||||
|
||||
if
|
||||
(
|
||||
sscanf(buffer.c_str(), "%d %lf %lf %lf", &typeId, &x, &y, &z) < 4
|
||||
|| typeId == 0
|
||||
|| typeId == PDRobstacle::MESH_PLANE
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int groupId = typeId / 100;
|
||||
typeId %= 100;
|
||||
|
||||
if
|
||||
(
|
||||
typeId == PDRobstacle::OLD_INLET
|
||||
|| (typeId == PDRobstacle::GRATING && pars.ignoreGratings)
|
||||
)
|
||||
{
|
||||
// Silent - already warned during counting
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeId == 0)
|
||||
{
|
||||
// Group location - not an obstacle
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!groups.found(groupId))
|
||||
{
|
||||
// Catch programming errors.
|
||||
// - group should be there after the previous read
|
||||
Warning
|
||||
<< "Encountered undefined group: " << groupId << nl;
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef USE_ZERO_INSTANCE_GROUPS
|
||||
const obstacleGrouping& group =
|
||||
(
|
||||
groups[groups[groupId].size() ? groupId : 0]
|
||||
);
|
||||
#else
|
||||
const obstacleGrouping& group = groups[groupId];
|
||||
#endif
|
||||
|
||||
// Add the obstacle to the list with different position
|
||||
// offsets according to its group. Non-group obstacles
|
||||
// are treated as group 0, which has a single instance
|
||||
// with position (0,0,0) and are added only once.
|
||||
|
||||
PDRobstacle scanObs;
|
||||
|
||||
if
|
||||
(
|
||||
!scanObs.setFromLegacy
|
||||
(
|
||||
(groupId * 100) + typeId,
|
||||
buffer,
|
||||
lineNo,
|
||||
inputFile
|
||||
)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
scanObs.scale(pars.scale);
|
||||
|
||||
// Ignore anything below minimum width
|
||||
if (scanObs.tooSmall(pars.min_width))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
for (const point& origin : group)
|
||||
{
|
||||
// A different (very small) shift for each obstacle
|
||||
// so that faces cannot be coincident
|
||||
shift += floatSMALL;
|
||||
const scalar shift2 = shift * 2.0;
|
||||
|
||||
switch (typeId)
|
||||
{
|
||||
case PDRobstacle::CYLINDER:
|
||||
{
|
||||
// Make a copy
|
||||
PDRobstacle obs(scanObs);
|
||||
|
||||
// Offset for the position of the group
|
||||
obs.pt += origin;
|
||||
|
||||
// Shift the end outwards so, if exactly on
|
||||
// cell boundary, now overlap cell.
|
||||
// So included in Aw.
|
||||
obs.pt -= point::uniform(shift);
|
||||
obs.len() += shift2;
|
||||
obs.dia() -= floatSMALL;
|
||||
|
||||
|
||||
// Trim against the mesh bounds
|
||||
// - ignore if it doesn't overlap
|
||||
const volumeType vt = obs.trim(meshBb);
|
||||
|
||||
switch (vt)
|
||||
{
|
||||
case volumeType::OUTSIDE:
|
||||
++nOutside;
|
||||
continue; // Can ignore the rest
|
||||
break;
|
||||
|
||||
case volumeType::MIXED:
|
||||
++nProtruding;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Later for position sorting
|
||||
switch (obs.orient)
|
||||
{
|
||||
case vector::X:
|
||||
obs.sortBias = obs.len();
|
||||
break;
|
||||
case vector::Y:
|
||||
obs.sortBias = 0.5*obs.dia();
|
||||
break;
|
||||
case vector::Z:
|
||||
obs.sortBias = 0.5*obs.dia();
|
||||
break;
|
||||
}
|
||||
|
||||
totVolume += obs.volume();
|
||||
cylinders.append(obs);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::DIAG_BEAM:
|
||||
{
|
||||
// Make a copy
|
||||
PDRobstacle obs(scanObs);
|
||||
|
||||
// Offset for the position of the group
|
||||
obs.pt += origin;
|
||||
|
||||
// Shift the end outwards so, if exactly on
|
||||
// cell boundary, now overlap cell.
|
||||
// So included in Aw.
|
||||
obs.pt -= point::uniform(shift);
|
||||
obs.len() += shift2;
|
||||
obs.wa += shift2;
|
||||
obs.wb += shift2;
|
||||
|
||||
totVolume += obs.volume();
|
||||
cylinders.append(obs);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PDRobstacle::CUBOID_1: // Cuboid "Type 1"
|
||||
case PDRobstacle::LOUVRE_BLOWOFF: // Louvred wall or blow-off panel
|
||||
case PDRobstacle::CUBOID: // Cuboid
|
||||
case PDRobstacle::WALL_BEAM: // Beam against wall (treated here as normal cuboid)
|
||||
case PDRobstacle::GRATING: // Grating
|
||||
case PDRobstacle::RECT_PATCH: // Inlet, outlet or ather b.c. (rectangular)
|
||||
{
|
||||
// Make a copy
|
||||
PDRobstacle obs(scanObs);
|
||||
|
||||
// Offset for the position of the group
|
||||
obs.pt += origin;
|
||||
|
||||
if (typeId == PDRobstacle::GRATING)
|
||||
{
|
||||
if (obs.slat_width <= 0)
|
||||
{
|
||||
obs.slat_width = pars.def_grating_slat_w;
|
||||
}
|
||||
}
|
||||
|
||||
// Shift the end outwards so, if exactly on
|
||||
// cell boundary, now overlap cell.
|
||||
// So included in Aw.
|
||||
obs.pt -= point::uniform(shift);
|
||||
obs.span += point::uniform(shift2);
|
||||
|
||||
|
||||
// Trim against the mesh bounds
|
||||
// - ignore if it doesn't overlap
|
||||
const volumeType vt = obs.trim(meshBb);
|
||||
|
||||
switch (vt)
|
||||
{
|
||||
case volumeType::OUTSIDE:
|
||||
++nOutside;
|
||||
continue; // Can ignore the rest
|
||||
break;
|
||||
|
||||
case volumeType::MIXED:
|
||||
++nProtruding;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
totVolume += obs.volume();
|
||||
|
||||
blocks.append(obs);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nOutside || nProtruding)
|
||||
{
|
||||
Info<< "Warning: " << nOutside << " obstacles outside domain, "
|
||||
<< nProtruding << " obstacles partly outside domain" << nl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #ifdef FULLDEBUG
|
||||
// Info<< blocks << nl << cylinders << nl;
|
||||
// #endif
|
||||
|
||||
return totVolume;
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::PDRobstacle::legacyReadFiles
|
||||
(
|
||||
const fileName& obsFileDir,
|
||||
const wordList& obsFileNames,
|
||||
const boundBox& meshBb,
|
||||
DynamicList<PDRobstacle>& blocks,
|
||||
DynamicList<PDRobstacle>& cylinders
|
||||
)
|
||||
{
|
||||
// Still just with legacy reading
|
||||
|
||||
// Count the obstacles and get the group locations
|
||||
Map<PDRlegacy::obstacleGrouping> groups;
|
||||
|
||||
const labelPair obsCounts =
|
||||
PDRlegacy::readObstacleFiles(obsFileDir, obsFileNames, groups);
|
||||
|
||||
const label nObstacle = obsCounts.first();
|
||||
const label nCylinder = obsCounts.second();
|
||||
|
||||
// Info<< "grouping: " << groups << endl;
|
||||
|
||||
if (!nObstacle && !nCylinder)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "No obstacles in domain" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
blocks.clear();
|
||||
blocks.reserve(4 * max(4, nObstacle));
|
||||
|
||||
cylinders.clear();
|
||||
cylinders.reserve(4 * max(4, nCylinder));
|
||||
|
||||
return PDRlegacy::readObstacleFiles
|
||||
(
|
||||
obsFileDir, obsFileNames, groups,
|
||||
meshBb,
|
||||
blocks,
|
||||
cylinders
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,512 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "PDRobstacleTypes.H"
|
||||
#include "PDRobstacleTypes.H"
|
||||
#include "Enum.H"
|
||||
#include "unitConversion.H"
|
||||
#include "addToMemberFunctionSelectionTable.H"
|
||||
|
||||
using namespace Foam::constant;
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#define addObstacleReader(obsType, obsName) \
|
||||
namespace Foam \
|
||||
{ \
|
||||
namespace PDRobstacles \
|
||||
{ \
|
||||
addNamedToMemberFunctionSelectionTable \
|
||||
( \
|
||||
PDRobstacle, \
|
||||
obsType, \
|
||||
read, \
|
||||
dictRead, \
|
||||
obsName \
|
||||
); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Read porosity, change to blockage. Clamp values [0-1] silently
|
||||
static const scalarMinMax limits01(scalarMinMax::zero_one());
|
||||
|
||||
// Volume porosity -> blockage
|
||||
inline scalar getPorosity(const dictionary& dict)
|
||||
{
|
||||
return 1 - limits01.clip(dict.getOrDefault<scalar>("porosity", 0));
|
||||
}
|
||||
|
||||
// Direction porosities -> blockage
|
||||
inline vector getPorosities(const dictionary& dict)
|
||||
{
|
||||
vector blockage(vector::one);
|
||||
|
||||
if (dict.readIfPresent("porosities", blockage))
|
||||
{
|
||||
for (scalar& val : blockage)
|
||||
{
|
||||
val = 1 - limits01.clip(val);
|
||||
}
|
||||
}
|
||||
|
||||
return blockage;
|
||||
}
|
||||
|
||||
|
||||
// Check for "porosity", or "porosities"
|
||||
// inline static bool hasPorosity(const dictionary& dict)
|
||||
// {
|
||||
// return dict.found("porosity") || dict.found("porosities");
|
||||
// }
|
||||
|
||||
|
||||
static const Foam::Enum<Foam::vector::components>
|
||||
vectorComponentsNames
|
||||
({
|
||||
{ vector::components::X, "x" },
|
||||
{ vector::components::Y, "y" },
|
||||
{ vector::components::Z, "z" },
|
||||
});
|
||||
|
||||
|
||||
enum inletDirnType
|
||||
{
|
||||
_X = -1, // -ve x
|
||||
_Y = -2, // -ve y
|
||||
_Z = -3, // -ve z
|
||||
X = 1, // +ve x
|
||||
Y = 2, // +ve y
|
||||
Z = 3, // +ve z
|
||||
};
|
||||
|
||||
static const Foam::Enum<inletDirnType>
|
||||
inletDirnNames
|
||||
({
|
||||
{ inletDirnType::_X, "-x" },
|
||||
{ inletDirnType::_Y, "-y" },
|
||||
{ inletDirnType::_Z, "-z" },
|
||||
{ inletDirnType::_X, "_x" },
|
||||
{ inletDirnType::_Y, "_y" },
|
||||
{ inletDirnType::_Z, "_z" },
|
||||
{ inletDirnType::X, "+x" },
|
||||
{ inletDirnType::Y, "+y" },
|
||||
{ inletDirnType::Z, "+z" },
|
||||
{ inletDirnType::X, "x" },
|
||||
{ inletDirnType::Y, "y" },
|
||||
{ inletDirnType::Z, "z" },
|
||||
});
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
addObstacleReader(cylinder, cyl);
|
||||
addObstacleReader(cylinder, cylinder);
|
||||
|
||||
void Foam::PDRobstacles::cylinder::read
|
||||
(
|
||||
PDRobstacle& obs,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
obs.PDRobstacle::readProperties(dict);
|
||||
obs.typeId = enumTypeId;
|
||||
|
||||
// Enforce complete blockage
|
||||
obs.xbkge = obs.ybkge = obs.zbkge = obs.vbkge = 1;
|
||||
// if (hasPorosity(dict)) ... warn?
|
||||
|
||||
|
||||
dict.readEntry("point", obs.pt);
|
||||
dict.readEntry("length", obs.len());
|
||||
dict.readEntry("diameter", obs.dia());
|
||||
|
||||
obs.orient = vectorComponentsNames.get("direction", dict);
|
||||
|
||||
// The sortBias for later position sorting
|
||||
switch (obs.orient)
|
||||
{
|
||||
case vector::X:
|
||||
obs.sortBias = obs.len();
|
||||
break;
|
||||
|
||||
default:
|
||||
obs.sortBias = 0.5*obs.dia();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
addObstacleReader(diagbeam, diag);
|
||||
addObstacleReader(diagbeam, diagbeam);
|
||||
|
||||
void Foam::PDRobstacles::diagbeam::read
|
||||
(
|
||||
PDRobstacle& obs,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
obs.PDRobstacle::readProperties(dict);
|
||||
obs.typeId = enumTypeId;
|
||||
|
||||
// Enforce complete blockage
|
||||
obs.xbkge = obs.ybkge = obs.zbkge = obs.vbkge = 1;
|
||||
// if (hasPorosity(dict)) ... warn?
|
||||
|
||||
|
||||
dict.readEntry("point", obs.pt);
|
||||
dict.readEntry("length", obs.len());
|
||||
obs.dia() = Zero;
|
||||
obs.theta() = Zero; // Fix later on
|
||||
|
||||
obs.orient = vectorComponentsNames.get("direction", dict);
|
||||
|
||||
// Angle (degrees) on input, limit to range [0, PI]
|
||||
scalar angle = dict.get<scalar>("angle");
|
||||
|
||||
while (angle > 180) angle -= 180;
|
||||
while (angle < 0) angle += 180;
|
||||
|
||||
labelPair dims;
|
||||
dict.readEntry("width", dims);
|
||||
|
||||
// Swap axes when theta > PI/2
|
||||
// For 89-90 degrees it becomes -ve, which is picked up in following section
|
||||
if (angle > 89)
|
||||
{
|
||||
angle -= 90;
|
||||
dims.flip(); // Swap dimensions
|
||||
}
|
||||
|
||||
obs.theta() = degToRad(angle);
|
||||
|
||||
obs.wa = dims.first();
|
||||
obs.wb = dims.second();
|
||||
|
||||
const scalar ctheta = cos(obs.theta());
|
||||
const scalar stheta = sin(obs.theta());
|
||||
|
||||
// The sortBias for later position sorting
|
||||
switch (obs.orient)
|
||||
{
|
||||
case vector::X:
|
||||
obs.sortBias = obs.len();
|
||||
break;
|
||||
|
||||
case vector::Y:
|
||||
obs.sortBias = 0.5*(obs.wa * stheta + obs.wb * ctheta);
|
||||
break;
|
||||
|
||||
case vector::Z:
|
||||
obs.sortBias = 0.5*(obs.wa * ctheta + obs.wb * stheta);
|
||||
break;
|
||||
}
|
||||
|
||||
// If very nearly aligned with axis, turn it into normal block,
|
||||
// to avoid 1/tan(theta) blowing up
|
||||
if (angle < 1)
|
||||
{
|
||||
Info<< "... changed diag-beam to box" << nl;
|
||||
|
||||
switch (obs.orient)
|
||||
{
|
||||
case vector::X:
|
||||
obs.span = vector(obs.len(), obs.wa, obs.wb);
|
||||
break;
|
||||
|
||||
case vector::Y:
|
||||
obs.span = vector(obs.wb, obs.len(), obs.wa);
|
||||
break;
|
||||
|
||||
case vector::Z:
|
||||
obs.span = vector(obs.wa, obs.wb, obs.len());
|
||||
break;
|
||||
}
|
||||
|
||||
// The pt was end centre (cylinder), now lower corner
|
||||
vector adjustPt = -0.5*obs.span;
|
||||
adjustPt[obs.orient] = 0;
|
||||
|
||||
obs.pt -= adjustPt;
|
||||
|
||||
obs.typeId = PDRobstacles::cuboid::enumTypeId;
|
||||
obs.sortBias = 0;
|
||||
obs.xbkge = obs.ybkge = obs.zbkge = obs.vbkge = 1.0;
|
||||
obs.blowoff_type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
addObstacleReader(cuboid, box);
|
||||
|
||||
void Foam::PDRobstacles::cuboid::read
|
||||
(
|
||||
PDRobstacle& obs,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
obs.PDRobstacle::readProperties(dict);
|
||||
obs.typeId = enumTypeId;
|
||||
|
||||
// Default - full blockage
|
||||
obs.xbkge = obs.ybkge = obs.zbkge = obs.vbkge = 1;
|
||||
|
||||
|
||||
dict.readEntry("point", obs.pt);
|
||||
dict.readEntry("size", obs.span);
|
||||
|
||||
// Optional
|
||||
obs.vbkge = getPorosity(dict);
|
||||
|
||||
// Optional
|
||||
const vector blockages = getPorosities(dict);
|
||||
obs.xbkge = blockages.x();
|
||||
obs.ybkge = blockages.y();
|
||||
obs.zbkge = blockages.z();
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
addObstacleReader(wallbeam, wallbeam);
|
||||
|
||||
void Foam::PDRobstacles::wallbeam::read
|
||||
(
|
||||
PDRobstacle& obs,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
PDRobstacles::cuboid::read(obs, dict);
|
||||
obs.typeId = enumTypeId;
|
||||
|
||||
// Enforce complete blockage
|
||||
obs.xbkge = obs.ybkge = obs.zbkge = obs.vbkge = 1;
|
||||
|
||||
// if (hasPorosity(dict)) ... warn?
|
||||
|
||||
// Additional adjustment for drag etc.
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
addObstacleReader(grating, grating);
|
||||
addObstacleReader(grating, grate);
|
||||
|
||||
void Foam::PDRobstacles::grating::read
|
||||
(
|
||||
PDRobstacle& obs,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
obs.PDRobstacle::readProperties(dict);
|
||||
obs.typeId = enumTypeId;
|
||||
|
||||
// Initialize to full blockage
|
||||
obs.xbkge = obs.ybkge = obs.zbkge = obs.vbkge = 1;
|
||||
|
||||
dict.readEntry("point", obs.pt);
|
||||
dict.readEntry("size", obs.span);
|
||||
|
||||
// TODO: better error handling
|
||||
// if (!equal(cmptProduct(obs.span), 0))
|
||||
// {
|
||||
// Info<< "Type " << typeId << " has non-zero thickness.";
|
||||
// ReportLineInfo(lineNo, inputFile);
|
||||
// }
|
||||
|
||||
obs.vbkge = getPorosity(dict);
|
||||
|
||||
const vector blockages = getPorosities(dict);
|
||||
obs.xbkge = blockages.x();
|
||||
obs.ybkge = blockages.y();
|
||||
obs.zbkge = blockages.z();
|
||||
|
||||
// TODO: Warning if porosity was not specified?
|
||||
|
||||
|
||||
// TBD: Default slat width from PDR.params
|
||||
obs.slat_width = dict.getOrDefault<scalar>("slats", Zero);
|
||||
|
||||
// Determine the orientation
|
||||
if (equal(obs.span.x(), 0))
|
||||
{
|
||||
obs.orient = vector::X;
|
||||
}
|
||||
else if (equal(obs.span.y(), 0))
|
||||
{
|
||||
obs.orient = vector::Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
obs.orient = vector::Z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
addObstacleReader(louver, louver);
|
||||
addObstacleReader(louver, louvre);
|
||||
|
||||
void Foam::PDRobstacles::louver::read
|
||||
(
|
||||
PDRobstacle& obs,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
obs.PDRobstacle::readProperties(dict);
|
||||
obs.typeId = enumTypeId;
|
||||
|
||||
// Initialize to full blockage
|
||||
obs.xbkge = obs.ybkge = obs.zbkge = obs.vbkge = 1;
|
||||
|
||||
dict.readEntry("point", obs.pt);
|
||||
dict.readEntry("size", obs.span);
|
||||
|
||||
// TODO: better error handling
|
||||
// if (!equal(cmptProduct(obs.span), 0))
|
||||
// {
|
||||
// Info<< "Type " << typeId << " has non-zero thickness.";
|
||||
// ReportLineInfo(lineNo, inputFile);
|
||||
// }
|
||||
|
||||
obs.vbkge = getPorosity(dict);
|
||||
|
||||
const vector blockages = getPorosities(dict);
|
||||
obs.xbkge = blockages.x();
|
||||
obs.ybkge = blockages.y();
|
||||
obs.zbkge = blockages.z();
|
||||
|
||||
// TODO: Warning if porosity was not specified?
|
||||
|
||||
|
||||
// Blowoff pressure [bar]
|
||||
const scalar blowoffPress = dict.get<scalar>("pressure");
|
||||
|
||||
obs.blowoff_press = barToPa(blowoffPress);
|
||||
obs.blowoff_time = dict.getOrDefault<scalar>("time", 0);
|
||||
obs.blowoff_type = dict.getOrDefault<label>("type", 2);
|
||||
|
||||
if (obs.blowoff_type == 1)
|
||||
{
|
||||
Info<< "Louver : blowoff-type 1 not yet implemented." << nl;
|
||||
// ReportLineInfo(lineNo, inputFile);
|
||||
|
||||
if (obs.blowoff_time != 0)
|
||||
{
|
||||
Info<< "Louver : has blowoff time set,"
|
||||
<< " not set to blow off cell-by-cell" << nl;
|
||||
// ReportLineInfo(lineNo, inputFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if
|
||||
(
|
||||
(obs.blowoff_type == 1 || obs.blowoff_type == 2)
|
||||
&& (blowoffPress > 0)
|
||||
)
|
||||
{
|
||||
if (blowoffPress > maxBlowoffPressure)
|
||||
{
|
||||
Info<< "Blowoff pressure (" << blowoffPress
|
||||
<< ") too high for blowoff type "
|
||||
<< obs.blowoff_type << nl;
|
||||
// ReportLineInfo(lineNo, inputFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< "Problem with blowoff parameters" << nl;
|
||||
// ReportLineInfo(lineNo, inputFile);
|
||||
Info<< "Pressure[bar] " << blowoffPress
|
||||
<< " Blowoff type " << obs.blowoff_type
|
||||
<< ", blowoff pressure " << obs.blowoff_press << nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
addObstacleReader(patch, patch);
|
||||
|
||||
void Foam::PDRobstacles::patch::read
|
||||
(
|
||||
PDRobstacle& obs,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
obs.PDRobstacle::readProperties(dict);
|
||||
obs.typeId = enumTypeId;
|
||||
|
||||
const auto nameLen = obs.identifier.length();
|
||||
|
||||
word patchName = word::validate(obs.identifier);
|
||||
|
||||
if (patchName.empty())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "RECT_PATCH without a patch name"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
else if (patchName.length() != nameLen)
|
||||
{
|
||||
WarningInFunction
|
||||
<< "RECT_PATCH stripped invalid characters from patch name: "
|
||||
<< obs.identifier
|
||||
<< exit(FatalError);
|
||||
|
||||
obs.identifier = std::move(patchName);
|
||||
}
|
||||
|
||||
// Enforce complete blockage
|
||||
obs.xbkge = obs.ybkge = obs.zbkge = obs.vbkge = 1;
|
||||
|
||||
dict.readEntry("point", obs.pt);
|
||||
dict.readEntry("size", obs.span);
|
||||
obs.inlet_dirn = inletDirnNames.get("direction", dict);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#undef addObstacleReader
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,300 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM 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.
|
||||
|
||||
OpenFOAM 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
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Namespace
|
||||
Foam::PDRobstacles
|
||||
|
||||
Description
|
||||
Reader classes for concrete PDRsetFields obstacle types.
|
||||
|
||||
SourceFiles
|
||||
PDRobstacleTypes.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef PDRobstacleTypes_H
|
||||
#define PDRobstacleTypes_H
|
||||
|
||||
#include "PDRobstacle.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace PDRobstacles
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class cylinder Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*! \brief A cylinder, selectable as \c cyl or \c cylinder
|
||||
|
||||
Dictionary controls
|
||||
\table
|
||||
Property | Description | Required | Default
|
||||
point | The end centre-point | yes |
|
||||
length | The cylinder length | yes |
|
||||
diameter | The cylinder diameter | yes |
|
||||
direction | The x/y/z direction | yes |
|
||||
\endtable
|
||||
|
||||
Example,
|
||||
\verbatim
|
||||
cyl
|
||||
{
|
||||
point (0 0 0);
|
||||
length 0.95;
|
||||
diameter 0.05;
|
||||
direction x;
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
\note Does not have porosity.
|
||||
*/
|
||||
struct cylinder : public PDRobstacle
|
||||
{
|
||||
static constexpr int enumTypeId = legacyTypes::CYLINDER;
|
||||
static void read(PDRobstacle& obs, const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class diagbeam Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*! \brief A diagonal beam, which is cylinder-like,
|
||||
selectable as \c diag or \c diagbeam
|
||||
|
||||
If the beam angle is close to zero, it will be changed to a box
|
||||
(PDRobstacles::cuboid)
|
||||
|
||||
Dictionary controls
|
||||
\table
|
||||
Property | Description | Required | Default
|
||||
point | The end centre-point | yes |
|
||||
length | The beam length | yes |
|
||||
width | The beam cross-dimensions | yes |
|
||||
angle | The beam angle (degrees) | yes |
|
||||
direction | The x/y/z direction | yes |
|
||||
\endtable
|
||||
|
||||
Example,
|
||||
\verbatim
|
||||
diag
|
||||
{
|
||||
point (0 0 0);
|
||||
length 0.95;
|
||||
width (0.05 0.01);
|
||||
angle 25;
|
||||
direction x;
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
\note Does not have porosity.
|
||||
*/
|
||||
struct diagbeam : public PDRobstacle
|
||||
{
|
||||
static constexpr int enumTypeId = legacyTypes::DIAG_BEAM;
|
||||
static void read(PDRobstacle& obs, const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class cuboid Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*! \brief A cuboid, selectable as \c box
|
||||
|
||||
Dictionary controls
|
||||
\table
|
||||
Property | Description | Required | Default
|
||||
point | The lower left corner | yes |
|
||||
size | The (x y z) dimensions | yes |
|
||||
porosity | The volumetric porosity | no | 0
|
||||
porosities | The directional porosities | no | (0 0 0)
|
||||
\endtable
|
||||
|
||||
Example,
|
||||
\verbatim
|
||||
box
|
||||
{
|
||||
point (0 0 0);
|
||||
size (0.05 0.05 2);
|
||||
}
|
||||
\endverbatim
|
||||
*/
|
||||
struct cuboid : public PDRobstacle
|
||||
{
|
||||
static constexpr int enumTypeId = legacyTypes::CUBOID;
|
||||
static void read(PDRobstacle& obs, const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class wallbeam Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*! \brief A wallbeam, selectable as \c wallbeam which is currently identical
|
||||
to a box (PDRobstacles::cuboid)
|
||||
|
||||
Dictionary controls
|
||||
\table
|
||||
Property | Description | Required | Default
|
||||
point | The lower left corner | yes |
|
||||
size | The (x y z) dimensions | yes |
|
||||
\endtable
|
||||
|
||||
Example,
|
||||
\verbatim
|
||||
wallbeam
|
||||
{
|
||||
point (0 0 0);
|
||||
size (0.05 0.05 2);
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
\note Does not have porosity.
|
||||
*/
|
||||
struct wallbeam : public PDRobstacle
|
||||
{
|
||||
static constexpr int enumTypeId = legacyTypes::WALL_BEAM;
|
||||
static void read(PDRobstacle& obs, const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class grating Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*! \brief A grating, selectable as \c grate or \c grating
|
||||
|
||||
The dimensions must include one component that is zero,
|
||||
which establishes the orientation.
|
||||
|
||||
Dictionary controls
|
||||
\table
|
||||
Property | Description | Required | Default
|
||||
point | The lower left corner | yes |
|
||||
size | The (x y z) dimensions | yes |
|
||||
slats | The slat width | no | 0
|
||||
\endtable
|
||||
|
||||
Example,
|
||||
\verbatim
|
||||
grating
|
||||
{
|
||||
point (0 0 0);
|
||||
size (0.1 0.1 0);
|
||||
slats 0.005;
|
||||
}
|
||||
\endverbatim
|
||||
*/
|
||||
struct grating : public PDRobstacle
|
||||
{
|
||||
static constexpr int enumTypeId = legacyTypes::GRATING;
|
||||
static void read(PDRobstacle& obs, const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class louver Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*! \brief Louver blowoff, selectable as \c louver or \c louvre
|
||||
|
||||
Dictionary controls
|
||||
\table
|
||||
Property | Description | Required | Default
|
||||
point | The lower left corner | yes |
|
||||
size | The (x y z) dimensions | yes |
|
||||
pressure | The blowoff pressure (bar) | yes |
|
||||
time | The blowoff time | no | 0
|
||||
type | The blowoff type (1, 2) | no | 1
|
||||
\endtable
|
||||
|
||||
Example,
|
||||
\verbatim
|
||||
louver
|
||||
{
|
||||
point (0 0 0);
|
||||
size (0.1 0.1);
|
||||
pressure 3;
|
||||
type 1;
|
||||
}
|
||||
\endverbatim
|
||||
*/
|
||||
struct louver : public PDRobstacle
|
||||
{
|
||||
static constexpr int enumTypeId = legacyTypes::LOUVER_BLOWOFF;
|
||||
static void read(PDRobstacle& obs, const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class patch Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
/*! \brief Rectangular patch, selectable as \c patch
|
||||
|
||||
Dictionary controls
|
||||
\table
|
||||
Property | Description | Required | Default
|
||||
name | The patch name corner | yes |
|
||||
point | The lower left corner | yes |
|
||||
size | The (x y z) dimensions | yes |
|
||||
direction | The direction (x/y/z, _x/_y/_z or "-x"/"-y"/"-z" | yes |
|
||||
\endtable
|
||||
|
||||
Example,
|
||||
\verbatim
|
||||
patch
|
||||
{
|
||||
name inlet;
|
||||
direction _x;
|
||||
point (0 0 0);
|
||||
size (0 0.05 1.0);
|
||||
}
|
||||
\endverbatim
|
||||
*/
|
||||
struct patch : public PDRobstacle
|
||||
{
|
||||
static constexpr int enumTypeId = legacyTypes::RECT_PATCH;
|
||||
static void read(PDRobstacle& obs, const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace PDRobstacles
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
Reference in New Issue
Block a user