Loading [MathJax]/extensions/TeX/AMSmath.js
ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
InterpolateAndShiftAtBoundary.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2021-2023 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
23
24#include <blockforest/StructuredBlockForest.h>
25#include <domain_decomposition/all.h>
26#include <stencil/D3Q19.h>
27
28#include <cmath>
29#include <functional>
30#include <memory>
31#include <stdexcept>
32#include <utility>
33
34namespace walberla {
35
36/**
37 * @brief Lees-Edwards sweep.
38 *
39 * @todo Currently is constrained by the blockforest domain decomposition.
40 * It only works if the structured block forest domain decomposition doesn't
41 * partition along the shear direction or the normal direction.
42 * The normal direction cannot be sliced, since we need full access to the
43 * sheared layer population on the opposite side of the box during the
44 * interpolation (we don't use the ghost populations).
45 * The shear direction cannot be sliced, because the ghost layer might not
46 * contain the data if the offset is larger than the ghost layer thickness.
47 *
48 * As a practical example, consider a simulation where the shear direction is
49 * the z-axis, it is possible to run on 2 MPI ranks with MPI Cartesian topology
50 * [2, 1, 1].
51 * At the moment, ESPResSo requires system.cell_system.node_grid to be in
52 * decreasing order, therefore parallelization requires a shear direction
53 * along the z-axis and a MPI node_grid of [x, y, 1] with x >= y.
54 */
55template <class FieldType, typename FloatType>
57public:
59 std::shared_ptr<StructuredBlockForest> blocks, BlockDataID field_id,
60 BlockDataID tmp_field_id, unsigned int n_ghost_layers,
61 unsigned int shear_direction, unsigned int shear_plane_normal,
62 std::function<double()> get_pos_offset,
63 std::function<double()> get_shift = []() { return 0.0; })
64 : m_blocks(std::move(blocks)), m_field_id(field_id),
65 m_tmp_field_id(tmp_field_id), m_n_ghost_layers(uint_c(n_ghost_layers)),
66 m_shear_direction(uint_c(shear_direction)),
67 m_shear_plane_normal(uint_c(shear_plane_normal)),
68 m_get_pos_offset(std::move(get_pos_offset)),
69 m_get_shift(std::move(get_shift)) {
70 if (m_n_ghost_layers != 1u) {
71 throw std::domain_error("The Lees-Edwards sweep is implemented "
72 "for a ghost layer of thickness 1");
73 }
74 if (m_shear_plane_normal == 0u) {
75 m_slab_min = stencil::W;
76 m_slab_max = stencil::E;
77 } else if (m_shear_plane_normal == 1u) {
78 m_slab_min = stencil::S;
79 m_slab_max = stencil::N;
80 } else if (m_shear_plane_normal == 2u) {
81 m_slab_min = stencil::B;
82 m_slab_max = stencil::T;
83 }
84 }
85
86 FloatType get_pos_offset() const {
87 return numeric_cast<FloatType>(m_get_pos_offset());
88 }
89
90 FloatType get_shift() const { return numeric_cast<FloatType>(m_get_shift()); }
91
92 void operator()(IBlock *block) {
93 kernel(block, m_slab_min);
94 kernel(block, m_slab_max);
95 }
96
97private:
98 void kernel(IBlock *block, stencil::Direction slab_dir) {
99 // setup lengths
100 assert(m_blocks->getNumberOfCells(*block, m_shear_plane_normal) >= 2u);
101 auto const dir = m_shear_direction;
102 auto const dim = cell_idx_c(m_blocks->getNumberOfCells(*block, dir));
103 auto const length = numeric_cast<FloatType>(dim);
104
105 // setup slab
106 auto field = block->template getData<FieldType>(m_field_id);
107 auto tmp_field = block->template getData<FieldType>(m_tmp_field_id);
108
109 CellInterval ci;
110 field->getGhostRegion(slab_dir, ci, cell_idx_t{1}, true);
111
112 // shift
113 auto const shift = get_shift();
114 // Note that the offset is applied to the interpolation source rather than
115 // the target
116 auto const prefactor =
117 ((slab_dir == m_slab_max) ? FloatType{-1} : FloatType{1});
118 auto const offset = static_cast<FloatType>(get_pos_offset()) * prefactor;
119 auto const folded_offset = modulo(offset, length);
120 // 0<=folded_offset<length
121 auto const weight1 = FloatType{1} - std::fmod(folded_offset, FloatType{1});
122 auto const weight2 = std::fmod(folded_offset, FloatType{1});
123 for (auto const &&cell : ci) {
124 Cell source1 = cell;
125 Cell source2 = cell;
126 auto const source_pos = static_cast<FloatType>(cell[dir]) + folded_offset;
127 auto const folded_source_pos = modulo(source_pos, length);
128 // 0 <= folded_source_pos < length
129 source1[dir] = cell_idx_c(std::floor(folded_source_pos));
130 // 0 <= source1[dir] < length, i.e. integer value up to length-1 inclusive
131 source2[dir] = cell_idx_c(modulo(FloatType(source1[dir] + 1), length));
132 // integer value between 0 and length -1 inclusive
133 for (uint_t q = 0u; q < FieldType::F_SIZE; ++q) {
134 tmp_field->get(cell, q) =
135 field->get(source1, q) * weight1 + field->get(source2, q) * weight2;
136 }
137 tmp_field->get(cell, m_shear_direction) -= prefactor * shift;
138 }
139
140 // swap
141 for (auto const &&cell : ci) {
142 for (uint_t f = 0; f < FieldType::F_SIZE; ++f) {
143 field->get(cell, f) = tmp_field->get(cell, f);
144 }
145 }
146 }
147
148 FloatType modulo(FloatType a, FloatType b) const {
149 auto const res = std::fmod(a, b);
150 return (res < FloatType{0}) ? res + b : res;
151 }
152
153private:
154 std::shared_ptr<StructuredBlockForest> m_blocks;
155 BlockDataID m_field_id;
156 BlockDataID m_tmp_field_id;
157 uint_t m_n_ghost_layers;
158 uint_t m_shear_direction;
159 uint_t m_shear_plane_normal;
160 std::function<double()> m_get_pos_offset;
161 std::function<double()> m_get_shift;
162 stencil::Direction m_slab_min;
163 stencil::Direction m_slab_max;
164};
165
166} // namespace walberla
Definition Cell.hpp:96
InterpolateAndShiftAtBoundary(std::shared_ptr< StructuredBlockForest > blocks, BlockDataID field_id, BlockDataID tmp_field_id, unsigned int n_ghost_layers, unsigned int shear_direction, unsigned int shear_plane_normal, std::function< double()> get_pos_offset, std::function< double()> get_shift=[]() { return 0.0;})
static double * block(double *p, std::size_t index, std::size_t size)
Definition elc.cpp:172
\file PackInfoPdfDoublePrecision.cpp \author pystencils