MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_rt_sc_solvers.t
Go to the documentation of this file.
1!=============================================================================
2!> Solvers of the short-characteristics (SC) radiative transfer and their dispatch
3!>
4!> rt_sc_solve runs once per step. It sweeps every ray through every band on rt_sc_amr_level and
5!> leaves the heating Q in i_qrad and the vertical flux Fy in i_frad. The pipelined KBA wavefront
6!> is the default. Its sweep corners run at once, each in its own intensity slot, and the rays of
7!> a corner follow one another through the wavefront. The ray-by-ray wavefront passes one ray at
8!> a time, agrees with it to round-off, and is the simplest to read. In both, each rank
9!> sweeps only its own band blocks, one diagonal of blocks at a time, with a getbc of the ray
10!> intensity between diagonals, so that no rank holds the whole band. The replicated gather
11!> assembles chi and B of the whole band on every rank and is kept as the reference. It differs
12!> from the wavefront only where rays cross a periodic seam
13!>
14!> The loops over block cells are written along (hdim, updim) through hv, so that one loop
15!> serves either stratification axis
16!>
17!> Structure:
18!> A. dispatch rt_sc_solve and the validation rt_sc_validate_pipe;
19!> B. gather rt_sc_solve_gather and rt_grid_index;
20!> C. band blocks rt_band_blocks, which lists the band blocks and caches chi and B, with
21!> rt_tau_tops and rt_block_tau for tau from the band top, which
22!> rt_sc_emergent_tau1 shares;
23!> D. wave stages rt_wave_begin, rt_wave_band_open, rt_wave_band_close, which forms Q, and
24!> rt_wave_end, with rt_add_moments and rt_exchange;
25!> E. block work rt_seam_read and rt_seam_write for the periodic seam, rt_inflow_ghosts for
26!> the band-end and edge inflows, and rt_sweep_one;
27!> F. schedules rt_sc_solve_wave and rt_sc_solve_wave_pipe, with wave_stage
28!>
29!> Jack Jenkins (10.2026); More or less finished in v1 state for amrvac4.0
30!=============================================================================
33 implicit none
34
35 public :: rt_sc_solve
36
37contains
38
39 !> Collective, once per step. For each band on rt_sc_amr_level it sweeps all rays and forms the
40 !> heating Q, the thin/thick blend with the tau^2 taper, in i_qrad, which is zero elsewhere,
41 !> together with Fy, and Iout on the gather path. rt_sc_add_source applies the energy every
42 !> stage. The gather path is for comparison only, see rt_sc_solve_gather
43 subroutine rt_sc_solve()
46 double precision :: tw(3)
47{^iftwod
48 if(.not.rt_sc_active) return
49 if(rt_sc_valpipe .and. rt_sc_distributed) then
51 return
52 end if
53 call timer_open(tw)
54 if(rt_sc_distributed) then
55 if(rt_sc_pipeline) then
57 else
58 call rt_sc_solve_wave()
59 end if
60 rt_wt_wave = rt_wt_wave + (mpi_wtime()-tw(1))
62 if(rt_sc_debug .and. mype==0 .and. mod(rt_nsolve,10)==0) write(*,'(a,i5,a,f9.3,a)') &
63 ' RT wave nsolve=',rt_nsolve,' cumulative wave-solve time=',rt_wt_wave,' s'
64 else
66 end if
68 ! The per-rank wall time, which includes the pipeline waits that show the imbalance
69 if(lb_diagnose) lb_rt_accum = mpi_wtime() - tw(1)
70}
71 end subroutine rt_sc_solve
72
73 !> Replicated gather, the original implementation, kept for comparison and not for production.
74 !> Every rank holds chi and B of the whole band, the rays are shared out over the node-local
75 !> ranks, and the moments are reduced within the node. Its memory grows with the whole band on
76 !> every rank, and its periodic seam takes a vacuum inflow for rays swept column by column
77 !> (sc_sweep_2d). Use KBA, pipelined or not
78 subroutine rt_sc_solve_gather()
80 use mod_comm_lib, only: mpistop
81 {^iftwod
82 double precision :: chi_l(ixG^T), b_l(ixG^T), tw
83 double precision, allocatable :: fill_g(:,:)
84 integer :: iigrid, igrid, ix1, ix2, ig, jg, k, ierr, ib
85 if(.not.rt_sc_active) return
86 if(.not.rt_sc_ready) call mpistop("rt_sc_solve_gather: rt_sc_setup not called")
87 if(i_qrad < 0) call mpistop("rt_sc_solve_gather: i_qrad not registered")
88 if(.not.associated(rt_sc_chib)) call mpistop("rt_sc_solve_gather: opacity callback not set")
89
90 ! The node-local and leader communicators are built on the first call. Every rank joins its
91 ! node communicator, and only the node leaders join the leader communicator
92 if(rt_node_comm == -1) then
93 call mpi_comm_split_type(icomm, mpi_comm_type_shared, 0, mpi_info_null, rt_node_comm, ierr)
94 call mpi_comm_rank(rt_node_comm, rt_node_rank, ierr)
95 call mpi_comm_size(rt_node_comm, rt_node_size, ierr)
96 if(rt_node_rank == 0) then
97 call mpi_comm_split(icomm, 0, mype, rt_lead_comm, ierr)
98 else
99 call mpi_comm_split(icomm, mpi_undefined, mype, rt_lead_comm, ierr)
100 end if
101 end if
102
103 nxg = rg_nh; dxg = rg_dh; dyg = rg_dv
104
105 ! Q is zeroed everywhere first, so the cells outside the bands receive no heating
106 do iigrid=1,igridstail; igrid=igrids(iigrid)
107 ps(igrid)%w(ixg^t,i_qrad) = 0.0d0
108 end do
109
110 do ib = 1, rt_sc_nband
111 ! Each band has its own incoming conditions and its own number of rows
112 bc_lo_thermal = (trim(rt_sc_bc_min(ib)) == 'thermal')
113 bc_hi_thermal = (trim(rt_sc_bc_max(ib)) == 'thermal')
114 nyg = band_nv(ib)
115 call rt_grid_alloc()
116 ! fill_g counts how many cells land on each grid point, which checks the AMR pin below
117 allocate(fill_g(nxg,nyg)); fill_g = 0.0d0
118 chi_g = 0.0d0; b_g = 0.0d0
119 tw = mpi_wtime()
120
121 ! Each rank writes chi and B of its own level-L band cells into the global grid
122 do iigrid=1,igridstail; igrid=igrids(iigrid)
123 if(node(plevel_,igrid) /= rt_sc_amr_level) cycle
124 call rt_sc_chib(ixg^ll, ixm^ll, ps(igrid)%w, ps(igrid)%x, chi_l, b_l)
125 do ix2=ixmlo2,ixmhi2; do ix1=ixmlo1,ixmhi1
126 if(.not.rt_grid_index(ps(igrid)%x(ix1,ix2,hdim), ps(igrid)%x(ix1,ix2,updim), ib, ig, jg)) cycle
127 chi_g(ig,jg) = chi_l(ix1,ix2); b_g(ig,jg) = b_l(ix1,ix2); fill_g(ig,jg) = 1.0d0
128 end do; end do
129 end do
130 ! Each point is written by one rank and is zero on all the others, so summing within the
131 ! node, then over the leaders, and broadcasting within the node gives every rank the field
132 call rt_allreduce_chunk(chi_g, nxg, nyg, rt_node_comm)
133 call rt_allreduce_chunk(b_g, nxg, nyg, rt_node_comm)
134 call rt_allreduce_chunk(fill_g, nxg, nyg, rt_node_comm)
135 if(rt_lead_comm /= mpi_comm_null) then
136 call rt_allreduce_chunk(chi_g, nxg, nyg, rt_lead_comm)
137 call rt_allreduce_chunk(b_g, nxg, nyg, rt_lead_comm)
138 call rt_allreduce_chunk(fill_g, nxg, nyg, rt_lead_comm)
139 end if
140 if(rt_node_size > 1) then
141 call mpi_bcast(chi_g, nxg*nyg, mpi_double_precision, 0, rt_node_comm, ierr)
142 call mpi_bcast(b_g, nxg*nyg, mpi_double_precision, 0, rt_node_comm, ierr)
143 call mpi_bcast(fill_g, nxg*nyg, mpi_double_precision, 0, rt_node_comm, ierr)
144 end if
145 ! Every grid point must have been filled exactly once. Anything else means the band is not
146 ! one complete level and the AMR pin has failed
147 if(minval(fill_g) /= 1.0d0 .or. maxval(fill_g) /= 1.0d0) then
148 if(mype==0) print *,'rt_sc_solve_gather: band',ib,' not a complete single level ', &
149 rt_sc_amr_level,' (minfill',minval(fill_g),' maxfill',maxval(fill_g),')'
150 call mpistop("rt_sc_solve_gather: RT band is not a complete single AMR level")
151 end if
152 deallocate(fill_g)
153 call sc_pad_grid()
154 rt_wt_gath = rt_wt_gath + (mpi_wtime()-tw); tw = mpi_wtime()
155
156 ! Without scattering each ray is one independent sweep, so the rays are shared out over the
157 ! node-local ranks and the moments summed within the node. Each sweep starts from zero
158 ! intensity, so the sum depends on the order and the number of ranks only through round-off
159 jm_g = 0.0d0; fx_g = 0.0d0; fy_g = 0.0d0
160 do k = rt_node_rank+1, rt_sc_nray, rt_node_size
161 call sc_sweep_2d(mu_h(k), mu_v(k))
162 ! J = sum_k w_k I_k, Fx = sum_k w_k mu_h,k I_k, Fy = sum_k w_k mu_v,k I_k
163 jm_g = jm_g + wmu(k)*ii_g
164 fx_g = fx_g + wmu(k)*mu_h(k)*ii_g
165 fy_g = fy_g + wmu(k)*mu_v(k)*ii_g
166 end do
167 rt_wt_swp = rt_wt_swp + (mpi_wtime()-tw); tw = mpi_wtime()
168 if(rt_node_size>1) then
169 call rt_allreduce_chunk(jm_g, nxg, nyg, rt_node_comm)
170 call rt_allreduce_chunk(fx_g, nxg, nyg, rt_node_comm)
171 call rt_allreduce_chunk(fy_g, nxg, nyg, rt_node_comm)
172 end if
173 rt_wt_red = rt_wt_red + (mpi_wtime()-tw); tw = mpi_wtime()
174 ! F = c sum_k w_k mu_k I_k
175 fx_g = c_norm*fx_g; fy_g = c_norm*fy_g
176 if(rt_sc_flux_blend) then
177 call rt_sc_heat_blend()
178 else
179 ! Q = c chi (J - B)
180 q_g = c_norm*chi_g*(jm_g - b_g)
181 end if
182 ! A vertical ray swept afterwards gives the emergent intensity Iout
183 call sc_sweep_2d(0.0d0, 1.0d0)
184
185 ! Q, Fy and Iout go back to the band's level-L cells through the same index map
186 do iigrid=1,igridstail; igrid=igrids(iigrid)
187 if(node(plevel_,igrid) /= rt_sc_amr_level) cycle
188 do ix2=ixmlo2,ixmhi2; do ix1=ixmlo1,ixmhi1
189 if(.not.rt_grid_index(ps(igrid)%x(ix1,ix2,hdim), ps(igrid)%x(ix1,ix2,updim), ib, ig, jg)) cycle
190 ps(igrid)%w(ix1,ix2,i_qrad) = q_g(ig,jg)
191 if(i_frad>0) ps(igrid)%w(ix1,ix2,i_frad) = fy_g(ig,jg)
192 if(i_iout>0) ps(igrid)%w(ix1,ix2,i_iout) = ii_g(ig,jg)
193 end do; end do
194 end do
195 rt_wt_scat = rt_wt_scat + (mpi_wtime()-tw)
196 end do
197 rt_nsolve = rt_nsolve + 1
198 if(rt_sc_debug .and. mype==0 .and. mod(rt_nsolve,10)==0) &
199 write(*,'(a,i5,4(a,f8.3),a,f8.3,a)') ' RT timing nsolve=',rt_nsolve, &
200 ' | gather+fieldreduce=',rt_wt_gath,' sweep=',rt_wt_swp,' momentreduce=',rt_wt_red, &
201 ' blend+scatter=',rt_wt_scat,' | total=',rt_wt_gath+rt_wt_swp+rt_wt_red+rt_wt_scat,' s (cumulative)'
202 }
203 end subroutine rt_sc_solve_gather
204
205 !> Indices (ig, jg) on the replicated grid of band ib of the cell centred at xh along hdim and xv
206 !> along updim, false when the cell lies outside the band
207 logical function rt_grid_index(xh, xv, ib, ig, jg) result(inside)
208 double precision, intent(in) :: xh, xv
209 integer, intent(in) :: ib
210 integer, intent(out) :: ig, jg
211 inside = .false.
212 if(xv <= band_lo_c(ib) .or. xv >= band_hi_c(ib)) return
213 ! ig = nint((x - x_lo)/dx + 1/2), jg = nint((y - y_band)/dy + 1/2)
214 ig = nint((xh - rg_hlo)/rg_dh + 0.5d0)
215 jg = nint((xv - band_lo_c(ib))/rg_dv + 0.5d0)
216 inside = ig >= 1 .and. ig <= rg_nh .and. jg >= 1 .and. jg <= band_nv(ib)
217 end function rt_grid_index
218
219 {^iftwod
220 !> Validation of the pipelined KBA. The ray-by-ray Q is copied into i_iout and the pipelined Q
221 !> left in i_qrad, and the largest difference is printed relative to the largest Q, both over
222 !> the whole band and away from the seam blocks, the outermost x-block on either side. Both
223 !> solves start from the same seam inflow, since the first one hands its own on to the second
226 integer :: iigrid, igrid, ierr, ix1, ix2
227 double precision :: dd, dmx, qmx, dmi, xedge, gbuf(3), gout(3)
228 double precision, allocatable :: seamL0(:,:), seamR0(:,:)
229 logical :: had_seam
230 had_seam = allocated(rt_sc_seaml_prev)
231 if(had_seam) then
232 seaml0 = rt_sc_seaml_prev; seamr0 = rt_sc_seamr_prev
233 end if
234 call rt_sc_solve_wave()
235 if(allocated(rt_sc_seaml_prev)) then
236 if(had_seam) then
237 rt_sc_seaml_prev = seaml0; rt_sc_seamr_prev = seamr0
238 else
239 rt_sc_seaml_prev = 0.0d0; rt_sc_seamr_prev = 0.0d0
240 end if
241 end if
242 do iigrid=1,igridstail; igrid=igrids(iigrid)
243 ps(igrid)%w(ixg^t,i_iout) = ps(igrid)%w(ixg^t,i_qrad)
244 end do
246 dmx=0.0d0; qmx=0.0d0; dmi=0.0d0
247 xedge = block_nx1*(xprobmax1-xprobmin1)/dble(domain_nx1)
248 do iigrid=1,igridstail; igrid=igrids(iigrid)
249 qmx=max(qmx, maxval(dabs(ps(igrid)%w(ixm^t,i_qrad))))
250 do ix2=ixmlo2,ixmhi2; do ix1=ixmlo1,ixmhi1
251 dd=dabs(ps(igrid)%w(ix1,ix2,i_qrad)-ps(igrid)%w(ix1,ix2,i_iout))
252 dmx=max(dmx,dd)
253 if(ps(igrid)%x(ix1,ix2,1) > xprobmin1+xedge .and. &
254 ps(igrid)%x(ix1,ix2,1) < xprobmax1-xedge) dmi=max(dmi,dd)
255 end do; end do
256 end do
257 ! rel = max abs(Q_pipe - Q_wave)/max abs(Q)
258 gbuf = (/ dmx, qmx, dmi /)
259 call mpi_allreduce(gbuf, gout, 3, mpi_double_precision, mpi_max, icomm, ierr)
260 if(mype==0) write(*,'(a,es12.5,a,es12.5,a,es12.5,a,es12.5)') &
261 ' rt_valpipe rel=',gout(1)/max(gout(2),1.0d-300),' rel(x-interior,no seam)=', &
262 gout(3)/max(gout(2),1.0d-300),' max|dQ|=',gout(1),' max|Q|=',gout(2)
263 end subroutine rt_sc_validate_pipe
264
265 !> List the blocks of this rank that lie in band ib, with their block position (bi, bj), cache chi
266 !> and B on them, ghosts included, and check that the band is one complete level
267 subroutine rt_band_blocks(ib)
269 use mod_comm_lib, only: mpistop
270 integer, intent(in) :: ib
271 integer :: iigrid, igrid, iblk, ierr
272 integer, allocatable :: cover(:,:)
273 ! The band holds whole blocks, since rt_sc_setup snapped band_hi_c to a block face, and its
274 ! longest diagonal p+q is rg_nbh + wv_nbxv
275 wv_nbxv = band_nbv(ib)
276 wv_dmax = rg_nbh + wv_nbxv
277 if(allocated(wv_ig)) deallocate(wv_ig,wv_bi,wv_bj)
278 allocate(wv_ig(max(igridstail,1)),wv_bi(max(igridstail,1)),wv_bj(max(igridstail,1)))
279 wv_nblk = 0
280 do iigrid=1,igridstail; igrid=igrids(iigrid)
281 if(node(plevel_,igrid)/=rt_sc_amr_level) cycle
282 if(rnode(rpxmin0_+updim,igrid) < band_lo_c(ib)-0.5d0*rg_dv) cycle
283 if(rnode(rpxmin0_+updim,igrid)+rg_bv*rg_dv > band_hi_c(ib)+0.5d0*rg_dv) cycle
284 wv_nblk = wv_nblk + 1
285 wv_ig(wv_nblk) = igrid
286 wv_bi(wv_nblk) = nint((rnode(rpxmin0_+hdim,igrid) -rg_hlo)/(rg_bh*rg_dh)) + 1
287 wv_bj(wv_nblk) = nint((rnode(rpxmin0_+updim,igrid)-band_lo_c(ib))/(rg_bv*rg_dv)) + 1
288 end do
289 ! The caches only grow and are otherwise reused
290 if(allocated(rtw_chib)) then
291 if(size(rtw_chib,3) < max(wv_nblk,1)) deallocate(rtw_chib,rtw_bb,rtw_fx)
292 end if
293 if(.not.allocated(rtw_chib)) allocate(rtw_chib(ixglo1:ixghi1,ixglo2:ixghi2,max(wv_nblk,1)), &
294 rtw_bb(ixglo1:ixghi1,ixglo2:ixghi2,max(wv_nblk,1)), &
295 rtw_fx(ixglo1:ixghi1,ixglo2:ixghi2,max(wv_nblk,1)))
296 ! Every (bi, bj) of the band must be owned exactly once, or the AMR pin has failed
297 allocate(cover(rg_nbh, wv_nbxv)); cover = 0
298 do iblk = 1, wv_nblk
299 igrid = wv_ig(iblk)
300 call rt_sc_chib(ixg^ll, ixg^ll, ps(igrid)%w, ps(igrid)%x, rtw_chib(:,:,iblk), rtw_bb(:,:,iblk))
301 ! Beyond the band ends chi and B take the edge values, as in the gather, so that the deep
302 ! row thermalises. A ghost extrapolation would turn the large deep chi into spurious heating
303 if(wv_bj(iblk)==1) call copy_edge_row(iglo_v, imlo_v-1, imlo_v, bc_lo_thermal)
304 if(wv_bj(iblk)==wv_nbxv) call copy_edge_row(imhi_v+1, ighi_v, imhi_v, bc_hi_thermal)
305 cover(wv_bi(iblk), wv_bj(iblk)) = cover(wv_bi(iblk), wv_bj(iblk)) + 1
306 end do
307 ! The coverage is summed over the ranks before it is checked
308 call mpi_allreduce(mpi_in_place, cover, rg_nbh*wv_nbxv, mpi_integer, mpi_sum, icomm, ierr)
309 if(minval(cover) /= 1 .or. maxval(cover) /= 1) then
310 if(mype==0) print *,'rt_band_blocks: band',ib,' not a complete single level ', &
311 rt_sc_amr_level,' (mincover',minval(cover),' maxcover',maxval(cover),')'
312 call mpistop("rt_band_blocks: RT band is not a complete single AMR level")
313 end if
314 contains
315 !> Ghost rows iv_lo to iv_hi of the chi and B caches of block iblk beyond the edge row iv_src.
316 !> At an open end chi is zero in the ghosts, so that the first step into the band crosses only
317 !> the half cell between the band edge and the cell centre, chi_edge dy/2 in optical depth.
318 !> Copying the edge chi would add a ghost half cell of emitting gas above the band and lower
319 !> the outgoing flux by about (3/4) q(0) dtau of sigma Teff**4. At a thermal end chi takes the
320 !> edge value and B keeps the value rt_sc_chiB gave the ghost state, which the boundary
321 !> conditions set, so that the inflow of rt_thermal_inflow does not follow the heating of the
322 !> band
323 subroutine copy_edge_row(iv_lo, iv_hi, iv_src, thermal)
324 integer, intent(in) :: iv_lo, iv_hi, iv_src
325 logical, intent(in) :: thermal
326 integer :: ih, iv, i1, i2, j1, j2
327 do iv = iv_lo, iv_hi
328 do ih = iglo_h, ighi_h
329 call hv(ih, iv, i1, i2)
330 call hv(ih, iv_src, j1, j2)
331 rtw_chib(i1,i2,iblk) = merge(rtw_chib(j1,j2,iblk), 0.0d0, thermal)
332 if(.not.thermal) rtw_bb(i1,i2,iblk) = rtw_bb(j1,j2,iblk)
333 end do
334 end do
335 end subroutine copy_edge_row
336 end subroutine rt_band_blocks
337
338 !> Tau at the top face of every block row of the current band, per global column along hdim.
339 !> Each block adds the optical thickness of its columns, one allreduce assembles the table, and
340 !> the top of a row lies below all the rows above it
341 subroutine rt_tau_tops(tautop)
343 double precision, intent(out) :: tautop(rg_nh,wv_nbxv)
344 double precision :: colthk(rg_nh,wv_nbxv), s
345 integer :: iblk, ih, iv, i1, i2, ig, bj, jb, ierr
346 colthk = 0.0d0
347 do iblk = 1, wv_nblk
348 do ih = imlo_h, imhi_h
349 ig = (wv_bi(iblk)-1)*rg_bh + (ih - imlo_h + 1)
350 ! colthk = sum_j chi_j dy down the block column
351 s = 0.0d0
352 do iv = imlo_v, imhi_v
353 call hv(ih, iv, i1, i2)
354 s = s + rtw_chib(i1,i2,iblk)
355 end do
356 colthk(ig,wv_bj(iblk)) = s*rg_dv
357 end do
358 end do
359 call mpi_allreduce(mpi_in_place, colthk, rg_nh*wv_nbxv, mpi_double_precision, mpi_sum, icomm, ierr)
360 do bj = 1, wv_nbxv
361 ! tau_top(bj) = sum of colthk(jb) over the rows jb > bj
362 tautop(:,bj) = 0.0d0
363 do jb = bj+1, wv_nbxv; tautop(:,bj) = tautop(:,bj) + colthk(:,jb); end do
364 end do
365 end subroutine rt_tau_tops
366
367 !> Tau from the band top to every cell centre of block iblk, integrated down from the top face
368 !> of the block
369 subroutine rt_block_tau(iblk, tautop, taub)
371 integer, intent(in) :: iblk
372 double precision, intent(in) :: tautop(rg_nh,wv_nbxv)
373 double precision, intent(out) :: taub(ixG^T)
374 integer :: ih, iv, i1, i2, j1, j2, ig
375 do ih = imlo_h, imhi_h
376 ig = (wv_bi(iblk)-1)*rg_bh + (ih - imlo_h + 1)
377 ! tau = tau_top + chi dy/2 in the top cell, tau_j = tau_(j+1) + (chi_(j+1) + chi_j) dy/2
378 call hv(ih, imhi_v, i1, i2)
379 taub(i1,i2) = tautop(ig,wv_bj(iblk)) + 0.5d0*rtw_chib(i1,i2,iblk)*rg_dv
380 do iv = imhi_v-1, imlo_v, -1
381 call hv(ih, iv, i1, i2)
382 call hv(ih, iv+1, j1, j2)
383 taub(i1,i2) = taub(j1,j2) + 0.5d0*(rtw_chib(j1,j2,iblk)+rtw_chib(i1,i2,iblk))*rg_dv
384 end do
385 end do
386 end subroutine rt_block_tau
387
388 !> Prologue of both KBA schedules on level rt_sc_amr_level. It checks the setup, sets the
389 !> x-distribution mode, refreshes the hydro ghosts, allocates the intensity state and the seam
390 !> buffers, and builds the nslot wavefront datatype. Each rank sweeps only its own band blocks,
391 !> ordered per ray by the diagonal p+q counted from the ray's upwind corner, with a getbc between
392 !> diagonals, so every cell is swept once per ray
393 subroutine rt_wave_begin(nslot)
395 use mod_comm_lib, only: mpistop
397 use mod_variables, only: iwstart, nwgc
398 integer, intent(in) :: nslot
399 if(.not.rt_sc_ready) call mpistop("rt_wave_begin: rt_sc_setup not called")
400 if(i_isw < 0) call mpistop("rt_wave_begin: wavefront slot not reserved")
401 if(i_qrad < 0 .or. i_frad < 0) call mpistop("rt_wave_begin: the wave needs i_qrad and i_frad")
402 ! With one block across x, sc_sweep_block wraps in place. With several, the x-upwind values
403 ! come from the neighbour's ghosts, the diagonal widens, and the periodic seam takes the
404 ! lagged wrap
405 wv_xwrap = (rg_nbh == 1)
406 if(.not.wv_xwrap .and. mype==0 .and. .not.rt_sc_seam_warned) then
407 write(*,*) 'rt_wave_begin: periodic seam closed by the lagged wrap'
408 rt_sc_seam_warned = .true.
409 end if
410 ! The hydro ghosts are refreshed so that chi and B can be evaluated on the ghost ring
411 call getbc(global_time, 0.0d0, ps, iwstart, nwgc)
412 call rt_sc_alloc_state()
413 ! A non-periodic hdim has inflow edges and therefore no seam
414 wv_seam = rt_sc_seam_on .and. .not.wv_xwrap .and. bc_h_periodic
415 if(wv_seam) call rt_sc_seam_alloc(rg_nv, rt_sc_nray)
416 ! From here on getbc of ps_rt exchanges only the nslot intensity columns
417 call create_bc_mpi_datatype(i_isw, nslot)
418 end subroutine rt_wave_begin
419
420 !> Open band ib. It lists and caches the band blocks and zeroes the accumulators
421 subroutine rt_wave_band_open(ib)
423 integer, intent(in) :: ib
424 integer :: iblk, igrid
425 ! Each band has its own incoming conditions at the ends
426 bc_lo_thermal = (trim(rt_sc_bc_min(ib)) == 'thermal')
427 bc_hi_thermal = (trim(rt_sc_bc_max(ib)) == 'thermal')
428 call rt_band_blocks(ib)
429 do iblk = 1, wv_nblk
430 igrid = wv_ig(iblk)
431 ! i_qrad accumulates J and later holds Q, while i_frad and rtw_fx accumulate Fy and Fx
432 ps(igrid)%w(ixg^t,i_qrad) = 0.0d0
433 ps(igrid)%w(ixg^t,i_frad) = 0.0d0
434 rtw_fx(:,:,iblk) = 0.0d0
435 end do
436 end subroutine rt_wave_band_open
437
438 !> Close the band. The fluxes are scaled by c_norm, Q is formed as the imbalance or as the -div F
439 !> blend with the tau^2 taper, and the nslot wavefront datatype is restored
440 subroutine rt_wave_band_close(nslot)
443 integer, intent(in) :: nslot
444 integer :: iblk, igrid, ix1, ix2, ih, iv, i1, i2
445 double precision :: taub(ixG^T), dfxdx, dfydy
446 double precision, allocatable :: tautop(:,:)
447 ! Fy = c sum_k w_k mu_v,k I_k, Fx = c sum_k w_k mu_h,k I_k
448 do iblk = 1, wv_nblk
449 igrid = wv_ig(iblk)
450 ps(igrid)%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_frad) = c_norm*ps(igrid)%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_frad)
451 rtw_fx(ixmlo1:ixmhi1,ixmlo2:ixmhi2,iblk) = c_norm*rtw_fx(ixmlo1:ixmhi1,ixmlo2:ixmhi2,iblk)
452 end do
453
454 if(.not.rt_sc_flux_blend) then
455 ! Without the blend Q is the imbalance, which is right in the thin limit but becomes stiff
456 ! and cancelling deep down
457 do iblk = 1, wv_nblk
458 igrid = wv_ig(iblk)
459 do ix2=ixmlo2,ixmhi2; do ix1=ixmlo1,ixmhi1
460 ! Q = c chi (J - B)
461 ps(igrid)%w(ix1,ix2,i_qrad) = c_norm*rtw_chib(ix1,ix2,iblk)* &
462 (ps(igrid)%w(ix1,ix2,i_qrad) - rtw_bb(ix1,ix2,iblk))
463 end do; end do
464 end do
465 return
466 end if
467
468 ! With the blend, Q = w c chi (J - B) + (1 - w)(-div F), where w = exp(-k tau) with tau
469 ! measured from the top, and the taper multiplies the result. dFx/dx needs Fx in the x
470 ! ghosts, which it reaches through the i_isw slot of ps_rt, and dFy/dy needs Fy in the y
471 ! ghosts from a getbc of i_frad
472 do iblk = 1, wv_nblk
473 ps_rt(wv_ig(iblk))%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_isw) = rtw_fx(ixmlo1:ixmhi1,ixmlo2:ixmhi2,iblk)
474 end do
475 call create_bc_mpi_datatype(i_isw, 1)
476 call rt_exchange(1)
477 call create_bc_mpi_datatype(i_frad, 1)
478 call getbc(global_time, 0.0d0, ps, i_frad, 1)
479 allocate(tautop(rg_nh,wv_nbxv))
480 call rt_tau_tops(tautop)
481 do iblk = 1, wv_nblk
482 igrid = wv_ig(iblk)
483 call rt_block_tau(iblk, tautop, taub)
484 do iv = imlo_v, imhi_v
485 do ih = imlo_h, imhi_h
486 call hv(ih, iv, i1, i2)
487 ! dFx/dx = (Fx_(i+1) - Fx_(i-1))/(2 dx), centred, since the ghosts hold the neighbour
488 ! block or the periodic image
489 dfxdx = (ps_rt(igrid)%w(i1+eh(1),i2+eh(2),i_isw) &
490 -ps_rt(igrid)%w(i1-eh(1),i2-eh(2),i_isw))/(2.0d0*rg_dh)
491 ! dFy/dy = (Fy_(j+1) - Fy_(j-1))/(2 dy), one-sided in the bottom and top rows of the
492 ! band, where the ghosts lie outside it
493 if(wv_bj(iblk)==1 .and. iv==imlo_v) then
494 dfydy = (ps(igrid)%w(i1+ev(1),i2+ev(2),i_frad)-ps(igrid)%w(i1,i2,i_frad))/rg_dv
495 else if(wv_bj(iblk)==wv_nbxv .and. iv==imhi_v) then
496 dfydy = (ps(igrid)%w(i1,i2,i_frad)-ps(igrid)%w(i1-ev(1),i2-ev(2),i_frad))/rg_dv
497 else
498 dfydy = (ps(igrid)%w(i1+ev(1),i2+ev(2),i_frad) &
499 -ps(igrid)%w(i1-ev(1),i2-ev(2),i_frad))/(2.0d0*rg_dv)
500 end if
501 ps(igrid)%w(i1,i2,i_qrad) = blend_heating(rtw_chib(i1,i2,iblk), &
502 ps(igrid)%w(i1,i2,i_qrad), rtw_bb(i1,i2,iblk), dfxdx+dfydy, taub(i1,i2))
503 end do
504 end do
505 end do
506 deallocate(tautop)
507 ! The datatype goes back to the nslot intensity columns for the next band
508 call create_bc_mpi_datatype(i_isw, nslot)
509 end subroutine rt_wave_band_close
510
511 !> Diffusion inflow of ray k at a thermal band end, written into the ghosts of the block copy iib.
512 !> Beyond the end the radiation is taken as the diffusion limit, I = B + abs(mu_v) dB/dtau with tau
513 !> increasing away from the band, where the isotropic I = B would carry no flux into the band and
514 !> leave it with half the diffusive flux at its edge. B and its gradient come from the two ghost
515 !> rows, the state the boundary conditions set, and not from the band, whose heating would
516 !> otherwise feed back on its own inflow. Only rays entering through the end change
517 subroutine rt_thermal_inflow(iblk, k, iib)
519 integer, intent(in) :: iblk, k
520 double precision, intent(inout) :: iib(ixG^T)
521 if(wv_bj(iblk)==1 .and. bc_lo_thermal .and. mu_v(k) > 0.0d0) &
522 call fill_rows(iglo_v, imlo_v-1, imlo_v-1, imlo_v-2)
523 if(wv_bj(iblk)==wv_nbxv .and. bc_hi_thermal .and. mu_v(k) < 0.0d0) &
524 call fill_rows(imhi_v+1, ighi_v, imhi_v+1, imhi_v+2)
525 contains
526 !> Ghost rows iv_lo to iv_hi, with the gradient from the ghost row g1 next to the band to the
527 !> one beyond it, g2. With a single ghost row there is no gradient, and the inflow is B itself
528 subroutine fill_rows(iv_lo, iv_hi, g1, g2)
529 integer, intent(in) :: iv_lo, iv_hi, g1, g2
530 integer :: ih, iv, i1, i2, a1, a2, b1, b2
531 double precision :: dbdtau
532 do ih = iglo_h, ighi_h
533 dbdtau = 0.0d0
534 call hv(ih, g1, a1, a2)
535 ! dB/dtau = (B_g2 - B_g1)/(chi dy), the gradient outward from the band, with the edge chi
536 ! that the ghosts carry. A transparent edge, where the switch is off, has no gradient
537 if(iv_hi > iv_lo .and. rtw_chib(a1,a2,iblk) > 0.0d0) then
538 call hv(ih, g2, b1, b2)
539 dbdtau = (rtw_bb(b1,b2,iblk) - rtw_bb(a1,a2,iblk))/(rtw_chib(a1,a2,iblk)*rg_dv)
540 end if
541 do iv = iv_lo, iv_hi
542 call hv(ih, iv, i1, i2)
543 ! I_ghost = B_ghost + abs(mu_v) dB/dtau
544 iib(i1,i2) = rtw_bb(i1,i2,iblk) + dabs(mu_v(k))*dbdtau
545 end do
546 end do
547 end subroutine fill_rows
548 end subroutine rt_thermal_inflow
549
550 !> Add the weighted intensity iib of ray k on block iblk to J in i_qrad, Fy in i_frad and Fx in
551 !> rtw_fx
552 subroutine rt_add_moments(iblk, k, iib)
554 integer, intent(in) :: iblk, k
555 double precision, intent(in) :: iib(ixG^T)
556 integer :: igrid
557 igrid = wv_ig(iblk)
558 ! J += w_k I_k, Fy += w_k mu_v,k I_k, Fx += w_k mu_h,k I_k
559 ps(igrid)%w(ixm^t,i_qrad) = ps(igrid)%w(ixm^t,i_qrad) + wmu(k)*iib(ixm^t)
560 ps(igrid)%w(ixm^t,i_frad) = ps(igrid)%w(ixm^t,i_frad) + wmu(k)*mu_v(k)*iib(ixm^t)
561 rtw_fx(ixm^t,iblk) = rtw_fx(ixm^t,iblk) + wmu(k)*mu_h(k)*iib(ixm^t)
562 end subroutine rt_add_moments
563
564 !> Ghost exchange of the nslot intensity columns of ps_rt from i_isw, with the physical
565 !> boundaries left alone, since the inflows are written separately
566 subroutine rt_exchange(nslot)
569 integer, intent(in) :: nslot
570 bcphys = .false.
571 call getbc(global_time, 0.0d0, ps_rt, i_isw, nslot)
572 bcphys = .true.
573 end subroutine rt_exchange
574
575 !> Epilogue. It hands the seam edges over to the next solve, restores the hydro datatype and
576 !> releases the block list
577 subroutine rt_wave_end()
580 use mod_variables, only: iwstart, nwgc
581 if(wv_seam) call rt_sc_seam_finalize(rg_nv, rt_sc_nray)
583 if(allocated(wv_ig)) deallocate(wv_ig,wv_bi,wv_bj)
584 end subroutine rt_wave_end
585
586 !> Seam read. At the domain edge where ray k enters, the wrapped upwind ghost column of the block
587 !> takes the outflow of the far edge from the last solve. A ray moving in +x enters through the
588 !> first block column and reads seamR, and one moving in -x enters through the last and reads
589 !> seamL. The column includes the corner cells above and below the block, which getbc fills from
590 !> a block the ray has not reached yet, except beyond a band end, where the inflow stays
591 subroutine rt_seam_read(iib, igrid, ibi, ibj, sx, k)
593 double precision, intent(inout) :: iib(ixG^T)
594 integer, intent(in) :: igrid, ibi, ibj, sx, k
595 integer :: iv, ivlo, ivhi, i1, i2, gy0
596 ! gy0 is the global row offset of the block along updim
597 gy0 = nint((rnode(rpxmin0_+updim,igrid)-rg_vlo)/rg_dv)
598 ivlo = merge(imlo_v, imlo_v-1, ibj==1)
599 ivhi = merge(imhi_v, imhi_v+1, ibj==wv_nbxv)
600 if(ibi==1 .and. sx>0) then
601 do iv = ivlo, ivhi
602 call hv(imlo_h-1, iv, i1, i2); iib(i1,i2) = rt_sc_seamr_prev(gy0+(iv-imlo_v)+1,k)
603 end do
604 else if(ibi==rg_nbh .and. sx<0) then
605 do iv = ivlo, ivhi
606 call hv(imhi_h+1, iv, i1, i2); iib(i1,i2) = rt_sc_seaml_prev(gy0+(iv-imlo_v)+1,k)
607 end do
608 end if
609 end subroutine rt_seam_read
610
611 !> Seam write. At the far domain edge the outflow of ray k is kept for the next solve. A ray
612 !> moving in +x leaves through the last block column into seamR, and one moving in -x leaves
613 !> through the first into seamL
614 subroutine rt_seam_write(iib, igrid, ibi, sx, k)
616 double precision, intent(in) :: iib(ixG^T)
617 integer, intent(in) :: igrid, ibi, sx, k
618 integer :: iv, i1, i2, gy0
619 gy0 = nint((rnode(rpxmin0_+updim,igrid)-rg_vlo)/rg_dv)
620 if(ibi==rg_nbh .and. sx>0) then
621 do iv = imlo_v, imhi_v
622 call hv(imhi_h, iv, i1, i2); rt_sc_seamr_cur(gy0+(iv-imlo_v)+1,k) = iib(i1,i2)
623 end do
624 else if(ibi==1 .and. sx<0) then
625 do iv = imlo_v, imhi_v
626 call hv(imlo_h, iv, i1, i2); rt_sc_seaml_cur(gy0+(iv-imlo_v)+1,k) = iib(i1,i2)
627 end do
628 end if
629 end subroutine rt_seam_write
630
631 !> Inflow intensity in slot islot. The ghosts beyond the band ends, and beyond the horizontal
632 !> edges where hdim is not periodic, take B of the edge cell when that end is thermal and zero
633 !> when it is open. The band ends are interior rows of the domain, so getbc never fills them
634 subroutine rt_inflow_ghosts(islot)
636 integer, intent(in) :: islot
637 integer :: iblk, igrid
638 do iblk = 1, wv_nblk
639 igrid = wv_ig(iblk)
640 if(wv_bj(iblk)==1) call fill_rows(iglo_v, imlo_v-1, imlo_v, bc_lo_thermal)
641 if(wv_bj(iblk)==wv_nbxv) call fill_rows(imhi_v+1, ighi_v, imhi_v, bc_hi_thermal)
642 end do
643 if(bc_h_periodic) return
644 do iblk = 1, wv_nblk
645 igrid = wv_ig(iblk)
646 if(wv_bi(iblk)==1) call fill_columns(iglo_h, imlo_h-1, imlo_h, bc_hlo_thermal)
647 if(wv_bi(iblk)==rg_nbh) call fill_columns(imhi_h+1, ighi_h, imhi_h, bc_hhi_thermal)
648 end do
649 contains
650 !> Ghost rows iv_lo to iv_hi, from the edge row iv_src
651 subroutine fill_rows(iv_lo, iv_hi, iv_src, thermal)
652 integer, intent(in) :: iv_lo, iv_hi, iv_src
653 logical, intent(in) :: thermal
654 integer :: ih, iv, i1, i2, j1, j2
655 do iv = iv_lo, iv_hi
656 do ih = iglo_h, ighi_h
657 call hv(ih, iv, i1, i2)
658 call hv(ih, iv_src, j1, j2)
659 ps_rt(igrid)%w(i1,i2,islot) = merge(rtw_bb(j1,j2,iblk), 0.0d0, thermal)
660 end do
661 end do
662 end subroutine fill_rows
663 !> Ghost columns ih_lo to ih_hi, from the edge column ih_src
664 subroutine fill_columns(ih_lo, ih_hi, ih_src, thermal)
665 integer, intent(in) :: ih_lo, ih_hi, ih_src
666 logical, intent(in) :: thermal
667 integer :: ih, iv, i1, i2, j1, j2
668 do ih = ih_lo, ih_hi
669 do iv = iglo_v, ighi_v
670 call hv(ih, iv, i1, i2)
671 call hv(ih_src, iv, j1, j2)
672 ps_rt(igrid)%w(i1,i2,islot) = merge(rtw_bb(j1,j2,iblk), 0.0d0, thermal)
673 end do
674 end do
675 end subroutine fill_columns
676 end subroutine rt_inflow_ghosts
677
678 !> Sweep block iblk for ray k in place on iib. With updim = 1 the block is transposed around the
679 !> kernel, which expects its first index to be horizontal. The sweep time of each block is
680 !> accumulated for the load balancer
681 subroutine rt_sweep_one(iblk, k, iib)
683 integer, intent(in) :: iblk, k
684 double precision, intent(inout) :: iib(ixG^T)
685 double precision :: tii(ixGlo2:ixGhi2,ixGlo1:ixGhi1), t0
686 integer :: igrid
687 if(lb_automatic) t0 = mpi_wtime()
688 igrid = wv_ig(iblk)
689 if(updim==2) then
690 call sc_sweep_block(mu_h(k), mu_v(k), rg_dh, rg_dv, wv_xwrap, ixg^ll, ixm^ll, &
691 rtw_chib(:,:,iblk), rtw_bb(:,:,iblk), iib)
692 else
693 tii = transpose(iib)
694 call sc_sweep_block(mu_h(k), mu_v(k), rg_dh, rg_dv, wv_xwrap, &
695 ixglo2,ixglo1,ixghi2,ixghi1, ixmlo2,ixmlo1,ixmhi2,ixmhi1, &
696 transpose(rtw_chib(:,:,iblk)), transpose(rtw_bb(:,:,iblk)), tii)
697 iib = transpose(tii)
698 end if
699 if(lb_automatic) block_cost_rt(igrid) = block_cost_rt(igrid) + (mpi_wtime() - t0)
700 end subroutine rt_sweep_one
701
702 !> Position of a block counted from the upwind end of a row of n blocks, for a ray whose cosine
703 !> along that row has sign s. With s = 0 no block depends on its neighbour along the row, so
704 !> every block sits at the first position
705 pure integer function wave_stage(b, n, s)
706 integer, intent(in) :: b, n, s
707 wave_stage = merge(b, merge(n+1-b, 1, s<0), s>0)
708 end function wave_stage
709
710 !> Ray-by-ray KBA, with one ray at a time passing through the diagonal wavefront
711 subroutine rt_sc_solve_wave()
713 integer :: ib, k, sx, sy, d, iblk, igrid
714 double precision :: iib(ixG^T)
715 call rt_wave_begin(1)
716 do ib = 1, rt_sc_nband
717 call rt_wave_band_open(ib)
718 do k = 1, rt_sc_nray
719 ! The signs of the cosines fix the upwind corner the wavefront starts from
720 sx = ray_sign(mu_h(k)); sy = ray_sign(mu_v(k))
721 ! The intensity is zeroed for every ray, as in the gather, so the rays stay independent
722 do iblk = 1, wv_nblk
723 ps_rt(wv_ig(iblk))%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_isw) = 0.0d0
724 end do
725 ! Diagonal d holds the blocks with p + q = d, starting from the upwind corner at d = 2.
726 ! Before each diagonal the getbc hands the outflow faces of the last one to their
727 ! downstream neighbours, and the band-end and edge inflows are written over its ghosts
728 do d = 2, wv_dmax
729 call rt_exchange(1)
730 call rt_inflow_ghosts(i_isw)
731 do iblk = 1, wv_nblk
732 if(wave_stage(wv_bi(iblk),rg_nbh,sx) + wave_stage(wv_bj(iblk),wv_nbxv,sy) /= d) cycle
733 igrid = wv_ig(iblk)
734 ! The block is swept on a copy that holds its ghosts, with the seam inflow added at the
735 ! domain edge, and its interior is written back
736 iib = ps_rt(igrid)%w(:,:,i_isw)
737 if(wv_seam) call rt_seam_read(iib, igrid, wv_bi(iblk), wv_bj(iblk), sx, k)
738 call rt_thermal_inflow(iblk, k, iib)
739 call rt_sweep_one(iblk, k, iib)
740 ps_rt(igrid)%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_isw) = iib(ixmlo1:ixmhi1,ixmlo2:ixmhi2)
741 if(wv_seam) call rt_seam_write(iib, igrid, wv_bi(iblk), sx, k)
742 end do
743 end do
744 ! Once the ray has crossed the whole band, its weighted intensity adds to J, Fy and Fx
745 do iblk = 1, wv_nblk
746 call rt_add_moments(iblk, k, ps_rt(wv_ig(iblk))%w(:,:,i_isw))
747 end do
748 end do
749 call rt_wave_band_close(1)
750 end do
751 call rt_wave_end()
752 end subroutine rt_sc_solve_wave
753
754 !> Pipelined KBA. The direction-sign corners run together in batches of up to nisw, and within
755 !> each corner the rays follow one another through its wavefront. Each corner has two slots, one
756 !> for its even and one for its odd rays: the diagonal upwind neighbour of a block is one ray
757 !> ahead, so with a single slot its ghost would already hold the next ray, while with two it
758 !> still holds the current one when the getbc delivers it. The result agrees with that of
759 !> rt_sc_solve_wave to round-off, since the moments are summed in another order
763 integer :: ib, k, sx, sy, iblk, igrid, islot, nslot
764 integer :: ng, gidx, g, jj, tt_clk, tmax, gbase, gend, gg, npb
765 integer :: grp_sx(9), grp_sy(9), grp_n(9)
766 integer, allocatable :: grp_ray(:,:)
767 double precision :: iib(ixG^T)
768
769 ! The rays are grouped by the signs of (mu_h, mu_v), since the rays of one group share an
770 ! upwind corner and therefore one schedule. Counting the zero sign there are nine sign pairs,
771 ! so no more groups can occur
772 allocate(grp_ray(9, max(rt_sc_nray,1)))
773 ng = 0
774 do k = 1, rt_sc_nray
775 sx = ray_sign(mu_h(k)); sy = ray_sign(mu_v(k))
776 gidx = 0
777 do g = 1, ng
778 if(grp_sx(g)==sx .and. grp_sy(g)==sy) then; gidx = g; exit; end if
779 end do
780 if(gidx==0) then
781 ng = ng + 1
782 grp_sx(ng)=sx; grp_sy(ng)=sy; grp_n(ng)=0; gidx=ng
783 end if
784 grp_n(gidx) = grp_n(gidx) + 1
785 grp_ray(gidx, grp_n(gidx)) = k
786 end do
787 ! Corners per batch, two slots each, within the nw columns of ps_rt
788 npb = max(1, min(nisw, nw/2))
789 call rt_wave_begin(2*npb)
790 do ib = 1, rt_sc_nband
791 call rt_wave_band_open(ib)
792 ! In group gg a block sits at stage p+q, and at clock tt_clk it sweeps ray
793 ! jj = tt_clk-(p+q)+2 of the group into slot i_isw+2(gg-gbase)+mod(jj,2). One getbc per
794 ! clock advances every front of the batch by one stage. The accumulators were zeroed when the
795 ! band opened, so they sum over the batches
796 do gbase = 1, ng, npb
797 gend = min(gbase+npb-1, ng)
798 nslot = 2*(gend-gbase+1)
799 ! The slots of the batch start from zero intensity
800 do iblk = 1, wv_nblk
801 ps_rt(wv_ig(iblk))%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,i_isw:i_isw+2*npb-1) = 0.0d0
802 end do
803 ! The last ray of a group reaches the last diagonal at clock grp_n + wv_dmax - 2, and the
804 ! batch runs until its longest group has finished
805 tmax = wv_dmax - 2
806 do gg = gbase, gend; tmax = max(tmax, grp_n(gg) + wv_dmax - 2); end do
807 ! The datatype is built once per batch, for its number of slots
808 call create_bc_mpi_datatype(i_isw, nslot)
809 do tt_clk = 1, tmax
810 ! One getbc moves every front, and the band-end and edge inflows are then written into
811 ! each slot
812 call rt_exchange(nslot)
813 do gg = gbase, gend
814 call rt_inflow_ghosts(i_isw+2*(gg-gbase))
815 call rt_inflow_ghosts(i_isw+2*(gg-gbase)+1)
816 end do
817 do gg = gbase, gend
818 do iblk = 1, wv_nblk
819 ! jj = t - (p + q) + 2, the ray of the group that reaches this block at clock t. A
820 ! block sweeps at this clock only when that is a ray of the group
821 jj = tt_clk - (wave_stage(wv_bi(iblk),rg_nbh,grp_sx(gg)) &
822 + wave_stage(wv_bj(iblk),wv_nbxv,grp_sy(gg))) + 2
823 if(jj < 1 .or. jj > grp_n(gg)) cycle
824 k = grp_ray(gg, jj)
825 islot = i_isw+2*(gg-gbase)+mod(jj,2)
826 igrid = wv_ig(iblk)
827 ! The copy keeps the upwind ghosts from getbc, and its interior is cleared for ray k
828 iib = ps_rt(igrid)%w(:,:,islot)
829 iib(ixmlo1:ixmhi1,ixmlo2:ixmhi2) = 0.0d0
830 if(wv_seam) call rt_seam_read(iib, igrid, wv_bi(iblk), wv_bj(iblk), grp_sx(gg), k)
831 call rt_thermal_inflow(iblk, k, iib)
832 call rt_sweep_one(iblk, k, iib)
833 ! The interior goes back into the slot, the seam edge is kept, and the weighted
834 ! intensity adds to J, Fy and Fx straight away
835 ps_rt(igrid)%w(ixmlo1:ixmhi1,ixmlo2:ixmhi2,islot) = iib(ixmlo1:ixmhi1,ixmlo2:ixmhi2)
836 if(wv_seam) call rt_seam_write(iib, igrid, wv_bi(iblk), grp_sx(gg), k)
837 call rt_add_moments(iblk, k, iib)
838 end do
839 end do
840 end do
841 end do
842 call rt_wave_band_close(2*npb)
843 end do
844 call rt_wave_end()
845 end subroutine rt_sc_solve_wave_pipe
846 }
847end module mod_rt_sc_solvers
subroutine fill_rows(iv_lo, iv_hi, g1, g2)
Ghost rows iv_lo to iv_hi, with the gradient from the ghost row g1 next to the band to the one beyond...
subroutine copy_edge_row(iv_lo, iv_hi, iv_src, thermal)
Ghost rows iv_lo to iv_hi of the chi and B caches of block iblk beyond the edge row iv_src....
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
update ghost cells of all blocks including physical boundaries
subroutine getbc(time, qdt, psb, nwstart, nwbc)
do update ghost cells of all blocks including physical boundaries
subroutine create_bc_mpi_datatype(nwstart, nwbc, nwfull)
This module contains definitions of global parameters and variables and some generic functions/subrou...
logical lb_diagnose
Per-rank load-balance timing diagnostic toggle (off by default). When .true., per-rank wall times are...
double precision global_time
The global simulation time.
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
integer ixm
the mesh range of a physical block without ghost cells
logical lb_automatic
Cost-weighted automatic load balancer toggle (off by default). When .true., the SFC partitioner cuts ...
double precision c_norm
Normalised speed of light.
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
double precision, dimension(:), allocatable block_cost_rt
Per-step per-block cost of the short-characteristics sweep, indexed by igrid. Separate from block_cos...
double precision lb_rt_accum
Per-rank wall time spent in the SC radiative-transfer KBA sweep this step, for the lb_diagnose rank-t...
integer, dimension(:,:), allocatable node
Solvers of the short-characteristics (SC) radiative transfer and their dispatch.
subroutine rt_wave_band_close(nslot)
Close the band. The fluxes are scaled by c_norm, Q is formed as the imbalance or as the -div F blend ...
subroutine rt_tau_tops(tautop)
Tau at the top face of every block row of the current band, per global column along hdim....
subroutine rt_band_blocks(ib)
List the blocks of this rank that lie in band ib, with their block position (bi, bj),...
subroutine rt_sc_solve_wave()
Ray-by-ray KBA, with one ray at a time passing through the diagonal wavefront.
subroutine rt_wave_begin(nslot)
Prologue of both KBA schedules on level rt_sc_amr_level. It checks the setup, sets the x-distribution...
subroutine rt_sc_solve_gather()
Replicated gather, the original implementation, kept for comparison and not for production....
logical function rt_grid_index(xh, xv, ib, ig, jg)
Indices (ig, jg) on the replicated grid of band ib of the cell centred at xh along hdim and xv along ...
subroutine rt_wave_band_open(ib)
Open band ib. It lists and caches the band blocks and zeroes the accumulators.
subroutine, public rt_sc_solve()
Collective, once per step. For each band on rt_sc_amr_level it sweeps all rays and forms the heating ...
subroutine rt_sweep_one(iblk, k, iib)
Sweep block iblk for ray k in place on iib. With updim = 1 the block is transposed around the kernel,...
subroutine rt_seam_write(iib, igrid, ibi, sx, k)
Seam write. At the far domain edge the outflow of ray k is kept for the next solve....
subroutine rt_thermal_inflow(iblk, k, iib)
Diffusion inflow of ray k at a thermal band end, written into the ghosts of the block copy iib....
subroutine rt_seam_read(iib, igrid, ibi, ibj, sx, k)
Seam read. At the domain edge where ray k enters, the wrapped upwind ghost column of the block takes ...
subroutine rt_inflow_ghosts(islot)
Inflow intensity in slot islot. The ghosts beyond the band ends, and beyond the horizontal edges wher...
subroutine rt_exchange(nslot)
Ghost exchange of the nslot intensity columns of ps_rt from i_isw, with the physical boundaries left ...
subroutine rt_sc_validate_pipe()
Validation of the pipelined KBA. The ray-by-ray Q is copied into i_iout and the pipelined Q left in i...
pure integer function wave_stage(b, n, s)
Position of a block counted from the upwind end of a row of n blocks, for a ray whose cosine along th...
subroutine rt_wave_end()
Epilogue. It hands the seam edges over to the next solve, restores the hydro datatype and releases th...
subroutine rt_sc_solve_wave_pipe()
Pipelined KBA. The direction-sign corners run together in batches of up to nisw, and within each corn...
subroutine rt_block_tau(iblk, tautop, taub)
Tau from the band top to every cell centre of block iblk, integrated down from the top face of the bl...
subroutine rt_add_moments(iblk, k, iib)
Add the weighted intensity iib of ray k on block iblk to J in i_qrad, Fy in i_frad and Fx in rtw_fx.
Parameters, resolved state and lifecycle of the short-characteristics (SC) radiative transfer,...
integer, public i_frad
double precision rg_dh
double precision, dimension(:), allocatable mu_h
Ray cosines along the RT grid axes (hdim, updim). They are (mux, muy) for updim = 2 and the swapped p...
integer, public rt_sc_nband
double precision, dimension(:), allocatable mu_v
integer, dimension(rt_sc_maxband) band_nv
integer rt_node_comm
Node-local communicator for the moment reduction of the gather. The ranks reach that reduction far ou...
logical, public rt_sc_distributed
The distributed KBA wavefront. Each rank sweeps only its own band blocks, one diagonal at a time,...
integer, public rt_sc_amr_level
The AMR level the RT runs on. The bands are held at this level so that the RT sees a uniform grid,...
double precision, public rt_wt_swp
integer, public rt_nsolve
double precision, public rt_wt_wave
logical, public rt_sc_active
True when the physics module has switched the RT on, with hd_radiative_transfer or mhd_radiative_tran...
logical, public rt_sc_flux_blend
rt_sc_flux_blend blends the thin heating c chi (J - B) with the thick -div F. rt_sc_cfl limits the he...
integer rg_nh
The RT grid on rt_sc_amr_level along (hdim, updim): cells across the domain, cells per block,...
subroutine rt_allreduce_chunk(a, n1, n2, comm)
In-place sum allreduce of a 2D array in chunks of whole columns of about 1 MB.
double precision, public rt_wt_red
integer rt_lead_comm
Communicator of one leader rank per node. The field gather sums within each node, then over the leade...
integer, public i_iout
character(len=10), dimension(rt_sc_maxband), public rt_sc_bc_min
double precision, public rt_wt_gath
Cumulative wall time of the field gather, the sweep, the moment reduction and the scatter,...
integer, public i_qrad
Extra-variable indices of the heating Q, the vertical flux F and the emergent intensity Iout,...
logical, public rt_sc_pipeline
The pipelined KBA. The four sweep corners run at once, each with its own intensity slot,...
double precision rg_dv
character(len=10), dimension(rt_sc_maxband), public rt_sc_bc_max
logical, public rt_sc_debug
Prints the cumulative RT timing every 10 solves.
logical, public rt_sc_valpipe
For validation, runs the ray-by-ray and pipelined waves on the same state and prints max|dQ|.
double precision time_rt
Definition mod_timing.t:19
subroutine timer_open(t0)
Open a timing window, whose length leaves out the EoS and ghost-cell time spent inside it,...
Definition mod_timing.t:66
double precision function timer_close(t0)
Wall time since timer_open, less the EoS and ghost-cell time spent in between.
Definition mod_timing.t:73
integer nwgc
Number of variables which need to be updated in ghost cells.