27 std::vector<double> out((std::size_t)(nInst_ > 0 ? nInst_ : 0) * 12, 0.0);
28 if (!hasScene_ || nInst_ <= 0 || cutOwner_.extent(0) != (std::size_t)nx_ * ny_ * nz_)
30 const std::size_t m = (std::size_t)nInst_ * 3;
31 Kokkos::View<double*, CCMem>
Fd(
"hydroF", m),
Td(
"hydroT", m),
Pd(
"hydroFp", m),
Vd(
"hydroFv", m);
32 Kokkos::deep_copy(
Fd, 0.0);
33 Kokkos::deep_copy(
Td, 0.0);
34 Kokkos::deep_copy(
Pd, 0.0);
35 Kokkos::deep_copy(
Vd, 0.0);
37 const C3 e = e_, og = og_;
38 const int nx = nx_,
ny = ny_;
39 const double mu = mu_;
48 const bool haveWallVel = (uwCell_[0].extent(0) == n_);
55 const auto box = sceneQ_->view().box;
59 "peclet::flow::hydro_force",
MD(
space, {
G,
G,
G}, {e.x -
G, e.y -
G, e.z -
G}),
61 const long st[3] = {1, e.x, (
long)e.x * e.y};
62 const long i = (
long)x + (
long)y * st[1] + (
long)z * st[2];
63 const double A[3] = {-(
oxv(i + st[0]) -
oxv(i)), -(
oyv(i + st[1]) -
oyv(i)),
64 -(
ozv(i + st[2]) -
ozv(i))};
65 if (A[0] == 0.0 && A[1] == 0.0 && A[2] == 0.0)
67 const int oi =
own((std::size_t)(x -
G) + (std::size_t)(y -
G) *
nx +
68 (std::size_t)(z -
G) * (std::size_t)
nx *
ny);
82 auto uc = [&](
int a,
long c) {
84 return a == 0 ?
wcx(c) : (a == 1 ?
wcy(c) :
wcz(c));
86 return 0.5 * (
F(c) +
F(c + st[a]));
99 for (
int a = 0; a < 3; ++a)
100 for (
int b = 0; b < 3; ++b)
101 gu[a][b] = 0.5 * (
uc(a, i + st[b]) -
uc(a, i - st[b]));
102 const double p = Pf(i);
105 double dF[3],
dFp[3],
dFv[3];
106 for (
int a = 0; a < 3; ++a) {
112 dFp[a] = (p * A[a]) * kA[a];
114 for (
int b = 0; b < 3; ++b) {
116 t += mu * (
rab *
gu[a][b] +
rba *
gu[b][a]) * (A[b] * kA[b]);
124 const peclet::core::Vec3<double>
rp = peclet::core::geom::minImage(
125 peclet::core::Vec3<double>{
126 sm.a[0] +
sm.b[0] * (
double)(x -
G + og.x) - cen[3 *
oi + 0],
127 sm.a[1] +
sm.b[1] * (
double)(y -
G + og.y) - cen[3 *
oi + 1],
128 sm.a[2] +
sm.b[2] * (
double)(z -
G + og.z) - cen[3 *
oi + 2]},
130 const peclet::core::Vec3<double> r{
rp.x *
sm.dToInt,
rp.y *
sm.dToInt,
rp.z *
sm.dToInt};
131 for (
int a = 0; a < 3; ++a) {
132 Kokkos::atomic_add(&
Fd(3 *
oi + a), dF[a]);
133 Kokkos::atomic_add(&
Pd(3 *
oi + a),
dFp[a]);
134 Kokkos::atomic_add(&
Vd(3 *
oi + a),
dFv[a]);
136 Kokkos::atomic_add(&
Td(3 *
oi + 0), r.y * dF[2] - r.z * dF[1]);
137 Kokkos::atomic_add(&
Td(3 *
oi + 1), r.z * dF[0] - r.x * dF[2]);
138 Kokkos::atomic_add(&
Td(3 *
oi + 2), r.x * dF[1] - r.y * dF[0]);
141 using HostV = Kokkos::View<double*, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
142 Kokkos::deep_copy(
HostV(out.data(), m),
Fd);
143 Kokkos::deep_copy(
HostV(out.data() + m, m),
Td);
144 Kokkos::deep_copy(
HostV(out.data() + 2 * m, m),
Pd);
145 Kokkos::deep_copy(
HostV(out.data() + 3 * m, m),
Vd);
151#ifdef PECLET_FLOW_MPI
155 std::vector<double> g(out.size(), 0.0);
165 std::vector<double> out((std::size_t)(nInst_ > 0 ? nInst_ : 0) * 6, 0.0);
166 if (!hasScene_ || nInst_ <= 0)
168 if constexpr (Grid::collocated)
169 throw std::runtime_error(
"hydro_force_torque_reaction: staggered only (v1)");
170 if (
implicitAdv() || porous_ || varRho_ || varProps_ || hasBc_ || ghostProjection_ || hasDrag_ ||
172 throw std::runtime_error(
173 "hydro_force_torque_reaction: implicit advection / porous / variable-properties / "
174 "domain-BC / ghost-projection / drag / star modes put momentum terms in the step that "
175 "this budget does not carry (v2) -- a missing term is a silently mis-attributed force");
177 throw std::runtime_error(
178 "hydro_force_torque_reaction: call step() first (u* is stashed "
180 if (advect_ && (!haveAdvRhs_ || advRhs_[0].extent(0) != n_))
181 throw std::runtime_error(
182 "hydro_force_torque_reaction: the advective RHS term was not "
183 "stashed -- set_advection was enabled after the last step()");
186 for (
int c = 0; c < 3; ++c)
188 const std::size_t m = (std::size_t)nInst_ * 3;
189 Kokkos::View<double*, CCMem>
Fd(
"reactF", m),
Td(
"reactT", m);
190 Kokkos::deep_copy(
Fd, 0.0);
191 Kokkos::deep_copy(
Td, 0.0);
193 const C3 e = e_, og = og_;
194 const double idt = rho_ / dt_, mu = mu_;
195 const auto q = sceneQ_->view();
196 auto cen = instCenD_;
197 const auto box = q.box;
199 const bool hasFb = hasCellForce_;
205 for (
int c = 0; c < 3; ++c) {
210 const double fc = f_[c];
211 const auto po = Grid::offset(c);
214 const double kRc =
kR[c];
215 Kokkos::parallel_for(
216 "peclet::flow::hydro_reaction",
219 const long st[3] = {1, e.x, (
long)e.x * e.y};
220 const long i = (
long)x + (
long)y * st[1] + (
long)z * st[2];
224 double R = idt * (
uc(i) -
un(i)) - fc -
225 (
fb.data() ? Grid::atVelocity(
fb, i, st[cc]) : 0.0) -
226 (
av.data() ?
av(i) : 0.0);
227 for (
int a = 0; a < 3; ++a) {
228 const long jp = i + st[a],
jm = i - st[a];
230 R -= mu * (us(
jp) - us(i));
232 R -= mu * (us(
jm) - us(i));
236 const peclet::core::Vec3<double> p{
sm.a[0] +
sm.b[0] * ((
double)(x -
G + og.x) +
offx),
237 sm.a[1] +
sm.b[1] * ((
double)(y -
G + og.y) +
offy),
239 const int oi = q.owner(p);
243 const double F = -R *
kRc;
244 Kokkos::atomic_add(&
Fd(3 *
oi + cc),
F);
245 const peclet::core::Vec3<double>
rq = peclet::core::geom::minImage(
246 peclet::core::Vec3<double>{p.x - cen(3 *
oi + 0), p.y - cen(3 *
oi + 1),
247 p.z - cen(3 *
oi + 2)},
249 const peclet::core::Vec3<double> r{
rq.x *
sm.dToInt,
rq.y *
sm.dToInt,
rq.z *
sm.dToInt};
252 Kokkos::atomic_add(&
Td(3 *
oi + 1), r.z *
F);
253 Kokkos::atomic_add(&
Td(3 *
oi + 2), -r.y *
F);
254 }
else if (cc == 1) {
255 Kokkos::atomic_add(&
Td(3 *
oi + 0), -r.z *
F);
256 Kokkos::atomic_add(&
Td(3 *
oi + 2), r.x *
F);
258 Kokkos::atomic_add(&
Td(3 *
oi + 0), r.y *
F);
259 Kokkos::atomic_add(&
Td(3 *
oi + 1), -r.x *
F);
271 if (hasMotion_ && cutcellPressure_) {
273 auto ang = instAngD_;
274 Kokkos::parallel_for(
275 "peclet::flow::hydro_reaction_torque_transpose",
278 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
279 const long i = (
long)x + (
long)y * sy + (
long)z * sz;
280 const double ax =
oxv(i +
sx) -
oxv(i);
281 const double ay =
oyv(i + sy) -
oyv(i);
282 const double az =
ozv(i + sz) -
ozv(i);
283 if (ax == 0.0 && ay == 0.0 &&
az == 0.0)
292 const peclet::core::Vec3<double> p{
sm.a[0] +
sm.b[0] * (
double)(x -
G + og.x),
295 const int oi = q.owner(p);
302 if (
wx == 0.0 &&
wy == 0.0 &&
wz == 0.0)
308 const peclet::core::Vec3<double>
rq = peclet::core::geom::minImage(
309 peclet::core::Vec3<double>{p.x - cen(3 *
oi + 0), p.y - cen(3 *
oi + 1),
310 p.z - cen(3 *
oi + 2)},
312 const peclet::core::Vec3<double> r{
rq.x *
sm.dToInt,
rq.y *
sm.dToInt,
rq.z *
sm.dToInt};
313 Kokkos::atomic_add(&
Td(3 *
oi + 0), mu * (r.y *
vz - r.z *
vy));
314 Kokkos::atomic_add(&
Td(3 *
oi + 1), mu * (r.z *
vx - r.x *
vz));
315 Kokkos::atomic_add(&
Td(3 *
oi + 2), mu * (r.x *
vy - r.y *
vx));
321 if (nInst_ > 1 && incremental_ && cutcellPressure_) {
324 for (
int c = 0; c < 3; ++c) {
326 const long strd = (c == 0) ? 1 : (c == 1) ? e_.
x : (
long)e_.
x * e_.
y;
327 const auto po = Grid::offset(c);
336 const double kP = u_.
vol / u_.
hp[c];
337 Kokkos::parallel_for(
338 "peclet::flow::hydro_reaction_owner_flux",
341 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
342 const long j = i +
strd;
343 if (mk(i) > 0.5 || mk(j) > 0.5)
345 const peclet::core::Vec3<double>
pa{
347 sm.a[1] +
sm.b[1] * ((
double)(y -
G + og.y) +
offy),
349 peclet::core::Vec3<double>
pb =
pa;
350 (cc == 0 ?
pb.x : cc == 1 ?
pb.y :
pb.z) +=
sm.b[cc];
351 const int oa = q.owner(
pa),
ob = q.owner(
pb);
361 const double flux = pf(i) * kP;
362 Kokkos::atomic_add(&
Fd(3 *
oa + cc), -flux);
363 Kokkos::atomic_add(&
Fd(3 *
ob + cc), +flux);
364 const peclet::core::Vec3<double>
raq = peclet::core::geom::minImage(
365 peclet::core::Vec3<double>{
pa.x - cen(3 *
oa + 0),
pa.y - cen(3 *
oa + 1),
366 pa.z - cen(3 *
oa + 2)},
368 const peclet::core::Vec3<double>
rbq = peclet::core::geom::minImage(
369 peclet::core::Vec3<double>{
pa.x - cen(3 *
ob + 0),
pa.y - cen(3 *
ob + 1),
370 pa.z - cen(3 *
ob + 2)},
372 const peclet::core::Vec3<double>
ra{
raq.x *
sm.dToInt,
raq.y *
sm.dToInt,
374 const peclet::core::Vec3<double> rb{
rbq.x *
sm.dToInt,
rbq.y *
sm.dToInt,
377 Kokkos::atomic_add(&
Td(3 *
oa + 1),
ra.z * -flux);
378 Kokkos::atomic_add(&
Td(3 *
oa + 2), -
ra.y * -flux);
379 Kokkos::atomic_add(&
Td(3 *
ob + 1), rb.z * +flux);
380 Kokkos::atomic_add(&
Td(3 *
ob + 2), -rb.y * +flux);
381 }
else if (cc == 1) {
382 Kokkos::atomic_add(&
Td(3 *
oa + 0), -
ra.z * -flux);
383 Kokkos::atomic_add(&
Td(3 *
oa + 2),
ra.x * -flux);
384 Kokkos::atomic_add(&
Td(3 *
ob + 0), -rb.z * +flux);
385 Kokkos::atomic_add(&
Td(3 *
ob + 2), rb.x * +flux);
387 Kokkos::atomic_add(&
Td(3 *
oa + 0),
ra.y * -flux);
388 Kokkos::atomic_add(&
Td(3 *
oa + 1), -
ra.x * -flux);
389 Kokkos::atomic_add(&
Td(3 *
ob + 0), rb.y * +flux);
390 Kokkos::atomic_add(&
Td(3 *
ob + 1), -rb.x * +flux);
396 using HostV = Kokkos::View<double*, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
397 Kokkos::deep_copy(
HostV(out.data(), m),
Fd);
398 Kokkos::deep_copy(
HostV(out.data() + m, m),
Td);
400#ifdef PECLET_FLOW_MPI
402 std::vector<double> g(out.size(), 0.0);
412 std::array<long, 3> out{0, 0, 0};
415 for (
int c = 0; c < 3; ++c) {
418 Kokkos::parallel_reduce(
419 "peclet::flow::fluid_cells",
422 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
427 out[(std::size_t)c] = n;
430#ifdef PECLET_FLOW_MPI
432 std::array<long, 3> g{0, 0, 0};
442 std::vector<double> out(6, 0.0);
445 const double idt = rho_ / dt_;
446 const bool haveA = advect_ && haveAdvRhs_ && advRhs_[0].extent(0) == n_;
447 for (
int c = 0; c < 3; ++c) {
450 double su = 0.0,
sa = 0.0;
451 Kokkos::parallel_reduce(
452 "peclet::flow::budget_terms",
455 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
458 au += idt * (
uc(i) -
un(i));
459 aa +=
av.data() ?
av(i) : 0.0;
462 out[(std::size_t)c] =
su;
463 out[(std::size_t)c + 3] =
sa;
466#ifdef PECLET_FLOW_MPI
468 std::vector<double> g(6, 0.0);
478 std::array<double, 3> out{0, 0, 0};
482 const C3 e = e_, og = og_;
484 double sx = 0, sy = 0, sz = 0;
485 Kokkos::parallel_reduce(
486 "peclet::flow::wall_area_probe",
488 KOKKOS_LAMBDA(
int x,
int y,
int z,
double& ax,
double& ay,
double&
az) {
489 const long st[3] = {1, e.x, (
long)e.x * e.y};
490 const long i = (
long)x + (
long)y * st[1] + (
long)z * st[2];
498#ifdef PECLET_FLOW_MPI
500 std::array<double, 3> g{0, 0, 0};