OpenLB 1.8.1
Loading...
Searching...
No Matches
serialization.h
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2023-24 Julius Jeßberger
4 * E-mail contact: info@openlb.net
5 * The most recent release of OpenLB can be downloaded at
6 * <http://www.openlb.net/>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22*/
23
29 #ifndef SERIALIZATION_H
30 #define SERIALIZATION_H
31
35#include "utilities/aliases.h"
38
39namespace olb {
40
41namespace opti {
42
43template<typename S>
45
46
47
49
50
55template<typename S, unsigned dim>
57{
58public:
64
66 virtual std::size_t getSerializedCellIndex(const int latticeR[]) const = 0;
67
69 std::size_t getSerializedCellIndex(LatticeR<dim+1> latticeR) const {
70 return getSerializedCellIndex(latticeR.data());
71 }
72
74 virtual LatticeR<dim+1> getLatticeR(std::size_t index) const = 0;
75
77 std::size_t getSerializedComponentIndex(const int latticeR[],
78 unsigned iD, unsigned fieldDim) const {
79 return fieldDim * getSerializedCellIndex(latticeR) + iD;
80 }
81
84 unsigned iD, unsigned fieldDim) const {
85 return fieldDim * getSerializedCellIndex(latticeR) + iD;
86 }
87
90 unsigned fieldDim) const {
91 return getSerializedComponentIndex(coords.latticeR, coords.fieldComponent, fieldDim);
92 }
93
95 LatticeAndFieldR getLatticeAndFieldR(std::size_t index, unsigned fieldDim) const {
96 // index = fieldDim * cellIndex + fieldComponent
97 const auto fieldCoord = std::div((long int) index, (long int) fieldDim);
98 const auto geomCoord = getLatticeR(fieldCoord.quot);
99 return {geomCoord, (std::size_t) fieldCoord.rem};
100 }
101
102 virtual unsigned getNoCells() const = 0;
103};
104
113template<typename S, unsigned dim>
115{
116protected:
118 const unsigned _noCuboids;
119
120private:
121 std::vector<unsigned> _cuboidSizes; // number of cells per cuboid
122 std::vector<unsigned> _offsets; // accumulated cuboid sizes
123 unsigned _noCells;
124
125public:
127 : _cGeometry(cGeometry),
128 _noCuboids(_cGeometry.size())
129 {
130 _cuboidSizes.reserve(_noCuboids);
131 _offsets.reserve(_noCuboids);
132 for (unsigned i = 0; i < _noCuboids; ++i) {
133 _cuboidSizes.push_back(_cGeometry.get(i).getLatticeVolume());
134 }
135 _offsets.push_back(0);
136 for (unsigned i = 1; i < _noCuboids; ++i) {
137 _offsets.push_back(_offsets[i-1] + _cuboidSizes[i-1]);
138 }
139
140 const auto mc = _cGeometry.getMotherCuboid();
141 if constexpr (dim == 2) {
142 _noCells = (mc.getNx() + 1) * (mc.getNy() + 1);
143 } else {
144 _noCells = (mc.getNx() + 1) * (mc.getNy() + 1) * (mc.getNz() + 1);
145 }
146 }
147
149 : SimpleGeometrySerializer(sGeometry.getCuboidDecomposition())
150 { }
151
153 std::size_t getSerializedCellIndex(const int latticeR[]) const override {
154 const Cuboid<S,dim>& c = _cGeometry.get(latticeR[0]);
155 const int nX = c.getNx();
156 const int nY = c.getNy();
157 std::size_t res;
158 if constexpr (dim == 2) {
159 // cuboid_offset + NX*y + x
160 res = _offsets[latticeR[0]] + nX*latticeR[2] + latticeR[1];
161 } else {
162 // cuboid_offset + NX*NY*z + NX*y + x
163 res = _offsets[latticeR[0]] + nX*nY*latticeR[3] + nX*latticeR[2] + latticeR[1];
164 }
165 return res;
166 }
167
169
171 LatticeR<dim+1> getLatticeR(std::size_t index) const override {
172 const auto cuboidIt = std::upper_bound(_offsets.begin(), _offsets.end(), index) - 1;
173 LatticeR<dim+1> res;
174 res[0] = std::distance(_offsets.begin(), cuboidIt);
175
176 const Cuboid<S,dim>& c = _cGeometry.get(res[0]);
177 const std::size_t nX = c.getNx();
178 if constexpr (dim == 2) {
179 // index = cuboid_offset + NX*y + x
180 const auto divByNx = std::ldiv((long int) index - *cuboidIt, (long int) nX);
181 res[2] = divByNx.quot;
182 res[1] = divByNx.rem;
183 } else {
184 // index = cuboid_offset + NX*NY*z + NX*y + x
185 const std::size_t nY = c.getNy();
186 const auto divByNxNy = std::ldiv((long int) index - *cuboidIt, (long int) nX*nY);
187 res[3] = divByNxNy.quot;
188 const auto divByNx = std::ldiv(divByNxNy.rem, (long int) nX);
189 res[2] = divByNx.quot;
190 res[1] = divByNx.rem;
191 }
192 return res;
193 }
194
195 unsigned getNoCells() const override {
196 return _noCells;
197 }
198};
199
200
208template<typename S, unsigned dim>
210{
211private:
212 using L = LatticeR<dim+1>;
213 std::vector<L> _coords;
214 mutable FunctorPtr<SuperIndicatorF<S,dim>> _indicator; // indicates which cells are serialized
215 // the _indicator should never be changed, otherwise this class loses its
216 // correctness. The "mutable" keyword is only necessary because FunctorPtr
217 // does not support const arithmetic and should be removed if possible.
218
219public:
222 : _indicator(std::move(indicator))
223 {
224 const auto cGeometry = superGeometry.getCuboidDecomposition();
225 L latticeR;
226 for (unsigned iC = 0; iC < cGeometry.size(); ++iC) {
227 latticeR[0] = iC;
228 const int nX = cGeometry.get(iC).getNx();
229 const int nY = cGeometry.get(iC).getNy();
230 for (int iX=0; iX<nX; iX++) {
231 latticeR[1] = iX;
232 for (int iY=0; iY<nY; iY++) {
233 latticeR[2] = iY;
234 if constexpr (dim == 2) {
235 if (_indicator(latticeR.data())) {
236 _coords.push_back(latticeR);
237 }
238 } else {
239 const int nZ = cGeometry.get(iC).getNz();
240 for (int iZ=0; iZ<nZ; iZ++) {
241 latticeR[3] = iZ;
242
243 if (_indicator(latticeR.data())) {
244 _coords.push_back(latticeR);
245 }
246 }
247 }
248 }
249 }
250 }
251 }
252
254 FunctorPtr<IndicatorF<S,dim>>&& indicator)
255 : SparseGeometrySerializer(superGeometry,
256 new SuperIndicatorFfromIndicatorF<S,dim>(std::move(indicator), superGeometry))
257 { }
258
260 std::size_t getSerializedCellIndex(const int latticeR[]) const override {
261#ifdef OLB_DEBUG
262 // check if cell lies in indicated domain
263 bool isInside;
264 _indicator(&isInside, latticeR);
265 if (! isInside) {
266 OstreamManager clout (std::cout, "SparseGeometrySerializer");
267 clout << "Warning: the passed cell does not lie in the indicated domain "
268 << "and can therefore not be accessed." << std::endl;
269 std::exit(1);
270 }
271#endif
272 const L point (latticeR);
273 const auto upper = std::upper_bound(
274 _coords.begin(), _coords.end(), point, [](const L& a, const L& b){
275 return lex_smaller(a, b);
276 });
277 return std::distance(_coords.begin(), upper - 1);
278 }
279
281
283 LatticeR<dim+1> getLatticeR(std::size_t index) const override {
284#ifdef OLB_DEBUG
285 // check if index lies in accessible range
286 if (index >= _coords.size()) {
287 OstreamManager clout (std::cout, "SparseGeometrySerializer");
288 clout << "Warning: the passed index is higher than the number of cells "
289 << "which are marked by indicator." << std::endl;
290 std::exit(1);
291 }
292#endif
293 return _coords[index];
294 }
295
296 unsigned getNoCells() const override {
297 return _coords.size();
298 }
299};
300
301
302
303
304
306
307template <typename T, typename DESCRIPTOR>
308class BlockLatticeSerialDataF : public BlockLatticeF<T,DESCRIPTOR> {
309protected:
310 static constexpr unsigned d = DESCRIPTOR::d;
313 const int _iC;
314 std::function<T(T)> _projection;
315public:
317 Controller<T>& controller, const GeometrySerializer<T,d>& serializer,
318 int iC, unsigned dataDim, std::function<T(T)> projection)
319 : BlockLatticeF<T,DESCRIPTOR>(blockLattice, dataDim),
320 _controller(controller), _serializer(serializer), _iC(iC),
321 _projection(projection)
322 {
323 this->getName() = "BlockLatticeSerialDataF";
324 }
325
326 bool operator() (T output[], const int input[])
327 {
328 if (this->getBlock().isInsideCore(input)) {
329 int latticeR[4] = {_iC, input[0], input[1], 0};
330 if constexpr (d == 3) {
331 latticeR[3] = input[2];
332 }
333 const auto index = _serializer.getSerializedComponentIndex(latticeR, 0, this->getTargetDim());
334 for (int i = 0; i < this->getTargetDim(); ++i) {
335 output[i] = _projection(_controller.getControl(index + i));
336 }
337 return true;
338 }
339 return false;
340 }
341};
342
343
348// Controller stores field values, which are (component-wise, point by point)
349// transformed by the projection function and then returned.
350// The controller is global, it is expected to have data for the entire domain.
351// Hence, every process must have access to the ''same'' controller.
352template <typename T, typename DESCRIPTOR>
353class SuperLatticeSerialDataF : public SuperLatticeF<T,DESCRIPTOR> {
354protected:
355 std::shared_ptr<const GeometrySerializer<T,DESCRIPTOR::d>> _serializer;
356public:
358 Controller<T>& controller, unsigned dataDim,
359 std::shared_ptr<const GeometrySerializer<T,DESCRIPTOR::d>> serializer,
360 std::function<T(T)> projection = [](T x){ return x; })
361 : SuperLatticeF<T,DESCRIPTOR>(superLattice, dataDim),
362 _serializer(serializer)
363 {
364 this->getName() = "SuperLatticeSerialDataF";
365
366 const int maxC = superLattice.getLoadBalancer().size();
367 this->_blockF.reserve(maxC);
368
369 for (int iC = 0; iC < maxC; ++iC) {
370 this->_blockF.emplace_back(
372 superLattice.getBlock(iC),
373 controller,
375 superLattice.getLoadBalancer().glob(iC),
376 dataDim,
377 projection)
378 );
379 }
380 }
381
383 Controller<T>& controller, unsigned dataDim,
384 std::function<T(T)> projection = [](T x){ return x; })
385 : SuperLatticeSerialDataF(superLattice, controller, dataDim,
386 std::make_shared<const SimpleGeometrySerializer<T,DESCRIPTOR::d>>(superLattice),
387 projection)
388 { }
389};
390
391
392
393
394
396
397
399// warning: this function breaks the parallelization concept. It is expensive
400// and should be used with care
401template<typename S, unsigned dim>
403{
404 int material = 0;
405 if ( sGeometry.getLoadBalancer().rank(latticeR[0]) == singleton::mpi().getRank() ) {
406 material = sGeometry.get(latticeR);
407 }
408#ifdef PARALLEL_MODE_MPI
409 singleton::mpi().bCast(&material, 1, sGeometry.getLoadBalancer().rank(latticeR[0]));
410#endif
411 return material;
412}
413
415// warning: this function breaks the parallelization concept. It is expensive
416// and should be used with care
417template <unsigned D, typename T>
419{
420 bool result = false;
421 const auto loadBalancer = f.getSuperGeometry().getLoadBalancer();
422 if ( loadBalancer.rank(input[0]) == singleton::mpi().getRank() ) {
423 result = f(input);
424 }
425#ifdef PARALLEL_MODE_MPI
426 singleton::mpi().bCast(&result, 1, loadBalancer.rank(input[0]));
427#endif
428 return result;
429}
430
432// warning: this function breaks the parallelization concept. It is expensive
433// and should be used with care
434template <unsigned D, typename T, typename U=T>
435bool evaluateSuperFglobally (SuperF<D,T,U>& f, U* output, const int input[])
436{
437 const auto loadBalancer = f.getSuperStructure().getLoadBalancer();
438 if ( loadBalancer.rank(input[0]) == singleton::mpi().getRank() ) {
439 f(output, input);
440 }
441#ifdef PARALLEL_MODE_MPI
442 singleton::mpi().bCast(output, f.getTargetDim(), loadBalancer.rank(input[0]));
443#endif
444 return true;
445}
446
447
448
449
450
452
465template <typename FIELD, typename T, typename DESCRIPTOR, typename C=std::vector<T>>
467 const GeometrySerializer<T,DESCRIPTOR::d>& serializer,
469 unsigned controlDim
470 )
471{
472 constexpr unsigned dim = DESCRIPTOR::d;
473 constexpr unsigned fieldDim = DESCRIPTOR::template size<FIELD>();
474 auto result = util::ContainerCreator<C>::create(controlDim);
475 const auto& cGeometry = sLattice.getCuboidDecomposition();
476 const auto& loadBalancer = sLattice.getLoadBalancer();
477
478 LatticeR<dim+1> latticeR;
479 for (int iC=0; iC<cGeometry.size(); iC++) {
480 latticeR[0] = iC;
481 const int nX = cGeometry.get(iC).getNx();
482 const int nY = cGeometry.get(iC).getNy();
483 const int nZ = cGeometry.get(iC).getNz();
484 for (int iX=0; iX<nX; iX++) {
485 latticeR[1] = iX;
486 for (int iY=0; iY<nY; iY++) {
487 latticeR[2] = iY;
488 for (int iZ=0; iZ<nZ; iZ++) {
489 latticeR[3] = iZ;
490
491 if (evaluateSuperIndicatorFglobally<dim,T>(indicator, latticeR.data())) {
492 Vector<T,fieldDim> dataHelp;
493 if (loadBalancer.rank(iC) == singleton::mpi().getRank()) {
494 for (unsigned iDim=0; iDim<fieldDim; iDim++) {
495 const auto cell = sLattice.get(latticeR);
496 dataHelp[iDim]
497 = cell.template getFieldComponent<FIELD>(iDim);
498 }
499 }
500#ifdef PARALLEL_MODE_MPI
501 singleton::mpi().bCast(&dataHelp[0], fieldDim, loadBalancer.rank(iC));
502#endif
503 for (unsigned iDim=0; iDim<fieldDim; iDim++) {
504 const auto index = serializer.getSerializedComponentIndex(latticeR, iDim, fieldDim);
505 result[index] = dataHelp[iDim];
506 }
507 }
508 }
509 }
510 }
511 }
512 return result;
513}
514
515// forward declaration
516template<
517 typename S,
518 template<typename,SolverMode> typename SOLVER,
519 concepts::Field CONTROLLED_FIELD,
520 template<typename...> typename PRIMAL_DYNAMICS,
521 typename C>
523
524
534template<
535 typename T,
536 template<typename,SolverMode> typename SOLVER,
537 concepts::Field CONTROLLED_FIELD,
538 template<typename...> typename PRIMAL_DYNAMICS,
539 typename C=std::vector<T>>
541 std::shared_ptr<SOLVER<T,SolverMode::Reference>> solver)
542{
543 using descriptor = typename SOLVER<T,SolverMode::Reference>::AdjointLbSolver::DESCRIPTOR;
544 return serialDataFromField<
545 CONTROLLED_FIELD, T, descriptor, C>(
546 solver->lattice(),
547 *(optiCase._serializer),
548 *(optiCase._controlIndicator),
549 optiCase._dimCtrl
550 );
551}
552
553
564template<
565 typename T,
566 template<typename,SolverMode> typename SOLVER,
567 concepts::Field CONTROLLED_FIELD,
568 template<typename...> typename PRIMAL_DYNAMICS,
569 typename C=std::vector<T>>
571 std::shared_ptr<SOLVER<T,SolverMode::Reference>> solver)
572{
574 std::transform(result.begin(), result.end(), result.begin(), [&](auto r){
575 return optiCase._projection->inverse(r);
576 });
577 return result;
578}
579
580
581}
582}
583#endif
Decomposition of a physical volume into a set of disjoint cuboids.
const Cuboid< T, D > & get(int iC) const
Read access to a single cuboid.
const Cuboid< T, D > & getMotherCuboid() const
Returns the smallest cuboid that includes all cuboids of the structure.
std::size_t getLatticeVolume() const
Returns the number of Nodes in the volume.
Definition cuboid.hh:62
Smart pointer for managing the various ways of passing functors around.
Definition functorPtr.h:60
class for marking output with some text
Representation of a statistic for a parallel 2D geometry.
int get(LatticeR< D+1 > latticeR) const
Read only access to the material numbers, error handling: returns 0 if data is not available.
Super class maintaining block lattices for a cuboid decomposition.
BlockLattice< T, DESCRIPTOR > & getBlock(int locC)
Return BlockLattice with local index locC.
Cell< T, DESCRIPTOR > get(LatticeR< DESCRIPTOR::d+1 > latticeR)
Get local cell interface.
LoadBalancer< T > & getLoadBalancer()
Read and write access to the load balancer.
CuboidDecomposition< T, D > & getCuboidDecomposition()
Read and write access to cuboid geometry.
Plain old scalar vector.
constexpr const T * data() const any_platform
Definition vector.h:172
static constexpr unsigned d
const GeometrySerializer< T, d > & _serializer
bool operator()(T output[], const int input[])
BlockLatticeSerialDataF(BlockLattice< T, DESCRIPTOR > &blockLattice, Controller< T > &controller, const GeometrySerializer< T, d > &serializer, int iC, unsigned dataDim, std::function< T(T)> projection)
S getControl(int i) const
Definition controller.h:59
This class serializes the cells inside the geometry.
virtual unsigned getNoCells() const =0
std::size_t getSerializedCellIndex(LatticeR< dim+1 > latticeR) const
Compute serialized cell index from lattice coordinates.
virtual std::size_t getSerializedCellIndex(const int latticeR[]) const =0
Compute serialized cell index from lattice coordinates.
std::size_t getSerializedComponentIndex(LatticeAndFieldR coords, unsigned fieldDim) const
Get index of field component from lattice coordinates and component index.
std::size_t getSerializedComponentIndex(LatticeR< dim+1 > latticeR, unsigned iD, unsigned fieldDim) const
Get index of field component from lattice coordinates and component index.
virtual LatticeR< dim+1 > getLatticeR(std::size_t index) const =0
Get lattice coordinates from serialized cell index.
LatticeAndFieldR getLatticeAndFieldR(std::size_t index, unsigned fieldDim) const
Get lattice coordinates and field component from serialized field index.
std::size_t getSerializedComponentIndex(const int latticeR[], unsigned iD, unsigned fieldDim) const
Get index of field component from lattice coordinates and component index.
This class implements the evaluation of the goal functional and its derivatives by using adjoint LBM.
std::size_t _dimCtrl
upper limit for the number of control variables (#voxels * field-dimension)
std::shared_ptr< SuperIndicatorF< S, dim > > _controlIndicator
Marks, where there are active control variables.
std::shared_ptr< GeometrySerializer< S, dim > > _serializer
This class serializes the cells inside the geometry.
SimpleGeometrySerializer(CuboidDecomposition< S, dim > &cGeometry)
SimpleGeometrySerializer(SuperGeometry< S, dim > &sGeometry)
std::size_t getSerializedCellIndex(const int latticeR[]) const override
Compute serialized cell index from lattice coordinates.
LatticeR< dim+1 > getLatticeR(std::size_t index) const override
Get lattice coordinates from serialized cell index.
CuboidDecomposition< S, dim > & _cGeometry
unsigned getNoCells() const override
This class serializes the cells which are marked by indicator.
SparseGeometrySerializer(SuperGeometry< S, dim > &superGeometry, FunctorPtr< SuperIndicatorF< S, dim > > &&indicator)
SparseGeometrySerializer(SuperGeometry< S, dim > &superGeometry, FunctorPtr< IndicatorF< S, dim > > &&indicator)
unsigned getNoCells() const override
std::size_t getSerializedCellIndex(const int latticeR[]) const override
Compute serialized cell index from lattice coordinates.
LatticeR< dim+1 > getLatticeR(std::size_t index) const override
Get lattice coordinates from serialized cell index.
A data field whose values are managed by a controller.
SuperLatticeSerialDataF(SuperLattice< T, DESCRIPTOR > &superLattice, Controller< T > &controller, unsigned dataDim, std::shared_ptr< const GeometrySerializer< T, DESCRIPTOR::d > > serializer, std::function< T(T)> projection=[](T x){ return x;})
std::shared_ptr< const GeometrySerializer< T, DESCRIPTOR::d > > _serializer
SuperLatticeSerialDataF(SuperLattice< T, DESCRIPTOR > &superLattice, Controller< T > &controller, unsigned dataDim, std::function< T(T)> projection=[](T x){ return x;})
void bCast(T *sendBuf, int sendCount, int root=0, MPI_Comm comm=MPI_COMM_WORLD)
Broadcast data from one processor to multiple processors.
int getRank() const
Returns the process ID.
The description of a Controller – header file.
bool evaluateSuperIndicatorFglobally(SuperIndicatorF< T, D > &f, const int input[])
Helper that gives global access to the values of an indicator.
bool evaluateSuperFglobally(SuperF< D, T, U > &f, U *output, const int input[])
Helper that gives global access to the values of a functor.
C getControl(OptiCaseDual< T, SOLVER, CONTROLLED_FIELD, PRIMAL_DYNAMICS, C > &optiCase, std::shared_ptr< SOLVER< T, SolverMode::Reference > > solver)
Get control values of some simulation in the context of adjoint optimization Take values of FIELD,...
C serialDataFromField(SuperLattice< T, DESCRIPTOR > &sLattice, const GeometrySerializer< T, DESCRIPTOR::d > &serializer, SuperIndicatorF< T, DESCRIPTOR::d > &indicator, unsigned controlDim)
Take values of a field and put them into a long vector Idea: FIELD[cartesianCoordinates] = result[ser...
int getMaterialGlobally(SuperGeometry< S, dim > &sGeometry, LatticeR< dim+1 > latticeR)
Helper that gives global access to material numbers.
MpiManager & mpi()
Top level namespace for all of OpenLB.
std::conditional_t< DESCRIPTOR::d==2, SuperLatticeF2D< T, DESCRIPTOR >, SuperLatticeF3D< T, DESCRIPTOR > > SuperLatticeF
Definition aliases.h:118
std::conditional_t< D==2, SuperIndicatorFfromIndicatorF2D< T >, SuperIndicatorFfromIndicatorF3D< T > > SuperIndicatorFfromIndicatorF
Definition aliases.h:277
std::conditional_t< DESCRIPTOR::d==2, BlockLatticeF2D< T, DESCRIPTOR >, BlockLatticeF3D< T, DESCRIPTOR > > BlockLatticeF
Definition aliases.h:147
std::conditional_t< D==2, SuperF2D< T, U >, SuperF3D< T, U > > SuperF
Definition aliases.h:187
std::conditional_t< D==2, IndicatorF2D< T >, IndicatorF3D< T > > IndicatorF
Definition aliases.h:247
std::string getName(OperatorScope scope)
Returns human-readable name of scope.
std::conditional_t< D==2, SuperIndicatorF2D< T >, SuperIndicatorF3D< T > > SuperIndicatorF
Definition aliases.h:197
Optimization Code.
An OptiCase using Adjoint-based Differentiation.
Bundle for lattice coordinates + field component.
Creates a container of type C.
Representation of a parallel 2D geometry – header file.