ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
LBWalberlaImpl.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2019-2024 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22/**
23 * @file
24 * @ref walberla::LBWalberlaImpl implements the interface of the LB
25 * waLBerla bridge using sweeps generated by lbmpy
26 * (see <tt>maintainer/walberla_kernels</tt>).
27 */
28
29#include <blockforest/Initialization.h>
30#include <blockforest/StructuredBlockForest.h>
31#include <blockforest/communication/UniformBufferedScheme.h>
32#include <domain_decomposition/BlockDataID.h>
33#include <domain_decomposition/IBlock.h>
34#include <field/AddToStorage.h>
35#include <field/GhostLayerField.h>
36#include <field/communication/PackInfo.h>
37#include <field/vtk/FlagFieldCellFilter.h>
38#include <field/vtk/VTKWriter.h>
39#include <stencil/D3Q19.h>
40#include <stencil/D3Q27.h>
41#if defined(__CUDACC__)
42#include <gpu/AddGPUFieldToStorage.h>
43#include <gpu/HostFieldAllocator.h>
44#include <gpu/communication/MemcpyPackInfo.h>
45#include <gpu/communication/UniformGPUScheme.h>
46#endif
47
48#include "../BoundaryHandling.hpp"
49#include "../BoundaryPackInfo.hpp"
50#include "../utils/boundary.hpp"
51#include "../utils/types_conversion.hpp"
53#include "ResetForce.hpp"
54#include "lb_kernels.hpp"
55#if defined(__CUDACC__)
56#include "lb_kernels.cuh"
57#endif
58
64
65#include <utils/Vector.hpp>
66#include <utils/index.hpp>
69
70#include <array>
71#include <bitset>
72#include <cmath>
73#include <cstddef>
74#include <functional>
75#include <initializer_list>
76#include <limits>
77#include <memory>
78#include <optional>
79#include <stdexcept>
80#include <string>
81#include <tuple>
82#include <type_traits>
83#include <utility>
84#include <variant>
85#include <vector>
86
87namespace walberla {
88
89/** @brief Class that runs and controls the LB on waLBerla. */
90template <typename FloatType, lbmpy::Arch Architecture>
92protected:
94 typename detail::KernelTrait<FloatType,
95 Architecture>::CollisionModelLeesEdwards;
97 typename detail::KernelTrait<FloatType,
98 Architecture>::CollisionModelThermalized;
105 typename detail::BoundaryHandlingTrait<
106 FloatType, Architecture>::DynamicUBB>;
108 std::variant<CollisionModelThermalized, CollisionModelLeesEdwards>;
109
110public:
111 /** @brief Stencil for collision and streaming operations. */
112 using Stencil = stencil::D3Q19;
113 /** @brief Stencil for ghost communication (includes domain corners). */
114 using StencilFull = stencil::D3Q27;
115 /** @brief Lattice model (e.g. blockforest). */
117
118protected:
119 template <typename FT, lbmpy::Arch AT = lbmpy::Arch::CPU> struct FieldTrait {
120 using PdfField = field::GhostLayerField<FT, Stencil::Size>;
121 using VectorField = field::GhostLayerField<FT, uint_t{3u}>;
122 template <class Field>
123 using PackInfo = field::communication::PackInfo<Field>;
128 template <class Stencil>
130 blockforest::communication::UniformBufferedScheme<Stencil>;
131 template <class Stencil>
133 blockforest::communication::UniformBufferedScheme<Stencil>;
134 };
135
136#if defined(__CUDACC__)
137 template <typename FT> struct FieldTrait<FT, lbmpy::Arch::GPU> {
138 private:
139 static auto constexpr AT = lbmpy::Arch::GPU;
140 template <class Field>
141 using MemcpyPackInfo = gpu::communication::MemcpyPackInfo<Field>;
142
143 public:
144 template <typename Stencil>
145 class UniformGPUScheme
146 : public gpu::communication::UniformGPUScheme<Stencil> {
147 public:
148 explicit UniformGPUScheme(auto const &bf)
149 : gpu::communication::UniformGPUScheme<Stencil>(
150 bf, /* sendDirectlyFromGPU */ false,
151 /* useLocalCommunication */ false) {}
152 };
153 using PdfField = gpu::GPUField<FT>;
154 using VectorField = gpu::GPUField<FT>;
155 template <class Field> using PackInfo = MemcpyPackInfo<Field>;
160 template <class Stencil>
161 using RegularCommScheme = UniformGPUScheme<Stencil>;
162 template <class Stencil>
163 using BoundaryCommScheme =
164 blockforest::communication::UniformBufferedScheme<Stencil>;
165 };
166#endif
167
168 // "underlying" field types (`GPUField` has no f-size info at compile time)
171
172public:
176#if defined(__CUDACC__)
177 using GPUField = gpu::GPUField<FloatType>;
178 using PdfFieldCpu =
180 using VectorFieldCpu =
182#endif
183
184 struct GhostComm {
185 /** @brief Ghost communication operations. */
186 enum GhostCommFlags : unsigned {
187 PDF, ///< PDFs communication
188 VEL, ///< velocities communication
189 LAF, ///< last applied forces communication
190 UBB, ///< boundaries communication
191 SIZE
192 };
193 };
194
195public:
196 template <typename T> FloatType FloatType_c(T t) const {
197 return numeric_cast<FloatType>(t);
198 }
199
200 [[nodiscard]] std::size_t stencil_size() const noexcept override {
201 return static_cast<std::size_t>(Stencil::Size);
202 }
203
204 [[nodiscard]] bool is_double_precision() const noexcept override {
205 return std::is_same_v<FloatType, double>;
206 }
207
208private:
209 class CollideSweepVisitor {
210 public:
211 using StructuredBlockStorage = LatticeWalberla::Lattice_T;
212
213 void operator()(CollisionModelThermalized &cm, IBlock *b) {
214 cm.configure(m_storage, b);
215 cm(b);
216 }
217
218 void operator()(CollisionModelLeesEdwards &cm, IBlock *b) {
219 cm.setV_s(static_cast<decltype(cm.getV_s())>(
220 m_lees_edwards_callbacks->get_shear_velocity()));
221 cm(b);
222 }
223
224 CollideSweepVisitor() = default;
225 CollideSweepVisitor(std::shared_ptr<StructuredBlockStorage> storage) {
226 m_storage = std::move(storage);
227 }
228 CollideSweepVisitor(std::shared_ptr<StructuredBlockStorage> storage,
229 std::shared_ptr<LeesEdwardsPack> callbacks) {
230 m_storage = std::move(storage);
231 m_lees_edwards_callbacks = std::move(callbacks);
232 }
233
234 private:
235 std::shared_ptr<StructuredBlockStorage> m_storage{};
236 std::shared_ptr<LeesEdwardsPack> m_lees_edwards_callbacks{};
237 };
238 CollideSweepVisitor m_run_collide_sweep{};
239
240 FloatType shear_mode_relaxation_rate() const {
241 return FloatType{2} / (FloatType{6} * m_viscosity + FloatType{1});
242 }
243
244 FloatType odd_mode_relaxation_rate(
245 FloatType shear_relaxation,
246 FloatType magic_number = FloatType{3} / FloatType{16}) const {
247 return (FloatType{4} - FloatType{2} * shear_relaxation) /
248 (FloatType{4} * magic_number * shear_relaxation + FloatType{2} -
249 shear_relaxation);
250 }
251
252 void reset_boundary_handling(std::shared_ptr<BlockStorage> const &blocks) {
253 m_boundary = std::make_shared<BoundaryModel>(blocks, m_pdf_field_id,
255 }
256
257 FloatType pressure_tensor_correction_factor() const {
258 return m_viscosity / (m_viscosity + FloatType{1} / FloatType{6});
259 }
260
261 void pressure_tensor_correction(Matrix3<FloatType> &tensor) const {
262 auto const revert_factor = pressure_tensor_correction_factor();
263 for (auto const i : {1u, 2u, 3u, 5u, 6u, 7u}) {
264 tensor[i] *= revert_factor;
265 }
266 }
267
268 void pressure_tensor_correction(std::span<FloatType, 9ul> tensor) const {
269 auto const revert_factor = pressure_tensor_correction_factor();
270 for (auto const i : {1u, 2u, 3u, 5u, 6u, 7u}) {
271 tensor[i] *= revert_factor;
272 }
273 }
274
275 class interpolation_illegal_access : public std::runtime_error {
276 public:
277 explicit interpolation_illegal_access(std::string const &field,
278 Utils::Vector3d const &pos,
279 std::array<int, 3> const &node,
280 double weight)
281 : std::runtime_error("Access to LB " + field + " field failed") {
282 std::cerr << "pos [" << pos << "], node [" << Utils::Vector3i(node)
283 << "], weight " << weight << "\n";
284 }
285 };
286
287 class vtk_runtime_error : public std::runtime_error {
288 public:
289 explicit vtk_runtime_error(std::string const &vtk_uid,
290 std::string const &reason)
291 : std::runtime_error("VTKOutput object '" + vtk_uid + "' " + reason) {}
292 };
293
294protected:
295 // Member variables
296 FloatType m_viscosity; /// kinematic viscosity
297 FloatType m_density;
298 FloatType m_kT;
299 unsigned int m_seed;
300
301 // Block data access handles
302 BlockDataID m_pdf_field_id;
304 BlockDataID m_flag_field_id;
305
308
311
312#if defined(__CUDACC__)
313 std::optional<BlockDataID> m_pdf_cpu_field_id;
314 std::optional<BlockDataID> m_vel_cpu_field_id;
315#endif
316
317 /** Flag for boundary cells. */
318 FlagUID const Boundary_flag{"boundary"};
319 bool m_has_boundaries{false};
320
321 /**
322 * @brief Full communicator.
323 * We use the D3Q27 directions to update cells along the diagonals during
324 * a full ghost communication. This is needed to properly update the corners
325 * of the ghost layer when setting cell velocities or populations.
326 */
328 typename FieldTrait<FloatType, Architecture>::template RegularCommScheme<
329 typename stencil::D3Q27>;
331 typename FieldTrait<FloatType, Architecture>::template BoundaryCommScheme<
332 typename stencil::D3Q27>;
333 /**
334 * @brief Regular communicator.
335 * We use the same directions as the stencil during integration.
336 */
338 typename FieldTrait<FloatType,
339 Architecture>::template RegularCommScheme<Stencil>;
340 template <class Field>
341 using PackInfo =
343
344 // communicators
345 std::shared_ptr<BoundaryFullCommunicator> m_boundary_communicator;
346 std::shared_ptr<RegularFullCommunicator> m_full_communicator;
347 std::shared_ptr<RegularFullCommunicator> m_pdf_communicator;
348 std::shared_ptr<RegularFullCommunicator> m_vel_communicator;
349 std::shared_ptr<RegularFullCommunicator> m_laf_communicator;
350 std::shared_ptr<PDFStreamingCommunicator> m_pdf_streaming_communicator;
351 std::bitset<GhostComm::SIZE> m_pending_ghost_comm;
352
353 // ResetForce sweep + external force handling
354 std::shared_ptr<ResetForce<PdfField, VectorField>> m_reset_force;
355
356 // Stream sweep
357 std::shared_ptr<StreamSweep> m_stream;
358
359 // Lees Edwards boundary interpolation
360 std::shared_ptr<LeesEdwardsPack> m_lees_edwards_callbacks;
361 std::shared_ptr<InterpolateAndShiftAtBoundary<_PdfField, FloatType>>
363 std::shared_ptr<InterpolateAndShiftAtBoundary<_VectorField, FloatType>>
365 std::shared_ptr<InterpolateAndShiftAtBoundary<_VectorField, FloatType>>
367
368 // Collision sweep
369 std::shared_ptr<CollisionModel> m_collision_model;
370
371 // boundaries
372 std::shared_ptr<BoundaryModel> m_boundary;
373
374 // lattice
375 std::shared_ptr<LatticeWalberla> m_lattice;
376
377#if defined(__CUDACC__)
378 std::shared_ptr<gpu::HostFieldAllocator<FloatType>> m_host_field_allocator;
379#endif
380
381 [[nodiscard]] std::optional<CellInterval>
382 get_interval(Utils::Vector3i const &lower_corner,
383 Utils::Vector3i const &upper_corner) const {
384 auto const &lattice = get_lattice();
385 auto const &cell_min = lower_corner;
386 auto const cell_max = upper_corner - Utils::Vector3i::broadcast(1);
387 auto const lower_bc = get_block_and_cell(lattice, cell_min, true);
388 auto const upper_bc = get_block_and_cell(lattice, cell_max, true);
389 if (not lower_bc or not upper_bc) {
390 return std::nullopt;
391 }
392
393 auto const block_extent =
394 get_min_corner(*upper_bc->block) - get_min_corner(*lower_bc->block);
395 auto const global_lower_cell = lower_bc->cell;
396 auto const global_upper_cell = upper_bc->cell + to_cell(block_extent);
397 return {CellInterval(global_lower_cell, global_upper_cell)};
398 }
399
400 // Interval within local block
401 [[nodiscard]] std::optional<CellInterval> get_block_interval(
402 Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner,
403 Utils::Vector3i const &block_offset, IBlock const &block) const {
404 auto block_lower_corner = m_lattice->get_block_corner(block, true);
405 if (not(upper_corner > block_lower_corner)) {
406 return std::nullopt;
407 }
408 for (uint_t f = 0u; f < 3u; ++f) {
409 block_lower_corner[f] = std::max(block_lower_corner[f], lower_corner[f]);
410 }
411 auto block_upper_corner = m_lattice->get_block_corner(block, false);
412 if (not(block_upper_corner > lower_corner)) {
413 return std::nullopt;
414 }
415 for (uint_t f = 0u; f < 3u; ++f) {
416 block_upper_corner[f] = std::min(block_upper_corner[f], upper_corner[f]);
417 }
418 block_upper_corner -= Utils::Vector3i::broadcast(1);
419 auto const block_lower_cell = to_cell(block_lower_corner - block_offset);
420 auto const block_upper_cell = to_cell(block_upper_corner - block_offset);
421 return {CellInterval(block_lower_cell, block_upper_cell)};
422 }
423
424 /**
425 * @brief Convenience function to add a field with a custom allocator.
426 *
427 * When vectorization is off, let waLBerla decide which memory allocator
428 * to use. When vectorization is on, the aligned memory allocator is
429 * required, otherwise <tt>cpu_vectorize_info["assume_aligned"]</tt> will
430 * trigger assertions. That is because for single-precision kernels the
431 * waLBerla heuristic in <tt>src/field/allocation/FieldAllocator.h</tt>
432 * will fall back to @c StdFieldAlloc, yet @c AllocateAligned is needed
433 * for intrinsics to work.
434 */
435 template <typename Field> auto add_to_storage(std::string const tag) {
436 auto const &blocks = m_lattice->get_blocks();
437 auto const n_ghost_layers = m_lattice->get_ghost_layers();
438 if constexpr (Architecture == lbmpy::Arch::CPU) {
439#ifdef ESPRESSO_BUILD_WITH_AVX_KERNELS
440#if defined(__AVX512F__)
441 constexpr uint_t alignment = 64u;
442#elif defined(__AVX__)
443 constexpr uint_t alignment = 32u;
444#elif defined(__SSE__)
445 constexpr uint_t alignment = 16u;
446#else
447#error "Unsupported arch, check walberla src/field/allocation/FieldAllocator.h"
448#endif
449 using value_type = typename Field::value_type;
450 using Allocator = field::AllocateAligned<value_type, alignment>;
451 auto const allocator = std::make_shared<Allocator>();
452 auto const empty_set = Set<SUID>::emptySet();
453 return field::addToStorage<Field>(
454 blocks, tag, field::internal::defaultSize, FloatType{0}, field::fzyx,
455 n_ghost_layers, false, {}, empty_set, empty_set, allocator);
456#else // ESPRESSO_BUILD_WITH_AVX_KERNELS
457 return field::addToStorage<Field>(blocks, tag, FloatType{0}, field::fzyx,
458 n_ghost_layers);
459#endif // ESPRESSO_BUILD_WITH_AVX_KERNELS
460 }
461#if defined(__CUDACC__)
462 else {
463 auto field_id = gpu::addGPUFieldToStorage<GPUField>(
464 blocks, tag, Field::F_SIZE, field::fzyx, n_ghost_layers);
465 if constexpr (std::is_same_v<Field, _VectorField>) {
466 for (auto block = blocks->begin(); block != blocks->end(); ++block) {
467 auto field = block->template getData<GPUField>(field_id);
468 lbm::accessor::Vector::initialize(field, Vector3<FloatType>{0});
469 }
470 } else if constexpr (std::is_same_v<Field, _PdfField>) {
471 for (auto block = blocks->begin(); block != blocks->end(); ++block) {
472 auto field = block->template getData<GPUField>(field_id);
474 field, std::array<FloatType, Stencil::Size>{});
475 }
476 }
477 return field_id;
478 }
479#endif
480 }
481
483 auto const setup = [this]<typename PackInfoPdf, typename PackInfoVec>() {
484 auto const &blocks = m_lattice->get_blocks();
486 std::make_shared<PDFStreamingCommunicator>(blocks);
487 m_pdf_streaming_communicator->addPackInfo(
488 std::make_shared<PackInfoPdf>(m_pdf_field_id));
489 m_pdf_streaming_communicator->addPackInfo(
490 std::make_shared<PackInfoVec>(m_last_applied_force_field_id));
491 };
493 using PackInfoPdf = typename FieldTrait::PackInfoStreamingPdf;
494 using PackInfoVec = typename FieldTrait::PackInfoStreamingVec;
495 if (m_has_boundaries or (m_collision_model and has_lees_edwards_bc())) {
496 setup.template operator()<PackInfo<PdfField>, PackInfoVec>();
497 } else {
498 setup.template operator()<PackInfoPdf, PackInfoVec>();
499 }
500 }
501
502public:
503 LBWalberlaImpl(std::shared_ptr<LatticeWalberla> lattice, double viscosity,
504 double density)
506 m_kT(FloatType{0}), m_seed(0u), m_lattice(std::move(lattice)) {
507
508 auto const &blocks = m_lattice->get_blocks();
509 auto const n_ghost_layers = m_lattice->get_ghost_layers();
510 if (n_ghost_layers == 0u)
511 throw std::runtime_error("At least one ghost layer must be used");
512
513 // Initialize and register fields (must use the "underlying" types)
514 m_pdf_field_id = add_to_storage<_PdfField>("pdfs");
515 m_pdf_tmp_field_id = add_to_storage<_PdfField>("pdfs_tmp");
516 m_last_applied_force_field_id = add_to_storage<_VectorField>("force last");
517 m_force_to_be_applied_id = add_to_storage<_VectorField>("force next");
518 m_velocity_field_id = add_to_storage<_VectorField>("velocity");
519 m_vel_tmp_field_id = add_to_storage<_VectorField>("velocity_tmp");
520#if defined(__CUDACC__)
521 m_host_field_allocator =
522 std::make_shared<gpu::HostFieldAllocator<FloatType>>();
523#endif
524
525 // Initialize and register pdf field
526 auto pdf_setter =
529 for (auto &block : *blocks) {
530 pdf_setter(&block);
531 }
532
533 // Initialize and register flag field (fluid/boundary)
534 m_flag_field_id = field::addFlagFieldToStorage<FlagField>(
535 blocks, "flag field", n_ghost_layers);
536 // Initialize boundary sweep
537 reset_boundary_handling(m_lattice->get_blocks());
538
539 // Set up the communication and register fields
541
542 m_full_communicator = std::make_shared<RegularFullCommunicator>(blocks);
543 m_full_communicator->addPackInfo(
544 std::make_shared<PackInfo<PdfField>>(m_pdf_field_id));
545 m_full_communicator->addPackInfo(
547 m_full_communicator->addPackInfo(
548 std::make_shared<PackInfo<VectorField>>(m_velocity_field_id));
549
550 m_pdf_communicator = std::make_shared<RegularFullCommunicator>(blocks);
551 m_vel_communicator = std::make_shared<RegularFullCommunicator>(blocks);
552 m_laf_communicator = std::make_shared<RegularFullCommunicator>(blocks);
553 m_pdf_communicator->addPackInfo(
554 std::make_shared<PackInfo<PdfField>>(m_pdf_field_id));
555 m_vel_communicator->addPackInfo(
556 std::make_shared<PackInfo<VectorField>>(m_velocity_field_id));
557 m_laf_communicator->addPackInfo(
559
561 std::make_shared<BoundaryFullCommunicator>(blocks);
562 m_boundary_communicator->addPackInfo(
563 std::make_shared<field::communication::PackInfo<FlagField>>(
565 auto boundary_packinfo = std::make_shared<
568 boundary_packinfo->setup_boundary_handle(m_lattice, m_boundary);
569 m_boundary_communicator->addPackInfo(boundary_packinfo);
570
572
573 // Instantiate the sweep responsible for force double buffering and
574 // external forces
575 m_reset_force = std::make_shared<ResetForce<PdfField, VectorField>>(
577
578 // Prepare LB sweeps
579 // Note: For now, combined collide-stream sweeps cannot be used,
580 // because the collide-push variant is not supported by lbmpy.
581 // The following functors are individual in-place collide and stream steps
582 m_stream = std::make_shared<StreamSweep>(
584 }
585
586private:
587 void integrate_stream(std::shared_ptr<BlockStorage> const &blocks) {
588 for (auto &block : *blocks)
589 (*m_stream)(&block);
590 }
591
592 void integrate_collide(std::shared_ptr<BlockStorage> const &blocks) {
593 auto &cm_variant = *m_collision_model;
594 for (auto &block : *blocks) {
595 auto const block_variant = std::variant<IBlock *>(&block);
596 std::visit(m_run_collide_sweep, cm_variant, block_variant);
597 }
598 if (auto *cm = std::get_if<CollisionModelThermalized>(&cm_variant)) {
599 cm->setTime_step(cm->getTime_step() + 1u);
600 }
601 }
602
603 auto has_lees_edwards_bc() const {
604 return std::holds_alternative<CollisionModelLeesEdwards>(
606 }
607
608 void apply_lees_edwards_pdf_interpolation(
609 std::shared_ptr<BlockStorage> const &blocks) {
610 for (auto &block : *blocks)
612 }
613
614 void apply_lees_edwards_vel_interpolation_and_shift(
615 std::shared_ptr<BlockStorage> const &blocks) {
616 for (auto &block : *blocks)
618 }
619
620 void apply_lees_edwards_last_applied_force_interpolation(
621 std::shared_ptr<BlockStorage> const &blocks) {
622 for (auto &block : *blocks)
624 }
625
626 void integrate_reset_force(std::shared_ptr<BlockStorage> const &blocks) {
627 for (auto &block : *blocks)
628 (*m_reset_force)(&block);
629 }
630
631 void integrate_boundaries(std::shared_ptr<BlockStorage> const &blocks) {
632 for (auto &block : *blocks)
633 (*m_boundary)(&block);
634 }
635
636 void integrate_push_scheme() {
637 auto const &blocks = get_lattice().get_blocks();
638 // Reset force fields
639 integrate_reset_force(blocks);
640 // LB collide
641 integrate_collide(blocks);
642 m_pdf_streaming_communicator->communicate();
643 // Handle boundaries
644 if (m_has_boundaries) {
645 integrate_boundaries(blocks);
646 }
647 // LB stream
648 integrate_stream(blocks);
649 // Mark pending ghost layer updates
653 // Refresh ghost layers
655 }
656
657 void integrate_pull_scheme() {
658 auto const &blocks = get_lattice().get_blocks();
659 // Handle boundaries
660 if (m_has_boundaries) {
661 integrate_boundaries(blocks);
662 }
663 // LB stream
664 integrate_stream(blocks);
665 // LB collide
666 integrate_collide(blocks);
667 // Reset force fields
668 integrate_reset_force(blocks);
669 // Mark pending ghost layer updates
673 // Refresh ghost layers
675 }
676
677protected:
678 void integrate_vtk_writers() override {
679 for (auto const &it : m_vtk_auto) {
680 auto &vtk_handle = it.second;
681 if (vtk_handle->enabled) {
682 vtk::writeFiles(vtk_handle->ptr)();
683 vtk_handle->execution_count++;
684 }
685 }
686 }
687
688public:
689 void integrate() override {
690 if (has_lees_edwards_bc()) {
691 integrate_pull_scheme();
692 } else {
693 integrate_push_scheme();
694 }
695 // Handle VTK writers
697 }
698
699 void ghost_communication() override {
700 if (m_pending_ghost_comm.any()) {
703 }
704 }
705
706 void ghost_communication_pdf() override {
708 m_pdf_communicator->communicate();
709 if (has_lees_edwards_bc()) {
710 auto const &blocks = get_lattice().get_blocks();
711 apply_lees_edwards_pdf_interpolation(blocks);
712 }
714 }
715 }
716
717 void ghost_communication_vel() override {
719 m_vel_communicator->communicate();
720 if (has_lees_edwards_bc()) {
721 auto const &blocks = get_lattice().get_blocks();
722 apply_lees_edwards_vel_interpolation_and_shift(blocks);
723 }
725 }
726 }
727
728 void ghost_communication_laf() override {
730 m_laf_communicator->communicate();
731 if (has_lees_edwards_bc()) {
732 auto const &blocks = get_lattice().get_blocks();
733 apply_lees_edwards_last_applied_force_interpolation(blocks);
734 }
736 }
737 }
738
745
747 m_full_communicator->communicate();
748 if (has_lees_edwards_bc()) {
749 auto const &blocks = get_lattice().get_blocks();
750 apply_lees_edwards_pdf_interpolation(blocks);
751 apply_lees_edwards_vel_interpolation_and_shift(blocks);
752 apply_lees_edwards_last_applied_force_interpolation(blocks);
753 }
757 }
758
760 if (has_lees_edwards_bc()) {
761 m_full_communicator->communicate();
762 auto const &blocks = get_lattice().get_blocks();
763 apply_lees_edwards_pdf_interpolation(blocks);
764 apply_lees_edwards_vel_interpolation_and_shift(blocks);
765 apply_lees_edwards_last_applied_force_interpolation(blocks);
769 }
770 }
771
772 void set_collision_model(double kT, unsigned int seed) override {
773 auto const omega = shear_mode_relaxation_rate();
774 auto const omega_odd = odd_mode_relaxation_rate(omega);
775 auto const blocks = get_lattice().get_blocks();
776 m_kT = FloatType_c(kT);
777 m_seed = seed;
779 m_pdf_field_id, m_kT, omega, omega,
780 omega_odd, omega, seed, uint32_t{0u});
781 m_collision_model = std::make_shared<CollisionModel>(std::move(obj));
782 m_run_collide_sweep = CollideSweepVisitor(blocks);
784 }
785
787 std::unique_ptr<LeesEdwardsPack> &&lees_edwards_pack) override {
788 assert(m_kT == 0.);
789#if defined(__CUDACC__)
790 if constexpr (Architecture == lbmpy::Arch::GPU) {
791 throw std::runtime_error("Lees-Edwards LB doesn't support GPU yet");
792 }
793#endif
794 auto const shear_direction = lees_edwards_pack->shear_direction;
795 auto const shear_plane_normal = lees_edwards_pack->shear_plane_normal;
796 auto const shear_vel = FloatType_c(lees_edwards_pack->get_shear_velocity());
797 auto const omega = shear_mode_relaxation_rate();
798 if (shear_plane_normal != 1u) {
799 throw std::domain_error(
800 "Lees-Edwards LB only supports shear_plane_normal=\"y\"");
801 }
802 auto const &lattice = get_lattice();
803 auto const n_ghost_layers = lattice.get_ghost_layers();
804 auto const blocks = lattice.get_blocks();
805 if (lattice.get_node_grid()[shear_direction] != 1 or
806 lattice.get_node_grid()[shear_plane_normal] != 1 or
807 blocks->getSize(shear_direction) != 1ul or
808 blocks->getSize(shear_plane_normal) != 1ul) {
809 throw std::domain_error("LB LEbc doesn't support domain decomposition "
810 "along the shear and normal directions.");
811 }
812 auto const agrid =
813 FloatType_c(lattice.get_grid_dimensions()[shear_plane_normal]);
814 auto obj = CollisionModelLeesEdwards(
815 m_last_applied_force_field_id, m_pdf_field_id, agrid, omega, shear_vel);
816 m_collision_model = std::make_shared<CollisionModel>(std::move(obj));
817 m_lees_edwards_callbacks = std::move(lees_edwards_pack);
818 m_run_collide_sweep = CollideSweepVisitor(blocks, m_lees_edwards_callbacks);
820 std::make_shared<InterpolateAndShiftAtBoundary<_PdfField, FloatType>>(
821 blocks, m_pdf_field_id, m_pdf_tmp_field_id, n_ghost_layers,
822 shear_direction, shear_plane_normal,
823 m_lees_edwards_callbacks->get_pos_offset);
824 m_lees_edwards_vel_interpol_sweep = std::make_shared<
826 blocks, m_velocity_field_id, m_vel_tmp_field_id, n_ghost_layers,
827 shear_direction, shear_plane_normal,
828 m_lees_edwards_callbacks->get_pos_offset,
829 m_lees_edwards_callbacks->get_shear_velocity);
833 n_ghost_layers, shear_direction, shear_plane_normal,
834 m_lees_edwards_callbacks->get_pos_offset);
836 }
837
838 void check_lebc(unsigned int shear_direction,
839 unsigned int shear_plane_normal) const override {
841 if (m_lees_edwards_callbacks->shear_direction != shear_direction or
842 m_lees_edwards_callbacks->shear_plane_normal != shear_plane_normal) {
843 throw std::runtime_error(
844 "MD and LB Lees-Edwards boundary conditions disagree");
845 }
846 }
847 }
848
849 void set_viscosity(double viscosity) override {
850 m_viscosity = FloatType_c(viscosity);
851 }
852
853 [[nodiscard]] double get_viscosity() const noexcept override {
854 return numeric_cast<double>(m_viscosity);
855 }
856
857 [[nodiscard]] double get_density() const noexcept override {
858 return numeric_cast<double>(m_density);
859 }
860
861 // Velocity
862 std::optional<Utils::Vector3d>
864 bool consider_ghosts = false) const override {
865 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::VEL)));
866 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::UBB)));
867 if (m_has_boundaries) {
868 auto const is_boundary = get_node_is_boundary(node, consider_ghosts);
869 if (is_boundary and *is_boundary) {
870 return get_node_velocity_at_boundary(node, consider_ghosts);
871 }
872 }
873 auto const bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
874 if (!bc)
875 return std::nullopt;
876
877 auto field = bc->block->template uncheckedFastGetData<VectorField>(
879 auto const vec = lbm::accessor::Vector::get(field, bc->cell);
880 return to_vector3d(vec);
881 }
882
884 Utils::Vector3d const &v) override {
887 auto bc = get_block_and_cell(get_lattice(), node, false);
888 if (!bc)
889 return false;
890
891 // We have to set both, the pdf and the stored velocity field
892 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
893 auto vel_field =
894 bc->block->template getData<VectorField>(m_velocity_field_id);
895 auto force_field =
896 bc->block->template getData<VectorField>(m_last_applied_force_field_id);
897 auto const vel = to_vector3<FloatType>(v);
898 lbm::accessor::Velocity::set(pdf_field, vel_field, force_field, vel,
899 bc->cell);
900
901 return true;
902 }
903
904 /**
905 * @brief Synchronize data between a sliced block and a container.
906 *
907 * Synchronize data between two data buffers representing sliced matrices
908 * with different memory layouts. The kernel takes as argument an index
909 * for the flattened data buffer containing the serialized block slice,
910 * an index for the flattened I/O buffer, and a block-local node position.
911 *
912 * @param bci Cell interval of the local block within a 3D slice
913 * @param ci Cell interval of the entire lattice within a 3D slice
914 * @param block_offset Origin of the local block
915 * @param lower_corner Lower corner of the 3D slice
916 * @param kernel Function to execute on the two data buffers
917 */
918 template <typename Kernel>
919 void copy_block_buffer(CellInterval const &bci, CellInterval const &ci,
920 Utils::Vector3i const &block_offset,
921 Utils::Vector3i const &lower_corner,
922 Kernel &&kernel) const {
923 auto const local_grid = to_vector3i(ci.max() - ci.min() + Cell(1, 1, 1));
924 auto const block_grid = to_vector3i(bci.max() - bci.min() + Cell(1, 1, 1));
925 auto const lower_cell = bci.min();
926 auto const upper_cell = bci.max();
927 // In the loop, x,y,z are in block coordinates
928 // The field data given in the argument knows about BlockForest
929 // lattice indices from lower_corner to upper_corner. It is converted
930 // to block coordinates
931 for (auto x = lower_cell.x(), i = 0; x <= upper_cell.x(); ++x, ++i) {
932 for (auto y = lower_cell.y(), j = 0; y <= upper_cell.y(); ++y, ++j) {
933 for (auto z = lower_cell.z(), k = 0; z <= upper_cell.z(); ++z, ++k) {
934 auto const node = block_offset + Utils::Vector3i{{x, y, z}};
935 auto const local_index = Utils::get_linear_index(
936 node - lower_corner, local_grid, Utils::MemoryOrder::ROW_MAJOR);
937 auto const block_index = Utils::get_linear_index(
938 i, j, k, block_grid, Utils::MemoryOrder::ROW_MAJOR);
939 kernel(static_cast<unsigned>(block_index),
940 static_cast<unsigned>(local_index), node);
941 }
942 }
943 }
944 }
945
946 std::vector<double>
948 Utils::Vector3i const &upper_corner) const override {
949 std::vector<double> out;
950 uint_t values_size = 0;
951 if (auto const ci = get_interval(lower_corner, upper_corner)) {
952 out = std::vector<double>(3u * ci->numCells());
953 auto const &lattice = get_lattice();
954 for (auto &block : *lattice.get_blocks()) {
955 auto const block_offset = lattice.get_block_corner(block, true);
956 if (auto const bci = get_block_interval(lower_corner, upper_corner,
957 block_offset, block)) {
958 auto const field =
959 block.template getData<VectorField>(m_velocity_field_id);
960 auto const values = lbm::accessor::Vector::get(field, *bci);
961 assert(values.size() == 3u * bci->numCells());
962 values_size += 3u * bci->numCells();
963
964 auto kernel = [&values, &out, this](unsigned const block_index,
965 unsigned const local_index,
966 Utils::Vector3i const &node) {
967 if (m_boundary->node_is_boundary(node)) {
968 auto const &vec = m_boundary->get_node_value_at_boundary(node);
969 for (uint_t f = 0u; f < 3u; ++f) {
970 out[3u * local_index + f] = double_c(vec[f]);
971 }
972 } else {
973 for (uint_t f = 0u; f < 3u; ++f) {
974 out[3u * local_index + f] =
975 double_c(values[3u * block_index + f]);
976 }
977 }
978 };
979
980 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
981 }
982 }
983 assert(values_size == 3u * ci->numCells());
984 }
985 return out;
986 }
987
988 void set_slice_velocity(Utils::Vector3i const &lower_corner,
989 Utils::Vector3i const &upper_corner,
990 std::vector<double> const &velocity) override {
993 if (auto const ci = get_interval(lower_corner, upper_corner)) {
994 assert(velocity.size() == 3u * ci->numCells());
995 auto const &lattice = get_lattice();
996 for (auto &block : *lattice.get_blocks()) {
997 auto const block_offset = lattice.get_block_corner(block, true);
998 if (auto const bci = get_block_interval(lower_corner, upper_corner,
999 block_offset, block)) {
1000 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1001 auto force_field = block.template getData<VectorField>(
1003 auto vel_field =
1004 block.template getData<VectorField>(m_velocity_field_id);
1005 std::vector<FloatType> values(3u * bci->numCells());
1006
1007 auto kernel = [&values, &velocity](unsigned const block_index,
1008 unsigned const local_index,
1009 Utils::Vector3i const &node) {
1010 for (uint_t f = 0u; f < 3u; ++f) {
1011 values[3u * block_index + f] =
1012 numeric_cast<FloatType>(velocity[3u * local_index + f]);
1013 }
1014 };
1015
1016 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1017 lbm::accessor::Velocity::set(pdf_field, vel_field, force_field,
1018 values, *bci);
1019 }
1020 }
1021 }
1022 }
1023
1024 [[nodiscard]] bool is_gpu() const noexcept override {
1025 return Architecture == lbmpy::Arch::GPU;
1026 }
1027
1028 void add_forces_at_pos(std::vector<Utils::Vector3d> const &pos,
1029 std::vector<Utils::Vector3d> const &forces) override {
1030 assert(pos.size() == forces.size());
1031 if (pos.empty()) {
1032 return;
1033 }
1034 if constexpr (Architecture == lbmpy::Arch::CPU) {
1035 for (std::size_t i = 0ul; i < pos.size(); ++i) {
1036 add_force_at_pos(pos[i], forces[i]);
1037 }
1038 }
1039#if defined(__CUDACC__)
1040 if constexpr (Architecture == lbmpy::Arch::GPU) {
1041 auto const &lattice = get_lattice();
1042 auto const &block = *(lattice.get_blocks()->begin());
1043 auto const origin = block.getAABB().min();
1044 std::vector<FloatType> host_pos;
1045 std::vector<FloatType> host_force;
1046 host_pos.reserve(3ul * pos.size());
1047 host_force.reserve(3ul * forces.size());
1048 assert(lattice.get_blocks()->getNumberOfBlocks() == 1u);
1049 for (auto const &vec : pos) {
1050#pragma unroll
1051 for (std::size_t i : {0ul, 1ul, 2ul}) {
1052 host_pos.emplace_back(static_cast<FloatType>(vec[i] - origin[i]));
1053 }
1054 }
1055 for (auto const &vec : forces) {
1056#pragma unroll
1057 for (std::size_t i : {0ul, 1ul, 2ul}) {
1058 host_force.emplace_back(static_cast<FloatType>(vec[i]));
1059 }
1060 }
1061 auto const gl = lattice.get_ghost_layers();
1062 auto field = block.template uncheckedFastGetData<VectorField>(
1064 lbm::accessor::Interpolation::set(field, host_pos, host_force, gl);
1065 }
1066#endif
1067 }
1068
1069 std::vector<Utils::Vector3d>
1070 get_velocities_at_pos(std::vector<Utils::Vector3d> const &pos) override {
1071 if (pos.empty()) {
1072 return {};
1073 }
1074 std::vector<Utils::Vector3d> vel{};
1075 if constexpr (Architecture == lbmpy::Arch::CPU) {
1076 vel.reserve(pos.size());
1077 for (auto const &vec : pos) {
1078 auto res = get_velocity_at_pos(vec, true);
1079 assert(res.has_value());
1080 vel.emplace_back(*res);
1081 }
1082 }
1083#if defined(__CUDACC__)
1084 if constexpr (Architecture == lbmpy::Arch::GPU) {
1085 auto const &lattice = get_lattice();
1086 auto const &block = *(lattice.get_blocks()->begin());
1087 auto const origin = block.getAABB().min();
1088 std::vector<FloatType> host_pos;
1089 host_pos.reserve(3ul * pos.size());
1090 assert(lattice.get_blocks()->getNumberOfBlocks() == 1u);
1091 for (auto const &vec : pos) {
1092#pragma unroll
1093 for (std::size_t i : {0ul, 1ul, 2ul}) {
1094 host_pos.emplace_back(static_cast<FloatType>(vec[i] - origin[i]));
1095 }
1096 }
1097 auto const gl = lattice.get_ghost_layers();
1098 auto field =
1099 block.template uncheckedFastGetData<VectorField>(m_velocity_field_id);
1100 auto const res = lbm::accessor::Interpolation::get(field, host_pos, gl);
1101 vel.reserve(res.size() / 3ul);
1102 for (auto it = res.begin(); it != res.end(); it += 3) {
1103 vel.emplace_back(Utils::Vector3d{static_cast<double>(*(it + 0)),
1104 static_cast<double>(*(it + 1)),
1105 static_cast<double>(*(it + 2))});
1106 }
1107 }
1108#endif
1109 return vel;
1110 }
1111
1112 std::optional<Utils::Vector3d>
1114 bool consider_points_in_halo = false) const override {
1115 assert(not m_pending_ghost_comm.test(GhostComm::VEL));
1116 assert(not m_pending_ghost_comm.test(GhostComm::UBB));
1117 if (!consider_points_in_halo and !m_lattice->pos_in_local_domain(pos))
1118 return std::nullopt;
1119 if (consider_points_in_halo and !m_lattice->pos_in_local_halo(pos))
1120 return std::nullopt;
1121 Utils::Vector3d v{0., 0., 0.};
1123 pos, [this, &v, &pos](std::array<int, 3> const node, double weight) {
1124 // Nodes with zero weight might not be accessible, because they can be
1125 // outside ghost layers
1126 if (weight != 0.) {
1127 auto const res = get_node_velocity(Utils::Vector3i(node), true);
1128 if (!res) {
1129 throw interpolation_illegal_access("velocity", pos, node, weight);
1130 }
1131 v += *res * weight;
1132 }
1133 });
1134 return {std::move(v)};
1135 }
1136
1137 std::optional<double>
1139 bool consider_points_in_halo = false) const override {
1140 assert(not m_pending_ghost_comm.test(GhostComm::PDF));
1141 if (!consider_points_in_halo and !m_lattice->pos_in_local_domain(pos))
1142 return std::nullopt;
1143 if (consider_points_in_halo and !m_lattice->pos_in_local_halo(pos))
1144 return std::nullopt;
1145 double dens = 0.;
1147 pos, [this, &dens, &pos](std::array<int, 3> const node, double weight) {
1148 // Nodes with zero weight might not be accessible, because they can be
1149 // outside ghost layers
1150 if (weight != 0.) {
1151 auto const res = get_node_density(Utils::Vector3i(node), true);
1152 if (!res) {
1153 throw interpolation_illegal_access("density", pos, node, weight);
1154 }
1155 dens += *res * weight;
1156 }
1157 });
1158 return {std::move(dens)};
1159 }
1160
1161 // Local force
1163 Utils::Vector3d const &force) override {
1164 if (!m_lattice->pos_in_local_halo(pos))
1165 return false;
1166 auto const force_at_node = [this, &force](std::array<int, 3> const node,
1167 double weight) {
1168 auto bc = get_block_and_cell(get_lattice(), Utils::Vector3i(node), false);
1169 if (!bc) {
1170 bc = get_block_and_cell(get_lattice(), Utils::Vector3i(node), true);
1171 }
1172
1173 if (bc) {
1174 auto const weighted_force = to_vector3<FloatType>(weight * force);
1175 auto force_field =
1176 bc->block->template uncheckedFastGetData<VectorField>(
1178 lbm::accessor::Vector::add(force_field, weighted_force, bc->cell);
1179 }
1180 };
1181 interpolate_bspline_at_pos(pos, force_at_node);
1182 return true;
1183 }
1184
1185 std::optional<Utils::Vector3d>
1186 get_node_force_to_be_applied(Utils::Vector3i const &node) const override {
1187 auto const bc = get_block_and_cell(get_lattice(), node, true);
1188 if (!bc)
1189 return std::nullopt;
1190
1191 auto field =
1192 bc->block->template getData<VectorField>(m_force_to_be_applied_id);
1193 auto const vec = lbm::accessor::Vector::get(field, bc->cell);
1194 return to_vector3d(vec);
1195 }
1196
1197 std::optional<Utils::Vector3d>
1199 bool consider_ghosts = false) const override {
1200 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::LAF)));
1201 auto const bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1202 if (!bc)
1203 return std::nullopt;
1204
1205 auto const field =
1206 bc->block->template getData<VectorField>(m_last_applied_force_field_id);
1207 auto const vec = lbm::accessor::Vector::get(field, bc->cell);
1208 return to_vector3d(vec);
1209 }
1210
1212 Utils::Vector3d const &force) override {
1215 auto bc = get_block_and_cell(get_lattice(), node, false);
1216 if (!bc)
1217 return false;
1218
1219 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1220 auto force_field =
1221 bc->block->template getData<VectorField>(m_last_applied_force_field_id);
1222 auto vel_field =
1223 bc->block->template getData<VectorField>(m_velocity_field_id);
1224 auto const vec = to_vector3<FloatType>(force);
1225 lbm::accessor::Force::set(pdf_field, vel_field, force_field, vec, bc->cell);
1226
1227 return true;
1228 }
1229
1230 std::vector<double> get_slice_last_applied_force(
1231 Utils::Vector3i const &lower_corner,
1232 Utils::Vector3i const &upper_corner) const override {
1233 std::vector<double> out;
1234 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1235 out = std::vector<double>(3u * ci->numCells());
1236 auto const &lattice = get_lattice();
1237 for (auto const &block : *lattice.get_blocks()) {
1238 auto const block_offset = lattice.get_block_corner(block, true);
1239 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1240 block_offset, block)) {
1241 auto const field = block.template getData<VectorField>(
1243 auto const values = lbm::accessor::Vector::get(field, *bci);
1244 assert(values.size() == 3u * bci->numCells());
1245
1246 auto kernel = [&values, &out](unsigned const block_index,
1247 unsigned const local_index,
1248 Utils::Vector3i const &node) {
1249 for (uint_t f = 0u; f < 3u; ++f) {
1250 out[3u * local_index + f] = values[3u * block_index + f];
1251 }
1252 };
1253
1254 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1255 }
1256 }
1257 }
1258 return out;
1259 }
1260
1262 Utils::Vector3i const &upper_corner,
1263 std::vector<double> const &force) override {
1266 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1267 assert(force.size() == 3u * ci->numCells());
1268 auto const &lattice = get_lattice();
1269 for (auto &block : *lattice.get_blocks()) {
1270 auto const block_offset = lattice.get_block_corner(block, true);
1271 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1272 block_offset, block)) {
1273 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1274 auto force_field = block.template getData<VectorField>(
1276 auto vel_field =
1277 block.template getData<VectorField>(m_velocity_field_id);
1278 std::vector<FloatType> values(3u * bci->numCells());
1279
1280 auto kernel = [&values, &force](unsigned const block_index,
1281 unsigned const local_index,
1282 Utils::Vector3i const &node) {
1283 for (uint_t f = 0u; f < 3u; ++f) {
1284 values[3u * block_index + f] =
1285 numeric_cast<FloatType>(force[3u * local_index + f]);
1286 }
1287 };
1288
1289 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1290 lbm::accessor::Force::set(pdf_field, vel_field, force_field, values,
1291 *bci);
1292 }
1293 }
1294 }
1295 }
1296
1297 // Population
1298 std::optional<std::vector<double>>
1300 bool consider_ghosts = false) const override {
1301 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::PDF)));
1302 auto bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1303 if (!bc)
1304 return std::nullopt;
1305
1306 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1307 auto const pop = lbm::accessor::Population::get(pdf_field, bc->cell);
1308 std::vector<double> population(Stencil::Size);
1309 for (uint_t f = 0u; f < Stencil::Size; ++f) {
1310 population[f] = double_c(pop[f]);
1311 }
1312
1313 return {std::move(population)};
1314 }
1315
1317 std::vector<double> const &population) override {
1320 auto bc = get_block_and_cell(get_lattice(), node, false);
1321 if (!bc)
1322 return false;
1323
1324 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1325 auto force_field =
1326 bc->block->template getData<VectorField>(m_last_applied_force_field_id);
1327 auto vel_field =
1328 bc->block->template getData<VectorField>(m_velocity_field_id);
1329 std::array<FloatType, Stencil::Size> pop;
1330 for (uint_t f = 0u; f < Stencil::Size; ++f) {
1331 pop[f] = FloatType_c(population[f]);
1332 }
1333 lbm::accessor::Population::set(pdf_field, vel_field, force_field, pop,
1334 bc->cell);
1335
1336 return true;
1337 }
1338
1339 std::vector<double>
1341 Utils::Vector3i const &upper_corner) const override {
1342 std::vector<double> out;
1343 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1344 out = std::vector<double>(stencil_size() * ci->numCells());
1345 auto const &lattice = get_lattice();
1346 for (auto const &block : *lattice.get_blocks()) {
1347 auto const block_offset = lattice.get_block_corner(block, true);
1348 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1349 block_offset, block)) {
1350 auto const pdf_field =
1351 block.template getData<PdfField>(m_pdf_field_id);
1352 auto const values = lbm::accessor::Population::get(pdf_field, *bci);
1353 assert(values.size() == stencil_size() * bci->numCells());
1354
1355 auto kernel = [&values, &out, this](unsigned const block_index,
1356 unsigned const local_index,
1357 Utils::Vector3i const &node) {
1358 for (uint_t f = 0u; f < stencil_size(); ++f) {
1359 out[stencil_size() * local_index + f] =
1360 values[stencil_size() * block_index + f];
1361 }
1362 };
1363
1364 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1365 }
1366 }
1367 }
1368 return out;
1369 }
1370
1371 void set_slice_population(Utils::Vector3i const &lower_corner,
1372 Utils::Vector3i const &upper_corner,
1373 std::vector<double> const &population) override {
1374 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1375 assert(population.size() == stencil_size() * ci->numCells());
1376 auto const &lattice = get_lattice();
1377 for (auto &block : *lattice.get_blocks()) {
1378 auto const block_offset = lattice.get_block_corner(block, true);
1379 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1380 block_offset, block)) {
1381 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1382 auto force_field = block.template getData<VectorField>(
1384 auto vel_field =
1385 block.template getData<VectorField>(m_velocity_field_id);
1386 std::vector<FloatType> values(stencil_size() * bci->numCells());
1387
1388 auto kernel = [&values, &population,
1389 this](unsigned const block_index,
1390 unsigned const local_index,
1391 Utils::Vector3i const &node) {
1392 for (uint_t f = 0u; f < stencil_size(); ++f) {
1393 values[stencil_size() * block_index + f] =
1394 numeric_cast<FloatType>(
1395 population[stencil_size() * local_index + f]);
1396 }
1397 };
1398
1399 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1400 lbm::accessor::Population::set(pdf_field, vel_field, force_field,
1401 values, *bci);
1402 }
1403 }
1404 }
1405 }
1406
1407 // Density
1408 std::optional<double>
1410 bool consider_ghosts = false) const override {
1411 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::PDF)));
1412 auto bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1413 if (!bc)
1414 return std::nullopt;
1415
1416 auto pdf_field =
1417 bc->block->template uncheckedFastGetData<PdfField>(m_pdf_field_id);
1418 auto const density = lbm::accessor::Density::get(pdf_field, bc->cell);
1419 return {double_c(density)};
1420 }
1421
1422 bool set_node_density(Utils::Vector3i const &node, double density) override {
1424 auto bc = get_block_and_cell(get_lattice(), node, false);
1425 if (!bc)
1426 return false;
1427
1428 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1429 lbm::accessor::Density::set(pdf_field, FloatType_c(density), bc->cell);
1430
1431 return true;
1432 }
1433
1434 std::vector<double>
1436 Utils::Vector3i const &upper_corner) const override {
1437 std::vector<double> out;
1438 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1439 out = std::vector<double>(ci->numCells());
1440 auto const &lattice = get_lattice();
1441 for (auto const &block : *lattice.get_blocks()) {
1442 auto const block_offset = lattice.get_block_corner(block, true);
1443 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1444 block_offset, block)) {
1445 auto const pdf_field =
1446 block.template getData<PdfField>(m_pdf_field_id);
1447 auto const values = lbm::accessor::Density::get(pdf_field, *bci);
1448 assert(values.size() == bci->numCells());
1449
1450 auto kernel = [&values, &out](unsigned const block_index,
1451 unsigned const local_index,
1452 Utils::Vector3i const &) {
1453 out[local_index] = values[block_index];
1454 };
1455
1456 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1457 }
1458 }
1459 }
1460 return out;
1461 }
1462
1463 void set_slice_density(Utils::Vector3i const &lower_corner,
1464 Utils::Vector3i const &upper_corner,
1465 std::vector<double> const &density) override {
1467 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1468 assert(density.size() == ci->numCells());
1469 auto const &lattice = get_lattice();
1470 for (auto &block : *lattice.get_blocks()) {
1471 auto const block_offset = lattice.get_block_corner(block, true);
1472 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1473 block_offset, block)) {
1474 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1475 std::vector<FloatType> values(bci->numCells());
1476
1477 auto kernel = [&values, &density](unsigned const block_index,
1478 unsigned const local_index,
1479 Utils::Vector3i const &node) {
1480 values[block_index] = numeric_cast<FloatType>(density[local_index]);
1481 };
1482
1483 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1484 lbm::accessor::Density::set(pdf_field, values, *bci);
1485 }
1486 }
1487 }
1488 }
1489
1490 std::optional<Utils::Vector3d>
1492 bool consider_ghosts = false) const override {
1493 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::UBB)));
1494 auto const bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1495 if (!bc or !m_boundary->node_is_boundary(node))
1496 return std::nullopt;
1497
1498 return {to_vector3d(m_boundary->get_node_value_at_boundary(node))};
1499 }
1500
1502 Utils::Vector3d const &velocity) override {
1505 auto bc = get_block_and_cell(get_lattice(), node, false);
1506 if (!bc) {
1507 bc = get_block_and_cell(get_lattice(), node, true);
1508 }
1509 if (bc) {
1510 m_boundary->set_node_value_at_boundary(
1511 node, to_vector3<FloatType>(velocity), *bc);
1512 }
1513 return bc.has_value();
1514 }
1515
1516 std::vector<std::optional<Utils::Vector3d>> get_slice_velocity_at_boundary(
1517 Utils::Vector3i const &lower_corner,
1518 Utils::Vector3i const &upper_corner) const override {
1519 std::vector<std::optional<Utils::Vector3d>> out;
1520 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1521 out = std::vector<std::optional<Utils::Vector3d>>(ci->numCells());
1522 auto const &lattice = get_lattice();
1523 for (auto const &block : *lattice.get_blocks()) {
1524 auto const block_offset = lattice.get_block_corner(block, true);
1525 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1526 block_offset, block)) {
1527
1528 auto kernel = [&out, this](unsigned const, unsigned const local_index,
1529 Utils::Vector3i const &node) {
1530 if (m_boundary->node_is_boundary(node)) {
1531 out[local_index] =
1532 to_vector3d(m_boundary->get_node_value_at_boundary(node));
1533 } else {
1534 out[local_index] = std::nullopt;
1535 }
1536 };
1537
1538 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1539 }
1540 }
1541 assert(out.size() == ci->numCells());
1542 }
1543 return out;
1544 }
1545
1547 Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner,
1548 std::vector<std::optional<Utils::Vector3d>> const &velocity) override {
1551 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1552 assert(velocity.size() == ci->numCells());
1553 auto const &lattice = get_lattice();
1554 for (auto &block : *lattice.get_blocks()) {
1555 auto const block_offset = lattice.get_block_corner(block, true);
1556 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1557 block_offset, block)) {
1558
1559 auto kernel = [&lattice, &block, &velocity,
1560 this](unsigned const, unsigned const local_index,
1561 Utils::Vector3i const &node) {
1562 auto const bc = get_block_and_cell(lattice, node, false);
1563 assert(bc->block->getAABB() == block.getAABB());
1564 auto const &opt = velocity[local_index];
1565 if (opt) {
1566 m_boundary->set_node_value_at_boundary(
1567 node, to_vector3<FloatType>(*opt), *bc);
1568 } else {
1569 m_boundary->remove_node_from_boundary(node, *bc);
1570 }
1571 };
1572
1573 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1574 }
1575 }
1576 }
1577 }
1578
1579 std::optional<Utils::Vector3d>
1580 get_node_boundary_force(Utils::Vector3i const &node) const override {
1581 auto const bc = get_block_and_cell(get_lattice(), node, true);
1582 if (!bc or !m_boundary->node_is_boundary(node))
1583 return std::nullopt;
1584
1585 return get_node_last_applied_force(node, true);
1586 }
1587
1588 bool remove_node_from_boundary(Utils::Vector3i const &node) override {
1589 auto bc = get_block_and_cell(get_lattice(), node, false);
1590 if (!bc) {
1591 bc = get_block_and_cell(get_lattice(), node, true);
1592 }
1593 if (bc) {
1594 m_boundary->remove_node_from_boundary(node, *bc);
1595 }
1596 return bc.has_value();
1597 }
1598
1599 std::optional<bool>
1601 bool consider_ghosts = false) const override {
1602 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::UBB)));
1603 auto const bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1604 if (!bc)
1605 return std::nullopt;
1606
1607 return {m_boundary->node_is_boundary(node)};
1608 }
1609
1610 std::vector<bool>
1612 Utils::Vector3i const &upper_corner) const override {
1613 std::vector<bool> out;
1614 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1615 out = std::vector<bool>(ci->numCells());
1616 auto const &lattice = get_lattice();
1617 for (auto const &block : *lattice.get_blocks()) {
1618 auto const block_offset = lattice.get_block_corner(block, true);
1619 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1620 block_offset, block)) {
1621
1622 auto kernel = [&out, this](unsigned const, unsigned const local_index,
1623 Utils::Vector3i const &node) {
1624 out[local_index] = m_boundary->node_is_boundary(node);
1625 };
1626
1627 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1628 }
1629 }
1630 assert(out.size() == ci->numCells());
1631 }
1632 return out;
1633 }
1634
1635 void reallocate_ubb_field() override { m_boundary->boundary_update(); }
1636
1638 if (not m_has_boundaries) {
1639 m_has_boundaries = true;
1641 }
1642 m_has_boundaries = true;
1643 }
1644
1645 void clear_boundaries() override {
1646 reset_boundary_handling(get_lattice().get_blocks());
1649 m_has_boundaries = false;
1651 }
1652
1653 void
1654 update_boundary_from_shape(std::vector<int> const &raster_flat,
1655 std::vector<double> const &data_flat) override {
1658 auto const grid_size = get_lattice().get_grid_dimensions();
1659 auto const data = fill_3D_vector_array(data_flat, grid_size);
1660 set_boundary_from_grid(*m_boundary, get_lattice(), raster_flat, data);
1663 }
1664
1665 // Pressure tensor
1666 std::optional<Utils::VectorXd<9>>
1667 get_node_pressure_tensor(Utils::Vector3i const &node) const override {
1668 auto bc = get_block_and_cell(get_lattice(), node, false);
1669 if (!bc)
1670 return std::nullopt;
1671
1672 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1673 auto tensor = lbm::accessor::PressureTensor::get(pdf_field, bc->cell);
1674 pressure_tensor_correction(tensor);
1675
1676 return to_vector9d(tensor);
1677 }
1678
1679 std::vector<double> get_slice_pressure_tensor(
1680 Utils::Vector3i const &lower_corner,
1681 Utils::Vector3i const &upper_corner) const override {
1682 std::vector<double> out;
1683 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1684 out = std::vector<double>(9u * ci->numCells());
1685 auto const &lattice = get_lattice();
1686 for (auto const &block : *lattice.get_blocks()) {
1687 auto const block_offset = lattice.get_block_corner(block, true);
1688 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1689 block_offset, block)) {
1690 auto const pdf_field =
1691 block.template getData<PdfField>(m_pdf_field_id);
1692 auto values = lbm::accessor::PressureTensor::get(pdf_field, *bci);
1693 assert(values.size() == 9u * bci->numCells());
1694
1695 auto kernel = [&values, &out, this](unsigned const block_index,
1696 unsigned const local_index,
1697 Utils::Vector3i const &node) {
1698 pressure_tensor_correction(
1699 std::span<FloatType, 9ul>(&values[9u * block_index], 9ul));
1700 for (uint_t f = 0u; f < 9u; ++f) {
1701 out[9u * local_index + f] = values[9u * block_index + f];
1702 }
1703 };
1704
1705 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1706 }
1707 }
1708 }
1709 return out;
1710 }
1711
1712 // Global pressure tensor
1713 [[nodiscard]] Utils::VectorXd<9> get_pressure_tensor() const override {
1714 Matrix3<FloatType> tensor(FloatType{0});
1715 for (auto const &block : *get_lattice().get_blocks()) {
1716 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1717 tensor += lbm::accessor::PressureTensor::reduce(pdf_field);
1718 }
1719 auto const grid_size = get_lattice().get_grid_dimensions();
1720 auto const number_of_nodes = Utils::product(grid_size);
1721 pressure_tensor_correction(tensor);
1722 return to_vector9d(tensor) * (1. / static_cast<double>(number_of_nodes));
1723 }
1724
1725 // Global momentum
1726 [[nodiscard]] Utils::Vector3d get_momentum() const override {
1727 Vector3<FloatType> mom(FloatType{0});
1728 for (auto const &block : *get_lattice().get_blocks()) {
1729 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1730 auto force_field =
1731 block.template getData<VectorField>(m_last_applied_force_field_id);
1732 mom += lbm::accessor::MomentumDensity::reduce(pdf_field, force_field);
1733 }
1734 return to_vector3d(mom);
1735 }
1736
1737 // Global external force
1738 void set_external_force(Utils::Vector3d const &ext_force) override {
1739 m_reset_force->set_ext_force(ext_force);
1740 }
1741
1742 [[nodiscard]] Utils::Vector3d get_external_force() const noexcept override {
1743 return m_reset_force->get_ext_force();
1744 }
1745
1746 [[nodiscard]] double get_kT() const noexcept override {
1747 return numeric_cast<double>(m_kT);
1748 }
1749
1750 [[nodiscard]] unsigned int get_seed() const noexcept override {
1751 return m_seed;
1752 }
1753
1754 [[nodiscard]] std::optional<uint64_t> get_rng_state() const override {
1755 auto const cm = std::get_if<CollisionModelThermalized>(&*m_collision_model);
1756 if (!cm or m_kT == 0.) {
1757 return std::nullopt;
1758 }
1759 return {static_cast<uint64_t>(cm->getTime_step())};
1760 }
1761
1762 void set_rng_state(uint64_t counter) override {
1763 auto const cm = std::get_if<CollisionModelThermalized>(&*m_collision_model);
1764 if (!cm or m_kT == 0.) {
1765 throw std::runtime_error("This LB instance is unthermalized");
1766 }
1767 assert(counter <=
1768 static_cast<uint32_t>(std::numeric_limits<uint_t>::max()));
1769 cm->setTime_step(static_cast<uint32_t>(counter));
1770 }
1771
1772 [[nodiscard]] LatticeWalberla const &get_lattice() const noexcept override {
1773 return *m_lattice;
1774 }
1775
1776 [[nodiscard]] std::size_t get_velocity_field_id() const noexcept override {
1777 return m_velocity_field_id;
1778 }
1779
1780 [[nodiscard]] std::size_t get_force_field_id() const noexcept override {
1782 }
1783
1784 void register_vtk_field_filters(walberla::vtk::VTKOutput &vtk_obj) override {
1785 field::FlagFieldCellFilter<FlagField> fluid_filter(m_flag_field_id);
1786 fluid_filter.addFlag(Boundary_flag);
1787 vtk_obj.addCellExclusionFilter(fluid_filter);
1788 }
1789
1790protected:
1791 template <typename Field_T, uint_t F_SIZE_ARG, typename OutputType>
1792 class VTKWriter : public vtk::BlockCellDataWriter<OutputType, F_SIZE_ARG> {
1793 public:
1794 VTKWriter(ConstBlockDataID const &block_id, std::string const &id,
1795 FloatType unit_conversion)
1796 : vtk::BlockCellDataWriter<OutputType, F_SIZE_ARG>(id),
1797 m_block_id(block_id), m_field(nullptr),
1798 m_conversion(unit_conversion) {}
1799
1800 protected:
1801 void configure() override {
1802 WALBERLA_ASSERT_NOT_NULLPTR(this->block_);
1803 m_field = this->block_->template getData<Field_T>(m_block_id);
1804 }
1805
1806 ConstBlockDataID const m_block_id;
1807 Field_T const *m_field;
1808 FloatType const m_conversion;
1809 };
1810
1811 template <typename OutputType = float>
1812 class DensityVTKWriter : public VTKWriter<PdfField, 1u, OutputType> {
1813 public:
1815 using Base::Base;
1816 using Base::evaluate;
1817
1818 protected:
1819 OutputType evaluate(cell_idx_t const x, cell_idx_t const y,
1820 cell_idx_t const z, cell_idx_t const) override {
1821 WALBERLA_ASSERT_NOT_NULLPTR(this->m_field);
1822 auto const density =
1823 lbm::accessor::Density::get(this->m_field, {x, y, z});
1824 return numeric_cast<OutputType>(this->m_conversion * density);
1825 }
1826 };
1827
1828 template <typename OutputType = float>
1829 class VelocityVTKWriter : public VTKWriter<VectorField, 3u, OutputType> {
1830 public:
1832 using Base::Base;
1833 using Base::evaluate;
1834
1835 protected:
1836 OutputType evaluate(cell_idx_t const x, cell_idx_t const y,
1837 cell_idx_t const z, cell_idx_t const f) override {
1838 WALBERLA_ASSERT_NOT_NULLPTR(this->m_field);
1839 auto const velocity =
1840 lbm::accessor::Vector::get(this->m_field, {x, y, z});
1841 return numeric_cast<OutputType>(this->m_conversion * velocity[uint_c(f)]);
1842 }
1843 };
1844
1845 template <typename OutputType = float>
1846 class PressureTensorVTKWriter : public VTKWriter<PdfField, 9u, OutputType> {
1847 public:
1849 using Base::Base;
1850 using Base::evaluate;
1851
1852 PressureTensorVTKWriter(ConstBlockDataID const &block_id,
1853 std::string const &id, FloatType unit_conversion,
1854 FloatType off_diag_factor)
1855 : Base(block_id, id, unit_conversion),
1856 m_off_diag_factor(off_diag_factor) {}
1857
1858 protected:
1859 OutputType evaluate(cell_idx_t const x, cell_idx_t const y,
1860 cell_idx_t const z, cell_idx_t const f) override {
1861 WALBERLA_ASSERT_NOT_NULLPTR(this->m_field);
1862 auto const pressure =
1864 auto const revert_factor =
1865 (f == 0 or f == 4 or f == 8) ? FloatType{1} : m_off_diag_factor;
1866 return numeric_cast<OutputType>(this->m_conversion * revert_factor *
1867 pressure[uint_c(f)]);
1868 }
1869 FloatType const m_off_diag_factor;
1870 };
1871
1872public:
1873 void register_vtk_field_writers(walberla::vtk::VTKOutput &vtk_obj,
1874 LatticeModel::units_map const &units,
1875 int flag_observables) override {
1876#if defined(__CUDACC__)
1877 auto const allocate_cpu_field_if_empty =
1878 [&]<typename Field>(auto const &blocks, std::string name,
1879 std::optional<BlockDataID> &cpu_field) {
1880 if (not cpu_field) {
1881 cpu_field = field::addToStorage<Field>(
1882 blocks, name, FloatType{0}, field::fzyx,
1883 m_lattice->get_ghost_layers(), m_host_field_allocator);
1884 }
1885 };
1886#endif
1887 if (flag_observables & static_cast<int>(OutputVTK::density)) {
1888 auto const unit_conversion = FloatType_c(units.at("density"));
1889#if defined(__CUDACC__)
1890 if constexpr (Architecture == lbmpy::Arch::GPU) {
1891 auto const &blocks = m_lattice->get_blocks();
1892 allocate_cpu_field_if_empty.template operator()<PdfFieldCpu>(
1893 blocks, "pdfs_cpu", m_pdf_cpu_field_id);
1894 vtk_obj.addBeforeFunction(gpu::fieldCpyFunctor<PdfFieldCpu, PdfField>(
1895 blocks, *m_pdf_cpu_field_id, m_pdf_field_id));
1896 }
1897#endif
1898 vtk_obj.addCellDataWriter(std::make_shared<DensityVTKWriter<float>>(
1899 m_pdf_field_id, "density", unit_conversion));
1900 }
1901 if (flag_observables & static_cast<int>(OutputVTK::velocity_vector)) {
1902 auto const unit_conversion = FloatType_c(units.at("velocity"));
1903#if defined(__CUDACC__)
1904 if constexpr (Architecture == lbmpy::Arch::GPU) {
1905 auto const &blocks = m_lattice->get_blocks();
1906 allocate_cpu_field_if_empty.template operator()<VectorFieldCpu>(
1907 blocks, "vel_cpu", m_vel_cpu_field_id);
1908 vtk_obj.addBeforeFunction(
1909 gpu::fieldCpyFunctor<VectorFieldCpu, VectorField>(
1910 blocks, *m_vel_cpu_field_id, m_velocity_field_id));
1911 }
1912#endif
1913 vtk_obj.addCellDataWriter(std::make_shared<VelocityVTKWriter<float>>(
1914 m_velocity_field_id, "velocity_vector", unit_conversion));
1915 }
1916 if (flag_observables & static_cast<int>(OutputVTK::pressure_tensor)) {
1917 auto const unit_conversion = FloatType_c(units.at("pressure"));
1918#if defined(__CUDACC__)
1919 if constexpr (Architecture == lbmpy::Arch::GPU) {
1920 auto const &blocks = m_lattice->get_blocks();
1921 allocate_cpu_field_if_empty.template operator()<PdfFieldCpu>(
1922 blocks, "pdfs_cpu", m_pdf_cpu_field_id);
1923 vtk_obj.addBeforeFunction(gpu::fieldCpyFunctor<PdfFieldCpu, PdfField>(
1924 blocks, *m_pdf_cpu_field_id, m_pdf_field_id));
1925 }
1926#endif
1927 vtk_obj.addCellDataWriter(
1928 std::make_shared<PressureTensorVTKWriter<float>>(
1929 m_pdf_field_id, "pressure_tensor", unit_conversion,
1930 pressure_tensor_correction_factor()));
1931 }
1932 }
1933
1934 ~LBWalberlaImpl() override = default;
1935};
1936
1937} // namespace walberla
LBWalberlaBase provides the public interface of the LB waLBerla bridge.
Vector implementation and trait types for boost qvm interoperability.
Definition Cell.hpp:96
Interface of a lattice-based fluid model.
std::map< std::string, std::shared_ptr< VTKHandle > > m_vtk_auto
VTK writers that are executed automatically.
std::unordered_map< std::string, double > units_map
Class that runs and controls the BlockForest in waLBerla.
walberla::blockforest::StructuredBlockForest Lattice_T
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value) noexcept
Create a vector that has all entries set to the same value.
Definition Vector.hpp:111
Boundary class optimized for sparse data.
field::FlagField< uint8_t > FlagField
OutputType evaluate(cell_idx_t const x, cell_idx_t const y, cell_idx_t const z, cell_idx_t const) override
OutputType evaluate(cell_idx_t const x, cell_idx_t const y, cell_idx_t const z, cell_idx_t const f) override
PressureTensorVTKWriter(ConstBlockDataID const &block_id, std::string const &id, FloatType unit_conversion, FloatType off_diag_factor)
VTKWriter(ConstBlockDataID const &block_id, std::string const &id, FloatType unit_conversion)
OutputType evaluate(cell_idx_t const x, cell_idx_t const y, cell_idx_t const z, cell_idx_t const f) override
Class that runs and controls the LB on waLBerla.
void add_forces_at_pos(std::vector< Utils::Vector3d > const &pos, std::vector< Utils::Vector3d > const &forces) override
typename FieldTrait< FloatType, Architecture >::template RegularCommScheme< typename stencil::D3Q27 > RegularFullCommunicator
Full communicator.
std::vector< double > get_slice_last_applied_force(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
std::shared_ptr< RegularFullCommunicator > m_pdf_communicator
std::vector< std::optional< Utils::Vector3d > > get_slice_velocity_at_boundary(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
std::optional< Utils::Vector3d > get_node_last_applied_force(Utils::Vector3i const &node, bool consider_ghosts=false) const override
stencil::D3Q19 Stencil
Stencil for collision and streaming operations.
std::optional< Utils::Vector3d > get_node_velocity_at_boundary(Utils::Vector3i const &node, bool consider_ghosts=false) const override
void ghost_communication() override
void copy_block_buffer(CellInterval const &bci, CellInterval const &ci, Utils::Vector3i const &block_offset, Utils::Vector3i const &lower_corner, Kernel &&kernel) const
Synchronize data between a sliced block and a container.
std::optional< Utils::Vector3d > get_node_velocity(Utils::Vector3i const &node, bool consider_ghosts=false) const override
void reallocate_ubb_field() override
std::shared_ptr< RegularFullCommunicator > m_full_communicator
typename detail::KernelTrait< FloatType, Architecture >::CollisionModelLeesEdwards CollisionModelLeesEdwards
std::size_t get_force_field_id() const noexcept override
std::shared_ptr< CollisionModel > m_collision_model
typename FieldTrait< FloatType, Architecture >::VectorField VectorField
void set_slice_velocity(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, std::vector< double > const &velocity) override
unsigned int get_seed() const noexcept override
void integrate_vtk_writers() override
bool remove_node_from_boundary(Utils::Vector3i const &node) override
std::optional< Utils::Vector3d > get_node_boundary_force(Utils::Vector3i const &node) const override
std::optional< double > get_density_at_pos(Utils::Vector3d const &pos, bool consider_points_in_halo=false) const override
void set_slice_velocity_at_boundary(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, std::vector< std::optional< Utils::Vector3d > > const &velocity) override
std::vector< double > get_slice_population(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
void set_rng_state(uint64_t counter) override
std::vector< double > get_slice_velocity(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
std::shared_ptr< LatticeWalberla > m_lattice
std::shared_ptr< LeesEdwardsPack > m_lees_edwards_callbacks
LBWalberlaImpl(std::shared_ptr< LatticeWalberla > lattice, double viscosity, double density)
bool set_node_last_applied_force(Utils::Vector3i const &node, Utils::Vector3d const &force) override
std::shared_ptr< InterpolateAndShiftAtBoundary< _VectorField, FloatType > > m_lees_edwards_vel_interpol_sweep
std::vector< bool > get_slice_is_boundary(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
std::vector< double > get_slice_density(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
bool is_double_precision() const noexcept override
void update_boundary_from_shape(std::vector< int > const &raster_flat, std::vector< double > const &data_flat) override
std::shared_ptr< BoundaryModel > m_boundary
std::vector< double > get_slice_pressure_tensor(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
bool set_node_density(Utils::Vector3i const &node, double density) override
void set_collision_model(double kT, unsigned int seed) override
std::variant< CollisionModelThermalized, CollisionModelLeesEdwards > CollisionModel
std::vector< Utils::Vector3d > get_velocities_at_pos(std::vector< Utils::Vector3d > const &pos) override
void ghost_communication_laf() override
std::optional< bool > get_node_is_boundary(Utils::Vector3i const &node, bool consider_ghosts=false) const override
std::shared_ptr< ResetForce< PdfField, VectorField > > m_reset_force
std::shared_ptr< StreamSweep > m_stream
FlagUID const Boundary_flag
Flag for boundary cells.
~LBWalberlaImpl() override=default
std::optional< uint64_t > get_rng_state() const override
std::optional< std::vector< double > > get_node_population(Utils::Vector3i const &node, bool consider_ghosts=false) const override
FloatType m_density
kinematic viscosity
double get_viscosity() const noexcept override
double get_kT() const noexcept override
Utils::Vector3d get_momentum() const override
void set_viscosity(double viscosity) override
std::shared_ptr< BoundaryFullCommunicator > m_boundary_communicator
std::size_t stencil_size() const noexcept override
auto add_to_storage(std::string const tag)
Convenience function to add a field with a custom allocator.
typename detail::KernelTrait< FloatType, Architecture >::InitialPDFsSetter InitialPDFsSetter
bool set_node_velocity_at_boundary(Utils::Vector3i const &node, Utils::Vector3d const &velocity) override
stencil::D3Q27 StencilFull
Stencil for ghost communication (includes domain corners).
void register_vtk_field_writers(walberla::vtk::VTKOutput &vtk_obj, LatticeModel::units_map const &units, int flag_observables) override
typename BoundaryModel::FlagField FlagField
std::optional< double > get_node_density(Utils::Vector3i const &node, bool consider_ghosts=false) const override
void check_lebc(unsigned int shear_direction, unsigned int shear_plane_normal) const override
void set_slice_density(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, std::vector< double > const &density) override
void ghost_communication_vel() override
typename FieldTrait< FloatType >::PdfField _PdfField
Utils::Vector3d get_external_force() const noexcept override
FloatType FloatType_c(T t) const
std::shared_ptr< RegularFullCommunicator > m_laf_communicator
std::size_t get_velocity_field_id() const noexcept override
Utils::VectorXd< 9 > get_pressure_tensor() const override
std::optional< CellInterval > get_block_interval(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, Utils::Vector3i const &block_offset, IBlock const &block) const
std::shared_ptr< InterpolateAndShiftAtBoundary< _PdfField, FloatType > > m_lees_edwards_pdf_interpol_sweep
void set_external_force(Utils::Vector3d const &ext_force) override
std::optional< Utils::Vector3d > get_velocity_at_pos(Utils::Vector3d const &pos, bool consider_points_in_halo=false) const override
void set_slice_last_applied_force(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, std::vector< double > const &force) override
LatticeWalberla::Lattice_T BlockStorage
Lattice model (e.g.
void set_collision_model(std::unique_ptr< LeesEdwardsPack > &&lees_edwards_pack) override
std::shared_ptr< RegularFullCommunicator > m_vel_communicator
typename FieldTrait< FloatType, Architecture >::template BoundaryCommScheme< typename stencil::D3Q27 > BoundaryFullCommunicator
std::bitset< GhostComm::SIZE > m_pending_ghost_comm
typename FieldTrait< FloatType, Architecture >::template RegularCommScheme< Stencil > PDFStreamingCommunicator
Regular communicator.
LatticeWalberla const & get_lattice() const noexcept override
void set_slice_population(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, std::vector< double > const &population) override
typename FieldTrait< FloatType, Architecture >::template PackInfo< Field > PackInfo
double get_density() const noexcept override
typename detail::KernelTrait< FloatType, Architecture >::CollisionModelThermalized CollisionModelThermalized
typename FieldTrait< FloatType, Architecture >::PdfField PdfField
void ghost_communication_pdf() override
void register_vtk_field_filters(walberla::vtk::VTKOutput &vtk_obj) override
std::optional< Utils::Vector3d > get_node_force_to_be_applied(Utils::Vector3i const &node) const override
typename FieldTrait< FloatType >::VectorField _VectorField
bool set_node_velocity(Utils::Vector3i const &node, Utils::Vector3d const &v) override
std::optional< CellInterval > get_interval(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const
bool set_node_population(Utils::Vector3i const &node, std::vector< double > const &population) override
bool is_gpu() const noexcept override
std::shared_ptr< PDFStreamingCommunicator > m_pdf_streaming_communicator
BlockDataID m_last_applied_force_field_id
std::optional< Utils::VectorXd< 9 > > get_node_pressure_tensor(Utils::Vector3i const &node) const override
std::shared_ptr< InterpolateAndShiftAtBoundary< _VectorField, FloatType > > m_lees_edwards_last_applied_force_interpol_sweep
typename detail::KernelTrait< FloatType, Architecture >::StreamSweep StreamSweep
bool add_force_at_pos(Utils::Vector3d const &pos, Utils::Vector3d const &force) override
void setup_boundary_handle(std::shared_ptr< LatticeWalberla > lattice, std::shared_ptr< Boundary_T > boundary)
static double weight(int type, double r_cut, double k, double r)
Definition dpd.cpp:78
static double * block(double *p, std::size_t index, std::size_t size)
Definition elc.cpp:172
T product(Vector< T, N > const &v)
Definition Vector.hpp:375
int get_linear_index(int a, int b, int c, const Vector3i &adim, MemoryOrder memory_order=MemoryOrder::COLUMN_MAJOR)
get the linear index from the position (a,b,c) in a 3D grid of dimensions adim.
Definition index.hpp:43
VectorXi< 3 > Vector3i
Definition Vector.hpp:174
void set(GhostLayerField< double, uint_t{19u}> *pdf_field, double const rho_in, Cell const &cell)
double get(GhostLayerField< double, uint_t{19u}> const *pdf_field, Cell const &cell)
void set(GhostLayerField< double, uint_t{19u}> const *pdf_field, GhostLayerField< double, uint_t{3u}> *velocity_field, GhostLayerField< double, uint_t{3u}> *force_field, Vector3< double > const &force, Cell const &cell)
std::vector< double > get(gpu::GPUField< double > const *vec_field, std::vector< double > const &pos, uint gl)
void set(gpu::GPUField< double > const *vec_field, std::vector< double > const &pos, std::vector< double > const &forces, uint gl)
auto reduce(GhostLayerField< double, uint_t{19u}> const *pdf_field, GhostLayerField< double, uint_t{3u}> const *force_field)
void set(GhostLayerField< double, uint_t{19u}> *pdf_field, std::array< double, 19u > const &pop, Cell const &cell)
auto get(GhostLayerField< double, uint_t{19u}> const *pdf_field, Cell const &cell)
void initialize(GhostLayerField< double, uint_t{19u}> *pdf_field, std::array< double, 19u > const &pop)
auto get(GhostLayerField< double, uint_t{19u}> const *pdf_field, Cell const &cell)
auto reduce(GhostLayerField< double, uint_t{19u}> const *pdf_field)
void initialize(GhostLayerField< double, uint_t{3u}> *vec_field, Vector3< double > const &vec)
void add(GhostLayerField< double, uint_t{3u}> *vec_field, Vector3< double > const &vec, Cell const &cell)
auto get(GhostLayerField< double, uint_t{3u}> const *vec_field, Cell const &cell)
void set(GhostLayerField< double, uint_t{19u}> *pdf_field, GhostLayerField< double, uint_t{3u}> *velocity_field, GhostLayerField< double, uint_t{3u}> const *force_field, Vector3< double > const &u, Cell const &cell)
\file PackInfoPdfDoublePrecision.cpp \author pystencils
std::optional< BlockAndCell > get_block_and_cell(::LatticeWalberla const &lattice, Utils::Vector3i const &node, bool consider_ghost_layers)
auto to_vector3d(Vector3< T > const &v) noexcept
void interpolate_bspline_at_pos(Utils::Vector3d const &pos, Function const &f)
void set_boundary_from_grid(BoundaryModel &boundary, LatticeWalberla const &lattice, std::vector< int > const &raster_flat, std::vector< DataType > const &data_flat)
Definition boundary.hpp:81
auto get_min_corner(IBlock const &block)
Get the block-local coordinates of the lower corner of a block.
Cell to_cell(Utils::Vector3i const &xyz)
Utils::Vector3i to_vector3i(Vector3< int > const &v) noexcept
auto to_vector9d(Matrix3< T > const &m) noexcept
std::vector< Utils::Vector3d > fill_3D_vector_array(std::vector< double > const &vec_flat, Utils::Vector3i const &grid_size)
Definition boundary.hpp:36
static Utils::Vector3d velocity(Particle const &p_ref, Particle const &p_vs)
Velocity of the virtual site.
Definition relative.cpp:64
DEVICE_QUALIFIER constexpr size_type size() const noexcept
Definition Array.hpp:166
typename detail::KernelTrait< FT, AT >::PackInfoVec PackInfoStreamingVec
typename detail::KernelTrait< FT, AT >::PackInfoPdf PackInfoStreamingPdf
field::GhostLayerField< FT, uint_t{3u}> VectorField
field::communication::PackInfo< Field > PackInfo
field::GhostLayerField< FT, Stencil::Size > PdfField
blockforest::communication::UniformBufferedScheme< Stencil > RegularCommScheme
blockforest::communication::UniformBufferedScheme< Stencil > BoundaryCommScheme
GhostCommFlags
Ghost communication operations.
@ LAF
last applied forces communication