Merge pull request #948 from paulromano/source-norm

Fix source normalization for fixed-source simulations
This commit is contained in:
Adam Nelson 2017-12-26 15:27:49 -05:00 committed by GitHub
commit 6a16da00d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 602 additions and 596 deletions

View file

@ -377,7 +377,6 @@ set(LIBOPENMC_FORTRAN_SRC
src/output.F90
src/particle_header.F90
src/particle_restart.F90
src/particle_restart_write.F90
src/physics_common.F90
src/physics.F90
src/physics_mg.F90

View file

@ -112,6 +112,7 @@ contains
legendre_to_tabular = .true.
legendre_to_tabular_points = 33
n_batch_interval = 1
n_lost_particles = 0
n_particles = 0
n_source_points = 0
n_state_points = 0

View file

@ -644,7 +644,7 @@ contains
subroutine compute_dhat()
use constants, only: CMFD_NOACCEL, ZERO
use output, only: write_message
use error, only: write_message
use string, only: to_str
integer :: nx ! maximum number of cells in x direction

View file

@ -366,7 +366,7 @@ contains
subroutine cmfd_tally_reset()
use output, only: write_message
use error, only: write_message
integer :: i ! loop counter

View file

@ -41,8 +41,7 @@ contains
subroutine read_cmfd_xml()
use constants, only: ZERO, ONE
use error, only: fatal_error, warning
use output, only: write_message
use error, only: fatal_error, warning, write_message
use string, only: to_lower
use xml_interface
use, intrinsic :: ISO_FORTRAN_ENV

View file

@ -5,12 +5,14 @@ module error
use constants
use message_passing
use settings, only: verbosity
implicit none
private
public :: fatal_error
public :: warning
public :: write_message
! Error codes
integer(C_INT), public, bind(C) :: E_UNASSIGNED = -1
@ -192,4 +194,59 @@ contains
end subroutine fatal_error
!===============================================================================
! WRITE_MESSAGE displays an informational message to the log file and the
! standard output stream.
!===============================================================================
subroutine write_message(message, level)
character(*), intent(in) :: message ! message to write
integer, intent(in), optional :: level ! verbosity level
integer :: i_start ! starting position
integer :: i_end ! ending position
integer :: line_wrap ! length of line
integer :: length ! length of message
integer :: last_space ! index of last space (relative to start)
! Set length of line
line_wrap = 80
! Only allow master to print to screen
if (.not. master .and. present(level)) return
if (.not. present(level) .or. level <= verbosity) then
! Determine length of message
length = len_trim(message)
i_start = 0
do
if (length - i_start < line_wrap + 1) then
! Remainder of message will fit on line
write(OUTPUT_UNIT, fmt='(1X,A)') message(i_start+1:length)
exit
else
! Determine last space in current line
last_space = index(message(i_start+1:i_start+line_wrap), &
' ', BACK=.true.)
if (last_space == 0) then
i_end = min(length + 1, i_start+line_wrap) - 1
write(OUTPUT_UNIT, fmt='(1X,A)') message(i_start+1:i_end)
else
i_end = i_start + last_space
write(OUTPUT_UNIT, fmt='(1X,A)') message(i_start+1:i_end-1)
end if
! Write up to last space
! Advance starting position
i_start = i_end
if (i_start > length) exit
end if
end do
end if
end subroutine write_message
end module error

View file

@ -1,18 +1,14 @@
module geometry
use constants
use error, only: fatal_error, warning
use error, only: fatal_error, warning, write_message
use geometry_header
use output, only: write_message
use particle_header, only: LocalCoord, Particle
use particle_restart_write, only: write_particle_restart
use simulation_header
use settings
use surface_header
use stl_vector, only: VectorInt
use string, only: to_str
use tally, only: score_surface_current
use tally_header
implicit none
@ -357,7 +353,7 @@ contains
else
! Particle is outside the lattice.
if (lat % outer == NO_OUTER_UNIVERSE) then
call handle_lost_particle(p, "Particle " // trim(to_str(p %id)) &
call p % mark_as_lost("Particle " // trim(to_str(p %id)) &
// " is outside lattice " // trim(to_str(lat % id)) &
// " but the lattice has no defined outer universe.")
return
@ -380,276 +376,6 @@ contains
end subroutine find_cell
!===============================================================================
! CROSS_SURFACE handles all surface crossings, whether the particle leaks out of
! the geometry, is reflected, or crosses into a new lattice or cell
!===============================================================================
subroutine cross_surface(p)
type(Particle), intent(inout) :: p
real(8) :: u ! x-component of direction
real(8) :: v ! y-component of direction
real(8) :: w ! z-component of direction
real(8) :: norm ! "norm" of surface normal
real(8) :: d ! distance between point and plane
real(8) :: xyz(3) ! Saved global coordinate
integer :: i_surface ! index in surfaces
logical :: rotational ! if rotational periodic BC applied
logical :: found ! particle found in universe?
class(Surface), pointer :: surf
i_surface = abs(p % surface)
surf => surfaces(i_surface)%obj
if (verbosity >= 10 .or. trace) then
call write_message(" Crossing surface " // trim(to_str(surf % id)))
end if
if (surf % bc == BC_VACUUM .and. (run_mode /= MODE_PLOTTING)) then
! =======================================================================
! PARTICLE LEAKS OUT OF PROBLEM
! Kill particle
p % alive = .false.
! Score any surface current tallies -- note that the particle is moved
! forward slightly so that if the mesh boundary is on the surface, it is
! still processed
if (active_current_tallies % size() > 0) then
! TODO: Find a better solution to score surface currents than
! physically moving the particle forward slightly
p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
end if
! Score to global leakage tally
global_tally_leakage = global_tally_leakage + p % wgt
! Display message
if (verbosity >= 10 .or. trace) then
call write_message(" Leaked out of surface " &
&// trim(to_str(surf % id)))
end if
return
elseif (surf % bc == BC_REFLECT .and. (run_mode /= MODE_PLOTTING)) then
! =======================================================================
! PARTICLE REFLECTS FROM SURFACE
! Do not handle reflective boundary conditions on lower universes
if (p % n_coord /= 1) then
call handle_lost_particle(p, "Cannot reflect particle " &
&// trim(to_str(p % id)) // " off surface in a lower universe.")
return
end if
! Score surface currents since reflection causes the direction of the
! particle to change -- artificially move the particle slightly back in
! case the surface crossing is coincident with a mesh boundary
if (active_current_tallies % size() > 0) then
xyz = p % coord(1) % xyz
p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
p % coord(1) % xyz = xyz
end if
! Reflect particle off surface
call surf%reflect(p%coord(1)%xyz, p%coord(1)%uvw)
! Make sure new particle direction is normalized
u = p%coord(1)%uvw(1)
v = p%coord(1)%uvw(2)
w = p%coord(1)%uvw(3)
norm = sqrt(u*u + v*v + w*w)
p%coord(1)%uvw(:) = [u, v, w] / norm
! Reassign particle's cell and surface
p % coord(1) % cell = p % last_cell(p % last_n_coord)
p % surface = -p % surface
! If a reflective surface is coincident with a lattice or universe
! boundary, it is necessary to redetermine the particle's coordinates in
! the lower universes.
p % n_coord = 1
call find_cell(p, found)
if (.not. found) then
call handle_lost_particle(p, "Couldn't find particle after reflecting&
& from surface " // trim(to_str(surf%id)) // ".")
return
end if
! Set previous coordinate going slightly past surface crossing
p % last_xyz_current = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
! Diagnostic message
if (verbosity >= 10 .or. trace) then
call write_message(" Reflected from surface " &
&// trim(to_str(surf%id)))
end if
return
elseif (surf % bc == BC_PERIODIC .and. run_mode /= MODE_PLOTTING) then
! =======================================================================
! PERIODIC BOUNDARY
! Do not handle periodic boundary conditions on lower universes
if (p % n_coord /= 1) then
call handle_lost_particle(p, "Cannot transfer particle " &
// trim(to_str(p % id)) // " across surface in a lower universe.&
& Boundary conditions must be applied to universe 0.")
return
end if
! Score surface currents since reflection causes the direction of the
! particle to change -- artificially move the particle slightly back in
! case the surface crossing is coincident with a mesh boundary
if (active_current_tallies % size() > 0) then
xyz = p % coord(1) % xyz
p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
p % coord(1) % xyz = xyz
end if
rotational = .false.
select type (surf)
type is (SurfaceXPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceXPlane)
p % coord(1) % xyz(1) = opposite % x0
type is (SurfaceYPlane)
rotational = .true.
! Rotate direction
u = p % coord(1) % uvw(1)
v = p % coord(1) % uvw(2)
p % coord(1) % uvw(1) = v
p % coord(1) % uvw(2) = -u
! Rotate position
p % coord(1) % xyz(1) = surf % x0 + p % coord(1) % xyz(2) - opposite % y0
p % coord(1) % xyz(2) = opposite % y0
end select
type is (SurfaceYPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceYPlane)
p % coord(1) % xyz(2) = opposite % y0
type is (SurfaceXPlane)
rotational = .true.
! Rotate direction
u = p % coord(1) % uvw(1)
v = p % coord(1) % uvw(2)
p % coord(1) % uvw(1) = -v
p % coord(1) % uvw(2) = u
! Rotate position
p % coord(1) % xyz(2) = surf % y0 + p % coord(1) % xyz(1) - opposite % x0
p % coord(1) % xyz(1) = opposite % x0
end select
type is (SurfaceZPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceZPlane)
p % coord(1) % xyz(3) = opposite % z0
end select
type is (SurfacePlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfacePlane)
! Get surface normal for opposite plane
xyz(:) = opposite % normal(p % coord(1) % xyz)
! Determine distance to plane
norm = xyz(1)*xyz(1) + xyz(2)*xyz(2) + xyz(3)*xyz(3)
d = opposite % evaluate(p % coord(1) % xyz) / norm
! Move particle along normal vector based on distance
p % coord(1) % xyz(:) = p % coord(1) % xyz(:) - d*xyz
end select
end select
! Reassign particle's surface
if (rotational) then
p % surface = surf % i_periodic
else
p % surface = sign(surf % i_periodic, p % surface)
end if
! Figure out what cell particle is in now
p % n_coord = 1
call find_cell(p, found)
if (.not. found) then
call handle_lost_particle(p, "Couldn't find particle after hitting &
&periodic boundary on surface " // trim(to_str(surf%id)) // ".")
return
end if
! Set previous coordinate going slightly past surface crossing
p % last_xyz_current = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
! Diagnostic message
if (verbosity >= 10 .or. trace) then
call write_message(" Hit periodic boundary on surface " &
// trim(to_str(surf%id)))
end if
return
end if
! ==========================================================================
! SEARCH NEIGHBOR LISTS FOR NEXT CELL
if (p % surface > 0 .and. allocated(surf%neighbor_pos)) then
! If coming from negative side of surface, search all the neighboring
! cells on the positive side
call find_cell(p, found, surf%neighbor_pos)
if (found) return
elseif (p % surface < 0 .and. allocated(surf%neighbor_neg)) then
! If coming from positive side of surface, search all the neighboring
! cells on the negative side
call find_cell(p, found, surf%neighbor_neg)
if (found) return
end if
! ==========================================================================
! COULDN'T FIND PARTICLE IN NEIGHBORING CELLS, SEARCH ALL CELLS
! Remove lower coordinate levels and assignment of surface
p % surface = NONE
p % n_coord = 1
call find_cell(p, found)
if (run_mode /= MODE_PLOTTING .and. (.not. found)) then
! If a cell is still not found, there are two possible causes: 1) there is
! a void in the model, and 2) the particle hit a surface at a tangent. If
! the particle is really traveling tangent to a surface, if we move it
! forward a tiny bit it should fix the problem.
p % n_coord = 1
p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
call find_cell(p, found)
! Couldn't find next cell anywhere! This probably means there is an actual
! undefined region in the geometry.
if (.not. found) then
call handle_lost_particle(p, "After particle " // trim(to_str(p % id)) &
// " crossed surface " // trim(to_str(surf%id)) &
// " it could not be located in any cell and it did not leak.")
return
end if
end if
end subroutine cross_surface
!===============================================================================
! CROSS_LATTICE moves a particle into a new lattice element
!===============================================================================
@ -690,7 +416,7 @@ contains
call find_cell(p, found)
if (.not. found) then
if (p % alive) then ! Particle may have been killed in find_cell
call handle_lost_particle(p, "Could not locate particle " &
call p % mark_as_lost("Could not locate particle " &
// trim(to_str(p % id)) // " after crossing a lattice boundary.")
return
end if
@ -713,9 +439,8 @@ contains
! Search for particle
call find_cell(p, found)
if (.not. found) then
call handle_lost_particle(p, "Could not locate particle " &
// trim(to_str(p % id)) &
// " after crossing a lattice boundary.")
call p % mark_as_lost("Could not locate particle " // &
trim(to_str(p % id)) // " after crossing a lattice boundary.")
return
end if
end if
@ -992,7 +717,7 @@ contains
end select LAT_TYPE
if (d_lat < ZERO) then
call handle_lost_particle(p, "Particle " // trim(to_str(p % id)) &
call p % mark_as_lost("Particle " // trim(to_str(p % id)) &
//" had a negative distance to a lattice boundary. d = " &
//trim(to_str(d_lat)))
end if
@ -1093,38 +818,6 @@ contains
end subroutine neighbor_lists
!===============================================================================
! HANDLE_LOST_PARTICLE
!===============================================================================
subroutine handle_lost_particle(p, message)
type(Particle), intent(inout) :: p
character(*) :: message
integer(8) :: tot_n_particles
! Print warning and write lost particle file
call warning(message)
call write_particle_restart(p)
! Increment number of lost particles
p % alive = .false.
!$omp atomic
n_lost_particles = n_lost_particles + 1
! Count the total number of simulated particles (on this processor)
tot_n_particles = n_batches * gen_per_batch * work
! Abort the simulation if the maximum number of lost particles has been
! reached
if (n_lost_particles >= MAX_LOST_PARTICLES .and. &
n_lost_particles >= REL_MAX_LOST_PARTICLES * tot_n_particles) then
call fatal_error("Maximum number of lost particles has been reached.")
end if
end subroutine handle_lost_particle
!===============================================================================
! CALC_OFFSETS calculates and stores the offsets in all fill cells. This
! routine is called once upon initialization.

View file

@ -10,7 +10,7 @@ module initialize
use bank_header, only: Bank
use constants
use set_header, only: SetInt
use error, only: fatal_error, warning
use error, only: fatal_error, warning, write_message
use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice,&
root_universe
use hdf5_interface, only: file_open, read_attribute, file_close, &
@ -19,7 +19,7 @@ module initialize
use material_header, only: Material
use message_passing
use mgxs_data, only: read_mgxs, create_macro_xs
use output, only: print_version, write_message, print_usage
use output, only: print_version, print_usage
use random_lcg, only: openmc_set_seed
use settings
#ifdef _OPENMP

View file

@ -10,7 +10,7 @@ module input_xml
use distribution_multivariate
use distribution_univariate
use endf, only: reaction_name
use error, only: fatal_error, warning
use error, only: fatal_error, warning, write_message
use geometry, only: calc_offsets, maximum_levels, count_instance, &
neighbor_lists
use geometry_header
@ -23,7 +23,7 @@ module input_xml
use mgxs_header
use multipole, only: multipole_read
use nuclide_header
use output, only: write_message, title, header, print_plot
use output, only: title, header, print_plot
use plot_header
use random_lcg, only: prn, openmc_set_seed
use surface_header

View file

@ -3,13 +3,12 @@ module mgxs_data
use constants
use algorithm, only: find
use dict_header, only: DictCharInt
use error, only: fatal_error
use error, only: fatal_error, write_message
use geometry_header, only: get_temperatures, cells
use hdf5_interface
use material_header, only: Material, materials, n_materials
use mgxs_header
use nuclide_header, only: n_nuclides
use output, only: write_message
use set_header, only: SetChar
use settings
use stl_vector, only: VectorReal

View file

@ -202,61 +202,6 @@ contains
end subroutine print_usage
!===============================================================================
! WRITE_MESSAGE displays an informational message to the log file and the
! standard output stream.
!===============================================================================
subroutine write_message(message, level)
character(*), intent(in) :: message ! message to write
integer, intent(in), optional :: level ! verbosity level
integer :: i_start ! starting position
integer :: i_end ! ending position
integer :: line_wrap ! length of line
integer :: length ! length of message
integer :: last_space ! index of last space (relative to start)
! Set length of line
line_wrap = 80
! Only allow master to print to screen
if (.not. master .and. present(level)) return
if (.not. present(level) .or. level <= verbosity) then
! Determine length of message
length = len_trim(message)
i_start = 0
do
if (length - i_start < line_wrap + 1) then
! Remainder of message will fit on line
write(ou, fmt='(1X,A)') message(i_start+1:length)
exit
else
! Determine last space in current line
last_space = index(message(i_start+1:i_start+line_wrap), &
' ', BACK=.true.)
if (last_space == 0) then
i_end = min(length + 1, i_start+line_wrap) - 1
write(ou, fmt='(1X,A)') message(i_start+1:i_end)
else
i_end = i_start + last_space
write(ou, fmt='(1X,A)') message(i_start+1:i_end-1)
end if
! Write up to last space
! Advance starting position
i_start = i_end
if (i_start > length) exit
end if
end do
end if
end subroutine write_message
!===============================================================================
! PRINT_PARTICLE displays the attributes of a particle
!===============================================================================

View file

@ -1,20 +1,27 @@
module particle_header
use bank_header, only: Bank
use constants, only: NEUTRON, ONE, NONE, ZERO, MAX_SECONDARY, &
MAX_DELAYED_GROUPS, ERROR_REAL
use error, only: fatal_error
use hdf5, only: HID_T
use bank_header, only: Bank, source_bank
use constants
use error, only: fatal_error, warning
use geometry_header, only: root_universe
use hdf5_interface
use settings
use simulation_header
use string, only: to_str
implicit none
private
!===============================================================================
! LOCALCOORD describes the location of a particle local to a single
! universe. When the geometry consists of nested universes, a particle will have
! a list of coordinates in each level
!===============================================================================
type LocalCoord
type, public :: LocalCoord
! Indices in various arrays for this level
integer :: cell = NONE
@ -39,7 +46,7 @@ module particle_header
! geometry
!===============================================================================
type Particle
type, public :: Particle
! Basic data
integer(8) :: id ! Unique ID
integer :: type ! Particle type (n, p, e, etc)
@ -107,20 +114,84 @@ module particle_header
type(Bank) :: secondary_bank(MAX_SECONDARY)
contains
procedure :: initialize => initialize_particle
procedure :: clear => clear_particle
procedure :: initialize_from_source
procedure :: clear
procedure :: create_secondary
procedure :: initialize
procedure :: initialize_from_source
procedure :: mark_as_lost
procedure :: write_restart
end type Particle
contains
!===============================================================================
! INITIALIZE_PARTICLE sets default attributes for a particle from the source
! bank
! RESET_COORD clears data from a single coordinate level
!===============================================================================
subroutine initialize_particle(this)
elemental subroutine reset_coord(this)
class(LocalCoord), intent(inout) :: this
this % cell = NONE
this % universe = NONE
this % lattice = NONE
this % lattice_x = NONE
this % lattice_y = NONE
this % lattice_z = NONE
this % rotated = .false.
end subroutine reset_coord
!===============================================================================
! CLEAR_PARTICLE resets all coordinate levels for the particle
!===============================================================================
subroutine clear(this)
class(Particle) :: this
integer :: i
! remove any coordinate levels
do i = 1, MAX_COORD
call this % coord(i) % reset()
end do
end subroutine clear
!===============================================================================
! CREATE_SECONDARY stores the current phase space attributes of the particle in
! the secondary bank and increments the number of sites in the secondary bank.
!===============================================================================
subroutine create_secondary(this, uvw, type, run_CE)
class(Particle), intent(inout) :: this
real(8), intent(in) :: uvw(3)
integer, intent(in) :: type
logical, intent(in) :: run_CE
integer(8) :: n
! Check to make sure that the hard-limit on secondary particles is not
! exceeded.
if (this % n_secondary == MAX_SECONDARY) then
call fatal_error("Too many secondary particles created.")
end if
n = this % n_secondary + 1
this % secondary_bank(n) % wgt = this % wgt
this % secondary_bank(n) % xyz(:) = this % coord(1) % xyz
this % secondary_bank(n) % uvw(:) = uvw
this % n_secondary = n
this % secondary_bank(this % n_secondary) % E = this % E
if (.not. run_CE) then
this % secondary_bank(this % n_secondary) % E = real(this % g, 8)
end if
end subroutine create_secondary
!===============================================================================
! INITIALIZE sets default attributes for a particle from the source bank
!===============================================================================
subroutine initialize(this)
class(Particle) :: this
@ -154,40 +225,7 @@ contains
this % n_coord = 1
this % last_n_coord = 1
end subroutine initialize_particle
!===============================================================================
! CLEAR_PARTICLE resets all coordinate levels for the particle
!===============================================================================
subroutine clear_particle(this)
class(Particle) :: this
integer :: i
! remove any coordinate levels
do i = 1, MAX_COORD
call this % coord(i) % reset()
end do
end subroutine clear_particle
!===============================================================================
! RESET_COORD clears data from a single coordinate level
!===============================================================================
elemental subroutine reset_coord(this)
class(LocalCoord), intent(inout) :: this
this % cell = NONE
this % universe = NONE
this % lattice = NONE
this % lattice_x = NONE
this % lattice_y = NONE
this % lattice_z = NONE
this % rotated = .false.
end subroutine reset_coord
end subroutine initialize
!===============================================================================
! INITIALIZE_FROM_SOURCE initializes a particle from data stored in a source
@ -225,34 +263,90 @@ contains
end subroutine initialize_from_source
!===============================================================================
! CREATE_SECONDARY stores the current phase space attributes of the particle in
! the secondary bank and increments the number of sites in the secondary bank.
! MARK_AS_LOST
!===============================================================================
subroutine create_secondary(this, uvw, type, run_CE)
subroutine mark_as_lost(this, message)
class(Particle), intent(inout) :: this
real(8), intent(in) :: uvw(3)
integer, intent(in) :: type
logical, intent(in) :: run_CE
character(*) :: message
integer(8) :: n
integer(8) :: tot_n_particles
! Check to make sure that the hard-limit on secondary particles is not
! exceeded.
if (this % n_secondary == MAX_SECONDARY) then
call fatal_error("Too many secondary particles created.")
! Print warning and write lost particle file
call warning(message)
call this % write_restart()
! Increment number of lost particles
this % alive = .false.
!$omp atomic
n_lost_particles = n_lost_particles + 1
! Count the total number of simulated particles (on this processor)
tot_n_particles = current_batch * gen_per_batch * work
! Abort the simulation if the maximum number of lost particles has been
! reached
if (n_lost_particles >= MAX_LOST_PARTICLES .and. &
n_lost_particles >= REL_MAX_LOST_PARTICLES * tot_n_particles) then
call fatal_error("Maximum number of lost particles has been reached.")
end if
n = this % n_secondary + 1
this % secondary_bank(n) % wgt = this % wgt
this % secondary_bank(n) % xyz(:) = this % coord(1) % xyz
this % secondary_bank(n) % uvw(:) = uvw
this % n_secondary = n
this % secondary_bank(this % n_secondary) % E = this % E
if (.not. run_CE) then
this % secondary_bank(this % n_secondary) % E = real(this % g, 8)
end if
end subroutine mark_as_lost
end subroutine create_secondary
!===============================================================================
! WRITE_RESTART creates a particle restart file
!===============================================================================
subroutine write_restart(this)
class(Particle), intent(in) :: this
integer(HID_T) :: file_id
character(MAX_FILE_LEN) :: filename
! Dont write another restart file if in particle restart mode
if (run_mode == MODE_PARTICLE) return
! Set up file name
filename = trim(path_output) // 'particle_' // trim(to_str(current_batch)) &
// '_' // trim(to_str(this % id)) // '.h5'
!$omp critical (WriteParticleRestart)
! Create file
file_id = file_create(filename)
associate (src => source_bank(current_work))
! Write filetype and version info
call write_attribute(file_id, 'filetype', 'particle restart')
call write_attribute(file_id, 'version', VERSION_PARTICLE_RESTART)
call write_attribute(file_id, "openmc_version", VERSION)
#ifdef GIT_SHA1
call write_attribute(file_id, "git_sha1", GIT_SHA1)
#endif
! Write data to file
call write_dataset(file_id, 'current_batch', current_batch)
call write_dataset(file_id, 'generations_per_batch', gen_per_batch)
call write_dataset(file_id, 'current_generation', current_gen)
call write_dataset(file_id, 'n_particles', n_particles)
select case(run_mode)
case (MODE_FIXEDSOURCE)
call write_dataset(file_id, 'run_mode', 'fixed source')
case (MODE_EIGENVALUE)
call write_dataset(file_id, 'run_mode', 'eigenvalue')
case (MODE_PARTICLE)
call write_dataset(file_id, 'run_mode', 'particle restart')
end select
call write_dataset(file_id, 'id', this % id)
call write_dataset(file_id, 'weight', src % wgt)
call write_dataset(file_id, 'energy', src % E)
call write_dataset(file_id, 'xyz', src % xyz)
call write_dataset(file_id, 'uvw', src % uvw)
end associate
! Close file
call file_close(file_id)
!$omp end critical (WriteParticleRestart)
end subroutine write_restart
end module particle_header

View file

@ -4,10 +4,11 @@ module particle_restart
use bank_header, only: Bank
use constants
use error, only: write_message
use hdf5_interface, only: file_open, file_close, read_dataset
use mgxs_header, only: energy_bin_avg
use nuclide_header, only: micro_xs, n_nuclides
use output, only: write_message, print_particle
use output, only: print_particle
use particle_header, only: Particle
use random_lcg, only: set_particle_seed
use settings

View file

@ -1,74 +0,0 @@
module particle_restart_write
use bank_header, only: Bank, source_bank
use hdf5_interface
use particle_header, only: Particle
use settings
use simulation_header
use string, only: to_str
use hdf5
implicit none
private
public :: write_particle_restart
contains
!===============================================================================
! WRITE_PARTICLE_RESTART is the main routine that writes out the particle file
!===============================================================================
subroutine write_particle_restart(p)
type(Particle), intent(in) :: p
integer(HID_T) :: file_id
character(MAX_FILE_LEN) :: filename
! Dont write another restart file if in particle restart mode
if (run_mode == MODE_PARTICLE) return
! Set up file name
filename = trim(path_output) // 'particle_' // trim(to_str(current_batch)) &
// '_' // trim(to_str(p%id)) // '.h5'
!$omp critical (WriteParticleRestart)
! Create file
file_id = file_create(filename)
associate (src => source_bank(current_work))
! Write filetype and version info
call write_attribute(file_id, 'filetype', 'particle restart')
call write_attribute(file_id, 'version', VERSION_PARTICLE_RESTART)
call write_attribute(file_id, "openmc_version", VERSION)
#ifdef GIT_SHA1
call write_attribute(file_id, "git_sha1", GIT_SHA1)
#endif
! Write data to file
call write_dataset(file_id, 'current_batch', current_batch)
call write_dataset(file_id, 'generations_per_batch', gen_per_batch)
call write_dataset(file_id, 'current_generation', current_gen)
call write_dataset(file_id, 'n_particles', n_particles)
select case(run_mode)
case (MODE_FIXEDSOURCE)
call write_dataset(file_id, 'run_mode', 'fixed source')
case (MODE_EIGENVALUE)
call write_dataset(file_id, 'run_mode', 'eigenvalue')
case (MODE_PARTICLE)
call write_dataset(file_id, 'run_mode', 'particle restart')
end select
call write_dataset(file_id, 'id', p%id)
call write_dataset(file_id, 'weight', src%wgt)
call write_dataset(file_id, 'energy', src%E)
call write_dataset(file_id, 'xyz', src%xyz)
call write_dataset(file_id, 'uvw', src%uvw)
end associate
! Close file
call file_close(file_id)
!$omp end critical (WriteParticleRestart)
end subroutine write_particle_restart
end module particle_restart_write

View file

@ -4,15 +4,13 @@ module physics
use constants
use cross_section, only: elastic_xs_0K
use endf, only: reaction_name
use error, only: fatal_error, warning
use error, only: fatal_error, warning, write_message
use material_header, only: Material, materials
use math
use mesh_header, only: meshes
use message_passing
use nuclide_header
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, advance_prn_seed, prn_set_stream
use reaction_header, only: Reaction
@ -172,7 +170,7 @@ contains
! Check to make sure that a nuclide was sampled
if (i_nuc_mat > mat % n_nuclides) then
call write_particle_restart(p)
call p % write_restart()
call fatal_error("Did not sample any nuclide during collision.")
end if
@ -384,7 +382,7 @@ contains
! Check to make sure inelastic scattering reaction sampled
if (i > size(nuc % reactions)) then
call write_particle_restart(p)
call p % write_restart()
call fatal_error("Did not sample any reaction for nuclide " &
&// trim(nuc % name))
end if
@ -1098,7 +1096,7 @@ contains
! Determine indices on ufs mesh for current location
call m % get_bin(p % coord(1) % xyz, mesh_bin)
if (mesh_bin == NO_BIN_FOUND) then
call write_particle_restart(p)
call p % write_restart()
call fatal_error("Source site outside UFS mesh!")
end if
@ -1251,7 +1249,7 @@ contains
! check for large number of resamples
n_sample = n_sample + 1
if (n_sample == MAX_SAMPLE) then
! call write_particle_restart(p)
! call p % write_restart()
call fatal_error("Resampled energy distribution maximum number of " &
// "times for nuclide " // nuc % name)
end if
@ -1275,7 +1273,7 @@ contains
! check for large number of resamples
n_sample = n_sample + 1
if (n_sample == MAX_SAMPLE) then
! call write_particle_restart(p)
! call p % write_restart()
call fatal_error("Resampled energy distribution maximum number of " &
// "times for nuclide " // nuc % name)
end if

View file

@ -4,16 +4,14 @@ module physics_mg
use bank_header
use constants
use error, only: fatal_error, warning
use error, only: fatal_error, warning, write_message
use material_header, only: Material, materials
use math, only: rotate_angle
use mesh_header, only: meshes
use mgxs_header
use message_passing
use nuclide_header, only: material_xs
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
@ -196,7 +194,7 @@ contains
call m % get_bin(p % coord(1) % xyz, mesh_bin)
if (mesh_bin == NO_BIN_FOUND) then
call write_particle_restart(p)
call p % write_restart()
call fatal_error("Source site outside UFS mesh!")
end if

View file

@ -5,11 +5,11 @@ module plot
use hdf5
use constants
use error, only: fatal_error
use error, only: fatal_error, write_message
use geometry, only: find_cell, check_cell_overlap
use geometry_header, only: Cell, root_universe, cells
use hdf5_interface
use output, only: write_message, time_stamp
use output, only: time_stamp
use material_header, only: materials
use particle_header, only: LocalCoord, Particle
use plot_header

View file

@ -16,12 +16,12 @@ module simulation
#ifdef _OPENMP
use eigenvalue, only: join_bank_from_threads
#endif
use error, only: fatal_error
use error, only: fatal_error, write_message
use geometry_header, only: n_cells
use message_passing
use mgxs_header, only: energy_bins, energy_bin_avg
use nuclide_header, only: micro_xs, n_nuclides
use output, only: write_message, header, print_columns, &
use output, only: header, print_columns, &
print_batch_keff, print_generation, print_runtime, &
print_results, print_overlap_check, write_tallies
use particle_header, only: Particle

View file

@ -13,7 +13,7 @@ module simulation_header
! GEOMETRY-RELATED VARIABLES
! Number of lost particles
integer :: n_lost_particles
integer :: n_lost_particles = 0
real(8) :: log_spacing ! spacing on logarithmic grid

View file

@ -19,13 +19,13 @@ module state_point
use constants
use eigenvalue, only: openmc_get_keff
use endf, only: reaction_name
use error, only: fatal_error, warning
use error, only: fatal_error, warning, write_message
use hdf5_interface
use mesh_header, only: RegularMesh, meshes, n_meshes
use message_passing
use mgxs_header, only: nuclides_MG
use nuclide_header, only: nuclides
use output, only: write_message, time_stamp
use output, only: time_stamp
use random_lcg, only: seed
use settings
use simulation_header

View file

@ -4,6 +4,7 @@ module summary
use constants
use endf, only: reaction_name
use error, only: write_message
use geometry_header
use hdf5_interface
use material_header, only: Material, n_materials
@ -11,7 +12,7 @@ module summary
use message_passing
use mgxs_header, only: nuclides_MG
use nuclide_header
use output, only: time_stamp, write_message
use output, only: time_stamp
use settings, only: run_CE
use surface_header
use string, only: to_str

View file

@ -9,7 +9,8 @@ module tally_header
use dict_header, only: DictIntInt
use message_passing, only: n_procs
use nuclide_header, only: nuclide_dict
use settings, only: reduce_tallies
use settings, only: reduce_tallies, run_mode
use source_header, only: external_source
use stl_vector, only: VectorInt
use string, only: to_lower, to_f_string, str_to_int, to_str
use tally_filter_header, only: TallyFilterContainer, filters, n_filters
@ -161,6 +162,7 @@ contains
integer :: i, j
real(C_DOUBLE) :: val
real(C_DOUBLE) :: total_source
! Increment number of realizations
if (reduce_tallies) then
@ -169,10 +171,17 @@ contains
this % n_realizations = this % n_realizations + n_procs
end if
! Calculate total source strength for normalization
if (run_mode == MODE_FIXEDSOURCE) then
total_source = sum(external_source(:) % strength)
else
total_source = ONE
end if
! Accumulate each result
do j = 1, size(this % results, 3)
do i = 1, size(this % results, 2)
val = this % results(RESULT_VALUE, i, j)/total_weight
val = this % results(RESULT_VALUE, i, j)/total_weight * total_source
this % results(RESULT_VALUE, i, j) = ZERO
this % results(RESULT_SUM, i, j) = &

View file

@ -8,8 +8,8 @@ module trigger
use constants
use eigenvalue, only: openmc_get_keff
use error, only: warning, write_message
use string, only: to_str
use output, only: warning, write_message
use mesh_header, only: RegularMesh, meshes
use message_passing, only: master
use settings

View file

@ -2,11 +2,10 @@ module tracking
use constants
use cross_section, only: calculate_xs
use error, only: fatal_error, warning
use error, only: fatal_error, warning, write_message
use geometry_header, only: cells
use geometry, only: find_cell, distance_to_boundary, cross_surface, &
cross_lattice, check_cell_overlap
use output, only: write_message
use geometry, only: find_cell, distance_to_boundary, cross_lattice, &
check_cell_overlap
use message_passing
use mgxs_header
use nuclide_header
@ -17,6 +16,7 @@ module tracking
use settings
use simulation_header
use string, only: to_str
use surface_header
use tally_header
use tally, only: score_analog_tally, score_tracklength_tally, &
score_collision_tally, score_surface_current, &
@ -278,4 +278,274 @@ contains
end subroutine transport
!===============================================================================
! CROSS_SURFACE handles all surface crossings, whether the particle leaks out of
! the geometry, is reflected, or crosses into a new lattice or cell
!===============================================================================
subroutine cross_surface(p)
type(Particle), intent(inout) :: p
real(8) :: u ! x-component of direction
real(8) :: v ! y-component of direction
real(8) :: w ! z-component of direction
real(8) :: norm ! "norm" of surface normal
real(8) :: d ! distance between point and plane
real(8) :: xyz(3) ! Saved global coordinate
integer :: i_surface ! index in surfaces
logical :: rotational ! if rotational periodic BC applied
logical :: found ! particle found in universe?
class(Surface), pointer :: surf
i_surface = abs(p % surface)
surf => surfaces(i_surface)%obj
if (verbosity >= 10 .or. trace) then
call write_message(" Crossing surface " // trim(to_str(surf % id)))
end if
if (surf % bc == BC_VACUUM .and. (run_mode /= MODE_PLOTTING)) then
! =======================================================================
! PARTICLE LEAKS OUT OF PROBLEM
! Kill particle
p % alive = .false.
! Score any surface current tallies -- note that the particle is moved
! forward slightly so that if the mesh boundary is on the surface, it is
! still processed
if (active_current_tallies % size() > 0) then
! TODO: Find a better solution to score surface currents than
! physically moving the particle forward slightly
p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
end if
! Score to global leakage tally
global_tally_leakage = global_tally_leakage + p % wgt
! Display message
if (verbosity >= 10 .or. trace) then
call write_message(" Leaked out of surface " &
&// trim(to_str(surf % id)))
end if
return
elseif (surf % bc == BC_REFLECT .and. (run_mode /= MODE_PLOTTING)) then
! =======================================================================
! PARTICLE REFLECTS FROM SURFACE
! Do not handle reflective boundary conditions on lower universes
if (p % n_coord /= 1) then
call p % mark_as_lost("Cannot reflect particle " &
// trim(to_str(p % id)) // " off surface in a lower universe.")
return
end if
! Score surface currents since reflection causes the direction of the
! particle to change -- artificially move the particle slightly back in
! case the surface crossing is coincident with a mesh boundary
if (active_current_tallies % size() > 0) then
xyz = p % coord(1) % xyz
p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
p % coord(1) % xyz = xyz
end if
! Reflect particle off surface
call surf%reflect(p%coord(1)%xyz, p%coord(1)%uvw)
! Make sure new particle direction is normalized
u = p%coord(1)%uvw(1)
v = p%coord(1)%uvw(2)
w = p%coord(1)%uvw(3)
norm = sqrt(u*u + v*v + w*w)
p%coord(1)%uvw(:) = [u, v, w] / norm
! Reassign particle's cell and surface
p % coord(1) % cell = p % last_cell(p % last_n_coord)
p % surface = -p % surface
! If a reflective surface is coincident with a lattice or universe
! boundary, it is necessary to redetermine the particle's coordinates in
! the lower universes.
p % n_coord = 1
call find_cell(p, found)
if (.not. found) then
call p % mark_as_lost("Couldn't find particle after reflecting&
& from surface " // trim(to_str(surf % id)) // ".")
return
end if
! Set previous coordinate going slightly past surface crossing
p % last_xyz_current = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
! Diagnostic message
if (verbosity >= 10 .or. trace) then
call write_message(" Reflected from surface " &
&// trim(to_str(surf%id)))
end if
return
elseif (surf % bc == BC_PERIODIC .and. run_mode /= MODE_PLOTTING) then
! =======================================================================
! PERIODIC BOUNDARY
! Do not handle periodic boundary conditions on lower universes
if (p % n_coord /= 1) then
call p % mark_as_lost("Cannot transfer particle " &
// trim(to_str(p % id)) // " across surface in a lower universe.&
& Boundary conditions must be applied to universe 0.")
return
end if
! Score surface currents since reflection causes the direction of the
! particle to change -- artificially move the particle slightly back in
! case the surface crossing is coincident with a mesh boundary
if (active_current_tallies % size() > 0) then
xyz = p % coord(1) % xyz
p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
p % coord(1) % xyz = xyz
end if
rotational = .false.
select type (surf)
type is (SurfaceXPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceXPlane)
p % coord(1) % xyz(1) = opposite % x0
type is (SurfaceYPlane)
rotational = .true.
! Rotate direction
u = p % coord(1) % uvw(1)
v = p % coord(1) % uvw(2)
p % coord(1) % uvw(1) = v
p % coord(1) % uvw(2) = -u
! Rotate position
p % coord(1) % xyz(1) = surf % x0 + p % coord(1) % xyz(2) - opposite % y0
p % coord(1) % xyz(2) = opposite % y0
end select
type is (SurfaceYPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceYPlane)
p % coord(1) % xyz(2) = opposite % y0
type is (SurfaceXPlane)
rotational = .true.
! Rotate direction
u = p % coord(1) % uvw(1)
v = p % coord(1) % uvw(2)
p % coord(1) % uvw(1) = -v
p % coord(1) % uvw(2) = u
! Rotate position
p % coord(1) % xyz(2) = surf % y0 + p % coord(1) % xyz(1) - opposite % x0
p % coord(1) % xyz(1) = opposite % x0
end select
type is (SurfaceZPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceZPlane)
p % coord(1) % xyz(3) = opposite % z0
end select
type is (SurfacePlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfacePlane)
! Get surface normal for opposite plane
xyz(:) = opposite % normal(p % coord(1) % xyz)
! Determine distance to plane
norm = xyz(1)*xyz(1) + xyz(2)*xyz(2) + xyz(3)*xyz(3)
d = opposite % evaluate(p % coord(1) % xyz) / norm
! Move particle along normal vector based on distance
p % coord(1) % xyz(:) = p % coord(1) % xyz(:) - d*xyz
end select
end select
! Reassign particle's surface
if (rotational) then
p % surface = surf % i_periodic
else
p % surface = sign(surf % i_periodic, p % surface)
end if
! Figure out what cell particle is in now
p % n_coord = 1
call find_cell(p, found)
if (.not. found) then
call p % mark_as_lost("Couldn't find particle after hitting &
&periodic boundary on surface " // trim(to_str(surf % id)) // ".")
return
end if
! Set previous coordinate going slightly past surface crossing
p % last_xyz_current = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
! Diagnostic message
if (verbosity >= 10 .or. trace) then
call write_message(" Hit periodic boundary on surface " &
// trim(to_str(surf%id)))
end if
return
end if
! ==========================================================================
! SEARCH NEIGHBOR LISTS FOR NEXT CELL
if (p % surface > 0 .and. allocated(surf%neighbor_pos)) then
! If coming from negative side of surface, search all the neighboring
! cells on the positive side
call find_cell(p, found, surf%neighbor_pos)
if (found) return
elseif (p % surface < 0 .and. allocated(surf%neighbor_neg)) then
! If coming from positive side of surface, search all the neighboring
! cells on the negative side
call find_cell(p, found, surf%neighbor_neg)
if (found) return
end if
! ==========================================================================
! COULDN'T FIND PARTICLE IN NEIGHBORING CELLS, SEARCH ALL CELLS
! Remove lower coordinate levels and assignment of surface
p % surface = NONE
p % n_coord = 1
call find_cell(p, found)
if (run_mode /= MODE_PLOTTING .and. (.not. found)) then
! If a cell is still not found, there are two possible causes: 1) there is
! a void in the model, and 2) the particle hit a surface at a tangent. If
! the particle is really traveling tangent to a surface, if we move it
! forward a tiny bit it should fix the problem.
p % n_coord = 1
p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
call find_cell(p, found)
! Couldn't find next cell anywhere! This probably means there is an actual
! undefined region in the geometry.
if (.not. found) then
call p % mark_as_lost("After particle " // trim(to_str(p % id)) &
// " crossed surface " // trim(to_str(surf % id)) &
// " it could not be located in any cell and it did not leak.")
return
end if
end if
end subroutine cross_surface
end module tracking

View file

@ -8,11 +8,12 @@ module volume_calc
#endif
use constants
use error, only: write_message
use geometry, only: find_cell
use geometry_header, only: universes, cells
use hdf5_interface, only: file_create, file_close, write_attribute, &
create_group, close_group, write_dataset
use output, only: write_message, header, time_stamp
use output, only: header, time_stamp
use material_header, only: materials
use message_passing
use nuclide_header, only: nuclides

View file

@ -1,8 +0,0 @@
<?xml version="1.0"?>
<geometry>
<!-- Sphere with radius 10 -->
<surface id="1" type="sphere" coeffs="0 0 0 10" boundary="vacuum"/>
<cell id="1" material="1" region="-1" />
</geometry>

View file

@ -0,0 +1,31 @@
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<cell id="1" material="1" region="-1" universe="1" />
<surface boundary="vacuum" coeffs="0.0 0.0 0.0 10.0" id="1" type="sphere" />
</geometry>
<?xml version='1.0' encoding='utf-8'?>
<materials>
<material id="1">
<density units="g/cc" value="7.5" />
<nuclide ao="1.0" name="O16" />
<nuclide ao="0.0001" name="U238" />
</material>
</materials>
<?xml version='1.0' encoding='utf-8'?>
<settings>
<run_mode>fixed source</run_mode>
<particles>100</particles>
<batches>10</batches>
<source strength="10.0">
<space type="point">
<parameters>0.0 0.0 0.0</parameters>
</space>
</source>
<temperature_default>294</temperature_default>
</settings>
<?xml version='1.0' encoding='utf-8'?>
<tallies>
<tally id="1">
<scores>flux</scores>
</tally>
</tallies>

View file

@ -1,10 +0,0 @@
<?xml version="1.0"?>
<materials>
<material id="1">
<density value="7.5" units="g/cc" />
<nuclide name="O16" ao="1.0" />
<nuclide name="U238" ao="0.0001" />
</material>
</materials>

View file

@ -1,6 +1,6 @@
tally 1:
4.448476E+02
1.984373E+04
4.448476E+03
1.984373E+06
leakage:
9.790000E+00
9.588300E+00

View file

@ -1,14 +0,0 @@
<?xml version="1.0"?>
<settings>
<run_mode>fixed source</run_mode>
<batches>10</batches>
<particles>100</particles>
<temperature_default>294</temperature_default>
<source>
<space type="point" parameters="0 0 0" />
</source>
</settings>

View file

@ -1,8 +0,0 @@
<?xml version="1.0"?>
<tallies>
<tally id="1">
<scores>flux</scores>
</tally>
</tallies>

View file

@ -5,17 +5,18 @@ import os
import sys
import numpy as np
sys.path.insert(0, os.pardir)
from testing_harness import TestHarness
from openmc import StatePoint
from testing_harness import PyAPITestHarness
import openmc
import openmc.stats
class FixedSourceTestHarness(TestHarness):
class FixedSourceTestHarness(PyAPITestHarness):
def _get_results(self):
"""Digest info in the statepoint and return as a string."""
# Read the statepoint file.
statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0]
outstr = ''
with StatePoint(statepoint) as sp:
with openmc.StatePoint(statepoint) as sp:
# Write out tally data.
for i, tally_ind in enumerate(sp.tallies):
tally = sp.tallies[tally_ind]
@ -36,5 +37,28 @@ class FixedSourceTestHarness(TestHarness):
if __name__ == '__main__':
harness = FixedSourceTestHarness('statepoint.10.h5')
mat = openmc.Material()
mat.add_nuclide('O16', 1.0)
mat.add_nuclide('U238', 0.0001)
mat.set_density('g/cc', 7.5)
surf = openmc.Sphere(R=10.0, boundary_type='vacuum')
cell = openmc.Cell(fill=mat, region=-surf)
model = openmc.model.Model()
model.geometry.root_universe = openmc.Universe(cells=[cell])
model.materials.append(mat)
model.settings.run_mode = 'fixed source'
model.settings.batches = 10
model.settings.particles = 100
model.settings.temperature = {'default': 294}
model.settings.source = openmc.Source(space=openmc.stats.Point(),
strength=10.0)
tally = openmc.Tally()
tally.scores = ['flux']
model.tallies.append(tally)
harness = FixedSourceTestHarness('statepoint.10.h5', model)
harness.main()