forked from openmc-dev/openmc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathphysics_mg.F90
270 lines (211 loc) · 9.43 KB
/
physics_mg.F90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
module physics_mg
! This module contains the multi-group specific physics routines so as to not
! hinder performance of the CE versions with multiple if-thens.
use constants
use error, only: fatal_error, warning
use global
use material_header, only: Material
use math, only: rotate_angle
use mgxs_header, only: Mgxs, MgxsContainer
use mesh, only: get_mesh_indices
use output, only: write_message
use particle_header, only: Particle
use particle_restart_write, only: write_particle_restart
use physics_common
use random_lcg, only: prn
use scattdata_header
use string, only: to_str
implicit none
contains
!===============================================================================
! COLLISION_MG samples a nuclide and reaction and then calls the appropriate
! routine for that reaction
!===============================================================================
subroutine collision_mg(p)
type(Particle), intent(inout) :: p
! Store pre-collision particle properties
p % last_wgt = p % wgt
p % last_g = p % g
p % last_E = p % E
p % last_uvw = p % coord(1) % uvw
! Add to collision counter for particle
p % n_collision = p % n_collision + 1
! Sample nuclide/reaction for the material the particle is in
call sample_reaction(p)
! Display information about collision
if (verbosity >= 10 .or. trace) then
call write_message(" " // "Energy Group = " // trim(to_str(p % g)))
end if
end subroutine collision_mg
!===============================================================================
! SAMPLE_REACTION samples a nuclide based on the macroscopic cross sections for
! each nuclide within a material and then samples a reaction for that nuclide
! and calls the appropriate routine to process the physics. Note that there is
! special logic when suvival biasing is turned on since fission and
! disappearance are treated implicitly.
!===============================================================================
subroutine sample_reaction(p)
type(Particle), intent(inout) :: p
type(Material), pointer :: mat
mat => materials(p % material)
! Create fission bank sites. Note that while a fission reaction is sampled,
! it never actually "happens", i.e. the weight of the particle does not
! change when sampling fission sites. The following block handles all
! absorption (including fission)
if (mat % fissionable) then
if (run_mode == MODE_EIGENVALUE) then
call create_fission_sites(p, fission_bank, n_bank)
elseif (run_mode == MODE_FIXEDSOURCE) then
call create_fission_sites(p, p % secondary_bank, p % n_secondary)
end if
end if
! If survival biasing is being used, the following subroutine adjusts the
! weight of the particle. Otherwise, it checks to see if absorption occurs
if (material_xs % absorption > ZERO) then
call absorption(p)
else
p % absorb_wgt = ZERO
end if
if (.not. p % alive) return
! Sample a scattering reaction and determine the secondary energy of the
! exiting neutron
call scatter(p)
! Play russian roulette if survival biasing is turned on
if (survival_biasing) then
call russian_roulette(p)
if (.not. p % alive) return
end if
end subroutine sample_reaction
!===============================================================================
! ABSORPTION
!===============================================================================
subroutine absorption(p)
type(Particle), intent(inout) :: p
if (survival_biasing) then
! Determine weight absorbed in survival biasing
p % absorb_wgt = (p % wgt * &
material_xs % absorption / material_xs % total)
! Adjust weight of particle by probability of absorption
p % wgt = p % wgt - p % absorb_wgt
p % last_wgt = p % wgt
! Score implicit absorption estimate of keff
!$omp atomic
global_tallies(K_ABSORPTION) % value = &
global_tallies(K_ABSORPTION) % value + p % absorb_wgt * &
material_xs % nu_fission / material_xs % absorption
else
! See if disappearance reaction happens
if (material_xs % absorption > prn() * material_xs % total) then
! Score absorption estimate of keff
!$omp atomic
global_tallies(K_ABSORPTION) % value = &
global_tallies(K_ABSORPTION) % value + p % wgt * &
material_xs % nu_fission / material_xs % absorption
p % alive = .false.
p % event = EVENT_ABSORB
end if
end if
end subroutine absorption
!===============================================================================
! SCATTER
!===============================================================================
subroutine scatter(p)
type(Particle), intent(inout) :: p
call macro_xs(p % material) % obj % sample_scatter(p % coord(1) % uvw, &
p % last_g, p % g, &
p % mu, p % wgt)
! Update energy value for downstream compatability (in tallying)
p % E = energy_bin_avg(p % g)
! Convert change in angle (mu) to new direction
p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, p % mu)
! Set event component
p % event = EVENT_SCATTER
end subroutine scatter
!===============================================================================
! CREATE_FISSION_SITES determines the average total, prompt, and delayed
! neutrons produced from fission and creates appropriate bank sites.
!===============================================================================
subroutine create_fission_sites(p, bank_array, size_bank)
type(Particle), intent(inout) :: p
type(Bank), intent(inout) :: bank_array(:)
integer(8), intent(inout) :: size_bank
integer :: i ! loop index
integer :: nu ! actual number of neutrons produced
integer :: ijk(3) ! indices in ufs mesh
real(8) :: nu_t ! total nu
real(8) :: mu ! fission neutron angular cosine
real(8) :: phi ! fission neutron azimuthal angle
real(8) :: weight ! weight adjustment for ufs method
logical :: in_mesh ! source site in ufs mesh?
class(Mgxs), pointer :: xs
! Get Pointers
xs => macro_xs(p % material) % obj
! TODO: Heat generation from fission
! If uniform fission source weighting is turned on, we increase of decrease
! the expected number of fission sites produced
if (ufs) then
! Determine indices on ufs mesh for current location
call get_mesh_indices(ufs_mesh, p % coord(1) % xyz, ijk, in_mesh)
if (.not. in_mesh) then
call write_particle_restart(p)
call fatal_error("Source site outside UFS mesh!")
end if
if (source_frac(1,ijk(1),ijk(2),ijk(3)) /= ZERO) then
weight = ufs_mesh % volume_frac / source_frac(1,ijk(1),ijk(2),ijk(3))
else
weight = ONE
end if
else
weight = ONE
end if
! Determine expected number of neutrons produced
nu_t = p % wgt / keff * weight * &
material_xs % nu_fission / material_xs % total
! Sample number of neutrons produced
if (prn() > nu_t - int(nu_t)) then
nu = int(nu_t)
else
nu = int(nu_t) + 1
end if
! Check for bank size getting hit. For fixed source calculations, this is a
! fatal error. For eigenvalue calculations, it just means that k-effective
! was too high for a single batch.
if (size_bank + nu > size(bank_array)) then
if (run_mode == MODE_FIXEDSOURCE) then
call fatal_error("Secondary particle bank size limit reached. If you &
&are running a subcritical multiplication problem, k-effective &
&may be too close to one.")
else
if (master) call warning("Maximum number of sites in fission bank &
&reached. This can result in irreproducible results using different &
&numbers of processes/threads.")
end if
end if
! Bank source neutrons
if (nu == 0 .or. size_bank == size(bank_array)) return
p % fission = .true. ! Fission neutrons will be banked
do i = int(size_bank,4) + 1, int(min(size_bank + nu, int(size(bank_array),8)),4)
! Bank source neutrons by copying particle data
bank_array(i) % xyz = p % coord(1) % xyz
! Set weight of fission bank site
bank_array(i) % wgt = ONE/weight
! Sample cosine of angle -- fission neutrons are treated as being emitted
! isotropically.
mu = TWO * prn() - ONE
! Sample azimuthal angle uniformly in [0,2*pi)
phi = TWO * PI * prn()
bank_array(i) % uvw(1) = mu
bank_array(i) % uvw(2) = sqrt(ONE - mu*mu) * cos(phi)
bank_array(i) % uvw(3) = sqrt(ONE - mu*mu) * sin(phi)
! Sample secondary energy distribution for fission reaction and set energy
! in fission bank
bank_array(i) % E = &
real(xs % sample_fission_energy(p % g, bank_array(i) % uvw), 8)
end do
! increment number of bank sites
size_bank = min(size_bank + nu, int(size(bank_array),8))
! Store total weight banked for analog fission tallies
p % n_bank = nu
p % wgt_bank = nu/weight
end subroutine create_fission_sites
end module physics_mg