coupledPolyPatch.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2020 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "coupledPolyPatch.H"
30 #include "ListOps.H"
31 #include "transform.H"
32 #include "OFstream.H"
33 
34 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38  defineTypeNameAndDebug(coupledPolyPatch, 0);
39 
40  const scalar coupledPolyPatch::defaultMatchTol_ = 1e-4;
41 }
42 
43 
44 const Foam::Enum
45 <
47 >
49 ({
50  { transformType::UNKNOWN, "unknown" },
51  { transformType::ROTATIONAL, "rotational" },
52  { transformType::TRANSLATIONAL, "translational" },
53  { transformType::COINCIDENTFULLMATCH, "coincidentFullMatch" },
54  { transformType::NOORDERING, "noOrdering" },
55 });
56 
57 
58 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
59 
61 {
62  os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
63 }
64 
65 
67 (
68  Ostream& os,
69  const pointField& points,
70  const labelList& pointLabels
71 )
72 {
74  {
75  writeOBJ(os, points[pointLabels[i]]);
76  }
77 }
78 
79 
81 (
82  Ostream& os,
83  const point& p0,
84  const point& p1,
85  label& vertI
86 )
87 {
88  writeOBJ(os, p0);
89  vertI++;
90 
91  writeOBJ(os, p1);
92  vertI++;
93 
94  os << "l " << vertI-1 << ' ' << vertI << nl;
95 }
96 
97 
99 (
100  const fileName& fName,
101  const UList<face>& faces,
102  const pointField& points
103 )
104 {
105  OFstream os(fName);
106 
107  Map<label> foamToObj(4*faces.size());
108 
109  label vertI = 0;
110 
111  forAll(faces, i)
112  {
113  const face& f = faces[i];
114 
115  forAll(f, fp)
116  {
117  if (foamToObj.insert(f[fp], vertI))
118  {
119  writeOBJ(os, points[f[fp]]);
120  vertI++;
121  }
122  }
123 
124  os << 'l';
125  forAll(f, fp)
126  {
127  os << ' ' << foamToObj[f[fp]]+1;
128  }
129  os << ' ' << foamToObj[f[0]]+1 << nl;
130  }
131 }
132 
133 
135 (
136  const UList<face>& faces,
137  const pointField& points,
139 )
140 {
141  pointField anchors(faces.size());
142 
143  if (transform != COINCIDENTFULLMATCH)
144  {
145  // Return the first point
146  forAll(faces, facei)
147  {
148  anchors[facei] = points[faces[facei][0]];
149  }
150  }
151  else
152  {
153  // Make anchor point unique
154  forAll(faces, facei)
155  {
156  const face& f = faces[facei];
157 
158  bool unique = true;
159 
160  forAll(f, fp1)
161  {
162  const point& p1 = points[f[fp1]];
163 
164  unique = true;
165 
166  for (label fp2 = 0; fp2 < f.size(); ++fp2)
167  {
168  if (f[fp1] == f[fp2])
169  {
170  continue;
171  }
172 
173  const point& p2 = points[f[fp2]];
174 
175  // TODO: Change to a tolerance and possibly select closest
176  // point to the origin
177  if (p1 == p2)
178  {
179  unique = false;
180  break;
181  }
182  }
183 
184  if (unique)
185  {
186  anchors[facei] = p1;
187  break;
188  }
189  }
190 
191  if (!unique)
192  {
193  anchors[facei] = points[faces[facei][0]];
194  }
195  }
196  }
197 
198  return anchors;
199 }
200 
201 
203 (
204  const UList<face>& faces,
205  const pointField& points,
206  const pointField& faceCentres
207 )
208 {
209  // Calculate typical distance per face
210  scalarField tols(faces.size());
211 
212  forAll(faces, facei)
213  {
214  const point& cc = faceCentres[facei];
215 
216  const face& f = faces[facei];
217 
218  // 1. calculate a typical size of the face. Use maximum distance
219  // to face centre
220  scalar maxLenSqr = -GREAT;
221  // 2. as measure of truncation error when comparing two coordinates
222  // use SMALL * maximum component
223  scalar maxCmpt = -GREAT;
224 
225  forAll(f, fp)
226  {
227  const point& pt = points[f[fp]];
228  maxLenSqr = max(maxLenSqr, magSqr(pt - cc));
229  maxCmpt = max(maxCmpt, cmptMax(cmptMag(pt)));
230  }
231 
232  tols[facei] = max
233  (
234  SMALL,
235  max(SMALL*maxCmpt, Foam::sqrt(maxLenSqr))
236  );
237  }
238  return tols;
239 }
240 
241 
243 (
244  const pointField& points,
245  const face& f,
246  const point& anchor,
247  const scalar tol
248 )
249 {
250  label anchorFp = -1;
251  scalar minDistSqr = GREAT;
252 
253  forAll(f, fp)
254  {
255  scalar distSqr = magSqr(anchor - points[f[fp]]);
256 
257  if (distSqr < minDistSqr)
258  {
259  minDistSqr = distSqr;
260  anchorFp = fp;
261  }
262  }
263 
264  if (anchorFp == -1 || Foam::sqrt(minDistSqr) > tol)
265  {
266  return -1;
267  }
268  else
269  {
270  // Check that anchor is unique.
271  forAll(f, fp)
272  {
273  scalar distSqr = magSqr(anchor - points[f[fp]]);
274 
275  if (distSqr == minDistSqr && fp != anchorFp)
276  {
278  << "Cannot determine unique anchor point on face "
280  << endl
281  << "Both at index " << anchorFp << " and " << fp
282  << " the vertices have the same distance "
283  << Foam::sqrt(minDistSqr)
284  << " to the anchor " << anchor
285  << ". Continuing but results might be wrong."
286  << nl << endl;
287  }
288  }
289 
290  // Positive rotation
291  return (f.size() - anchorFp) % f.size();
292  }
293 }
294 
295 
297 (
298  const vectorField& Cf,
299  const vectorField& Cr,
300  const vectorField& nf,
301  const vectorField& nr,
302  const scalarField& smallDist,
303  const scalar absTol,
305 ) const
306 {
307  if (debug)
308  {
309  Pout<< "coupledPolyPatch::calcTransformTensors : " << name() << endl
310  << " transform:" << transformTypeNames[transform] << nl
311  << " (half)size:" << Cf.size() << nl
312  << " absTol:" << absTol << nl
313  << " smallDist min:" << min(smallDist) << nl
314  << " smallDist max:" << max(smallDist) << nl
315  << " sum(mag(nf & nr)):" << sum(mag(nf & nr)) << endl;
316  }
317 
318  // Tolerance calculation.
319  // - normal calculation: assume absTol is the absolute error in a
320  // single normal/transformation calculation. Consists both of numerical
321  // precision (on the order of SMALL and of writing precision
322  // (from e.g. decomposition)
323  // Then the overall error of summing the normals is sqrt(size())*absTol
324  // - separation calculation: pass in from the outside an allowable error.
325 
326  if (Cf.size() == 0)
327  {
328  // Dummy geometry. Assume non-separated, parallel.
329  separation_.setSize(0);
330  forwardT_.clear();
331  reverseT_.clear();
332  collocated_.setSize(0);
333  }
334  else
335  {
336  scalar error = absTol*Foam::sqrt(1.0*Cf.size());
337 
338  if (debug)
339  {
340  Pout<< " error:" << error << endl;
341  }
342 
343  if
344  (
345  transform == ROTATIONAL
346  || (
347  transform != TRANSLATIONAL
348  && transform != COINCIDENTFULLMATCH
349  && (sum(mag(nf & nr)) < Cf.size() - error)
350  )
351  )
352  {
353  // Type is rotation or unknown and normals not aligned
354 
355  // Assume per-face differing transformation, correct later
356 
357  separation_.setSize(0);
358 
359  forwardT_.setSize(Cf.size());
360  reverseT_.setSize(Cf.size());
361  collocated_.setSize(Cf.size());
362  collocated_ = false;
363 
364  forAll(forwardT_, facei)
365  {
366  forwardT_[facei] = rotationTensor(-nr[facei], nf[facei]);
367  reverseT_[facei] = rotationTensor(nf[facei], -nr[facei]);
368  }
369 
370  if (debug)
371  {
372  Pout<< " sum(mag(forwardT_ - forwardT_[0])):"
373  << sum(mag(forwardT_ - forwardT_[0]))
374  << endl;
375  }
376 
377  if (sum(mag(forwardT_ - forwardT_[0])) < error)
378  {
379  forwardT_.setSize(1);
380  reverseT_.setSize(1);
381  collocated_.setSize(1);
382 
383  if (debug)
384  {
385  Pout<< " difference in rotation less than"
386  << " local tolerance "
387  << error << ". Assuming uniform rotation." << endl;
388  }
389  }
390  }
391  else
392  {
393  // Translational or (unknown and normals aligned)
394 
395  forwardT_.setSize(0);
396  reverseT_.setSize(0);
397 
398  separation_ = Cr - Cf;
399 
400  collocated_.setSize(separation_.size());
401 
402  // Three situations:
403  // - separation is zero. No separation.
404  // - separation is same. Single separation vector.
405  // - separation differs per face. Separation vectorField.
406 
407  // Check for different separation per face
408  bool sameSeparation = true;
409  bool doneWarning = false;
410 
411  forAll(separation_, facei)
412  {
413  scalar smallSqr = sqr(smallDist[facei]);
414 
415  collocated_[facei] = (magSqr(separation_[facei]) < smallSqr);
416 
417  // Check if separation differing w.r.t. face 0.
418  if (magSqr(separation_[facei] - separation_[0]) > smallSqr)
419  {
420  sameSeparation = false;
421 
422  if (!doneWarning && debug)
423  {
424  doneWarning = true;
425 
426  Pout<< " separation " << separation_[facei]
427  << " at " << facei
428  << " differs from separation[0] " << separation_[0]
429  << " by more than local tolerance "
430  << smallDist[facei]
431  << ". Assuming non-uniform separation." << endl;
432  }
433  }
434  }
435 
436  if (sameSeparation)
437  {
438  // Check for zero separation (at 0 so everywhere)
439  if (collocated_[0])
440  {
441  if (debug)
442  {
443  Pout<< " separation " << mag(separation_[0])
444  << " less than local tolerance " << smallDist[0]
445  << ". Assuming zero separation." << endl;
446  }
447 
448  separation_.setSize(0);
449  collocated_ = boolList(1, true);
450  }
451  else
452  {
453  if (debug)
454  {
455  Pout<< " separation " << mag(separation_[0])
456  << " more than local tolerance " << smallDist[0]
457  << ". Assuming uniform separation." << endl;
458  }
459 
460  separation_.setSize(1);
461  collocated_ = boolList(1, false);
462  }
463  }
464  }
465  }
466 
467  if (debug)
468  {
469  Pout<< " separation_:" << separation_.size() << nl
470  << " forwardT size:" << forwardT_.size() << endl;
471  }
472 }
473 
474 
475 // * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * //
476 
478 (
479  const word& name,
480  const label size,
481  const label start,
482  const label index,
483  const polyBoundaryMesh& bm,
484  const word& patchType,
486 )
487 :
488  polyPatch(name, size, start, index, bm, patchType),
489  matchTolerance_(defaultMatchTol_),
490  transform_(transform)
491 {}
492 
493 
495 (
496  const word& name,
497  const dictionary& dict,
498  const label index,
499  const polyBoundaryMesh& bm,
500  const word& patchType
501 )
502 :
503  polyPatch(name, dict, index, bm, patchType),
504  matchTolerance_(dict.getOrDefault("matchTolerance", defaultMatchTol_)),
505  transform_
506  (
507  transformTypeNames.getOrDefault
508  (
509  "transform",
510  dict,
511  transformType::UNKNOWN
512  )
513  )
514 {}
515 
516 
518 (
519  const coupledPolyPatch& pp,
520  const polyBoundaryMesh& bm
521 )
522 :
523  polyPatch(pp, bm),
524  matchTolerance_(pp.matchTolerance_),
525  transform_(pp.transform_)
526 {}
527 
528 
530 (
531  const coupledPolyPatch& pp,
532  const polyBoundaryMesh& bm,
533  const label index,
534  const label newSize,
535  const label newStart
536 )
537 :
538  polyPatch(pp, bm, index, newSize, newStart),
539  matchTolerance_(pp.matchTolerance_),
540  transform_(pp.transform_)
541 {}
542 
543 
545 (
546  const coupledPolyPatch& pp,
547  const polyBoundaryMesh& bm,
548  const label index,
549  const labelUList& mapAddressing,
550  const label newStart
551 )
552 :
553  polyPatch(pp, bm, index, mapAddressing, newStart),
554  matchTolerance_(pp.matchTolerance_),
555  transform_(pp.transform_)
556 {}
557 
558 
559 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
560 
562 {}
563 
564 
565 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
566 
568 {
569  polyPatch::write(os);
570  //if (matchTolerance_ != defaultMatchTol_)
571  {
572  os.writeEntry("matchTolerance", matchTolerance_);
573  os.writeEntry("transform", transformTypeNames[transform_]);
574  }
575 }
576 
577 
578 // ************************************************************************* //
Foam::coupledPolyPatch::getAnchorPoints
static pointField getAnchorPoints(const UList< face > &, const pointField &, const transformType)
Get a unique anchor point for all faces.
Definition: coupledPolyPatch.C:135
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::Vector::x
const Cmpt & x() const
Access to the vector x component.
Definition: VectorI.H:73
Foam::Enum
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: IOstreamOption.H:57
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:62
Foam::fileName
A class for handling file names.
Definition: fileName.H:69
Foam::polyBoundaryMesh
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO.
Definition: polyBoundaryMesh.H:62
Foam::meshTools::writeOBJ
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition: meshTools.C:203
Foam::polyPatch::write
virtual void write(Ostream &) const
Write the polyPatch data as a dictionary.
Definition: polyPatch.C:405
Foam::coupledPolyPatch::transformTypeNames
static const Enum< transformType > transformTypeNames
Definition: coupledPolyPatch.H:69
Foam::Map< label >
Foam::boolList
List< bool > boolList
A List of bools.
Definition: List.H:69
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
Foam::coupledPolyPatch
The coupledPolyPatch is an abstract base class for patches that couple regions of the computational d...
Definition: coupledPolyPatch.H:53
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Foam::transform
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:519
Foam::Vector::z
const Cmpt & z() const
Access to the vector z component.
Definition: VectorI.H:85
Foam::min
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
OFstream.H
Foam::magSqr
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
Foam::cmptMag
void cmptMag(FieldField< Field, Type > &cf, const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:400
Foam::cmptMax
void cmptMax(FieldField< Field, typename FieldField< Field, Type >::cmptType > &cf, const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:253
coupledPolyPatch.H
Foam::Field< vector >
Foam::polyPatch
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:68
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
Foam::coupledPolyPatch::getRotation
static label getRotation(const pointField &points, const face &f, const point &anchor, const scalar tol)
Get the number of vertices face f needs to be rotated such that.
Definition: coupledPolyPatch.C:243
Foam::coupledPolyPatch::write
virtual void write(Ostream &) const
Write the polyPatch data as a dictionary.
Definition: coupledPolyPatch.C:567
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:121
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::coupledPolyPatch::calcFaceTol
static scalarField calcFaceTol(const UList< face > &faces, const pointField &points, const pointField &faceCentres)
Calculate typical tolerance per face. Is currently max distance.
Definition: coupledPolyPatch.C:203
Foam::OFstream
Output to file stream, using an OSstream.
Definition: OFstream.H:53
Foam::Vector::y
const Cmpt & y() const
Access to the vector y component.
Definition: VectorI.H:79
Foam::sqr
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Definition: dimensionedSymmTensor.C:51
Foam::nl
constexpr char nl
Definition: Ostream.H:385
f
labelList f(nPoints)
Foam::Vector< scalar >
Foam::coupledPolyPatch::~coupledPolyPatch
virtual ~coupledPolyPatch()
Destructor.
Definition: coupledPolyPatch.C:561
Foam::List< label >
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:144
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::UList< face >
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::sum
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
Definition: DimensionedFieldFunctions.C:327
Foam::coupledPolyPatch::calcTransformTensors
void calcTransformTensors(const vectorField &Cf, const vectorField &Cr, const vectorField &nf, const vectorField &nr, const scalarField &smallDist, const scalar absTol, const transformType=UNKNOWN) const
Calculate the transformation tensors.
Definition: coupledPolyPatch.C:297
Foam::Ostream::writeEntry
Ostream & writeEntry(const keyType &key, const T &value)
Write a keyword/value entry.
Definition: Ostream.H:232
Foam::UList::size
void size(const label n) noexcept
Override size to be inconsistent with allocated storage.
Definition: UListI.H:360
Foam::UIndirectList
A List with indirect addressing.
Definition: faMatrix.H:60
Foam::face
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:72
ListOps.H
Various functions to operate on Lists.
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
p0
const volScalarField & p0
Definition: EEqn.H:36
transform.H
3D tensor transformation operations.
Foam::rotationTensor
tensor rotationTensor(const vector &n1, const vector &n2)
Rotational transformation tensor from vector n1 to n2.
Definition: transform.H:51
Foam::coupledPolyPatch::writeOBJ
static void writeOBJ(Ostream &os, const point &pt)
Write point in OBJ format.
Definition: coupledPolyPatch.C:60
Foam::coupledPolyPatch::transformType
transformType
Definition: coupledPolyPatch.H:59
Foam::dictionary::getOrDefault
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Definition: dictionaryTemplates.C:122
Foam::coupledPolyPatch::coupledPolyPatch
coupledPolyPatch(const word &name, const label size, const label start, const label index, const polyBoundaryMesh &bm, const word &patchType, const transformType transform)
Construct from components.
Definition: coupledPolyPatch.C:478
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:303
Foam::error
Class to handle errors and exceptions in a simple, consistent stream-based manner.
Definition: error.H:70
pointLabels
labelList pointLabels(nPoints, -1)