From 8df7694b828542b9f74aae0e09f3875cba50825c Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 8 Jun 2013 14:28:21 -0400 Subject: [PATCH 01/29] Added means of outputting lost particle tracks --- src/constants.F90 | 1 + src/physics.F90 | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/constants.F90 b/src/constants.F90 index e9688e761d..8887e1fc9b 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -363,6 +363,7 @@ module constants integer, parameter :: UNIT_STATE = 16 ! unit # for writing state point integer, parameter :: CMFD_BALANCE = 17 ! unit # for writing cmfd balance file integer, parameter :: UNIT_PARTICLE = 18 ! unit # for writing particle restart + integer, parameter :: UNIT_TRACK = 19 ! unit # for writing particle tracks !============================================================================= ! CMFD CONSTANTS diff --git a/src/physics.F90 b/src/physics.F90 index 707049b1a8..e1c7f12c32 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -73,6 +73,12 @@ contains ! Force calculation of cross-sections by setting last energy to zero micro_xs % last_E = ZERO + if (run_mode == MODE_PARTICLE) then + open(UNIT=UNIT_TRACK, FILE='test', STATUS='replace', & + ACCESS='stream') + write(UNIT_TRACK) p % coord0 % xyz + end if + do while (p % alive) ! Calculate microscopic and macroscopic cross sections -- note: if the @@ -100,6 +106,10 @@ contains coord % xyz = coord % xyz + distance * coord % uvw coord => coord % next end do + + if (run_mode == MODE_PARTICLE) then + write(UNIT_TRACK) p % coord0 % xyz + end if ! Score track-length tallies if (active_tracklength_tallies % size() > 0) & @@ -175,6 +185,10 @@ contains end do + if (run_mode == MODE_PARTICLE) then + close(UNIT=UNIT_TRACK) + end if + end subroutine transport !=============================================================================== From 18ea0af331e255cd4497336a408187f9adb5e5c1 Mon Sep 17 00:00:00 2001 From: Sterling Date: Mon, 10 Jun 2013 12:35:59 -0400 Subject: [PATCH 02/29] Cleaned up implimentation of track output --- src/DEPENDENCIES | 1 + src/global.F90 | 3 +++ src/initialize.F90 | 2 ++ src/particle_restart.F90 | 20 ++++++++++++++++++++ src/physics.F90 | 16 ++++++---------- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index c241153e76..dfe7390f1d 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -294,6 +294,7 @@ physics.o: material_header.o physics.o: mesh.o physics.o: output.o physics.o: particle_header.o +physics.o: particle_restart.o physics.o: particle_restart_write.o physics.o: random_lcg.o physics.o: search.o diff --git a/src/global.F90 b/src/global.F90 index e0f0bd5bbf..43f8025455 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -283,6 +283,9 @@ module global ! Particle restart run logical :: particle_restart_run = .false. + ! Particle track output + logical :: write_track = .false. + ! ============================================================================ ! CMFD VARIABLES diff --git a/src/initialize.F90 b/src/initialize.F90 index dd28f9bf9c..f8a474c545 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -311,6 +311,8 @@ contains i = i + 1 path_particle_restart = argv(i) particle_restart_run = .true. + case ('-tr', '-track', '--track') + write_track = .true. case default message = "Unknown command line option: " // argv(i) call fatal_error() diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index 07c7f5475e..e3bd96afb8 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -11,6 +11,7 @@ module particle_restart use physics, only: transport use random_lcg, only: set_particle_seed use source, only: initialize_particle + use string, only: to_str #ifdef HDF5 use hdf5_interface @@ -19,6 +20,7 @@ module particle_restart implicit none private public :: run_particle_restart + public :: write_particle_track #ifdef HDF5 integer(HID_T) :: hdf5_particle_file @@ -118,6 +120,7 @@ contains subroutine run_particle_restart() integer(8) :: particle_seed + character(MAX_FILE_LEN) :: filename ! initialize the particle to be tracked allocate(p) @@ -138,9 +141,22 @@ contains current_gen - 1)*n_particles + p % id call set_particle_seed(particle_seed) + ! Open particle track output file. + if (write_track) then + filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & + // '_' // trim(to_str(current_work)) // '.binary' + open(UNIT=UNIT_TRACK, FILE=filename, STATUS='replace', & + ACCESS='stream') + end if + ! transport neutron call transport() + ! Close particle track output file. + if (write_track) then + close(UNIT=UNIT_TRACK) + endif + ! write output if particle made it write(ou,*) 'Particle Successfully Transport:' write(ou,*) 'WEIGHT:', p % wgt @@ -150,4 +166,8 @@ contains end subroutine run_particle_restart + subroutine write_particle_track() + write(UNIT_TRACK) p % coord0 % xyz + end subroutine write_particle_track + end module particle_restart diff --git a/src/physics.F90 b/src/physics.F90 index e1c7f12c32..ba8eac3fa5 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -15,6 +15,7 @@ module physics use mesh, only: get_mesh_indices use output, only: write_message use particle_header, only: LocalCoord + use particle_restart, only: write_particle_track use particle_restart_write, only: write_particle_restart use random_lcg, only: prn use search, only: binary_search @@ -73,14 +74,13 @@ contains ! Force calculation of cross-sections by setting last energy to zero micro_xs % last_E = ZERO - if (run_mode == MODE_PARTICLE) then - open(UNIT=UNIT_TRACK, FILE='test', STATUS='replace', & - ACCESS='stream') - write(UNIT_TRACK) p % coord0 % xyz - end if - do while (p % alive) + ! Write particle track. + if (write_track) then + call write_particle_track() + endif + ! Calculate microscopic and macroscopic cross sections -- note: if the ! material is the same as the last material and the energy of the ! particle hasn't changed, we don't need to lookup cross sections again. @@ -185,10 +185,6 @@ contains end do - if (run_mode == MODE_PARTICLE) then - close(UNIT=UNIT_TRACK) - end if - end subroutine transport !=============================================================================== From 3bae132b6d3b29481fd9879a6fc1f4452887e7bb Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 10 Jun 2013 22:08:27 -0400 Subject: [PATCH 03/29] Cleaned up Fortran implimentation of track writing and added track.py - a utility for converting binary track files to VTK files. --- src/utils/track.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 src/utils/track.py diff --git a/src/utils/track.py b/src/utils/track.py new file mode 100755 index 0000000000..7c836809ea --- /dev/null +++ b/src/utils/track.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python2 +"""Convert binary particle track to VTK poly data. + +Run 'track.py -help' for usage. + +""" + +import os +import argparse +import struct +import vtk + +if __name__ == '__main__': + # Create argument parser. + parser = argparse.ArgumentParser( + description='Convert binary particle track file to a .pvtp file.') + parser.add_argument('input', metavar='IN', type=argparse.FileType('rb'), + help='Input particle track data filename.') + parser.add_argument('-o', '-out', '--out', metavar='OUT', type=str, dest='out', + help='Output VTK poly data filename.') + # Parse commandline arguments. + args = parser.parse_args() + + # Make sure that the output filename ends with '.pvtp'. + if not args.out: + args.out = ''.join([os.path.splitext(args.input.name)[0], '.pvtp']) + elif os.path.splitext(args.out)[1] != '.pvtp': + args.out = ''.join([args.out, '.pvtp']) + + # Convert binary data into a list of coordinate triplets. + track = args.input.read() + coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) + for i in range(len(track)/24)] + + # Create VTK points. + points = vtk.vtkPoints() + for triplet in coords: + points.InsertNextPoint(triplet) + + # Create VTK line and assign points to line. + line = vtk.vtkPolyLine() + line.GetPointIds().SetNumberOfIds(points.GetNumberOfPoints()) + for i in range(points.GetNumberOfPoints()): + line.GetPointIds().SetId(i,i) + + cells = vtk.vtkCellArray() + cells.InsertNextCell(line) + + data = vtk.vtkPolyData() + data.SetPoints(points) + data.SetLines(cells) + + writer = vtk.vtkXMLPPolyDataWriter() + writer.SetInput(data) + writer.SetFileName(args.out) + writer.Write() + +##poly_mapper = vtk.vtkPolyDataMapper() +##poly_mapper.SetInputConnection(data.GetProducerPort()) +## +##poly_actor = vtk.vtkActor() +##poly_actor.SetMapper(poly_mapper) +## +##ren1 = vtk.vtkRenderer() +##ren1.AddActor(poly_actor) +##ren1.SetBackground(0.0, 0.0, 0.0) +## +##ren1.ResetCamera() +## +##ren_win = vtk.vtkRenderWindow() +##ren_win.AddRenderer(ren1) +##ren_win.SetSize(400, 400) +## +##iren = vtk.vtkRenderWindowInteractor() +##iren.SetRenderWindow(ren_win) +##iren.Initialize() +##iren.Start() From 883d69343c90a21dc9a7bb30c187822ebcf48af6 Mon Sep 17 00:00:00 2001 From: Sterling Date: Tue, 11 Jun 2013 21:20:34 -0400 Subject: [PATCH 04/29] Added comments to write_particle_track subroutine and gave track.py the ability to add multiple tracks to one file. --- src/particle_restart.F90 | 7 +++++ src/utils/track.py | 67 ++++++++++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index 1af6e2d30c..15afeb4cb5 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -165,6 +165,13 @@ contains end subroutine run_particle_restart +!=============================================================================== +! WRITE_PARTICLE_TRACK outputs particle position to a binary file. This +! subroutine needs to be modified to work with HDF5 files. Perhaps it should +! also be made somehow more general so that it can output information other +! than just particle position. +!=============================================================================== + subroutine write_particle_track() write(UNIT_TRACK) p % coord0 % xyz end subroutine write_particle_track diff --git a/src/utils/track.py b/src/utils/track.py index 7c836809ea..a5b33b7258 100755 --- a/src/utils/track.py +++ b/src/utils/track.py @@ -10,51 +10,78 @@ import argparse import struct import vtk -if __name__ == '__main__': + +def _parse_args(): # Create argument parser. parser = argparse.ArgumentParser( description='Convert binary particle track file to a .pvtp file.') parser.add_argument('input', metavar='IN', type=argparse.FileType('rb'), + nargs='+', help='Input particle track data filename.') parser.add_argument('-o', '-out', '--out', metavar='OUT', type=str, dest='out', help='Output VTK poly data filename.') - # Parse commandline arguments. - args = parser.parse_args() + # Parse and return commandline arguments. + return parser.parse_args() + + +def main(): + # Parse commandline argumetns. + args = _parse_args() + # Make sure that the output filename ends with '.pvtp'. if not args.out: args.out = ''.join([os.path.splitext(args.input.name)[0], '.pvtp']) elif os.path.splitext(args.out)[1] != '.pvtp': args.out = ''.join([args.out, '.pvtp']) - # Convert binary data into a list of coordinate triplets. - track = args.input.read() - coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) - for i in range(len(track)/24)] - - # Create VTK points. points = vtk.vtkPoints() - for triplet in coords: - points.InsertNextPoint(triplet) - - # Create VTK line and assign points to line. - line = vtk.vtkPolyLine() - line.GetPointIds().SetNumberOfIds(points.GetNumberOfPoints()) - for i in range(points.GetNumberOfPoints()): - line.GetPointIds().SetId(i,i) - cells = vtk.vtkCellArray() - cells.InsertNextCell(line) + j = 0 + k = 0 + arr = vtk.vtkIntArray() + for fin in args.input: + track = fin.read() + coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) + for i in range(len(track)/24)] + n_points = len(coords) + i=0 + for triplet in coords: + points.InsertNextPoint(triplet) + arr.InsertTuple1(i,k) + i+=1 + + # Create VTK line and assign points to line. + line = vtk.vtkPolyLine() + line.GetPointIds().SetNumberOfIds(n_points) + for i in range(n_points): + line.GetPointIds().SetId(i, j+i) + + cells.InsertNextCell(line) + j += n_points + k += 1 + + points.SetData(arr) data = vtk.vtkPolyData() data.SetPoints(points) data.SetLines(cells) - + writer = vtk.vtkXMLPPolyDataWriter() writer.SetInput(data) writer.SetFileName(args.out) writer.Write() + + +if __name__ == '__main__': + main() + + +#### The following code is retained for debugging purposes. If 'data' in the +#### in the function 'main' is made a global variable, the following code will +#### automatically plot the tracks. + ##poly_mapper = vtk.vtkPolyDataMapper() ##poly_mapper.SetInputConnection(data.GetProducerPort()) ## From 44999762e7deedb1cb8bb7274ca33d3579a639ec Mon Sep 17 00:00:00 2001 From: Sterling Date: Wed, 12 Jun 2013 16:41:09 -0400 Subject: [PATCH 05/29] Made minor edits to implimentation. Moved particle track subroutines to the output module. Particle tracks can now be written in any run mode that calls transport. track.py should correctly handle any number of input particle tracks. --- src/DEPENDENCIES | 1 - src/output.F90 | 40 ++++++++++++++++++++++++++++++++++++++++ src/particle_restart.F90 | 26 -------------------------- src/physics.F90 | 22 ++++++++++++++++++---- src/utils/track.py | 36 +++++++++++++++++++++--------------- 5 files changed, 79 insertions(+), 46 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index b71354ccaa..fcabd989e0 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -297,7 +297,6 @@ physics.o: material_header.o physics.o: mesh.o physics.o: output.o physics.o: particle_header.o -physics.o: particle_restart.o physics.o: particle_restart_write.o physics.o: random_lcg.o physics.o: search.o diff --git a/src/output.F90 b/src/output.F90 index b8302226d7..e2114c8b48 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1945,4 +1945,44 @@ contains end function get_label +!=============================================================================== +! INITIALIZE_PARTICLE_TRACK opens a particle track output file. +! +! TODO: This subroutine needs to be modified to work with HDF5 files. It +! should also probably write a header that identifies the file as a particle +! track and maybe adds particle identifying information (batch #, etc.). +!=============================================================================== + + subroutine initialize_particle_track() + character(MAX_FILE_LEN) :: filename + + filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & + // '_' // trim(to_str(current_work)) // '.binary' + open(UNIT=UNIT_TRACK, FILE=filename, STATUS='replace', & + ACCESS='stream') + + end subroutine initialize_particle_track + +!=============================================================================== +! WRITE_PARTICLE_TRACK outputs particle position to a binary file. +! +! TODO: This subroutine needs to be modified to work with HDF5 files. Perhaps +! it should also be made somehow more general so that it can output +! information other than just particle position. +!=============================================================================== + + subroutine write_particle_track() + write(UNIT_TRACK) p % coord0 % xyz + end subroutine write_particle_track + +!=============================================================================== +! FINALIZE_PARTICLE_TRACK closes the particle track file. +! +! TODO: This subroutine needs to be modified to work with HDF5 files. +!=============================================================================== + + subroutine finalize_particle_track() + close(UNIT=UNIT_TRACK) + end subroutine finalize_particle_track + end module output diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index 15afeb4cb5..39d8156fdb 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -11,7 +11,6 @@ module particle_restart use physics, only: transport use random_lcg, only: set_particle_seed use source, only: initialize_particle - use string, only: to_str #ifdef HDF5 use hdf5_interface @@ -20,7 +19,6 @@ module particle_restart implicit none private public :: run_particle_restart - public :: write_particle_track #ifdef HDF5 integer(HID_T) :: hdf5_particle_file @@ -144,36 +142,12 @@ contains current_gen - 1)*n_particles + p % id call set_particle_seed(particle_seed) - ! Open particle track output file. - if (write_track) then - filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_work)) // '.binary' - open(UNIT=UNIT_TRACK, FILE=filename, STATUS='replace', & - ACCESS='stream') - end if - ! transport neutron call transport() - ! Close particle track output file. - if (write_track) then - close(UNIT=UNIT_TRACK) - endif - ! write output if particle made it call print_particle() end subroutine run_particle_restart -!=============================================================================== -! WRITE_PARTICLE_TRACK outputs particle position to a binary file. This -! subroutine needs to be modified to work with HDF5 files. Perhaps it should -! also be made somehow more general so that it can output information other -! than just particle position. -!=============================================================================== - - subroutine write_particle_track() - write(UNIT_TRACK) p % coord0 % xyz - end subroutine write_particle_track - end module particle_restart diff --git a/src/physics.F90 b/src/physics.F90 index ba8eac3fa5..a5f3318538 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -13,9 +13,11 @@ module physics use interpolation, only: interpolate_tab1 use material_header, only: Material use mesh, only: get_mesh_indices - use output, only: write_message + use output, only: write_message, initialize_particle_track, & + write_particle_track, & + finalize_particle_track use particle_header, only: LocalCoord - use particle_restart, only: write_particle_track +! use particle_restart, only: write_particle_track use particle_restart_write, only: write_particle_restart use random_lcg, only: prn use search, only: binary_search @@ -74,6 +76,11 @@ contains ! Force calculation of cross-sections by setting last energy to zero micro_xs % last_E = ZERO + ! Prepare to write out particle track. + if (write_track) then + call initialize_particle_track() + endif + do while (p % alive) ! Write particle track. @@ -106,9 +113,11 @@ contains coord % xyz = coord % xyz + distance * coord % uvw coord => coord % next end do + + ! Write particle track - if (run_mode == MODE_PARTICLE) then - write(UNIT_TRACK) p % coord0 % xyz + if (write_track) then + call write_particle_track() end if ! Score track-length tallies @@ -185,6 +194,11 @@ contains end do + ! Finish particle track. + if (write_track) then + call finalize_particle_track() + endif + end subroutine transport !=============================================================================== diff --git a/src/utils/track.py b/src/utils/track.py index a5b33b7258..1907d1ec0a 100755 --- a/src/utils/track.py +++ b/src/utils/track.py @@ -1,7 +1,19 @@ #!/usr/bin/env python2 """Convert binary particle track to VTK poly data. -Run 'track.py -help' for usage. +Usage information can be obtained by running 'track.py --help': + + usage: track.py [-h] [-o OUT] IN [IN ...] + + Convert binary particle track file to a .pvtp file. + + positional arguments: + IN Input particle track data filename(s). + + optional arguments: + -h, --help show this help message and exit + -o OUT, -out OUT, --out OUT + Output VTK poly data filename. """ @@ -17,13 +29,14 @@ def _parse_args(): description='Convert binary particle track file to a .pvtp file.') parser.add_argument('input', metavar='IN', type=argparse.FileType('rb'), nargs='+', - help='Input particle track data filename.') - parser.add_argument('-o', '-out', '--out', metavar='OUT', type=str, dest='out', + help='Input particle track data filename(s).') + parser.add_argument('-o', '-out', '--out', metavar='OUT', type=str, + dest='out', help='Output VTK poly data filename.') # Parse and return commandline arguments. return parser.parse_args() - + def main(): # Parse commandline argumetns. @@ -31,7 +44,7 @@ def main(): # Make sure that the output filename ends with '.pvtp'. if not args.out: - args.out = ''.join([os.path.splitext(args.input.name)[0], '.pvtp']) + args.out = 'tracks.pvtp' elif os.path.splitext(args.out)[1] != '.pvtp': args.out = ''.join([args.out, '.pvtp']) @@ -39,18 +52,14 @@ def main(): cells = vtk.vtkCellArray() j = 0 k = 0 - arr = vtk.vtkIntArray() for fin in args.input: track = fin.read() coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) for i in range(len(track)/24)] n_points = len(coords) - - i=0 + for triplet in coords: points.InsertNextPoint(triplet) - arr.InsertTuple1(i,k) - i+=1 # Create VTK line and assign points to line. line = vtk.vtkPolyLine() @@ -61,8 +70,7 @@ def main(): cells.InsertNextCell(line) j += n_points k += 1 - - points.SetData(arr) + global data data = vtk.vtkPolyData() data.SetPoints(points) data.SetLines(cells) @@ -78,9 +86,7 @@ if __name__ == '__main__': main() -#### The following code is retained for debugging purposes. If 'data' in the -#### in the function 'main' is made a global variable, the following code will -#### automatically plot the tracks. +#### The following code is retained for debugging purposes: ##poly_mapper = vtk.vtkPolyDataMapper() ##poly_mapper.SetInputConnection(data.GetProducerPort()) From f866b437d8741de457bcf8deb25a9f2c9e626126 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 17 Jun 2013 15:00:37 -0400 Subject: [PATCH 06/29] Added settings.xml track options and updated docs Tracks can now be made using the element in settings.xml and the documentation has been updated to include track plotting. --- docs/img/Tracks.png | Bin 0 -> 94057 bytes docs/source/usersguide/input.rst | 9 +++++++++ docs/source/usersguide/processing.rst | 25 ++++++++++++++++++++++--- src/global.F90 | 8 +++++--- src/initialize.F90 | 2 +- src/input_xml.F90 | 17 +++++++++++++++++ src/output.F90 | 3 ++- src/source.F90 | 16 ++++++++++++++++ src/templates/settings_t.xml | 1 + 9 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 docs/img/Tracks.png diff --git a/docs/img/Tracks.png b/docs/img/Tracks.png new file mode 100644 index 0000000000000000000000000000000000000000..39c83cd59557dc5942c9838adf79e9f91ef1fc72 GIT binary patch literal 94057 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 ZOPvQ@6B2T8{EcUP%2zqHctN05hNK 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%0AH;1+ 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

