Loading [MathJax]/extensions/tex2jax.js
ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
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