@@ -107,7 +107,7 @@ Analogue of `Base._InitialValue` for `column_reduce!` and `column_accumulate!`.
107107struct UnspecifiedInit end
108108
109109"""
110- column_reduce!(f, output, input; [init], [transform])
110+ column_reduce!(f, output, input; [init], [transform], [reverse] )
111111
112112Applies `reduce` to `input` along the vertical direction, storing the result in
113113`output`. The `input` can be either a `Field` or an `AbstractBroadcasted` that
@@ -116,10 +116,12 @@ computed by iteratively applying `f` to the values in `input`, starting from the
116116bottom of each column and moving upward, and the result of the final iteration
117117is passed to the `transform` function before being stored in `output`. If `init`
118118is specified, it is used as the initial value of the iteration; otherwise, the
119- value at the bottom of each column in `input` is used as the initial value.
119+ value at the starting boundary of each column in `input` is used as the initial
120+ value. By default, reduction starts at the bottom boundary and proceeds upward.
121+ When `reverse = true`, it starts at the top boundary and proceeds downward.
120122
121123With `first_level` and `last_level` denoting the indices of the boundary levels
122- of `input`, the reduction in each column can be summarized as follows:
124+ of `input`, the default reduction in each column can be summarized as follows:
123125 - If `init` is unspecified,
124126 ```
125127 reduced_value = input[first_level]
@@ -143,10 +145,11 @@ function column_reduce!(
143145 input:: Union{Fields.Field, PointwiseOrColumnwiseBroadcasted} ;
144146 init = UnspecifiedInit (),
145147 transform:: T = identity,
148+ reverse:: Bool = false ,
146149) where {F, T}
147150 device = ClimaComms. device (output)
148151 space = axes (input)
149- column_reduce_device! (device, f, transform, output, input, init, space)
152+ column_reduce_device! (device, f, transform, output, input, init, space, reverse )
150153end
151154
152155function column_reduce_device! (
@@ -157,11 +160,12 @@ function column_reduce_device!(
157160 input,
158161 init,
159162 space,
163+ reverse,
160164) where {F, T}
161165 mask = Spaces. get_mask (space)
162166 if space isa Spaces. FiniteDifferenceSpace
163167 @assert mask isa DataLayouts. NoMask
164- single_column_reduce! (f, transform, output, input, init, space)
168+ single_column_reduce! (f, transform, output, input, init, space, reverse )
165169 else
166170 Fields. bycolumn (space) do colidx
167171 I = Fields. universal_index (colidx)
@@ -173,6 +177,7 @@ function column_reduce_device!(
173177 input[colidx],
174178 init,
175179 space[colidx],
180+ reverse,
176181 )
177182 end
178183 end
@@ -187,37 +192,45 @@ function single_column_reduce!(
187192 _input,
188193 init,
189194 space,
195+ reverse,
190196) where {F, T}
191197 first_level = left_idx (space)
192198 last_level = right_idx (space)
199+ start_level, stop_level, direction =
200+ reverse ? (last_level, first_level, - 1 ) : (first_level, last_level, 1 )
193201 @inbounds if init == UnspecifiedInit ()
194- reduced_value = get_level_value (space, _input, first_level )
195- next_level = first_level + 1
202+ reduced_value = get_level_value (space, _input, start_level )
203+ next_level = start_level + direction
196204 else
197205 reduced_value = init
198- next_level = first_level
206+ next_level = start_level
199207 end
200- @inbounds for level in next_level: last_level
208+ n_steps = direction * (stop_level - next_level) + 1
209+ @inbounds for i in 1 : n_steps
210+ level = next_level + direction * (i - 1 )
201211 reduced_value = f (reduced_value, get_level_value (space, _input, level))
202212 end
203213 Fields. field_values (_output)[] = transform (reduced_value)
204214 return nothing
205215end
206216
207217"""
208- column_accumulate!(f, output, input; [init], [transform])
218+ column_accumulate!(f, output, input; [init], [transform], [reverse] )
209219
210220Applies `accumulate` to `input` along the vertical direction, storing the result
211221in `output`. The `input` can be either a `Field` or an `AbstractBroadcasted`
212- that performs pointwise or columnwise operations on `Field`s. Each accumulated
213- value is computed by iteratively applying `f` to the values in `input`, starting
214- from the bottom of each column and moving upward, and the result of each
215- iteration is passed to the `transform` function before being stored in `output`.
222+ that performs pointwise or columnwise operations on `Field`s. By default, each
223+ accumulated value is computed by iteratively applying `f` to the values in
224+ `input`, starting from the bottom of each column and moving upward, and the
225+ result of each iteration is passed to the `transform` function before being
226+ stored in `output`.
216227The `init` value is is optional for center-to-center, face-to-face, and
217228face-to-center accumulation, but it is required for center-to-face accumulation.
229+ When `reverse = true`, accumulation starts at the top boundary and proceeds
230+ downward, with the corresponding staggered boundary offsets reversed.
218231
219232With `first_level` and `last_level` denoting the indices of the boundary levels
220- of `input`, the accumulation in each column can be summarized as follows:
233+ of `input`, the default accumulation in each column can be summarized as follows:
221234 - For center-to-center and face-to-face accumulation with `init` unspecified,
222235 ```
223236 accumulated_value = input[first_level]
@@ -267,14 +280,15 @@ function column_accumulate!(
267280 input:: Union{Fields.Field, PointwiseOrColumnwiseBroadcasted} ;
268281 init = UnspecifiedInit (),
269282 transform:: T = identity,
283+ reverse:: Bool = false ,
270284) where {F, T}
271285 device = ClimaComms. device (output)
272286 space = axes (input)
273287 init == UnspecifiedInit () &&
274288 Spaces. staggering (space) == Spaces. CellCenter () &&
275289 Spaces. staggering (axes (output)) == Spaces. CellFace () &&
276290 error (" init must be specified for center-to-face accumulation" )
277- column_accumulate_device! (device, f, transform, output, input, init, space)
291+ column_accumulate_device! (device, f, transform, output, input, init, space, reverse )
278292end
279293
280294function column_accumulate_device! (
@@ -285,11 +299,12 @@ function column_accumulate_device!(
285299 input,
286300 init,
287301 space,
302+ reverse,
288303) where {F, T}
289304 mask = Spaces. get_mask (space)
290305 if space isa Spaces. FiniteDifferenceSpace
291306 @assert mask isa DataLayouts. NoMask
292- single_column_accumulate! (f, transform, output, input, init, space)
307+ single_column_accumulate! (f, transform, output, input, init, space, reverse )
293308 else
294309 Fields. bycolumn (space) do colidx
295310 I = Fields. universal_index (colidx)
@@ -301,6 +316,7 @@ function column_accumulate_device!(
301316 input[colidx],
302317 init,
303318 space[colidx],
319+ reverse,
304320 )
305321 end
306322 end
@@ -315,6 +331,7 @@ function single_column_accumulate!(
315331 _input,
316332 init,
317333 space,
334+ reverse,
318335) where {F, T}
319336 device = ClimaComms. device (space)
320337 first_level = left_idx (space)
@@ -323,25 +340,32 @@ function single_column_accumulate!(
323340 is_c2c_or_f2f = Spaces. staggering (space) == Spaces. staggering (axes (output))
324341 is_c2f = ! is_c2c_or_f2f && Spaces. staggering (space) == Spaces. CellCenter ()
325342 is_f2c = ! is_c2c_or_f2f && ! is_c2f
343+ # With `reverse = true`, start at the top boundary, step downward, and
344+ # reverse the center/face half-level offset used by staggered accumulation.
345+ start_level, stop_level, direction =
346+ reverse ? (last_level, first_level, - 1 ) : (first_level, last_level, 1 )
347+ stagger = reverse ? - half : half
326348 @inbounds if init == UnspecifiedInit ()
327349 @assert ! is_c2f
328- accumulated_value = get_level_value (space, _input, first_level )
329- next_level = first_level + 1
330- init_output_level = is_c2c_or_f2f ? first_level : nothing
350+ accumulated_value = get_level_value (space, _input, start_level )
351+ next_level = start_level + direction
352+ init_output_level = is_c2c_or_f2f ? start_level : nothing
331353 else
332354 accumulated_value =
333- is_f2c ? f (init, get_level_value (space, _input, first_level )) : init
334- next_level = is_f2c ? first_level + 1 : first_level
335- init_output_level = is_c2f ? first_level - half : nothing
355+ is_f2c ? f (init, get_level_value (space, _input, start_level )) : init
356+ next_level = is_f2c ? start_level + direction : start_level
357+ init_output_level = is_c2f ? start_level - stagger : nothing
336358 end
337359 @inbounds if ! isnothing (init_output_level)
338360 Fields. level (output, init_output_level)[] = transform (accumulated_value)
339361 end
340- @inbounds for level in next_level: last_level
362+ n_steps = direction * (stop_level - next_level) + 1
363+ @inbounds for i in 1 : n_steps
364+ level = next_level + direction * (i - 1 )
341365 accumulated_value =
342366 f (accumulated_value, get_level_value (space, _input, level))
343367 output_level =
344- is_c2c_or_f2f ? level : (is_c2f ? level + half : level - half )
368+ is_c2c_or_f2f ? level : (is_c2f ? level + stagger : level - stagger )
345369 Fields. level (output, output_level)[] = transform (accumulated_value)
346370 end
347371end
0 commit comments