MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_escape_probability.t
Go to the documentation of this file.
1!> Module for escape probability radiative cooling modification.
2!>
3!> Computes column mass along the 1D domain and provides an escape probability
4!> factor beta(tau) = (1 - exp(-tau))/tau to multiply the radiative cooling rate.
5!> This replaces the ad-hoc density taper with a physically-motivated opacity proxy
6!> based on the formalism of Carlsson & Leenaarts (2012).
7!>
8!> Column mass is measured from the domain centre (apex of a symmetric loop)
9!> TOWARD the footpoints. This gives zero column mass at the apex (full coronal
10!> cooling) and large column mass at the footpoints (suppressed chromospheric
11!> cooling).
12!>
13!> Currently supports 1D slab_uniform geometry with AMR (Phase 1).
15 use mod_global_parameters, only: std_len
16 implicit none
17 private
18
19 ! Public routines
20 public :: escape_prob_init
22
23 ! Module state
24 integer :: n_aux = 0 !< Total cells at finest AMR level
25 double precision :: dx_aux = 0.0d0 !< Cell width at finest level
26 integer :: iw_colmass_ = -1 !< Index into wextra for column mass
27 logical :: escape_sym_ = .false. !< Symmetric loop (apex at domain center)
28 double precision :: escape_height_ = 0.0d0 !< Max height from footpoint for colmass (code units); 0 = no limit
29 logical :: arrays_allocated = .false. !< Deferred allocation flag
30
31 ! Work arrays (allocated on first use, after mesh params are set)
32 double precision, allocatable :: rho_dx_local(:)
33 double precision, allocatable :: rho_dx_global(:)
34 double precision, allocatable :: colmass_left(:)
35 double precision, allocatable :: colmass_right(:)
36
37contains
38
39 !> Register escape probability parameters.
40 !> Called during hd_phys_init (before mesh parameters are available).
41 !> Grid-dependent allocation is deferred to the first compute call.
42 subroutine escape_prob_init(iw_colmass, escape_sym, escape_height)
44 use mod_comm_lib, only: mpistop
45 integer, intent(in) :: iw_colmass
46 logical, intent(in) :: escape_sym
47 double precision, intent(in) :: escape_height
48
49 iw_colmass_ = iw_colmass
50 escape_sym_ = escape_sym
51 escape_height_ = escape_height
52
53 {^nooned
54 call mpistop("escape probability currently only supports 1D")
55 }
56
57 end subroutine escape_prob_init
58
59 !> Allocate work arrays once mesh parameters are available.
60 subroutine escape_prob_setup_arrays()
62 integer :: level_factor
63
64 {^ifoned
65 level_factor = 2**(refine_max_level - 1)
66 n_aux = domain_nx1 * level_factor
67 dx_aux = (xprobmax1 - xprobmin1) / dble(n_aux)
68 }
69
70 allocate(rho_dx_local(n_aux))
71 allocate(rho_dx_global(n_aux))
72 allocate(colmass_left(n_aux))
73 allocate(colmass_right(n_aux))
74 arrays_allocated = .true.
75
76 if (mype == 0) then
77 write(*, '(A,I8,A,ES10.3,A,L1,A,ES10.3)') &
78 ' Escape probability: n_aux=', n_aux, &
79 ' dx_aux=', dx_aux, ' symmetric=', escape_sym_, &
80 ' height=', escape_height_
81 end if
82
83 end subroutine escape_prob_setup_arrays
84
85 !> Compute column mass from the corona toward the footpoints and scatter
86 !> to wextra. Called once per timestep before advance().
87 !>
88 !> For symmetric loops (escape_sym_=.true.): uses the domain centre as
89 !> the apex and integrates outward toward both footpoints. This avoids
90 !> the apex jumping when condensations form (density minimum is not stable).
91 !> For non-symmetric atmospheres: integrates from the top of the domain
92 !> (xprobmax) downward.
95 use mod_variables, only: iw_rho
97
98 integer :: iigrid, igrid, level, ratio
99 integer :: ix1, k, klo, khi, kk, k_apex, k_cutoff_left, k_cutoff_right
100 integer :: k_left, k_right
101 double precision :: x_cell, rho_cell, running_sum
102 double precision :: cm_value
103
104 ! Deferred allocation on first call
105 if (.not. arrays_allocated) call escape_prob_setup_arrays()
106 if (n_aux == 0) return
107
108 {^ifoned
109 ! 1. Zero local accumulator
110 rho_dx_local = 0.0d0
111
112 ! 2. Gather: loop over active blocks, deposit density onto aux array
113 do iigrid = 1, igridstail_active
114 igrid = igrids_active(iigrid)
115 level = node(plevel_, igrid)
116 ratio = 2**(refine_max_level - level)
117
118 do ix1 = ixmlo1, ixmhi1
119 x_cell = ps(igrid)%x(ix1, 1)
120 rho_cell = ps(igrid)%w(ix1, iw_rho)
121
122 ! Map cell center to aux index (1-based)
123 k = floor((x_cell - xprobmin1) / dx_aux) + 1
124 k = max(1, min(k, n_aux))
125
126 if (ratio == 1) then
127 ! Fine level: 1:1 mapping
128 rho_dx_local(k) = rho_dx_local(k) + rho_cell * dx_aux
129 else
130 ! Coarse level: spread density across 'ratio' fine cells.
131 ! The coarse cell spans 'ratio' fine cells centered on k.
132 klo = k - ratio/2 + 1
133 khi = klo + ratio - 1
134 klo = max(1, klo)
135 khi = min(n_aux, khi)
136 do kk = klo, khi
137 rho_dx_local(kk) = rho_dx_local(kk) + rho_cell * dx_aux
138 end do
139 end if
140 end do
141 end do
142
143 ! 3. MPI global reduction
144 call mpi_allreduce(rho_dx_local, rho_dx_global, n_aux, &
145 mpi_double_precision, mpi_sum, icomm, ierrmpi)
146
147 ! 3b. Fill gaps left by AMR level transitions.
148 ! At coarse/fine boundaries, the interior-cell gather can miss
149 ! a few aux cells (ghost cell region between blocks at different
150 ! levels). Use symmetric fill: average of nearest left and right
151 ! populated neighbours for interior gaps, boundary fill for edges.
152 do k = 1, n_aux
153 if (rho_dx_global(k) == 0.0d0) then
154 ! Find nearest populated neighbour to the left
155 k_left = 0
156 do kk = k - 1, 1, -1
157 if (rho_dx_global(kk) /= 0.0d0) then
158 k_left = kk
159 exit
160 end if
161 end do
162 ! Find nearest populated neighbour to the right
163 k_right = 0
164 do kk = k + 1, n_aux
165 if (rho_dx_global(kk) /= 0.0d0) then
166 k_right = kk
167 exit
168 end if
169 end do
170 ! Average of both neighbours (symmetric), or one-sided at edges
171 if (k_left > 0 .and. k_right > 0) then
172 rho_dx_global(k) = half * (rho_dx_global(k_left) &
173 + rho_dx_global(k_right))
174 else if (k_left > 0) then
175 rho_dx_global(k) = rho_dx_global(k_left)
176 else if (k_right > 0) then
177 rho_dx_global(k) = rho_dx_global(k_right)
178 end if
179 end if
180 end do
181
182 ! 3c. Zero rho_dx above escape_height from each footpoint.
183 ! This makes the column mass integration START at the cutoff height
184 ! rather than at the apex, so coronal condensations are excluded
185 ! from the chromospheric column mass.
186 if (escape_height_ > 0.0d0) then
187 k_cutoff_left = floor(escape_height_ / dx_aux) + 1
188 k_cutoff_left = min(k_cutoff_left, n_aux)
189 if (escape_sym_) then
190 k_cutoff_right = n_aux - k_cutoff_left + 1
191 k_cutoff_right = max(1, k_cutoff_right)
192 do k = k_cutoff_left + 1, k_cutoff_right - 1
193 rho_dx_global(k) = 0.0d0
194 end do
195 else
196 do k = k_cutoff_left + 1, n_aux
197 rho_dx_global(k) = 0.0d0
198 end do
199 end if
200 end if
201
202 ! 4. Prefix sums for column mass from both boundaries
203 ! Left: colmass_left(i) = integral of rho*dx from x=xprobmin to cell i
204 running_sum = 0.0d0
205 do k = 1, n_aux
206 running_sum = running_sum + rho_dx_global(k)
207 colmass_left(k) = running_sum
208 end do
209
210 ! Right: colmass_right(i) = integral of rho*dx from cell i to x=xprobmax
211 running_sum = 0.0d0
212 do k = n_aux, 1, -1
213 running_sum = running_sum + rho_dx_global(k)
214 colmass_right(k) = running_sum
215 end do
216
217 ! 5. Set apex index for column mass reference point
218 if (escape_sym_) then
219 ! Symmetric loop: apex is at the domain centre (fixed)
220 k_apex = n_aux / 2
221 else
222 ! Non-symmetric: "corona" is at the top of the domain
223 k_apex = n_aux
224 end if
225
226 ! 6. Scatter: write column mass FROM APEX TOWARD FOOTPOINTS to wextra.
227 ! For symmetric: cm(x) = integral from apex to x (outward from corona)
228 ! This gives 0 at apex, increasing toward both footpoints.
229 do iigrid = 1, igridstail_active
230 igrid = igrids_active(iigrid)
231
232 do ix1 = ixglo1, ixghi1
233 x_cell = ps(igrid)%x(ix1, 1)
234 k = floor((x_cell - xprobmin1) / dx_aux) + 1
235 k = max(1, min(k, n_aux))
236
237 if (escape_sym_) then
238 ! Symmetric loop: column mass from cell to midpoint face
239 ! The midpoint face lies between cells k_apex and k_apex+1.
240 ! Left half: sum(k+1 : k_apex), right half: sum(k_apex+1 : k-1)
241 ! This gives identical cell counts for mirror-symmetric pairs.
242 if (k <= k_apex) then
243 ! Left half: cm = colmass from k to midpoint face
244 cm_value = colmass_left(k_apex) - colmass_left(k)
245 else
246 ! Right half: cm = colmass from midpoint face to k
247 cm_value = colmass_right(k_apex + 1) - colmass_right(k)
248 end if
249 else
250 ! Non-symmetric: cm from top of domain downward = colmass_right
251 cm_value = colmass_right(k)
252 end if
253
254 ps(igrid)%wextra(ix1, iw_colmass_) = max(cm_value, 0.0d0)
255 end do
256 end do
257 }
258
259 end subroutine escape_prob_compute_colmass
260
261end module mod_escape_probability
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
This module contains variables that describe the connectivity of the mesh and also data structures fo...
integer, dimension(:), allocatable igrids_active
Module for escape probability radiative cooling modification.
subroutine, public escape_prob_compute_colmass()
Compute column mass from the corona toward the footpoints and scatter to wextra. Called once per time...
subroutine, public escape_prob_init(iw_colmass, escape_sym, escape_height)
Register escape probability parameters. Called during hd_phys_init (before mesh parameters are availa...
This module contains definitions of global parameters and variables and some generic functions/subrou...
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
integer ierrmpi
A global MPI error return code.
integer refine_max_level
Maximal number of AMR levels.
integer, dimension(:,:), allocatable node
integer iw_rho
Index of the (gas) density.