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 b = blocks->begin(); b != blocks->end(); ++b) {
530 pdf_setter(&*b);
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 b = blocks->begin(); b != blocks->end(); ++b)
589 (*m_stream)(&*b);
590 }
591
592 void integrate_collide(std::shared_ptr<BlockStorage> const &blocks) {
593 auto &cm_variant = *m_collision_model;
594 for (auto b = blocks->begin(); b != blocks->end(); ++b)
595 std::visit(m_run_collide_sweep, cm_variant, std::variant<IBlock *>(&*b));
596 if (auto *cm = std::get_if<CollisionModelThermalized>(&cm_variant)) {
597 cm->setTime_step(cm->getTime_step() + 1u);
598 }
599 }
600
601 auto has_lees_edwards_bc() const {
602 return std::holds_alternative<CollisionModelLeesEdwards>(
604 }
605
606 void apply_lees_edwards_pdf_interpolation(
607 std::shared_ptr<BlockStorage> const &blocks) {
608 for (auto b = blocks->begin(); b != blocks->end(); ++b)
610 }
611
612 void apply_lees_edwards_vel_interpolation_and_shift(
613 std::shared_ptr<BlockStorage> const &blocks) {
614 for (auto b = blocks->begin(); b != blocks->end(); ++b)
616 }
617
618 void apply_lees_edwards_last_applied_force_interpolation(
619 std::shared_ptr<BlockStorage> const &blocks) {
620 for (auto b = blocks->begin(); b != blocks->end(); ++b)
622 }
623
624 void integrate_reset_force(std::shared_ptr<BlockStorage> const &blocks) {
625 for (auto b = blocks->begin(); b != blocks->end(); ++b)
626 (*m_reset_force)(&*b);
627 }
628
629 void integrate_boundaries(std::shared_ptr<BlockStorage> const &blocks) {
630 for (auto b = blocks->begin(); b != blocks->end(); ++b)
631 (*m_boundary)(&*b);
632 }
633
634 void integrate_push_scheme() {
635 auto const &blocks = get_lattice().get_blocks();
636 // Reset force fields
637 integrate_reset_force(blocks);
638 // LB collide
639 integrate_collide(blocks);
640 m_pdf_streaming_communicator->communicate();
641 // Handle boundaries
642 if (m_has_boundaries) {
643 integrate_boundaries(blocks);
644 }
645 // LB stream
646 integrate_stream(blocks);
647 // Mark pending ghost layer updates
651 // Refresh ghost layers
653 }
654
655 void integrate_pull_scheme() {
656 auto const &blocks = get_lattice().get_blocks();
657 // Handle boundaries
658 if (m_has_boundaries) {
659 integrate_boundaries(blocks);
660 }
661 // LB stream
662 integrate_stream(blocks);
663 // LB collide
664 integrate_collide(blocks);
665 // Reset force fields
666 integrate_reset_force(blocks);
667 // Mark pending ghost layer updates
671 // Refresh ghost layers
673 }
674
675protected:
676 void integrate_vtk_writers() override {
677 for (auto const &it : m_vtk_auto) {
678 auto &vtk_handle = it.second;
679 if (vtk_handle->enabled) {
680 vtk::writeFiles(vtk_handle->ptr)();
681 vtk_handle->execution_count++;
682 }
683 }
684 }
685
686public:
687 void integrate() override {
688 if (has_lees_edwards_bc()) {
689 integrate_pull_scheme();
690 } else {
691 integrate_push_scheme();
692 }
693 // Handle VTK writers
695 }
696
697 void ghost_communication() override {
698 if (m_pending_ghost_comm.any()) {
701 }
702 }
703
704 void ghost_communication_pdf() override {
706 m_pdf_communicator->communicate();
707 if (has_lees_edwards_bc()) {
708 auto const &blocks = get_lattice().get_blocks();
709 apply_lees_edwards_pdf_interpolation(blocks);
710 }
712 }
713 }
714
715 void ghost_communication_vel() override {
717 m_vel_communicator->communicate();
718 if (has_lees_edwards_bc()) {
719 auto const &blocks = get_lattice().get_blocks();
720 apply_lees_edwards_vel_interpolation_and_shift(blocks);
721 }
723 }
724 }
725
726 void ghost_communication_laf() override {
728 m_laf_communicator->communicate();
729 if (has_lees_edwards_bc()) {
730 auto const &blocks = get_lattice().get_blocks();
731 apply_lees_edwards_last_applied_force_interpolation(blocks);
732 }
734 }
735 }
736
743
745 m_full_communicator->communicate();
746 if (has_lees_edwards_bc()) {
747 auto const &blocks = get_lattice().get_blocks();
748 apply_lees_edwards_pdf_interpolation(blocks);
749 apply_lees_edwards_vel_interpolation_and_shift(blocks);
750 apply_lees_edwards_last_applied_force_interpolation(blocks);
751 }
755 }
756
758 if (has_lees_edwards_bc()) {
759 m_full_communicator->communicate();
760 auto const &blocks = get_lattice().get_blocks();
761 apply_lees_edwards_pdf_interpolation(blocks);
762 apply_lees_edwards_vel_interpolation_and_shift(blocks);
763 apply_lees_edwards_last_applied_force_interpolation(blocks);
767 }
768 }
769
770 void set_collision_model(double kT, unsigned int seed) override {
771 auto const omega = shear_mode_relaxation_rate();
772 auto const omega_odd = odd_mode_relaxation_rate(omega);
773 auto const blocks = get_lattice().get_blocks();
774 m_kT = FloatType_c(kT);
775 m_seed = seed;
777 m_pdf_field_id, m_kT, omega, omega,
778 omega_odd, omega, seed, uint32_t{0u});
779 m_collision_model = std::make_shared<CollisionModel>(std::move(obj));
780 m_run_collide_sweep = CollideSweepVisitor(blocks);
782 }
783
785 std::unique_ptr<LeesEdwardsPack> &&lees_edwards_pack) override {
786 assert(m_kT == 0.);
787#if defined(__CUDACC__)
788 if constexpr (Architecture == lbmpy::Arch::GPU) {
789 throw std::runtime_error("Lees-Edwards LB doesn't support GPU yet");
790 }
791#endif
792 auto const shear_direction = lees_edwards_pack->shear_direction;
793 auto const shear_plane_normal = lees_edwards_pack->shear_plane_normal;
794 auto const shear_vel = FloatType_c(lees_edwards_pack->get_shear_velocity());
795 auto const omega = shear_mode_relaxation_rate();
796 if (shear_plane_normal != 1u) {
797 throw std::domain_error(
798 "Lees-Edwards LB only supports shear_plane_normal=\"y\"");
799 }
800 auto const &lattice = get_lattice();
801 auto const n_ghost_layers = lattice.get_ghost_layers();
802 auto const blocks = lattice.get_blocks();
803 if (lattice.get_node_grid()[shear_direction] != 1 or
804 lattice.get_node_grid()[shear_plane_normal] != 1 or
805 blocks->getSize(shear_direction) != 1ul or
806 blocks->getSize(shear_plane_normal) != 1ul) {
807 throw std::domain_error("LB LEbc doesn't support domain decomposition "
808 "along the shear and normal directions.");
809 }
810 auto const agrid =
811 FloatType_c(lattice.get_grid_dimensions()[shear_plane_normal]);
812 auto obj = CollisionModelLeesEdwards(
813 m_last_applied_force_field_id, m_pdf_field_id, agrid, omega, shear_vel);
814 m_collision_model = std::make_shared<CollisionModel>(std::move(obj));
815 m_lees_edwards_callbacks = std::move(lees_edwards_pack);
816 m_run_collide_sweep = CollideSweepVisitor(blocks, m_lees_edwards_callbacks);
818 std::make_shared<InterpolateAndShiftAtBoundary<_PdfField, FloatType>>(
819 blocks, m_pdf_field_id, m_pdf_tmp_field_id, n_ghost_layers,
820 shear_direction, shear_plane_normal,
821 m_lees_edwards_callbacks->get_pos_offset);
822 m_lees_edwards_vel_interpol_sweep = std::make_shared<
824 blocks, m_velocity_field_id, m_vel_tmp_field_id, n_ghost_layers,
825 shear_direction, shear_plane_normal,
826 m_lees_edwards_callbacks->get_pos_offset,
827 m_lees_edwards_callbacks->get_shear_velocity);
831 n_ghost_layers, shear_direction, shear_plane_normal,
832 m_lees_edwards_callbacks->get_pos_offset);
834 }
835
836 void check_lebc(unsigned int shear_direction,
837 unsigned int shear_plane_normal) const override {
839 if (m_lees_edwards_callbacks->shear_direction != shear_direction or
840 m_lees_edwards_callbacks->shear_plane_normal != shear_plane_normal) {
841 throw std::runtime_error(
842 "MD and LB Lees-Edwards boundary conditions disagree");
843 }
844 }
845 }
846
847 void set_viscosity(double viscosity) override {
848 m_viscosity = FloatType_c(viscosity);
849 }
850
851 [[nodiscard]] double get_viscosity() const noexcept override {
852 return numeric_cast<double>(m_viscosity);
853 }
854
855 [[nodiscard]] double get_density() const noexcept override {
856 return numeric_cast<double>(m_density);
857 }
858
859 // Velocity
860 std::optional<Utils::Vector3d>
862 bool consider_ghosts = false) const override {
863 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::VEL)));
864 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::UBB)));
865 if (m_has_boundaries) {
866 auto const is_boundary = get_node_is_boundary(node, consider_ghosts);
867 if (is_boundary and *is_boundary) {
868 return get_node_velocity_at_boundary(node, consider_ghosts);
869 }
870 }
871 auto const bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
872 if (!bc)
873 return std::nullopt;
874
875 auto field = bc->block->template uncheckedFastGetData<VectorField>(
877 auto const vec = lbm::accessor::Vector::get(field, bc->cell);
878 return to_vector3d(vec);
879 }
880
882 Utils::Vector3d const &v) override {
885 auto bc = get_block_and_cell(get_lattice(), node, false);
886 if (!bc)
887 return false;
888
889 // We have to set both, the pdf and the stored velocity field
890 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
891 auto vel_field =
892 bc->block->template getData<VectorField>(m_velocity_field_id);
893 auto force_field =
894 bc->block->template getData<VectorField>(m_last_applied_force_field_id);
895 auto const vel = to_vector3<FloatType>(v);
896 lbm::accessor::Velocity::set(pdf_field, vel_field, force_field, vel,
897 bc->cell);
898
899 return true;
900 }
901
902 /**
903 * @brief Synchronize data between a sliced block and a container.
904 *
905 * Synchronize data between two data buffers representing sliced matrices
906 * with different memory layouts. The kernel takes as argument an index
907 * for the flattened data buffer containing the serialized block slice,
908 * an index for the flattened I/O buffer, and a block-local node position.
909 *
910 * @param bci Cell interval of the local block within a 3D slice
911 * @param ci Cell interval of the entire lattice within a 3D slice
912 * @param block_offset Origin of the local block
913 * @param lower_corner Lower corner of the 3D slice
914 * @param kernel Function to execute on the two data buffers
915 */
916 template <typename Kernel>
917 void copy_block_buffer(CellInterval const &bci, CellInterval const &ci,
918 Utils::Vector3i const &block_offset,
919 Utils::Vector3i const &lower_corner,
920 Kernel &&kernel) const {
921 auto const local_grid = to_vector3i(ci.max() - ci.min() + Cell(1, 1, 1));
922 auto const block_grid = to_vector3i(bci.max() - bci.min() + Cell(1, 1, 1));
923 auto const lower_cell = bci.min();
924 auto const upper_cell = bci.max();
925 // In the loop, x,y,z are in block coordinates
926 // The field data given in the argument knows about BlockForest
927 // lattice indices from lower_corner to upper_corner. It is converted
928 // to block coordinates
929 for (auto x = lower_cell.x(), i = 0; x <= upper_cell.x(); ++x, ++i) {
930 for (auto y = lower_cell.y(), j = 0; y <= upper_cell.y(); ++y, ++j) {
931 for (auto z = lower_cell.z(), k = 0; z <= upper_cell.z(); ++z, ++k) {
932 auto const node = block_offset + Utils::Vector3i{{x, y, z}};
933 auto const local_index = Utils::get_linear_index(
934 node - lower_corner, local_grid, Utils::MemoryOrder::ROW_MAJOR);
935 auto const block_index = Utils::get_linear_index(
936 i, j, k, block_grid, Utils::MemoryOrder::ROW_MAJOR);
937 kernel(static_cast<unsigned>(block_index),
938 static_cast<unsigned>(local_index), node);
939 }
940 }
941 }
942 }
943
944 std::vector<double>
946 Utils::Vector3i const &upper_corner) const override {
947 std::vector<double> out;
948 uint_t values_size = 0;
949 if (auto const ci = get_interval(lower_corner, upper_corner)) {
950 out = std::vector<double>(3u * ci->numCells());
951 auto const &lattice = get_lattice();
952 for (auto &block : *lattice.get_blocks()) {
953 auto const block_offset = lattice.get_block_corner(block, true);
954 if (auto const bci = get_block_interval(lower_corner, upper_corner,
955 block_offset, block)) {
956 auto const field =
957 block.template getData<VectorField>(m_velocity_field_id);
958 auto const values = lbm::accessor::Vector::get(field, *bci);
959 assert(values.size() == 3u * bci->numCells());
960 values_size += 3u * bci->numCells();
961
962 auto kernel = [&values, &out, this](unsigned const block_index,
963 unsigned const local_index,
964 Utils::Vector3i const &node) {
965 if (m_boundary->node_is_boundary(node)) {
966 auto const &vec = m_boundary->get_node_value_at_boundary(node);
967 for (uint_t f = 0u; f < 3u; ++f) {
968 out[3u * local_index + f] = double_c(vec[f]);
969 }
970 } else {
971 for (uint_t f = 0u; f < 3u; ++f) {
972 out[3u * local_index + f] =
973 double_c(values[3u * block_index + f]);
974 }
975 }
976 };
977
978 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
979 }
980 }
981 assert(values_size == 3u * ci->numCells());
982 }
983 return out;
984 }
985
986 void set_slice_velocity(Utils::Vector3i const &lower_corner,
987 Utils::Vector3i const &upper_corner,
988 std::vector<double> const &velocity) override {
991 if (auto const ci = get_interval(lower_corner, upper_corner)) {
992 assert(velocity.size() == 3u * ci->numCells());
993 auto const &lattice = get_lattice();
994 for (auto &block : *lattice.get_blocks()) {
995 auto const block_offset = lattice.get_block_corner(block, true);
996 if (auto const bci = get_block_interval(lower_corner, upper_corner,
997 block_offset, block)) {
998 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
999 auto force_field = block.template getData<VectorField>(
1001 auto vel_field =
1002 block.template getData<VectorField>(m_velocity_field_id);
1003 std::vector<FloatType> values(3u * bci->numCells());
1004
1005 auto kernel = [&values, &velocity](unsigned const block_index,
1006 unsigned const local_index,
1007 Utils::Vector3i const &node) {
1008 for (uint_t f = 0u; f < 3u; ++f) {
1009 values[3u * block_index + f] =
1010 numeric_cast<FloatType>(velocity[3u * local_index + f]);
1011 }
1012 };
1013
1014 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1015 lbm::accessor::Velocity::set(pdf_field, vel_field, force_field,
1016 values, *bci);
1017 }
1018 }
1019 }
1020 }
1021
1022 [[nodiscard]] bool is_gpu() const noexcept override {
1023 return Architecture == lbmpy::Arch::GPU;
1024 }
1025
1026 void add_forces_at_pos(std::vector<Utils::Vector3d> const &pos,
1027 std::vector<Utils::Vector3d> const &forces) override {
1028 assert(pos.size() == forces.size());
1029 if (pos.empty()) {
1030 return;
1031 }
1032 if constexpr (Architecture == lbmpy::Arch::CPU) {
1033 for (std::size_t i = 0ul; i < pos.size(); ++i) {
1034 add_force_at_pos(pos[i], forces[i]);
1035 }
1036 }
1037#if defined(__CUDACC__)
1038 if constexpr (Architecture == lbmpy::Arch::GPU) {
1039 auto const &lattice = get_lattice();
1040 auto const &block = *(lattice.get_blocks()->begin());
1041 auto const origin = block.getAABB().min();
1042 std::vector<FloatType> host_pos;
1043 std::vector<FloatType> host_force;
1044 host_pos.reserve(3ul * pos.size());
1045 host_force.reserve(3ul * forces.size());
1046 assert(lattice.get_blocks()->getNumberOfBlocks() == 1u);
1047 for (auto const &vec : pos) {
1048#pragma unroll
1049 for (std::size_t i : {0ul, 1ul, 2ul}) {
1050 host_pos.emplace_back(static_cast<FloatType>(vec[i] - origin[i]));
1051 }
1052 }
1053 for (auto const &vec : forces) {
1054#pragma unroll
1055 for (std::size_t i : {0ul, 1ul, 2ul}) {
1056 host_force.emplace_back(static_cast<FloatType>(vec[i]));
1057 }
1058 }
1059 auto const gl = lattice.get_ghost_layers();
1060 auto field = block.template uncheckedFastGetData<VectorField>(
1062 lbm::accessor::Interpolation::set(field, host_pos, host_force, gl);
1063 }
1064#endif
1065 }
1066
1067 std::vector<Utils::Vector3d>
1068 get_velocities_at_pos(std::vector<Utils::Vector3d> const &pos) override {
1069 if (pos.empty()) {
1070 return {};
1071 }
1072 std::vector<Utils::Vector3d> vel{};
1073 if constexpr (Architecture == lbmpy::Arch::CPU) {
1074 vel.reserve(pos.size());
1075 for (auto const &vec : pos) {
1076 auto res = get_velocity_at_pos(vec, true);
1077 assert(res.has_value());
1078 vel.emplace_back(*res);
1079 }
1080 }
1081#if defined(__CUDACC__)
1082 if constexpr (Architecture == lbmpy::Arch::GPU) {
1083 auto const &lattice = get_lattice();
1084 auto const &block = *(lattice.get_blocks()->begin());
1085 auto const origin = block.getAABB().min();
1086 std::vector<FloatType> host_pos;
1087 host_pos.reserve(3ul * pos.size());
1088 assert(lattice.get_blocks()->getNumberOfBlocks() == 1u);
1089 for (auto const &vec : pos) {
1090#pragma unroll
1091 for (std::size_t i : {0ul, 1ul, 2ul}) {
1092 host_pos.emplace_back(static_cast<FloatType>(vec[i] - origin[i]));
1093 }
1094 }
1095 auto const gl = lattice.get_ghost_layers();
1096 auto field =
1097 block.template uncheckedFastGetData<VectorField>(m_velocity_field_id);
1098 auto const res = lbm::accessor::Interpolation::get(field, host_pos, gl);
1099 vel.reserve(res.size() / 3ul);
1100 for (auto it = res.begin(); it != res.end(); it += 3) {
1101 vel.emplace_back(Utils::Vector3d{static_cast<double>(*(it + 0)),
1102 static_cast<double>(*(it + 1)),
1103 static_cast<double>(*(it + 2))});
1104 }
1105 }
1106#endif
1107 return vel;
1108 }
1109
1110 std::optional<Utils::Vector3d>
1112 bool consider_points_in_halo = false) const override {
1113 assert(not m_pending_ghost_comm.test(GhostComm::VEL));
1114 assert(not m_pending_ghost_comm.test(GhostComm::UBB));
1115 if (!consider_points_in_halo and !m_lattice->pos_in_local_domain(pos))
1116 return std::nullopt;
1117 if (consider_points_in_halo and !m_lattice->pos_in_local_halo(pos))
1118 return std::nullopt;
1119 Utils::Vector3d v{0., 0., 0.};
1121 pos, [this, &v, &pos](std::array<int, 3> const node, double weight) {
1122 // Nodes with zero weight might not be accessible, because they can be
1123 // outside ghost layers
1124 if (weight != 0.) {
1125 auto const res = get_node_velocity(Utils::Vector3i(node), true);
1126 if (!res) {
1127 throw interpolation_illegal_access("velocity", pos, node, weight);
1128 }
1129 v += *res * weight;
1130 }
1131 });
1132 return {std::move(v)};
1133 }
1134
1135 std::optional<double>
1137 bool consider_points_in_halo = false) const override {
1138 assert(not m_pending_ghost_comm.test(GhostComm::PDF));
1139 if (!consider_points_in_halo and !m_lattice->pos_in_local_domain(pos))
1140 return std::nullopt;
1141 if (consider_points_in_halo and !m_lattice->pos_in_local_halo(pos))
1142 return std::nullopt;
1143 double dens = 0.;
1145 pos, [this, &dens, &pos](std::array<int, 3> const node, double weight) {
1146 // Nodes with zero weight might not be accessible, because they can be
1147 // outside ghost layers
1148 if (weight != 0.) {
1149 auto const res = get_node_density(Utils::Vector3i(node), true);
1150 if (!res) {
1151 throw interpolation_illegal_access("density", pos, node, weight);
1152 }
1153 dens += *res * weight;
1154 }
1155 });
1156 return {std::move(dens)};
1157 }
1158
1159 // Local force
1161 Utils::Vector3d const &force) override {
1162 if (!m_lattice->pos_in_local_halo(pos))
1163 return false;
1164 auto const force_at_node = [this, &force](std::array<int, 3> const node,
1165 double weight) {
1166 auto bc = get_block_and_cell(get_lattice(), Utils::Vector3i(node), false);
1167 if (!bc) {
1168 bc = get_block_and_cell(get_lattice(), Utils::Vector3i(node), true);
1169 }
1170
1171 if (bc) {
1172 auto const weighted_force = to_vector3<FloatType>(weight * force);
1173 auto force_field =
1174 bc->block->template uncheckedFastGetData<VectorField>(
1176 lbm::accessor::Vector::add(force_field, weighted_force, bc->cell);
1177 }
1178 };
1179 interpolate_bspline_at_pos(pos, force_at_node);
1180 return true;
1181 }
1182
1183 std::optional<Utils::Vector3d>
1184 get_node_force_to_be_applied(Utils::Vector3i const &node) const override {
1185 auto const bc = get_block_and_cell(get_lattice(), node, true);
1186 if (!bc)
1187 return std::nullopt;
1188
1189 auto field =
1190 bc->block->template getData<VectorField>(m_force_to_be_applied_id);
1191 auto const vec = lbm::accessor::Vector::get(field, bc->cell);
1192 return to_vector3d(vec);
1193 }
1194
1195 std::optional<Utils::Vector3d>
1197 bool consider_ghosts = false) const override {
1198 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::LAF)));
1199 auto const bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1200 if (!bc)
1201 return std::nullopt;
1202
1203 auto const field =
1204 bc->block->template getData<VectorField>(m_last_applied_force_field_id);
1205 auto const vec = lbm::accessor::Vector::get(field, bc->cell);
1206 return to_vector3d(vec);
1207 }
1208
1210 Utils::Vector3d const &force) override {
1213 auto bc = get_block_and_cell(get_lattice(), node, false);
1214 if (!bc)
1215 return false;
1216
1217 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1218 auto force_field =
1219 bc->block->template getData<VectorField>(m_last_applied_force_field_id);
1220 auto vel_field =
1221 bc->block->template getData<VectorField>(m_velocity_field_id);
1222 auto const vec = to_vector3<FloatType>(force);
1223 lbm::accessor::Force::set(pdf_field, vel_field, force_field, vec, bc->cell);
1224
1225 return true;
1226 }
1227
1228 std::vector<double> get_slice_last_applied_force(
1229 Utils::Vector3i const &lower_corner,
1230 Utils::Vector3i const &upper_corner) const override {
1231 std::vector<double> out;
1232 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1233 out = std::vector<double>(3u * ci->numCells());
1234 auto const &lattice = get_lattice();
1235 for (auto const &block : *lattice.get_blocks()) {
1236 auto const block_offset = lattice.get_block_corner(block, true);
1237 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1238 block_offset, block)) {
1239 auto const field = block.template getData<VectorField>(
1241 auto const values = lbm::accessor::Vector::get(field, *bci);
1242 assert(values.size() == 3u * bci->numCells());
1243
1244 auto kernel = [&values, &out](unsigned const block_index,
1245 unsigned const local_index,
1246 Utils::Vector3i const &node) {
1247 for (uint_t f = 0u; f < 3u; ++f) {
1248 out[3u * local_index + f] = values[3u * block_index + f];
1249 }
1250 };
1251
1252 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1253 }
1254 }
1255 }
1256 return out;
1257 }
1258
1260 Utils::Vector3i const &upper_corner,
1261 std::vector<double> const &force) override {
1264 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1265 assert(force.size() == 3u * ci->numCells());
1266 auto const &lattice = get_lattice();
1267 for (auto &block : *lattice.get_blocks()) {
1268 auto const block_offset = lattice.get_block_corner(block, true);
1269 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1270 block_offset, block)) {
1271 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1272 auto force_field = block.template getData<VectorField>(
1274 auto vel_field =
1275 block.template getData<VectorField>(m_velocity_field_id);
1276 std::vector<FloatType> values(3u * bci->numCells());
1277
1278 auto kernel = [&values, &force](unsigned const block_index,
1279 unsigned const local_index,
1280 Utils::Vector3i const &node) {
1281 for (uint_t f = 0u; f < 3u; ++f) {
1282 values[3u * block_index + f] =
1283 numeric_cast<FloatType>(force[3u * local_index + f]);
1284 }
1285 };
1286
1287 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1288 lbm::accessor::Force::set(pdf_field, vel_field, force_field, values,
1289 *bci);
1290 }
1291 }
1292 }
1293 }
1294
1295 // Population
1296 std::optional<std::vector<double>>
1298 bool consider_ghosts = false) const override {
1299 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::PDF)));
1300 auto bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1301 if (!bc)
1302 return std::nullopt;
1303
1304 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1305 auto const pop = lbm::accessor::Population::get(pdf_field, bc->cell);
1306 std::vector<double> population(Stencil::Size);
1307 for (uint_t f = 0u; f < Stencil::Size; ++f) {
1308 population[f] = double_c(pop[f]);
1309 }
1310
1311 return {std::move(population)};
1312 }
1313
1315 std::vector<double> const &population) override {
1318 auto bc = get_block_and_cell(get_lattice(), node, false);
1319 if (!bc)
1320 return false;
1321
1322 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1323 auto force_field =
1324 bc->block->template getData<VectorField>(m_last_applied_force_field_id);
1325 auto vel_field =
1326 bc->block->template getData<VectorField>(m_velocity_field_id);
1327 std::array<FloatType, Stencil::Size> pop;
1328 for (uint_t f = 0u; f < Stencil::Size; ++f) {
1329 pop[f] = FloatType_c(population[f]);
1330 }
1331 lbm::accessor::Population::set(pdf_field, vel_field, force_field, pop,
1332 bc->cell);
1333
1334 return true;
1335 }
1336
1337 std::vector<double>
1339 Utils::Vector3i const &upper_corner) const override {
1340 std::vector<double> out;
1341 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1342 out = std::vector<double>(stencil_size() * ci->numCells());
1343 auto const &lattice = get_lattice();
1344 for (auto const &block : *lattice.get_blocks()) {
1345 auto const block_offset = lattice.get_block_corner(block, true);
1346 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1347 block_offset, block)) {
1348 auto const pdf_field =
1349 block.template getData<PdfField>(m_pdf_field_id);
1350 auto const values = lbm::accessor::Population::get(pdf_field, *bci);
1351 assert(values.size() == stencil_size() * bci->numCells());
1352
1353 auto kernel = [&values, &out, this](unsigned const block_index,
1354 unsigned const local_index,
1355 Utils::Vector3i const &node) {
1356 for (uint_t f = 0u; f < stencil_size(); ++f) {
1357 out[stencil_size() * local_index + f] =
1358 values[stencil_size() * block_index + f];
1359 }
1360 };
1361
1362 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1363 }
1364 }
1365 }
1366 return out;
1367 }
1368
1369 void set_slice_population(Utils::Vector3i const &lower_corner,
1370 Utils::Vector3i const &upper_corner,
1371 std::vector<double> const &population) override {
1372 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1373 assert(population.size() == stencil_size() * ci->numCells());
1374 auto const &lattice = get_lattice();
1375 for (auto &block : *lattice.get_blocks()) {
1376 auto const block_offset = lattice.get_block_corner(block, true);
1377 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1378 block_offset, block)) {
1379 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1380 auto force_field = block.template getData<VectorField>(
1382 auto vel_field =
1383 block.template getData<VectorField>(m_velocity_field_id);
1384 std::vector<FloatType> values(stencil_size() * bci->numCells());
1385
1386 auto kernel = [&values, &population,
1387 this](unsigned const block_index,
1388 unsigned const local_index,
1389 Utils::Vector3i const &node) {
1390 for (uint_t f = 0u; f < stencil_size(); ++f) {
1391 values[stencil_size() * block_index + f] =
1392 numeric_cast<FloatType>(
1393 population[stencil_size() * local_index + f]);
1394 }
1395 };
1396
1397 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1398 lbm::accessor::Population::set(pdf_field, vel_field, force_field,
1399 values, *bci);
1400 }
1401 }
1402 }
1403 }
1404
1405 // Density
1406 std::optional<double>
1408 bool consider_ghosts = false) const override {
1409 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::PDF)));
1410 auto bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1411 if (!bc)
1412 return std::nullopt;
1413
1414 auto pdf_field =
1415 bc->block->template uncheckedFastGetData<PdfField>(m_pdf_field_id);
1416 auto const density = lbm::accessor::Density::get(pdf_field, bc->cell);
1417 return {double_c(density)};
1418 }
1419
1420 bool set_node_density(Utils::Vector3i const &node, double density) override {
1422 auto bc = get_block_and_cell(get_lattice(), node, false);
1423 if (!bc)
1424 return false;
1425
1426 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1427 lbm::accessor::Density::set(pdf_field, FloatType_c(density), bc->cell);
1428
1429 return true;
1430 }
1431
1432 std::vector<double>
1434 Utils::Vector3i const &upper_corner) const override {
1435 std::vector<double> out;
1436 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1437 out = std::vector<double>(ci->numCells());
1438 auto const &lattice = get_lattice();
1439 for (auto const &block : *lattice.get_blocks()) {
1440 auto const block_offset = lattice.get_block_corner(block, true);
1441 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1442 block_offset, block)) {
1443 auto const pdf_field =
1444 block.template getData<PdfField>(m_pdf_field_id);
1445 auto const values = lbm::accessor::Density::get(pdf_field, *bci);
1446 assert(values.size() == bci->numCells());
1447
1448 auto kernel = [&values, &out](unsigned const block_index,
1449 unsigned const local_index,
1450 Utils::Vector3i const &) {
1451 out[local_index] = values[block_index];
1452 };
1453
1454 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1455 }
1456 }
1457 }
1458 return out;
1459 }
1460
1461 void set_slice_density(Utils::Vector3i const &lower_corner,
1462 Utils::Vector3i const &upper_corner,
1463 std::vector<double> const &density) override {
1465 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1466 assert(density.size() == ci->numCells());
1467 auto const &lattice = get_lattice();
1468 for (auto &block : *lattice.get_blocks()) {
1469 auto const block_offset = lattice.get_block_corner(block, true);
1470 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1471 block_offset, block)) {
1472 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1473 std::vector<FloatType> values(bci->numCells());
1474
1475 auto kernel = [&values, &density](unsigned const block_index,
1476 unsigned const local_index,
1477 Utils::Vector3i const &node) {
1478 values[block_index] = numeric_cast<FloatType>(density[local_index]);
1479 };
1480
1481 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1482 lbm::accessor::Density::set(pdf_field, values, *bci);
1483 }
1484 }
1485 }
1486 }
1487
1488 std::optional<Utils::Vector3d>
1490 bool consider_ghosts = false) const override {
1491 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::UBB)));
1492 auto const bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1493 if (!bc or !m_boundary->node_is_boundary(node))
1494 return std::nullopt;
1495
1496 return {to_vector3d(m_boundary->get_node_value_at_boundary(node))};
1497 }
1498
1500 Utils::Vector3d const &velocity) override {
1503 auto bc = get_block_and_cell(get_lattice(), node, false);
1504 if (!bc) {
1505 bc = get_block_and_cell(get_lattice(), node, true);
1506 }
1507 if (bc) {
1508 m_boundary->set_node_value_at_boundary(
1509 node, to_vector3<FloatType>(velocity), *bc);
1510 }
1511 return bc.has_value();
1512 }
1513
1514 std::vector<std::optional<Utils::Vector3d>> get_slice_velocity_at_boundary(
1515 Utils::Vector3i const &lower_corner,
1516 Utils::Vector3i const &upper_corner) const override {
1517 std::vector<std::optional<Utils::Vector3d>> out;
1518 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1519 out = std::vector<std::optional<Utils::Vector3d>>(ci->numCells());
1520 auto const &lattice = get_lattice();
1521 for (auto const &block : *lattice.get_blocks()) {
1522 auto const block_offset = lattice.get_block_corner(block, true);
1523 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1524 block_offset, block)) {
1525
1526 auto kernel = [&out, this](unsigned const, unsigned const local_index,
1527 Utils::Vector3i const &node) {
1528 if (m_boundary->node_is_boundary(node)) {
1529 out[local_index] =
1530 to_vector3d(m_boundary->get_node_value_at_boundary(node));
1531 } else {
1532 out[local_index] = std::nullopt;
1533 }
1534 };
1535
1536 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1537 }
1538 }
1539 assert(out.size() == ci->numCells());
1540 }
1541 return out;
1542 }
1543
1545 Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner,
1546 std::vector<std::optional<Utils::Vector3d>> const &velocity) override {
1549 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1550 assert(velocity.size() == ci->numCells());
1551 auto const &lattice = get_lattice();
1552 for (auto &block : *lattice.get_blocks()) {
1553 auto const block_offset = lattice.get_block_corner(block, true);
1554 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1555 block_offset, block)) {
1556
1557 auto kernel = [&lattice, &block, &velocity,
1558 this](unsigned const, unsigned const local_index,
1559 Utils::Vector3i const &node) {
1560 auto const bc = get_block_and_cell(lattice, node, false);
1561 assert(bc->block->getAABB() == block.getAABB());
1562 auto const &opt = velocity[local_index];
1563 if (opt) {
1564 m_boundary->set_node_value_at_boundary(
1565 node, to_vector3<FloatType>(*opt), *bc);
1566 } else {
1567 m_boundary->remove_node_from_boundary(node, *bc);
1568 }
1569 };
1570
1571 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1572 }
1573 }
1574 }
1575 }
1576
1577 std::optional<Utils::Vector3d>
1578 get_node_boundary_force(Utils::Vector3i const &node) const override {
1579 auto const bc = get_block_and_cell(get_lattice(), node, true);
1580 if (!bc or !m_boundary->node_is_boundary(node))
1581 return std::nullopt;
1582
1583 return get_node_last_applied_force(node, true);
1584 }
1585
1586 bool remove_node_from_boundary(Utils::Vector3i const &node) override {
1587 auto bc = get_block_and_cell(get_lattice(), node, false);
1588 if (!bc) {
1589 bc = get_block_and_cell(get_lattice(), node, true);
1590 }
1591 if (bc) {
1592 m_boundary->remove_node_from_boundary(node, *bc);
1593 }
1594 return bc.has_value();
1595 }
1596
1597 std::optional<bool>
1599 bool consider_ghosts = false) const override {
1600 assert(not(consider_ghosts and m_pending_ghost_comm.test(GhostComm::UBB)));
1601 auto const bc = get_block_and_cell(get_lattice(), node, consider_ghosts);
1602 if (!bc)
1603 return std::nullopt;
1604
1605 return {m_boundary->node_is_boundary(node)};
1606 }
1607
1608 std::vector<bool>
1610 Utils::Vector3i const &upper_corner) const override {
1611 std::vector<bool> out;
1612 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1613 out = std::vector<bool>(ci->numCells());
1614 auto const &lattice = get_lattice();
1615 for (auto const &block : *lattice.get_blocks()) {
1616 auto const block_offset = lattice.get_block_corner(block, true);
1617 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1618 block_offset, block)) {
1619
1620 auto kernel = [&out, this](unsigned const, unsigned const local_index,
1621 Utils::Vector3i const &node) {
1622 out[local_index] = m_boundary->node_is_boundary(node);
1623 };
1624
1625 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1626 }
1627 }
1628 assert(out.size() == ci->numCells());
1629 }
1630 return out;
1631 }
1632
1633 void reallocate_ubb_field() override { m_boundary->boundary_update(); }
1634
1636 if (not m_has_boundaries) {
1637 m_has_boundaries = true;
1639 }
1640 m_has_boundaries = true;
1641 }
1642
1643 void clear_boundaries() override {
1644 reset_boundary_handling(get_lattice().get_blocks());
1647 m_has_boundaries = false;
1649 }
1650
1651 void
1652 update_boundary_from_shape(std::vector<int> const &raster_flat,
1653 std::vector<double> const &data_flat) override {
1656 auto const grid_size = get_lattice().get_grid_dimensions();
1657 auto const data = fill_3D_vector_array(data_flat, grid_size);
1658 set_boundary_from_grid(*m_boundary, get_lattice(), raster_flat, data);
1661 }
1662
1663 // Pressure tensor
1664 std::optional<Utils::VectorXd<9>>
1665 get_node_pressure_tensor(Utils::Vector3i const &node) const override {
1666 auto bc = get_block_and_cell(get_lattice(), node, false);
1667 if (!bc)
1668 return std::nullopt;
1669
1670 auto pdf_field = bc->block->template getData<PdfField>(m_pdf_field_id);
1671 auto tensor = lbm::accessor::PressureTensor::get(pdf_field, bc->cell);
1672 pressure_tensor_correction(tensor);
1673
1674 return to_vector9d(tensor);
1675 }
1676
1677 std::vector<double> get_slice_pressure_tensor(
1678 Utils::Vector3i const &lower_corner,
1679 Utils::Vector3i const &upper_corner) const override {
1680 std::vector<double> out;
1681 if (auto const ci = get_interval(lower_corner, upper_corner)) {
1682 out = std::vector<double>(9u * ci->numCells());
1683 auto const &lattice = get_lattice();
1684 for (auto const &block : *lattice.get_blocks()) {
1685 auto const block_offset = lattice.get_block_corner(block, true);
1686 if (auto const bci = get_block_interval(lower_corner, upper_corner,
1687 block_offset, block)) {
1688 auto const pdf_field =
1689 block.template getData<PdfField>(m_pdf_field_id);
1690 auto values = lbm::accessor::PressureTensor::get(pdf_field, *bci);
1691 assert(values.size() == 9u * bci->numCells());
1692
1693 auto kernel = [&values, &out, this](unsigned const block_index,
1694 unsigned const local_index,
1695 Utils::Vector3i const &node) {
1696 pressure_tensor_correction(
1697 std::span<FloatType, 9ul>(&values[9u * block_index], 9ul));
1698 for (uint_t f = 0u; f < 9u; ++f) {
1699 out[9u * local_index + f] = values[9u * block_index + f];
1700 }
1701 };
1702
1703 copy_block_buffer(*bci, *ci, block_offset, lower_corner, kernel);
1704 }
1705 }
1706 }
1707 return out;
1708 }
1709
1710 // Global pressure tensor
1711 [[nodiscard]] Utils::VectorXd<9> get_pressure_tensor() const override {
1712 Matrix3<FloatType> tensor(FloatType{0});
1713 for (auto const &block : *get_lattice().get_blocks()) {
1714 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1715 tensor += lbm::accessor::PressureTensor::reduce(pdf_field);
1716 }
1717 auto const grid_size = get_lattice().get_grid_dimensions();
1718 auto const number_of_nodes = Utils::product(grid_size);
1719 pressure_tensor_correction(tensor);
1720 return to_vector9d(tensor) * (1. / static_cast<double>(number_of_nodes));
1721 }
1722
1723 // Global momentum
1724 [[nodiscard]] Utils::Vector3d get_momentum() const override {
1725 Vector3<FloatType> mom(FloatType{0});
1726 for (auto const &block : *get_lattice().get_blocks()) {
1727 auto pdf_field = block.template getData<PdfField>(m_pdf_field_id);
1728 auto force_field =
1729 block.template getData<VectorField>(m_last_applied_force_field_id);
1730 mom += lbm::accessor::MomentumDensity::reduce(pdf_field, force_field);
1731 }
1732 return to_vector3d(mom);
1733 }
1734
1735 // Global external force
1736 void set_external_force(Utils::Vector3d const &ext_force) override {
1737 m_reset_force->set_ext_force(ext_force);
1738 }
1739
1740 [[nodiscard]] Utils::Vector3d get_external_force() const noexcept override {
1741 return m_reset_force->get_ext_force();
1742 }
1743
1744 [[nodiscard]] double get_kT() const noexcept override {
1745 return numeric_cast<double>(m_kT);
1746 }
1747
1748 [[nodiscard]] unsigned int get_seed() const noexcept override {
1749 return m_seed;
1750 }
1751
1752 [[nodiscard]] std::optional<uint64_t> get_rng_state() const override {
1753 auto const cm = std::get_if<CollisionModelThermalized>(&*m_collision_model);
1754 if (!cm or m_kT == 0.) {
1755 return std::nullopt;
1756 }
1757 return {static_cast<uint64_t>(cm->getTime_step())};
1758 }
1759
1760 void set_rng_state(uint64_t counter) override {
1761 auto const cm = std::get_if<CollisionModelThermalized>(&*m_collision_model);
1762 if (!cm or m_kT == 0.) {
1763 throw std::runtime_error("This LB instance is unthermalized");
1764 }
1765 assert(counter <=
1766 static_cast<uint32_t>(std::numeric_limits<uint_t>::max()));
1767 cm->setTime_step(static_cast<uint32_t>(counter));
1768 }
1769
1770 [[nodiscard]] LatticeWalberla const &get_lattice() const noexcept override {
1771 return *m_lattice;
1772 }
1773
1774 [[nodiscard]] std::size_t get_velocity_field_id() const noexcept override {
1775 return m_velocity_field_id;
1776 }
1777
1778 [[nodiscard]] std::size_t get_force_field_id() const noexcept override {
1780 }
1781
1782 void register_vtk_field_filters(walberla::vtk::VTKOutput &vtk_obj) override {
1783 field::FlagFieldCellFilter<FlagField> fluid_filter(m_flag_field_id);
1784 fluid_filter.addFlag(Boundary_flag);
1785 vtk_obj.addCellExclusionFilter(fluid_filter);
1786 }
1787
1788protected:
1789 template <typename Field_T, uint_t F_SIZE_ARG, typename OutputType>
1790 class VTKWriter : public vtk::BlockCellDataWriter<OutputType, F_SIZE_ARG> {
1791 public:
1792 VTKWriter(ConstBlockDataID const &block_id, std::string const &id,
1793 FloatType unit_conversion)
1794 : vtk::BlockCellDataWriter<OutputType, F_SIZE_ARG>(id),
1795 m_block_id(block_id), m_field(nullptr),
1796 m_conversion(unit_conversion) {}
1797
1798 protected:
1799 void configure() override {
1800 WALBERLA_ASSERT_NOT_NULLPTR(this->block_);
1801 m_field = this->block_->template getData<Field_T>(m_block_id);
1802 }
1803
1804 ConstBlockDataID const m_block_id;
1805 Field_T const *m_field;
1806 FloatType const m_conversion;
1807 };
1808
1809 template <typename OutputType = float>
1810 class DensityVTKWriter : public VTKWriter<PdfField, 1u, OutputType> {
1811 public:
1813 using Base::Base;
1814 using Base::evaluate;
1815
1816 protected:
1817 OutputType evaluate(cell_idx_t const x, cell_idx_t const y,
1818 cell_idx_t const z, cell_idx_t const) override {
1819 WALBERLA_ASSERT_NOT_NULLPTR(this->m_field);
1820 auto const density =
1821 lbm::accessor::Density::get(this->m_field, {x, y, z});
1822 return numeric_cast<OutputType>(this->m_conversion * density);
1823 }
1824 };
1825
1826 template <typename OutputType = float>
1827 class VelocityVTKWriter : public VTKWriter<VectorField, 3u, OutputType> {
1828 public:
1830 using Base::Base;
1831 using Base::evaluate;
1832
1833 protected:
1834 OutputType evaluate(cell_idx_t const x, cell_idx_t const y,
1835 cell_idx_t const z, cell_idx_t const f) override {
1836 WALBERLA_ASSERT_NOT_NULLPTR(this->m_field);
1837 auto const velocity =
1838 lbm::accessor::Vector::get(this->m_field, {x, y, z});
1839 return numeric_cast<OutputType>(this->m_conversion * velocity[uint_c(f)]);
1840 }
1841 };
1842
1843 template <typename OutputType = float>
1844 class PressureTensorVTKWriter : public VTKWriter<PdfField, 9u, OutputType> {
1845 public:
1847 using Base::Base;
1848 using Base::evaluate;
1849
1850 PressureTensorVTKWriter(ConstBlockDataID const &block_id,
1851 std::string const &id, FloatType unit_conversion,
1852 FloatType off_diag_factor)
1853 : Base(block_id, id, unit_conversion),
1854 m_off_diag_factor(off_diag_factor) {}
1855
1856 protected:
1857 OutputType evaluate(cell_idx_t const x, cell_idx_t const y,
1858 cell_idx_t const z, cell_idx_t const f) override {
1859 WALBERLA_ASSERT_NOT_NULLPTR(this->m_field);
1860 auto const pressure =
1862 auto const revert_factor =
1863 (f == 0 or f == 4 or f == 8) ? FloatType{1} : m_off_diag_factor;
1864 return numeric_cast<OutputType>(this->m_conversion * revert_factor *
1865 pressure[uint_c(f)]);
1866 }
1867 FloatType const m_off_diag_factor;
1868 };
1869
1870public:
1871 void register_vtk_field_writers(walberla::vtk::VTKOutput &vtk_obj,
1872 LatticeModel::units_map const &units,
1873 int flag_observables) override {
1874#if defined(__CUDACC__)
1875 auto const allocate_cpu_field_if_empty =
1876 [&]<typename Field>(auto const &blocks, std::string name,
1877 std::optional<BlockDataID> &cpu_field) {
1878 if (not cpu_field) {
1879 cpu_field = field::addToStorage<Field>(
1880 blocks, name, FloatType{0}, field::fzyx,
1881 m_lattice->get_ghost_layers(), m_host_field_allocator);
1882 }
1883 };
1884#endif
1885 if (flag_observables & static_cast<int>(OutputVTK::density)) {
1886 auto const unit_conversion = FloatType_c(units.at("density"));
1887#if defined(__CUDACC__)
1888 if constexpr (Architecture == lbmpy::Arch::GPU) {
1889 auto const &blocks = m_lattice->get_blocks();
1890 allocate_cpu_field_if_empty.template operator()<PdfFieldCpu>(
1891 blocks, "pdfs_cpu", m_pdf_cpu_field_id);
1892 vtk_obj.addBeforeFunction(gpu::fieldCpyFunctor<PdfFieldCpu, PdfField>(
1893 blocks, *m_pdf_cpu_field_id, m_pdf_field_id));
1894 }
1895#endif
1896 vtk_obj.addCellDataWriter(std::make_shared<DensityVTKWriter<float>>(
1897 m_pdf_field_id, "density", unit_conversion));
1898 }
1899 if (flag_observables & static_cast<int>(OutputVTK::velocity_vector)) {
1900 auto const unit_conversion = FloatType_c(units.at("velocity"));
1901#if defined(__CUDACC__)
1902 if constexpr (Architecture == lbmpy::Arch::GPU) {
1903 auto const &blocks = m_lattice->get_blocks();
1904 allocate_cpu_field_if_empty.template operator()<VectorFieldCpu>(
1905 blocks, "vel_cpu", m_vel_cpu_field_id);
1906 vtk_obj.addBeforeFunction(
1907 gpu::fieldCpyFunctor<VectorFieldCpu, VectorField>(
1908 blocks, *m_vel_cpu_field_id, m_velocity_field_id));
1909 }
1910#endif
1911 vtk_obj.addCellDataWriter(std::make_shared<VelocityVTKWriter<float>>(
1912 m_velocity_field_id, "velocity_vector", unit_conversion));
1913 }
1914 if (flag_observables & static_cast<int>(OutputVTK::pressure_tensor)) {
1915 auto const unit_conversion = FloatType_c(units.at("pressure"));
1916#if defined(__CUDACC__)
1917 if constexpr (Architecture == lbmpy::Arch::GPU) {
1918 auto const &blocks = m_lattice->get_blocks();
1919 allocate_cpu_field_if_empty.template operator()<PdfFieldCpu>(
1920 blocks, "pdfs_cpu", m_pdf_cpu_field_id);
1921 vtk_obj.addBeforeFunction(gpu::fieldCpyFunctor<PdfFieldCpu, PdfField>(
1922 blocks, *m_pdf_cpu_field_id, m_pdf_field_id));
1923 }
1924#endif
1925 vtk_obj.addCellDataWriter(
1926 std::make_shared<PressureTensorVTKWriter<float>>(
1927 m_pdf_field_id, "pressure_tensor", unit_conversion,
1928 pressure_tensor_correction_factor()));
1929 }
1930 }
1931
1932 ~LBWalberlaImpl() override = default;
1933};
1934
1935} // namespace walberla
LBWalberlaBase provides the public interface of the LB waLBerla bridge.
Vector implementation and trait types for boost qvm interoperability.
Definition Cell.hpp:97
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:110
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:374
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:173
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:164
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