streamlines.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) 2011-2026 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 "streamlines.H"
27 #include "streamlinesCloud.H"
28 #include "sampledSet.H"
29 #include "globalIndex.H"
31 #include "PatchTools.H"
32 #include "writeFile.H"
33 #include "polyTopoChangeMap.H"
34 #include "polyMeshMap.H"
35 #include "polyDistributionMap.H"
36 #include "OSspecific.H"
38 
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
40 
41 namespace Foam
42 {
43  namespace functionObjects
44  {
47  }
48 }
49 
52 {
53  "forward",
54  "backward",
55  "both"
56 };
57 
58 
59 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
60 
62 (
63  const word& name,
64  const Time& runTime,
65  const dictionary& dict
66 )
67 :
68  fvMeshFunctionObject(name, runTime, dict),
69  nSubCycle_(0)
70 {
71  read(dict);
72 }
73 
74 
75 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
76 
78 {}
79 
80 
81 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
82 
84 {
85  dict.lookup("fields") >> fields_;
86 
87  UName_ = dict.lookupOrDefault("U", word("U"));
88 
89  writeAge_ = dict.lookupOrDefault<Switch>("writeAge", true);
90 
91  trackDirection_ = trackDirectionNames_[word(dict.lookup("direction"))];
92 
93  trackOutside_ = dict.lookupOrDefault<Switch>("outside", false);
94 
95  dict.lookup("lifeTime") >> lifeTime_;
96 
97  if (lifeTime_ < 1)
98  {
100  << "Illegal value " << lifeTime_ << " for lifeTime"
101  << exit(FatalError);
102  }
103 
104  bool subCycling = dict.found("nSubCycle");
105  bool fixedLength = dict.found("trackLength");
106  if (subCycling && fixedLength)
107  {
109  << "Cannot both specify automatic time stepping (through '"
110  << "nSubCycle' specification) and fixed track length (through '"
111  << "trackLength')"
112  << exit(FatalIOError);
113  }
114  if (subCycling)
115  {
116  nSubCycle_ = max(dict.lookup<scalar>("nSubCycle"), 1);
117  trackLength_ = vGreat;
118  Info<< indent << "automatic track length specified through"
119  << " number of sub cycles : " << nSubCycle_ << endl;
120  }
121  else
122  {
123  nSubCycle_ = 1;
124  dict.lookup("trackLength") >> trackLength_;
125  Info<< indent << " fixed track length specified : "
126  << trackLength_ << endl;
127  }
128 
129  interpolationScheme_ =
130  dict.lookupOrDefault
131  (
132  "interpolationScheme",
134  );
135 
136  cloudName_ = dict.lookupOrDefault<word>("cloudName", "streamlines");
137 
138  sampledSetPtr_ = sampledSet::New
139  (
140  "seedSampleSet",
141  mesh_,
142  dict.subDict("seedSampleSet")
143  );
144 
145  formatterPtr_ = setWriter::New(dict.lookup("setFormat"), dict);
146 
147  return true;
148 }
149 
150 
152 {
153  wordList allFields(fields_);
154  allFields.append(UName_);
155 
156  return allFields;
157 }
158 
159 
161 {
162  return true;
163 }
164 
165 
167 {
168  Info<< indent << type() << " " << name() << " write:" << nl;
169 
170  // Create list of available fields
172  forAll(fields_, fieldi)
173  {
174  if
175  (
176  false
177  #define FoundTypeField(Type, nullArg) \
178  || foundObject<VolField<Type>>(fields_[fieldi])
180  #undef FoundTypeField
181  )
182  {
183  fieldNames.append(fields_[fieldi]);
184  }
185  else
186  {
187  cannotFindObject(fields_[fieldi]);
188  }
189  }
190 
191  // Lookup fields and construct interpolators
192  #define DeclareTypeInterpolator(Type, nullArg) \
193  PtrList<interpolation<Type>> Type##Interp(fieldNames.size());
195  #undef DeclareTypeInterpolator
196  forAll(fieldNames, fieldi)
197  {
198  #define ConstructTypeInterpolator(Type, nullArg) \
199  if (mesh_.foundObject<VolField<Type>>(fieldNames[fieldi])) \
200  { \
201  Type##Interp.set \
202  ( \
203  fieldi, \
204  interpolation<Type>::New \
205  ( \
206  interpolationScheme_, \
207  mesh_.lookupObject<VolField<Type>>(fieldNames[fieldi]) \
208  ) \
209  ); \
210  }
212  #undef ConstructTypeInterpolator
213  }
214 
215  // Create a velocity interpolator if it is not already available
216  const label UIndex = findIndex(fieldNames, UName_);
217  tmpNrc<interpolation<vector>> UInterp(nullptr);
218  if (UIndex == -1)
219  {
220  UInterp =
222  (
224  (
225  interpolationScheme_,
226  mesh_.lookupObject<volVectorField>(UName_)
227  ).ptr()
228  );
229  }
230 
231  // Get the mesh searching engine
232  const meshSearch& searchEngine = meshSearch::New(mesh_);
233 
234  // Do tracking to create sampled data
235  DynamicField<point> allPositions;
236  DynamicField<label> allTracks;
237  DynamicField<label> allTrackParts;
238  DynamicField<scalar> allAges;
239  #define DeclareAllTypes(Type, nullArg) \
240  List<DynamicField<Type>> all##Type##s(fieldNames.size());
242  #undef DeclareAllTypes
243  {
244  // Create a cloud and initialise with points from the sampled set
245  globalIndex gi(sampledSetPtr_().size());
247  (
248  mesh_,
249  cloudName_,
251  );
252  label nLocateBoundaryHits;
253  forAll(sampledSetPtr_(), i)
254  {
255  particles.addParticle
256  (
258  (
259  searchEngine,
260  sampledSetPtr_().positions()[i],
261  sampledSetPtr_().cells()[i],
262  nLocateBoundaryHits,
263  lifeTime_,
264  gi.toGlobal(i)
265  )
266  );
267  }
268 
269  // Report the number of successful seeds
270  const label nSeeds = returnReduce(particles.size(), sumOp<label>());
271  Info<< indent << " Seeded " << nSeeds << " particles" << endl;
272 
273  // Create tracking data
275  (
276  particles,
277  #define TypeInterpolatorParameter(Type, nullArg) \
278  Type##Interp,
281  UIndex != -1 ? vectorInterp[UIndex] : UInterp(),
282  trackDirection_ == trackDirection::forward,
283  trackOutside_,
284  nSubCycle_,
285  trackLength_,
286  allPositions,
287  allTracks,
288  allTrackParts,
289  allAges
290  #define AllTypesParameter(Type, nullArg) \
291  , all##Type##s
293  #undef AllTypesParameter
294  );
295 
296  // Track
297  IDLList<streamlinesParticle> initialParticles;
298  if (trackDirection_ == trackDirection::both)
299  {
300  initialParticles = particles;
301  }
302 
303  particles.move(particles, td);
304 
305  if (trackDirection_ == trackDirection::both)
306  {
307  particles.IDLList<streamlinesParticle>::operator=(initialParticles);
308  td.trackForward_ = !td.trackForward_;
309  particles.move(particles, td);
310  }
311  }
312 
313  // Gather data on the master
314  if (Pstream::parRun())
315  {
316  Pstream::concatenateList(allPositions);
317  Pstream::concatenateList(allTracks);
318  Pstream::concatenateList(allTrackParts);
319  Pstream::concatenateList(allAges);
320  forAll(fieldNames, fieldi)
321  {
322  #define ConcatenateListAllTypes(Type, nullArg) \
323  if (Type##Interp.set(fieldi)) \
324  { \
325  Pstream::concatenateList(all##Type##s[fieldi]); \
326  }
328  #undef ConcatenateListAllTypes
329  }
330  }
331 
332  // Report the total number of samples
333  Info<< indent << "Sampled " << allPositions.size() << " locations" << endl;
334 
335  // Bin-sort by track and trackPart to build an ordering
336  labelList order(allPositions.size());
337  if (Pstream::master() && allPositions.size())
338  {
339  const label nTracks = max(allTracks) + 1;
340  const label trackParti0 = min(allTrackParts);
341  const label trackParti1 = max(allTrackParts) + 1;
342 
343  labelListList trackPartCounts
344  (
345  nTracks,
346  labelList(trackParti1 - trackParti0, 0)
347  );
348  forAll(allPositions, samplei)
349  {
350  const label tracki = allTracks[samplei];
351  const label trackParti = -trackParti0 + allTrackParts[samplei];
352  trackPartCounts[tracki][trackParti] ++;
353  }
354 
355  label offset = 0;
356  labelListList trackPartOffsets
357  (
358  nTracks,
359  labelList(trackParti1 - trackParti0, 0)
360  );
361  forAll(trackPartOffsets, tracki)
362  {
363  forAll(trackPartOffsets[tracki], trackParti)
364  {
365  trackPartOffsets[tracki][trackParti] += offset;
366  offset += trackPartCounts[tracki][trackParti];
367  }
368  }
369 
370  forAll(trackPartCounts, tracki)
371  {
372  trackPartCounts[tracki] = 0;
373  }
374 
375  forAll(allPositions, samplei)
376  {
377  const label tracki = allTracks[samplei];
378  const label trackParti = -trackParti0 + allTrackParts[samplei];
379 
380  order[samplei] =
381  trackPartOffsets[tracki][trackParti]
382  + trackPartCounts[tracki][trackParti];
383 
384  trackPartCounts[tracki][trackParti] ++;
385  }
386  }
387 
388  //auto reportTrackParts = [&]()
389  //{
390  // Info<< nl;
391  // forAll(allPositions, samplei)
392  // {
393  // if
394  // (
395  // samplei == 0
396  // || allTracks[samplei] != allTracks[samplei - 1]
397  // || allTrackParts[samplei] != allTrackParts[samplei - 1]
398  // )
399  // {
400  // Info<< "track #" << allTracks[samplei]
401  // << " part #" << allTrackParts[samplei]
402  // << " from i=" << samplei << " to ";
403  // }
404  // if
405  // (
406  // samplei == allPositions.size() - 1
407  // || allTracks[samplei + 1] != allTracks[samplei]
408  // || allTrackParts[samplei + 1] != allTrackParts[samplei]
409  // )
410  // {
411  // Info<< "i=" << samplei << nl;
412  // }
413  // }
414  //};
415 
416  //reportTrackParts();
417 
418  // Reorder
419  if (Pstream::master())
420  {
421  allPositions.rmap(allPositions, order);
422  allTracks.rmap(allTracks, order);
423  allTrackParts.rmap(allTrackParts, order);
424  allAges.rmap(allAges, order);
425  forAll(fieldNames, fieldi)
426  {
427  #define RMapAllTypes(Type, nullArg) \
428  if (Type##Interp.set(fieldi)) \
429  { \
430  all##Type##s[fieldi].rmap(all##Type##s[fieldi], order); \
431  }
433  #undef RMapAllTypes
434  }
435  }
436 
437  //reportTrackParts();
438 
439  // Relabel tracks and track parts into track labels only, and join the
440  // forward and backward track parts that are connected to the seed
441  if (Pstream::master())
442  {
443  label samplei = 0, tracki = 0;
444  forAll(allPositions, samplej)
445  {
446  const label trackj = allTracks[samplej];
447  const label trackPartj = allTrackParts[samplej];
448 
449  allPositions[samplei] = allPositions[samplej];
450  allTracks[samplei] = tracki;
451  allTrackParts[samplei] = 0;
452  allAges[samplei] = allAges[samplej];
453  forAll(fieldNames, fieldi)
454  {
455  #define ShuffleUpAllTypes(Type, nullArg) \
456  if (Type##Interp.set(fieldi)) \
457  { \
458  all##Type##s[fieldi][samplei] = \
459  all##Type##s[fieldi][samplej]; \
460  }
462  #undef ShuffleUpAllTypes
463  }
464 
465  const bool joinNewParts =
466  samplej != allPositions.size() - 1
467  && trackPartj == -1
468  && allTrackParts[samplej + 1] == 0;
469 
470  if (!joinNewParts) samplei ++;
471 
472  const bool newPart =
473  samplej == allPositions.size() - 1
474  || trackj != allTracks[samplej + 1]
475  || trackPartj != allTrackParts[samplej + 1];
476 
477  if (!joinNewParts && newPart) tracki ++;
478  }
479 
480  allPositions.resize(samplei);
481  allTracks.resize(samplei);
482  allTrackParts.resize(samplei);
483  allAges.resize(samplei);
484  forAll(fieldNames, fieldi)
485  {
486  #define ResizeAllTypes(Type, nullArg) \
487  if (Type##Interp.set(fieldi)) \
488  { \
489  all##Type##s[fieldi].resize(samplei); \
490  }
492  #undef ResizeAllTypes
493  }
494  }
495 
496  //reportTrackParts();
497 
498  // Write
499  if (Pstream::master() && allPositions.size())
500  {
501  // Make output directory
502  const fileName outputPath =
503  time_.globalPath()
505  /(mesh_.name() != polyMesh::defaultRegion ? mesh_.name() : word())
506  /name()
507  /time_.name();
508  mkDir(outputPath);
509 
510  // Pass data to the formatter to write
511  const label nValueSets = fieldNames.size() + writeAge_;
512  wordList valueSetNames(nValueSets);
513  #define DeclareTypeValueSets(Type, nullArg) \
514  UPtrList<const Field<Type>> Type##ValueSets(nValueSets);
516  #undef DeclareTypeValueSets
517  if (writeAge_)
518  {
519  valueSetNames[0] = "age";
520  scalarValueSets.set(0, &allAges);
521  }
522  forAll(fieldNames, fieldi)
523  {
524  valueSetNames[fieldi + writeAge_] = fieldNames[fieldi];
525 
526  #define SetTypeValueSetPtr(Type, nullArg) \
527  if (Type##Interp.set(fieldi)) \
528  { \
529  Type##ValueSets.set \
530  ( \
531  fieldi + writeAge_, \
532  &all##Type##s[fieldi] \
533  ); \
534  }
536  #undef SetTypeValueSetPtr
537  }
538  formatterPtr_->write
539  (
540  outputPath,
541  "tracks",
542  coordSet(allTracks, word::null, allPositions),
543  valueSetNames
544  #define TypeValueSetsParameter(Type, nullArg) , Type##ValueSets
547  );
548  }
549 
550  Info<< endl;
551 
552  return true;
553 }
554 
555 
557 {
558  if (&mesh == &mesh_)
559  {
560  sampledSetPtr_->movePoints();
561  }
562 }
563 
564 
566 (
567  const polyTopoChangeMap& map
568 )
569 {
570  if (&map.mesh() == &mesh_)
571  {
572  sampledSetPtr_->topoChange(map);
573  }
574 }
575 
576 
578 (
579  const polyMeshMap& map
580 )
581 {
582  if (&map.mesh() == &mesh_)
583  {
584  sampledSetPtr_->mapMesh(map);
585  }
586 }
587 
588 
590 (
591  const polyDistributionMap& map
592 )
593 {
594  if (&map.mesh() == &mesh_)
595  {
596  sampledSetPtr_->distribute(map);
597  }
598 }
599 
600 
601 // ************************************************************************* //
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:449
Macros for easy insertion into run-time selection tables.
Dynamically sized Field.
Definition: DynamicField.H:72
void resize(const label)
Alter the addressed list size.
void rmap(const UList< Type > &mapF, const labelUList &mapAddressing)
1 to 1 reverse-map from the given field
Definition: Field.C:386
Generic GeometricField class.
Template class for intrusive linked lists.
Definition: ILList.H:67
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:178
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
Initialise the NamedEnum HashTable from the static list of names.
Definition: NamedEnum.H:55
static void concatenateList(ListType &list)
Gather the given list and concatenate on the master.
A simple wrapper around bool so that it can be read as a word: true/false, on/off,...
Definition: Switch.H:61
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:76
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:399
Holds list of sampling positions.
Definition: coordSet.H:51
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
A class for handling file names.
Definition: fileName.H:82
Abstract base-class for Time/database functionObjects.
Specialisation of Foam::functionObject for an Foam::fvMesh, providing a reference to the Foam::fvMesh...
This functionObject tracks a particle cloud in the specified velocity field of an incompressible flow...
Definition: particles.H:111
Generates streamline data by sampling a set of user-specified fields along a particle track,...
Definition: streamlines.H:208
streamlines(const word &name, const Time &runTime, const dictionary &dict)
Construct from Time and dictionary.
Definition: streamlines.C:62
virtual wordList fields() const
Return the list of fields required.
Definition: streamlines.C:151
virtual void topoChange(const polyTopoChangeMap &)
Update topology using the given map.
Definition: streamlines.C:566
virtual void distribute(const polyDistributionMap &)
Redistribute or update using the given distribution map.
Definition: streamlines.C:590
virtual ~streamlines()
Destructor.
Definition: streamlines.C:77
virtual void mapMesh(const polyMeshMap &)
Update from another mesh using the given map.
Definition: streamlines.C:578
virtual void movePoints(const polyMesh &)
Update for mesh point-motion.
Definition: streamlines.C:556
virtual bool execute()
Do nothing.
Definition: streamlines.C:160
virtual bool write()
Calculate and write the streamlines.
Definition: streamlines.C:166
virtual bool read(const dictionary &)
Read the field average data.
Definition: streamlines.C:83
static const NamedEnum< trackDirection, 3 > trackDirectionNames_
Track direction enumeration names.
Definition: streamlines.H:222
static const word outputPrefix
Directory prefix.
Definition: writeFile.H:72
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:64
label toGlobal(const label i) const
From local to global.
Definition: globalIndexI.H:82
static autoPtr< interpolation< Type > > New(const word &interpolationType, const VolField< Type > &psi)
Return a reference to the specified interpolation scheme.
Definition: interpolation.C:53
Piecewise-linear interpolation method. Uses volPointInterpolation to create values on the points....
Mesh object that implements searches within the local cells and faces.
Definition: meshSearch.H:59
static const meshSearch & New(const polyMesh &mesh, const pointInCellShapes=pointInCellShapes::tets)
Lookup or construct from mesh and cell decomposition option.
Definition: meshSearch.C:61
Class containing mesh-to-mesh mapping information after a mesh distribution where we send parts of me...
const polyMesh & mesh() const
Return polyMesh.
Class containing mesh-to-mesh mapping information.
Definition: polyMeshMap.H:51
const polyMesh & mesh() const
Return polyMesh.
Definition: polyMeshMap.H:75
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:78
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:260
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
const polyMesh & mesh() const
Return polyMesh.
static autoPtr< sampledSet > New(const word &name, const polyMesh &mesh, const dictionary &dict)
Return a reference to the selected sampledSet.
Definition: sampledSet.C:162
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 Cloud of streamlines particles.
Particle class that samples fields as it passes through. Used in streamlines calculation.
A class for managing temporary objects without reference counting.
Definition: tmpNrc.H:53
A class for handling words, derived from string.
Definition: word.H:63
static const word null
An empty word.
Definition: word.H:78
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
static List< word > fieldNames
Definition: globalFoam.H:46
const cellShapeList & cells
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.name(), lagrangian::cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
defineTypeNameAndDebug(fvMeshFunctionObject, 0)
addToRunTimeSelectionTable(functionObject, fvModel, dictionary)
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
List< label > labelList
A List of labels.
Definition: labelList.H:56
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
int order(const scalar s)
FOR_ALL_FIELD_TYPES(makeDimensionedPointFieldFunctions)
messageStream Info
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
IOerror FatalIOError
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
error FatalError
void offset(label &lst, const label o)
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:243
static const char nl
Definition: Ostream.H:297
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
fileType type(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return the file type: directory or file.
Definition: POSIX.C:488
dictionary dict
#define FoundTypeField(Type, nullArg)
#define DeclareTypeValueSets(Type, nullArg)
#define SetTypeValueSetPtr(Type, nullArg)
#define DeclareAllTypes(Type, nullArg)
#define AllTypesParameter(Type, nullArg)
#define ResizeAllTypes(Type, nullArg)
#define TypeValueSetsParameter(Type, nullArg)
#define DeclareTypeInterpolator(Type, nullArg)
#define ConstructTypeInterpolator(Type, nullArg)
#define RMapAllTypes(Type, nullArg)
#define TypeInterpolatorParameter(Type, nullArg)
#define ShuffleUpAllTypes(Type, nullArg)
#define ConcatenateListAllTypes(Type, nullArg)