1
1
"""
2
- AzimuthalQuadrature{N,T<:Real}
2
+ AzimuthalQuadrature{N,N2,N4, T<:Real}
3
3
4
- Holds information of the azimuthal quadrature, such as the azimuthal angles `ϕs`, the
5
- azimuthal spacings `δ` as well as effective azimuthal spacings `δs` and the azimuthal
6
- weights `ωₐ`.
4
+ Represents the angular discretization for ray tracing in neutron transport calculations.
5
+
6
+ This structure holds all information related to the azimuthal quadrature, including the discretized
7
+ azimuthal angles, their associated weights for numerical integration, and the spacing parameters
8
+ used in the ray tracing algorithm.
9
+
10
+ ## Type Parameters
11
+ - `N`: Total number of azimuthal angles (must be a multiple of 4)
12
+ - `N2`: Number of angles in the half-plane (0, π), equal to `N÷2`
13
+ - `N4`: Number of angles in the first quadrant (0, π/2), equal to `N÷4`
14
+ - `T<:Real`: Numeric type for all calculations (Float64, Float32, etc.)
15
+
16
+ ## Fields
17
+ - `δ::T`: Nominal azimuthal spacing used to generate the quadrature
18
+ - `δs::Vector{T}`: Effective azimuthal spacings for each angle (length N2)
19
+ - `ϕs::Vector{T}`: Azimuthal angles in radians (length N2, spanning 0 to π)
20
+ - `ωₐ::Vector{T}`: Angular weights for numerical integration (length N2)
21
+
22
+ ## Mathematical Structure
23
+
24
+ The azimuthal quadrature organizes angles into a symmetric structure:
25
+
26
+ - **Full domain**: [0, 2π) with N angles total
27
+ - **Half-plane**: [0, π) with N2 angles (unique directions)
28
+ - **First quadrant**: [0, π/2) with N4 angles
29
+ - **Second quadrant**: [π/2, π) with N4 angles
30
+
31
+ Angles in (π, 2π) are avoided because of the symmetry of the problem. This helps reducing the
32
+ computational cost.
33
+
34
+ ## Usage
35
+
36
+ ```julia
37
+ # Create quadrature with 8 angles and spacing 0.02
38
+ aq = AzimuthalQuadrature(8, 0.02)
39
+
40
+ # Access quadrature properties
41
+ n_total = n_azim_total(aq) # 8
42
+ n_half = n_azim_half(aq) # 4
43
+ n_quad = n_azim_quad(aq) # 2
44
+
45
+ # Get angle ranges
46
+ quad1 = azimuthal_quadrant_1(aq) # 1:2
47
+ quad2 = azimuthal_quadrant_2(aq) # 3:4
48
+
49
+ # Check direction
50
+ is_rightward_direction(aq, 1) # true
51
+ is_leftward_direction(aq, 3) # true
52
+
53
+ # Find supplementary angle
54
+ supp_idx = supplementary_azimuthal_idx(aq, 1) # 4
55
+ ```
56
+
57
+ ## Integration Weights
58
+
59
+ The weights `ωₐ` are computed to ensure proper numerical integration:
60
+
61
+ ```julia
62
+ # Sum of weights should integrate to unity
63
+ sum(aq.ωₐ) ≈ 0.5 # Over half-plane (0, π)
64
+ ```
65
+
66
+ ## Notes
67
+
68
+ - The structure is designed for 2D ray tracing in neutron transport
69
+ - Angles are automatically organized to exploit symmetry
70
+ - Weights are normalized for proper integration over the angular domain
71
+ - The quadrature supports various numeric types for precision control
72
+
73
+ See also: [`init_weights!`](@ref), [`n_azim_total`](@ref), [`azimuthal_quadrant_1`](@ref)
7
74
"""
8
- struct AzimuthalQuadrature{N,T<: Real ,N2,N4 }
75
+ struct AzimuthalQuadrature{N,N2,N4, T<: Real }
9
76
δ:: T
10
77
δs:: Vector{T}
11
78
ϕs:: Vector{T}
12
79
ωₐ:: Vector{T}
13
80
end
14
81
15
- nazim (:: AzimuthalQuadrature{N} ) where {N} = N
16
- nazim2 (:: AzimuthalQuadrature{N,T,N2} ) where {N,T,N2} = N2
17
- nazim4 (:: AzimuthalQuadrature{N,T,N2,N4} ) where {N,T,N2,N4} = N4
82
+ """
83
+ n_azim_total(aq::AzimuthalQuadrature)
84
+
85
+ Returns the total number of azimuthal angles in the quadrature.
86
+
87
+ This is the full number of azimuthal directions used in the discretization, spanning the complete
88
+ angular domain [0, 2π).
89
+
90
+ ## Arguments
91
+ - `aq`: Azimuthal quadrature structure
92
+
93
+ ## Returns
94
+ - Total number of azimuthal angles (N)
95
+
96
+ ## Example
97
+ ```julia
98
+ aq = AzimuthalQuadrature(8, 0.1)
99
+ n_azim_total(aq) # Returns 8
100
+ ```
101
+ """
102
+ n_azim_total (:: AzimuthalQuadrature{N} ) where {N} = N
18
103
104
+ """
105
+ n_azim_half(aq::AzimuthalQuadrature)
106
+
107
+ Returns the number of azimuthal angles in the half-plane (0, π).
108
+
109
+ This represents the number of unique azimuthal directions needed for 2D ray tracing, since angles in
110
+ (π, 2π) are supplementary to those in (0, π).
111
+
112
+ ## Arguments
113
+ - `aq`: Azimuthal quadrature structure
114
+
115
+ ## Returns
116
+ - Number of azimuthal angles in half-plane (N2 = N/2)
117
+
118
+ ## Example
119
+ ```julia
120
+ aq = AzimuthalQuadrature(8, 0.1)
121
+ n_azim_half(aq) # Returns 4
122
+ ```
123
+ """
124
+ n_azim_half (:: AzimuthalQuadrature{N,N2} ) where {N,N2} = N2
125
+
126
+ """
127
+ n_azim_quad(aq::AzimuthalQuadrature)
128
+
129
+ Returns the number of azimuthal angles in the first quadrant (0, π/2).
130
+
131
+ This represents the number of unique directions in the first quadrant, which is used for computing
132
+ track origins and directions in the ray tracing algorithm.
133
+
134
+ ## Arguments
135
+ - `aq`: Azimuthal quadrature structure
136
+
137
+ ## Returns
138
+ - Number of azimuthal angles in first quadrant (N4 = N/4)
139
+
140
+ ## Example
141
+ ```julia
142
+ aq = AzimuthalQuadrature(8, 0.1)
143
+ n_azim_quad(aq) # Returns 2
144
+ ```
145
+ """
146
+ n_azim_quad (:: AzimuthalQuadrature{N,N2,N4} ) where {N,N2,N4} = N4
147
+
148
+ """
149
+ azimuthal_quadrant_1(aq::AzimuthalQuadrature)
150
+
151
+ Returns the indices for the first quadrant of azimuthal angles (0, π/2).
152
+
153
+ In azimuthal quadrature, angles are divided into quadrants. The first quadrant contains angles from
154
+ 0 to π/2 radians, corresponding to indices 1 to N4.
155
+ """
156
+ azimuthal_quadrant_1 (:: AzimuthalQuadrature{N,N2,N4} ) where {N,N2,N4} = 1 : N4
157
+
158
+ """
159
+ azimuthal_quadrant_2(aq::AzimuthalQuadrature)
160
+
161
+ Returns the indices for the second quadrant of azimuthal angles (π/2, π).
162
+
163
+ The second quadrant contains angles from π/2 to π radians, corresponding to indices N4+1 to N2.
164
+ """
165
+ azimuthal_quadrant_2 (:: AzimuthalQuadrature{N,N2,N4} ) where {N,N2,N4} = (N4+ 1 ): N2
166
+
167
+ """
168
+ azimuthal_half_plane(aq::AzimuthalQuadrature)
169
+
170
+ Returns the indices for all azimuthal angles in the half-plane (0, π).
171
+
172
+ This includes both quadrants and corresponds to indices 1 to N2.
173
+ """
174
+ azimuthal_half_plane (:: AzimuthalQuadrature{N,N2} ) where {N,N2} = 1 : N2
175
+
176
+ """
177
+ is_rightward_direction(aq::AzimuthalQuadrature, i)
178
+
179
+ Checks if the azimuthal angle at index `i` points in the rightward direction.
180
+
181
+ An angle points rightward if it has a positive x-component, which corresponds to angles in the first
182
+ quadrant (indices 1 to N4).
183
+ """
184
+ is_rightward_direction (:: AzimuthalQuadrature{N,N2,N4} , i) where {N,N2,N4} = i ≤ N4
185
+
186
+ """
187
+ is_leftward_direction(aq::AzimuthalQuadrature, i)
188
+
189
+ Checks if the azimuthal angle at index `i` points in the leftward direction.
190
+
191
+ An angle points leftward if it has a negative x-component, which corresponds to angles in the second
192
+ quadrant (indices N4+1 to N2).
193
+ """
194
+ is_leftward_direction (aq:: AzimuthalQuadrature , i) = ! is_rightward_direction (aq, i)
195
+
196
+ """
197
+ supplementary_azimuthal_idx(aq::AzimuthalQuadrature, i)
198
+
199
+ Returns the index of the supplementary azimuthal angle for index `i`.
200
+
201
+ In azimuthal quadrature, angles are organized in pairs where each angle has a supplementary angle
202
+ that differs by π radians (180°). This function computes the index of the supplementary angle using
203
+ the relationship: `supplementary_idx = N2 - i + 1`, where `N2` is the number of azimuthal angles in
204
+ the half-plane (0, π).
205
+
206
+ ## Arguments
207
+ - `aq`: Azimuthal quadrature structure
208
+ - `i`: Index of the azimuthal angle
209
+
210
+ ## Returns
211
+ - Index of the supplementary azimuthal angle
212
+
213
+ ## Example
214
+ ```julia
215
+ # For N=8 azimuthal angles, N2=4
216
+ # supplementary_azimuthal_idx(aq, 1) == 4 # angles 1 and 4 are supplementary
217
+ # supplementary_azimuthal_idx(aq, 2) == 3 # angles 2 and 3 are supplementary
218
+ ```
219
+ """
220
+ supplementary_azimuthal_idx (:: AzimuthalQuadrature{N,N2} , i) where {N,N2} = N2 - i + 1
221
+
222
+ """
223
+ AzimuthalQuadrature(n_azim::Int, δ::Real)
224
+
225
+ Construct an azimuthal quadrature structure for ray tracing with `N` azimuthal angles and spacing
226
+ `δ`.
227
+
228
+ ## Arguments
229
+ - `N`: Number of azimuthal angles (must be positive and a multiple of 4)
230
+ - `δ`: Azimuthal spacing (must be positive)
231
+
232
+ ## Returns
233
+ - `AzimuthalQuadrature{N,T,N2,N4}` where `N2 = N÷2` (half-plane angles) and `N4 = N÷4` (quadrant
234
+ angles)
235
+
236
+ ## Validation
237
+ - Throws `DomainError` if `N ≤ 0`
238
+ - Throws `DomainError` if `N` is not a multiple of 4
239
+ - Throws `DomainError` if `δ ≤ 0`
240
+
241
+ ## Notes
242
+ The constructor initializes uninitialized vectors for effective spacings (`δs`), azimuthal angles
243
+ (`ϕs`), and weights (`ωₐ`), each of length `N2`. These arrays are populated later during the tracing
244
+ process.
245
+
246
+ ## Example
247
+ ```julia
248
+ aq = AzimuthalQuadrature(8, 0.02) # 8 angles, spacing 0.02
249
+ ```
250
+ """
19
251
AzimuthalQuadrature (n_azim:: Int , δ:: Real ) = AzimuthalQuadrature (Val (n_azim), δ)
20
252
21
253
function AzimuthalQuadrature (:: Val{N} , δ:: T ) where {N,T<: Real }
@@ -29,35 +261,76 @@ function AzimuthalQuadrature(::Val{N}, δ::T) where {N,T<:Real}
29
261
30
262
δs, ϕs, ωₐ = ntuple (_ -> Vector {T} (undef, N2), 3 )
31
263
32
- return AzimuthalQuadrature {N,T, N2,N4} (δ, δs, ϕs, ωₐ)
264
+ return AzimuthalQuadrature {N,N2,N4,T } (δ, δs, ϕs, ωₐ)
33
265
end
34
266
267
+ """
268
+ init_weights!(aq::AzimuthalQuadrature)
269
+
270
+ Initialize the azimuthal quadrature weights `ωₐ` based on the computed azimuthal angles `ϕs`.
271
+
272
+ This function computes the angular weights for numerical integration over the azimuthal domain. The
273
+ weights are calculated using a finite difference approximation of the angular spacing between
274
+ adjacent azimuthal angles.
275
+
276
+ ## Weight Calculation
277
+
278
+ For each azimuthal angle index `i` in the first quadrant:
279
+
280
+ - **First angle (i=1)**: `ωₐ[i] = (ϕs[i+1] - ϕs[i]) / (4π)`
281
+ - **Last angle (i=N4)**: `ωₐ[i] = (π - ϕs[i] - ϕs[i-1]) / (4π)`
282
+ - **Middle angles**: `ωₐ[i] = (ϕs[i+1] - ϕs[i-1]) / (4π)`
283
+
284
+ The weights are then copied to the supplementary angles using the relationship `ωₐ[j] = ωₐ[i]` where
285
+ `j = supplementary_azimuthal_idx(aq, i)`.
286
+
287
+ ## Arguments
288
+ - `aq`: Azimuthal quadrature structure to initialize
289
+
290
+ ## Returns
291
+ - `nothing` (modifies `aq.ωₐ` in-place)
292
+
293
+ ## Mathematical Background
294
+
295
+ The weights represent the angular measure associated with each azimuthal direction, normalized by
296
+ `4π` to ensure proper integration over the full solid angle. This normalization accounts for the
297
+ fact that we're working in 2D (azimuthal angles only) but the weights should integrate to unity over
298
+ the full angular domain.
299
+
300
+ ## Example
301
+ ```julia
302
+ aq = AzimuthalQuadrature(8, 0.1)
303
+ # ... compute ϕs ...
304
+ init_weights!(aq)
305
+ # Now aq.ωₐ contains the normalized angular weights
306
+ ```
307
+ """
35
308
function init_weights! (aq:: AzimuthalQuadrature )
36
309
@unpack ϕs, ωₐ = aq
37
- n_azim_4 = nazim4 (aq)
310
+ n_azim_4 = n_azim_quad (aq)
311
+ inv_4π = inv (4 π)
38
312
313
+ # Compute weights for the first quadrant
39
314
for i in 1 : n_azim_4
315
+ # Calculate weight based on position in the quadrant
40
316
if isone (i)
317
+ # First angle: weight from current to next angle
41
318
ωₐ[i] = ϕs[i+ 1 ] - ϕs[i]
42
319
elseif isequal (i, n_azim_4)
320
+ # Last angle: weight from previous angle to π
43
321
ωₐ[i] = π - ϕs[i] - ϕs[i- 1 ]
44
322
else
323
+ # Middle angles: weight spans from previous to next angle
45
324
ωₐ[i] = ϕs[i+ 1 ] - ϕs[i- 1 ]
46
325
end
47
- ωₐ[i] /= 4 π
48
326
49
- j = suplementary_idx (aq, i)
327
+ # Normalize by 4π for proper integration
328
+ ωₐ[i] *= inv_4π
329
+
330
+ # Copy weight to supplementary angle (symmetry)
331
+ j = supplementary_azimuthal_idx (aq, i)
50
332
ωₐ[j] = ωₐ[i]
51
333
end
334
+
52
335
return nothing
53
336
end
54
-
55
- # TODO : use north-west y cosas asi?
56
- right_dir (:: AzimuthalQuadrature{N,T,N2,N4} ) where {N,T,N2,N4} = 1 : N4
57
- left_dir (:: AzimuthalQuadrature{N,T,N2,N4} ) where {N,T,N2,N4} = (N4+ 1 ): N2
58
- both_dir (:: AzimuthalQuadrature{N,T,N2} ) where {N,T,N2} = 1 : N2
59
-
60
- points_right (:: AzimuthalQuadrature{N,T,N2,N4} , i) where {N,T,N2,N4} = i ≤ N4
61
- points_left (aq:: AzimuthalQuadrature , i) = ! points_right (aq, i)
62
-
63
- suplementary_idx (:: AzimuthalQuadrature{N,T,N2} , i) where {N,T,N2} = N2 - i + 1
0 commit comments