>rPy^m{&64m%@njLjSUXMiR*^4~rjsnKI$dJksZ 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 XOpW+RCHjDED@aRCU$tD>Hsb#PV-jyl literal 0 HcmV?d00001 diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index d9b43c8214..fcd4bb73c3 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -390,6 +390,15 @@ integers: the batch number, generation number, and particle number. *Default*: None +.. _track: + +```` Element +------------------- + +The ```` element specifies particles for which OpenMC will output binary files describing particle position at every step of its transport. This element should be followed by triplets of integers. Each triplet describes one particle. The integers in each triplet specify the batch number, generation number, and particle number, respectively. + + *Default*: None + ```` Element ------------------------ diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index 7105e9f7d1..94b8a71be0 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -345,15 +345,34 @@ Getting Data into MATLAB ------------------------ There is currently no front-end utility to dump tally data to MATLAB files, but -the process is straightforward. First extract the data using a custom Python -script with statepoint.py, put the data into appropriately-shaped numpy arrays, +the process is straightforward. First extract the data using a custom Python script with statepoint.py, put the data into appropriately-shaped numpy arrays, and then use the `Scipy MATLAB IO routines `_ to save to a MAT file. Note that the data contained in the output from ``StatePoint.extract_result`` is already in a Numpy array that can be reshaped and dumped to MATLAB in one step. - +---------------------------- +Particle Track Visualization +---------------------------- + +.. image:: ../../img/Tracks.png + :height: 200px + +OpenMC can dump particle tracks—the position of particles as they are transported through the geometry. There are two ways to make OpenMC output tracks: all particle tracks through a commandline argument or specific particle tracks through settings.xml. + +Running OpenMC with the argument "-tr", "-track", or "--track" will cause a track file to be created for every particle transported in the code. + +The settings.xml file can dictate that specific particle tracks are output. These particles are specified withen a ''track'' element. The ''track'' element should contain triplets of integers specifying the batch, generation, and particle numbers, respectively. For example, to output the tracks for particles 3 and 4 of batch 1 and generation 2 the settings.xml file should contain: + +.. code-block:: xml + + + 1 2 3 + 1 2 4 + + +After running OpenMC, the directory should contain a file of the form "track_(batch #)_(work #).binary" for each particle tracked. These binary track files can be converted into VTK poly data files with the "track.py" utility. The usage of track.py is of the form "track.py [-o OUT] IN" where OUT is the optional output filename and IN is one or more filenames describing track binary files. The default output name is "track.pvtp". A common usage of track.py is "track.py track*.binary" which will use the data from all binary track files in the directory to write a "track.pvtp" VTK output file. The .pvtp file can then be read and plotted by 3d visualization programs such as Paraview. diff --git a/src/global.F90 b/src/global.F90 index a45704f399..79e6ecf71a 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -284,12 +284,14 @@ module global integer :: trace_gen integer(8) :: trace_particle + ! Particle tracks + logical :: write_track = .false. + logical :: write_all_tracks = .false. + integer, allocatable :: track_identifiers(:,:) + ! Particle restart run logical :: particle_restart_run = .false. - ! Particle track output - logical :: write_track = .false. - ! ============================================================================ ! CMFD VARIABLES diff --git a/src/initialize.F90 b/src/initialize.F90 index ce5c457e80..7038544daf 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -378,7 +378,7 @@ contains ! Handle options that would be based to PETSC i = i + 1 case ('-tr', '-track', '--track') - write_track = .true. + write_all_tracks = .true. case default message = "Unknown command line option: " // argv(i) call fatal_error() diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 45abaf9a2e..4ce0512139 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -52,6 +52,7 @@ contains integer :: i ! loop index integer :: n integer :: coeffs_reqd + integer :: n_tracks logical :: file_exists character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type @@ -383,6 +384,22 @@ contains trace_particle = trace_(3) end if + ! Particle tracks + if (associated(track_)) then + n_tracks = size(track_) + ! Make sure that there are three values per particle + if (mod(n_tracks, 3) /= 0) then + message = "Number of integers specified in 'track' is not divisible & + &by 3. Please provide 3 integers per particle to be tracked." + call fatal_error() + end if + n_tracks = n_tracks/3 + allocate(track_identifiers(3,n_tracks)) + do i=1, n_tracks + track_identifiers(1:3,i) = track_(3*(i-1)+1 : 3*(i-1)+4) + end do + end if + ! Shannon Entropy mesh if (size(entropy_) > 0) then ! Check to make sure enough values were supplied diff --git a/src/output.F90 b/src/output.F90 index 504630d799..c10891f265 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1994,7 +1994,8 @@ contains character(MAX_FILE_LEN) :: filename filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_work)) // '.binary' + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & + // '.binary' open(UNIT=UNIT_TRACK, FILE=filename, STATUS='replace', & ACCESS='stream') diff --git a/src/source.F90 b/src/source.F90 index 9ffea50089..02e1c3b997 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -152,6 +152,7 @@ contains subroutine get_source_particle(index_source) integer(8), intent(in) :: index_source + integer :: i ! iterator index integer(8) :: particle_seed ! unique index for particle type(Bank), pointer :: src => null() @@ -175,6 +176,21 @@ contains if (current_batch == trace_batch .and. current_gen == trace_gen .and. & p % id == trace_particle) trace = .true. + ! Set particle track. + write_track = .false. + if (write_all_tracks) then + write_track = .true. + else if (allocated(track_identifiers)) then + do i=1, size(track_identifiers(1,:)) + if (current_batch == track_identifiers(1,i) .and. & + ¤t_gen == track_identifiers(2,i) .and. & + &p % id == track_identifiers(3,i)) then + write_track = .true. + exit + end if + end do + end if + end subroutine get_source_particle !=============================================================================== diff --git a/src/templates/settings_t.xml b/src/templates/settings_t.xml index 3afa61745e..3903a35143 100644 --- a/src/templates/settings_t.xml +++ b/src/templates/settings_t.xml @@ -59,6 +59,7 @@ + From e9a5d18f0e419d96f2d90533390e7829a60ef441 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 17 Jun 2013 15:23:09 -0400 Subject: [PATCH 07/29] Fixed a typo. --- docs/source/usersguide/processing.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index 94b8a71be0..8d6fea0374 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -345,7 +345,8 @@ Getting Data into MATLAB ------------------------ There is currently no front-end utility to dump tally data to MATLAB files, but -the process is straightforward. First extract the data using a custom Python script with statepoint.py, put the data into appropriately-shaped numpy arrays, +the process is straightforward. First extract the data using a custom Python +script with statepoint.py, put the data into appropriately-shaped numpy arrays, and then use the `Scipy MATLAB IO routines `_ to save to a MAT file. Note that the data contained in the output from @@ -372,7 +373,7 @@ The settings.xml file can dictate that specific particle tracks are output. The 1 2 4 -After running OpenMC, the directory should contain a file of the form "track_(batch #)_(work #).binary" for each particle tracked. These binary track files can be converted into VTK poly data files with the "track.py" utility. The usage of track.py is of the form "track.py [-o OUT] IN" where OUT is the optional output filename and IN is one or more filenames describing track binary files. The default output name is "track.pvtp". A common usage of track.py is "track.py track*.binary" which will use the data from all binary track files in the directory to write a "track.pvtp" VTK output file. The .pvtp file can then be read and plotted by 3d visualization programs such as Paraview. +After running OpenMC, the directory should contain a file of the form "track_(batch #)_(generation #)_(particle #).binary" for each particle tracked. These binary track files can be converted into VTK poly data files with the "track.py" utility. The usage of track.py is of the form "track.py [-o OUT] IN" where OUT is the optional output filename and IN is one or more filenames describing track binary files. The default output name is "track.pvtp". A common usage of track.py is "track.py track*.binary" which will use the data from all binary track files in the directory to write a "track.pvtp" VTK output file. The .pvtp file can then be read and plotted by 3d visualization programs such as Paraview. From a164dfa69fb88f0a2875beeaee6ff60f1ad94012 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 18 Jun 2013 10:46:06 -0400 Subject: [PATCH 08/29] Small fixes Changed the '-tr' commandline option to '-k'. Removed redundant 'write_particle_track' call. Moved particle_track subroutines to their own module. Layed (commented out) groundwork for particle_track to use output_interface. --- docs/source/usersguide/processing.rst | 2 +- src/DEPENDENCIES | 5 +++ src/OBJECTS | 1 + src/initialize.F90 | 3 +- src/output.F90 | 42 +------------------- src/particle_track.F90 | 56 +++++++++++++++++++++++++++ src/physics.F90 | 12 ++---- 7 files changed, 70 insertions(+), 51 deletions(-) create mode 100644 src/particle_track.F90 diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index 8d6fea0374..f51f87685f 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -362,7 +362,7 @@ Particle Track Visualization OpenMC can dump particle tracks—the position of particles as they are transported through the geometry. There are two ways to make OpenMC output tracks: all particle tracks through a commandline argument or specific particle tracks through settings.xml. -Running OpenMC with the argument "-tr", "-track", or "--track" will cause a track file to be created for every particle transported in the code. +Running OpenMC with the argument "-k", "-track", or "--track" will cause a track file to be created for every particle transported in the code. The settings.xml file can dictate that specific particle tracks are output. These particles are specified withen a ''track'' element. The ''track'' element should contain triplets of integers specifying the batch, generation, and particle numbers, respectively. For example, to output the tracks for particles 3 and 4 of batch 1 and generation 2 the settings.xml file should contain: diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index e8cc541f4f..48ef958f49 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -272,6 +272,10 @@ output_interface.o: tally_header.o particle_header.o: constants.o +particle_track.o: constants.o +particle_track.o: global.o +particle_track.o: string.o + particle_restart.o: bank_header.o particle_restart.o: constants.o particle_restart.o: geometry_header.o @@ -302,6 +306,7 @@ physics.o: material_header.o physics.o: mesh.o physics.o: output.o physics.o: particle_header.o +physics.o: particle_track.o physics.o: particle_restart_write.o physics.o: random_lcg.o physics.o: search.o diff --git a/src/OBJECTS b/src/OBJECTS index b079906751..6ba5516d5f 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -43,6 +43,7 @@ mpiio_interface.o \ output.o \ output_interface.o \ particle_header.o \ +particle_track.o \ particle_restart.o \ particle_restart_write.o \ physics.o \ diff --git a/src/initialize.F90 b/src/initialize.F90 index 7038544daf..3d54173bea 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -377,8 +377,9 @@ contains case ('-eps_tol', '-ksp_gmres_restart') ! Handle options that would be based to PETSC i = i + 1 - case ('-tr', '-track', '--track') + case ('-k', '-track', '--track') write_all_tracks = .true. + i = i + 1 case default message = "Unknown command line option: " // argv(i) call fatal_error() diff --git a/src/output.F90 b/src/output.F90 index c10891f265..77b323ce6e 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -170,6 +170,7 @@ contains write(OUTPUT_UNIT,*) ' -r, --restart Restart a previous run from a state point' write(OUTPUT_UNIT,*) ' or a particle restart file' write(OUTPUT_UNIT,*) ' -t, --tallies Write tally results from state point' + write(OUTPUT_UNIT,*) ' -k, --tracks Write tracks for all particles' write(OUTPUT_UNIT,*) ' -v, --version Show version information' write(OUTPUT_UNIT,*) ' -?, --help Show this message' end if @@ -1982,45 +1983,4 @@ contains end function get_label -!=============================================================================== -! INITIALIZE_PARTICLE_TRACK opens a particle track output file. -! -! TODO: This subroutine needs to be modified to work with HDF5 files. It -! should also probably write a header that identifies the file as a particle -! track and maybe adds particle identifying information (batch #, etc.). -!=============================================================================== - - subroutine initialize_particle_track() - character(MAX_FILE_LEN) :: filename - - filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & - // '.binary' - open(UNIT=UNIT_TRACK, FILE=filename, STATUS='replace', & - ACCESS='stream') - - end subroutine initialize_particle_track - -!=============================================================================== -! WRITE_PARTICLE_TRACK outputs particle position to a binary file. -! -! TODO: This subroutine needs to be modified to work with HDF5 files. Perhaps -! it should also be made somehow more general so that it can output -! information other than just particle position. -!=============================================================================== - - subroutine write_particle_track() - write(UNIT_TRACK) p % coord0 % xyz - end subroutine write_particle_track - -!=============================================================================== -! FINALIZE_PARTICLE_TRACK closes the particle track file. -! -! TODO: This subroutine needs to be modified to work with HDF5 files. -!=============================================================================== - - subroutine finalize_particle_track() - close(UNIT=UNIT_TRACK) - end subroutine finalize_particle_track - end module output diff --git a/src/particle_track.F90 b/src/particle_track.F90 new file mode 100644 index 0000000000..c4c218520e --- /dev/null +++ b/src/particle_track.F90 @@ -0,0 +1,56 @@ +module particle_track + + use constants + use global + !use output_interface only: file_create, file_close, write_double1Darray + use string, only: to_str + + implicit none + +contains + +!=============================================================================== +! INITIALIZE_PARTICLE_TRACK opens a particle track output file. +! +! TODO: This subroutine needs to be modified to work with HDF5 files. It +! should also probably write a header that identifies the file as a particle +! track and maybe adds particle identifying information (batch #, etc.). +!=============================================================================== + + subroutine initialize_particle_track() + character(MAX_FILE_LEN) :: filename + + filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & + // '.binary' + open(UNIT=UNIT_TRACK, FILE=filename, STATUS='replace', & + ACCESS='stream') + !file_create(filename, 'serial') + + end subroutine initialize_particle_track + +!=============================================================================== +! WRITE_PARTICLE_TRACK outputs particle position to a binary file. +! +! TODO: This subroutine needs to be modified to work with HDF5 files. Perhaps +! it should also be made somehow more general so that it can output +! information other than just particle position. +!=============================================================================== + + subroutine write_particle_track() + write(UNIT_TRACK) p % coord0 % xyz + !write_double1Darray(p % coord0 % xyz, 'coordinates', 3) + end subroutine write_particle_track + +!=============================================================================== +! FINALIZE_PARTICLE_TRACK closes the particle track file. +! +! TODO: This subroutine needs to be modified to work with HDF5 files. +!=============================================================================== + + subroutine finalize_particle_track() + close(UNIT=UNIT_TRACK) + !file_close('serial') + end subroutine finalize_particle_track + +end module particle_track diff --git a/src/physics.F90 b/src/physics.F90 index 8b67642003..35ecb3aa65 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -14,10 +14,11 @@ module physics use interpolation, only: interpolate_tab1 use material_header, only: Material use mesh, only: get_mesh_indices - use output, only: write_message, initialize_particle_track, & + use output, only: write_message + use particle_header, only: LocalCoord + use particle_track, only: initialize_particle_track, & write_particle_track, & finalize_particle_track - use particle_header, only: LocalCoord use particle_restart_write, only: write_particle_restart use random_lcg, only: prn use search, only: binary_search @@ -114,12 +115,6 @@ contains coord => coord % next end do - ! Write particle track - - if (write_track) then - call write_particle_track() - end if - ! Score track-length tallies if (active_tracklength_tallies % size() > 0) & call score_tracklength_tally(distance) @@ -196,6 +191,7 @@ contains ! Finish particle track. if (write_track) then + call write_particle_track() call finalize_particle_track() endif From 65bdb0b06cf6c396f6a3daf9fef9e27b1197ca4d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 18 Jun 2013 19:50:50 -0400 Subject: [PATCH 09/29] Update settings.xml RELAX NG schema for . --- src/templates/settings.rnc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/templates/settings.rnc b/src/templates/settings.rnc index 47dd690bcc..f5f723b8d8 100644 --- a/src/templates/settings.rnc +++ b/src/templates/settings.rnc @@ -99,6 +99,8 @@ element settings { element trace { list { xsd:positiveInteger+ } }? & + element track { list { xsd:positiveInteger+ } }? & + element verbosity { xsd:positiveInteger }? & element uniform_fs{ From 51a1bc028b88d1b0886e1a53173789f0453ccac3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 18 Jun 2013 19:52:55 -0400 Subject: [PATCH 10/29] Update man page with -k, --track. --- man/man1/openmc.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/man/man1/openmc.1 b/man/man1/openmc.1 index eb8981f38f..d07653d2a9 100644 --- a/man/man1/openmc.1 +++ b/man/man1/openmc.1 @@ -24,6 +24,9 @@ Run in plotting mode Restart a previous run from a state point or a particle restart file named \fIbinaryFile\fP. .TP +.B "\-k\fR, \fP\-\-track" +Write tracks for all particles. +.TP .B "\-v\fR, \fP\-\-version" Show version information. .TP From 3a0fdefacb19c27cb41aa1526e3d0231cb2892a4 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 18 Jun 2013 19:56:32 -0400 Subject: [PATCH 11/29] Minor fixes. --- src/output.F90 | 2 +- src/source.F90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/output.F90 b/src/output.F90 index 77b323ce6e..76c48ec219 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -170,7 +170,7 @@ contains write(OUTPUT_UNIT,*) ' -r, --restart Restart a previous run from a state point' write(OUTPUT_UNIT,*) ' or a particle restart file' write(OUTPUT_UNIT,*) ' -t, --tallies Write tally results from state point' - write(OUTPUT_UNIT,*) ' -k, --tracks Write tracks for all particles' + write(OUTPUT_UNIT,*) ' -k, --track Write tracks for all particles' write(OUTPUT_UNIT,*) ' -v, --version Show version information' write(OUTPUT_UNIT,*) ' -?, --help Show this message' end if diff --git a/src/source.F90 b/src/source.F90 index 02e1c3b997..3e7443703b 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -187,7 +187,7 @@ contains &p % id == track_identifiers(3,i)) then write_track = .true. exit - end if + end if end do end if From 58738986c71ab25a5ae46cfcbddc83c21ec719c4 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 21 Jun 2013 09:34:19 -0400 Subject: [PATCH 12/29] Changed commandline flag from -k to -t --- docs/source/usersguide/processing.rst | 2 +- man/man1/openmc.1 | 2 +- src/initialize.F90 | 2 +- src/output.F90 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index f51f87685f..bd0d19a1a4 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -362,7 +362,7 @@ Particle Track Visualization OpenMC can dump particle tracks—the position of particles as they are transported through the geometry. There are two ways to make OpenMC output tracks: all particle tracks through a commandline argument or specific particle tracks through settings.xml. -Running OpenMC with the argument "-k", "-track", or "--track" will cause a track file to be created for every particle transported in the code. +Running OpenMC with the argument "-t", "-track", or "--track" will cause a track file to be created for every particle transported in the code. The settings.xml file can dictate that specific particle tracks are output. These particles are specified withen a ''track'' element. The ''track'' element should contain triplets of integers specifying the batch, generation, and particle numbers, respectively. For example, to output the tracks for particles 3 and 4 of batch 1 and generation 2 the settings.xml file should contain: diff --git a/man/man1/openmc.1 b/man/man1/openmc.1 index 326860e5e4..56ae873a48 100644 --- a/man/man1/openmc.1 +++ b/man/man1/openmc.1 @@ -24,7 +24,7 @@ Run in plotting mode Restart a previous run from a state point or a particle restart file named \fIbinaryFile\fP. .TP -.B "\-k\fR, \fP\-\-track" +.B "\-t\fR, \fP\-\-track" Write tracks for all particles. .TP .B "\-v\fR, \fP\-\-version" diff --git a/src/initialize.F90 b/src/initialize.F90 index ad9c0ada23..cecb38719e 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -369,7 +369,7 @@ contains case ('-eps_tol', '-ksp_gmres_restart') ! Handle options that would be based to PETSC i = i + 1 - case ('-k', '-track', '--track') + case ('-t', '-track', '--track') write_all_tracks = .true. i = i + 1 case default diff --git a/src/output.F90 b/src/output.F90 index 57ab50c7f7..9afc676841 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -170,7 +170,7 @@ contains write(OUTPUT_UNIT,*) ' -p, --plot Run in plotting mode' write(OUTPUT_UNIT,*) ' -r, --restart Restart a previous run from a state point' write(OUTPUT_UNIT,*) ' or a particle restart file' - write(OUTPUT_UNIT,*) ' -k, --track Write tracks for all particles' + write(OUTPUT_UNIT,*) ' -t, --track Write tracks for all particles' write(OUTPUT_UNIT,*) ' -v, --version Show version information' write(OUTPUT_UNIT,*) ' -?, --help Show this message' end if From 73826aa2fd0c6ffef194387e8fa335aba7c8f9f6 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 24 Jun 2013 14:58:22 -0400 Subject: [PATCH 13/29] Deallocated track_identifiers array --- src/global.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/global.F90 b/src/global.F90 index 79e6ecf71a..b2b445bdbd 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -448,6 +448,9 @@ contains call active_tracklength_tallies % clear() call active_current_tallies % clear() call active_tallies % clear() + + ! Deallocate track_identifiers + if (allocated(track_identifiers)) deallocate(track_identifiers) ! Deallocate dictionaries call cell_dict % clear() From e0936553cb6c9e73130e332c20f1b6cd0ecd62c4 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 6 Jul 2013 20:34:09 -0400 Subject: [PATCH 14/29] Added groundwork for HDF5 support Currently there is an indexing error with the HDF write statement. --- src/Makefile | 4 +- src/particle_track.F90 | 96 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/Makefile b/src/Makefile index 05cb7df6e8..c4a9c3dbef 100644 --- a/src/Makefile +++ b/src/Makefile @@ -21,7 +21,7 @@ DEBUG = no PROFILE = no OPTIMIZE = no MPI = no -HDF5 = no +HDF5 = yes PETSC = no #=============================================================================== @@ -29,7 +29,7 @@ PETSC = no #=============================================================================== MPI_DIR = /opt/mpich/3.0.4-$(COMPILER) -HDF5_DIR = /opt/hdf5/1.8.11-$(COMPILER) +HDF5_DIR = /usr/local/hdf5 PHDF5_DIR = /opt/phdf5/1.8.11-$(COMPILER) PETSC_DIR = /opt/petsc/3.3-p6-$(COMPILER) diff --git a/src/particle_track.F90 b/src/particle_track.F90 index c4c218520e..bc11497fbd 100644 --- a/src/particle_track.F90 +++ b/src/particle_track.F90 @@ -5,8 +5,22 @@ module particle_track !use output_interface only: file_create, file_close, write_double1Darray use string, only: to_str +#ifdef HDF5 + use hdf5 +#endif + implicit none +#ifdef HDF5 + integer, private :: hdf5_err ! HDF5 error code + integer(HID_T), private :: track_dset ! dataset handle + integer(HID_T), private :: track_dspace ! master dataspace handle + integer(HID_T), private :: track_selspace ! selected dataspace handle + integer(HID_T), private :: track_fh ! HDF5 file handle + integer(HID_T), private :: cparms ! chunk parameters + integer(HSIZE_T), private :: n_tracks ! total number of tracks +#endif + contains !=============================================================================== @@ -19,13 +33,35 @@ contains subroutine initialize_particle_track() character(MAX_FILE_LEN) :: filename + ! Replace the following with '#ifdef HDF5'. +#if 0 + integer(HSIZE_T) :: dims(2), max_dims(2), i + integer(HID_T) :: cparms + integer(HID_T) :: dapl +#endif filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & - // '.binary' - open(UNIT=UNIT_TRACK, FILE=filename, STATUS='replace', & - ACCESS='stream') - !file_create(filename, 'serial') + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) + + ! Replace the following with '#ifdef HDF5'. +#if 0 + filename = filename // '.h5' + call h5fcreate_f(filename, H5F_ACC_TRUNC_F, track_fh, hdf5_err) + dims = (/3, 1/) + i = 3 + max_dims = (/i, H5S_UNLIMITED_F/) + call h5screate_simple_f(2, dims, track_dspace, hdf5_err, max_dims) + call h5pcreate_f(H5P_DATASET_CREATE_F, cparms, hdf5_err) + call h5pset_chunk_f(cparms, 2, dims, hdf5_err) + call h5pset_fill_value_f(cparms, H5T_NATIVE_DOUBLE, 0, hdf5_err) + call h5dcreate_f(track_fh, 'coordinates', H5T_NATIVE_DOUBLE, track_dspace, & + track_dset, hdf5_err, cparms) + n_tracks = 1 +#else + filename = filename // '.binary' + open(UNIT=UNIT_TRACK, FILE=filename, ACTION="write", & + STATUS='replace', ACCESS='stream') +#endif end subroutine initialize_particle_track @@ -38,8 +74,40 @@ contains !=============================================================================== subroutine write_particle_track() + ! Replace the following with '#ifdef HDF5'. +#if 0 + integer(HSIZE_T) :: dims(2), max_dims(2), offset(2), i + integer(HSIZE_T), parameter :: cnt(2) = (/3, 1/), write_dims(2) = (/3, 1/) + real(8) :: coords(3, 1) + integer(HSIZE_T) :: test + + i = 3 + dims = (/i, n_tracks/) + call h5dget_space_f(track_dset, track_dspace, hdf5_err) + call h5dset_extent_f(track_dset, dims, hdf5_err) + call h5dget_space_f(track_dset, track_dspace, hdf5_err) + i = 1 + offset = (/i, n_tracks/) + call h5sselect_hyperslab_f(track_dspace, H5S_SELECT_SET_F, offset, cnt, & + hdf5_err) + !call h5sget_select_npoints_f(track_dspace, test, hdf5_err) + print *, test + coords(:, 1) = p % coord0 % xyz + print *, coords + if (n_tracks<20) then + call h5dwrite_f(track_dset, H5T_NATIVE_DOUBLE, coords, & + write_dims, hdf5_err, file_space_id=track_dspace) + end if + !call h5sselect_hyperslab_f(track_dspace, H5S_SELECT_SET_F, offset, dims, & + ! hdf5_err) + !i = 3 + n_tracks = n_tracks + 1 + !dims = (/i, n_tracks/) + !call h5sset_extent_simple_f(track_dspace, 2, dims, max_dims, hdf5_err) + !call h5dset_extent_f(track_dset, dims, hdf5_err) +#else write(UNIT_TRACK) p % coord0 % xyz - !write_double1Darray(p % coord0 % xyz, 'coordinates', 3) +#endif end subroutine write_particle_track !=============================================================================== @@ -49,8 +117,22 @@ contains !=============================================================================== subroutine finalize_particle_track() + ! Replace the following with '#ifdef HDF5'. +#if 0 + integer(HSIZE_T) :: dims(2), max_dims(2), i + + call h5sget_simple_extent_dims_f(track_dspace, dims, max_dims, hdf5_err) + !i = 3 + !dims = (/i, dims(2)-1/) + !call h5dextend_f(track_dset, dims, hdf5_err) + + call h5dclose_f(track_dset, hdf5_err) + call h5sclose_f(track_dspace, hdf5_err) + call h5pclose_f(cparms, hdf5_err) + call h5fclose_f(track_fh, hdf5_err) +#else close(UNIT=UNIT_TRACK) - !file_close('serial') +#endif end subroutine finalize_particle_track end module particle_track From 52233d0173494368ebe87210e748cf5403a382c3 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 8 Jul 2013 15:10:50 -0400 Subject: [PATCH 15/29] Implimented HDF5 support. --- docs/source/usersguide/processing.rst | 2 +- src/DEPENDENCIES | 1 + src/Makefile | 4 +- src/particle_track.F90 | 136 +++++++++++++++----------- src/utils/track.py | 43 +++++--- 5 files changed, 112 insertions(+), 74 deletions(-) diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index bd0d19a1a4..c12041214a 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -373,7 +373,7 @@ The settings.xml file can dictate that specific particle tracks are output. The 1 2 4 -After running OpenMC, the directory should contain a file of the form "track_(batch #)_(generation #)_(particle #).binary" for each particle tracked. These binary track files can be converted into VTK poly data files with the "track.py" utility. The usage of track.py is of the form "track.py [-o OUT] IN" where OUT is the optional output filename and IN is one or more filenames describing track binary files. The default output name is "track.pvtp". A common usage of track.py is "track.py track*.binary" which will use the data from all binary track files in the directory to write a "track.pvtp" VTK output file. The .pvtp file can then be read and plotted by 3d visualization programs such as Paraview. +After running OpenMC, the directory should contain a file of the form "track_(batch #)_(generation #)_(particle #).(binary or h5)" for each particle tracked. These track files can be converted into VTK poly data files with the "track.py" utility. The usage of track.py is of the form "track.py [-o OUT] IN" where OUT is the optional output filename and IN is one or more filenames describing track files. The default output name is "track.pvtp". A common usage of track.py is "track.py track*.binary" which will use the data from all binary track files in the directory to write a "track.pvtp" VTK output file. The .pvtp file can then be read and plotted by 3d visualization programs such as Paraview. diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index ea81f9ce9f..064acd707c 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -275,6 +275,7 @@ particle_header.o: constants.o particle_track.o: constants.o particle_track.o: global.o +particle_track.o: output_interface.o particle_track.o: string.o particle_restart.o: bank_header.o diff --git a/src/Makefile b/src/Makefile index c4a9c3dbef..05cb7df6e8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -21,7 +21,7 @@ DEBUG = no PROFILE = no OPTIMIZE = no MPI = no -HDF5 = yes +HDF5 = no PETSC = no #=============================================================================== @@ -29,7 +29,7 @@ PETSC = no #=============================================================================== MPI_DIR = /opt/mpich/3.0.4-$(COMPILER) -HDF5_DIR = /usr/local/hdf5 +HDF5_DIR = /opt/hdf5/1.8.11-$(COMPILER) PHDF5_DIR = /opt/phdf5/1.8.11-$(COMPILER) PETSC_DIR = /opt/petsc/3.3-p6-$(COMPILER) diff --git a/src/particle_track.F90 b/src/particle_track.F90 index bc11497fbd..852a07e100 100644 --- a/src/particle_track.F90 +++ b/src/particle_track.F90 @@ -1,110 +1,136 @@ +!=============================================================================== +! PARTICLE_TRACK handles output of particle tracks to disk. +! +! TODO: This module writes binary output files via a stream access i.e. it +! writes the particle's location to disk everytime write_particle_track is +! called. But HDF5 does not allow for a simple implimentation of stream access. +! An attempt was made to write HDF5 files in a stream fashion using an +! extendable dataset, but it always failed at the write call because of an out- +! of-bounds error. The extendable dataset code is commented out by enclosing it +! within "#if 0"/"#endif" blocks, and has been replaced by code that writes +! particle coordinates to an ever-expanding array which is written to disk in +! its entirety when finalize_particle_track is called. +! +! In the interest of consistency, maybe this module should be changed so that it +! uses the extendable HDF5 dataset, or it writes binary output all in one call +! using the output_interface (like the current HDF5 implimentation). +!=============================================================================== + module particle_track use constants use global - !use output_interface only: file_create, file_close, write_double1Darray - use string, only: to_str + use string, only: to_str -#ifdef HDF5 +#if 0 use hdf5 #endif +#ifdef HDF5 + use output_interface, only: file_create, file_close, write_data +#endif implicit none +#if 0 + integer, private :: hdf5_err ! HDF error code + integer(HID_T), private :: track_dset ! dataset handle + integer(HID_T), private :: track_dspace ! dataspace handle + integer(HID_T), private :: track_fh ! HDF file handle + integer(HID_T), private :: cparms ! chunk parameters + integer(HSIZE_T), private :: n_tracks ! total number of tracks +#endif #ifdef HDF5 - integer, private :: hdf5_err ! HDF5 error code - integer(HID_T), private :: track_dset ! dataset handle - integer(HID_T), private :: track_dspace ! master dataspace handle - integer(HID_T), private :: track_selspace ! selected dataspace handle - integer(HID_T), private :: track_fh ! HDF5 file handle - integer(HID_T), private :: cparms ! chunk parameters - integer(HSIZE_T), private :: n_tracks ! total number of tracks + character(MAX_FILE_LEN), private :: fname ! file name + integer, private :: n_tracks ! total number of tracks + real(8), private, allocatable :: coords(:,:) ! track coordinates #endif contains !=============================================================================== ! INITIALIZE_PARTICLE_TRACK opens a particle track output file. -! -! TODO: This subroutine needs to be modified to work with HDF5 files. It -! should also probably write a header that identifies the file as a particle -! track and maybe adds particle identifying information (batch #, etc.). !=============================================================================== subroutine initialize_particle_track() character(MAX_FILE_LEN) :: filename - ! Replace the following with '#ifdef HDF5'. #if 0 - integer(HSIZE_T) :: dims(2), max_dims(2), i + integer(HSIZE_T) :: dims(2), max_dims(2), i, j(1) integer(HID_T) :: cparms - integer(HID_T) :: dapl -#endif - filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) - - ! Replace the following with '#ifdef HDF5'. -#if 0 - filename = filename // '.h5' + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & + // '.h5' + n_tracks = 0 + ! Create file. call h5fcreate_f(filename, H5F_ACC_TRUNC_F, track_fh, hdf5_err) + ! Create a dataspace with an unlimited max dimension. dims = (/3, 1/) i = 3 max_dims = (/i, H5S_UNLIMITED_F/) call h5screate_simple_f(2, dims, track_dspace, hdf5_err, max_dims) + ! Set dataspace chunking. call h5pcreate_f(H5P_DATASET_CREATE_F, cparms, hdf5_err) call h5pset_chunk_f(cparms, 2, dims, hdf5_err) + ! Set fill value. call h5pset_fill_value_f(cparms, H5T_NATIVE_DOUBLE, 0, hdf5_err) + ! Create dataset. call h5dcreate_f(track_fh, 'coordinates', H5T_NATIVE_DOUBLE, track_dspace, & track_dset, hdf5_err, cparms) - n_tracks = 1 +#endif +#ifdef HDF5 + filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & + // '.h5' + n_tracks = 0 + fname = filename + allocate(coords(3,1)) #else - filename = filename // '.binary' + filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & + // '.binary' open(UNIT=UNIT_TRACK, FILE=filename, ACTION="write", & STATUS='replace', ACCESS='stream') #endif - end subroutine initialize_particle_track !=============================================================================== ! WRITE_PARTICLE_TRACK outputs particle position to a binary file. -! -! TODO: This subroutine needs to be modified to work with HDF5 files. Perhaps -! it should also be made somehow more general so that it can output -! information other than just particle position. !=============================================================================== subroutine write_particle_track() - ! Replace the following with '#ifdef HDF5'. #if 0 integer(HSIZE_T) :: dims(2), max_dims(2), offset(2), i integer(HSIZE_T), parameter :: cnt(2) = (/3, 1/), write_dims(2) = (/3, 1/) - real(8) :: coords(3, 1) - integer(HSIZE_T) :: test + real(8) :: coords(3) + ! There is another set of track coordinates. Incriment the counter. + n_tracks = n_tracks + 1 + ! Make the dataset the right size to fit the track coordinates. i = 3 dims = (/i, n_tracks/) - call h5dget_space_f(track_dset, track_dspace, hdf5_err) call h5dset_extent_f(track_dset, dims, hdf5_err) + ! Get the dataspace with the updated dimensions. call h5dget_space_f(track_dset, track_dspace, hdf5_err) + ! Select the hyperslab where the latest coordinates will be written. i = 1 offset = (/i, n_tracks/) + if (n_tracks < 20) then call h5sselect_hyperslab_f(track_dspace, H5S_SELECT_SET_F, offset, cnt, & hdf5_err) - !call h5sget_select_npoints_f(track_dspace, test, hdf5_err) - print *, test - coords(:, 1) = p % coord0 % xyz - print *, coords - if (n_tracks<20) then + endif + ! Write the coordinates to the dataset. + coords = p % coord0 % xyz call h5dwrite_f(track_dset, H5T_NATIVE_DOUBLE, coords, & write_dims, hdf5_err, file_space_id=track_dspace) - end if - !call h5sselect_hyperslab_f(track_dspace, H5S_SELECT_SET_F, offset, dims, & - ! hdf5_err) - !i = 3 +#endif +#ifdef HDF5 + real(8) :: old_coords(3, n_tracks) + + old_coords = coords n_tracks = n_tracks + 1 - !dims = (/i, n_tracks/) - !call h5sset_extent_simple_f(track_dspace, 2, dims, max_dims, hdf5_err) - !call h5dset_extent_f(track_dset, dims, hdf5_err) + deallocate(coords) + allocate(coords(3, n_tracks)) + coords(:, 1:n_tracks-1) = old_coords + coords(:, n_tracks) = p % coord0 % xyz #else write(UNIT_TRACK) p % coord0 % xyz #endif @@ -112,24 +138,20 @@ contains !=============================================================================== ! FINALIZE_PARTICLE_TRACK closes the particle track file. -! -! TODO: This subroutine needs to be modified to work with HDF5 files. !=============================================================================== subroutine finalize_particle_track() - ! Replace the following with '#ifdef HDF5'. #if 0 - integer(HSIZE_T) :: dims(2), max_dims(2), i - - call h5sget_simple_extent_dims_f(track_dspace, dims, max_dims, hdf5_err) - !i = 3 - !dims = (/i, dims(2)-1/) - !call h5dextend_f(track_dset, dims, hdf5_err) - call h5dclose_f(track_dset, hdf5_err) call h5sclose_f(track_dspace, hdf5_err) call h5pclose_f(cparms, hdf5_err) call h5fclose_f(track_fh, hdf5_err) +#endif +#ifdef HDF5 + call file_create(fname, 'serial') + call write_data(coords, 'coordinates', length=(/3, n_tracks/)) + call file_close('serial') + deallocate(coords) #else close(UNIT=UNIT_TRACK) #endif diff --git a/src/utils/track.py b/src/utils/track.py index 1907d1ec0a..f0b1f23e5a 100755 --- a/src/utils/track.py +++ b/src/utils/track.py @@ -5,7 +5,7 @@ Usage information can be obtained by running 'track.py --help': usage: track.py [-h] [-o OUT] IN [IN ...] - Convert binary particle track file to a .pvtp file. + Convert particle track file to a .pvtp file. positional arguments: IN Input particle track data filename(s). @@ -26,9 +26,8 @@ import vtk def _parse_args(): # Create argument parser. parser = argparse.ArgumentParser( - description='Convert binary particle track file to a .pvtp file.') - parser.add_argument('input', metavar='IN', type=argparse.FileType('rb'), - nargs='+', + description='Convert particle track file to a .pvtp file.') + parser.add_argument('input', metavar='IN', type=str, nargs='+', help='Input particle track data filename(s).') parser.add_argument('-o', '-out', '--out', metavar='OUT', type=str, dest='out', @@ -39,8 +38,15 @@ def _parse_args(): def main(): - # Parse commandline argumetns. + # Parse commandline arguments. args = _parse_args() + + # Check input file extensions. + for fname in args.input: + if len(fname) > 3 and fname[-3:] == '.h5': continue + if len(fname) > 7 and fname[-7:] == '.binary': continue + raise ValueError("Input file names must either end with '.h5' or " + "'.binary'.") # Make sure that the output filename ends with '.pvtp'. if not args.out: @@ -48,19 +54,28 @@ def main(): elif os.path.splitext(args.out)[1] != '.pvtp': args.out = ''.join([args.out, '.pvtp']) + # Import HDF library if HDF files are present + for fname in args.input: + if fname[-3:] != '.h5': continue + import h5py + break + points = vtk.vtkPoints() cells = vtk.vtkCellArray() j = 0 k = 0 - for fin in args.input: - track = fin.read() - coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) - for i in range(len(track)/24)] - n_points = len(coords) - - for triplet in coords: - points.InsertNextPoint(triplet) - + for fname in args.input: + if len(fname) > 7 and fname[-7:] == '.binary': + track = open(fname, 'rb').read() + coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) + for i in range(len(track)/24)] + n_points = len(coords) + for triplet in coords: points.InsertNextPoint(triplet) + elif fname[-3:] == '.h5': + coords = h5py.File(fname).get('coordinates') + n_points = coords.shape[0] + for i in range(n_points): points.InsertNextPoint(coords[i,:]) + # Create VTK line and assign points to line. line = vtk.vtkPolyLine() line.GetPointIds().SetNumberOfIds(n_points) From 06c3bd3690140c4a7778f01df410c748e69f0619 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 9 Jul 2013 11:04:04 -0400 Subject: [PATCH 16/29] Switched to consistent use of output_interface --- src/particle_track.F90 | 159 ++++++++++++----------------------------- 1 file changed, 47 insertions(+), 112 deletions(-) diff --git a/src/particle_track.F90 b/src/particle_track.F90 index 852a07e100..c0d7894ce3 100644 --- a/src/particle_track.F90 +++ b/src/particle_track.F90 @@ -1,160 +1,95 @@ !=============================================================================== -! PARTICLE_TRACK handles output of particle tracks to disk. -! -! TODO: This module writes binary output files via a stream access i.e. it -! writes the particle's location to disk everytime write_particle_track is -! called. But HDF5 does not allow for a simple implimentation of stream access. -! An attempt was made to write HDF5 files in a stream fashion using an -! extendable dataset, but it always failed at the write call because of an out- -! of-bounds error. The extendable dataset code is commented out by enclosing it -! within "#if 0"/"#endif" blocks, and has been replaced by code that writes -! particle coordinates to an ever-expanding array which is written to disk in -! its entirety when finalize_particle_track is called. -! -! In the interest of consistency, maybe this module should be changed so that it -! uses the extendable HDF5 dataset, or it writes binary output all in one call -! using the output_interface (like the current HDF5 implimentation). +! PARTICLE_TRACK handles output of particle tracks (the paths taken by particles +! as they are transported through the geometry). !=============================================================================== module particle_track use constants use global - use string, only: to_str - -#if 0 - use hdf5 -#endif -#ifdef HDF5 use output_interface, only: file_create, file_close, write_data -#endif + use string, only: to_str implicit none -#if 0 - integer, private :: hdf5_err ! HDF error code - integer(HID_T), private :: track_dset ! dataset handle - integer(HID_T), private :: track_dspace ! dataspace handle - integer(HID_T), private :: track_fh ! HDF file handle - integer(HID_T), private :: cparms ! chunk parameters - integer(HSIZE_T), private :: n_tracks ! total number of tracks -#endif -#ifdef HDF5 - character(MAX_FILE_LEN), private :: fname ! file name integer, private :: n_tracks ! total number of tracks real(8), private, allocatable :: coords(:,:) ! track coordinates -#endif contains !=============================================================================== -! INITIALIZE_PARTICLE_TRACK opens a particle track output file. +! INITIALIZE_PARTICLE_TRACK !=============================================================================== subroutine initialize_particle_track() - character(MAX_FILE_LEN) :: filename -#if 0 - integer(HSIZE_T) :: dims(2), max_dims(2), i, j(1) - integer(HID_T) :: cparms - filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & - // '.h5' n_tracks = 0 - ! Create file. - call h5fcreate_f(filename, H5F_ACC_TRUNC_F, track_fh, hdf5_err) - ! Create a dataspace with an unlimited max dimension. - dims = (/3, 1/) - i = 3 - max_dims = (/i, H5S_UNLIMITED_F/) - call h5screate_simple_f(2, dims, track_dspace, hdf5_err, max_dims) - ! Set dataspace chunking. - call h5pcreate_f(H5P_DATASET_CREATE_F, cparms, hdf5_err) - call h5pset_chunk_f(cparms, 2, dims, hdf5_err) - ! Set fill value. - call h5pset_fill_value_f(cparms, H5T_NATIVE_DOUBLE, 0, hdf5_err) - ! Create dataset. - call h5dcreate_f(track_fh, 'coordinates', H5T_NATIVE_DOUBLE, track_dspace, & - track_dset, hdf5_err, cparms) -#endif -#ifdef HDF5 - filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & - // '.h5' - n_tracks = 0 - fname = filename - allocate(coords(3,1)) -#else - filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & - // '.binary' - open(UNIT=UNIT_TRACK, FILE=filename, ACTION="write", & - STATUS='replace', ACCESS='stream') -#endif + allocate(coords(1,1)) end subroutine initialize_particle_track !=============================================================================== -! WRITE_PARTICLE_TRACK outputs particle position to a binary file. +! WRITE_PARTICLE_TRACK copies particle position to an array. !=============================================================================== subroutine write_particle_track() -#if 0 - integer(HSIZE_T) :: dims(2), max_dims(2), offset(2), i - integer(HSIZE_T), parameter :: cnt(2) = (/3, 1/), write_dims(2) = (/3, 1/) - real(8) :: coords(3) - - ! There is another set of track coordinates. Incriment the counter. - n_tracks = n_tracks + 1 - ! Make the dataset the right size to fit the track coordinates. - i = 3 - dims = (/i, n_tracks/) - call h5dset_extent_f(track_dset, dims, hdf5_err) - ! Get the dataspace with the updated dimensions. - call h5dget_space_f(track_dset, track_dspace, hdf5_err) - ! Select the hyperslab where the latest coordinates will be written. - i = 1 - offset = (/i, n_tracks/) - if (n_tracks < 20) then - call h5sselect_hyperslab_f(track_dspace, H5S_SELECT_SET_F, offset, cnt, & - hdf5_err) - endif - ! Write the coordinates to the dataset. - coords = p % coord0 % xyz - call h5dwrite_f(track_dset, H5T_NATIVE_DOUBLE, coords, & - write_dims, hdf5_err, file_space_id=track_dspace) -#endif -#ifdef HDF5 real(8) :: old_coords(3, n_tracks) + ! Save the coordinates gathered in previous calls. old_coords = coords + + ! Add another column to coords. n_tracks = n_tracks + 1 deallocate(coords) allocate(coords(3, n_tracks)) + + ! Put the old coordinates back into the array. coords(:, 1:n_tracks-1) = old_coords + + ! Write current coordinates into the newest column. coords(:, n_tracks) = p % coord0 % xyz -#else - write(UNIT_TRACK) p % coord0 % xyz -#endif end subroutine write_particle_track !=============================================================================== -! FINALIZE_PARTICLE_TRACK closes the particle track file. +! FINALIZE_PARTICLE_TRACK writes the particle track array to disk. +! +! output_interface currently does not support writing 2D binary arrays so there +! are two different versions of this subroutine; an HDF version which simply +! writes the array and a binary version which flattens the array into a 1D shape +! before writing. !=============================================================================== - subroutine finalize_particle_track() -#if 0 - call h5dclose_f(track_dset, hdf5_err) - call h5sclose_f(track_dspace, hdf5_err) - call h5pclose_f(cparms, hdf5_err) - call h5fclose_f(track_fh, hdf5_err) -#endif #ifdef HDF5 + + subroutine finalize_particle_track() + character(MAX_FILE_LEN) :: fname + + fname = trim(path_output) // 'track_' // trim(to_str(current_batch)) & + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & + // '.h5' call file_create(fname, 'serial') call write_data(coords, 'coordinates', length=(/3, n_tracks/)) call file_close('serial') deallocate(coords) -#else - close(UNIT=UNIT_TRACK) -#endif end subroutine finalize_particle_track +#else + + subroutine finalize_particle_track() + character(MAX_FILE_LEN) :: fname + integer :: i + real(8) :: flat_coords(3*n_tracks) + + fname = trim(path_output) // 'track_' // trim(to_str(current_batch)) & + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & + // '.binary' + call file_create(fname, 'serial') + do i=1, n_tracks + flat_coords(3*i-2 : 3*i) = coords(:,i) + end do + call write_data(flat_coords, 'coordinates', length=3*n_tracks) + call file_close('serial') + deallocate(coords) + end subroutine finalize_particle_track + +#endif + end module particle_track From f945e20dcb12b4a89c1fd74a8f4cc50dea654894 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 21 Sep 2013 16:50:44 -0400 Subject: [PATCH 17/29] Changed 'write_track' from global var to attribute --- src/DEPENDENCIES | 2 +- src/constants.F90 | 1 - src/global.F90 | 1 - src/particle_header.F90 | 3 +++ src/particle_track.F90 | 1 - src/source.F90 | 6 +++--- src/tracking.F90 | 6 +++--- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 933dabb93d..0c23afa917 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -372,9 +372,9 @@ output.o: plot_header.o output.o: string.o output.o: tally_header.o -particle_track.o: constants.o particle_track.o: global.o particle_track.o: output_interface.o +particle_track.o: particle_header.o particle_track.o: string.o physics.o: ace_header.o diff --git a/src/constants.F90 b/src/constants.F90 index ef7625b3b6..46ae8e9c1a 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -365,7 +365,6 @@ module constants integer, parameter :: CMFD_BALANCE = 15 ! unit # for writing cmfd balance file integer, parameter :: UNIT_PARTICLE = 16 ! unit # for writing particle restart integer, parameter :: UNIT_OUTPUT = 17 ! unit # for writing output - integer, parameter :: UNIT_TRACK = 18 ! unit # for writing particle tracks !============================================================================= ! CMFD CONSTANTS diff --git a/src/global.F90 b/src/global.F90 index b15fabfb13..f803d4c2d3 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -277,7 +277,6 @@ module global integer(8) :: trace_particle ! Particle tracks - logical :: write_track = .false. logical :: write_all_tracks = .false. integer, allocatable :: track_identifiers(:,:) diff --git a/src/particle_header.F90 b/src/particle_header.F90 index 201de518eb..b9762960ed 100644 --- a/src/particle_header.F90 +++ b/src/particle_header.F90 @@ -76,6 +76,9 @@ module particle_header ! Statistical data integer :: n_collision ! # of collisions + ! Track output + logical :: write_track + contains procedure :: initialize => initialize_particle procedure :: clear => clear_particle diff --git a/src/particle_track.F90 b/src/particle_track.F90 index 5bc392b0f0..4053dd1d77 100644 --- a/src/particle_track.F90 +++ b/src/particle_track.F90 @@ -5,7 +5,6 @@ module particle_track - use constants use global use output_interface, only: BinaryOutput use particle_header, only: Particle diff --git a/src/source.F90 b/src/source.F90 index 25e1553a81..bbf86d4ada 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -178,15 +178,15 @@ contains p % id == trace_particle) trace = .true. ! Set particle track. - write_track = .false. + p % write_track = .false. if (write_all_tracks) then - write_track = .true. + p % write_track = .true. else if (allocated(track_identifiers)) then do i=1, size(track_identifiers(1,:)) if (current_batch == track_identifiers(1,i) .and. & ¤t_gen == track_identifiers(2,i) .and. & &p % id == track_identifiers(3,i)) then - write_track = .true. + p % write_track = .true. exit end if end do diff --git a/src/tracking.F90 b/src/tracking.F90 index 2c535e290f..4a22e8ccad 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -67,14 +67,14 @@ contains micro_xs % last_E = ZERO ! Prepare to write out particle track. - if (write_track) then + if (p % write_track) then call initialize_particle_track() endif do while (p % alive) ! Write particle track. - if (write_track) call write_particle_track(p) + if (p % write_track) call write_particle_track(p) if (check_overlaps) call check_cell_overlap(p) @@ -200,7 +200,7 @@ contains end do ! Finish particle track output. - if (write_track) then + if (p % write_track) then call write_particle_track(p) call finalize_particle_track(p) endif From ca0ace41cbc6b25aff6f1b05545d7853f4743067 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 21 Sep 2013 17:32:53 -0400 Subject: [PATCH 18/29] Moved 'particle_track' to 'track_output' --- src/DEPENDENCIES | 10 +++--- src/{particle_track.F90 => track_output.F90} | 37 ++++---------------- src/tracking.F90 | 4 +-- 3 files changed, 13 insertions(+), 38 deletions(-) rename src/{particle_track.F90 => track_output.F90} (72%) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 0c23afa917..2ece570ae6 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -310,7 +310,7 @@ tracking.o: geometry_header.o tracking.o: global.o tracking.o: output.o tracking.o: particle_header.o -tracking.o: particle_track.o +tracking.o: track_output.o tracking.o: physics.o tracking.o: random_lcg.o tracking.o: string.o @@ -372,10 +372,10 @@ output.o: plot_header.o output.o: string.o output.o: tally_header.o -particle_track.o: global.o -particle_track.o: output_interface.o -particle_track.o: particle_header.o -particle_track.o: string.o +track_output.o: global.o +track_output.o: output_interface.o +track_output.o: particle_header.o +track_output.o: string.o physics.o: ace_header.o physics.o: constants.o diff --git a/src/particle_track.F90 b/src/track_output.F90 similarity index 72% rename from src/particle_track.F90 rename to src/track_output.F90 index 4053dd1d77..5681a4de48 100644 --- a/src/particle_track.F90 +++ b/src/track_output.F90 @@ -1,9 +1,9 @@ !=============================================================================== -! PARTICLE_TRACK handles output of particle tracks (the paths taken by particles +! TRACK_OUTPUT handles output of particle tracks (the paths taken by particles ! as they are transported through the geometry). !=============================================================================== -module particle_track +module track_output use global use output_interface, only: BinaryOutput @@ -52,52 +52,27 @@ contains !=============================================================================== ! FINALIZE_PARTICLE_TRACK writes the particle track array to disk. -! -! output_interface currently does not support writing 2D binary arrays so there -! are two different versions of this subroutine; an HDF version which simply -! writes the array and a binary version which flattens the array into a 1D shape -! before writing. !=============================================================================== -#ifdef HDF5 - subroutine finalize_particle_track(p) type(Particle), intent(in) :: p character(MAX_FILE_LEN) :: fname type(BinaryOutput) :: binout +#ifdef HDF5 fname = trim(path_output) // 'track_' // trim(to_str(current_batch)) & // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & // '.h5' - call file_create(fname) - call write_data(coords, 'coordinates', length=(/3, n_tracks/)) - call file_close() - deallocate(coords) - end subroutine finalize_particle_track - #else - - subroutine finalize_particle_track(p) - type(Particle), intent(in) :: p - - character(MAX_FILE_LEN) :: fname - type(BinaryOutput) :: binout - integer :: i - real(8) :: flat_coords(3*n_tracks) - fname = trim(path_output) // 'track_' // trim(to_str(current_batch)) & // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & // '.binary' +#endif call binout % file_create(fname) - do i=1, n_tracks - flat_coords(3*i-2 : 3*i) = coords(:,i) - end do - call binout % write_data(flat_coords, 'coordinates', length=3*n_tracks) + call binout % write_data(coords, 'coordinates', length=(/3, n_tracks/)) call binout % file_close() deallocate(coords) end subroutine finalize_particle_track -#endif - -end module particle_track +end module track_output diff --git a/src/tracking.F90 b/src/tracking.F90 index 4a22e8ccad..e8d3f7b1a4 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -8,13 +8,13 @@ module tracking use global use output, only: write_message use particle_header, only: LocalCoord, Particle - use particle_track, only: initialize_particle_track, write_particle_track, & - finalize_particle_track use physics, only: collision use random_lcg, only: prn use string, only: to_str use tally, only: score_analog_tally, score_tracklength_tally, & score_surface_current + use track_output, only: initialize_particle_track, write_particle_track, & + finalize_particle_track contains From be6a8d22ecb1c37151c86b380b84318848b7c15d Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 2 Oct 2013 21:21:05 -0400 Subject: [PATCH 19/29] Improved track.py readability and PEP 8 compliance --- src/utils/track.py | 68 ++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/src/utils/track.py b/src/utils/track.py index f0b1f23e5a..5fe20ce537 100755 --- a/src/utils/track.py +++ b/src/utils/track.py @@ -8,12 +8,11 @@ Usage information can be obtained by running 'track.py --help': Convert particle track file to a .pvtp file. positional arguments: - IN Input particle track data filename(s). + IN Input particle track data filename(s). optional arguments: - -h, --help show this help message and exit - -o OUT, -out OUT, --out OUT - Output VTK poly data filename. + -h, --help show this help message and exit + -o OUT, --out OUT Output VTK poly data filename. """ @@ -29,8 +28,7 @@ def _parse_args(): description='Convert particle track file to a .pvtp file.') parser.add_argument('input', metavar='IN', type=str, nargs='+', help='Input particle track data filename(s).') - parser.add_argument('-o', '-out', '--out', metavar='OUT', type=str, - dest='out', + parser.add_argument('-o', '--out', metavar='OUT', type=str, dest='out', help='Output VTK poly data filename.') # Parse and return commandline arguments. @@ -43,10 +41,9 @@ def main(): # Check input file extensions. for fname in args.input: - if len(fname) > 3 and fname[-3:] == '.h5': continue - if len(fname) > 7 and fname[-7:] == '.binary': continue - raise ValueError("Input file names must either end with '.h5' or " - "'.binary'.") + if not (fname.endswith('.h5') or fname.endswith('.binary')): + raise ValueError("Input file names must either end iwth '.h5' or" + "'.binary'.") # Make sure that the output filename ends with '.pvtp'. if not args.out: @@ -56,36 +53,37 @@ def main(): # Import HDF library if HDF files are present for fname in args.input: - if fname[-3:] != '.h5': continue - import h5py - break + if fname.endswith('.h5'): + import h5py + break + # Initialize data arrays and offset. points = vtk.vtkPoints() cells = vtk.vtkCellArray() - j = 0 - k = 0 + point_offset = 0 for fname in args.input: - if len(fname) > 7 and fname[-7:] == '.binary': + # Write coordinate values to points array. + if fname.endswith('.binary'): track = open(fname, 'rb').read() coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) for i in range(len(track)/24)] n_points = len(coords) - for triplet in coords: points.InsertNextPoint(triplet) - elif fname[-3:] == '.h5': + for triplet in coords: + points.InsertNextPoint(triplet) + else: coords = h5py.File(fname).get('coordinates') n_points = coords.shape[0] - for i in range(n_points): points.InsertNextPoint(coords[i,:]) + for i in range(n_points): + points.InsertNextPoint(coords[i,:]) # Create VTK line and assign points to line. line = vtk.vtkPolyLine() line.GetPointIds().SetNumberOfIds(n_points) for i in range(n_points): - line.GetPointIds().SetId(i, j+i) + line.GetPointIds().SetId(i, point_offset+i) cells.InsertNextCell(line) - j += n_points - k += 1 - global data + point_offset += n_points data = vtk.vtkPolyData() data.SetPoints(points) data.SetLines(cells) @@ -99,27 +97,3 @@ def main(): if __name__ == '__main__': main() - - -#### The following code is retained for debugging purposes: - -##poly_mapper = vtk.vtkPolyDataMapper() -##poly_mapper.SetInputConnection(data.GetProducerPort()) -## -##poly_actor = vtk.vtkActor() -##poly_actor.SetMapper(poly_mapper) -## -##ren1 = vtk.vtkRenderer() -##ren1.AddActor(poly_actor) -##ren1.SetBackground(0.0, 0.0, 0.0) -## -##ren1.ResetCamera() -## -##ren_win = vtk.vtkRenderWindow() -##ren_win.AddRenderer(ren1) -##ren_win.SetSize(400, 400) -## -##iren = vtk.vtkRenderWindowInteractor() -##iren.SetRenderWindow(ren_win) -##iren.Initialize() -##iren.Start() From beb39a44bbaf9a21f6c9534fc86378a1de1057b5 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 4 Oct 2013 10:17:30 -0400 Subject: [PATCH 20/29] Use move_alloc to expand array --- src/track_output.F90 | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/track_output.F90 b/src/track_output.F90 index 5681a4de48..4b02dfe9a2 100644 --- a/src/track_output.F90 +++ b/src/track_output.F90 @@ -33,18 +33,13 @@ contains subroutine write_particle_track(p) type(Particle), intent(in) :: p - real(8) :: old_coords(3, n_tracks) - - ! Save the coordinates gathered in previous calls. - old_coords = coords + real(8), allocatable :: new_coords(:, :) ! Add another column to coords. n_tracks = n_tracks + 1 - deallocate(coords) - allocate(coords(3, n_tracks)) - - ! Put the old coordinates back into the array. - coords(:, 1:n_tracks-1) = old_coords + allocate(new_coords(3, n_tracks)) + new_coords(:, 1:n_tracks-1) = coords + call move_alloc(FROM=new_coords, TO=coords) ! Write current coordinates into the newest column. coords(:, n_tracks) = p % coord0 % xyz From bfc172f26485487bd780a37d233d0f4961df4bca Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 10 Oct 2013 00:02:26 -0400 Subject: [PATCH 21/29] Added track output test. Also fixed a typo in track.py --- src/utils/track.py | 2 +- tests/test_track_output/geometry.xml | 305 +++++++++++++++++++ tests/test_track_output/materials.xml | 95 ++++++ tests/test_track_output/settings.xml | 30 ++ tests/test_track_output/test_track_output.py | 80 +++++ tests/test_track_output/true_poly.pvtp | 9 + 6 files changed, 520 insertions(+), 1 deletion(-) create mode 100644 tests/test_track_output/geometry.xml create mode 100644 tests/test_track_output/materials.xml create mode 100644 tests/test_track_output/settings.xml create mode 100644 tests/test_track_output/test_track_output.py create mode 100644 tests/test_track_output/true_poly.pvtp diff --git a/src/utils/track.py b/src/utils/track.py index 5fe20ce537..a2dca34e14 100755 --- a/src/utils/track.py +++ b/src/utils/track.py @@ -42,7 +42,7 @@ def main(): # Check input file extensions. for fname in args.input: if not (fname.endswith('.h5') or fname.endswith('.binary')): - raise ValueError("Input file names must either end iwth '.h5' or" + raise ValueError("Input file names must either end with '.h5' or" "'.binary'.") # Make sure that the output filename ends with '.pvtp'. diff --git a/tests/test_track_output/geometry.xml b/tests/test_track_output/geometry.xml new file mode 100644 index 0000000000..f3566ab17f --- /dev/null +++ b/tests/test_track_output/geometry.xml @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 + 2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 + 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 + 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 + 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 + 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 3 2 2 2 2 2 2 2 1 2 2 2 + 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + + + + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 + 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 + 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 + + + + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 + 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + + + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + + + + + + + + + + -12.2682 -12.2682 + 1.63576 1.63576 + + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + + + + + + + + + + + + + + + + + -85.8774 -85.8774 + 24.5364 24.5364 + + 999 999 130 140 150 999 999 + 999 220 230 240 250 260 999 + 130 320 777 222 333 360 150 + 410 240 444 111 666 240 470 + 510 520 888 555 998 560 570 + 999 620 630 240 650 660 999 + 999 999 510 740 570 999 999 + + + + + + + + diff --git a/tests/test_track_output/materials.xml b/tests/test_track_output/materials.xml new file mode 100644 index 0000000000..7befbddf54 --- /dev/null +++ b/tests/test_track_output/materials.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_track_output/settings.xml b/tests/test_track_output/settings.xml new file mode 100644 index 0000000000..420bc5949b --- /dev/null +++ b/tests/test_track_output/settings.xml @@ -0,0 +1,30 @@ + + + + + + + + + + 8 + 1 + 1000 + + + + + + + + + -1 -1 -1 1 1 1 + + + + + 1 1 1 + 1 1 2 + + + diff --git a/tests/test_track_output/test_track_output.py b/tests/test_track_output/test_track_output.py new file mode 100644 index 0000000000..d652d97e3f --- /dev/null +++ b/tests/test_track_output/test_track_output.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +import os +from subprocess import Popen, STDOUT, PIPE, call +import filecmp +from nose_mpi import NoseMPI +import glob + +pwd = os.path.dirname(__file__) + +def setup(): + os.putenv('PWD', pwd) + os.chdir(pwd) + +def test_run(): + openmc_path = pwd + '/../../src/openmc' + if int(NoseMPI.mpi_np) > 0: + proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path], + stderr=STDOUT, stdout=PIPE) + else: + proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) + returncode = proc.wait() + print(proc.communicate()[0]) + assert returncode == 0 + +def test_created_outputs(): + outputs = [glob.glob(''.join((pwd, '/track_1_1_1.*')))] + outputs.append(glob.glob(''.join((pwd, '/track_1_1_2.*')))) + for files in outputs: + assert len(files) == 1 + assert files[0].endswith('binary') or files[0].endswith('h5') + +##def test_outputs(): +## outputs = [glob.glob(''.join((pwd, '/track_1_1_1.*')))[0]] +## outputs.append(glob.glob(''.join((pwd, '/track_1_1_2.*')))[0]) +## if outputs[0].endswith('binary'): +## metrics = [''.join((pwd, '/track_1_1_1_true.binary')), +## ''.join((pwd, '/track_1_1_2_true.binary'))] +## else: +## metrics = [''.join((pwd, '/track_1_1_1_true.h5')), +## ''.join((pwd, '/track_1_1_2_true.h5'))] +## for i in range(2): +## output = outputs[i] +## metric = metrics[i] +## compare = filecmp.cmp(output, metric) +## if not compare: +## if outputs[0].endswith('binary'): +## extension = 'binary' +## else: +## extension = 'h5' +## os.rename(output, ''.join((pwd, '/track_1_1_{}_error.{}'.format( +## i+1, extension)))) +## assert compare + +def test_outputs(): + call(['../../src/utils/track.py', '-o', 'poly'] + + glob.glob(''.join((pwd, '/track*')))) + poly = ''.join((pwd, '/poly.pvtp')) + assert os.path.isfile(poly) + metric = ''.join((pwd, '/true_poly.pvtp')) + compare = filecmp.cmp(poly, metric) + if not compare: + os.rename('poly.pvtp', 'poly_error.pvtp') + assert compare + +##def test_results(): +## statepoint = glob.glob(pwd + '/statepoint.10.*') +## call(['python', 'results.py', statepoint[0]]) +## compare = filecmp.cmp('results_test.dat', 'results_true.dat') +## if not compare: +## os.rename('results_test.dat', 'results_error.dat') +## assert compare + +def teardown(): + temp_files = glob.glob(''.join((pwd, '/statepoint*'))) + temp_files = temp_files + glob.glob(''.join((pwd, '/track*'))) + temp_files = temp_files + glob.glob(''.join((pwd, '/poly*'))) + for f in temp_files: + if os.path.exists(f): + os.remove(f) diff --git a/tests/test_track_output/true_poly.pvtp b/tests/test_track_output/true_poly.pvtp new file mode 100644 index 0000000000..6ded87a0ec --- /dev/null +++ b/tests/test_track_output/true_poly.pvtp @@ -0,0 +1,9 @@ + + + + + + + + + From c1d5725d41f54dfa783cf07a93aac30610899b76 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 11 Oct 2013 03:18:46 -0400 Subject: [PATCH 22/29] Removed error file from teardown scope Also removed scaffold code --- tests/test_track_output/test_track_output.py | 32 +------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/tests/test_track_output/test_track_output.py b/tests/test_track_output/test_track_output.py index d652d97e3f..4600c89315 100644 --- a/tests/test_track_output/test_track_output.py +++ b/tests/test_track_output/test_track_output.py @@ -30,28 +30,6 @@ def test_created_outputs(): assert len(files) == 1 assert files[0].endswith('binary') or files[0].endswith('h5') -##def test_outputs(): -## outputs = [glob.glob(''.join((pwd, '/track_1_1_1.*')))[0]] -## outputs.append(glob.glob(''.join((pwd, '/track_1_1_2.*')))[0]) -## if outputs[0].endswith('binary'): -## metrics = [''.join((pwd, '/track_1_1_1_true.binary')), -## ''.join((pwd, '/track_1_1_2_true.binary'))] -## else: -## metrics = [''.join((pwd, '/track_1_1_1_true.h5')), -## ''.join((pwd, '/track_1_1_2_true.h5'))] -## for i in range(2): -## output = outputs[i] -## metric = metrics[i] -## compare = filecmp.cmp(output, metric) -## if not compare: -## if outputs[0].endswith('binary'): -## extension = 'binary' -## else: -## extension = 'h5' -## os.rename(output, ''.join((pwd, '/track_1_1_{}_error.{}'.format( -## i+1, extension)))) -## assert compare - def test_outputs(): call(['../../src/utils/track.py', '-o', 'poly'] + glob.glob(''.join((pwd, '/track*')))) @@ -60,17 +38,9 @@ def test_outputs(): metric = ''.join((pwd, '/true_poly.pvtp')) compare = filecmp.cmp(poly, metric) if not compare: - os.rename('poly.pvtp', 'poly_error.pvtp') + os.rename('poly.pvtp', 'error_poly.pvtp') assert compare -##def test_results(): -## statepoint = glob.glob(pwd + '/statepoint.10.*') -## call(['python', 'results.py', statepoint[0]]) -## compare = filecmp.cmp('results_test.dat', 'results_true.dat') -## if not compare: -## os.rename('results_test.dat', 'results_error.dat') -## assert compare - def teardown(): temp_files = glob.glob(''.join((pwd, '/statepoint*'))) temp_files = temp_files + glob.glob(''.join((pwd, '/track*'))) From c87291ab9238b45c6cad2080847f9b8de0e81f5c Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 11 Oct 2013 03:52:23 -0400 Subject: [PATCH 23/29] Added documentation. --- docs/source/usersguide/install.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 159c689e98..527ec6b302 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -342,6 +342,7 @@ OpenMC accepts the following command line flags: -r, --restart file Restart a previous run from a state point or a particle restart file -s, --threads N Run with *N* OpenMP threads +-t, --track Write tracks for all particles -v, --version Show version information ----------------------------------------------------- From b4bc149737930471b9f6c9cf9bd1f4bcfca490dd Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 11 Oct 2013 03:57:29 -0400 Subject: [PATCH 24/29] Fixed a merge error --- src/source.F90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/source.F90 b/src/source.F90 index c3e706b7e3..2082ad6dda 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -157,6 +157,7 @@ contains integer(8) :: particle_seed ! unique index for particle integer :: i type(Bank), pointer, save :: src => null() +!$omp threadprivate(src) ! set defaults call p % initialize() From 4fc6643e0ac9175ed9fa063194f80e3305b784d0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 7 Nov 2013 10:04:27 +0100 Subject: [PATCH 25/29] Fix --track in man page. --- man/man1/openmc.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/man/man1/openmc.1 b/man/man1/openmc.1 index 64e97ce21c..cac8d59087 100644 --- a/man/man1/openmc.1 +++ b/man/man1/openmc.1 @@ -26,6 +26,7 @@ Restart a previous run from a state point or a particle restart file named .TP .BI \-s " N" "\fR,\fP \-\-threads" " N" Use \fIN\fP OpenMP threads. +.TP .B "\-t\fR, \fP\-\-track" Write tracks for all particles. .TP From 5b11dc668701e1808ea5c4344a1f09a3e6448887 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 7 Nov 2013 10:04:51 +0100 Subject: [PATCH 26/29] Add line breaks in processing.rst. --- docs/source/usersguide/processing.rst | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index c12041214a..780fe37938 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -360,11 +360,19 @@ Particle Track Visualization .. image:: ../../img/Tracks.png :height: 200px -OpenMC can dump particle tracks—the position of particles as they are transported through the geometry. There are two ways to make OpenMC output tracks: all particle tracks through a commandline argument or specific particle tracks through settings.xml. +OpenMC can dump particle tracks—the position of particles as they are +transported through the geometry. There are two ways to make OpenMC output +tracks: all particle tracks through a commandline argument or specific particle +tracks through settings.xml. -Running OpenMC with the argument "-t", "-track", or "--track" will cause a track file to be created for every particle transported in the code. +Running OpenMC with the argument "-t", "-track", or "--track" will cause a track +file to be created for every particle transported in the code. -The settings.xml file can dictate that specific particle tracks are output. These particles are specified withen a ''track'' element. The ''track'' element should contain triplets of integers specifying the batch, generation, and particle numbers, respectively. For example, to output the tracks for particles 3 and 4 of batch 1 and generation 2 the settings.xml file should contain: +The settings.xml file can dictate that specific particle tracks are output. +These particles are specified withen a ''track'' element. The ''track'' element +should contain triplets of integers specifying the batch, generation, and +particle numbers, respectively. For example, to output the tracks for particles +3 and 4 of batch 1 and generation 2 the settings.xml file should contain: .. code-block:: xml @@ -373,9 +381,13 @@ The settings.xml file can dictate that specific particle tracks are output. The 1 2 4 -After running OpenMC, the directory should contain a file of the form "track_(batch #)_(generation #)_(particle #).(binary or h5)" for each particle tracked. These track files can be converted into VTK poly data files with the "track.py" utility. The usage of track.py is of the form "track.py [-o OUT] IN" where OUT is the optional output filename and IN is one or more filenames describing track files. The default output name is "track.pvtp". A common usage of track.py is "track.py track*.binary" which will use the data from all binary track files in the directory to write a "track.pvtp" VTK output file. The .pvtp file can then be read and plotted by 3d visualization programs such as Paraview. - - - - - +After running OpenMC, the directory should contain a file of the form +"track_(batch #)_(generation #)_(particle #).(binary or h5)" for each particle +tracked. These track files can be converted into VTK poly data files with the +"track.py" utility. The usage of track.py is of the form "track.py [-o OUT] IN" +where OUT is the optional output filename and IN is one or more filenames +describing track files. The default output name is "track.pvtp". A common +usage of track.py is "track.py track*.binary" which will use the data from all +binary track files in the directory to write a "track.pvtp" VTK output file. +The .pvtp file can then be read and plotted by 3d visualization programs such as +Paraview. From 19c52d915a30585ae461b76310227ff18e508a8b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 7 Nov 2013 10:05:39 +0100 Subject: [PATCH 27/29] Fix material in test_track_output. --- tests/test_track_output/materials.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_track_output/materials.xml b/tests/test_track_output/materials.xml index 7befbddf54..bbf880d960 100644 --- a/tests/test_track_output/materials.xml +++ b/tests/test_track_output/materials.xml @@ -8,7 +8,7 @@ - + From 727451319d6313d27480d333cbe626b0230ca71a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 7 Nov 2013 10:17:51 +0100 Subject: [PATCH 28/29] Skip one test in test_track_output if vtk Python module is not available. --- tests/test_track_output/test_track_output.py | 21 +++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/test_track_output/test_track_output.py b/tests/test_track_output/test_track_output.py index 4600c89315..0e7674154c 100644 --- a/tests/test_track_output/test_track_output.py +++ b/tests/test_track_output/test_track_output.py @@ -3,26 +3,32 @@ import os from subprocess import Popen, STDOUT, PIPE, call import filecmp -from nose_mpi import NoseMPI import glob +from nose.plugins.skip import SkipTest + +from nose_mpi import NoseMPI + pwd = os.path.dirname(__file__) -def setup(): + +def setup(): os.putenv('PWD', pwd) os.chdir(pwd) + def test_run(): openmc_path = pwd + '/../../src/openmc' if int(NoseMPI.mpi_np) > 0: proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path], - stderr=STDOUT, stdout=PIPE) + stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) returncode = proc.wait() print(proc.communicate()[0]) assert returncode == 0 + def test_created_outputs(): outputs = [glob.glob(''.join((pwd, '/track_1_1_1.*')))] outputs.append(glob.glob(''.join((pwd, '/track_1_1_2.*')))) @@ -30,7 +36,15 @@ def test_created_outputs(): assert len(files) == 1 assert files[0].endswith('binary') or files[0].endswith('h5') + def test_outputs(): + # If vtk python module is not available, we can't run track.py so skip this + # test + try: + import vtk + except ImportError: + raise SkipTest + call(['../../src/utils/track.py', '-o', 'poly'] + glob.glob(''.join((pwd, '/track*')))) poly = ''.join((pwd, '/poly.pvtp')) @@ -41,6 +55,7 @@ def test_outputs(): os.rename('poly.pvtp', 'error_poly.pvtp') assert compare + def teardown(): temp_files = glob.glob(''.join((pwd, '/statepoint*'))) temp_files = temp_files + glob.glob(''.join((pwd, '/track*'))) From b9bd67d2cd61fe13e2081df8f2db0c904f3c35a8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 7 Nov 2013 10:23:15 +0100 Subject: [PATCH 29/29] Run fewer particles for test_track_output. --- tests/test_track_output/settings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_track_output/settings.xml b/tests/test_track_output/settings.xml index 420bc5949b..ef74341dbb 100644 --- a/tests/test_track_output/settings.xml +++ b/tests/test_track_output/settings.xml @@ -7,9 +7,9 @@ - 8 - 1 - 1000 + 2 + 0 + 100