LagrangianDistribution.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2025 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "LagrangianDistribution.H"
27 #include "LagrangianFields.H"
28 #include "OSspecific.H"
29 #include "writeFile.H"
30 #include "unintegrable.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
37 namespace functionObjects
38 {
41  (
45  );
46 }
47 }
48 
49 
50 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
51 
52 void Foam::functionObjects::LagrangianDistribution::readCoeffs
53 (
54  const dictionary& dict
55 )
56 {
57  // Read the fields
58  const bool haveFields = dict.found("fields");
59  const bool haveField = dict.found("field");
60  if (haveFields == haveField)
61  {
63  << "keywords fields and field both "
64  << (haveFields ? "" : "un") << "defined in "
65  << "dictionary " << dict.name()
66  << exit(FatalIOError);
67  }
68  else if (haveFields)
69  {
70  dict.lookup("fields") >> fields_;
71  }
72  else if (haveField)
73  {
74  fields_.resize(1);
75  dict.lookup("field") >> fields_.first();
76  }
77 
78  // Read the weight fields
79  const bool haveWeightFields = dict.found("weightFields");
80  const bool haveWeightField = dict.found("weightField");
81  if (haveWeightFields && haveWeightField)
82  {
84  << "keywords weightFields and weightField both "
85  << "defined in dictionary " << dict.name()
86  << exit(FatalIOError);
87  }
88  else if (haveWeightFields)
89  {
90  dict.lookup("weightFields") >> weightFields_;
91  }
92  else if (haveWeightField)
93  {
94  weightFields_.resize(1);
95  dict.lookup("weightField") >> weightFields_.first();
96  }
97  else
98  {
99  // No weights
100  weightFields_.clear();
101  }
102 
103  // The number of bins in the plot
104  dict.lookup("nBins") >> nBins_;
105 
106  // Construct the formatter
107  formatter_ = setWriter::New(dict.lookup("setFormat"), dict);
108 }
109 
110 
111 template<template<class> class GeoField>
112 bool Foam::functionObjects::LagrangianDistribution::multiplyWeight
113 (
114  const word& weightFieldName,
115  scalarField& weight
116 ) const
117 {
118  if (!mesh().foundObject<GeoField<scalar>>(weightFieldName)) return false;
119 
120  const GeoField<scalar>& w =
121  mesh().lookupObject<GeoField<scalar>>(weightFieldName);
122 
123  weight *= w;
124 
125  return true;
126 }
127 
128 
129 void Foam::functionObjects::LagrangianDistribution::writeDistribution
130 (
131  const word& fieldName,
132  const word& componentName,
133  const scalarField& x,
134  const scalarField& PDF,
135  const scalarField& CDF
136 ) const
137 {
138  if (!Pstream::master()) return;
139 
140  const fileName outputPath =
141  time_.globalPath()
143  /(
145  ? mesh().mesh().name()
146  : word::null
147  )
148  /name()
149  /time_.name();
150 
151  mkDir(outputPath);
152 
153  const word fieldComponentName =
154  fieldName
155  + (componentName.empty() ? "" : "_")
156  + componentName;
157 
158  formatter_->write
159  (
160  outputPath,
161  fieldComponentName,
162  coordSet(true, fieldComponentName, x),
163  "PDF", PDF,
164  "CDF", CDF
165  );
166 }
167 
168 
169 void Foam::functionObjects::LagrangianDistribution::writeDistribution
170 (
171  const scalarField& weight,
172  const word& fieldName,
173  const word& componentName,
174  const scalarField& field
175 )
176 {
177  // Get the range of the distribution
178  const Pair<scalar> range(gMin(field), gMax(field));
179 
180  // Write a single point if the distribution is uniform
181  if (range.first() == range.second())
182  {
183  writeDistribution
184  (
185  fieldName,
186  componentName,
187  scalarField(1, range.first()),
188  scalarField(1, vGreat),
189  scalarField(1, scalar(1))
190  );
191  return;
192  }
193 
194  // Construct the limits of the bins
195  scalarField x(nBins_ + 1);
196  for (label nodei = 0; nodei <= nBins_; ++ nodei)
197  {
198  const scalar f = scalar(nodei)/nBins_;
199  x[nodei] = (1 - f)*range.first() + f*range.second();
200  }
201 
202  // Populate the bins
203  scalarField PDF(nBins_ + 1, scalar(0));
204  forAll(field, i)
205  {
206  const scalar x = field[i];
207  const scalar f =
208  (x - range.first())
209  /max(range.second() - range.first(), rootVSmall);
210  const label bini = min(max(floor(f*nBins_), 0), nBins_ - 1);
211  const scalar g = f*nBins_ - scalar(bini);
212  PDF[bini] += weight[i]*(1 - g);
213  PDF[bini + 1] += weight[i]*g;
214  }
215 
216  // Synchronise
217  Pstream::listCombineGather(PDF, plusEqOp<scalar>());
219 
220  // Normalise and correct the ends, as they have half as many samples as the
221  // interior points
222  PDF /= sum(PDF)*(range.second() - range.first())/nBins_;
223  PDF.first() *= 2;
224  PDF.last() *= 2;
225 
226  // Write
227  writeDistribution
228  (
229  fieldName,
230  componentName,
231  x,
232  PDF,
234  );
235 }
236 
237 
238 template<template<class> class GeoField, class Type>
239 bool Foam::functionObjects::LagrangianDistribution::writeDistribution
240 (
241  const scalarField& weight,
242  const word& fieldName
243 )
244 {
245  if (!mesh().foundObject<GeoField<Type>>(fieldName)) return false;
246 
247  const typename GeoField<Type>::FieldType& field =
248  mesh().lookupObject<GeoField<Type>>(fieldName).primitiveField();
249 
250  for (direction d = 0; d < pTraits<Type>::nComponents; ++ d)
251  {
252  writeDistribution
253  (
254  weight,
255  fieldName,
256  pTraits<Type>::componentNames[d],
257  field.component(d)()
258  );
259  }
260 
261  return true;
262 }
263 
264 
265 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
266 
268 (
269  const word& name,
270  const Time& runTime,
271  const dictionary& dict
272 )
273 :
275  fields_(),
276  weightFields_(),
277  nBins_(-1),
278  formatter_(nullptr)
279 {
280  readCoeffs(dict);
281 }
282 
283 
284 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
285 
287 {}
288 
289 
290 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
291 
293 {
295  {
296  readCoeffs(dict);
297  return true;
298  }
299  else
300  {
301  return false;
302  }
303 }
304 
305 
307 {
308  wordList result(fields_);
309  result.append(weightFields_);
310  return result;
311 }
312 
313 
315 {
316  return true;
317 }
318 
319 
321 {
322  // We can't construct a distribution without any samples
323  if (returnReduce(mesh().size(), sumOp<label>()) == 0) return true;
324 
325  // Construct the weights
326  scalarField weight(mesh().size(), 1);
327  forAll(weightFields_, weightFieldi)
328  {
329  const word& weightFieldName = weightFields_[weightFieldi];
330 
331  if
332  (
333  !multiplyWeight<LagrangianField>(weightFieldName, weight)
334  && !multiplyWeight<LagrangianDynamicField>(weightFieldName, weight)
335  && !multiplyWeight<LagrangianInternalField>(weightFieldName, weight)
336  )
337  {
339  << "Weight field " << weightFieldName << " was not found"
340  << exit(FatalError);
341  }
342  }
343 
344  // Write the field values
345  forAll(fields_, fieldi)
346  {
347  const word& fieldName = fields_[fieldi];
348 
349  #define WRITE_FIELD_VALUE(Type, GeoField) \
350  && !writeDistribution<GeoField, Type>(weight, fieldName)
351 
352  if
353  (
354  true
358  )
359  {
360  cannotFindObject(fields_[fieldi]);
361  }
362 
363  #undef WRITE_COLUMN_VALUE
364  }
365 
366  return true;
367 }
368 
369 
370 // ************************************************************************* //
scalar range
#define WRITE_FIELD_VALUE(Type, GeoField)
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
Macros for easy insertion into run-time selection tables.
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
Generic GeometricField class.
const word & name() const
Return name.
Definition: IOobject.H:307
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:178
void resize(const label)
Alias for setSize(const label)
Definition: ListI.H:138
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:125
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Scatter data. Reverse of combineGather.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:76
T & first()
Return the first element of the list.
Definition: UListI.H:114
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
static tmp< scalarField > integrate(const scalarField &x, const scalarField &y)
Integrate the values y with respect to the coordinates x.
Definition: unintegrable.C:34
Abstract base-class for Time/database functionObjects.
Function to generate a plot of the distribution of the values in a Lagrangian field.
virtual wordList fields() const
Return the list of fields required.
LagrangianDistribution(const word &name, const Time &runTime, const dictionary &dict)
Construct from Time and dictionary.
virtual bool execute()
Execute. Does nothing.
virtual bool read(const dictionary &)
Read parameters.
Base class for function objects relating to a Lagrangian mesh.
virtual bool read(const dictionary &)
Read optional controls.
static const word outputPrefix
Directory prefix.
Definition: writeFile.H:72
const polyMesh & mesh() const
Return reference to polyMesh.
Definition: fvMesh.H:443
const Type & lookupObject(const word &name) const
Lookup and return the object of the given Type and name.
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:270
static autoPtr< setWriter > New(const word &writeType, const IOstream::streamFormat writeFormat=IOstream::ASCII, const IOstream::compressionType writeCompression=IOstream::UNCOMPRESSED)
Select given write options.
Definition: setWriter.C:286
A class for handling words, derived from string.
Definition: word.H:62
static const word null
An empty word.
Definition: word.H:77
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:346
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
defineTypeNameAndDebug(adjustTimeStepToCombustion, 0)
addToRunTimeSelectionTable(functionObject, adjustTimeStepToCombustion, dictionary)
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
bool mkDir(const fileName &, mode_t=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:290
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
IOerror FatalIOError
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
error FatalError
FOR_ALL_FIELD_TYPES(makeFieldSourceTypedef)
Type gMin(const FieldField< Field, Type > &f)
Type gMax(const FieldField< Field, Type > &f)
uint8_t direction
Definition: direction.H:45
labelList f(nPoints)
dictionary dict