REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_MaskedAverageDown.H
Go to the documentation of this file.
1#ifndef REMORA_MASKED_AVERAGE_DOWN_H_
2#define REMORA_MASKED_AVERAGE_DOWN_H_
3
4#include <AMReX_Algorithm.H>
5#include <AMReX_Array4.H>
6#include <AMReX_IntVect.H>
7#include <AMReX_REAL.H>
8
9/**
10 * Two-way nesting's fine-to-coarse update, weighted by the land/sea mask.
11 *
12 * Ported from ROMS fine2coarse2d/fine2coarse3d in Nonlinear/nesting.F, the AreaAvg = .FALSE.
13 * branch that every call site there uses:
14 *
15 * num = sum of (fine value * fine mask) over the fine points under the coarse one
16 * den = sum of min(1, fine mask) over the same
17 * crse = num/den * coarse mask
18 *
19 * Dividing by the count of wet fine points rather than by how many there are means a coarse
20 * point only partly covered by water takes the mean of that water, not a value diluted toward
21 * zero by land. That is not conservative; ROMS makes the same trade deliberately. The coarse
22 * mask has the final say on wet or dry.
23 *
24 * Where every fine point is wet the mask-weighted kernels have to reproduce amrex_avgdown and
25 * amrex_avgdown_faces bit for bit, or every existing multilevel answer moves in its last bit.
26 * avgdown_weighted with a volume weight (cf_avgdown_volume_weighted) is the exception: it
27 * changes the answer by design.
28 */
30
31/** Weight of a fine cell: its 2D mask, capped at 1 and read at k=0 */
32struct MaskWeight {
33 amrex::Array4<amrex::Real const> msk;
34 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
35 amrex::Real operator() (int i, int j, int) const noexcept {
36 return amrex::min(amrex::Real(1.0), msk(i,j,0));
37 }
38};
39
40/** Weight of a fine cell read from a 3D array, e.g. mask times volume */
42 amrex::Array4<amrex::Real const> w;
43 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
44 amrex::Real operator() (int i, int j, int k) const noexcept { return w(i,j,k); }
45};
46
47/** Cell-centered fields: the weighted mean of every fine cell under the coarse one. */
48template <typename Weight>
49AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
50void avgdown_weighted (int i, int j, int k, int n,
51 amrex::Array4<amrex::Real> const& crse,
52 amrex::Array4<amrex::Real const> const& fine,
53 Weight const& weight,
54 amrex::Array4<amrex::Real const> const& cmsk,
55 int ccomp, int fcomp, amrex::IntVect const& ratio) noexcept
56{
57 using amrex::Real;
58 const int facx = ratio[0];
59 const int facy = ratio[1];
60 const int facz = ratio[2];
61 const int ii = i*facx;
62 const int jj = j*facy;
63 const int kk = k*facz;
64
65 Real num = Real(0.0);
66 Real den = Real(0.0);
67 // AMReX's loop order, and the mask multiplying the value before it is accumulated rather
68 // than after (x * 1.0 == x), are what make the all-wet case bit-identical to amrex_avgdown.
69 for (int kref = 0; kref < facz; ++kref) {
70 for (int jref = 0; jref < facy; ++jref) {
71 for (int iref = 0; iref < facx; ++iref) {
72 const Real m = weight(ii+iref,jj+jref,kk+kref);
73 num += fine(ii+iref,jj+jref,kk+kref,n+fcomp) * m;
74 den += m;
75 }}}
76
77 // Multiplication by the reciprocal, not num/den: this is AMReX's volfrac, and dividing
78 // would differ in the last bit. den == 0 under a wet coarse point cannot happen once
79 // check_mask_consistency passes, so write zero rather than diagnose it from a kernel.
80 crse(i,j,k,n+ccomp) = (den > Real(0.0))
81 ? num * (Real(1.0)/den) * cmsk(i,j,0)
82 : Real(0.0);
83}
84
85/** Cell-centered fields weighted by the fine mask alone, as ROMS's fine2coarse3d does. */
86AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
87void avgdown_masked (int i, int j, int k, int n,
88 amrex::Array4<amrex::Real> const& crse,
89 amrex::Array4<amrex::Real const> const& fine,
90 amrex::Array4<amrex::Real const> const& fmsk,
91 amrex::Array4<amrex::Real const> const& cmsk,
92 int ccomp, int fcomp, amrex::IntVect const& ratio) noexcept
93{
94 avgdown_weighted(i,j,k,n,crse,fine,MaskWeight{fmsk},cmsk,ccomp,fcomp,ratio);
95}
96
97/** Face-centered fields: only the fine faces in the coarse face's own plane, the stencil
98 * amrex_avgdown_faces uses, so "wet" means the flow really does cross here. */
99AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
100void avgdown_faces_masked (int i, int j, int k, int n,
101 amrex::Array4<amrex::Real> const& crse,
102 amrex::Array4<amrex::Real const> const& fine,
103 amrex::Array4<amrex::Real const> const& fmsk,
104 amrex::Array4<amrex::Real const> const& cmsk,
105 int ccomp, int fcomp, amrex::IntVect const& ratio, int idir) noexcept
106{
107 using amrex::Real;
108 const int facx = ratio[0];
109 const int facy = ratio[1];
110 const int facz = ratio[2];
111 const int ii = i*facx;
112 const int jj = j*facy;
113 const int kk = k*facz;
114
115 Real num = Real(0.0);
116 Real den = Real(0.0);
117
118 if (idir == 0) {
119 for (int kref = 0; kref < facz; ++kref) {
120 for (int jref = 0; jref < facy; ++jref) {
121 const Real m = amrex::min(Real(1.0), fmsk(ii,jj+jref,0));
122 num += fine(ii,jj+jref,kk+kref,n+fcomp) * m;
123 den += m;
124 }}
125 } else if (idir == 1) {
126 for (int kref = 0; kref < facz; ++kref) {
127 for (int iref = 0; iref < facx; ++iref) {
128 const Real m = amrex::min(Real(1.0), fmsk(ii+iref,jj,0));
129 num += fine(ii+iref,jj,kk+kref,n+fcomp) * m;
130 den += m;
131 }}
132 } else {
133 for (int jref = 0; jref < facy; ++jref) {
134 for (int iref = 0; iref < facx; ++iref) {
135 const Real m = amrex::min(Real(1.0), fmsk(ii+iref,jj+jref,0));
136 num += fine(ii+iref,jj+jref,kk,n+fcomp) * m;
137 den += m;
138 }}
139 }
140
141 // As in avgdown_masked: reciprocal rather than divide, and zero when nothing is wet.
142 crse(i,j,k,n+ccomp) = (den > Real(0.0))
143 ? num * (Real(1.0)/den) * cmsk(i,j,0)
144 : Real(0.0);
145}
146
147} // namespace REMORAMaskedAvgDown
148
149#endif
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void avgdown_faces_masked(int i, int j, int k, int n, amrex::Array4< amrex::Real > const &crse, amrex::Array4< amrex::Real const > const &fine, amrex::Array4< amrex::Real const > const &fmsk, amrex::Array4< amrex::Real const > const &cmsk, int ccomp, int fcomp, amrex::IntVect const &ratio, int idir) noexcept
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void avgdown_masked(int i, int j, int k, int n, amrex::Array4< amrex::Real > const &crse, amrex::Array4< amrex::Real const > const &fine, amrex::Array4< amrex::Real const > const &fmsk, amrex::Array4< amrex::Real const > const &cmsk, int ccomp, int fcomp, amrex::IntVect const &ratio) noexcept
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void avgdown_weighted(int i, int j, int k, int n, amrex::Array4< amrex::Real > const &crse, amrex::Array4< amrex::Real const > const &fine, Weight const &weight, amrex::Array4< amrex::Real const > const &cmsk, int ccomp, int fcomp, amrex::IntVect const &ratio) noexcept
amrex::Array4< amrex::Real const > w
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real operator()(int i, int j, int k) const noexcept
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real operator()(int i, int j, int) const noexcept
amrex::Array4< amrex::Real const > msk