22#include <blockforest/communication/UniformBufferedScheme.h>
23#include <field/AddToStorage.h>
24#include <field/FlagField.h>
25#include <field/FlagUID.h>
26#include <field/GhostLayerField.h>
27#include <field/communication/PackInfo.h>
28#include <field/vtk/FlagFieldCellFilter.h>
29#include <field/vtk/VTKWriter.h>
30#include <stencil/D3Q27.h>
32#include "../BoundaryHandling.hpp"
33#include "../utils/boundary.hpp"
34#include "../utils/types_conversion.hpp"
56template <std::
size_t FluxCount = 13,
typename FloatType =
double>
58 using ContinuityKernel =
60 using DiffusiveFluxKernelUnthermalized =
62 using DiffusiveFluxKernelThermalized =
64 using AdvectiveFluxKernel =
66 using FrictionCouplingKernel =
68 using DiffusiveFluxKernelElectrostaticUnthermalized =
70 using DiffusiveFluxKernelElectrostaticThermalized =
71 typename detail::KernelTrait<
72 FloatType>::DiffusiveFluxKernelElectrostaticThermalized;
74 using DiffusiveFluxKernel = std::variant<DiffusiveFluxKernelUnthermalized,
75 DiffusiveFluxKernelThermalized>;
76 using DiffusiveFluxKernelElectrostatic =
77 std::variant<DiffusiveFluxKernelElectrostaticUnthermalized,
78 DiffusiveFluxKernelElectrostaticThermalized>;
85 using FluxField = GhostLayerField<FloatType, FluxCount>;
86 using FlagField = walberla::FlagField<walberla::uint8_t>;
94 return numeric_cast<FloatType>(t);
102 return std::is_same<FloatType, double>::value;
106 FloatType m_diffusion;
111 bool m_friction_coupling;
137 std::unique_ptr<DiffusiveFluxKernelElectrostatic>
145 [[nodiscard]] std::optional<CellInterval>
149 auto const &cell_min = lower_corner;
153 if (not lower_bc or not upper_bc) {
156 assert(&(*(lower_bc->block)) == &(*(upper_bc->block)));
157 return {CellInterval(lower_bc->cell, upper_bc->cell)};
173 typename stencil::D3Q27>;
179 double density,
bool advection,
bool friction_coupling,
180 bool thermalized,
unsigned int seed)
182 m_valency(
FloatType_c(valency)), m_ext_efield(ext_efield),
183 m_advection(advection), m_friction_coupling(friction_coupling),
184 m_seed(seed),
m_lattice(std::move(lattice)) {
187 field::fzyx,
m_lattice->get_ghost_layers());
189 field::addFlattenedShallowCopyToStorage<DensityField>(
191 "flattened density field");
193 m_lattice->get_blocks(),
"flux field", FloatType{0}, field::fzyx,
196 field::addFlattenedShallowCopyToStorage<FluxField>(
203 set_diffusion_kernels(seed);
205 set_diffusion_kernels();
210 m_lattice->get_blocks(),
"flag field density",
215 m_lattice->get_blocks(),
"flag field flux",
220 std::make_shared<FullCommunicator>(
m_lattice->get_blocks());
222 std::make_shared<field::communication::PackInfo<DensityField>>(
233 [[nodiscard]]
double get_kT() const noexcept
override {
return m_kT; }
241 return m_friction_coupling;
247 return static_cast<bool>(
250 [[nodiscard]]
unsigned int get_seed() const noexcept
override {
259 return {
static_cast<uint64_t
>(kernel->time_step_)};
264 auto visitor = [m_diffusion = m_diffusion](
auto &kernel) {
265 kernel.D_ = m_diffusion;
273 std::visit([m_kT = m_kT](
auto &kernel) { kernel.kT_ = m_kT; },
279 std::visit([m_valency = m_valency](
auto &kernel) { kernel.z_ = m_valency; },
286 m_friction_coupling = friction_coupling;
292 auto const kernel_electrostatic =
293 std::get_if<DiffusiveFluxKernelElectrostaticThermalized>(
296 if (!kernel or !kernel_electrostatic) {
297 throw std::runtime_error(
"This EK instance is unthermalized");
300 static_cast<uint32_t
>(std::numeric_limits<uint_t>::max()));
301 kernel->time_step_ =
static_cast<uint32_t
>(counter);
302 kernel_electrostatic->time_step_ =
static_cast<uint32_t
>(counter);
306 m_ext_efield = field;
309 [
this](
auto &kernel) {
320 void set_diffusion_kernels() {
324 m_diffusive_flux = std::make_unique<DiffusiveFluxKernel>(std::move(kernel));
326 auto kernel_electrostatic = DiffusiveFluxKernelElectrostaticUnthermalized(
333 std::make_unique<DiffusiveFluxKernelElectrostatic>(
334 std::move(kernel_electrostatic));
337 void set_diffusion_kernels(
unsigned int seed) {
340 auto kernel = DiffusiveFluxKernelThermalized(
342 FloatType_c(m_diffusion), grid_dim[0], grid_dim[1], grid_dim[2], seed,
345 auto kernel_electrostatic = DiffusiveFluxKernelElectrostaticThermalized(
354 for (
auto b = blocks->begin(); b != blocks->end(); ++b) {
355 kernel.configure(blocks, &*b);
356 kernel_electrostatic.configure(blocks, &*b);
359 m_diffusive_flux = std::make_unique<DiffusiveFluxKernel>(std::move(kernel));
361 std::make_unique<DiffusiveFluxKernelElectrostatic>(
362 std::move(kernel_electrostatic));
365 void kernel_boundary_density() {
367 (*m_boundary_density)(&
block);
371 void kernel_boundary_flux() {
373 (*m_boundary_flux)(&
block);
377 void kernel_continuity() {
379 (*m_continuity).run(&
block);
383 void kernel_diffusion() {
385 std::visit([&
block](
auto &kernel) { kernel.run(&
block); },
391 kernel->time_step_++;
393 auto *kernel_electrostatic =
394 std::get_if<DiffusiveFluxKernelElectrostaticThermalized>(
396 kernel_electrostatic->time_step_++;
400 void kernel_advection(
const std::size_t &velocity_id) {
403 BlockDataID(velocity_id));
409 void kernel_friction_coupling(
const std::size_t &force_id) {
410 auto kernel = FrictionCouplingKernel(
418 void kernel_diffusion_electrostatic(
const std::size_t &potential_id) {
419 auto const phiID = BlockDataID(potential_id);
420 std::visit([phiID](
auto &kernel) { kernel.phiID = phiID; },
424 std::visit([&
block](
auto &kernel) { kernel.run(&
block); },
428 if (
auto *kernel_electrostatic =
429 std::get_if<DiffusiveFluxKernelElectrostaticThermalized>(
431 kernel_electrostatic->time_step_++;
435 kernel->time_step_++;
439 void kernel_migration() {}
441 void updated_boundary_fields() {
449 auto &vtk_handle = it.second;
450 if (vtk_handle->enabled) {
451 vtk::writeFiles(vtk_handle->ptr)();
452 vtk_handle->execution_count++;
458 void integrate(std::size_t potential_id, std::size_t velocity_id,
459 std::size_t force_id)
override {
461 updated_boundary_fields();
467 if (potential_id == walberla::BlockDataID{}) {
468 throw std::runtime_error(
"Walberla EK: electrostatic potential enabled "
469 "but no field accessible. potential id is " +
470 std::to_string(potential_id));
472 kernel_diffusion_electrostatic(potential_id);
478 kernel_boundary_flux();
481 if (force_id == walberla::BlockDataID{}) {
482 throw std::runtime_error(
"Walberla EK: friction coupling enabled but "
483 "no force field accessible. force_id is " +
484 std::to_string(force_id) +
485 ". Hint: LB may be inactive.");
487 kernel_friction_coupling(force_id);
491 if (velocity_id == walberla::BlockDataID{}) {
492 throw std::runtime_error(
"Walberla EK: advection enabled but no "
493 "velocity field accessible. velocity_id is " +
494 std::to_string(velocity_id) +
495 ". Hint: LB may be inactive.");
497 kernel_advection(velocity_id);
502 kernel_boundary_density();
509 static_assert(std::is_same_v<std::size_t, walberla::uint_t>);
525 [[nodiscard]] std::optional<double>
527 bool consider_ghosts =
false)
const override {
533 auto const density_field =
536 return {double_c(density_field->get(bc->cell))};
539 [[nodiscard]] std::vector<double>
542 std::vector<double> out;
543 if (
auto const ci =
get_interval(lower_corner, upper_corner)) {
545 auto const &
block = *(lattice.get_blocks()->begin());
546 auto const density_field =
548 auto const lower_cell = ci->min();
549 auto const upper_cell = ci->max();
550 auto const n_values = ci->numCells();
551 out.reserve(n_values);
552 for (
auto x = lower_cell.x(); x <= upper_cell.x(); ++x) {
553 for (
auto y = lower_cell.y(); y <= upper_cell.y(); ++y) {
554 for (
auto z = lower_cell.z(); z <= upper_cell.z(); ++z) {
555 out.emplace_back(density_field->get(
Cell{x, y, z}));
559 assert(out.size() == n_values);
566 std::vector<double>
const &
density)
override {
567 if (
auto const ci =
get_interval(lower_corner, upper_corner)) {
569 auto &
block = *(lattice.get_blocks()->begin());
573 auto const lower_cell = ci->min();
574 auto const upper_cell = ci->max();
575 assert(
density.size() == ci->numCells());
576 for (
auto x = lower_cell.x(); x <= upper_cell.x(); ++x) {
577 for (
auto y = lower_cell.y(); y <= upper_cell.y(); ++y) {
578 for (
auto z = lower_cell.z(); z <= upper_cell.z(); ++z) {
600 node, to_vector3<FloatType>(flux), *bc);
605 [[nodiscard]] std::optional<Utils::Vector3d>
607 bool consider_ghosts =
false)
const override {
637 [[nodiscard]] std::optional<double>
639 bool consider_ghosts =
false)
const override {
649 std::vector<std::optional<double>>
const &
density)
override {
650 if (
auto const ci =
get_interval(lower_corner, upper_corner)) {
652 auto const local_offset = std::get<0>(lattice.get_local_grid_range());
653 auto const lower_cell = ci->min();
654 auto const upper_cell = ci->max();
656 assert(
density.size() == ci->numCells());
657 for (
auto x = lower_cell.x(); x <= upper_cell.x(); ++x) {
658 for (
auto y = lower_cell.y(); y <= upper_cell.y(); ++y) {
659 for (
auto z = lower_cell.z(); z <= upper_cell.z(); ++z) {
662 auto const &opt = *it;
676 [[nodiscard]] std::vector<std::optional<double>>
680 std::vector<std::optional<double>> out;
681 if (
auto const ci =
get_interval(lower_corner, upper_corner)) {
683 auto const local_offset = std::get<0>(lattice.get_local_grid_range());
684 auto const lower_cell = ci->min();
685 auto const upper_cell = ci->max();
686 auto const n_values = ci->numCells();
687 out.reserve(n_values);
688 for (
auto x = lower_cell.x(); x <= upper_cell.x(); ++x) {
689 for (
auto y = lower_cell.y(); y <= upper_cell.y(); ++y) {
690 for (
auto z = lower_cell.z(); z <= upper_cell.z(); ++z) {
693 out.emplace_back(double_c(
696 out.emplace_back(std::nullopt);
701 assert(out.size() == n_values);
708 std::vector<std::optional<Utils::Vector3d>>
const &flux)
override {
709 if (
auto const ci =
get_interval(lower_corner, upper_corner)) {
711 auto const local_offset = std::get<0>(lattice.get_local_grid_range());
712 auto const lower_cell = ci->min();
713 auto const upper_cell = ci->max();
714 auto it = flux.begin();
715 assert(flux.size() == ci->numCells());
716 for (
auto x = lower_cell.x(); x <= upper_cell.x(); ++x) {
717 for (
auto y = lower_cell.y(); y <= upper_cell.y(); ++y) {
718 for (
auto z = lower_cell.z(); z <= upper_cell.z(); ++z) {
721 auto const &opt = *it;
724 node, to_vector3<FloatType>(*opt), *bc);
735 [[nodiscard]] std::vector<std::optional<Utils::Vector3d>>
739 std::vector<std::optional<Utils::Vector3d>> out;
740 if (
auto const ci =
get_interval(lower_corner, upper_corner)) {
742 auto const local_offset = std::get<0>(lattice.get_local_grid_range());
743 auto const lower_cell = ci->min();
744 auto const upper_cell = ci->max();
745 auto const n_values = ci->numCells();
746 out.reserve(n_values);
747 for (
auto x = lower_cell.x(); x <= upper_cell.x(); ++x) {
748 for (
auto y = lower_cell.y(); y <= upper_cell.y(); ++y) {
749 for (
auto z = lower_cell.z(); z <= upper_cell.z(); ++z) {
755 out.emplace_back(std::nullopt);
760 assert(out.size() == n_values);
765 [[nodiscard]] std::vector<bool>
768 std::vector<bool> out;
769 if (
auto const ci =
get_interval(lower_corner, upper_corner)) {
771 auto const local_offset = std::get<0>(lattice.get_local_grid_range());
772 auto const lower_cell = ci->min();
773 auto const upper_cell = ci->max();
774 auto const n_values = ci->numCells();
775 out.reserve(n_values);
776 for (
auto x = lower_cell.x(); x <= upper_cell.x(); ++x) {
777 for (
auto y = lower_cell.y(); y <= upper_cell.y(); ++y) {
778 for (
auto z = lower_cell.z(); z <= upper_cell.z(); ++z) {
785 assert(out.size() == n_values);
800 [[nodiscard]] std::optional<bool>
802 bool consider_ghosts)
const override {
810 [[nodiscard]] std::optional<bool>
812 bool consider_ghosts)
const override {
820 [[nodiscard]] std::optional<bool>
822 bool consider_ghosts =
false)
const override {
832 const std::vector<int> &raster_flat,
833 const std::vector<double> &data_flat)
override {
841 const std::vector<int> &raster_flat,
842 const std::vector<double> &data_flat)
override {
863 vtk_obj.addCellExclusionFilter(fluid_filter);
867 template <
typename Field_T, u
int_t F_SIZE_ARG,
typename OutputType>
868 class VTKWriter :
public vtk::BlockCellDataWriter<OutputType, F_SIZE_ARG> {
870 VTKWriter(ConstBlockDataID
const &block_id, std::string
const &
id,
871 FloatType unit_conversion)
872 : vtk::BlockCellDataWriter<OutputType, F_SIZE_ARG>(id),
878 WALBERLA_ASSERT_NOT_NULLPTR(this->block_);
887 template <
typename OutputType = float,
894 OutputType
evaluate(cell_idx_t
const x, cell_idx_t
const y,
895 cell_idx_t
const z, cell_idx_t
const)
override {
896 WALBERLA_ASSERT_NOT_NULLPTR(this->
m_field);
897 auto const density = VectorTrait<typename DensityField::value_type>::get(
898 this->
m_field->get(x, y, z, 0), uint_c(0));
906 int flag_observables)
override {
908 auto const unit_conversion =
FloatType_c(units.at(
"density"));
Vector implementation and trait types for boost qvm interoperability.
Interface of a lattice-based electrokinetic 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.
auto get_grid_dimensions() const
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value)
Create a vector that has all entries set to the same value.
Boundary class optimized for sparse data.
OutputType evaluate(cell_idx_t const x, cell_idx_t const y, cell_idx_t const z, cell_idx_t const) override
FloatType const m_conversion
void configure() override
VTKWriter(ConstBlockDataID const &block_id, std::string const &id, FloatType unit_conversion)
ConstBlockDataID const m_block_id
Class that runs and controls the EK on waLBerla.
void update_density_boundary_from_shape(const std::vector< int > &raster_flat, const std::vector< double > &data_flat) override
double get_kT() const noexcept override
std::optional< CellInterval > get_interval(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const
blockforest::communication::UniformBufferedScheme< typename stencil::D3Q27 > FullCommunicator
std::shared_ptr< LatticeWalberla > m_lattice
Block forest.
bool get_friction_coupling() const noexcept override
void set_friction_coupling(bool friction_coupling) override
void register_vtk_field_writers(walberla::vtk::VTKOutput &vtk_obj, LatticeModel::units_map const &units, int flag_observables) override
std::vector< std::optional< double > > get_slice_density_at_boundary(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
void set_slice_density_boundary(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, std::vector< std::optional< double > > const &density) override
~EKinWalberlaImpl() override=default
double get_valency() const noexcept override
std::optional< uint64_t > get_rng_state() const override
void register_vtk_field_filters(walberla::vtk::VTKOutput &vtk_obj) override
void set_rng_state(uint64_t counter) override
std::unique_ptr< DiffusiveFluxKernelElectrostatic > m_diffusive_flux_electrostatic
walberla::FlagField< walberla::uint8_t > FlagField
void set_slice_flux_boundary(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, std::vector< std::optional< Utils::Vector3d > > const &flux) override
bool remove_node_from_flux_boundary(Utils::Vector3i const &node) override
void clear_flux_boundaries() override
bool remove_node_from_density_boundary(Utils::Vector3i const &node) override
std::optional< Utils::Vector3d > get_node_flux_at_boundary(Utils::Vector3i const &node, bool consider_ghosts=false) const override
void set_diffusion(double diffusion) override
EKinWalberlaImpl(std::shared_ptr< LatticeWalberla > lattice, double diffusion, double kT, double valency, Utils::Vector3d const &ext_efield, double density, bool advection, bool friction_coupling, bool thermalized, unsigned int seed)
std::size_t stencil_size() const noexcept override
BlockDataID m_flag_field_density_id
void update_flux_boundary_from_shape(const std::vector< int > &raster_flat, const std::vector< double > &data_flat) override
bool set_node_density_boundary(Utils::Vector3i const &node, double density) override
bool is_thermalized() const noexcept override
void set_ext_efield(Utils::Vector3d const &field) override
std::vector< double > get_slice_density(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
std::unique_ptr< BoundaryModelDensity > m_boundary_density
std::optional< bool > get_node_is_flux_boundary(Utils::Vector3i const &node, bool consider_ghosts) const override
BlockDataID m_flux_field_id
unsigned int get_seed() const noexcept override
BlockDataID m_density_field_flattened_id
bool get_advection() const noexcept override
std::size_t get_density_id() const noexcept override
GhostLayerField< FloatType, FluxCount > FluxField
FloatType FloatType_c(T t)
void integrate_vtk_writers() override
double get_diffusion() const noexcept override
FlagUID const Boundary_flag
Flag for boundary cells.
void reset_density_boundary_handling()
BlockDataID m_density_field_id
bool set_node_flux_boundary(Utils::Vector3i const &node, Utils::Vector3d const &flux) override
std::optional< double > get_node_density(Utils::Vector3i const &node, bool consider_ghosts=false) const override
void reset_flux_boundary_handling()
std::shared_ptr< FullCommunicator > m_full_communication
std::unique_ptr< BoundaryModelFlux > m_boundary_flux
void set_slice_density(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, std::vector< double > const &density) override
FlagUID const Domain_flag
Flag for domain cells, i.e.
bool is_double_precision() const noexcept override
void ghost_communication() override
void reallocate_density_boundary_field()
void set_kT(double kT) override
void set_valency(double valency) override
std::optional< double > get_node_density_at_boundary(Utils::Vector3i const &node, bool consider_ghosts=false) const override
void integrate(std::size_t potential_id, std::size_t velocity_id, std::size_t force_id) override
std::optional< bool > get_node_is_density_boundary(Utils::Vector3i const &node, bool consider_ghosts) const override
void reallocate_flux_boundary_field()
GhostLayerField< FloatType, 1 > DensityField
Utils::Vector3d get_ext_efield() const noexcept override
LatticeWalberla const & get_lattice() const noexcept override
bool set_node_density(Utils::Vector3i const &node, double density) override
std::unique_ptr< ContinuityKernel > m_continuity
BlockDataID m_flux_field_flattened_id
void clear_density_boundaries() override
std::optional< bool > get_node_is_boundary(Utils::Vector3i const &node, bool consider_ghosts=false) const override
std::unique_ptr< DiffusiveFluxKernel > m_diffusive_flux
void set_advection(bool advection) override
BlockDataID m_flag_field_flux_id
std::vector< std::optional< Utils::Vector3d > > get_slice_flux_at_boundary(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
std::vector< bool > get_slice_is_boundary(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
static double * block(double *p, std::size_t index, std::size_t size)
static FUNC_PREFIX double *RESTRICT int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const uint32_t uint32_t uint32_t double double double double double uint32_t seed
\file PackInfoPdfDoublePrecision.cpp \author pystencils
std::optional< BlockAndCell > get_block_and_cell(::LatticeWalberla const &lattice, Utils::Vector3i const &node, bool consider_ghost_layers)
std::vector< double > fill_3D_scalar_array(std::vector< double > const &vec_flat, Utils::Vector3i const &grid_size)
void set_boundary_from_grid(BoundaryModel &boundary, LatticeWalberla const &lattice, std::vector< int > const &raster_flat, std::vector< DataType > const &data_flat)
std::vector< Utils::Vector3d > fill_3D_vector_array(std::vector< double > const &vec_flat, Utils::Vector3i const &grid_size)
Utils::Vector3d to_vector3d(Vector3< float > const &v)