Skip to content

Commit d9f327b

Browse files
committed
feat: ⚡ improve code and docs
1 parent 9dd8a24 commit d9f327b

File tree

5 files changed

+636
-91
lines changed

5 files changed

+636
-91
lines changed

src/azimuthal_quad.jl

Lines changed: 295 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,253 @@
11
"""
2-
AzimuthalQuadrature{N,T<:Real}
2+
AzimuthalQuadrature{N,N2,N4,T<:Real}
33
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)
774
"""
8-
struct AzimuthalQuadrature{N,T<:Real,N2,N4}
75+
struct AzimuthalQuadrature{N,N2,N4,T<:Real}
976
δ::T
1077
δs::Vector{T}
1178
ϕs::Vector{T}
1279
ωₐ::Vector{T}
1380
end
1481

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
18103

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+
"""
19251
AzimuthalQuadrature(n_azim::Int, δ::Real) = AzimuthalQuadrature(Val(n_azim), δ)
20252

21253
function AzimuthalQuadrature(::Val{N}, δ::T) where {N,T<:Real}
@@ -29,35 +261,76 @@ function AzimuthalQuadrature(::Val{N}, δ::T) where {N,T<:Real}
29261

30262
δs, ϕs, ωₐ = ntuple(_ -> Vector{T}(undef, N2), 3)
31263

32-
return AzimuthalQuadrature{N,T,N2,N4}(δ, δs, ϕs, ωₐ)
264+
return AzimuthalQuadrature{N,N2,N4,T}(δ, δs, ϕs, ωₐ)
33265
end
34266

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+
"""
35308
function init_weights!(aq::AzimuthalQuadrature)
36309
@unpack ϕs, ωₐ = aq
37-
n_azim_4 = nazim4(aq)
310+
n_azim_4 = n_azim_quad(aq)
311+
inv_4π = inv(4π)
38312

313+
# Compute weights for the first quadrant
39314
for i in 1:n_azim_4
315+
# Calculate weight based on position in the quadrant
40316
if isone(i)
317+
# First angle: weight from current to next angle
41318
ωₐ[i] = ϕs[i+1] - ϕs[i]
42319
elseif isequal(i, n_azim_4)
320+
# Last angle: weight from previous angle to π
43321
ωₐ[i] = π - ϕs[i] - ϕs[i-1]
44322
else
323+
# Middle angles: weight spans from previous to next angle
45324
ωₐ[i] = ϕs[i+1] - ϕs[i-1]
46325
end
47-
ωₐ[i] /= 4π
48326

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)
50332
ωₐ[j] = ωₐ[i]
51333
end
334+
52335
return nothing
53336
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

Comments
 (0)