From 8df7694b828542b9f74aae0e09f3875cba50825c Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 8 Jun 2013 14:28:21 -0400 Subject: [PATCH 001/192] 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 002/192] 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 003/192] 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 004/192] 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 005/192] 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 006/192] 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 007/192] 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 008/192] 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 009/192] 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 010/192] 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 011/192] 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 012/192] 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 013/192] 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 014/192] 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 34a84688ed5492651efdddfc6e0a2d087ecc137e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 8 Jul 2013 08:24:15 -0400 Subject: [PATCH 015/192] added matrix header file --- src/OBJECTS | 1 + src/matrix_header.F90 | 121 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/matrix_header.F90 diff --git a/src/OBJECTS b/src/OBJECTS index b079906751..4fbaf80a76 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -37,6 +37,7 @@ list_header.o \ main.o \ material_header.o \ math.o \ +matrix_header.o \ mesh_header.o \ mesh.o \ mpiio_interface.o \ diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 new file mode 100644 index 0000000000..a2ca46f7e4 --- /dev/null +++ b/src/matrix_header.F90 @@ -0,0 +1,121 @@ +module matrix_header + + implicit none + private + +# ifdef PETSC +# include +# endif + + type, public :: matrix + integer :: n ! number of rows/cols in matrix + integer :: nz ! number of nonzeros in matrix + integer :: n_kount ! counter for length of matrix + integer :: nz_kount ! counter for number of non zeros + integer, allocatable :: row(:) ! csr row vector + integer, allocatable :: col(:) ! column vector + real(8), allocatable :: val(:) ! matrix value vector +# ifdef PETSC + Mat :: petsc_mat +# endif + contains + procedure :: create => matrix_create + procedure :: destroy => matrix_destroy + procedure :: add_value => matrix_add_value + procedure :: new_row => matrix_new_row +# ifdef PETSC + procedure :: setup_petsc => matrix_setup_petsc +# endif + end type matrix + +contains + +!=============================================================================== +! MATRIX_CREATE allocates CSR vectors +!=============================================================================== + + subroutine matrix_create(self, n, nz) + + integer :: n + integer :: nz + class(matrix) :: self + + ! preallocate vectors + if (.not.allocated(self % row)) allocate(self % row(n+1)) + if (.not.allocated(self % col)) allocate(self % col(nz)) + if (.not.allocated(self % val)) allocate(self % val(nz)) + + ! set counters to 1 + self % n_kount = 1 + self % nz_kount = 1 + + end subroutine matrix_create + +!=============================================================================== +! MATRIX_DESTROY deallocates all space associated with a matrix +!=============================================================================== + + subroutine matrix_destroy(self) + + class(matrix) :: self + + if (allocated(self % row)) deallocate(self % row) + if (allocated(self % col)) deallocate(self % col) + if (allocated(self % val)) deallocate(self % val) + + end subroutine matrix_destroy + +!=============================================================================== +! MATRIX_ADD_VALUE adds a value to the matrix +!=============================================================================== + + subroutine matrix_add_value(self, col, val) + + integer :: col + real(8) :: val + class(matrix) :: self + + self % col(self % nz_kount) = col + self % val(self % nz_kount) = val + self % nz_kount = self % nz_kount + 1 + + end subroutine matrix_add_value + +!=============================================================================== +! MATRIX_NEW_ROW adds a new row by saving the column position of the new row +!=============================================================================== + + subroutine matrix_new_row(self) + + class(matrix) :: self + + self % row(self % n_kount) = self % nz_kount + self % n_kount = self % n_kount + 1 + + end subroutine matrix_new_row + +# ifdef PETSC + +!=============================================================================== +! MATRIX_USE_PETSC +!=============================================================================== + + subroutine matrix_use_petsc(self) + + class(matrix) :: self + + integer :: petsc_err + + ! change indices to c notation + self % row = self % row - 1 + self % col = self % col - 1 + + ! link to petsc + call MatCreateSeqAIJWithArrays(PETSC_COMM_WORLD, self % n, self % n, & + self % row, self % col, self % val, loss % petsc_mat, petsc_err) + + end subroutine matrix_use_petsc + +# endif + +end module matrix_header From 8d23b2355841dd26eb6f92cb261dbac2cf0a39ec Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 8 Jul 2013 11:04:28 -0400 Subject: [PATCH 016/192] fixed incorrect naming in matrix header and added petsc solver module --- src/OBJECTS | 1 + src/matrix_header.F90 | 8 ++-- src/petsc_solver.F90 | 97 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 src/petsc_solver.F90 diff --git a/src/OBJECTS b/src/OBJECTS index 4fbaf80a76..030679cca6 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -46,6 +46,7 @@ output_interface.o \ particle_header.o \ particle_restart.o \ particle_restart_write.o \ +petsc_solver.o \ physics.o \ plot.o \ plot_header.o \ diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index a2ca46f7e4..2b53facd96 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -97,10 +97,10 @@ contains # ifdef PETSC !=============================================================================== -! MATRIX_USE_PETSC +! MATRIX_SETUP_PETSC !=============================================================================== - subroutine matrix_use_petsc(self) + subroutine matrix_setup_petsc(self) class(matrix) :: self @@ -112,9 +112,9 @@ contains ! link to petsc call MatCreateSeqAIJWithArrays(PETSC_COMM_WORLD, self % n, self % n, & - self % row, self % col, self % val, loss % petsc_mat, petsc_err) + self % row, self % col, self % val, self % petsc_mat, petsc_err) - end subroutine matrix_use_petsc + end subroutine matrix_setup_petsc # endif diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 new file mode 100644 index 0000000000..c9136e4c70 --- /dev/null +++ b/src/petsc_solver.F90 @@ -0,0 +1,97 @@ +module petsc_solver + + implicit none + private + +# ifdef PETSC +# include +# endif + + type, public :: gmres +# ifdef PETSC + KSP :: ksp + PC :: pc +# endif + contains + procedure :: create => gmres_create + procedure :: precondition => gmres_precondition + procedure :: set_oper => gmres_set_oper + procedure :: destroy => gmres_destroy + end type gmres + + integer :: petsc_err + +contains + +!=============================================================================== +! GMRES_CREATE +!=============================================================================== + + subroutine gmres_create(self) + + class(gmres) :: self + + integer :: ilu_levels = 5 + real(8) :: rtol = 1.0e-10_8 + real(8) :: atol = 1.0e-10_8 + +# ifdef PETSC + call KSPCreate(PETSC_COMM_WORLD, self % ksp, petsc_err) + call KSPSetTolerances(self % ksp, rtol, atol, & + PETSC_DEFAULT_DOUBLE_PRECISION, PETSC_DEFAULT_INTEGER, petsc_err) + call KSPSetType(self % ksp, KSPGMRES, petsc_err) + call KSPSetInitialGuessNonzero(self % ksp, PETSC_TRUE, petsc_err) + call KSPGetPC(self % ksp, self % pc, petsc_err) + call PCFactorSetLevels(self % pc, ilu_levels, petsc_err) + call KSPSetUp(self % ksp, petsc_err) +# endif + + end subroutine gmres_create + +!=============================================================================== +! GMRES_PRECONDITION +!=============================================================================== + + subroutine gmres_precondition(self, matrix) + + class(gmres) :: self + Mat :: matrix + +# ifdef PETSC + call PCFactorGetMatrix(self % pc, matrix, petsc_err) +# endif + + end subroutine gmres_precondition + +!=============================================================================== +! GMRES_SET_OPER +!=============================================================================== + + subroutine gmres_set_oper(self, prec_matrix, matrix) + + class(gmres) :: self + Mat :: prec_matrix + Mat :: matrix + +# ifdef PETSC + call KSPSetOperators(self % ksp, matrix, prec_matrix, & + SAME_NONZERO_PATTERN, petsc_err) +# endif + + end subroutine gmres_set_oper + +!=============================================================================== +! GMRES_DESTROY +!=============================================================================== + + subroutine gmres_destroy(self) + + class(gmres) :: self + +# ifdef PETSC + call KSPDestroy(self % ksp, petsc_err) +# endif + + end subroutine gmres_destroy + +end module petsc_solver From 5710cf6fa9e9279ecf3fd9d5b6a110b05245cbf5 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 8 Jul 2013 11:42:04 -0400 Subject: [PATCH 017/192] changed over loss operator to new notation and fixed up matrix header --- src/DEPENDENCIES | 556 +++++++++++++++++++------------------ src/cmfd_loss_operator.F90 | 320 ++++++++------------- src/matrix_header.F90 | 26 +- 3 files changed, 406 insertions(+), 496 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index e5d20644b0..973386c406 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -1,17 +1,49 @@ -ace.o: ace_header.o -ace.o: constants.o -ace.o: endf.o -ace.o: error.o -ace.o: fission.o -ace.o: global.o -ace.o: material_header.o -ace.o: output.o -ace.o: set_header.o -ace.o: string.o +set_header.o: constants.o +set_header.o: list_header.o + +energy_grid.o: constants.o +energy_grid.o: global.o +energy_grid.o: list_header.o +energy_grid.o: output.o + +cmfd_output.o: cmfd_data.o +cmfd_output.o: cmfd_header.o +cmfd_output.o: constants.o +cmfd_output.o: global.o + +list_header.o: constants.o ace_header.o: constants.o ace_header.o: endf_header.o +cmfd_loss_operator.o: constants.o +cmfd_loss_operator.o: global.o +cmfd_loss_operator.o: matrix_header.o + +particle_restart.o: bank_header.o +particle_restart.o: constants.o +particle_restart.o: geometry_header.o +particle_restart.o: global.o +particle_restart.o: hdf5_interface.o +particle_restart.o: output.o +particle_restart.o: particle_header.o +particle_restart.o: physics.o +particle_restart.o: random_lcg.o +particle_restart.o: source.o + +doppler.o: constants.o + +tally_header.o: constants.o + +cmfd_power_solver.o: cmfd_loss_operator.o +cmfd_power_solver.o: cmfd_prod_operator.o +cmfd_power_solver.o: constants.o +cmfd_power_solver.o: global.o +cmfd_power_solver.o: string.o + +cmfd_message_passing.o: cmfd_header.o +cmfd_message_passing.o: global.o + cmfd_data.o: cmfd_header.o cmfd_data.o: constants.o cmfd_data.o: error.o @@ -20,119 +52,17 @@ cmfd_data.o: mesh.o cmfd_data.o: mesh_header.o cmfd_data.o: tally_header.o -cmfd_execute.o: cmfd_data.o -cmfd_execute.o: cmfd_message_passing.o -cmfd_execute.o: cmfd_power_solver.o -cmfd_execute.o: cmfd_snes_solver.o -cmfd_execute.o: constants.o -cmfd_execute.o: error.o -cmfd_execute.o: global.o -cmfd_execute.o: mesh.o -cmfd_execute.o: mesh_header.o -cmfd_execute.o: output.o -cmfd_execute.o: search.o -cmfd_execute.o: tally.o - -cmfd_header.o: constants.o - -cmfd_input.o: cmfd_message_passing.o -cmfd_input.o: error.o -cmfd_input.o: global.o -cmfd_input.o: mesh_header.o -cmfd_input.o: output.o -cmfd_input.o: string.o -cmfd_input.o: tally.o -cmfd_input.o: tally_header.o -cmfd_input.o: tally_initialize.o -cmfd_input.o: templates/cmfd_t.o - -cmfd_jacobian_operator.o: cmfd_loss_operator.o -cmfd_jacobian_operator.o: cmfd_prod_operator.o -cmfd_jacobian_operator.o: constants.o -cmfd_jacobian_operator.o: global.o - -cmfd_loss_operator.o: constants.o -cmfd_loss_operator.o: global.o - -cmfd_message_passing.o: cmfd_header.o -cmfd_message_passing.o: global.o - -cmfd_output.o: cmfd_data.o -cmfd_output.o: cmfd_header.o -cmfd_output.o: constants.o -cmfd_output.o: global.o - -cmfd_power_solver.o: cmfd_loss_operator.o -cmfd_power_solver.o: cmfd_prod_operator.o -cmfd_power_solver.o: constants.o -cmfd_power_solver.o: global.o -cmfd_power_solver.o: string.o - -cmfd_prod_operator.o: constants.o -cmfd_prod_operator.o: global.o - -cmfd_slepc_solver.o: cmfd_loss_operator.o -cmfd_slepc_solver.o: cmfd_prod_operator.o -cmfd_slepc_solver.o: constants.o -cmfd_slepc_solver.o: global.o - -cmfd_snes_solver.o: cmfd_jacobian_operator.o -cmfd_snes_solver.o: cmfd_loss_operator.o -cmfd_snes_solver.o: cmfd_power_solver.o -cmfd_snes_solver.o: cmfd_prod_operator.o -cmfd_snes_solver.o: constants.o -cmfd_snes_solver.o: global.o -cmfd_snes_solver.o: string.o - -cross_section.o: ace_header.o -cross_section.o: constants.o -cross_section.o: error.o -cross_section.o: fission.o -cross_section.o: global.o -cross_section.o: material_header.o -cross_section.o: random_lcg.o -cross_section.o: search.o - -doppler.o: constants.o - -eigenvalue.o: cmfd_execute.o -eigenvalue.o: constants.o -eigenvalue.o: error.o -eigenvalue.o: global.o -eigenvalue.o: math.o -eigenvalue.o: mesh.o -eigenvalue.o: mesh_header.o -eigenvalue.o: output.o -eigenvalue.o: physics.o -eigenvalue.o: random_lcg.o -eigenvalue.o: search.o -eigenvalue.o: source.o -eigenvalue.o: state_point.o -eigenvalue.o: string.o -eigenvalue.o: tally.o - -endf.o: constants.o -endf.o: string.o - -energy_grid.o: constants.o -energy_grid.o: global.o -energy_grid.o: list_header.o -energy_grid.o: output.o - -error.o: global.o - -finalize.o: cmfd_output.o -finalize.o: global.o -finalize.o: hdf5_interface.o -finalize.o: output.o -finalize.o: tally.o - -fission.o: ace_header.o -fission.o: constants.o -fission.o: error.o -fission.o: global.o -fission.o: interpolation.o -fission.o: search.o +plot.o: constants.o +plot.o: error.o +plot.o: geometry.o +plot.o: geometry_header.o +plot.o: global.o +plot.o: output.o +plot.o: particle_header.o +plot.o: plot_header.o +plot.o: ppmlib.o +plot.o: source.o +plot.o: string.o fixed_source.o: constants.o fixed_source.o: global.o @@ -144,64 +74,55 @@ fixed_source.o: state_point.o fixed_source.o: string.o fixed_source.o: tally.o -geometry.o: constants.o -geometry.o: error.o -geometry.o: geometry_header.o -geometry.o: global.o -geometry.o: output.o -geometry.o: particle_header.o -geometry.o: particle_restart_write.o -geometry.o: string.o -geometry.o: tally.o +source.o: bank_header.o +source.o: constants.o +source.o: error.o +source.o: geometry_header.o +source.o: global.o +source.o: output.o +source.o: particle_header.o +source.o: physics.o +source.o: random_lcg.o +source.o: string.o -global.o: ace_header.o -global.o: bank_header.o -global.o: cmfd_header.o -global.o: constants.o -global.o: dict_header.o -global.o: geometry_header.o -global.o: hdf5_interface.o -global.o: material_header.o -global.o: mesh_header.o -global.o: particle_header.o -global.o: plot_header.o -global.o: set_header.o -global.o: source_header.o -global.o: tally_header.o -global.o: timer_header.o +cmfd_prod_operator.o: constants.o +cmfd_prod_operator.o: global.o -hdf5_summary.o: ace_header.o -hdf5_summary.o: constants.o -hdf5_summary.o: endf.o -hdf5_summary.o: geometry_header.o -hdf5_summary.o: global.o -hdf5_summary.o: material_header.o -hdf5_summary.o: mesh_header.o -hdf5_summary.o: output_interface.o -hdf5_summary.o: output.o -hdf5_summary.o: string.o -hdf5_summary.o: tally_header.o +cmfd_slepc_solver.o: cmfd_loss_operator.o +cmfd_slepc_solver.o: cmfd_prod_operator.o +cmfd_slepc_solver.o: constants.o +cmfd_slepc_solver.o: global.o -initialize.o: ace.o -initialize.o: bank_header.o -initialize.o: constants.o -initialize.o: dict_header.o -initialize.o: energy_grid.o -initialize.o: error.o -initialize.o: geometry.o -initialize.o: geometry_header.o -initialize.o: global.o -initialize.o: hdf5_interface.o -initialize.o: hdf5_summary.o -initialize.o: input_xml.o -initialize.o: output_interface.o -initialize.o: output.o -initialize.o: random_lcg.o -initialize.o: source.o -initialize.o: state_point.o -initialize.o: string.o -initialize.o: tally_header.o -initialize.o: tally_initialize.o +fission.o: ace_header.o +fission.o: constants.o +fission.o: error.o +fission.o: global.o +fission.o: interpolation.o +fission.o: search.o + +cmfd_jacobian_operator.o: cmfd_loss_operator.o +cmfd_jacobian_operator.o: cmfd_prod_operator.o +cmfd_jacobian_operator.o: constants.o +cmfd_jacobian_operator.o: global.o + +cmfd_snes_solver.o: cmfd_jacobian_operator.o +cmfd_snes_solver.o: cmfd_loss_operator.o +cmfd_snes_solver.o: cmfd_power_solver.o +cmfd_snes_solver.o: cmfd_prod_operator.o +cmfd_snes_solver.o: constants.o +cmfd_snes_solver.o: global.o +cmfd_snes_solver.o: string.o + +cmfd_input.o: cmfd_message_passing.o +cmfd_input.o: error.o +cmfd_input.o: global.o +cmfd_input.o: mesh_header.o +cmfd_input.o: output.o +cmfd_input.o: string.o +cmfd_input.o: tally.o +cmfd_input.o: tally_header.o +cmfd_input.o: tally_initialize.o +cmfd_input.o: templates/cmfd_t.o input_xml.o: cmfd_input.o input_xml.o: constants.o @@ -224,15 +145,6 @@ input_xml.o: templates/plots_t.o input_xml.o: templates/settings_t.o input_xml.o: templates/tallies_t.o -interpolation.o: constants.o -interpolation.o: endf_header.o -interpolation.o: error.o -interpolation.o: global.o -interpolation.o: search.o -interpolation.o: string.o - -list_header.o: constants.o - main.o: constants.o main.o: eigenvalue.o main.o: finalize.o @@ -242,14 +154,194 @@ main.o: initialize.o main.o: particle_restart.o main.o: plot.o +particle_restart_write.o: bank_header.o +particle_restart_write.o: global.o +particle_restart_write.o: hdf5_interface.o +particle_restart_write.o: string.o + +timer_header.o: constants.o + math.o: constants.o +interpolation.o: constants.o +interpolation.o: endf_header.o +interpolation.o: error.o +interpolation.o: global.o +interpolation.o: search.o +interpolation.o: string.o + +random_lcg.o: global.o + +global.o: ace_header.o +global.o: bank_header.o +global.o: cmfd_header.o +global.o: constants.o +global.o: dict_header.o +global.o: geometry_header.o +global.o: hdf5_interface.o +global.o: material_header.o +global.o: mesh_header.o +global.o: particle_header.o +global.o: plot_header.o +global.o: set_header.o +global.o: source_header.o +global.o: tally_header.o +global.o: timer_header.o + +string.o: constants.o +string.o: error.o +string.o: global.o + +finalize.o: cmfd_output.o +finalize.o: global.o +finalize.o: hdf5_interface.o +finalize.o: output.o +finalize.o: tally.o + +tally_initialize.o: constants.o +tally_initialize.o: global.o +tally_initialize.o: tally_header.o + +tally.o: ace_header.o +tally.o: constants.o +tally.o: error.o +tally.o: global.o +tally.o: math.o +tally.o: mesh.o +tally.o: mesh_header.o +tally.o: output.o +tally.o: particle_header.o +tally.o: search.o +tally.o: string.o +tally.o: tally_header.o + +particle_header.o: constants.o + +output_interface.o: constants.o +output_interface.o: error.o +output_interface.o: global.o +output_interface.o: hdf5_interface.o +output_interface.o: mpiio_interface.o +output_interface.o: tally_header.o + mesh.o: constants.o mesh.o: global.o mesh.o: mesh_header.o mesh.o: particle_header.o mesh.o: search.o +endf.o: constants.o +endf.o: string.o + +initialize.o: ace.o +initialize.o: bank_header.o +initialize.o: constants.o +initialize.o: dict_header.o +initialize.o: energy_grid.o +initialize.o: error.o +initialize.o: geometry.o +initialize.o: geometry_header.o +initialize.o: global.o +initialize.o: hdf5_interface.o +initialize.o: hdf5_summary.o +initialize.o: input_xml.o +initialize.o: output.o +initialize.o: output_interface.o +initialize.o: random_lcg.o +initialize.o: source.o +initialize.o: state_point.o +initialize.o: string.o +initialize.o: tally_header.o +initialize.o: tally_initialize.o + +cross_section.o: ace_header.o +cross_section.o: constants.o +cross_section.o: error.o +cross_section.o: fission.o +cross_section.o: global.o +cross_section.o: material_header.o +cross_section.o: random_lcg.o +cross_section.o: search.o + +state_point.o: constants.o +state_point.o: error.o +state_point.o: global.o +state_point.o: output.o +state_point.o: output_interface.o +state_point.o: string.o +state_point.o: tally_header.o + +eigenvalue.o: cmfd_execute.o +eigenvalue.o: constants.o +eigenvalue.o: error.o +eigenvalue.o: global.o +eigenvalue.o: math.o +eigenvalue.o: mesh.o +eigenvalue.o: mesh_header.o +eigenvalue.o: output.o +eigenvalue.o: physics.o +eigenvalue.o: random_lcg.o +eigenvalue.o: search.o +eigenvalue.o: source.o +eigenvalue.o: state_point.o +eigenvalue.o: string.o +eigenvalue.o: tally.o + +search.o: error.o +search.o: global.o + +ace.o: ace_header.o +ace.o: constants.o +ace.o: endf.o +ace.o: error.o +ace.o: fission.o +ace.o: global.o +ace.o: material_header.o +ace.o: output.o +ace.o: set_header.o +ace.o: string.o + +geometry.o: constants.o +geometry.o: error.o +geometry.o: geometry_header.o +geometry.o: global.o +geometry.o: output.o +geometry.o: particle_header.o +geometry.o: particle_restart_write.o +geometry.o: string.o +geometry.o: tally.o + +plot_header.o: constants.o + +cmfd_header.o: constants.o + +cmfd_execute.o: cmfd_data.o +cmfd_execute.o: cmfd_message_passing.o +cmfd_execute.o: cmfd_power_solver.o +cmfd_execute.o: cmfd_snes_solver.o +cmfd_execute.o: constants.o +cmfd_execute.o: error.o +cmfd_execute.o: global.o +cmfd_execute.o: mesh.o +cmfd_execute.o: mesh_header.o +cmfd_execute.o: output.o +cmfd_execute.o: search.o +cmfd_execute.o: tally.o + +hdf5_summary.o: ace_header.o +hdf5_summary.o: constants.o +hdf5_summary.o: endf.o +hdf5_summary.o: geometry_header.o +hdf5_summary.o: global.o +hdf5_summary.o: material_header.o +hdf5_summary.o: mesh_header.o +hdf5_summary.o: output.o +hdf5_summary.o: output_interface.o +hdf5_summary.o: string.o +hdf5_summary.o: tally_header.o + +error.o: global.o + output.o: ace_header.o output.o: constants.o output.o: endf.o @@ -264,31 +356,6 @@ output.o: plot_header.o output.o: string.o output.o: tally_header.o -output_interface.o: constants.o -output_interface.o: error.o -output_interface.o: global.o -output_interface.o: hdf5_interface.o -output_interface.o: mpiio_interface.o -output_interface.o: tally_header.o - -particle_header.o: constants.o - -particle_restart.o: bank_header.o -particle_restart.o: constants.o -particle_restart.o: geometry_header.o -particle_restart.o: global.o -particle_restart.o: hdf5_interface.o -particle_restart.o: output.o -particle_restart.o: particle_header.o -particle_restart.o: physics.o -particle_restart.o: random_lcg.o -particle_restart.o: source.o - -particle_restart_write.o: bank_header.o -particle_restart_write.o: global.o -particle_restart_write.o: hdf5_interface.o -particle_restart_write.o: string.o - physics.o: ace_header.o physics.o: constants.o physics.o: cross_section.o @@ -309,68 +376,3 @@ physics.o: search.o physics.o: string.o physics.o: tally.o -plot.o: constants.o -plot.o: error.o -plot.o: geometry.o -plot.o: geometry_header.o -plot.o: global.o -plot.o: output.o -plot.o: particle_header.o -plot.o: plot_header.o -plot.o: ppmlib.o -plot.o: source.o -plot.o: string.o - -plot_header.o: constants.o - -random_lcg.o: global.o - -search.o: error.o -search.o: global.o - -set_header.o: constants.o -set_header.o: list_header.o - -source.o: bank_header.o -source.o: constants.o -source.o: error.o -source.o: geometry_header.o -source.o: global.o -source.o: output.o -source.o: particle_header.o -source.o: physics.o -source.o: random_lcg.o -source.o: string.o - -state_point.o: constants.o -state_point.o: error.o -state_point.o: global.o -state_point.o: output.o -state_point.o: output_interface.o -state_point.o: string.o -state_point.o: tally_header.o - -string.o: constants.o -string.o: error.o -string.o: global.o - -tally.o: ace_header.o -tally.o: constants.o -tally.o: error.o -tally.o: global.o -tally.o: math.o -tally.o: mesh.o -tally.o: mesh_header.o -tally.o: output.o -tally.o: particle_header.o -tally.o: search.o -tally.o: string.o -tally.o: tally_header.o - -tally_header.o: constants.o - -tally_initialize.o: constants.o -tally_initialize.o: global.o -tally_initialize.o: tally_header.o - -timer_header.o: constants.o diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 index 4b5fb1196d..258fa650c4 100644 --- a/src/cmfd_loss_operator.F90 +++ b/src/cmfd_loss_operator.F90 @@ -1,64 +1,35 @@ module cmfd_loss_operator -# ifdef PETSC + use constants, only: CMFD_NOACCEL, ZERO + use global, only: cmfd, cmfd_coremap + use matrix_header, only: Matrix implicit none private - public :: init_M_operator, build_loss_matrix, destroy_M_operator - -# include - - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups - integer :: ierr ! petsc error code - - type, public :: loss_operator - Mat :: M ! petsc matrix for neutronic loss operator - integer :: n ! dimensions of matrix - integer :: nnz ! max number of nonzeros - integer :: localn ! local size on proc - integer, allocatable :: d_nnz(:) ! vector of diagonal preallocation - integer, allocatable :: o_nnz(:) ! vector of off-diagonal preallocation - end type loss_operator - - logical :: adjoint_calc = .false. ! adjoint calculation + public :: init_loss_matrix, build_loss_matrix contains !=============================================================================== -! INIT_M_OPERATOR +! INIT_LOSS_MATRIX !=============================================================================== - subroutine init_M_operator(this) + subroutine init_loss_matrix(loss_matrix) - type(loss_operator) :: this + type(Matrix) :: loss_matrix ! cmfd loss matrix - ! get indices - call get_M_indices(this) - - ! get preallocation - call preallocate_loss_matrix(this) - - ! set up M operator - call MatCreateAIJ(PETSC_COMM_WORLD, this%localn, this%localn, PETSC_DECIDE,& - PETSC_DECIDE, PETSC_NULL_INTEGER, this%d_nnz, PETSC_NULL_INTEGER, & - this%o_nnz, this%M,ierr) - call MatSetOption(this%M, MAT_NEW_NONZERO_LOCATIONS, PETSC_TRUE, ierr) - call MatSetOption(this%M, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE, ierr) - - end subroutine init_M_operator - -!=============================================================================== -! GET_M_INDICES -!=============================================================================== - - subroutine get_M_indices(this) - - use global, only: cmfd, cmfd_coremap - - type(loss_operator) :: this + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: nz ! maximum number of z cells + integer :: ng ! maximum number of groups + integer :: n ! total length of matrix + integer :: nnz ! number of nonzeros in matrix + integer :: n_i ! number of interior cells + integer :: n_c ! number of corner cells + integer :: n_s ! number side cells + integer :: nz_c ! number of non-zero corner cells + integer :: nz_s ! number of non-zero side cells + integer :: nz_i ! number of non-zero interior cells ! get maximum number of cells in each direction nx = cmfd%indices(1) @@ -66,38 +37,50 @@ contains nz = cmfd%indices(3) ng = cmfd%indices(4) - ! get number of nonzeros - this%nnz = 7 + ng - 1 - ! calculate dimensions of matrix if (cmfd_coremap) then - this%n = cmfd % mat_dim * ng + n = cmfd % mat_dim * ng else - this%n = nx*ny*nz*ng + n = nx*ny*nz*ng end if - end subroutine get_M_indices + ! calculate number of nonzeros, if core map -> need to determine manually + if (cmfd_coremap) then + nnz = preallocate_loss_matrix(nx, ny, nz, ng, n) + else ! structured Cartesian grid + n_c = 4 ! define # of corners + n_s = 2*(nx + ny) - 8 ! define # of sides + n_i = nx*ny - (n_c + n_S) ! define # of interiors + nz_c = ng*n_c*(3 + ng - 1) ! define # nonzero corners + nz_s = ng*n_s*(4 + ng - 1) ! define # nonzero sides + nz_i = ng*n_i*(5 + ng - 1) ! define # nonzero interiors + nnz = nz_c + nz_s + nz_i + end if + + ! configure loss matrix + call loss_matrix % create(n, nnz) + + end subroutine init_loss_matrix !=============================================================================== ! PREALLOCATE_LOSS_MATRIX !=============================================================================== - subroutine preallocate_loss_matrix(this) + function preallocate_loss_matrix(nx, ny, nz, ng, n) result(nnz) - use constants, only: CMFD_NOACCEL - use global, only: cmfd, cmfd_coremap + integer, intent(in) :: nx ! maximum number of x cells + integer, intent(in) :: ny ! maximum number of y cells + integer, intent(in) :: nz ! maximum number of z cells + integer, intent(in) :: ng ! maximum number of groups + integer, intent(in) :: n ! total length of matrix + integer :: nnz ! number of nonzeros - type(loss_operator) :: this - - integer :: rank ! rank of processor - integer :: sizen ! number of procs integer :: i ! iteration counter for x integer :: j ! iteration counter for y integer :: k ! iteration counter for z integer :: g ! iteration counter for groups integer :: l ! iteration counter for leakages integer :: h ! energy group when doing scattering - integer :: n ! the extent of the matrix integer :: irow ! row counter integer :: bound(6) ! vector for comparing when looking for bound integer :: xyz_idx ! index for determining if x,y or z leakage @@ -105,45 +88,11 @@ contains integer :: neig_idx(3) ! spatial indices of neighbour integer :: nxyz(3,2) ! single vector containing bound. locations integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: row_start ! index of local starting row - integer :: row_end ! index of local final row integer :: neig_mat_idx ! matrix index of neighbor cell integer :: scatt_mat_idx ! matrix index for h-->g scattering terms - ! initialize size and rank - sizen = 0 - rank = 0 - - ! get rank and max rank of procs - call MPI_COMM_RANK(PETSC_COMM_WORLD, rank, ierr) - call MPI_COMM_SIZE(PETSC_COMM_WORLD, sizen, ierr) - - ! get local problem size - n = this%n - - ! determine local size, divide evenly between all other procs - this%localn = n/(sizen) - - ! add 1 more if less proc id is less than mod - if (rank < mod(n,sizen)) this%localn = this%localn + 1 - - ! determine local starting row - row_start = 0 - if (rank < mod(n,sizen)) then - row_start = rank*(n/sizen+1) - else - row_start = min(mod(n,sizen)*(n/sizen+1) + & - (rank - mod(n,sizen))*(n/sizen),n) - end if - - ! determine local final row - row_end = row_start + this%localn - 1 - - ! allocate counters - if (.not. allocated(this%d_nnz)) allocate(this%d_nnz(row_start:row_end)) - if (.not. allocated(this%o_nnz)) allocate(this%o_nnz(row_start:row_end)) - this % d_nnz = 0 - this % o_nnz = 0 + ! reset to 0 + nnz = 0 ! create single vector of these indices for boundary calculation nxyz(1,:) = (/1,nx/) @@ -151,14 +100,13 @@ contains nxyz(3,:) = (/1,nz/) ! begin loop around local rows - ROWS: do irow = row_start,row_end + ROWS: do irow = 1, n - ! initialize counters - this%d_nnz(irow) = 1 ! already add in matrix diagonal - this%o_nnz(irow) = 0 + ! set a nonzero for diagonal + nnz = nnz + 1 ! get location indices - call matrix_to_indices(irow, g, i, j, k) + call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) ! create boundary vector bound = (/i,i,j,j,k,k/) @@ -187,15 +135,10 @@ contains ! get neighbor matrix index call indices_to_matrix(g,neig_idx(1), neig_idx(2), & - neig_idx(3), neig_mat_idx) + neig_idx(3), neig_mat_idx, ng, nx, ny) ! record nonzero - if (((neig_mat_idx-1) >= row_start) .and. & - ((neig_mat_idx-1) <= row_end)) then - this%d_nnz(irow) = this%d_nnz(irow) + 1 - else - this%o_nnz(irow) = this%o_nnz(irow) + 1 - end if + nnz = nnz + 1 end if @@ -203,15 +146,10 @@ contains ! get neighbor matrix index call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & - neig_mat_idx) + neig_mat_idx, ng, nx, ny) ! record nonzero - if (((neig_mat_idx-1) >= row_start) .and. & - ((neig_mat_idx-1) <= row_end)) then - this%d_nnz(irow) = this%d_nnz(irow) + 1 - else - this%o_nnz(irow) = this%o_nnz(irow) + 1 - end if + nnz = nnz + 1 end if @@ -226,32 +164,24 @@ contains if (h == g) cycle ! get neighbor matrix index - call indices_to_matrix(h, i, j, k, scatt_mat_idx) + call indices_to_matrix(h, i, j, k, scatt_mat_idx, ng, nx, ny) ! record nonzero - if (((scatt_mat_idx-1) >= row_start) .and. & - ((scatt_mat_idx-1) <= row_end)) then - this%d_nnz(irow) = this%d_nnz(irow) + 1 - else - this%o_nnz(irow) = this%o_nnz(irow) + 1 - end if + nnz = nnz + 1 end do SCATTR end do ROWS - end subroutine preallocate_loss_matrix + end function preallocate_loss_matrix !=============================================================================== ! BUILD_LOSS_MATRIX creates the matrix representing loss of neutrons !=============================================================================== - subroutine build_loss_matrix(this, adjoint) + subroutine build_loss_matrix(loss_matrix, adjoint) - use constants, only: CMFD_NOACCEL, ZERO - use global, only: cmfd, cmfd_coremap, cmfd_write_matrices - - type(loss_operator) :: this + type(Matrix) :: loss_matrix ! cmfd loss matrix logical, optional :: adjoint ! set up the adjoint integer :: nxyz(3,2) ! single vector containing bound. locations @@ -261,6 +191,10 @@ contains integer :: g ! iteration counter for groups integer :: l ! iteration counter for leakages integer :: h ! energy group when doing scattering + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: nz ! maximum number of z cells + integer :: ng ! maximum number of groups integer :: neig_mat_idx ! matrix index of neighbor cell integer :: scatt_mat_idx ! matrix index for h-->g scattering terms integer :: bound(6) ! vector for comparing when looking for bound @@ -268,9 +202,8 @@ contains integer :: dir_idx ! index for determining - or + face of cell integer :: neig_idx(3) ! spatial indices of neighbour integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: row_start ! the first local row on the processor - integer :: row_finish ! the last local row on the processor integer :: irow ! iteration counter over row + logical :: adjoint_calc ! is this a physical adjoint calculation? real(8) :: totxs ! total macro cross section real(8) :: scattxsgg ! scattering macro cross section g-->g real(8) :: scattxshg ! scattering macro cross section h-->g @@ -283,25 +216,28 @@ contains real(8) :: val ! temporary variable before saving to ! check for adjoint + adjoint_calc = .false. if (present(adjoint)) adjoint_calc = adjoint + ! get maximum number of cells in each direction + nx = cmfd%indices(1) + ny = cmfd%indices(2) + nz = cmfd%indices(3) + ng = cmfd%indices(4) + ! create single vector of these indices for boundary calculation nxyz(1,:) = (/1,nx/) nxyz(2,:) = (/1,ny/) nxyz(3,:) = (/1,nz/) - ! initialize row start and finish - row_start = 0 - row_finish = 0 - - ! get row bounds for this processor - call MatGetOwnershipRange(this%M, row_start, row_finish, ierr) - ! begin iteration loops - ROWS: do irow = row_start, row_finish-1 + ROWS: do irow = 1, loss_matrix % n + + ! set up a new row in matrix + call loss_matrix % new_row ! get indices for that row - call matrix_to_indices(irow, g, i, j, k) + call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) ! retrieve cell data totxs = cmfd%totalxs(g,i,j,k) @@ -347,14 +283,13 @@ contains ! get neighbor matrix index call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & - neig_mat_idx) + neig_mat_idx, ng, nx, ny) ! compute value and record to bank val = jn/hxyz(xyz_idx) ! record value in matrix - call MatSetValue(this%M, irow, neig_mat_idx-1, val, & - INSERT_VALUES, ierr) + call loss_matrix % add_value(neig_mat_idx, val) end if @@ -365,14 +300,13 @@ contains ! get neighbor matrix index call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & - neig_mat_idx) + neig_mat_idx, ng, nx, ny) ! compute value and record to bank val = jn/hxyz(xyz_idx) ! record value in matrix - call MatSetValue(this%M, irow, neig_mat_idx-1, val, & - INSERT_VALUES, ierr) + call loss_matrix % add_value(neig_mat_idx, val) end if @@ -391,7 +325,7 @@ contains val = jnet + totxs - scattxsgg ! record diagonal term - call MatSetValue(this%M, irow, irow, val, INSERT_VALUES, ierr) + call loss_matrix % add_value(irow, val) ! begin loop over off diagonal in-scattering SCATTR: do h = 1, ng @@ -400,7 +334,7 @@ contains if (h == g) cycle ! get neighbor matrix index - call indices_to_matrix(h, i, j, k, scatt_mat_idx) + call indices_to_matrix(h, i, j, k, scatt_mat_idx, ng, nx, ny) ! get scattering macro xs scattxshg = cmfd%scattxs(h, g, i, j, k) @@ -410,39 +344,49 @@ contains ! check for adjoint and bank value if (adjoint_calc) then - call MatSetValue(this%M, scatt_mat_idx-1, irow, val, & - INSERT_VALUES, ierr) + + ! get scattering macro xs, transposed! + scattxshg = cmfd%scattxs(g, h, i, j, k) + + ! negate the scattering xs + val = -scattxshg + + ! record value in matrix + call loss_matrix % add_value(scatt_mat_idx, val) + else - call MatSetValue(this%M, irow, scatt_mat_idx-1, val, & - INSERT_VALUES, ierr) + + ! get scattering macro xs + scattxshg = cmfd%scattxs(h, g, i, j, k) + + ! negate the scattering xs + val = -scattxshg + + ! record value in matrix + call loss_matrix % add_value(scatt_mat_idx, val) + end if end do SCATTR end do ROWS - ! assemble matrix - call MatAssemblyBegin(this%M, MAT_FLUSH_ASSEMBLY, ierr) - call MatAssemblyEnd(this%M, MAT_FINAL_ASSEMBLY, ierr) - - ! print out operator to file - if (cmfd_write_matrices) call print_M_operator(this) - end subroutine build_loss_matrix !=============================================================================== ! INDICES_TO_MATRIX takes (x,y,z,g) indices and computes location in matrix !=============================================================================== - subroutine indices_to_matrix(g, i, j, k, matidx) - - use global, only: cmfd, cmfd_coremap + subroutine indices_to_matrix(g, i, j, k, matidx, ng, nx, ny) integer :: matidx ! the index location in matrix integer :: i ! current x index integer :: j ! current y index integer :: k ! current z index integer :: g ! current group index + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: ng ! maximum number of groups ! check if coremap is used if (cmfd_coremap) then @@ -463,15 +407,17 @@ contains ! MATRIX_TO_INDICES !=============================================================================== - subroutine matrix_to_indices(irow, g, i, j, k) - - use global, only: cmfd, cmfd_coremap + subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) integer :: i ! iteration counter for x integer :: j ! iteration counter for y integer :: k ! iteration counter for z integer :: g ! iteration counter for groups integer :: irow ! iteration counter over row (0 reference) + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: nz ! maximum number of z cells + integer :: ng ! maximum number of groups ! check for core map if (cmfd_coremap) then @@ -494,46 +440,4 @@ contains end subroutine matrix_to_indices -!=============================================================================== -! PRINT_M_OPERATOR -!=============================================================================== - - subroutine print_M_operator(this) - - type(loss_operator) :: this - - PetscViewer :: viewer - - ! write out matrix in binary file (debugging) - if (adjoint_calc) then - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_lossmat.bin', & - FILE_MODE_WRITE, viewer, ierr) - else - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'lossmat.bin', & - FILE_MODE_WRITE, viewer, ierr) - end if - call MatView(this%M, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) - - end subroutine print_M_operator - -!============================================================================== -! DESTROY_M_OPERATOR -!============================================================================== - - subroutine destroy_M_operator(this) - - type(loss_operator) :: this - - ! deallocate matrix - call MatDestroy(this%M, ierr) - - ! deallocate other parameters - if (allocated(this%d_nnz)) deallocate(this%d_nnz) - if (allocated(this%o_nnz)) deallocate(this%o_nnz) - - end subroutine destroy_M_operator - -# endif - end module cmfd_loss_operator diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index 2b53facd96..82a80ad396 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -7,9 +7,9 @@ module matrix_header # include # endif - type, public :: matrix + type, public :: Matrix integer :: n ! number of rows/cols in matrix - integer :: nz ! number of nonzeros in matrix + integer :: nnz ! number of nonzeros in matrix integer :: n_kount ! counter for length of matrix integer :: nz_kount ! counter for number of non zeros integer, allocatable :: row(:) ! csr row vector @@ -34,21 +34,25 @@ contains ! MATRIX_CREATE allocates CSR vectors !=============================================================================== - subroutine matrix_create(self, n, nz) + subroutine matrix_create(self, n, nnz) integer :: n - integer :: nz - class(matrix) :: self + integer :: nnz + class(Matrix) :: self ! preallocate vectors if (.not.allocated(self % row)) allocate(self % row(n+1)) - if (.not.allocated(self % col)) allocate(self % col(nz)) - if (.not.allocated(self % val)) allocate(self % val(nz)) + if (.not.allocated(self % col)) allocate(self % col(nnz)) + if (.not.allocated(self % val)) allocate(self % val(nnz)) ! set counters to 1 self % n_kount = 1 self % nz_kount = 1 + ! set n and nnz + self % n = n + self % nnz = nnz + end subroutine matrix_create !=============================================================================== @@ -57,7 +61,7 @@ contains subroutine matrix_destroy(self) - class(matrix) :: self + class(Matrix) :: self if (allocated(self % row)) deallocate(self % row) if (allocated(self % col)) deallocate(self % col) @@ -73,7 +77,7 @@ contains integer :: col real(8) :: val - class(matrix) :: self + class(Matrix) :: self self % col(self % nz_kount) = col self % val(self % nz_kount) = val @@ -87,7 +91,7 @@ contains subroutine matrix_new_row(self) - class(matrix) :: self + class(Matrix) :: self self % row(self % n_kount) = self % nz_kount self % n_kount = self % n_kount + 1 @@ -102,7 +106,7 @@ contains subroutine matrix_setup_petsc(self) - class(matrix) :: self + class(Matrix) :: self integer :: petsc_err From 3749332218df54e885417e837ae9a20794b418e9 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 8 Jul 2013 12:01:18 -0400 Subject: [PATCH 018/192] fixed some redundant statements and simplified --- src/cmfd_loss_operator.F90 | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 index 258fa650c4..0e86d38a63 100644 --- a/src/cmfd_loss_operator.F90 +++ b/src/cmfd_loss_operator.F90 @@ -336,37 +336,21 @@ contains ! get neighbor matrix index call indices_to_matrix(h, i, j, k, scatt_mat_idx, ng, nx, ny) - ! get scattering macro xs - scattxshg = cmfd%scattxs(h, g, i, j, k) - - ! record value in matrix (negate it) - val = -scattxshg - - ! check for adjoint and bank value + ! check for adjoint if (adjoint_calc) then - ! get scattering macro xs, transposed! scattxshg = cmfd%scattxs(g, h, i, j, k) - - ! negate the scattering xs - val = -scattxshg - - ! record value in matrix - call loss_matrix % add_value(scatt_mat_idx, val) - else - ! get scattering macro xs scattxshg = cmfd%scattxs(h, g, i, j, k) - - ! negate the scattering xs - val = -scattxshg - - ! record value in matrix - call loss_matrix % add_value(scatt_mat_idx, val) - end if + ! negate the scattering xs + val = -scattxshg + + ! record value in matrix + call loss_matrix % add_value(scatt_mat_idx, val) + end do SCATTR end do ROWS From 9703a55c684159fb0a0fb21b0b6a349e2bd8ddea Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 8 Jul 2013 12:02:26 -0400 Subject: [PATCH 019/192] change prod operator construction --- src/DEPENDENCIES | 1 + src/cmfd_prod_operator.F90 | 319 ++++++++----------------------------- 2 files changed, 65 insertions(+), 255 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 973386c406..ab014e45fd 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -87,6 +87,7 @@ source.o: string.o cmfd_prod_operator.o: constants.o cmfd_prod_operator.o: global.o +cmfd_prod_operator.o: matrix_header.o cmfd_slepc_solver.o: cmfd_loss_operator.o cmfd_slepc_solver.o: cmfd_prod_operator.o diff --git a/src/cmfd_prod_operator.F90 b/src/cmfd_prod_operator.F90 index a5eac6df74..02f2559a36 100644 --- a/src/cmfd_prod_operator.F90 +++ b/src/cmfd_prod_operator.F90 @@ -1,66 +1,29 @@ module cmfd_prod_operator -# ifdef PETSC + use constants, only: CMFD_NOACCEL + use global, only: cmfd, cmfd_coremap + use matrix_header, only: Matrix implicit none private - public :: init_F_operator,build_prod_matrix,destroy_F_operator - -# include - - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups - integer :: ierr ! petsc error code - - type, public :: prod_operator - - Mat :: F ! petsc matrix for neutronic prod operator - integer :: n ! dimensions of matrix - integer :: nnz ! max number of nonzeros - integer :: localn ! local size on proc - integer, allocatable :: d_nnz(:) ! vector of diagonal preallocation - integer, allocatable :: o_nnz(:) ! vector of off-diagonal preallocation - - end type prod_operator - - logical :: adjoint_calc = .false. ! adjoint calculation + public :: init_prod_matrix, build_prod_matrix contains !============================================================================== -! INIT_F_OPERATOR +! INIT_PROD_MATRIX !============================================================================== - subroutine init_F_operator(this) + subroutine init_prod_matrix(prod_matrix) - type(prod_operator) :: this + type(Matrix) :: prod_matrix - ! get indices - call get_F_indices(this) - - ! get preallocation - call preallocate_prod_matrix(this) - - ! set up M operator - call MatCreateAIJ(PETSC_COMM_WORLD, this%localn, this%localn, PETSC_DECIDE, & - PETSC_DECIDE, PETSC_NULL_INTEGER, this%d_nnz, PETSC_NULL_INTEGER, & - this%o_nnz, this%F, ierr) - call MatSetOption(this%F, MAT_NEW_NONZERO_LOCATIONS, PETSC_TRUE, ierr) - call MatSetOption(this%F, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE, ierr) - - end subroutine init_F_operator - -!============================================================================== -! GET_F_INDICES -!============================================================================== - - subroutine get_F_indices(this) - - use global, only: cmfd, cmfd_coremap - - type(prod_operator) :: this + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: nz ! maximum number of z cells + integer :: ng ! maximum number of groups + integer :: n ! total length of matrix + integer :: nnz ! number of nonzeros in matrix ! get maximum number of cells in each direction nx = cmfd%indices(1) @@ -68,132 +31,26 @@ contains nz = cmfd%indices(3) ng = cmfd%indices(4) - ! get number of nonzeros - this%nnz = 7 + ng - 1 - - ! calculate dimensions of matrix + ! calculate dimensions and number of nonzeros in matrix if (cmfd_coremap) then - this%n = cmfd % mat_dim * ng + n = cmfd % mat_dim * ng else - this%n = nx*ny*nz*ng + n = nx*ny*nz*ng end if + nz = n * ng - end subroutine get_F_indices + ! configure prod matrix + call prod_matrix % create(n, nnz) + + end subroutine init_prod_matrix !=============================================================================== -! PREALLOCATE_PROD_MATRIX +! BUILD_PROD_MATRIX creates the matrix representing production of neutrons !=============================================================================== - subroutine preallocate_prod_matrix(this) + subroutine build_prod_matrix(prod_matrix, adjoint) - use constants, only: CMFD_NOACCEL - use global, only: cmfd, cmfd_coremap - - type(prod_operator) :: this - - integer :: rank ! rank of processor - integer :: sizen ! number of procs - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: h ! energy group when doing scattering - integer :: n ! the extent of the matrix - integer :: irow ! row counter - integer :: row_start ! index of local starting row - integer :: row_end ! index of local final row - integer :: hmat_idx ! index in matrix for energy group h - - ! initialize size and rank - rank = 0 - sizen = 0 - - ! get rank and max rank of procs - call MPI_COMM_RANK(PETSC_COMM_WORLD, rank, ierr) - call MPI_COMM_SIZE(PETSC_COMM_WORLD, sizen, ierr) - - ! get local problem size - n = this%n - - ! determine local size, divide evenly between all other procs - this%localn = n/(sizen) - - ! add 1 more if less proc id is less than mod - if (rank < mod(n, sizen)) this%localn = this%localn + 1 - - ! determine local starting row - row_start = 0 - if (rank < mod(n, sizen)) then - row_start = rank*(n/sizen+1) - else - row_start = min(mod(n, sizen)*(n/sizen+1) + & - (rank - mod(n, sizen))*(n/sizen), n) - end if - - ! determine local final row - row_end = row_start + this%localn - 1 - - ! allocate counters - if (.not. allocated(this%d_nnz)) allocate(this%d_nnz(row_start:row_end)) - if (.not. allocated(this%o_nnz)) allocate(this%o_nnz(row_start:row_end)) - this % d_nnz = 0 - this % o_nnz = 0 - - ! begin loop around local rows - ROWS: do irow = row_start, row_end - - ! initialize counters - this%d_nnz(irow) = 1 ! already add in matrix diagonal - this%o_nnz(irow) = 0 - - ! get location indices - call matrix_to_indices(irow, g, i, j, k) - - ! check if not including reflector - if (cmfd_coremap) then - - ! check if at a reflector - if (cmfd % coremap(i,j,k) == CMFD_NOACCEL) then - cycle - end if - - end if - - ! begin loop over off diagonal in-scattering - NFISS: do h = 1, ng - - ! cycle though if h=g - if (h == g) then - cycle - end if - - ! get neighbor matrix index - call indices_to_matrix(h, i, j, k, hmat_idx) - - ! record nonzero - if (((hmat_idx-1) >= row_start) .and. & - ((hmat_idx-1) <= row_end)) then - this%d_nnz(irow) = this%d_nnz(irow) + 1 - else - this%o_nnz(irow) = this%o_nnz(irow) + 1 - end if - - end do NFISS - - end do ROWS - - end subroutine preallocate_prod_matrix - -!=============================================================================== -! BUILD_PROD_MATRIX creates the matrix representing loss of neutrons -!=============================================================================== - - subroutine build_prod_matrix(this, adjoint) - - use constants, only: CMFD_NOACCEL - use global, only: cmfd, cmfd_coremap, cmfd_write_matrices - - type(prod_operator) :: this + type(Matrix) :: prod_matrix logical, optional :: adjoint integer :: i ! iteration counter for x @@ -201,29 +58,25 @@ contains integer :: k ! iteration counter for z integer :: g ! iteration counter for groups integer :: h ! energy group when doing scattering + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: nz ! maximum number of z cells + integer :: ng ! maximum number of groups integer :: hmat_idx ! index in matrix for energy group h - integer :: ierr ! Petsc error code - integer :: row_start ! the first local row on the processor - integer :: row_finish ! the last local row on the processor integer :: irow ! iteration counter over row + logical :: adjoint_calc ! is this a physical adjoint? real(8) :: nfissxs ! nufission cross section h-->g real(8) :: val ! temporary variable for nfissxs ! check for adjoint + adjoint_calc = .false. if (present(adjoint)) adjoint_calc = adjoint - ! initialize row start and finish - row_start = 0 - row_finish = 0 - - ! get row bounds for this processor - call MatGetOwnershipRange(this%F, row_start, row_finish, ierr) - ! begin iteration loops - ROWS: do irow = row_start, row_finish-1 + ROWS: do irow = 1, prod_matrix % n ! get indices for that row - call matrix_to_indices(irow, g, i, j, k) + call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) ! check if not including reflector if (cmfd_coremap) then @@ -238,52 +91,48 @@ contains ! loop around all other groups NFISS: do h = 1, ng - ! get cell data - nfissxs = cmfd%nfissxs(h,g,i,j,k) - ! get matrix column location - call indices_to_matrix(h, i, j, k, hmat_idx) - - ! reocrd value in matrix - val = nfissxs + call indices_to_matrix(h, i, j, k, hmat_idx, ng, nx, ny) ! check for adjoint and bank val if (adjoint_calc) then - call MatSetValue(this%F, hmat_idx-1, irow, val, INSERT_VALUES, ierr) + ! get nu-fission cross section from cell + nfissxs = cmfd%nfissxs(g,h,i,j,k) else - call MatSetValue(this%F, irow, hmat_idx-1, val, INSERT_VALUES, ierr) + ! get nu-fission cross section from cell + nfissxs = cmfd%nfissxs(h,g,i,j,k) end if + ! set as value to be recorded + val = nfissxs + + ! record value in matrix + call prod_matrix % add_value(hmat_idx, val) + end do NFISS end do ROWS - ! assemble matrix - call MatAssemblyBegin(this%F, MAT_FLUSH_ASSEMBLY, ierr) - call MatAssemblyEnd(this%F, MAT_FINAL_ASSEMBLY, ierr) - - ! print out operator to file - if (cmfd_write_matrices) call print_F_operator(this) - end subroutine build_prod_matrix !=============================================================================== ! INDICES_TO_MATRIX takes (x,y,z,g) indices and computes location in matrix !=============================================================================== - subroutine indices_to_matrix(g, i, j, k, matidx) + subroutine indices_to_matrix(g, i, j, k, matidx, ng, nx, ny) - use global, only: cmfd, cmfd_coremap + integer :: matidx ! the index location in matrix + integer :: i ! current x index + integer :: j ! current y index + integer :: k ! current z index + integer :: g ! current group index + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: ng ! maximum number of groups - integer :: matidx ! the index location in matrix - integer :: i ! current x index - integer :: j ! current y index - integer :: k ! current z index - integer :: g ! current group index - ! check if coremap is used if (cmfd_coremap) then - + ! get idx from core map matidx = ng*(cmfd % coremap(i,j,k)) - (ng - g) @@ -300,21 +149,23 @@ contains ! MATRIX_TO_INDICES !=============================================================================== - subroutine matrix_to_indices(irow, g, i, j, k) + subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - use global, only: cmfd, cmfd_coremap - - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: irow ! iteration counter over row (0 reference) + integer :: i ! iteration counter for x + integer :: j ! iteration counter for y + integer :: k ! iteration counter for z + integer :: g ! iteration counter for groups + integer :: irow ! iteration counter over row (0 reference) + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: nz ! maximum number of z cells + integer :: ng ! maximum number of groups ! check for core map if (cmfd_coremap) then ! get indices from indexmap - g = mod(irow,ng) + 1 + g = mod(irow, ng) + 1 i = cmfd % indexmap(irow/ng+1,1) j = cmfd % indexmap(irow/ng+1,2) k = cmfd % indexmap(irow/ng+1,3) @@ -331,46 +182,4 @@ contains end subroutine matrix_to_indices -!=============================================================================== -! PRINT_F_OPERATOR -!=============================================================================== - - subroutine print_F_operator(this) - - type(prod_operator) :: this - - PetscViewer :: viewer - - ! write out matrix in binary file (debugging) - if (adjoint_calc) then - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_prodmat.bin', & - FILE_MODE_WRITE, viewer, ierr) - else - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'prodmat.bin', & - FILE_MODE_WRITE, viewer, ierr) - end if - call MatView(this%F, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) - - end subroutine print_F_operator - -!============================================================================== -! DESTROY_F_OPERATOR -!============================================================================== - - subroutine destroy_F_operator(this) - - type(prod_operator) :: this - - ! deallocate matrix - call MatDestroy(this%F, ierr) - - ! deallocate other parameters - if (allocated(this%d_nnz)) deallocate(this%d_nnz) - if (allocated(this%o_nnz)) deallocate(this%o_nnz) - - end subroutine destroy_F_operator - -# endif - end module cmfd_prod_operator From 9f5255e2be3d6308b8e3fcaff1e6e439b5161162 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 8 Jul 2013 13:47:01 -0400 Subject: [PATCH 020/192] added a vector class --- src/DEPENDENCIES | 2 + src/OBJECTS | 3 +- src/vector_header.F90 | 95 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/vector_header.F90 diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index ab014e45fd..7dfbfe54f3 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -264,6 +264,8 @@ cross_section.o: material_header.o cross_section.o: random_lcg.o cross_section.o: search.o +vector_header.o: constants.o + state_point.o: constants.o state_point.o: error.o state_point.o: global.o diff --git a/src/OBJECTS b/src/OBJECTS index 030679cca6..af668b4b5b 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -61,4 +61,5 @@ string.o \ tally.o \ tally_header.o \ tally_initialize.o \ -timer_header.o +timer_header.o \ +vector_header.o diff --git a/src/vector_header.F90 b/src/vector_header.F90 new file mode 100644 index 0000000000..7f2abd7113 --- /dev/null +++ b/src/vector_header.F90 @@ -0,0 +1,95 @@ +module vector_header + + use constants, only: ZERO + + implicit none + private + +# ifdef PETSC +# include +# endif + + type, public :: Vector + integer :: n ! number of rows/cols in matrix + real(8), allocatable :: val(:) ! matrix value vector +# ifdef PETSC + Vec :: petsc_vec +# endif + contains + procedure :: create => vector_create + procedure :: destroy => vector_destroy + procedure :: add_value => vector_add_value +# ifdef PETSC + procedure :: setup_petsc => vector_setup_petsc +# endif + end type Vector + +contains + +!=============================================================================== +! VECTOR_CREATE allocates and initializes a vector +!=============================================================================== + + subroutine vector_create(self, n) + + integer :: n + class(Vector) :: self + + ! preallocate vector + if (.not.allocated(self % val)) allocate(self % val(n)) + + ! set n + self % n = n + + ! initialize to zero + self % val = ZERO + + end subroutine vector_create + +!=============================================================================== +! VECTOR_DESTROY deallocates all space associated with a vector +!=============================================================================== + + subroutine vector_destroy(self) + + class(Vector) :: self + + if (allocated(self % val)) deallocate(self % val) + + end subroutine vector_destroy + +!=============================================================================== +! VECTOR_ADD_VALUE adds a value to the vector +!=============================================================================== + + subroutine vector_add_value(self, idx, val) + + integer :: idx + real(8) :: val + class(Vector) :: self + + self % val(idx) = val + + end subroutine vector_add_value + +# ifdef PETSC + +!=============================================================================== +! VECTOR_SETUP_PETSC +!=============================================================================== + + subroutine vector_setup_petsc(self) + + class(Vector) :: self + + integer :: petsc_err + + ! link to petsc + call VecCreateSeqWithArray(PETSC_COMM_WORLD, 1, self % n, self % val, & + self % petsc_vec, petsc_err) + + end subroutine vector_setup_petsc + +# endif + +end module vector_header From 52233d0173494368ebe87210e748cf5403a382c3 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 8 Jul 2013 15:10:50 -0400 Subject: [PATCH 021/192] 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 e06137938cbc014d4e44b65653fb4f1b35d3b10c Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 8 Jul 2013 17:01:44 -0400 Subject: [PATCH 022/192] changed over power iteration and added more methods --- src/DEPENDENCIES | 6 +- src/cmfd_loss_operator.F90 | 19 ++- src/cmfd_power_solver.F90 | 292 ++++++++++--------------------------- src/cmfd_prod_operator.F90 | 30 +++- src/matrix_header.F90 | 161 +++++++++++++++++++- src/petsc_solver.F90 | 51 ++++--- src/vector_header.F90 | 19 +++ 7 files changed, 321 insertions(+), 257 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 7dfbfe54f3..f74355babb 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -39,7 +39,9 @@ cmfd_power_solver.o: cmfd_loss_operator.o cmfd_power_solver.o: cmfd_prod_operator.o cmfd_power_solver.o: constants.o cmfd_power_solver.o: global.o -cmfd_power_solver.o: string.o +cmfd_power_solver.o: matrix_header.o +cmfd_power_solver.o: petsc_solver.o +cmfd_power_solver.o: vector_header.o cmfd_message_passing.o: cmfd_header.o cmfd_message_passing.o: global.o @@ -101,8 +103,6 @@ fission.o: global.o fission.o: interpolation.o fission.o: search.o -cmfd_jacobian_operator.o: cmfd_loss_operator.o -cmfd_jacobian_operator.o: cmfd_prod_operator.o cmfd_jacobian_operator.o: constants.o cmfd_jacobian_operator.o: global.o diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 index 0e86d38a63..67551f78b9 100644 --- a/src/cmfd_loss_operator.F90 +++ b/src/cmfd_loss_operator.F90 @@ -234,7 +234,7 @@ contains ROWS: do irow = 1, loss_matrix % n ! set up a new row in matrix - call loss_matrix % new_row + call loss_matrix % new_row() ! get indices for that row call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) @@ -355,6 +355,9 @@ contains end do ROWS + ! CSR requires n+1 row + call loss_matrix % new_row() + end subroutine build_loss_matrix !=============================================================================== @@ -408,17 +411,17 @@ contains ! get indices from indexmap g = mod(irow, ng) + 1 - i = cmfd % indexmap(irow/ng+1,1) - j = cmfd % indexmap(irow/ng+1,2) - k = cmfd % indexmap(irow/ng+1,3) + i = cmfd % indexmap((irow-1)/ng+1,1) + j = cmfd % indexmap((irow-1)/ng+1,2) + k = cmfd % indexmap((irow-1)/ng+1,3) else ! compute indices - g = mod(irow, ng) + 1 - i = mod(irow, ng*nx)/ng + 1 - j = mod(irow, ng*nx*ny)/(ng*nx)+ 1 - k = mod(irow, ng*nx*ny*nz)/(ng*nx*ny) + 1 + g = mod(irow-1, ng) + 1 + i = mod(irow-1, ng*nx)/ng + 1 + j = mod(irow-1, ng*nx*ny)/(ng*nx)+ 1 + k = mod(irow-1, ng*nx*ny*nz)/(ng*nx*ny) + 1 end if diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 8376b964d0..0a258aac13 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -1,41 +1,30 @@ module cmfd_power_solver -# ifdef PETSC - ! This module contains routines to execute the power iteration solver - use cmfd_loss_operator, only: loss_operator,init_M_operator, & - build_loss_matrix,destroy_M_operator - use cmfd_prod_operator, only: prod_operator,init_F_operator, & - build_prod_matrix,destroy_F_operator + use cmfd_loss_operator, only: init_loss_matrix, build_loss_matrix + use cmfd_prod_operator, only: init_prod_matrix, build_prod_matrix + use matrix_header, only: Matrix + use petsc_solver, only: Petsc_gmres + use vector_header, only: Vector implicit none private public :: cmfd_power_execute -# include - - type(loss_operator) :: loss ! M loss matrix - type(prod_operator) :: prod ! F production matrix - - Vec :: phi_n ! new flux eigenvector - Vec :: phi_o ! old flux eigenvector - Vec :: S_n ! new source vector - Vec :: S_o ! old source vector - KSP :: krylov ! krylov solver - KSP :: sub_krylov ! sub-ksp for bjacobi - PC :: prec ! preconditioner for krylov - PC :: sub_prec ! sub-prec for bjacobi - integer :: ierr ! error flag - integer :: nlocal - integer :: first + logical :: iconv ! did the problem converged real(8) :: k_n ! new k-eigenvalue real(8) :: k_o ! old k-eigenvalue real(8) :: ktol = 1.e-8_8 ! tolerance on keff real(8) :: stol = 1.e-8_8 ! tolerance on source - logical :: iconv ! did the problem converged - logical :: adjoint_calc = .false. ! run an adjoint calculation + type(Matrix) :: loss ! cmfd loss matrix + type(Matrix) :: prod ! cmfd prod matrix + type(Vector) :: phi_n ! new flux vector + type(Vector) :: phi_o ! old flux vector + type(Vector) :: S_n ! new source vector + type(Vector) :: S_o ! old flux vector + type(Petsc_gmres) :: gmres ! gmres solver contains @@ -45,7 +34,7 @@ contains subroutine cmfd_power_execute(k_tol, s_tol, adjoint) - use global, only: cmfd_adjoint_type, n_procs_cmfd + use global, only: cmfd_adjoint_type real(8), optional :: k_tol ! tolerance on keff real(8), optional :: s_tol ! tolerance on source @@ -65,26 +54,20 @@ contains physical_adjoint = .true. ! initialize solver - call init_solver() + call gmres % create() ! initialize matrices and vectors - call init_data() - - ! set up M loss matrix - call build_loss_matrix(loss, adjoint=physical_adjoint) - - ! set up F production matrix - call build_prod_matrix(prod, adjoint=physical_adjoint) + call init_data(physical_adjoint) ! check for adjoint calculation if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & call compute_adjoint() ! set up krylov info - call KSPSetOperators(krylov, loss%M, loss%M, SAME_NONZERO_PATTERN, ierr) + call gmres % set_oper(loss % petsc_mat, loss % petsc_mat) ! precondition matrix - call precondition_matrix() + call gmres % precondition(loss % petsc_mat) ! begin power iteration call execute_power_iter() @@ -98,122 +81,58 @@ contains end subroutine cmfd_power_execute !=============================================================================== -! INIT_DATA allocates matrices vectors for CMFD solution +! INIT_DATA allocates matrices and vectors for CMFD solution !=============================================================================== - subroutine init_data() + subroutine init_data(adjoint) use constants, only: ONE + logical :: adjoint + integer :: n ! problem size real(8) :: guess ! initial guess ! set up matrices - call init_M_operator(loss) - call init_F_operator(prod) + call init_loss_matrix(loss) + call init_prod_matrix(prod) ! get problem size - n = loss%localn + n = loss % n ! set up flux vectors - call VecCreateMPI(PETSC_COMM_WORLD, n, PETSC_DECIDE, phi_n, ierr) - call VecCreateMPI(PETSC_COMM_WORLD, n, PETSC_DECIDE, phi_o, ierr) + call phi_n % create(n) + call phi_o % create(n) ! set up source vectors - call VecCreateMPI(PETSC_COMM_WORLD, n, PETSC_DECIDE, S_n, ierr) - call VecCreateMPI(PETSC_COMM_WORLD, n, PETSC_DECIDE, S_o, ierr) + call S_n % create(n) + call S_o % create(n) ! set initial guess guess = ONE - call VecSet(phi_n, guess, ierr) - call VecSet(phi_o, guess, ierr) + phi_n % val = guess + phi_o % val = guess k_n = guess k_o = guess + ! set up loss matrix + call build_loss_matrix(loss, adjoint=adjoint) + + ! set up production matrix + call build_prod_matrix(prod, adjoint=adjoint) + + ! setup petsc for everything + call loss % assemble() + call prod % assemble() + call loss % setup_petsc() + call prod % setup_petsc() + call phi_n % setup_petsc() + call phi_o % setup_petsc() + call S_o % setup_petsc() + call S_n % setup_petsc() + end subroutine init_data -!=============================================================================== -! INIT_SOLVER -!=============================================================================== - - subroutine init_solver() - - real(8) :: rtol ! relative tolerance - real(8) :: atol ! absolute tolerance - - ! set tolerance - rtol = 1.0e-10_8 - atol = 1.0e-10_8 - - ! set up krylov solver - call KSPCreate(PETSC_COMM_WORLD, krylov, ierr) - call KSPSetTolerances(krylov, rtol, atol, PETSC_DEFAULT_DOUBLE_PRECISION, & - PETSC_DEFAULT_INTEGER, ierr) - call KSPSetType(krylov, KSPGMRES, ierr) - call KSPSetInitialGuessNonzero(krylov, PETSC_TRUE, ierr) - - end subroutine init_solver - -!=============================================================================== -! PRECONDITION_MATRIX -!=============================================================================== - - subroutine precondition_matrix() - - use global, only: cmfd_power_monitor, cmfd_solver_type, & - n_procs_cmfd, cmfd_ilu_levels, master - use string, only: to_str - use, intrinsic :: ISO_FORTRAN_ENV - - character(len=20) :: ksptype,pctype - - ! set up preconditioner - call KSPGetPC(krylov, prec, ierr) - if (n_procs_cmfd == 1) then - call PCSetType(prec, PCILU, ierr) - call PCFactorSetLevels(prec, cmfd_ilu_levels, ierr) - call KSPSetUp(krylov, ierr) - call PCFactorGetMatrix(prec, loss%M, ierr) - else - call PetscOptionsSetValue("-pc_type", "bjacobi", ierr) - call PetscOptionsSetValue("-sub_pc_type", "ilu", ierr) - call PetscOptionsSetValue("-sub_pc_factor_levels", & - trim(to_str(cmfd_ilu_levels)), ierr) - call PCSetFromOptions(prec, ierr) - call KSPSetUp(krylov, ierr) - end if - - ! get options - if (trim(cmfd_solver_type) == 'power') call KSPSetFromOptions(krylov, ierr) - - ! get all types and print - call KSPGetType(krylov, ksptype, ierr) - call PCGetType(prec, pctype, ierr) - - ! print heading information - if (cmfd_power_monitor .and. master) then - write(OUTPUT_UNIT,'(A)') '' - write(OUTPUT_UNIT,'(A)') '########################################################' - write(OUTPUT_UNIT,'(A)') '################ Power Iteration Solver ################' - write(OUTPUT_UNIT,'(A)') '########################################################' - write(OUTPUT_UNIT,'(A)') - write(OUTPUT_UNIT,100) 'Eigenvalue Tolerance:', ktol - write(OUTPUT_UNIT,100) 'Source Tolerance: ', stol - write(OUTPUT_UNIT,'(A)') '' - write(OUTPUT_UNIT,102) 'Linear Solver Type: ', ksptype - write(OUTPUT_UNIT,102) 'Preconditioner Type: ', pctype - write(OUTPUT_UNIT,101) 'ILU Fill Levels:', cmfd_ilu_levels - write(OUTPUT_UNIT,'(A)') '' - write(OUTPUT_UNIT,'(A)') '---------------------------------------------' - write(OUTPUT_UNIT,'(A)') '' - end if - - 100 FORMAT(A,1X,1PE11.4) - 101 FORMAT(A,1X,I0) - 102 FORMAT(A,1X,A) - - end subroutine precondition_matrix - !=============================================================================== ! COMPUTE_ADJOINT !=============================================================================== @@ -222,23 +141,14 @@ contains use global, only: cmfd_write_matrices - PetscViewer :: viewer - ! transpose matrices - call MatTranspose(loss%M, MAT_REUSE_MATRIX, loss%M, ierr) - call MatTranspose(prod%F, MAT_REUSE_MATRIX, prod%F, ierr) + call loss % transpose() + call prod % transpose() ! write out matrix in binary file (debugging) if (cmfd_write_matrices) then - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_lossmat.bin', & - FILE_MODE_WRITE, viewer, ierr) - call MatView(loss%M, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) - - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_prodmat.bin', & - FILE_MODE_WRITE, viewer, ierr) - call MatView(prod%F, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) + call loss % write_petsc_binary('adj_lossmat.bin') + call prod % write_petsc_binary('adj_prodmat.bin') end if end subroutine compute_adjoint @@ -250,10 +160,6 @@ contains subroutine execute_power_iter() - use constants, only: ONE - - real(8) :: num ! numerator for eigenvalue update - real(8) :: den ! denominator for eigenvalue update integer :: i ! iteration counter ! reset convergence flag @@ -263,24 +169,22 @@ contains do i = 1, 10000 ! compute source vector - call MatMult(prod%F, phi_o, S_o, ierr) + call prod % vector_multiply(phi_o % petsc_vec, S_o % petsc_vec) ! normalize source vector - call VecScale(S_o, ONE/k_o, ierr) + S_o % val = S_o % val / k_o ! compute new flux vector - call KSPSolve(krylov, S_o, phi_n, ierr) + call gmres % solve(S_o % petsc_vec, phi_n % petsc_vec) ! compute new source vector - call MatMult(prod%F, phi_n, S_n, ierr) + call prod % vector_multiply(phi_n % petsc_vec, S_n % petsc_vec) ! compute new k-eigenvalue - call VecSum(S_n, num, ierr) - call VecSum(S_o, den, ierr) - k_n = num/den + k_n = sum(S_n % val) / sum(S_o % val) ! renormalize the old source - call VecScale(S_o, k_o, ierr) + S_o % val = S_o % val * k_o ! check convergence call convergence(i) @@ -289,7 +193,7 @@ contains if (iconv) exit ! record old values - call VecCopy(phi_n, phi_o, ierr) + phi_o % val = phi_n % val k_o = k_n end do @@ -302,16 +206,14 @@ contains subroutine convergence(iter) - use global, only: cmfd_power_monitor, master + use constants, only: ONE + use global, only: cmfd_power_monitor, master use, intrinsic :: ISO_FORTRAN_ENV integer :: iter ! iteration number real(8) :: kerr ! error in keff real(8) :: serr ! error in source - real(8) :: norm_n ! L2 norm of new source - real(8) :: norm_o ! L2 norm of old source - integer :: ierr ! petsc error code ! reset convergence flag iconv = .false. @@ -320,9 +222,7 @@ contains kerr = abs(k_o - k_n)/k_n ! calculate max error in source - call VecNorm(S_n, NORM_2, norm_n, ierr) - call VecNorm(S_o, NORM_2, norm_o, ierr) - serr = abs(norm_n-norm_o)/norm_n + serr = sqrt(ONE/dble(S_n % n) * sum(((S_n % val - S_o % val)/S_n % val)**2)) ! check for convergence if(kerr < ktol .and. serr < stol) iconv = .true. @@ -341,18 +241,13 @@ contains subroutine extract_results() - use constants, only: ZERO - use global, only: cmfd, n_procs_cmfd, cmfd_write_matrices + use global, only: cmfd, cmfd_write_matrices + character(len=25) :: filename ! name of file to write data integer :: n ! problem size - integer :: row_start ! local row start - integer :: row_end ! local row end - real(8),allocatable :: mybuf(:) ! temp buffer - PetscScalar, pointer :: phi_v(:) ! pointer to eigenvector info - PetscViewer :: viewer ! petsc viewer for binary write ! get problem size - n = loss%n + n = loss % n ! also allocate in cmfd object if (adjoint_calc) then @@ -360,19 +255,13 @@ contains else if (.not. allocated(cmfd%phi)) allocate(cmfd%phi(n)) end if - if (.not. allocated(mybuf)) allocate(mybuf(n)) - ! get ownership range - call VecGetOwnershipRange(phi_n, row_start, row_end, ierr) - - ! convert petsc phi_object to cmfd_obj - call VecGetArrayF90(phi_n, phi_v, ierr) + ! save values if (adjoint_calc) then - cmfd%adj_phi(row_start+1:row_end) = phi_v + cmfd % adj_phi = phi_n % val else - cmfd%phi(row_start+1:row_end) = phi_v + cmfd % phi = phi_n % val end if - call VecRestoreArrayF90(phi_n, phi_v, ierr) ! save eigenvalue if(adjoint_calc) then @@ -381,16 +270,6 @@ contains cmfd%keff = k_n end if - ! reduce result to all - mybuf = ZERO - if (adjoint_calc) then - call MPI_ALLREDUCE(cmfd%adj_phi, mybuf, n, MPI_REAL8, MPI_SUM, & - PETSC_COMM_WORLD, ierr) - else - call MPI_ALLREDUCE(cmfd%phi, mybuf, n, MPI_REAL8, MPI_SUM, & - PETSC_COMM_WORLD, ierr) - end if - ! normalize phi to 1 if (adjoint_calc) then cmfd%adj_phi = cmfd%adj_phi/sqrt(sum(cmfd%adj_phi*cmfd%adj_phi)) @@ -401,20 +280,13 @@ contains ! write out results if (cmfd_write_matrices) then if (adjoint_calc) then - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_fluxvec.bin', & - FILE_MODE_WRITE, viewer, ierr) + filename = 'adj_fluxvec.bin' else - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'fluxvec.bin', & - FILE_MODE_WRITE, viewer, ierr) + filename = 'fluxvec.bin' end if - call VecView(phi_n, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) + call phi_n % write_petsc_binary(filename) end if - ! nullify pointer and deallocate local vars - if (associated(phi_v)) nullify(phi_v) - if (allocated(mybuf)) deallocate(mybuf) - end subroutine extract_results !=============================================================================== @@ -423,23 +295,15 @@ contains subroutine finalize() - use global, only: n_procs_cmfd - - ! finalize solver objects - call KSPDestroy(krylov, ierr) - call KSPDestroy(sub_krylov, ierr) - - ! finalize data objects - if (n_procs_cmfd > 1) call destroy_M_operator(loss) ! only destroy for jacobi - call destroy_F_operator(prod) - - call VecDestroy(phi_n, ierr) - call VecDestroy(phi_o, ierr) - call VecDestroy(S_n, ierr) - call VecDestroy(S_o, ierr) + ! destroy all objects + call gmres % destroy() + call loss % destroy() + call prod % destroy() + call phi_n % destroy() + call phi_o % destroy() + call S_n % destroy() + call S_o % destroy() end subroutine finalize -# endif - end module cmfd_power_solver diff --git a/src/cmfd_prod_operator.F90 b/src/cmfd_prod_operator.F90 index 02f2559a36..9e017013f8 100644 --- a/src/cmfd_prod_operator.F90 +++ b/src/cmfd_prod_operator.F90 @@ -37,7 +37,7 @@ contains else n = nx*ny*nz*ng end if - nz = n * ng + nnz = n * ng ! configure prod matrix call prod_matrix % create(n, nnz) @@ -68,6 +68,12 @@ contains real(8) :: nfissxs ! nufission cross section h-->g real(8) :: val ! temporary variable for nfissxs + ! get maximum number of cells in each direction + nx = cmfd%indices(1) + ny = cmfd%indices(2) + nz = cmfd%indices(3) + ng = cmfd%indices(4) + ! check for adjoint adjoint_calc = .false. if (present(adjoint)) adjoint_calc = adjoint @@ -75,6 +81,9 @@ contains ! begin iteration loops ROWS: do irow = 1, prod_matrix % n + ! add a new row to matrix + call prod_matrix % new_row() + ! get indices for that row call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) @@ -89,6 +98,7 @@ contains end if ! loop around all other groups + NFISS: do h = 1, ng ! get matrix column location @@ -107,12 +117,16 @@ contains val = nfissxs ! record value in matrix + call prod_matrix % add_value(hmat_idx, val) end do NFISS end do ROWS + ! CSR requires n+1 row + call prod_matrix % new_row() + end subroutine build_prod_matrix !=============================================================================== @@ -166,17 +180,17 @@ contains ! get indices from indexmap g = mod(irow, ng) + 1 - i = cmfd % indexmap(irow/ng+1,1) - j = cmfd % indexmap(irow/ng+1,2) - k = cmfd % indexmap(irow/ng+1,3) + i = cmfd % indexmap((irow-1)/ng+1,1) + j = cmfd % indexmap((irow-1)/ng+1,2) + k = cmfd % indexmap((irow-1)/ng+1,3) else ! compute indices - g = mod(irow, ng) + 1 - i = mod(irow, ng*nx)/ng + 1 - j = mod(irow, ng*nx*ny)/(ng*nx)+ 1 - k = mod(irow, ng*nx*ny*nz)/(ng*nx*ny) + 1 + g = mod(irow-1, ng) + 1 + i = mod(irow-1, ng*nx)/ng + 1 + j = mod(irow-1, ng*nx*ny)/(ng*nx)+ 1 + k = mod(irow-1, ng*nx*ny*nz)/(ng*nx*ny) + 1 end if diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index 82a80ad396..f402f32428 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -23,8 +23,12 @@ module matrix_header procedure :: destroy => matrix_destroy procedure :: add_value => matrix_add_value procedure :: new_row => matrix_new_row + procedure :: assemble => matrix_assemble # ifdef PETSC - procedure :: setup_petsc => matrix_setup_petsc + procedure :: setup_petsc => matrix_setup_petsc + procedure :: write_petsc_binary => matrix_write_petsc_binary + procedure :: transpose => matrix_transpose + procedure :: vector_multiply => matrix_vector_multiply # endif end type matrix @@ -100,6 +104,111 @@ contains # ifdef PETSC +!=============================================================================== +! MATRIX_ASSEMBLE +!=============================================================================== + + subroutine matrix_assemble(self) + + class(Matrix) :: self + + integer :: i + integer :: first + integer :: last + + ! loop around row vector + do i = 1, self % n + + ! get bounds + first = self % row(i) + last = self % row(i+1) - 1 + + ! sort a row + call sort_csr(self % col, self % val, first, last) + + end do + + end subroutine matrix_assemble + +!=============================================================================== +! SORT_CSR +!=============================================================================== + + recursive subroutine sort_csr(col, val, first, last) + + integer :: col(:) + integer :: first + integer :: last + real(8) :: val(:) + + integer :: mid + + if (first < last) then + call split(col, val, first, last, mid) + call sort_csr(col, val, first, mid-1) + call sort_csr(col, val, mid+1, last) + end if + + end subroutine sort_csr + +!=============================================================================== +! SPLIT +!=============================================================================== + + subroutine split(col, val, low, high, mid) + + integer :: col(:) + integer :: low + integer :: high + integer :: mid + real(8) :: val(:) + + integer :: left + integer :: right + integer :: iswap + integer :: pivot + real(8) :: rswap + real(8) :: val0 + + left = low + right = high + pivot = col(low) + val0 = val(low) + + ! repeat the following while left and right havent met + do while (left < right) + + ! scan right to left to find element < pivot + do while (left < right .and. col(right) >= pivot) + right = right - 1 + end do + + ! scan left to right to find element > pivot + do while (left < right .and. col(left) <= pivot) + left = left + 1 + end do + + ! if left and right havent met, exchange the items + if (left < right) then + iswap = col(left) + col(left) = col(right) + col(right) = iswap + rswap = val(left) + val(left) = val(right) + val(right) = rswap + end if + + end do + + ! switch the element in split position with pivot + col(low) = col(right) + col(right) = pivot + mid = right + val(low) = val(right) + val(right) = val0 + + end subroutine split + !=============================================================================== ! MATRIX_SETUP_PETSC !=============================================================================== @@ -120,6 +229,56 @@ contains end subroutine matrix_setup_petsc +!=============================================================================== +! MATRIX_WRITE_PETSC_BINARY +!=============================================================================== + + subroutine matrix_write_petsc_binary(self, filename) + + character(*) :: filename + class(Matrix) :: self + + integer :: petsc_err + PetscViewer :: viewer + + call PetscViewerBinaryOpen(PETSC_COMM_WORLD, trim(filename), & + FILE_MODE_WRITE, viewer, petsc_err) + call MatView(self % petsc_mat, viewer, petsc_err) + call PetscViewerDestroy(viewer, petsc_err) + + end subroutine matrix_write_petsc_binary + +!=============================================================================== +! MATRIX_TRANSPOSE +!=============================================================================== + + subroutine matrix_transpose(self) + + class(Matrix) :: self + + integer :: petsc_err + + call MatTranspose(self % petsc_mat, MAT_REUSE_MATRIX, self % petsc_mat, & + petsc_err) + + end subroutine matrix_transpose + +!=============================================================================== +! MATRIX_VECTOR_MULTIPLY +!=============================================================================== + + subroutine matrix_vector_multiply(self, vec_in, vec_out) + + class(Matrix) :: self + Vec :: vec_in + Vec :: vec_out + + integer :: petsc_err + call self % write_petsc_binary('prod_mat.bin') + call MatMult(self % petsc_mat, vec_in, vec_out, petsc_err) + + end subroutine matrix_vector_multiply + # endif end module matrix_header diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index c9136e4c70..8f42f53202 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -3,23 +3,20 @@ module petsc_solver implicit none private -# ifdef PETSC # include -# endif - type, public :: gmres -# ifdef PETSC + type, public :: Petsc_gmres KSP :: ksp PC :: pc -# endif contains procedure :: create => gmres_create procedure :: precondition => gmres_precondition procedure :: set_oper => gmres_set_oper procedure :: destroy => gmres_destroy - end type gmres + procedure :: solve => gmres_solve + end type Petsc_gmres - integer :: petsc_err + integer :: Petsc_err contains @@ -29,13 +26,12 @@ contains subroutine gmres_create(self) - class(gmres) :: self + class(Petsc_gmres) :: self integer :: ilu_levels = 5 real(8) :: rtol = 1.0e-10_8 real(8) :: atol = 1.0e-10_8 -# ifdef PETSC call KSPCreate(PETSC_COMM_WORLD, self % ksp, petsc_err) call KSPSetTolerances(self % ksp, rtol, atol, & PETSC_DEFAULT_DOUBLE_PRECISION, PETSC_DEFAULT_INTEGER, petsc_err) @@ -43,8 +39,6 @@ contains call KSPSetInitialGuessNonzero(self % ksp, PETSC_TRUE, petsc_err) call KSPGetPC(self % ksp, self % pc, petsc_err) call PCFactorSetLevels(self % pc, ilu_levels, petsc_err) - call KSPSetUp(self % ksp, petsc_err) -# endif end subroutine gmres_create @@ -54,12 +48,11 @@ contains subroutine gmres_precondition(self, matrix) - class(gmres) :: self + class(Petsc_gmres) :: self Mat :: matrix -# ifdef PETSC + call KSPSetUp(self % ksp, petsc_err) call PCFactorGetMatrix(self % pc, matrix, petsc_err) -# endif end subroutine gmres_precondition @@ -69,14 +62,12 @@ contains subroutine gmres_set_oper(self, prec_matrix, matrix) - class(gmres) :: self - Mat :: prec_matrix - Mat :: matrix + class(Petsc_gmres) :: self + Mat :: prec_matrix + Mat :: matrix -# ifdef PETSC call KSPSetOperators(self % ksp, matrix, prec_matrix, & SAME_NONZERO_PATTERN, petsc_err) -# endif end subroutine gmres_set_oper @@ -86,12 +77,26 @@ contains subroutine gmres_destroy(self) - class(gmres) :: self + class(Petsc_gmres) :: self -# ifdef PETSC call KSPDestroy(self % ksp, petsc_err) -# endif end subroutine gmres_destroy -end module petsc_solver +!=============================================================================== +! GMRES_SOLVE +!=============================================================================== + + subroutine gmres_solve(self, b, x) + + class(Petsc_gmres) :: self + Vec :: b + Vec :: x + + integer :: petsc_err + + call KSPSolve(self % ksp, b, x, petsc_err) + + end subroutine gmres_solve + +end module petsc_solver diff --git a/src/vector_header.F90 b/src/vector_header.F90 index 7f2abd7113..53d3bb6fe5 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -21,6 +21,7 @@ module vector_header procedure :: add_value => vector_add_value # ifdef PETSC procedure :: setup_petsc => vector_setup_petsc + procedure :: write_petsc_binary => vector_write_petsc_binary # endif end type Vector @@ -90,6 +91,24 @@ contains end subroutine vector_setup_petsc +!=============================================================================== +! VECTOR_WRITE_PETSC_BINARY +!=============================================================================== + + subroutine vector_write_petsc_binary(self, filename) + + character(*) :: filename + class(Vector) :: self + + integer :: petsc_err + PetscViewer :: viewer + + call PetscViewerBinaryOpen(PETSC_COMM_WORLD, trim(filename), & + FILE_MODE_WRITE, viewer, petsc_err) + call VecView(self % petsc_vec, viewer, petsc_err) + call PetscViewerDestroy(viewer, petsc_err) + + end subroutine vector_write_petsc_binary # endif end module vector_header From cb203502e5a48ef3174c24f59688b57f1b8370f0 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 09:10:00 -0400 Subject: [PATCH 023/192] removed cmfd message passing and re-made dependencies --- src/DEPENDENCIES | 9 --------- src/OBJECTS | 1 - 2 files changed, 10 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index f74355babb..1fdb68cfcc 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -43,9 +43,6 @@ cmfd_power_solver.o: matrix_header.o cmfd_power_solver.o: petsc_solver.o cmfd_power_solver.o: vector_header.o -cmfd_message_passing.o: cmfd_header.o -cmfd_message_passing.o: global.o - cmfd_data.o: cmfd_header.o cmfd_data.o: constants.o cmfd_data.o: error.o @@ -106,15 +103,10 @@ fission.o: search.o cmfd_jacobian_operator.o: constants.o cmfd_jacobian_operator.o: global.o -cmfd_snes_solver.o: cmfd_jacobian_operator.o -cmfd_snes_solver.o: cmfd_loss_operator.o -cmfd_snes_solver.o: cmfd_power_solver.o -cmfd_snes_solver.o: cmfd_prod_operator.o cmfd_snes_solver.o: constants.o cmfd_snes_solver.o: global.o cmfd_snes_solver.o: string.o -cmfd_input.o: cmfd_message_passing.o cmfd_input.o: error.o cmfd_input.o: global.o cmfd_input.o: mesh_header.o @@ -319,7 +311,6 @@ plot_header.o: constants.o cmfd_header.o: constants.o cmfd_execute.o: cmfd_data.o -cmfd_execute.o: cmfd_message_passing.o cmfd_execute.o: cmfd_power_solver.o cmfd_execute.o: cmfd_snes_solver.o cmfd_execute.o: constants.o diff --git a/src/OBJECTS b/src/OBJECTS index af668b4b5b..fe309726d7 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -8,7 +8,6 @@ cmfd_header.o \ cmfd_input.o \ cmfd_jacobian_operator.o \ cmfd_loss_operator.o \ -cmfd_message_passing.o \ cmfd_output.o \ cmfd_power_solver.o \ cmfd_prod_operator.o \ From f6e33c5db70397f233a920b45698ca3a4b2d1892 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 09:10:38 -0400 Subject: [PATCH 024/192] got rid of 2 group fixed data --- src/cmfd_data.F90 | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index 9943379784..8d44b3d0d7 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -22,7 +22,7 @@ contains use cmfd_header, only: allocate_cmfd use constants, only: CMFD_NOACCEL - use global, only: cmfd, cmfd_coremap, cmfd_run_2grp + use global, only: cmfd, cmfd_coremap ! initialize cmfd object if (.not.allocated(cmfd%flux)) call allocate_cmfd(cmfd) @@ -31,14 +31,11 @@ contains if ((cmfd_coremap) .and. (cmfd%mat_dim == CMFD_NOACCEL)) call set_coremap() ! calculate all cross sections based on reaction rates from last batch - if (.not. cmfd_run_2grp) call compute_xs() + call compute_xs() ! check neutron balance ! call neutron_balance(670) - ! fix 2 grp cross sections - if (cmfd_run_2grp) call fix_2_grp() - ! calculate dtilde call compute_dtilde() @@ -807,36 +804,6 @@ contains end function get_reflector_albedo -!=============================================================================== -! FIX_2_GRP modifies xs for benchmark with stand alone (homogeneous) -!=============================================================================== - - subroutine fix_2_grp() - - use global, only: cmfd - - ! overwrite cross sections - cmfd % totalxs(1,:,:,:) = 0.02597_8 - cmfd % totalxs(2,:,:,:) = 0.06669_8 - cmfd % scattxs(1,1,:,:,:) = 0.0_8 - cmfd % scattxs(1,2,:,:,:) = 0.01742_8 - cmfd % scattxs(2,1,:,:,:) = 0.0_8 - cmfd % scattxs(2,2,:,:,:) = 0.0_8 - cmfd % nfissxs(1,1,:,:,:) = 0.00536_8 - cmfd % nfissxs(1,2,:,:,:) = 0.0_8 - cmfd % nfissxs(2,1,:,:,:) = 0.10433_8 - cmfd % nfissxs(2,2,:,:,:) = 0.0_8 - cmfd % diffcof(1,:,:,:) = 1.4176_8 - cmfd % diffcof(2,:,:,:) = 0.37336_8 - cmfd % hxyz(1,:,:,:) = 0.5_8 - cmfd % hxyz(2,:,:,:) = 0.5_8 - cmfd % hxyz(3,:,:,:) = 0.5_8 - - ! set dhat reset to true - dhat_reset = .true. - - end subroutine fix_2_grp - !=============================================================================== ! FIX_NEUTRON_BALANCE !=============================================================================== From 51c78ada0feb7f8be7e92e5b64bc36e277ac2e5a Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 09:31:17 -0400 Subject: [PATCH 025/192] changed comments and updated some parts of code --- src/DEPENDENCIES | 1 + src/cmfd_data.F90 | 444 +++++++++++++++++++++++----------------------- 2 files changed, 221 insertions(+), 224 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 1fdb68cfcc..433ed14828 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -49,6 +49,7 @@ cmfd_data.o: error.o cmfd_data.o: global.o cmfd_data.o: mesh.o cmfd_data.o: mesh_header.o +cmfd_data.o: string.o cmfd_data.o: tally_header.o plot.o: constants.o diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index 8d44b3d0d7..4e81c2aaf4 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -15,31 +15,34 @@ module cmfd_data contains !============================================================================== -! SET_UP_CMFD +! SET_UP_CMFD configures cmfd object for a CMFD eigenvalue calculation !============================================================================== subroutine set_up_cmfd() use cmfd_header, only: allocate_cmfd use constants, only: CMFD_NOACCEL - use global, only: cmfd, cmfd_coremap + use global, only: cmfd, cmfd_coremap, cmfd_downscatter - ! initialize cmfd object + ! Initialize cmfd object if (.not.allocated(cmfd%flux)) call allocate_cmfd(cmfd) - ! check for core map and set it up + ! Check for core map and set it up if ((cmfd_coremap) .and. (cmfd%mat_dim == CMFD_NOACCEL)) call set_coremap() - ! calculate all cross sections based on reaction rates from last batch + ! Calculate all cross sections based on reaction rates from last batch call compute_xs() - ! check neutron balance + ! Compute effective downscatter cross section + if (cmfd_downscatter) call compute_effective_downscatter() + + ! Check neutron balance ! call neutron_balance(670) - ! calculate dtilde + ! Calculate dtilde call compute_dtilde() - ! calucate dhat + ! Calculate dhat call compute_dhat() end subroutine set_up_cmfd @@ -52,11 +55,13 @@ contains use constants, only: FILTER_MESH, FILTER_ENERGYIN, FILTER_ENERGYOUT, & FILTER_SURFACE, IN_RIGHT, OUT_RIGHT, IN_FRONT, & - OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, ONE + OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, & + ONE, TINY_BIT use error, only: fatal_error use global, only: cmfd, message, n_cmfd_tallies, cmfd_tallies, meshes use mesh, only: mesh_indices_to_bin use mesh_header, only: StructuredMesh + use string, only: to_str use tally_header, only: TallyObject integer :: nx ! number of mesh cells in x direction @@ -80,30 +85,30 @@ contains type(TallyObject), pointer :: t => null() ! pointer for tally object type(StructuredMesh), pointer :: m => null() ! pointer for mesh object - ! extract spatial and energy indices from object + ! Extract spatial and energy indices from object nx = cmfd % indices(1) ny = cmfd % indices(2) nz = cmfd % indices(3) ng = cmfd % indices(4) - ! set flux object and source distribution to all zeros + ! Set flux object and source distribution to all zeros cmfd % flux = ZERO cmfd % openmc_src = ZERO - ! associate tallies and mesh + ! Associate tallies and mesh t => cmfd_tallies(1) i_mesh = t % filters(t % find_filter(FILTER_MESH)) % int_bins(1) m => meshes(i_mesh) - ! set mesh widths + ! Set mesh widths cmfd % hxyz(1,:,:,:) = m % width(1) ! set x width cmfd % hxyz(2,:,:,:) = m % width(2) ! set y width cmfd % hxyz(3,:,:,:) = m % width(3) ! set z width - ! begin loop around tallies + ! Begin loop around tallies TAL: do ital = 1, n_cmfd_tallies - ! associate tallies and mesh + ! Associate tallies and mesh t => cmfd_tallies(ital) i_mesh = t % filters(t % find_filter(FILTER_MESH)) % int_bins(1) m => meshes(i_mesh) @@ -113,98 +118,99 @@ contains i_filter_eout = t % find_filter(FILTER_ENERGYOUT) i_filter_surf = t % find_filter(FILTER_SURFACE) - ! begin loop around space + ! Begin loop around space ZLOOP: do k = 1,nz YLOOP: do j = 1,ny XLOOP: do i = 1,nx - ! check for active mesh cell + ! Check for active mesh cell if (allocated(cmfd%coremap)) then if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) then cycle end if end if - ! loop around energy groups + ! Loop around energy groups OUTGROUP: do h = 1,ng - ! start tally 1 + ! Start tally 1 TALLY: if (ital == 1) then - ! reset all bins to 1 + ! Reset all bins to 1 t % matching_bins = 1 - ! set ijk as mesh indices + ! Set ijk as mesh indices ijk = (/ i, j, k /) - ! get bin number for mesh indices + ! Get bin number for mesh indices t % matching_bins(i_filter_mesh) = mesh_indices_to_bin(m,ijk) - ! apply energy in filter + ! Apply energy in filter if (i_filter_ein > 0) then t % matching_bins(i_filter_ein) = ng - h + 1 end if - ! calculate score index from bins + ! Calculate score index from bins score_index = sum((t % matching_bins - 1) * t%stride) + 1 - ! get flux + ! Get flux flux = t % results(1,score_index) % sum cmfd % flux(h,i,j,k) = flux - ! detect zero flux - if ((flux - 0.0D0) < 1.0E-10_8) then - print *,h,i,j,k,flux - message = 'Detected zero flux without coremap overlay' + ! Detect zero flux, abort if located + if ((flux - ZERO) < TINY_BIT) then + message = 'Detected zero flux without coremap overlay at: (' & + // to_str(i) // ',' // to_str(j) // ',' // to_str(k) & + // ') in group ' // to_str(h) call fatal_error() end if - ! get total rr and convert to total xs + ! Get total rr and convert to total xs cmfd % totalxs(h,i,j,k) = t % results(2,score_index) % sum / flux - ! get p1 scatter rr and convert to p1 scatter xs + ! Get p1 scatter rr and convert to p1 scatter xs cmfd % p1scattxs(h,i,j,k) = t % results(3,score_index) % sum / flux - ! calculate diffusion coefficient + ! Calculate diffusion coefficient cmfd % diffcof(h,i,j,k) = ONE/(3.0_8*(cmfd % totalxs(h,i,j,k) - & cmfd % p1scattxs(h,i,j,k))) else if (ital == 2) then - ! begin loop to get energy out tallies + ! Begin loop to get energy out tallies INGROUP: do g = 1, ng - ! reset all bins to 1 + ! Reset all bins to 1 t % matching_bins = 1 - ! set ijk as mesh indices + ! Set ijk as mesh indices ijk = (/ i, j, k /) - ! get bin number for mesh indices + ! Get bin number for mesh indices t % matching_bins(i_filter_mesh) = mesh_indices_to_bin(m,ijk) if (i_filter_ein > 0) then - ! apply energy in filter + ! Apply energy in filter t % matching_bins(i_filter_ein) = ng - h + 1 - ! set energy out bin + ! Set energy out bin t % matching_bins(i_filter_eout) = ng - g + 1 end if - ! calculate score index from bins + ! Calculate score index from bins score_index = sum((t % matching_bins - 1) * t%stride) + 1 - ! get scattering + ! Get scattering cmfd % scattxs(h,g,i,j,k) = t % results(1,score_index) % sum /& cmfd % flux(h,i,j,k) - ! get nu-fission + ! Get nu-fission cmfd % nfissxs(h,g,i,j,k) = t % results(2,score_index) % sum /& cmfd % flux(h,i,j,k) - ! bank source + ! Bank source cmfd % openmc_src(g,i,j,k) = cmfd % openmc_src(g,i,j,k) + & t % results(2,score_index) % sum @@ -212,13 +218,13 @@ contains else if (ital == 3) then - ! initialize and filter for energy + ! Initialize and filter for energy t % matching_bins = 1 if (i_filter_ein > 0) then t % matching_bins(i_filter_ein) = ng - h + 1 end if - ! left surface + ! Left surface t % matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i-1, j, k /) + 1, .true.) t % matching_bins(i_filter_surf) = IN_RIGHT @@ -228,7 +234,7 @@ contains score_index = sum((t % matching_bins - 1) * t % stride) + 1 ! incoming cmfd % current(2,h,i,j,k) = t % results(1,score_index) % sum - ! right surface + ! Right surface t % matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /) + 1, .true.) t % matching_bins(i_filter_surf) = IN_RIGHT @@ -238,7 +244,7 @@ contains score_index = sum((t % matching_bins - 1) * t % stride) + 1 ! outgoing cmfd % current(4,h,i,j,k) = t % results(1,score_index) % sum - ! back surface + ! Back surface t % matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j-1, k /) + 1, .true.) t % matching_bins(i_filter_surf) = IN_FRONT @@ -248,7 +254,7 @@ contains score_index = sum((t % matching_bins - 1) * t % stride) + 1 ! incoming cmfd % current(6,h,i,j,k) = t % results(1,score_index) % sum - ! front surface + ! Front surface t % matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /) + 1, .true.) t % matching_bins(i_filter_surf) = IN_FRONT @@ -258,7 +264,7 @@ contains score_index = sum((t % matching_bins - 1) * t % stride) + 1 ! outgoing cmfd % current(8,h,i,j,k) = t % results(1,score_index) % sum - ! bottom surface + ! Bottom surface t % matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k-1 /) + 1, .true.) t % matching_bins(i_filter_surf) = IN_TOP @@ -268,7 +274,7 @@ contains score_index = sum((t % matching_bins - 1) * t % stride) + 1 ! incoming cmfd % current(10,h,i,j,k) = t % results(1,score_index) % sum - ! top surface + ! Top surface t % matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, & (/ i, j, k /) + 1, .true.) t % matching_bins(i_filter_surf) = IN_TOP @@ -290,10 +296,10 @@ contains end do TAL - ! normalize openmc source distribution + ! Normalize openmc source distribution cmfd % openmc_src = cmfd % openmc_src/sum(cmfd % openmc_src)*cmfd%norm - ! nullify all pointers + ! Nullify all pointers if (associated(t)) nullify(t) if (associated(m)) nullify(m) @@ -308,42 +314,42 @@ contains use constants, only: CMFD_NOACCEL use global, only: cmfd - integer :: kount=1 ! counter for unique fuel assemblies - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z + integer :: kount=1 ! counter for unique fuel assemblies + integer :: nx ! number of mesh cells in x direction + integer :: ny ! number of mesh cells in y direction + integer :: nz ! number of mesh cells in z direction + integer :: i ! iteration counter for x + integer :: j ! iteration counter for y + integer :: k ! iteration counter for z - ! extract spatial indices from object + ! Extract spatial indices from object nx = cmfd % indices(1) ny = cmfd % indices(2) nz = cmfd % indices(3) - ! count how many fuel assemblies exist + ! Count how many fuel assemblies exist cmfd % mat_dim = sum(cmfd % coremap - 1) - ! allocate indexmap + ! Allocate indexmap if (.not. allocated(cmfd % indexmap)) & allocate(cmfd % indexmap(cmfd % mat_dim,3)) - ! begin loops over spatial indices + ! Begin loops over spatial indices ZLOOP: do k = 1, nz YLOOP: do j = 1, ny XLOOP: do i = 1, nx - ! check for reflector + ! Check for reflector if (cmfd % coremap(i,j,k) == 1) then - ! reset value to 99999 + ! reset value to CMFD no acceleration constant cmfd % coremap(i,j,k) = CMFD_NOACCEL else - ! must be a fuel --> give unique id number + ! Must be a fuel --> give unique id number cmfd % coremap(i,j,k) = kount cmfd % indexmap(kount,1) = i cmfd % indexmap(kount,2) = j @@ -386,19 +392,19 @@ contains real(8) :: fission ! fission term in neutron balance real(8) :: res ! residual of neutron balance - ! check if keff is close to 0 (happens on first active batch) + ! Check if keff is close to 0 (happens on first active batch) if (keff < 1e-8_8) return - ! extract spatial and energy indices from object + ! Extract spatial and energy indices from object nx = cmfd % indices(1) ny = cmfd % indices(2) nz = cmfd % indices(3) ng = cmfd % indices(4) - ! allocate res dataspace + ! Allocate res dataspace if (.not. allocated(cmfd%resnb)) allocate(cmfd%resnb(ng,nx,ny,nz)) - ! begin loop around space and energy groups + ! Begin loop around space and energy groups ZLOOP: do k = 1, nz YLOOP: do j = 1, ny @@ -407,7 +413,7 @@ contains GROUPG: do g = 1, ng - ! check for active mesh + ! Check for active mesh if (allocated(cmfd%coremap)) then if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) then cmfd%resnb(g,i,j,k) = CMFD_NORES @@ -415,7 +421,7 @@ contains end if end if - ! get leakage + ! Get leakage leakage = ZERO LEAK: do l = 1, 3 @@ -426,10 +432,10 @@ contains end do LEAK - ! interactions + ! Interactions interactions = cmfd % totalxs(g,i,j,k) * cmfd % flux(g,i,j,k) - ! get scattering and fission + ! Get scattering and fission scattering = ZERO fission = ZERO GROUPH: do h = 1, ng @@ -442,16 +448,16 @@ contains end do GROUPH - ! compute residual + ! Compute residual res = leakage + interactions - scattering - (ONE/keff)*fission - ! normalize by flux + ! Normalize by flux res = res/cmfd%flux(g,i,j,k) - ! bank res in cmfd object + ! Bank res in cmfd object cmfd%resnb(g,i,j,k) = res - ! write out info to file + ! Write out info to file write(uid,'(A,1X,I0,1X,I0,1X,I0,1X,A,1X,I0,1X,A,1PE11.4)') & 'Location',i,j,k,' Group:',g,'Balance:',res write(uid,100) 'Leakage:',leakage @@ -475,7 +481,7 @@ contains end subroutine neutron_balance !=============================================================================== -! COMPUTE_DTILDE computes the diffusion coupling coefficient +! COMPUTE_DTILDE precomputes the diffusion coupling coefficient !=============================================================================== subroutine compute_dtilde() @@ -483,44 +489,44 @@ contains use constants, only: CMFD_NOACCEL, ZERO_FLUX, TINY_BIT use global, only: cmfd, cmfd_coremap - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups - integer :: nxyz(3,2) ! single vector containing boundary locations - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: l ! iteration counter for leakages - integer :: xyz_idx ! index for determining if x,y or z leakage - integer :: dir_idx ! index for determining - or + face of cell - integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: neig_idx(3) ! spatial indices of neighbour - integer :: bound(6) ! vector containing indices for boudary check - real(8) :: albedo(6) ! albedo vector with global boundaries - real(8) :: cell_dc ! diffusion coef of current cell - real(8) :: cell_hxyz(3) ! cell dimensions of current ijk cell - real(8) :: neig_dc ! diffusion coefficient of neighbor cell - real(8) :: neig_hxyz(3) ! cell dimensions of neighbor cell - real(8) :: dtilde ! finite difference coupling parameter - real(8) :: ref_albedo ! albedo to reflector + integer :: nx ! maximum number of cells in x direction + integer :: ny ! maximum number of cells in y direction + integer :: nz ! maximum number of cells in z direction + integer :: ng ! maximum number of energy groups + integer :: nxyz(3,2) ! single vector containing boundary locations + integer :: i ! iteration counter for x + integer :: j ! iteration counter for y + integer :: k ! iteration counter for z + integer :: g ! iteration counter for groups + integer :: l ! iteration counter for leakages + integer :: xyz_idx ! index for determining if x,y or z leakage + integer :: dir_idx ! index for determining - or + face of cell + integer :: shift_idx ! parameter to shift index by +1 or -1 + integer :: neig_idx(3) ! spatial indices of neighbour + integer :: bound(6) ! vector containing indices for boudary check + real(8) :: albedo(6) ! albedo vector with global boundaries + real(8) :: cell_dc ! diffusion coef of current cell + real(8) :: cell_hxyz(3) ! cell dimensions of current ijk cell + real(8) :: neig_dc ! diffusion coefficient of neighbor cell + real(8) :: neig_hxyz(3) ! cell dimensions of neighbor cell + real(8) :: dtilde ! finite difference coupling parameter + real(8) :: ref_albedo ! albedo to reflector - ! get maximum of spatial and group indices + ! Get maximum of spatial and group indices nx = cmfd%indices(1) ny = cmfd%indices(2) nz = cmfd%indices(3) ng = cmfd%indices(4) - ! create single vector of these indices for boundary calculation + ! Create single vector of these indices for boundary calculation nxyz(1,:) = (/1,nx/) nxyz(2,:) = (/1,ny/) nxyz(3,:) = (/1,nz/) - ! get boundary condition information + ! Get boundary condition information albedo = cmfd%albedo - ! geting loop over group and spatial indices + ! Loop over group and spatial indices ZLOOP: do k = 1, nz YLOOP: do j = 1, ny @@ -529,63 +535,63 @@ contains GROUP: do g = 1, ng - ! check for active mesh cell + ! Check for active mesh cell if (allocated(cmfd%coremap)) then if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) cycle end if - ! get cell data + ! Get cell data cell_dc = cmfd%diffcof(g,i,j,k) cell_hxyz = cmfd%hxyz(:,i,j,k) - ! setup of vector to identify boundary conditions + ! Setup of vector to identify boundary conditions bound = (/i,i,j,j,k,k/) - ! begin loop around sides of cell for leakage + ! Begin loop around sides of cell for leakage LEAK: do l = 1, 6 - ! define xyz and +/- indices + ! Define xyz and +/- indices xyz_idx = int(ceiling(real(l)/real(2))) ! x=1, y=2, z=3 dir_idx = 2 - mod(l,2) ! -=1, +=2 - shift_idx = -2*mod(l,2) + 1 ! shift neig by -1 or +1 + shift_idx = -2*mod(l,2) + 1 ! shift neig by -1 or +1 - ! check if at a boundary + ! Check if at a boundary if (bound(l) == nxyz(xyz_idx,dir_idx)) then - ! compute dtilde + ! Compute dtilde with albedo boundary condition dtilde = (2*cell_dc*(1-albedo(l)))/(4*cell_dc*(1+albedo(l)) + & (1-albedo(l))*cell_hxyz(xyz_idx)) - ! check for zero flux + ! Check for zero flux if (abs(albedo(l) - ZERO_FLUX) < TINY_BIT) dtilde = 2*cell_dc / & cell_hxyz(xyz_idx) else ! not a boundary - ! compute neighboring cell indices + ! Compute neighboring cell indices neig_idx = (/i,j,k/) ! begin with i,j,k neig_idx(xyz_idx) = shift_idx + neig_idx(xyz_idx) - ! get neigbor cell data + ! Get neigbor cell data neig_dc = cmfd%diffcof(g,neig_idx(1),neig_idx(2),neig_idx(3)) neig_hxyz = cmfd%hxyz(:,neig_idx(1),neig_idx(2),neig_idx(3)) - ! check for fuel-reflector interface + ! Check for fuel-reflector interface if (cmfd_coremap) then if (cmfd % coremap(neig_idx(1),neig_idx(2),neig_idx(3)) == & CMFD_NOACCEL .and. cmfd % coremap(i,j,k) /= CMFD_NOACCEL) then - ! get albedo + ! Get albedo ref_albedo = get_reflector_albedo(l,g,i,j,k) - ! compute dtilde + ! Compute dtilde dtilde = (2*cell_dc*(1-ref_albedo))/(4*cell_dc*(1+ & ref_albedo)+(1-ref_albedo)*cell_hxyz(xyz_idx)) - else ! not next to a reflector or no core map + else ! Not next to a reflector or no core map - ! compute dtilde + ! Compute dtilde to neighbor cell dtilde = (2*cell_dc*neig_dc)/(neig_hxyz(xyz_idx)*cell_dc + & cell_hxyz(xyz_idx)*neig_dc) @@ -593,7 +599,7 @@ contains else ! no core map - ! compute dtilde + ! Compute dtilde to neighbor cell dtilde = (2*cell_dc*neig_dc)/(neig_hxyz(xyz_idx)*cell_dc + & cell_hxyz(xyz_idx)*neig_dc) @@ -601,7 +607,7 @@ contains end if - ! record dtilde in cmfd object + ! Record dtilde in cmfd object cmfd%dtilde(l,g,i,j,k) = dtilde end do LEAK @@ -625,40 +631,40 @@ contains use constants, only: CMFD_NOACCEL, ZERO use global, only: cmfd, cmfd_coremap - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups - integer :: nxyz(3,2) ! single vector containing boundary locations - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: l ! iteration counter for leakages - integer :: xyz_idx ! index for determining if x,y or z leakage - integer :: dir_idx ! index for determining - or + face of cell - integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: neig_idx(3) ! spatial indices of neighbour - integer :: bound(6) ! vector containing indices for boudary check - real(8) :: cell_dtilde(6) ! cell dtilde for each face - real(8) :: cell_flux ! flux in current cell - real(8) :: current(12) ! area integrated cell current at each face - real(8) :: net_current ! net current on a face - real(8) :: neig_flux ! flux in neighbor cell - real(8) :: dhat ! dhat equivalence parameter + integer :: nx ! maximum number of cells in x direction + integer :: ny ! maximum number of cells in y direction + integer :: nz ! maximum number of cells in z direction + integer :: ng ! maximum number of energy groups + integer :: nxyz(3,2) ! single vector containing boundary locations + integer :: i ! iteration counter for x + integer :: j ! iteration counter for y + integer :: k ! iteration counter for z + integer :: g ! iteration counter for groups + integer :: l ! iteration counter for leakages + integer :: xyz_idx ! index for determining if x,y or z leakage + integer :: dir_idx ! index for determining - or + face of cell + integer :: shift_idx ! parameter to shift index by +1 or -1 + integer :: neig_idx(3) ! spatial indices of neighbour + integer :: bound(6) ! vector containing indices for boudary check + real(8) :: cell_dtilde(6) ! cell dtilde for each face + real(8) :: cell_flux ! flux in current cell + real(8) :: current(12) ! area integrated cell current at each face + real(8) :: net_current ! net current on a face + real(8) :: neig_flux ! flux in neighbor cell + real(8) :: dhat ! dhat equivalence parameter - ! get maximum of spatial and group indices + ! Get maximum of spatial and group indices nx = cmfd%indices(1) ny = cmfd%indices(2) nz = cmfd%indices(3) ng = cmfd%indices(4) - ! create single vector of these indices for boundary calculation + ! Create single vector of these indices for boundary calculation nxyz(1,:) = (/1,nx/) nxyz(2,:) = (/1,ny/) nxyz(3,:) = (/1,nz/) - ! geting loop over group and spatial indices + ! Geting loop over group and spatial indices ZLOOP: do k = 1,nz YLOOP: do j = 1,ny @@ -667,51 +673,51 @@ contains GROUP: do g = 1,ng - ! check for active mesh cell + ! Check for active mesh cell if (allocated(cmfd%coremap)) then if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) then cycle end if end if - ! get cell data + ! Get cell data cell_dtilde = cmfd%dtilde(:,g,i,j,k) cell_flux = cmfd%flux(g,i,j,k)/product(cmfd%hxyz(:,i,j,k)) current = cmfd%current(:,g,i,j,k) - ! setup of vector to identify boundary conditions + ! Setup of vector to identify boundary conditions bound = (/i,i,j,j,k,k/) - ! begin loop around sides of cell for leakage + ! Begin loop around sides of cell for leakage LEAK: do l = 1,6 - ! define xyz and +/- indices + ! Define xyz and +/- indices xyz_idx = int(ceiling(real(l)/real(2))) ! x=1, y=2, z=3 dir_idx = 2 - mod(l,2) ! -=1, +=2 shift_idx = -2*mod(l,2) +1 ! shift neig by -1 or +1 - ! calculate net current on l face (divided by surf area) + ! Calculate net current on l face (divided by surf area) net_current = (current(2*l) - current(2*l-1)) / & product(cmfd%hxyz(:,i,j,k)) * cmfd%hxyz(xyz_idx,i,j,k) - ! check if at a boundary + ! Check if at a boundary if (bound(l) == nxyz(xyz_idx,dir_idx)) then - ! compute dhat + ! Compute dhat dhat = (net_current - shift_idx*cell_dtilde(l)*cell_flux) / & cell_flux else ! not a boundary - ! compute neighboring cell indices + ! Compute neighboring cell indices neig_idx = (/i,j,k/) ! begin with i,j,k neig_idx(xyz_idx) = shift_idx + neig_idx(xyz_idx) - ! get neigbor flux + ! Get neigbor flux neig_flux = cmfd%flux(g,neig_idx(1),neig_idx(2),neig_idx(3)) / & product(cmfd%hxyz(:,neig_idx(1),neig_idx(2),neig_idx(3))) - ! check for fuel-reflector interface + ! Check for fuel-reflector interface if (cmfd_coremap) then if (cmfd % coremap(neig_idx(1),neig_idx(2),neig_idx(3)) == & @@ -723,7 +729,7 @@ contains else ! not a fuel-reflector interface - ! compute dhat + ! Compute dhat dhat = (net_current + shift_idx*cell_dtilde(l)* & (neig_flux - cell_flux))/(neig_flux + cell_flux) @@ -731,7 +737,7 @@ contains else ! not for fuel-reflector case - ! compute dhat + ! Compute dhat dhat = (net_current + shift_idx*cell_dtilde(l)* & (neig_flux - cell_flux))/(neig_flux + cell_flux) @@ -739,13 +745,6 @@ contains end if - ! check for zero net current -! if ((abs(current(2*l)-current(2*l-1)) < 1e-8_8).and.xyz_idx/=3) then -! print *,'Zero net current interface',g,i,j,k -! print *,current(2*l),current(2*l-1),net_current -! dhat = ZERO -! end if - ! record dhat in cmfd object cmfd%dhat(l,g,i,j,k) = dhat @@ -784,13 +783,13 @@ contains real(8) :: current(12) ! partial currents for all faces of mesh cell real(8) :: albedo ! the albedo - ! get partial currents from object + ! Get partial currents from object current = cmfd%current(:,g,i,j,k) - ! define xyz and +/- indices + ! Define xyz and +/- indices shift_idx = -2*mod(l,2) + 1 ! shift neig by -1 or +1 - ! calculate albedo + ! Calculate albedo if ((shift_idx == 1 .and. current(2*l ) < 1.0e-10_8) .or. & (shift_idx == -1 .and. current(2*l-1) < 1.0e-10_8)) then albedo = ALBEDO_REJECT @@ -799,7 +798,7 @@ contains albedo = (current(2*l-1)/current(2*l))**(shift_idx) end if - ! assign to function variable + ! Assign to function variable get_reflector_albedo = albedo end function get_reflector_albedo @@ -814,54 +813,54 @@ contains use global, only: cmfd, keff use, intrinsic :: ISO_FORTRAN_ENV - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: ng ! number of energy groups - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: l ! iteration counter for surface - real(8) :: leak1 ! leakage rate in group 1 - real(8) :: leak2 ! leakage rate in group 2 - real(8) :: flux1 ! group 1 volume int flux - real(8) :: flux2 ! group 2 volume int flux - real(8) :: sigt1 ! group 1 total xs - real(8) :: sigt2 ! group 2 total xs - real(8) :: sigs11 ! scattering transfer 1 --> 1 - real(8) :: sigs21 ! scattering transfer 2 --> 1 - real(8) :: sigs12 ! scattering transfer 1 --> 2 - real(8) :: sigs22 ! scattering transfer 2 --> 2 - real(8) :: nsigf11 ! fission transfer 1 --> 1 - real(8) :: nsigf21 ! fission transfer 2 --> 1 - real(8) :: nsigf12 ! fission transfer 1 --> 2 - real(8) :: nsigf22 ! fission transfer 2 --> 2 - real(8) :: siga1 ! group 1 abs xs - real(8) :: siga2 ! group 2 abs xs - real(8) :: sigs12_eff ! effective downscatter xs + integer :: nx ! number of mesh cells in x direction + integer :: ny ! number of mesh cells in y direction + integer :: nz ! number of mesh cells in z direction + integer :: ng ! number of energy groups + integer :: i ! iteration counter for x + integer :: j ! iteration counter for y + integer :: k ! iteration counter for z + integer :: l ! iteration counter for surface + real(8) :: leak1 ! leakage rate in group 1 + real(8) :: leak2 ! leakage rate in group 2 + real(8) :: flux1 ! group 1 volume int flux + real(8) :: flux2 ! group 2 volume int flux + real(8) :: sigt1 ! group 1 total xs + real(8) :: sigt2 ! group 2 total xs + real(8) :: sigs11 ! scattering transfer 1 --> 1 + real(8) :: sigs21 ! scattering transfer 2 --> 1 + real(8) :: sigs12 ! scattering transfer 1 --> 2 + real(8) :: sigs22 ! scattering transfer 2 --> 2 + real(8) :: nsigf11 ! fission transfer 1 --> 1 + real(8) :: nsigf21 ! fission transfer 2 --> 1 + real(8) :: nsigf12 ! fission transfer 1 --> 2 + real(8) :: nsigf22 ! fission transfer 2 --> 2 + real(8) :: siga1 ! group 1 abs xs + real(8) :: siga2 ! group 2 abs xs + real(8) :: sigs12_eff ! effective downscatter xs - ! extract spatial and energy indices from object + ! Extract spatial and energy indices from object nx = cmfd % indices(1) ny = cmfd % indices(2) nz = cmfd % indices(3) ng = cmfd % indices(4) - ! return if not two groups + ! Return if not two groups if (ng /= 2) return - ! begin loop around space and energy groups + ! Begin loop around space and energy groups ZLOOP: do k = 1, nz YLOOP: do j = 1, ny XLOOP: do i = 1, nx - ! check for active mesh + ! Check for active mesh if (allocated(cmfd%coremap)) then if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) cycle end if - ! compute leakage in groups 1 and 2 + ! Compute leakage in groups 1 and 2 leak1 = ZERO leak2 = ZERO LEAK: do l = 1, 3 @@ -879,7 +878,7 @@ contains end do LEAK - ! extract cross sections and flux from object + ! Extract cross sections and flux from object flux1 = cmfd % flux(1,i,j,k) flux2 = cmfd % flux(2,i,j,k) sigt1 = cmfd % totalxs(1,i,j,k) @@ -893,37 +892,37 @@ contains nsigf12 = cmfd % nfissxs(1,2,i,j,k) nsigf22 = cmfd % nfissxs(2,2,i,j,k) - ! check for no fission into group 2 + ! Check for no fission into group 2 if (.not.(nsigf12 < 1e-6_8 .and. nsigf22 < 1e-6_8)) then write(OUTPUT_UNIT,'(A,1PE11.4,1X,1PE11.4)') 'Fission in G=2', & nsigf12,nsigf22 end if - ! compute absorption xs + ! Compute absorption xs siga1 = sigt1 - sigs11 - sigs12 siga2 = sigt2 - sigs22 - sigs21 - ! compute effective downscatter xs + ! Compute effective downscatter xs sigs12_eff = (ONE/keff*nsigf11*flux1 - leak1 - siga1*flux1 & - ONE/keff*nsigf21/siga2*leak2 ) / ( flux1*(ONE & - ONE/keff*nsigf21/siga2)) - ! redefine flux 2 + ! Redefine flux 2 flux2 = (sigs12_eff*flux1 - leak2)/siga2 cmfd % flux(2,i,j,k) = flux2 - ! recompute total cross sections (use effective and no upscattering) + ! Recompute total cross sections (use effective and no upscattering) sigt1 = siga1 + sigs11 + sigs12_eff sigt2 = siga2 + sigs22 - ! record total xs + ! Record total xs cmfd % totalxs(1,i,j,k) = sigt1 cmfd % totalxs(2,i,j,k) = sigt2 - ! record effective downscatter xs + ! Record effective downscatter xs cmfd % scattxs(1,2,i,j,k) = sigs12_eff - ! zero out upscatter cross section + ! Zero out upscatter cross section cmfd % scattxs(2,1,i,j,k) = ZERO end do XLOOP @@ -935,7 +934,7 @@ contains end subroutine fix_neutron_balance !=============================================================================== -! FIX_NEUTRON_BALANCE +! COMPUTE_EFFECTIVE_DOWNSCATTER changes downscatter rate for zero upscatter !=============================================================================== subroutine compute_effective_downscatter() @@ -962,31 +961,28 @@ contains real(8) :: siga2 ! group 2 abs xs real(8) :: sigs12_eff ! effective downscatter xs - ! check for balance - if (.not. cmfd_downscatter) return - - ! extract spatial and energy indices from object + ! Extract spatial and energy indices from object nx = cmfd % indices(1) ny = cmfd % indices(2) nz = cmfd % indices(3) ng = cmfd % indices(4) - ! return if not two groups + ! Return if not two groups if (ng /= 2) return - ! begin loop around space and energy groups + ! Begin loop around space and energy groups ZLOOP: do k = 1, nz YLOOP: do j = 1, ny XLOOP: do i = 1, nx - ! check for active mesh + ! Check for active mesh if (allocated(cmfd%coremap)) then if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) cycle end if - ! extract cross sections and flux from object + ! Extract cross sections and flux from object flux1 = cmfd % flux(1,i,j,k) flux2 = cmfd % flux(2,i,j,k) sigt1 = cmfd % totalxs(1,i,j,k) @@ -996,25 +992,25 @@ contains sigs12 = cmfd % scattxs(1,2,i,j,k) sigs22 = cmfd % scattxs(2,2,i,j,k) - ! compute absorption xs + ! Compute absorption xs siga1 = sigt1 - sigs11 - sigs12 siga2 = sigt2 - sigs22 - sigs21 - ! compute effective downscatter xs + ! Compute effective downscatter xs sigs12_eff = sigs12 - sigs21*flux2/flux1 - ! recompute total cross sections (use effective and no upscattering) + ! Recompute total cross sections (use effective and no upscattering) sigt1 = siga1 + sigs11 + sigs12_eff sigt2 = siga2 + sigs22 - ! record total xs + ! Record total xs cmfd % totalxs(1,i,j,k) = sigt1 cmfd % totalxs(2,i,j,k) = sigt2 - ! record effective downscatter xs + ! Record effective downscatter xs cmfd % scattxs(1,2,i,j,k) = sigs12_eff - ! zero out upscatter cross section + ! Zero out upscatter cross section cmfd % scattxs(2,1,i,j,k) = ZERO end do XLOOP From 989e3b067c7d93c748bbaf6ea8b7127adfab253d Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 09:51:16 -0400 Subject: [PATCH 026/192] cleaned up comments and some bits of code --- src/cmfd_execute.F90 | 313 ++++++++++++++++++++----------------------- 1 file changed, 145 insertions(+), 168 deletions(-) diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 7588d11a52..8f94bbe18a 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -5,60 +5,39 @@ module cmfd_execute ! cross section generation, diffusion calculation, and source re-weighting !============================================================================== + use global + implicit none private public :: execute_cmfd, cmfd_init_batch -# ifdef PETSC -# include -# endif - contains !============================================================================== -! EXECUTE_CMFD +! EXECUTE_CMFD runs the CMFD calculation !============================================================================== subroutine execute_cmfd() -# ifdef PETSC - use cmfd_data, only: set_up_cmfd - use cmfd_message_passing, only: petsc_init_mpi, cmfd_bcast use cmfd_power_solver, only: cmfd_power_execute use cmfd_snes_solver, only: cmfd_snes_execute use error, only: warning, fatal_error - use global, only: n_procs_cmfd, cmfd, & - cmfd_solver_type, time_cmfd, & - cmfd_run_adjoint, cmfd_write_hdf5, & - cmfd_feedback,cmfd_hold_weights, & - cmfd_inact_flush, cmfd_keff_tol, & - cmfd_act_flush, current_batch, keff, & - n_batches, message, master, mpi_err, rank - ! stop cmfd timer + ! CMFD single processor on master if (master) then + + ! Start cmfd timer call time_cmfd % start() - end if - ! filter processors (lowest PETSc group) - if (rank < n_procs_cmfd) then + ! Create cmfd data from OpenMC tallies + call set_up_cmfd() - ! set up cmfd data (master only) - if (master) call set_up_cmfd() - - ! broadcast cmfd to all petsc procs - call cmfd_bcast() - - ! process solver options + ! Process solver options call process_cmfd_options() - end if - - ! filter processors (lowest PETSc group) - if (rank < n_procs_cmfd) then - - ! call solver + ! Call solver + call time_solver % start() if (trim(cmfd_solver_type) == 'power') then call cmfd_power_execute() elseif (trim(cmfd_solver_type) == 'jfnk') then @@ -67,20 +46,18 @@ contains message = 'solver type became invalid after input processing' call fatal_error() end if + call time_solver % stop() - ! perform any last batch tasks - if (current_batch == n_batches) then - - ! check for adjoint run - if (cmfd_run_adjoint) then - if (trim(cmfd_solver_type) == 'power') then - call cmfd_power_execute(adjoint = .true.) - elseif (trim(cmfd_solver_type) == 'jfnk') then - call cmfd_snes_execute(adjoint = .true.) - end if + ! check to perform adjoint on last batch + call time_solver % start() + if (current_batch == n_batches .and. cmfd_run_adjoint) then + if (trim(cmfd_solver_type) == 'power') then + call cmfd_power_execute(adjoint = .true.) + elseif (trim(cmfd_solver_type) == 'jfnk') then + call cmfd_snes_execute(adjoint = .true.) end if - end if + call time_solver % stop() end if @@ -91,19 +68,12 @@ contains if (cmfd_feedback) call cmfd_reweight(.true.) ! stop cmfd timer - if (master) then - call time_cmfd % stop() - end if - - ! wait here for all procs - call MPI_Barrier(MPI_COMM_WORLD, mpi_err) - -# endif + if (master) call time_cmfd % stop() end subroutine execute_cmfd !============================================================================== -! CMFD_INIT_BATCH +! CMFD_INIT_BATCH handles cmfd options at the start of every batch !============================================================================== subroutine cmfd_init_batch() @@ -112,7 +82,7 @@ contains cmfd_inact_flush, cmfd_act_flush, cmfd_run, & current_batch, cmfd_hold_weights - ! check to activate CMFD diffusion and possible feedback + ! Check to activate CMFD diffusion and possible feedback ! this guarantees that when cmfd begins at least one batch of tallies are ! accumulated if (cmfd_run .and. cmfd_begin == current_batch) then @@ -120,18 +90,16 @@ contains cmfd_tally_on = .true. end if - ! check to flush cmfd tallies for active batches, no more inactive flush + ! Check to flush cmfd tallies for active batches, no more inactive flush if (cmfd_run .and. cmfd_act_flush == current_batch) then call cmfd_tally_reset() cmfd_tally_on = .true. cmfd_inact_flush(2) = -1 end if - ! check to flush cmfd tallies during inactive batches (>= on number of + ! Check to flush cmfd tallies during inactive batches (>= on number of ! flushes important as the code will flush on the first batch which we ! dont want to count) -! if (cmfd_run .and. current_batch < n_inactive .and. mod(current_batch-1,cmfd_inact_flush(1)) & -! == 0 .and. cmfd_inact_flush(2) >= 0) then if (cmfd_run .and. mod(current_batch,cmfd_inact_flush(1)) & == 0 .and. cmfd_inact_flush(2) > 0 .and. cmfd_begin < current_batch) then cmfd_hold_weights = .true. @@ -144,20 +112,22 @@ contains # ifdef PETSC !============================================================================== -! PROCESS_CMFD_OPTIONS +! PROCESS_CMFD_OPTIONS processes user options that interface with PETSc !============================================================================== subroutine process_cmfd_options() use global, only: cmfd_snes_monitor, cmfd_ksp_monitor, mpi_err - ! check for snes monitor +#ifdef PETSC + ! Check for snes monitor if (cmfd_snes_monitor) call PetscOptionsSetValue("-snes_monitor", & "stdout", mpi_err) - ! check for ksp monitor + ! Check for ksp monitor if (cmfd_ksp_monitor) call PetscOptionsSetValue("-ksp_monitor", & "stdout", mpi_err) +#endif end subroutine process_cmfd_options @@ -169,38 +139,42 @@ contains use constants, only: CMFD_NOACCEL, ZERO, TWO use global, only: cmfd, cmfd_coremap, master, mpi_err, entropy_on +#ifdef MPI + use mpi +#endif - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups - integer :: n ! total size - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: idx ! index in vector + integer :: nx ! maximum number of cells in x direction + integer :: ny ! maximum number of cells in y direction + integer :: nz ! maximum number of cells in z direction + integer :: ng ! maximum number of energy groups + integer :: n ! total size + integer :: i ! iteration counter for x + integer :: j ! iteration counter for y + integer :: k ! iteration counter for z + integer :: g ! iteration counter for groups + integer :: idx ! index in vector real(8) :: hxyz(3) ! cell dimensions of current ijk cell real(8) :: vol ! volume of cell real(8),allocatable :: source(:,:,:,:) ! tmp source array for entropy - ! get maximum of spatial and group indices - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) + ! Get maximum of spatial and group indices + nx = cmfd % indices(1) + ny = cmfd % indices(2) + nz = cmfd % indices(3) + ng = cmfd % indices(4) n = ng*nx*ny*nz - ! allocate cmfd source if not already allocated and allocate buffer - if (.not. allocated(cmfd%cmfd_src)) allocate(cmfd%cmfd_src(ng,nx,ny,nz)) + ! Allocate cmfd source if not already allocated and allocate buffer + if (.not. allocated(cmfd % cmfd_src)) & + allocate(cmfd % cmfd_src(ng,nx,ny,nz)) - ! reset cmfd source to 0 - cmfd%cmfd_src = ZERO + ! Reset cmfd source to 0 + cmfd % cmfd_src = ZERO - ! only perform for master + ! Only perform for master if (master) then - ! loop around indices to map to cmfd object + ! Loop around indices to map to cmfd object ZLOOP: do k = 1, nz YLOOP: do j = 1, ny @@ -209,25 +183,25 @@ contains GROUP: do g = 1, ng - ! check for core map + ! Check for core map if (cmfd_coremap) then - if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) then + if (cmfd % coremap(i,j,k) == CMFD_NOACCEL) then cycle end if end if - ! get dimensions of cell - hxyz = cmfd%hxyz(:,i,j,k) + ! Get dimensions of cell + hxyz = cmfd % hxyz(:,i,j,k) - ! calculate volume + ! Calculate volume vol = hxyz(1)*hxyz(2)*hxyz(3) - ! get first index + ! Get first index idx = get_matrix_idx(1,i,j,k,ng,nx,ny) - ! compute fission source - cmfd%cmfd_src(g,i,j,k) = sum(cmfd%nfissxs(:,g,i,j,k) * & - cmfd%phi(idx:idx+(ng-1)))*vol + ! Compute fission source + cmfd % cmfd_src(g,i,j,k) = sum(cmfd % nfissxs(:,g,i,j,k) * & + cmfd % phi(idx:idx + (ng - 1)))*vol end do GROUP @@ -237,38 +211,38 @@ contains end do ZLOOP - ! normalize source such that it sums to 1.0 - cmfd%cmfd_src = cmfd%cmfd_src/sum(cmfd%cmfd_src) + ! Normalize source such that it sums to 1.0 + cmfd % cmfd_src = cmfd % cmfd_src/sum(cmfd % cmfd_src) - ! compute entropy + ! Compute entropy if (entropy_on) then - ! allocate tmp array + ! Allocate tmp array if (.not.allocated(source)) allocate(source(ng,nx,ny,nz)) - ! initialize the source + ! Initialize the source source = ZERO - ! compute log - where (cmfd%cmfd_src > ZERO) - source = cmfd%cmfd_src*log(cmfd%cmfd_src)/log(TWO) + ! Compute log + where (cmfd % cmfd_src > ZERO) + source = cmfd % cmfd_src*log(cmfd % cmfd_src)/log(TWO) end where - ! sum that source - cmfd%entropy = -sum(source) + ! Sum that source + cmfd % entropy = -sum(source) - ! deallocate tmp array + ! Deallocate tmp array if (allocated(source)) deallocate(source) end if - ! normalize source so average is 1.0 - cmfd%cmfd_src = cmfd%cmfd_src/sum(cmfd%cmfd_src)*cmfd%norm + ! Normalize source so average is 1.0 + cmfd % cmfd_src = cmfd % cmfd_src/sum(cmfd % cmfd_src)*cmfd % norm end if - ! broadcast full source to all procs - call MPI_BCAST(cmfd%cmfd_src, n, MPI_REAL8, 0, MPI_COMM_WORLD, mpi_err) + ! Broadcast full source to all procs + call MPI_BCAST(cmfd % cmfd_src, n, MPI_REAL8, 0, MPI_COMM_WORLD, mpi_err) end subroutine calc_fission_source @@ -287,114 +261,117 @@ contains use mesh, only: count_bank_sites, get_mesh_indices use search, only: binary_search - ! local variables - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups - integer :: i ! iteration counter - integer :: ijk(3) ! spatial bin location - integer :: e_bin ! energy bin of source particle - integer :: n_groups ! number of energy groups - integer(8) :: size_bank ! size of source bank - logical :: outside ! any source sites outside mesh - logical :: in_mesh ! source site is inside mesh - logical :: new_weights ! calcualte new weights - type(StructuredMesh), pointer :: m ! point to mesh - real(8), allocatable :: egrid(:) +#ifdef MPI + use mpi +#endif - ! associate pointer + integer :: nx ! maximum number of cells in x direction + integer :: ny ! maximum number of cells in y direction + integer :: nz ! maximum number of cells in z direction + integer :: ng ! maximum number of energy groups + integer :: i ! iteration counter + integer :: ijk(3) ! spatial bin location + integer :: e_bin ! energy bin of source particle + integer :: n_groups ! number of energy groups + integer(8) :: size_bank ! size of source bank + logical :: outside ! any source sites outside mesh + logical :: in_mesh ! source site is inside mesh + logical :: new_weights ! calcualte new weights + type(StructuredMesh), pointer :: m ! point to mesh + real(8), allocatable :: egrid(:) ! energy grid + + ! Associate pointer m => meshes(n_user_meshes + 1) - ! get maximum of spatial and group indices - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) + ! Get maximum of spatial and group indices + nx = cmfd % indices(1) + ny = cmfd % indices(2) + nz = cmfd % indices(3) + ng = cmfd % indices(4) - ! compute size of source bank + ! Compute size of source bank size_bank = bank_last - bank_first + 1_8 - ! allocate arrays in cmfd object (can take out later extend to multigroup) - if (.not.allocated(cmfd%sourcecounts)) then - allocate(cmfd%sourcecounts(ng,nx,ny,nz)) + ! Allocate arrays in cmfd object (can take out later extend to multigroup) + if (.not.allocated(cmfd % sourcecounts)) then + allocate(cmfd % sourcecounts(ng,nx,ny,nz)) cmfd % sourcecounts = 0 end if - if (.not.allocated(cmfd%weightfactors)) then - allocate(cmfd%weightfactors(ng,nx,ny,nz)) + if (.not.allocated(cmfd % weightfactors)) then + allocate(cmfd % weightfactors(ng,nx,ny,nz)) cmfd % weightfactors = ONE end if - ! allocate energy grid and reverse cmfd energy grid - if (.not. allocated(egrid)) allocate(egrid(ng+1)) - egrid = (/(cmfd%egrid(ng-i+2),i = 1,ng+1)/) + ! Allocate energy grid and reverse cmfd energy grid + if (.not. allocated(egrid)) allocate(egrid(ng + 1)) + egrid = (/(cmfd % egrid(ng - i + 2), i = 1, ng + 1)/) - ! compute new weight factors + ! Compute new weight factors if (new_weights) then - ! zero out weights + ! Zero out weights cmfd%weightfactors = ZERO - ! count bank sites in mesh + ! Count bank sites in mesh call count_bank_sites(m, source_bank, cmfd%sourcecounts, egrid, & sites_outside=outside, size_bank = size_bank) - ! check for sites outside of the mesh + ! Check for sites outside of the mesh if (master .and. outside) then message = "Source sites outside of the CMFD mesh!" call fatal_error() end if - ! have master compute weight factors + ! Have master compute weight factors (watch for 0s) if (master) then - where(cmfd%cmfd_src > ZERO .and. cmfd%sourcecounts > ZERO) - cmfd%weightfactors = cmfd%cmfd_src/sum(cmfd%cmfd_src)* & - sum(cmfd%sourcecounts) / cmfd%sourcecounts + where(cmfd % cmfd_src > ZERO .and. cmfd % sourcecounts > ZERO) + cmfd % weightfactors = cmfd % cmfd_src/sum(cmfd % cmfd_src)* & + sum(cmfd % sourcecounts) / cmfd % sourcecounts end where end if - ! broadcast weight factors to all procs - call MPI_BCAST(cmfd%weightfactors, ng*nx*ny*nz, MPI_REAL8, 0, & + ! Broadcast weight factors to all procs + call MPI_BCAST(cmfd % weightfactors, ng*nx*ny*nz, MPI_REAL8, 0, & MPI_COMM_WORLD, mpi_err) end if - ! begin loop over source bank + ! Begin loop over source bank do i = 1, int(size_bank, 4) - ! determine spatial bin - call get_mesh_indices(m, source_bank(i)%xyz, ijk, in_mesh) + ! Determine spatial bin + call get_mesh_indices(m, source_bank(i) % xyz, ijk, in_mesh) - ! determine energy bin - n_groups = size(cmfd%egrid) - 1 - if (source_bank(i) % E < cmfd%egrid(1)) then + ! Determine energy bin + n_groups = size(cmfd % egrid) - 1 + if (source_bank(i) % E < cmfd % egrid(1)) then e_bin = 1 - message = 'source pt below energy grid' + message = 'Source pt below energy grid' call warning() - elseif (source_bank(i) % E > cmfd%egrid(n_groups+1)) then + elseif (source_bank(i) % E > cmfd % egrid(n_groups + 1)) then e_bin = n_groups - message = 'source pt above energy grid' + message = 'Source pt above energy grid' call warning() else - e_bin = binary_search(cmfd%egrid, n_groups + 1, source_bank(i) % E) + e_bin = binary_search(cmfd % egrid, n_groups + 1, source_bank(i) % E) end if - ! reverese energy bin (lowest grp is highest energy bin) + ! Reverese energy bin (lowest grp is highest energy bin) e_bin = n_groups - e_bin + 1 - ! check for outside of mesh + ! Check for outside of mesh if (.not. in_mesh) then - message = 'Source site found outside of CMFD mesh!' + message = 'Source site found outside of CMFD mesh' call fatal_error() end if - ! reweight particle - source_bank(i)%wgt = source_bank(i)%wgt * & - cmfd%weightfactors(e_bin,ijk(1),ijk(2),ijk(3)) + ! Reweight particle + source_bank(i) % wgt = source_bank(i) % wgt * & + cmfd % weightfactors(e_bin, ijk(1), ijk(2), ijk(3)) end do - ! deallocate + ! Deallocate all if (allocated(egrid)) deallocate(egrid) end subroutine cmfd_reweight @@ -416,15 +393,15 @@ contains integer :: ny ! maximum number of cells in y direction integer :: ng ! maximum number of energy groups - ! check if coremap is used + ! Check if coremap is used if (cmfd_coremap) then - ! get idx from core map + ! Get idx from core map matidx = ng*(cmfd % coremap(i,j,k)) - (ng - g) else - ! compute index + ! Compute index matidx = g + ng*(i - 1) + ng*nx*(j - 1) + ng*nx*ny*(k - 1) end if @@ -445,14 +422,14 @@ contains integer :: i ! loop counter - ! print message + ! Print message message = "CMFD tallies reset" call write_message(7) - ! begin loop around CMFD tallies + ! Begin loop around CMFD tallies do i = 1, n_cmfd_tallies - ! reset that tally + ! Reset that tally cmfd_tallies(i) % n_realizations = 0 call reset_result(cmfd_tallies(i) % results) From 9cf923e63797d1c22b71a43acf8fbd7c740f3cc7 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 09:56:57 -0400 Subject: [PATCH 027/192] cleaned up cmfd input comments and bits of code --- src/cmfd_input.F90 | 221 ++++++++++++++++++++++++--------------------- 1 file changed, 116 insertions(+), 105 deletions(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index c228890d1e..d251ec379a 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -1,29 +1,53 @@ module cmfd_input + use global + implicit none private public :: configure_cmfd +# ifdef PETSC +# include +# endif + contains !=============================================================================== -! CONFIGURE_CMFD +! CONFIGURE_CMFD initializes PETSc and CMFD parameters !=============================================================================== subroutine configure_cmfd() -# ifdef PETSC - use cmfd_message_passing, only: petsc_init_mpi -# endif + integer :: new_comm ! new mpi communicator + integer :: color ! color group of processor - ! read in cmfd input file + ! Read in cmfd input file call read_cmfd_xml() - ! initialize petsc on mpi -# ifdef PETSC - call petsc_init_mpi() + ! Assign color + if (master) then + color = 1 + else + color = 2 + end if + + ! Split up procs +# ifdef MPI + call MPI_COMM_SPLIT(MPI_COMM_WORLD,color,0,new_comm,mpi_err) # endif + ! assign to PETSc +# ifdef PETSC + PETSC_COMM_WORLD = new_comm + + ! Initialize petsc on all procs + call PetscInitialize(PETSC_NULL_CHARACTER,mpi_err) +# endif + + ! initialize timers + call time_cmfd % reset() + call time_solver % reset() + end subroutine configure_cmfd !=============================================================================== @@ -33,17 +57,16 @@ contains subroutine read_cmfd_xml() use error, only: fatal_error - use global use output, only: write_message use string, only: lower_case use xml_data_cmfd_t use, intrinsic :: ISO_FORTRAN_ENV - integer :: ng - logical :: file_exists ! does cmfd.xml exist? - character(MAX_LINE_LEN) :: filename + integer :: ng ! number of energy groups + logical :: file_exists ! does cmfd.xml exist? + character(MAX_LINE_LEN) :: filename ! name of input file - ! read cmfd infput file + ! Read cmfd input file filename = trim(path_input) // "cmfd.xml" inquire(FILE=filename, EXIST=file_exists) if (.not. file_exists) then @@ -55,79 +78,78 @@ contains return else - ! tell user + ! Tell user message = "Reading CMFD XML file..." call write_message(5) end if - ! parse cmfd.xml file + ! Parse cmfd.xml file call read_xml_file_cmfd_t(filename) - ! set spatial dimensions in cmfd object - cmfd % indices(1:3) = mesh_ % dimension(1:3) ! sets spatial dimensions + ! Set spatial dimensions in cmfd object (structed Cartesian mesh) + cmfd % indices(1:3) = mesh_ % dimension(1:3) - ! get number of energy groups + ! Get number of energy groups or set to 1 group default if (associated(mesh_ % energy)) then ng = size(mesh_ % energy) - if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(ng)) - cmfd%egrid = mesh_ % energy + if(.not.allocated(cmfd % egrid)) allocate(cmfd % egrid(ng)) + cmfd % egrid = mesh_ % energy cmfd % indices(4) = ng - 1 ! sets energy group dimension else - if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(2)) - cmfd%egrid = (/0.0_8,20.0_8/) + if(.not.allocated(cmfd % egrid)) allocate(cmfd % egrid(2)) + cmfd % egrid = (/0.0_8,20.0_8/) cmfd % indices(4) = 1 ! one energy group end if - ! set global albedo + ! Set global albedo, these can be overwritten by coremap if (associated(mesh_ % albedo)) then cmfd % albedo = mesh_ % albedo else cmfd % albedo = (/1.0, 1.0, 1.0, 1.0, 1.0, 1.0/) end if - ! get acceleration map + ! Get core map overlay for a subset mesh for CMFD if (associated(mesh_ % map)) then + + ! Allocate a core map with appropriate dimensions allocate(cmfd % coremap(cmfd % indices(1), cmfd % indices(2), & cmfd % indices(3))) + + ! Check and make sure it is of correct size if (size(mesh_ % map) /= product(cmfd % indices(1:3))) then - message = 'FATAL==>CMFD coremap not to correct dimensions' + message = 'CMFD coremap not to correct dimensions' call fatal_error() end if - cmfd % coremap = reshape(mesh_ % map,(cmfd % indices(1:3))) - cmfd_coremap = .true. - end if - ! check for core map activation by printing note - if (cmfd_coremap .and. master) then + ! Reshape core map vector into (x,y,z) array + cmfd % coremap = reshape(mesh_ % map,(cmfd % indices(1:3))) + + ! Indicate to cmfd that a core map overlay is active + cmfd_coremap = .true. + + ! Write to stdout that a core map overlay is active message = "Core Map Overlay Activated" call write_message() + end if - ! check for normalization constant + ! Get normalization constant for source (default is 1.0 from XML) cmfd % norm = norm_ - ! set feedback logical + ! Set feedback logical call lower_case(feedback_) if (feedback_ == 'true' .or. feedback_ == '1') cmfd_feedback = .true. - ! set balance logical - ! call lower_case(balance_) - ! if (balance_ == 'true' .or. balance == '1') cmfd_balance = .true. + ! Set downscatter logical + call lower_case(downscatter_) + if (downscatter_ == 'true' .or. downscatter_ == '1') & + cmfd_downscatter = .true. - ! set downscatter logical - ! call lower_case(downscatter_) - ! if (downscatter_ == 'true' .or. downscatter == '1') & - ! cmfd_downscatter = downscatter_ - - ! set 2 group fix - call lower_case(run_2grp_) - if (run_2grp_ == 'true' .or. run_2grp_ == '1') cmfd_run_2grp = .true. - - ! set the solver type + ! Set the solver type (default power from XML) cmfd_solver_type = solver_(1:10) - ! set monitoring + ! Set convergence monitoring call lower_case(snes_monitor_) call lower_case(ksp_monitor_) call lower_case(power_monitor_) @@ -138,47 +160,36 @@ contains if (power_monitor_ == 'true' .or. power_monitor_ == '1') & cmfd_power_monitor = .true. - ! output logicals + ! Output logicals call lower_case(write_balance_) call lower_case(write_matrices_) - ! call lower_case(write_hdf5_) if (write_balance_ == 'true' .or. write_balance_ == '1') & cmfd_write_balance = .true. if (write_matrices_ == 'true' .or. write_matrices_ == '1') & cmfd_write_matrices = .true. - ! if (write_hdf5_ == 'true' .or. write_hdf5_ == '1') & - ! cmfd_write_hdf5 = .true. - ! run an adjoint calc + ! Run an adjoint calc call lower_case(run_adjoint_) if (run_adjoint_ == 'true' .or. run_adjoint_ == '1') & cmfd_run_adjoint = .true. - ! batch to begin cmfd + ! Batch to begin cmfd (default is 1 from XML) cmfd_begin = begin_ - ! tally during inactive batches + ! Tally during inactive batches (by default we will always tally from 1) call lower_case(inactive_) if (inactive_ == 'false' .or. inactive_ == '0') cmfd_tally_on = .false. - ! inactive batch flush window - cmfd_inact_flush(1) = inactive_flush_ - cmfd_inact_flush(2) = num_flushes_ + ! Inactive batch flush window + cmfd_inact_flush(1) = inactive_flush_ ! the interval of batches + cmfd_inact_flush(2) = num_flushes_ ! number of times to do this - ! last flush before active batches + ! Last flush before active batches cmfd_act_flush = active_flush_ - ! tolerance on keff - cmfd_keff_tol = keff_tol_ - - ! create tally objects + ! Create tally objects call create_cmfd_tally() - ! set number of CMFD processors and report to user - n_procs_cmfd = n_cmfd_procs_ - if (master) write(OUTPUT_UNIT,'(A,1X,I0,1X,A)') "CMFD Running on", & - n_procs_cmfd," processors." - end subroutine read_cmfd_xml !=============================================================================== @@ -193,7 +204,6 @@ contains subroutine create_cmfd_tally() use error, only: fatal_error, warning - use global use mesh_header, only: StructuredMesh use string use tally, only: setup_active_cmfdtallies @@ -211,22 +221,22 @@ contains type(StructuredMesh), pointer :: m => null() type(TallyFilter) :: filters(N_FILTER_TYPES) ! temporary filters - ! parse cmfd.xml file + ! Parse cmfd.xml file filename = trim(path_input) // "cmfd.xml" call read_xml_file_cmfd_t(filename) - ! set global variables if they are 0 (this can happen if there is no tally + ! Set global variables if they are 0 (this can happen if there is no tally ! file) if (n_meshes == 0) n_meshes = n_user_meshes + n_cmfd_meshes - ! allocate mesh + ! Allocate mesh if (.not. allocated(meshes)) allocate(meshes(n_meshes)) m => meshes(n_user_meshes+1) - ! set mesh id + ! Set mesh id m % id = n_user_meshes + 1 - ! set mesh type to rectangular + ! Set mesh type to rectangular m % type = LATTICE_RECT ! Determine number of dimensions for mesh @@ -321,20 +331,20 @@ contains ! Add mesh to dictionary call mesh_dict % add_key(m % id, n_user_meshes + 1) - ! allocate tallies + ! Allocate tallies call add_tallies("cmfd", n_cmfd_tallies) - ! begin loop around tallies + ! Begin loop around tallies do i = 1, n_cmfd_tallies - ! point t to tally variable + ! Point t to tally variable t => cmfd_tallies(i) - ! set reset property + ! Set reset property call lower_case(reset_) if (reset_ == 'true' .or. reset_ == '1') t % reset = .true. - ! set up mesh filter + ! Set up mesh filter n_filters = 1 filters(n_filters) % type = FILTER_MESH filters(n_filters) % n_bins = product(m % dimension) @@ -342,7 +352,7 @@ contains filters(n_filters) % int_bins(1) = n_user_meshes + 1 t % find_filter(FILTER_MESH) = n_filters - ! read and set incoming energy mesh filter + ! Read and set incoming energy mesh filter if (associated(mesh_ % energy)) then n_filters = n_filters + 1 filters(n_filters) % type = FILTER_ENERGYIN @@ -353,40 +363,40 @@ contains t % find_filter(FILTER_ENERGYIN) = n_filters end if - ! set number of nucilde bins + ! Set number of nucilde bins allocate(t % nuclide_bins(1)) t % nuclide_bins(1) = -1 t % n_nuclide_bins = 1 - ! record tally id which is equivalent to loop number + ! Record tally id which is equivalent to loop number t % id = i_cmfd_tallies + i if (i == 1) then - ! set label + ! Set label t % label = "CMFD flux, total, scatter-1" - ! set tally estimator to analog + ! Set tally estimator to analog t % estimator = ESTIMATOR_ANALOG - ! set tally type to volume + ! Set tally type to volume t % type = TALLY_VOLUME - ! allocate and set filters + ! Allocate and set filters t % n_filters = n_filters allocate(t % filters(n_filters)) t % filters = filters(1:n_filters) - ! allocate scoring bins + ! Allocate scoring bins allocate(t % score_bins(3)) t % n_score_bins = 3 t % n_user_score_bins = 3 - ! allocate scattering order data + ! Allocate scattering order data allocate(t % scatt_order(3)) t % scatt_order = 0 - ! set macro_bins + ! Set macro_bins t % score_bins(1) = SCORE_FLUX t % score_bins(2) = SCORE_TOTAL t % score_bins(3) = SCORE_SCATTER_N @@ -394,16 +404,16 @@ contains else if (i == 2) then - ! set label + ! Set label t % label = "CMFD neutron production" - ! set tally estimator to analog + ! Set tally estimator to analog t % estimator = ESTIMATOR_ANALOG - ! set tally type to volume + ! Set tally type to volume t % type = TALLY_VOLUME - ! read and set outgoing energy mesh filter + ! Read and set outgoing energy mesh filter if (associated(mesh_ % energy)) then n_filters = n_filters + 1 filters(n_filters) % type = FILTER_ENERGYOUT @@ -414,34 +424,34 @@ contains t % find_filter(FILTER_ENERGYOUT) = n_filters end if - ! allocate and set filters + ! Allocate and set filters t % n_filters = n_filters allocate(t % filters(n_filters)) t % filters = filters(1:n_filters) - ! deallocate filters bins array + ! Deallocate filters bins array if (associated(mesh_ % energy)) & deallocate(filters(n_filters) % real_bins) - ! allocate macro reactions + ! Allocate macro reactions allocate(t % score_bins(2)) t % n_score_bins = 2 t % n_user_score_bins = 2 - ! allocate scattering order data + ! Allocate scattering order data allocate(t % scatt_order(2)) t % scatt_order = 0 - ! set macro_bins + ! Set macro_bins t % score_bins(1) = SCORE_NU_SCATTER t % score_bins(2) = SCORE_NU_FISSION else if (i == 3) then - ! set label + ! Set label t % label = "CMFD surface currents" - ! set tally estimator to analog + ! Set tally estimator to analog t % estimator = ESTIMATOR_ANALOG ! Add extra filter for surface @@ -458,40 +468,41 @@ contains end if t % find_filter(FILTER_SURFACE) = n_filters - ! allocate and set filters + ! Allocate and set filters t % n_filters = n_filters allocate(t % filters(n_filters)) t % filters = filters(1:n_filters) - ! deallocate filters bins array + ! Deallocate filters bins array deallocate(filters(n_filters) % int_bins) - ! allocate macro reactions + ! Allocate macro reactions allocate(t % score_bins(1)) t % n_score_bins = 1 t % n_user_score_bins = 1 - ! allocate scattering order data + ! Allocate scattering order data allocate(t % scatt_order(1)) t % scatt_order = 0 - ! set macro bins + ! Set macro bins t % score_bins(1) = SCORE_CURRENT t % type = TALLY_SURFACE_CURRENT - ! we need to increase the dimension by one since we also need + ! We need to increase the dimension by one since we also need ! currents coming into and out of the boundary mesh cells. i_filter_mesh = t % find_filter(FILTER_MESH) t % filters(i_filter_mesh) % n_bins = product(m % dimension + 1) end if - ! deallocate filter bins + ! Deallocate filter bins deallocate(filters(1) % int_bins) if (associated(mesh_ % energy)) deallocate(filters(2) % real_bins) end do + ! Put cmfd tallies into active tally array and turn tallies on call setup_active_cmfdtallies() tallies_on = .true. From 971946fc4c8f5c54d06927306f8baf4716c560b9 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 09:57:28 -0400 Subject: [PATCH 028/192] no longer need cmfd message passing routines --- src/cmfd_message_passing.F90 | 98 ------------------------------------ 1 file changed, 98 deletions(-) delete mode 100644 src/cmfd_message_passing.F90 diff --git a/src/cmfd_message_passing.F90 b/src/cmfd_message_passing.F90 deleted file mode 100644 index f5269bc2a7..0000000000 --- a/src/cmfd_message_passing.F90 +++ /dev/null @@ -1,98 +0,0 @@ -module cmfd_message_passing - -# ifdef PETSC - -! This module contains MPI routines related to the CMFD calculation - - implicit none - private - public :: petsc_init_mpi, cmfd_bcast - -# include - -contains - -!=============================================================================== -! PETSC_INIT_MPI -!=============================================================================== - - subroutine petsc_init_mpi() - - use global, only: n_procs_cmfd, time_cmfd, rank, mpi_err - - integer :: new_comm ! new communicator - integer :: color ! communicator color - - ! assign color - if (rank < n_procs_cmfd) then - color = 1 - else - color = 2 - end if - - ! split up procs - call MPI_COMM_SPLIT(MPI_COMM_WORLD,color,0,new_comm,mpi_err) - - ! assign to PETSc - PETSC_COMM_WORLD = new_comm - - ! initialize petsc on all procs - call PetscInitialize(PETSC_NULL_CHARACTER,mpi_err) - - ! initialize timer - call time_cmfd % reset() - - end subroutine petsc_init_mpi - -!=============================================================================== -! CMFD_BCAST -!=============================================================================== - - subroutine cmfd_bcast() - - use global, only: cmfd_coremap, cmfd, mpi_err - use cmfd_header, only: allocate_cmfd - - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: ng ! number of energy groups - - ! extract spatial and energy indices from object - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - ng = cmfd % indices(4) - - ! initialize data - if (.not. allocated(cmfd%flux)) call allocate_cmfd(cmfd) - - ! sync up procs - call MPI_Barrier(PETSC_COMM_WORLD, mpi_err) - - ! broadcast all data - call MPI_BCAST(cmfd%flux, ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%totalxs, ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%p1scattxs, ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%scattxs, ng*ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%nfissxs, ng*ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%diffcof, ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%dtilde, 6*ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%dhat, 6*ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%hxyz, 3*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%current, 12*ng*nx*ny*nz, MPI_REAL8, 0, PETSC_COMM_WORLD, mpi_err) - - ! broadcast coremap info - if (cmfd_coremap) then - call MPI_BCAST(cmfd%coremap, nx*ny*nz, MPI_INTEGER8, 0, PETSC_COMM_WORLD, mpi_err) - call MPI_BCAST(cmfd%mat_dim, 1, MPI_INTEGER8, 0, PETSC_COMM_WORLD, mpi_err) - if (.not. allocated(cmfd % indexmap)) & - allocate(cmfd % indexmap(cmfd % mat_dim,3)) - call MPI_BCAST(cmfd%indexmap, cmfd%mat_dim*3, MPI_INTEGER8, 0, PETSC_COMM_WORLD, mpi_err) - end if - - end subroutine cmfd_bcast - -# endif - -end module cmfd_message_passing From 171f92711e85e825fe0cca4b3dbc87fef8cf4912 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 10:00:46 -0400 Subject: [PATCH 029/192] cleaned up comments in cmfd output --- src/cmfd_output.F90 | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/cmfd_output.F90 b/src/cmfd_output.F90 index ebfec812b0..2936dda35a 100644 --- a/src/cmfd_output.F90 +++ b/src/cmfd_output.F90 @@ -1,8 +1,6 @@ module cmfd_output -#ifdef PETSC - -! This modules cleans up cmfd objects and echos the results +! This modules cleans up cmfd objects and outputs neutron balance info implicit none private @@ -11,28 +9,30 @@ module cmfd_output contains !=============================================================================== -! FINALIZE_CMFD +! FINALIZE_CMFD finalizes PETSc and frees memory associated with CMFD !=============================================================================== subroutine finalize_cmfd() - use global, only: cmfd, cmfd_write_balance, cmfd_write_hdf5, & + use global, only: cmfd, cmfd_write_balance, & master, mpi_err use cmfd_header, only: deallocate_cmfd - ! finalize petsc + ! Finalize petsc +#ifdef PETSC call PetscFinalize(mpi_err) +# endif - ! write out final neutron balance + ! Write out final neutron balance if (master .and. cmfd_write_balance) call write_neutron_balance() - ! deallocate cmfd object + ! Deallocate cmfd object call deallocate_cmfd(cmfd) end subroutine finalize_cmfd !=============================================================================== -! WRITE_NEUTRON_BALANCE +! WRITE_NEUTRON_BALANCE writes an ASCII neutron balance file out !=============================================================================== subroutine write_neutron_balance() @@ -45,17 +45,15 @@ contains filename = trim(path_output) // 'neutron_balance.out' - ! open file for output + ! Open file for output open(UNIT=CMFD_BALANCE, FILE=filename, ACTION='write') - ! write out the tally + ! Write out the tally call neutron_balance(CMFD_BALANCE) - ! close file + ! Close file close(CMFD_BALANCE) end subroutine write_neutron_balance -#endif - end module cmfd_output From 3b23097ff0eb5b5989b570703383c50262a6d200 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 10:01:32 -0400 Subject: [PATCH 030/192] removed some useless cmfd vars and removed use mpi from global --- src/global.F90 | 59 ++++++++++++---------------------------- src/templates/cmfd_t.xml | 40 ++++++++++++--------------- 2 files changed, 36 insertions(+), 63 deletions(-) diff --git a/src/global.F90 b/src/global.F90 index ee9f752295..bfffb2f37b 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -16,10 +16,6 @@ module global use tally_header, only: TallyObject, TallyMap, TallyResult use timer_header, only: Timer -#ifdef MPI - use mpi -#endif - #ifdef HDF5 use hdf5_interface, only: HID_T #endif @@ -300,78 +296,59 @@ module global type(Timer) :: time_cmfd ! timer for whole cmfd calculation type(Timer) :: time_solver ! timer for solver - ! Flag for CMFD only - logical :: cmfd_only = .false. - - ! Flag for coremap accelerator + ! Flag for active core map logical :: cmfd_coremap = .false. - ! number of processors for cmfd - integer :: n_procs_cmfd - - ! reset dhats to zero + ! Flag to reset dhats to zero logical :: dhat_reset = .false. - ! activate neutronic feedback + ! Flag to activate neutronic feedback via source weights logical :: cmfd_feedback = .false. - ! activate auto-balance of tallies (2grp only) -! logical :: cmfd_balance = .false. + ! User-defined tally information + integer :: n_cmfd_meshes = 1 ! # of structured meshes + integer :: n_cmfd_tallies = 3 ! # of user-defined tallies - ! calculate effective downscatter -! logical :: cmfd_downscatter = .false. - - ! user-defined tally information - integer :: n_cmfd_meshes = 1 ! # of structured meshes - integer :: n_cmfd_tallies = 3 ! # of user-defined tallies - - ! overwrite with 2grp xs - logical :: cmfd_run_2grp = .false. - - ! hold cmfd weight adjustment factors + ! Flag to hold cmfd weight adjustment factors logical :: cmfd_hold_weights = .false. - ! eigenvalue solver type + ! Eigenvalue solver type character(len=10) :: cmfd_solver_type = 'power' - ! adjoint method type + ! Adjoint method type character(len=10) :: cmfd_adjoint_type = 'physical' - ! number of incomplete ilu factorization levels + ! Number of incomplete ilu factorization levels integer :: cmfd_ilu_levels = 1 - ! batch to begin cmfd + ! Batch to begin cmfd integer :: cmfd_begin = 1 - ! when and how long to flush cmfd tallies during inactive batches + ! When and how long to flush cmfd tallies during inactive batches integer :: cmfd_inact_flush(2) = (/9999,1/) - ! batch to last flush before active batches + ! Batch to last flush before active batches integer :: cmfd_act_flush = 0 - ! compute effective downscatter cross section + ! Compute effective downscatter cross section logical :: cmfd_downscatter = .false. - ! convergence monitoring + ! Convergence monitoring logical :: cmfd_snes_monitor = .false. logical :: cmfd_ksp_monitor = .false. logical :: cmfd_power_monitor = .false. - ! cmfd output + ! Cmfd output logical :: cmfd_write_balance = .false. logical :: cmfd_write_matrices = .false. - logical :: cmfd_write_hdf5 = .false. - ! run an adjoint calculation (last batch only) + ! Run an adjoint calculation (last batch only) logical :: cmfd_run_adjoint = .false. - ! cmfd run logicals + ! Cmfd run logicals logical :: cmfd_on = .false. logical :: cmfd_tally_on = .true. - ! tolerance on keff to run cmfd - real(8) :: cmfd_keff_tol = 0.005_8 - ! Information about state points to be written integer :: n_state_points = 0 type(SetInt) :: statepoint_batch diff --git a/src/templates/cmfd_t.xml b/src/templates/cmfd_t.xml index c9ae3c1fdc..631f2dd44d 100644 --- a/src/templates/cmfd_t.xml +++ b/src/templates/cmfd_t.xml @@ -13,27 +13,23 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + From cd859cdaa31b73ffb570a4619bee716f13588d50 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 10:44:32 -0400 Subject: [PATCH 031/192] added cmfd timers and output them at end of run --- src/cmfd_execute.F90 | 4 ---- src/cmfd_input.F90 | 3 ++- src/cmfd_power_solver.F90 | 10 +++++++++- src/global.F90 | 5 +++-- src/output.F90 | 6 +++++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 8f94bbe18a..b2762ee236 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -37,7 +37,6 @@ contains call process_cmfd_options() ! Call solver - call time_solver % start() if (trim(cmfd_solver_type) == 'power') then call cmfd_power_execute() elseif (trim(cmfd_solver_type) == 'jfnk') then @@ -46,10 +45,8 @@ contains message = 'solver type became invalid after input processing' call fatal_error() end if - call time_solver % stop() ! check to perform adjoint on last batch - call time_solver % start() if (current_batch == n_batches .and. cmfd_run_adjoint) then if (trim(cmfd_solver_type) == 'power') then call cmfd_power_execute(adjoint = .true.) @@ -57,7 +54,6 @@ contains call cmfd_snes_execute(adjoint = .true.) end if end if - call time_solver % stop() end if diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index d251ec379a..e98eaac762 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -46,7 +46,8 @@ contains ! initialize timers call time_cmfd % reset() - call time_solver % reset() + call time_cmfdbuild % reset() + call time_cmfdsolve % reset() end subroutine configure_cmfd diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 0a258aac13..92bd77bcc7 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -34,7 +34,7 @@ contains subroutine cmfd_power_execute(k_tol, s_tol, adjoint) - use global, only: cmfd_adjoint_type + use global, only: cmfd_adjoint_type, time_cmfdbuild, time_cmfdsolve real(8), optional :: k_tol ! tolerance on keff real(8), optional :: s_tol ! tolerance on source @@ -53,6 +53,9 @@ contains if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'physical') & physical_adjoint = .true. + ! start timer for build + call time_cmfdbuild % start() + ! initialize solver call gmres % create() @@ -69,8 +72,13 @@ contains ! precondition matrix call gmres % precondition(loss % petsc_mat) + ! stop timer for build + call time_cmfdbuild % stop() + ! begin power iteration + call time_cmfdsolve % start() call execute_power_iter() + call time_cmfdsolve % stop() ! extract results call extract_results() diff --git a/src/global.F90 b/src/global.F90 index bfffb2f37b..14b2c066e1 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -293,8 +293,9 @@ module global logical :: cmfd_run = .false. ! Timing objects - type(Timer) :: time_cmfd ! timer for whole cmfd calculation - type(Timer) :: time_solver ! timer for solver + type(Timer) :: time_cmfd ! timer for whole cmfd calculation + type(Timer) :: time_cmfdbuild ! timer for matrix build + type(Timer) :: time_cmfdsolve ! timer for solver ! Flag for active core map logical :: cmfd_coremap = .false. diff --git a/src/output.F90 b/src/output.F90 index f19148fb02..728f845a70 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1396,13 +1396,17 @@ contains write(ou,100) "Total time in simulation", time_inactive % elapsed + & time_active % elapsed write(ou,100) " Time in transport only", time_transport % elapsed - if(cmfd_run) write(ou,100) "Total CMFD time", time_cmfd % elapsed write(ou,100) " Time in inactive batches", time_inactive % elapsed write(ou,100) " Time in active batches", time_active % elapsed write(ou,100) " Time synchronizing fission bank", time_bank % elapsed write(ou,100) " Sampling source sites", time_bank_sample % elapsed write(ou,100) " SEND/RECV source sites", time_bank_sendrecv % elapsed write(ou,100) " Time accumulating tallies", time_tallies % elapsed + if (cmfd_run) write(ou,100) " Total CMFD time", time_cmfd % elapsed + if (cmfd_run) write(ou,100) " Time building matrices", & + time_cmfdbuild % elapsed + if (cmfd_run) write(ou,100) " Time solving matrices", & + time_cmfdsolve % elapsed write(ou,100) "Total time for finalization", time_finalize % elapsed write(ou,100) "Total time elapsed", time_total % elapsed From 00101e39d95f56feb5f078fadc9b44fb1b085b7c Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 10:48:07 -0400 Subject: [PATCH 032/192] changed CMFD timer output to match convention --- src/output.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/output.F90 b/src/output.F90 index 728f845a70..c6810dc083 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1402,10 +1402,10 @@ contains write(ou,100) " Sampling source sites", time_bank_sample % elapsed write(ou,100) " SEND/RECV source sites", time_bank_sendrecv % elapsed write(ou,100) " Time accumulating tallies", time_tallies % elapsed - if (cmfd_run) write(ou,100) " Total CMFD time", time_cmfd % elapsed - if (cmfd_run) write(ou,100) " Time building matrices", & + if (cmfd_run) write(ou,100) " Time in CMFD", time_cmfd % elapsed + if (cmfd_run) write(ou,100) " Building matrices", & time_cmfdbuild % elapsed - if (cmfd_run) write(ou,100) " Time solving matrices", & + if (cmfd_run) write(ou,100) " Solving matrices", & time_cmfdsolve % elapsed write(ou,100) "Total time for finalization", time_finalize % elapsed write(ou,100) "Total time elapsed", time_total % elapsed From 06c3bd3690140c4a7778f01df410c748e69f0619 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 9 Jul 2013 11:04:04 -0400 Subject: [PATCH 033/192] 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 11dad806ffc6d6903cc43976568932c8fd3539b0 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 11:04:36 -0400 Subject: [PATCH 034/192] fixed comments in these modules --- src/cmfd_header.F90 | 56 +++++++-------- src/cmfd_loss_operator.F90 | 136 ++++++++++++++++++------------------- src/cmfd_power_solver.F90 | 52 +++++++------- src/cmfd_prod_operator.F90 | 10 +-- 4 files changed, 127 insertions(+), 127 deletions(-) diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 index c4dad04f2a..027172ee27 100644 --- a/src/cmfd_header.F90 +++ b/src/cmfd_header.F90 @@ -8,67 +8,67 @@ module cmfd_header type, public :: cmfd_type - ! indices for problem + ! Indices for problem integer :: indices(4) - ! albedo boundary condition + ! Albedo boundary condition real(8) :: albedo(6) - ! core overlay map + ! Core overlay map integer, allocatable :: coremap(:,:,:) integer, allocatable :: indexmap(:,:) integer :: mat_dim = CMFD_NOACCEL - ! energy grid + ! Energy grid real(8), allocatable :: egrid(:) - ! cross sections + ! Cross sections real(8), allocatable :: totalxs(:,:,:,:) real(8), allocatable :: p1scattxs(:,:,:,:) real(8), allocatable :: scattxs(:,:,:,:,:) real(8), allocatable :: nfissxs(:,:,:,:,:) - ! diffusion coefficient + ! Diffusion coefficient real(8), allocatable :: diffcof(:,:,:,:) - ! current + ! Current real(8), allocatable :: current(:,:,:,:,:) - ! flux + ! Flux real(8), allocatable :: flux(:,:,:,:) - ! coupling coefficients and equivalence parameters + ! Coupling coefficients and equivalence parameters real(8), allocatable :: dtilde(:,:,:,:,:) real(8), allocatable :: dhat(:,:,:,:,:) - ! dimensions of mesh cells ([hu,hv,hw],xloc,yloc,zloc) + ! Dimensions of mesh cells ([hu,hv,hw],xloc,yloc,zloc) real(8), allocatable :: hxyz(:,:,:,:) - ! source distributions + ! Source distributions real(8), allocatable :: cmfd_src(:,:,:,:) real(8), allocatable :: openmc_src(:,:,:,:) - ! source sites in each mesh box + ! Source sites in each mesh box real(8), allocatable :: sourcecounts(:,:,:,:) - ! weight adjustment factors + ! Weight adjustment factors real(8), allocatable :: weightfactors(:,:,:,:) - ! eigenvector/eigenvalue from cmfd run + ! Eigenvector/eigenvalue from cmfd run real(8), allocatable :: phi(:) real(8) :: keff = ZERO - ! eigenvector/eigenvalue from adjoint run + ! Eigenvector/eigenvalue from adjoint run real(8), allocatable :: adj_phi(:) real(8) :: adj_keff = ZERO - ! residual for neutron balance + ! Residual for neutron balance real(8), allocatable :: resnb(:,:,:,:) - ! openmc source normalization factor + ! Openmc source normalization factor real(8) :: norm = ONE - ! Shannon entropy from cmfd fission source + ! "Shannon entropy" from cmfd fission source real(8) :: entropy end type cmfd_type @@ -76,7 +76,7 @@ module cmfd_header contains !============================================================================== -! ALLOCATE_CMFD +! ALLOCATE_CMFD allocates all data in of cmfd type !============================================================================== subroutine allocate_cmfd(this) @@ -88,13 +88,13 @@ contains integer :: nz ! number of mesh cells in z direction integer :: ng ! number of energy groups - ! extract spatial and energy indices from object + ! Extract spatial and energy indices from object nx = this % indices(1) ny = this % indices(2) nz = this % indices(3) ng = this % indices(4) - ! allocate flux, cross sections and diffusion coefficient + ! Allocate flux, cross sections and diffusion coefficient if (.not. allocated(this % flux)) allocate(this % flux(ng,nx,ny,nz)) if (.not. allocated(this % totalxs)) allocate(this % totalxs(ng,nx,ny,nz)) if (.not. allocated(this % p1scattxs)) allocate(this % p1scattxs(ng,nx,ny,nz)) @@ -102,25 +102,25 @@ contains if (.not. allocated(this % nfissxs)) allocate(this % nfissxs(ng,ng,nx,ny,nz)) if (.not. allocated(this % diffcof)) allocate(this % diffcof(ng,nx,ny,nz)) - ! allocate dtilde and dhat + ! Allocate dtilde and dhat if (.not. allocated(this % dtilde)) allocate(this % dtilde(6,ng,nx,ny,nz)) if (.not. allocated(this % dhat)) allocate(this % dhat(6,ng,nx,ny,nz)) - ! allocate dimensions for each box (here for general case) + ! Allocate dimensions for each box (here for general case) if (.not. allocated(this % hxyz)) allocate(this % hxyz(3,nx,ny,nz)) - ! allocate surface currents + ! Allocate surface currents if (.not. allocated(this % current)) allocate(this % current(12,ng,nx,ny,nz)) - ! allocate source distributions + ! Allocate source distributions if (.not. allocated(this % cmfd_src)) allocate(this % cmfd_src(ng,nx,ny,nz)) if (.not. allocated(this % openmc_src)) allocate(this % openmc_src(ng,nx,ny,nz)) - ! allocate source weight modification vars + ! Allocate source weight modification vars if (.not. allocated(this % sourcecounts)) allocate(this % sourcecounts(ng,nx,ny,nz)) if (.not. allocated(this % weightfactors)) allocate(this % weightfactors(ng,nx,ny,nz)) - ! set everthing to 0 except weight multiply factors if feedback isnt on + ! Set everthing to 0 except weight multiply factors if feedback isnt on this % flux = ZERO this % totalxs = ZERO this % p1scattxs = ZERO @@ -139,7 +139,7 @@ contains end subroutine allocate_cmfd !=============================================================================== -! DEALLOCATE_CMFD +! DEALLOCATE_CMFD frees all memory of cmfd type !=============================================================================== subroutine deallocate_cmfd(this) diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 index 67551f78b9..22fa8dfa14 100644 --- a/src/cmfd_loss_operator.F90 +++ b/src/cmfd_loss_operator.F90 @@ -11,7 +11,7 @@ module cmfd_loss_operator contains !=============================================================================== -! INIT_LOSS_MATRIX +! INIT_LOSS_MATRIX preallocates loss matrix and initializes it !=============================================================================== subroutine init_loss_matrix(loss_matrix) @@ -31,20 +31,20 @@ contains integer :: nz_s ! number of non-zero side cells integer :: nz_i ! number of non-zero interior cells - ! get maximum number of cells in each direction + ! Get maximum number of cells in each direction nx = cmfd%indices(1) ny = cmfd%indices(2) nz = cmfd%indices(3) ng = cmfd%indices(4) - ! calculate dimensions of matrix + ! Calculate dimensions of matrix if (cmfd_coremap) then n = cmfd % mat_dim * ng else n = nx*ny*nz*ng end if - ! calculate number of nonzeros, if core map -> need to determine manually + ! Calculate number of nonzeros, if core map -> need to determine manually if (cmfd_coremap) then nnz = preallocate_loss_matrix(nx, ny, nz, ng, n) else ! structured Cartesian grid @@ -57,13 +57,13 @@ contains nnz = nz_c + nz_s + nz_i end if - ! configure loss matrix + ! Configure loss matrix call loss_matrix % create(n, nnz) end subroutine init_loss_matrix !=============================================================================== -! PREALLOCATE_LOSS_MATRIX +! PREALLOCATE_LOSS_MATRIX manually preallocates the loss matrix !=============================================================================== function preallocate_loss_matrix(nx, ny, nz, ng, n) result(nnz) @@ -91,64 +91,64 @@ contains integer :: neig_mat_idx ! matrix index of neighbor cell integer :: scatt_mat_idx ! matrix index for h-->g scattering terms - ! reset to 0 + ! Reset number of nonzeros to 0 nnz = 0 - ! create single vector of these indices for boundary calculation + ! Create single vector of these indices for boundary calculation nxyz(1,:) = (/1,nx/) nxyz(2,:) = (/1,ny/) nxyz(3,:) = (/1,nz/) - ! begin loop around local rows + ! Begin loop around local rows ROWS: do irow = 1, n - ! set a nonzero for diagonal + ! Set a nonzero for diagonal nnz = nnz + 1 - ! get location indices + ! Get location indices call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - ! create boundary vector + ! Create boundary vector bound = (/i,i,j,j,k,k/) - ! begin loop over leakages + ! Begin loop over leakages LEAK: do l = 1,6 - ! define (x,y,z) and (-,+) indices + ! Define (x,y,z) and (-,+) indices xyz_idx = int(ceiling(real(l)/real(2))) ! x=1, y=2, z=3 dir_idx = 2 - mod(l,2) ! -=1, +=2 - ! calculate spatial indices of neighbor + ! Calculate spatial indices of neighbor neig_idx = (/i,j,k/) ! begin with i,j,k shift_idx = -2*mod(l,2) +1 ! shift neig by -1 or +1 neig_idx(xyz_idx) = shift_idx + neig_idx(xyz_idx) - ! check for global boundary + ! Check for global boundary if (bound(l) /= nxyz(xyz_idx,dir_idx)) then - ! check for coremap + ! Check for coremap if (cmfd_coremap) then - ! check for neighbor that is non-acceleartred + ! Check for neighbor that is non-acceleartred if (cmfd % coremap(neig_idx(1),neig_idx(2),neig_idx(3)) /= & CMFD_NOACCEL) then - ! get neighbor matrix index + ! Get neighbor matrix index call indices_to_matrix(g,neig_idx(1), neig_idx(2), & neig_idx(3), neig_mat_idx, ng, nx, ny) - ! record nonzero + ! Record nonzero nnz = nnz + 1 end if else - ! get neighbor matrix index + ! Get neighbor matrix index call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & neig_mat_idx, ng, nx, ny) - ! record nonzero + ! Record nonzero nnz = nnz + 1 end if @@ -157,16 +157,16 @@ contains end do LEAK - ! begin loop over off diagonal in-scattering + ! Begin loop over off diagonal in-scattering SCATTR: do h = 1, ng - ! cycle though if h=g + ! Cycle though if h=g, it was already banked in removal xs if (h == g) cycle - ! get neighbor matrix index + ! Get neighbor matrix index call indices_to_matrix(h, i, j, k, scatt_mat_idx, ng, nx, ny) - ! record nonzero + ! Record nonzero nnz = nnz + 1 end do SCATTR @@ -215,140 +215,140 @@ contains real(8) :: jnet ! net leakage from jo real(8) :: val ! temporary variable before saving to - ! check for adjoint + ! Check for adjoint adjoint_calc = .false. if (present(adjoint)) adjoint_calc = adjoint - ! get maximum number of cells in each direction + ! Get maximum number of cells in each direction nx = cmfd%indices(1) ny = cmfd%indices(2) nz = cmfd%indices(3) ng = cmfd%indices(4) - ! create single vector of these indices for boundary calculation + ! Create single vector of these indices for boundary calculation nxyz(1,:) = (/1,nx/) nxyz(2,:) = (/1,ny/) nxyz(3,:) = (/1,nz/) - ! begin iteration loops + ! Begin iteration loops ROWS: do irow = 1, loss_matrix % n - ! set up a new row in matrix + ! Set up a new row in matrix call loss_matrix % new_row() - ! get indices for that row + ! Get indices for that row call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - ! retrieve cell data + ! Retrieve cell data totxs = cmfd%totalxs(g,i,j,k) scattxsgg = cmfd%scattxs(g,g,i,j,k) dtilde = cmfd%dtilde(:,g,i,j,k) hxyz = cmfd%hxyz(:,i,j,k) - ! check and get dhat + ! Check and get dhat if (allocated(cmfd%dhat)) then dhat = cmfd%dhat(:,g,i,j,k) else dhat = ZERO end if - ! create boundary vector + ! Create boundary vector bound = (/i,i,j,j,k,k/) - ! begin loop over leakages + ! Begin loop over leakages ! 1=-x, 2=+x, 3=-y, 4=+y, 5=-z, 6=+z LEAK: do l = 1,6 - ! define (x,y,z) and (-,+) indices + ! Define (x,y,z) and (-,+) indices xyz_idx = int(ceiling(real(l)/real(2))) ! x=1, y=2, z=3 dir_idx = 2 - mod(l,2) ! -=1, +=2 - ! calculate spatial indices of neighbor + ! Calculate spatial indices of neighbor neig_idx = (/i,j,k/) ! begin with i,j,k shift_idx = -2*mod(l,2) +1 ! shift neig by -1 or +1 neig_idx(xyz_idx) = shift_idx + neig_idx(xyz_idx) - ! check for global boundary + ! Check for global boundary if (bound(l) /= nxyz(xyz_idx,dir_idx)) then - ! check for core map + ! Check for core map if (cmfd_coremap) then - ! check that neighbor is not reflector + ! Check that neighbor is not reflector if (cmfd % coremap(neig_idx(1),neig_idx(2),neig_idx(3)) /= & CMFD_NOACCEL) then - ! compute leakage coefficient for neighbor + ! Compute leakage coefficient for neighbor jn = -dtilde(l) + shift_idx*dhat(l) - ! get neighbor matrix index + ! Get neighbor matrix index call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & neig_mat_idx, ng, nx, ny) - ! compute value and record to bank + ! Compute value and record to bank val = jn/hxyz(xyz_idx) - ! record value in matrix + ! Record value in matrix call loss_matrix % add_value(neig_mat_idx, val) end if else - ! compute leakage coefficient for neighbor + ! Compute leakage coefficient for neighbor jn = -dtilde(l) + shift_idx*dhat(l) - ! get neighbor matrix index + ! Get neighbor matrix index call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & neig_mat_idx, ng, nx, ny) - ! compute value and record to bank + ! Compute value and record to bank val = jn/hxyz(xyz_idx) - ! record value in matrix + ! Record value in matrix call loss_matrix % add_value(neig_mat_idx, val) end if end if - ! compute leakage coefficient for target + ! Compute leakage coefficient for target jo(l) = shift_idx*dtilde(l) + dhat(l) end do LEAK - ! calate net leakage coefficient for target + ! Calculate net leakage coefficient for target jnet = (jo(2) - jo(1))/hxyz(1) + (jo(4) - jo(3))/hxyz(2) + & (jo(6) - jo(5))/hxyz(3) - ! calculate loss of neutrons + ! Calculate loss of neutrons val = jnet + totxs - scattxsgg - ! record diagonal term + ! Record diagonal term call loss_matrix % add_value(irow, val) - ! begin loop over off diagonal in-scattering + ! Begin loop over off diagonal in-scattering SCATTR: do h = 1, ng - ! cycle though if h=g + ! Cycle though if h=g, value already banked in removal xs if (h == g) cycle - ! get neighbor matrix index + ! Get neighbor matrix index call indices_to_matrix(h, i, j, k, scatt_mat_idx, ng, nx, ny) - ! check for adjoint + ! Check for adjoint if (adjoint_calc) then - ! get scattering macro xs, transposed! + ! Get scattering macro xs, transposed! scattxshg = cmfd%scattxs(g, h, i, j, k) else - ! get scattering macro xs + ! Get scattering macro xs scattxshg = cmfd%scattxs(h, g, i, j, k) end if - ! negate the scattering xs + ! Negate the scattering xs val = -scattxshg - ! record value in matrix + ! Record value in matrix call loss_matrix % add_value(scatt_mat_idx, val) end do SCATTR @@ -375,15 +375,15 @@ contains integer :: ny ! maximum number of y cells integer :: ng ! maximum number of groups - ! check if coremap is used + ! Check if coremap is used if (cmfd_coremap) then - ! get idx from core map + ! Get idx from core map matidx = ng*(cmfd % coremap(i,j,k)) - (ng - g) else - ! compute index + ! Compute index matidx = g + ng*(i - 1) + ng*nx*(j - 1) + ng*nx*ny*(k - 1) end if @@ -391,7 +391,7 @@ contains end subroutine indices_to_matrix !=============================================================================== -! MATRIX_TO_INDICES +! MATRIX_TO_INDICES converts a matrix index to spatial and group indicies !=============================================================================== subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) @@ -406,10 +406,10 @@ contains integer :: nz ! maximum number of z cells integer :: ng ! maximum number of groups - ! check for core map + ! Check for core map if (cmfd_coremap) then - ! get indices from indexmap + ! Get indices from indexmap g = mod(irow, ng) + 1 i = cmfd % indexmap((irow-1)/ng+1,1) j = cmfd % indexmap((irow-1)/ng+1,2) @@ -417,7 +417,7 @@ contains else - ! compute indices + ! Compute indices g = mod(irow-1, ng) + 1 i = mod(irow-1, ng*nx)/ng + 1 j = mod(irow-1, ng*nx*ny)/(ng*nx)+ 1 diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 92bd77bcc7..6de504f5a3 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -29,7 +29,7 @@ module cmfd_power_solver contains !=============================================================================== -! CMFD_POWER_EXECUTE +! CMFD_POWER_EXECUTE sets up and runs power iteration solver for CMFD !=============================================================================== subroutine cmfd_power_execute(k_tol, s_tol, adjoint) @@ -42,48 +42,48 @@ contains logical :: physical_adjoint = .false. - ! set tolerances if present + ! Set tolerances if present if (present(k_tol)) ktol = k_tol if (present(s_tol)) stol = s_tol - ! check for adjoint execution + ! Check for adjoint execution if (present(adjoint)) adjoint_calc = adjoint - ! check for physical adjoint + ! Check for physical adjoint if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'physical') & physical_adjoint = .true. - ! start timer for build + ! Start timer for build call time_cmfdbuild % start() - ! initialize solver + ! Initialize solver call gmres % create() - ! initialize matrices and vectors + ! Initialize matrices and vectors call init_data(physical_adjoint) - ! check for adjoint calculation + ! Check for adjoint calculation if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & call compute_adjoint() - ! set up krylov info + ! Set up krylov info call gmres % set_oper(loss % petsc_mat, loss % petsc_mat) - ! precondition matrix + ! Precondition matrix call gmres % precondition(loss % petsc_mat) - ! stop timer for build + ! Stop timer for build call time_cmfdbuild % stop() - ! begin power iteration + ! Begin power iteration call time_cmfdsolve % start() call execute_power_iter() call time_cmfdsolve % stop() - ! extract results + ! Extract results call extract_results() - ! deallocate petsc objects + ! Deallocate petsc objects call finalize() end subroutine cmfd_power_execute @@ -101,35 +101,35 @@ contains integer :: n ! problem size real(8) :: guess ! initial guess - ! set up matrices + ! Set up matrices call init_loss_matrix(loss) call init_prod_matrix(prod) - ! get problem size + ! Get problem size n = loss % n - ! set up flux vectors + ! Set up flux vectors call phi_n % create(n) call phi_o % create(n) - ! set up source vectors + ! Set up source vectors call S_n % create(n) call S_o % create(n) - ! set initial guess + ! Set initial guess guess = ONE phi_n % val = guess phi_o % val = guess k_n = guess k_o = guess - ! set up loss matrix + ! Set up loss matrix call build_loss_matrix(loss, adjoint=adjoint) - ! set up production matrix + ! Set up production matrix call build_prod_matrix(prod, adjoint=adjoint) - ! setup petsc for everything + ! Setup petsc for everything call loss % assemble() call prod % assemble() call loss % setup_petsc() @@ -142,7 +142,7 @@ contains end subroutine init_data !=============================================================================== -! COMPUTE_ADJOINT +! COMPUTE_ADJOINT computes a mathematical adjoint of CMFD problem !=============================================================================== subroutine compute_adjoint() @@ -209,7 +209,7 @@ contains end subroutine execute_power_iter !=============================================================================== -! CONVERGENCE +! CONVERGENCE checks the convergence of the CMFD problem !=============================================================================== subroutine convergence(iter) @@ -244,7 +244,7 @@ contains end subroutine convergence !=============================================================================== -! EXTRACT_RESULTS +! EXTRACT_RESULTS takes results and puts them in CMFD global data object !=============================================================================== subroutine extract_results() @@ -298,7 +298,7 @@ contains end subroutine extract_results !=============================================================================== -! FINALIZE +! FINALIZE frees all memory associated with power iteration !=============================================================================== subroutine finalize() diff --git a/src/cmfd_prod_operator.F90 b/src/cmfd_prod_operator.F90 index 9e017013f8..4c6a28fb88 100644 --- a/src/cmfd_prod_operator.F90 +++ b/src/cmfd_prod_operator.F90 @@ -11,7 +11,7 @@ module cmfd_prod_operator contains !============================================================================== -! INIT_PROD_MATRIX +! INIT_PROD_MATRIX preallocates prod matrix and initializes it !============================================================================== subroutine init_prod_matrix(prod_matrix) @@ -25,13 +25,13 @@ contains integer :: n ! total length of matrix integer :: nnz ! number of nonzeros in matrix - ! get maximum number of cells in each direction + ! Get maximum number of cells in each direction nx = cmfd%indices(1) ny = cmfd%indices(2) nz = cmfd%indices(3) ng = cmfd%indices(4) - ! calculate dimensions and number of nonzeros in matrix + ! Calculate dimensions and number of nonzeros in matrix if (cmfd_coremap) then n = cmfd % mat_dim * ng else @@ -39,7 +39,7 @@ contains end if nnz = n * ng - ! configure prod matrix + ! Configure prod matrix call prod_matrix % create(n, nnz) end subroutine init_prod_matrix @@ -160,7 +160,7 @@ contains end subroutine indices_to_matrix !=============================================================================== -! MATRIX_TO_INDICES +! MATRIX_TO_INDICES converts matrix index to spatial and group indices !=============================================================================== subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) From decf2e8b8aec912f975f8fde28b5fc26b708a87e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 9 Jul 2013 11:54:08 -0400 Subject: [PATCH 035/192] added compiler flags so that code compiles without petsc --- src/DEPENDENCIES | 9 +++++++-- src/cmfd_execute.F90 | 4 ---- src/cmfd_input.F90 | 2 +- src/cmfd_power_solver.F90 | 10 +++++----- src/matrix_header.F90 | 25 +++++++++++++++---------- src/petsc_solver.F90 | 37 +++++++++++++++++++++++++++---------- src/vector_header.F90 | 9 ++++----- 7 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 433ed14828..0de6323f20 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -11,11 +11,14 @@ cmfd_output.o: cmfd_header.o cmfd_output.o: constants.o cmfd_output.o: global.o -list_header.o: constants.o - ace_header.o: constants.o ace_header.o: endf_header.o +list_header.o: constants.o + +petsc_solver.o: matrix_header.o +petsc_solver.o: vector_header.o + cmfd_loss_operator.o: constants.o cmfd_loss_operator.o: global.o cmfd_loss_operator.o: matrix_header.o @@ -118,6 +121,8 @@ cmfd_input.o: tally_header.o cmfd_input.o: tally_initialize.o cmfd_input.o: templates/cmfd_t.o +matrix_header.o: vector_header.o + input_xml.o: cmfd_input.o input_xml.o: constants.o input_xml.o: dict_header.o diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index b2762ee236..c08fa4fd1b 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -105,8 +105,6 @@ contains end subroutine cmfd_init_batch -# ifdef PETSC - !============================================================================== ! PROCESS_CMFD_OPTIONS processes user options that interface with PETSc !============================================================================== @@ -404,8 +402,6 @@ contains end function get_matrix_idx -# endif - !=============================================================================== ! CMFD_TALLY_RESET !=============================================================================== diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index e98eaac762..7b5e9c2d62 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -32,7 +32,7 @@ contains end if ! Split up procs -# ifdef MPI +# ifdef PETSC call MPI_COMM_SPLIT(MPI_COMM_WORLD,color,0,new_comm,mpi_err) # endif diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 6de504f5a3..60ba09f2c6 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -67,10 +67,10 @@ contains call compute_adjoint() ! Set up krylov info - call gmres % set_oper(loss % petsc_mat, loss % petsc_mat) + call gmres % set_oper(loss, loss) ! Precondition matrix - call gmres % precondition(loss % petsc_mat) + call gmres % precondition(loss) ! Stop timer for build call time_cmfdbuild % stop() @@ -177,16 +177,16 @@ contains do i = 1, 10000 ! compute source vector - call prod % vector_multiply(phi_o % petsc_vec, S_o % petsc_vec) + call prod % vector_multiply(phi_o, S_o) ! normalize source vector S_o % val = S_o % val / k_o ! compute new flux vector - call gmres % solve(S_o % petsc_vec, phi_n % petsc_vec) + call gmres % solve(S_o, phi_n) ! compute new source vector - call prod % vector_multiply(phi_n % petsc_vec, S_n % petsc_vec) + call prod % vector_multiply(phi_n, S_n) ! compute new k-eigenvalue k_n = sum(S_n % val) / sum(S_o % val) diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index f402f32428..3abe276d71 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -24,12 +24,10 @@ module matrix_header procedure :: add_value => matrix_add_value procedure :: new_row => matrix_new_row procedure :: assemble => matrix_assemble -# ifdef PETSC procedure :: setup_petsc => matrix_setup_petsc procedure :: write_petsc_binary => matrix_write_petsc_binary procedure :: transpose => matrix_transpose procedure :: vector_multiply => matrix_vector_multiply -# endif end type matrix contains @@ -102,8 +100,6 @@ contains end subroutine matrix_new_row -# ifdef PETSC - !=============================================================================== ! MATRIX_ASSEMBLE !=============================================================================== @@ -224,8 +220,10 @@ contains self % col = self % col - 1 ! link to petsc +#ifdef PETSC call MatCreateSeqAIJWithArrays(PETSC_COMM_WORLD, self % n, self % n, & self % row, self % col, self % val, self % petsc_mat, petsc_err) +#endif end subroutine matrix_setup_petsc @@ -239,12 +237,14 @@ contains class(Matrix) :: self integer :: petsc_err +#ifdef PETSC PetscViewer :: viewer call PetscViewerBinaryOpen(PETSC_COMM_WORLD, trim(filename), & FILE_MODE_WRITE, viewer, petsc_err) call MatView(self % petsc_mat, viewer, petsc_err) call PetscViewerDestroy(viewer, petsc_err) +#endif end subroutine matrix_write_petsc_binary @@ -258,8 +258,10 @@ contains integer :: petsc_err +#ifdef PETSC call MatTranspose(self % petsc_mat, MAT_REUSE_MATRIX, self % petsc_mat, & petsc_err) +#endif end subroutine matrix_transpose @@ -269,16 +271,19 @@ contains subroutine matrix_vector_multiply(self, vec_in, vec_out) + use vector_header, only: Vector + class(Matrix) :: self - Vec :: vec_in - Vec :: vec_out + type(Vector) :: vec_in + type(Vector) :: vec_out integer :: petsc_err - call self % write_petsc_binary('prod_mat.bin') - call MatMult(self % petsc_mat, vec_in, vec_out, petsc_err) + +#ifdef PETSC + call MatMult(self % petsc_mat, vec_in % petsc_vec, vec_out % petsc_vec, & + petsc_err) +#endif end subroutine matrix_vector_multiply -# endif - end module matrix_header diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index 8f42f53202..446e12732a 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -1,13 +1,20 @@ module petsc_solver + use matrix_header, only: Matrix + use vector_header, only: Vector + implicit none private +#ifdef PETSC # include +#endif type, public :: Petsc_gmres +#ifdef PETSC KSP :: ksp PC :: pc +#endif contains procedure :: create => gmres_create procedure :: precondition => gmres_precondition @@ -32,6 +39,7 @@ contains real(8) :: rtol = 1.0e-10_8 real(8) :: atol = 1.0e-10_8 +#ifdef PETSC call KSPCreate(PETSC_COMM_WORLD, self % ksp, petsc_err) call KSPSetTolerances(self % ksp, rtol, atol, & PETSC_DEFAULT_DOUBLE_PRECISION, PETSC_DEFAULT_INTEGER, petsc_err) @@ -39,6 +47,7 @@ contains call KSPSetInitialGuessNonzero(self % ksp, PETSC_TRUE, petsc_err) call KSPGetPC(self % ksp, self % pc, petsc_err) call PCFactorSetLevels(self % pc, ilu_levels, petsc_err) +#endif end subroutine gmres_create @@ -46,13 +55,15 @@ contains ! GMRES_PRECONDITION !=============================================================================== - subroutine gmres_precondition(self, matrix) + subroutine gmres_precondition(self, mat) class(Petsc_gmres) :: self - Mat :: matrix + type(Matrix) :: mat +#ifdef PETSC call KSPSetUp(self % ksp, petsc_err) - call PCFactorGetMatrix(self % pc, matrix, petsc_err) + call PCFactorGetMatrix(self % pc, mat % petsc_mat, petsc_err) +#endif end subroutine gmres_precondition @@ -60,14 +71,16 @@ contains ! GMRES_SET_OPER !=============================================================================== - subroutine gmres_set_oper(self, prec_matrix, matrix) + subroutine gmres_set_oper(self, prec_mat, mat) class(Petsc_gmres) :: self - Mat :: prec_matrix - Mat :: matrix + type(Matrix) :: prec_mat + type(Matrix) :: mat - call KSPSetOperators(self % ksp, matrix, prec_matrix, & +#ifdef PETSC + call KSPSetOperators(self % ksp, mat % petsc_mat, prec_mat % petsc_mat, & SAME_NONZERO_PATTERN, petsc_err) +#endif end subroutine gmres_set_oper @@ -79,7 +92,9 @@ contains class(Petsc_gmres) :: self +#ifdef PETSC call KSPDestroy(self % ksp, petsc_err) +#endif end subroutine gmres_destroy @@ -90,12 +105,14 @@ contains subroutine gmres_solve(self, b, x) class(Petsc_gmres) :: self - Vec :: b - Vec :: x + type(Vector) :: b + type(Vector) :: x integer :: petsc_err - call KSPSolve(self % ksp, b, x, petsc_err) +#ifdef PETSC + call KSPSolve(self % ksp, b % petsc_vec, x % petsc_vec, petsc_err) +#endif end subroutine gmres_solve diff --git a/src/vector_header.F90 b/src/vector_header.F90 index 53d3bb6fe5..ec9427e916 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -19,10 +19,8 @@ module vector_header procedure :: create => vector_create procedure :: destroy => vector_destroy procedure :: add_value => vector_add_value -# ifdef PETSC procedure :: setup_petsc => vector_setup_petsc procedure :: write_petsc_binary => vector_write_petsc_binary -# endif end type Vector contains @@ -73,8 +71,6 @@ contains end subroutine vector_add_value -# ifdef PETSC - !=============================================================================== ! VECTOR_SETUP_PETSC !=============================================================================== @@ -86,8 +82,10 @@ contains integer :: petsc_err ! link to petsc +#ifdef PETSC call VecCreateSeqWithArray(PETSC_COMM_WORLD, 1, self % n, self % val, & self % petsc_vec, petsc_err) +#endif end subroutine vector_setup_petsc @@ -101,14 +99,15 @@ contains class(Vector) :: self integer :: petsc_err +#ifdef PETSC PetscViewer :: viewer call PetscViewerBinaryOpen(PETSC_COMM_WORLD, trim(filename), & FILE_MODE_WRITE, viewer, petsc_err) call VecView(self % petsc_vec, viewer, petsc_err) call PetscViewerDestroy(viewer, petsc_err) +#endif end subroutine vector_write_petsc_binary -# endif end module vector_header From 22c373dd03b956264c8559204bcd8b7f34004878 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 10 Jul 2013 11:45:46 -0400 Subject: [PATCH 036/192] restructued jacobian and jfnk routines to fit with new convention --- src/DEPENDENCIES | 19 +- src/OBJECTS | 3 +- src/cmfd_execute.F90 | 6 +- src/cmfd_jacobian_operator.F90 | 357 ------------------------- src/cmfd_jfnk_solver.F90 | 353 ++++++++++++++++++++++++ src/cmfd_snes_solver.F90 | 473 --------------------------------- src/matrix_header.F90 | 38 ++- src/petsc_solver.F90 | 146 +++++++++- src/vector_header.F90 | 6 +- 9 files changed, 544 insertions(+), 857 deletions(-) delete mode 100644 src/cmfd_jacobian_operator.F90 create mode 100644 src/cmfd_jfnk_solver.F90 delete mode 100644 src/cmfd_snes_solver.F90 diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 0de6323f20..bb1f7a7cd6 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -104,13 +104,6 @@ fission.o: global.o fission.o: interpolation.o fission.o: search.o -cmfd_jacobian_operator.o: constants.o -cmfd_jacobian_operator.o: global.o - -cmfd_snes_solver.o: constants.o -cmfd_snes_solver.o: global.o -cmfd_snes_solver.o: string.o - cmfd_input.o: error.o cmfd_input.o: global.o cmfd_input.o: mesh_header.o @@ -121,6 +114,7 @@ cmfd_input.o: tally_header.o cmfd_input.o: tally_initialize.o cmfd_input.o: templates/cmfd_t.o +matrix_header.o: constants.o matrix_header.o: vector_header.o input_xml.o: cmfd_input.o @@ -144,6 +138,15 @@ input_xml.o: templates/plots_t.o input_xml.o: templates/settings_t.o input_xml.o: templates/tallies_t.o +cmfd_jfnk_solver.o: cmfd_loss_operator.o +cmfd_jfnk_solver.o: cmfd_power_solver.o +cmfd_jfnk_solver.o: cmfd_prod_operator.o +cmfd_jfnk_solver.o: constants.o +cmfd_jfnk_solver.o: global.o +cmfd_jfnk_solver.o: matrix_header.o +cmfd_jfnk_solver.o: petsc_solver.o +cmfd_jfnk_solver.o: vector_header.o + main.o: constants.o main.o: eigenvalue.o main.o: finalize.o @@ -317,8 +320,8 @@ plot_header.o: constants.o cmfd_header.o: constants.o cmfd_execute.o: cmfd_data.o +cmfd_execute.o: cmfd_jfnk_solver.o cmfd_execute.o: cmfd_power_solver.o -cmfd_execute.o: cmfd_snes_solver.o cmfd_execute.o: constants.o cmfd_execute.o: error.o cmfd_execute.o: global.o diff --git a/src/OBJECTS b/src/OBJECTS index fe309726d7..5075a5ee53 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -6,12 +6,11 @@ cmfd_data.o \ cmfd_execute.o \ cmfd_header.o \ cmfd_input.o \ -cmfd_jacobian_operator.o \ +cmfd_jfnk_solver.o \ cmfd_loss_operator.o \ cmfd_output.o \ cmfd_power_solver.o \ cmfd_prod_operator.o \ -cmfd_snes_solver.o \ constants.o \ cross_section.o \ dict_header.o \ diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index c08fa4fd1b..60452c8850 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -21,7 +21,7 @@ contains use cmfd_data, only: set_up_cmfd use cmfd_power_solver, only: cmfd_power_execute - use cmfd_snes_solver, only: cmfd_snes_execute + use cmfd_jfnk_solver, only: cmfd_jfnk_execute use error, only: warning, fatal_error ! CMFD single processor on master @@ -40,7 +40,7 @@ contains if (trim(cmfd_solver_type) == 'power') then call cmfd_power_execute() elseif (trim(cmfd_solver_type) == 'jfnk') then - call cmfd_snes_execute() + call cmfd_jfnk_execute() else message = 'solver type became invalid after input processing' call fatal_error() @@ -51,7 +51,7 @@ contains if (trim(cmfd_solver_type) == 'power') then call cmfd_power_execute(adjoint = .true.) elseif (trim(cmfd_solver_type) == 'jfnk') then - call cmfd_snes_execute(adjoint = .true.) + call cmfd_jfnk_execute(adjoint = .true.) end if end if diff --git a/src/cmfd_jacobian_operator.F90 b/src/cmfd_jacobian_operator.F90 deleted file mode 100644 index b5532b66a7..0000000000 --- a/src/cmfd_jacobian_operator.F90 +++ /dev/null @@ -1,357 +0,0 @@ -module cmfd_jacobian_operator - -# ifdef PETSC - - use cmfd_loss_operator, only: loss_operator,init_M_operator, & - build_loss_matrix,destroy_M_operator - use cmfd_prod_operator, only: prod_operator,init_F_operator, & - build_prod_matrix,destroy_F_operator - implicit none - private - public :: init_J_operator, build_jacobian_matrix, destroy_J_operator - -# include - - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups - integer :: ierr ! petsc error code - - type, public :: jacobian_operator - Mat :: J ! petsc matrix for neutronic prod operator - integer :: n ! dimensions of matrix - integer :: nnz ! max number of nonzeros - integer :: localn ! local size on proc - integer, allocatable :: d_nnz(:) ! vector of diagonal preallocation - integer, allocatable :: o_nnz(:) ! vector of off-diagonal preallocation - end type jacobian_operator - - type, public :: operators - type(loss_operator) :: loss - type(prod_operator) :: prod - end type operators - -contains - -!============================================================================== -! INIT_J_OPERATOR -!============================================================================== - - subroutine init_J_operator(this,ctx) - - type(jacobian_operator) :: this - type(operators) :: ctx - - ! get indices - call get_J_indices(this) - - ! get preallocation - call preallocate_jacobian_matrix(this,ctx) - - ! set up M operator - call MatCreateAIJ(PETSC_COMM_WORLD, this%localn, this%localn, PETSC_DECIDE,& - PETSC_DECIDE, PETSC_NULL_INTEGER, this%d_nnz, PETSC_NULL_INTEGER, & - this%o_nnz, this%J,ierr) - call MatSetOption(this%J, MAT_NEW_NONZERO_LOCATIONS, PETSC_TRUE, ierr) - call MatSetOption(this%J, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE, ierr) - - end subroutine init_J_operator - -!============================================================================== -! GET_J_INDICES -!============================================================================== - - subroutine get_J_indices(this) - - use global, only: cmfd, cmfd_coremap - - type(jacobian_operator) :: this - - ! get maximum number of cells in each direction - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) - - ! get number of nonzeros - this%nnz = 7 + ng - 1 - - ! calculate dimensions of matrix - this%n = nx*ny*nz*ng - - ! calculate dimensions of matrix - if (cmfd_coremap) then - this%n = cmfd % mat_dim * ng - else - this%n = nx*ny*nz*ng - end if - - ! add 1 for eigenvalue row -! this%n = this%n + 1 - - end subroutine get_J_indices - -!=============================================================================== -! PREALLOCATE_JACOBIAN_MATRIX -!=============================================================================== - - subroutine preallocate_jacobian_matrix(this,ctx) - - use global, only: cmfd, n_procs_cmfd, rank - - type(jacobian_operator) :: this - type(operators) :: ctx - - integer :: n ! the extent of the matrix - integer :: row_start ! index of local starting row - integer :: row_end ! index of local final row - - ! get local problem size - n = this%n - - ! determine local size, divide evenly between all other procs - this%localn = n/(n_procs_cmfd) - - ! add 1 more if less proc id is less than mod - if (rank < mod(n,n_procs_cmfd)) this%localn = this%localn + 1 - - ! add another 1 on last proc - if (rank == n_procs_cmfd - 1) this%localn = this%localn + 1 - - ! determine local starting row - row_start = 0 - if (rank < mod(n,n_procs_cmfd)) then - row_start = rank*(n/n_procs_cmfd+1) - else - row_start = min(mod(n,n_procs_cmfd)*(n/n_procs_cmfd+1) + (rank - & - mod(n,n_procs_cmfd))*(n/n_procs_cmfd),n) - end if - - ! determine local final row - row_end = row_start + this%localn - 1 - - ! allocate counters - if (.not. allocated(this%d_nnz)) allocate(this%d_nnz(row_start:row_end)) - if (.not. allocated(this%o_nnz)) allocate(this%o_nnz(row_start:row_end)) - this % d_nnz = 0 - this % o_nnz = 0 - - ! start with pattern from loss matrix - if (rank == n_procs_cmfd - 1) then - this % d_nnz(row_start:row_end-1) = ctx%loss%d_nnz - this % o_nnz(row_start:row_end-1) = ctx%loss%o_nnz - else - this % d_nnz = ctx%loss%d_nnz - this % o_nnz = ctx%loss%o_nnz - end if - - ! append -F*phi term for last processor will take care of 1 for lambda - if (rank == n_procs_cmfd - 1) then - this%d_nnz = this%d_nnz + 1 - else - this%o_nnz = this%o_nnz + 1 - end if - - ! do last row which has all filled (already did lower left corner above) - if (rank == n_procs_cmfd - 1) then - this % d_nnz(row_end) = this % d_nnz(row_end) + (row_end - row_start) - this % o_nnz(row_end) = this % o_nnz(row_end) + (this%n - (row_end - & - row_start)) - end if - - end subroutine preallocate_jacobian_matrix - -!=============================================================================== -! BUILD_JACOBIAN_MATRIX creates the matrix representing loss of neutrons -!=============================================================================== - - subroutine build_jacobian_matrix(snes,x,jac,jac_prec,flag,ctx,ierr) - - use constants, only: ZERO, ONE - use global, only: n_procs_cmfd, cmfd_write_matrices, rank - - SNES :: snes ! the snes context - Vec :: x ! the solution vector - Mat :: jac ! the jacobian matrix - Mat :: jac_prec ! the jacobian preconditioner - MatStructure :: flag ! not used - type(operators) :: ctx ! not used - integer :: ierr ! petsc error flag - - Vec :: phi ! flux vector - Vec :: source ! source vector - integer :: n ! problem size - integer :: k ! implied do loop counter - integer :: ncols ! number of nonzeros in cols - integer :: irow ! row counter - integer :: row_start! starting local row on process - integer :: row_end ! ending local row on process - integer, allocatable :: dims(:) ! vec of starting and ending rows - integer, allocatable :: dims1(:) ! vec of sizes on each proc - integer, allocatable :: cols(:) ! vector of column numbers - real(8) :: lambda ! eigenvalue - real(8), pointer :: xptr(:) ! pointer to solution vector - real(8), pointer :: sptr(:) ! pointer to source vector - real(8), allocatable :: vals(:) ! vector of row values - real(8), allocatable :: phi_tmp(:) ! temp buffer for flux - - ! create operators - call build_loss_matrix(ctx%loss) - call build_prod_matrix(ctx%prod) - - ! get problem size - n = ctx%loss%n - - ! get local size on each processor - call MatGetOwnershipRange(jac_prec, row_start, row_end, ierr) - - ! allocate cols and initialize to zero - if (.not. allocated(cols)) allocate(cols(& - maxval(ctx%loss%d_nnz + ctx%loss%o_nnz))) - if (.not. allocated(vals)) allocate(vals(& - maxval(ctx%loss%d_nnz + ctx%loss%o_nnz))) - cols = 0 - vals = ZERO - - ! get pointers to residual vector - call VecGetArrayF90(x, xptr, ierr) - - ! create petsc vector for flux - call VecCreateMPI(PETSC_COMM_WORLD, ctx%loss%localn, PETSC_DECIDE, phi, ierr) - - ! extract flux and eigenvalue - call VecPlaceArray(phi, xptr, ierr) - if (rank == n_procs_cmfd - 1) lambda = xptr(size(xptr)) - call MPI_BCAST(lambda, 1, MPI_REAL8, n_procs_cmfd-1, PETSC_COMM_WORLD, ierr) - - ! compute math (M-lambda*F) M is overwritten here - call MatAXPY(ctx%loss%M, -lambda, ctx%prod%F, & - DIFFERENT_NONZERO_PATTERN, ierr) - - ! create tmp petsc vector for source - call VecCreateMPI(PETSC_COMM_WORLD, ctx%loss%localn, PETSC_DECIDE, & - source, ierr) - - ! perform math (-F*phi --> source) - call MatMult(ctx%prod%F, phi, source, ierr) - call VecScale(source, -ONE, ierr) - - ! get pointer to source - call VecGetArrayF90(source, sptr, ierr) - - ! begin loop to insert things into matrix - do irow = row_start, row_end - 1 - - ! don't do last row - if (irow == n) cycle - - ! get row of matrix - call MatGetRow(ctx%loss%M, irow, ncols, cols, vals, ierr) - - ! set that row to Jacobian matrix - call MatSetValues(jac_prec, 1, (/irow/), ncols, cols(1:ncols), vals, & - INSERT_VALUES, ierr) - - ! restore the row - call MatRestoreRow(ctx%loss%M, irow, ncols, cols, vals, ierr) - - ! insert source value - call MatSetValue(jac_prec, irow, n, sptr(irow-row_start+1), & - INSERT_VALUES, ierr) - - end do - - ! allocate space for flux vector buffer - if (rank == n_procs_cmfd - 1) then - if (.not. allocated(phi_tmp)) allocate(phi_tmp(0:n-1)) - end if - - ! get size on each proc - if (.not. allocated(dims)) allocate(dims(0:n_procs_cmfd)) - if (.not. allocated(dims1)) allocate(dims1(0:n_procs_cmfd-1)) - call VecGetOwnershipRanges(phi, dims, ierr) - do k = 0, n_procs_cmfd-1 - dims1(k) = dims(k+1) - dims(k) - end do - - ! gather data on all procs (will truncate xptr if needed for last proc) - call MPI_GATHERV(xptr, dims1(rank), MPI_REAL8, phi_tmp, dims1, & - dims(0:n_procs_cmfd-1), MPI_REAL8, n_procs_cmfd-1, & - PETSC_COMM_WORLD, ierr) - - ! set values in last row of matrix - if (rank == n_procs_cmfd - 1) then - phi_tmp = -phi_tmp ! negate the transpose - call MatSetValues(jac_prec, 1, (/n/), n, (/(k,k=0,n-1)/), phi_tmp, & - INSERT_VALUES, ierr) - call MatSetValue(jac_prec, n, n, ONE, INSERT_VALUES, ierr) - end if - - ! assemble matrix - call MatAssemblyBegin(jac_prec, MAT_FINAL_ASSEMBLY, ierr) - call MatAssemblyEnd(jac_prec, MAT_FINAL_ASSEMBLY, ierr) - call MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY, ierr) - call MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY, ierr) - - ! reset all vectors - call VecResetArray(phi,ierr) - - ! restore all vectors - call VecRestoreArrayF90(x, xptr, ierr) - call VecRestoreArrayF90(source, sptr, ierr) - - ! destroy all temporary objects - call VecDestroy(phi, ierr) - call VecDestroy(source, ierr) - - ! deallocate all temporary space - if (allocated(cols)) deallocate(cols) - if (allocated(vals)) deallocate(vals) - if (allocated(phi_tmp)) deallocate(phi_tmp) - if (allocated(dims)) deallocate(dims) - if (allocated(dims1)) deallocate(dims1) - - ! print jacobian out - if (cmfd_write_matrices) call print_J_operator(jac_prec) - - end subroutine build_jacobian_matrix - -!=============================================================================== -! PRINT_J_OPERATOR -!=============================================================================== - - subroutine print_J_operator(jac) - - Mat :: jac - - PetscViewer :: viewer - - ! write out matrix in binary file (debugging) - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'jacobian.bin', & - FILE_MODE_WRITE, viewer, ierr) - call MatView(jac, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) - - end subroutine print_J_operator - -!============================================================================== -! DESTROY_J_OPERATOR -!============================================================================== - - subroutine destroy_J_operator(this) - - type(jacobian_operator) :: this - - ! deallocate matrix - call MatDestroy(this%J,ierr) - - ! deallocate other parameters - if (allocated(this%d_nnz)) deallocate(this%d_nnz) - if (allocated(this%o_nnz)) deallocate(this%o_nnz) - - end subroutine destroy_J_operator - -# endif - -end module cmfd_jacobian_operator diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 new file mode 100644 index 0000000000..acba55b84b --- /dev/null +++ b/src/cmfd_jfnk_solver.F90 @@ -0,0 +1,353 @@ +module cmfd_jfnk_solver + + use cmfd_loss_operator, only: init_loss_matrix, build_loss_matrix + use cmfd_power_solver, only: cmfd_power_execute + use cmfd_prod_operator, only: init_prod_matrix, build_prod_matrix + use matrix_header, only: Matrix + use petsc_solver, only: Petsc_jfnk, Jfnk_ctx + use vector_header, only: Vector + + implicit none + private + public :: cmfd_jfnk_execute + + logical :: adjoint_calc = .false. ! adjoint calculation + type(Jfnk_ctx) :: jfnk_data + type(Matrix) :: jac_prec + type(Matrix) :: loss + type(Matrix) :: prod + type(Petsc_jfnk) :: jfnk + type(Vector) :: resvec + type(Vector) :: xvec + +contains + +!=============================================================================== +! CMFD_JFNK_EXECUTE +!=============================================================================== + + subroutine cmfd_jfnk_execute(adjoint) + + logical, optional :: adjoint ! adjoint calculation + + ! check for adjoint + if (present(adjoint)) adjoint_calc = adjoint + + ! seed with power iteration + call cmfd_power_execute(k_tol=1.E-3_8, s_tol=1.E-3_8, adjoint=adjoint_calc) + + ! initialize data + call init_data() + + ! initialize solver + call jfnk % create() + + ! solve the system +! call SNESSolve(snes, PETSC_NULL_DOUBLE, xvec, ierr) + + ! extracts results to cmfd object + call extract_results() + + ! deallocate all slepc data + call finalize() + + end subroutine cmfd_jfnk_execute + +!=============================================================================== +! INIT_DATA allocates matrices vectors for CMFD solution +!=============================================================================== + + subroutine init_data() + + use constants, only: ONE + use global, only: cmfd + + integer :: n ! problem size + + ! Set up all matrices + call init_loss_matrix(loss) + call init_prod_matrix(prod) + call init_jacobian_matrix() + + ! Set up for use with petsc + call jac_prec % setup_petsc() + + ! Get problem size + n = jac_prec % n + + ! Create problem vectors + call resvec % create(n) + call xvec % create(n) + + ! set flux in guess + if (adjoint_calc) then + xvec % val(1:n) = cmfd % adj_phi + else + xvec % val(1:n) = cmfd % phi + end if + + ! set keff in guess + if (adjoint_calc) then + xvec % val(n + 1) = ONE/cmfd % adj_keff + else + xvec % val(n + 1) = ONE/cmfd % keff + end if + + ! set solver names + + end subroutine init_data + +!============================================================================== +! INIT_JACOBIAN_MATRIX preallocates jacobian matrix and initializes it +!============================================================================== + + subroutine init_jacobian_matrix() + + integer :: nnz + integer :: n + + ! Get length of matrix and number of nonzeros total in loss matrix + nnz = loss % nnz + n = loss % n + + ! There is one more nonzero for each row and and additional row of nonzeros + nnz = nnz + 2*n + 1 + + ! We need 1 more row for the jacobian + n = n + 1 + + ! Configure Jacobian matrix + call jac_prec % create(n, nnz) + + end subroutine init_jacobian_matrix + +!=============================================================================== +! BUILD_JACOBIAN_MATRIX creates the Jacobian of nonlinear eigenvalue problem +!=============================================================================== + + subroutine build_jacobian_matrix(x) + + use constants, only: ONE + + type(Vector) :: x + + integer :: i ! loop counter for jacobian rows + integer :: jjac ! loop counter for jacobian cols + integer :: jloss ! loop counter for loss matrix cols + integer :: jprod ! loop counter for prod matrix cols + integer :: n ! problem size + real(8) :: lambda ! eigenvalue + real(8) :: val ! temporary real scalar + type(Vector) :: flux ! flux vector + type(Vector) :: fsrc ! fission source vector + + ! get the problem size + n = loss % n + + ! get flux and eigenvalue + flux % val => x % val(1:n) + lambda = x % val(n + 1) + + ! create fission source vector + call fsrc % create(n) + call prod % vector_multiply(flux, fsrc) + + ! reset counters in Jacobian matrix + jac_prec % n_kount = 1 + jac_prec % nz_kount = 1 + + ! begin loop around rows of Jacobian matrix + ROWS: do i = 1, n + + ! add a new row in Jacobian + call jac_prec % new_row() + + ! begin loop around columns of loss matrix + COLS_LOSS: do jloss = loss % row(i), loss % row(i + 1) - 1 + + ! start with the value in the loss matrix + val = loss % val(jloss) + + ! loop around columns of prod matrix + COLS_PROD: do jprod = prod % row(i), prod % row(i + 1) - 1 + + ! see if columns agree with loss matrix + if (prod % col(jprod) == loss % col(jloss)) then + val = val - lambda*prod % val(jprod) + exit + end if + + end do COLS_PROD + + ! record value in jacobian + call jac_prec % add_value(jloss, val) + + end do COLS_LOSS + + ! add fission source value + val = fsrc % val(n) + call jac_prec % add_value(n + 1, val) + + end do ROWS + + ! need to add negative transpose of flux vector on last row + call jac_prec % new_row() + do jjac = 1, n + val = -flux % val(jjac) + call jac_prec % add_value(jjac, val) + end do + + ! add unity on bottom right corner of matrix + call jac_prec % add_value(n + 1, ONE) + + ! free all allocated memory + flux % val => null() + call fsrc % destroy() + + end subroutine build_jacobian_matrix + +!=============================================================================== +! COMPUTE_NONLINEAR_RESIDUAL +!=============================================================================== + + subroutine compute_nonlinear_residual(x) + + use global, only: cmfd_write_matrices, cmfd_adjoint_type + + type(Vector) :: x + + character(len=25) :: filename + integer :: n + logical :: physical_adjoint = .false. + real(8) :: lambda + type(Vector) :: res_loss + type(Vector) :: res_prod + type(Vector) :: flux + + ! Check for physical adjoint + if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'physical') & + physical_adjoint = .true. + + ! Create operators + call build_loss_matrix(loss, adjoint = physical_adjoint) + call build_prod_matrix(prod, adjoint = physical_adjoint) + + ! Check for adjoint + if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & + call compute_adjoint() + + ! Get problem size + n = loss % n + + ! Set up temporary vectors + call res_loss % create(n) + call res_prod % create(n) + + ! Extract flux + flux % n = n + flux % val => x % val(1:n) + + ! Extract eigenvalue + lambda = x % val(n + 1) + + ! Calculate M*flux then F*flux + call loss % vector_multiply(flux, res_loss) + call prod % vector_multiply(flux, res_prod) + + ! Put flux component values in residual vector + resvec % val(1:n) = res_loss % val - lambda*res_prod % val + + ! Put eigenvalue component in residual vector + resvec % val(n+1) = 0.5_8 - 0.5_8*sum(flux % val * flux % val) + + ! write out data in binary files (debugging) + if (cmfd_write_matrices) then + + ! write out residual vector + if (adjoint_calc) then + filename = 'adj_res.bin' + else + filename = 'res.bin' + end if + call resvec % write_petsc_binary(filename) + + ! write out solution vector + if (adjoint_calc) then + filename = 'adj_x.bin' + else + filename = 'x.bin' + end if + call x % write_petsc_binary(filename) + + end if + + end subroutine compute_nonlinear_residual + +!=============================================================================== +! COMPUTE_ADJOINT +!=============================================================================== + + subroutine compute_adjoint() + + use global, only: cmfd_write_matrices + + ! transpose matrices + call loss % transpose() + call prod % transpose() + + ! write out matrix in binary file (debugging) + if (cmfd_write_matrices) then + call loss % write_petsc_binary('adj_lossmat.bin') + call loss % write_petsc_binary('adj_prodmat.bin') + end if + + end subroutine compute_adjoint + +!=============================================================================== +! EXTRACT_RESULTS +!=============================================================================== + + subroutine extract_results() + + use constants, only: ZERO, ONE + use global, only: cmfd + + integer :: n ! problem size + + ! get problem size + n = jfnk_data % loss % n + + ! also allocate in cmfd object + if (adjoint_calc) then + if (.not. allocated(cmfd % adj_phi)) allocate(cmfd % adj_phi(n)) + else + if (.not. allocated(cmfd % phi)) allocate(cmfd % phi(n)) + end if + + ! Get flux and eigenvalue + if (adjoint_calc) then + cmfd % adj_phi = xvec % val(1:n) + cmfd % adj_keff = ONE / xvec % val(n+1) + else + cmfd % phi = xvec % val(1:n) + cmfd % keff = xvec % val(n+1) + end if + + end subroutine extract_results + +!=============================================================================== +! FINALIZE frees all memory from a JFNK calculation +!=============================================================================== + + subroutine finalize() + + call jfnk_data % loss % destroy() + call jfnk_data % prod % destroy() + call jac_prec % destroy() + call xvec % destroy() + call resvec % destroy() + call jfnk % destroy() + + end subroutine finalize + +end module cmfd_jfnk_solver diff --git a/src/cmfd_snes_solver.F90 b/src/cmfd_snes_solver.F90 deleted file mode 100644 index 6293720207..0000000000 --- a/src/cmfd_snes_solver.F90 +++ /dev/null @@ -1,473 +0,0 @@ -module cmfd_snes_solver - -# ifdef PETSC - - use cmfd_loss_operator, only: loss_operator,init_M_operator, & - build_loss_matrix,destroy_M_operator - use cmfd_prod_operator, only: prod_operator,init_F_operator, & - build_prod_matrix,destroy_F_operator - use cmfd_jacobian_operator, only: jacobian_operator,init_J_operator, & - build_jacobian_matrix,destroy_J_operator,& - operators - use cmfd_power_solver, only: cmfd_power_execute - - implicit none - private - public :: cmfd_snes_execute - -# include - - type(jacobian_operator) :: jac_prec - type(operators) :: ctx - - Mat :: jac ! jacobian matrix - Vec :: resvec ! residual vector - Vec :: xvec ! results - KSP :: ksp ! linear solver context - PC :: pc ! preconditioner - SNES :: snes ! nonlinear solver context - integer :: ierr ! error flag - - logical :: adjoint_calc = .false. ! adjoint calculation - -contains - -!=============================================================================== -! CMFD_SNES_EXECUTE -!=============================================================================== - - subroutine cmfd_snes_execute(adjoint) - - logical, optional :: adjoint ! adjoint calculation - - ! check for adjoint - if (present(adjoint)) adjoint_calc = adjoint - - ! seed with power iteration - call cmfd_power_execute(k_tol=1.E-3_8, s_tol=1.E-3_8, adjoint=adjoint_calc) - - ! initialize data - call init_data() - - ! initialize solver - call init_solver() - - ! precondition - call precondition_matrix() - - ! solve the system - call SNESSolve(snes, PETSC_NULL_DOUBLE, xvec, ierr) - - ! extracts results to cmfd object - call extract_results() - - ! deallocate all slepc data - call finalize() - - end subroutine cmfd_snes_execute - -!=============================================================================== -! INIT_DATA allocates matrices vectors for CMFD solution -!=============================================================================== - - subroutine init_data() - - use constants, only: ONE - use global, only: cmfd, n_procs_cmfd, rank - - integer :: k ! implied do counter - integer :: n ! problem size - integer :: row_start ! local row start - integer :: row_end ! local row end - real(8), pointer :: xptr(:) ! solution pointer - - ! set up operator matrices - call init_M_operator(ctx%loss) - call init_F_operator(ctx%prod) - call init_J_operator(jac_prec, ctx) - - ! get problem size - n = jac_prec%n - - ! create PETSc vectors - call VecCreateMPI(PETSC_COMM_WORLD, jac_prec%localn, PETSC_DECIDE, & - resvec, ierr) - call VecCreateMPI(PETSC_COMM_WORLD, jac_prec%localn, PETSC_DECIDE, & - xvec, ierr) - - ! get the local dimensions for each process - call VecGetOwnershipRange(xvec, row_start, row_end, ierr) - - if (rank == n_procs_cmfd - 1) row_end = n - - ! set flux in guess - if (adjoint_calc) then - call VecSetValues(xvec, row_end-row_start, (/(k,k=row_start,row_end-1)/), & - cmfd%adj_phi(row_start+1:row_end), INSERT_VALUES, ierr) - else - call VecSetValues(xvec, row_end-row_start, (/(k,k=row_start,row_end-1)/), & - cmfd%phi(row_start+1:row_end), INSERT_VALUES, ierr) - end if - call VecAssemblyBegin(xvec, ierr) - call VecAssemblyEnd(xvec, ierr) - - ! set keff in guess - if (rank == n_procs_cmfd - 1) then - call VecGetArrayF90(xvec, xptr, ierr) - if (adjoint_calc) then - xptr(size(xptr)) = ONE/cmfd%adj_keff - else - xptr(size(xptr)) = ONE/cmfd%keff - end if - call VecRestoreArrayF90(xvec, xptr, ierr) - end if - - ! nullify pointers - if (associated(xptr)) nullify(xptr) - - end subroutine init_data - -!=============================================================================== -! INIT_SOLVER -!=============================================================================== - - subroutine init_solver() - - use global, only: cmfd_snes_monitor, n_procs_cmfd - - ! turn on mf_operator option - call PetscOptionsSetValue("-snes_mf_operator", "TRUE", ierr) - - ! create SNES context - call SNESCreate(PETSC_COMM_WORLD, snes, ierr) - - ! set the residual function - call SNESSetFunction(snes, resvec, compute_nonlinear_residual, & - PETSC_NULL_DOUBLE, ierr) - - ! set GMRES solver - call SNESGetKSP(snes, ksp, ierr) - call KSPSetType(ksp, KSPGMRES, ierr) - - ! create matrix free jacobian - call MatCreateSNESMF(snes, jac, ierr) - - ! set matrix free finite difference - call SNESSetJacobian(snes, jac, jac_prec, build_jacobian_matrix, ctx, ierr) - - ! set lags - call SNESSetLagJacobian(snes, -2, ierr) - call SNESSetLagPreconditioner(snes, -1, ierr) - - ! set convergence -! call SNESSetTolerances(snes, 1.0e-8_8, 1.0e-8_8, 1.0e-8_8, 50, 10000, ierr) - - ! set SNES options - call SNESSetFromOptions(snes, ierr) - - ! turn off line searching -! call SNESLineSearchSet(snes, SNESLineSearchNo, PETSC_NULL_OBJECT, ierr) - - end subroutine init_solver - -!=============================================================================== -! PRECONDITION_MATRIX -!=============================================================================== - - subroutine precondition_matrix() - - use global, only: cmfd_snes_monitor, cmfd_solver_type, & - n_procs_cmfd, cmfd_ilu_levels, master - use string, only: to_str - use, intrinsic :: ISO_FORTRAN_ENV - - character(LEN=20) :: snestype, ksptype, pctype - - ! set up preconditioner - call KSPGetPC(ksp, pc, ierr) - if (n_procs_cmfd == 1) then - call PCSetType(pc, PCILU, ierr) - call PCFactorSetLevels(pc, cmfd_ilu_levels, ierr) - else - call PetscOptionsSetValue("-pc_type", "bjacobi", ierr) - call PetscOptionsSetValue("-sub_pc_type", "ilu", ierr) - call PetscOptionsSetValue("-sub_pc_factor_levels", & - trim(to_str(cmfd_ilu_levels)), ierr) - end if - - ! get options - call PCSetFromOptions(pc, ierr) - call KSPSetFromOptions(ksp, ierr) - - ! finalize ksp setup -! call KSPSetUp(ksp, ierr) - - ! get all types and print - call SNESGetType(snes, snestype, ierr) - call KSPGetType(ksp, ksptype, ierr) - call PCGetType(pc, pctype, ierr) - - ! display solver info to user - if (cmfd_snes_monitor .and. master) then - write(OUTPUT_UNIT,'(A)') '' - write(OUTPUT_UNIT,'(A)') '########################################################' - write(OUTPUT_UNIT,'(A)') '################ JFNK Nonlinear Solver ################' - write(OUTPUT_UNIT,'(A)') '########################################################' - write(OUTPUT_UNIT,'(A)') '' - write(OUTPUT_UNIT,101) 'NONLINEAR SOLVER: ', snestype - write(OUTPUT_UNIT,101) 'LINEAR SOLVER: ', ksptype - write(OUTPUT_UNIT,101) 'PRECONDITIONER: ', pctype - write(OUTPUT_UNIT,100) 'ILU levels: ', cmfd_ilu_levels - write(OUTPUT_UNIT,'(A)') '' - write(OUTPUT_UNIT,'(A)') '---------------------------------------------' - write(OUTPUT_UNIT,'(A)') '' - end if - - 100 FORMAT(A,1X,I0) - 101 FORMAT(A,1X,A) - - end subroutine precondition_matrix - -!=============================================================================== -! COMPUTE_NONLINEAR_RESIDUAL -!=============================================================================== - - subroutine compute_nonlinear_residual(snes, x, res, ierr) - - use global, only: n_procs_cmfd, cmfd_write_matrices, & - cmfd_adjoint_type, rank - - SNES :: snes ! nonlinear solver context - Vec :: x ! independent vector - Vec :: res ! residual vector - integer :: ierr ! error flag - - Vec :: phi ! flux vector - Vec :: rphi ! flux part of residual - Vec :: phiM ! M part of residual flux calc - integer :: n ! problem size - real(8) :: lambda ! eigenvalue - real(8) :: reslamb ! residual for lambda - - real(8), pointer :: xptr(:) ! pointer to solution vector - real(8), pointer :: rptr(:) ! pointer to residual vector - PetscViewer :: viewer - - logical :: physical_adjoint = .false. - - ! check for physical adjoint - if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'physical') & - physical_adjoint = .true. - - ! create operators - call build_loss_matrix(ctx%loss, adjoint = physical_adjoint) - call build_prod_matrix(ctx%prod, adjoint = physical_adjoint) - - ! check for adjoint - if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & - call compute_adjoint() - - ! get problem size - n = ctx%loss%n - - ! get pointers to vectors - call VecGetArrayF90(x, xptr, ierr) - call VecGetArrayF90(res, rptr, ierr) - - ! create petsc vector for flux - call VecCreateMPI(PETSC_COMM_WORLD, ctx%loss%localn, & - PETSC_DECIDE, phi, ierr) - call VecCreateMPI(PETSC_COMM_WORLD, ctx%loss%localn, & - PETSC_DECIDE, rphi, ierr) - - ! extract flux and place in petsc vector - call VecPlaceArray(phi, xptr, ierr) - call VecPlaceArray(rphi, rptr, ierr) - - ! extract eigenvalue and broadcast (going to want to make this more general in future) - if (rank == n_procs_cmfd - 1) lambda = xptr(size(xptr)) - call MPI_BCAST(lambda, 1, MPI_REAL8, n_procs_cmfd-1, PETSC_COMM_WORLD, ierr) - - ! create new petsc vectors to perform math - call VecCreateMPI(PETSC_COMM_WORLD, ctx%loss%localn, PETSC_DECIDE, & - phiM, ierr) - - ! calculate flux part of residual vector - call MatMult(ctx%loss%M, phi, phiM, ierr) - call MatMult(ctx%prod%F, phi, rphi, ierr) - call VecAYPX(rphi, -lambda, phiM, ierr) - - ! set eigenvalue part of residual vector - call VecDot(phi, phi, reslamb, ierr) - - ! map to ptr - if (rank == n_procs_cmfd - 1) rptr(size(rptr)) = 0.5_8 - 0.5_8*reslamb - - ! reset arrays that are not used - call VecResetArray(phi, ierr) - call VecResetArray(rphi, ierr) - - ! restore arrays for residual and solution - call VecRestoreArrayF90(x, xptr, ierr) - call VecRestoreArrayF90(res, rptr, ierr) - - ! destroy all temp vectors - call VecDestroy(phi, ierr) - call VecDestroy(phiM, ierr) - call VecDestroy(rphi, ierr) - - ! nullify all pointers - if (associated(xptr)) nullify(xptr) - if (associated(rptr)) nullify(rptr) - - ! write out matrix in binary file (debugging) - if (cmfd_write_matrices) then - if (adjoint_calc) then - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_res.bin', & - FILE_MODE_WRITE, viewer, ierr) - else - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'res.bin', & - FILE_MODE_WRITE, viewer, ierr) - end if - call VecView(res, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) - - if (adjoint_calc) then - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_x.bin', & - FILE_MODE_WRITE, viewer, ierr) - else - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'x.bin', & - FILE_MODE_WRITE, viewer, ierr) - end if - call VecView(x, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) - - end if - - end subroutine compute_nonlinear_residual - -!=============================================================================== -! COMPUTE_ADJOINT -!=============================================================================== - - subroutine compute_adjoint() - - use global, only: cmfd_write_matrices - - PetscViewer :: viewer - - ! transpose matrices - call MatTranspose(ctx%loss%M, MAT_REUSE_MATRIX, ctx%loss%M, ierr) - call MatTranspose(ctx%prod%F, MAT_REUSE_MATRIX, ctx%prod%F, ierr) - - ! write out matrix in binary file (debugging) - if (cmfd_write_matrices) then - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_lossmat.bin', & - FILE_MODE_WRITE, viewer, ierr) - call MatView(ctx%loss%M, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) - - call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'adj_prodmat.bin', & - FILE_MODE_WRITE, viewer, ierr) - call MatView(ctx%prod%F, viewer, ierr) - call PetscViewerDestroy(viewer, ierr) - end if - - end subroutine compute_adjoint - -!=============================================================================== -! EXTRACT_RESULTS -!=============================================================================== - - subroutine extract_results() - - use constants, only: ZERO, ONE - use global, only: cmfd, n_procs_cmfd, rank - - integer :: n ! problem size - integer :: row_start ! local row start - integer :: row_end ! local row end - real(8) :: keff ! keff of problem - real(8),allocatable :: mybuf(:) ! temp buffer - PetscScalar, pointer :: xptr(:) ! pointer to eigenvector info - - ! get problem size - n = ctx%loss%n - - ! also allocate in cmfd object - if (adjoint_calc) then - if (.not. allocated(cmfd%adj_phi)) allocate(cmfd%adj_phi(n)) - else - if (.not. allocated(cmfd%phi)) allocate(cmfd%phi(n)) - end if - if (.not. allocated(mybuf)) allocate(mybuf(n)) - - ! get ownership range - call VecGetOwnershipRange(xvec, row_start, row_end, ierr) - - ! resize the last proc - if (rank == n_procs_cmfd - 1) row_end = row_end - 1 - - ! convert petsc phi_object to cmfd_obj - call VecGetArrayF90(xvec, xptr, ierr) - if (adjoint_calc) then - cmfd%adj_phi(row_start+1:row_end) = xptr(1:row_end - row_start) - else - cmfd%phi(row_start+1:row_end) = xptr(1:row_end - row_start) - end if - - ! reduce result to all - mybuf = ZERO - if (adjoint_calc) then - call MPI_ALLREDUCE(cmfd%adj_phi, mybuf, n, MPI_REAL8, MPI_SUM, & - PETSC_COMM_WORLD, ierr) - else - call MPI_ALLREDUCE(cmfd%phi, mybuf, n, MPI_REAL8, MPI_SUM, & - PETSC_COMM_WORLD, ierr) - end if - - ! move buffer to object and deallocate - cmfd%phi = mybuf - if(allocated(mybuf)) deallocate(mybuf) - - ! save eigenvalue - if(rank == n_procs_cmfd - 1) keff = ONE / xptr(size(xptr)) - if (adjoint_calc) then - cmfd%adj_keff = keff - call MPI_BCAST(cmfd%adj_keff, 1, MPI_REAL8, n_procs_cmfd-1, & - PETSC_COMM_WORLD, ierr) - else - cmfd%keff = keff - call MPI_BCAST(cmfd%keff, 1, MPI_REAL8, n_procs_cmfd-1, & - PETSC_COMM_WORLD, ierr) - end if - call VecRestoreArrayF90(xvec, xptr, ierr) - - ! nullify pointers and deallocate local variables - if (associated(xptr)) nullify(xptr) - if (allocated(mybuf)) deallocate(mybuf) - - end subroutine extract_results - -!=============================================================================== -! FINALIZE -!=============================================================================== - - subroutine finalize() - - ! finalize data objects - call destroy_M_operator(ctx%loss) - call destroy_F_operator(ctx%prod) - call destroy_J_operator(jac_prec) - call VecDestroy(xvec, ierr) - call VecDestroy(resvec, ierr) - call MatDestroy(jac, ierr) - - ! finalize solver objects - call SNESDestroy(snes, ierr) - - end subroutine finalize - -# endif - -end module cmfd_snes_solver diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index 3abe276d71..dd62c42a6d 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -18,6 +18,7 @@ module matrix_header # ifdef PETSC Mat :: petsc_mat # endif + logical :: petsc_active = .false. contains procedure :: create => matrix_create procedure :: destroy => matrix_destroy @@ -223,6 +224,7 @@ contains #ifdef PETSC call MatCreateSeqAIJWithArrays(PETSC_COMM_WORLD, self % n, self % n, & self % row, self % col, self % val, self % petsc_mat, petsc_err) + self % petsc_active = .true. #endif end subroutine matrix_setup_petsc @@ -271,18 +273,44 @@ contains subroutine matrix_vector_multiply(self, vec_in, vec_out) + use constants, only: ZERO use vector_header, only: Vector class(Matrix) :: self type(Vector) :: vec_in type(Vector) :: vec_out - integer :: petsc_err + integer :: i + integer :: j + integer :: shift -#ifdef PETSC - call MatMult(self % petsc_mat, vec_in % petsc_vec, vec_out % petsc_vec, & - petsc_err) -#endif +! integer :: petsc_err + +!#ifdef PETSC +! call MatMult(self % petsc_mat, vec_in % petsc_vec, vec_out % petsc_vec, & +! petsc_err) +!#endif + + ! set shift by default 0 and change if petsc is active + ! this is because PETSc needs vectors to remain referenced to 0 + shift = 0 + if (self % petsc_active) shift = 1 + + ! begin loop around rows + ROWS: do i = 1, vec_in % n + + ! initialize target location in vector + vec_out % val(i) = ZERO + + ! begin loop around columns (need to shift if petsc is active) + COLS: do j = self % row(i) + shift, self % row(i + 1) - 1 + shift + + vec_out % val(i) = vec_out % val(i) + self % val(j) * & + vec_in % val(self % col(j) + shift) + + end do COLS + + end do ROWS end subroutine matrix_vector_multiply diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index 446e12732a..7f4c2e78ad 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -10,6 +10,7 @@ module petsc_solver # include #endif + ! Petsc GMRES solver context type, public :: Petsc_gmres #ifdef PETSC KSP :: ksp @@ -23,8 +24,40 @@ module petsc_solver procedure :: solve => gmres_solve end type Petsc_gmres + ! Derived type to contain list of data needed to be passed to procedures + type, public :: Jfnk_ctx + type(Matrix) :: loss + type(Matrix) :: prod + procedure (res_interface), pointer, nopass :: res_proc + procedure (jac_interface), pointer, nopass :: jac_proc + end type Jfnk_ctx + + ! Petsc SNES JFNK solver context + type, public :: Petsc_jfnk +#ifdef PETSC + KSP :: ksp + PC :: pc + SNES :: snes + Mat :: jac_mf +#endif + contains + procedure :: create => jfnk_create + procedure :: destroy => jfnk_destroy + end type Petsc_jfnk + integer :: Petsc_err + ! Abstract interface stating how jacobian and residual routines look + abstract interface + subroutine res_interface(x) + real(8) :: x + end subroutine res_interface + + subroutine jac_interface(x) + real(8) :: x + end subroutine jac_interface + end interface + contains !=============================================================================== @@ -55,14 +88,14 @@ contains ! GMRES_PRECONDITION !=============================================================================== - subroutine gmres_precondition(self, mat) + subroutine gmres_precondition(self, mat_in) class(Petsc_gmres) :: self - type(Matrix) :: mat + type(Matrix) :: mat_in #ifdef PETSC call KSPSetUp(self % ksp, petsc_err) - call PCFactorGetMatrix(self % pc, mat % petsc_mat, petsc_err) + call PCFactorGetMatrix(self % pc, mat_in % petsc_mat, petsc_err) #endif end subroutine gmres_precondition @@ -71,14 +104,14 @@ contains ! GMRES_SET_OPER !=============================================================================== - subroutine gmres_set_oper(self, prec_mat, mat) + subroutine gmres_set_oper(self, prec_mat, mat_in) class(Petsc_gmres) :: self type(Matrix) :: prec_mat - type(Matrix) :: mat + type(Matrix) :: mat_in #ifdef PETSC - call KSPSetOperators(self % ksp, mat % petsc_mat, prec_mat % petsc_mat, & + call KSPSetOperators(self % ksp, mat_in % petsc_mat, prec_mat % petsc_mat, & SAME_NONZERO_PATTERN, petsc_err) #endif @@ -116,4 +149,105 @@ contains end subroutine gmres_solve +!=============================================================================== +! JFNK_CREATE +!=============================================================================== + + subroutine jfnk_create(self) + + class(Petsc_jfnk) :: self + +#ifdef PETSC + ! Turn on mf_operator option for matrix free jacobian + call PetscOptionsSetValue("-snes_mf_operator", "TRUE", petsc_err) + + ! Create the SNES context + call SNESCreate(PETSC_COMM_WORLD, self % snes, petsc_err) + + ! Set up the GMRES solver + call SNESGetKSP(self % snes, self % ksp, petsc_err) + call KSPSetType(self % ksp, KSPGMRES, petsc_err) + + ! Create the matrix free jacobian + call MatCreateSNESMF(self % snes, self % jac_mf, petsc_err) + + ! Set up Jacobian Lags + call SNESSetLagJacobian(self % snes, -2, petsc_err) + call SNESSetLagPreconditioner(self % snes, -1, petsc_err) + + ! Set up preconditioner + call KSPGetPC(self % ksp, self % pc, petsc_err) + call PCSetType(self % pc, PCILU, petsc_err) + call PCFactorSetLevels(self % pc, 5, petsc_err) +#endif + + end subroutine jfnk_create + +!=============================================================================== +! JFNK_DESTROY +!=============================================================================== + + subroutine jfnk_destroy(self) + + class(Petsc_jfnk) :: self + +#ifdef PETSC + call MatDestroy(self % jac_mf, petsc_err) + call SNESDestroy(self % snes, petsc_err) +#endif + + end subroutine jfnk_destroy + +!=============================================================================== +! JFNK_SET_FUNCTIONS +!=============================================================================== + + subroutine jfnk_set_functions(self, ctx, res, jac_prec) + + class(Petsc_jfnk) :: self + type(Jfnk_ctx) :: ctx + type(Vector) :: res + type(Matrix) :: jac_prec + + ! Set residual procedure + call SNESSetFunction(self % snes, res % petsc_vec, jfnk_compute_residual, & + ctx, petsc_err) + + ! Set Jacobian procedure + call SNESSetJacobian(self % snes, self % jac_mf, jac_prec % petsc_mat, & + jfnk_compute_jacobian, ctx, petsc_err) + + end subroutine jfnk_set_functions + +!=============================================================================== +! JFNK_COMPUTE_RESIDUAL +!=============================================================================== + + subroutine jfnk_compute_residual(snes, x, res, ctx, petsc_err) + + SNES :: snes + Vec :: x + Vec :: res + integer :: petsc_err + type(Jfnk_ctx) :: ctx + + end subroutine jfnk_compute_residual + +!=============================================================================== +! JFNK_COMPUTE_JACOBIAN +!=============================================================================== + + subroutine jfnk_compute_jacobian(snes, x, jac_mf, jac_prec, flag, ctx, & + petsc_err) + + SNES :: snes + Vec :: x + Mat :: jac_mf + Mat :: jac_prec + MatStructure :: flag + type(Jfnk_ctx) :: ctx + integer :: petsc_err + + end subroutine jfnk_compute_jacobian + end module petsc_solver diff --git a/src/vector_header.F90 b/src/vector_header.F90 index ec9427e916..1a867ef5c6 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -11,7 +11,7 @@ module vector_header type, public :: Vector integer :: n ! number of rows/cols in matrix - real(8), allocatable :: val(:) ! matrix value vector + real(8), pointer :: val(:) ! matrix value vector # ifdef PETSC Vec :: petsc_vec # endif @@ -35,7 +35,7 @@ contains class(Vector) :: self ! preallocate vector - if (.not.allocated(self % val)) allocate(self % val(n)) + if (.not.associated(self % val)) allocate(self % val(n)) ! set n self % n = n @@ -53,7 +53,7 @@ contains class(Vector) :: self - if (allocated(self % val)) deallocate(self % val) + if (associated(self % val)) deallocate(self % val) end subroutine vector_destroy From 657d8cd532c16801b9798addc643ad6a97b1bb28 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 11 Jul 2013 12:03:21 -0400 Subject: [PATCH 037/192] Fixed bugs in JFNK but currently does Full Newton Iterations --- src/cmfd_jfnk_solver.F90 | 148 +++++++++++++++++++++++++++------------ src/matrix_header.F90 | 7 +- src/petsc_solver.F90 | 136 ++++++++++++++++++++++++++--------- 3 files changed, 212 insertions(+), 79 deletions(-) diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index acba55b84b..7b6ce7dbb0 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -11,14 +11,14 @@ module cmfd_jfnk_solver private public :: cmfd_jfnk_execute - logical :: adjoint_calc = .false. ! adjoint calculation + logical :: adjoint_calc type(Jfnk_ctx) :: jfnk_data - type(Matrix) :: jac_prec + type(Matrix), target :: jac_prec type(Matrix) :: loss type(Matrix) :: prod type(Petsc_jfnk) :: jfnk - type(Vector) :: resvec - type(Vector) :: xvec + type(Vector), target :: resvec + type(Vector), target :: xvec contains @@ -31,6 +31,7 @@ contains logical, optional :: adjoint ! adjoint calculation ! check for adjoint + adjoint_calc = .false. if (present(adjoint)) adjoint_calc = adjoint ! seed with power iteration @@ -42,8 +43,13 @@ contains ! initialize solver call jfnk % create() + ! set up residual and jacobian routines + jfnk_data % res_proc_ptr => compute_nonlinear_residual + jfnk_data % jac_proc_ptr => build_jacobian_matrix + call jfnk % set_functions(jfnk_data, resvec, jac_prec) + ! solve the system -! call SNESSolve(snes, PETSC_NULL_DOUBLE, xvec, ierr) + call jfnk % solve(xvec) ! extracts results to cmfd object call extract_results() @@ -60,24 +66,46 @@ contains subroutine init_data() use constants, only: ONE - use global, only: cmfd - - integer :: n ! problem size + use global, only: cmfd, cmfd_adjoint_type + logical :: physical_adjoint + integer :: n + type(Vector), pointer :: x + type(Vector), pointer :: res + type(Matrix), pointer :: jac + x => xvec + res => resvec + jac => jac_prec ! Set up all matrices call init_loss_matrix(loss) call init_prod_matrix(prod) call init_jacobian_matrix() - ! Set up for use with petsc - call jac_prec % setup_petsc() + ! Check for physical adjoint + physical_adjoint = .false. + if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'physical') & + physical_adjoint = .true. + + ! Create matrix operators + call build_loss_matrix(loss, adjoint = physical_adjoint) + call build_prod_matrix(prod, adjoint = physical_adjoint) + + ! Assembly matrices and use petsc + call loss % assemble() + call prod % assemble() + call loss % setup_petsc() + call prod % setup_petsc() + + ! Check for mathematical adjoint + if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & + call compute_adjoint() ! Get problem size - n = jac_prec % n + n = loss % n ! Create problem vectors - call resvec % create(n) - call xvec % create(n) + call resvec % create(n + 1) + call xvec % create(n + 1) ! set flux in guess if (adjoint_calc) then @@ -93,7 +121,21 @@ contains xvec % val(n + 1) = ONE/cmfd % keff end if - ! set solver names + ! Set up vectors for petsc + call resvec % setup_petsc() + call xvec % setup_petsc() + + ! Build jacobian from initial guess + call build_jacobian_matrix(xvec) + + ! Set up Jacobian for Petsc + call jac_prec % setup_petsc() + + ! write all out + call loss % write_petsc_binary('loss.bin') + call prod % write_petsc_binary('prod.bin') + call jac_prec % write_petsc_binary('jac.bin') +! call xvec % write_petsc_binary('x.bin') end subroutine init_data @@ -136,17 +178,22 @@ contains integer :: jloss ! loop counter for loss matrix cols integer :: jprod ! loop counter for prod matrix cols integer :: n ! problem size + integer :: shift ! integer to account for index shift for Petsc real(8) :: lambda ! eigenvalue real(8) :: val ! temporary real scalar type(Vector) :: flux ! flux vector type(Vector) :: fsrc ! fission source vector - +print *, 'IN JACOBIAN' ! get the problem size n = loss % n + ! Check to use shift + shift = 0 + if (loss % petsc_active) shift = 1 + ! get flux and eigenvalue - flux % val => x % val(1:n) - lambda = x % val(n + 1) + flux % val => xvec % val(1:n) + lambda = xvec % val(n + 1) ! create fission source vector call fsrc % create(n) @@ -163,13 +210,15 @@ contains call jac_prec % new_row() ! begin loop around columns of loss matrix - COLS_LOSS: do jloss = loss % row(i), loss % row(i + 1) - 1 + COLS_LOSS: do jloss = loss % row(i) + shift, & + loss % row(i + 1) - 1 + shift ! start with the value in the loss matrix val = loss % val(jloss) ! loop around columns of prod matrix - COLS_PROD: do jprod = prod % row(i), prod % row(i + 1) - 1 + COLS_PROD: do jprod = prod % row(i) + shift, & + prod % row(i + 1) - 1 + shift ! see if columns agree with loss matrix if (prod % col(jprod) == loss % col(jloss)) then @@ -180,12 +229,12 @@ contains end do COLS_PROD ! record value in jacobian - call jac_prec % add_value(jloss, val) + call jac_prec % add_value(loss % col(jloss) + shift, val) end do COLS_LOSS ! add fission source value - val = fsrc % val(n) + val = -fsrc % val(i) call jac_prec % add_value(n + 1, val) end do ROWS @@ -200,6 +249,15 @@ contains ! add unity on bottom right corner of matrix call jac_prec % add_value(n + 1, ONE) + ! CRS requires a final value in row + call jac_prec % new_row() + + ! If the Jacobian is already associated with PETSc need to reshift row/col + if (jac_prec % petsc_active) then + jac_prec % row = jac_prec % row - 1 + jac_prec % col = jac_prec % col - 1 + end if + ! free all allocated memory flux % val => null() call fsrc % destroy() @@ -210,31 +268,27 @@ contains ! COMPUTE_NONLINEAR_RESIDUAL !=============================================================================== - subroutine compute_nonlinear_residual(x) + subroutine compute_nonlinear_residual(x, res) - use global, only: cmfd_write_matrices, cmfd_adjoint_type + use global, only: cmfd_write_matrices - type(Vector) :: x + type(Vector) :: x + type(Vector) :: res character(len=25) :: filename integer :: n - logical :: physical_adjoint = .false. real(8) :: lambda type(Vector) :: res_loss type(Vector) :: res_prod type(Vector) :: flux - ! Check for physical adjoint - if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'physical') & - physical_adjoint = .true. + type(Vector), pointer :: res_ptr + type(Vector), pointer :: x_ptr - ! Create operators - call build_loss_matrix(loss, adjoint = physical_adjoint) - call build_prod_matrix(prod, adjoint = physical_adjoint) + res_ptr => resvec + x_ptr => xvec - ! Check for adjoint - if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & - call compute_adjoint() +print *, 'IN RESIDUAL' ! Get problem size n = loss % n @@ -255,10 +309,10 @@ contains call prod % vector_multiply(flux, res_prod) ! Put flux component values in residual vector - resvec % val(1:n) = res_loss % val - lambda*res_prod % val + res % val(1:n) = res_loss % val - lambda*res_prod % val ! Put eigenvalue component in residual vector - resvec % val(n+1) = 0.5_8 - 0.5_8*sum(flux % val * flux % val) + res % val(n+1) = 0.5_8 - 0.5_8*sum(flux % val * flux % val) ! write out data in binary files (debugging) if (cmfd_write_matrices) then @@ -269,7 +323,7 @@ contains else filename = 'res.bin' end if - call resvec % write_petsc_binary(filename) + call res % write_petsc_binary(filename) ! write out solution vector if (adjoint_calc) then @@ -280,7 +334,8 @@ contains call x % write_petsc_binary(filename) end if - +! print *, x % val +! print *, res % val end subroutine compute_nonlinear_residual !=============================================================================== @@ -313,9 +368,12 @@ contains use global, only: cmfd integer :: n ! problem size - +call loss % write_petsc_binary('loss.mat') +call prod % write_petsc_binary('prod.mat') +call xvec % write_petsc_binary('x.bin') +call resvec % write_petsc_binary('res.bin') ! get problem size - n = jfnk_data % loss % n + n = loss % n ! also allocate in cmfd object if (adjoint_calc) then @@ -330,9 +388,9 @@ contains cmfd % adj_keff = ONE / xvec % val(n+1) else cmfd % phi = xvec % val(1:n) - cmfd % keff = xvec % val(n+1) + cmfd % keff = ONE / xvec % val(n+1) end if - +stop end subroutine extract_results !=============================================================================== @@ -341,12 +399,14 @@ contains subroutine finalize() - call jfnk_data % loss % destroy() - call jfnk_data % prod % destroy() + call loss % destroy() + call prod % destroy() call jac_prec % destroy() call xvec % destroy() call resvec % destroy() call jfnk % destroy() + nullify(jfnk_data % res_proc_ptr) + nullify(jfnk_data % jac_proc_ptr) end subroutine finalize diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index dd62c42a6d..f3ec97ecb5 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -18,7 +18,7 @@ module matrix_header # ifdef PETSC Mat :: petsc_mat # endif - logical :: petsc_active = .false. + logical :: petsc_active contains procedure :: create => matrix_create procedure :: destroy => matrix_destroy @@ -56,6 +56,9 @@ contains self % n = n self % nnz = nnz + ! set petsc active by default to false + self % petsc_active = .false. + end subroutine matrix_create !=============================================================================== @@ -297,7 +300,7 @@ contains if (self % petsc_active) shift = 1 ! begin loop around rows - ROWS: do i = 1, vec_in % n + ROWS: do i = 1, self % n ! initialize target location in vector vec_out % val(i) = ZERO diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index 7f4c2e78ad..f3de5e1f2c 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -26,10 +26,8 @@ module petsc_solver ! Derived type to contain list of data needed to be passed to procedures type, public :: Jfnk_ctx - type(Matrix) :: loss - type(Matrix) :: prod - procedure (res_interface), pointer, nopass :: res_proc - procedure (jac_interface), pointer, nopass :: jac_proc + procedure (res_interface), pointer, nopass :: res_proc_ptr + procedure (jac_interface), pointer, nopass :: jac_proc_ptr end type Jfnk_ctx ! Petsc SNES JFNK solver context @@ -39,22 +37,28 @@ module petsc_solver PC :: pc SNES :: snes Mat :: jac_mf + SNESLineSearch :: ls #endif contains - procedure :: create => jfnk_create - procedure :: destroy => jfnk_destroy + procedure :: create => jfnk_create + procedure :: destroy => jfnk_destroy + procedure :: set_functions => jfnk_set_functions + procedure :: solve => jfnk_solve end type Petsc_jfnk - integer :: Petsc_err + integer :: petsc_err ! Abstract interface stating how jacobian and residual routines look abstract interface - subroutine res_interface(x) - real(8) :: x + subroutine res_interface(x, r) + import :: Vector + type(Vector) :: x + type(Vector) :: r end subroutine res_interface subroutine jac_interface(x) - real(8) :: x + import :: Vector + type(Vector) :: x end subroutine jac_interface end interface @@ -141,8 +145,6 @@ contains type(Vector) :: b type(Vector) :: x - integer :: petsc_err - #ifdef PETSC call KSPSolve(self % ksp, b % petsc_vec, x % petsc_vec, petsc_err) #endif @@ -159,26 +161,11 @@ contains #ifdef PETSC ! Turn on mf_operator option for matrix free jacobian - call PetscOptionsSetValue("-snes_mf_operator", "TRUE", petsc_err) +! call PetscOptionsSetValue("-snes_mf_operator", "TRUE", petsc_err) ! Create the SNES context call SNESCreate(PETSC_COMM_WORLD, self % snes, petsc_err) - ! Set up the GMRES solver - call SNESGetKSP(self % snes, self % ksp, petsc_err) - call KSPSetType(self % ksp, KSPGMRES, petsc_err) - - ! Create the matrix free jacobian - call MatCreateSNESMF(self % snes, self % jac_mf, petsc_err) - - ! Set up Jacobian Lags - call SNESSetLagJacobian(self % snes, -2, petsc_err) - call SNESSetLagPreconditioner(self % snes, -1, petsc_err) - - ! Set up preconditioner - call KSPGetPC(self % ksp, self % pc, petsc_err) - call PCSetType(self % pc, PCILU, petsc_err) - call PCFactorSetLevels(self % pc, 5, petsc_err) #endif end subroutine jfnk_create @@ -213,24 +200,85 @@ contains call SNESSetFunction(self % snes, res % petsc_vec, jfnk_compute_residual, & ctx, petsc_err) + ! Set up the GMRES solver + call SNESGetKSP(self % snes, self % ksp, petsc_err) + call KSPSetType(self % ksp, KSPGMRES, petsc_err) + + ! Create the matrix free jacobian +! call MatCreateSNESMF(self % snes, self % jac_mf, petsc_err) + ! Set Jacobian procedure - call SNESSetJacobian(self % snes, self % jac_mf, jac_prec % petsc_mat, & + call SNESSetJacobian(self % snes, jac_prec % petsc_mat, jac_prec % petsc_mat, & jfnk_compute_jacobian, ctx, petsc_err) + ! Set up Jacobian Lags +! call SNESSetLagJacobian(self % snes, -2, petsc_err) +! call SNESSetLagPreconditioner(self % snes, -1, petsc_err) + + ! Apply options + call SNESGetLineSearch(self % snes, self % ls, petsc_err) + call SNESLineSearchSetType(self % ls, SNESLINESEARCHBASIC, petsc_err) + call SNESSetFromOptions(self % snes, petsc_err) + call SNESView(self % snes, PETSC_VIEWER_STDOUT_WORLD, petsc_err) + + ! Set up preconditioner + call KSPGetPC(self % ksp, self % pc, petsc_err) + call PCSetType(self % pc, PCILU, petsc_err) + call PCFactorSetLevels(self % pc, 5, petsc_err) + call PCSetFromOptions(self % pc, petsc_err) + call KSPSetFromOptions(self % ksp, petsc_err) + end subroutine jfnk_set_functions +!=============================================================================== +! JFNK_SOLVE +!=============================================================================== + + subroutine jfnk_solve(self, xvec) + + class(Petsc_jfnk) :: self + type(Vector) :: xvec + +#ifdef PETSC + call SNESSolve(self % snes, PETSC_NULL_DOUBLE, xvec % petsc_vec, petsc_err) +#endif + + end subroutine jfnk_solve + !=============================================================================== ! JFNK_COMPUTE_RESIDUAL !=============================================================================== - subroutine jfnk_compute_residual(snes, x, res, ctx, petsc_err) + subroutine jfnk_compute_residual(snes, x, res, ctx, ierr) SNES :: snes Vec :: x Vec :: res - integer :: petsc_err + integer :: ierr type(Jfnk_ctx) :: ctx + PetscViewer ::viewer + + type(Vector) :: xvec + type(Vector) :: resvec + + call VecGetArrayF90(x, xvec % val, ierr) + call VecGetArrayF90(res, resvec % val, ierr) + + call ctx % res_proc_ptr(xvec, resvec) +! call VecView(x, PETSC_VIEWER_STDOUT_WORLD, ierr) +! call VecView(res, PETSC_VIEWER_STDOUT_WORLD, ierr) + +! call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'res.bin', FILE_MODE_WRITE, viewer, ierr) +! call VecView(res, viewer, ierr) +! call PetscViewerDestroy(viewer, ierr) +! call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'x.bin', FILE_MODE_WRITE, viewer, ierr) +! call VecView(x, viewer, ierr) +! call PetscViewerDestroy(viewer, ierr) + + call VecRestoreArrayF90(x, xvec % val, ierr) + call VecRestoreArrayF90(res, resvec % val, ierr) + read * end subroutine jfnk_compute_residual !=============================================================================== @@ -238,7 +286,7 @@ contains !=============================================================================== subroutine jfnk_compute_jacobian(snes, x, jac_mf, jac_prec, flag, ctx, & - petsc_err) + ierr) SNES :: snes Vec :: x @@ -246,7 +294,29 @@ contains Mat :: jac_prec MatStructure :: flag type(Jfnk_ctx) :: ctx - integer :: petsc_err + integer :: ierr + + type(Vector) :: xvec + + PetscViewer :: viewer + + call VecGetArrayF90(x, xvec % val, ierr) + + call ctx % jac_proc_ptr(xvec) + + call VecRestoreArrayF90(x, xvec % val, ierr) + +! call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'jac.bin', FILE_MODE_WRITE, viewer, petsc_err) +! call MatView(jac_prec, viewer, petsc_err) +! call PetscViewerDestroy(viewer, petsc_err) + +! call PetscViewerASCIIOpen(PETSC_COMM_WORLD, 'jac.out', viewer, petsc_err) +! call MatView(jac_prec, viewer, petsc_err) +! call PetscViewerDestroy(viewer, petsc_err) +#ifdef PETSC +! call MatAssemblyBegin(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) +! call MatAssemblyEnd(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) +#endif end subroutine jfnk_compute_jacobian From 3a941886d96febfa1359783f88725b8a92005c87 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 11 Jul 2013 12:13:28 -0400 Subject: [PATCH 038/192] Put back in the Jacobian free mode in the JFNK solver --- src/cmfd_jfnk_solver.F90 | 6 ++--- src/petsc_solver.F90 | 50 ++++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 7b6ce7dbb0..225e2b7211 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -183,7 +183,7 @@ contains real(8) :: val ! temporary real scalar type(Vector) :: flux ! flux vector type(Vector) :: fsrc ! fission source vector -print *, 'IN JACOBIAN' +!nprint *, 'IN JACOBIAN' ! get the problem size n = loss % n @@ -288,7 +288,7 @@ print *, 'IN JACOBIAN' res_ptr => resvec x_ptr => xvec -print *, 'IN RESIDUAL' +!print *, 'IN RESIDUAL' ! Get problem size n = loss % n @@ -390,7 +390,7 @@ call resvec % write_petsc_binary('res.bin') cmfd % phi = xvec % val(1:n) cmfd % keff = ONE / xvec % val(n+1) end if -stop + end subroutine extract_results !=============================================================================== diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index f3de5e1f2c..3fd38a7f3d 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -161,11 +161,28 @@ contains #ifdef PETSC ! Turn on mf_operator option for matrix free jacobian -! call PetscOptionsSetValue("-snes_mf_operator", "TRUE", petsc_err) + call PetscOptionsSetValue("-snes_mf_operator", "TRUE", petsc_err) ! Create the SNES context call SNESCreate(PETSC_COMM_WORLD, self % snes, petsc_err) + ! Set up the GMRES solver + call SNESGetKSP(self % snes, self % ksp, petsc_err) + call KSPSetType(self % ksp, KSPGMRES, petsc_err) + + ! Apply options + call SNESGetLineSearch(self % snes, self % ls, petsc_err) + call SNESLineSearchSetType(self % ls, SNESLINESEARCHBASIC, petsc_err) + call SNESSetFromOptions(self % snes, petsc_err) +! call SNESView(self % snes, PETSC_VIEWER_STDOUT_WORLD, petsc_err) + + ! Set up preconditioner + call KSPGetPC(self % ksp, self % pc, petsc_err) + call PCSetType(self % pc, PCILU, petsc_err) + call PCFactorSetLevels(self % pc, 5, petsc_err) + call PCSetFromOptions(self % pc, petsc_err) + call KSPSetFromOptions(self % ksp, petsc_err) + #endif end subroutine jfnk_create @@ -200,33 +217,16 @@ contains call SNESSetFunction(self % snes, res % petsc_vec, jfnk_compute_residual, & ctx, petsc_err) - ! Set up the GMRES solver - call SNESGetKSP(self % snes, self % ksp, petsc_err) - call KSPSetType(self % ksp, KSPGMRES, petsc_err) - ! Create the matrix free jacobian -! call MatCreateSNESMF(self % snes, self % jac_mf, petsc_err) + call MatCreateSNESMF(self % snes, self % jac_mf, petsc_err) ! Set Jacobian procedure - call SNESSetJacobian(self % snes, jac_prec % petsc_mat, jac_prec % petsc_mat, & + call SNESSetJacobian(self % snes, self % jac_mf, jac_prec % petsc_mat, & jfnk_compute_jacobian, ctx, petsc_err) ! Set up Jacobian Lags -! call SNESSetLagJacobian(self % snes, -2, petsc_err) -! call SNESSetLagPreconditioner(self % snes, -1, petsc_err) - - ! Apply options - call SNESGetLineSearch(self % snes, self % ls, petsc_err) - call SNESLineSearchSetType(self % ls, SNESLINESEARCHBASIC, petsc_err) - call SNESSetFromOptions(self % snes, petsc_err) - call SNESView(self % snes, PETSC_VIEWER_STDOUT_WORLD, petsc_err) - - ! Set up preconditioner - call KSPGetPC(self % ksp, self % pc, petsc_err) - call PCSetType(self % pc, PCILU, petsc_err) - call PCFactorSetLevels(self % pc, 5, petsc_err) - call PCSetFromOptions(self % pc, petsc_err) - call KSPSetFromOptions(self % ksp, petsc_err) + call SNESSetLagJacobian(self % snes, -2, petsc_err) + call SNESSetLagPreconditioner(self % snes, -1, petsc_err) end subroutine jfnk_set_functions @@ -278,7 +278,7 @@ contains call VecRestoreArrayF90(x, xvec % val, ierr) call VecRestoreArrayF90(res, resvec % val, ierr) - read * + ! read * end subroutine jfnk_compute_residual !=============================================================================== @@ -314,8 +314,8 @@ contains ! call MatView(jac_prec, viewer, petsc_err) ! call PetscViewerDestroy(viewer, petsc_err) #ifdef PETSC -! call MatAssemblyBegin(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) -! call MatAssemblyEnd(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) + call MatAssemblyBegin(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) + call MatAssemblyEnd(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) #endif end subroutine jfnk_compute_jacobian From f92dbfff7ddceb36b1dd8fa69837b870f5eb31a7 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 11 Jul 2013 12:24:26 -0400 Subject: [PATCH 039/192] cleaned up the JFNK routines --- src/cmfd_jfnk_solver.F90 | 36 +++++++++--------------------------- src/petsc_solver.F90 | 38 +++++++++++++++----------------------- 2 files changed, 24 insertions(+), 50 deletions(-) diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 225e2b7211..ea0a7bb304 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -13,12 +13,12 @@ module cmfd_jfnk_solver logical :: adjoint_calc type(Jfnk_ctx) :: jfnk_data - type(Matrix), target :: jac_prec + type(Matrix) :: jac_prec type(Matrix) :: loss type(Matrix) :: prod type(Petsc_jfnk) :: jfnk - type(Vector), target :: resvec - type(Vector), target :: xvec + type(Vector) :: resvec + type(Vector) :: xvec contains @@ -70,12 +70,7 @@ contains logical :: physical_adjoint integer :: n - type(Vector), pointer :: x - type(Vector), pointer :: res - type(Matrix), pointer :: jac - x => xvec - res => resvec - jac => jac_prec + ! Set up all matrices call init_loss_matrix(loss) call init_prod_matrix(prod) @@ -135,7 +130,6 @@ contains call loss % write_petsc_binary('loss.bin') call prod % write_petsc_binary('prod.bin') call jac_prec % write_petsc_binary('jac.bin') -! call xvec % write_petsc_binary('x.bin') end subroutine init_data @@ -183,7 +177,7 @@ contains real(8) :: val ! temporary real scalar type(Vector) :: flux ! flux vector type(Vector) :: fsrc ! fission source vector -!nprint *, 'IN JACOBIAN' + ! get the problem size n = loss % n @@ -192,8 +186,8 @@ contains if (loss % petsc_active) shift = 1 ! get flux and eigenvalue - flux % val => xvec % val(1:n) - lambda = xvec % val(n + 1) + flux % val => x % val(1:n) + lambda = x % val(n + 1) ! create fission source vector call fsrc % create(n) @@ -282,14 +276,6 @@ contains type(Vector) :: res_prod type(Vector) :: flux - type(Vector), pointer :: res_ptr - type(Vector), pointer :: x_ptr - - res_ptr => resvec - x_ptr => xvec - -!print *, 'IN RESIDUAL' - ! Get problem size n = loss % n @@ -334,8 +320,7 @@ contains call x % write_petsc_binary(filename) end if -! print *, x % val -! print *, res % val + end subroutine compute_nonlinear_residual !=============================================================================== @@ -368,10 +353,7 @@ contains use global, only: cmfd integer :: n ! problem size -call loss % write_petsc_binary('loss.mat') -call prod % write_petsc_binary('prod.mat') -call xvec % write_petsc_binary('x.bin') -call resvec % write_petsc_binary('res.bin') + ! get problem size n = loss % n diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index 3fd38a7f3d..97f1b05839 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -174,7 +174,6 @@ contains call SNESGetLineSearch(self % snes, self % ls, petsc_err) call SNESLineSearchSetType(self % ls, SNESLINESEARCHBASIC, petsc_err) call SNESSetFromOptions(self % snes, petsc_err) -! call SNESView(self % snes, PETSC_VIEWER_STDOUT_WORLD, petsc_err) ! Set up preconditioner call KSPGetPC(self % ksp, self % pc, petsc_err) @@ -182,7 +181,6 @@ contains call PCFactorSetLevels(self % pc, 5, petsc_err) call PCSetFromOptions(self % pc, petsc_err) call KSPSetFromOptions(self % ksp, petsc_err) - #endif end subroutine jfnk_create @@ -257,28 +255,24 @@ contains integer :: ierr type(Jfnk_ctx) :: ctx - PetscViewer ::viewer - type(Vector) :: xvec type(Vector) :: resvec + ! Need to point an OpenMC vector to PETSc vector +#ifdef PETSC call VecGetArrayF90(x, xvec % val, ierr) call VecGetArrayF90(res, resvec % val, ierr) +#endif + ! Call user residual routine call ctx % res_proc_ptr(xvec, resvec) -! call VecView(x, PETSC_VIEWER_STDOUT_WORLD, ierr) -! call VecView(res, PETSC_VIEWER_STDOUT_WORLD, ierr) - -! call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'res.bin', FILE_MODE_WRITE, viewer, ierr) -! call VecView(res, viewer, ierr) -! call PetscViewerDestroy(viewer, ierr) -! call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'x.bin', FILE_MODE_WRITE, viewer, ierr) -! call VecView(x, viewer, ierr) -! call PetscViewerDestroy(viewer, ierr) + ! Need to restore the PETSc vector +#ifdef PETSC call VecRestoreArrayF90(x, xvec % val, ierr) call VecRestoreArrayF90(res, resvec % val, ierr) - ! read * +#endif + end subroutine jfnk_compute_residual !=============================================================================== @@ -298,21 +292,19 @@ contains type(Vector) :: xvec - PetscViewer :: viewer - + ! Need to point OpenMC vector to PETSc Vector +#ifdef PETSC call VecGetArrayF90(x, xvec % val, ierr) +#endif + ! Evaluate user jacobian routine call ctx % jac_proc_ptr(xvec) + ! Restore the PETSc vector +#ifdef PETSC call VecRestoreArrayF90(x, xvec % val, ierr) +#endif -! call PetscViewerBinaryOpen(PETSC_COMM_WORLD, 'jac.bin', FILE_MODE_WRITE, viewer, petsc_err) -! call MatView(jac_prec, viewer, petsc_err) -! call PetscViewerDestroy(viewer, petsc_err) - -! call PetscViewerASCIIOpen(PETSC_COMM_WORLD, 'jac.out', viewer, petsc_err) -! call MatView(jac_prec, viewer, petsc_err) -! call PetscViewerDestroy(viewer, petsc_err) #ifdef PETSC call MatAssemblyBegin(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) call MatAssemblyEnd(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) From ef6425fa10e0a61bbc220fb4f16cc0188fdf8499 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 11 Jul 2013 15:32:38 -0400 Subject: [PATCH 040/192] fixed memory leaks in cmfd based routines using valgrind --- src/cmfd_power_solver.F90 | 2 +- src/matrix_header.F90 | 15 +++++++++------ src/petsc_solver.F90 | 5 +++-- src/vector_header.F90 | 16 +++++++++++++--- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 60ba09f2c6..c63cff3205 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -70,7 +70,7 @@ contains call gmres % set_oper(loss, loss) ! Precondition matrix - call gmres % precondition(loss) +! call gmres % precondition(loss) ! Stop timer for build call time_cmfdbuild % stop() diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index f3ec97ecb5..b920e41bbd 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -31,6 +31,8 @@ module matrix_header procedure :: vector_multiply => matrix_vector_multiply end type matrix + integer :: petsc_err + contains !=============================================================================== @@ -69,6 +71,10 @@ contains class(Matrix) :: self +#ifdef PETSC + if (self % petsc_active) call MatDestroy(self % petsc_mat, petsc_err) +#endif + if (allocated(self % row)) deallocate(self % row) if (allocated(self % col)) deallocate(self % col) if (allocated(self % val)) deallocate(self % val) @@ -217,8 +223,6 @@ contains class(Matrix) :: self - integer :: petsc_err - ! change indices to c notation self % row = self % row - 1 self % col = self % col - 1 @@ -227,9 +231,11 @@ contains #ifdef PETSC call MatCreateSeqAIJWithArrays(PETSC_COMM_WORLD, self % n, self % n, & self % row, self % col, self % val, self % petsc_mat, petsc_err) - self % petsc_active = .true. #endif + ! Petsc is now active + self % petsc_active = .true. + end subroutine matrix_setup_petsc !=============================================================================== @@ -241,7 +247,6 @@ contains character(*) :: filename class(Matrix) :: self - integer :: petsc_err #ifdef PETSC PetscViewer :: viewer @@ -261,8 +266,6 @@ contains class(Matrix) :: self - integer :: petsc_err - #ifdef PETSC call MatTranspose(self % petsc_mat, MAT_REUSE_MATRIX, self % petsc_mat, & petsc_err) diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index 97f1b05839..49802a9dcd 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -84,6 +84,7 @@ contains call KSPSetInitialGuessNonzero(self % ksp, PETSC_TRUE, petsc_err) call KSPGetPC(self % ksp, self % pc, petsc_err) call PCFactorSetLevels(self % pc, ilu_levels, petsc_err) + call KSPSetFromOptions(self % ksp, petsc_err) #endif end subroutine gmres_create @@ -98,8 +99,7 @@ contains type(Matrix) :: mat_in #ifdef PETSC - call KSPSetUp(self % ksp, petsc_err) - call PCFactorGetMatrix(self % pc, mat_in % petsc_mat, petsc_err) +! call PCFactorGetMatrix(self % pc, mat_in % petsc_mat, petsc_err) #endif end subroutine gmres_precondition @@ -117,6 +117,7 @@ contains #ifdef PETSC call KSPSetOperators(self % ksp, mat_in % petsc_mat, prec_mat % petsc_mat, & SAME_NONZERO_PATTERN, petsc_err) + call KSPSetUp(self % ksp, petsc_err) #endif end subroutine gmres_set_oper diff --git a/src/vector_header.F90 b/src/vector_header.F90 index 1a867ef5c6..1c525fd2f8 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -15,6 +15,7 @@ module vector_header # ifdef PETSC Vec :: petsc_vec # endif + logical :: petsc_active contains procedure :: create => vector_create procedure :: destroy => vector_destroy @@ -23,6 +24,8 @@ module vector_header procedure :: write_petsc_binary => vector_write_petsc_binary end type Vector + integer :: petsc_err + contains !=============================================================================== @@ -43,6 +46,9 @@ contains ! initialize to zero self % val = ZERO + ! petsc is default not active + self % petsc_active = .false. + end subroutine vector_create !=============================================================================== @@ -53,6 +59,10 @@ contains class(Vector) :: self +#ifdef PETSC + if (self % petsc_active) call VecDestroy(self % petsc_vec, petsc_err) +#endif + if (associated(self % val)) deallocate(self % val) end subroutine vector_destroy @@ -79,14 +89,15 @@ contains class(Vector) :: self - integer :: petsc_err - ! link to petsc #ifdef PETSC call VecCreateSeqWithArray(PETSC_COMM_WORLD, 1, self % n, self % val, & self % petsc_vec, petsc_err) #endif + ! set that petsc is now active + self % petsc_active = .true. + end subroutine vector_setup_petsc !=============================================================================== @@ -98,7 +109,6 @@ contains character(*) :: filename class(Vector) :: self - integer :: petsc_err #ifdef PETSC PetscViewer :: viewer From abd7e4a49ce4064eada501e495413f0df04360e7 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 11 Jul 2013 15:53:56 -0400 Subject: [PATCH 041/192] cleaned up and added comments to cmfd code --- src/cmfd_jfnk_solver.F90 | 93 ++++++++++++++++++--------------------- src/cmfd_power_solver.F90 | 53 +++++++++++----------- src/matrix_header.F90 | 57 +++++++++++------------- src/petsc_solver.F90 | 49 +++++++++------------ src/vector_header.F90 | 16 +++---- 5 files changed, 122 insertions(+), 146 deletions(-) diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index ea0a7bb304..54c26326b5 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -11,50 +11,50 @@ module cmfd_jfnk_solver private public :: cmfd_jfnk_execute - logical :: adjoint_calc - type(Jfnk_ctx) :: jfnk_data - type(Matrix) :: jac_prec - type(Matrix) :: loss - type(Matrix) :: prod - type(Petsc_jfnk) :: jfnk - type(Vector) :: resvec - type(Vector) :: xvec + logical :: adjoint_calc ! true if an adjoint is to be calculated + type(Jfnk_ctx) :: jfnk_data ! object that holds pointers to user routines + type(Matrix) :: jac_prec ! Jacobian preconditioner object + type(Matrix) :: loss ! CMFD loss matrix + type(Matrix) :: prod ! CMFD production matrix + type(Petsc_jfnk) :: jfnk ! JFNK solver object + type(Vector) :: resvec ! JFNK residual vector + type(Vector) :: xvec ! JFNK solution vector contains !=============================================================================== -! CMFD_JFNK_EXECUTE +! CMFD_JFNK_EXECUTE main routine for JFNK solver !=============================================================================== subroutine cmfd_jfnk_execute(adjoint) logical, optional :: adjoint ! adjoint calculation - ! check for adjoint + ! Check for adjoint adjoint_calc = .false. if (present(adjoint)) adjoint_calc = adjoint - ! seed with power iteration + ! Seed with power iteration to help converge to fundamental mode call cmfd_power_execute(k_tol=1.E-3_8, s_tol=1.E-3_8, adjoint=adjoint_calc) - ! initialize data + ! Initialize data call init_data() - ! initialize solver + ! Initialize solver call jfnk % create() - ! set up residual and jacobian routines + ! Set up residual and jacobian routines jfnk_data % res_proc_ptr => compute_nonlinear_residual jfnk_data % jac_proc_ptr => build_jacobian_matrix call jfnk % set_functions(jfnk_data, resvec, jac_prec) - ! solve the system + ! Solve the system call jfnk % solve(xvec) - ! extracts results to cmfd object + ! Extracts results to cmfd object call extract_results() - ! deallocate all slepc data + ! Deallocate all slepc data call finalize() end subroutine cmfd_jfnk_execute @@ -102,14 +102,14 @@ contains call resvec % create(n + 1) call xvec % create(n + 1) - ! set flux in guess + ! Set flux in guess if (adjoint_calc) then xvec % val(1:n) = cmfd % adj_phi else xvec % val(1:n) = cmfd % phi end if - ! set keff in guess + ! Set keff in guess if (adjoint_calc) then xvec % val(n + 1) = ONE/cmfd % adj_keff else @@ -126,11 +126,6 @@ contains ! Set up Jacobian for Petsc call jac_prec % setup_petsc() - ! write all out - call loss % write_petsc_binary('loss.bin') - call prod % write_petsc_binary('prod.bin') - call jac_prec % write_petsc_binary('jac.bin') - end subroutine init_data !============================================================================== @@ -178,43 +173,43 @@ contains type(Vector) :: flux ! flux vector type(Vector) :: fsrc ! fission source vector - ! get the problem size + ! Get the problem size n = loss % n ! Check to use shift shift = 0 if (loss % petsc_active) shift = 1 - ! get flux and eigenvalue + ! Get flux and eigenvalue flux % val => x % val(1:n) lambda = x % val(n + 1) - ! create fission source vector + ! Create fission source vector call fsrc % create(n) call prod % vector_multiply(flux, fsrc) - ! reset counters in Jacobian matrix + ! Reset counters in Jacobian matrix jac_prec % n_kount = 1 jac_prec % nz_kount = 1 - ! begin loop around rows of Jacobian matrix + ! Begin loop around rows of Jacobian matrix ROWS: do i = 1, n - ! add a new row in Jacobian + ! Add a new row in Jacobian call jac_prec % new_row() - ! begin loop around columns of loss matrix + ! Begin loop around columns of loss matrix COLS_LOSS: do jloss = loss % row(i) + shift, & loss % row(i + 1) - 1 + shift - ! start with the value in the loss matrix + ! Start with the value in the loss matrix val = loss % val(jloss) - ! loop around columns of prod matrix + ! Loop around columns of prod matrix COLS_PROD: do jprod = prod % row(i) + shift, & prod % row(i + 1) - 1 + shift - ! see if columns agree with loss matrix + ! See if columns agree with loss matrix if (prod % col(jprod) == loss % col(jloss)) then val = val - lambda*prod % val(jprod) exit @@ -222,25 +217,25 @@ contains end do COLS_PROD - ! record value in jacobian + ! Record value in jacobian call jac_prec % add_value(loss % col(jloss) + shift, val) end do COLS_LOSS - ! add fission source value + ! Add fission source value val = -fsrc % val(i) call jac_prec % add_value(n + 1, val) end do ROWS - ! need to add negative transpose of flux vector on last row + ! Need to add negative transpose of flux vector on last row call jac_prec % new_row() do jjac = 1, n val = -flux % val(jjac) call jac_prec % add_value(jjac, val) end do - ! add unity on bottom right corner of matrix + ! Add unity on bottom right corner of matrix call jac_prec % add_value(n + 1, ONE) ! CRS requires a final value in row @@ -252,14 +247,14 @@ contains jac_prec % col = jac_prec % col - 1 end if - ! free all allocated memory + ! Free all allocated memory flux % val => null() call fsrc % destroy() end subroutine build_jacobian_matrix !=============================================================================== -! COMPUTE_NONLINEAR_RESIDUAL +! COMPUTE_NONLINEAR_RESIDUAL computes the residual of the nonlinear equations !=============================================================================== subroutine compute_nonlinear_residual(x, res) @@ -300,10 +295,10 @@ contains ! Put eigenvalue component in residual vector res % val(n+1) = 0.5_8 - 0.5_8*sum(flux % val * flux % val) - ! write out data in binary files (debugging) + ! Write out data in binary files (debugging) if (cmfd_write_matrices) then - ! write out residual vector + ! Write out residual vector if (adjoint_calc) then filename = 'adj_res.bin' else @@ -311,7 +306,7 @@ contains end if call res % write_petsc_binary(filename) - ! write out solution vector + ! Write out solution vector if (adjoint_calc) then filename = 'adj_x.bin' else @@ -324,18 +319,18 @@ contains end subroutine compute_nonlinear_residual !=============================================================================== -! COMPUTE_ADJOINT +! COMPUTE_ADJOINT calculates mathematical adjoint by taking transposes !=============================================================================== subroutine compute_adjoint() use global, only: cmfd_write_matrices - ! transpose matrices + ! Transpose matrices call loss % transpose() call prod % transpose() - ! write out matrix in binary file (debugging) + ! Write out matrix in binary file (debugging) if (cmfd_write_matrices) then call loss % write_petsc_binary('adj_lossmat.bin') call loss % write_petsc_binary('adj_prodmat.bin') @@ -344,7 +339,7 @@ contains end subroutine compute_adjoint !=============================================================================== -! EXTRACT_RESULTS +! EXTRACT_RESULTS gets the results and puts them in global CMFD object !=============================================================================== subroutine extract_results() @@ -354,10 +349,10 @@ contains integer :: n ! problem size - ! get problem size + ! Get problem size n = loss % n - ! also allocate in cmfd object + ! Also allocate in cmfd object if (adjoint_calc) then if (.not. allocated(cmfd % adj_phi)) allocate(cmfd % adj_phi(n)) else diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index c63cff3205..6c6bcbd9d2 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -69,9 +69,6 @@ contains ! Set up krylov info call gmres % set_oper(loss, loss) - ! Precondition matrix -! call gmres % precondition(loss) - ! Stop timer for build call time_cmfdbuild % stop() @@ -149,11 +146,11 @@ contains use global, only: cmfd_write_matrices - ! transpose matrices + ! Transpose matrices call loss % transpose() call prod % transpose() - ! write out matrix in binary file (debugging) + ! Write out matrix in binary file (debugging) if (cmfd_write_matrices) then call loss % write_petsc_binary('adj_lossmat.bin') call prod % write_petsc_binary('adj_prodmat.bin') @@ -170,37 +167,37 @@ contains integer :: i ! iteration counter - ! reset convergence flag + ! Reset convergence flag iconv = .false. - ! begin power iteration + ! Begin power iteration do i = 1, 10000 - ! compute source vector + ! Compute source vector call prod % vector_multiply(phi_o, S_o) - ! normalize source vector + ! Normalize source vector S_o % val = S_o % val / k_o - ! compute new flux vector + ! Compute new flux vector call gmres % solve(S_o, phi_n) - ! compute new source vector + ! Compute new source vector call prod % vector_multiply(phi_n, S_n) - ! compute new k-eigenvalue + ! Compute new k-eigenvalue k_n = sum(S_n % val) / sum(S_o % val) - ! renormalize the old source + ! Renormalize the old source S_o % val = S_o % val * k_o - ! check convergence + ! Check convergence call convergence(i) - ! to break or not to break + ! To break or not to break if (iconv) exit - ! record old values + ! Record old values phi_o % val = phi_n % val k_o = k_n @@ -223,19 +220,19 @@ contains real(8) :: kerr ! error in keff real(8) :: serr ! error in source - ! reset convergence flag + ! Reset convergence flag iconv = .false. - ! calculate error in keff + ! Calculate error in keff kerr = abs(k_o - k_n)/k_n - ! calculate max error in source + ! Calculate max error in source serr = sqrt(ONE/dble(S_n % n) * sum(((S_n % val - S_o % val)/S_n % val)**2)) - ! check for convergence + ! Check for convergence if(kerr < ktol .and. serr < stol) iconv = .true. - ! print out to user + ! Print out to user if (cmfd_power_monitor .and. master) then write(OUTPUT_UNIT,FMT='(I0,":",T10,"k-eff: ",F0.8,T30,"k-error: ",1PE12.5,T55, & "src-error: ",1PE12.5)') iter, k_n, kerr, serr @@ -254,38 +251,38 @@ contains character(len=25) :: filename ! name of file to write data integer :: n ! problem size - ! get problem size + ! Get problem size n = loss % n - ! also allocate in cmfd object + ! Also allocate in cmfd object if (adjoint_calc) then if (.not. allocated(cmfd%adj_phi)) allocate(cmfd%adj_phi(n)) else if (.not. allocated(cmfd%phi)) allocate(cmfd%phi(n)) end if - ! save values + ! Save values if (adjoint_calc) then cmfd % adj_phi = phi_n % val else cmfd % phi = phi_n % val end if - ! save eigenvalue + ! Save eigenvalue if(adjoint_calc) then cmfd%adj_keff = k_n else cmfd%keff = k_n end if - ! normalize phi to 1 + ! Normalize phi to 1 if (adjoint_calc) then cmfd%adj_phi = cmfd%adj_phi/sqrt(sum(cmfd%adj_phi*cmfd%adj_phi)) else cmfd%phi = cmfd%phi/sqrt(sum(cmfd%phi*cmfd%phi)) end if - ! write out results + ! Write out results if (cmfd_write_matrices) then if (adjoint_calc) then filename = 'adj_fluxvec.bin' @@ -303,7 +300,7 @@ contains subroutine finalize() - ! destroy all objects + ! Destroy all objects call gmres % destroy() call loss % destroy() call prod % destroy() diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index b920e41bbd..f62437c417 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -45,20 +45,20 @@ contains integer :: nnz class(Matrix) :: self - ! preallocate vectors + ! Preallocate vectors if (.not.allocated(self % row)) allocate(self % row(n+1)) if (.not.allocated(self % col)) allocate(self % col(nnz)) if (.not.allocated(self % val)) allocate(self % val(nnz)) - ! set counters to 1 + ! Set counters to 1 self % n_kount = 1 self % nz_kount = 1 - ! set n and nnz + ! Set n and nnz self % n = n self % nnz = nnz - ! set petsc active by default to false + ! Set petsc active by default to false self % petsc_active = .false. end subroutine matrix_create @@ -111,7 +111,7 @@ contains end subroutine matrix_new_row !=============================================================================== -! MATRIX_ASSEMBLE +! MATRIX_ASSEMBLE main rountine to sort all the columns in CSR matrix !=============================================================================== subroutine matrix_assemble(self) @@ -122,14 +122,14 @@ contains integer :: first integer :: last - ! loop around row vector + ! Loop around row vector do i = 1, self % n - ! get bounds + ! Get bounds first = self % row(i) last = self % row(i+1) - 1 - ! sort a row + ! Sort a row call sort_csr(self % col, self % val, first, last) end do @@ -137,7 +137,7 @@ contains end subroutine matrix_assemble !=============================================================================== -! SORT_CSR +! SORT_CSR main routine that performs a sort on a CSR col/val subvector !=============================================================================== recursive subroutine sort_csr(col, val, first, last) @@ -158,7 +158,7 @@ contains end subroutine sort_csr !=============================================================================== -! SPLIT +! SPLIT bisects the search space for the sorting routine !=============================================================================== subroutine split(col, val, low, high, mid) @@ -181,20 +181,20 @@ contains pivot = col(low) val0 = val(low) - ! repeat the following while left and right havent met + ! Repeat the following while left and right havent met do while (left < right) - ! scan right to left to find element < pivot + ! Scan right to left to find element < pivot do while (left < right .and. col(right) >= pivot) right = right - 1 end do - ! scan left to right to find element > pivot + ! Scan left to right to find element > pivot do while (left < right .and. col(left) <= pivot) left = left + 1 end do - ! if left and right havent met, exchange the items + ! If left and right havent met, exchange the items if (left < right) then iswap = col(left) col(left) = col(right) @@ -206,7 +206,7 @@ contains end do - ! switch the element in split position with pivot + ! Switch the element in split position with pivot col(low) = col(right) col(right) = pivot mid = right @@ -216,7 +216,7 @@ contains end subroutine split !=============================================================================== -! MATRIX_SETUP_PETSC +! MATRIX_SETUP_PETSC configures the row/col vectors and links to a petsc object !=============================================================================== subroutine matrix_setup_petsc(self) @@ -227,7 +227,7 @@ contains self % row = self % row - 1 self % col = self % col - 1 - ! link to petsc + ! Link to petsc #ifdef PETSC call MatCreateSeqAIJWithArrays(PETSC_COMM_WORLD, self % n, self % n, & self % row, self % col, self % val, self % petsc_mat, petsc_err) @@ -239,7 +239,7 @@ contains end subroutine matrix_setup_petsc !=============================================================================== -! MATRIX_WRITE_PETSC_BINARY +! MATRIX_WRITE_PETSC_BINARY writes a petsc matrix binary file !=============================================================================== subroutine matrix_write_petsc_binary(self, filename) @@ -259,7 +259,7 @@ contains end subroutine matrix_write_petsc_binary !=============================================================================== -! MATRIX_TRANSPOSE +! MATRIX_TRANSPOSE uses PETSc to transpose a matrix !=============================================================================== subroutine matrix_transpose(self) @@ -274,7 +274,7 @@ contains end subroutine matrix_transpose !=============================================================================== -! MATRIX_VECTOR_MULTIPLY +! MATRIX_VECTOR_MULTIPLY allow a vector to multiply the matrix !=============================================================================== subroutine matrix_vector_multiply(self, vec_in, vec_out) @@ -290,25 +290,18 @@ contains integer :: j integer :: shift -! integer :: petsc_err - -!#ifdef PETSC -! call MatMult(self % petsc_mat, vec_in % petsc_vec, vec_out % petsc_vec, & -! petsc_err) -!#endif - - ! set shift by default 0 and change if petsc is active - ! this is because PETSc needs vectors to remain referenced to 0 + ! Set shift by default 0 and change if petsc is active + ! This is because PETSc needs vectors to remain referenced to 0 shift = 0 if (self % petsc_active) shift = 1 - ! begin loop around rows + ! Begin loop around rows ROWS: do i = 1, self % n - ! initialize target location in vector + ! Initialize target location in vector vec_out % val(i) = ZERO - ! begin loop around columns (need to shift if petsc is active) + ! Begin loop around columns (need to shift if petsc is active) COLS: do j = self % row(i) + shift, self % row(i + 1) - 1 + shift vec_out % val(i) = vec_out % val(i) + self % val(j) * & diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index 49802a9dcd..8318ca174b 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -10,7 +10,7 @@ module petsc_solver # include #endif - ! Petsc GMRES solver context + ! PETSc GMRES solver type type, public :: Petsc_gmres #ifdef PETSC KSP :: ksp @@ -18,7 +18,6 @@ module petsc_solver #endif contains procedure :: create => gmres_create - procedure :: precondition => gmres_precondition procedure :: set_oper => gmres_set_oper procedure :: destroy => gmres_destroy procedure :: solve => gmres_solve @@ -30,7 +29,7 @@ module petsc_solver procedure (jac_interface), pointer, nopass :: jac_proc_ptr end type Jfnk_ctx - ! Petsc SNES JFNK solver context + ! Petsc SNES JFNK solver type type, public :: Petsc_jfnk #ifdef PETSC KSP :: ksp @@ -50,10 +49,10 @@ module petsc_solver ! Abstract interface stating how jacobian and residual routines look abstract interface - subroutine res_interface(x, r) + subroutine res_interface(x, res) import :: Vector type(Vector) :: x - type(Vector) :: r + type(Vector) :: res end subroutine res_interface subroutine jac_interface(x) @@ -65,7 +64,7 @@ module petsc_solver contains !=============================================================================== -! GMRES_CREATE +! GMRES_CREATE sets up a PETSc GMRES solver !=============================================================================== subroutine gmres_create(self) @@ -90,22 +89,7 @@ contains end subroutine gmres_create !=============================================================================== -! GMRES_PRECONDITION -!=============================================================================== - - subroutine gmres_precondition(self, mat_in) - - class(Petsc_gmres) :: self - type(Matrix) :: mat_in - -#ifdef PETSC -! call PCFactorGetMatrix(self % pc, mat_in % petsc_mat, petsc_err) -#endif - - end subroutine gmres_precondition - -!=============================================================================== -! GMRES_SET_OPER +! GMRES_SET_OPER sets the matrix opetors for the GMRES solver !=============================================================================== subroutine gmres_set_oper(self, prec_mat, mat_in) @@ -123,7 +107,7 @@ contains end subroutine gmres_set_oper !=============================================================================== -! GMRES_DESTROY +! GMRES_DESTROY frees all memory associated with the GMRES solver !=============================================================================== subroutine gmres_destroy(self) @@ -137,7 +121,7 @@ contains end subroutine gmres_destroy !=============================================================================== -! GMRES_SOLVE +! GMRES_SOLVE solves the linear system !=============================================================================== subroutine gmres_solve(self, b, x) @@ -153,7 +137,7 @@ contains end subroutine gmres_solve !=============================================================================== -! JFNK_CREATE +! JFNK_CREATE sets up a JFNK solver using PETSc SNES !=============================================================================== subroutine jfnk_create(self) @@ -187,7 +171,7 @@ contains end subroutine jfnk_create !=============================================================================== -! JFNK_DESTROY +! JFNK_DESTROY frees all memory associated with JFNK solver !=============================================================================== subroutine jfnk_destroy(self) @@ -202,7 +186,7 @@ contains end subroutine jfnk_destroy !=============================================================================== -! JFNK_SET_FUNCTIONS +! JFNK_SET_FUNCTIONS sets user functions and matrix free objects !=============================================================================== subroutine jfnk_set_functions(self, ctx, res, jac_prec) @@ -230,7 +214,7 @@ contains end subroutine jfnk_set_functions !=============================================================================== -! JFNK_SOLVE +! JFNK_SOLVE solves the nonlinear system !=============================================================================== subroutine jfnk_solve(self, xvec) @@ -245,7 +229,7 @@ contains end subroutine jfnk_solve !=============================================================================== -! JFNK_COMPUTE_RESIDUAL +! JFNK_COMPUTE_RESIDUAL buffer routine to user specifed residual routine !=============================================================================== subroutine jfnk_compute_residual(snes, x, res, ctx, ierr) @@ -259,6 +243,10 @@ contains type(Vector) :: xvec type(Vector) :: resvec + ! We need to use the x and res that come from PETSc because the pointer + ! location changes and therefore we can not use module variables + ! for the residual and x vectors here + ! Need to point an OpenMC vector to PETSc vector #ifdef PETSC call VecGetArrayF90(x, xvec % val, ierr) @@ -293,6 +281,9 @@ contains type(Vector) :: xvec + ! Again, we use the vector that comes from Petsc to build the Jacobian + ! matrix + ! Need to point OpenMC vector to PETSc Vector #ifdef PETSC call VecGetArrayF90(x, xvec % val, ierr) diff --git a/src/vector_header.F90 b/src/vector_header.F90 index 1c525fd2f8..a201dc9c21 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -37,16 +37,16 @@ contains integer :: n class(Vector) :: self - ! preallocate vector + ! Preallocate vector if (.not.associated(self % val)) allocate(self % val(n)) - ! set n + ! Set n self % n = n - ! initialize to zero + ! Initialize to zero self % val = ZERO - ! petsc is default not active + ! Petsc is default not active self % petsc_active = .false. end subroutine vector_create @@ -82,26 +82,26 @@ contains end subroutine vector_add_value !=============================================================================== -! VECTOR_SETUP_PETSC +! VECTOR_SETUP_PETSC links the data to a PETSc vector !=============================================================================== subroutine vector_setup_petsc(self) class(Vector) :: self - ! link to petsc + ! Link to PETSc #ifdef PETSC call VecCreateSeqWithArray(PETSC_COMM_WORLD, 1, self % n, self % val, & self % petsc_vec, petsc_err) #endif - ! set that petsc is now active + ! Set that PETSc is now active self % petsc_active = .true. end subroutine vector_setup_petsc !=============================================================================== -! VECTOR_WRITE_PETSC_BINARY +! VECTOR_WRITE_PETSC_BINARY writes the PETSc vector to a binary file !=============================================================================== subroutine vector_write_petsc_binary(self, filename) From 5285c37b72900e95a2615958938b56b189dc85f7 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 11 Jul 2013 16:55:14 -0400 Subject: [PATCH 042/192] fixed some comments --- src/cmfd_input.F90 | 2 +- src/cmfd_jfnk_solver.F90 | 10 +++++----- src/cmfd_output.F90 | 2 +- src/cmfd_power_solver.F90 | 18 +++++++++--------- src/matrix_header.F90 | 10 +++++----- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 7b5e9c2d62..b8e90cb262 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -40,7 +40,7 @@ contains # ifdef PETSC PETSC_COMM_WORLD = new_comm - ! Initialize petsc on all procs + ! Initialize PETSc on all procs call PetscInitialize(PETSC_NULL_CHARACTER,mpi_err) # endif diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 54c26326b5..70593a97ef 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -85,7 +85,7 @@ contains call build_loss_matrix(loss, adjoint = physical_adjoint) call build_prod_matrix(prod, adjoint = physical_adjoint) - ! Assembly matrices and use petsc + ! Assemble matrices and use PETSc call loss % assemble() call prod % assemble() call loss % setup_petsc() @@ -102,28 +102,28 @@ contains call resvec % create(n + 1) call xvec % create(n + 1) - ! Set flux in guess + ! Set flux in guess from rough power iteration if (adjoint_calc) then xvec % val(1:n) = cmfd % adj_phi else xvec % val(1:n) = cmfd % phi end if - ! Set keff in guess + ! Set keff in guess from rough power iteration if (adjoint_calc) then xvec % val(n + 1) = ONE/cmfd % adj_keff else xvec % val(n + 1) = ONE/cmfd % keff end if - ! Set up vectors for petsc + ! Set up vectors for PETSc call resvec % setup_petsc() call xvec % setup_petsc() ! Build jacobian from initial guess call build_jacobian_matrix(xvec) - ! Set up Jacobian for Petsc + ! Set up Jacobian for PETSc call jac_prec % setup_petsc() end subroutine init_data diff --git a/src/cmfd_output.F90 b/src/cmfd_output.F90 index 2936dda35a..eb2bf69f93 100644 --- a/src/cmfd_output.F90 +++ b/src/cmfd_output.F90 @@ -18,7 +18,7 @@ contains master, mpi_err use cmfd_header, only: deallocate_cmfd - ! Finalize petsc + ! Finalize PETSc #ifdef PETSC call PetscFinalize(mpi_err) # endif diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 6c6bcbd9d2..5c8333cbc1 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -62,7 +62,7 @@ contains ! Initialize matrices and vectors call init_data(physical_adjoint) - ! Check for adjoint calculation + ! Check for mathematical adjoint calculation if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & call compute_adjoint() @@ -80,7 +80,7 @@ contains ! Extract results call extract_results() - ! Deallocate petsc objects + ! Deallocate data call finalize() end subroutine cmfd_power_execute @@ -120,10 +120,10 @@ contains k_n = guess k_o = guess - ! Set up loss matrix + ! Fill in loss matrix call build_loss_matrix(loss, adjoint=adjoint) - ! Set up production matrix + ! Fill in production matrix call build_prod_matrix(prod, adjoint=adjoint) ! Setup petsc for everything @@ -159,13 +159,13 @@ contains end subroutine compute_adjoint !=============================================================================== -! EXECUTE_POWER_ITER in the main power iteration routine +! EXECUTE_POWER_ITER is the main power iteration routine ! for the cmfd calculation !=============================================================================== subroutine execute_power_iter() - integer :: i ! iteration counter + integer :: i ! iteration counter ! Reset convergence flag iconv = .false. @@ -234,8 +234,8 @@ contains ! Print out to user if (cmfd_power_monitor .and. master) then - write(OUTPUT_UNIT,FMT='(I0,":",T10,"k-eff: ",F0.8,T30,"k-error: ",1PE12.5,T55, & - "src-error: ",1PE12.5)') iter, k_n, kerr, serr + write(OUTPUT_UNIT,FMT='(I0,":",T10,"k-eff: ",F0.8,T30,"k-error: ", & + &1PE12.5,T55, "src-error: ",1PE12.5)') iter, k_n, kerr, serr end if end subroutine convergence @@ -254,7 +254,7 @@ contains ! Get problem size n = loss % n - ! Also allocate in cmfd object + ! Allocate in cmfd object if not already allocated if (adjoint_calc) then if (.not. allocated(cmfd%adj_phi)) allocate(cmfd%adj_phi(n)) else diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index f62437c417..d85951d527 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -58,7 +58,7 @@ contains self % n = n self % nnz = nnz - ! Set petsc active by default to false + ! Set PETSc active by default to false self % petsc_active = .false. end subroutine matrix_create @@ -216,7 +216,7 @@ contains end subroutine split !=============================================================================== -! MATRIX_SETUP_PETSC configures the row/col vectors and links to a petsc object +! MATRIX_SETUP_PETSC configures the row/col vectors and links to a PETSc object !=============================================================================== subroutine matrix_setup_petsc(self) @@ -239,7 +239,7 @@ contains end subroutine matrix_setup_petsc !=============================================================================== -! MATRIX_WRITE_PETSC_BINARY writes a petsc matrix binary file +! MATRIX_WRITE_PETSC_BINARY writes a PETSc matrix binary file !=============================================================================== subroutine matrix_write_petsc_binary(self, filename) @@ -290,7 +290,7 @@ contains integer :: j integer :: shift - ! Set shift by default 0 and change if petsc is active + ! Set shift by default 0 and change if PETSc is active ! This is because PETSc needs vectors to remain referenced to 0 shift = 0 if (self % petsc_active) shift = 1 @@ -301,7 +301,7 @@ contains ! Initialize target location in vector vec_out % val(i) = ZERO - ! Begin loop around columns (need to shift if petsc is active) + ! Begin loop around columns (need to shift if PETSc is active) COLS: do j = self % row(i) + shift, self % row(i + 1) - 1 + shift vec_out % val(i) = vec_out % val(i) + self % val(j) * & From e4f55c92467083913916cb7de108e7fbab0ece97 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Jul 2013 10:03:33 -0400 Subject: [PATCH 043/192] changed code on how columns are printed out --- src/eigenvalue.F90 | 2 +- src/output.F90 | 48 ++++++++++++++++------------------------------ 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index 82ad2d73a4..e67876b734 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -43,7 +43,7 @@ contains allocate(p) ! Display column titles - call print_columns() + if(master) call print_columns() ! Turn on inactive timer call time_inactive % start() diff --git a/src/output.F90 b/src/output.F90 index c6810dc083..a4ce08fc3b 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1214,37 +1214,28 @@ contains subroutine print_columns() - if (entropy_on) then - if (cmfd_run) then - message = " Bat./Gen. k Entropy Average k CMFD k CMFD Ent" - call write_message(1) - message = " ========= ======== ======== ==================== ======== ========" - call write_message(1) - else - message = " Bat./Gen. k Entropy Average k" - call write_message(1) - message = " ========= ======== ======== ====================" - call write_message(1) - end if - else - if (cmfd_run) then - message = " Bat./Gen. k Average k CMFD k" - call write_message(1) - message = " ========= ======== ==================== ========" - call write_message(1) - else - message = " Bat./Gen. k Average k" - call write_message(1) - message = " ========= ======== ====================" - call write_message(1) - end if + write(UNIT=ou, FMT='(2X,A9,3X)', ADVANCE='NO') "Bat./Gen." + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') " k " + if (entropy_on) write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "Entropy " + write(UNIT=ou, FMT='(A20,3X)', ADVANCE='NO') " Average k " + if (cmfd_run) then + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') " CMFD k " end if + write(UNIT=ou, FMT=*) + + write(UNIT=ou, FMT='(2X,A9,3X)', ADVANCE='NO') "=========" + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" + if (entropy_on) write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" + write(UNIT=ou, FMT='(A20,3X)', ADVANCE='NO') "====================" + if (cmfd_run) then + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "=========" + end if + write(UNIT=ou, FMT=*) end subroutine print_columns !=============================================================================== -! PRINT_GENERATION displays information for a generation of neutrons. For now, -! if the user has entropy on, it will print out the entropy +! PRINT_GENERATION displays information for a generation of neutrons. !=============================================================================== subroutine print_generation() @@ -1294,15 +1285,10 @@ contains write(UNIT=OUTPUT_UNIT, FMT='(23X)', ADVANCE='NO') end if - ! write out cmfd keff if it is active if (cmfd_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & cmfd % keff - ! write out cmfd entopy - if (cmfd_on .and. entropy_on) write(UNIT=OUTPUT_UNIT, & - FMT='(3X, F8.5)', ADVANCE='NO') cmfd % entropy - ! next line write(UNIT=OUTPUT_UNIT, FMT=*) From 212589556d2d87531f7cc08c3f8f6ba8113308e4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Jul 2013 12:20:30 -0400 Subject: [PATCH 044/192] add new column display for CMFD, added dominance ratio calculation, redid neutron balance, cmfd can display batchwise entropy, RMS of neutron balance, RMS of source deviation or dominance ratio --- src/DEPENDENCIES | 7 +---- src/OBJECTS | 1 - src/cmfd_data.F90 | 35 ++++++++++------------- src/cmfd_execute.F90 | 9 ++++-- src/cmfd_header.F90 | 31 ++++++++++++++++++-- src/cmfd_input.F90 | 13 ++++++--- src/cmfd_output.F90 | 59 --------------------------------------- src/cmfd_power_solver.F90 | 22 ++++++++++++--- src/constants.F90 | 5 ++-- src/finalize.F90 | 11 +++----- src/global.F90 | 8 ++++-- src/output.F90 | 34 ++++++++++++++++++++-- src/templates/cmfd_t.xml | 3 +- 13 files changed, 120 insertions(+), 118 deletions(-) delete mode 100644 src/cmfd_output.F90 diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index bb1f7a7cd6..b4c9c7980c 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -6,11 +6,6 @@ energy_grid.o: global.o energy_grid.o: list_header.o energy_grid.o: output.o -cmfd_output.o: cmfd_data.o -cmfd_output.o: cmfd_header.o -cmfd_output.o: constants.o -cmfd_output.o: global.o - ace_header.o: constants.o ace_header.o: endf_header.o @@ -104,6 +99,7 @@ fission.o: global.o fission.o: interpolation.o fission.o: search.o +cmfd_input.o: cmfd_header.o cmfd_input.o: error.o cmfd_input.o: global.o cmfd_input.o: mesh_header.o @@ -194,7 +190,6 @@ string.o: constants.o string.o: error.o string.o: global.o -finalize.o: cmfd_output.o finalize.o: global.o finalize.o: hdf5_interface.o finalize.o: output.o diff --git a/src/OBJECTS b/src/OBJECTS index 5075a5ee53..4a2c48a717 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -8,7 +8,6 @@ cmfd_header.o \ cmfd_input.o \ cmfd_jfnk_solver.o \ cmfd_loss_operator.o \ -cmfd_output.o \ cmfd_power_solver.o \ cmfd_prod_operator.o \ constants.o \ diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index 4e81c2aaf4..ef971f47e4 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -24,9 +24,6 @@ contains use constants, only: CMFD_NOACCEL use global, only: cmfd, cmfd_coremap, cmfd_downscatter - ! Initialize cmfd object - if (.not.allocated(cmfd%flux)) call allocate_cmfd(cmfd) - ! Check for core map and set it up if ((cmfd_coremap) .and. (cmfd%mat_dim == CMFD_NOACCEL)) call set_coremap() @@ -37,7 +34,7 @@ contains if (cmfd_downscatter) call compute_effective_downscatter() ! Check neutron balance -! call neutron_balance(670) + call neutron_balance() ! Calculate dtilde call compute_dtilde() @@ -370,10 +367,10 @@ contains ! NEUTRON_BALANCE writes a file that contains n. bal. info for all cmfd mesh !=============================================================================== - subroutine neutron_balance(uid) + subroutine neutron_balance() use constants, only: ONE, ZERO, CMFD_NOACCEL, CMFD_NORES - use global, only: cmfd, keff + use global, only: cmfd, keff, current_batch integer :: nx ! number of mesh cells in x direction integer :: ny ! number of mesh cells in y direction @@ -385,15 +382,13 @@ contains integer :: g ! iteration counter for g integer :: h ! iteration counter for outgoing groups integer :: l ! iteration counter for leakage - integer :: uid + integer :: cnt ! number of locations to count neutron balance real(8) :: leakage ! leakage term in neutron balance real(8) :: interactions ! total number of interactions in balance real(8) :: scattering ! scattering term in neutron balance real(8) :: fission ! fission term in neutron balance real(8) :: res ! residual of neutron balance - - ! Check if keff is close to 0 (happens on first active batch) - if (keff < 1e-8_8) return + real(8) :: rms ! RMS of the residual ! Extract spatial and energy indices from object nx = cmfd % indices(1) @@ -404,6 +399,10 @@ contains ! Allocate res dataspace if (.not. allocated(cmfd%resnb)) allocate(cmfd%resnb(ng,nx,ny,nz)) + ! Reset rms and cnt + rms = ZERO + cnt = 0 + ! Begin loop around space and energy groups ZLOOP: do k = 1, nz @@ -457,16 +456,9 @@ contains ! Bank res in cmfd object cmfd%resnb(g,i,j,k) = res - ! Write out info to file - write(uid,'(A,1X,I0,1X,I0,1X,I0,1X,A,1X,I0,1X,A,1PE11.4)') & - 'Location',i,j,k,' Group:',g,'Balance:',res - write(uid,100) 'Leakage:',leakage - write(uid,100) 'Interactions:',interactions - write(uid,100) 'Scattering:',scattering - write(uid,100) 'Fission:',fission - write(uid,100) 'k-eff:',keff - write(uid,100) 'k-eff balanced:',fission/(leakage+interactions-scattering) - write(uid,100) 'Balance:',keff - fission/(leakage+interactions-scattering) + ! Take square for RMS calculation + rms = rms + res**2 + cnt = cnt + 1 end do GROUPG @@ -476,7 +468,8 @@ contains end do ZLOOP - 100 FORMAT(A,1X,1PE11.4) + ! Calculate RMS and record in vector for this batch + cmfd % balance(current_batch) = sqrt(ONE/dble(cnt)*rms) end subroutine neutron_balance diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 60452c8850..a514b0cd8b 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -132,7 +132,8 @@ contains subroutine calc_fission_source() use constants, only: CMFD_NOACCEL, ZERO, TWO - use global, only: cmfd, cmfd_coremap, master, mpi_err, entropy_on + use global, only: cmfd, cmfd_coremap, master, mpi_err, entropy_on, & + current_batch #ifdef MPI use mpi #endif @@ -223,7 +224,7 @@ contains end where ! Sum that source - cmfd % entropy = -sum(source) + cmfd % entropy(current_batch) = -sum(source) ! Deallocate tmp array if (allocated(source)) deallocate(source) @@ -233,6 +234,10 @@ contains ! Normalize source so average is 1.0 cmfd % cmfd_src = cmfd % cmfd_src/sum(cmfd % cmfd_src)*cmfd % norm + ! Calculate differences between normalized sources + cmfd % src_cmp(current_batch) = sqrt(ONE/cmfd % norm * & + sum((cmfd % cmfd_src - cmfd % openmc_src)**2)) + end if ! Broadcast full source to all procs diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 index 027172ee27..3d6b3fd61a 100644 --- a/src/cmfd_header.F90 +++ b/src/cmfd_header.F90 @@ -69,7 +69,16 @@ module cmfd_header real(8) :: norm = ONE ! "Shannon entropy" from cmfd fission source - real(8) :: entropy + real(8), allocatable :: entropy(:) + + ! RMS of neutron balance equations + real(8), allocatable :: balance(:) + + ! RMS deviation of OpenMC and CMFD normalized source + real(8), allocatable :: src_cmp(:) + + ! Dominance ratio + real(8), allocatable :: dom(:) end type cmfd_type @@ -79,9 +88,11 @@ contains ! ALLOCATE_CMFD allocates all data in of cmfd type !============================================================================== - subroutine allocate_cmfd(this) + subroutine allocate_cmfd(this, n_batches, entropy_on) - type(cmfd_type) :: this + integer :: n_batches + logical :: entropy_on + type(cmfd_type) :: this integer :: nx ! number of mesh cells in x direction integer :: ny ! number of mesh cells in y direction @@ -120,6 +131,12 @@ contains if (.not. allocated(this % sourcecounts)) allocate(this % sourcecounts(ng,nx,ny,nz)) if (.not. allocated(this % weightfactors)) allocate(this % weightfactors(ng,nx,ny,nz)) + ! Allocate batchwise parameters + if (.not. allocated(this % entropy) .and. entropy_on) allocate(this % entropy(n_batches)) + if (.not. allocated(this % balance)) allocate(this % balance(n_batches)) + if (.not. allocated(this % src_cmp)) allocate(this % src_cmp(n_batches)) + if (.not. allocated(this % dom)) allocate(this % dom(n_batches)) + ! Set everthing to 0 except weight multiply factors if feedback isnt on this % flux = ZERO this % totalxs = ZERO @@ -135,6 +152,10 @@ contains this % openmc_src = ZERO this % sourcecounts = ZERO this % weightfactors = ONE + this % balance = ZERO + this % src_cmp = ZERO + this % dom = ZERO + if (entropy_on) this % entropy = ZERO end subroutine allocate_cmfd @@ -164,6 +185,10 @@ contains if (allocated(this % weightfactors)) deallocate(this % weightfactors) if (allocated(this % cmfd_src)) deallocate(this % cmfd_src) if (allocated(this % openmc_src)) deallocate(this % openmc_src) + if (allocated(this % balance)) deallocate(this % balance) + if (allocated(this % src_cmp)) deallocate(this % src_cmp) + if (allocated(this % dom)) deallocate(this % dom) + if (allocated(this % entropy)) deallocate(this % entropy) end subroutine deallocate_cmfd diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index b8e90cb262..d14c65ee93 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -18,6 +18,8 @@ contains subroutine configure_cmfd() + use cmfd_header, only: allocate_cmfd + integer :: new_comm ! new mpi communicator integer :: color ! color group of processor @@ -44,11 +46,14 @@ contains call PetscInitialize(PETSC_NULL_CHARACTER,mpi_err) # endif - ! initialize timers + ! Initialize timers call time_cmfd % reset() call time_cmfdbuild % reset() call time_cmfdsolve % reset() + ! Allocate cmfd object + call allocate_cmfd(cmfd, n_batches, entropy_on) + end subroutine configure_cmfd !=============================================================================== @@ -162,10 +167,7 @@ contains cmfd_power_monitor = .true. ! Output logicals - call lower_case(write_balance_) call lower_case(write_matrices_) - if (write_balance_ == 'true' .or. write_balance_ == '1') & - cmfd_write_balance = .true. if (write_matrices_ == 'true' .or. write_matrices_ == '1') & cmfd_write_matrices = .true. @@ -188,6 +190,9 @@ contains ! Last flush before active batches cmfd_act_flush = active_flush_ + ! Get display + cmfd_display = display_ + ! Create tally objects call create_cmfd_tally() diff --git a/src/cmfd_output.F90 b/src/cmfd_output.F90 deleted file mode 100644 index eb2bf69f93..0000000000 --- a/src/cmfd_output.F90 +++ /dev/null @@ -1,59 +0,0 @@ -module cmfd_output - -! This modules cleans up cmfd objects and outputs neutron balance info - - implicit none - private - public :: finalize_cmfd - -contains - -!=============================================================================== -! FINALIZE_CMFD finalizes PETSc and frees memory associated with CMFD -!=============================================================================== - - subroutine finalize_cmfd() - - use global, only: cmfd, cmfd_write_balance, & - master, mpi_err - use cmfd_header, only: deallocate_cmfd - - ! Finalize PETSc -#ifdef PETSC - call PetscFinalize(mpi_err) -# endif - - ! Write out final neutron balance - if (master .and. cmfd_write_balance) call write_neutron_balance() - - ! Deallocate cmfd object - call deallocate_cmfd(cmfd) - - end subroutine finalize_cmfd - -!=============================================================================== -! WRITE_NEUTRON_BALANCE writes an ASCII neutron balance file out -!=============================================================================== - - subroutine write_neutron_balance() - - use cmfd_data, only: neutron_balance - use constants, only: CMFD_BALANCE, MAX_FILE_LEN - use global, only: path_output - - character(MAX_FILE_LEN) :: filename - - filename = trim(path_output) // 'neutron_balance.out' - - ! Open file for output - open(UNIT=CMFD_BALANCE, FILE=filename, ACTION='write') - - ! Write out the tally - call neutron_balance(CMFD_BALANCE) - - ! Close file - close(CMFD_BALANCE) - - end subroutine write_neutron_balance - -end module cmfd_output diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 5c8333cbc1..1d96ea2944 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -17,7 +17,9 @@ module cmfd_power_solver real(8) :: k_o ! old k-eigenvalue real(8) :: ktol = 1.e-8_8 ! tolerance on keff real(8) :: stol = 1.e-8_8 ! tolerance on source - logical :: adjoint_calc = .false. ! run an adjoint calculation + real(8) :: norm_n ! current norm of source vector + real(8) :: norm_o ! old norm of source vector + logical :: adjoint_calc ! run an adjoint calculation type(Matrix) :: loss ! cmfd loss matrix type(Matrix) :: prod ! cmfd prod matrix type(Vector) :: phi_n ! new flux vector @@ -47,6 +49,7 @@ contains if (present(s_tol)) stol = s_tol ! Check for adjoint execution + adjoint_calc = .false. if (present(adjoint)) adjoint_calc = adjoint ! Check for physical adjoint @@ -91,7 +94,7 @@ contains subroutine init_data(adjoint) - use constants, only: ONE + use constants, only: ONE, ZERO logical :: adjoint @@ -136,6 +139,10 @@ contains call S_o % setup_petsc() call S_n % setup_petsc() + ! Set norms to 0 + norm_n = ZERO + norm_o = ZERO + end subroutine init_data !=============================================================================== @@ -194,12 +201,13 @@ contains ! Check convergence call convergence(i) - ! To break or not to break + ! Break loop if converged if (iconv) exit ! Record old values phi_o % val = phi_n % val k_o = k_n + norm_o = norm_n end do @@ -232,6 +240,9 @@ contains ! Check for convergence if(kerr < ktol .and. serr < stol) iconv = .true. + ! Save the L2 norm of the source + norm_n = serr + ! Print out to user if (cmfd_power_monitor .and. master) then write(OUTPUT_UNIT,FMT='(I0,":",T10,"k-eff: ",F0.8,T30,"k-error: ", & @@ -246,7 +257,7 @@ contains subroutine extract_results() - use global, only: cmfd, cmfd_write_matrices + use global, only: cmfd, cmfd_write_matrices, current_batch character(len=25) :: filename ! name of file to write data integer :: n ! problem size @@ -282,6 +293,9 @@ contains cmfd%phi = cmfd%phi/sqrt(sum(cmfd%phi*cmfd%phi)) end if + ! Save dominance ratio + cmfd % dom(current_batch) = norm_n/norm_o + ! Write out results if (cmfd_write_matrices) then if (adjoint_calc) then diff --git a/src/constants.F90 b/src/constants.F90 index 6734a7bcd7..1a4080f622 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -363,9 +363,8 @@ module constants integer, parameter :: UNIT_TALLY = 12 ! unit # for writing tally file integer, parameter :: UNIT_PLOT = 13 ! unit # for writing plot file integer, parameter :: UNIT_XS = 14 ! unit # for writing xs summary file - 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_PARTICLE = 15 ! unit # for writing particle restart + integer, parameter :: UNIT_OUTPUT = 16 ! unit # for writing output !============================================================================= ! CMFD CONSTANTS diff --git a/src/finalize.F90 b/src/finalize.F90 index ac64e90fba..540726d447 100644 --- a/src/finalize.F90 +++ b/src/finalize.F90 @@ -1,8 +1,5 @@ module finalize -# ifdef PETSC - use cmfd_output, only: finalize_cmfd -# endif use global use output, only: print_runtime, print_results, & print_overlap_check, write_tallies @@ -42,11 +39,11 @@ contains end if #ifdef PETSC - ! finalize cmfd - if (cmfd_run) call finalize_cmfd() + ! Finalize PETSc + call PetscFinalize(mpi_err) #endif - ! stop timers and show timing statistics + ! Stop timers and show timing statistics call time_finalize % stop() call time_total % stop() if (master .and. (run_mode /= MODE_PLOTTING .and. & @@ -56,7 +53,7 @@ contains if (check_overlaps) call print_overlap_check() end if - ! deallocate arrays + ! Deallocate arrays call free_memory() #ifdef HDF5 diff --git a/src/global.F90 b/src/global.F90 index 14b2c066e1..9c130cd729 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -340,16 +340,18 @@ module global logical :: cmfd_power_monitor = .false. ! Cmfd output - logical :: cmfd_write_balance = .false. logical :: cmfd_write_matrices = .false. ! Run an adjoint calculation (last batch only) logical :: cmfd_run_adjoint = .false. - ! Cmfd run logicals + ! CMFD run logicals logical :: cmfd_on = .false. logical :: cmfd_tally_on = .true. + ! CMFD display info + character(len=25) :: cmfd_display + ! Information about state points to be written integer :: n_state_points = 0 type(SetInt) :: statepoint_batch @@ -413,7 +415,7 @@ contains if (allocated(source_bank)) deallocate(source_bank) if (allocated(entropy_p)) deallocate(entropy_p) - ! Deallocate cmfd + ! Deallocate CMFD call deallocate_cmfd(cmfd) ! Deallocate tally node lists diff --git a/src/output.F90 b/src/output.F90 index a4ce08fc3b..2b6b04511d 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1220,6 +1220,16 @@ contains write(UNIT=ou, FMT='(A20,3X)', ADVANCE='NO') " Average k " if (cmfd_run) then write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') " CMFD k " + select case(trim(cmfd_display)) + case('entropy') + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "CMFD Ent" + case('balance') + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "RMS Bal " + case('source') + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "RMS Src " + case('dominance') + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "Dom Rat " + end select end if write(UNIT=ou, FMT=*) @@ -1228,7 +1238,9 @@ contains if (entropy_on) write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" write(UNIT=ou, FMT='(A20,3X)', ADVANCE='NO') "====================" if (cmfd_run) then - write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "=========" + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" + if (cmfd_display /= '') & + write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" end if write(UNIT=ou, FMT=*) @@ -1285,9 +1297,25 @@ contains write(UNIT=OUTPUT_UNIT, FMT='(23X)', ADVANCE='NO') end if - ! write out cmfd keff if it is active - if (cmfd_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & + ! write out cmfd keff if it is active and other display info + if (cmfd_on) then + write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & cmfd % keff + select case(trim(cmfd_display)) + case('entropy') + write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & + cmfd % entropy(current_batch) + case('balance') + write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & + cmfd % balance(current_batch) + case('source') + write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & + cmfd % src_cmp(current_batch) + case('dominance') + write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & + cmfd % dom(current_batch) + end select + end if ! next line write(UNIT=OUTPUT_UNIT, FMT=*) diff --git a/src/templates/cmfd_t.xml b/src/templates/cmfd_t.xml index 631f2dd44d..0e49e0e5ab 100644 --- a/src/templates/cmfd_t.xml +++ b/src/templates/cmfd_t.xml @@ -17,13 +17,11 @@ - - @@ -31,5 +29,6 @@ + From d28a83cd690186e59d087746a50af27ccbf10f4b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Jul 2013 15:37:24 -0400 Subject: [PATCH 045/192] GNU compiler has runtime error with the allocatable pointer sometimes, therefore now using an allocatable array and the pointer references that --- src/vector_header.F90 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vector_header.F90 b/src/vector_header.F90 index a201dc9c21..b91f926b07 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -11,7 +11,8 @@ module vector_header type, public :: Vector integer :: n ! number of rows/cols in matrix - real(8), pointer :: val(:) ! matrix value vector + real(8), allocatable :: data(:) ! where vector data is stored + real(8), pointer :: val(:) ! pointer to vector data # ifdef PETSC Vec :: petsc_vec # endif @@ -35,10 +36,11 @@ contains subroutine vector_create(self, n) integer :: n - class(Vector) :: self + class(Vector), target :: self ! Preallocate vector - if (.not.associated(self % val)) allocate(self % val(n)) + if (.not.allocated(self % data)) allocate(self % data(n)) + self % val => self % data(1:n) ! Set n self % n = n @@ -63,7 +65,8 @@ contains if (self % petsc_active) call VecDestroy(self % petsc_vec, petsc_err) #endif - if (associated(self % val)) deallocate(self % val) + if (associated(self % val)) nullify(self % val) + if (allocated(self % data)) deallocate(self % data) end subroutine vector_destroy From 547b71fbfaf43ce24ba89315c2467cdafb3a78c4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Jul 2013 15:38:07 -0400 Subject: [PATCH 046/192] add warning if user tries to get dominance ratio information and not using power iteration --- src/cmfd_input.F90 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index d14c65ee93..45cac6b549 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -62,7 +62,7 @@ contains subroutine read_cmfd_xml() - use error, only: fatal_error + use error, only: fatal_error, warning use output, only: write_message use string, only: lower_case use xml_data_cmfd_t @@ -192,6 +192,12 @@ contains ! Get display cmfd_display = display_ + if (trim(cmfd_display) == 'dominance' .and. & + trim(cmfd_solver_type) /= 'power') then + message = 'Dominance Ratio only aviable with power iteration solver' + call warning() + cmfd_display = '' + end if ! Create tally objects call create_cmfd_tally() From 70030f788c9e7b0fb16999e99fc03f66510e5a41 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Jul 2013 16:44:35 -0400 Subject: [PATCH 047/192] matrix row and column properties only accessible through get_row and get_col now --- src/cmfd_jfnk_solver.F90 | 21 +++---------- src/matrix_header.F90 | 64 +++++++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 70593a97ef..c84c0321a4 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -167,7 +167,6 @@ contains integer :: jloss ! loop counter for loss matrix cols integer :: jprod ! loop counter for prod matrix cols integer :: n ! problem size - integer :: shift ! integer to account for index shift for Petsc real(8) :: lambda ! eigenvalue real(8) :: val ! temporary real scalar type(Vector) :: flux ! flux vector @@ -176,10 +175,6 @@ contains ! Get the problem size n = loss % n - ! Check to use shift - shift = 0 - if (loss % petsc_active) shift = 1 - ! Get flux and eigenvalue flux % val => x % val(1:n) lambda = x % val(n + 1) @@ -199,18 +194,16 @@ contains call jac_prec % new_row() ! Begin loop around columns of loss matrix - COLS_LOSS: do jloss = loss % row(i) + shift, & - loss % row(i + 1) - 1 + shift + COLS_LOSS: do jloss = loss % get_row(i), loss % get_row(i + 1) - 1 ! Start with the value in the loss matrix val = loss % val(jloss) ! Loop around columns of prod matrix - COLS_PROD: do jprod = prod % row(i) + shift, & - prod % row(i + 1) - 1 + shift + COLS_PROD: do jprod = prod % get_row(i), prod % get_row(i + 1) - 1 ! See if columns agree with loss matrix - if (prod % col(jprod) == loss % col(jloss)) then + if (prod % get_col(jprod) == loss % get_col(jloss)) then val = val - lambda*prod % val(jprod) exit end if @@ -218,7 +211,7 @@ contains end do COLS_PROD ! Record value in jacobian - call jac_prec % add_value(loss % col(jloss) + shift, val) + call jac_prec % add_value(loss % get_col(jloss), val) end do COLS_LOSS @@ -241,12 +234,6 @@ contains ! CRS requires a final value in row call jac_prec % new_row() - ! If the Jacobian is already associated with PETSc need to reshift row/col - if (jac_prec % petsc_active) then - jac_prec % row = jac_prec % row - 1 - jac_prec % col = jac_prec % col - 1 - end if - ! Free all allocated memory flux % val => null() call fsrc % destroy() diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index d85951d527..5b7aa7f725 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -12,8 +12,8 @@ module matrix_header integer :: nnz ! number of nonzeros in matrix integer :: n_kount ! counter for length of matrix integer :: nz_kount ! counter for number of non zeros - integer, allocatable :: row(:) ! csr row vector - integer, allocatable :: col(:) ! column vector + integer, private, allocatable :: row(:) ! csr row vector + integer, private, allocatable :: col(:) ! column vector real(8), allocatable :: val(:) ! matrix value vector # ifdef PETSC Mat :: petsc_mat @@ -25,6 +25,8 @@ module matrix_header procedure :: add_value => matrix_add_value procedure :: new_row => matrix_new_row procedure :: assemble => matrix_assemble + procedure :: get_row => matrix_get_row + procedure :: get_col => matrix_get_col procedure :: setup_petsc => matrix_setup_petsc procedure :: write_petsc_binary => matrix_write_petsc_binary procedure :: transpose => matrix_transpose @@ -91,8 +93,15 @@ contains real(8) :: val class(Matrix) :: self + ! Record the data self % col(self % nz_kount) = col self % val(self % nz_kount) = val + + ! Need to adjust column indices if PETSc is active + if (self % petsc_active) self % col(self % nz_kount) = & + self % col(self % nz_kount) - 1 + + ! Increment the number of nonzeros currently stored self % nz_kount = self % nz_kount + 1 end subroutine matrix_add_value @@ -105,7 +114,14 @@ contains class(Matrix) :: self + ! Record the current number of nonzeros self % row(self % n_kount) = self % nz_kount + + ! If PETSc is active, we have to reference indices off 0 + if (self % petsc_active) self % row(self % n_kount) = & + self % row(self % n_kount) - 1 + + ! Increment the current row that we are on self % n_kount = self % n_kount + 1 end subroutine matrix_new_row @@ -215,6 +231,38 @@ contains end subroutine split +!=============================================================================== +! MATRIX_GET_ROW +!=============================================================================== + + function matrix_get_row(self, i) result(row) + + class(Matrix) :: self + integer :: i + integer :: row + + row = self % row(i) + + if (self % petsc_active) row = row + 1 + + end function matrix_get_row + +!=============================================================================== +! MATRIX_GET_COL +!=============================================================================== + + function matrix_get_col(self, i) result(col) + + class(Matrix) :: self + integer :: i + integer :: col + + col = self % col(i) + + if (self % petsc_active) col = col + 1 + + end function matrix_get_col + !=============================================================================== ! MATRIX_SETUP_PETSC configures the row/col vectors and links to a PETSc object !=============================================================================== @@ -288,12 +336,6 @@ contains integer :: i integer :: j - integer :: shift - - ! Set shift by default 0 and change if PETSc is active - ! This is because PETSc needs vectors to remain referenced to 0 - shift = 0 - if (self % petsc_active) shift = 1 ! Begin loop around rows ROWS: do i = 1, self % n @@ -301,11 +343,11 @@ contains ! Initialize target location in vector vec_out % val(i) = ZERO - ! Begin loop around columns (need to shift if PETSc is active) - COLS: do j = self % row(i) + shift, self % row(i + 1) - 1 + shift + ! Begin loop around columns + COLS: do j = self % get_row(i), self % get_row(i + 1) - 1 vec_out % val(i) = vec_out % val(i) + self % val(j) * & - vec_in % val(self % col(j) + shift) + vec_in % val(self % get_col(j)) end do COLS From 3ae4e46cb8cef003645064053c93292d27611688 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Jul 2013 16:49:12 -0400 Subject: [PATCH 048/192] added CMFD timers to JFNK solver --- src/cmfd_jfnk_solver.F90 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index c84c0321a4..c4b0688917 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -28,6 +28,8 @@ contains subroutine cmfd_jfnk_execute(adjoint) + use global, only: time_cmfdbuild, time_cmfdsolve + logical, optional :: adjoint ! adjoint calculation ! Check for adjoint @@ -37,6 +39,9 @@ contains ! Seed with power iteration to help converge to fundamental mode call cmfd_power_execute(k_tol=1.E-3_8, s_tol=1.E-3_8, adjoint=adjoint_calc) + ! Start timer for build + call time_cmfdbuild % start() + ! Initialize data call init_data() @@ -48,8 +53,13 @@ contains jfnk_data % jac_proc_ptr => build_jacobian_matrix call jfnk % set_functions(jfnk_data, resvec, jac_prec) + ! Stop timer for build + call time_cmfdbuild % stop() + ! Solve the system + call time_cmfdsolve % start() call jfnk % solve(xvec) + call time_cmfdsolve % stop() ! Extracts results to cmfd object call extract_results() From 75e583ec07cf8bc0806fb5637451d0a4df29319f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 17 Jul 2013 15:34:13 -0400 Subject: [PATCH 049/192] Petsc finalizes only when it is a cmfd run --- src/finalize.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/finalize.F90 b/src/finalize.F90 index 540726d447..dbc4099c92 100644 --- a/src/finalize.F90 +++ b/src/finalize.F90 @@ -40,7 +40,7 @@ contains #ifdef PETSC ! Finalize PETSc - call PetscFinalize(mpi_err) + if (cmfd_run) call PetscFinalize(mpi_err) #endif ! Stop timers and show timing statistics From af38bb0100ab7d4a46e9c0c5896e47ea221fcf8d Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 19 Jul 2013 13:58:55 -0400 Subject: [PATCH 050/192] changed kount to count --- src/cmfd_data.F90 | 24 ++++++++++++------------ src/cmfd_jfnk_solver.F90 | 4 ++-- src/matrix_header.F90 | 26 +++++++++++++------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index ef971f47e4..11b49e88ad 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -311,13 +311,13 @@ contains use constants, only: CMFD_NOACCEL use global, only: cmfd - integer :: kount=1 ! counter for unique fuel assemblies - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z + integer :: counter=1 ! counter for unique fuel assemblies + integer :: nx ! number of mesh cells in x direction + integer :: ny ! number of mesh cells in y direction + integer :: nz ! number of mesh cells in z direction + integer :: i ! iteration counter for x + integer :: j ! iteration counter for y + integer :: k ! iteration counter for z ! Extract spatial indices from object nx = cmfd % indices(1) @@ -347,11 +347,11 @@ contains else ! Must be a fuel --> give unique id number - cmfd % coremap(i,j,k) = kount - cmfd % indexmap(kount,1) = i - cmfd % indexmap(kount,2) = j - cmfd % indexmap(kount,3) = k - kount = kount + 1 + cmfd % coremap(i,j,k) = counter + cmfd % indexmap(counter,1) = i + cmfd % indexmap(counter,2) = j + cmfd % indexmap(counter,3) = k + counter = counter + 1 end if diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index c4b0688917..04f28ac426 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -194,8 +194,8 @@ contains call prod % vector_multiply(flux, fsrc) ! Reset counters in Jacobian matrix - jac_prec % n_kount = 1 - jac_prec % nz_kount = 1 + jac_prec % n_count = 1 + jac_prec % nz_count = 1 ! Begin loop around rows of Jacobian matrix ROWS: do i = 1, n diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index 5b7aa7f725..fe5c2c5bcf 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -10,8 +10,8 @@ module matrix_header type, public :: Matrix integer :: n ! number of rows/cols in matrix integer :: nnz ! number of nonzeros in matrix - integer :: n_kount ! counter for length of matrix - integer :: nz_kount ! counter for number of non zeros + integer :: n_count ! counter for length of matrix + integer :: nz_count ! counter for number of non zeros integer, private, allocatable :: row(:) ! csr row vector integer, private, allocatable :: col(:) ! column vector real(8), allocatable :: val(:) ! matrix value vector @@ -53,8 +53,8 @@ contains if (.not.allocated(self % val)) allocate(self % val(nnz)) ! Set counters to 1 - self % n_kount = 1 - self % nz_kount = 1 + self % n_count = 1 + self % nz_count = 1 ! Set n and nnz self % n = n @@ -94,15 +94,15 @@ contains class(Matrix) :: self ! Record the data - self % col(self % nz_kount) = col - self % val(self % nz_kount) = val + self % col(self % nz_count) = col + self % val(self % nz_count) = val ! Need to adjust column indices if PETSc is active - if (self % petsc_active) self % col(self % nz_kount) = & - self % col(self % nz_kount) - 1 + if (self % petsc_active) self % col(self % nz_count) = & + self % col(self % nz_count) - 1 ! Increment the number of nonzeros currently stored - self % nz_kount = self % nz_kount + 1 + self % nz_count = self % nz_count + 1 end subroutine matrix_add_value @@ -115,14 +115,14 @@ contains class(Matrix) :: self ! Record the current number of nonzeros - self % row(self % n_kount) = self % nz_kount + self % row(self % n_count) = self % nz_count ! If PETSc is active, we have to reference indices off 0 - if (self % petsc_active) self % row(self % n_kount) = & - self % row(self % n_kount) - 1 + if (self % petsc_active) self % row(self % n_count) = & + self % row(self % n_count) - 1 ! Increment the current row that we are on - self % n_kount = self % n_kount + 1 + self % n_count = self % n_count + 1 end subroutine matrix_new_row From 38f52a5db9b5c750cab77f5ac568aa471f90ea05 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 10:42:42 -0400 Subject: [PATCH 051/192] added FoX XML API in folder xml --- src/Makefile | 17 +- src/xml/LICENSE | 71 + src/xml/README | 3 + src/xml/common/FoX_common.F90 | 55 + src/xml/common/m_common_attrs.F90 | 1081 ++ src/xml/common/m_common_buffer.F90 | 261 + src/xml/common/m_common_charset.F90 | 447 + src/xml/common/m_common_content_model.F90 | 490 + src/xml/common/m_common_element.F90 | 1645 +++ src/xml/common/m_common_elstack.F90 | 236 + src/xml/common/m_common_entities.F90 | 475 + src/xml/common/m_common_entity_expand.F90 | 86 + src/xml/common/m_common_error.F90 | 211 + src/xml/common/m_common_io.F90 | 102 + src/xml/common/m_common_namecheck.F90 | 501 + src/xml/common/m_common_namespaces.F90 | 830 ++ src/xml/common/m_common_notations.F90 | 120 + src/xml/common/m_common_struct.F90 | 121 + src/xml/common/makefile | 58 + src/xml/dom/FoX_dom.F90 | 261 + src/xml/dom/m_dom_dom.F90 | 12321 ++++++++++++++++ src/xml/dom/m_dom_error.F90 | 211 + src/xml/dom/m_dom_extras.F90 | 2376 +++ src/xml/dom/m_dom_parse.F90 | 577 + src/xml/dom/m_dom_utils.F90 | 433 + src/xml/dom/makefile | 48 + src/xml/fsys/fox_m_fsys_abort_flush.F90 | 119 + src/xml/fsys/fox_m_fsys_array_str.F90 | 90 + src/xml/fsys/fox_m_fsys_count_parse_input.F90 | 561 + src/xml/fsys/fox_m_fsys_format.F90 | 2250 +++ src/xml/fsys/fox_m_fsys_parse_input.F90 | 2204 +++ src/xml/fsys/fox_m_fsys_realtypes.F90 | 12 + src/xml/fsys/fox_m_fsys_string.F90 | 35 + src/xml/fsys/fox_m_fsys_string_list.F90 | 191 + src/xml/fsys/m_ieee.F90 | 17 + src/xml/fsys/makefile | 47 + src/xml/sax/FoX_sax.F90 | 34 + src/xml/sax/m_sax_operate.F90 | 313 + src/xml/sax/m_sax_parser.F90 | 3027 ++++ src/xml/sax/m_sax_reader.F90 | 402 + src/xml/sax/m_sax_tokenizer.F90 | 1185 ++ src/xml/sax/m_sax_types.F90 | 161 + src/xml/sax/m_sax_xml_source.F90 | 438 + src/xml/sax/makefile | 49 + src/xml/utils/FoX_utils.F90 | 22 + src/xml/utils/fox_m_utils_mtprng.F90 | 360 + src/xml/utils/fox_m_utils_uri.F90 | 1013 ++ src/xml/utils/fox_m_utils_uuid.F90 | 247 + src/xml/utils/makefile | 45 + src/xml/wxml/FoX_wxml.F90 | 43 + src/xml/wxml/m_wxml_core.F90 | 1848 +++ src/xml/wxml/m_wxml_escape.F90 | 140 + src/xml/wxml/m_wxml_overloads.F90 | 1028 ++ src/xml/wxml/makefile | 46 + 54 files changed, 38962 insertions(+), 2 deletions(-) create mode 100644 src/xml/LICENSE create mode 100644 src/xml/README create mode 100644 src/xml/common/FoX_common.F90 create mode 100644 src/xml/common/m_common_attrs.F90 create mode 100644 src/xml/common/m_common_buffer.F90 create mode 100644 src/xml/common/m_common_charset.F90 create mode 100644 src/xml/common/m_common_content_model.F90 create mode 100644 src/xml/common/m_common_element.F90 create mode 100644 src/xml/common/m_common_elstack.F90 create mode 100644 src/xml/common/m_common_entities.F90 create mode 100644 src/xml/common/m_common_entity_expand.F90 create mode 100644 src/xml/common/m_common_error.F90 create mode 100644 src/xml/common/m_common_io.F90 create mode 100644 src/xml/common/m_common_namecheck.F90 create mode 100644 src/xml/common/m_common_namespaces.F90 create mode 100644 src/xml/common/m_common_notations.F90 create mode 100644 src/xml/common/m_common_struct.F90 create mode 100644 src/xml/common/makefile create mode 100644 src/xml/dom/FoX_dom.F90 create mode 100644 src/xml/dom/m_dom_dom.F90 create mode 100644 src/xml/dom/m_dom_error.F90 create mode 100644 src/xml/dom/m_dom_extras.F90 create mode 100644 src/xml/dom/m_dom_parse.F90 create mode 100644 src/xml/dom/m_dom_utils.F90 create mode 100644 src/xml/dom/makefile create mode 100644 src/xml/fsys/fox_m_fsys_abort_flush.F90 create mode 100644 src/xml/fsys/fox_m_fsys_array_str.F90 create mode 100644 src/xml/fsys/fox_m_fsys_count_parse_input.F90 create mode 100644 src/xml/fsys/fox_m_fsys_format.F90 create mode 100644 src/xml/fsys/fox_m_fsys_parse_input.F90 create mode 100644 src/xml/fsys/fox_m_fsys_realtypes.F90 create mode 100644 src/xml/fsys/fox_m_fsys_string.F90 create mode 100644 src/xml/fsys/fox_m_fsys_string_list.F90 create mode 100644 src/xml/fsys/m_ieee.F90 create mode 100644 src/xml/fsys/makefile create mode 100644 src/xml/sax/FoX_sax.F90 create mode 100644 src/xml/sax/m_sax_operate.F90 create mode 100644 src/xml/sax/m_sax_parser.F90 create mode 100644 src/xml/sax/m_sax_reader.F90 create mode 100644 src/xml/sax/m_sax_tokenizer.F90 create mode 100644 src/xml/sax/m_sax_types.F90 create mode 100644 src/xml/sax/m_sax_xml_source.F90 create mode 100644 src/xml/sax/makefile create mode 100644 src/xml/utils/FoX_utils.F90 create mode 100644 src/xml/utils/fox_m_utils_mtprng.F90 create mode 100644 src/xml/utils/fox_m_utils_uri.F90 create mode 100644 src/xml/utils/fox_m_utils_uuid.F90 create mode 100644 src/xml/utils/makefile create mode 100644 src/xml/wxml/FoX_wxml.F90 create mode 100644 src/xml/wxml/m_wxml_core.F90 create mode 100644 src/xml/wxml/m_wxml_escape.F90 create mode 100644 src/xml/wxml/m_wxml_overloads.F90 create mode 100644 src/xml/wxml/makefile diff --git a/src/Makefile b/src/Makefile index 05cb7df6e8..9f0cf555d5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -227,7 +227,14 @@ endif # Targets #=============================================================================== -all: xml-fortran $(program) +all: xml xml-fortran $(program) +xml: + cd xml; cd fsys; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd utils; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd common; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd wxml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd sax; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd dom; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" xml-fortran: cd xml-fortran; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" cd templates; make F90=$(F90) F90FLAGS="$(F90FLAGS)" @@ -248,6 +255,12 @@ uninstall: @rm $(DESTDIR)$(prefix)/share/man/man1/openmc.1 @rm $(DESTDIR)$(prefix)/share/doc/$(program)/copyright distclean: clean + cd xml; cd fsys; make clean + cd xml; cd utils; make clean + cd xml; cd common; make clean + cd xml; cd wxml; make clean + cd xml; cd sax; make clean + cd xml; cd dom; make clean cd xml-fortran; make clean cd templates; make clean clean: @@ -260,7 +273,7 @@ neat: #=============================================================================== .SUFFIXES: .F90 .o -.PHONY: all xml-fortran install uninstall clean neat distclean +.PHONY: all xml xml-fortran install uninstall clean neat distclean %.o: %.F90 $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml-fortran -Itemplates -c $< diff --git a/src/xml/LICENSE b/src/xml/LICENSE new file mode 100644 index 0000000000..53df0326a6 --- /dev/null +++ b/src/xml/LICENSE @@ -0,0 +1,71 @@ +FoX - Fortran XML library + +FoX was originally derived from the xmlf90 codebase, +(c) Alberto Garcia & Jon Wakelin, 2003-2004. + +FoX also includes externally-written code from +Scott Ladd , which is licensed +as shown in the file utils/fox_m_utils_mtprng.f90 + +This version of FoX is: +(c) 2005-2009 Toby White +(c) 2007-2009 Gen-Tao Chiang +(c) 2008-2012 Andrew Walker + +All rights reserved. + +* Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This distribution also includes code wrtten by Scott Ladd +utils/, + +! This computer program source file is supplied "AS IS". Scott Robert
+! Ladd (hereinafter referred to as "Author") disclaims all warranties,
+! expressed or implied, including, without limitation, the warranties
+! of merchantability and of fitness for any purpose. The Author
+! assumes no liability for direct, indirect, incidental, special,
+! exemplary, or consequential damages, which may result from the use
+! of this software, even if advised of the possibility of such damage.
+!
+! The Author hereby grants anyone permission to use, copy, modify, and
+! distribute this source code, or portions hereof, for any purpose,
+! without fee, subject to the following restrictions:
+!
+! 1. The origin of this source code must not be misrepresented.
+!
+! 2. Altered versions must be plainly marked as such and must not
+! be misrepresented as being the original source.
+!
+! 3. This Copyright notice may not be removed or altered from any
+! source or altered source distribution.
+!
+! The Author specifically permits (without fee) and encourages the use
+! of this source code for entertainment, education, or decoration. If
+! you use this source code in a product, acknowledgment is not required
+! but would be appreciated. diff --git a/src/xml/README b/src/xml/README new file mode 100644 index 0000000000..5707d45244 --- /dev/null +++ b/src/xml/README @@ -0,0 +1,3 @@ +This directory contains source code from FoX - A Fortran Library for XML +Current version is 4.1.2 +www1.gly.bris.ac.uk/~walker/FoX/ diff --git a/src/xml/common/FoX_common.F90 b/src/xml/common/FoX_common.F90 new file mode 100644 index 0000000000..eaa7e684f9 --- /dev/null +++ b/src/xml/common/FoX_common.F90 @@ -0,0 +1,55 @@ +module FoX_common + + use fox_m_fsys_array_str + use fox_m_fsys_format + use fox_m_fsys_parse_input + use fox_m_fsys_count_parse_input + use m_common_attrs + use m_common_error + + implicit none + private + +#ifdef DUMMYLIB + character(len=*), parameter :: FoX_version = '4.1.2-dummy' +#else + character(len=*), parameter :: FoX_version = '4.1.2' +#endif + + public :: FoX_version + + public :: rts + public :: countrts + public :: str + public :: operator(//) + + public :: FoX_set_fatal_errors + public :: FoX_get_fatal_errors + public :: FoX_set_fatal_warnings + public :: FoX_get_fatal_warnings + +#ifndef DUMMYLIB + public :: str_vs + public :: vs_str + public :: alloc + public :: concat +#endif + +!These are all exported through SAX now + public :: dictionary_t +!SAX functions + public :: getIndex + public :: getLength + public :: getLocalName + public :: getQName + public :: getURI + public :: getValue + public :: getType + public :: isSpecified + public :: isDeclared + public :: setSpecified + public :: setDeclared +!For convenience + public :: hasKey + +end module FoX_common diff --git a/src/xml/common/m_common_attrs.F90 b/src/xml/common/m_common_attrs.F90 new file mode 100644 index 0000000000..33a2cdb482 --- /dev/null +++ b/src/xml/common/m_common_attrs.F90 @@ -0,0 +1,1081 @@ +module m_common_attrs + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only : str_vs, vs_str_alloc + use m_common_element, only: get_att_type_enum, ATT_CDATA, ATT_CDAMB, ATT_TYPES + use m_common_error, only : FoX_error, FoX_fatal + + implicit none + private + + !Initial length of dictionary + integer, parameter :: DICT_INIT_LEN = 10 + !Multiplier if we need to extend it. + real, parameter :: DICT_LEN_MULT = 1.5 + + type dict_item + character(len=1), pointer, dimension(:) :: nsURI => null() + character(len=1), pointer, dimension(:) :: localName => null() + character(len=1), pointer, dimension(:) :: prefix => null() + character(len=1), pointer, dimension(:) :: key => null() + character(len=1), pointer, dimension(:) :: value => null() + logical :: specified = .true. + logical :: declared = .false. + logical :: isId = .false. + integer :: type = 11 + end type dict_item + + type dict_item_ptr + type(dict_item), pointer :: d => null() + end type dict_item_ptr +#endif + + type dictionary_t + private +#ifndef DUMMYLIB + type(dict_item_ptr), dimension(:), pointer :: list => null() + character, dimension(:), pointer :: base => null() +#else + integer :: i +#endif + end type dictionary_t + + public :: dictionary_t + + ! Building procedures +#ifndef DUMMYLIB + public :: init_dict + public :: reset_dict + public :: add_item_to_dict + public :: destroy_dict +#endif + ! Query and extraction procedures + + ! SAX names: + public :: getIndex + public :: getLength + public :: getLocalName + public :: getQName + public :: getURI + public :: getValue + public :: getType + public :: isSpecified + public :: setSpecified + public :: isDeclared + public :: setDeclared + public :: hasKey + +#ifndef DUMMYLIB + public :: len + public :: get_key + public :: get_value + public :: remove_key + public :: has_key + public :: print_dict + + ! Namespaces + public :: get_prefix + public :: get_localName + public :: set_nsURI + public :: set_prefix + public :: set_localName +#endif + + ! For internal FoX use only: + public :: get_att_index_pointer + public :: getWhitespaceHandling + public :: setIsId + public :: getIsId + + public :: setBase + public :: getBase + + public :: sortAttrs + + interface len + module procedure getLength + end interface + + interface hasKey + module procedure has_key, has_key_ns + end interface + + interface getIndex + module procedure get_key_index, get_key_index_ns + end interface + + interface getQName + module procedure get_key + end interface + + interface getValue + module procedure get_value_by_key, get_value_by_index, get_value_by_key_ns + end interface +#ifndef DUMMYLIB + interface get_value + module procedure get_value_by_key, get_value_by_index + end interface + interface remove_key + module procedure remove_key_by_index + end interface +#endif + + interface getURI + module procedure get_nsURI_by_index + end interface +#ifndef DUMMYLIB + interface get_prefix + module procedure get_prefix_by_index + end interface +#endif + interface getLocalName + module procedure get_localName_by_index + end interface +#ifndef DUMMYLIB + interface get_localName + module procedure get_localName_by_index + end interface + interface set_nsURI + module procedure set_nsURI_by_index + end interface + interface set_prefix + module procedure set_prefix_by_index + end interface + interface set_localName + module procedure set_localName_by_index_s + module procedure set_localName_by_index_vs + end interface +#endif + + interface getType + module procedure getType_by_index + module procedure getType_by_keyname + end interface + + interface isSpecified + module procedure isSpecified_by_index + module procedure isSpecified_by_key + module procedure isSpecified_by_keyNS + end interface + + interface isDeclared + module procedure isDeclared_by_index + module procedure isDeclared_by_key + module procedure isDeclared_by_keyNS + end interface + +#ifndef DUMMYLIB + interface getIsId + module procedure getIsId_by_index + end interface + + interface setIsId + module procedure setIsId_by_index + end interface + + interface destroy + module procedure destroy_dict_item + module procedure destroy_dict + end interface + +#endif + +contains + + pure function getLength(dict) result(n) + type(dictionary_t), intent(in) :: dict + integer :: n + +#ifndef DUMMYLIB + n = ubound(dict%list, 1) +#else + n = 0 +#endif + end function getLength + + + function has_key(dict, key) result(found) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key + logical :: found + +#ifndef DUMMYLIB + integer :: i + + do i = 1, ubound(dict%list, 1) + if (key==str_vs(dict%list(i)%d%key)) then + found = .true. + return + endif + enddo + found = .false. +#else + found = .false. +#endif + end function has_key + + function has_key_ns(dict, uri, localname) result(found) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: uri, localname + logical :: found + +#ifndef DUMMYLIB + integer :: i + found = .false. + do i = 1, ubound(dict%list, 1) + ! FIXME xlf 10.01 segfaults if the below is done as + ! an AND rather than two separate ifs. This is + ! probably due to the Heisenbug + if (uri==str_vs(dict%list(i)%d%nsURI)) then + if (localname==str_vs(dict%list(i)%d%localname)) then + found = .true. + exit + endif + endif + enddo +#else + found = .false. +#endif + end function has_key_ns + + pure function get_key_index(dict, key) result(ind) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key + integer :: ind + +#ifndef DUMMYLIB + integer :: i + do i = 1, ubound(dict%list, 1) + if (key == str_vs(dict%list(i)%d%key)) then + ind = i + return + endif + enddo + ind = 0 +#else + ind = 0 +#endif + end function get_key_index + + pure function get_key_index_ns(dict, uri, localname) result(ind) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: uri, localname + integer :: ind + +#ifndef DUMMYLIB + integer :: i + ind = -1 + do i = 1, ubound(dict%list, 1) + if (uri==str_vs(dict%list(i)%d%nsURI) & + .and. localname==str_vs(dict%list(i)%d%localname)) then + ind = i + exit + endif + enddo +#else + ind = 0 +#endif + end function get_key_index_ns + +#ifndef DUMMYLIB + pure function get_value_by_key_len(dict, key) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key + integer :: n + + integer :: i + + do i = 1, ubound(dict%list, 1) + if (key == str_vs(dict%list(i)%d%key)) then + n = size(dict%list(i)%d%value) + return + endif + enddo + n = 0 + end function get_value_by_key_len +#endif + + function get_value_by_key(dict, key) result(value) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key +#ifndef DUMMYLIB + character(len=get_value_by_key_len(dict,key)) :: value + + integer :: i + + do i = 1, ubound(dict%list, 1) + if (key == str_vs(dict%list(i)%d%key)) then + value = str_vs(dict%list(i)%d%value) + return + endif + enddo + value = "" +#else + character(len=1) :: value + value = "" +#endif + end function get_value_by_key + +#ifndef DUMMYLIB + pure function get_value_by_key_ns_len(dict, nsUri, localname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: nsUri + character(len=*), intent(in) :: localname + integer :: n + + integer :: i + + do i = 1, ubound(dict%list, 1) + if (nsUri==str_vs(dict%list(i)%d%nsURI) & + .and.localname==str_vs(dict%list(i)%d%localname)) then + n = size(dict%list(i)%d%value) + return + endif + enddo + n = 0 + end function get_value_by_key_ns_len +#endif + + function get_value_by_key_ns(dict, nsUri, localname) result(value) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: nsUri + character(len=*), intent(in) :: localname +#ifndef DUMMYLIB + character(len=get_value_by_key_ns_len(dict, nsURI, localname)) :: value + + integer :: i + + do i = 1, ubound(dict%list, 1) + if (nsUri==str_vs(dict%list(i)%d%nsURI) & + .and.localname==str_vs(dict%list(i)%d%localname)) then + value = str_vs(dict%list(i)%d%value) + return + endif + enddo + value = "" +#else + character(len=1) :: value + value = "" +#endif + end function get_value_by_key_ns + +#ifndef DUMMYLIB + subroutine get_att_index_pointer(dict, key, i, value) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key + integer, intent(out) :: i + character, pointer :: value(:) + + value => null() + do i = 1, ubound(dict%list, 1) + if (key == str_vs(dict%list(i)%d%key)) then + value => dict%list(i)%d%value + return + endif + enddo + i = 0 + end subroutine get_att_index_pointer + + subroutine remove_key_by_index(dict, ind) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: ind + + integer :: i, n + type(dict_item_ptr), pointer :: tempList(:) + + n = ubound(dict%list, 1) + + if (ind<=0.or.ind>n) return + + allocate(tempList(0:n-1)) + do i = 0, ind-1 + tempList(i)%d => dict%list(i)%d + enddo + call destroy(dict%list(ind)%d) + do i = ind+1, n + tempList(i-1)%d => dict%list(i)%d + enddo + deallocate(dict%list) + dict%list => tempList + end subroutine remove_key_by_index +#endif + + pure function get_value_by_index_len(dict, i) result(n) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + integer :: n + +#ifdef DUMMYLIB + n = 1 +#else + if (i>0.and.i<=ubound(dict%list, 1)) then + n = size(dict%list(i)%d%value) + else + n = 0 + endif +#endif + end function get_value_by_index_len + + function get_value_by_index(dict, i) result(value) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=get_value_by_index_len(dict, i)) :: value + + if (i>0.and.i<=ubound(dict%list, 1)) then + value = str_vs(dict%list(i)%d%value) + else + value = "" + endif +#else + character(len=1) :: value + value = "" +#endif + end function get_value_by_index + +#ifndef DUMMYLIB + pure function get_key_len(dict, i) result(n) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + integer :: n + + if (i>0.and.i<=ubound(dict%list, 1)) then + n = size(dict%list(i)%d%key) + else + n = 0 + endif + end function get_key_len +#endif + + function get_key(dict, i) result(key) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=get_key_len(dict,i)) :: key + + if (i>0.and.i<=ubound(dict%list, 1)) then + key = str_vs(dict%list(i)%d%key) + else + key = "" + endif +#else + character(len=1) :: key + key = "" +#endif + end function get_key + +#ifndef DUMMYLIB + subroutine add_item_to_dict(dict, key, value, prefix, nsURI, type, itype, specified, declared) + type(dictionary_t), intent(inout) :: dict + character(len=*), intent(in) :: key + character(len=*), intent(in) :: value + character(len=*), intent(in), optional :: prefix + character(len=*), intent(in), optional :: nsURI + character(len=*), intent(in), optional :: type + integer, intent(in), optional :: itype + logical, intent(in), optional :: specified + logical, intent(in), optional :: declared + + type(dict_item_ptr), pointer :: tempList(:) + integer :: i, n + + if (present(prefix) .eqv. .not.present(nsURI)) & + call FoX_Error('Namespace improperly specified') + + n = ubound(dict%list, 1) + allocate(tempList(0:n+1)) + do i = 0, n + tempList(i)%d => dict%list(i)%d + enddo + n = n + 1 + + allocate(tempList(n)%d) + tempList(n)%d%value => vs_str_alloc(value) + if (present(prefix)) then + tempList(n)%d%key => vs_str_alloc(prefix//":"//key) + tempList(n)%d%localname => vs_str_alloc(key) + tempList(n)%d%prefix => vs_str_alloc(prefix) + tempList(n)%d%nsURI => vs_str_alloc(nsURI) + else + tempList(n)%d%key => vs_str_alloc(key) + tempList(n)%d%localname => vs_str_alloc(key) + allocate(tempList(n)%d%prefix(0)) + allocate(tempList(n)%d%nsURI(0)) + endif + if (present(type)) then + if (present(itype)) & + call FoX_fatal("internal library error in add_item_to_dict") + tempList(n)%d%type = get_att_type_enum(type) + elseif (present(itype)) then + tempList(n)%d%type = itype + else + tempList(n)%d%type = ATT_CDAMB + endif + if (present(specified)) then + tempList(n)%d%specified = specified + else + tempList(n)%d%specified = .true. + endif + if (present(declared)) then + tempList(n)%d%declared = declared + else + tempList(n)%d%declared = .false. + endif + + deallocate(dict%list) + dict%list => tempList + + end subroutine add_item_to_dict + + subroutine set_nsURI_by_index(dict, i, nsURI) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + character(len=*), intent(in) :: nsURI + + if (associated(dict%list(i)%d%nsURI)) & + deallocate(dict%list(i)%d%nsURI) + dict%list(i)%d%nsURI => vs_str_alloc(nsURI) + end subroutine set_nsURI_by_index + + subroutine set_prefix_by_index(dict, i, prefix) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + character(len=*), intent(in) :: prefix + + if (associated(dict%list(i)%d%prefix)) & + deallocate(dict%list(i)%d%prefix) + dict%list(i)%d%prefix => vs_str_alloc(prefix) + end subroutine set_prefix_by_index + + subroutine set_localName_by_index_s(dict, i, localName) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + character(len=*), intent(in) :: localName + + if (associated(dict%list(i)%d%localName)) & + deallocate(dict%list(i)%d%localName) + dict%list(i)%d%localName => vs_str_alloc(localName) + end subroutine set_localName_by_index_s + + subroutine set_localName_by_index_vs(dict, i, localName) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + character(len=1), dimension(:), intent(in) :: localName + + if (associated(dict%list(i)%d%localName)) & + deallocate(dict%list(i)%d%localName) + allocate(dict%list(i)%d%localName(size(localName))) + dict%list(i)%d%localName = localName + end subroutine set_localName_by_index_vs +#endif + + pure function get_nsURI_by_index(dict, i) result(nsURI) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=size(dict%list(i)%d%nsURI)) :: nsURI + nsURI = str_vs(dict%list(i)%d%nsURI) +#else + character(len=1) :: nsURI + nsURI = "" +#endif + end function get_nsURI_by_index + +#ifndef DUMMYLIB + pure function get_prefix_by_index(dict, i) result(prefix) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + character(len=size(dict%list(i)%d%prefix)) :: prefix + + prefix = str_vs(dict%list(i)%d%prefix) + end function get_prefix_by_index +#endif + + pure function get_localName_by_index(dict, i) result(localName) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=size(dict%list(i)%d%localName)) :: localName + localName = str_vs(dict%list(i)%d%localName) +#else + character(len=1) :: localName + localName = "" +#endif + end function get_localName_by_index + +#ifndef DUMMYLIB + pure function get_nsURI_by_keyname_len(dict, keyname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + integer :: n + + integer :: i + + i = get_key_index(dict, keyname) + n = size(dict%list(i)%d%nsURI) + end function get_nsURI_by_keyname_len +#endif + +#ifndef DUMMYLIB + pure function get_nsURI_by_keyname(dict, keyname) result(nsURI) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + character(len=get_nsURI_by_keyname_len(dict, keyname)) :: nsURI + integer :: i + + i = get_key_index(dict, keyname) + nsURI = str_vs(dict%list(i)%d%nsURI) + end function get_nsURI_by_keyname + + pure function get_prefix_by_keyname_len(dict, keyname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + integer :: n + + integer :: i + + i = get_key_index(dict, keyname) + n = size(dict%list(i)%d%prefix) + + end function get_prefix_by_keyname_len + + pure function get_prefix_by_keyname(dict, keyname) result(prefix) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + character(len=get_prefix_by_keyname_len(dict,keyname)) :: prefix + integer :: i + + i = get_key_index(dict, keyname) + prefix = str_vs(dict%list(i)%d%prefix) + + end function get_prefix_by_keyname + + pure function get_localname_by_keyname_len(dict, keyname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + integer :: n + + integer :: i + + i = get_key_index(dict, keyname) + n = size(dict%list(i)%d%localName) + + end function get_localname_by_keyname_len + + pure function get_localName_by_keyname(dict, keyname) result(localName) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + character(get_localname_by_keyname_len(dict, keyname)) :: localname + integer :: i + + i=get_key_index(dict, keyname) + localName = str_vs(dict%list(i)%d%localName) + + end function get_localName_by_keyname +#endif + +#ifndef DUMMYLIB + pure function getType_by_index_len(dict, i) result(n) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + integer :: n + + if (i>0.and.i<=ubound(dict%list, 1)) then + n = len_trim(ATT_TYPES(dict%list(i)%d%type)) + else + n = 0 + endif + end function getType_by_index_len +#endif + + function getType_by_index(dict, i) result(type) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=getType_by_index_len(dict, i)) :: type + + if (i>0.and.i<=ubound(dict%list, 1)) then + type = ATT_TYPES(dict%list(i)%d%type) + else + type = "" + endif +#else + character(len=1) :: type + type = "" +#endif + end function getType_by_index + +#ifndef DUMMYLIB + pure function getType_by_keyname_len(dict, keyname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + integer :: n + + integer :: i + i = get_key_index(dict, keyname) + + if (i>0) then + n = len_trim(ATT_TYPES(i)) + else + n = 0 + endif + end function getType_by_keyname_len +#endif + + function getType_by_keyname(dict, keyname) result(type) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname +#ifndef DUMMYLIB + character(len=getType_by_keyname_len(dict, keyname)) :: type + + integer :: i + i = get_key_index(dict, keyname) + if (i>0) then + type = ATT_TYPES(dict%list(i)%d%type) + else + type = "" + endif +#else + character(len=1) :: type + type = "" +#endif + end function getType_by_keyname + + function isSpecified_by_index(dict, i) result(p) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + logical :: p + +#ifndef DUMMYLIB + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%specified + else + p = .false. + endif +#else + p = .false. +#endif + end function isSpecified_by_index + + function isSpecified_by_key(dict, qName) result(p) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: qName + logical :: p + +#ifndef DUMMYLIB + integer :: i + i = getIndex(dict, qName) + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%specified + else + p = .false. + endif +#else + p = .false. +#endif + end function isSpecified_by_key + + subroutine setSpecified(dict, i, p) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + logical, intent(in) :: p + +#ifndef DUMMYLIB + if (i>0.and.i<=ubound(dict%list, 1)) then + dict%list(i)%d%specified = p + endif +#endif + end subroutine setSpecified + + function isSpecified_by_keyNS(dict, uri, localName) result(p) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: uri + character(len=*), intent(in) :: localName + logical :: p + +#ifndef DUMMYLIB + integer :: i + i = getIndex(dict, uri, localName) + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%specified + else + p = .false. + endif +#else + p = .false. +#endif + end function isSpecified_by_keyNS + + function isDeclared_by_index(dict, i) result(p) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + logical :: p + +#ifndef DUMMYLIB + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%declared + else + p = .false. + endif +#else + p = .false. +#endif + end function isDeclared_by_index + + function isDeclared_by_key(dict, qName) result(p) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: qName + logical :: p + +#ifndef DUMMYLIB + integer :: i + i = getIndex(dict, qName) + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%declared + else + p = .false. + endif +#else + p = .false. +#endif + end function isDeclared_by_key + + function isDeclared_by_keyNS(dict, uri, localName) result(p) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: uri + character(len=*), intent(in) :: localName + logical :: p + +#ifndef DUMMYLIB + integer :: i + i = getIndex(dict, uri, localName) + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%declared + else + p = .false. + endif +#else + p = .false. +#endif + end function isDeclared_by_keyNS + + subroutine setDeclared(dict, i, p) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + logical, intent(in) :: p + +#ifndef DUMMYLIB + if (i>0.and.i<=ubound(dict%list, 1)) then + dict%list(i)%d%declared = p + endif +#endif + end subroutine setDeclared + +#ifndef DUMMYLIB + function getIsId_by_index(dict, i) result(p) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + logical :: p + + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%isId + else + p = .false. + endif + + end function getIsId_by_index + + subroutine setIsId_by_index(dict, i, p) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + logical, intent(in) :: p + + if (i>0.and.i<=ubound(dict%list, 1)) then + dict%list(i)%d%isId = p + endif + end subroutine setIsId_by_index + + function getWhitespaceHandling(dict, i) result(j) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + integer :: j + + if (i<=ubound(dict%list, 1)) then + select case(dict%list(i)%d%type) + case (ATT_CDATA) + j = 0 ! + case (ATT_CDAMB) + j = 1 + case default + j = 2 + end select + else + j = 2 + endif + + end function getWhitespaceHandling + + subroutine setBase(dict, base) + type(dictionary_t), intent(inout) :: dict + character(len=*), intent(in) :: base + + if (associated(dict%base)) deallocate(dict%base) + dict%base => vs_str_alloc(base) + end subroutine setBase + + pure function getBase_len(dict) result(n) + type(dictionary_t), intent(in) :: dict + integer :: n + + if (associated(dict%base)) then + n = size(dict%base) + else + n = 0 + endif + end function getBase_len + + function getBase(dict) result(base) + type(dictionary_t), intent(in) :: dict + character(len=getBase_len(dict)) :: base + + if (associated(dict%base)) then + base = str_vs(dict%base) + else + base = "" + endif + end function getBase + + subroutine destroy_dict_item(d) + type(dict_item), pointer :: d + + if (associated(d)) then + deallocate(d%key) + deallocate(d%value) + deallocate(d%nsURI) + deallocate(d%prefix) + deallocate(d%localName) + deallocate(d) + endif + end subroutine destroy_dict_item + + subroutine init_dict(dict) + type(dictionary_t), intent(out) :: dict + + + allocate(dict%list(0:0)) + allocate(dict%list(0)%d) + allocate(dict%list(0)%d%key(0)) + + end subroutine init_dict + + subroutine destroy_dict(dict) + type(dictionary_t), intent(inout) :: dict + integer :: i + + if (associated(dict%list)) then + deallocate(dict%list(0)%d%key) + deallocate(dict%list(0)%d) + do i = 1, ubound(dict%list, 1) + call destroy(dict%list(i)%d) + enddo + deallocate(dict%list) + endif + if (associated(dict%base)) deallocate(dict%base) + + end subroutine destroy_dict + + + subroutine reset_dict(dict) + type(dictionary_t), intent(inout) :: dict + + call destroy_dict(dict) + call init_dict(dict) + + end subroutine reset_dict + + subroutine sortAttrs(dict) + type(dictionary_t), intent(inout) :: dict + + logical :: done(ubound(dict%list, 1)) + type(dict_item_ptr), dimension(:), pointer :: list => null() + integer :: i, j, n, firstIndex + character, pointer :: firstKey(:) + + !Ridiculously naive sort algorithm. We are unlikely + !to ever be sorting more then ten or so attributes though + + n = ubound(dict%list, 1) + + allocate(list(0:n)) + list(0)%d => dict%list(0)%d + + j = 1 + done = .false. + + firstIndex = 1 + do while (firstIndex/=0) + firstIndex = 0 + firstKey => null() + do i = 1, n + if (.not.done(i).and.str_vs(dict%list(i)%d%key)=="xmlns" & + .or. str_vs(dict%list(i)%d%prefix)=="xmlns") then + firstIndex = i + if (associated(firstKey)) then + if (llt(str_vs(dict%list(i)%d%key),str_vs(firstKey))) then + firstIndex = i + firstKey => dict%list(i)%d%key + endif + else + firstIndex = i + firstKey => dict%list(i)%d%key + endif + endif + enddo + if (firstIndex/=0) then + done(firstIndex) = .true. + list(j)%d => dict%list(firstIndex)%d + j = j + 1 + endif + enddo + + do while (any(.not.done)) + firstIndex = 0 + firstKey => null() + do i = 1, n + if (.not.done(i)) then + if (associated(firstKey)) then + if (llt(str_vs(dict%list(i)%d%key),str_vs(firstKey))) then + firstIndex = i + firstKey => dict%list(i)%d%key + endif + else + firstIndex = i + firstKey => dict%list(i)%d%key + endif + endif + enddo + done(firstIndex) = .true. + list(j)%d => dict%list(firstIndex)%d + j = j + 1 + enddo + + deallocate(dict%list) + dict%list => list + + end subroutine sortAttrs + + + subroutine print_dict(dict) + type(dictionary_t), intent(in) :: dict + + integer :: i + + do i = 1, ubound(dict%list, 1) + write(*,'(7a)') str_vs(dict%list(i)%d%key), " [ {", str_vs(dict%list(i)%d%nsURI), & + "}", str_vs(dict%list(i)%d%localName), " ] = ", str_vs(dict%list(i)%d%value) + enddo + + end subroutine print_dict + +#endif +end module m_common_attrs diff --git a/src/xml/common/m_common_buffer.F90 b/src/xml/common/m_common_buffer.F90 new file mode 100644 index 0000000000..0bf518362c --- /dev/null +++ b/src/xml/common/m_common_buffer.F90 @@ -0,0 +1,261 @@ +module m_common_buffer + +#ifndef DUMMYLIB + use fox_m_fsys_format, only: str + use m_common_charset, only: XML1_0 + use m_common_error, only: FoX_error, FoX_warning + + implicit none + private + + ! At this point we use a fixed-size buffer. + ! Note however that buffer overflows will only be + ! triggered by overly long *unbroken* pcdata values, or + ! by overly long attribute values. Hopefully + ! element or attribute names are "short enough". + ! + ! In a forthcoming implementation it could be made dynamical... + + ! MAX_BUFF_SIZE cannot be bigger than the maximum available + ! record length for a compiler. In practice, this means + ! 1024 seems to be the biggest available size. + + integer, parameter :: MAX_BUFF_SIZE = 1024 + + type buffer_t + private + integer :: size + character(len=MAX_BUFF_SIZE) :: str + integer :: unit + integer :: xml_version + end type buffer_t + + public :: buffer_t + + public :: add_to_buffer + public :: print_buffer, str, char, len + public :: buffer_to_chararray + public :: reset_buffer + public :: dump_buffer + + interface str + module procedure buffer_to_str + end interface + + interface char + module procedure buffer_to_str + end interface + + interface len + module procedure buffer_length + end interface + +contains + + subroutine reset_buffer(buffer, unit, xml_version) + type(buffer_t), intent(inout) :: buffer + integer, intent(in), optional :: unit + integer, intent(in) :: xml_version + + buffer%size = 0 + if (present(unit)) then + buffer%unit = unit + else + buffer%unit = 6 + endif + buffer%xml_version = xml_version + + end subroutine reset_buffer + + + subroutine print_buffer(buffer) + type(buffer_t), intent(in) :: buffer + + write(unit=6,fmt="(a)") buffer%str(:buffer%size) + + end subroutine print_buffer + + + function buffer_to_str(buffer) result(str) + type(buffer_t), intent(in) :: buffer + character(len=buffer%size) :: str + + str = buffer%str(:buffer%size) + end function buffer_to_str + + + function buffer_to_chararray(buffer) result(str) + type(buffer_t), intent(in) :: buffer + character(len=1), dimension(buffer%size) :: str + integer :: i + + do i = 1, buffer%size + str(i) = buffer%str(i:i) + enddo + end function buffer_to_chararray + + + function buffer_length(buffer) result(length) + type(buffer_t), intent(in) :: buffer + integer :: length + + length = buffer%size + + end function buffer_length + + + subroutine dump_buffer(buffer, lf) + type(buffer_t), intent(inout) :: buffer + logical, intent(in), optional :: lf + + integer :: i, n + logical :: lf_ + + if (present(lf)) then + lf_ = lf + else + lf_ = .true. + endif + + i = scan(buffer%str(:buffer%size), achar(10)//achar(13)) + n = 1 + do while (i>0) + write(buffer%unit, '(a)', advance="yes") buffer%str(n:n+i-2) + n = n + i + if (n>buffer%size) exit + i = scan(buffer%str(n:), achar(10)//achar(13)) + enddo + + if (n<=buffer%size) then + if (lf_) then + write(buffer%unit, '(a)', advance="yes") buffer%str(n:buffer%size) + else + write(buffer%unit, '(a)', advance="no") buffer%str(n:buffer%size) + endif + endif + + buffer%size = 0 + end subroutine dump_buffer + + + subroutine check_buffer(s, version) + character(len=*), intent(in) :: s + integer, intent(in) :: version + + integer :: i + +!FIXME this is almost a duplicate of logic in wxml/m_wxml_escape.f90 + + ! We have to do it this way (with achar etc) in case the native + ! platform encoding is not ASCII + + do i = 1, len(s) + select case (iachar(s(i:i))) + case (0) + call FoX_error("Tried to output a NUL character") + case (1:8,11:12,14:31) + if (version==XML1_0) then + call FoX_error("Tried to output a character invalid under XML 1.0: &#"//str(iachar(s(i:i)))//";") + endif + case (128:) + !TOHW we should maybe just disallow this ... + call FoX_warning("emitting non-ASCII character. Platform-dependent result!") + end select + enddo + + end subroutine check_buffer + + + subroutine add_to_buffer(s, buffer, ws_significant) + character(len=*), intent(in) :: s + type(buffer_t), intent(inout) :: buffer + logical, intent(in), optional :: ws_significant + + character(len=(buffer%size+len(s))) :: s2 + integer :: i, n, len_b + logical :: warning, ws_ + + ! Is whitespace significant in this context? + ! We have to assume so unless told otherwise. + if (present(ws_significant)) then + ws_ = ws_significant + else + ws_ = .true. + endif + + ! FIXME The algorithm below unilaterally forces all + ! line feeds and carriage returns to native EOL, regardless + ! of input document. Thus it is impossible to output a + ! document containing a literal non-native newline character + ! Ideally we would put this under the control of the user. + + ! We check if whitespace is significant. If not, we can + ! adjust the buffer without worrying about it. + ! But if we are not told, we warn about it. + ! And if we are told it definitely is - then we error out. + + ! If we overreach our buffer size, we will be unable to + ! output any more characters without a newline. + ! Go through new string, insert newlines + ! at spaces just before MAX_BUFF_SIZE chars + ! until we have less than MAX_BUFF_SIZE left to go, + ! then put that in the buffer. + + ! If no whitespace is found in the newly-added string, then + ! insert a new line immediately before it (at the end of the + ! current buffer) + + call check_buffer(s, buffer%xml_version) + + s2 = buffer%str(:buffer%size)//s + + + ! output as much of this using existing newlines as possible. + warning = .false. + n = 1 + do while (n<=len(s2)) + ! Note this is an XML-1.0 only definition of newline + i = scan(s2(n:), achar(10)//achar(13)) + if (i>0) then + ! turn that newline into an output newline ... + write(buffer%unit, '(a)') s2(n:n+i-2) + n = n + i + elseif (n<=len(s2)-MAX_BUFF_SIZE) then + ! We need to insert a newline, or we'll overrun the buffer + ! No suitable newline, so convert a space or tab into a newline. + i = scan(s2(n:n+MAX_BUFF_SIZE-1), achar(9)//achar(32), back=.true.) + ! If no space or tab is present, we fail. + if (i>0.and..not.present(ws_significant)) then + ! We can insert a newline, but we don't know whether it is significant. Warn: + if (.not.warning) then + ! We only output this warning once. + call FoX_warning( & + "Fortran made FoX insert a newline. "// & + "If whitespace might be significant, check your output.") + warning = .true. + endif + elseif (i==0) then + call FoX_error( & + "Fortran made FoX insert a newline but it can't. Stopping now.") + elseif (ws_) then + call FoX_error( & + "Fortran made FoX insert a newline but whitespace is significant. Stopping now.") + else + continue ! without error or warning, because whitespace is not significant + endif + write(buffer%unit, '(a)') s2(n:n+i-1) + n = n + i + else + ! We don't need to do anything, just add the remainder to the buffer. + exit + endif + enddo + + len_b = len(s2) - n + 1 + buffer%str(:len_b) = s2(n:) + buffer%size = len_b + + end subroutine add_to_buffer + +#endif +end module m_common_buffer diff --git a/src/xml/common/m_common_charset.F90 b/src/xml/common/m_common_charset.F90 new file mode 100644 index 0000000000..d46a95ec2c --- /dev/null +++ b/src/xml/common/m_common_charset.F90 @@ -0,0 +1,447 @@ +module m_common_charset + +#ifndef DUMMYLIB + ! Written to use ASCII charset only. Full UNICODE would + ! take much more work and need a proper unicode library. + + use fox_m_fsys_string, only: toLower + + implicit none + private + +!!$ character(len=1), parameter :: ASCII = & +!!$achar(0)//achar(1)//achar(2)//achar(3)//achar(4)//achar(5)//achar(6)//achar(7)//achar(8)//achar(9)//& +!!$achar(10)//achar(11)//achar(12)//achar(13)//achar(14)//achar(15)//achar(16)//achar(17)//achar(18)//achar(19)//& +!!$achar(20)//achar(21)//achar(22)//achar(23)//achar(24)//achar(25)//achar(26)//achar(27)//achar(28)//achar(29)//& +!!$achar(30)//achar(31)//achar(32)//achar(33)//achar(34)//achar(35)//achar(36)//achar(37)//achar(38)//achar(39)//& +!!$achar(40)//achar(41)//achar(42)//achar(43)//achar(44)//achar(45)//achar(46)//achar(47)//achar(48)//achar(49)//& +!!$achar(50)//achar(51)//achar(52)//achar(53)//achar(54)//achar(55)//achar(56)//achar(57)//achar(58)//achar(59)//& +!!$achar(60)//achar(61)//achar(62)//achar(63)//achar(64)//achar(65)//achar(66)//achar(67)//achar(68)//achar(69)//& +!!$achar(70)//achar(71)//achar(72)//achar(73)//achar(74)//achar(75)//achar(76)//achar(77)//achar(78)//achar(79)//& +!!$achar(80)//achar(81)//achar(82)//achar(83)//achar(84)//achar(85)//achar(86)//achar(87)//achar(88)//achar(89)//& +!!$achar(90)//achar(91)//achar(92)//achar(93)//achar(94)//achar(95)//achar(96)//achar(97)//achar(98)//achar(99)//& +!!$achar(100)//achar(101)//achar(102)//achar(103)//achar(104)//achar(105)//achar(106)//achar(107)//achar(108)//achar(109)//& +!!$achar(110)//achar(111)//achar(112)//achar(113)//achar(114)//achar(115)//achar(116)//achar(117)//achar(118)//achar(119)//& +!!$achar(120)//achar(121)//achar(122)//achar(123)//achar(124)//achar(125)//achar(126)//achar(127) + + character(len=1), parameter :: SPACE = achar(32) + character(len=1), parameter :: NEWLINE = achar(10) + character(len=1), parameter :: CARRIAGE_RETURN = achar(13) + character(len=1), parameter :: TAB = achar(9) + + character(len=*), parameter :: whitespace = SPACE//NEWLINE//CARRIAGE_RETURN//TAB + + character(len=*), parameter :: lowerCase = "abcdefghijklmnopqrstuvwxyz" + character(len=*), parameter :: upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + character(len=*), parameter :: digits = "0123456789" + character(len=*), parameter :: hexdigits = "0123456789abcdefABCDEF" + character(len=*), parameter :: InitialNCNameChars = lowerCase//upperCase//"_" + character(len=*), parameter :: NCNameChars = InitialNCNameChars//digits//".-" + character(len=*), parameter :: InitialNameChars = InitialNCNameChars//":" + character(len=*), parameter :: NameChars = NCNameChars//":" + + character(len=*), parameter :: PubIdChars = NameChars//whitespace//"'()+,/=?;!*#@$%" + character(len=*), parameter :: validchars = & + whitespace//"!""#$%&'()*+,-./"//digits// & + ":;<=>?@"//upperCase//"[\]^_`"//lowerCase//"{|}~" + ! these are all the standard ASCII printable characters: whitespace + (33-126) + ! which are the only characters we can guarantee to know how to handle properly. + + integer, parameter :: XML1_0 = 10 ! NB 0x7F was legal in XML-1.0, but illegal in XML-1.1 + + integer, parameter :: XML1_1 = 11 + + character(len=*), parameter :: XML1_0_ILLEGALCHARS = achar(0) + character(len=*), parameter :: XML1_1_ILLEGALCHARS = NameChars + + character(len=*), parameter :: XML1_0_INITIALNAMECHARS = InitialNameChars + character(len=*), parameter :: XML1_1_INITIALNAMECHARS = InitialNameChars + character(len=*), parameter :: XML1_0_NAMECHARS = NameChars + character(len=*), parameter :: XML1_1_NAMECHARS = NameChars + + character(len=*), parameter :: XML1_0_INITIALNCNAMECHARS = InitialNCNameChars + character(len=*), parameter :: XML1_1_INITIALNCNAMECHARS = InitialNCNameChars + character(len=*), parameter :: XML1_0_NCNAMECHARS = NCNameChars + character(len=*), parameter :: XML1_1_NCNAMECHARS = NCNameChars + + + character(len=*), parameter :: XML_WHITESPACE = whitespace + character(len=*), parameter :: XML_INITIALENCODINGCHARS = lowerCase//upperCase + character(len=*), parameter :: XML_ENCODINGCHARS = lowerCase//upperCase//digits//'._-' + + public :: validchars + public :: whitespace + public :: uppercase + + public :: digits + public :: hexdigits + + public :: XML1_0 + public :: XML1_1 + public :: XML1_0_NAMECHARS + public :: XML1_1_NAMECHARS + public :: XML1_0_INITIALNAMECHARS + public :: XML1_1_INITIALNAMECHARS + public :: XML_WHITESPACE + public :: XML_INITIALENCODINGCHARS + public :: XML_ENCODINGCHARS + + public :: isLegalChar + public :: isLegalCharRef + public :: isRepCharRef + public :: isInitialNameChar + public :: isNameChar + public :: isInitialNCNameChar + public :: isNCNameChar + public :: isXML1_0_NameChar + public :: isXML1_1_NameChar + public :: checkChars + public :: isUSASCII + public :: allowed_encoding + +contains + + pure function isLegalChar(c, ascii_p, xml_version) result(p) + character, intent(in) :: c + ! really we should check the encoding here & be more intelligent + ! for now we worry only about is it ascii or not. + logical, intent(in) :: ascii_p + integer, intent(in) :: xml_version + logical :: p + ! Is this character legal as a source character in the document? + integer :: i + i = iachar(c) + if (i<0) then + p = .false. + return + elseif (i>127) then + p = .not.ascii_p + return + ! ie if we are ASCII, then >127 is definitely illegal. + ! otherwise maybe it's ok + endif + select case(xml_version) + case (XML1_0) + p = (i==9.or.i==10.or.i==13.or.(i>31.and.i<128)) + case (XML1_1) + p = (i==9.or.i==10.or.i==13.or.(i>31.and.i<127)) + ! NB 0x7F was legal in XML-1.0, but illegal in XML-1.1 + end select + end function isLegalChar + + pure function isLegalCharRef(i, xml_version) result(p) + integer, intent(in) :: i + integer, intent(in) :: xml_version + logical :: p + + ! Is Unicode character #i legal as a character reference? + + if (xml_version==XML1_0) then + p = (i==9).or.(i==10).or.(i==13).or.(i>31.and.i<55296).or.(i>57343.and.i<65534).or.(i>65535.and.i<1114112) + elseif (xml_version==XML1_1) then + p = (i>0.and.i<55296).or.(i>57343.and.i<65534).or.(i>65535.and.i<1114112) + ! XML 1.1 made all control characters legal as character references. + end if + end function isLegalCharRef + + pure function isRepCharRef(i, xml_version) result(p) + integer, intent(in) :: i + integer, intent(in) :: xml_version + logical :: p + + ! Is Unicode character #i legal and representable here? + + if (xml_version==XML1_0) then + p = (i==9).or.(i==10).or.(i==13).or.(i>31.and.i<128) + elseif (xml_version==XML1_1) then + p = (i>0.and.i<128) + ! XML 1.1 made all control characters legal as character references. + end if + end function isRepCharRef + + pure function isInitialNameChar(c, xml_version) result(p) + character, intent(in) :: c + integer, intent(in) :: xml_version + logical :: p + + select case(xml_version) + case (XML1_0) + p = (verify(c, XML1_0_INITIALNAMECHARS)==0) + case (XML1_1) + p = (verify(c, XML1_1_INITIALNAMECHARS)==0) + end select + + end function isInitialNameChar + + pure function isNameChar(c, xml_version) result(p) + character(len=*), intent(in) :: c + integer, intent(in) :: xml_version + logical :: p + + select case(xml_version) + case (XML1_0) + p = (verify(c, XML1_0_NAMECHARS)==0) + case (XML1_1) + p = (verify(c, XML1_1_NAMECHARS)==0) + end select + + end function isNameChar + + pure function isInitialNCNameChar(c, xml_version) result(p) + character, intent(in) :: c + integer, intent(in) :: xml_version + logical :: p + + select case(xml_version) + case (XML1_0) + p = (verify(c, XML1_0_INITIALNCNAMECHARS)==0) + case (XML1_1) + p = (verify(c, XML1_1_INITIALNCNAMECHARS)==0) + end select + end function isInitialNCNameChar + + pure function isNCNameChar(c, xml_version) result(p) + character(len=*), intent(in) :: c + integer, intent(in) :: xml_version + logical :: p + + select case(xml_version) + case (XML1_0) + p = (verify(c, XML1_0_NCNAMECHARS)==0) + case (XML1_1) + p = (verify(c, XML1_1_NCNAMECHARS)==0) + end select + end function isNCNameChar + + function isXML1_0_NameChar(c) result(p) + character, intent(in) :: c + logical :: p + + p = (verify(c, XML1_0_NAMECHARS)==0) + + end function isXML1_0_NameChar + + function isXML1_1_NameChar(c) result(p) + character, intent(in) :: c + logical :: p + + p = (verify(c, XML1_1_NAMECHARS)==0) + + end function isXML1_1_NameChar + + pure function checkChars(value, xv) result(p) + character(len=*), intent(in) :: value + integer, intent(in) :: xv + logical :: p + + ! This checks if value only contains values + ! legal to appear (escaped or unescaped) + ! according to whichever XML version is in force. + integer :: i + + p = .true. + do i = 1, len(value) + if (xv == XML1_0) then + select case(iachar(value(i:i))) + case (0,8) + p = .false. + case (11,12) + p = .false. + end select + else + if (iachar(value(i:i))==0) p =.false. + endif + enddo + end function checkChars + + function isUSASCII(encoding) result(p) + character(len=*), intent(in) :: encoding + logical :: p + + character(len=len(encoding)) :: enc + enc = toLower(encoding) + p = (enc=="ansi_x3.4-1968" & + .or. enc=="ansi_x3.4-1986" & + .or. enc=="iso_646.irv:1991" & + .or. enc=="ascii" & + .or. enc=="iso646-us" & + .or. enc=="us-ascii" & + .or. enc=="us" & + .or. enc=="ibm367" & + .or. enc=="cp367" & + .or. enc=="csascii") + + end function isUSASCII + + function allowed_encoding(encoding) result(p) + character(len=*), intent(in) :: encoding + logical :: p + + character(len=len(encoding)) :: enc + logical :: utf8, usascii, iso88591, iso88592, iso88593, iso88594, & + iso88595, iso88596, iso88597, iso88598, iso88599, iso885910, & + iso885913, iso885914, iso885915, iso885916 + + enc = toLower(encoding) + + ! From http://www.iana.org/assignments/character-sets + ! We can only reliably do US-ASCII (the below is mostly + ! a list of synonyms for US-ASCII) but we also accept + ! UTF-8 as a practicality. We bail out if any non-ASCII + ! characters are used later on. + utf8 = (enc=="utf-8") + + usascii = (enc=="ansi_x3.4-1968" & + .or. enc=="ansi_x3.4-1986" & + .or. enc=="iso_646.irv:1991" & + .or. enc=="ascii" & + .or. enc=="iso646-us" & + .or. enc=="us-ascii" & + .or. enc=="us" & + .or. enc=="ibm367" & + .or. enc=="cp367" & + .or. enc=="csascii") +! As of FoX 4.0, we accept ISO-8859-??, also as practicality +! since we know it is identical to ASCII as far as 0x7F + + iso88591 = (enc =="iso_8859-1:1987" & + .or. enc=="iso-ir-100" & + .or. enc=="iso_8859-1" & + .or. enc=="iso-8859-1" & + .or. enc=="latin1" & + .or. enc=="l1" & + .or. enc=="ibm819" & + .or. enc=="cp819" & + .or. enc=="csisolatin1") + + iso88592 = (enc=="iso_8859-2:1987" & + .or. enc=="iso-ir-101" & + .or. enc=="iso_8859-2" & + .or. enc=="iso-8859-2" & + .or. enc=="latin2" & + .or. enc=="l2" & + .or. enc=="csisolatin2") + + iso88593 = (enc=="iso_8859-3:1988" & + .or. enc=="iso-ir-109" & + .or. enc=="iso_8859-3" & + .or. enc=="iso-8859-3" & + .or. enc=="latin3" & + .or. enc=="l3" & + .or. enc=="csisolatin3") + + iso88594 = (enc=="iso_8859-4:1988" & + .or. enc=="iso-ir-110" & + .or. enc=="iso_8859-4" & + .or. enc=="iso-8859-4" & + .or. enc=="latin4" & + .or. enc=="l4" & + .or. enc=="csisolatin4") + + iso88595 = (enc=="iso_8859-5:1988" & + .or. enc=="iso-ir-144" & + .or. enc=="iso_8859-5" & + .or. enc=="iso-8859-5" & + .or. enc=="cyrillic" & + .or. enc=="csisolatincyrillic") + + iso88596 = (enc=="iso_8859-6:1987" & + .or. enc=="iso-ir-127" & + .or. enc=="iso_8859-6" & + .or. enc=="iso-8859-6" & + .or. enc=="ecma-114" & + .or. enc=="asmo-708" & + .or. enc=="arabic" & + .or. enc=="csisolatinarabic") + + iso88597 = (enc=="iso_8859-7:1987" & + .or. enc=="iso-ir-126" & + .or. enc=="iso_8859-7" & + .or. enc=="iso-8859-7" & + .or. enc=="elot_928" & + .or. enc=="ecma-118" & + .or. enc=="greek" & + .or. enc=="greek8" & + .or. enc=="csisolatingreek") + + iso88598 = (enc=="iso_8859-8:1988" & + .or. enc=="iso-ir-138" & + .or. enc=="iso_8859-8" & + .or. enc=="iso-8859-8" & + .or. enc=="hebrew" & + .or. enc=="csisolatinhebrew") + + iso88599 = (enc=="iso_8859-9:1989" & + .or. enc=="iso-ir-148" & + .or. enc=="iso_8859-9" & + .or. enc=="iso-8859-9" & + .or. enc=="latin5" & + .or. enc=="l5" & + .or. enc=="csisolatin5") + + iso885910 = (enc=="iso-8859-10" & + .or. enc=="iso-ir-157" & + .or. enc=="l6" & + .or. enc=="iso_8859-10:1992" & + .or. enc=="csisolatin6" & + .or. enc=="latin6") + +! ISO 6937 replaces $ sign with currency sign. +! JIS-X0201 has Yen instead of backslash, macron instead of tilde +! 16, 17, 18, 19 - Japanese encoding we can't use. +! BS 4730 replaces hash with UK pound sign, and tilde to macron +! 21, 22, 23, 24, 25, 26 - other variants of iso646, similar but not identical + +! iso10646utf1 = (enc=="iso-10646-utf-1") ! FIXME check +! iso656basic1983 = (enc=="iso_646.basic:1983" & +! .or. enc=="csiso646basic1983") ! FIXME check +! INVARIANT - almost but not quite a subset of ASCII +! iso646irv = (enc=="iso_646.irv:1983" & +! .or. enc=="iso-ir-2" & +! .or. enc=="irv") +! 31, 32, 33, 34 - NATS scandinavian, different from ASCII +! 35 - another iso646 variant +! 36, 37, 38 Korean shifted/multibyte +! 39, 40 Japanese shifted/multibyte +! 41, 42, JIS (iso646inv 7 bits) +! 43 another iso646 variantt +! 44, 45 greek variants +! 46 another iso646 variant +! 47 greek +! 48 cyrillic ascii relationship unknown +! 49 JIS again +! 50 similar not identical +! 51, 52, 53 not identical +! 54 see 48 +! 55 see 47 +! 56 another iso646 variant +! 57 chinese +! ... to be continued + + iso885913 = (enc=="iso-8859-13") + iso885914 = (enc=="iso-8859-14" & + .or. enc=="iso-ir-199" & + .or. enc=="iso_8859-14:1998" & + .or. enc=="iso_8849-14" & + .or. enc=="iso_latin8" & + .or. enc=="iso-celtic" & + .or. enc=="l8") + iso885915 = (enc=="iso-8859-15" & + .or. enc=="iso-8859-15" & + .or. enc=="latin-9") + iso885916 = (enc=="iso-8859-16" & + .or. enc=="iso-ir226" & + .or. enc=="iso_8859-16:2001" & + .or. enc=="iso_8859-16" & + .or. enc=="latin10" & + .or. enc=="l10") + + p = utf8.or.usascii.or.iso88591.or.iso88592.or.iso88593 & + .or.iso88594.or.iso88595.or.iso88596.or.iso88597 & + .or.iso88598.or.iso88599.or.iso885910.or.iso885913 & + .or.iso885914.or.iso885915.or.iso885916 + + end function allowed_encoding + +#endif +end module m_common_charset diff --git a/src/xml/common/m_common_content_model.F90 b/src/xml/common/m_common_content_model.F90 new file mode 100644 index 0000000000..1b5df709e6 --- /dev/null +++ b/src/xml/common/m_common_content_model.F90 @@ -0,0 +1,490 @@ +module m_common_content_model + +#ifndef DUMMYLIB + ! Allow validating the content model of an XML document + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + implicit none + private + + integer, parameter :: OP_NULL = 0 + integer, parameter :: OP_EMPTY = 1 + integer, parameter :: OP_ANY = 2 + integer, parameter :: OP_MIXED = 3 + integer, parameter :: OP_NAME = 4 + integer, parameter :: OP_CHOICE = 5 + integer, parameter :: OP_SEQ = 6 + + integer, parameter :: REP_NULL = 0 + integer, parameter :: REP_ONCE = 1 + integer, parameter :: REP_QUESTION_MARK = 2 + integer, parameter :: REP_ASTERISK = 3 + + type content_particle_t + character, pointer :: name(:) => null() + integer :: operator = OP_NULL + integer :: repeater = REP_NULL + type(content_particle_t), pointer :: nextSibling => null() + type(content_particle_t), pointer :: parent => null() + type(content_particle_t), pointer :: firstChild => null() + end type content_particle_t + + public :: content_particle_t + + public :: newCP + public :: transformCPPlus + public :: checkCP + public :: checkCPToEnd + public :: elementContentCP + public :: emptyContentCP + public :: destroyCPtree + public :: dumpCPtree + + public :: OP_NULL, OP_NAME, OP_MIXED, OP_CHOICE, OP_SEQ + public :: REP_QUESTION_MARK, REP_ASTERISK + +contains + + function newCP(empty, any, name, repeat) result(cp) + logical, intent(in), optional :: empty + logical, intent(in), optional :: any + character(len=*), intent(in), optional :: name + character, intent(in), optional :: repeat + type(content_particle_t), pointer :: cp + + allocate(cp) + if (present(empty)) then + cp%operator = OP_EMPTY + elseif (present(any)) then + cp%operator = OP_ANY + elseif (present(name)) then + cp%operator = OP_NAME + cp%name => vs_str_alloc(name) + else + cp%operator = OP_SEQ + endif + if (present(repeat)) then + select case (repeat) + case("?") + cp%repeater = REP_QUESTION_MARK + case("*") + cp%repeater = REP_ASTERISK + end select + endif + + end function newCP + + function copyCP(cp) result(cp_out) + type(content_particle_t), pointer :: cp + type(content_particle_t), pointer :: cp_out + + allocate(cp_out) + if (associated(cp%name)) cp_out%name => vs_vs_alloc(cp%name) + cp_out%operator = cp%operator + cp_out%repeater = cp%repeater + end function copyCP + + function copyCPtree(cp) result(cp_out) + type(content_particle_t), pointer :: cp + type(content_particle_t), pointer :: cp_out + + type(content_particle_t), pointer :: tcp, tcp_out, tcpn_out, tcpp_out + logical :: done + + tcp => cp + cp_out => copyCP(cp) + tcp_out => cp_out + done = .false. + do while (associated(tcp_out)) + if (.not.done) then + do while (associated(tcp%firstChild)) + tcp => tcp%firstChild + tcpn_out => copyCP(tcp) + tcp_out%firstChild => tcpn_out + tcpn_out%parent => tcp_out + tcp_out => tcpn_out + enddo + endif + tcpp_out => tcp_out%parent + if (associated(tcp%nextSibling)) then + done = .false. + tcp => tcp%nextSibling + tcpn_out => copyCP(tcp) + tcp_out%nextSibling => tcpn_out + tcpn_out%parent => tcpp_out + tcp_out => tcpn_out + else + done = .true. + tcp => tcp%parent + tcp_out => tcp_out%parent + endif + enddo + end function copyCPtree + + subroutine transformCPPlus(cp) + type(content_particle_t), pointer :: cp + + type(content_particle_t), pointer :: tcp, cp_new + + ! Make copy of cp, and graft children on + cp_new => copyCP(cp) + cp_new%firstChild => cp%firstChild + + ! Reset children's parents ... + tcp => cp%firstChild + do while (associated(tcp)) + tcp%parent => cp_new + tcp => tcp%nextSibling + enddo + + ! Clear cp & make it an SEQ + if (associated(cp%name)) deallocate(cp%name) + cp%operator = OP_SEQ + + ! Append our copied cp to the now-an-SEQ + cp%firstChild => cp_new + cp_new%parent => cp + + ! Copy it for a sibling, and make the sibling a * + cp_new%nextSibling => copyCPtree(cp_new) + cp_new%nextSibling%parent => cp + cp_new%nextSibling%repeater = REP_ASTERISK + + end subroutine transformCPPlus + + function checkCP(cp, name) result(p) + type(content_particle_t), pointer :: cp + character(len=*), intent(in) :: name + logical :: p + + type(content_particle_t), pointer :: tcp + + ! for EMPTY, ANY or MIXED, cp never moves. + ! for element content, we move the pointer as we + ! move through the regex. + + ! If the regex includes ambiguous content, we are + ! a bit screwed. But the document is in error if so. + ! (and we are not required to diagnose errors.) + + p = .false. + if (.not.associated(cp)) return + + select case(cp%operator) + case (OP_EMPTY) + continue ! anything fails + case (OP_ANY) + p = .true. + case (OP_MIXED) + tcp => cp%firstChild + do while (associated(tcp)) + if (name==str_vs(tcp%name)) then + p = .true. + exit + endif + tcp => tcp%nextSibling + enddo + case default + do + if (.not.associated(cp)) exit + select case (cp%operator) + case (OP_NAME) + p = (name==str_vs(cp%name)) + if (p) then + tcp => nextCPAfterMatch(cp) + cp => tcp + exit + else + tcp => nextCPAfterFail(cp) + cp => tcp + endif + case (OP_CHOICE, OP_SEQ) + cp => cp%firstChild + end select + end do + end select + + end function checkCP + + function nextCPaftermatch(cp) result(cp_next) + type (content_particle_t), pointer :: cp + type (content_particle_t), pointer :: cp_next + + type (content_particle_t), pointer :: tcp + + cp_next => cp + + do + if (cp_next%repeater==REP_ASTERISK) exit + tcp => cp_next%parent + if (associated(tcp)) then + if (tcp%operator==OP_CHOICE) then + ! siblings are uninteresting, we've matched this CHOICE + cp_next => tcp + ! Move up & try the whole thing again on the parent CHOICE + elseif (tcp%operator==OP_SEQ) then + ! we do care about siblings, move onto next one + if (associated(cp_next%nextSibling)) then + cp_next => cp_next%nextSibling + ! thatll do, itll be the next thing to try + exit + else + ! No sibling, move up a level + cp_next => tcp + ! and try again + endif + endif + else + ! We've got to the top already. + cp_next => tcp + exit + endif + enddo + + end function nextCPaftermatch + + function nextCPafterfail(cp) result(cp_next) + type(content_particle_t), pointer :: cp + type(content_particle_t), pointer :: cp_next + + type(content_particle_t), pointer :: tcp + logical :: match + + match = .false. + cp_next => cp + do + tcp => cp_next%parent + if (associated(tcp)) then + if (tcp%operator==OP_CHOICE) then + ! we care about siblings, lets try the next one + if (associated(cp_next%nextSibling)) then + cp_next => cp_next%nextSibling + ! super, lets go back and try that + exit + else ! weve failed to match any cp in this CHOICE ... + cp_next => tcp + ! go up a level and see if theres another legitimate choice + endif + elseif (tcp%operator==OP_SEQ) then + if ((match.or.cp_next%repeater/=REP_NULL) & + .and.associated(cp_next%nextSibling)) then + ! we were allowed to fail to match, try sibling + cp_next => cp_next%nextSibling + exit + elseif (cp_next%repeater/=REP_NULL) then + match = .true. + ! The last item was optional, so weve matched at this level + cp_next => tcp + elseif (associated(tcp%firstChild, cp_next)) then + ! we havent matched - but we hadnt started, Maybe it was ok + ! not to match because we are nested inside an optional thingy + cp_next => tcp + else + ! We were not allowed to fail there, + ! there is no legitimate next choice. + cp_next => null() + exit + endif + endif + else + ! weve got all the way to the top without + ! finding a new cp to try. But if this top-level + ! cp is ASTERISK'ed we can try it agin + cp_next => null() + exit + endif + enddo + + end function nextCPafterfail + + function checkCPToEnd(cp) result(p) + type(content_particle_t), pointer :: cp + logical :: p + + type(content_particle_t), pointer :: tcp + + if (associated(cp)) then + select case(cp%operator) + case (OP_EMPTY, OP_ANY, OP_MIXED) + p = .true. + case default + tcp => nextCPMustMatch(cp) + p = .not.associated(tcp) + end select + else + p = .true. + endif + end function checkCPToEnd + + function nextCPMustMatch(cp) result(cp_next) + type(content_particle_t), pointer :: cp + type(content_particle_t), pointer :: cp_next + + type(content_particle_t), pointer :: tcp + + if (.not.associated(cp)) return + if (.not.associated(cp%parent)) then + ! we havent started exploring this one. + ! get the first starting position + cp_next => cp + do while (cp_next%repeater==REP_NULL) + if (associated(cp_next%firstChild)) then + cp_next => cp_next%firstChild + else + exit + endif + enddo + else + cp_next => cp + endif + if (cp_next%repeater==REP_NULL) return + do + tcp => cp_next%parent + if (associated(tcp)) then + if (tcp%operator==OP_CHOICE) then + ! its matched by the optional one we are on, go up a level + cp_next => tcp + elseif (tcp%operator==OP_SEQ) then + ! check all siblings for any compulsory ones + do while (associated(cp_next%nextSibling)) + cp_next => cp_next%nextSibling + if (cp_next%repeater==REP_NULL) return + enddo + ! all were optional, go up a level + cp_next => tcp + endif + else + ! weve got all the way to the top without + ! finding a new cp to try + cp_next => tcp + exit + endif + enddo + + end function nextCPMustMatch + + function elementContentCP(cp) result(p) + type(content_particle_t), pointer :: cp + logical :: p + + if (associated(cp)) then + select case (cp%operator) + case (OP_EMPTY, OP_ANY, OP_MIXED) + p = .false. + case default + p = .true. + end select + else + p = .true. + endif + + end function elementContentCP + + function emptyContentCP(cp) result(p) + type(content_particle_t), pointer :: cp + logical :: p + + if (associated(cp)) then + p = cp%operator==OP_EMPTY + else + p = .false. + endif + + end function emptyContentCP + + subroutine destroyCP(cp) + type(content_particle_t), pointer :: cp + + if (associated(cp%name)) deallocate(cp%name) + deallocate(cp) + end subroutine destroyCP + + subroutine destroyCPtree(cp) + type(content_particle_t), pointer :: cp + + type(content_particle_t), pointer :: current, tcp + + current => cp + do + do while (associated(current%firstChild)) + current => current%firstChild + enddo + if (associated(current, cp)) exit + tcp => current + if (associated(current%nextSibling)) then + current => current%nextSibling + call destroyCP(tcp) + else + current => current%parent + call destroyCP(tcp) + current%firstChild => null() + endif + enddo + call destroyCP(cp) + + end subroutine destroyCPtree + + subroutine dumpCP(cp) + type(content_particle_t), pointer :: cp + + select case(cp%operator) + case (OP_EMPTY) + write(*,'(a)', advance="no") "EMPTY" + case (OP_ANY) + write(*,'(a)', advance="no") "ANY" + case (OP_MIXED) + write(*,'(a)', advance="no") "MIXED" + case (OP_NAME) + write(*,'(a)', advance="no") str_vs(cp%name) + case (OP_CHOICE) + write(*,'(a)', advance="no") "CHOICE" + case (OP_SEQ) + write(*,'(a)', advance="no") "SEQ" + end select + select case(cp%repeater) + case (REP_QUESTION_MARK) + write(*,'(a)', advance="no") "?" + case (REP_ASTERISK) + write(*,'(a)', advance="no") "*" + end select + write(*,*) + end subroutine dumpCP + + subroutine dumpCPtree(cp) + type(content_particle_t), pointer :: cp + + type(content_particle_t), pointer :: current + + integer :: i + logical :: done + i = 0 + current => cp + done = .false. + call dumpCP(current) + do + if (.not.done) then + do while (associated(current%firstChild)) + i = i + 2 + current => current%firstChild + write(*,'(a)', advance="no") repeat(" ",i) + call dumpCP(current) + enddo + endif + if (associated(current, cp)) exit + if (associated(current%nextSibling)) then + done = .false. + current => current%nextSibling + write(*,'(a)', advance="no") repeat(" ",i) + call dumpCP(current) + else + done = .true. + i = i - 2 + current => current%parent + endif + enddo + + end subroutine dumpCPtree + +#endif + +end module m_common_content_model diff --git a/src/xml/common/m_common_element.F90 b/src/xml/common/m_common_element.F90 new file mode 100644 index 0000000000..5eecbbfdc3 --- /dev/null +++ b/src/xml/common/m_common_element.F90 @@ -0,0 +1,1645 @@ +module m_common_element + +#ifndef DUMMYLIB + ! Structure and manipulation of element specification + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + use fox_m_fsys_string_list, only: string_list, init_string_list, & + destroy_string_list, add_string, tokenize_to_string_list, & + registered_string + use m_common_charset, only: isInitialNameChar, isNameChar, & + upperCase, XML_WHITESPACE + use m_common_content_model, only: content_particle_t, newCP, destroyCPtree, & + OP_MIXED, OP_CHOICE, OP_SEQ, OP_NAME, & + REP_QUESTION_MARK, REP_ASTERISK, & + transformCPPlus ! , dumpCPtree ! For debugging - see below. + use m_common_error, only: error_stack, add_error, in_error + use m_common_namecheck, only: checkName, checkNames, checkNCName, & + checkNCNames, checkQName, checkNmtoken, checkNmtokens + + implicit none + private + + integer, parameter :: ST_START = 0 + integer, parameter :: ST_EMPTYANY = 1 + integer, parameter :: ST_FIRSTCHILD = 2 + integer, parameter :: ST_END = 3 + integer, parameter :: ST_PCDATA = 4 + integer, parameter :: ST_NAME = 5 + integer, parameter :: ST_CHILD = 6 + integer, parameter :: ST_AFTERBRACKET = 7 + integer, parameter :: ST_AFTERLASTBRACKET = 8 + integer, parameter :: ST_SEPARATOR = 9 + integer, parameter :: ST_AFTERNAME = 10 + integer, parameter :: ST_ATTTYPE = 11 + integer, parameter :: ST_AFTER_NOTATION = 12 + integer, parameter :: ST_NOTATION_LIST = 13 + integer, parameter :: ST_ENUMERATION = 14 + integer, parameter :: ST_ENUM_NAME = 15 + integer, parameter :: ST_AFTER_ATTTYPE_SPACE = 16 + integer, parameter :: ST_AFTER_ATTTYPE = 17 + integer, parameter :: ST_DEFAULT_DECL = 18 + integer, parameter :: ST_AFTERDEFAULTDECL = 19 + integer, parameter :: ST_DEFAULTVALUE = 20 + + integer, parameter :: ATT_NULL = 0 + + integer, parameter :: ATT_CDATA = 1 + integer, parameter :: ATT_ID = 2 + integer, parameter :: ATT_IDREF = 3 + integer, parameter :: ATT_IDREFS = 4 + integer, parameter :: ATT_ENTITY = 5 + integer, parameter :: ATT_ENTITIES = 6 + integer, parameter :: ATT_NMTOKEN = 7 + integer, parameter :: ATT_NMTOKENS = 8 + integer, parameter :: ATT_NOTATION = 9 + integer, parameter :: ATT_ENUM = 10 + integer, parameter :: ATT_CDANO = 11 + integer, parameter :: ATT_CDAMB = 12 + + character(len=8), parameter :: ATT_TYPES(12) = (/ & + "CDATA ", & + "ID ", & + "IDREF ", & + "IDREFS ", & + "ENTITY ", & + "ENTITIES", & + "NMTOKEN ", & + "NMTOKENS", & + "NOTATION", & + "ENUM ", & + "CDANO ", & + "CDAMB "/) + + integer, parameter :: ATT_REQUIRED = 1 + integer, parameter :: ATT_IMPLIED = 2 + integer, parameter :: ATT_DEFAULT = 4 + integer, parameter :: ATT_FIXED = 3 + + + type attribute_t + character, pointer :: name(:) => null() + integer :: attType = ATT_NULL + integer :: attDefault = ATT_NULL + type(string_list) :: enumerations + character, pointer :: default(:) => null() + logical :: internal = .true. + end type attribute_t + + type attribute_list + type(attribute_t), pointer :: list(:) => null() + end type attribute_list + + type element_t + character, pointer :: name(:) => null() + logical :: empty = .false. + logical :: any = .false. + logical :: mixed = .false. + logical :: id_declared = .false. + logical :: internal = .true. + type (content_particle_t), pointer :: cp => null() + character, pointer :: model(:) => null() + type(attribute_list) :: attlist + end type element_t + + type element_list + type(element_t), pointer :: list(:) => null() + end type element_list + + + public :: element_t + public :: element_list + + public :: attribute_t + public :: attribute_list + + public :: init_element_list + public :: destroy_element_list + public :: existing_element + public :: declared_element + public :: get_element + public :: add_element + + public :: parse_dtd_element + + public :: init_attribute_list + public :: destroy_attribute_list + + + public :: parse_dtd_attlist + + public :: report_declarations + + public :: attribute_has_default + public :: get_attlist_size + public :: get_attribute_declaration + public :: express_attribute_declaration + + public :: att_value_normalize + + public :: get_att_type_enum + + public :: ATT_NULL + public :: ATT_CDATA + public :: ATT_ID + public :: ATT_IDREF + public :: ATT_IDREFS + public :: ATT_ENTITY + public :: ATT_ENTITIES + public :: ATT_NMTOKEN + public :: ATT_NMTOKENS + public :: ATT_NOTATION + public :: ATT_ENUM + + public :: ATT_CDANO + public :: ATT_CDAMB + + public :: ATT_REQUIRED + public :: ATT_IMPLIED + public :: ATT_DEFAULT + public :: ATT_FIXED + + public :: ATT_TYPES + + interface get_attribute_declaration + module procedure get_attdecl_by_index + module procedure get_attdecl_by_name + end interface + +contains + + subroutine init_element_list(e_list) + type(element_list), intent(inout) :: e_list + + allocate(e_list%list(0)) + end subroutine init_element_list + + subroutine destroy_element_list(e_list) + type(element_list), intent(inout) :: e_list + + integer :: i + + do i = 1, size(e_list%list) + deallocate(e_list%list(i)%name) + if (associated(e_list%list(i)%cp)) call destroyCPtree(e_list%list(i)%cp) + if (associated(e_list%list(i)%model)) deallocate(e_list%list(i)%model) + call destroy_attribute_list(e_list%list(i)%attlist) + enddo + deallocate(e_list%list) + end subroutine destroy_element_list + + function existing_element(e_list, name) result(p) + type(element_list), intent(in) :: e_list + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + do i = 1, size(e_list%list) + if (str_vs(e_list%list(i)%name)==name) then + p = .true. + exit + endif + enddo + end function existing_element + + function declared_element(e_list, name) result(p) + type(element_list), intent(in) :: e_list + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + do i = 1, size(e_list%list) + if (str_vs(e_list%list(i)%name)==name) then + p = associated(e_list%list(i)%model) + exit + endif + enddo + end function declared_element + + function get_element(e_list, name) result(e) + type(element_list), intent(in) :: e_list + character(len=*), intent(in) :: name + type(element_t), pointer :: e + + integer :: i + + do i = 1, size(e_list%list) + if (str_vs(e_list%list(i)%name)==name) then + e => e_list%list(i) + return + endif + enddo + e => null() + end function get_element + + function add_element(e_list, name) result(e) + type(element_list), intent(inout) :: e_list + character(len=*), intent(in) :: name + type(element_t), pointer :: e + + type(element_t), pointer :: temp(:) + integer :: i + + temp => e_list%list + + allocate(e_list%list(size(temp)+1)) + do i = 1, size(temp) + e_list%list(i)%name => temp(i)%name + e_list%list(i)%model => temp(i)%model + e_list%list(i)%empty = temp(i)%empty + e_list%list(i)%any = temp(i)%any + e_list%list(i)%mixed = temp(i)%mixed + e_list%list(i)%cp => temp(i)%cp + e_list%list(i)%id_declared = temp(i)%id_declared + e_list%list(i)%internal = temp(i)%internal + e_list%list(i)%attlist%list => temp(i)%attlist%list + enddo + deallocate(temp) + e => e_list%list(i) + e%name => vs_str_alloc(name) + call init_attribute_list(e%attlist) + + end function add_element + + subroutine parse_dtd_element(contents, xv, stack, element, internal) + character(len=*), intent(in) :: contents + integer, intent(in) :: xv + type(error_stack), intent(inout) :: stack + type(element_t), pointer :: element + logical, intent(in) :: internal + + integer :: state + integer :: i, nbrackets + logical :: mixed, empty, any + character :: c + character, pointer :: order(:), name(:), temp(:) + type(content_particle_t), pointer :: top, current, tcp + logical :: mixed_additional, firstChild + + ! FIXME should we check namespaces here (for element names) + ! checking duplicates - valid or wf? - and only for MIXED? + + order => null() + name => null() + temp => null() + + any = .false. + empty = .false. + mixed = .false. + nbrackets = 0 + mixed_additional = .false. + firstChild = .true. + state = ST_START + + top => null() + + do i = 1, len(contents) + 1 + if (i<=len(contents)) then + c = contents(i:i) + else + c = ' ' + endif + + if (state==ST_START) then + !write(*,*)'ST_START' + if (verify(c, XML_WHITESPACE)==0) then + continue + elseif (verify(c, 'EMPTYANY')==0) then + name => vs_str_alloc(c) + state = ST_EMPTYANY + elseif (c=='(') then + order => vs_str_alloc(" ") + nbrackets = 1 + top => newCP() + current => top + state = ST_FIRSTCHILD + else + call add_error(stack, & + 'Unexpected character "'//c//'" at start of ELEMENT specification') + goto 100 + endif + + elseif (state==ST_EMPTYANY) then + !write(*,*)'ST_EMPTYANY' + if (verify(c, upperCase)==0) then + temp => name + name => vs_str_alloc(str_vs(temp)//c) + deallocate(temp) + elseif (verify(c, XML_WHITESPACE)==0) then + if (str_vs(name)=='EMPTY') then + empty = .true. + top => newCP(empty=.true.) + current => top + elseif (str_vs(name)=='ANY') then + any = .true. + top => newCP(any=.true.) + current => top + else + call add_error(stack, & + 'Unexpected ELEMENT specification; expecting EMPTY or ANY') + goto 100 + endif + deallocate(name) + state = ST_END + else + call add_error(stack, & + 'Unexpected ELEMENT specification; expecting EMPTY or ANY') + goto 100 + endif + + elseif (state==ST_FIRSTCHILD) then + !write(*,*)'ST_FIRSTCHILD' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='#') then + mixed = .true. + state = ST_PCDATA + name => vs_str_alloc("") + elseif (isInitialNameChar(c, xv)) then + allocate(name(1)) + name(1) = c + state = ST_NAME + elseif (c=='(') then + nbrackets = nbrackets + 1 + deallocate(order) + tcp => newCP() + current%firstChild => tcp + tcp%parent => current + current => tcp + order => vs_str_alloc(" ") + state = ST_CHILD + else + call add_error(stack, & + 'Unexpected character in ELEMENT specification') + goto 100 + endif + + elseif (state==ST_PCDATA) then + !write(*,*)'ST_PCDATA' + if (verify(c, 'PCDATA')==0) then + temp => name + name => vs_str_alloc(str_vs(temp)//c) + deallocate(temp) + elseif (verify(c, XML_WHITESPACE)==0) then + if (str_vs(name)=='PCDATA') then + deallocate(name) + else + call add_error(stack, & + 'Unexpected token after #') + goto 100 + endif + ! Must be first child + current%operator = OP_MIXED + tcp => newCP(name="#PCDATA") + current%firstChild => tcp + tcp%parent => current + current => tcp + firstChild = .false. + state = ST_SEPARATOR + elseif (c==')') then + if (str_vs(name)=='PCDATA') then + deallocate(name) + nbrackets = 0 + state = ST_AFTERLASTBRACKET + deallocate(order) + else + call add_error(stack, & + 'Unexpected token after #') + goto 100 + endif + ! Must be first child + current%operator = OP_MIXED + tcp => newCP(name="#PCDATA") + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + elseif (c=='|') then + if (str_vs(name)=='PCDATA') then + firstChild = .false. + deallocate(name) + else + call add_error(stack, & + 'Unexpected token after #') + goto 100 + endif + ! Must be first child + current%operator = OP_MIXED + tcp => newCP(name="#PCDATA") + current%firstChild => tcp + tcp%parent => current + current => tcp + firstChild = .false. + order(1) = '|' + state = ST_CHILD + elseif (c==',') then + call add_error(stack, & + 'Ordered specification not allowed for Mixed elements') + goto 100 + else + call add_error(stack, & + 'Unexpected character in ELEMENT specification') + goto 100 + endif + + elseif (state==ST_NAME) then + !write(*,*)'ST_NAME' + if (isNameChar(c, xv)) then + temp => name + name => vs_str_alloc(str_vs(temp)//c) + deallocate(temp) + elseif (scan(c, "?+*")>0) then + if (mixed) then + call add_error(stack, & + 'Repeat operators forbidden for Mixed elements') + goto 100 + endif + tcp => newCP(name=str_vs(name), repeat=c) + deallocate(name) + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + else + current%nextSibling => tcp + tcp%parent => current%parent + endif + current => tcp + if (c=="+") call transformCPPlus(current) + state = ST_SEPARATOR + elseif (verify(c, XML_WHITESPACE)==0) then + if (mixed) mixed_additional = .true. + tcp => newCP(name=str_vs(name)) + deallocate(name) + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + else + current%nextSibling => tcp + tcp%parent => current%parent + endif + current => tcp + state = ST_SEPARATOR + elseif (scan(c,',|')>0) then + if (order(nbrackets)=='') then + order(nbrackets)=c + elseif (order(nbrackets)/=c) then + call add_error(stack, & + 'Cannot mix ordered and unordered elements') + goto 100 + endif + if (mixed) mixed_additional = .true. + tcp => newCP(name=str_vs(name)) + deallocate(name) + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + else + current%nextSibling => tcp + tcp%parent => current%parent + endif + current => tcp + if (c=="|".and.current%parent%operator/=OP_MIXED) & + current%parent%operator = OP_CHOICE + state = ST_CHILD + elseif (c==')') then + if (mixed) mixed_additional = .true. + nbrackets = nbrackets - 1 + if (nbrackets==0) then + state = ST_AFTERLASTBRACKET + deallocate(order) + else + temp => order + allocate(order(nbrackets)) + order = temp(:size(order)) + deallocate(temp) + state = ST_AFTERBRACKET + endif + tcp => newCP(name=str_vs(name)) + deallocate(name) + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + else + current%nextSibling => tcp + tcp%parent => current%parent + current => current%parent + if (.not.check_duplicates(current)) & + goto 100 + endif + else + call add_error(stack, & + 'Unexpected character found after element name') + goto 100 + endif + + elseif (state==ST_CHILD) then + !write(*,*)'ST_CHILD' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='#') then + call add_error(stack, & + '# forbidden except as first child element') + goto 100 + elseif (isInitialNameChar(c, xv)) then + name => vs_str_alloc(c) + state = ST_NAME + elseif (c=='(') then + if (mixed) then + call add_error(stack, & + 'Nested brackets forbidden for Mixed content') + goto 100 + endif + tcp => newCP() + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + else + current%nextSibling => tcp + tcp%parent => current%parent + firstChild = .true. + endif + current => tcp + nbrackets = nbrackets + 1 + temp => order + order => vs_str_alloc(str_vs(temp)//" ") + deallocate(temp) + else + call add_error(stack, & + 'Unexpected character "'//c//'" found after (') + goto 100 + endif + + elseif (state==ST_SEPARATOR) then + !write(*,*)'ST_SEPARATOR' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='#') then + call add_error(stack, & + '#PCDATA must be first in list') + goto 100 + elseif (scan(c,'|,')>0) then + if (order(nbrackets)=='') then + order(nbrackets) = c + elseif (order(nbrackets)/=c) then + call add_error(stack, & + 'Cannot mix ordered and unordered elements') + goto 100 + endif + if (c=="|".and.current%parent%operator/=OP_MIXED) & + current%parent%operator = OP_CHOICE + state = ST_CHILD + elseif (c==')') then + nbrackets = nbrackets - 1 + if (nbrackets==0) then + state = ST_AFTERLASTBRACKET + deallocate(order) + else + temp => order + allocate(order(nbrackets)) + order = temp(:size(order)) + deallocate(temp) + state = ST_AFTERBRACKET + endif + current => current%parent + if (.not.check_duplicates(current)) & + goto 100 + else + call add_error(stack, & + 'Unexpected character found in element declaration.') + goto 100 + endif + + elseif (state==ST_AFTERBRACKET) then + !write(*,*)'ST_AFTERBRACKET' + if (c=='*') then + current%repeater = REP_ASTERISK + state = ST_SEPARATOR + elseif (c=='+') then + call transformCPPlus(current) + state = ST_SEPARATOR + elseif (c=='?') then + current%repeater = REP_QUESTION_MARK + state = ST_SEPARATOR + elseif (verify(c, XML_WHITESPACE)==0) then + state = ST_SEPARATOR + elseif (scan(c,'|,')>0) then + if (order(nbrackets)=='') then + order(nbrackets) = c + elseif (order(nbrackets)/=c) then + call add_error(stack, & + 'Cannot mix ordered and unordered elements') + goto 100 + endif + if (c=="|".and.current%parent%operator/=OP_MIXED) & + current%parent%operator = OP_CHOICE + state = ST_CHILD + elseif (c==')') then + nbrackets = nbrackets - 1 + if (nbrackets==0) then + deallocate(order) + state = ST_AFTERLASTBRACKET + else + temp => order + allocate(order(nbrackets)) + order = temp(:size(order)) + deallocate(temp) + state = ST_AFTERBRACKET + endif + current => current%parent + if (.not.check_duplicates(current)) & + goto 100 + else + call add_error(stack, & + 'Unexpected character "'//c//'"found after ")"') + goto 100 + endif + + elseif (state==ST_AFTERLASTBRACKET) then + !write(*,*)'ST_AFTERLASTBRACKET' + if (c=='*') then + state = ST_END + current%repeater = REP_ASTERISK + elseif (c=='+') then + if (mixed) then + call add_error(stack, & + '+ operator disallowed for Mixed elements') + goto 100 + endif + call transformCPPlus(current) + state = ST_END + elseif (c=='?') then + if (mixed) then + call add_error(stack, & + '? operator disallowed for Mixed elements') + goto 100 + endif + current%repeater = REP_QUESTION_MARK + state = ST_END + elseif (verify(c, XML_WHITESPACE)==0) then + if (mixed) then + if (mixed_additional) then + call add_error(stack, & + 'Missing "*" at end of Mixed element specification') + goto 100 + endif + endif + state = ST_END + else + call add_error(stack, & + 'Unexpected character "'//c//'" found after final ")"') + goto 100 + endif + + elseif (state==ST_END) then + !write(*,*)'ST_END' + if (verify(c, XML_WHITESPACE)==0) then + continue + else + call add_error(stack, & + 'Unexpected token found after end of element specification') + goto 100 + endif + + endif + + enddo + + if (state/=ST_END) then + call add_error(stack, "Error in parsing contents of element declaration") + goto 100 + endif + + if (associated(element)) then + element%any = any + element%empty = empty + element%mixed = mixed + element%model => vs_str_alloc(trim(strip_spaces(contents))) + element%cp => top + element%internal = internal +! For debugging it may be useful to dump the result here... +! Also need to use the subroutine. +! call dumpCPtree(top) + else + if (associated(top)) call destroyCPtree(top) + endif + return + +100 if (associated(order)) deallocate(order) + if (associated(name)) deallocate(name) + if (associated(top)) call destroyCPtree(top) + + contains + function strip_spaces(s1) result(s2) + character(len=*) :: s1 + character(len=len(s1)) :: s2 + integer :: i, i2 + i2 = 1 + do i = 1, len(s1) + if (verify(s1(i:i), XML_WHITESPACE)==0) cycle + s2(i2:i2) = s1(i:i) + i2 = i2 + 1 + end do + s2(i2:) = '' + end function strip_spaces + + function check_duplicates(cp) result(p) + type(content_particle_t), pointer :: cp + logical :: p + + type(string_list) :: sl + type(content_particle_t), pointer :: tcp + + if (cp%operator==OP_SEQ) then + p = .true. + return + endif + + call init_string_list(sl) + tcp => cp%firstChild + p = .false. + do while (associated(tcp)) + if (tcp%operator==OP_NAME) then + if (registered_string(sl, str_vs(tcp%name))) then + call destroy_string_list(sl) + if (cp%operator==OP_MIXED) then + call add_error(stack, & + "Duplicate element names found in MIXED") + elseif (cp%operator==OP_CHOICE) then + call add_error(stack, & + "Duplicate element names found in CHOICE") + endif + return + else + call add_string(sl, str_vs(tcp%name)) + endif + endif + tcp => tcp%nextSibling + enddo + p = .true. + call destroy_string_list(sl) + end function check_duplicates + end subroutine parse_dtd_element + + + subroutine init_attribute_list(a_list) + type(attribute_list), intent(inout) :: a_list + + allocate(a_list%list(0)) + end subroutine init_attribute_list + + subroutine destroy_attribute_t(a) + type(attribute_t), pointer :: a + + if (associated(a%name)) deallocate(a%name) + if (associated(a%default)) deallocate(a%default) + call destroy_string_list(a%enumerations) + + deallocate(a) + end subroutine destroy_attribute_t + + subroutine destroy_attribute_list(a_list) + type(attribute_list), intent(inout) :: a_list + + integer :: i + + do i = 1, size(a_list%list) + deallocate(a_list%list(i)%name) + if (associated(a_list%list(i)%default)) deallocate(a_list%list(i)%default) + call destroy_string_list(a_list%list(i)%enumerations) + enddo + deallocate(a_list%list) + + end subroutine destroy_attribute_list + + function existing_attribute(a_list, name) result(p) + type(attribute_list), intent(inout) :: a_list + character(len=*), intent(in) :: name + logical :: p + + integer :: i + p = .false. + do i = 1, size(a_list%list) + p = (str_vs(a_list%list(i)%name)==name) + if (p) exit + enddo + end function existing_attribute + + function add_attribute(a_list, name, internal) result(a) + type(attribute_list), intent(inout) :: a_list + character(len=*), intent(in) :: name + logical, intent(in) :: internal + type(attribute_t), pointer :: a + + integer :: i + type(attribute_t), pointer :: temp(:) + + temp => a_list%list + allocate(a_list%list(size(temp)+1)) + do i = 1, size(temp) + a_list%list(i)%name => temp(i)%name + a_list%list(i)%atttype = temp(i)%atttype + a_list%list(i)%attdefault = temp(i)%attdefault + a_list%list(i)%default => temp(i)%default + a_list%list(i)%enumerations%list => temp(i)%enumerations%list + a_list%list(i)%internal = temp(i)%internal + enddo + deallocate(temp) + a => a_list%list(i) + + a%name => vs_str_alloc(name) + call init_string_list(a%enumerations) + a%internal = internal + + end function add_attribute + + function get_attribute(a_list, name) result(a) + type(attribute_list), intent(inout) :: a_list + character(len=*), intent(in) :: name + type(attribute_t), pointer :: a + + integer :: i + do i = 1, size(a_list%list) + if (str_vs(a_list%list(i)%name)==name) then + a => a_list%list(i) + exit + endif + enddo + end function get_attribute + + subroutine parse_dtd_attlist(contents, xv, namespaces, validCheck, stack, elem, internal) + character(len=*), intent(in) :: contents + integer, intent(in) :: xv + logical, intent(in) :: validCheck + logical, intent(in) :: namespaces + type(error_stack), intent(inout) :: stack + type(element_t), pointer :: elem + logical, intent(in) :: internal + + integer :: i + integer :: state + character :: c, q + character, pointer :: name(:), attType(:), default(:), value(:), temp(:) + + type(attribute_t), pointer :: ca + type(attribute_t), pointer :: ignore_att + + ignore_att => null() + ! We need ignore_att to process but not take account of duplicate attributes + ! elem is optional so we can not record declarations if necessary. + ca => null() + name => null() + attType => null() + default => null() + value => null() + temp => null() + + state = ST_START + + do i = 1, len(contents) + 1 + if (in_error(stack)) exit + if (i<=len(contents)) then + c = contents(i:i) + else + c = " " + endif + + if (state==ST_START) then + !write(*,*)'ST_START' + if (verify(c, XML_WHITESPACE)==0) cycle + if (isInitialNameChar(c, xv)) then + name => vs_str_alloc(c) + state = ST_NAME + else + call add_error(stack, & + 'Unexpected character in Attlist') + endif + + elseif (state==ST_NAME) then + !write(*,*)'ST_NAME' + if (isNameChar(c, xv)) then + temp => vs_str_alloc(str_vs(name)//c) + deallocate(name) + name => temp + elseif (verify(c, XML_WHITESPACE)==0) then + if (namespaces.and..not.checkQName(str_vs(name), xv)) then + call add_error(stack, & + "Attribute name in ATTLIST must be QName") + elseif (associated(elem)) then + if (existing_attribute(elem%attlist, str_vs(name))) then + if (associated(ignore_att)) call destroy_attribute_t(ignore_att) + allocate(ignore_att) + call init_string_list(ignore_att%enumerations) + ignore_att%name => vs_vs_alloc(name) + ca => ignore_att + else + ca => add_attribute(elem%attlist, str_vs(name), internal) + endif + else + if (associated(ignore_att)) call destroy_attribute_t(ignore_att) + allocate(ignore_att) + call init_string_list(ignore_att%enumerations) + ignore_att%name => vs_vs_alloc(name) + ca => ignore_att + endif + deallocate(name) + state = ST_AFTERNAME + else + call add_error(stack, & + 'Unexpected character in Attlist Name') + endif + + elseif (state==ST_AFTERNAME) then + !write(*,*)'ST_AFTERNAME' + if (verify(c, XML_WHITESPACE)==0) cycle + if (verify(c, upperCase)==0) then + attType => vs_str_alloc(c) + state = ST_ATTTYPE + elseif (c=='(') then + allocate(value(0)) + ca%attType = ATT_ENUM + state = ST_ENUMERATION + else + call add_error(stack, & + 'Unexpected error after Attlist Name') + endif + + elseif (state==ST_ATTTYPE) then + !write(*,*)'ST_ATTTYPE' + if (verify(c, upperCase)==0) then + temp => attType + attType => vs_str_alloc(str_vs(temp)//c) + deallocate(temp) + elseif (verify(c, XML_WHITESPACE)==0) then + ! xml:id constraint + if (str_vs(ca%name)=="xml:id" & + .and..not.str_vs(attType)=="ID") then + call add_error(stack, & + "xml:id attribute must be declared as type ID") + elseif (str_vs(attType)=='CDATA') then + ca%attType = ATT_CDATA + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='ID') then + if (validCheck) then + ! Validity Constraint: One ID per Element Type + if (associated(elem)) then + if (elem%id_declared) then + call add_error(stack, & + "Cannot have two declared attributes of type ID on one element type.") + else + elem%id_declared = .true. + endif + endif + endif + ca%attType = ATT_ID + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='IDREF') then + ca%attType = ATT_IDREF + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='IDREFS') then + ca%attType = ATT_IDREFS + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='ENTITY') then + ca%attType = ATT_ENTITY + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='ENTITIES') then + ca%attType = ATT_ENTITIES + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='NMTOKEN') then + ca%attType = ATT_NMTOKEN + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='NMTOKENS') then + ca%attType = ATT_NMTOKENS + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='NOTATION') then + ca%attType = ATT_NOTATION + state = ST_AFTER_NOTATION + else + call add_error(stack, & + 'Unknown AttType') + endif + deallocate(attType) + else + call add_error(stack, & + 'Unexpected character in AttType') + endif + + elseif (state==ST_AFTER_NOTATION) then + !write(*,*)'ST_AFTER_NOTATION' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='(') then + state = ST_NOTATION_LIST + else + call add_error(stack, & + 'Unexpected character after Notation') + endif + + elseif (state==ST_NOTATION_LIST) then + !write(*,*)'ST_NOTATION_LIST' + if (verify(c, XML_WHITESPACE)==0) cycle + if (isInitialNameChar(c, xv)) then + value => vs_str_alloc(c) + state = ST_ENUM_NAME + else + call add_error(stack, & + 'Unexpected character in Notation list') + endif + + elseif (state==ST_ENUMERATION) then + !write(*,*)'ST_ENUMERATION' + if (verify(c, XML_WHITESPACE)==0) cycle + if (isNameChar(c, xv)) then + temp => vs_str_alloc(str_vs(value)//c) + deallocate(value) + value => temp + state = ST_ENUM_NAME + elseif (c=='|') then + call add_error(stack, & + "Missing token in Enumeration") + elseif (c==')') then + call add_error(stack, & + "Missing tokens in Enumeration") + else + call add_error(stack, & + 'Unexpected character in attlist enumeration') + endif + + elseif (state==ST_ENUM_NAME) then + !write(*,*)'ST_ENUM_NAME' + if (isNameChar(c, xv)) then + temp => vs_str_alloc(str_vs(value)//c) + deallocate(value) + value => temp + elseif (verify(c, XML_WHITESPACE)==0) then + if (validCheck.and.registered_string(ca%enumerations, str_vs(value))) then + call add_error(stack, & + "Duplicate enumeration value in ATTLIST") + elseif (namespaces.and.ca%attType==ATT_NOTATION & + .and..not.checkNCName(str_vs(value), xv)) then + call add_error(stack, & + "Notation name must be NCName") + else + call add_string(ca%enumerations, str_vs(value)) + endif + deallocate(value) + state = ST_SEPARATOR + elseif (c=='|') then + if (validCheck.and.registered_string(ca%enumerations, str_vs(value))) then + call add_error(stack, & + "Duplicate enumeration value in ATTLIST") + elseif (namespaces.and.ca%attType==ATT_NOTATION & + .and..not.checkNCName(str_vs(value), xv)) then + call add_error(stack, & + "Notation name must be NCName") + else + call add_string(ca%enumerations, str_vs(value)) + endif + deallocate(value) + if (ca%attType==ATT_NOTATION) then + state = ST_NOTATION_LIST + else + allocate(value(0)) + state = ST_ENUMERATION + endif + elseif (c==')') then + if (size(value)==0) then + call add_error(stack, & + 'Missing token in Enumeration list') + endif + if (validCheck.and.registered_string(ca%enumerations, str_vs(value))) then + call add_error(stack, & + "Duplicate enumeration value in ATTLIST") + elseif (namespaces.and.ca%attType==ATT_NOTATION & + .and..not.checkNCName(str_vs(value), xv)) then + call add_error(stack, & + "Notation name must be NCName") + else + call add_string(ca%enumerations, str_vs(value)) + endif + deallocate(value) + state = ST_AFTER_ATTTYPE_SPACE + else + call add_error(stack, & + 'Unexpected character in attlist enumeration') + endif + + elseif (state==ST_SEPARATOR) then + !write(*,*)'ST_SEPARATOR' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='|') then + if (ca%attType==ATT_NOTATION) then + state = ST_NOTATION_LIST + else + allocate(value(0)) + state = ST_ENUMERATION + endif + elseif (c==')') then + state = ST_AFTER_ATTTYPE_SPACE + else + call add_error(stack, & + 'Unexpected character in attlist enumeration') + endif + + elseif (state==ST_AFTER_ATTTYPE_SPACE) then + if (verify(c, XML_WHITESPACE)/=0) then + call add_error(stack, & + 'Missing whitespace in attlist enumeration') + endif + state = ST_AFTER_ATTTYPE + + elseif (state==ST_AFTER_ATTTYPE) then + !write(*,*)'ST_AFTER_ATTTYPE' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='#') then + allocate(default(0)) + state = ST_DEFAULT_DECL + elseif (c=='"'.or.c=="'") then + if (validCheck) then + ! Validity Constraint: ID Attribute Default + if (ca%attType==ATT_ID) & + call add_error(stack, & + "Attribute of type ID may not have default value") + endif + ca%attDefault = ATT_DEFAULT + q = c + allocate(value(0)) + state = ST_DEFAULTVALUE + else + call add_error(stack, & + 'Unexpected character after AttType') + endif + + elseif (state==ST_DEFAULT_DECL) then + !write(*,*)'ST_DEFAULT_DECL' + if (verify(c, upperCase)==0) then + temp => vs_str_alloc(str_vs(default)//c) + deallocate(default) + default => temp + elseif (verify(c, XML_WHITESPACE)==0) then + if (str_vs(default)=='REQUIRED') then + ca%attdefault = ATT_REQUIRED + deallocate(default) + state = ST_START + elseif (str_vs(default)=='IMPLIED') then + ca%attdefault = ATT_IMPLIED + deallocate(default) + state = ST_START + elseif (str_vs(default)=='FIXED') then + if (validCheck) then + ! Validity Constraint: ID Attribute Default + if (ca%attType==ATT_ID) & + call add_error(stack, & + "Attribute of type ID may not have FIXED value") + endif + ca%attdefault = ATT_FIXED + deallocate(default) + state = ST_AFTERDEFAULTDECL + else + call add_error(stack, & + 'Unknown Default declaration') + endif + else + call add_error(stack, & + 'Unexpected character in Default declaration') + endif + + elseif (state==ST_AFTERDEFAULTDECL) then + !write(*,*)'ST_AFTERDEFAULTDECL' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='"') then + q = c + allocate(value(0)) + state = ST_DEFAULTVALUE + elseif (c=="'") then + q = c + allocate(value(0)) + state = ST_DEFAULTVALUE + else + call add_error(stack, & + 'Unexpected character after Default declaration') + endif + + elseif (state==ST_DEFAULTVALUE) then + !write(*,*)'ST_DEFAULTVALUE' + if (c==q) then + if (ca%attType/=ATT_CDATA) then + temp => vs_str_alloc(att_value_normalize(str_vs(value))) + deallocate(value) + value => temp + endif + if (validCheck) then + select case(ca%attType) + ! Can't have ID with defaults + case (ATT_IDREF) + ! VC: IDREF + if (namespaces) then + if (.not.checkNCName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type IDREF must have a value which is an XML NCName") + else + if (.not.checkName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type IDREF must have a value which is an XML Name") + endif + case (ATT_IDREFS) + ! VC: IDREF + if (namespaces) then + if (.not.checkNCNames(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type IDREFS must have a value which contains only XML NCNames") + else + if (.not.checkNames(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type IDREFS must have a value which contains only XML Names") + endif + case (ATT_ENTITY) + ! VC: Entity Name + if (namespaces) then + if (.not.checkNCName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENTITY must have a value which is an XML NCName") + else + if (.not.checkName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENTITY must have a value which is an XML Name") + endif + case (ATT_ENTITIES) + ! VC: Entity Name + if (namespaces) then + if (.not.checkNames(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENTITIES must have a value which contains only XML NCNames") + else + if (.not.checkNames(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENTITIES must have a value which contains only XML Names") + endif + case (ATT_NMTOKEN) + ! VC Name Token + if (.not.checkNmtoken(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type NMTOKEN must have a value which is a NMTOKEN") + case (ATT_NMTOKENS) + ! VC: Name Token + if (.not.checkNmtokens(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type NMTOKENS must have a value which contain only NMTOKENs") + case (ATT_NOTATION) + ! VC: Notation Attributes + if (namespaces) then + if (.not.checkNCName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type NOTATION must have a value which is an XMLNCName") + else + if (.not.checkName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type NOTATION must have a value which is an XML Name") + endif + case (ATT_ENUM) + ! VC: Enumeration + if (.not.checkNmtoken(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENUM must have a value which is an NMTOKENs") + if (.not.registered_string(ca%enumerations, str_vs(value))) & + call add_error(stack, & + "Default value of ENUM does not match permitted values") + end select + endif + if (.not.in_error(stack)) then + if (ca%attType==ATT_ENTITIES) then + call destroy_string_list(ca%enumerations) + ca%enumerations = tokenize_to_string_list(str_vs(value)) + endif + ca%default => value + value => null() + state = ST_START + endif + else + temp => vs_str_alloc(str_vs(value)//c) + deallocate(value) + value => temp + endif + + endif + + enddo + + if (associated(ignore_att)) call destroy_attribute_t(ignore_att) + + if (.not.in_error(stack)) then + if (state==ST_START) then + return + else + call add_error(stack, & + 'Incomplete Attlist declaration') + endif + endif + + if (associated(name)) deallocate(name) + if (associated(attType)) deallocate(attType) + if (associated(default)) deallocate(default) + if (associated(value)) deallocate(value) + + end subroutine parse_dtd_attlist + + subroutine report_declarations(elem, attributeDecl_handler) + type(element_t), intent(in) :: elem + interface + subroutine attributeDecl_handler(eName, aName, type, mode, value) + character(len=*), intent(in) :: eName + character(len=*), intent(in) :: aName + character(len=*), intent(in) :: type + character(len=*), intent(in), optional :: mode + character(len=*), intent(in), optional :: value + end subroutine attributeDecl_handler + end interface + + integer :: i + character(len=8) :: type + character(len=8) :: mode + type(attribute_t), pointer :: a + + do i = 1, size(elem%attlist%list) + a => elem%attlist%list(i) + type = ATT_TYPES(a%attType) + select case (a%attDefault) + case (ATT_REQUIRED) + mode = "REQUIRED" + case (ATT_IMPLIED) + mode = "IMPLIED" + case (ATT_FIXED) + mode = "FIXED" + end select + + if (a%attType==ATT_NOTATION) then + if (a%attDefault==ATT_DEFAULT) then + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + 'NOTATION '//make_token_group(a%enumerations), value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + 'NOTATION '//make_token_group(a%enumerations)) + endif + else + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + 'NOTATION '//make_token_group(a%enumerations), mode=trim(mode), & + value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + 'NOTATION '//make_token_group(a%enumerations), mode=trim(mode)) + endif + endif + elseif (a%attType==ATT_ENUM) then + if (a%attDefault==ATT_DEFAULT) then + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + make_token_group(a%enumerations), value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + make_token_group(a%enumerations)) + endif + else + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + make_token_group(a%enumerations), mode=trim(mode), & + value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + make_token_group(a%enumerations), mode=trim(mode)) + endif + endif + else + if (a%attDefault==ATT_DEFAULT) then + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + trim(type), value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + trim(type)) + endif + else + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + trim(type), mode=trim(mode), value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + trim(type), mode=trim(mode)) + endif + endif + endif + enddo + + + end subroutine report_declarations + + pure function make_token_group_len(s_list) result(n) + type(string_list), intent(in) :: s_list + integer :: n + + integer :: i + n = size(s_list%list) + 1 + do i = 1, size(s_list%list) + n = n + size(s_list%list(i)%s) + enddo + end function make_token_group_len + + function make_token_group(s_list) result(s) + type(string_list), intent(in) :: s_list + character(len=make_token_group_len(s_list)) :: s + + integer :: i, m, n + s(1:1) = '(' + n = 2 + do i = 1, size(s_list%list)-1 + m = size(s_list%list(i)%s) + s(n:n+m) = str_vs(s_list%list(i)%s)//'|' + n = n + m + 1 + enddo + s(n:) = str_vs(s_list%list(i)%s)//')' + end function make_token_group + + function attribute_has_default(att) result(p) + type(attribute_t), pointer :: att + logical :: p + + if (associated(att)) then + p = att%attDefault==ATT_DEFAULT.or.att%attDefault==ATT_FIXED + else + p = .false. + endif + end function attribute_has_default + + function get_attlist_size(elem) result(n) + type(element_t), pointer :: elem + integer :: n + + if (associated(elem)) then + n = size(elem%attlist%list) + else + n = 0 + endif + end function get_attlist_size + + function get_attdecl_by_index(elem, n) result(att) + type(element_t), pointer :: elem + integer, intent(in) :: n + type(attribute_t), pointer :: att + + att => null() + if (associated(elem)) then + if (n>0.and.n<=size(elem%attlist%list)) then + att => elem%attlist%list(n) + endif + endif + end function get_attdecl_by_index + + function get_attdecl_by_name(elem, name) result(att) + type(element_t), pointer :: elem + character(len=*), intent(in) :: name + type(attribute_t), pointer :: att + + integer :: i + att => null() + if (associated(elem)) then + do i = 1, size(elem%attlist%list) + if (str_vs(elem%attlist%list(i)%name)==name) then + att => elem%attlist%list(i) + return + endif + enddo + endif + end function get_attdecl_by_name + + pure function express_att_decl_len(a) result(n) + type(attribute_t), intent(in) :: a + integer :: n + + if (a%attType==ATT_ENUM) then + n = size(a%name) + else + n = size(a%name)+1+len_trim(ATT_TYPES(a%attType)) + endif + + if (a%attType==ATT_NOTATION & + .or.a%attType==ATT_ENUM) & + n = n + 1 + make_token_group_len(a%enumerations) + + select case(a%attDefault) + case (ATT_REQUIRED) + n = n + len(" #REQUIRED") + case (ATT_IMPLIED) + n = n + len(" #IMPLIED") + case (ATT_DEFAULT) + n = n + len(" ") + case (ATT_FIXED) + n = n + len(" #FIXED") + end select + + if (associated(a%default)) & + n = n + 3 + size(a%default) + end function express_att_decl_len + + function express_attribute_declaration(a) result(s) + type(attribute_t), intent(in) :: a + character(len=express_att_decl_len(a)) :: s + + if (a%attType==ATT_ENUM) then + s = str_vs(a%name) + else + s = str_vs(a%name)//" "//ATT_TYPES(a%attType) + endif + if (a%attType==ATT_NOTATION & + .or.a%attType==ATT_ENUM) & + s = trim(s)//" "//make_token_group(a%enumerations) + + select case(a%attDefault) + case (ATT_REQUIRED) + s = trim(s)//" #REQUIRED" + case (ATT_IMPLIED) + s = trim(s)//" #IMPLIED" + case (ATT_DEFAULT) + s = trim(s)//" " + case (ATT_FIXED) + s = trim(s)//" #FIXED" + end select + + if (associated(a%default)) & + s = trim(s)//" """//str_vs(a%default)//"""" + end function express_attribute_declaration + + function get_att_type_enum(s) result(n) + character(len=*), intent(in) :: s + integer :: n + + select case(s) + case ('CDATA') + n = ATT_CDATA + case ('ID') + n = ATT_ID + case ('IDREF') + n = ATT_IDREF + case ('IDREFS') + n = ATT_IDREFS + case ('NMTOKEN') + n = ATT_NMTOKEN + case ('NMTOKENS') + n = ATT_NMTOKENS + case ('ENTITY') + n = ATT_ENTITY + case ('ENTITIES') + n = ATT_ENTITIES + case ('NOTATION') + n = ATT_NOTATION + case ('CDANO') + n= ATT_CDANO + case ('CDAMB') + n = ATT_CDAMB + end select + end function get_att_type_enum + + pure function att_value_normalize_len(s1) result(n) + character(len=*), intent(in) :: s1 + integer :: n + + integer :: i + logical :: w + + n = 0 + w = .true. + do i = 1, len(s1) + if (w.and.(verify(s1(i:i),XML_WHITESPACE)==0)) cycle + w = .false. + n = n + 1 + if (verify(s1(i:i),XML_WHITESPACE)==0) w = .true. + enddo + if (w) n = n - 1 ! Discard final space + + end function att_value_normalize_len + + function att_value_normalize(s1) result(s2) + character(len=*), intent(in) :: s1 + character(len=att_value_normalize_len(s1)) :: s2 + + integer :: i, i2 + logical :: w + + i = 0 + i2 = 1 + w = .true. + do while (i2<=len(s2)) + i = i + 1 + if (w.and.(verify(s1(i:i),XML_WHITESPACE)==0)) cycle + w = .false. + s2(i2:i2) = s1(i:i) + i2 = i2 + 1 + if (verify(s1(i:i),XML_WHITESPACE)==0) w = .true. + enddo + + end function att_value_normalize + +#endif +end module m_common_element diff --git a/src/xml/common/m_common_elstack.F90 b/src/xml/common/m_common_elstack.F90 new file mode 100644 index 0000000000..ccfca15dc5 --- /dev/null +++ b/src/xml/common/m_common_elstack.F90 @@ -0,0 +1,236 @@ +module m_common_elstack + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str + use m_common_error, only: FoX_fatal + use m_common_content_model, only: content_particle_t, checkCP, & + elementContentCP, emptyContentCP, checkCPToEnd + + implicit none + private + + ! Element stack during parsing. Keeps track of element names + ! and optionally tracks validity of content model + + ! Initial stack size: + integer, parameter :: STACK_SIZE_INIT = 10 + ! Multiplier when stack is exceeded: + real, parameter :: STACK_SIZE_MULT = 1.5 + + type :: elstack_item + character, dimension(:), pointer :: name => null() + type(content_particle_t), pointer :: cp => null() + end type elstack_item + + type :: elstack_t + private + integer :: n_items + type(elstack_item), pointer, dimension(:) :: stack => null() + end type elstack_t + + public :: elstack_t + + public :: push_elstack, pop_elstack, init_elstack, destroy_elstack, reset_elstack, print_elstack + public :: get_top_elstack, is_empty + public :: checkContentModel + public :: checkContentModelToEnd + public :: elementContent + public :: emptyContent + public :: len + + interface len + module procedure number_of_items + end interface + + interface is_empty + module procedure is_empty_elstack + end interface + +contains + + subroutine init_elstack(elstack) + type(elstack_t), intent(inout) :: elstack + + ! We go from 0 (and initialize the 0th string to "") + ! in order that we can safely check the top of an + ! empty stack + allocate(elstack%stack(0:STACK_SIZE_INIT)) + elstack%n_items = 0 + allocate(elstack%stack(0)%name(0)) + + end subroutine init_elstack + + subroutine destroy_elstack(elstack) + type(elstack_t), intent(inout) :: elstack + integer :: i + do i = 0, elstack % n_items + deallocate(elstack%stack(i)%name) + enddo + deallocate(elstack%stack) + end subroutine destroy_elstack + + subroutine reset_elstack(elstack) + type(elstack_t), intent(inout) :: elstack + + call destroy_elstack(elstack) + call init_elstack(elstack) + + end subroutine reset_elstack + + subroutine resize_elstack(elstack) + type(elstack_t), intent(inout) :: elstack + type(elstack_item), dimension(0:ubound(elstack%stack,1)) :: temp + integer :: i, s + + s = ubound(elstack%stack, 1) + + do i = 0, s + temp(i)%name => elstack%stack(i)%name + temp(i)%cp => elstack%stack(i)%cp + enddo + deallocate(elstack%stack) + allocate(elstack%stack(0:nint(s*STACK_SIZE_MULT))) + do i = 0, s + elstack%stack(i)%name => temp(i)%name + elstack%stack(i)%cp => temp(i)%cp + enddo + + end subroutine resize_elstack + + pure function is_empty_elstack(elstack) result(answer) + type(elstack_t), intent(in) :: elstack + logical :: answer + + answer = (elstack%n_items == 0) + end function is_empty_elstack + + function number_of_items(elstack) result(n) + type(elstack_t), intent(in) :: elstack + integer :: n + + n = elstack%n_items + end function number_of_items + + subroutine push_elstack(elstack, name, cp) + type(elstack_t), intent(inout) :: elstack + character(len=*), intent(in) :: name + type(content_particle_t), pointer, optional :: cp + + integer :: n + + n = elstack%n_items + n = n + 1 + if (n == size(elstack%stack)) then + call resize_elstack(elstack) + endif + allocate(elstack%stack(n)%name(len(name))) + elstack%stack(n)%name = vs_str(name) + if (present(cp)) elstack%stack(n)%cp => cp + elstack%n_items = n + + end subroutine push_elstack + + function pop_elstack(elstack) result(item) + type(elstack_t), intent(inout) :: elstack + character(len=merge(size(elstack%stack(elstack%n_items)%name), 0, elstack%n_items > 0)) :: item + + integer :: n + + n = elstack%n_items + if (n == 0) then + call FoX_fatal("Element stack empty") + endif + item = str_vs(elstack%stack(n)%name) + deallocate(elstack%stack(n)%name) + elstack%n_items = n - 1 + + end function pop_elstack + + pure function get_top_elstack(elstack) result(item) + ! Get the top element of the stack, *without popping it*. + type(elstack_t), intent(in) :: elstack + character(len=merge(size(elstack%stack(elstack%n_items)%name), 0, elstack%n_items > 0)) :: item + + integer :: n + + n = elstack%n_items + + if (n==0) then + item = "" + else + item = str_vs(elstack%stack(n)%name) + endif + + end function get_top_elstack + + function checkContentModel(elstack, name) result(p) + type(elstack_t), intent(inout) :: elstack + character(len=*), intent(in) :: name + logical :: p + + type(content_particle_t), pointer :: cp + + integer :: n + n = elstack%n_items + if (n==0) then + p = .true. + else + cp => elstack%stack(n)%cp + p = checkCP(cp, name) + elstack%stack(n)%cp => cp + endif + end function checkContentModel + + function checkContentModelToEnd(elstack) result(p) + type(elstack_t), intent(inout) :: elstack + logical :: p + + type(content_particle_t), pointer :: cp + + integer :: n + n = elstack%n_items + + cp => elstack%stack(n)%cp + p = checkCPToEnd(cp) + + end function checkContentModelToEnd + + function elementContent(elstack) result(p) + type(elstack_t), intent(in) :: elstack + logical :: p + + integer :: n + n = elstack%n_items + if (n==0) then + p = .false. + else + p = elementContentCP(elstack%stack(n)%cp) + endif + end function elementContent + + function emptyContent(elstack) result(p) + type(elstack_t), intent(in) :: elstack + logical :: p + + integer :: n + n = elstack%n_items + if (n==0) then + p = .false. + else + p = emptyContentCP(elstack%stack(n)%cp) + endif + end function emptyContent + + subroutine print_elstack(elstack,unit) + type(elstack_t), intent(in) :: elstack + integer, intent(in) :: unit + integer :: i + + do i = elstack%n_items, 1, -1 + write(unit=unit,fmt=*) elstack%stack(i)%name + enddo + + end subroutine print_elstack + +#endif +end module m_common_elstack diff --git a/src/xml/common/m_common_entities.F90 b/src/xml/common/m_common_entities.F90 new file mode 100644 index 0000000000..071455b297 --- /dev/null +++ b/src/xml/common/m_common_entities.F90 @@ -0,0 +1,475 @@ +module m_common_entities + +#ifndef DUMMYLIB + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc + use fox_m_fsys_format, only: str_to_int_10, str_to_int_16 + use fox_m_utils_uri, only: URI, destroyURI + use m_common_charset, only: digits, hexdigits + use m_common_error, only: FoX_error + + implicit none + private + + type entity_t + logical :: external + logical :: wfc ! Was this entity declared externally or in a PE, where + ! a non-validating processor might not see it? + character(len=1), dimension(:), pointer :: name => null() + character(len=1), dimension(:), pointer :: text => null() + character(len=1), dimension(:), pointer :: publicId => null() + character(len=1), dimension(:), pointer :: systemId => null() + character(len=1), dimension(:), pointer :: notation => null() + type(URI), pointer :: baseURI => null() + end type entity_t + + type entity_list + private + type(entity_t), dimension(:), pointer :: list => null() + end type entity_list + + public :: is_unparsed_entity + public :: is_external_entity + + public :: expand_entity_text + public :: expand_entity_text_len + public :: existing_entity + + public :: expand_char_entity + + public :: expand_entity + public :: expand_entity_len + + public :: entity_t + public :: entity_list + public :: init_entity_list + public :: reset_entity_list + public :: destroy_entity_list + public :: print_entity_list + public :: add_internal_entity + public :: add_external_entity + public :: pop_entity_list + + interface size + module procedure size_el + end interface + + interface is_unparsed_entity + module procedure is_unparsed_entity_ + module procedure is_unparsed_entity_from_list + end interface + + public :: getEntityByIndex + public :: getEntityByName + + public :: size + +contains + + function size_el(el) result(n) + type(entity_list), intent(in) :: el + integer :: n + + n = ubound(el%list, 1) + end function size_el + + function shallow_copy_entity(ent1) result(ent2) + type(entity_t), intent(in) :: ent1 + type(entity_t) :: ent2 + + ent2%external = ent1%external + ent2%wfc = ent1%wfc + ent2%name => ent1%name + ent2%text => ent1%text + ent2%publicId => ent1%publicId + ent2%systemId => ent1%systemId + ent2%notation => ent1%notation + ent2%baseURI => ent1%baseURI + + end function shallow_copy_entity + + function getEntityByIndex(el, i) result(e) + type(entity_list), intent(in) :: el + integer, intent(in) :: i + type(entity_t), pointer :: e + + e => el%list(i) + end function getEntityByIndex + + function getEntityNameByIndex(el, i) result(c) + type(entity_list), intent(in) :: el + integer, intent(in) :: i + character(len=size(el%list(i)%name)) :: c + + c = str_vs(el%list(i)%name) + end function getEntityNameByIndex + + function getEntityByName(el, name) result(e) + type(entity_list), intent(in) :: el + character(len=*), intent(in) :: name + type(entity_t), pointer :: e + + integer :: i + + e => null() + do i = 1, size(el%list) + if (str_vs(el%list(i)%name)==name) then + e => el%list(i) + exit + endif + enddo + end function getEntityByName + + + subroutine destroy_entity(ent) + type(entity_t), intent(inout) :: ent + + deallocate(ent%name) + deallocate(ent%text) + deallocate(ent%publicId) + deallocate(ent%systemId) + deallocate(ent%notation) + + if (associated(ent%baseURI)) call destroyURI(ent%baseURI) + + end subroutine destroy_entity + + + subroutine init_entity_list(ents) + type(entity_list), intent(inout) :: ents + + if (associated(ents%list)) deallocate(ents%list) + allocate(ents%list(0)) + + end subroutine init_entity_list + + + subroutine reset_entity_list(ents) + type(entity_list), intent(inout) :: ents + + call destroy_entity_list(ents) + call init_entity_list(ents) + + end subroutine reset_entity_list + + + subroutine destroy_entity_list(ents) + type(entity_list), intent(inout) :: ents + + integer :: i, n + + n = size(ents%list) + do i = 1, n + call destroy_entity(ents%list(i)) + enddo + deallocate(ents%list) + end subroutine destroy_entity_list + + function pop_entity_list(ents) result(name) + type(entity_list), intent(inout) :: ents + character(len=size(ents%list(size(ents%list))%name)) :: name + + type(entity_t), pointer :: ents_tmp(:) + integer :: i, n + n = size(ents%list) + + ents_tmp => ents%list + allocate(ents%list(n-1)) + do i = 1, n - 1 + ents%list(i) = shallow_copy_entity(ents_tmp(i)) + enddo + name = str_vs(ents_tmp(i)%name) + + call destroy_entity(ents_tmp(i)) + deallocate(ents_tmp) + end function pop_entity_list + + subroutine print_entity_list(ents) + type(entity_list), intent(in) :: ents + + integer :: i, n + + n = size(ents%list) + write(*,'(a)') '>ENTITYLIST' + do i = 1, n + write(*,'(a)') str_vs(ents%list(i)%name) + write(*,'(a)') str_vs(ents%list(i)%text) + write(*,'(a)') str_vs(ents%list(i)%publicId) + write(*,'(a)') str_vs(ents%list(i)%systemId) + write(*,'(a)') str_vs(ents%list(i)%notation) + enddo + write(*,'(a)') ' ents%list + allocate(ents%list(n+1)) + do i = 1, n + ents%list(i) = shallow_copy_entity(ents_tmp(i)) + enddo + deallocate(ents_tmp) + ents%list(i)%external = len(systemId)>0 + ents%list(i)%wfc = wfc + ents%list(i)%name => vs_str_alloc(name) + ents%list(i)%text => vs_str_alloc(text) + ents%list(i)%publicId => vs_str_alloc(publicId) + ents%list(i)%systemId => vs_str_alloc(systemId) + ents%list(i)%notation => vs_str_alloc(notation) + ents%list(i)%baseURI => baseURI + end subroutine add_entity + + + subroutine add_internal_entity(ents, name, text, baseURI, wfc) + type(entity_list), intent(inout) :: ents + character(len=*), intent(in) :: name + character(len=*), intent(in) :: text + type(URI), pointer :: baseURI + logical, intent(in) :: wfc + + call add_entity(ents, name=name, text=text, & + publicId="", systemId="", notation="", baseURI=baseURI, wfc=wfc) + end subroutine add_internal_entity + + + subroutine add_external_entity(ents, name, systemId, baseURI, wfc, publicId, notation) + type(entity_list), intent(inout) :: ents + character(len=*), intent(in) :: name + character(len=*), intent(in) :: systemId + character(len=*), intent(in), optional :: publicId + character(len=*), intent(in), optional :: notation + type(URI), pointer :: baseURI + logical, intent(in) :: wfc + + if (present(publicId) .and. present(notation)) then + call add_entity(ents, name=name, text="", & + publicId=publicId, systemId=systemId, notation=notation, & + wfc=wfc, baseURI=baseURI) + elseif (present(publicId)) then + call add_entity(ents, name=name, text="", & + publicId=publicId, systemId=systemId, notation="", & + wfc=wfc, baseURI=baseURI) + elseif (present(notation)) then + call add_entity(ents, name=name, text="", & + publicId="", systemId=systemId, notation=notation, & + wfc=wfc, baseURI=baseURI) + else + call add_entity(ents, name=name, text="", & + publicId="", systemId=systemId, notation="", & + wfc=wfc, baseURI=baseURI) + endif + end subroutine add_external_entity + + + function is_unparsed_entity_from_list(ents, name) result(p) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + p = (size(ents%list(i)%notation)>0) + exit + endif + enddo + end function is_unparsed_entity_from_list + + function is_unparsed_entity_(ent) result(p) + type(entity_t), intent(in) :: ent + logical :: p + + p = (size(ent%notation)>0) + + end function is_unparsed_entity_ + + + function is_external_entity(ents, name) result(p) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + p = ents%list(i)%external + exit + endif + enddo + end function is_external_entity + + pure function expand_char_entity_len(name) result(n) + character(len=*), intent(in) :: name + integer :: n + + integer :: number + + if (name(1:1) == "#") then + if (name(2:2) == "x") then ! hex character reference + if (verify(name(3:), hexdigits) == 0) then + number = str_to_int_16(name(3:)) + if (0 <= number .and. number <= 128) then + n = 1 + else + n = len(name) + 2 + endif + else + n = 0 + endif + else ! decimal character reference + if (verify(name(3:), digits) == 0) then + number = str_to_int_10(name(2:)) + if (0 <= number .and. number <= 128) then + n = 1 + else + n = len(name) + 2 + endif + else + n = 0 + endif + endif + else + n = 0 + endif + end function expand_char_entity_len + + + function expand_char_entity(name) result(text) + character(len=*), intent(in) :: name + character(len=expand_char_entity_len(name)) :: text + + integer :: number + + select case (len(text)) + case (0) + call FoX_error("Invalid character entity reference") + case (1) + if (name(2:2) == "x") then ! hex character reference + number = str_to_int_16(name(3:)) + else ! decimal character reference + number = str_to_int_10(name(2:)) + endif + text = achar(number) + ! FIXME what about > 127 ... + case default + text = "&"//name//";" + end select + + end function expand_char_entity + + + pure function existing_entity(ents, name) result(p) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + +!FIXME the following test is not entirely in accordance with the valid chars check we do elsewhere... + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + p = .true. + return + endif + enddo + + end function existing_entity + + + pure function expand_entity_text_len(ents, name) result(n) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + integer :: n + + integer :: i + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + n = size(ents%list(i)%text) + endif + enddo + + end function expand_entity_text_len + + + function expand_entity_text(ents, name) result(text) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + character(len=expand_entity_text_len(ents, name)) :: text + + integer :: i + + ! No error checking - make sure entity exists before calling it. + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + text = str_vs(ents%list(i)%text) + exit + endif + enddo + + end function expand_entity_text + + + pure function expand_entity_len(ents, name) result(n) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + integer :: n + + integer :: i + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + n = size(ents%list(i)%text) + endif + enddo + + end function expand_entity_len + + + function expand_entity(ents, name) result(text) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + character(len=expand_entity_len(ents, name)) :: text + + integer :: i + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + text = str_vs(ents%list(i)%text) + endif + enddo + + end function expand_entity + +#endif +end module m_common_entities diff --git a/src/xml/common/m_common_entity_expand.F90 b/src/xml/common/m_common_entity_expand.F90 new file mode 100644 index 0000000000..5657007109 --- /dev/null +++ b/src/xml/common/m_common_entity_expand.F90 @@ -0,0 +1,86 @@ +module m_common_entity_expand + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str + use m_common_entities, only: expand_char_entity + use m_common_error, only: error_stack, add_error + use m_common_namecheck, only: checkName, checkCharacterEntityReference, & + checkRepCharEntityReference + use m_common_struct, only: xml_doc_state + + implicit none + private + + public :: expand_entity_value_alloc + + ! This does the first level of expansion of the contents of an entity + ! reference, for storage during processing. Only character references + ! are expanded. + +contains + + function expand_entity_value_alloc(repl, xds, stack) result(repl_new) + !perform expansion of character entity references + ! check that no parameter entities are present + ! and check that all general entity references are well-formed. + !before storing it. + ! + ! This is only ever called from the SAX parser + ! (might it be called from WXML?) + ! so input & output is with character arrays, not strings. + character, dimension(:), intent(in) :: repl + type(xml_doc_state), intent(in) :: xds + type(error_stack), intent(inout) :: stack + character, dimension(:), pointer :: repl_new + + character, dimension(size(repl)) :: repl_temp + integer :: i, i2, j + + allocate(repl_new(0)) + if (index(str_vs(repl),'%')/=0) then + call add_error(stack, "Not allowed % in internal subset general entity value") + return + endif + + i = 1 + i2 = 1 + do + if (i>size(repl)) exit + if (repl(i)=='&') then + j = index(str_vs(repl(i+1:)),';') + if (j==0) then + call add_error(stack, "Not allowed bare & in entity value") + return + elseif (checkName(str_vs(repl(i+1:i+j-1)), xds%xml_version)) then + repl_temp(i2:i2+j) = repl(i:i+j) + i = i + j + 1 + i2 = i2 + j + 1 + ! For SAX, we need to be able to represent the character: + elseif (checkRepCharEntityReference(str_vs(repl(i+1:i+j-1)), xds%xml_version)) then + !if it is ascii then + repl_temp(i2:i2) = vs_str(expand_char_entity(str_vs(repl(i+1:i+j-1)))) + i = i + j + 1 + i2 = i2 + 1 + elseif (checkCharacterEntityReference(str_vs(repl(i+1:i+j-1)), xds%xml_version)) then + ! We can't represent it. Issue an error and stop. + call add_error(stack, "Unable to digest character entity reference in entity value, sorry.") + return + else + call add_error(stack, "Invalid entity reference in entity value") + return + endif + else + repl_temp(i2) = repl(i) + i = i + 1 + i2 = i2 + 1 + endif + enddo + + deallocate(repl_new) + allocate(repl_new(i2-1)) + repl_new = repl_temp(:i2-1) + + end function expand_entity_value_alloc + +#endif +end module m_common_entity_expand diff --git a/src/xml/common/m_common_error.F90 b/src/xml/common/m_common_error.F90 new file mode 100644 index 0000000000..0a1f1c1a8d --- /dev/null +++ b/src/xml/common/m_common_error.F90 @@ -0,0 +1,211 @@ +module m_common_error + +#ifndef DUMMYLIB + use fox_m_fsys_abort_flush, only: pxfabort, pxfflush + use fox_m_fsys_array_str, only: vs_str_alloc + + implicit none + private + + integer, parameter :: ERR_NULL = 0 + integer, parameter :: ERR_WARNING = 1 + integer, parameter :: ERR_ERROR = 2 + integer, parameter :: ERR_FATAL = 3 +#endif + logical, save :: errors_are_fatal = .false. + logical, save :: warnings_are_fatal = .false. + +#ifndef DUMMYLIB + type error_t + integer :: severity = ERR_NULL + integer :: error_code = 0 + character, dimension(:), pointer :: msg => null() + end type error_t + + type error_stack + type(error_t), dimension(:), pointer :: stack => null() + end type error_stack + + interface FoX_warning + module procedure FoX_warning_base + end interface + + interface FoX_error + module procedure FoX_error_base + end interface + + interface FoX_fatal + module procedure FoX_fatal_base + end interface + + public :: ERR_NULL + public :: ERR_WARNING + public :: ERR_ERROR + public :: ERR_FATAL + + public :: error_t + public :: error_stack + + public :: init_error_stack + public :: destroy_error_stack + + public :: FoX_warning + public :: FoX_error + public :: FoX_fatal + + public :: FoX_warning_base + public :: FoX_error_base + public :: FoX_fatal_base + + public :: add_error + public :: in_error + +#endif + public :: FoX_set_fatal_errors + public :: FoX_get_fatal_errors + public :: FoX_set_fatal_warnings + public :: FoX_get_fatal_warnings + +contains +#ifndef DUMMYLIB + + subroutine FoX_warning_base(msg) + ! Emit warning, but carry on. + character(len=*), intent(in) :: msg + + if (warnings_are_fatal) then + write(0,'(a)') 'FoX warning made fatal' + call FoX_fatal_base(msg) + endif + + write(0,'(a)') 'WARNING(FoX)' + write(0,'(a)') msg + call pxfflush(0) + + end subroutine FoX_warning_base + + + subroutine FoX_error_base(msg) + ! Emit error message and stop. + ! No clean up is done here, but this can + ! be overridden to include clean-up routines + character(len=*), intent(in) :: msg + + if (errors_are_fatal) then + write(0,'(a)') 'FoX error made fatal' + call FoX_fatal_base(msg) + endif + + write(0,'(a)') 'ERROR(FoX)' + write(0,'(a)') msg + call pxfflush(0) + + stop + + end subroutine FoX_error_base + + subroutine FoX_fatal_base(msg) + !Emit error message and abort with coredump. + !No clean-up occurs + + character(len=*), intent(in) :: msg + + write(0,'(a)') 'ABORT(FOX)' + write(0,'(a)') msg + call pxfflush(0) + + call pxfabort() + + end subroutine FoX_fatal_base + + + subroutine init_error_stack(stack) + type(error_stack), intent(inout) :: stack + + allocate(stack%stack(0)) + end subroutine init_error_stack + + + subroutine destroy_error_stack(stack) + type(error_stack), intent(inout) :: stack + + integer :: i + + do i = 1, size(stack%stack) + deallocate(stack%stack(i)%msg) + enddo + deallocate(stack%stack) + end subroutine destroy_error_stack + + + subroutine add_error(stack, msg, severity, error_code) + type(error_stack), intent(inout) :: stack + character(len=*), intent(in) :: msg + integer, intent(in), optional :: severity + integer, intent(in), optional :: error_code + + integer :: i, n + type(error_t), dimension(:), pointer :: temp_stack + + if (.not.associated(stack%stack)) & + call init_error_stack(stack) + + n = size(stack%stack) + + temp_stack => stack%stack + allocate(stack%stack(n+1)) + do i = 1, size(temp_stack) + stack%stack(i)%msg => temp_stack(i)%msg + stack%stack(i)%severity = temp_stack(i)%severity + stack%stack(i)%error_code = temp_stack(i)%error_code + enddo + deallocate(temp_stack) + + stack%stack(n+1)%msg => vs_str_alloc(msg) + if (present(severity)) then + stack%stack(n+1)%severity = severity + else + stack%stack(n+1)%severity = ERR_ERROR + endif + if (present(error_code)) then + stack%stack(n+1)%error_code = error_code + else + stack%stack(n+1)%error_code = -1 + endif + + end subroutine add_error + + + function in_error(stack) result(p) + type(error_stack), intent(in) :: stack + logical :: p + + if (associated(stack%stack)) then + p = (size(stack%stack) > 0) + else + p = .false. + endif + end function in_error + +#endif + subroutine FoX_set_fatal_errors(newvalue) + logical, intent(in) :: newvalue + errors_are_fatal = newvalue + end subroutine FoX_set_fatal_errors + + function FoX_get_fatal_errors() + logical :: FoX_get_fatal_errors + FoX_get_fatal_errors = errors_are_fatal + end function FoX_get_fatal_errors + + subroutine FoX_set_fatal_warnings(newvalue) + logical, intent(in) :: newvalue + warnings_are_fatal = newvalue + end subroutine FoX_set_fatal_warnings + + function FoX_get_fatal_warnings() + logical :: FoX_get_fatal_warnings + FoX_get_fatal_warnings = warnings_are_fatal + end function FoX_get_fatal_warnings + +end module m_common_error diff --git a/src/xml/common/m_common_io.F90 b/src/xml/common/m_common_io.F90 new file mode 100644 index 0000000000..a15a25dbc6 --- /dev/null +++ b/src/xml/common/m_common_io.F90 @@ -0,0 +1,102 @@ +module m_common_io + +#ifndef DUMMYLIB + use m_common_error, only : FoX_error + + implicit none + private + + ! Basic I/O tools + + integer, save :: io_eor + integer, save :: io_eof + integer, save :: io_err + + public :: io_eor + public :: io_eof + public :: io_err + public :: get_unit + public :: setup_io + +contains + + subroutine setup_io() + call find_eor_eof(io_eor, io_eof) + end subroutine setup_io + + + subroutine get_unit(lun,iostat) + ! Get an available Fortran unit number + integer, intent(out) :: lun + integer, intent(out) :: iostat + + integer :: i + logical :: unit_used + + do i = 10, 99 + lun = i + inquire(unit=lun,opened=unit_used) + if (.not. unit_used) then + iostat = 0 + return + endif + enddo + iostat = -1 + lun = -1 + end subroutine get_unit + + + subroutine find_eor_eof(io_eor,io_eof) + ! Determines the values of the iostat values for End of File and + ! End of Record (in non-advancing I/O) +#ifdef __NAG__ + use f90_iostat +#endif + integer, intent(out) :: io_eor + integer, intent(out) :: io_eof + +#ifdef __NAG__ + io_eor = ioerr_eor + io_eof = ioerr_eof +#else + integer :: lun, iostat + character(len=1) :: c + + call get_unit(lun,iostat) + + if (iostat /= 0) call FoX_error("Out of unit numbers") + + open(unit=lun,status="scratch",form="formatted", & + action="readwrite",position="rewind",iostat=iostat) + if (iostat /= 0) call FoX_error("Cannot open test file") + + write(unit=lun,fmt=*) "a" + write(unit=lun,fmt=*) "b" + + rewind(unit=lun) + + io_eor = 0 + do + read(unit=lun,fmt="(a1)",advance="no",iostat=io_eor) c + if (io_eor /= 0) exit + enddo + + io_eof = 0 + do + read(unit=lun,fmt=*,iostat=io_eof) + if (io_eof /= 0) exit + enddo + + close(unit=lun,status="delete") +#endif + + ! Invent an io_err ... + io_err = 1 + do + if (io_err/=0.and.io_err/=io_eor.and.io_err/=io_eof) exit + io_err = io_err + 1 + end do + end subroutine find_eor_eof + +#endif +end module m_common_io diff --git a/src/xml/common/m_common_namecheck.F90 b/src/xml/common/m_common_namecheck.F90 new file mode 100644 index 0000000000..a796732e72 --- /dev/null +++ b/src/xml/common/m_common_namecheck.F90 @@ -0,0 +1,501 @@ +module m_common_namecheck + +#ifndef DUMMYLIB + ! These are basically a collection of what would be regular + ! expressions in a more sensible language. + ! The only external dependency should be knowing how these + ! regular expressions may differ between XML-1.0 and 1.1 (which + ! is only in the areas of + ! 1: allowing character entity references to control characters + ! 2: More characters allowed in Names (but this only affects + ! unicode-aware programs, so is only skeleton here) + + use fox_m_fsys_format, only: str_to_int_10, str_to_int_16, operator(//) + use fox_m_fsys_string, only: toLower + use m_common_charset, only: isLegalCharRef, isNCNameChar, & + isInitialNCNameChar, isInitialNameChar, isNameChar, isRepCharRef + + implicit none + private + + character(len=*), parameter :: lowerCase = "abcdefghijklmnopqrstuvwxyz" + character(len=*), parameter :: upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + character(len=*), parameter :: letters = lowerCase//upperCase + character(len=*), parameter :: digits = "0123456789" + character(len=*), parameter :: hexdigits = "0123456789abcdefABCDEF" + character(len=*), parameter :: NameChars = lowerCase//upperCase//digits//".-_:" + + public :: checkName + public :: checkNames + public :: checkQName + public :: checkQNames + public :: checkNmtoken + public :: checkNmtokens + public :: checkNCName + public :: checkNCNames + public :: checkEncName + public :: checkPITarget + public :: checkPublicId + public :: checkPEDef + public :: checkPseudoAttValue + public :: checkAttValue + public :: checkCharacterEntityReference + public :: checkRepCharEntityReference + public :: likeCharacterEntityReference + + public :: prefixOfQName + public :: localpartOfQName + +contains + + pure function checkEncName(name) result(good) + ![81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* + character(len=*), intent(in) :: name + logical :: good + + integer :: n + + n = len(name) + good = (n > 0) + if (good) good = (scan(name(1:1), letters) /= 0) + if (good .and. n > 1) & + good = (verify(name(2:), letters//digits//'.-_') == 0) + end function checkEncName + + + function checkPITarget(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for a NAME + ! Is not fully compliant; ignores UTF issues. + + good = checkName(name, xv) & + .and.toLower(name)/="xml" + + end function checkPITarget + + + pure function checkName(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for a NAME + ! Is not fully compliant; ignores UTF issues. + + good = (len(name) > 0) + if (.not.good) return + if (good) good = isInitialNameChar(name(1:1), xv) + if (.not.good.or.len(name)==1) return + good = isNameChar(name(2:), xv) + + end function checkName + + pure function checkNames(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the production for NAMES + + integer :: i, j + + good = (len(name) > 0) + if (.not.good) return + i = verify(name, " ") + if (i==0) then + good = .false. + return + endif + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + do + good = checkName(name(i:j), xv) + if (.not.good) return + i = j + 1 + j = verify(name(i:), " ") + if (j==0) exit + i = i + j - 1 + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + enddo + + end function checkNames + + + pure function checkQName(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for a NAME + ! Is not fully compliant; ignores UTF issues. + + integer :: n + + n = index(name, ':') + if (n == 0) then + good = checkNCName(name, xv) + else + good = (checkNCName(name(:n-1), xv) .and. checkNCName(name(n+1:), xv)) + endif + end function checkQName + + + pure function checkQNames(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the production for NAMES + + integer :: i, j + + good = (len(name) > 0) + if (.not.good) return + i = verify(name, " ") + if (i==0) then + good = .false. + return + endif + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + do + good = checkQName(name(i:j), xv) + if (.not.good) return + i = j + 1 + j = verify(name(i:), " ") + if (j==0) exit + i = i + j - 1 + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + enddo + + end function checkQNames + + + pure function checkNCName(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for an NCNAME + ! Is not fully compliant; ignores UTF issues. + + good = (len(name)/=0) + if (.not.good) return + good = isInitialNCNameChar(name(1:1), xv) + if (.not.good.or.len(name)==1) return + good = isNCNameChar(name(2:), xv) + + end function checkNCName + + + pure function checkNCNames(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the production for NAMES + + integer :: i, j + + good = (len(name) > 0) + if (.not.good) return + i = verify(name, " ") + if (i==0) then + good = .false. + return + endif + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + do + good = checkNCName(name(i:j), xv) + if (.not.good) return + i = j + 1 + j = verify(name(i:), " ") + if (j==0) exit + i = i + j - 1 + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + enddo + + end function checkNCNames + + + pure function checkNmtoken(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for an NCNAME + ! Is not fully compliant; ignores UTF issues. + + good = isNameChar(name, xv) + + end function checkNmtoken + + + pure function checkNmtokens(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for an NCNAME + ! Is not fully compliant; ignores UTF issues. + + integer :: i, j + + good = (len(name) > 0) + if (.not.good) return + i = verify(name, " ") + if (i==0) then + good = .false. + return + endif + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + do + good = isNameChar(name(i:j), xv) + if (.not.good) return + i = j + 1 + j = verify(name(i:), " ") + if (j==0) exit + i = i + j - 1 + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + enddo + + end function checkNmtokens + + + function checkPublicId(value) result(good) + character(len=*), intent(in) :: value + logical :: good + character(len=*), parameter :: PubIdChars = & + " "//achar(10)//achar(13)//lowerCase//upperCase//digits//"-'()+,./:=?;!*#@$_%" + + good = (verify(value, PubIdChars)==0) + end function checkPublicId + + + function checkPEDef(value, xv) result(p) + character(len=*), intent(in) :: value + integer, intent(in) :: xv + logical :: p + + integer :: i1, i2 + + p = .false. + if (scan(value, '%&')==0) then + p = .true. + elseif (scan(value, '"')==0) then + i1 = scan(value, '%&') + i2 = 0 + do while (i1>0) + i1 = i2 + i1 + i2 = index(value(i1+1:),';') + if (i2==0) return + i2 = i1 + i2 + if (value(i1:i1)=='&') then + if (.not.checkName(value(i1+1:i2-1), xv) .and. & + .not.checkCharacterEntityReference(value(i1+1:i2-1), xv)) return + else + if (.not.checkName(value(i1+1:i2-1), xv)) & + return + endif + i1 = scan(value(i2+1:), '%&') + enddo + p = .true. + endif + end function checkPEDef + + function checkPseudoAttValue(value, xv) result(p) + character(len=*), intent(in) :: value + integer, intent(in) :: xv + logical :: p + + integer :: i1, i2 +!fixme can we have entrefs in PIs? + p = .false. + if (scan(value, '"<&')==0) then + p = .true. + elseif (index(value, '&') > 0) then + i1 = index(value, '&') + i2 = 0 + do while (i1 > 0) + i1 = i2 + i1 + i2 = index(value(i1+1:),';') + if (i2==0) return + i2 = i1 + i2 + if (value(i1+1:i2-1) /= 'amp' .and. & + value(i1+1:i2-1) /= 'lt' .and. & + value(i1+1:i2-1) /= 'gt' .and. & + value(i1+1:i2-1) /= 'quot' .and. & + value(i1+1:i2-1) /= 'apos' .and. & + .not.checkCharacterEntityReference(value(i1+1:i2-1), xv)) & + return + i1 = index(value(i2+1:), '&') + enddo + p = .true. + endif + end function checkPseudoAttValue + + function checkAttValue(value, xv) result(p) + character(len=*), intent(in) :: value + integer, intent(in) :: xv + logical :: p + ! This function is called basically to make sure + ! that any attribute value looks like one. It will + ! not flag up out-of-range character entities, and + ! is a very weak check. Only used from xml_AddAttribute + ! when escaping is off. + integer :: i1, i2 + + p = .false. + if (scan(value, '"<&'//"'")==0) then + p = .true. + elseif (index(value, '&') > 0) then + i1 = index(value, '&') + i2 = 0 + do while (i1 > 0) + i1 = i1 + i2 + 1 + i2 = scan(value(i1+1:),';') + if (i2 == 0) return + i2 = i1 + i2 + if (.not.checkName(value(i1+1:i2-1), xv) .and. & + .not.likeCharacterEntityReference(value(i1+1:i2-1))) then + print*, value(i1+1:i2-1), " ", & + likeCharacterEntityReference(value(i1+1:i2-1)) + return + endif + i1 = index(value(i2+1:), '&') + enddo + p = .true. + endif + end function checkAttValue + + + function likeCharacterEntityReference(code) result(good) + character(len=*), intent(in) :: code + logical :: good + + good = .false. + if (len(code) > 0) then + if (code(1:1) == "#") then + if (code(2:2) == "x") then + if (len(code) > 2) then + good = (verify(code(3:), hexdigits) == 0) + endif + else + good = (verify(code(2:), digits) == 0) + endif + endif + endif + + end function likeCharacterEntityReference + + function checkCharacterEntityReference(code, xv) result(good) + character(len=*), intent(in) :: code + integer, intent(in) :: xv + logical :: good + + integer :: i + + good = .false. + if (len(code) > 0) then + if (code(1:1) == "#") then + if (code(2:2) == "x") then + if (len(code) > 2) then + good = (verify(code(3:), hexdigits) == 0) + if (good) then + i = str_to_int_16(code(3:)) + endif + endif + else + good = (verify(code(2:), digits) == 0) + if (good) then + i = str_to_int_10(code(2:)) + endif + endif + endif + endif + if (good) good = isLegalCharRef(i, xv) + + end function checkCharacterEntityReference + + function checkRepCharEntityReference(code, xv) result(good) + character(len=*), intent(in) :: code + integer, intent(in) :: xv + logical :: good + + ! Is this a reference to a character we can actually represent + ! in memory? ie without unicode, US-ASCII only. + + integer :: i + + good = .false. + if (len(code) > 0) then + if (code(1:1) == "#") then + if (code(2:2) == "x") then + if (len(code) > 2) then + good = (verify(code(3:), hexdigits) == 0) + if (good) then + i = str_to_int_16(code(3:)) + endif + endif + else + good = (verify(code(2:), digits) == 0) + if (good) then + i = str_to_int_10(code(2:)) + endif + endif + endif + endif + if (good) good = isRepCharRef(i, xv) + + end function checkRepCharEntityReference + + + pure function prefixOfQName(qname) result(prefix) + character(len=*), intent(in) :: qname + character(len=max(index(qname, ':')-1,0)) :: prefix + + prefix = qname ! automatic truncation + end function prefixOfQName + + + pure function localpartOfQname(qname) result(localpart) + character(len=*), intent(in) :: qname + character(len=max(len(qname)-index(qname,':'),0)) ::localpart + + localpart = qname(index(qname,':')+1:) + end function localpartOfQname + +#endif +end module m_common_namecheck diff --git a/src/xml/common/m_common_namespaces.F90 b/src/xml/common/m_common_namespaces.F90 new file mode 100644 index 0000000000..e3bdff60cf --- /dev/null +++ b/src/xml/common/m_common_namespaces.F90 @@ -0,0 +1,830 @@ +module m_common_namespaces + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str, vs_str_alloc + + use fox_m_utils_uri, only: URI, parseURI, destroyURI, hasScheme + use m_common_attrs, only: dictionary_t, get_key, get_value, remove_key, getLength, hasKey + use m_common_attrs, only: set_nsURI, set_localName, get_prefix, add_item_to_dict + use m_common_charset, only: XML1_0, XML1_1 + use m_common_error, only: FoX_error, FoX_warning, error_stack, add_error, in_error + use m_common_namecheck, only: checkNCName + use m_common_struct, only: xml_doc_state + + implicit none + private + + character(len=*), parameter :: invalidNS = '::INVALID::' + ! an invalid URI name to indicate a namespace error. + + type URIMapping + character, dimension(:), pointer :: URI + integer :: ix ! link back to node depth + end type URIMapping + !This is a single URI, and the node depth under which + !its namespace applies. + + type prefixMapping + character, dimension(:), pointer :: prefix + type(URIMapping), dimension(:), pointer :: urilist + end type prefixMapping + !This is the mapping for a single prefix; with the + !list of namespaces which are in force at various + !depths + + type namespaceDictionary + private + type(URIMapping), dimension(:), pointer :: defaults + type(prefixMapping), dimension(:), pointer :: prefixes + end type namespaceDictionary + !This is the full namespace dictionary; defaults is + !the list of default namespaces in force; prefix a + !list of all prefixes in force. + + public :: invalidNS + + public :: initNamespaceDictionary + public :: destroyNamespaceDictionary + public :: namespaceDictionary + public :: checkNamespaces + public :: checkNamespacesWriting + public :: checkEndNamespaces + public :: getnamespaceURI + interface getnamespaceURI + module procedure getURIofDefaultNS, getURIofPrefixedNS + end interface + public :: isPrefixInForce + public :: isDefaultNSInForce + public :: getNumberOfPrefixes + public :: getPrefixByIndex + + public :: dumpnsdict !FIXME + + public :: addDefaultNS + public :: removeDefaultNS + public :: addPrefixedNS + public :: removePrefixedNS + +contains + + + subroutine initNamespaceDictionary(nsDict) + type(namespaceDictionary), intent(inout) :: nsDict + + !We need to properly initialize 0th elements + !(which are never used) in order to provide + !sensible behaviour when trying to manipulate + !an empty dictionary. + + allocate(nsDict%defaults(0:0)) + allocate(nsDict%defaults(0)%URI(0)) + !The 0th element of the defaults NS is the empty namespace + nsDict%defaults(0)%ix = -1 + + allocate(nsDict%prefixes(0:0)) + allocate(nsDict%prefixes(0)%prefix(0)) + allocate(nsDict%prefixes(0)%urilist(0:0)) + allocate(nsDict%prefixes(0)%urilist(0)%URI(len(invalidNS))) + nsDict%prefixes(0)%urilist(0)%URI = vs_str(invalidNS) + nsDict%prefixes(0)%urilist(0)%ix = -1 + + end subroutine initNamespaceDictionary + + + subroutine destroyNamespaceDictionary(nsDict) + type(namespaceDictionary), intent(inout) :: nsDict + + integer :: i, j + + do i = 0, ubound(nsDict%defaults,1) + deallocate(nsDict%defaults(i)%URI) + enddo + deallocate(nsDict%defaults) + do i = 0, ubound(nsDict%prefixes,1) + do j = 0, ubound(nsDict%prefixes(i)%urilist,1) + deallocate(nsDict%prefixes(i)%urilist(j)%URI) + enddo + deallocate(nsDict%prefixes(i)%prefix) + deallocate(nsDict%prefixes(i)%urilist) + enddo + deallocate(nsDict%prefixes) + end subroutine destroyNamespaceDictionary + + + subroutine copyURIMapping(urilist1, urilist2, l_m) + type(URIMapping), dimension(0:), intent(inout) :: urilist1 + type(URIMapping), dimension(0:), intent(inout) :: urilist2 + integer, intent(in):: l_m + integer :: i + + if (ubound(urilist1,1) < l_m .or. ubound(urilist2,1) < l_m) then + call FoX_error('Internal error in m_sax_namespaces:copyURIMapping') + endif + ! Now copy all defaults across (or rather - add pointers to them) + do i = 0, l_m + urilist2(i)%ix = urilist1(i)%ix + urilist2(i)%URI => urilist1(i)%URI + enddo + + end subroutine copyURIMapping + + + subroutine addDefaultNS(nsDict, uri, ix, es) + type(namespaceDictionary), intent(inout) :: nsDict + character(len=*), intent(in) :: uri + integer, intent(in) :: ix + type(error_stack), intent(inout), optional :: es + + type(URIMapping), dimension(:), allocatable :: tempMap + integer :: l_m, l_s + + if (uri=="http://www.w3.org/XML/1998/namespace") then + if (present(es)) then + call add_error(es, "Attempt to assign incorrect URI to prefix 'xml'") + else + call FoX_error("Attempt to assign incorrect URI to prefix 'xml'") + endif + elseif (uri=="http://www.w3.org/2000/xmlns/") then + if (present(es)) then + call add_error(es, "Attempt to assign prefix to xmlns namespace") + else + call FoX_error("Attempt to assign prefix to xmlns namespace") + endif + endif + + ! FIXME check URI is valid ... + + l_m = ubound(nsDict%defaults,1) + allocate(tempMap(0:l_m)) + ! Now copy all defaults across ... + call copyURIMapping(nsDict%defaults, tempMap, l_m) + deallocate(nsDict%defaults) + l_m = l_m + 1 + allocate(nsDict%defaults(0:l_m)) + !Now copy everything back ... + call copyURIMapping(tempMap, nsDict%defaults, l_m-1) + deallocate(tempMap) + ! And finally, add the new default NS + nsDict%defaults(l_m)%ix = ix + l_s = len(uri) + allocate(nsDict%defaults(l_m)%URI(l_s)) + nsDict%defaults(l_m)%URI = vs_str(uri) + + end subroutine addDefaultNS + + + subroutine addPrefixedURI(nsPrefix, uri, ix) + type(PrefixMapping), intent(inout) :: nsPrefix + character, dimension(:), intent(in) :: uri + integer, intent(in) :: ix + + type(URIMapping), dimension(:), allocatable :: tempMap + integer :: l_m, l_s + + l_m = ubound(nsPrefix%urilist,1) + allocate(tempMap(0:l_m)) + ! Now copy all across ... + call copyURIMapping(nsPrefix%urilist, tempMap, l_m) + deallocate(nsPrefix%urilist) + l_m = l_m + 1 + allocate(nsPrefix%urilist(0:l_m)) + !Now copy everything back ... + call copyURIMapping(tempMap, nsPrefix%urilist, l_m-1) + deallocate(tempMap) + ! And finally, add the new default NS + nsPrefix%urilist(l_m)%ix = ix + l_s = size(uri) + allocate(nsPrefix%urilist(l_m)%URI(l_s)) + nsPrefix%urilist(l_m)%URI = uri + + end subroutine addPrefixedURI + + subroutine removeDefaultNS(nsDict) + type(namespaceDictionary), intent(inout) :: nsDict + + type(URIMapping), dimension(:), allocatable :: tempMap + integer :: l_m + + l_m = ubound(nsDict%defaults,1) + allocate(tempMap(0:l_m-1)) + ! Now copy all defaults across ... + call copyURIMapping(nsDict%defaults, tempMap, l_m-1) + !And remove tail-end charlie + deallocate(nsDict%defaults(l_m)%URI) + deallocate(nsDict%defaults) + l_m = l_m - 1 + allocate(nsDict%defaults(0:l_m)) + !Now copy everything back ... + call copyURIMapping(tempMap, nsDict%defaults, l_m) + deallocate(tempMap) + + end subroutine removeDefaultNS + + subroutine removePrefixedURI(nsPrefix) + type(PrefixMapping), intent(inout) :: nsPrefix + + type(URIMapping), dimension(:), allocatable :: tempMap + integer :: l_m + + l_m = ubound(nsPrefix%urilist,1) + allocate(tempMap(0:l_m-1)) + ! Now copy all defaults across ... + call copyURIMapping(nsPrefix%urilist, tempMap, l_m-1) + !And remove tail-end charlie + deallocate(nsPrefix%urilist(l_m)%URI) + deallocate(nsPrefix%urilist) + l_m = l_m - 1 + allocate(nsPrefix%urilist(0:l_m)) + !Now copy everything back ... + call copyURIMapping(tempMap, nsPrefix%urilist, l_m) + deallocate(tempMap) + + end subroutine removePrefixedURI + + subroutine addPrefixedNS(nsDict, prefix, URI, ix, xds, xml, es) + type(namespaceDictionary), intent(inout) :: nsDict + character(len=*), intent(in) :: prefix + character(len=*), intent(in) :: uri + integer, intent(in) :: ix + type(xml_doc_state), intent(in) :: xds + logical, intent(in), optional :: xml + type(error_stack), intent(inout), optional :: es + + integer :: l_p, p_i, i + logical :: xml_ + + if (present(xml)) then + xml_ = xml + else + xml_ = .false. + endif + + if (prefix=='xml' .and. & + URI/='http://www.w3.org/XML/1998/namespace') then + if (present(es)) then + call add_error(es, "Attempt to assign incorrect URI to prefix 'xml'") + else + call FoX_error("Attempt to assign incorrect URI to prefix 'xml'") + endif + elseif (prefix/='xml' .and. & + URI=='http://www.w3.org/XML/1998/namespace') then + if (present(es)) then + call add_error(es, "Attempt to assign incorrect prefix to XML namespace") + else + call FoX_error("Attempt to assign incorrect prefix to XML namespace") + endif + elseif (prefix == 'xmlns') then + if (present(es)) then + call add_error(es, "Attempt to declare 'xmlns' prefix") + else + call FoX_error("Attempt to declare 'xmlns' prefix") + endif + elseif (URI=="http://www.w3.org/2000/xmlns/") then + if (present(es)) then + call add_error(es, "Attempt to assign prefix to xmlns namespace") + else + call FoX_error("Attempt to assign prefix to xmlns namespace") + endif + elseif (len(prefix) > 2) then + if ((verify(prefix(1:1), 'xX') == 0) & + .and. (verify(prefix(2:2), 'mM') == 0) & + .and. (verify(prefix(3:3), 'lL') == 0)) then + if (.not.xml_) then + ! FIXME need working warning infrastructure + !if (present(es)) then + ! call add_error(es, "Attempt to declare reserved prefix: "//prefix) + !else + call FoX_warning("Attempt to declare reserved prefix: "//prefix) + !endif + endif + endif + endif + + if (.not.checkNCName(prefix, xds%xml_version)) & + call FoX_error("Attempt to declare invalid prefix: "//prefix) + + ! FIXME check URI is valid + + l_p = ubound(nsDict%prefixes, 1) + + p_i = 0 + do i = 1, l_p + if (str_vs(nsDict%prefixes(i)%prefix) == prefix) then + p_i = i + exit + endif + enddo + + if (p_i == 0) then + call addPrefix(nsDict, vs_str(prefix)) + p_i = l_p + 1 + endif + + call addPrefixedURI(nsDict%prefixes(p_i), vs_str(URI), ix) + + end subroutine addPrefixedNS + + subroutine removePrefixedNS(nsDict, prefix) + type(namespaceDictionary), intent(inout) :: nsDict + character, dimension(:), intent(in) :: prefix + integer :: l_p, p_i, i + l_p = ubound(nsDict%prefixes, 1) + + p_i = 0 + do i = 1, l_p + if (str_vs(nsDict%prefixes(i)%prefix) == str_vs(prefix)) then + p_i = i + exit + endif + enddo + + if (p_i /= 0) then + call removePrefixedURI(nsDict%prefixes(p_i)) + if (ubound(nsDict%prefixes(p_i)%urilist,1) == 0) then + !that was the last mapping for that prefix + call removePrefix(nsDict, p_i) + endif + else + call FoX_error('Internal error in m_sax_namespaces:removePrefixedNS') + endif + + end subroutine removePrefixedNS + + subroutine addPrefix(nsDict, prefix) + type(namespaceDictionary), intent(inout) :: nsDict + character, dimension(:), intent(in) :: prefix + integer :: l_p + + type(prefixMapping), dimension(:), pointer :: tempPrefixMap + + integer :: i + + !Add a new prefix to the namespace dictionary. + !Unfortunately this involves copying the entire + !prefixes dictionary to a temporary structure, then + !reallocating the prefixes dictionary to be one + !longer, then copying everything back: + + l_p = ubound(nsDict%prefixes, 1) + allocate(tempPrefixMap(0:l_p)) + + !for each current prefix, append everything to temporary structure + do i = 0, l_p + tempPrefixMap(i)%prefix => nsDict%prefixes(i)%prefix + tempPrefixMap(i)%urilist => nsDict%prefixes(i)%urilist + enddo + deallocate(nsDict%prefixes) + !extend prefix dictionary by one ... + l_p = l_p + 1 + allocate(nsDict%prefixes(0:l_p)) + !and copy back ... + do i = 0, l_p-1 + nsDict%prefixes(i)%prefix => tempPrefixMap(i)%prefix + nsDict%prefixes(i)%urilist => tempPrefixMap(i)%urilist + enddo + deallocate(tempPrefixMap) + + allocate(nsDict%prefixes(l_p)%prefix(size(prefix))) + nsDict%prefixes(l_p)%prefix = prefix + allocate(nsDict%prefixes(l_p)%urilist(0:0)) + allocate(nsDict%prefixes(l_p)%urilist(0)%URI(len(invalidNS))) + nsDict%prefixes(l_p)%urilist(0)%URI = vs_str(invalidNS) + nsDict%prefixes(l_p)%urilist(0)%ix = -1 + + end subroutine addPrefix + + subroutine removePrefix(nsDict, i_p) + type(namespaceDictionary), intent(inout) :: nsDict + integer, intent(in) :: i_p + integer :: l_p + + type(prefixMapping), dimension(:), pointer :: tempPrefixMap + + integer :: i + + !Remove a prefix from the namespace dictionary. + !Unfortunately this involves copying the entire + !prefixes dictionary to a temporary structure, then + !reallocating the prefixes dictionary to be one + !shorter, then copying everything back: + + l_p = ubound(nsDict%prefixes, 1) + allocate(tempPrefixMap(0:l_p-1)) + + !for each current prefix, append everything to temporary structure + do i = 0, i_p-1 + tempPrefixMap(i)%prefix => nsDict%prefixes(i)%prefix + tempPrefixMap(i)%urilist => nsDict%prefixes(i)%urilist + enddo + deallocate(nsDict%prefixes(i_p)%urilist(0)%URI) + deallocate(nsDict%prefixes(i_p)%urilist) + deallocate(nsDict%prefixes(i_p)%prefix) + !this subroutine will only get called if the urilist is already + !empty, so no need to deallocate it. + do i = i_p+1, l_p + tempPrefixMap(i-1)%prefix => nsDict%prefixes(i)%prefix + tempPrefixMap(i-1)%urilist => nsDict%prefixes(i)%urilist + enddo + deallocate(nsDict%prefixes) + !shorten prefix dictionary by one ... + l_p = l_p - 1 + allocate(nsDict%prefixes(0:l_p)) + !and copy back ... + do i = 0, l_p + nsDict%prefixes(i)%prefix => tempPrefixMap(i)%prefix + nsDict%prefixes(i)%urilist => tempPrefixMap(i)%urilist + enddo + deallocate(tempPrefixMap) + + end subroutine removePrefix + + + subroutine checkNamespaces(atts, nsDict, ix, xds, namespace_prefixes, xmlns_uris, es, & + partial, start_prefix_handler, end_prefix_handler) + type(dictionary_t), intent(inout) :: atts + type(namespaceDictionary), intent(inout) :: nsDict + integer, intent(in) :: ix ! depth of nesting of current element. + type(xml_doc_state), intent(in) :: xds + logical, intent(in) :: namespace_prefixes, xmlns_uris + type(error_stack), intent(inout) :: es + logical, intent(in) :: partial ! if so, don't try and resolve anything except xml & xmlns + optional :: start_prefix_handler, end_prefix_handler + + interface + subroutine start_prefix_handler(namespaceURI, prefix) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: prefix + end subroutine start_prefix_handler + subroutine end_prefix_handler(prefix) + character(len=*), intent(in) :: prefix + end subroutine end_prefix_handler + end interface + + character(len=6) :: xmlns + character, dimension(:), pointer :: QName, URIstring + integer :: i, n + type(URI), pointer :: URIref + !Check for namespaces; *and* remove xmlns references from + !the attributes dictionary. + + ! we can't do a simple loop across the attributes, + ! because we need to remove some as we go along ... + i = 1 + do while (i <= getLength(atts)) + xmlns = get_key(atts, i) + if (xmlns == 'xmlns ') then + !Default namespace is being set + URIstring => vs_str_alloc(get_value(atts, i)) + if (str_vs(URIstring)=="") then + ! Empty nsURI on default namespace has same effect in 1.0 and 1.1 + if (present(end_prefix_handler)) & + call end_prefix_handler("") + call addDefaultNS(nsDict, invalidNS, ix) + deallocate(URIstring) + else + URIref => parseURI(str_vs(URIstring)) + if (.not.associated(URIref)) then + call add_error(es, "Invalid URI: "//str_vs(URIstring)) + deallocate(URIstring) + return + elseif (.not.hasScheme(URIref)) then + call add_error(es, "Relative namespace in URI deprecated: "//str_vs(URIstring)) + deallocate(URIstring) + call destroyURI(URIref) + return + endif + call destroyURI(URIref) + if (present(start_prefix_handler)) & + call start_prefix_handler(str_vs(URIstring), "") + call addDefaultNS(nsDict, str_vs(URIstring), ix) + deallocate(URIstring) + endif + if (namespace_prefixes) then + i = i + 1 + else + call remove_key(atts, i) + endif + elseif (xmlns == 'xmlns:') then + !Prefixed namespace is being set + QName => vs_str_alloc(get_key(atts, i)) + URIstring => vs_str_alloc(get_value(atts, i)) + if (str_vs(URIstring)=="") then + if (xds%xml_version==XML1_0) then + call add_error(es, "Empty nsURI is invalid in XML 1.0") + deallocate(URIstring) + deallocate(QName) + return + elseif (xds%xml_version==XML1_1) then + call addPrefixedNS(nsDict, str_vs(QName(7:)), invalidNS, ix, xds, es=es) + if (in_error(es)) then + deallocate(URIstring) + deallocate(QName) + return + elseif (present(end_prefix_handler)) then + call end_prefix_handler(str_vs(QName(7:))) + endif + deallocate(URIstring) + deallocate(QName) + endif + else + URIref => parseURI(str_vs(URIstring)) + if (.not.associated(URIref)) then + call add_error(es, "Invalid URI: "//str_vs(URIstring)) + deallocate(URIstring) + deallocate(QName) + return + elseif (.not.hasScheme(URIref)) then + call add_error(es, "Relative namespace in URI deprecated: "//str_vs(URIstring)) + deallocate(URIstring) + deallocate(QName) + call destroyURI(URIref) + return + endif + call destroyURI(URIref) + call addPrefixedNS(nsDict, str_vs(QName(7:)), str_vs(URIstring), ix, xds, es=es) + if (in_error(es)) then + deallocate(URIstring) + deallocate(QName) + return + elseif (present(start_prefix_handler)) then + call start_prefix_handler(str_vs(URIstring), str_vs(QName(7:))) + endif + deallocate(URIstring) + deallocate(QName) + endif + if (namespace_prefixes) then + i = i + 1 + else + call remove_key(atts, i) + endif + else + ! we only increment if we haven't removed a key + i = i + 1 + endif + enddo + + ! having done that, now resolve all attribute namespaces: + do i = 1, getLength(atts) + QName => vs_str_alloc(get_key(atts,i)) + n = index(str_vs(QName), ":") + if (n > 0) then + if (str_vs(QName(1:n-1))=="xmlns") then + ! FIXME but this can be controlled by SAX configuration xmlns-uris + if (xmlns_uris) then + call set_nsURI(atts, i, "http://www.w3.org/2000/xmlns/") + else + call set_nsURI(atts, i, "") + endif + else + if (str_vs(QName(1:n-1))=="xml") then + call set_nsURI(atts, i, "http://www.w3.org/XML/1998/namespace") + elseif (getnamespaceURI(nsDict, str_vs(QName(1:n-1)))==invalidNS) then + ! Sometimes we don't want to worry about unbound prefixes, + ! eg if we are in the middle of parsing an entity. + if (.not.partial) then + call add_error(es, "Unbound namespace prefix") + deallocate(QName) + return + else + call set_nsURI(atts, i, "") + endif + else + call set_nsURI(atts, i, getnamespaceURI(nsDict, str_vs(QName(1:n-1)))) + endif + endif + else + if (xmlns_uris.and.str_vs(QName)=="xmlns") then + call set_nsURI(atts, i, "http://www.w3.org/2000/xmlns/") + else + call set_nsURI(atts, i, "") ! no such thing as a default namespace on attributes + endif + endif + ! Check for duplicates + if (hasKey(atts, getnamespaceURI(nsDict, str_vs(QName(1:n-1))), str_vs(QName(n+1:)))) then + call add_error(es, "Duplicate attribute names after namespace processing") + deallocate(QName) + return + endif + call set_localName(atts, i, QName(n+1:)) + deallocate(QName) + enddo + + end subroutine checkNamespaces + + + subroutine checkNamespacesWriting(atts, nsdict, ix) + type(dictionary_t), intent(inout) :: atts + type(namespaceDictionary), intent(inout) :: nsDict + integer, intent(in) :: ix + ! Read through a list of attributes, check with currently + ! active namespaces & add any necessary declarations + + integer :: i, i_p, l_d, l_ps, n + + n = getLength(atts) ! we need the length before we fiddle with it + + !Does the default NS need added? + l_d = ubound(nsDict%defaults,1) + if (nsDict%defaults(l_d)%ix == ix) then + !It's not been registered yet: + call add_item_to_dict(atts, "xmlns", & + str_vs(nsDict%defaults(l_d)%URI), type="CDATA") + endif + + !next, add any overdue prefixed NS's in the same way: + ! there should only ever be one. More would be an error, + ! but the check should have been done earlier. + do i_p = 0, ubound(nsDict%prefixes, 1) + l_ps = ubound(nsDict%prefixes(i_p)%urilist,1) + if (nsDict%prefixes(i_p)%urilist(l_ps)%ix == ix) then + call add_item_to_dict(atts, & + "xmlns:"//str_vs(nsDict%prefixes(i_p)%prefix), & + str_vs(nsDict%prefixes(i_p)%urilist(l_ps)%URI), & + type="CDATA") + endif + enddo + + + !Finally, we may have some we've added for attribute QNames + ! have to get those too: + do i = 1, getLength(atts) + ! get prefix, and identify the relevant NS mapping + i_p = getPrefixIndex(nsDict, get_prefix(atts, i)) + l_ps = ubound(nsDict%prefixes(i_p)%urilist,1) + !If the index is greater than what it should be: + if (nsDict%prefixes(i_p)%urilist(l_ps)%ix > ix) then + !we only just added this, so we need to declare it + call add_item_to_dict(atts, "xmlns:"//get_prefix(atts, i), & + str_vs(nsDict%prefixes(i_p)%urilist(l_ps)%URI), & + type="CDATA") + !Reset the index to the right value: + nsDict%prefixes(i_p)%urilist(l_ps)%ix = ix + endif + enddo + + end subroutine checkNamespacesWriting + + + subroutine checkEndNamespaces(nsDict, ix, end_prefix_handler) + type(namespaceDictionary), intent(inout) :: nsDict + integer, intent(in) :: ix + + optional :: end_prefix_handler + + interface + subroutine end_prefix_handler(prefix) + character(len=*), intent(in) :: prefix + end subroutine end_prefix_handler + end interface + + integer :: l_d, l_p, l_ps, i + character, pointer :: prefix(:) + + !It will only ever be the final elements in the list which + ! might have expired. + + l_d = ubound(nsDict%defaults,1) + do while (nsDict%defaults(l_d)%ix == ix) + if (present(end_prefix_handler)) & + call end_prefix_handler("") + call removeDefaultNS(nsDict) + l_d = ubound(nsDict%defaults,1) + enddo + + l_p = ubound(nsDict%prefixes, 1) + i = 1 + do while (i <= l_p) + l_ps = ubound(nsDict%prefixes(l_p)%urilist,1) + if (nsDict%prefixes(i)%urilist(l_ps)%ix == ix) then + if (present(end_prefix_handler)) & + call end_prefix_handler(str_vs(nsDict%prefixes(i)%prefix)) + ! We have to assign this pointer explicitly, otherwise the next call + ! aliases its arguments illegally. + prefix => nsDict%prefixes(i)%prefix + call removePrefixedNS(nsDict, prefix) + if (l_p > ubound(nsDict%prefixes, 1)) then + ! we just removed the last reference to that prefix, + ! so our list of prefixes has shrunk - update the running total. + ! and go to the next prefix, which is at the same index + l_p = l_p - 1 + cycle + endif + endif + i = i + 1 + enddo + + end subroutine checkEndNamespaces + + + subroutine dumpnsdict(nsdict) + type(namespaceDictionary), intent(in) :: nsdict + integer :: i, j + write(*,'(a)')'* default namespaces *' + + do i = 1, ubound(nsdict%defaults, 1) + write(*,'(i0,a)') nsdict%defaults(i)%ix, str_vs(nsdict%defaults(i)%URI) + enddo + write(*,'(a)') '* Prefixed namespaces *' + do i = 1, ubound(nsdict%prefixes, 1) + write(*,'(2a)') '* prefix: ', str_vs(nsdict%prefixes(i)%prefix) + do j = 1, ubound(nsdict%prefixes(i)%urilist, 1) + write(*,'(i0,a)') nsdict%prefixes(i)%urilist(j)%ix, str_vs(nsdict%prefixes(i)%urilist(j)%URI) + enddo + enddo + + end subroutine dumpnsdict + + + pure function getURIofDefaultNS(nsDict) result(uri) + type(namespaceDictionary), intent(in) :: nsDict + character(len=size(nsDict%defaults(ubound(nsDict%defaults,1))%URI)) :: URI + + integer :: l_d + l_d = ubound(nsDict%defaults,1) + uri = str_vs(nsDict%defaults(l_d)%URI) + end function getURIofDefaultNS + + + pure function isPrefixInForce(nsDict, prefix) result(force) + type(namespaceDictionary), intent(in) :: nsDict + character(len=*), intent(in) :: prefix + logical :: force + integer :: i, l_s + + force = .false. + do i = 1, ubound(nsDict%prefixes, 1) + if (str_vs(nsDict%prefixes(i)%prefix) == prefix) then + l_s = ubound(nsDict%prefixes(i)%urilist, 1) + force = (size(nsdict%prefixes(i)%urilist(l_s)%URI) > 0) + exit + endif + enddo + + end function isPrefixInForce + + + pure function isDefaultNSInForce(nsDict) result(force) + type(namespaceDictionary), intent(in) :: nsDict + logical :: force + integer :: l_s + + force = .false. + l_s = ubound(nsDict%defaults, 1) + if (l_s > 0) & + force = (size(nsdict%defaults(l_s)%URI) > 0) + + end function isDefaultNSInForce + + + pure function getPrefixIndex(nsDict, prefix) result(p) + type(namespaceDictionary), intent(in) :: nsDict + character(len=*), intent(in) :: prefix + integer :: p + + integer :: i + p = 0 + do i = 1, ubound(nsDict%prefixes, 1) + if (str_vs(nsDict%prefixes(i)%prefix) == prefix) then + p = i + exit + endif + enddo + end function getPrefixIndex + + + function getNumberOfPrefixes(nsDict) result(n) + type(namespaceDictionary), intent(in) :: nsDict + integer :: n + n = ubound(nsDict%prefixes, 1) + end function getNumberOfPrefixes + + + function getPrefixByIndex(nsDict, i) result(c) + type(namespaceDictionary), intent(in) :: nsDict + integer, intent(in) :: i + character(len=size(nsDict%prefixes(i)%prefix)) :: c + + c = str_vs(nsDict%prefixes(i)%prefix) + end function getPrefixByIndex + + + pure function getURIofPrefixedNS(nsDict, prefix) result(uri) + type(namespaceDictionary), intent(in) :: nsDict + character(len=*), intent(in) :: prefix + character(len=size( & + nsDict%prefixes( & + getPrefixIndex(nsDict,prefix) & + ) & + %urilist( & + ubound(nsDict%prefixes(getPrefixIndex(nsDict,prefix))%urilist, 1) & + ) & + %uri)) :: URI + integer :: p_i, l_m + p_i = getPrefixIndex(nsDict, prefix) + l_m = ubound(nsDict%prefixes(p_i)%urilist, 1) + uri = str_vs(nsDict%prefixes(p_i)%urilist(l_m)%URI) + + end function getURIofPrefixedNS + +#endif +end module m_common_namespaces diff --git a/src/xml/common/m_common_notations.F90 b/src/xml/common/m_common_notations.F90 new file mode 100644 index 0000000000..ee404da352 --- /dev/null +++ b/src/xml/common/m_common_notations.F90 @@ -0,0 +1,120 @@ +module m_common_notations + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: vs_str, str_vs + use m_common_error, only: FoX_error + + implicit none + private + + type notation + character(len=1), dimension(:), pointer :: name + character(len=1), dimension(:), pointer :: systemID + character(len=1), dimension(:), pointer :: publicId + end type notation + + type notation_list + type(notation), dimension(:), pointer :: list + end type notation_list + + public :: notation + public :: notation_list + public :: init_notation_list + public :: destroy_notation_list + public :: add_notation + public :: notation_exists + +contains + + subroutine init_notation_list(nlist) +! It is not clear how we should specify the +! intent of this argument - different +! compilers seem to have different semantics + type(notation_list), intent(inout) :: nlist + + allocate(nlist%list(0:0)) + allocate(nlist%list(0)%name(0)) + allocate(nlist%list(0)%systemId(0)) + allocate(nlist%list(0)%publicId(0)) + + end subroutine init_notation_list + + + subroutine destroy_notation_list(nlist) + type(notation_list), intent(inout) :: nlist + + integer :: i + + do i = 0, ubound(nlist%list, 1) + deallocate(nlist%list(i)%name) + deallocate(nlist%list(i)%systemId) + deallocate(nlist%list(i)%publicId) + enddo + deallocate(nlist%list) + end subroutine destroy_notation_list + + + subroutine add_notation(nlist, name, systemId, publicId) + type(notation_list), intent(inout) :: nlist + character(len=*), intent(in) :: name + character(len=*), intent(in), optional :: systemId + character(len=*), intent(in), optional :: publicId + + integer :: i + type(notation), dimension(:), pointer :: temp + ! pointer not allocatable to avoid bug on Lahey + + if (.not.present(systemId) .and. .not.present(publicId)) & + call FoX_error("Neither System nor Public Id specified for notation: "//name) + + allocate(temp(0:ubound(nlist%list,1))) + do i = 0, ubound(nlist%list, 1) + temp(i)%name => nlist%list(i)%name + temp(i)%systemId => nlist%list(i)%systemId + temp(i)%publicId => nlist%list(i)%publicId + enddo + + deallocate(nlist%list) + allocate(nlist%list(0:ubound(temp, 1)+1)) + do i = 0, ubound(temp, 1) + nlist%list(i)%name => temp(i)%name + nlist%list(i)%systemId => temp(i)%systemId + nlist%list(i)%publicId => temp(i)%publicId + enddo + deallocate(temp) + + allocate(nlist%list(i)%name(len(name))) + nlist%list(i)%name = vs_str(name) + if (present(systemId)) then + allocate(nlist%list(i)%systemId(len(systemId))) + nlist%list(i)%systemId = vs_str(systemId) + else + allocate(nlist%list(i)%systemId(0)) + endif + if (present(publicId)) then + allocate(nlist%list(i)%publicId(len(publicId))) + nlist%list(i)%publicId = vs_str(publicId) + else + allocate(nlist%list(i)%publicId(0)) + endif + end subroutine add_notation + + + function notation_exists(nlist, name) result(p) + type(notation_list), intent(in) :: nlist + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + do i = 1, ubound(nlist%list, 1) + if (str_vs(nlist%list(i)%name) == name) then + p = .true. + exit + endif + enddo + end function notation_exists + +#endif +end module m_common_notations diff --git a/src/xml/common/m_common_struct.F90 b/src/xml/common/m_common_struct.F90 new file mode 100644 index 0000000000..ec92b5291c --- /dev/null +++ b/src/xml/common/m_common_struct.F90 @@ -0,0 +1,121 @@ +module m_common_struct + +#ifndef DUMMYLIB + ! Common parts of an XML document. Shared by both SAX & WXML. + + use FoX_utils, only: URI + + use m_common_charset, only: XML1_0 + use m_common_entities, only: entity_list, init_entity_list, destroy_entity_list, & + add_internal_entity, add_external_entity + use m_common_element, only: element_list, init_element_list, destroy_element_list + use m_common_notations, only: notation_list, init_notation_list, destroy_notation_list + + implicit none + private + + type xml_doc_state + logical :: building = .false. ! Are we in the middle of building this doc? + integer :: xml_version = XML1_0 + logical :: standalone_declared = .false. + logical :: standalone = .false. + type(entity_list) :: entityList + type(entity_list) :: PEList + type(notation_list) :: nList + type(element_list) :: element_list + logical :: warning = .false. ! Do we care about warnings? + logical :: valid = .true. ! Do we care about validity? + logical :: liveNodeLists = .true. ! Do we want live nodelists? + character, pointer :: encoding(:) => null() + character, pointer :: inputEncoding(:) => null() + character, pointer :: documentURI(:) => null() + character, pointer :: intSubset(:) => null() + end type xml_doc_state + + public :: xml_doc_state + + public :: init_xml_doc_state + public :: destroy_xml_doc_state + + public :: register_internal_PE + public :: register_external_PE + public :: register_internal_GE + public :: register_external_GE + +contains + + subroutine init_xml_doc_state(xds) + type(xml_doc_state), intent(inout) :: xds + call init_entity_list(xds%entityList) + call init_entity_list(xds%PEList) + call init_notation_list(xds%nList) + call init_element_list(xds%element_list) + allocate(xds%inputEncoding(0)) + allocate(xds%intSubset(0)) + end subroutine init_xml_doc_state + + subroutine destroy_xml_doc_state(xds) + type(xml_doc_state), intent(inout) :: xds + call destroy_entity_list(xds%entityList) + call destroy_entity_list(xds%PEList) + call destroy_notation_list(xds%nList) + call destroy_element_list(xds%element_list) + if (associated(xds%encoding)) deallocate(xds%encoding) + if (associated(xds%inputEncoding)) deallocate(xds%inputEncoding) + if (associated(xds%documentURI)) deallocate(xds%documentURI) + deallocate(xds%intSubset) + end subroutine destroy_xml_doc_state + + subroutine register_internal_PE(xds, name, text, wfc, baseURI) + type(xml_doc_state), intent(inout) :: xds + character(len=*), intent(in) :: name + character(len=*), intent(in) :: text + logical, intent(in) :: wfc + type(URI), pointer :: baseURI + + call add_internal_entity(xds%PEList, name=name, text=text, & + wfc=wfc, baseURI=baseURI) + + end subroutine register_internal_PE + + subroutine register_external_PE(xds, name, systemId, wfc, baseURI, publicId) + type(xml_doc_state), intent(inout) :: xds + character(len=*), intent(in) :: name + character(len=*), intent(in) :: systemId + logical, intent(in) :: wfc + character(len=*), intent(in), optional :: publicId + type(URI), pointer :: baseURI + + call add_external_entity(xds%PEList, name=name, & + publicId=publicId, systemId=systemId, & + wfc=wfc, baseURI=baseURI) + end subroutine register_external_PE + + subroutine register_internal_GE(xds, name, text, wfc, baseURI) + type(xml_doc_state), intent(inout) :: xds + character(len=*), intent(in) :: name + character(len=*), intent(in) :: text + logical, intent(in) :: wfc + type(URI), pointer :: baseURI + + call add_internal_entity(xds%entityList, name=name, text=text, & + wfc=wfc, baseURI=baseURI) + + end subroutine register_internal_GE + + subroutine register_external_GE(xds, name, systemId, wfc, baseURI, publicId, notation) + type(xml_doc_state), intent(inout) :: xds + character(len=*), intent(in) :: name + character(len=*), intent(in) :: systemId + logical, intent(in) :: wfc + character(len=*), intent(in), optional :: publicId + character(len=*), intent(in), optional :: notation + type(URI), pointer :: baseURI + + call add_external_entity(xds%entityList, name=name, & + systemId=systemId, publicId=publicId, notation=notation, & + wfc=wfc, baseURI=baseURI) + end subroutine register_external_GE + +#endif +end module m_common_struct diff --git a/src/xml/common/makefile b/src/xml/common/makefile new file mode 100644 index 0000000000..063c288a21 --- /dev/null +++ b/src/xml/common/makefile @@ -0,0 +1,58 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys -I../utils $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_common.o: m_common_attrs.o +m_common_attrs.o: m_common_element.o m_common_error.o +m_common_buffer.o: m_common_charset.o m_common_error.o +m_common_element.o: m_common_charset.o m_common_content_model.o m_common_error.o m_common_namecheck.o +m_common_elstack.o: m_common_content_model.o m_common_error.o +m_common_entities.o: m_common_charset.o m_common_error.o +m_common_entity_expand.o: m_common_entities.o m_common_error.o +m_common_entity_expand.o: m_common_namecheck.o m_common_struct.o +m_common_io.o: m_common_error.o +m_common_namecheck.o: m_common_charset.o +m_common_namespaces.o: m_common_attrs.o m_common_charset.o m_common_error.o +m_common_namespaces.o: m_common_namecheck.o m_common_struct.o +m_common_notations.o: m_common_error.o +m_common_struct.o: m_common_charset.o m_common_element.o m_common_entities.o +m_common_struct.o: m_common_notations.o diff --git a/src/xml/dom/FoX_dom.F90 b/src/xml/dom/FoX_dom.F90 new file mode 100644 index 0000000000..e6d8cafc62 --- /dev/null +++ b/src/xml/dom/FoX_dom.F90 @@ -0,0 +1,261 @@ +module FoX_dom + + use fox_m_fsys_array_str + use fox_m_fsys_format + + use m_dom_dom + use m_dom_error + use m_dom_extras + use m_dom_parse + use m_dom_utils + + implicit none + private + + public :: str_vs, vs_vs_alloc, vs_str_alloc + public :: str, operator(//) + + public :: DOMImplementation + public :: Node + public :: NodeList + public :: NamedNodeMap + + ! DOM DOMString + ! no + + ! DOM DOMTimestamp + ! public :: DOMTimestamp + + ! DOM Exceptions + public :: DOMException + public :: inException + public :: getExceptionCode + + public :: INDEX_SIZE_ERR + public :: DOMSTRING_SIZE_ERR + public :: HIERARCHY_REQUEST_ERR + public :: WRONG_DOCUMENT_ERR + public :: INVALID_CHARACTER_ERR + public :: NO_DATA_ALLOWED_ERR + public :: NO_MODIFICATION_ALLOWED_ERR + public :: NOT_FOUND_ERR + public :: NOT_SUPPORTED_ERR + public :: INUSE_ATTRIBUTE_ERR + public :: INVALID_STATE_ERR + public :: SYNTAX_ERR + public :: INVALID_MODIFICATION_ERR + public :: NAMESPACE_ERR + public :: INVALID_ACCESS_ERR + public :: VALIDATION_ERR + public :: TYPE_MISMATCH_ERR + ! XPath + public :: INVALID_EXPRESSION_ERR + public :: TYPE_ERR + ! LS + public :: PARSE_ERR + public :: SERIALIZE_ERR + + ! DOM Implementation + public :: hasFeature + public :: createDocumentType + public :: createDocument + + ! DOM Document + public :: getDocumentElement + public :: getDocType + public :: getImplementation + public :: createDocumentFragment + public :: createElement + public :: createTextNode + public :: createComment + public :: createCDATASection + public :: createProcessingInstruction + public :: createAttribute + public :: createEntityReference + public :: getElementsByTagName + public :: getElementById + + public :: importNode + public :: createElementNS + public :: createAttributeNS + public :: getElementsByTagNameNS + + public :: getXmlStandalone + public :: setXmlStandalone + public :: getXmlVersion + public :: setXmlVersion + public :: getXmlEncoding + public :: getInputEncoding + public :: getDocumentURI + public :: setDocumentURI + public :: getStrictErrorChecking + public :: setStrictErrorChecking + public :: getDomConfig + public :: normalizeDocument + public :: renameNode + public :: adoptNode + + ! DOM Node + public :: ELEMENT_NODE + public :: ATTRIBUTE_NODE + public :: TEXT_NODE + public :: CDATA_SECTION_NODE + public :: ENTITY_REFERENCE_NODE + public :: ENTITY_NODE + public :: PROCESSING_INSTRUCTION_NODE + public :: COMMENT_NODE + public :: DOCUMENT_NODE + public :: DOCUMENT_TYPE_NODE + public :: DOCUMENT_FRAGMENT_NODE + public :: NOTATION_NODE + + public :: getNodeName + public :: getNodeValue + public :: setNodeValue + public :: getNodeType + public :: getFirstChild + public :: getLastChild + public :: getAttributes + public :: getNextSibling + public :: getPreviousSibling + public :: getParentNode + public :: getChildNodes + public :: getOwnerDocument + public :: insertBefore + public :: replaceChild + public :: removeChild + public :: appendChild + public :: hasChildNodes + public :: cloneNode + public :: normalize + + public :: isSupported + public :: getNamespaceURI + public :: getPrefix + public :: setPrefix + public :: getLocalName + public :: hasAttributes + + public :: getTextContent + public :: setTextContent + public :: isEqualNode + public :: isSameNode + public :: isDefaultNamespace + public :: lookupNamespaceURI + public :: lookupPrefix + + ! DOM NodeList + public :: item + public :: append + + ! DOM NamedNodeMap + public :: getLength + public :: getNamedItem + public :: setNamedItem + public :: removeNamedItem +! public :: item + public :: getNamedItemNS + public :: setNamedItemNS + public :: removeNamedItemNS + + ! DOM CharacterData + ! NB We use the native Fortran string type here + ! rather than inventing a DOM String, thus no + ! string type to make public +! public :: getData +! public :: setData + public :: substringData + public :: appendData + public :: insertData + public :: deleteData + public :: replaceData + + ! DOM Attr +! public :: getName + public :: getSpecified + public :: getValue + public :: setValue + public :: getOwnerElement + public :: getIsId + + ! DOM Element + public :: getTagName + public :: getAttribute + public :: setAttribute + public :: removeAttribute + public :: getAttributeNode + public :: setAttributeNode + public :: removeAttributeNode +! public :: getElementsByTagName + public :: getAttributeNS + public :: setAttributeNS + public :: removeAttributeNS + public :: getAttributeNodeNS + public :: setAttributeNodeNS +! public :: getElementsByTagNameNS + public :: hasAttribute + public :: hasAttributeNS + public :: setIdAttribute + public :: setIdAttributeNS + public :: setIdAttributeNode + + !DOM Text + public :: splitText + public :: getIsElementContentWhitespace + + !DOM CData +! public :: getData +! public :: setData + + !DOM DocumentType + public :: getEntities + public :: getNotations + public :: getInternalSubset + + !DOM Notation + + !DOM Entity + public :: getNotationName + + !DOM EntityReference + + !DOM ProcessingInstruction +! public :: getData +! public :: setData + public :: getTarget + + !DOM common + public :: getData + public :: setData + public :: getName + public :: getPublicId + public :: getSystemId + + !DOM Configuration + public :: DOMConfiguration + public :: getParameter + public :: setParameter + public :: canSetParameter + public :: getParameterNames + + ! FoX-only interfaces + public :: newDOMConfig + + public :: getNodePath + + public :: extractDataContent + public :: extractDataAttribute + public :: extractDataAttributeNS + + public :: parseFile + public :: parseString + public :: serialize + + public :: destroy + public :: getFoX_checks + public :: setFoX_checks + public :: getLiveNodeLists + public :: setLiveNodeLists + public :: getNamespaceNodes + +end module FoX_dom diff --git a/src/xml/dom/m_dom_dom.F90 b/src/xml/dom/m_dom_dom.F90 new file mode 100644 index 0000000000..6725f52433 --- /dev/null +++ b/src/xml/dom/m_dom_dom.F90 @@ -0,0 +1,12321 @@ +! ATTENTION +! THIS FILE IS AUTOGENERATED +! DO NOT EDIT DIRECTLY +! EDIT FILES dom/m_dom_***.m4 +! +module m_dom_dom + + use fox_m_fsys_array_str, only: str_vs, vs_str, vs_str_alloc + use fox_m_fsys_format, only: operator(//) + use fox_m_fsys_string, only: toLower + use fox_m_utils_uri, only: URI, parseURI, destroyURI, isAbsoluteURI, & + rebaseURI, expressURI + use m_common_charset, only: checkChars, XML1_0, XML1_1 + use m_common_element, only: element_t, get_element, attribute_t, & + attribute_has_default, get_attribute_declaration, get_attlist_size + use m_common_namecheck, only: checkQName, prefixOfQName, localPartOfQName, & + checkName, checkPublicId, checkNCName + use m_common_struct, only: xml_doc_state, init_xml_doc_state, destroy_xml_doc_state + + use m_dom_error, only: DOMException, throw_exception, inException, getExceptionCode, & + NO_MODIFICATION_ALLOWED_ERR, NOT_FOUND_ERR, HIERARCHY_REQUEST_ERR, & + WRONG_DOCUMENT_ERR, FoX_INTERNAL_ERROR, FoX_NODE_IS_NULL, FoX_LIST_IS_NULL, & + INUSE_ATTRIBUTE_ERR, FoX_MAP_IS_NULL, INVALID_CHARACTER_ERR, NAMESPACE_ERR, & + FoX_INVALID_PUBLIC_ID, FoX_INVALID_SYSTEM_ID, FoX_IMPL_IS_NULL, FoX_INVALID_NODE, & + FoX_INVALID_CHARACTER, FoX_INVALID_COMMENT, FoX_INVALID_CDATA_SECTION, & + FoX_INVALID_PI_DATA, NOT_SUPPORTED_ERR, FoX_INVALID_ENTITY, & + INDEX_SIZE_ERR, FoX_NO_SUCH_ENTITY, FoX_HIERARCHY_REQUEST_ERR, & + FoX_INVALID_URI + + implicit none + private + + + integer, parameter :: configParamLen = 42 + + character(len=configParamLen), parameter :: configParams(24) = (/ & + ! DOM 3 Core: + "canonical-form ", & + "cdata-sections ", & + "check-character-normalization ", & + "comments ", & + "datatype-normalization ", & + "element-content-whitespace ", & + "entities ", & + "error-handler ", & +! "infoset ", & is not a real config option + "namespaces ", & + "namespace-declarations ", & + "normalize-characters ", & +! "schema-location ", & we dont implement +! "schema-type ", & we dont implement + "split-cdata-sections ", & + "validate ", & + "validate-if-schema ", & + "well-formed ", & + ! DOM 3 LS (Parser): + "charset-overrides-xml-encoding ", & + "disallow-doctype ", & + "ignore-unknown-character-denormalizations", & + "resource-resolver ", & + "supported-media-types-only ", & + ! DOM 3 LS (Serializer) + "discard-default-content ", & + "format-pretty-print ", & + "xml-declaration ", & + ! Extra (FoX) configuration options + "invalid-pretty-print " /) + + integer, parameter :: paramSettable = 27293398 + integer, parameter :: paramDefaults = 10786516 + + type DOMConfiguration + private + integer :: parameters = paramDefaults + ! FIXME make sure this is 32 bit at least. + end type DOMConfiguration + + interface canSetParameter + module procedure canSetParameter_log + module procedure canSetParameter_ch + end interface canSetParameter + + public :: setParameter + public :: getParameter + public :: canSetParameter + public :: getParameterNames + + public :: newDOMConfig + public :: copyDOMConfig + + + integer, parameter :: ELEMENT_NODE = 1 + integer, parameter :: ATTRIBUTE_NODE = 2 + integer, parameter :: TEXT_NODE = 3 + integer, parameter :: CDATA_SECTION_NODE = 4 + integer, parameter :: ENTITY_REFERENCE_NODE = 5 + integer, parameter :: ENTITY_NODE = 6 + integer, parameter :: PROCESSING_INSTRUCTION_NODE = 7 + integer, parameter :: COMMENT_NODE = 8 + integer, parameter :: DOCUMENT_NODE = 9 + integer, parameter :: DOCUMENT_TYPE_NODE = 10 + integer, parameter :: DOCUMENT_FRAGMENT_NODE = 11 + integer, parameter :: NOTATION_NODE = 12 + integer, parameter :: XPATH_NAMESPACE_NODE = 13 + + type DOMImplementation + private + character(len=7) :: id = "FoX_DOM" + logical :: FoX_checks = .true. ! Do extra checks not mandated by DOM + end type DOMImplementation + + type ListNode + private + type(Node), pointer :: this => null() + end type ListNode + + type NodeList + private + character, pointer :: nodeName(:) => null() ! What was getByTagName run on? + character, pointer :: localName(:) => null() ! What was getByTagNameNS run on? + character, pointer :: namespaceURI(:) => null() ! What was getByTagNameNS run on? + type(Node), pointer :: element => null() ! which element or document was the getByTagName run from? + type(ListNode), pointer :: nodes(:) => null() + integer :: length = 0 + end type NodeList + + type NodeListptr + private + type(NodeList), pointer :: this + end type NodeListptr + + type NamedNodeMap + private + logical :: readonly = .false. + type(Node), pointer :: ownerElement => null() + type(ListNode), pointer :: nodes(:) => null() + integer :: length = 0 + end type NamedNodeMap + + type documentExtras + type(DOMImplementation), pointer :: implementation => null() ! only for doctype + type(Node), pointer :: docType => null() + type(Node), pointer :: documentElement => null() + character, pointer :: inputEncoding(:) => null() + character, pointer :: xmlEncoding(:) => null() + type(NodeListPtr), pointer :: nodelists(:) => null() ! document + ! In order to keep track of all nodes not connected to the document + logical :: liveNodeLists ! For the document, are nodelists live? + type(NodeList) :: hangingNodes ! For the document, list of nodes not associated with doc + type(xml_doc_state), pointer :: xds => null() + logical :: strictErrorChecking = .true. + logical :: brokenNS = .false. ! FIXME consolidate these logical variables into bitmask + type(DOMConfiguration), pointer :: domConfig => null() + end type documentExtras + + type elementOrAttributeExtras + ! Needed for all: + character, pointer, dimension(:) :: namespaceURI => null() + character, pointer, dimension(:) :: prefix => null() + character, pointer, dimension(:) :: localName => null() + ! Needed for elements: + type(NamedNodeMap) :: attributes + type(NodeList) :: namespaceNodes + ! Needed for attributes: + type(Node), pointer :: ownerElement => null() + logical :: specified = .true. + logical :: isId = .false. + logical :: dom1 = .false. + end type elementOrAttributeExtras + + type docTypeExtras + character, pointer :: publicId(:) => null() ! doctype, entity, notation + character, pointer :: systemId(:) => null() ! doctype, entity, notation + character, pointer :: notationName(:) => null() ! entity + logical :: illFormed = .false. ! entity + type(namedNodeMap) :: entities ! doctype + type(namedNodeMap) :: notations ! doctype + end type docTypeExtras + + type Node + private + logical :: readonly = .false. + character, pointer, dimension(:) :: nodeName => null() + character, pointer, dimension(:) :: nodeValue => null() + integer :: nodeType = 0 + type(Node), pointer :: parentNode => null() + type(Node), pointer :: firstChild => null() + type(Node), pointer :: lastChild => null() + type(Node), pointer :: previousSibling => null() + type(Node), pointer :: nextSibling => null() + type(Node), pointer :: ownerDocument => null() + type(NodeList) :: childNodes ! not for text, cdata, PI, comment, notation, docType, XPath + logical :: inDocument = .false.! For a node, is this node associated to the doc? + logical :: ignorableWhitespace = .false. ! Text nodes only + type(documentExtras), pointer :: docExtras => null() + type(elementOrAttributeExtras), pointer :: elExtras => null() + type(docTypeExtras), pointer :: dtdExtras => null() + integer :: textContentLength = 0 + end type Node + + type(DOMImplementation), save, target :: FoX_DOM + + interface destroy + module procedure destroyNode + module procedure destroyNodeList + module procedure destroyNamedNodeMap + module procedure destroyDOMConfig + end interface destroy + + public :: ELEMENT_NODE + public :: ATTRIBUTE_NODE + public :: TEXT_NODE + public :: CDATA_SECTION_NODE + public :: ENTITY_REFERENCE_NODE + public :: ENTITY_NODE + public :: PROCESSING_INSTRUCTION_NODE + public :: COMMENT_NODE + public :: DOCUMENT_NODE + public :: DOCUMENT_TYPE_NODE + public :: DOCUMENT_FRAGMENT_NODE + public :: NOTATION_NODE + + public :: DOMImplementation + public :: DOMConfiguration + public :: Node + + public :: ListNode + public :: NodeList + public :: NamedNodeMap + + public :: destroy + public :: destroyAllNodesRecursively + + + + public :: getNodeName + public :: getNodeValue + public :: setNodeValue + public :: getNodeType + public :: getParentNode + public :: getChildNodes + public :: getFirstChild + public :: getLastChild + public :: getNextSibling + public :: getPreviousSibling + public :: getAttributes + public :: getOwnerDocument + public :: insertBefore + public :: replaceChild + public :: removeChild + public :: appendChild + public :: hasChildNodes + public :: cloneNode + public :: normalize + public :: isSupported + public :: getNamespaceURI + public :: getPrefix + public :: setPrefix + public :: getLocalName + public :: hasAttributes + public :: isEqualNode + public :: isSameNode + public :: isDefaultNamespace + public :: lookupNamespaceURI + public :: lookupPrefix + public :: getTextContent + public :: setTextContent + + public :: getNodePath + + public :: setStringValue + public :: getStringValue + public :: setReadonlyNode + public :: getReadOnly + + public :: getBaseURI + + + + public :: item + public :: append + public :: pop_nl + public :: remove_nl + public :: destroyNodeList + + interface append + module procedure append_nl + end interface + + interface item + module procedure item_nl + end interface + + interface getLength + module procedure getLength_nl + end interface getLength + + + public :: getNamedItem + public :: setNamedItem + public :: removeNamedItem +! public :: item +! public :: getLength + public :: getNamedItemNS + public :: setNamedItemNS + public :: removeNamedItemNS + +! public :: append + public :: setReadOnlyMap + public :: destroyNamedNodeMap + + + interface item + module procedure item_nnm + end interface + + interface getLength + module procedure getLength_nnm + end interface + + + + public :: hasFeature + public :: createDocument + public :: createDocumentType + + public :: destroyDocument + public :: createEmptyDocument + + public :: getFoX_checks + public :: setFoX_checks + + + +!FIXME lots of these should have a check if(namespaces) checkNCName + + public :: getDocType + public :: getImplementation + public :: getDocumentElement + public :: setDocumentElement + + public :: createElement + public :: createDocumentFragment + public :: createTextNode + public :: createComment + public :: createCdataSection + public :: createProcessingInstruction + public :: createAttribute + public :: createEntityReference + public :: createEmptyEntityReference + public :: getElementsByTagName + public :: importNode + public :: createElementNS + public :: createAttributeNS + public :: getElementsByTagNameNS + public :: getElementById + public :: getXmlStandalone + public :: setXmlStandalone + public :: getXmlVersion + public :: setXmlVersion + public :: getXmlEncoding + public :: getInputEncoding + public :: getDocumentURI + public :: setDocumentURI + public :: getStrictErrorChecking + public :: setStrictErrorChecking + public :: getDomConfig + public :: renameNode + public :: adoptNode + + public :: setDocType + public :: setDomConfig + public :: setXds + public :: createNamespaceNode + public :: createEntity + public :: createNotation + public :: setGCstate + public :: getXds + public :: getLiveNodeLists + public :: setLiveNodeLists + + + !public :: getName + public :: getEntities + public :: getNotations +! public :: getPublicId +! public :: getSystemId + public :: getInternalSubset + + + + public :: getTagName + public :: getAttribute + public :: setAttribute + public :: removeAttribute + public :: getAttributeNode + public :: setAttributeNode + public :: removeAttributeNode + public :: getAttributeNS + public :: setAttributeNS + public :: removeAttributeNS + public :: getAttributeNodeNS + public :: setAttributeNodeNS + public :: removeAttributeNodeNS + public :: hasAttribute + public :: hasAttributeNS + public :: setIdAttribute + public :: setIdAttributeNS + public :: setIdAttributeNode + + + + !public :: getName + public :: getSpecified + public :: setSpecified + interface getValue + module procedure getValue_DOM + end interface + public :: getValue + public :: setValue + public :: getOwnerElement + + public :: getIsId + public :: setIsId + interface getIsId + module procedure getIsId_DOM + end interface + interface setIsId + module procedure setIsId_DOM + end interface + + + + public :: getLength +! public :: getData +! public :: setData + public :: substringData + public :: appendData + public :: insertData + public :: deleteData + public :: replaceData + + interface getLength + module procedure getLength_characterdata + end interface + + + + public :: getNotationName + + public :: getIllFormed + public :: setIllFormed + + + + public :: getTarget + + + public :: splitText + public :: getIsElementContentWhitespace + public :: setIsElementContentWhitespace + + +! Assorted functions with identical signatures despite belonging to different types. + + public :: getData + public :: setData + public :: getName + public :: getPublicId + public :: getSystemId + + + + public :: normalizeDocument + + public :: getNamespaceNodes + public :: namespaceFixup + + +contains + + + subroutine resetParameter(domConfig, name) + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + + integer :: i, n + do i = 1, size(configParams) + if (toLower(name)==trim(configParams(i))) then + n = i + exit + endif + enddo + if (i>size(configParams)) return + if (.not.btest(paramSettable, n)) return + if (btest(paramDefaults, n)) then + domConfig%parameters = ibset(domConfig%parameters, n) + else + domConfig%parameters = ibclr(domConfig%parameters, n) + endif + end subroutine resetParameter + + recursive subroutine setParameter(domConfig, name, value, ex) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + logical, intent(in) :: value + integer :: i, n + + if (toLower(name)=="infoset") then + if (value) then + call setParameter(domConfig, "validate-if-schema", .false.) + call setParameter(domConfig, "entities", .false.) + ! cant do datatype-normalization + call setParameter(domConfig, "cdata-sections", .false.) + call setParameter(domConfig, "namespace-declarations", .true.) + ! well-formed cannot be changed + call setParameter(domConfig, "element-content-whitespace", .true.) + call setParameter(domConfig, "comments", .true.) + call setParameter(domConfig, "namespaces", .true.) + endif + return + endif + + do i = 1, size(configParams) + if (toLower(name)==trim(configParams(i))) then + n = i + exit + endif + enddo + if (i > size(configParams)) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "setParameter", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (.not.canSetParameter(domConfig, name, value)) then + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "setParameter", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (value) then + domConfig%parameters = ibset(domConfig%parameters, n) + else + domConfig%parameters = ibclr(domConfig%parameters, n) + endif + + select case (toLower(name)) + case ("canonical-form") + if (value) then + domConfig%parameters = ibclr(domConfig%parameters, 7) + ! cant do normalize-characters + domConfig%parameters = ibclr(domConfig%parameters, 2) + domConfig%parameters = ibset(domConfig%parameters, 9) + domConfig%parameters = ibset(domConfig%parameters, 10) + ! well-formed cannot be changed + domConfig%parameters = ibset(domConfig%parameters, 6) + ! FIXME when we work out pretty-print/preserve-whitespace semantics + ! call setParameter(domConfig, "format-pretty-print", .false.) + domConfig%parameters = ibclr(domConfig%parameters, 21) + domConfig%parameters = ibclr(domConfig%parameters, 23) + domConfig%parameters = ibclr(domConfig%parameters, 24) + else + call resetParameter(domConfig, "entities") + ! cant do normalize-characters + call resetParameter(domConfig, "cdata-sections") + call resetParameter(domConfig, "namespaces") + call resetParameter(domConfig, "namespace-declarations") + ! well-formed cannot be changed + call resetParameter(domConfig, "element-content-whitespace") + call resetParameter(domConfig, "format-pretty-print") + call resetParameter(domConfig, "discard-default-content") + call resetParameter(domConfig, "xml-declaration") + call resetParameter(domConfig, "invalid-pretty-print") + endif + case ("cdata-sections") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("element-content-whitespace") + if (.not.value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("entities") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("namespaces") + if (.not.value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("namespaces-declarations") + if (.not.value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case("validate") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 14) + case ("validate-if-schema") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 13) + case ("format-pretty-print") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("discard-default-content") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("xml-declaration") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("invalid-pretty-print") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + end select + + end subroutine setParameter + + recursive function getParameter(domConfig, name, ex)result(value) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + logical :: value + + integer :: i, n + + if (toLower(name)=="infoset") then + value = & + .not.getParameter(domConfig, "validate-if-schema") & + .and..not.getParameter(domConfig, "entities") & + .and..not.getParameter(domConfig, "datatype-normalization") & + .and..not.getParameter(domConfig, "cdata-sections") & + .and.getParameter(domConfig, "namespace-declarations") & + .and.getParameter(domConfig, "well-formed") & + .and.getParameter(domConfig, "element-content-whitespace") & + .and.getParameter(domConfig, "comments") & + .and.getParameter(domConfig, "namespaces") + return + endif + + do i = 1, size(configParams) + if (toLower(name)==trim(configParams(i))) then + n = i + exit + endif + enddo + if (i > size(configParams)) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "getParameter", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + value = btest(domConfig%parameters, n) + + end function getParameter + + function canSetParameter_log(domConfig, name, value, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + logical, intent(in) :: value + logical :: p + + integer :: i, n + + if (toLower(name)=="infoset") then + p = .true. + return + endif + do i = 1, size(configParams) + if (toLower(name)==trim(configParams(i))) then + n = i + exit + endif + enddo + if (i > size(configParams)) then + p = .false. + return + endif + + p = btest(paramSettable, n) + + end function canSetParameter_log + + function canSetParameter_ch(domConfig, name, value, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + logical :: p + + ! DOM 3 allows some config options to be set to strings + ! (eg schemaLocation) but we dont support any of these, + ! so no parameter can be set to a string. + p = .false. + + end function canSetParameter_ch + + function getParameterNames(domConfig, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=configParamLen) :: s(size(configParams)) + + s = configParams + end function getParameterNames + + function newDOMConfig() result(dc) + type(DOMConfiguration), pointer :: dc + allocate(dc) + end function newDOMConfig + + subroutine copyDOMConfig(dc1, dc2) + type(DOMConfiguration), pointer :: dc1, dc2 + + dc1%parameters = dc2%parameters + end subroutine copyDOMConfig + + subroutine destroyDOMConfig(dc) + type(DOMConfiguration), pointer :: dc + + deallocate(dc) + end subroutine destroyDOMConfig + + + + function createNode(arg, nodeType, nodeName, nodeValue, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: nodeType + character(len=*), intent(in) :: nodeName + character(len=*), intent(in) :: nodeValue + type(Node), pointer :: np + + allocate(np) + np%ownerDocument => arg + np%nodeType = nodeType + np%nodeName => vs_str_alloc(nodeName) + np%nodeValue => vs_str_alloc(nodeValue) + + allocate(np%childNodes%nodes(0)) + + end function createNode + + recursive subroutine destroyNode(np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + + if (.not.associated(np)) return + + select case(np%nodeType) + case (ELEMENT_NODE, ATTRIBUTE_NODE, XPATH_NAMESPACE_NODE) + call destroyElementOrAttribute(np) + case (DOCUMENT_TYPE_NODE) + call destroyDocumentType(np) + case (ENTITY_NODE, NOTATION_NODE) + call destroyEntityOrNotation(np) + case (DOCUMENT_NODE) + call destroyDocument(np) + end select + call destroyNodeContents(np) + deallocate(np) + + end subroutine destroyNode + + recursive subroutine destroyElementOrAttribute(np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + + integer :: i + + if (np%nodeType /= ELEMENT_NODE & + .and. np%nodeType /= ATTRIBUTE_NODE & + .and. np%nodeType /= XPATH_NAMESPACE_NODE) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "destroyElementOrAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%elExtras%attributes%nodes)) deallocate(np%elExtras%attributes%nodes) + do i = 1, np%elExtras%namespaceNodes%length + call destroyNode(np%elExtras%namespaceNodes%nodes(i)%this) + enddo + if (associated(np%elExtras%namespaceNodes%nodes)) deallocate(np%elExtras%namespaceNodes%nodes) + if (associated(np%elExtras%namespaceURI)) deallocate(np%elExtras%namespaceURI) + if (associated(np%elExtras%prefix)) deallocate(np%elExtras%prefix) + if (associated(np%elExtras%localName)) deallocate(np%elExtras%localName) + deallocate(np%elExtras) + + end subroutine destroyElementOrAttribute + + subroutine destroyEntityOrNotation(np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + + if (np%nodeType /= ENTITY_NODE & + .and. np%nodeType /= NOTATION_NODE) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "destroyEntityOrNotation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%dtdExtras%publicId)) deallocate(np%dtdExtras%publicId) + if (associated(np%dtdExtras%systemId)) deallocate(np%dtdExtras%systemId) + if (associated(np%dtdExtras%notationName)) deallocate(np%dtdExtras%notationName) + + deallocate(np%dtdExtras) + + end subroutine destroyEntityOrNotation + + subroutine destroyDocumentType(np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + + integer :: i + + if (np%nodeType /= DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "destroyDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%dtdExtras%publicId)) deallocate(np%dtdExtras%publicId) + if (associated(np%dtdExtras%systemId)) deallocate(np%dtdExtras%systemId) + + ! Destroy all entities & notations (docType only) + if (associated(np%dtdExtras%entities%nodes)) then + do i = 1, size(np%dtdExtras%entities%nodes) + call destroyAllNodesRecursively(np%dtdExtras%entities%nodes(i)%this) + enddo + deallocate(np%dtdExtras%entities%nodes) + endif + if (associated(np%dtdExtras%notations%nodes)) then + do i = 1, size(np%dtdExtras%notations%nodes) + call destroy(np%dtdExtras%notations%nodes(i)%this) + enddo + deallocate(np%dtdExtras%notations%nodes) + endif + + deallocate(np%dtdExtras) + + end subroutine destroyDocumentType + + recursive subroutine destroyAllNodesRecursively(arg, except) + ! Only recurses once into destroyDocumentType + type(Node), pointer :: arg + logical, intent(in), optional :: except + + type(Node), pointer :: this, deadNode, treeroot + logical :: doneChildren, doneAttributes + integer :: i_tree + + if (.not.associated(arg)) return + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + deadNode => null() + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + deadNode => null() + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + deadNode => this + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + call destroy(deadNode) + endif + + enddo + + + + deallocate(arg%childNodes%nodes) + allocate(arg%childNodes%nodes(0)) + arg%firstChild => null() + arg%lastChild => null() + + if (.not.present(except)) call destroyNode(arg) + + end subroutine destroyAllNodesRecursively + + subroutine destroyNodeContents(np) + type(Node), intent(inout) :: np + + if (associated(np%nodeName)) deallocate(np%nodeName) + if (associated(np%nodeValue)) deallocate(np%nodeValue) + + deallocate(np%childNodes%nodes) + + end subroutine destroyNodeContents + + + + + pure function getnodeName_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p) then + n = size(np%nodeName) + else + n = 0 + endif + end function getnodeName_len +function getnodeName(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getnodeName_len(np, .true.)) :: c +#else + character(len=getnodeName_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnodeName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c = str_vs(np%nodeName) + + end function getnodeName + + + pure function getNodeValue_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + n = 0 + if (.not.p) return + + select case(np%nodeType) + case (ATTRIBUTE_NODE) + n = getTextContent_len(np, .true.) + case (CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE, TEXT_NODE) + n = size(np%nodeValue) + end select + + end function getNodeValue_len + + function getNodeValue(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getNodeValue_len(np, .true.)) :: c +#else + character(len=getNodeValue_len(np, associated(np))) :: c +#endif + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getNodeValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + select case(np%nodeType) + case (ATTRIBUTE_NODE) + c = getTextContent(np) + case (CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE, TEXT_NODE) + c = str_vs(np%nodeValue) + case default + c = "" + end select + + end function getNodeValue + + subroutine setNodeValue(arg, nodeValue, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*) :: nodeValue + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setNodeValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(getOwnerDocument(arg))) then + if (.not.checkChars(nodeValue, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "setNodeValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif ! Otherwise its a document node, and nothing will happen anyway + + select case(arg%nodeType) + case (ATTRIBUTE_NODE) + call setValue(arg, nodeValue, ex) + case (CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE, TEXT_NODE) + call setData(arg, nodeValue, ex) + end select + + end subroutine setNodeValue + +function getnodeType(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + integer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnodeType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c = np%nodeType + + end function getnodeType + + +function getparentNode(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getparentNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%parentNode + + end function getparentNode + + +function getchildNodes(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(NodeList), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getchildNodes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%childNodes + + end function getchildNodes + + +function getfirstChild(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getfirstChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%firstChild + + end function getfirstChild + + +function getlastChild(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getlastChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%lastChild + + end function getlastChild + + +function getpreviousSibling(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getpreviousSibling", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%previousSibling + + end function getpreviousSibling + + +function getnextSibling(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnextSibling", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%nextSibling + + end function getnextSibling + + + function getAttributes(arg, ex)result(nnm) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(NamedNodeMap), pointer :: nnm + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttributes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg)==ELEMENT_NODE) then + nnm => arg%elExtras%attributes + else + nnm => null() + endif + end function getAttributes + + function getOwnerDocument(arg, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getOwnerDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType==DOCUMENT_NODE) then + np => null() + else + np => arg%ownerDocument + endif + end function getOwnerDocument + +subroutine setownerDocument(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setownerDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setownerDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%ownerDocument => c + + end subroutine setownerDocument + + + function insertBefore(arg, newChild, refChild, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newChild + type(Node), pointer :: refChild + type(Node), pointer :: np + + type(Node), pointer :: testChild, testParent, treeroot, this + type(ListNode), pointer :: temp_nl(:) + integer :: i, i2, i_t, i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg).or..not.associated(newChild)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(refChild)) then + np => appendChild(arg, newChild, ex) + return + endif + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + testParent => arg + ! Check if you are allowed to put a newChild nodetype under a arg nodetype + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + testChild => newChild%childNodes%nodes(i)%this + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement))) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType)))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + enddo + else + testChild => newChild + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement))) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType)))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + ! And then check that newChild is not arg or one of args ancestors + ! (this would never be true if newChild is a documentFragment) + testParent => arg + do while (associated(testParent)) + if (associated(testParent, newChild)) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + testParent => testParent%parentNode + enddo + endif + + if (getNodeType(newChild)/=DOCUMENT_TYPE_NODE.and. & + .not.(associated(arg%ownerDocument, newChild%ownerDocument) & + .or.associated(arg, newChild%ownerDocument))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE & + .and. newChild%childNodes%length==0) then + np => newChild + return + ! Nothing to do + endif + if (associated(getParentNode(newChild))) then + np => removeChild(getParentNode(newChild), newChild, ex) + newChild => np + endif + + if (arg%childNodes%length==0) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + allocate(temp_nl(arg%childNodes%length+newChild%childNodes%length)) + else + allocate(temp_nl(arg%childNodes%length+1)) + endif + + i_t = 0 + np => null() + do i = 1, arg%childNodes%length + if (associated(arg%childNodes%nodes(i)%this, refChild)) then + np => refChild + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i2 = 1, newChild%childNodes%length + i_t = i_t + 1 + temp_nl(i_t)%this => newChild%childNodes%nodes(i2)%this + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + enddo + else + i_t = i_t + 1 + temp_nl(i_t)%this => newChild + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + endif + if (i==1) then + arg%firstChild => temp_nl(1)%this + !temp_nl(1)%this%previousSibling => null() ! This is a no-op + else + temp_nl(i-1)%this%nextSibling => temp_nl(i)%this + temp_nl(i)%this%previousSibling => temp_nl(i-1)%this + endif + arg%childNodes%nodes(i)%this%previousSibling => temp_nl(i_t)%this + temp_nl(i_t)%this%nextSibling => arg%childNodes%nodes(i)%this + endif + i_t = i_t + 1 + temp_nl(i_t)%this => arg%childNodes%nodes(i)%this + enddo + + if (.not.associated(np)) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + + if (associated(temp_nl)) deallocate(temp_nl) + return + endif + endif +endif + + endif + + np => newChild + if (getGCstate(arg%ownerDocument)) then + if (arg%inDocument) then + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + call putNodesInDocument(arg%ownerDocument, newChild%childNodes%nodes(i)%this) + enddo + else + call putNodesInDocument(arg%ownerDocument, newChild) + endif + ! If newChild was originally in document, it was removed above so must be re-added + ! Ideally we would avoid the cost of removal & readding to hanging nodelist + endif + ! If arg was not in the document, then newChildren were either + ! a) removed above in call to removeChild or + ! b) in a document fragment and therefore not part of doc either + endif + + + if (getNodeType(newChild)==DOCUMENT_FRAGMENT_NODE) then + deallocate(newChild%childNodes%nodes) + allocate(newChild%childNodes%nodes(0)) + newChild%childNodes%length = 0 + endif + deallocate(arg%childNodes%nodes) + arg%childNodes%nodes => temp_nl + arg%childNodes%length = size(arg%childNodes%nodes) + + call updateNodeLists(arg%ownerDocument) + + call updateTextContentLength(arg, newChild%textContentLength) + + end function insertBefore + + + function replaceChild(arg, newChild, oldChild, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newChild + type(Node), pointer :: oldChild + type(Node), pointer :: np + + type(Node), pointer :: testChild, testParent, treeroot, this + type(ListNode), pointer :: temp_nl(:) + integer :: i, i2, i_t, i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg).or..not.associated(newChild).or..not.associated(oldChild)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + testParent => arg + ! Check if you are allowed to put a newChild nodetype under a arg nodetype + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + testChild => newChild%childNodes%nodes(i)%this + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement) & + .and.oldChild%nodeType/=ELEMENT_NODE)) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType) & + .and.oldChild%nodeType/=DOCUMENT_TYPE_NODE))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + enddo + else + testChild => newChild + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement) & + .and.oldChild%nodeType/=ELEMENT_NODE)) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType) & + .and.oldChild%nodeType/=DOCUMENT_TYPE_NODE))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + ! And then check that newChild is not arg or one of args ancestors + ! (this would never be true if newChild is a documentFragment) + testParent => arg + do while (associated(testParent)) + if (associated(testParent, newChild)) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + testParent => testParent%parentNode + enddo + endif + + if (getNodeType(newChild)/=DOCUMENT_TYPE_NODE.and. & + .not.(associated(arg%ownerDocument, newChild%ownerDocument) & + .or.associated(arg, newChild%ownerDocument))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(getParentNode(newChild))) & + newChild => removeChild(getParentNode(newChild), newChild, ex) + + if (arg%childNodes%length==0) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + allocate(temp_nl(arg%childNodes%length+newChild%childNodes%length-1)) + else + temp_nl => arg%childNodes%nodes + endif + + i_t = 0 + np => null() + do i = 1, arg%childNodes%length + if (associated(arg%childNodes%nodes(i)%this, oldChild)) then + np => oldChild + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i2 = 1, newChild%childNodes%length + i_t = i_t + 1 + temp_nl(i_t)%this => newChild%childNodes%nodes(i2)%this + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + enddo + else + i_t = i_t + 1 + temp_nl(i_t)%this => newChild + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + endif + if (i==1) then + arg%firstChild => temp_nl(1)%this + !temp_nl(1)%this%previousSibling => null() ! This is a no-op + else + temp_nl(i-1)%this%nextSibling => temp_nl(i)%this + temp_nl(i)%this%previousSibling => temp_nl(i-1)%this + endif + if (i==arg%childNodes%length) then + arg%lastChild => temp_nl(i_t)%this + !temp_nl(i_t)%this%nextSibling => null() ! This is a no-op + else + arg%childNodes%nodes(i+1)%this%previousSibling => temp_nl(i_t)%this + temp_nl(i_t)%this%nextSibling => arg%childNodes%nodes(i+1)%this + endif + else + i_t = i_t + 1 + temp_nl(i_t)%this => arg%childNodes%nodes(i)%this + endif + enddo + + if (.not.associated(np)) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + np%parentNode => null() + np%previousSibling => null() + np%nextSibling => null() + +! call namespaceFixup(np) + + if (getGCstate(arg%ownerDocument)) then + if (arg%inDocument) then + call removeNodesFromDocument(arg%ownerDocument, oldChild) + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + call putNodesInDocument(arg%ownerDocument, newChild%childNodes%nodes(i)%this) + enddo + else + call putNodesInDocument(arg%ownerDocument, newChild) + endif + ! If newChild was originally in document, it was removed above so must be re-added + ! Ideally we would avoid the cost of removing & re-adding to hangingnodelist + endif + ! If arg was not in the document, then newChildren were either + ! a) removed above in call to removeChild or + ! b) in a document fragment and therefore not part of doc either + endif + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + deallocate(newChild%childNodes%nodes) + allocate(newChild%childNodes%nodes(0)) + newChild%childNodes%length = 0 + deallocate(arg%childNodes%nodes) + arg%childNodes%nodes => temp_nl + arg%childNodes%length = size(arg%childNodes%nodes) + endif + + call updateNodeLists(arg%ownerDocument) + + call updateTextContentLength(arg, newChild%textContentLength-oldChild%textContentLength) + + end function replaceChild + + + function removeChild(arg, oldChild, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: oldChild + type(Node), pointer :: np + + type(ListNode), pointer :: temp_nl(:) + integer :: i, i_t + + if (.not.associated(arg).or..not.associated(oldChild)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + allocate(temp_nl(size(arg%childNodes%nodes)-1)) + i_t = 1 + do i = 1, size(arg%childNodes%nodes) + if (associated(arg%childNodes%nodes(i)%this, oldChild)) then + if (associated(arg%firstChild, arg%lastChild)) then + ! There is only one child, we are removing it. + arg%firstChild => null() + arg%lastChild => null() + elseif (i==1) then + ! We are removing the first child, but there is a second + arg%firstChild => arg%childNodes%nodes(2)%this + arg%childNodes%nodes(2)%this%previousSibling => null() + elseif (i==size(arg%childNodes%nodes)) then + ! We are removing the last child, but there is a second-to-last + arg%lastChild => arg%childNodes%nodes(i-1)%this + arg%childNodes%nodes(i-1)%this%nextSibling => null() + else + ! We are removing a child in the middle + arg%childNodes%nodes(i-1)%this%nextSibling => arg%childNodes%nodes(i+1)%this + arg%childNodes%nodes(i+1)%this%previousSibling => arg%childNodes%nodes(i-1)%this + endif + else + if (i_t==size(arg%childNodes%nodes)) exit ! We have failed to find the child + temp_nl(i_t)%this => arg%childNodes%nodes(i)%this + i_t = i_t + 1 + endif + enddo + + deallocate(arg%childNodes%nodes) + arg%childNodes%nodes => temp_nl + arg%childNodes%length = size(temp_nl) + if (i==i_t) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + oldChild%parentNode => null() + oldChild%previousSibling => null() + oldChild%nextSibling => null() + +! call namespaceFixup(oldChild) + + if (getGCstate(arg%ownerDocument)) then + if (arg%inDocument) then + call removeNodesFromDocument(arg%ownerDocument, oldChild) + endif + endif + + np => oldChild + + call updateNodeLists(arg%ownerDocument) + + call updateTextContentLength(arg, -oldChild%textContentLength) + + end function removeChild + + + function appendChild(arg, newChild, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newChild + type(Node), pointer :: np + + type(Node), pointer :: testChild, testParent, treeroot, this + type(ListNode), pointer :: temp_nl(:) + integer :: i, i_t, i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg).or..not.associated(newChild)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + testParent => arg + ! Check if you are allowed to put a newChild nodetype under a arg nodetype + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + testChild => newChild%childNodes%nodes(i)%this + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement))) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType)))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + enddo + else + testChild => newChild + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement))) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType)))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + ! And then check that newChild is not arg or one of args ancestors + ! (this would never be true if newChild is a documentFragment) + testParent => arg + do while (associated(testParent)) + if (associated(testParent, newChild)) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + testParent => testParent%parentNode + enddo + endif + + if (getNodeType(newChild)/=DOCUMENT_TYPE_NODE.and. & + .not.(associated(arg%ownerDocument, newChild%ownerDocument) & + .or.associated(arg, newChild%ownerDocument))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE & + .and. newChild%childNodes%length==0) then + np => newChild + return + ! Nothing to do + endif + + if (associated(getParentNode(newChild))) & + newChild => removeChild(getParentNode(newChild), newChild, ex) + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + allocate(temp_nl(arg%childNodes%length+newChild%childNodes%length)) + else + allocate(temp_nl(arg%childNodes%length+1)) + endif + + do i = 1, arg%childNodes%length + temp_nl(i)%this => arg%childNodes%nodes(i)%this + enddo + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + i_t = arg%childNodes%length + do i = 1, newChild%childNodes%length + i_t = i_t + 1 + temp_nl(i_t)%this => newChild%childNodes%nodes(i)%this + if (arg%inDocument) & + call putNodesInDocument(arg%ownerDocument, temp_nl(i_t)%this) + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + enddo + if (arg%childNodes%length==0) then + arg%firstChild => newChild%firstChild + else + newChild%firstChild%previousSibling => arg%lastChild + arg%lastChild%nextSibling => newChild%firstChild + endif + arg%lastChild => newChild%lastChild + newChild%firstChild => null() + newChild%lastChild => null() + deallocate(newChild%childNodes%nodes) + allocate(newChild%childNodes%nodes(0)) + newChild%childNodes%length = 0 + else + temp_nl(i)%this => newChild + if (i==1) then + arg%firstChild => newChild + newChild%previousSibling => null() + else + temp_nl(i-1)%this%nextSibling => newChild + newChild%previousSibling => temp_nl(i-1)%this + endif + if (getGCstate(arg%ownerDocument)) then + if (arg%inDocument.and..not.newChild%inDocument) then + call putNodesInDocument(arg%ownerDocument, newChild) + endif + endif + newChild%nextSibling => null() + arg%lastChild => newChild + newChild%parentNode => arg +! call namespaceFixup(newChild) + endif + + deallocate(arg%childNodes%nodes) + arg%childNodes%nodes => temp_nl + arg%childNodes%length = size(temp_nl) + + np => newChild + + call updateNodeLists(arg%ownerDocument) + + call updateTextContentLength(arg, newChild%textContentLength) + + end function appendChild + + + function hasChildNodes(arg, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical :: hasChildNodes + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "hasChildNodes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + hasChildNodes = associated(arg%firstChild) + + end function hasChildNodes + + recursive function cloneNode(arg, deep, ex)result(np) + type(DOMException), intent(out), optional :: ex + ! Needs to be recursive in case of entity-references within each other. + type(Node), pointer :: arg + logical, intent(in) :: deep + type(Node), pointer :: np + + type(Node), pointer :: doc, treeroot, thatParent, this, new, ERchild + + logical :: doneAttributes, doneChildren, readonly, brokenNS + integer :: i_tree + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "cloneNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + thatParent => null() + ERchild => null() + doc => getOwnerDocument(arg) + if (.not.associated(doc)) return + np => null() + brokenNS = doc%docExtras%brokenNS + doc%docExtras%brokenNS = .true. ! May need to do stupid NS things + readonly = .false. + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + new => null() + select case(getNodeType(this)) + case (ELEMENT_NODE) + if (getParameter(getDomConfig(doc), "namespaces")) then + new => createEmptyElementNS(doc, getNamespaceURI(this), getTagName(this)) + else + new => createEmptyElement(doc, getTagName(this)) + endif + case (ATTRIBUTE_NODE) + if (getParameter(getDomConfig(doc), "namespaces")) then + new => createAttributeNS(doc, getNamespaceURI(this), getName(this)) + else + new => createAttribute(doc, getName(this)) + endif + if (associated(this, arg)) then + call setSpecified(new, .true.) + else + call setSpecified(new, getSpecified(this)) + endif + case (TEXT_NODE) + new => createTextNode(doc, getData(this)) + case (CDATA_SECTION_NODE) + new => createCDataSection(doc, getData(this)) + case (ENTITY_REFERENCE_NODE) + ERchild => this + readonly = .true. + new => createEntityReference(doc, getNodeName(this)) + doneChildren = .true. + case (ENTITY_NODE) + return + case (PROCESSING_INSTRUCTION_NODE) + new => createProcessingInstruction(doc, getTarget(this), getData(this)) + case (COMMENT_NODE) + new => createComment(doc, getData(this)) + case (DOCUMENT_NODE) + return + case (DOCUMENT_TYPE_NODE) + return + case (DOCUMENT_FRAGMENT_NODE) + new => createDocumentFragment(doc) + case (NOTATION_NODE) + return + end select + + if (.not.associated(thatParent)) then + thatParent => new + elseif (associated(new)) then + if (this%nodeType==ATTRIBUTE_NODE) then + new => setAttributeNode(thatParent, new) + else + new => appendChild(thatParent, new) + endif + endif + + if (.not.deep) then + if (getNodeType(arg)==ATTRIBUTE_NODE.or.getNodeType(arg)==ELEMENT_NODE) then + continue + else + exit + endif + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + + if (getNodeType(this)==ENTITY_REFERENCE_NODE & + .and.associated(ERchild, this)) then + ERchild => null() + readonly = .false. + endif + this%readonly = readonly + + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + if (.not.associated(this, treeroot)) thatParent => getLastChild(thatParent) + this => item(getAttributes(this), 0) + else + if (.not.deep) exit + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + if (getNodeType(this)==ELEMENT_NODE.and..not.deep) exit + if (.not.associated(this, treeroot)) then + if (getNodeType(this)==ATTRIBUTE_NODE) then + thatParent => item(getAttributes(thatParent), i_tree) + else + thatParent => getLastChild(thatParent) + endif + endif + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + if (associated(getParentNode(thatParent))) thatParent => getParentNode(thatParent) + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + if (.not.associated(this, treeroot)) then + if (getNodeType(this)==ATTRIBUTE_NODE) then + thatParent => getOwnerElement(thatParent) + else + thatParent => getParentNode(thatParent) + endif + endif + endif + endif + + enddo + + + + np => thatParent + doc%docExtras%brokenNS = brokenNS + + end function cloneNode + + + function hasAttributes(arg, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical :: hasAttributes + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "hasAttributes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType == ELEMENT_NODE) then + hasAttributes = (getLength(getAttributes(arg)) > 0) + else + hasAttributes = .false. + endif + + end function hasAttributes + +! function getBaseURI FIXME + +! function compareDocumentPosition FIXME + + subroutine normalize(arg, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: this, tempNode, oldNode, treeroot + integer :: i_tree, i_t + logical :: doneChildren, doneAttributes + character, pointer :: temp(:) + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "normalize", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +! DOM standard requires we ignore readonly status + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + if (getNodeType(this)==TEXT_NODE) then + if (associated(this, arg)) exit ! If we are called on a text node itself, then do nothing. + i_t = getLength(this) + tempNode => getNextSibling(this) + do while (associated(tempNode)) + if (getNodeType(tempNode)/=TEXT_NODE) exit + i_t = i_t + getLength(tempNode) + tempNode => getNextSibling(tempNode) + enddo + if (.not.associated(tempNode, getNextSibling(this))) then + allocate(temp(i_t)) + temp(:getLength(this)) = vs_str(getData(this)) + i_t = getLength(this) + tempNode => getNextSibling(this) + do while (associated(tempNode)) + if (getNodeType(tempNode)/=TEXT_NODE) exit + temp(i_t+1:i_t+getLength(tempNode)) = vs_str(getData(tempNode)) + i_t = i_t + getLength(tempNode) + oldNode => tempNode + tempNode => getNextSibling(tempNode) + oldNode => removeChild(getParentNode(oldNode), oldNode) + call remove_node_nl(arg%ownerDocument%docExtras%hangingNodes, oldNode) + call destroy(oldNode) + enddo + deallocate(this%nodeValue) + this%nodeValue => temp + endif + end if + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + + end subroutine normalize + + function isSupported(arg, feature, version, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: feature + character(len=*), intent(in) :: version + logical :: p + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "isSupported", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + p = hasFeature(getImplementation(arg%ownerDocument), feature, version) + end function isSupported + + pure function getNamespaceURI_len(arg, p) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (p) then + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + n = size(arg%elExtras%namespaceURI) + endif + endif + + end function getNamespaceURI_len + + function getNamespaceURI(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getNamespaceURI_len(arg, .true.)) :: c +#else + character(len=getNamespaceURI_len(arg, associated(arg))) :: c +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getNamespaceURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = "" + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + c = str_vs(arg%elExtras%namespaceURI) + endif + end function getNamespaceURI + +subroutine setnamespaceURI(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*) :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setnamespaceURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=XPATH_NAMESPACE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setnamespaceURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%elExtras%namespaceURI)) deallocate(np%elExtras%namespaceURI) + np%elExtras%namespaceURI => vs_str_alloc(c) + + end subroutine setnamespaceURI + + + pure function getPrefix_len(arg, p) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (p) then + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + n = size(arg%elExtras%prefix) + endif + endif + + end function getPrefix_len + + function getPrefix(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getPrefix_len(arg, .true.)) :: c +#else + character(len=getPrefix_len(arg, associated(arg))) :: c +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = "" + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + c = str_vs(arg%elExtras%prefix) + endif + + end function getPrefix + + subroutine setPrefix(arg, prefix, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*) :: prefix + + character, pointer :: tmp(:) + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(prefix, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkNCName(prefix, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (size(arg%elExtras%namespaceURI)==0) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefix=="xml" .and. & + str_vs(arg%elExtras%namespaceURI)/="http://www.w3.org/XML/1998/namespace") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefix=="xmlns" .and. (getNodeType(arg)/=ATTRIBUTE_NODE & + .or. str_vs(arg%elExtras%namespaceURI)/="http://www.w3.org/2000/xmlns/")) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)==ATTRIBUTE_NODE.and.getName(arg)=="xmlns") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif +! FIXME check if prefix is declared and already points to same namespace +! but only if we ever get full error-checking up and running. + deallocate(arg%elExtras%prefix) + arg%elExtras%prefix => vs_str_alloc(prefix) + tmp => arg%nodeName + i = index(str_vs(arg%nodeName), ":") + if (i==0) then + arg%nodeName => vs_str_alloc(prefix//":"//str_vs(tmp)) + else + arg%nodeName => vs_str_alloc(prefix//str_vs(tmp(i:))) + endif + deallocate(tmp) + endif + + call updateNodeLists(arg%ownerDocument) + + end subroutine setPrefix + + pure function getLocalName_len(arg, p) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (p) then + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + n = size(arg%elExtras%localName) + endif + endif + + end function getLocalName_len + + function getLocalName(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getLocalName_len(arg, .true.)) :: c +#else + character(len=getLocalName_len(arg, associated(arg))) :: c +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getLocalName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = "" + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + c = str_vs(arg%elExtras%localName) + endif + + end function getLocalName + + recursive function isEqualNode(arg, other, ex)result(p) + type(DOMException), intent(out), optional :: ex + ! We only have one level of recursion, in case of element attributes + type(Node), pointer :: arg + type(Node), pointer :: other + logical :: p + + type(Node), pointer :: this, that, treeroot, treeroot2, att1, att2 + type(NodeList), pointer :: children1, children2 + type(NamedNodeMap), pointer :: atts1, atts2 + + integer :: i_tree, i + logical :: doneChildren, doneAttributes, equal + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "isEqualNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (isSameNode(arg, other)) then + ! Shortcut the treewalking + p = .true. + return + else + p = .false. + endif + + treeroot => arg + treeroot2 => other + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + that => treeroot2 + equal = .false. + do + if (getNodeType(this)/=getNodeType(that)) exit + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + if (getNodeType(this)/=getNodeType(that)) return + ! Check necessary equal attributes ... + if (getNodeName(this)/=getNodeName(that) & + .or. getLocalName(this)/=getLocalName(that) & + .or. getNamespaceURI(this)/=getNamespaceURI(that) & + .or. getPrefix(this)/=getPrefix(that) & + .or. getNodeValue(this)/=getNodeValue(that)) & + return + children1 => getChildNodes(this) + children2 => getChildNodes(that) + if (getLength(children1)/=getLength(children2)) return + ! Well get to the contents of the children later on anyway. + if (getNodeType(this)==ELEMENT_NODE) then + ! We must treat attributes specially here (rather than relying on + ! treewalk) since the order can legitimately change. + atts1 => getAttributes(this) + atts2 => getAttributes(that) + if (getLength(atts1)/=getLength(atts2)) return + do i = 0, getLength(atts1)-1 + att1 => item(atts1, i) + if (getNamespaceURI(att1)=="") then + att2 => getNamedItem(atts2, getNodeName(att1)) + else + att2 => getNamedItemNS(atts2, getLocalName(att1), getNamespaceURI(att1)) + endif + if (.not.associated(att2)) return + if (.not.isEqualNode(att1, att2)) return + enddo + doneAttributes = .true. + elseif (getNodeType(this)==DOCUMENT_TYPE_NODE) then + if (getPublicId(this)/=getPublicId(that) & + .or. getSystemId(this)/=getSystemId(that) & + .or. getInternalSubset(this)/=getInternalSubset(that)) return + atts1 => getEntities(this) + atts2 => getEntities(that) + if (getLength(atts1)/=getLength(atts2)) return + do i = 0, getLength(atts1)-1 + att1 => item(atts1, i) + att2 => getNamedItem(atts2, getNodeName(att1)) + if (.not.associated(att2)) return + if (.not.isEqualNode(att1, att2)) return + enddo + atts1 => getNotations(this) + atts2 => getNotations(that) + if (getLength(atts1)/=getLength(atts2)) return + do i = 0, getLength(atts1)-1 + att1 => item(atts1, i) + att2 => getNamedItem(atts2, getNodeName(att1)) + if (.not.associated(att2)) return + if (.not.isEqualNode(att1, att2)) return + enddo + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))/=getLength(getAttributes(that))) exit + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + that => item(getAttributes(that), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this).or.hasChildNodes(that)) then + if (getLength(getChildNodes(this))/=getLength(getChildNodes(that))) exit + this => getFirstChild(this) + that => getFirstChild(that) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + that => item(getAttributes(getOwnerElement(that)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + that => getOwnerElement(that) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + that => getNextSibling(that) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + that => getParentNode(that) + endif + endif + + enddo + + + + p = .true. + + end function isEqualNode + + + function isSameNode(arg, other, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: other + logical :: isSameNode + + if (.not.associated(arg).or..not.associated(other)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "isSameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + isSameNode = associated(arg, other) + + end function isSameNode + + !FIXME all the lookup* functions below are out of spec, + ! since they rely on a statically-calculated set of NSnodes + ! which is only generated at parse time, and updated after + ! normalize. + ! the spec reckons it should be dynamic, but because we need + ! to know string lengths, which must be calculated inside + ! a pure function, we cant do the recursive walk we need to. + ! (although isDefaultNamespace could be fixed easily enough) + + function isDefaultNamespace(np, namespaceURI, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*), intent(in) :: namespaceURI + logical :: p + + type(Node), pointer :: el + integer :: i + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "isDefaultNamespace", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + el => null() + select case(getNodeType(np)) + case (ELEMENT_NODE) + el => np + case (ATTRIBUTE_NODE) + el => getOwnerElement(np) + case (DOCUMENT_NODE) + el => getDocumentElement(np) + end select + + p = .false. + if (associated(el)) then + do i = 1, el%elExtras%namespaceNodes%length + if (size(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==0) then + p = (str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) + return + endif + enddo + endif + end function isDefaultNamespace + + pure function lookupNamespaceURI_len(np, prefix, p) result(n) + type(Node), intent(in) :: np + character(len=*), intent(in) :: prefix + logical, intent(in) :: p + integer :: n + + integer :: i + + n = 0 + if (.not.p) return + if (np%nodeType/=ELEMENT_NODE & + .and. np%nodeType/=ATTRIBUTE_NODE & + .and. np%nodeType/=DOCUMENT_NODE) return + + if (prefix=="xml".or.prefix=="xmlns") then + n = 0 + return + endif + + select case(np%nodeType) + case (ELEMENT_NODE) + do i = 1, np%elExtras%namespaceNodes%length + if (str_vs(np%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==prefix) then + n = size(np%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI) + return + endif + enddo + case (ATTRIBUTE_NODE) + if (associated(np%elExtras%ownerElement)) then + do i = 1, np%elExtras%ownerElement%elExtras%namespaceNodes%length + if (str_vs(np%elExtras%ownerElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==prefix) then + n = size(np%elExtras%ownerElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI) + return + endif + enddo + endif + case (DOCUMENT_NODE) + if (associated(np%docExtras%documentElement)) then + do i = 1, np%docExtras%documentElement%elExtras%namespaceNodes%length + if (str_vs(np%docExtras%documentElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==prefix) then + n = size(np%docExtras%documentElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI) + return + endif + enddo + endif + end select + + end function lookupNamespaceURI_len + + function lookupNamespaceURI(np, prefix, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*), intent(in) :: prefix +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=lookupNamespaceURI_len(np, prefix, .true.)) :: c +#else + character(len=lookupNamespaceURI_len(np, prefix, associated(np))) :: c +#endif + + type(Node), pointer :: el + integer :: i + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "lookupNamespaceURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c)==0) then + c = "" + return + endif + + el => null() + select case(getNodeType(np)) + case (ELEMENT_NODE) + el => np + case (ATTRIBUTE_NODE) + el => getOwnerElement(np) + case (DOCUMENT_NODE) + el => getDocumentElement(np) + end select + + if (associated(el)) then + do i = 1, el%elExtras%namespaceNodes%length + if (str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==prefix) then + c = str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI) + return + endif + enddo + endif + + end function lookupNamespaceURI + + pure function lookupPrefix_len(np, namespaceURI, p) result(n) + type(Node), intent(in) :: np + character(len=*), intent(in) :: namespaceURI + logical, intent(in) :: p + integer :: n + + integer :: i + + n = 0 + if (.not.p) return + if (np%nodeType/=ELEMENT_NODE & + .and. np%nodeType/=ATTRIBUTE_NODE & + .and. np%nodeType/=DOCUMENT_NODE) return + + if (namespaceURI=="" & + .or. namespaceURI=="http://www.w3.org/XML/1998/namespace" & + .or. namespaceURI=="http://www.w3.org/2000/xmlns/") then + return + endif + + select case(np%nodeType) + case (ELEMENT_NODE) + do i = 1, np%elExtras%namespaceNodes%length + if (str_vs(np%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) then + n = size(np%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix) + return + endif + enddo + case (ATTRIBUTE_NODE) + if (associated(np%elExtras%ownerElement)) then + do i = 1, np%elExtras%ownerElement%elExtras%namespaceNodes%length + if (str_vs(np%elExtras%ownerElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) then + n = size(np%elExtras%ownerElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix) + return + endif + enddo + endif + case (DOCUMENT_NODE) + if (associated(np%docExtras%documentElement)) then + do i = 1, np%docExtras%documentElement%elExtras%namespaceNodes%length + if (str_vs(np%docExtras%documentElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) then + n = size(np%docExtras%documentElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix) + return + endif + enddo + endif + end select + + end function lookupPrefix_len + + function lookupPrefix(np, namespaceURI, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*), intent(in) :: namespaceURI +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=lookupPrefix_len(np, namespaceURI, .true.)) :: c +#else + character(len=lookupPrefix_len(np, namespaceURI, associated(np))) :: c +#endif + + type(Node), pointer :: el + integer :: i + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "lookupPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c)==0) then + c = "" + return + endif + + el => null() + select case(getNodeType(np)) + case (ELEMENT_NODE) + el => np + case (ATTRIBUTE_NODE) + el => getOwnerElement(np) + case (DOCUMENT_NODE) + el => getDocumentElement(np) + end select + + if (associated(el)) then + do i = 1, el%elExtras%namespaceNodes%length + if (str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) then + c = str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix) + return + endif + enddo + endif + end function lookupPrefix + + ! function getUserData + ! function setUserData + ! will not implement ... + + subroutine updateTextContentLength(np, n) + type(Node), pointer :: np + integer, intent(in) :: n + + type(Node), pointer :: this + + if (n/=0) then + this => np + do while (associated(this)) + this%textContentLength = this%textContentLength + n + this => getParentNode(this) + if (associated(this)) then + if (getNodeType(this)==DOCUMENT_NODE) exit + endif + enddo + endif + end subroutine updateTextContentLength + + pure function getTextContent_len(arg, p) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + integer :: n + + if (p) then + n = arg%textContentLength + else + n = 0 + endif + end function getTextContent_len + + function getTextContent(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getTextContent_len(arg, .true.)) :: c +#else + character(len=getTextContent_len(arg, associated(arg))) :: c +#endif + + type(Node), pointer :: this, treeroot + integer :: i, i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c) == 0) then + c = "" + return + endif + + i = 1 + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (associated(this, treeroot).and.isCharData(getNodeType(this))) then + c = getData(this) + return + endif + select case(getNodeType(this)) + case (ELEMENT_NODE) + doneAttributes = .true. + ! Ignore attributes for text content (unless this is an attribute!) + case(TEXT_NODE, CDATA_SECTION_NODE) + if (.not.getIsElementContentWhitespace(this)) then + c(i:i+size(this%nodeValue)-1) = str_vs(this%nodeValue) + i = i + size(this%nodeValue) + endif + end select + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + end function getTextContent + + subroutine setTextContent(arg, textContent, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: textContent + + type(Node), pointer :: np + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkChars(textContent, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "setTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + select case(getNodeType(arg)) + case (ELEMENT_NODE, ATTRIBUTE_NODE, DOCUMENT_FRAGMENT_NODE) + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + do i = 1, getLength(getChildNodes(arg)) + call destroyNode(arg%childNodes%nodes(i)%this) + enddo + deallocate(arg%childNodes%nodes) + allocate(arg%childNodes%nodes(0)) + arg%childNodes%length = 0 + arg%firstChild => null() + arg%lastChild => null() + arg%textContentLength = 0 + np => createTextNode(getOwnerDocument(arg), textContent) + np => appendChild(arg, np) + case (TEXT_NODE, CDATA_SECTION_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE) + call setData(arg, textContent) + case (ENTITY_NODE, ENTITY_REFERENCE_NODE) + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + end subroutine setTextContent + + function getBaseURI(arg, ex)result(baseURI) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=200) :: baseURI + + type(Node), pointer :: el + type(URI), pointer :: URIref, URIbase, newURI + + select case(getNodeType(arg)) + case (ELEMENT_NODE) + el => arg + case (ATTRIBUTE_NODE) + if (getName(arg)=="xml:base") then + if (associated(getOwnerElement(arg))) then + el => getParentNode(getOwnerElement(arg)) + else + el => null() + endif + else + el => getOwnerElement(arg) + endif + case (TEXT_NODE) + ! then are we in an attribute or textContent? + el => getParentNode(arg) + do while (associated(el)) + if (getNodeType(el)==ELEMENT_NODE) then + exit + elseif (getNodeType(el)==ATTRIBUTE_NODE) then + el => getOwnerElement(el) + exit + else + el => getParentNode(el) + endif + enddo + case (PROCESSING_INSTRUCTION_NODE) + ! then are we in or out of element content? + el => getParentNode(arg) + do while (associated(el)) + if (getNodeType(el)==ELEMENT_NODE) then + exit + elseif (getNodeType(el)==DOCUMENT_NODE) then + el => getOwnerElement(el) + exit + else + el => getParentNode(el) + endif + enddo + case default + el => null() + end select + + URIref => parseURI("") + + do while (associated(el)) + select case (getNodeType(el)) + case (ELEMENT_NODE) + if (hasAttribute(el, "xml:base")) then + URIbase => parseURI(getAttribute(el, "xml:base")) + newURI => rebaseURI(URIbase, URIref) + call destroyURI(URIbase) + call destroyURI(URIref) + URIref => newURI + if (isAbsoluteURI(URIref)) exit + endif + case (ENTITY_REFERENCE_NODE) + if (getSystemId(el)/="") then + URIbase => parseURI(getSystemId(el)) + newURI => rebaseURI(URIbase, URIref) + call destroyURI(URIbase) + call destroyURI(URIref) + URIref => newURI + if (isAbsoluteURI(URIref)) exit + endif + case default + exit + end select + el => getParentNode(el) + end do + + if (isAbsoluteURI(URIref)) then + baseURI = expressURI(URIref) + else + baseURI = "" + endif + call destroyURI(URIref) + + end function getBaseURI + + recursive function getNodePath(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + ! recursive only for atts and text + type(Node), pointer :: arg + character(len=100) :: c + + type(Node), pointer :: this, this2 + character(len=len(c)) :: c2 + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getNodePath", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = "" + if (.not.arg%inDocument) return + select case(getNodeType(arg)) + + case (ELEMENT_NODE) + this => arg + do while (getNodeType(this)/=DOCUMENT_NODE) + c2 = "" + this2 => getPreviousSibling(this) + n = 0 + do while (associated(this2)) + if (getNodeType(this2)==ELEMENT_NODE & + .and.getNodeName(this2)==getNodeName(this)) n = n + 1 + this2 => getPreviousSibling(this2) + enddo + if (n==0) then + this2 => getNextSibling(this) + do while (associated(this2)) + if (getNodeType(this2)==ELEMENT_NODE & + .and.getNodeName(this2)==getNodeName(this)) then + n = 1 + exit + endif + this2 => getNextSibling(this2) + enddo + else + n = n + 1 + endif + if (n>0) c2 = "["//n//"]" + ! What name to use: + if (getNamespaceURI(this)/="".and.getPrefix(this)=="") then + ! default namespace; need to do the * trick + ! how many previous siblings? + c2 = "/*"//c2 + else + c2 = "/"//getNodeName(this)//c2 + endif + c = trim(c2)//c + this => getParentNode(this) + enddo + + case (ATTRIBUTE_NODE) + c = trim(getNodePath(getOwnerElement(arg)))//"/@"//getNodeName(arg) + + case (TEXT_NODE, CDATA_SECTION_NODE) + ! FIXME this will give wrong answers sometimes if + ! the tree contains entity references + this => getParentNode(arg) + do while (associated(this)) + if (getNodeType(this)==ELEMENT_NODE) exit + this => getParentNode(this) + enddo + if (getNodeType(this)/=ELEMENT_NODE) & + this => getOwnerElement(this) + c = trim(getNodePath(this))//"/text()" + this => getPreviousSibling(arg) + n = 0 + do while (associated(this)) + if (getNodeType(this)==TEXT_NODE & + .or.getNodeType(this)==CDATA_SECTION_NODE) n = n + 1 + this => getPreviousSibling(this) + enddo + if (n==0) then + this => getNextSibling(arg) + do while (associated(this)) + if (getNodeType(this)==COMMENT_NODE & + .or.getNodeType(this)==CDATA_SECTION_NODE) then + n = 1 + exit + endif + this => getNextSibling(this) + enddo + else + n = n + 1 + endif + if (n>0) c = trim(c)//"["//n//"]" + + case (PROCESSING_INSTRUCTION_NODE) + this => getParentNode(arg) + c = trim(getNodePath(this))//"/processing-instruction("//getNodeName(arg)//")" + this => getPreviousSibling(arg) + n = 0 + do while (associated(this)) + if (getNodeType(this)==PROCESSING_INSTRUCTION_NODE & + .and.getNodeName(this)==getNodeName(arg)) n = n + 1 + this => getPreviousSibling(this) + enddo + if (n==0) then + this => getNextSibling(arg) + do while (associated(this)) + if (getNodeType(this)==PROCESSING_INSTRUCTION_NODE & + .and.getNodeName(this)==getNodeName(arg)) then + n = 1 + exit + endif + this => getNextSibling(this) + enddo + else + n = n + 1 + endif + if (n>0) c = trim(c)//"["//n//"]" + + case (COMMENT_NODE) + this => getParentNode(arg) + c = trim(getNodePath(this))//"/comment()" + this => getPreviousSibling(arg) + n = 0 + do while (associated(this)) + if (getNodeType(this)==COMMENT_NODE) n = n + 1 + this => getPreviousSibling(this) + enddo + if (n==0) then + this => getNextSibling(arg) + do while (associated(this)) + if (getNodeType(this)==COMMENT_NODE) then + n = 1 + exit + endif + this => getNextSibling(this) + enddo + else + n = n + 1 + endif + if (n>0) c = trim(c)//"["//n//"]" + + case (DOCUMENT_NODE) + c = "/" + + case (XPATH_NAMESPACE_NODE) + this => getOwnerElement(arg) + if (getPrefix(arg)=="") then + c = trim(getNodePath(this))//"/namespace::xmlns" + else + c = trim(getNodePath(this))//"/namespace::"//getPrefix(arg) + endif + ! FIXME namespace nodes are not marked as inDocument correctly + + end select + + end function getNodePath + + subroutine putNodesInDocument(doc, arg) + type(Node), pointer :: doc, arg + type(Node), pointer :: this, treeroot + logical :: doneChildren, doneAttributes + integer :: i_tree + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + this%inDocument = .true. + call remove_node_nl(doc%docExtras%hangingNodes, this) + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + + end subroutine putNodesInDocument + + subroutine removeNodesFromDocument(doc, arg) + type(Node), pointer :: doc, arg + type(Node), pointer :: this, treeroot + logical :: doneChildren, doneAttributes + integer :: i_tree + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + this%inDocument = .false. + call append_nl(doc%docExtras%hangingNodes, this) + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + end subroutine removeNodesFromDocument + + subroutine setReadOnlyNode(arg, p, deep) + type(Node), pointer :: arg + logical, intent(in) :: p + logical, intent(in) :: deep + + type(Node), pointer :: this, treeroot + integer :: i_tree + logical :: doneAttributes, doneChildren + + if (deep) then + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + this%readonly = p + if (this%nodeType==ELEMENT_NODE) & + this%elExtras%attributes%readonly = p + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + else + arg%readonly = p + if (arg%nodeType==ELEMENT_NODE) & + arg%elExtras%attributes%readonly = p + endif + + end subroutine setReadOnlyNode + +function getreadonly(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getreadonly", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c = np%readonly + + end function getreadonly + + + + + function item_nl(list, index, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NodeList), pointer :: list + integer, intent(in) :: index + type(Node), pointer :: np + + if (.not.associated(list)) then + if (getFoX_checks().or.FoX_LIST_IS_NULL<200) then + call throw_exception(FoX_LIST_IS_NULL, "item_nl", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (index>=0.and.index list%nodes(index+1)%this + else + np => null() + endif + + end function item_nl + + subroutine append_nl(list, arg) + type(NodeList), intent(inout) :: list + type(Node), pointer :: arg + + type(ListNode), pointer :: temp_nl(:) + integer :: i + + if (.not.associated(list%nodes)) then + allocate(list%nodes(1)) + list%nodes(1)%this => arg + list%length = 1 + else + temp_nl => list%nodes + allocate(list%nodes(size(temp_nl)+1)) + do i = 1, size(temp_nl) + list%nodes(i)%this => temp_nl(i)%this + enddo + deallocate(temp_nl) + list%nodes(size(list%nodes))%this => arg + list%length = size(list%nodes) + endif + + end subroutine append_nl + + function pop_nl(list, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NodeList), pointer :: list + type(Node), pointer :: np + + type(ListNode), pointer :: temp_nl(:) + integer :: i + + if (list%length==0) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "pop_nl", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => list%nodes(size(list%nodes))%this + + if (list%length==1) then + deallocate(list%nodes) + list%length = 0 + else + temp_nl => list%nodes + allocate(list%nodes(size(temp_nl)-1)) + do i = 1, size(temp_nl)-1 + list%nodes(i)%this => temp_nl(i)%this + enddo + deallocate(temp_nl) + list%length = size(list%nodes) + endif + + end function pop_nl + + + function remove_nl(nl, index, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NodeList), intent(inout) :: nl + integer, intent(in) :: index + type(Node), pointer :: np + + type(ListNode), pointer :: temp_nl(:) + + integer :: i + + if (index>nl%length) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "remove_nl", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => nl%nodes(index)%this + temp_nl => nl%nodes + allocate(nl%nodes(size(temp_nl)-1)) + nl%length = nl%length - 1 + do i = 1, index - 1 + nl%nodes(i)%this => temp_nl(i)%this + enddo + do i = index, nl%length + nl%nodes(i)%this => temp_nl(i+1)%this + enddo + deallocate(temp_nl) + + end function remove_nl + + + subroutine remove_node_nl(nl, np) + type(NodeList), intent(inout) :: nl + type(Node), pointer :: np + + integer :: i + + do i = 1, nl%length + if (associated(nl%nodes(i)%this, np)) exit + enddo + np => remove_nl(nl, i) + + end subroutine remove_node_nl + + + function getLength_nl(nl, ex)result(n) + type(DOMException), intent(out), optional :: ex + type(NodeList), pointer :: nl + integer :: n + + if (.not.associated(nl)) then + if (getFoX_checks().or.FoX_LIST_IS_NULL<200) then + call throw_exception(FoX_LIST_IS_NULL, "getLength_nl", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + n = size(nl%nodes) + end function getLength_nl + + subroutine destroyNodeList(nl) + type(NodeList), pointer :: nl + + if (associated(nl%nodes)) deallocate(nl%nodes) + if (associated(nl%nodeName)) deallocate(nl%nodeName) + if (associated(nl%localName)) deallocate(nl%localName) + if (associated(nl%namespaceURI)) deallocate(nl%namespaceURI) + deallocate(nl) + end subroutine destroyNodeList + + subroutine updateNodeLists(doc) + ! When triggered, update all nodelists + type(Node), pointer :: doc + + type(NodeList), pointer :: nl, nl_orig + type(NodeListPtr), pointer :: temp_nll(:) + integer :: i, i_t + + if (.not.getGCstate(doc)) return + if (.not.doc%docExtras%liveNodeLists) return + if (.not.associated(doc%docExtras%nodelists)) return + + ! We point the old list of nodelists to temp_nll, then recalculate + ! them all (which repopulates nodelists) + temp_nll => doc%docExtras%nodelists + i_t = size(temp_nll) + allocate(doc%docExtras%nodelists(0)) + do i = 1, i_t + nl_orig => temp_nll(i)%this + ! + ! Although all nodes should be searched whatever the result, + ! we should only do the appropriate sort of search for this + ! list - according to namespaces or not. + ! + if (associated(nl_orig%nodeName)) then + ! this was made by getElementsByTagName + nl => getElementsByTagName(nl_orig%element, str_vs(nl_orig%nodeName)) + elseif (associated(nl_orig%namespaceURI)) then + ! this was made by getElementsByTagNameNS + nl => getElementsByTagNameNS(nl_orig%element, & + str_vs(nl_orig%localName), str_vs(nl_orig%namespaceURI)) + endif + enddo + ! We dont care about the nodelists weve calculated now + nullify(nl) + + deallocate(temp_nll) + + end subroutine updateNodeLists + + + + function getNamedItem(map, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + character(len=*), intent(in) :: name + type(Node), pointer :: np + + integer :: i + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "getNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + do i = 1, map%length + if (str_vs(map%nodes(i)%this%nodeName)==name) then + np => map%nodes(i)%this + return + endif + enddo + + np => null() + + end function getNamedItem + + + function setNamedItem(map, arg, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + type(Node), pointer :: arg + type(Node), pointer :: np + + integer :: i + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (map%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (map%ownerElement%nodeType==ELEMENT_NODE) then + if (.not.associated(map%ownerElement%ownerDocument, arg%ownerDocument)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ATTRIBUTE_NODE) then + !Additional check from DOM 3 + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + + if (getNodeType(arg)==ATTRIBUTE_NODE) then + if (associated(map%ownerElement, getOwnerElement(arg))) then + ! we are looking at literally the same node + np => arg + return + elseif (associated(getOwnerElement(arg))) then + if (getFoX_checks().or.INUSE_ATTRIBUTE_ERR<200) then + call throw_exception(INUSE_ATTRIBUTE_ERR, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + arg%elExtras%ownerElement => map%ownerElement + endif + + do i = 0, getLength(map)-1 + np => item(map, i) + if (getNodeName(np)==getNodeName(arg)) then + map%nodes(i+1)%this => arg + exit + endif + enddo + + if (i null() + call append_nnm(map, arg) + endif + + if (map%ownerElement%nodeType==ELEMENT_NODE) then + if (getGCstate(getOwnerDocument(map%ownerElement))) then + ! We need to worry about importing this node + if (map%ownerElement%inDocument) then + if (.not.arg%inDocument) & + call putNodesInDocument(getOwnerDocument(map%ownerElement), arg) + else + if (arg%inDocument) & + call removeNodesFromDocument(getOwnerDocument(map%ownerElement), arg) + endif + endif + endif + ! Otherwise we only ever setNNM when building the doc, so we know this + ! does not matter + + end function setNamedItem + + + function removeNamedItem(map, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + character(len=*), intent(in) :: name + type(Node), pointer :: np + + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + type(ListNode), pointer :: temp_nl(:) + integer :: i, i2 + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "removeNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (map%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + do i = 0, map%length-1 + np => item(map, i) + if (getNodeName(np)==name) then + xds => getXds(getOwnerDocument(map%ownerElement)) + elem => get_element(xds%element_list, getNodeName(map%ownerElement)) + att => get_attribute_declaration(elem, name) + if (associated(att)) then + if (attribute_has_default(att)) then ! there is a default value + ! Well swap the old one out & put a new one in. + ! Do *nothing* about namespace handling at this stage, + ! wait until we are asked for namespace normalization + if (getParameter( & + getDomConfig(getOwnerDocument(map%ownerElement)), & + "namespaces")) then + np => createAttributeNS(getOwnerDocument(map%ownerElement), "", name) + else + np => createAttribute(getOwnerDocument(map%ownerElement), name) + endif + call setValue(np, str_vs(att%default)) + call setSpecified(np, .false.) + np => setNamedItem(map, np) + call setSpecified(np, .true.) + return + endif + endif + ! Otherwise there was no default value, so we just remove the node. + ! Grab this node + if (getNodeType(np)==ATTRIBUTE_NODE) np%elExtras%ownerElement => null() + ! and shrink the node list + temp_nl => map%nodes + allocate(map%nodes(size(temp_nl)-1)) + do i2 = 1, i + map%nodes(i2)%this => temp_nl(i2)%this + enddo + do i2 = i + 2, map%length + map%nodes(i2-1)%this => temp_nl(i2)%this + enddo + map%length = size(map%nodes) + deallocate(temp_nl) + if (np%inDocument.and.getGCstate(getOwnerDocument(map%ownerElement))) & + call removeNodesFromDocument(getOwnerDocument(map%ownerElement), np) + !otherwise we are only going to destroy these nodes anyway, + ! and finish + return + endif + enddo + + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + + end function removeNamedItem + + + function item_nnm(map, index, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + integer, intent(in) :: index + type(Node), pointer :: np + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "item_nnm", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (index<0 .or. index>map%length-1) then + np => null() + else + np => map%nodes(index+1)%this + endif + + end function item_nnm + + function getLength_nnm(map, ex)result(n) + type(DOMException), intent(out), optional :: ex + type(namedNodeMap), pointer :: map + integer :: n + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "getLength_nnm", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + n = map%length + + end function getLength_nnm + + + function getNamedItemNS(map, namespaceURI, localName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + type(Node), pointer :: np + + integer :: i + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "getNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (map%ownerElement%nodeType/=ELEMENT_NODE) then + np => null() + return + endif + + do i = 0, getLength(map) - 1 + np => item(map, i) + if (getNamespaceURI(np)==namespaceURI & + .and. getLocalName(np)==localName) then + return + endif + enddo + np => null() + + end function getNamedItemNS + + + function setNamedItemNS(map, arg, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + type(Node), pointer :: arg + type(Node), pointer :: np + + integer :: i + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (map%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (map%ownerElement%nodeType==ELEMENT_NODE) then + if (.not.associated(map%ownerElement%ownerDocument, arg%ownerDocument)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ATTRIBUTE_NODE) then + !Additional check from DOM 3 + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + + if (getNodeType(arg)==ATTRIBUTE_NODE) then + if (associated(map%ownerElement, getOwnerElement(arg))) then + ! we are looking at literally the same node, so do nothing else + np => arg + return + elseif (associated(getOwnerElement(arg))) then + if (getFoX_checks().or.INUSE_ATTRIBUTE_ERR<200) then + call throw_exception(INUSE_ATTRIBUTE_ERR, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + arg%elExtras%ownerElement => map%ownerElement + endif + + do i = 0, getLength(map) - 1 + np => item(map, i) + if ((getLocalName(arg)==getLocalName(np) & + .and.getNamespaceURI(arg)==getNamespaceURI(np)) & + ! Additional case to catch adding of specified attributeNS over + ! default (NS but unspecified URI) attribute + .or.(getNamespaceURI(arg)=="".and.getName(arg)==getName(np))) then + map%nodes(i+1)%this => arg + exit + endif + enddo + + if (i null() + call append_nnm(map, arg) + endif + + if (map%ownerElement%nodeType==ELEMENT_NODE) then + if (getGCstate(getOwnerDocument(map%ownerElement))) then + ! We need to worry about importing this node + if (map%ownerElement%inDocument) then + if (.not.arg%inDocument) & + call putNodesInDocument(getOwnerDocument(map%ownerElement), arg) + else + if (arg%inDocument) & + call removeNodesFromDocument(getOwnerDocument(map%ownerElement), arg) + endif + endif + endif + + end function setNamedItemNS + + + function removeNamedItemNS(map, namespaceURI, localName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + type(Node), pointer :: np + + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + type(ListNode), pointer :: temp_nl(:) + integer :: i, i2 + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "removeNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (map%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + do i = 0, getLength(map) - 1 + np => item(map, i) + if (getNamespaceURI(np)==namespaceURI & + .and. getLocalName(np)==localName) then + ! Grab this node + xds => getXds(getOwnerDocument(map%ownerElement)) + elem => get_element(xds%element_list, getNodeName(map%ownerElement)) + att => get_attribute_declaration(elem, getName(np)) + if (associated(att)) then + if (attribute_has_default(att)) then ! there is a default value + ! Well swap the old one out & put a new one in. + ! Do *nothing* about namespace handling at this stage, + ! wait until we are asked for namespace normalization + np => createAttributeNS(getOwnerDocument(map%ownerElement), getNamespaceURI(np), getName(np)) + call setValue(np, str_vs(att%default)) + call setSpecified(np, .false.) + np => setNamedItemNS(map, np) + call setSpecified(np, .true.) + return + endif + endif + ! Otherwise there was no default value, so we just remove the node. + ! and shrink the node list + if (getNodeType(np)==ATTRIBUTE_NODE) np%elExtras%ownerElement => null() + temp_nl => map%nodes + allocate(map%nodes(size(temp_nl)-1)) + do i2 = 1, i + map%nodes(i2)%this => temp_nl(i2)%this + enddo + do i2 = i + 2, map%length + map%nodes(i2-1)%this => temp_nl(i2)%this + enddo + map%length = size(map%nodes) + deallocate(temp_nl) + if (np%inDocument.and.getGCstate(getOwnerDocument(map%ownerElement))) & + call removeNodesFromDocument(getOwnerDocument(map%ownerElement), np) + !otherwise we are only going to destroy these nodes anyway, + ! and finish + return + endif + enddo + + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + + end function removeNamedItemNS + + + subroutine append_nnm(map, arg) + type(namedNodeMap), pointer :: map + type(node), pointer :: arg + + type(ListNode), pointer :: temp_nl(:) + integer :: i + + if (.not.associated(map%nodes)) then + allocate(map%nodes(1)) + map%nodes(1)%this => arg + map%length = 1 + else + temp_nl => map%nodes + allocate(map%nodes(size(temp_nl)+1)) + do i = 1, size(temp_nl) + map%nodes(i)%this => temp_nl(i)%this + enddo + deallocate(temp_nl) + map%nodes(size(map%nodes))%this => arg + map%length = size(map%nodes) + endif + if (getNodeType(arg)==ATTRIBUTE_NODE) arg%elExtras%ownerElement => map%ownerElement + + end subroutine append_nnm + + + subroutine setReadOnlyMap(map, r) + type(namedNodeMap), pointer :: map + logical, intent(in) :: r + + map%readonly = r + end subroutine setReadOnlyMap + + subroutine destroyNamedNodeMap(map) + type(namedNodeMap), pointer :: map + + if (associated(map%nodes)) deallocate(map%nodes) + deallocate(map) + end subroutine destroyNamedNodeMap + + + + function hasFeature(impl, feature, version, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(DOMImplementation), pointer :: impl + character(len=*), intent(in) :: feature + character(len=*), intent(in) :: version + logical :: p + + if (.not.associated(impl)) then + if (getFoX_checks().or.FoX_IMPL_IS_NULL<200) then + call throw_exception(FoX_IMPL_IS_NULL, "hasFeature", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (version=="1.0".or.version=="2.0".or.version=="") then + p = (toLower(feature)=="core".or.toLower(feature)=="xml") + else + p = .false. + endif + + end function hasFeature + + function createDocumentType(impl, qualifiedName, publicId, systemId, ex)result(dt) + type(DOMException), intent(out), optional :: ex + type(DOMImplementation), pointer :: impl + character(len=*), intent(in) :: qualifiedName + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + type(Node), pointer :: dt + + type(URI), pointer :: URIref + + dt => null() + + if (.not.associated(impl)) then + if (getFoX_checks().or.FoX_IMPL_IS_NULL<200) then + call throw_exception(FoX_IMPL_IS_NULL, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkName(qualifiedName, XML1_0)) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkQName(qualifiedName, XML1_0)) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkPublicId(publicId)) then + if (getFoX_checks().or.FoX_INVALID_PUBLIC_ID<200) then + call throw_exception(FoX_INVALID_PUBLIC_ID, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + URIref => parseURI(systemId) + if (.not.associated(URIref)) then + if (getFoX_checks().or.FoX_INVALID_SYSTEM_ID<200) then + call throw_exception(FoX_INVALID_SYSTEM_ID, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call destroyURI(URIref) + +! Dont use raw null() below or PGI will complain + dt => createNode(dt, DOCUMENT_TYPE_NODE, qualifiedName, "") + allocate(dt%dtdExtras) + dt%readonly = .true. + dt%dtdExtras%publicId => vs_str_alloc(publicId) + dt%dtdExtras%systemId => vs_str_alloc(systemId) + dt%dtdExtras%entities%ownerElement => dt + dt%dtdExtras%notations%ownerElement => dt + + dt%ownerDocument => null() + + end function createDocumentType + + + function createDocument(impl, namespaceURI, qualifiedName, docType, ex)result(doc) + type(DOMException), intent(out), optional :: ex + type(DOMImplementation), pointer :: impl + character(len=*), intent(in), optional :: namespaceURI + character(len=*), intent(in), optional :: qualifiedName + type(Node), pointer :: docType + type(Node), pointer :: doc, dt, de + + doc => null() + + if (.not.associated(impl)) then + if (getFoX_checks().or.FoX_IMPL_IS_NULL<200) then + call throw_exception(FoX_IMPL_IS_NULL, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (associated(docType)) then + if (associated(getOwnerDocument(docType))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + + if (.not.checkName(qualifiedName, XML1_0)) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif(.not.checkQName(qualifiedName, XML1_0)) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="".and.namespaceURI=="") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)=="xml".neqv.namespaceURI=="http://www.w3.org/XML/1998/namespace") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (qualifiedName=="xmlns" .or. prefixOfQName(qualifiedName)=="xmlns") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +! Dont use raw null() below or PGI will complain + doc => createNode(doc, DOCUMENT_NODE, "#document", "") + doc%ownerDocument => doc ! Makes life easier. DOM compliance in getter + doc%inDocument = .true. + + allocate(doc%docExtras) + doc%docExtras%implementation => FoX_DOM + allocate(doc%docExtras%nodelists(0)) + allocate(doc%docExtras%xds) + call init_xml_doc_state(doc%docExtras%xds) + allocate(doc%docExtras%xds%documentURI(0)) + allocate(doc%docExtras%domConfig) + + if (associated(docType)) then + dt => docType + dt%ownerDocument => doc + doc%docExtras%docType => appendChild(doc, dt, ex) + endif + + if (qualifiedName/="") then + ! NB It is impossible to create a non-namespaced document. + ! since createDocument doesnt exist in DOM Core 1 + de => createElementNS(doc, namespaceURI, qualifiedName) + de => appendChild(doc, de) + call setDocumentElement(doc, de) + endif + + call setGCstate(doc, .true.) + + end function createDocument + + + function createEmptyDocument() result(doc) + type(Node), pointer :: doc + +! PGI again + doc => null() + doc => createNode(doc, DOCUMENT_NODE, "#document", "") + doc%ownerDocument => doc ! Makes life easier. DOM compliance maintained in getter + doc%inDocument = .true. + + allocate(doc%docExtras) + doc%docExtras%implementation => FoX_DOM + allocate(doc%docExtras%nodelists(0)) + allocate(doc%docExtras%xds) + call init_xml_doc_state(doc%docExtras%xds) + + end function createEmptyDocument + + + subroutine destroyDocument(arg, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "destroyDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "destroyDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +! Switch off all GC - since this is GC! + call setGCstate(arg, .false.) + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "destroyDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +! Destroy all remaining nodelists + + do i = 1, size(arg%docExtras%nodelists) + call destroy(arg%docExtras%nodelists(i)%this) + enddo + deallocate(arg%docExtras%nodelists) + + ! Destroy all remaining hanging nodes + do i = 1, arg%docExtras%hangingNodes%length + call destroy(arg%docExtras%hangingNodes%nodes(i)%this) + enddo + if (associated(arg%docExtras%hangingNodes%nodes)) deallocate(arg%docExtras%hangingNodes%nodes) + + call destroy_xml_doc_state(arg%docExtras%xds) + deallocate(arg%docExtras%xds) + deallocate(arg%docExtras%domConfig) + deallocate(arg%docExtras) + + call destroyAllNodesRecursively(arg, except=.true.) + + end subroutine destroyDocument + + function getFoX_checks() result(FoX_checks) + logical :: FoX_checks + + FoX_checks = FoX_DOM%FoX_checks + end function getFoX_checks + + subroutine setFoX_checks(FoX_checks) + logical, intent(in) :: FoX_checks + + FoX_DOM%FoX_checks = FoX_checks + end subroutine setFoX_checks + + + + +function getdocType(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdocType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdocType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%docExtras%docType + + end function getdocType + + + subroutine setDocType(arg, np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setDocType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setDocType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + arg%docExtras%docType => np +!NB special case in order to set ownerDocument + np%ownerDocument => arg + end subroutine setDocType + +function getdocumentElement(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%docExtras%documentElement + + end function getdocumentElement + + + subroutine setXds(arg, xds, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(xml_doc_state), pointer :: xds + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setXds", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setXds", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif +!NB special case in order to destroy_xml_doc_state etc + call destroy_xml_doc_state(arg%docExtras%xds) + deallocate(arg%docExtras%xds) + arg%docExtras%xds => xds + + end subroutine setXds + + function getImplementation(arg, ex)result(imp) + type(DOMException), intent(out), optional :: ex + type(Node), pointer, optional :: arg + type(DOMImplementation), pointer :: imp + + ! According to the testsuite, you get to call + ! getImplementation with no args. Dont know + ! where they get that from ... + if (present(arg)) then + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getImplementation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getImplementation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + imp => arg%docExtras%implementation + else + imp => FoX_DOM + endif + end function getImplementation + + + subroutine setDocumentElement(arg, np, ex) + type(DOMException), intent(out), optional :: ex + ! Only for use by FoX, not exported through FoX_DOM interface + type(Node), pointer :: arg + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setDocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +!NB special case due to additional error conditions: + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setDocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (np%nodeType/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setDocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(np%ownerDocument, arg)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setDocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + arg%docExtras%documentElement => np + + end subroutine setDocumentElement + + ! Methods + + function createElement(arg, tagName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: tagName + type(Node), pointer :: np + + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + logical :: defaults_ + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(tagName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + np => createNode(arg, ELEMENT_NODE, tagName, "") + allocate(np%elExtras) + np%elExtras%dom1 = .true. + np%elExtras%attributes%ownerElement => np + allocate(np%elExtras%namespaceURI(0)) + allocate(np%elExtras%prefix(0)) + allocate(np%elExtras%localname(0)) + allocate(np%elExtras%namespaceNodes%nodes(0)) + + np%elExtras%attributes%ownerElement => np + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + ! We only add default attributes if we are *not* building the doc + xds => getXds(arg) + elem => get_element(xds%element_list, tagName) + if (associated(elem)) then + do i = 1, get_attlist_size(elem) + att => get_attribute_declaration(elem, i) + if (attribute_has_default(att)) then + ! Since this is a non-namespaced function, we create + ! a non-namespaced attribute ... + call setAttribute(np, str_vs(att%name), str_vs(att%default)) + endif + enddo + endif + else + np%inDocument = .true. + endif + + end function createElement + + function createEmptyElement(arg, tagName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: tagName + type(Node), pointer :: np + +! NO CHECKS ! + + np => createNode(arg, ELEMENT_NODE, tagName, "") + allocate(np%elExtras) + np%elExtras%dom1 = .true. + np%elExtras%attributes%ownerElement => np + allocate(np%elExtras%namespaceURI(0)) + allocate(np%elExtras%prefix(0)) + allocate(np%elExtras%localname(0)) + allocate(np%elExtras%namespaceNodes%nodes(0)) + + np%elExtras%attributes%ownerElement => np + + if (getGCstate(arg)) then + call append(arg%docExtras%hangingnodes, np) + np%inDocument = .false. + else + np%inDocument = .true. + endif + end function createEmptyElement + + function createDocumentFragment(arg, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createDocumentFragment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createDocumentFragment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, DOCUMENT_FRAGMENT_NODE, "#document-fragment", "") + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createDocumentFragment + + function createTextNode(arg, data, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: data + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createTextNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createTextNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(data, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "createTextNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, TEXT_NODE, "#text", data) + np%textContentLength = len(data) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createTextNode + + function createComment(arg, data, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: data + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createComment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createComment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(data, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "createComment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (index(data,"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "createComment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, COMMENT_NODE, "#comment", data) + np%textContentLength = len(data) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createComment + + function createCdataSection(arg, data, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: data + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createCdataSection", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createCdataSection", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(data, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "createCdataSection", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (index(data,"]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "createCdataSection", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, CDATA_SECTION_NODE, "#cdata-section", data) + np%textContentLength = len(data) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createCdataSection + + function createProcessingInstruction(arg, target, data, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: target + character(len=*), intent(in) :: data + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(target, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(data, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (index(data,"?>")>0) then + if (getFoX_checks().or.FoX_INVALID_PI_DATA<200) then + call throw_exception(FoX_INVALID_PI_DATA, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, PROCESSING_INSTRUCTION_NODE, target, data) + np%textContentLength = len(data) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createProcessingInstruction + + function createAttribute(arg, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(name, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, ATTRIBUTE_NODE, name, "") + allocate(np%elExtras) + np%elExtras%dom1 = .true. + allocate(np%elExtras%namespaceURI(0)) + allocate(np%elExtras%prefix(0)) + allocate(np%elExtras%localname(0)) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createAttribute + + + recursive function createEntityReference(arg, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + ! Needs to be recursive in case of entity-references within each other. + type(Node), pointer :: arg + character(len=*), intent(in) :: name + type(Node), pointer :: np + + type(Node), pointer :: ent, newNode + integer :: i + logical :: brokenNS + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(name, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getXmlStandalone(arg).and..not.associated(getDocType(arg))) then + if (getFoX_checks().or.FoX_NO_SUCH_ENTITY<200) then + call throw_exception(FoX_NO_SUCH_ENTITY, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, ENTITY_REFERENCE_NODE, name, "") + if (getGCstate(arg)) then ! otherwise the parser will fill these nodes in itself + if (associated(getDocType(arg))) then + ent => getNamedItem(getEntities(getDocType(arg)), name) + if (associated(ent)) then + if (getIllFormed(ent)) then + if (getFoX_checks().or.FoX_INVALID_ENTITY<200) then + call throw_exception(FoX_INVALID_ENTITY, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + brokenNS = arg%docExtras%brokenNS + arg%docExtras%brokenNS = .true. ! We need to not worry about NS errors for a bit + do i = 0, getLength(getChildNodes(ent)) - 1 + newNode => appendChild(np, cloneNode(item(getChildNodes(ent), i), .true., ex)) + ! No namespace calcs here - wait for a namespace normalization + call setReadOnlyNode(newNode, .true., .true.) + enddo + arg%docExtras%brokenNS = brokenNS ! FIXME also for all new default attributes + elseif (getXmlStandalone(arg)) then + if (getFoX_checks().or.FoX_NO_SUCH_ENTITY<200) then + call throw_exception(FoX_NO_SUCH_ENTITY, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + + if (associated(np)) deallocate(np) + return + endif + endif +endif + + endif + endif + endif + + call setReadOnlyNode(np, .true., .false.) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append_nl(arg%docExtras%hangingNodes, np) + ! All child nodes were created outside the document by cloneNode above + else + np%inDocument = .true. + endif + + end function createEntityReference + + function createEmptyEntityReference(arg, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createEmptyEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createEmptyEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(name, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createEmptyEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, ENTITY_REFERENCE_NODE, name, "") + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createEmptyEntityReference + + function getElementsByTagName(doc, tagName, name, ex)result(list) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + character(len=*), intent(in), optional :: tagName, name + type(NodeList), pointer :: list + + type(NodeListPtr), pointer :: nll(:), temp_nll(:) + type(Node), pointer :: arg, this, treeroot + logical :: doneChildren, doneAttributes, allElements + integer :: i, i_tree + + if (.not.associated(doc)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getElementsByTagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (doc%nodeType==DOCUMENT_NODE) then + if (present(name).or..not.present(tagName)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + elseif (doc%nodeType==ELEMENT_NODE) then + if (present(name).or..not.present(tagName)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + else + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (doc%nodeType==DOCUMENT_NODE) then + arg => getDocumentElement(doc) + else + arg => doc + endif + + allocate(list) + allocate(list%nodes(0)) + list%element => doc + if (present(name)) list%nodeName => vs_str_alloc(name) + if (present(tagName)) list%nodeName => vs_str_alloc(tagName) + + allElements = (str_vs(list%nodeName)=="*") + + if (doc%nodeType==DOCUMENT_NODE) then + nll => doc%docExtras%nodelists + elseif (doc%nodeType==ELEMENT_NODE) then + nll => doc%ownerDocument%docExtras%nodelists + endif + allocate(temp_nll(size(nll)+1)) + do i = 1, size(nll) + temp_nll(i)%this => nll(i)%this + enddo + temp_nll(i)%this => list + deallocate(nll) + if (doc%nodeType==DOCUMENT_NODE) then + doc%docExtras%nodelists => temp_nll + elseif (doc%nodeType==ELEMENT_NODE) then + doc%ownerDocument%docExtras%nodelists => temp_nll + endif + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + if (this%nodeType==ELEMENT_NODE) then + if ((allElements .or. str_vs(this%nodeName)==tagName) & + .and..not.(getNodeType(doc)==ELEMENT_NODE.and.associated(this, arg))) & + call append(list, this) + doneAttributes = .true. + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + end function getElementsByTagName + + function importNode(doc , arg, deep , ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + type(Node), pointer :: arg + logical, intent(in) :: deep + type(Node), pointer :: np + + type(Node), pointer :: this, thatParent, new, treeroot + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + logical :: doneAttributes, doneChildren, brokenNS + integer :: i_tree + + if (.not.associated(doc).or..not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(doc)/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)==DOCUMENT_NODE .or. & + getNodeType(arg)==DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + brokenNS = doc%docExtras%brokenNS + doc%docExtras%brokenNS = .true. ! We need to do stupid NS things + xds => getXds(doc) + thatParent => null() + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + new => null() + select case (getNodeType(this, ex)) + case (ELEMENT_NODE) + if (.not.doneAttributes) then + ! We dont create an empty node - we insist on having all default + ! properties created. + if (getParameter(getDomConfig(doc, ex), "namespaces", ex)) then + new => createElementNS(doc, getNamespaceURI(this, ex), getTagName(this, ex), ex) + else + new => createElement(doc, getTagName(this, ex), ex) + endif + endif + case (ATTRIBUTE_NODE) + if (associated(this, arg).or.getSpecified(this, ex)) then + ! We are importing just this attribute node + ! or this was an explicitly specified attribute; either + ! way, we import it as is, and it remains specified. + if (getParameter(getDomConfig(doc), "namespaces")) then + new => createAttributeNS(doc, getNamespaceURI(this, ex), getName(this, ex), ex) + else + new => createAttribute(doc, getName(this), ex) + endif + call setSpecified(new, .true.) + else + ! This is an attribute being imported as part of a hierarchy, + ! but its only here by default. Is there a default attribute + ! of this name in the new document? + elem => get_element(xds%element_list, & + getTagName(getOwnerElement(this))) + att => get_attribute_declaration(elem, getName(this)) + if (attribute_has_default(att)) then + ! Create the new default: + if (getParameter(getDomConfig(doc, ex), "namespaces", ex)) then + ! We create a namespaced attribute. Of course, its + ! namespaceURI remains empty for the moment unless we know it ... + if (prefixOfQName(getName(this, ex))=="xml") then + new => createAttributeNS(doc, & + "http://www.w3.org/XML/1998/namespace", & + getName(this, ex), ex) + elseif (getName(this, ex)=="xmlns" & + .or. prefixOfQName(getName(this, ex))=="xmlns") then + new => createAttributeNS(doc, & + "http://www.w3.org/2000/xmlns/", & + getName(this, ex), ex) + else + ! Wait for namespace fixup ... + new => createAttributeNS(doc, "", & + getName(this, ex), ex) + endif + else + new => createAttribute(doc, getName(this, ex), ex) + endif + call setValue(new, str_vs(att%default), ex) + call setSpecified(new, .false.) + endif + ! In any case, we dont want to copy the children of this node. + doneChildren=.true. + endif + case (TEXT_NODE) + new => createTextNode(doc, getData(this, ex), ex) + case (CDATA_SECTION_NODE) + new => createCDataSection(doc, getData(this, ex), ex) + case (ENTITY_REFERENCE_NODE) + new => createEntityReference(doc, getNodeName(this, ex), ex) + ! This will automatically populate the entity reference if doc defines it, so no children needed + doneChildren = .true. + case (ENTITY_NODE) + new => createEntity(doc, getNodeName(this, ex), & + getPublicId(this, ex), getSystemId(this, ex), & + getNotationName(this, ex), ex) + case (PROCESSING_INSTRUCTION_NODE) + new => createProcessingInstruction(doc, & + getTarget(this, ex), getData(this, ex), ex) + case (COMMENT_NODE) + new => createComment(doc, getData(this, ex), ex) + case (DOCUMENT_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (DOCUMENT_TYPE_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (DOCUMENT_FRAGMENT_NODE) + new => createDocumentFragment(doc, ex) + case (NOTATION_NODE) + new => createNotation(doc, getNodeName(this, ex), & + getPublicId(this, ex), getSystemId(this, ex), ex) + end select + + if (.not.associated(thatParent)) then + thatParent => new + elseif (associated(new)) then + if (getNodeType(this, ex)==ATTRIBUTE_NODE) then + new => setAttributeNode(thatParent, new, ex) + else + new => appendChild(thatParent, new, ex) + endif + endif + + if (.not.deep) then + if (getNodeType(arg, ex)==ATTRIBUTE_NODE & + .or.getNodeType(arg, ex)==ELEMENT_NODE) then + continue + else + exit + endif + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + if (.not.associated(this, treeroot)) thatParent => getLastChild(thatParent) + this => item(getAttributes(this), 0) + else + if (.not.deep) exit + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + if (getNodeType(this)==ELEMENT_NODE.and..not.deep) exit + if (.not.associated(this, treeroot)) then + if (getNodeType(this)==ATTRIBUTE_NODE) then + thatParent => item(getAttributes(thatParent), i_tree) + else + thatParent => getLastChild(thatParent) + endif + endif + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + if (associated(getParentNode(thatParent))) thatParent => getParentNode(thatParent) + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + if (.not.associated(this, treeroot)) then + if (getNodeType(this)==ATTRIBUTE_NODE) then + thatParent => getOwnerElement(thatParent) + else + thatParent => getParentNode(thatParent) + endif + endif + endif + endif + + enddo + + + + np => thatParent + doc%docExtras%brokenNS = brokenNS +! call namespaceFixup(np) + + end function importNode + + function createElementNS(arg, namespaceURI, qualifiedName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, qualifiedName + type(Node), pointer :: np + + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + integer :: i + logical :: brokenNS + type(URI), pointer :: URIref + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkQName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="" & + .and. namespaceURI=="".and..not.arg%docExtras%brokenNS) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/XML/1998/namespace" .neqv. & + prefixOfQName(qualifiedName)=="xml") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + URIref => parseURI(namespaceURI) + if (.not.associated(URIref)) then + if (getFoX_checks().or.FoX_INVALID_URI<200) then + call throw_exception(FoX_INVALID_URI, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call destroyURI(URIref) + + np => createNode(arg, ELEMENT_NODE, qualifiedName, "") + allocate(np%elExtras) + np%elExtras%namespaceURI => vs_str_alloc(namespaceURI) + np%elExtras%prefix => vs_str_alloc(prefixOfQName(qualifiedname)) + np%elExtras%localName => vs_str_alloc(localpartOfQName(qualifiedname)) + allocate(np%elExtras%namespaceNodes%nodes(0)) + + np%elExtras%attributes%ownerElement => np + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + ! We only add default attributes if we are *not* building the doc + xds => getXds(arg) + elem => get_element(xds%element_list, qualifiedName) + if (associated(elem)) then + do i = 1, get_attlist_size(elem) + att => get_attribute_declaration(elem, i) + if (attribute_has_default(att)) then + ! Since this is a namespaced function, we create a namespaced + ! attribute. Of course, its namespaceURI remains empty + ! for the moment unless we know it ... + if (prefixOfQName(str_vs(att%name))=="xml") then + call setAttributeNS(np, & + "http://www.w3.org/XML/1998/namespace", & + str_vs(att%name), str_vs(att%default), ex) + elseif (str_vs(att%name)=="xmlns" & + .or. prefixOfQName(str_vs(att%name))=="xmlns") then + call setAttributeNS(np, & + "http://www.w3.org/2000/xmlns/", & + str_vs(att%name), str_vs(att%default), ex) + else + ! Wait for namespace fixup ... + brokenNS = arg%docExtras%brokenNS + arg%docExtras%brokenNS = .true. + call setAttributeNS(np, "", str_vs(att%name), & + str_vs(att%default), ex) + arg%docExtras%brokenNS = brokenNS + endif + endif + enddo + endif + else + np%inDocument = .true. + endif + + end function createElementNS + + function createEmptyElementNS(arg, namespaceURI, qualifiedName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, qualifiedName + type(Node), pointer :: np + +! NO CHECKS ! + + np => createNode(arg, ELEMENT_NODE, qualifiedName, "") + allocate(np%elExtras) + np%elExtras%namespaceURI => vs_str_alloc(namespaceURI) + np%elExtras%prefix => vs_str_alloc(prefixOfQName(qualifiedname)) + np%elExtras%localName => vs_str_alloc(localpartOfQName(qualifiedname)) + allocate(np%elExtras%namespaceNodes%nodes(0)) + + np%elExtras%attributes%ownerElement => np + + if (getGCstate(arg)) then + call append(arg%docExtras%hangingnodes, np) + np%inDocument = .false. + else + np%inDocument = .true. + endif + end function createEmptyElementNS + + function createAttributeNS(arg, namespaceURI, qualifiedname, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, qualifiedName + type(Node), pointer :: np + + type(URI), pointer :: URIref + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkQName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="" & + .and. namespaceURI=="".and..not.arg%docExtras%brokenNS) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/XML/1998/namespace" .neqv. & + prefixOfQName(qualifiedName)=="xml") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/" .neqv. & + (qualifiedName=="xmlns" .or. prefixOfQName(qualifiedName)=="xmlns")) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + URIref => parseURI(namespaceURI) + if (.not.associated(URIref)) then + if (getFoX_checks().or.FoX_INVALID_URI<200) then + call throw_exception(FoX_INVALID_URI, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call destroyURI(URIref) + + + np => createNode(arg, ATTRIBUTE_NODE, qualifiedName, "") + allocate(np%elExtras) + np%elExtras%namespaceURI => vs_str_alloc(namespaceURI) + np%elExtras%localname => vs_str_alloc(localPartofQName(qualifiedname)) + np%elExtras%prefix => vs_str_alloc(PrefixofQName(qualifiedname)) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createAttributeNS + + function getElementsByTagNameNS(doc, namespaceURI, localName, ex)result(list) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + character(len=*), intent(in) :: namespaceURI, localName + type(NodeList), pointer :: list + + type(NodeListPtr), pointer :: nll(:), temp_nll(:) + type(Node), pointer :: this, arg, treeroot + logical :: doneChildren, doneAttributes, allLocalNames, allNameSpaces + integer :: i, i_tree + + if (.not.associated(doc)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getElementsByTagNameNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (doc%nodeType/=DOCUMENT_NODE.and.doc%nodeType/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementsByTagNameNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + allNamespaces = (namespaceURI=="*") + allLocalNames = (localName=="*") + + if (doc%nodeType==DOCUMENT_NODE) then + arg => getDocumentElement(doc) + else + arg => doc + endif + + allocate(list) + allocate(list%nodes(0)) + list%element => doc + list%localName => vs_str_alloc(localName) + list%namespaceURI => vs_str_alloc(namespaceURI) + + if (doc%nodeType==DOCUMENT_NODE) then + nll => doc%docExtras%nodelists + elseif (doc%nodeType==ELEMENT_NODE) then + nll => doc%ownerDocument%docExtras%nodelists + endif + allocate(temp_nll(size(nll)+1)) + do i = 1, size(nll) + temp_nll(i)%this => nll(i)%this + enddo + temp_nll(i)%this => list + deallocate(nll) + if (doc%nodeType==DOCUMENT_NODE) then + doc%docExtras%nodelists => temp_nll + elseif (doc%nodeType==ELEMENT_NODE) then + doc%ownerDocument%docExtras%nodelists => temp_nll + endif + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)==ELEMENT_NODE) then + if (getNamespaceURI(this)/="") then + if ((allNameSpaces .or. getNameSpaceURI(this)==namespaceURI) & + .and. (allLocalNames .or. getLocalName(this)==localName) & + .and..not.(getNodeType(doc)==ELEMENT_NODE.and.associated(this, arg))) & + call append(list, this) + else + if ((allNameSpaces .or. namespaceURI=="") & + .and. (allLocalNames .or. getNodeName(this)==localName) & + .and..not.(getNodeType(doc)==ELEMENT_NODE.and.associated(this, arg))) & + call append(list, this) + endif + doneAttributes = .true. ! Never search attributes + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + end function getElementsByTagNameNS + + + function getElementById(arg, elementId, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: elementId + type(Node), pointer :: np + + type(Node), pointer :: this, treeroot + integer :: i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getElementById", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementById", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => null() + treeroot => getDocumentElement(arg) + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + if (this%nodeType==ATTRIBUTE_NODE) then + if (getIsId(this).and.getValue(this)==elementId) then + np => getOwnerElement(this) + return + endif + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + end function getElementById + +function getxmlStandalone(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getxmlStandalone", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getxmlStandalone", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%docExtras%xds%standalone + + end function getxmlStandalone + +subroutine setxmlStandalone(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setxmlStandalone", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setxmlStandalone", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%xds%standalone = c + + end subroutine setxmlStandalone + +! FIXME additional check on setting - do we have any undefined entrefs present? + + function getXmlVersion(arg, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=3) :: s + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE & + .and.arg%nodeType/=ENTITY_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getXmlVersionEnum(arg)==XML1_0) then + s = "1.0" + elseif (getXmlVersionEnum(arg)==XML1_1) then + s = "1.1" + else + s = "XXX" + endif + + end function getXmlVersion + + subroutine setXmlVersion(arg, s, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*) :: s + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (s=="1.0") then + arg%docExtras%xds%xml_version = XML1_0 + elseif (s=="1.1") then + arg%docExtras%xds%xml_version = XML1_1 + else + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "setXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end subroutine setXmlVersion + + pure function getXmlEncoding_len(arg, p) result(n) + type(Node), pointer :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (.not.p) return + if (arg%nodeType==DOCUMENT_NODE) & + n = size(arg%docExtras%xds%encoding) + end function getXmlEncoding_len + + function getXmlEncoding(arg, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getXmlEncoding_len(arg, .true.)) :: s +#else + character(len=getXmlEncoding_len(arg, associated(arg))) :: s +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getXmlEncoding", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType==DOCUMENT_NODE) then + s = str_vs(arg%docExtras%xds%encoding) + elseif (arg%nodeType==ENTITY_NODE) then + s = "" !FIXME revisit when we have working external entities + else + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getXmlEncoding", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end function getXmlEncoding + + pure function getInputEncoding_len(arg, p) result(n) + type(Node), pointer :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (.not.p) return + if (arg%nodeType==DOCUMENT_NODE) & + n = size(arg%docExtras%xds%inputEncoding) + end function getInputEncoding_len + + function getInputEncoding(arg, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getInputEncoding_len(arg, .true.)) :: s +#else + character(len=getInputEncoding_len(arg, associated(arg))) :: s +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getInputEncoding", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType==DOCUMENT_NODE) then + s = str_vs(arg%docExtras%xds%inputEncoding) + elseif (arg%nodeType==ENTITY_NODE) then + s = "" !FIXME revisit when we have working external entities + else + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getInputEncoding", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end function getInputEncoding + + + pure function getdocumentURI_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==DOCUMENT_NODE .or. & + .false.)) then + n = size(np%docExtras%xds%documentURI) + else + n = 0 + endif + end function getdocumentURI_len +function getdocumentURI(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getdocumentURI_len(np, .true.)) :: c +#else + character(len=getdocumentURI_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdocumentURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdocumentURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%docExtras%xds%documentURI) + + end function getdocumentURI + +subroutine setdocumentURI(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*) :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setdocumentURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setdocumentURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%docExtras%xds%documentURI)) deallocate(np%docExtras%xds%documentURI) + np%docExtras%xds%documentURI => vs_str_alloc(c) + + end subroutine setdocumentURI + + +function getstrictErrorChecking(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getstrictErrorChecking", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getstrictErrorChecking", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%docExtras%strictErrorChecking + + end function getstrictErrorChecking + +subroutine setstrictErrorChecking(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setstrictErrorChecking", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setstrictErrorChecking", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%strictErrorChecking = c + + end subroutine setstrictErrorChecking + + + function adoptNode(doc , arg , ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + type(Node), pointer :: arg + type(Node), pointer :: np + + type(Node), pointer :: this, thatParent, new, treeroot, parent, dead + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + logical :: doneAttributes, doneChildren, brokenNS + integer :: i_tree + + if (.not.associated(doc).or..not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(doc)/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)==DOCUMENT_NODE .or. & + getNodeType(arg)==DOCUMENT_TYPE_NODE .or. & + getNodeType(arg)==NOTATION_NODE .or. & + getNodeType(arg)==ENTITY_NODE) then + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getReadonly(arg)) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + brokenNS = doc%docExtras%brokenNS + doc%docExtras%brokenNS = .true. ! We need to do stupid NS things + xds => getXds(doc) + + if (associated(getParentNode(arg))) then + np => removeChild(getParentNode(arg), arg) + else + np => arg + endif + + if (associated(arg, getOwnerDocument(arg))) return + + thatParent => null() + treeroot => np + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + select case (getNodeType(this)) + case (ELEMENT_NODE) + if (.not.doneAttributes) call setOwnerDocument(this, doc) + case (ATTRIBUTE_NODE) + if (associated(this, arg).or.getSpecified(this)) then + ! We are importing just this attribute node + ! or this was an explicitly specified attribute; either + ! way, we import it as is, and it becomes/remains specified. + call setOwnerDocument(this, doc) + call setSpecified(this, .true.) + else + ! This is an attribute being imported as part of a hierarchy, + ! but its only here by default. Is there a default attribute + ! of this name in the new document? + elem => get_element(xds%element_list, & + getTagName(getOwnerElement(this))) + att => get_attribute_declaration(elem, getName(this)) + if (attribute_has_default(att)) then + ! Create the new default: + if (getParameter(getDomConfig(doc), "namespaces")) then + ! We create a namespaced attribute. Of course, its + ! namespaceURI remains empty for the moment unless we know it ... + if (prefixOfQName(getName(this))=="xml") then + new => createAttributeNS(np, & + "http://www.w3.org/XML/1998/namespace", & + getName(this)) + elseif (getName(this)=="xmlns" & + .or. prefixOfQName(getName(this))=="xmlns") then + new => createAttributeNS(np, & + "http://www.w3.org/2000/xmlns/", & + getName(this)) + else + ! Wait for namespace fixup ... + new => createAttributeNS(np, "", & + getName(this)) + endif + else + new => createAttribute(doc, getName(this)) + endif + call setValue(new, str_vs(att%default)) + call setSpecified(new, .false.) + ! In any case, we dont want to copy the children of this node. + doneChildren = .true. + dead => setAttributeNode(getOwnerElement(this), new) + this => new + call destroyAllNodesRecursively(dead) + endif + ! Otherwise no attribute here, so go back to previous node + dead => this + if (i_tree==0) then + this => getOwnerElement(this) + else + i_tree = i_tree - 1 + this => item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .true. + endif + call removeAttribute(getOwnerElement(dead), getNodeName(dead)) + endif + case (ENTITY_REFERENCE_NODE) + new => createEntityReference(doc, getNodeName(this)) + ! This will automatically populate the entity reference if doc defines it, so no children needed + parent => getParentNode(this) + if (associated(parent)) then + dead => replaceChild(parent, new, this) + this => new + call destroyAllNodesRecursively(dead) + endif + doneChildren = .true. + case (ENTITY_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (DOCUMENT_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (DOCUMENT_TYPE_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (NOTATION_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case default + call setOwnerDocument(this, doc) + end select + + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + doc%docExtras%brokenNS = brokenNS +! call namespaceFixup(np) + + end function adoptNode + +function getdomConfig(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(DOMConfiguration), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdomConfig", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdomConfig", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%docExtras%domConfig + + end function getdomConfig + +subroutine setdomConfig(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(DOMConfiguration), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setdomConfig", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setdomConfig", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%domConfig => c + + end subroutine setdomConfig + + + + function renameNode(arg, n, namespaceURI, qualifiedName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: n + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: qualifiedName + type(Node), pointer :: np + + type(Node), pointer :: attNode + integer :: i + logical :: brokenNS + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + type(xml_doc_state), pointer :: xds + type(URI), pointer :: URIref + + if (.not.associated(arg).or..not.associated(n)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg)/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(arg, getOwnerDocument(n))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkQName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="" & + .and. namespaceURI=="".and..not.arg%docExtras%brokenNS) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/XML/1998/namespace" .neqv. & + prefixOfQName(qualifiedName)=="xml") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + URIref => parseURI(namespaceURI) + if (.not.associated(URIref)) then + if (getFoX_checks().or.FoX_INVALID_URI<200) then + call throw_exception(FoX_INVALID_URI, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call destroyURI(URIref) + +! FIXME what if this is called on a Level 1 node +! FIXME what if this is called on a read-only node +! FIXME what if this is called on an attribute whose specified=fals + select case(getNodeType(n)) + case (ELEMENT_NODE, ATTRIBUTE_NODE) + deallocate(n%nodeName) + n%nodeName => vs_str_alloc(qualifiedName) + deallocate(n%elExtras%namespaceURI) + n%elExtras%namespaceURI => vs_str_alloc(namespaceURI) + deallocate(n%elExtras%localName) + n%elExtras%localName => vs_str_alloc(localpartOfQName(qualifiedname)) + case default + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + if (getNodeType(n)==ELEMENT_NODE) then + i = 0 + do while (i item(getAttributes(n), i) + if (.not.getSpecified(attNode)) then + attNode => removeAttributeNode(n, attNode) + call destroyNode(attNode) + else + i = i + 1 + endif + enddo + xds => getXds(arg) + elem => get_element(xds%element_list, qualifiedName) + if (associated(elem)) then + do i = 1, get_attlist_size(elem) + att => get_attribute_declaration(elem, i) + if (attribute_has_default(att)) then + ! Since this is a namespaced function, we create a namespaced + ! attribute. Of course, its namespaceURI remains empty + ! for the moment unless we know it ... + if (prefixOfQName(str_vs(att%name))=="xml") then + call setAttributeNS(np, & + "http://www.w3.org/XML/1998/namespace", & + str_vs(att%name), str_vs(att%default)) + elseif (str_vs(att%name)=="xmlns" & + .or. prefixOfQName(str_vs(att%name))=="xmlns") then + call setAttributeNS(np, & + "http://www.w3.org/2000/xmlns/", & + str_vs(att%name), str_vs(att%default)) + else + ! Wait for namespace fixup ... + brokenNS = arg%docExtras%brokenNS + arg%docExtras%brokenNS = .true. + call setAttributeNS(np, "", str_vs(att%name), & + str_vs(att%default)) + arg%docExtras%brokenNS = brokenNS + endif + endif + enddo + endif + endif + + np => n + + end function renameNode + + ! Internal function, not part of API + + function createNamespaceNode(arg, prefix, URI, specified, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: prefix + character(len=*), intent(in) :: URI + logical, intent(in) :: specified + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createNamespaceNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createNamespaceNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, XPATH_NAMESPACE_NODE, "#namespace", URI) + allocate(np%elExtras) + np%elExtras%prefix => vs_str_alloc(prefix) + np%elExtras%namespaceURI => vs_str_alloc(URI) + np%elExtras%specified = specified + + end function createNamespaceNode + + function createEntity(arg, name, publicId, systemId, notationName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + character(len=*), intent(in) :: notationName + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createEntity", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createEntity", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, ENTITY_NODE, name, "") + allocate(np%dtdExtras) + np%dtdExtras%publicId => vs_str_alloc(publicId) + np%dtdExtras%systemId => vs_str_alloc(systemId) + np%dtdExtras%notationName => vs_str_alloc(notationName) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createEntity + + function createNotation(arg, name, publicId, systemId, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createNotation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createNotation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, NOTATION_NODE, name, "") + allocate(np%dtdExtras) + np%dtdExtras%publicId => vs_str_alloc(publicId) + np%dtdExtras%systemId => vs_str_alloc(systemId) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createNotation + + function getXmlVersionEnum(arg, ex)result(n) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "getXmlVersionEnum", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + n = arg%docExtras%xds%xml_version + + end function getXmlVersionEnum + + function getXds(arg, ex)result(xds) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(xml_doc_state), pointer :: xds + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "getXds", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + xds => arg%docExtras%xds + + end function getXds + + +function getGCstate(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getGCstate", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getGCstate", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%docExtras%xds%building + + end function getGCstate + +subroutine setGCstate(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setGCstate", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setGCstate", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%xds%building = c + + end subroutine setGCstate + + +function getliveNodeLists(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getliveNodeLists", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getliveNodeLists", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%docExtras%liveNodeLists + + end function getliveNodeLists + +subroutine setliveNodeLists(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setliveNodeLists", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setliveNodeLists", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%liveNodeLists = c + + end subroutine setliveNodeLists + + + + +! function getName(docType) result(c) See m_dom_common + + function getEntities(arg, ex)result(nnp) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(NamedNodeMap), pointer :: nnp + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getEntities", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getEntities", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + nnp => arg%dtdExtras%entities + end function getEntities + + function getNotations(arg, ex)result(nnp) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(NamedNodeMap), pointer :: nnp + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getNotations", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getNotations", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + nnp => arg%dtdExtras%notations + end function getNotations + + +! function getPublicId(docType) result(c) See m_dom_common + + +! function getSystemId(docType) result(c) See m_dom_common + + pure function getInternalSubset_len(arg, p) result(n) + type(Node), pointer :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (p) then + if (associated(arg%ownerDocument)) then + if (associated(arg%ownerDocument%docExtras%xds%intSubset)) then + n = size(arg%ownerDocument%docExtras%xds%intSubset) + endif + endif + endif + end function getInternalSubset_len + + function getInternalSubset(arg, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getInternalSubset_len(arg, .true.)) :: s +#else + character(len=getInternalSubset_len(arg, associated(arg))) :: s +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getInternalSubset", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getInternalSubset", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(s)>0) then + s = str_vs(arg%ownerDocument%docExtras%xds%intSubset) + else + s = "" + endif + end function getInternalSubset + + + + + pure function gettagName_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==ELEMENT_NODE .or. & + .false.)) then + n = size(np%nodeName) + else + n = 0 + endif + end function gettagName_len +function gettagName(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=gettagName_len(np, .true.)) :: c +#else + character(len=gettagName_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "gettagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ELEMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "gettagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodeName) + + end function gettagName + + + pure function getAttribute_len(arg, p, name) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + character(len=*), intent(in) :: name + integer :: n + + integer :: i + + n = 0 + if (.not.p) return + if (arg%nodeType/=ELEMENT_NODE) return + + do i = 1, arg%elExtras%attributes%length + if (str_vs(arg%elExtras%attributes%nodes(i)%this%nodeName)==name) then + n = getTextContent_len(arg%elExtras%attributes%nodes(i)%this, .true.) + exit + endif + enddo + + end function getAttribute_len + + function getAttribute(arg, name, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getAttribute_len(arg, .true., name)) :: c +#else + character(len=getAttribute_len(arg, associated(arg), name)) :: c +#endif + + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg) /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c)>0) then + do i = 1, arg%elExtras%attributes%length + if (str_vs(arg%elExtras%attributes%nodes(i)%this%nodeName)==name) then + c = getTextContent(arg%elExtras%attributes%nodes(i)%this) + exit + endif + enddo + else + c = "" + endif + + end function getAttribute + + + subroutine setAttribute(arg, name, value, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + + type(Node), pointer :: nn, dummy + logical :: quickFix + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodetype(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(name, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(value, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + quickFix = getGCstate(getOwnerDocument(arg)) & + .and. arg%inDocument + + if (quickFix) call setGCstate(getOwnerDocument(arg), .false.) + ! then the created attribute is going straight into the document, + ! so dont faff with hanging-node lists. + + nn => createAttribute(arg%ownerDocument, name) + call setValue(nn, value) + dummy => setNamedItem(getAttributes(arg), nn) + if (associated(dummy)) then + if (getGCstate(getOwnerDocument(arg)).and..not.dummy%inDocument) & + call putNodesInDocument(getOwnerDocument(arg), dummy) + ! ... so that dummy & children are removed from hangingNodes list. + call destroyAllNodesRecursively(dummy) + endif + + if (quickFix) call setGCstate(getOwnerDocument(arg), .true.) + + end subroutine setAttribute + + + subroutine removeAttribute(arg, name, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + type(DOMException) :: ex2 + type(Node), pointer :: dummy + integer :: e + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodetype(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "removeAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%inDocument) & + call setGCstate(getOwnerDocument(arg), .false.) + + dummy => removeNamedItem(getAttributes(arg), name, ex2) + ! removeNamedItem took care of any default attributes + if (inException(ex2)) then + e = getExceptionCode(ex2) + if (e/=NOT_FOUND_ERR) then + if (getFoX_checks().or.e<200) then + call throw_exception(e, "removeAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + else + if (.not.arg%inDocument) then + ! dummy was not in the doc, so was on hangingNode list. + ! To remove it from the list: + call putNodesInDocument(arg%ownerDocument, dummy) + endif + call destroyAllNodesRecursively(dummy) + endif + + if (arg%inDocument) & + call setGCstate(arg%ownerDocument, .true.) + + end subroutine removeAttribute + + + function getAttributeNode(arg, name, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + attr => getNamedItem(getAttributes(arg), name) + + end function getAttributeNode + + + function setAttributeNode(arg, newattr, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newattr + type(Node), pointer :: attr + type(Node), pointer :: dummy + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(arg%ownerDocument, newattr%ownerDocument)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(getOwnerElement(newattr), arg)) then + attr => newattr + return + ! Nothing to do, this attribute is already in this element + elseif (associated(getOwnerElement(newattr))) then + if (getFoX_checks().or.INUSE_ATTRIBUTE_ERR<200) then + call throw_exception(INUSE_ATTRIBUTE_ERR, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! this checks if attribute exists already + ! It also does any adding/removing of hangingnodes + ! and sets ownerElement appropriately + dummy => setNamedItem(getAttributes(arg), newattr, ex) + attr => dummy + + end function setAttributeNode + + + function removeAttributeNode(arg, oldattr, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: oldattr + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "removeAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(arg, getOwnerElement(oldattr))) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + attr => removeNamedItem(getAttributes(arg), & + getNodeName(oldattr), ex) + + end function removeAttributeNode + + +! function getElementsByTagName - see m_dom_document + + + pure function getAttributesNS_len(arg, p, localname, namespaceURI) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + character(len=*), intent(in) :: localname + character(len=*), intent(in) :: namespaceURI + integer :: n + + integer :: i + + n = 0 + if (.not.p) return + if (arg%nodeType/=ELEMENT_NODE) return + + do i = 1, arg%elExtras%attributes%length + if ((str_vs(arg%elExtras%attributes%nodes(i)%this%elExtras%localName)==localname & + .and. str_vs(arg%elExtras%attributes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) & + .or. (namespaceURI=="".and.str_vs(arg%elExtras%attributes%nodes(i)%this%nodeName)==localname)) then + n = getTextContent_len(arg%elExtras%attributes%nodes(i)%this, .true.) + exit + endif + enddo + + end function getAttributesNS_len + + function getAttributeNS(arg, namespaceURI, localName, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getAttributesNS_len(arg, .true., localname, namespaceURI)) :: c +#else + character(len=getAttributesNS_len(arg, associated(arg), localname, namespaceURI)) :: c +#endif + + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c)>0) then + do i = 1, arg%elExtras%attributes%length + if ((str_vs(arg%elExtras%attributes%nodes(i)%this%elExtras%localName)==localname & + .and. str_vs(arg%elExtras%attributes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) & + .or. (namespaceURI=="".and.str_vs(arg%elExtras%attributes%nodes(i)%this%nodeName)==localname)) then + c = getTextContent(arg%elExtras%attributes%nodes(i)%this) + exit + endif + enddo + else + c = "" + endif + + end function getAttributeNS + + + subroutine setAttributeNS(arg, namespaceURI, qualifiedname, value, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: qualifiedName + character(len=*), intent(in) :: value + + type(Node), pointer :: nn, dummy + logical :: quickfix + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(qualifiedname, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (.not.arg%ownerDocument%docExtras%brokenNS) then + if (.not.checkQName(qualifiedname, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="" & + .and. namespaceURI=="") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)=="xml" .neqv. & + namespaceURI=="http://www.w3.org/XML/1998/namespace") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/" .neqv. & + (qualifiedName=="xmlns" .or. prefixOfQName(qualifiedName)=="xmlns")) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + +! FIXME what if namespace is undeclared? Throw an error *only* if FoX_errors is on, otherwise its taken care of by namespace fixup on serialization + + quickFix = getGCstate(getOwnerDocument(arg)) & + .and. arg%inDocument + + if (quickFix) call setGCstate(getOwnerDocument(arg), .false.) + ! then the created attribute is going straight into the document, + ! so dont faff with hanging-node lists. + + nn => createAttributeNS(arg%ownerDocument, namespaceURI, qualifiedname) + call setValue(nn, value) + dummy => setNamedItemNS(getAttributes(arg), nn) + + if (associated(dummy)) then + if (getGCstate(getOwnerDocument(arg)).and..not.dummy%inDocument) & + call putNodesInDocument(getOwnerDocument(arg), dummy) + ! ... so that dummy & children are removed from hangingNodes list. + call destroyAllNodesRecursively(dummy) + endif + + if (quickFix) call setGCstate(getOwnerDocument(arg), .true.) + + end subroutine setAttributeNS + + + subroutine removeAttributeNS(arg, namespaceURI, localName, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + + type(DOMException) :: ex2 + type(Node), pointer :: dummy + integer :: e + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "removeAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%inDocument) & + call setGCstate(getOwnerDocument(arg), .false.) + ! So we dont add the removed nodes to the hanging node list + + dummy => removeNamedItemNS(getAttributes(arg), namespaceURI, localName, ex2) + ! removeNamedItemNS took care of any default attributes + if (inException(ex2)) then + e = getExceptionCode(ex2) + if (e/=NOT_FOUND_ERR) then + if (getFoX_checks().or.e<200) then + call throw_exception(e, "removeAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + else + if (.not.arg%inDocument) then + ! dummy was not in the doc, so was already on hangingNode list. + ! To remove it from the list: + call putNodesInDocument(arg%ownerDocument, dummy) + endif + call destroyAllNodesRecursively(dummy) + endif + + if (arg%inDocument) & + call setGCstate(arg%ownerDocument, .true.) + + end subroutine removeAttributeNS + + + function getAttributeNodeNS(arg, namespaceURI, localName, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + attr => null() ! as per specs, if not found + attr => getNamedItemNS(getAttributes(arg), namespaceURI, localname) + end function getAttributeNodeNS + + + function setAttributeNodeNS(arg, newattr, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newattr + type(Node), pointer :: attr + type(Node), pointer :: dummy + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(arg%ownerDocument, newattr%ownerDocument)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(getOwnerElement(newattr), arg)) then + attr => newattr + return + ! Nothing to do, this attribute is already in this element + elseif (associated(getOwnerElement(newattr))) then + if (getFoX_checks().or.INUSE_ATTRIBUTE_ERR<200) then + call throw_exception(INUSE_ATTRIBUTE_ERR, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! this checks if attribute exists already + ! It also does any adding/removing of hangingnodes + ! and sets ownerElement appropriately + dummy => setNamedItemNS(getAttributes(arg), newattr, ex) + attr => dummy + + end function setAttributeNodeNS + + + function removeAttributeNodeNS(arg, oldattr, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: oldattr + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "removeAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(arg, getOwnerElement(oldattr))) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + attr => removeNamedItemNS(getAttributes(arg), & + getNamespaceURI(oldattr), getLocalName(oldattr), ex) + + end function removeAttributeNodeNS + + +! function getElementsByTagNameNS - see m_dom_document + + + function hasAttribute(arg, name, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + logical :: p + + integer :: i + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "hasAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "hasAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + p = .false. + do i = 0, getLength(getAttributes(arg)) - 1 + attr => item(getAttributes(arg), i) + if (getNodeName(attr)==name) then + p = .true. + exit + endif + enddo + + end function hasAttribute + + + function hasAttributeNS(arg, namespaceURI, localName, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + logical :: p + + integer :: i + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "hasAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "hasAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + p = .false. + do i = 0, getLength(getAttributes(arg))-1 + attr => item(getAttributes(arg), i) + if (getNamespaceURI(attr)==namespaceURI & + .and. getLocalName(attr)==localName) then + p = .true. + exit + endif + enddo + + end function hasAttributeNS + + subroutine setIdAttribute(arg, name, isId, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + logical, intent(in) :: isId + + type(Node), pointer :: np + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setIdAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => getAttributeNode(arg, name) + if (associated(np)) then + call setIsId(np, isId) + else + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "setIdAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end subroutine setIdAttribute + + subroutine setIdAttributeNS(arg, namespaceURI, localname, isId, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + logical, intent(in) :: isId + + type(Node), pointer :: np + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setIdAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => getAttributeNodeNS(arg, namespaceURI, localname) + if (associated(np)) then + call setIsId(np, isId) + else + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "setIdAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end subroutine setIdAttributeNS + + subroutine setIdAttributeNode(arg, idAttr, isId, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: idAttr + logical, intent(in) :: isId + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setIdAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(arg, getOwnerElement(idAttr))) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "setIdAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + call setIsId(idAttr, isId) + + end subroutine setIdAttributeNode + + + + ! function getName(attribute) result(c) See m_dom_common + +! NB All functions manipulating attributes play with the nodelist +! directly rather than through helper functions. +! This is so that getValue_length can be pure, and the nodeList +! can be explicitly kept up to dat. + +function getspecified(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getspecified", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getspecified", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%elExtras%specified + + end function getspecified + + +subroutine setspecified(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setspecified", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setspecified", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%elExtras%specified = c + + end subroutine setspecified + + +function getisId_DOM(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getisId_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getisId_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%elExtras%isId + + end function getisId_DOM + + +subroutine setisId_DOM(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setisId_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setisId_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%elExtras%isId = c + + end subroutine setisId_DOM + + +function getownerElement(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getownerElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getownerElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%elExtras%ownerElement + + end function getownerElement + + + function getValue_DOM(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getTextContent_len(arg, .true.)) :: c +#else + character(len=getTextContent_len(arg, associated(arg))) :: c +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getValue_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg)/=ATTRIBUTE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getValue_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = getTextContent(arg, ex) + + end function getValue_DOM + + subroutine setValue(arg, value, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: value + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg)/=ATTRIBUTE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + call setTextContent(arg, value, ex) + + end subroutine setValue + + + + pure function isCharData(nodeType) result(p) + integer, intent(in) :: nodeType + logical :: p + + p = (nodeType == TEXT_NODE .or. & + nodeType == COMMENT_NODE .or. & + nodeType == CDATA_SECTION_NODE) + end function isCharData + + + function getLength_characterdata(arg, ex)result(n) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getLength_characterdata", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getLength_characterdata", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + n = size(arg%nodeValue) + + end function getLength_characterdata + + + function subStringData(arg, offset, count, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + integer, intent(in) :: count + character(len=count) :: c + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "subStringData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "subStringData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0.or.offset>size(arg%nodeValue).or.count<0) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "subStringData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (offset+count>size(arg%nodeValue)) then + c = str_vs(arg%nodeValue(offset+1:)) + else + c = str_vs(arg%nodeValue(offset+1:offset+count)) + endif + + end function subStringData + + + subroutine appendData(arg, data, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: data + + character, pointer :: tmp(:) + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkChars(data, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + tmp => arg%nodeValue + arg%nodeValue => vs_str_alloc(str_vs(tmp)//data) + deallocate(tmp) + + ! We have to do these checks *after* appending data in case offending string + ! spans old & new data + if (arg%nodeType==COMMENT_NODE .and. index(str_vs(arg%nodeValue),"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%nodeType==CDATA_SECTION_NODE .and. index(str_vs(arg%nodeValue), "]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! And propagate length upwards ... + if (getNodeType(arg)/=COMMENT_NODE) & + call updateTextContentLength(arg, len(data)) + + end subroutine appendData + + + subroutine insertData(arg, offset, data, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + character(len=*), intent(in) :: data + + character, pointer :: tmp(:) + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0.or.offset>size(arg%nodeValue)) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkChars(data, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + tmp => arg%nodeValue + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))//data//str_vs(tmp(offset+1:))) + deallocate(tmp) + + ! We have to do these checks *after* appending data in case offending string + ! spans old & new data + if (arg%nodeType==COMMENT_NODE .and. index(str_vs(arg%nodeValue),"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%nodeType==CDATA_SECTION_NODE .and. index(str_vs(arg%nodeValue), "]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! And propagate length upwards ... + if (getNodeType(arg)/=COMMENT_NODE) & + call updateTextContentLength(arg, len(data)) + + end subroutine insertData + + + subroutine deleteData(arg, offset, count, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + integer, intent(in) :: count + + character, pointer :: tmp(:) + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "deleteData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "deleteData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "deleteData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0.or.offset>size(arg%nodeValue).or.count<0) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "deleteData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (offset+count>size(arg%nodeValue)) then + n = size(arg%nodeValue)-offset + else + n = count + endif + + tmp => arg%nodeValue + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))//str_vs(tmp(offset+count+1:))) + deallocate(tmp) + + ! And propagate length upwards ... + if (getNodeType(arg)/=COMMENT_NODE) & + call updateTextContentLength(arg, -n) + + end subroutine deleteData + + + subroutine replaceData(arg, offset, count, data, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + integer, intent(in) :: count + character(len=*), intent(in) :: data + + character, pointer :: tmp(:) + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0.or.offset>size(arg%nodeValue).or.count<0) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkChars(data, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (offset+count>size(arg%nodeValue)) then + n = len(data)-(size(arg%nodeValue)-offset) + else + n = len(data)-count + endif + + tmp => arg%nodeValue + if (offset+count <= size(arg%nodeValue)) then + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))//data//str_vs(tmp(offset+count+1:))) + else + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))//data) + endif + deallocate(tmp) + + ! We have to do these checks *after* appending data in case offending string + ! spans old & new data + if (arg%nodeType==COMMENT_NODE .and. index(str_vs(arg%nodeValue),"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%nodeType==CDATA_SECTION_NODE .and. index(str_vs(arg%nodeValue), "]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! And propagate length upwards ... + if (getNodeType(arg)/=COMMENT_NODE) & + call updateTextContentLength(arg, n) + + end subroutine replaceData + + + + + pure function getnotationName_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==ENTITY_NODE .or. & + .false.)) then + n = size(np%dtdExtras%notationName) + else + n = 0 + endif + end function getnotationName_len +function getnotationName(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getnotationName_len(np, .true.)) :: c +#else + character(len=getnotationName_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnotationName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getnotationName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%dtdExtras%notationName) + + end function getnotationName + + +!Internally-used getters/setters: + + function getillFormed(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getillFormed", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getillFormed", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%dtdExtras%illFormed + + end function getillFormed + + subroutine setillFormed(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setillFormed", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setillFormed", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%dtdExtras%illFormed = c + + end subroutine setillFormed + + + + pure function getstringValue_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==ENTITY_NODE .or. & + .false.)) then + n = size(np%nodeValue) + else + n = 0 + endif + end function getstringValue_len +function getstringValue(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getstringValue_len(np, .true.)) :: c +#else + character(len=getstringValue_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getstringValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getstringValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodeValue) + + end function getstringValue + + subroutine setstringValue(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*) :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setstringValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setstringValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%nodeValue)) deallocate(np%nodeValue) + np%nodeValue => vs_str_alloc(c) + + end subroutine setstringValue + + + + + + pure function getTarget_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==PROCESSING_INSTRUCTION_NODE .or. & + .false.)) then + n = size(np%nodename) + else + n = 0 + endif + end function getTarget_len +function getTarget(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getTarget_len(np, .true.)) :: c +#else + character(len=getTarget_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getTarget", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=PROCESSING_INSTRUCTION_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getTarget", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodename) + + end function getTarget + + + + + function splitText(arg, offset, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + + type(Node), pointer :: np + + character, pointer :: tmp(:) + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "splitText", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.(arg%nodeType==TEXT_NODE.or.arg%nodeType==CDATA_SECTION_NODE)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "splitText", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "splitText", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0 .or. offset>size(arg%nodeValue)) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "splitText", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + tmp => arg%nodeValue + if (arg%nodeType==TEXT_NODE) then + np => createTextNode(arg%ownerDocument, str_vs(tmp(offset+1:))) + elseif (arg%nodeType==CDATA_SECTION_NODE) then + np => createCdataSection(arg%ownerDocument, str_vs(tmp(offset+1:))) + endif + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))) + deallocate(tmp) + if (associated(arg%parentNode)) then + if (associated(arg%nextSibling)) then + np => insertBefore(arg%parentNode, np, arg%nextSibling) + else + np => appendChild(arg%parentNode, np) + endif + endif + + end function splitText + +function getisElementContentWhitespace(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getisElementContentWhitespace", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=TEXT_NODE .and. & +getNodeType(np)/=CDATA_SECTION_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getisElementContentWhitespace", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%ignorableWhitespace + + end function getisElementContentWhitespace + + + subroutine setIsElementContentWhitespace(np, isElementContentWhitespace, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: isElementContentWhitespace + + integer :: n + + np%ignorableWhitespace = isElementContentWhitespace + + if (isElementContentWhitespace) then + n = -np%textContentLength + else + n = size(np%nodeValue) + endif + + call updateTextContentLength(np, n) + end subroutine setIsElementContentWhitespace + +! function getWholeText +! function replaceWholeText + + + + + pure function getdata_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==TEXT_NODE .or. & + np%nodeType==COMMENT_NODE .or. & + np%nodeType==CDATA_SECTION_NODE .or. & + np%nodeType==PROCESSING_INSTRUCTION_NODE .or. & + .false.)) then + n = size(np%nodeValue) + else + n = 0 + endif + end function getdata_len +function getdata(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getdata_len(np, .true.)) :: c +#else + character(len=getdata_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdata", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=TEXT_NODE .and. & +getNodeType(np)/=COMMENT_NODE .and. & +getNodeType(np)/=CDATA_SECTION_NODE .and. & +getNodeType(np)/=PROCESSING_INSTRUCTION_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdata", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodeValue) + + end function getdata + + + subroutine setData(arg, data, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*) :: data + + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +!NB special case in order to check readonly correctly + if (arg%nodeType==TEXT_NODE .or. & + arg%nodeType==COMMENT_NODE .or. & + arg%nodeType==CDATA_SECTION_NODE .or. & + arg%nodeType==PROCESSING_INSTRUCTION_NODE) then + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + else + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + select case (arg%nodeType) + case (CDATA_SECTION_NODE) + if (index(data,"]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (COMMENT_NODE) + if (index(data,"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (PROCESSING_INSTRUCTION_NODE) + if (index(data,"?>")>0) then + if (getFoX_checks().or.FoX_INVALID_PI_DATA<200) then + call throw_exception(FoX_INVALID_PI_DATA, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + end select + + deallocate(arg%nodeValue) + arg%nodeValue => vs_str_alloc(data) + + if (arg%nodeType==TEXT_NODE .or. & + arg%nodeType==CDATA_SECTION_NODE) then + n = len(data) - arg%textContentLength + call updateTextContentLength(arg, n) + endif + + end subroutine setData + + + pure function getname_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==DOCUMENT_TYPE_NODE .or. & + np%nodeType==ATTRIBUTE_NODE .or. & + .false.)) then + n = size(np%nodeName) + else + n = 0 + endif + end function getname_len +function getname(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getname_len(np, .true.)) :: c +#else + character(len=getname_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getname", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_TYPE_NODE .and. & +getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getname", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodeName) + + end function getname + + + + pure function getpublicId_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==DOCUMENT_TYPE_NODE .or. & + np%nodeType==NOTATION_NODE .or. & + np%nodeType==ENTITY_NODE .or. & + .false.)) then + n = size(np%dtdExtras%publicId) + else + n = 0 + endif + end function getpublicId_len +function getpublicId(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getpublicId_len(np, .true.)) :: c +#else + character(len=getpublicId_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getpublicId", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_TYPE_NODE .and. & +getNodeType(np)/=NOTATION_NODE .and. & +getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getpublicId", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%dtdExtras%publicId) + + end function getpublicId + + + + pure function getsystemId_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==DOCUMENT_TYPE_NODE .or. & + np%nodeType==NOTATION_NODE .or. & + np%nodeType==ENTITY_NODE .or. & + .false.)) then + n = size(np%dtdExtras%systemId) + else + n = 0 + endif + end function getsystemId_len +function getsystemId(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getsystemId_len(np, .true.)) :: c +#else + character(len=getsystemId_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getsystemId", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_TYPE_NODE .and. & +getNodeType(np)/=NOTATION_NODE .and. & +getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getsystemId", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%dtdExtras%systemId) + + end function getsystemId + + + + + function getnamespaceNodes(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(NodeList), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnamespaceNodes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ELEMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getnamespaceNodes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%elExtras%namespaceNodes + + end function getnamespaceNodes + + + subroutine appendNSNode(np, prefix, namespaceURI, specified, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*), intent(in) :: prefix + character(len=*), intent(in) :: namespaceURI + logical, intent(in) :: specified + + type(Node), pointer :: ns + type(NodeList), pointer :: nsnodes + integer :: i + logical :: quickFix + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "appendNSNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (np%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "appendNSNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! We never put namespace nodes in the hanging nodes + ! list since they can never be separated from their + ! parent element node, so will always be destroyed alongside it. + quickFix = getGCState(getOwnerDocument(np)) + call setGCState(getOwnerDocument(np), .false.) + nsnodes => getNamespaceNodes(np) + ! If we already have this prefix registered in the list, then remove it + do i = 0, getLength(nsNodes)-1 + ns => item(nsNodes, i) +! Intel 8.1 & 9.1 insist on separate variable here and just below + if (getPrefix(ns)==prefix) then + call setNamespaceURI(ns, namespaceURI) + exit + endif + enddo + if (i==getLength(nsNodes)) then + ns => createNamespaceNode(getOwnerDocument(np), & + prefix, namespaceURI, specified) + call append_nl(nsNodes, ns) + endif + call setGCState(getOwnerDocument(np), quickFix) + + end subroutine appendNSNode + + subroutine normalizeDocument(doc, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + + type(Node), pointer :: this, treeroot, dummy, new, old, nsp + type(DOMConfiguration), pointer :: dc + logical :: doneAttributes, doneChildren + integer :: i_tree, i_children + + type(Node), pointer :: parent, attr + type(NamedNodeMap), pointer :: attrs + type(NodeList), pointer :: nsNodes, nsNodesParent + integer :: i, nsIndex + logical :: merged, ns + + if (.not.associated(doc)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(doc)/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + dc => getDomConfig(doc) + ns = getParameter(dc, "namespaces") + treeroot => doc + + call setGCstate(doc, .false.) + ! switch off the memory management, we are going + ! to destroy all nodes we remove from the tree + ! immediately. + + ! exception object is *not* passed through in any + ! of the DOM calls below. This is because all of + ! these should succeed - if they dont then there + ! is a problem so we need to terminate immediately + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (.not.getReadonly(this)) then + select case (getNodeType(this)) + case (ELEMENT_NODE) + if (ns) then + + ! Clear all current namespace nodes: + nsnodes => getNamespaceNodes(this) + do i = 1, getLength(nsNodes) + call destroyNode(nsNodes%nodes(i)%this) + enddo + deallocate(nsNodes%nodes) + + parent => getParentNode(this) + do while (associated(parent)) + ! Go up (through perhaps multiple entref nodes) + if (getNodeType(parent)==ELEMENT_NODE) exit + parent => getParentNode(parent) + enddo + ! Inherit from parent (or not ...) + if (associated(parent)) then + nsNodesParent => getNamespaceNodes(parent) + allocate(nsNodes%nodes(getLength(nsNodesParent))) + nsNodes%length = getLength(nsNodesParent) + do i = 0, getLength(nsNodes) - 1 + ! separate variable for intel + nsp => item(nsNodesParent, i) + nsNodes%nodes(i+1)%this => & + createNamespaceNode(getOwnerDocument(this), & + getPrefix(nsp), getNamespaceURI(nsp), & + specified=.false.) + enddo + else + allocate(nsNodes%nodes(0)) + nsNodes%length = 0 + endif + + ! Now check for broken NS declarations, and add namespace + ! nodes for all non-broken declarations + attrs => getAttributes(this) + do i = 0, getLength(attrs)-1 + attr => item(attrs, i) + if ((getLocalName(attr)=="xmlns" & + .or.getPrefix(attr)=="xmlns") & + .and.getNamespaceURI(attr)/="http://www.w3.org/2000/xmlns/") then + ! This can only I think happen if we bugger about with setPrefix ... + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (getNamespaceURI(attr)=="http://www.w3.org/2000/xmlns/") then + if (getLocalName(attr)=="xmlns") then + call appendNSNode(this, "", getValue(attr), specified=.true.) + else + call appendNSNode(this, getLocalName(attr), & + getValue(attr), specified=.true.) + endif + endif + enddo + + + if (getNamespaceURI(this)/="") then + ! Is the nsURI of this node bound to its prefix? + ! This will automatically do any necessary replacements ... + if (getPrefix(this)=="") then + if (.not.isDefaultNamespace(this, getNamespaceURI(this))) then + ! We are dealing with the default prefix + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns", getNamespaceURI(this)) + call appendNSNode(this, "", getNamespaceURI(this), specified=.true.) + endif + elseif (lookupNamespaceURI(this, getPrefix(this))/=getNamespaceURI(this)) then + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(this), getNamespaceURI(this)) + call appendNSNode(this, getPrefix(this), getNamespaceURI(this), specified=.true.) + endif + else + ! No (or empty) namespace URI ... + if (getLocalName(this)=="") then + ! DOM level 1 node ... report error + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + else + ! We must declare the elements prefix to have an empty nsURI + if (lookupNamespaceURI(this, getPrefix(this))/="") then + if (getPrefix(this)=="") then + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns", "") + else + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(this), "") + endif + ! and add a namespace node for the empty nsURI + call appendNSNode(this, getPrefix(this), "", specified=.true.) + endif + endif + endif + + do i = 0, getLength(attrs)-1 + ! This loops over the number of attrs present initially, so any we + ! add within this loop will not get checked - but they will only + ! be namespace declarations about which we dont care anyway. + attr => item(attrs, i) + if (getNamespaceURI(attr)=="http://www.w3.org/2000/xmlns/") then + cycle ! We already worried about it above. + elseif (getNamespaceURI(attr)=="http://www.w3.org/XML/1998/namespace") then + cycle ! We dont have to declare these + elseif (getNamespaceURI(attr)/="") then + ! This is a namespaced attribute + if (getPrefix(attr)=="" & + .or. lookupNamespaceURI(this, getPrefix(attr))/=getNamespaceURI(attr)) then + ! It has an inappropriate prefix + if (lookupPrefix(this, getNamespaceURI(attr))/="") then + ! then an appropriate prefix exists, use it. + call setPrefix(attr, lookupPrefix(this, getNamespaceURI(attr))) + ! FIXME should be "most local" prefix. Make sure lookupPrefix does that. + else + ! No suitable prefix exists, declare one. + if (getPrefix(attr)/="") then + ! Then the current prefix is not in use, its just undeclared. + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(attr), getNamespaceURI(attr)) + call appendNSNode(this, getPrefix(attr), getNamespaceURI(attr), specified=.true.) + else + ! This node has no prefix, but needs one. Make it up. + nsIndex = 1 + do while (lookupNamespaceURI(this, "NS"//nsIndex)/="") + ! FIXME this will exit if the namespace is undeclared *or* if it is declared to be empty. + nsIndex = nsIndex+1 + enddo + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:NS"//nsIndex, getNamespaceURI(attr)) + ! and create namespace node + call appendNSNode(this, "NS"//nsIndex, getNamespaceURI(attr), specified=.true.) + call setPrefix(attr, "NS"//nsIndex) + endif + endif + endif + else + ! attribute has no namespace URI + if (getLocalName(this)=="") then + ! DOM level 1 node ... report error + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + ! otherwise no problem + endif + enddo + + endif + + case (ATTRIBUTE_NODE) + if (getParameter(dc, "entities")) then + ! we dont care about any attribute children, + ! we arent going to do anything + doneChildren = .true. + endif + + case (TEXT_NODE) + ! we may need to reset "this" later on ... + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + merged = .false. + if (getIsElementContentWhitespace(this) & + .and..not.getParameter(dc, "element-content-whitespace")) then + dummy => removeChild(getParentNode(this), this) + call destroy(dummy) + this => old + merged = .true. + endif + if (.not.merged) then + ! We didnt just remove this node. + ! Do we need to normalize? + dummy => getPreviousSibling(this) + if (associated(dummy)) then + if (getNodeType(dummy)==TEXT_NODE) then + call appendData(dummy, getData(this)) + parent => getParentNode(this) + dummy => removeChild(parent, this) + call destroy(dummy) + this => old + endif + endif + endif + + case (CDATA_SECTION_NODE) + if (.not.getParameter(dc, "cdata-sections")) then + ! we may need to reset "this" later on ... + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + merged = .false. + dummy => getPreviousSibling(this) + if (associated(dummy)) then + if (getNodeType(dummy)==TEXT_NODE) then + ! append the data to the previous node & chuck away this node + call appendData(dummy, getData(this)) + dummy => removeChild(getParentNode(this), this) + call destroy(dummy) + this => old + merged =.true. + endif + endif + if (.not.merged) then + ! we didnt merge it so just convert this to a text node + new => createTextNode(doc, getData(this)) + dummy => replaceChild(getParentNode(this), new, this) + call destroy(dummy) + this => new + endif + elseif (.not.getParameter(dc, "split-cdata-sections")) then + ! Actually, on re-reading DOM 3, this is a ridiculous + ! option. Ignoring for now. + endif + + case (ENTITY_REFERENCE_NODE) + if (.not.getParameter(dc, "entities")) then + if (associated(getFirstChild(this))) then + !If this node is not representing an unexpanded entity + ! we will need to reset "this" later on ... + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + ! take each child, and insert it immediately before the current node + do i_children = 0, getLength(getChildNodes(this))-1 + dummy => insertBefore(getParentNode(this), getFirstChild(this), this) + enddo + ! and finally remove the current node + dummy => removeChild(getParentNode(this), this) + call destroy(dummy) + ! and set the "this" pointer back so we go over these again + this => old + endif + endif + + case (COMMENT_NODE) + if (.not.getParameter(dc, "comments")) then + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + dummy => removeChild(getParentNode(this), this) + call destroy(dummy) + this => old + endif + + case (DOCUMENT_TYPE_NODE) + if (getParameter(dc, "canonical-form")) then + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + dummy => removeChild(getParentNode(this), this) + call destroy(this) + this => old + endif + + end select + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + end subroutine normalizeDocument + + recursive subroutine namespaceFixup(this, deep, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: this + logical, intent(in) :: deep + + type(Node), pointer :: parent, child, attr, nsp + type(NamedNodeMap), pointer :: attrs + type(NodeList), pointer :: nsNodes, nsNodesParent + integer :: i, nsIndex + + if (getNodeType(this) /= ELEMENT_NODE & + .and. getNodeType(this) /= ENTITY_REFERENCE_NODE & + .and. getNodeType(this)/=DOCUMENT_FRAGMENT_NODE) then + return + endif + + if (this%nodeType==ELEMENT_NODE) then + + ! Clear all current namespace nodes: + nsnodes => getNamespaceNodes(this) + do i = 1, getLength(nsNodes) + call destroyNode(nsNodes%nodes(i)%this) + enddo + deallocate(nsNodes%nodes) + + parent => getParentNode(this) + do while (associated(parent)) + ! Go up (through perhaps multiple entref nodes) + if (getNodeType(parent)==ELEMENT_NODE) exit + parent => getParentNode(parent) + enddo + ! Inherit from parent (or not ...) + if (associated(parent)) then + nsNodesParent => getNamespaceNodes(parent) + allocate(nsNodes%nodes(getLength(nsNodesParent))) + nsNodes%length = getLength(nsNodesParent) + do i = 0, getLength(nsNodes) - 1 + ! separate variable for intel + nsp => item(nsNodesParent, i) + nsNodes%nodes(i+1)%this => & + createNamespaceNode(getOwnerDocument(this), & + getPrefix(nsp), getNamespaceURI(nsp), & + specified=.false.) + enddo + else + allocate(nsNodes%nodes(0)) + nsNodes%length = 0 + endif + + ! Now check for broken NS declarations, and add namespace + ! nodes for all non-broken declarations + attrs => getAttributes(this) + do i = 0, getLength(attrs)-1 + attr => item(attrs, i) + if ((getLocalName(attr)=="xmlns" & + .or.getPrefix(attr)=="xmlns") & + .and.getNamespaceURI(attr)/="http://www.w3.org/2000/xmlns/") then + ! This can only I think happen if we bugger about with setPrefix ... + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "namespaceFixup", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (getNamespaceURI(attr)=="http://www.w3.org/2000/xmlns/") then + if (getLocalName(attr)=="xmlns") then + call appendNSNode(this, "", getValue(attr), specified=.true.) + else + call appendNSNode(this, getLocalName(attr), & + getValue(attr), specified=.true.) + endif + endif + enddo + + + if (getNamespaceURI(this)/="") then + ! Is the nsURI of this node bound to its prefix? + ! This will automatically do any necessary replacements ... + if (getPrefix(this)=="") then + if (.not.isDefaultNamespace(this, getNamespaceURI(this))) then + ! We are dealing with the default prefix + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns", getNamespaceURI(this)) + call appendNSNode(this, "", getNamespaceURI(this), specified=.true.) + endif + elseif (lookupNamespaceURI(this, getPrefix(this))/=getNamespaceURI(this)) then + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(this), getNamespaceURI(this)) + call appendNSNode(this, getPrefix(this), getNamespaceURI(this), specified=.true.) + endif + else + ! No (or empty) namespace URI ... + if (getLocalName(this)=="") then + ! DOM level 1 node ... report error + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "namespaceFixup", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + else + ! We must declare the elements prefix to have an empty nsURI + if (lookupNamespaceURI(this, getPrefix(this))/="") then + if (getPrefix(this)=="") then + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns", "") + else + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(this), "") + endif + ! and add a namespace node for the empty nsURI + call appendNSNode(this, getPrefix(this), "", specified=.true.) + endif + endif + endif + + do i = 0, getLength(attrs)-1 + ! This loops over the number of attrs present initially, so any we + ! add within this loop will not get checked - but they will only + ! be namespace declarations about which we dont care anyway. + attr => item(attrs, i) + if (getNamespaceURI(attr)=="http://www.w3.org/2000/xmlns/") then + cycle ! We already worried about it above. + elseif (getNamespaceURI(attr)=="http://www.w3.org/XML/1998/namespace") then + cycle ! We dont have to declare these + elseif (getNamespaceURI(attr)/="") then + ! This is a namespaced attribute + if (getPrefix(attr)=="" & + .or. lookupNamespaceURI(this, getPrefix(attr))/=getNamespaceURI(attr)) then + ! It has an inappropriate prefix + if (lookupPrefix(this, getNamespaceURI(attr))/="") then + ! then an appropriate prefix exists, use it. + call setPrefix(attr, lookupPrefix(this, getNamespaceURI(attr))) + ! FIXME should be "most local" prefix. Make sure lookupPrefix does that. + else + ! No suitable prefix exists, declare one. + if (getPrefix(attr)/="") then + ! Then the current prefix is not in use, its just undeclared. + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(attr), getNamespaceURI(attr)) + call appendNSNode(this, getPrefix(attr), getNamespaceURI(attr), specified=.true.) + else + ! This node has no prefix, but needs one. Make it up. + nsIndex = 1 + do while (lookupNamespaceURI(this, "NS"//nsIndex)/="") + ! FIXME this will exit if the namespace is undeclared *or* if it is declared to be empty. + nsIndex = nsIndex+1 + enddo + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:NS"//nsIndex, getNamespaceURI(attr)) + ! and create namespace node + call appendNSNode(this, "NS"//nsIndex, getNamespaceURI(attr), specified=.true.) + call setPrefix(attr, "NS"//nsIndex) + endif + endif + endif + else + ! attribute has no namespace URI + if (getLocalName(this)=="") then + ! DOM level 1 node ... report error + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "namespaceFixup", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + ! otherwise no problem + endif + enddo + + endif + + if (deep) then + ! And now call this on all appropriate children ... + child => getFirstChild(this) + do while (associated(child)) + call namespaceFixup(child, .true.) + child => getNextSibling(child) + enddo + endif + + end subroutine namespaceFixup + + +end module m_dom_dom diff --git a/src/xml/dom/m_dom_error.F90 b/src/xml/dom/m_dom_error.F90 new file mode 100644 index 0000000000..c6fdae4187 --- /dev/null +++ b/src/xml/dom/m_dom_error.F90 @@ -0,0 +1,211 @@ +module m_dom_error + + use fox_m_fsys_abort_flush, only: pxfabort + + use m_common_error, only: error_stack, add_error, in_error, destroy_error_stack + + implicit none + private + + type DOMException + private + type(error_stack) :: stack + end type DOMException + + integer, parameter, public :: INDEX_SIZE_ERR = 1 + integer, parameter, public :: DOMSTRING_SIZE_ERR = 2 + integer, parameter, public :: HIERARCHY_REQUEST_ERR = 3 + integer, parameter, public :: WRONG_DOCUMENT_ERR = 4 + integer, parameter, public :: INVALID_CHARACTER_ERR = 5 + integer, parameter, public :: NO_DATA_ALLOWED_ERR = 6 + integer, parameter, public :: NO_MODIFICATION_ALLOWED_ERR = 7 + integer, parameter, public :: NOT_FOUND_ERR = 8 + integer, parameter, public :: NOT_SUPPORTED_ERR = 9 + integer, parameter, public :: INUSE_ATTRIBUTE_ERR = 10 + integer, parameter, public :: INVALID_STATE_ERR = 11 + integer, parameter, public :: SYNTAX_ERR = 12 + integer, parameter, public :: INVALID_MODIFICATION_ERR = 13 + integer, parameter, public :: NAMESPACE_ERR = 14 + integer, parameter, public :: INVALID_ACCESS_ERR = 15 + integer, parameter, public :: VALIDATION_ERR = 16 + integer, parameter, public :: TYPE_MISMATCH_ERR = 17 + + integer, parameter, public :: INVALID_EXPRESSION_ERR = 51 + integer, parameter, public :: TYPE_ERR = 52 + + integer, parameter, public :: PARSE_ERR = 81 + integer, parameter, public :: SERIALIZE_ERR = 82 + + integer, parameter, public :: FoX_INVALID_NODE = 201 + integer, parameter, public :: FoX_INVALID_CHARACTER = 202 + integer, parameter, public :: FoX_NO_SUCH_ENTITY = 203 + integer, parameter, public :: FoX_INVALID_PI_DATA = 204 + integer, parameter, public :: FoX_INVALID_CDATA_SECTION = 205 + integer, parameter, public :: FoX_HIERARCHY_REQUEST_ERR = 206 + integer, parameter, public :: FoX_INVALID_PUBLIC_ID = 207 + integer, parameter, public :: FoX_INVALID_SYSTEM_ID = 208 + integer, parameter, public :: FoX_INVALID_COMMENT = 209 + integer, parameter, public :: FoX_NODE_IS_NULL = 210 + integer, parameter, public :: FoX_INVALID_ENTITY = 211 + integer, parameter, public :: FoX_INVALID_URI = 212 + integer, parameter, public :: FoX_IMPL_IS_NULL = 213 + integer, parameter, public :: FoX_MAP_IS_NULL = 214 + integer, parameter, public :: FoX_LIST_IS_NULL = 215 + + integer, parameter, public :: FoX_INTERNAL_ERROR = 999 + + + public :: DOMException + public :: getExceptionCode + public :: throw_exception + public :: inException + public :: dom_error + public :: internal_error + +contains + + pure function errorString(code) result(s) + integer, intent(in) :: code + character(len=27) :: s + + select case(code) + case(1) + s = "INDEX_SIZE_ERR" + case(2) + s = "DOMSTRING_SIZE_ERR" + case(3) + s = "HIERARCHY_REQUEST_ERR" + case(4) + s = "WRONG_DOCUMENT_ERR" + case(5) + s = "INVALID_CHARACTER_ERR" + case(6) + s = "NO_DATA_ALLOWED_ERR" + case(7) + s = "NO_MODIFICATION_ALLOWED_ERR" + case(8) + s = "NOT_FOUND_ERR" + case(9) + s = "NOT_SUPPORTED_ERR" + case(10) + s = "INUSE_ATTRIBUTE_ERR" + case(11) + s = "INVALID_STATE_ERR" + case(12) + s = "SYNTAX_ERR" + case(13) + s = "INVALID_MODIFICATION_ERR" + case(14) + s = "NAMESPACE_ERR" + case(15) + s = "INVALID_ACCESS_ERR" + case(16) + s = "VALIDATION_ERR" + case(18) + s = "TYPE_MISMATCH_ERR" + case(51) + s = "INVALID_EXPRESSION_ERR" + case(52) + s = "TYPE_ERR" + case(81) + s = "PARSE_ERR" + case(82) + s = "SERIALIZE_ERR" + case(201) + s = "FoX_INVALID_NODE" + case(202) + s = "FoX_INVALID_CHARACTER" + case(203) + s = "FoX_NO_SUCH_ENTITY" + case(204) + s = "FoX_INVALID_PI_DATA" + case(205) + s = "FoX_INVALID_CDATA_SECTION" + case(206) + s = "FoX_HIERARCHY_REQUEST_ERR" + case(207) + s = "FoX_INVALID_PUBLIC_ID" + case(208) + s = "FoX_INVALID_SYSTEM_ID" + case(209) + s = "FoX_INVALID_COMMENT" + case(210) + s = "FoX_NODE_IS_NULL" + case(211) + s = "FoX_INVALID_ENTITY" + case(212) + s = "FoX_NO_DOCTYPE" + case(213) + s = "FoX_IMPL_IS_NULL" + case(214) + s = "FoX_MAP_IS_NULL" + case(215) + s = "FoX_LIST_IS_NULL" + case default + s = "INTERNAL ERROR!!!!" + end select + + end function errorString + + function getExceptionCode(ex) result(n) + type(DOMException), intent(inout) :: ex + integer :: n + + if (in_error(ex%stack)) then + n = ex%stack%stack(size(ex%stack%stack))%error_code + call destroy_error_stack(ex%stack) + else + n = 0 + endif + end function getExceptionCode + + subroutine throw_exception(code, msg, ex) + integer, intent(in) :: code + character(len=*), intent(in) :: msg + type(DOMException), intent(inout), optional :: ex + + if (present(ex)) then + call add_error(ex%stack, msg, error_code=code) ! FIXME + else + write(0,'(a)') errorString(code) + write(0,'(i0,a)') code, " "//msg + call pxfabort() + endif + + end subroutine throw_exception + + + subroutine dom_error(name,code,msg) + character(len=*), intent(in) :: name, msg + integer, intent(in) :: code + + write(0,'(4a)') "Routine ", name, ":", msg + write(0,'(a)') errorString(code) + call pxfabort() + + end subroutine dom_error + + + subroutine destroyDOMException(ex) + type(DOMException), intent(inout) :: ex + + call destroy_error_stack(ex%stack) + end subroutine destroyDOMException + + + subroutine internal_error(name,msg) + character(len=*), intent(in) :: name, msg + + write(0,'(4a)') "Internal error in ", name, ":", msg + call pxfabort() + + end subroutine internal_error + + function inException(ex) result(p) + type(DOMException), intent(in) :: ex + logical :: p + + p = in_error(ex%stack) + end function inException + +end module m_dom_error diff --git a/src/xml/dom/m_dom_extras.F90 b/src/xml/dom/m_dom_extras.F90 new file mode 100644 index 0000000000..f301622d5b --- /dev/null +++ b/src/xml/dom/m_dom_extras.F90 @@ -0,0 +1,2376 @@ +module m_dom_extras + + use fox_m_fsys_realtypes, only: sp, dp + use fox_m_fsys_parse_input, only: rts + + use m_dom_error, only: DOMException, inException, throw_exception, & + FoX_NODE_IS_NULL, FoX_INVALID_NODE + use m_dom_dom, only: Node, ELEMENT_NODE, & + getAttribute, getAttributeNS, getTextContent, getNodeType, getFoX_checks + + implicit none + private + + public :: extractDataContent + public :: extractDataAttribute + public :: extractDataAttributeNS + + interface extractDataContent + module procedure extractDataContentCmplxDpSca + module procedure extractDataContentCmplxDpArr + module procedure extractDataContentCmplxDpMat + module procedure extractDataContentCmplxSpSca + module procedure extractDataContentCmplxSpArr + module procedure extractDataContentCmplxSpMat + module procedure extractDataContentRealDpSca + module procedure extractDataContentRealDpArr + module procedure extractDataContentRealDpMat + module procedure extractDataContentRealSpSca + module procedure extractDataContentRealSpArr + module procedure extractDataContentRealSpMat + module procedure extractDataContentIntSca + module procedure extractDataContentIntArr + module procedure extractDataContentIntMat + module procedure extractDataContentLgSca + module procedure extractDataContentLgArr + module procedure extractDataContentLgMat + module procedure extractDataContentChSca + module procedure extractDataContentChArr + module procedure extractDataContentChMat + + end interface extractDataContent + + interface extractDataAttribute + module procedure extractDataAttributeCmplxDpSca + module procedure extractDataAttributeCmplxDpArr + module procedure extractDataAttributeCmplxDpMat + module procedure extractDataAttributeCmplxSpSca + module procedure extractDataAttributeCmplxSpArr + module procedure extractDataAttributeCmplxSpMat + module procedure extractDataAttributeRealDpSca + module procedure extractDataAttributeRealDpArr + module procedure extractDataAttributeRealDpMat + module procedure extractDataAttributeRealSpSca + module procedure extractDataAttributeRealSpArr + module procedure extractDataAttributeRealSpMat + module procedure extractDataAttributeIntSca + module procedure extractDataAttributeIntArr + module procedure extractDataAttributeIntMat + module procedure extractDataAttributeLgSca + module procedure extractDataAttributeLgArr + module procedure extractDataAttributeLgMat + module procedure extractDataAttributeChSca + module procedure extractDataAttributeChArr + module procedure extractDataAttributeChMat + + end interface extractDataAttribute + + interface extractDataAttributeNS + module procedure extractDataAttNSCmplxDpSca + module procedure extractDataAttNSCmplxDpArr + module procedure extractDataAttNSCmplxDpMat + module procedure extractDataAttNSCmplxSpSca + module procedure extractDataAttNSCmplxSpArr + module procedure extractDataAttNSCmplxSpMat + module procedure extractDataAttNSRealDpSca + module procedure extractDataAttNSRealDpArr + module procedure extractDataAttNSRealDpMat + module procedure extractDataAttNSRealSpSca + module procedure extractDataAttNSRealSpArr + module procedure extractDataAttNSRealSpMat + module procedure extractDataAttNSIntSca + module procedure extractDataAttNSIntArr + module procedure extractDataAttNSIntMat + module procedure extractDataAttNSLgSca + module procedure extractDataAttNSLgArr + module procedure extractDataAttNSLgMat + module procedure extractDataAttNSChSca + module procedure extractDataAttNSChArr + module procedure extractDataAttNSChMat + + end interface extractDataAttributeNS + +contains + +subroutine extractDataContentCmplxDpSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(dp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxDpSca + +subroutine extractDataContentCmplxSpSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(sp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxSpSca + +subroutine extractDataContentRealDpSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(dp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealDpSca + +subroutine extractDataContentRealSpSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(sp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealSpSca + +subroutine extractDataContentIntSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentIntSca + +subroutine extractDataContentLgSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical, intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentLgSca + +subroutine extractDataContentChSca(arg, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + if (present(ex)) then + call rts(getTextContent(arg, ex), data, separator, csv, num, iostat) + else + call rts(getTextContent(arg), data, separator, csv, num, iostat) + endif + + end subroutine extractDataContentChSca + + +subroutine extractDataContentCmplxDpArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(dp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxDpArr + +subroutine extractDataContentCmplxSpArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(sp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxSpArr + +subroutine extractDataContentRealDpArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(dp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealDpArr + +subroutine extractDataContentRealSpArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(sp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealSpArr + +subroutine extractDataContentIntArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentIntArr + +subroutine extractDataContentLgArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical, dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentLgArr + +subroutine extractDataContentChArr(arg, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), dimension(:), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + if (present(ex)) then + call rts(getTextContent(arg, ex), data, separator, csv, num, iostat) + else + call rts(getTextContent(arg), data, separator, csv, num, iostat) + endif + + end subroutine extractDataContentChArr + + +subroutine extractDataContentCmplxDpMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(dp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxDpMat + +subroutine extractDataContentCmplxSpMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(sp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxSpMat + +subroutine extractDataContentRealDpMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(dp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealDpMat + +subroutine extractDataContentRealSpMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(sp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealSpMat + +subroutine extractDataContentIntMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentIntMat + +subroutine extractDataContentLgMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical, dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentLgMat + +subroutine extractDataContentChMat(arg, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), dimension(:,:), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + if (present(ex)) then + call rts(getTextContent(arg, ex), data, separator, csv, num, iostat) + else + call rts(getTextContent(arg), data, separator, csv, num, iostat) + endif + + end subroutine extractDataContentChMat + + +subroutine extractDataAttributeCmplxDpSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(dp), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxDpSca + +subroutine extractDataAttributeCmplxSpSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(sp), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxSpSca + +subroutine extractDataAttributeRealDpSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(dp), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealDpSca + +subroutine extractDataAttributeRealSpSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(sp), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealSpSca + +subroutine extractDataAttributeIntSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + integer, intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeIntSca + +subroutine extractDataAttributeLgSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeLgSca + +subroutine extractDataAttributeChSca(arg, name, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + character(len=*), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, separator, csv, num, iostat) + else + call rts(getAttribute(arg, name), data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttributeChSca + + +subroutine extractDataAttributeCmplxDpArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(dp), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxDpArr + +subroutine extractDataAttributeCmplxSpArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(sp), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxSpArr + +subroutine extractDataAttributeRealDpArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(dp), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealDpArr + +subroutine extractDataAttributeRealSpArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(sp), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealSpArr + +subroutine extractDataAttributeIntArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + integer, dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeIntArr + +subroutine extractDataAttributeLgArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeLgArr + +subroutine extractDataAttributeChArr(arg, name, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + character(len=*), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, separator, csv, num, iostat) + else + call rts(getAttribute(arg, name), data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttributeChArr + + +subroutine extractDataAttributeCmplxDpMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(dp), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxDpMat + +subroutine extractDataAttributeCmplxSpMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(sp), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxSpMat + +subroutine extractDataAttributeRealDpMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(dp), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealDpMat + +subroutine extractDataAttributeRealSpMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(sp), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealSpMat + +subroutine extractDataAttributeIntMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + integer, dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeIntMat + +subroutine extractDataAttributeLgMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeLgMat + +subroutine extractDataAttributeChMat(arg, name, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + character(len=*), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, separator, csv, num, iostat) + else + call rts(getAttribute(arg, name), data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttributeChMat + + +subroutine extractDataAttNSCmplxDpSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(dp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxDpSca + +subroutine extractDataAttNSCmplxSpSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(sp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxSpSca + +subroutine extractDataAttNSRealDpSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(dp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealDpSca + +subroutine extractDataAttNSRealSpSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(sp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealSpSca + +subroutine extractDataAttNSIntSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + integer, intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSIntSca + +subroutine extractDataAttNSLgSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + logical, intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSLgSca + +subroutine extractDataAttNSChSca(arg, namespaceURI, localName, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + character(len=*), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, separator, csv, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttNSChSca + + +subroutine extractDataAttNSCmplxDpArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(dp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxDpArr + +subroutine extractDataAttNSCmplxSpArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(sp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxSpArr + +subroutine extractDataAttNSRealDpArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(dp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealDpArr + +subroutine extractDataAttNSRealSpArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(sp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealSpArr + +subroutine extractDataAttNSIntArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + integer, dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSIntArr + +subroutine extractDataAttNSLgArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + logical, dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSLgArr + +subroutine extractDataAttNSChArr(arg, namespaceURI, localName, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + character(len=*), dimension(:), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, separator, csv, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttNSChArr + + +subroutine extractDataAttNSCmplxDpMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(dp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxDpMat + +subroutine extractDataAttNSCmplxSpMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(sp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxSpMat + +subroutine extractDataAttNSRealDpMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(dp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealDpMat + +subroutine extractDataAttNSRealSpMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(sp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealSpMat + +subroutine extractDataAttNSIntMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + integer, dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSIntMat + +subroutine extractDataAttNSLgMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + logical, dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSLgMat + +subroutine extractDataAttNSChMat(arg, namespaceURI, localName, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + character(len=*), dimension(:,:), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, separator, csv, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttNSChMat + + + +end module m_dom_extras diff --git a/src/xml/dom/m_dom_parse.F90 b/src/xml/dom/m_dom_parse.F90 new file mode 100644 index 0000000000..72e683732e --- /dev/null +++ b/src/xml/dom/m_dom_parse.F90 @@ -0,0 +1,577 @@ +module m_dom_parse + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc + use fox_m_utils_uri, only: URI, parseURI, rebaseURI, expressURI, destroyURI + use m_common_attrs, only: hasKey, getValue, getIndex, getIsId, getBase, & + add_item_to_dict + use m_common_entities, only: entity_t, size, getEntityByIndex + use m_common_error, only: FoX_error, in_error + use m_common_struct, only: xml_doc_state + use FoX_common, only: dictionary_t, getLength + use FoX_common, only: getQName, getValue, getURI, isSpecified + use m_sax_parser, only: sax_parse + use FoX_sax, only: xml_t + use FoX_sax, only: open_xml_file, open_xml_string, close_xml_t + + ! Public interfaces + use m_dom_dom, only: DOMConfiguration, Node, NamedNodeMap, & + TEXT_NODE, & + getAttributes, getData, getDocType, getEntities, getImplementation, & + getLastChild, getNodeType, & + getNotations, getParameter, getParentNode, getXmlVersion, & + setAttribute, setAttributeNS, setData, setValue, & + appendChild, createAttribute, createAttributeNS, createCdataSection, & + createComment, createDocumentType, createElement, createElementNS, & + createEntityReference, createProcessingInstruction, createTextNode, & + getNamedItem, setAttributeNode, setAttributeNodeNS, setNamedItem, & + getFoX_checks + + ! Private interfaces + use m_dom_dom, only: copyDOMConfig, createEmptyDocument, setDocumentElement, & + createEmptyEntityReference, createEntity, createNotation, & + getReadOnly, getStringValue, getXds, destroy, destroyAllNodesRecursively, & + namespaceFixup, setDocType, setDomConfig, setGCstate, setIllFormed, & + setIsElementContentWhitespace, setIsId, setReadOnlyMap, setReadonlyNode, & + setSpecified, setXds, setStringValue + + use m_dom_error, only: DOMException, inException, throw_exception, & + getExceptionCode, PARSE_ERR + + implicit none + private + + public :: parsefile + public :: parsestring + + type(xml_t), target, save :: fxml + + type(Node), pointer, save :: mainDoc => null() + type(Node), pointer, save :: current => null() + + type(DOMConfiguration), pointer :: domConfig + + logical :: cdata + character, pointer :: error(:) => null() + character, pointer :: inEntity(:) => null() + +contains + + subroutine startElement_handler(nsURI, localname, name, attrs) + character(len=*), intent(in) :: nsURI + character(len=*), intent(in) :: localname + character(len=*), intent(in) :: name + type(dictionary_t), intent(in) :: attrs + + type(URI), pointer :: URIref, URIbase, newURI + type(Node), pointer :: el, attr, dummy + character, pointer :: baseURI(:) + integer :: i + + if (getParameter(domConfig, "namespaces")) then + el => createElementNS(mainDoc, nsURI, name) + else + el => createElement(mainDoc, name) + endif + + if (getBase(attrs)/="") then + i = getIndex(attrs, "xml:base") + if (i>0) then + URIbase => parseURI(getBase(attrs)) + URIref => parseURI(getValue(attrs, i)) + newURI => rebaseURI(URIbase, URIref) + call destroyURI(URIbase) + call destroyURI(URIref) + baseURI => vs_str_alloc(expressURI(newURI)) + call destroyURI(newURI) + else + baseURI => vs_str_alloc(getBase(attrs)) + endif + if (getParameter(domConfig, "namespaces")) then + attr => createAttributeNS(mainDoc, & + "http://www.w3.org/XML/1998/namespace", "xml:base") + else + attr => createAttribute(mainDoc, "xml:base") + endif + call setValue(attr, str_vs(baseURI)) + deallocate(baseURI) + if (i>0) then + call setSpecified(attr, isSpecified(attrs, i)) + call setIsId(attr, getIsId(attrs, i)) + endif + if (getParameter(domConfig, "namespaces")) then + dummy => setAttributeNodeNS(el, attr) + else + dummy => setAttributeNode(el, attr) + endif + endif + + do i = 1, getLength(attrs) + if (getQName(attrs, i)=="xml:base") cycle + if (getParameter(domConfig, "namespaces")) then + attr => createAttributeNS(mainDoc, getURI(attrs, i), getQName(attrs, i)) + else + attr => createAttribute(mainDoc, getQName(attrs, i)) + endif + call setValue(attr, getValue(attrs, i)) + call setSpecified(attr, isSpecified(attrs, i)) + call setIsId(attr, getIsId(attrs, i)) + if (getParameter(domConfig, "namespaces")) then + dummy => setAttributeNodeNS(el, attr) + else + dummy => setAttributeNode(el, attr) + endif + if (associated(inEntity)) call setReadOnlyNode(attr, .true., .true.) + enddo + + if (associated(current, mainDoc)) then + current => appendChild(current,el) + call setDocumentElement(mainDoc, current) + else + current => appendChild(current,el) + endif + if (getParameter(domConfig, "namespaces")) & + call namespaceFixup(current, .false.) + + if (associated(inEntity)) & + call setReadOnlyMap(getAttributes(current), .true.) + + cdata = .false. + + end subroutine startElement_handler + + subroutine endElement_handler(URI, localName, name) + character(len=*), intent(in) :: URI + character(len=*), intent(in) :: localname + character(len=*), intent(in) :: name + + if (associated(inEntity)) call setReadOnlyNode(current, .true., .false.) + + current => getParentNode(current) + end subroutine endElement_handler + + ! FIXME to pick up entity references within attribute values, we need + ! separate just_the_element, start_attribute, attribute_text etc. calls. + + subroutine characters_handler(chunk) + character(len=*), intent(in) :: chunk + + type(Node), pointer :: temp + logical :: readonly + + temp => getLastChild(current) + if (associated(temp)) then + if (.not.cdata.and.getNodeType(temp)==TEXT_NODE) then + readonly = getReadOnly(temp) ! Reset readonly status quickly + call setReadOnlyNode(temp, .false., .false.) + call setData(temp, getData(temp)//chunk) + call setReadOnlyNode(temp, readonly, .false.) + return + endif + endif + if (cdata) then + temp => createCdataSection(mainDoc, chunk) + temp => appendChild(current, temp) + else + temp => createTextNode(mainDoc, chunk) + temp => appendChild(current, temp) + endif + + if (associated(inEntity)) call setReadOnlyNode(temp, .true., .false.) + + end subroutine characters_handler + + subroutine ignorableWhitespace_handler(chunk) + character(len=*), intent(in) :: chunk + + type(Node), pointer :: temp + logical :: readonly + + if (getParameter(domConfig, "element-content-whitespace")) then + temp => getLastChild(current) + if (associated(temp)) then + if (getNodeType(temp)==TEXT_NODE) then + readonly = getReadOnly(temp) ! Reset readonly status quickly + call setReadOnlyNode(temp, .false., .false.) + call setData(temp, getData(temp)//chunk) + call setReadOnlyNode(temp, readonly, .false.) + call setIsElementContentWhitespace(temp, .true.) + return + endif + endif + temp => createTextNode(mainDoc, chunk) + temp => appendChild(current, temp) + call setIsElementContentWhitespace(temp, .true.) + if (associated(inEntity)) call setReadOnlyNode(temp, .true., .false.) + endif + + end subroutine ignorableWhitespace_handler + + subroutine comment_handler(comment) + character(len=*), intent(in) :: comment + + type(Node), pointer :: temp + + if (getParameter(domConfig, "comments")) then + temp => appendChild(current, createComment(mainDoc, comment)) + if (associated(inEntity)) call setReadOnlyNode(temp, .true., .false.) + endif + + end subroutine comment_handler + + subroutine processingInstruction_handler(target, data) + character(len=*), intent(in) :: target + character(len=*), intent(in) :: data + + type(Node), pointer :: temp + + temp => appendChild(current, & + createProcessingInstruction(mainDoc, target, data)) + + if (associated(inEntity)) call setReadOnlyNode(temp, .true., .false.) + end subroutine processingInstruction_handler + + subroutine startDocument_handler + mainDoc => createEmptyDocument() + current => mainDoc + call setGCstate(mainDoc, .false.) + call setDomConfig(mainDoc, domConfig) + end subroutine startDocument_handler + + subroutine endDocument_Handler + call setGCstate(mainDoc, .true.) + end subroutine endDocument_Handler + + subroutine startDTD_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + + type(Node), pointer :: np + + np => createDocumentType(getImplementation(mainDoc), name, publicId=publicId, systemId=systemId) + np => appendChild(mainDoc, np) + call setDocType(mainDoc, np) + + end subroutine startDTD_handler + + subroutine endDTD_handler + + type(Node), pointer :: np, oldcurrent + type(NamedNodeMap), pointer :: entities + type(xml_t) :: subsax + type(xml_doc_state), pointer :: xds + type(entity_t), pointer :: ent + integer :: i, ios + logical :: ok + + entities => getEntities(getDocType(mainDoc)) + xds => getXds(mainDoc) + + do i = 1, size(xds%entityList) + ent => getEntityByIndex(xds%entityList, i) + np => getNamedItem(entities, str_vs(ent%name)) + + ok = .false. + if (ent%external) then + if (size(ent%notation)==0) then + call open_xml_file(subsax, expressURI(ent%baseURI), iostat=ios) + if (ios/=0) then + call setIllFormed(np, .true.) + else + ok = .true. + endif + endif + else + call open_xml_string(subsax, getStringValue(np)) + ok = .true. + endif + if (ok) then + oldcurrent => current + current => np + ! Run the parser over value + ! We do this with all entities already declared. + call sax_parse(subsax%fx, subsax%fb, & + startElement_handler=startElement_handler, & + endElement_handler=endElement_handler, & + characters_handler=characters_handler, & + startCdata_handler=startCdata_handler, & + endCdata_handler=endCdata_handler, & + comment_handler=comment_handler, & + processingInstruction_handler=processingInstruction_handler, & + fatalError_handler=entityErrorHandler, & + startInCharData = .true., & + externalEntity = ent%external, & + xmlVersion = getXmlVersion(mainDoc), & + namespaces=getParameter(domConfig, "namespaces"), & + initial_entities = xds%entityList) + call close_xml_t(subsax) + + current => oldcurrent + endif + enddo + + if (associated(getDocType(mainDoc))) then + call setReadonlyMap(getEntities(getDocType(mainDoc)), .true.) + call setReadonlyMap(getNotations(getDocType(mainDoc)), .true.) + endif + + end subroutine endDTD_handler + + subroutine FoX_endDTD_handler(state) + type(xml_doc_state), pointer :: state + + call setXds(mainDoc, state) + + end subroutine FoX_endDTD_handler + + subroutine notationDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + + type(Node), pointer :: np + + np => createNotation(mainDoc, name, publicId=publicId, systemId=systemId) + np => setNamedItem(getNotations(getDocType(mainDoc)), np) + ! The SAX parser will never give us duplicate entities, + ! so there is no need to check + + end subroutine notationDecl_handler + + subroutine startCdata_handler() + if (getParameter(domConfig, "cdata-sections")) cdata = .true. + end subroutine startCdata_handler + subroutine endCdata_handler() + cdata = .false. + end subroutine endCdata_handler + + subroutine internalEntityDecl_handler(name, value) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + + type(Node), pointer :: np + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + ! We only note that these exist here. + ! A second parsing stage is triggered at the end + ! of the DTD, in order to resolve entity references (which + ! need not be declared in order) + + np => createEntity(mainDoc, name, "", "", "") + call setStringValue(np, value) + np => setNamedItem(getEntities(getDocType(mainDoc)), np) + + end subroutine internalEntityDecl_handler + + subroutine normalErrorHandler(msg) + character(len=*), intent(in) :: msg + ! This is called if the main parsing routine fails + error => vs_str_alloc(msg) + end subroutine normalErrorHandler + + subroutine entityErrorHandler(msg) + character(len=*), intent(in) :: msg + + !This gets called if parsing of an entity failed. If so, + !then we need to destroy all nodes which were being generated as + !children of this entity, then mark the entity as ill-formed - but + !otherwise carry on parsing the document, and only throw an error + !if a reference is made to it. + + call destroyAllNodesRecursively(current, except=.true.) + call setIllFormed(current, .true.) + end subroutine entityErrorHandler + + subroutine externalEntityDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + type(Node), pointer :: np + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + np => createEntity(mainDoc, name, & + publicId=publicId, systemId=systemId, notationName="") + np => setNamedItem(getEntities(getDocType(mainDoc)), np) + + end subroutine externalEntityDecl_handler + + subroutine unparsedEntityDecl_handler(name, publicId, systemId, notationName) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + character(len=*), intent(in) :: notationName + type(Node), pointer :: np + + np => getNamedItem(getEntities(getDocType(mainDoc)), name) + if (.not.associated(np)) then + np => createEntity(mainDoc, name, publicId=publicId, systemId=systemId, notationName=notationName) + np => setNamedItem(getEntities(getDocType(mainDoc)), np) + endif + + end subroutine unparsedEntityDecl_handler + + subroutine startEntity_handler(name) + character(len=*), intent(in) :: name + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + if (getParameter(domConfig, "entities")) then + if (.not.associated(inEntity)) then + inEntity => vs_str_alloc(name) + endif + current => appendChild(current, createEmptyEntityReference(mainDoc, name)) + endif + end subroutine startEntity_handler + + subroutine endEntity_handler(name) + character(len=*), intent(in) :: name + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + if (getParameter(domConfig, "entities")) then + call setReadOnlyNode(current, .true., .false.) + if (str_vs(inEntity)==name) deallocate(inEntity) + current => getParentNode(current) + endif + + end subroutine endEntity_handler + + subroutine skippedEntity_handler(name) + character(len=*), intent(in) :: name + + type(Node), pointer :: temp + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + temp => appendChild(current, createEntityReference(mainDoc, name)) + if (associated(inEntity)) call setReadonlyNode(temp, .true., .false.) + end subroutine skippedEntity_handler + + + subroutine runParser(fxml, configuration, ex) + type(DOMException), intent(out), optional :: ex + type(xml_t), intent(inout) :: fxml + type(DOMConfiguration), pointer, optional :: configuration + + allocate(DOMConfig) + if (present(configuration)) call copyDOMConfig(DOMConfig, configuration) + +! We use internal sax_parse rather than public interface in order +! to use internal callbacks to get extra info. + call sax_parse(fx=fxml%fx, fb=fxml%fb,& + characters_handler=characters_handler, & + endDocument_handler=endDocument_handler, & + endElement_handler=endElement_handler, & + !endPrefixMapping_handler, & + ignorableWhitespace_handler=ignorableWhitespace_handler, & + processingInstruction_handler=processingInstruction_handler, & + ! setDocumentLocator + skippedEntity_handler=skippedEntity_handler, & + startDocument_handler=startDocument_handler, & + startElement_handler=startElement_handler, & + !startPrefixMapping_handler, & + notationDecl_handler=notationDecl_handler, & + unparsedEntityDecl_handler=unparsedEntityDecl_handler, & + !error_handler, & + fatalError_handler=normalErrorHandler, & + !warning_handler, & + !attributeDecl_handler, & + !elementDecl_handler, & + externalEntityDecl_handler=externalEntityDecl_handler, & + internalEntityDecl_handler=internalEntityDecl_handler, & + comment_handler=comment_handler, & + endCdata_handler=endCdata_handler, & + endDTD_handler=endDTD_handler, & + endEntity_handler=endEntity_handler, & + startCdata_handler=startCdata_handler, & + startDTD_handler=startDTD_handler, & + startEntity_handler=startEntity_handler, & + FoX_endDTD_handler=FoX_endDTD_handler, & + namespaces = getParameter(domConfig, "namespaces"), & + namespace_prefixes = .true., & + validate = getParameter(domConfig, "validate"), & ! FIXME what about validate-if-present ... + xmlns_uris = .true.) + + call close_xml_t(fxml) + + if (associated(error)) then + if (associated(inEntity)) deallocate(inEntity) + ! FIXME pass the value of the error through + ! when we let exceptions do that + deallocate(error) + call destroy(mainDoc) + if (getFoX_checks().or.PARSE_ERR<200) then + call throw_exception(PARSE_ERR, "runParser", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end subroutine runParser + + + function parsefile(filename, configuration, iostat, ex) + type(DOMException), intent(out), optional :: ex + character(len=*), intent(in) :: filename + type(DOMConfiguration), pointer, optional :: configuration + integer, intent(out), optional :: iostat + type(Node), pointer :: parsefile + + type(DOMException) :: ex_ + integer :: iostat_ + + call open_xml_file(fxml, filename, iostat_) + if (present(iostat)) then + iostat = iostat_ + if (iostat/=0) return + elseif (in_error(fxml%fx%error_stack)) then + call FoX_error(str_vs(fxml%fx%error_stack%stack(1)%msg)) + elseif (iostat_/=0) then + call FoX_error("Cannot open file") + endif + + if (present(ex)) then + call runParser(fxml, configuration, ex) + elseif (present(iostat)) then + call runParser(fxml, configuration, ex_) + else + call runParser(fxml, configuration) + endif + + if (present(iostat).and.inException(ex_)) then + iostat = getExceptionCode(ex_) + endif + + parsefile => mainDoc + mainDoc => null() + + end function parsefile + + + function parsestring(string, configuration, ex) + type(DOMException), intent(out), optional :: ex + character(len=*), intent(in) :: string + type(DOMConfiguration), pointer, optional :: configuration + type(Node), pointer :: parsestring + + call open_xml_string(fxml, string) + + call runParser(fxml, configuration, ex) + + parsestring => mainDoc + mainDoc => null() + + end function parsestring + +end module m_dom_parse diff --git a/src/xml/dom/m_dom_utils.F90 b/src/xml/dom/m_dom_utils.F90 new file mode 100644 index 0000000000..f0e443f21d --- /dev/null +++ b/src/xml/dom/m_dom_utils.F90 @@ -0,0 +1,433 @@ +module m_dom_utils + + use fox_m_fsys_array_str, only: str_vs, vs_str + use fox_m_fsys_format, only: operator(//) + use m_common_attrs, only: getValue + use m_common_element, only: element_t, attribute_t, & + get_attlist_size, get_attribute_declaration, express_attribute_declaration + use m_common_struct, only: xml_doc_state + + ! Public interfaces + use m_dom_dom, only: DOMConfiguration, NamedNodeMap, Node, & + NodeList, & + ATTRIBUTE_NODE, CDATA_SECTION_NODE, COMMENT_NODE, DOCUMENT_NODE, & + DOCUMENT_TYPE_NODE, ELEMENT_NODE, ENTITY_REFERENCE_NODE, & + PROCESSING_INSTRUCTION_NODE, TEXT_NODE, & + getAttributes, getChildNodes, getData, getDomConfig, getEntities, & + getFirstChild, getFoX_checks, getLength, getLocalName, getName, & + getNamespaceURI, getNextSibling, getNodeName, getNodeType, getNotationName,& + getNotations, getOwnerDocument, getOwnerElement, getParameter, & + getParentNode, getPrefix, getPublicId, getSpecified, getSystemId, & + getTagName, getTarget, getXmlStandalone, getXmlVersion, getValue, & + haschildnodes, item, normalizeDocument + + ! Private interfaces + use m_dom_dom, only: getNamespaceNodes, getStringValue, getXds, namespaceFixup + + use m_dom_error, only: DOMException, inException, throw_exception, & + getExceptionCode, & + NAMESPACE_ERR, SERIALIZE_ERR, FoX_INTERNAL_ERROR, FoX_INVALID_NODE + + use FoX_wxml, only: xmlf_t, & + xml_AddAttribute, xml_AddCharacters, xml_AddComment, xml_AddElementToDTD, & + xml_AddEntityReference, xml_AddExternalEntity, xml_AddInternalEntity, & + xml_AddDOCTYPE, xml_AddNotation, xml_AddXMLDeclaration, xml_AddXMLPI, & + xml_EndElement, xml_Close, xml_DeclareNamespace, xml_NewElement, & + xml_OpenFile, xml_UndeclareNamespace, xml_AddAttlistToDTD + + implicit none + + public :: dumpTree + public :: serialize + + private + +contains + + subroutine dumpTree(startNode) + + type(Node), pointer :: startNode + + integer :: indent_level + + indent_level = 0 + + call dump2(startNode) + + contains + + recursive subroutine dump2(input) + type(Node), pointer :: input + type(Node), pointer :: temp, np + type(NamedNodeMap), pointer :: attrs + type(NodeList), pointer :: nsnodes + integer :: i + temp => input + do while(associated(temp)) + write(*,"(3a,i0)") repeat(" ", indent_level), & + getNodeName(temp), " of type ", & + getNodeType(temp) + if (getNodeType(temp)==ELEMENT_NODE) then + write(*,"(2a)") repeat(" ", indent_level), & + " ATTRIBUTES:" + attrs => getAttributes(temp) + do i = 0, getLength(attrs) - 1 + np => item(attrs, i) + write(*, "(2a)") repeat(" ", indent_level)//" ", & + getName(np) + enddo + write(*,"(2a)") repeat(" ", indent_level), & + " IN-SCOPE NAMESPACES:" + nsnodes => getNamespaceNodes(temp) + do i = 0, getLength(nsnodes) - 1 + np => item(nsnodes, i) + write(*,"(4a)") repeat(" ", indent_level)//" ", & + getPrefix(np), ':', & + getNamespaceURI(np) + enddo + endif + if (hasChildNodes(temp)) then + indent_level = indent_level + 3 + call dump2(getFirstChild(temp)) + indent_level = indent_level - 3 + endif + temp => getNextSibling(temp) + enddo + + end subroutine dump2 + + end subroutine dumpTree + + + subroutine serialize(startNode, name, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: startNode + character(len=*), intent(in) :: name + + type(Node), pointer :: doc + type(xmlf_t) :: xf + integer :: iostat + logical :: xmlDecl + + if (getNodeType(startNode)/=DOCUMENT_NODE & + .and.getNodeType(startNode)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "serialize", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (getNodeType(startNode)==DOCUMENT_NODE) then + doc => startNode + if (getParameter(getDomConfig(doc), "canonical-form") & + .and.getXmlVersion(doc)=="1.1") then + if (getFoX_checks().or.SERIALIZE_ERR<200) then + call throw_exception(SERIALIZE_ERR, "serialize", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call normalizeDocument(startNode, ex) + if (present(ex)) then + ! Only possible error should be namespace error ... + if (getExceptionCode(ex)/=NAMESPACE_ERR) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "serialize", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + else + if (getFoX_checks().or.SERIALIZE_ERR<200) then + call throw_exception(SERIALIZE_ERR, "serialize", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + else + doc => getOwnerDocument(startNode) + ! We need to do this namespace fixup or serialization will fail. + ! it doesn't change the semantics of the docs, but other + ! normalization would, so we done here + ! But only normalize if this is not a DOM level 1 node. + if (getLocalName(startNode)/="" & + .and.getParameter(getDomConfig(doc), "namespaces")) & + call namespaceFixup(startNode, .true.) + endif + xmlDecl = getParameter(getDomConfig(doc), "xml-declaration") + + ! FIXME we shouldnt really normalize the Document here + ! (except for namespace Normalization) but rather just + ! pay attention to the DOMConfig values + + ! NOTE: We set pretty_print on the basis of the FoX specific + ! "invalid-pretty-print" config option. The DOM-L3-LS + ! option "format-pretty-print is always false and is + ! not settable by the user - this is because WXML + ! cannot preserve validity conditions that may be set + ! by a DTD. If WXML ever learns to do this we will need + ! to pass the value of "format-pretty-print" through. + call xml_OpenFile(name, xf, iostat=iostat, unit=-1, & + pretty_print=getParameter(getDomConfig(doc), "invalid-pretty-print"), & + canonical=getParameter(getDomConfig(doc), "canonical-form"), & + warning=.false., addDecl=.false.) + if (iostat/=0) then + if (getFoX_checks().or.SERIALIZE_ERR<200) then + call throw_exception(SERIALIZE_ERR, "serialize", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (xmlDecl) then + if (getXmlStandalone(doc)) then + call xml_AddXMLDeclaration(xf, version=getXmlVersion(doc), standalone=.true.) + else + call xml_AddXMLDeclaration(xf, version=getXmlVersion(doc)) + endif + endif + + call iter_dmp_xml(xf, startNode, ex) + call xml_Close(xf) + + end subroutine serialize + + subroutine iter_dmp_xml(xf, arg, ex) + type(DOMException), intent(out), optional :: ex + type(xmlf_t), intent(inout) :: xf + + type(Node), pointer :: this, arg, treeroot + type(Node), pointer :: doc, attrchild, np + type(NamedNodeMap), pointer :: nnm + type(DOMConfiguration), pointer :: dc + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att_decl + integer :: i_tree, j, k + logical :: doneChildren, doneAttributes + character, pointer :: attrvalue(:), tmp(:) + + if (getNodeType(arg)==DOCUMENT_NODE) then + doc => arg + else + doc => getOwnerDocument(arg) + endif + dc => getDomConfig(doc) + xds => getXds(doc) + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + select case(getNodeType(this)) + case (ELEMENT_NODE) + nnm => getAttributes(this) + do j = 0, getLength(nnm) - 1 + attrchild => item(nnm, j) + if (getLocalName(attrchild)=="xmlns") then + if (len(getValue(attrchild))==0) then + call xml_UndeclareNamespace(xf) + else + call xml_DeclareNamespace(xf, getValue(attrchild)) + endif + elseif (getPrefix(attrchild)=="xmlns") then + if (len(getValue(attrchild))==0) then + call xml_UndeclareNamespace(xf, getLocalName(attrchild)) + else + call xml_DeclareNamespace(xf, getValue(attrchild), & + getLocalName(attrchild)) + endif + endif + enddo + call xml_NewElement(xf, getTagName(this)) + case (ATTRIBUTE_NODE) + if ((.not.getParameter(dc, "discard-default-content") & + .or.getSpecified(this)) & + ! only output it if it is not a default, or we are outputting defaults + .and. (getPrefix(this)/="xmlns".and.getLocalName(this)/="xmlns")) then + ! and we dont output NS declarations here. + ! complex loop below is because we might have to worry about entrefs + ! being preserved in the attvalue. If we dont, we only go through the loop once anyway. + allocate(attrvalue(0)) + do j = 0, getLength(getChildNodes(this)) - 1 + attrchild => item(getChildNodes(this), j) + if (getNodeType(attrchild)==TEXT_NODE) then + tmp => attrvalue + allocate(attrvalue(size(tmp)+getLength(attrchild))) + attrvalue(:size(tmp)) = tmp + attrvalue(size(tmp)+1:) = vs_str(getData(attrChild)) + deallocate(tmp) + elseif (getNodeType(attrchild)==ENTITY_REFERENCE_NODE) then + tmp => attrvalue + allocate(attrvalue(size(tmp)+len(getNodeName(attrchild))+2)) + attrvalue(:size(tmp)) = tmp + attrvalue(size(tmp)+1:) = vs_str("&"//getData(attrChild)//";") + deallocate(tmp) + else + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "iter_dmp_xml", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + enddo + call xml_AddAttribute(xf, getName(this), str_vs(attrvalue)) + deallocate(attrvalue) + endif + doneChildren = .true. + case (TEXT_NODE) + call xml_AddCharacters(xf, getData(this)) + case (CDATA_SECTION_NODE) + if (getParameter(getDomConfig(doc), "canonical-form")) then + call xml_AddCharacters(xf, getData(this)) + else + call xml_AddCharacters(xf, getData(this), parsed = .false.) + endif + case (ENTITY_REFERENCE_NODE) + if (.not.getParameter(getDomConfig(doc), "canonical-form")) then + call xml_AddEntityReference(xf, getNodeName(this)) + doneChildren = .true. + endif + case (PROCESSING_INSTRUCTION_NODE) + call xml_AddXMLPI(xf, getTarget(this), getData(this)) + case (COMMENT_NODE) + if (.not.getParameter(getDomConfig(doc), "comments")) then + call xml_AddComment(xf, getData(this)) + endif + case (DOCUMENT_TYPE_NODE) + if (.not.getParameter(getDomConfig(doc), "canonical-form")) then + call xml_AddDOCTYPE(xf, getName(this)) + nnm => getNotations(this) + do j = 0, getLength(nnm)-1 + np => item(nnm, j) + if (getSystemId(np)=="") then + call xml_AddNotation(xf, getNodeName(np), public=getPublicId(np)) + elseif (getPublicId(np)=="") then + call xml_AddNotation(xf, getNodeName(np), system=getSystemId(np)) + else + call xml_AddNotation(xf, getNodeName(np), system=getSystemId(np), & + public=getPublicId(np)) + endif + enddo + nnm => getEntities(this) + do j = 0, getLength(nnm)-1 + np => item(nnm, j) + if (getSystemId(np)=="") then + call xml_AddInternalEntity(xf, getNodeName(np), getStringValue(np)) + elseif (getPublicId(np)=="".and.getNotationName(np)=="") then + call xml_AddExternalEntity(xf, getNodeName(np), system=getSystemId(np)) + elseif (getNotationName(np)=="") then + call xml_AddExternalEntity(xf, getNodeName(np), system=getSystemId(np), & + public=getPublicId(np)) + elseif (getPublicId(np)=="") then + call xml_AddExternalEntity(xf, getNodeName(np), system=getSystemId(np), & + notation=getNotationName(np)) + else + call xml_AddExternalEntity(xf, getNodeName(np), system=getSystemId(np), & + public=getPublicId(np), notation=getNotationName(np)) + endif + enddo + do j = 1, size(xds%element_list%list) + elem => xds%element_list%list(j) + if (associated(elem%model)) & + call xml_AddElementToDTD(xf, str_vs(elem%name), str_vs(elem%model)) + ! Because we may have some undeclared but referenced elements + do k = 1, get_attlist_size(elem) + att_decl => get_attribute_declaration(elem, k) + call xml_AddAttlistToDTD(xf, str_vs(elem%name), & + express_attribute_declaration(att_decl)) + enddo + enddo + endif + end select + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + if (getNodeType(this)==ELEMENT_NODE) then + call xml_EndElement(xf, getTagName(this)) + endif + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + this => getFirstChild(this) + doneChildren = .false. + doneAttributes = .false. + else + doneChildren = .true. + doneAttributes = .false. + endif + + else ! if doneChildren + + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + endif + endif + + enddo + + + + end subroutine iter_dmp_xml + +end module m_dom_utils diff --git a/src/xml/dom/makefile b/src/xml/dom/makefile new file mode 100644 index 0000000000..98286bc783 --- /dev/null +++ b/src/xml/dom/makefile @@ -0,0 +1,48 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils -I../sax -I../wxml $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_dom.o: m_dom_dom.o m_dom_error.o m_dom_extras.o m_dom_parse.o m_dom_utils.o +m_dom_dom.o: m_dom_error.o +m_dom_extras.o: m_dom_dom.o m_dom_error.o +m_dom_parse.o: m_dom_dom.o m_dom_error.o +m_dom_utils.o: m_dom_dom.o m_dom_error.o diff --git a/src/xml/fsys/fox_m_fsys_abort_flush.F90 b/src/xml/fsys/fox_m_fsys_abort_flush.F90 new file mode 100644 index 0000000000..612523bb9d --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_abort_flush.F90 @@ -0,0 +1,119 @@ +module fox_m_fsys_abort_flush + + implicit none + + public :: pxfflush + public :: pxfabort + public :: pure_pxfabort + + ! pxf.F90 - assortment of Fortran wrappers to various + ! unix-y system calls. + ! + ! Copyright Toby White, 2005 + + ! The name pxf is intended to be reminiscent of the POSIX + ! fortran interfaces defined by IEEE 1003.9-1992, although + ! in fact I don't think that either flush or abort were + ! covered by said standard. + + ! Of the preprocessor defines used here, only xlC is + ! automatically defined by the appropriate compiler. All + ! others must be defined by some other mechanism - I + ! recommend autoconf. + + + ! FLUSH: flushes buffered output on a given unit. Not guaranteed + ! to do anything at all (particularly under MPI when even FLUSHed + ! buffers may not make it to the host cpu after an abort. + ! + ! IMPLEMENTATION: in F2003, this is a native operation called by the + ! FLUSH statement. + ! In almost every compiler, there is a FLUSH intrinsic subroutine + ! available which takes one argument, the unit to be flushed. + ! (some will flush all open units when no argument is given - this + ! facility is not used here. + ! NAG complicates matters by having to USE a module to get at flush. + ! + ! If no FLUSH is available, the subroutine is a no-op. + + +CONTAINS + + subroutine pxfflush(unit) +#ifdef __NAG__ + use f90_unix_io, only : flush +#endif + integer, intent(in) :: unit + +#if defined(F2003) + flush(unit) +#elif defined(xlC) + call flush_(unit) +#elif defined (FC_HAVE_FLUSH) + call flush(unit) +#else + continue +#endif + + end subroutine pxfflush + + ! ABORT: terminates program execution in such a way that a backtrace + ! is produced. (see abort() in the C Standard Library). No arguments. + ! + ! IMPLEMENTATION: In F2003, the C interoperability features mean that + ! the abort in stdlib.h is available to be linked against. + ! In several other compilers an ABORT intrinsic subroutine is available. + ! Again, NAG complicates matters by needing to USE a module. + ! + ! In the case where no native ABORT can be found, we emulate one + ! by dereferencing a null pointer. This has reliably produced coredumps + ! on every platform I've tried it with. Just in case it doesn't (it need + ! not even stop execution) a stop is given to force termination. + + subroutine pxfabort() +#ifdef __NAG__ + use f90_unix_proc, only : abort +#endif +#ifdef F2003 + interface + subroutine abort() bind(c) + end subroutine abort + end interface +#define FC_HAVE_ABORT +#endif +#ifndef FC_HAVE_ABORT + integer, pointer :: i +#endif + + call pxfflush(6) + +#ifdef FC_HAVE_ABORT +#ifdef FC_ABORT_UNDERSCORE + call abort_() +#elif defined(FC_ABORT_ARG) + call abort("") +#else + call abort() +#endif +#else + i=>null() + Print*,i +#endif + stop + + end subroutine pxfabort + + ! For where we need a pxfabort that is pure, we have + ! this below. I am less sure of its working everywhe + ! also it must be used as a function not a subroutine + ! (otherwise it would be optimized away as side-effect + ! free + + pure function pure_pxfabort() result (crash) + integer :: crash + integer, pointer :: i + nullify(i) + crash = i + end function pure_pxfabort + +end module fox_m_fsys_abort_flush diff --git a/src/xml/fsys/fox_m_fsys_array_str.F90 b/src/xml/fsys/fox_m_fsys_array_str.F90 new file mode 100644 index 0000000000..b49554832e --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_array_str.F90 @@ -0,0 +1,90 @@ +module fox_m_fsys_array_str +#ifndef DUMMYLIB + + implicit none + private + + interface destroy + module procedure destroy_vs + end interface + + interface concat + module procedure vs_s_concat + end interface + + interface alloc + module procedure vs_str_alloc + end interface + + public :: str_vs + public :: vs_str + public :: vs_str_alloc + public :: vs_vs_alloc + + public :: concat + public :: alloc + + public :: destroy + +contains + + pure function str_vs(vs) result(s) + character, dimension(:), intent(in) :: vs + character(len=size(vs)) :: s +#ifdef PGF90 +!PGI crashes on this use of transfer. Knob-ends. + integer :: i + do i = 1, size(vs) + s(i:i) = vs(i) + enddo +#else + s = transfer(vs, s) +#endif + end function str_vs + + + pure function vs_str(s) result(vs) + character(len=*), intent(in) :: s + character, dimension(len(s)) :: vs + + vs = transfer(s, vs) + end function vs_str + + pure function vs_str_alloc(s) result(vs) + character(len=*), intent(in) :: s + character, dimension(:), pointer :: vs + + allocate(vs(len(s))) + vs = vs_str(s) + end function vs_str_alloc + + pure function vs_vs_alloc(s) result(vs) + character, dimension(:), pointer :: s + character, dimension(:), pointer :: vs + + if (associated(s)) then + allocate(vs(size(s))) + vs = s + else + vs => null() + endif + end function vs_vs_alloc + + pure function vs_s_concat(vs, s) result(vs2) + character, dimension(:), intent(in) :: vs + character(len=*), intent(in) :: s + character, dimension(:), pointer :: vs2 + + allocate(vs2(size(vs)+len(s))) + vs2(:size(vs)) = vs + vs2(size(vs)+1:) = vs_str(s) + end function vs_s_concat + + subroutine destroy_vs(vs) + character, dimension(:), pointer :: vs + + deallocate(vs) + end subroutine destroy_vs + +#endif +end module fox_m_fsys_array_str diff --git a/src/xml/fsys/fox_m_fsys_count_parse_input.F90 b/src/xml/fsys/fox_m_fsys_count_parse_input.F90 new file mode 100644 index 0000000000..b158d26fa7 --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_count_parse_input.F90 @@ -0,0 +1,561 @@ +module fox_m_fsys_count_parse_input + + use fox_m_fsys_realtypes, only: sp, dp + + implicit none + private + + character(len=1), parameter :: SPACE = achar(32) + character(len=1), parameter :: NEWLINE = achar(10) + character(len=1), parameter :: CARRIAGE_RETURN = achar(13) + character(len=1), parameter :: TAB = achar(9) + character(len=*), parameter :: whitespace = & + SPACE//NEWLINE//CARRIAGE_RETURN//TAB + + interface countrts + module procedure countstring + module procedure countlogical + module procedure countinteger + module procedure countrealsp + module procedure countrealdp + module procedure countcomplexsp + module procedure countcomplexdp + end interface + + public :: countrts + +contains + + pure function countstring(s, datatype, separator, csv) result(num) + character(len=*), intent(in) :: s + character(len=*), intent(in) :: datatype + character, intent(in), optional :: separator + logical, intent(in), optional :: csv + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + character(len=len(s)) :: s2 + logical :: csv_, eof + integer :: m + + csv_ = .false. + if (present(csv)) then + if (csv) csv_ = csv + endif + + s_i = 1 + err = 0 + eof = .false. + ij = 0 + loop: do + if (csv_) then + if (s_i>len(s)) then + ij = ij + 1 + exit loop + endif + k = verify(s(s_i:), achar(10)//achar(13)) + if (k==0) then + ij = ij + 1 + exit loop + elseif (s(s_i+k-1:s_i+k-1)=="""") then + ! we have a quote-delimited string; + s_i = s_i + k + s2 = "" + quote: do + k = index(s(s_i:), """") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 1 + s2(m:) = s(s_i:k) + m = m + (k-s_i+1) + k = k + 2 + if (k>len(s)) then + err = 2 + exit loop + endif + if (s(k:k)/="""") exit + s_i = k + 1 + if (s_i > len(s)) then + err = 2 + exit loop + endif + m = m + 1 + s2(m:m) = """" + enddo quote + k = scan(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + else + s_i = s_i + k - 1 + k = scan(s(s_i:), achar(10)//achar(13)//",") + if (k==0) then + eof = .true. + k = len(s) + else + k = s_i + k - 2 + endif + if (index(s(s_i:k), """")/=0) then + err = 2 + exit loop + endif + endif + ij = ij + 1 + s_i = k + 2 + if (eof) exit loop + + else + if (present(separator)) then + k = index(s(s_i:), separator) + else + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + k = scan(s(s_i:), whitespace) + endif + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + ij = ij + 1 + s_i = k + 2 + if (s_i>len(s)) exit loop + + endif + end do loop + + num = ij + if (err/=0) num=0 + +#else + num = 0 +#endif + end function countstring + + pure function countlogical(s, datatype) result(num) + character(len=*), intent(in) :: s + logical, intent(in) :: datatype + logical :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + if (.not.((s(s_i:k)=="true".or.s(s_i:k)=="1").or. & + (s(s_i:k)=="false".or.s(s_i:k)=="0"))) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countlogical + + pure function countinteger(s, datatype) result(num) + character(len=*), intent(in) :: s + integer, intent(in) :: datatype + integer :: dummy_data + integer num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) dummy_data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif +end function countinteger + + pure function countrealsp(s, datatype) result(num) + character(len=*), intent(in) :: s + real(sp), intent(in) :: datatype + real(sp) :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) dummy_data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countrealsp + + pure function countrealdp(s, datatype) result(num) + character(len=*), intent(in) :: s + real(dp), intent(in) :: datatype + real(dp) :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) dummy_data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countrealdp + + pure function countcomplexsp(s, datatype) result(num) + character(len=*), intent(in) :: s + complex(sp), intent(in) :: datatype + complex(sp) :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + bracketed = .false. + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + select case (s(s_i:s_i)) + case ("(") + bracketed = .true. + k = verify(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + s_i = s_i + k + case (",") + k = verify(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + s_i = s_i + k - 1 + case ("+", "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9") + continue + case default + err = 2 + exit loop + end select + if (bracketed) then + k = index(s(s_i:), ")+i(") + else + k = scan(s(s_i:), whitespace//",") + endif + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + read(s(s_i:k), *, iostat=ios) r + if (ios/=0) then + err = 2 + exit loop + endif + if (bracketed) then + s_i = k + 5 + if (s_i>len(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countcomplexsp + + pure function countcomplexdp(s, datatype) result(num) + character(len=*), intent(in) :: s + complex(dp), intent(in) :: datatype + complex(dp) :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + bracketed = .false. + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + select case (s(s_i:s_i)) + case ("(") + bracketed = .true. + k = verify(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + s_i = s_i + k + case (",") + k = verify(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + s_i = s_i + k - 1 + case ("+", "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9") + continue + case default + err = 2 + exit loop + end select + if (bracketed) then + k = index(s(s_i:), ")+i(") + else + k = scan(s(s_i:), whitespace//",") + endif + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + read(s(s_i:k), *, iostat=ios) r + if (ios/=0) then + err = 2 + exit loop + endif + if (bracketed) then + s_i = k + 5 + if (s_i>len(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countcomplexdp + +end module fox_m_fsys_count_parse_input diff --git a/src/xml/fsys/fox_m_fsys_format.F90 b/src/xml/fsys/fox_m_fsys_format.F90 new file mode 100644 index 0000000000..448f44ebd4 --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_format.F90 @@ -0,0 +1,2250 @@ +module fox_m_fsys_format + +!Note that there are several oddities to this package, +!to get round assorted compiler bugs. + +!All the _matrix_ subroutines should be straight +!call-throughs to the relevant _array_ subroutine, +!but with flattened arrays. (this would allow easy +!generation of all functions up to 7 dimensions) +!but unfortunately that breaks PGI-6.1, and causes +!errors on Pathscale-2.4. + +!The Logical array/matrix functions should be able +!to COUNT their length inline in the specification +!expression, but Pathscale-2.4 gives an error on that. + + use fox_m_fsys_abort_flush, only: pxfflush + use fox_m_fsys_realtypes, only: sp, dp + + implicit none + private + +#ifndef DUMMYLIB + integer, parameter :: sig_sp = digits(1.0_sp)/4 + integer, parameter :: sig_dp = digits(1.0_dp)/4 ! Approximate precision worth outputting of each type. + + character(len=*), parameter :: digit = "0123456789:" + character(len=*), parameter :: hexdigit = "0123456789abcdefABCDEF" +#endif + + interface str +! This is for external use only: str should not be called within this +! file. +! All *_chk subroutines check that the fmt they are passed is valid. + module procedure str_string, str_string_array, str_string_matrix, & + str_integer, str_integer_array, str_integer_matrix, & + str_integer_fmt, str_integer_array_fmt, str_integer_matrix_fmt, & + str_logical, str_logical_array, str_logical_matrix, & + str_real_sp, str_real_sp_fmt_chk, & + str_real_sp_array, str_real_sp_array_fmt_chk, & + str_real_sp_matrix, str_real_sp_matrix_fmt_chk, & + str_real_dp, str_real_dp_fmt_chk, & + str_real_dp_array, str_real_dp_array_fmt_chk, & + str_real_dp_matrix, str_real_dp_matrix_fmt_chk, & + str_complex_sp, str_complex_sp_fmt_chk, & + str_complex_sp_array, str_complex_sp_array_fmt_chk, & + str_complex_sp_matrix, str_complex_sp_matrix_fmt_chk, & + str_complex_dp, str_complex_dp_fmt_chk, & + str_complex_dp_array, str_complex_dp_array_fmt_chk, & + str_complex_dp_matrix, str_complex_dp_matrix_fmt_chk + end interface str + +#ifndef DUMMYLIB + interface safestr +! This is for internal use only - no check is made on the validity of +! any fmt input. + module procedure str_string, str_string_array, str_string_matrix, & + str_integer, str_integer_array, str_integer_matrix, & + str_logical, str_logical_array, str_logical_matrix, & + str_real_sp, str_real_sp_fmt, & + str_real_sp_array, str_real_sp_array_fmt, & + str_real_sp_matrix, str_real_sp_matrix_fmt, & + str_real_dp, str_real_dp_fmt, & + str_real_dp_array, str_real_dp_array_fmt, & + str_real_dp_matrix, str_real_dp_matrix_fmt, & + str_complex_sp, str_complex_sp_fmt, & + str_complex_sp_array, str_complex_sp_array_fmt, & + str_complex_sp_matrix, str_complex_sp_matrix_fmt, & + str_complex_dp, str_complex_dp_fmt, & + str_complex_dp_array, str_complex_dp_array_fmt, & + str_complex_dp_matrix, str_complex_dp_matrix_fmt + end interface safestr + + interface len + module procedure str_integer_len, str_integer_array_len, str_integer_matrix_len, & + str_integer_fmt_len, str_integer_array_fmt_len, str_integer_matrix_fmt_len, & + str_logical_len, str_logical_array_len, str_logical_matrix_len, & + str_real_sp_len, str_real_sp_fmt_len, & + str_real_sp_array_len, str_real_sp_array_fmt_len, & + str_real_sp_matrix_len, str_real_sp_matrix_fmt_len, & + str_real_dp_len, str_real_dp_fmt_len, & + str_real_dp_array_len, str_real_dp_array_fmt_len, & + str_real_dp_matrix_len, str_real_dp_matrix_fmt_len, & + str_complex_sp_len, str_complex_sp_fmt_len, & + str_complex_sp_array_len, str_complex_sp_array_fmt_len, & + str_complex_sp_matrix_len, str_complex_sp_matrix_fmt_len, & + str_complex_dp_len, str_complex_dp_fmt_len, & + str_complex_dp_array_len, str_complex_dp_array_fmt_len, & + str_complex_dp_matrix_len, str_complex_dp_matrix_fmt_len + end interface +#endif + + interface operator(//) + module procedure concat_str_int, concat_int_str, & + concat_str_logical, concat_logical_str, & + concat_real_sp_str, concat_str_real_sp, & + concat_real_dp_str, concat_str_real_dp, & + concat_complex_sp_str, concat_str_complex_sp, & + concat_complex_dp_str, concat_str_complex_dp + end interface + + public :: str + public :: operator(//) + +#ifndef DUMMYLIB + public :: str_to_int_10 + public :: str_to_int_16 +#endif + +contains + +#ifndef DUMMYLIB + ! NB: The len generic module procedure is used in + ! many initialisation statments (to set the + ! length of the output string needed for the + ! converted number). As of the Fortran 2008 + ! spec every specific function belonging to + ! a generic used in this way must be defined + ! in the module before use. This is enforced + ! by at least version 7.4.4 of the Cray + ! Fortran compiler. Hence we put all the *_len + ! functions here at the top of the file. + pure function str_string_array_len(st) result(n) + character(len=*), dimension(:), intent(in) :: st + integer :: n + + integer :: k + + n = size(st) - 1 + do k = 1, size(st) + n = n + len(st(k)) + enddo + + end function str_string_array_len + + pure function str_string_matrix_len(st) result(n) + character(len=*), dimension(:, :), intent(in) :: st + integer :: n + + n = len(st) * size(st) + size(st) - 1 + end function str_string_matrix_len + + pure function str_integer_len(i) result(n) + integer, intent(in) :: i + integer :: n + + n = int(log10(real(max(abs(i),1)))) + 1 + dim(-i,0)/max(abs(i),1) + + end function str_integer_len + + pure function str_integer_base_len(i, b) result(n) + integer, intent(in) :: i, b + integer :: n + + n = int(log10(real(max(abs(i),1)))/log10(real(b))) & + + 1 + dim(-i,0)/max(abs(i),1) + + end function str_integer_base_len + + pure function str_integer_fmt_len(i, fmt) result(n) + integer, intent(in) :: i + character(len=*), intent(in) :: fmt + integer :: n + + select case (len(fmt)) + case(0) + n = 0 + case(1) + if (fmt=="x") then + n = int(log10(real(max(abs(i),1)))/log10(16.0)) + 1 + dim(-i,0)/max(abs(i),1) + elseif (fmt=="d") then + n = int(log10(real(max(abs(i),1)))) + 1 + dim(-i,0)/max(abs(i),1) + else + return + endif + case default + if (fmt(1:1)/='x'.and.fmt(1:1)/='d') then + n = 0 + elseif (verify(fmt(2:), digit)==0) then + n = str_to_int_10(fmt(2:)) + else + n = 0 + endif + end select + + end function str_integer_fmt_len + + pure function str_integer_array_len(ia) result(n) + integer, dimension(:), intent(in) :: ia + integer :: n + + integer :: j + + n = size(ia) - 1 + + do j = 1, size(ia) + n = n + len(ia(j)) + enddo + + end function str_integer_array_len + + pure function str_integer_array_fmt_len(ia, fmt) result(n) + integer, dimension(:), intent(in) :: ia + character(len=*), intent(in) :: fmt + integer :: n + + integer :: j + + n = size(ia) - 1 + + do j = 1, size(ia) + n = n + len(ia(j), fmt) + enddo + + end function str_integer_array_fmt_len + + pure function str_integer_matrix_len(ia) result(n) + integer, dimension(:,:), intent(in) :: ia + integer :: n + + integer :: j, k + + n = size(ia) - 1 + + do k = 1, size(ia, 2) + do j = 1, size(ia, 1) + n = n + len(ia(j, k)) + enddo + enddo + + end function str_integer_matrix_len + + pure function str_integer_matrix_fmt_len(ia, fmt) result(n) + integer, dimension(:,:), intent(in) :: ia + character(len=*), intent(in) :: fmt + integer :: n + + integer :: j, k + + n = size(ia) - 1 + + do k = 1, size(ia, 2) + do j = 1, size(ia, 1) + n = n + len(ia(j, k), fmt) + enddo + enddo + + end function str_integer_matrix_fmt_len + + pure function str_logical_len(l) result (n) + logical, intent(in) :: l + integer :: n + + if (l) then + n = 4 + else + n = 5 + endif + end function str_logical_len + + pure function str_logical_array_len(la) result(n) +! This function should be inlined in the declarations of +! str_logical_array below but PGI and pathscale don't like it. + logical, dimension(:), intent(in) :: la + integer :: n + n = 5*size(la) - 1 + count(.not.la) + end function str_logical_array_len + + pure function str_logical_matrix_len(la) result(n) +! This function should be inlined in the declarations of +! str_logical_matrix below but PGI and pathscale don't like it. + logical, dimension(:,:), intent(in) :: la + integer :: n + n = 5*size(la) - 1 + count(.not.la) + end function str_logical_matrix_len + + pure function str_real_sp_fmt_len(x, fmt) result(n) + real(sp), intent(in) :: x + character(len=*), intent(in) :: fmt + integer :: n + + integer :: dec, sig + integer :: e + + if (.not.checkFmt(fmt)) then + n = 0 + return + endif + + if (x == 0.0_sp) then + e = 1 + else + e = floor(log10(abs(x))) + endif + + if (x < 0.0_sp) then + n = 1 + else + n = 0 + endif + + if (len(fmt) == 0) then + sig = sig_sp + + n = n + sig + 2 + len(e) + ! for the decimal point and the e + + elseif (fmt(1:1) == "s") then + if (len(fmt) > 1) then + sig = str_to_int_10(fmt(2:)) + else + sig = sig_sp + endif + sig = max(sig, 1) + sig = min(sig, digits(1.0_sp)) + + if (sig > 1) n = n + 1 + ! for the decimal point + + n = n + sig + 1 + len(e) + + elseif (fmt(1:1) == "r") then + + if (len(fmt) > 1) then + dec = str_to_int_10(fmt(2:)) + else + dec = sig_sp - e - 1 + endif + dec = min(dec, digits(1.0_sp)-e) + dec = max(dec, 0) + + if (dec > 0) n = n + 1 + if (abs(x) >= 1.0_sp) n = n + 1 + + ! Need to know if there's an overflow .... + if (e+dec+1 > 0) then + if (index(real_sp_str(abs(x), e+dec+1), "!") == 1) & + e = e + 1 + endif + + n = n + abs(e) + dec + + endif + + end function str_real_sp_fmt_len + + pure function str_real_sp_len(x) result(n) + real(sp), intent(in) :: x + integer :: n + + n = len(x, "") + + end function str_real_sp_len + + pure function str_real_sp_array_len(xa) result(n) + real(sp), dimension(:), intent(in) :: xa + integer :: n + + integer :: k + + n = size(xa) - 1 + do k = 1, size(xa) + n = n + len(xa(k), "") + enddo + + end function str_real_sp_array_len + + pure function str_real_sp_array_fmt_len(xa, fmt) result(n) + real(sp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt + integer :: n + + integer :: k + + n = size(xa) - 1 + do k = 1, size(xa) + n = n + len(xa(k), fmt) + enddo + + end function str_real_sp_array_fmt_len + + pure function str_real_sp_matrix_fmt_len(xa, fmt) result(n) + real(sp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt + integer :: n + + integer :: j, k + + n = size(xa) - 1 + do k = 1, size(xa, 2) + do j = 1, size(xa, 1) + n = n + len(xa(j,k), fmt) + enddo + enddo + + end function str_real_sp_matrix_fmt_len + + pure function str_real_sp_matrix_len(xa) result(n) + real(sp), dimension(:,:), intent(in) :: xa + integer :: n + + n = len(xa, "") + end function str_real_sp_matrix_len + + pure function str_real_dp_fmt_len(x, fmt) result(n) + real(dp), intent(in) :: x + character(len=*), intent(in) :: fmt + integer :: n + + integer :: dec, sig + integer :: e + + if (.not.checkFmt(fmt)) then + n = 0 + return + endif + + if (x == 0.0_dp) then + e = 1 + else + e = floor(log10(abs(x))) + endif + + if (x < 0.0_dp) then + n = 1 + else + n = 0 + endif + + if (len(fmt) == 0) then + sig = sig_dp + + n = n + sig + 2 + len(e) + ! for the decimal point and the e + + elseif (fmt(1:1) == "s") then + if (len(fmt) > 1) then + sig = str_to_int_10(fmt(2:)) + else + sig = sig_dp + endif + sig = max(sig, 1) + sig = min(sig, digits(1.0_dp)) + + if (sig > 1) n = n + 1 + ! for the decimal point + + n = n + sig + 1 + len(e) + + elseif (fmt(1:1) == "r") then + + if (len(fmt) > 1) then + dec = str_to_int_10(fmt(2:)) + else + dec = sig_dp - e - 1 + endif + dec = min(dec, digits(1.0_dp)-e) + dec = max(dec, 0) + + if (dec > 0) n = n + 1 + if (abs(x) >= 1.0_dp) n = n + 1 + + ! Need to know if there's an overflow .... + if (e+dec+1 > 0) then + if (index(real_dp_str(abs(x), e+dec+1), "!") == 1) & + e = e + 1 + endif + + n = n + abs(e) + dec + + endif + + end function str_real_dp_fmt_len + + pure function str_real_dp_len(x) result(n) + real(dp), intent(in) :: x + integer :: n + + n = len(x, "") + + end function str_real_dp_len + + pure function str_real_dp_array_len(xa) result(n) + real(dp), dimension(:), intent(in) :: xa + integer :: n + + integer :: k + + n = size(xa) - 1 + do k = 1, size(xa) + n = n + len(xa(k), "") + enddo + + end function str_real_dp_array_len + + pure function str_real_dp_array_fmt_len(xa, fmt) result(n) + real(dp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt + integer :: n + + integer :: k + + n = size(xa) - 1 + do k = 1, size(xa) + n = n + len(xa(k), fmt) + enddo + + end function str_real_dp_array_fmt_len + + pure function str_real_dp_matrix_fmt_len(xa, fmt) result(n) + real(dp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt + integer :: n + + integer :: j, k + + n = size(xa) - 1 + do k = 1, size(xa, 2) + do j = 1, size(xa, 1) + n = n + len(xa(j,k), fmt) + enddo + enddo + + end function str_real_dp_matrix_fmt_len + + pure function str_real_dp_matrix_len(xa) result(n) + real(dp), dimension(:,:), intent(in) :: xa + integer :: n + + n = len(xa, "") + end function str_real_dp_matrix_len + + pure function str_complex_sp_fmt_len(c, fmt) result(n) + complex(sp), intent(in) :: c + character(len=*), intent(in) :: fmt + integer :: n + + real(sp) :: re, im + re = real(c) + im = aimag(c) + + n = len(re, fmt) + len(im, fmt) + 6 + end function str_complex_sp_fmt_len + + pure function str_complex_sp_len(c) result(n) + complex(sp), intent(in) :: c + integer :: n + + n = len(c, "") + end function str_complex_sp_len + + pure function str_complex_sp_array_fmt_len(ca, fmt) result(n) + complex(sp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt + integer :: n + + integer :: i + + n = size(ca) - 1 + do i = 1, size(ca) + n = n + len(ca(i), fmt) + enddo + end function str_complex_sp_array_fmt_len + + pure function str_complex_sp_array_len(ca) result(n) + complex(sp), dimension(:), intent(in) :: ca + integer :: n + + n = len(ca, "") + end function str_complex_sp_array_len + + pure function str_complex_sp_matrix_fmt_len(ca, fmt) result(n) + complex(sp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt + integer :: n + + integer :: i, j + + n = size(ca) - 1 + do i = 1, size(ca, 1) + do j = 1, size(ca, 2) + n = n + len(ca(i, j), fmt) + enddo + enddo + end function str_complex_sp_matrix_fmt_len + + pure function str_complex_sp_matrix_len(ca) result(n) + complex(sp), dimension(:, :), intent(in) :: ca + integer :: n + + n = len(ca, "") + end function str_complex_sp_matrix_len + + pure function str_complex_dp_fmt_len(c, fmt) result(n) + complex(dp), intent(in) :: c + character(len=*), intent(in) :: fmt + integer :: n + + real(dp) :: re, im + re = real(c) + im = aimag(c) + + n = len(re, fmt) + len(im, fmt) + 6 + end function str_complex_dp_fmt_len + + pure function str_complex_dp_len(c) result(n) + complex(dp), intent(in) :: c + integer :: n + + n = len(c, "") + end function str_complex_dp_len + + pure function str_complex_dp_array_fmt_len(ca, fmt) result(n) + complex(dp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt + integer :: n + + integer :: i + + n = size(ca) - 1 + do i = 1, size(ca) + n = n + len(ca(i), fmt) + enddo + end function str_complex_dp_array_fmt_len + + pure function str_complex_dp_array_len(ca) result(n) + complex(dp), dimension(:), intent(in) :: ca + integer :: n + + n = len(ca, "") + end function str_complex_dp_array_len + + pure function str_complex_dp_matrix_fmt_len(ca, fmt) result(n) + complex(dp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt + integer :: n + + integer :: i, j + + n = size(ca) - 1 + do i = 1, size(ca, 1) + do j = 1, size(ca, 2) + n = n + len(ca(i, j), fmt) + enddo + enddo + end function str_complex_dp_matrix_fmt_len + + pure function str_complex_dp_matrix_len(ca) result(n) + complex(dp), dimension(:, :), intent(in) :: ca + integer :: n + + n = len(ca, "") + end function str_complex_dp_matrix_len +#endif + +#ifndef DUMMYLIB + subroutine FoX_error(msg) + ! Emit error message and stop. + ! No clean up is done here, but this can + ! be overridden to include clean-up routines + character(len=*), intent(in) :: msg + + write(0,'(a)') 'ERROR(FoX)' + write(0,'(a)') msg + call pxfflush(0) + + stop + + end subroutine FoX_error + + + pure function str_to_int_10(str) result(n) + ! Takes a string containing digits, and returns + ! the integer representable by those digits. + ! Does not deal with negative numbers, and + ! presumes that the number is representable + ! in a default integer + ! Error is flagged by returning -1 + character(len=*), intent(in) :: str + integer :: n + + integer :: max_power, i, j + + if (verify(str, digit) > 0) then + n = -1 + return + endif + + max_power = len(str) - 1 + + n = 0 + do i = 0, max_power + j = max_power - i + 1 + n = n + (index(digit, str(j:j)) - 1) * 10**i + enddo + + end function str_to_int_10 + + pure function str_to_int_16(str) result(n) + ! Takes a string containing hexadecimal digits, and returns + ! the integer representable by those digits. + ! Does not deal with negative numbers, and + ! presumes that the number is representable + ! in a default integer + ! Error is flagged by returning -1 + character(len=*), intent(in) :: str + integer :: n + + character(len=len(str)) :: str_l + integer :: max_power, i, j + + if (verify(str, hexdigit) == 0) then + str_l = to_lower(str) + else + n = -1 + return + endif + + max_power = len(str) - 1 + + n = 0 + do i = 0, max_power + j = max_power - i + 1 + n = n + (index(hexdigit, str_l(j:j)) - 1) * 16**i + enddo + + contains + pure function to_lower(s) result(s2) + character(len=*), intent(in) :: s + character(len=len(s)) :: s2 + character(len=*), parameter :: hex = "abcdef" + integer :: j, k + do j = 1, len(s) + k = index('ABCDEF', s(j:j)) + if (k > 0) then + s2(j:j) = hex(k:k) + else + s2(j:j) = s(j:j) + endif + enddo + end function to_lower + + end function str_to_int_16 +#endif + + pure function str_string(st) result(s) + character(len=*), intent(in) :: st +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(st)) :: s + s = st +#endif + end function str_string + + pure function str_string_array(st, delimiter) result(s) + character(len=*), dimension(:), intent(in) :: st + character(len=1), intent(in), optional :: delimiter +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=str_string_array_len(st)) :: s + + integer :: k, n + character(len=1) :: d + + if (present(delimiter)) then + d = delimiter + else + d = " " + endif + + n = 1 + do k = 1, size(st) - 1 + s(n:n+len(st(k))) = st(k)//d + n = n + len(st(k)) + 1 + enddo + s(n:) = st(k) +#endif + end function str_string_array + + pure function str_string_matrix(st, delimiter) result(s) + character(len=*), dimension(:, :), intent(in) :: st + character(len=1), intent(in), optional :: delimiter +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=str_string_matrix_len(st)) :: s + + integer :: j, k, n + character(len=1) :: d + + if (present(delimiter)) then + d = delimiter + else + d = " " + endif + + s(1:len(st)) = st(1,1) + n = len(st) + 1 + do j = 2, size(st, 1) + s(n:n+len(st)) = d//st(j,1) + n = n + len(st) + 1 + enddo + do k = 2, size(st, 2) + do j = 1, size(st, 1) + s(n:n+len(st(j,k))) = d//st(j,k) + n = n + len(st) + 1 + enddo + enddo +#endif + end function str_string_matrix + + pure function str_integer(i) result(s) + integer, intent(in) :: i +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=str_integer_len(i)) :: s + + integer :: b, ii, j, k, n + + b = 10 + + if (i < 0) then + s(1:1) = "-" + n = 2 + else + n = 1 + endif + ii = abs(i) + do k = len(s) - n, 0, -1 + j = ii/(b**k) + ii = ii - j*(b**k) + s(n:n) = digit(j+1:j+1) + n = n + 1 + enddo +#endif + end function str_integer + + pure function str_integer_fmt(i, fmt) result(s) + integer, intent(in) :: i + character(len=*), intent(in):: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=str_integer_fmt_len(i, fmt)) :: s + + character :: f + integer :: b, ii, j, k, n, ls + + if (len(fmt)>0) then + if (fmt(1:1)=="d") then + f = 'd' + b = 10 + elseif (fmt(1:1)=="x") then + f = 'x' + b = 16 + else + ! Undefined outcome + s = "" + return + endif + else + ! Undefined outcome + s = "" + return + endif + + ls = str_integer_base_len(i, b) + n = len(s) - ls + 1 + + if (i < 0) then + if (n>0) s(:n) = "-"//repeat("0", n-1) + n = n + 1 + else + if (n>1) s(:n) = repeat("0", n) + endif + + ii = abs(i) + do k = 1, -n + 1 + j = ii/(b**k) + ii = ii - j*(b**k) + n = n + 1 + enddo + do k = len(s) - n, 0, -1 + j = ii/(b**k) + ii = ii - j*(b**k) + s(n:n) = hexdigit(j+1:j+1) + n = n + 1 + enddo +#endif + end function str_integer_fmt + + pure function str_integer_array(ia) result(s) + integer, dimension(:), intent(in) :: ia +#ifdef DUMMYLIB + character(len=1) :: s +#else + character(len=len(ia, "d")) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(ia) - 1 + j = len(ia(k)) + s(n:n+j) = str(ia(k))//" " + n = n + j + 1 + enddo + s(n:) = str(ia(k)) +#endif + end function str_integer_array + + + function str_integer_array_fmt(ia, fmt) result(s) + integer, dimension(:), intent(in) :: ia + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ia, fmt)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(ia) - 1 + j = len(ia(k), fmt) + s(n:n+j) = str(ia(k), fmt)//" " + n = n + j + 1 + enddo + s(n:) = str(ia(k), fmt) +#endif + end function str_integer_array_fmt + + pure function str_integer_matrix(ia) result(s) + integer, dimension(:,:), intent(in) :: ia +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ia, "d")) :: s + + integer :: j, k, n + + s(:len(ia(1,1))) = str(ia(1,1)) + n = len(ia(1,1)) + 1 + do j = 2, size(ia, 1) + s(n:n+len(ia(j,1))) = " "//str(ia(j,1)) + n = n + len(ia(j,1)) + 1 + enddo + do k = 2, size(ia, 2) + do j = 1, size(ia, 1) + s(n:n+len(ia(j,k))) = " "//str(ia(j,k)) + n = n + len(ia(j,k)) + 1 + enddo + enddo +#endif + end function str_integer_matrix + + + pure function str_integer_matrix_fmt(ia, fmt) result(s) + integer, dimension(:,:), intent(in) :: ia + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ia, fmt)) :: s + + integer :: j, k, n + + s(:len(ia(1,1), fmt)) = str(ia(1,1), fmt) + n = len(ia(1,1), fmt) + 1 + do j = 2, size(ia, 1) + s(n:n+len(ia(j,1), fmt)) = " "//str(ia(j,1), fmt) + n = n + len(ia(j,1), fmt) + 1 + enddo + do k = 2, size(ia, 2) + do j = 1, size(ia, 1) + s(n:n+len(ia(j,k), fmt)) = " "//str(ia(j,k), fmt) + n = n + len(ia(j,k), fmt) + 1 + enddo + enddo +#endif + end function str_integer_matrix_fmt + + pure function str_logical(l) result(s) + logical, intent(in) :: l +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else +! Pathscale 2.5 gets it wrong if we use merge here +! character(len=merge(4,5,l)) :: s +! And g95 (sep2007) cant resolve the generic here + character(len=str_logical_len(l)) :: s + + if (l) then + s="true" + else + s="false" + endif +#endif + end function str_logical + + pure function str_logical_array(la) result(s) + logical, dimension(:), intent(in) :: la +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(la)) :: s + + integer :: k, n + + n = 1 + do k = 1, size(la) - 1 + if (la(k)) then + s(n:n+3) = "true" + n = n + 5 + else + s(n:n+4) = "false" + n = n + 6 + endif + s(n-1:n-1) = " " + enddo + if (la(k)) then + s(n:) = "true" + else + s(n:) = "false" + endif +#endif + end function str_logical_array + + pure function str_logical_matrix(la) result(s) + logical, dimension(:,:), intent(in) :: la +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(la)) :: s + + integer :: j, k, n + + if (la(1,1)) then + s(:4) = "true" + n = 5 + else + s(:5) = "false" + n = 6 + endif + do j = 2, size(la, 1) + s(n:n) = " " + if (la(j,1)) then + s(n+1:n+4) = "true" + n = n + 5 + else + s(n+1:n+5) = "false" + n = n + 6 + endif + enddo + do k = 2, size(la, 2) + do j = 1, size(la, 1) + s(n:n) = " " + if (la(j,k)) then + s(n+1:n+4) = "true" + n = n + 5 + else + s(n+1:n+5) = "false" + n = n + 6 + endif + enddo + enddo +#endif + end function str_logical_matrix + +#ifndef DUMMYLIB + ! In order to convert real numbers to strings, we need to + ! perform an internal write - but how long will the + ! resultant string be? We don't know & there is no way + ! to discover for an arbitrary format. Therefore, + ! (if we have the capability; f95 or better) + ! we assume it will be less than 100 characters, write + ! it to a string of that length, then remove leading & + ! trailing whitespace. (this means that if the specified + ! format includes whitespace, this will be lost.) + ! + ! If we are working with an F90-only compiler, then + ! we cannot do this trick - the output string will + ! always be 100 chars in length, though we will remove + ! leading whitespace. + + + ! The standard Fortran format functions do not give us + ! enough control, so we write our own real number formatting + ! routines here. For each real type, we optionally take a + ! format like so: + ! "r" which will produce output without an exponent, + ! and digits after the decimal point. + ! or + ! "s": which implies scientific notation, with an + ! exponent, with significant figures. + ! If the integer is absent, then the precision will be + ! half of the number of significant figures available + ! for that real type. + ! The absence of a format implies scientific notation, with + ! the default precision. + + ! These routines are fairly imperfect - they are inaccurate for + ! the lower-end bits of the number, since they work by simple + ! multiplications by 10. + ! Also they will probably be orders of magnitude slower than library IO. + ! Ideally they'd be rewritten to convert from teh native format by + ! bit-twidding. Not sure how to do that portably though. + + ! The format specification could be done more nicely - but unfortunately + ! not in F95 due to *stupid* restrictions on specification expressions. + + ! And I wouldn't have to invent my own format specification if Fortran + ! had a proper IO library anyway. + +!FIXME Signed zero is not handled correctly; don't quite understand why. +!FIXME too much duplication between sp & dp, we should m4. + + pure function real_sp_str(x, sig) result(s) + real(sp), intent(in) :: x + integer, intent(in) :: sig + character(len=sig) :: s + ! make a string of numbers sig long of x. + integer :: e, i, j, k, n + real(sp) :: x_ + + if (sig < 1) then + s ="" + return + endif + + if (x == 0.0_sp) then + e = 1 + else + e = floor(log10(abs(x))) + endif + x_ = abs(x) + ! Have to do this next in a loop rather than just exponentiating in + ! order to avoid under/over-flow. + do i = 1, abs(e) + ! Have to multiply by 10^-e rather than divide by 10^e + ! to avoid rounding errors. + x_ = x_ * (10.0_sp**(-abs(e)/e)) + enddo + n = 1 + do k = sig - 2, 0, -1 + ! This baroque way of taking int() ensures the optimizer + ! stores it in j without keeping a different value in cache. + j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 + if (j==10) then + ! This can happen if, on the previous cycle, int(x_) in + ! the line above gave a result approx. 1.0 less than + ! expected. + ! In this case we want to quit the cycle & just get 999... to the end + s(n:) = repeat("9", sig - n + 1) + return + endif + s(n:n) = digit(j+1:j+1) + n = n + 1 + x_ = (x_ - j) * 10.0_sp + enddo + j = nint(x_) + if (j == 10) then + ! Now round ... + s(n:n) = "9" + ! Are they all 9's? + i = verify(s, "9", .true.) + if (i == 0) then + s(1:1) = "!" + ! overflow + return + endif + j = index(digit, s(i:i)) + s(i:i) = digit(j+1:j+1) + s(i+1:) = repeat("0", sig - i + 1) + else + s(n:n) = digit(j+1:j+1) + endif + + end function real_sp_str + +#endif + + function str_real_sp_fmt_chk(x, fmt) result(s) + real(sp), intent(in) :: x + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(x, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(x, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_real_sp_fmt_chk + +#ifndef DUMMYLIB + pure function str_real_sp_fmt(x, fmt) result(s) + real(sp), intent(in) :: x + character(len=*), intent(in) :: fmt + character(len=len(x, fmt)) :: s + + integer :: sig, dec + integer :: e, n + character(len=len(x, fmt)) :: num !this will always be enough memory. + + if (x == 0.0_sp) then + e = 0 + else + e = floor(log10(abs(x))) + endif + + if (x < 0.0_sp) then + s(1:1) = "-" + n = 2 + else + n = 1 + endif + + if (len(fmt) == 0) then + + sig = sig_sp + + num = real_sp_str(abs(x), sig) + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (sig == 1) then + s(n:n) = num + n = n + 1 + else + s(n:n+1) = num(1:1)//"." + s(n+2:n+sig) = num(2:) + n = n + sig + 1 + endif + + s(n:n) = "e" + s(n+1:) = str(e) + + elseif (fmt(1:1) == "s") then + + if (len(fmt) > 1) then + sig = str_to_int_10(fmt(2:)) + else + sig = sig_sp + endif + sig = max(sig, 1) + sig = min(sig, digits(1.0_sp)) + + num = real_sp_str(abs(x), sig) + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (sig == 1) then + s(n:n) = num + n = n + 1 + else + s(n:n+1) = num(1:1)//"." + s(n+2:n+sig) = num(2:) + n = n + sig + 1 + endif + + s(n:n) = "e" + s(n+1:) = str(e) + + elseif (fmt(1:1) == "r") then + + if (len(fmt) > 1) then + dec = str_to_int_10(fmt(2:)) + else + dec = sig_sp - e - 1 + endif + dec = min(dec, digits(1.0_sp)-e-1) + dec = max(dec, 0) + + if (e+dec+1 > 0) then + num = real_sp_str(abs(x), e+dec+1) + else + num = "" + endif + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (abs(x) >= 1.0_sp) then + s(n:n+e) = num(:e+1) + n = n + e + 1 + if (dec > 0) then + s(n:n) = "." + n = n + 1 + s(n:) = num(e+2:) + endif + else + s(n:n) = "0" + if (dec > 0) then + s(n+1:n+1) = "." + n = n + 2 + if (dec < -e-1) then + s(n:) = repeat("0", dec) + else + s(n:n-e-2) = repeat("0", max(-e-1,0)) + n = n - min(e,-1) - 1 + if (n <= len(s)) then + s(n:) = num + endif + endif + endif + endif + + endif + + end function str_real_sp_fmt +#endif + + pure function str_real_sp(x) result(s) + real(sp), intent(in) :: x +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(x)) :: s + + s = safestr(x, "") +#endif + end function str_real_sp + + pure function str_real_sp_array(xa) result(s) + real(sp), dimension(:), intent(in) :: xa +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(xa) - 1 + j = len(xa(k), "") + s(n:n+j) = safestr(xa(k), "")//" " + n = n + j + 1 + enddo + s(n:) = safestr(xa(k), "") +#endif + end function str_real_sp_array + +#ifndef DUMMYLIB + pure function str_real_sp_array_fmt(xa, fmt) result(s) + real(sp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt + character(len=len(xa, fmt)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(xa) - 1 + j = len(xa(k), fmt) + s(n:n+j) = safestr(xa(k), fmt)//" " + n = n + j + 1 + enddo + s(n:) = safestr(xa(k), fmt) + + end function str_real_sp_array_fmt +#endif + + function str_real_sp_array_fmt_chk(xa, fmt) result(s) + real(sp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(xa, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_real_sp_array_fmt_chk + +#ifndef DUMMYLIB + pure function str_real_sp_matrix_fmt(xa, fmt) result(s) + real(sp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt + character(len=len(xa,fmt)) :: s + + integer :: i, j, k, n + + i = len(xa(1,1), fmt) + s(:i) = safestr(xa(1,1), fmt) + n = i + 1 + do j = 2, size(xa, 1) + i = len(xa(j,1), fmt) + s(n:n+i) = " "//safestr(xa(j,1), fmt) + n = n + i + 1 + enddo + do k = 2, size(xa, 2) + do j = 1, size(xa, 1) + i = len(xa(j,k), fmt) + s(n:n+i) = " "//safestr(xa(j,k), fmt) + n = n + i + 1 + enddo + enddo + + end function str_real_sp_matrix_fmt +#endif + + function str_real_sp_matrix_fmt_chk(xa, fmt) result(s) + real(sp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa,fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(xa, fmt) + else + call FoX_error("Invalid format: "//fmt) + end if +#endif + end function str_real_sp_matrix_fmt_chk + + pure function str_real_sp_matrix(xa) result(s) + real(sp), dimension(:,:), intent(in) :: xa +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa)) :: s + + s = safestr(xa, "") +#endif + end function str_real_sp_matrix + +#ifndef DUMMYLIB + pure function real_dp_str(x, sig) result(s) + real(dp), intent(in) :: x + integer, intent(in) :: sig + character(len=sig) :: s + ! make a string of numbers sig long of x. + integer :: e, i, j, k, n + real(dp) :: x_ + + if (sig < 1) then + s ="" + return + endif + + if (x == 0.0_dp) then + e = 1 + else + e = floor(log10(abs(x))) + endif + x_ = abs(x) + ! Have to do this next in a loop rather than just exponentiating in + ! order to avoid under/over-flow. + do i = 1, abs(e) + ! Have to multiply by 10^-e rather than divide by 10^e + ! to avoid rounding errors. + x_ = x_ * (10.0_dp**(-abs(e)/e)) + enddo + n = 1 + do k = sig - 2, 0, -1 + ! This baroque way of taking int() ensures the optimizer definitely + ! stores it in j without keeping a different value in cache. + j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 + if (j==10) then + ! This can happen if, on the previous cycle, int(x_) in + ! the line above gave a result almost exactly 1.0 less than + ! expected - but FP arithmetic is not consistent. + ! In this case we want to quit the cycle & just get 999... to the end + s(n:) = repeat("9", sig - n + 1) + return + endif + s(n:n) = digit(j+1:j+1) + n = n + 1 + x_ = (x_ - j) * 10.0_dp + enddo + j = nint(x_) + if (j == 10) then + ! Now round ... + s(n:n) = "9" + i = verify(s, "9", .true.) + if (i == 0) then + s(1:1) = "!" + !overflow + return + endif + j = index(digit, s(i:i)) + s(i:i) = digit(j+1:j+1) + s(i+1:) = repeat("0", sig - i + 1) + else + s(n:n) = digit(j+1:j+1) + endif + + end function real_dp_str + + +#endif + + function str_real_dp_fmt_chk(x, fmt) result(s) + real(dp), intent(in) :: x + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(x, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(x, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_real_dp_fmt_chk + +#ifndef DUMMYLIB + pure function str_real_dp_fmt(x, fmt) result(s) + real(dp), intent(in) :: x + character(len=*), intent(in) :: fmt + character(len=len(x, fmt)) :: s + + integer :: sig, dec + integer :: e, n + character(len=len(x, fmt)) :: num !this will always be enough memory. + + if (x == 0.0_dp) then + e = 0 + else + e = floor(log10(abs(x))) + endif + + if (x < 0.0_dp) then + s(1:1) = "-" + n = 2 + else + n = 1 + endif + + if (len(fmt) == 0) then + + sig = sig_dp + + num = real_dp_str(abs(x), sig) + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (sig == 1) then + s(n:n) = num + n = n + 1 + else + s(n:n+1) = num(1:1)//"." + s(n+2:n+sig) = num(2:) + n = n + sig + 1 + endif + + s(n:n) = "e" + s(n+1:) = safestr(e) + + elseif (fmt(1:1) == "s") then + + if (len(fmt) > 1) then + sig = str_to_int_10(fmt(2:)) + else + sig = sig_dp + endif + sig = max(sig, 1) + sig = min(sig, digits(1.0_dp)) + + num = real_dp_str(abs(x), sig) + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (sig == 1) then + s(n:n) = num + n = n + 1 + else + s(n:n+1) = num(1:1)//"." + s(n+2:n+sig) = num(2:) + n = n + sig + 1 + endif + + s(n:n) = "e" + s(n+1:) = safestr(e) + + elseif (fmt(1:1) == "r") then + + if (len(fmt) > 1) then + dec = str_to_int_10(fmt(2:)) + else + dec = sig_dp - e - 1 + endif + dec = min(dec, digits(1.0_dp)-e-1) + dec = max(dec, 0) + + if (e+dec+1 > 0) then + num = real_dp_str(abs(x), e+dec+1) + else + num = "" + endif + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (abs(x) >= 1.0_dp) then + s(n:n+e) = num(:e+1) + n = n + e + 1 + if (dec > 0) then + s(n:n) = "." + n = n + 1 + s(n:) = num(e+2:) + endif + else + s(n:n) = "0" + if (dec > 0) then + s(n+1:n+1) = "." + n = n + 2 + if (dec < -e-1) then + s(n:) = repeat("0", dec) + else + s(n:n-e-2) = repeat("0", max(-e-1,0)) + n = n - min(e,-1) - 1 + if (n <= len(s)) then + s(n:) = num + endif + endif + endif + endif + + endif + + end function str_real_dp_fmt + +#endif + + pure function str_real_dp(x) result(s) + real(dp), intent(in) :: x +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(x)) :: s + + s = safestr(x, "") +#endif + end function str_real_dp + + pure function str_real_dp_array(xa) result(s) + real(dp), dimension(:), intent(in) :: xa +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(xa) - 1 + j = len(xa(k), "") + s(n:n+j) = safestr(xa(k), "")//" " + n = n + j + 1 + enddo + s(n:) = safestr(xa(k)) +#endif + end function str_real_dp_array + +#ifndef DUMMYLIB + pure function str_real_dp_array_fmt(xa, fmt) result(s) + real(dp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt + character(len=len(xa, fmt)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(xa) - 1 + j = len(xa(k), fmt) + s(n:n+j) = safestr(xa(k), fmt)//" " + n = n + j + 1 + enddo + s(n:) = safestr(xa(k), fmt) + + end function str_real_dp_array_fmt +#endif + + function str_real_dp_array_fmt_chk(xa, fmt) result(s) + real(dp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(xa, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_real_dp_array_fmt_chk + +#ifndef DUMMYLIB + function str_real_dp_matrix_fmt(xa, fmt) result(s) + real(dp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt + character(len=len(xa,fmt)) :: s + + integer :: i, j, k, n + + i = len(xa(1,1), fmt) + s(:i) = safestr(xa(1,1), fmt) + n = i + 1 + do j = 2, size(xa, 1) + i = len(xa(j,1), fmt) + s(n:n+i) = " "//safestr(xa(j,1), fmt) + n = n + i + 1 + enddo + do k = 2, size(xa, 2) + do j = 1, size(xa, 1) + i = len(xa(j,k), fmt) + s(n:n+i) = " "//safestr(xa(j,k), fmt) + n = n + i + 1 + enddo + enddo + + end function str_real_dp_matrix_fmt +#endif + + function str_real_dp_matrix_fmt_chk(xa, fmt) result(s) + real(dp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa,fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(xa, fmt) + else + call FoX_error("Invalid format: "//fmt) + end if +#endif + end function str_real_dp_matrix_fmt_chk + + function str_real_dp_matrix(xa) result(s) + real(dp), dimension(:,:), intent(in) :: xa +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa)) :: s + + s = safestr(xa, "") +#endif + end function str_real_dp_matrix + +! For complex numbers, there's not really much prior art, so +! we use the easy solution: a+bi, where a & b are real numbers +! as output above. + + function str_complex_sp_fmt_chk(c, fmt) result(s) + complex(sp), intent(in) :: c + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(c, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(c, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_sp_fmt_chk + +#ifndef DUMMYLIB + pure function str_complex_sp_fmt(c, fmt) result(s) + complex(sp), intent(in) :: c + character(len=*), intent(in) :: fmt + character(len=len(c, fmt)) :: s + + real(sp) :: re, im + integer :: i + re = real(c) + im = aimag(c) + i = len(re, fmt) + s(:i+4) = "("//safestr(re, fmt)//")+i" + s(i+5:)="("//safestr(im,fmt)//")" + end function str_complex_sp_fmt +#endif + + pure function str_complex_sp(c) result(s) + complex(sp), intent(in) :: c +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(c, "")) :: s + + s = safestr(c, "") +#endif + end function str_complex_sp + +#ifndef DUMMYLIB + pure function str_complex_sp_array_fmt(ca, fmt) result(s) + complex(sp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt + character(len=len(ca, fmt)) :: s + + integer :: i, n + + s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) + n = len(ca(1), fmt)+1 + do i = 2, size(ca) + s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) + n = n + len(ca(i), fmt)+1 + enddo + end function str_complex_sp_array_fmt +#endif + + function str_complex_sp_array_fmt_chk(ca, fmt) result(s) + complex(sp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(ca, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_sp_array_fmt_chk + + pure function str_complex_sp_array(ca) result(s) + complex(sp), dimension(:), intent(in) :: ca +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca)) :: s + + s = safestr(ca, "") +#endif + end function str_complex_sp_array + +#ifndef DUMMYLIB + pure function str_complex_sp_matrix_fmt(ca, fmt) result(s) + complex(sp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt + character(len=len(ca, fmt)) :: s + + integer :: i, j, k, n + + i = len(ca(1,1), fmt) + s(:i) = safestr(ca(1,1), fmt) + n = i + 1 + do j = 2, size(ca, 1) + i = len(ca(j,1), fmt) + s(n:n+i) = " "//safestr(ca(j,1), fmt) + n = n + i + 1 + enddo + do k = 2, size(ca, 2) + do j = 1, size(ca, 1) + i = len(ca(j,k), fmt) + s(n:n+i) = " "//safestr(ca(j,k), fmt) + n = n + i + 1 + enddo + enddo + + end function str_complex_sp_matrix_fmt +#endif + + function str_complex_sp_matrix_fmt_chk(ca, fmt) result(s) + complex(sp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(ca, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_sp_matrix_fmt_chk + + pure function str_complex_sp_matrix(ca) result(s) + complex(sp), dimension(:, :), intent(in) :: ca +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca)) :: s + + s = safestr(ca, "") +#endif + end function str_complex_sp_matrix + + function str_complex_dp_fmt_chk(c, fmt) result(s) + complex(dp), intent(in) :: c + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(c, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(c, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_dp_fmt_chk + +#ifndef DUMMYLIB + pure function str_complex_dp_fmt(c, fmt) result(s) + complex(dp), intent(in) :: c + character(len=*), intent(in) :: fmt + character(len=len(c, fmt)) :: s + + real(dp) :: re, im + integer :: i + re = real(c) + im = aimag(c) + i = len(re, fmt) + s(:i+4) = "("//safestr(re, fmt)//")+i" + s(i+5:)="("//safestr(im,fmt)//")" + end function str_complex_dp_fmt +#endif + + pure function str_complex_dp(c) result(s) + complex(dp), intent(in) :: c +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(c, "")) :: s + + s = safestr(c, "") +#endif + end function str_complex_dp + +#ifndef DUMMYLIB + pure function str_complex_dp_array_fmt(ca, fmt) result(s) + complex(dp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt + character(len=len(ca, fmt)) :: s + + integer :: i, n + + s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) + n = len(ca(1), fmt)+1 + do i = 2, size(ca) + s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) + n = n + len(ca(i), fmt)+1 + enddo + end function str_complex_dp_array_fmt +#endif + + function str_complex_dp_array_fmt_chk(ca, fmt) result(s) + complex(dp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(ca, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_dp_array_fmt_chk + + pure function str_complex_dp_array(ca) result(s) + complex(dp), dimension(:), intent(in) :: ca +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca)) :: s + + s = safestr(ca, "") +#endif + end function str_complex_dp_array + +#ifndef DUMMYLIB + pure function str_complex_dp_matrix_fmt(ca, fmt) result(s) + complex(dp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt + character(len=len(ca, fmt)) :: s + + integer :: i, j, k, n + + i = len(ca(1,1), fmt) + s(:i) = safestr(ca(1,1), fmt) + n = i + 1 + do j = 2, size(ca, 1) + i = len(ca(j,1), fmt) + s(n:n+i) = " "//safestr(ca(j,1), fmt) + n = n + i + 1 + enddo + do k = 2, size(ca, 2) + do j = 1, size(ca, 1) + i = len(ca(j,k), fmt) + s(n:n+i) = " "//safestr(ca(j,k), fmt) + n = n + i + 1 + enddo + enddo + + end function str_complex_dp_matrix_fmt +#endif + + function str_complex_dp_matrix_fmt_chk(ca, fmt) result(s) + complex(dp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(ca, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_dp_matrix_fmt_chk + + pure function str_complex_dp_matrix(ca) result(s) + complex(dp), dimension(:, :), intent(in) :: ca +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca)) :: s + + s = safestr(ca, "") +#endif + end function str_complex_dp_matrix + +#ifndef DUMMYLIB + pure function checkFmt(fmt) result(good) + character(len=*), intent(in) :: fmt + logical :: good + + ! should be ([rs]\d*)? + + if (len(fmt) > 0) then + if (fmt(1:1) == "r" .or. fmt(1:1) == "s") then + if (len(fmt) > 1) then + good = (verify(fmt(2:), digit) == 0) + else + good = .true. + endif + else + good = .false. + endif + else + good = .true. + endif + end function checkFmt +#endif + + pure function concat_str_int(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + integer, intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_int + pure function concat_int_str(s1, s2) result(s3) + integer, intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_int_str + + pure function concat_str_logical(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + logical, intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_logical + pure function concat_logical_str(s1, s2) result(s3) + logical, intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_logical_str + + pure function concat_str_real_sp(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + real(sp), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_real_sp + pure function concat_real_sp_str(s1, s2) result(s3) + real(sp), intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_real_sp_str + + pure function concat_str_real_dp(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + real(dp), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_real_dp + pure function concat_real_dp_str(s1, s2) result(s3) + real(dp), intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_real_dp_str + + pure function concat_str_complex_sp(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + complex(sp), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_complex_sp + pure function concat_complex_sp_str(s1, s2) result(s3) + complex(sp), intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_complex_sp_str + + pure function concat_str_complex_dp(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + complex(dp), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_complex_dp + pure function concat_complex_dp_str(s1, s2) result(s3) + complex(dp), intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_complex_dp_str + +end module fox_m_fsys_format diff --git a/src/xml/fsys/fox_m_fsys_parse_input.F90 b/src/xml/fsys/fox_m_fsys_parse_input.F90 new file mode 100644 index 0000000000..e0307e99c2 --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_parse_input.F90 @@ -0,0 +1,2204 @@ +module fox_m_fsys_parse_input + + use fox_m_fsys_realtypes, only: sp, dp + + implicit none + private + + character(len=1), parameter :: SPACE = achar(32) + character(len=1), parameter :: NEWLINE = achar(10) + character(len=1), parameter :: CARRIAGE_RETURN = achar(13) + character(len=1), parameter :: TAB = achar(9) + character(len=*), parameter :: whitespace = & + SPACE//NEWLINE//CARRIAGE_RETURN//TAB + + interface rts + module procedure scalartostring + module procedure scalartological + module procedure scalartointeger + module procedure scalartorealsp + module procedure scalartorealdp + module procedure scalartocomplexsp + module procedure scalartocomplexdp + module procedure arraytostring + module procedure arraytological + module procedure arraytointeger + module procedure arraytorealsp + module procedure arraytorealdp + module procedure arraytocomplexsp + module procedure arraytocomplexdp + module procedure matrixtostring + module procedure matrixtological + module procedure matrixtointeger + module procedure matrixtorealsp + module procedure matrixtorealdp + module procedure matrixtocomplexsp + module procedure matrixtocomplexdp + end interface + + public :: rts + +contains + + subroutine scalartostring(s, data, separator, csv, num, iostat) + character(len=*), intent(in) :: s + character(len=*), intent(out) :: data + character, intent(in), optional :: separator + logical, intent(in), optional :: csv + integer, intent(out), optional :: num, iostat +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + character(len=len(s)) :: s2 + logical :: csv_, eof, sp + integer :: m + + csv_ = .false. + if (present(csv)) then + csv_ = csv + endif + + s_i = 1 + err = 0 + eof = .false. + data = "" + ij = 0 + length = 1 + loop: do + if (csv_) then + if (s_i>len(s)) then + data = "" + ij = ij + 1 + exit loop + endif + k = verify(s(s_i:), achar(10)//achar(13)) + if (k==0) then + data = "" + ij = ij + 1 + exit loop + elseif (s(s_i+k-1:s_i+k-1)=="""") then + ! we have a quote-delimited string; + s_i = s_i + k + s2 = "" + quote: do + k = index(s(s_i:), """") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 1 + s2(m:) = s(s_i:k) + m = m + (k-s_i+1) + k = k + 2 + if (k>len(s)) then + err = 2 + exit loop + endif + if (s(k:k)/="""") exit + s_i = k + 1 + if (s_i > len(s)) then + err = 2 + exit loop + endif + m = m + 1 + s2(m:m) = """" + enddo quote + data = s2 + k = scan(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + else + s_i = s_i + k - 1 + k = scan(s(s_i:), achar(10)//achar(13)//",") + if (k==0) then + eof = .true. + k = len(s) + else + if (ij+1==length.and.s(s_i+k-1:s_i+k-1)==",") err = 1 + k = s_i + k - 2 + endif + data = s(s_i:k) + if (index(data, """")/=0) then + err = 2 + exit loop + endif + endif + ij = ij + 1 + s_i = k + 2 + if (eof) exit loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + if (s(s_i:k)=="true".or.s(s_i:k)=="1") then + data = .true. + elseif (s(s_i:k)=="false".or.s(s_i:k)=="0") then + data = .false. + else + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + data(i) = "" + ij = ij + 1 + exit loop + endif + k = verify(s(s_i:), achar(10)//achar(13)) + if (k==0) then + data(i) = "" + ij = ij + 1 + exit loop + elseif (s(s_i+k-1:s_i+k-1)=="""") then + ! we have a quote-delimited string; + s_i = s_i + k + s2 = "" + quote: do + k = index(s(s_i:), """") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 1 + s2(m:) = s(s_i:k) + m = m + (k-s_i+1) + k = k + 2 + if (k>len(s)) then + err = 2 + exit loop + endif + if (s(k:k)/="""") exit + s_i = k + 1 + if (s_i > len(s)) then + err = 2 + exit loop + endif + m = m + 1 + s2(m:m) = """" + enddo quote + data(i) = s2 + k = scan(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + else + s_i = s_i + k - 1 + k = scan(s(s_i:), achar(10)//achar(13)//",") + if (k==0) then + eof = .true. + k = len(s) + else + if (ij+1==length.and.s(s_i+k-1:s_i+k-1)==",") err = 1 + k = s_i + k - 2 + endif + data(i) = s(s_i:k) + if (index(data(i), """")/=0) then + err = 2 + exit loop + endif + endif + ij = ij + 1 + s_i = k + 2 + if (eof) exit loop + + else + if (present(separator)) then + k = index(s(s_i:), separator) + else + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + k = scan(s(s_i:), whitespace) + endif + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + data(i) = s(s_i:k) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + endif + end do loop + + if (present(num)) num = ij + if (ij=0) & + err = 1 + else + if (verify(s(s_i:), whitespace)/=0) & + err = 1 + endif + endif + + if (present(iostat)) then + iostat = err + else + select case (err) + case(-1) + write(0, *) "Error in arraytostring" + write(0, *) "Too few elements found" + stop + case(1) + write(0, *) "Error in arraytostring" + write(0, *) "Too many elements found" + stop + case(2) + write(0, *) "Error in arraytostring" + write(0, *) "Malformed input" + stop + end select + end if + +#else + data = "" +#endif + end subroutine arraytostring + + subroutine matrixtostring(s, data, separator, csv, num, iostat) + character(len=*) :: data(:,:) + character, intent(in), optional :: separator + logical, intent(in), optional :: csv + character(len=*), intent(in) :: s + integer, intent(out), optional :: num + integer, intent(out), optional :: iostat + +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + character(len=len(s)) :: s2 + logical :: csv_, eof + integer :: m + + csv_ = .false. + if (present(csv)) then + if (csv) csv_ = csv + endif + + s_i = 1 + err = 0 + eof = .false. + data = "" + ij = 0 + length = size(data) + loop: do j = 1, size(data, 2) + do i = 1, size(data, 1) + if (csv_) then + if (s_i>len(s)) then + data(i, j) = "" + ij = ij + 1 + exit loop + endif + k = verify(s(s_i:), achar(10)//achar(13)) + if (k==0) then + data(i, j) = "" + ij = ij + 1 + exit loop + elseif (s(s_i+k-1:s_i+k-1)=="""") then + ! we have a quote-delimited string; + s_i = s_i + k + s2 = "" + quote: do + k = index(s(s_i:), """") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 1 + s2(m:) = s(s_i:k) + m = m + (k-s_i+1) + k = k + 2 + if (k>len(s)) then + err = 2 + exit loop + endif + if (s(k:k)/="""") exit + s_i = k + 1 + if (s_i > len(s)) then + err = 2 + exit loop + endif + m = m + 1 + s2(m:m) = """" + enddo quote + data(i, j) = s2 + k = scan(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + else + s_i = s_i + k - 1 + k = scan(s(s_i:), achar(10)//achar(13)//",") + if (k==0) then + eof = .true. + k = len(s) + else + if (ij+1==length.and.s(s_i+k-1:s_i+k-1)==",") err = 1 + k = s_i + k - 2 + endif + data(i, j) = s(s_i:k) + if (index(data(i, j), """")/=0) then + err = 2 + exit loop + endif + endif + ij = ij + 1 + s_i = k + 2 + if (eof) exit loop + else + if (present(separator)) then + k = index(s(s_i:), separator) + else + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + k = scan(s(s_i:), whitespace) + endif + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + data(i, j) = s(s_i:k) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + endif + end do + end do loop + + if (present(num)) num = ij + if (ij=0) & + err = 1 + else + if (verify(s(s_i:), whitespace)/=0) & + err = 1 + endif + endif + + if (present(iostat)) then + iostat = err + else + select case (err) + case(-1) + write(0, *) "Error in matrixtostring" + write(0, *) "Too few elements found" + stop + case(1) + write(0, *) "Error in matrixtostring" + write(0, *) "Too many elements found" + stop + case(2) + write(0, *) "Error in matrixtostring" + write(0, *) "Malformed input" + stop + end select + end if + +#else + data = "" +#endif + end subroutine matrixtostring + + subroutine arraytological(s, data, num, iostat) + logical, intent(out) :: data(:) + character(len=*), intent(in) :: s + integer, intent(out), optional :: num + integer, intent(out), optional :: iostat + +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + data = .false. + ij = 0 + length = size(data) + loop: do i = 1, size(data) + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + if (s(s_i:k)=="true".or.s(s_i:k)=="1") then + data(i) = .true. + elseif (s(s_i:k)=="false".or.s(s_i:k)=="0") then + data(i) = .false. + else + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + if (s(s_i:k)=="true".or.s(s_i:k)=="1") then + data(i, j) = .true. + elseif (s(s_i:k)=="false".or.s(s_i:k)=="0") then + data(i, j) = .false. + else + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i, j) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i, j) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i, j) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data(i) = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data(i, j) = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data(i) = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data(i, j) = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ij0) then + s(i:i) = lowerAlphabet(n:n) + else + s(i:i) = s_in(i:i) + endif + enddo + + end function toLower + +#endif +end module fox_m_fsys_string diff --git a/src/xml/fsys/fox_m_fsys_string_list.F90 b/src/xml/fsys/fox_m_fsys_string_list.F90 new file mode 100644 index 0000000000..ecf407483c --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_string_list.F90 @@ -0,0 +1,191 @@ +module fox_m_fsys_string_list +#ifndef DUMMYLIB + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc + implicit none + private + + type string_t + character, pointer :: s(:) => null() + end type string_t + + type string_list + type(string_t), pointer :: list(:) => null() + end type string_list + + public :: string_t + public :: string_list + + public :: init_string_list + public :: destroy_string_list + public :: add_string + public :: remove_last_string + public :: get_last_string + public :: tokenize_to_string_list + public :: tokenize_and_add_strings + public :: registered_string + + interface destroy + module procedure destroy_string_list + end interface + + public :: destroy + +contains + + subroutine init_string_list(s_list) + type(string_list), intent(inout) :: s_list + + allocate(s_list%list(0)) + end subroutine init_string_list + + subroutine destroy_string_list(s_list) + type(string_list), intent(inout) :: s_list + + integer :: i + + if (associated(s_list%list)) then + do i = 1, ubound(s_list%list, 1) + deallocate(s_list%list(i)%s) + enddo + deallocate(s_list%list) + endif + end subroutine destroy_string_list + + subroutine add_string(s_list, s) + type(string_list), intent(inout) :: s_list + character(len=*), intent(in) :: s + + integer :: i + type(string_t), pointer :: temp(:) + + temp => s_list%list + allocate(s_list%list(size(temp)+1)) + do i = 1, size(temp) + s_list%list(i)%s => temp(i)%s + enddo + deallocate(temp) + s_list%list(i)%s => vs_str_alloc(s) + end subroutine add_string + + subroutine remove_last_string(s_list) + type(string_list), intent(inout) :: s_list + + integer :: i + type(string_t), pointer :: temp(:) + + temp => s_list%list + allocate(s_list%list(size(temp)-1)) + do i = 1, size(temp)-1 + s_list%list(i)%s => temp(i)%s + enddo + deallocate(temp) + + end subroutine remove_last_string + + function get_last_string(s_list) result(s) + type(string_list), intent(in) :: s_list + character(len=size(s_list%list(size(s_list%list))%s)) :: s + + s = str_vs(s_list%list(size(s_list%list))%s) + end function get_last_string + + function tokenize_to_string_list(s) result(s_list) + character(len=*), intent(in) :: s + type(string_list) :: s_list + + ! tokenize a whitespace-separated list of strings + ! and place results in a string list + + character(len=*), parameter :: & + WHITESPACE = achar(9)//achar(10)//achar(13)//achar(32) + integer :: i, j + + call init_string_list(s_list) + + i = verify(s, WHITESPACE) + if (i==0) return + j = scan(s(i:), WHITESPACE) + if (j==0) then + j = len(s) + else + j = i + j - 2 + endif + do + call add_string(s_list, s(i:j)) + i = j + 1 + j = verify(s(i:), WHITESPACE) + if (j==0) exit + i = i + j - 1 + j = scan(s(i:), WHITESPACE) + if (j==0) then + j = len(s) + else + j = i + j - 2 + endif + enddo + + end function tokenize_to_string_list + + function registered_string(s_list, s) result(p) + type(string_list), intent(in) :: s_list + character(len=*), intent(in) :: s + logical :: p + + integer :: i + + p = .false. + do i = 1, size(s_list%list) + if (str_vs(s_list%list(i)%s)//"x"==s//"x") then + p = .true. + exit + endif + enddo + end function registered_string + + subroutine tokenize_and_add_strings(s_list, s, uniquify) + type(string_list), intent(inout) :: s_list + character(len=*), intent(in) :: s + logical, intent(in), optional :: uniquify + + ! tokenize a whitespace-separated list of strings + ! and place results in the given string list + + character(len=*), parameter :: & + WHITESPACE = achar(9)//achar(10)//achar(13)//achar(32) + integer :: i, j + logical :: uniquify_ + + if (present(uniquify)) then + uniquify_ = uniquify + else + uniquify_ = .false. + endif + + i = verify(s, WHITESPACE) + if (i==0) return + j = scan(s(i:), WHITESPACE) + if (j==0) then + j = len(s) + else + j = i + j - 2 + endif + do + if (uniquify_.and..not.registered_string(s_list, s(i:j))) & + call add_string(s_list, s(i:j)) + i = j + 1 + j = verify(s(i:), WHITESPACE) + if (j==0) exit + i = i + j - 1 + j = scan(s(i:), WHITESPACE) + if (j==0) then + j = len(s) + else + j = i + j - 2 + endif + enddo + + end subroutine tokenize_and_add_strings + +#endif +end module fox_m_fsys_string_list diff --git a/src/xml/fsys/m_ieee.F90 b/src/xml/fsys/m_ieee.F90 new file mode 100644 index 0000000000..afec6f0807 --- /dev/null +++ b/src/xml/fsys/m_ieee.F90 @@ -0,0 +1,17 @@ +module m_ieee + + implicit none + private + + public :: generate_nan + +contains + + function generate_nan() result(nan) + real :: nan + real :: zero + zero = 0.0 + nan = 0.0/zero + end function generate_nan + +end module m_ieee diff --git a/src/xml/fsys/makefile b/src/xml/fsys/makefile new file mode 100644 index 0000000000..d6b81cc870 --- /dev/null +++ b/src/xml/fsys/makefile @@ -0,0 +1,47 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +fox_m_fsys_format.o: fox_m_fsys_realtypes.o fox_m_fsys_abort_flush.o +fox_m_fsys_parse_input.o: fox_m_fsys_realtypes.o +fox_m_fsys_count_parse_input.o: fox_m_fsys_realtypes.o +fox_m_fsys_string_list.o: fox_m_fsys_array_str.o diff --git a/src/xml/sax/FoX_sax.F90 b/src/xml/sax/FoX_sax.F90 new file mode 100644 index 0000000000..17dc2280ed --- /dev/null +++ b/src/xml/sax/FoX_sax.F90 @@ -0,0 +1,34 @@ +module FoX_sax + + use FoX_common + use m_sax_operate + + implicit none + private + + public :: open_xml_file + public :: open_xml_string + public :: close_xml_t + public :: parse + public :: stop_parser + public :: SAX_OPEN_ERROR + + public :: xml_t + + public :: dictionary_t +!SAX functions + public :: getIndex + public :: getLength + public :: getLocalName + public :: getQName + public :: getURI + public :: getValue + public :: getType + public :: isSpecified + public :: isDeclared + public :: setSpecified + public :: setDeclared +!For convenience + public :: hasKey + +end module FoX_sax diff --git a/src/xml/sax/m_sax_operate.F90 b/src/xml/sax/m_sax_operate.F90 new file mode 100644 index 0000000000..531618ad28 --- /dev/null +++ b/src/xml/sax/m_sax_operate.F90 @@ -0,0 +1,313 @@ +module m_sax_operate + +#ifndef DUMMYLIB + use m_common_error, only: FoX_error, in_error + use FoX_common, only : str_vs + + use m_sax_reader, only: open_file, close_file + use m_sax_parser, only: sax_parser_init, sax_parser_destroy, sax_parse + use m_sax_types, only: ST_STOP +#endif + use m_sax_types, only: xml_t + + implicit none + private + + integer, parameter :: SAX_OPEN_ERROR = 1001 + + public :: xml_t + public :: open_xml_file + public :: open_xml_string + public :: close_xml_t + public :: parse + public :: stop_parser + public :: SAX_OPEN_ERROR + +contains + + subroutine open_xml_file(xt, file, iostat, lun) + type(xml_t), intent(out) :: xt + character(len=*), intent(in) :: file + integer, intent(out), optional :: iostat + integer, intent(in), optional :: lun +#ifdef DUMMYLIB + if (present(iostat)) iostat = 0 +#else + integer :: i + + call open_file(xt%fb, file=trim(file), iostat=i, lun=lun, es=xt%fx%error_stack) + if (present(iostat)) then + if (in_error(xt%fx%error_stack)) i = SAX_OPEN_ERROR + iostat = i + if (i/=0) return + else + if (i/=0) & + call FoX_error("Error opening file in open_xml_file") + if (in_error(xt%fx%error_stack)) & + call FoX_error(str_vs(xt%fx%error_stack%stack(1)%msg)) + endif + + if (i==0) call sax_parser_init(xt%fx, xt%fb) +#endif + end subroutine open_xml_file + + subroutine open_xml_string(xt, string) + type(xml_t), intent(out) :: xt + character(len=*), intent(in) :: string +#ifndef DUMMYLIB + integer :: iostat + + call open_file(xt%fb, string=string, iostat=iostat, es=xt%fx%error_stack) + call sax_parser_init(xt%fx, xt%fb) +#endif + end subroutine open_xml_string + + subroutine close_xml_t(xt) + type(xml_t), intent(inout) :: xt +#ifndef DUMMYLIB + call close_file(xt%fb) + call sax_parser_destroy(xt%fx) +#endif + end subroutine close_xml_t + + + subroutine parse(xt, & + characters_handler, & + endDocument_handler, & + endElement_handler, & + endPrefixMapping_handler, & + ignorableWhitespace_handler, & + processingInstruction_handler, & + ! setDocumentLocator + skippedEntity_handler, & + startDocument_handler, & + startElement_handler, & + startPrefixMapping_handler, & + notationDecl_handler, & + unparsedEntityDecl_handler, & + error_handler, & + fatalError_handler, & + warning_handler, & + attributeDecl_handler, & + elementDecl_handler, & + externalEntityDecl_handler, & + internalEntityDecl_handler, & + comment_handler, & + endCdata_handler, & + endDTD_handler, & + endEntity_handler, & + startCdata_handler, & + startDTD_handler, & + startEntity_handler, & +! Features / properties + namespaces, & + namespace_prefixes, & + validate, & + xmlns_uris) + + type(xml_t), intent(inout) :: xt + optional :: characters_handler + optional :: endDocument_handler + optional :: endElement_handler + optional :: endPrefixMapping_handler + optional :: ignorableWhitespace_handler + optional :: processingInstruction_handler + optional :: skippedEntity_handler + optional :: startElement_handler + optional :: startDocument_handler + optional :: startPrefixMapping_handler + optional :: notationDecl_handler + optional :: unparsedEntityDecl_handler + optional :: error_handler + optional :: fatalError_handler + optional :: warning_handler + optional :: attributeDecl_handler + optional :: elementDecl_handler + optional :: externalEntityDecl_handler + optional :: internalEntityDecl_handler + optional :: comment_handler + optional :: endCdata_handler + optional :: endEntity_handler + optional :: endDTD_handler + optional :: startCdata_handler + optional :: startDTD_handler + optional :: startEntity_handler + + logical, intent(in), optional :: namespaces + logical, intent(in), optional :: namespace_prefixes + logical, intent(in), optional :: validate + logical, intent(in), optional :: xmlns_uris + + interface + + subroutine characters_handler(chunk) + character(len=*), intent(in) :: chunk + end subroutine characters_handler + + subroutine endDocument_handler() + end subroutine endDocument_handler + + subroutine endElement_handler(namespaceURI, localName, name) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + character(len=*), intent(in) :: name + end subroutine endElement_handler + + subroutine endPrefixMapping_handler(prefix) + character(len=*), intent(in) :: prefix + end subroutine endPrefixMapping_handler + + subroutine ignorableWhitespace_handler(chars) + character(len=*), intent(in) :: chars + end subroutine ignorableWhitespace_handler + + subroutine processingInstruction_handler(name, content) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: content + end subroutine processingInstruction_handler + + subroutine skippedEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine skippedEntity_handler + + subroutine startDocument_handler() + end subroutine startDocument_handler + + subroutine startElement_handler(namespaceURI, localName, name, attributes) + use FoX_common + character(len=*), intent(in) :: namespaceUri + character(len=*), intent(in) :: localName + character(len=*), intent(in) :: name + type(dictionary_t), intent(in) :: attributes + end subroutine startElement_handler + + subroutine startPrefixMapping_handler(namespaceURI, prefix) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: prefix + end subroutine startPrefixMapping_handler + + subroutine notationDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine notationDecl_handler + + subroutine unparsedEntityDecl_handler(name, publicId, systemId, notation) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + character(len=*), intent(in) :: notation + end subroutine unparsedEntityDecl_handler + + subroutine error_handler(msg) + character(len=*), intent(in) :: msg + end subroutine error_handler + + subroutine fatalError_handler(msg) + character(len=*), intent(in) :: msg + end subroutine fatalError_handler + + subroutine warning_handler(msg) + character(len=*), intent(in) :: msg + end subroutine warning_handler + + subroutine attributeDecl_handler(eName, aName, type, mode, value) + character(len=*), intent(in) :: eName + character(len=*), intent(in) :: aName + character(len=*), intent(in) :: type + character(len=*), intent(in), optional :: mode + character(len=*), intent(in), optional :: value + end subroutine attributeDecl_handler + + subroutine elementDecl_handler(name, model) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: model + end subroutine elementDecl_handler + + subroutine externalEntityDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine externalEntityDecl_handler + + subroutine internalEntityDecl_handler(name, value) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + end subroutine internalEntityDecl_handler + + subroutine comment_handler(comment) + character(len=*), intent(in) :: comment + end subroutine comment_handler + + subroutine endCdata_handler() + end subroutine endCdata_handler + + subroutine endDTD_handler() + end subroutine endDTD_handler + + subroutine endEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine endEntity_handler + + subroutine startCdata_handler() + end subroutine startCdata_handler + + subroutine startDTD_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine startDTD_handler + + subroutine startEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine startEntity_handler + + end interface +#ifndef DUMMYLIB + ! FIXME check xt is initialized + + call sax_parse(xt%fx, xt%fb, & + characters_handler, & + endDocument_handler, & + endElement_handler, & + endPrefixMapping_handler, & + ignorableWhitespace_handler, & + processingInstruction_handler, & + ! setDocumentLocator + skippedEntity_handler, & + startDocument_handler, & + startElement_handler, & + startPrefixMapping_handler, & + notationDecl_handler, & + unparsedEntityDecl_handler, & + error_handler, & + fatalError_handler, & + warning_handler, & + attributeDecl_handler, & + elementDecl_handler, & + externalEntityDecl_handler, & + internalEntityDecl_handler, & + comment_handler, & + endCdata_handler, & + endDTD_handler, & + endEntity_handler, & + startCdata_handler, & + startDTD_handler, & + startEntity_handler, & + namespaces=namespaces, & + namespace_prefixes=namespace_prefixes, & + validate=validate, & + xmlns_uris=xmlns_uris) +#endif + end subroutine parse + + subroutine stop_parser(xt) + ! To be called from within a callback function; + ! this will stop the parser. + type(xml_t), intent(inout) :: xt +#ifndef DUMMYLIB + xt%fx%state = ST_STOP +#endif + end subroutine stop_parser + +end module m_sax_operate diff --git a/src/xml/sax/m_sax_parser.F90 b/src/xml/sax/m_sax_parser.F90 new file mode 100644 index 0000000000..6d63fd046c --- /dev/null +++ b/src/xml/sax/m_sax_parser.F90 @@ -0,0 +1,3027 @@ +module m_sax_parser + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + use fox_m_fsys_string_list, only: string_list, destroy_string_list, & + tokenize_to_string_list, registered_string, init_string_list, & + add_string, tokenize_and_add_strings, destroy + use m_common_attrs, only: init_dict, destroy_dict, reset_dict, & + add_item_to_dict, has_key, get_value, get_att_index_pointer, & + getLength, setIsId, setBase + use m_common_charset, only: XML1_0, XML1_1, XML_WHITESPACE + use m_common_element, only: element_t, existing_element, add_element, & + get_element, parse_dtd_element, parse_dtd_attlist, report_declarations, & + declared_element, attribute_t, att_value_normalize, & + get_attribute_declaration, & + ATT_CDATA, ATT_ID, ATT_IDREF, ATT_IDREFS, ATT_ENTITY, ATT_ENTITIES, & + ATT_NMTOKEN, ATT_NMTOKENS, ATT_NOTATION, ATT_ENUM, & + ATT_REQUIRED, ATT_IMPLIED, ATT_DEFAULT, ATT_FIXED + use m_common_elstack, only: push_elstack, pop_elstack, init_elstack, & + destroy_elstack, is_empty, len, get_top_elstack, checkContentModel, & + elementContent, emptyContent, checkContentModelToEnd + use m_common_entities, only: existing_entity, init_entity_list, & + destroy_entity_list, add_internal_entity, is_unparsed_entity, & + expand_entity, expand_char_entity, pop_entity_list, size, & + entity_t, getEntityByIndex, getEntityByName + use m_common_entity_expand, only: expand_entity_value_alloc + use m_common_error, only: FoX_error, add_error, & + init_error_stack, destroy_error_stack, in_error + use m_common_namecheck, only: checkName, checkPublicId, & + checkCharacterEntityReference, likeCharacterEntityReference, & + checkQName, checkNCName, checkPITarget, checkNmtoken, checkNmtokens, & + checkRepCharEntityReference, checkNames, checkNCNames + use m_common_namespaces, only: getnamespaceURI, invalidNS, & + checkNamespaces, checkEndNamespaces, namespaceDictionary, & + initNamespaceDictionary, destroyNamespaceDictionary + use m_common_notations, only: init_notation_list, destroy_notation_list, & + add_notation, notation_exists + use m_common_struct, only: init_xml_doc_state, & + destroy_xml_doc_state, register_internal_PE, register_external_PE, & + register_internal_GE, register_external_GE + + use FoX_utils, only: URI, parseURI, rebaseURI, copyURI, destroyURI, & + hasFragment, expressURI + + use m_sax_reader, only: file_buffer_t, pop_buffer_stack, open_new_string, & + open_new_file, parse_xml_declaration, parse_text_declaration, & + reading_main_file, reading_first_entity + use m_sax_tokenizer, only: sax_tokenize, normalize_attribute_text, & + expand_pe_text + use m_sax_types ! everything, really + + implicit none + private + + public :: getNSDict + + public :: sax_parser_init + public :: sax_parser_destroy + public :: sax_parse + +contains + + function getNSDict(fx) result(ns) + type(sax_parser_t), target :: fx + type(namespaceDictionary), pointer :: ns + + ns => fx%nsDict + end function getNSDict + + subroutine sax_parser_init(fx, fb) + type(sax_parser_t), intent(out) :: fx + type(file_buffer_t), intent(in) :: fb +#ifdef PGF90 + type(URI), pointer :: nullURI + + nullURI => null() +#endif + + allocate(fx%token(0)) + + call init_error_stack(fx%error_stack) + call init_elstack(fx%elstack) + call init_dict(fx%attributes) + + call initNamespaceDictionary(fx%nsdict) + call init_notation_list(fx%nlist) + ! FIXME do we copy correctly from fx%nlist to fx%xds%nlist? + allocate(fx%xds) + call init_xml_doc_state(fx%xds) + deallocate(fx%xds%inputEncoding) + fx%xds%inputEncoding => vs_str_alloc("us-ascii") + ! because it always is ... + if (fb%f(1)%lun>0) then + fx%xds%documentURI => vs_vs_alloc(fb%f(1)%filename) + else + fx%xds%documentURI => vs_str_alloc("") + endif + + fx%xds%standalone = fb%standalone + + call init_entity_list(fx%forbidden_ge_list) + call init_entity_list(fx%forbidden_pe_list) + call init_entity_list(fx%predefined_e_list) + +#ifdef PGF90 + call add_internal_entity(fx%predefined_e_list, 'amp', '&', nullURI, .false.) + call add_internal_entity(fx%predefined_e_list, 'lt', '<', nullURI, .false.) + call add_internal_entity(fx%predefined_e_list, 'gt', '>', nullURI, .false.) + call add_internal_entity(fx%predefined_e_list, 'apos', "'", nullURI, .false.) + call add_internal_entity(fx%predefined_e_list, 'quot', '"', nullURI, .false.) +#else + call add_internal_entity(fx%predefined_e_list, 'amp', '&', null(), .false.) + call add_internal_entity(fx%predefined_e_list, 'lt', '<', null(), .false.) + call add_internal_entity(fx%predefined_e_list, 'gt', '>', null(), .false.) + call add_internal_entity(fx%predefined_e_list, 'apos', "'", null(), .false.) + call add_internal_entity(fx%predefined_e_list, 'quot', '"', null(), .false.) +#endif + end subroutine sax_parser_init + + subroutine sax_parser_destroy(fx) + type(sax_parser_t), intent(inout) :: fx + + fx%context = CTXT_NULL + fx%state = ST_NULL + + if (associated(fx%token)) deallocate(fx%token) + if (associated(fx%root_element)) deallocate(fx%root_element) + + call destroy_error_stack(fx%error_stack) + call destroy_elstack(fx%elstack) + call destroy_dict(fx%attributes) + call destroyNamespaceDictionary(fx%nsdict) + call destroy_notation_list(fx%nlist) + if (.not.fx%xds_used) then + call destroy_xml_doc_state(fx%xds) + deallocate(fx%xds) + endif + + call destroy_entity_list(fx%forbidden_ge_list) + call destroy_entity_list(fx%forbidden_pe_list) + call destroy_entity_list(fx%predefined_e_list) + + if (associated(fx%token)) deallocate(fx%token) + if (associated(fx%content)) deallocate(fx%content) + if (associated(fx%name)) deallocate(fx%name) + if (associated(fx%attname)) deallocate(fx%attname) + if (associated(fx%publicId)) deallocate(fx%publicId) + if (associated(fx%systemId)) deallocate(fx%systemId) + if (associated(fx%Ndata)) deallocate(fx%Ndata) + + end subroutine sax_parser_destroy + + recursive subroutine sax_parse(fx, fb, & + ! org.xml.sax + ! SAX ContentHandler + characters_handler, & + endDocument_handler, & + endElement_handler, & + endPrefixMapping_handler, & + ignorableWhitespace_handler, & + processingInstruction_handler, & + ! setDocumentLocator + skippedEntity_handler, & + startDocument_handler, & + startElement_handler, & + startPrefixMapping_handler, & + ! SAX DTDHandler + notationDecl_handler, & + unparsedEntityDecl_handler, & + ! SAX ErrorHandler + error_handler, & + fatalError_handler, & + warning_handler, & + ! org.xml.sax.ext + ! SAX DeclHandler + attributeDecl_handler, & + elementDecl_handler, & + externalEntityDecl_handler, & + internalEntityDecl_handler, & + ! SAX LexicalHandler + comment_handler, & + endCdata_handler, & + endDTD_handler, & + endEntity_handler, & + startCdata_handler, & + startDTD_handler, & + startEntity_handler, & + namespaces, & + namespace_prefixes, & + xmlns_uris, & + validate, & + FoX_endDTD_handler, & + startInCharData, & + externalEntity, & + xmlVersion, & + initial_entities) + + type(sax_parser_t), intent(inout) :: fx + type(file_buffer_t), intent(inout) :: fb + optional :: characters_handler + optional :: endDocument_handler + optional :: endElement_handler + optional :: endPrefixMapping_handler + optional :: ignorableWhitespace_handler + optional :: processingInstruction_handler + optional :: skippedEntity_handler + optional :: startElement_handler + optional :: startDocument_handler + optional :: startPrefixMapping_handler + optional :: notationDecl_handler + optional :: unparsedEntityDecl_handler + optional :: error_handler + optional :: fatalError_handler + optional :: warning_handler + optional :: attributeDecl_handler + optional :: elementDecl_handler + optional :: externalEntityDecl_handler + optional :: internalEntityDecl_handler + optional :: comment_handler + optional :: endCdata_handler + optional :: endEntity_handler + optional :: endDTD_handler + optional :: FoX_endDTD_handler + optional :: startCdata_handler + optional :: startDTD_handler + optional :: startEntity_handler + + logical, intent(in), optional :: namespaces + logical, intent(in), optional :: namespace_prefixes + logical, intent(in), optional :: xmlns_uris + + logical, intent(in), optional :: validate + logical, intent(in), optional :: startInCharData + logical, intent(in), optional :: externalEntity + character(len=*), intent(in), optional :: xmlVersion + + type(entity_list), optional :: initial_entities +#ifdef PGF90 + type(URI), pointer :: nullURI +#endif + + interface + + subroutine characters_handler(chunk) + character(len=*), intent(in) :: chunk + end subroutine characters_handler + + subroutine endDocument_handler() + end subroutine endDocument_handler + + subroutine endElement_handler(namespaceURI, localName, name) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + character(len=*), intent(in) :: name + end subroutine endElement_handler + + subroutine endPrefixMapping_handler(prefix) + character(len=*), intent(in) :: prefix + end subroutine endPrefixMapping_handler + + subroutine ignorableWhitespace_handler(chars) + character(len=*), intent(in) :: chars + end subroutine ignorableWhitespace_handler + + subroutine processingInstruction_handler(name, content) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: content + end subroutine processingInstruction_handler + + subroutine skippedEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine skippedEntity_handler + + subroutine startDocument_handler() + end subroutine startDocument_handler + + subroutine startElement_handler(namespaceURI, localName, name, attributes) + use FoX_common + character(len=*), intent(in) :: namespaceUri + character(len=*), intent(in) :: localName + character(len=*), intent(in) :: name + type(dictionary_t), intent(in) :: attributes + end subroutine startElement_handler + + subroutine startPrefixMapping_handler(namespaceURI, prefix) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: prefix + end subroutine startPrefixMapping_handler + + subroutine notationDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine notationDecl_handler + + subroutine unparsedEntityDecl_handler(name, publicId, systemId, notation) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + character(len=*), intent(in) :: notation + end subroutine unparsedEntityDecl_handler + + subroutine error_handler(msg) + character(len=*), intent(in) :: msg + end subroutine error_handler + + subroutine fatalError_handler(msg) + character(len=*), intent(in) :: msg + end subroutine fatalError_handler + + subroutine warning_handler(msg) + character(len=*), intent(in) :: msg + end subroutine warning_handler + + subroutine attributeDecl_handler(eName, aName, type, mode, value) + character(len=*), intent(in) :: eName + character(len=*), intent(in) :: aName + character(len=*), intent(in) :: type + character(len=*), intent(in), optional :: mode + character(len=*), intent(in), optional :: value + end subroutine attributeDecl_handler + + subroutine elementDecl_handler(name, model) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: model + end subroutine elementDecl_handler + + subroutine externalEntityDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine externalEntityDecl_handler + + subroutine internalEntityDecl_handler(name, value) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + end subroutine internalEntityDecl_handler + + subroutine comment_handler(comment) + character(len=*), intent(in) :: comment + end subroutine comment_handler + + subroutine endCdata_handler() + end subroutine endCdata_handler + + subroutine endDTD_handler() + end subroutine endDTD_handler + + subroutine FoX_endDTD_handler(state) + use m_common_struct, only: xml_doc_state + type(xml_doc_state), pointer :: state + end subroutine FoX_endDTD_handler + + subroutine endEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine endEntity_handler + + subroutine startCdata_handler() + end subroutine startCdata_handler + + subroutine startDTD_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine startDTD_handler + + subroutine startEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine startEntity_handler + + end interface + + logical :: validCheck, startInCharData_, processDTD, pe, nameOK, eof + logical :: namespaces_, namespace_prefixes_, xmlns_uris_, externalEntity_ + integer :: i, iostat, temp_i, nextState, ignoreDepth, declSepValue + character, pointer :: tempString(:) + character :: dummy + type(element_t), pointer :: elem + type(attribute_t), pointer :: attDecl + type(entity_t), pointer :: ent + type(URI), pointer :: extSubsetURI, URIref, newURI + integer, pointer :: wf_stack(:), temp_wf_stack(:), extEntStack(:) + logical :: inExtSubset + type(string_list) :: id_list, idref_list + +#ifdef PGF90 + nullURI => null() +#endif + tempString => null() + elem => null() + attDecl => null() + + if (present(namespaces)) then + namespaces_ = namespaces + else + namespaces_ = .true. + endif + if (present(namespace_prefixes)) then + namespace_prefixes_ = namespace_prefixes + else + namespace_prefixes_ = .false. + endif + if (present(xmlns_uris)) then + xmlns_uris_ = xmlns_uris + else + xmlns_uris_ = .false. + endif + if (present(validate)) then + validCheck = validate + else + validCheck = .false. + endif + if (present(startInCharData)) then + startInCharData_ = startInCharData + else + startInCharData_ = .false. + endif + if (present(externalEntity)) then + externalEntity_ = externalEntity + else + externalEntity_ = .false. + endif + if (present(initial_entities)) then + do i = 1, size(initial_entities) + ent => getEntityByIndex(initial_entities, i) + if (ent%external) then + call register_external_GE(fx%xds, & + name=str_vs(ent%name), systemId=str_vs(ent%systemId), & + publicId=str_vs(ent%publicId), & + wfc=ent%wfc, baseURI=copyURI(ent%baseURI)) + else + call register_internal_GE(fx%xds, & + name=str_vs(ent%name), text=str_vs(ent%text), & + wfc=ent%wfc, baseURI=copyURI(ent%baseURI)) + endif + enddo + endif + + allocate(wf_stack(1)) + wf_stack(1) = 0 + allocate(extEntStack(0)) + fx%inIntSubset = .false. + extSubsetURI => null() + inExtSubset = .false. + declSepValue = 0 + processDTD = .true. + iostat = 0 + + if (startInCharData_) then + fx%context = CTXT_IN_CONTENT + fx%state = ST_CHAR_IN_CONTENT + fx%well_formed = .true. + if (externalEntity_) call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + if (present(xmlVersion)) then + if (xmlVersion=="1.0") then + fx%xds%xml_version = XML1_0 + elseif (xmlVersion=="1.1") then + fx%xds%xml_version = XML1_1 + endif + endif + elseif (reading_main_file(fb)) then + fx%context = CTXT_BEFORE_DTD + fx%state = ST_MISC + if (present(startDocument_handler)) then + call startDocument_handler() + if (fx%state==ST_STOP) goto 100 + endif + call parse_xml_declaration(fb, fx%xds%xml_version, fx%xds%encoding, fx%xds%standalone, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + call init_string_list(id_list) + call init_string_list(idref_list) + endif + + do + call sax_tokenize(fx, fb, eof) + if (in_error(fx%error_stack)) then + ! Any error, we want to quit sax_tokenizer + call add_error(fx%error_stack, 'Error getting token') + goto 100 + elseif (eof.and..not.reading_main_file(fb)) then + if (inExtSubset.and.reading_first_entity(fb)) then + if (wf_stack(1)>0) then + call add_error(fx%error_stack, & + "Unclosed conditional section or markup in external subset") + goto 100 + elseif (fx%state_dtd/=ST_DTD_SUBSET) then + call add_error(fx%error_stack, & + "Markup not terminated in external subset") + goto 100 + endif + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + inExtSubset = .false. + fx%state = ST_MISC + fx%context = CTXT_BEFORE_CONTENT + elseif (fx%context==CTXT_IN_DTD) then + if (validCheck) then + if (wf_stack(1)/=0) then + call add_error(fx%error_stack, & + "Markup not terminated in parameter entity") + goto 100 + endif + endif + if (declSepValue==size(wf_stack)) then + if (wf_stack(1)/=0) then + call add_error(fx%error_stack, & + "Markup not terminated in parameter entity") + goto 100 + else + declSepValue = 0 + endif + endif + if (present(endEntity_handler)) then + call endEntity_handler('%'//pop_entity_list(fx%forbidden_pe_list)) + if (fx%state==ST_STOP) goto 100 + else + dummy = pop_entity_list(fx%forbidden_pe_list) + endif + if (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & + .or.fx%state_dtd==ST_DTD_ELEMENT_CONTENTS) then + ! stick the token back in contents ... + fx%content => fx%token + fx%token => vs_str_alloc("") + endif + if (reading_main_file(fb)) & + fx%inIntSubset = .true. + elseif (fx%context==CTXT_IN_CONTENT) then + if (fx%state==ST_TAG_IN_CONTENT) fx%state = ST_CHAR_IN_CONTENT + ! because CHAR_IN_CONTENT *always* leads to TAG_IN_CONTENT + ! *except* when it is the end of an entity expansion + if (present(endEntity_handler)) then + call endEntity_handler(pop_entity_list(fx%forbidden_ge_list)) + if (fx%state==ST_STOP) goto 100 + else + dummy = pop_entity_list(fx%forbidden_ge_list) + endif + if (fx%state/=ST_CHAR_IN_CONTENT.or.wf_stack(1)/=0) then + call add_error(fx%error_stack, 'Ill-formed entity') + goto 100 + endif + endif + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)-1)) + wf_stack = temp_wf_stack(2:) + ! If we are not doing validity checking, we might have + ! finished PE expansion with wf_stack(1) non-zero + wf_stack(1) = wf_stack(1) + temp_wf_stack(1) + deallocate(temp_wf_stack) + temp_wf_stack => extEntStack + allocate(extEntStack(size(temp_wf_stack)-1)) + extEntStack = temp_wf_stack(2:) + deallocate(temp_wf_stack) + call pop_buffer_stack(fb) + cycle + endif + if (fx%tokenType==TOK_NULL) then + call add_error(fx%error_stack, 'Internal error! No token found!') + goto 100 + endif + + nextState = ST_NULL + + select case (fx%state) + + case (ST_MISC) + !write(*,*) 'ST_MISC', str_vs(fx%token) + select case (fx%tokenType) + case (TOK_PI_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_START_PI + case (TOK_BANG_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_BANG_TAG + case (TOK_OPEN_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_START_TAG + end select + + + case (ST_BANG_TAG) + !write(*,*) 'ST_BANG_TAG' + select case (fx%tokenType) + case (TOK_OPEN_SB) + nextState = ST_START_CDATA_DECLARATION + case (TOK_OPEN_COMMENT) + nextState = ST_START_COMMENT + case (TOK_NAME) + if (str_vs(fx%token)=='DOCTYPE') then + fx%context = CTXT_IN_DTD + nextState = ST_IN_DOCTYPE + endif + end select + + + case (ST_START_PI) + !write(*,*)'ST_START_PI' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (nameOk) then + if (str_vs(fx%token)=='xml') then + call add_error(fx%error_stack, "XML declaration must be at start of document") + goto 100 + elseif (checkPITarget(str_vs(fx%token), fx%xds%xml_version)) then + nextState = ST_PI_CONTENTS + fx%name => fx%token + fx%token => null() + else + call add_error(fx%error_stack, "Invalid PI target name") + goto 100 + endif + endif + end select + + case (ST_PI_CONTENTS) + !write(*,*)'ST_PI_CONTENTS' + if (validCheck) then + if (emptyContent(fx%elstack)) then + call add_error(fx%error_stack, "Content inside empty element") + goto 100 + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case(fx%tokenType) + case (TOK_CHAR) + if (present(processingInstruction_handler)) then + call processingInstruction_handler(str_vs(fx%name), str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + deallocate(fx%name) + nextState = ST_PI_END + case (TOK_PI_END) + if (present(processingInstruction_handler)) then + call processingInstruction_handler(str_vs(fx%name), "") + if (fx%state==ST_STOP) goto 100 + endif + deallocate(fx%name) + if (fx%context==CTXT_IN_CONTENT) then + nextState = ST_CHAR_IN_CONTENT + else + nextState = ST_MISC + endif + end select + + case (ST_PI_END) + !write(*,*)'ST_PI_END' + select case(fx%tokenType) + case (TOK_PI_END) + if (fx%context==CTXT_IN_CONTENT) then + nextState = ST_CHAR_IN_CONTENT + else + nextState = ST_MISC + endif + end select + + case (ST_START_COMMENT) + !write(*,*)'ST_START_COMMENT' + select case (fx%tokenType) + case (TOK_CHAR) + fx%name => fx%token + nullify(fx%token) + nextState = ST_COMMENT_END + end select + + case (ST_COMMENT_END) + !write(*,*)'ST_COMMENT_END' + if (validCheck) then + if (emptyContent(fx%elstack)) then + call add_error(fx%error_stack, "Content inside empty element") + goto 100 + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case (fx%tokenType) + case (TOK_COMMENT_END) + if (present(comment_handler)) then + call comment_handler(str_vs(fx%name)) + if (fx%state==ST_STOP) goto 100 + endif + deallocate(fx%name) + if (fx%context==CTXT_IN_CONTENT) then + nextState = ST_CHAR_IN_CONTENT + else + nextState = ST_MISC + endif + end select + + case (ST_START_TAG) + !write(*,*)'ST_START_TAG', fx%context + select case (fx%tokenType) + case (TOK_NAME) + if (fx%context==CTXT_BEFORE_DTD & + .or. fx%context==CTXT_BEFORE_CONTENT & + .or. fx%context==CTXT_IN_CONTENT) then + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Illegal element name") + goto 100 + endif + fx%name => fx%token + nullify(fx%token) + nextState = ST_IN_TAG + elseif (fx%context == CTXT_AFTER_CONTENT) then + call add_error(fx%error_stack, "Cannot open second root element") + goto 100 + elseif (fx%context == CTXT_IN_DTD) then + call add_error(fx%error_stack, "Cannot open root element before DTD is finished") + goto 100 + endif + end select + + case (ST_START_CDATA_DECLARATION) + !write(*,*) "ST_START_CDATA_DECLARATION" + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=="CDATA") then + if (fx%context/=CTXT_IN_CONTENT) then + call add_error(fx%error_stack, "CDATA section only allowed in text content.") + goto 100 + else + nextState = ST_FINISH_CDATA_DECLARATION + endif + else + call add_error(fx%error_stack, "Unknown keyword found in marked section declaration.") + endif + end select + + case (ST_FINISH_CDATA_DECLARATION) + !write(*,*) "ST_FINISH_CDATA_DECLARATION" + select case (fx%tokenType) + case (TOK_OPEN_SB) + nextState = ST_CDATA_CONTENTS + end select + + + case (ST_CDATA_CONTENTS) + !write(*,*)'ST_CDATA_CONTENTS' + select case (fx%tokenType) + case (TOK_CHAR) + fx%name => fx%token + nullify(fx%token) + nextState = ST_CDATA_END + end select + + case (ST_CDATA_END) + !write(*,*)'ST_CDATA_END' + if (validCheck) then + if (emptyContent(fx%elstack).or.elementContent(fx%elstack)) then + call add_error(fx%error_stack, "Content inside empty element") + goto 100 + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case(fx%tokenType) + case (TOK_SECTION_END) + if (present(startCdata_handler)) then + call startCdata_handler + if (fx%state==ST_STOP) goto 100 + endif + if (size(fx%name)>0) then + if (present(characters_handler)) then + call characters_handler(str_vs(fx%name)) + if (fx%state==ST_STOP) goto 100 + endif + endif + if (present(endCdata_handler)) then + call endCdata_handler + if (fx%state==ST_STOP) goto 100 + endif + deallocate(fx%name) + nextState = ST_CHAR_IN_CONTENT + end select + + case (ST_IN_TAG) + !write(*,*)'ST_IN_TAG' + select case (fx%tokenType) + case (TOK_END_TAG) + if (fx%context /= CTXT_IN_CONTENT) then + if (associated(fx%root_element)) then + if (validCheck) then + if (str_vs(fx%name)/=str_vs(fx%root_element)) then + call add_error(fx%error_stack, "Root element name does not match document name") + goto 100 + endif + endif + deallocate(fx%root_element) + elseif (validCheck) then + call add_error(fx%error_stack, "No DTD defined") + goto 100 + else + ! We havent had a DTD, so we havent handed xds + ! over to the DOM. + if (present(FoX_endDTD_handler)) then + fx%xds_used = .true. + call FoX_endDTD_handler(fx%xds) + endif + endif + fx%context = CTXT_IN_CONTENT + endif + call open_tag + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + deallocate(fx%name) + nextState = ST_CHAR_IN_CONTENT + + case (TOK_END_TAG_CLOSE) + if (fx%context==CTXT_IN_CONTENT) then + nextState = ST_CHAR_IN_CONTENT + else + ! only a single element in this doc + if (associated(fx%root_element)) then + if (validCheck) then + if (str_vs(fx%name)/=str_vs(fx%root_element)) then + call add_error(fx%error_stack, "Root element name does not match document name") + goto 100 + endif + endif + deallocate(fx%root_element) + elseif (validCheck) then + call add_error(fx%error_stack, "No DTD defined") + goto 100 + else + ! No DTD, so we havent handed over xds + if (present(FoX_endDTD_handler)) then + fx%xds_used = .true. + call FoX_endDTD_handler(fx%xds) + endif + endif + endif + call open_tag + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + call close_tag + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + deallocate(fx%name) + if (fx%context/=CTXT_IN_CONTENT) then + fx%well_formed = .true. + fx%context = CTXT_AFTER_CONTENT + nextState = ST_MISC + endif + + case (TOK_NAME) + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Illegal attribute name") + goto 100 + endif + !Have we already had this dictionary item? + if (has_key(fx%attributes, str_vs(fx%token))) then + call add_error(fx%error_stack, "Duplicate attribute name") + goto 100 + endif + fx%attname => fx%token + nullify(fx%token) + if (associated(elem)) then + attDecl => get_attribute_declaration(elem, str_vs(fx%attname)) + else + attDecl => null() + endif + nextState = ST_ATT_NAME + end select + + case (ST_ATT_NAME) + !write(*,*)'ST_ATT_NAME' + select case (fx%tokenType) + case (TOK_EQUALS) + nextState = ST_ATT_EQUALS + end select + + case (ST_ATT_EQUALS) + !write(*,*)'ST_ATT_EQUALS' + ! token is pre-processed attribute value. + ! fx%name still contains attribute name + select case (fx%tokenType) + case (TOK_CHAR) + !First, expand all entities: + tempString => normalize_attribute_text(fx, fx%token) + deallocate(fx%token) + fx%token => tempString + tempString => null() + !If this attribute is not CDATA, we must process further; + if (associated(attDecl)) then + temp_i = attDecl%attType + else + temp_i = ATT_CDATA + endif + if (temp_i==ATT_CDATA) then + call add_item_to_dict(fx%attributes, str_vs(fx%attname), & + str_vs(fx%token), itype=ATT_CDATA, declared=associated(attDecl)) + else + if (validCheck) then + if (fx%xds%standalone.and..not.attDecl%internal & + .and.(str_vs(fx%token)//"x"/=att_value_normalize(str_vs(fx%token))//"x")) then + call add_error(fx%error_stack, & + "Externally-declared attribute value normalization results in changed value "// & + "in standalone document") + goto 100 + endif + endif + call add_item_to_dict(fx%attributes, str_vs(fx%attname), & + att_value_normalize(str_vs(fx%token)), itype=temp_i, & + declared=.true.) + endif + deallocate(fx%attname) + nextState = ST_IN_TAG + end select + + case (ST_CHAR_IN_CONTENT) + !write(*,*)'ST_CHAR_IN_CONTENT' + select case (fx%tokenType) + case (TOK_CHAR) + if (size(fx%token)>0) then + if (validCheck) then + if (elementContent(fx%elstack)) then + if (verify(str_vs(fx%token), XML_WHITESPACE)==0) then + if (fx%xds%standalone.and..not.elem%internal) then + call add_error(fx%error_stack, & + "Externally-specified ignorable whitespace used in standalone document") + goto 100 + endif + if (present(ignorableWhitespace_handler)) then + call ignorableWhitespace_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + else + call add_error(fx%error_stack, "Forbidden content inside elementc: "//get_top_elstack(fx%elstack)) + goto 100 + endif + elseif (emptyContent(fx%elstack)) then + call add_error(fx%error_stack, "Forbidden content inside element: "//get_top_elstack(fx%elstack)) + goto 100 + else + if (present(characters_handler)) then + call characters_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + endif + else + if (present(characters_handler)) then + call characters_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + endif + endif + nextState = ST_TAG_IN_CONTENT + end select + + case (ST_TAG_IN_CONTENT) + !write(*,*) 'ST_TAG_IN_CONTENT' + select case (fx%tokenType) + case (TOK_ENTITY) + nextState = ST_START_ENTITY + case (TOK_PI_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_START_PI + case (TOK_BANG_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_BANG_TAG + case (TOK_CLOSE_TAG) + nextState = ST_CLOSING_TAG + case (TOK_OPEN_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_START_TAG + end select + + case (ST_START_ENTITY) + !write(*,*) 'ST_START_ENTITY' + select case (fx%tokenType) + case (TOK_NAME) + if (validCheck) then + elem => get_element(fx%xds%element_list, get_top_elstack(fx%elstack)) + if (associated(elem)) then + if (elem%empty) then + call add_error(fx%error_stack, & + "Forbidden content inside element") + goto 100 + endif + else + call add_error(fx%error_stack, & + 'Encountered reference to undeclared entity') + endif + endif + ent => getEntityByName(fx%forbidden_ge_list, str_vs(fx%token)) + if (associated(ent)) then + call add_error(fx%error_stack, 'Recursive entity reference') + goto 100 + endif + ent => getEntityByName(fx%predefined_e_list, str_vs(fx%token)) + if (associated(ent)) then + if (present(startEntity_handler)) then + call startEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + if (validCheck) then + if (associated(elem)) then + if (.not.elem%mixed.and..not.elem%any) then + call add_error(fx%error_stack, & + "Forbidden content inside element") + goto 100 + endif + endif + endif + if (present(characters_handler)) then + call characters_handler(expand_entity(fx%predefined_e_list, str_vs(fx%token))) + if (fx%state==ST_STOP) goto 100 + endif + if (present(endEntity_handler)) then + call endEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + elseif (likeCharacterEntityReference(str_vs(fx%token))) then + if (checkRepCharEntityReference(str_vs(fx%token), fx%xds%xml_version)) then + if (validCheck) then + if (associated(elem)) then + if (.not.elem%mixed.and..not.elem%any) then + call add_error(fx%error_stack, & + "Forbidden content inside element") + goto 100 + endif + endif + endif + if (present(characters_handler)) then + call characters_handler(expand_char_entity(str_vs(fx%token))) + if (fx%state==ST_STOP) goto 100 + endif + elseif (checkCharacterEntityReference(str_vs(fx%token), fx%xds%xml_version)) then + call add_error(fx%error_stack, "Unable to digest character entity reference in content, sorry.") + goto 100 + else + call add_error(fx%error_stack, "Illegal character reference") + goto 100 + endif + elseif (existing_entity(fx%xds%entityList, str_vs(fx%token))) then + ent => getEntityByName(fx%xds%entityList, str_vs(fx%token)) + if (ent%wfc.and.fx%xds%standalone) then + call add_error(fx%error_stack, & + 'Externally declared entity referenced in standalone document') + goto 100 + elseif (str_vs(ent%notation)/="") then + call add_error(fx%error_stack, & + 'Cannot reference unparsed entity in content') + goto 100 + elseif (ent%external) then + call open_new_file(fb, ent%baseURI, iostat) + if (iostat/=0) then + if (validCheck) then + call add_error(fx%error_stack, & + "Unable to retrieve external entity "//str_vs(fx%token)) + goto 100 + endif + if (present(skippedEntity_handler)) then + call skippedEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + else + if (present(startEntity_handler)) then + call startEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", null(), .false.) +#endif + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)+1)) + wf_stack = (/0, temp_wf_stack/) + deallocate(temp_wf_stack) + temp_wf_stack => extEntStack + allocate(extEntStack(size(temp_wf_stack)+1)) + extEntStack = (/len(fx%elstack), temp_wf_stack/) + deallocate(temp_wf_stack) + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + endif + else + if (validCheck.and.associated(elem)) then + if (elem%empty) then + call add_error(fx%error_stack, & + "Forbidden content inside element") + goto 100 + endif + endif + if (present(startEntity_handler)) then + call startEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", null(), .false.) +#endif + call open_new_string(fb, expand_entity(fx%xds%entityList, str_vs(fx%token)), str_vs(fx%token), baseURI=ent%baseURI) + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)+1)) + wf_stack = (/0, temp_wf_stack/) + deallocate(temp_wf_stack) + endif + else + ! Unknown entity check standalone etc + if (fx%skippedExternal.and..not.fx%xds%standalone) then + if (present(skippedEntity_handler)) then + call skippedEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + else + call add_error(fx%error_stack, & + 'Encountered reference to undeclared entity') + endif + endif + nextState = ST_CHAR_IN_CONTENT + end select + + case (ST_CLOSING_TAG) + !write(*,*)'ST_CLOSING_TAG' + select case (fx%tokenType) + case (TOK_NAME) + if (checkName(str_vs(fx%token), fx%xds%xml_version)) then + fx%name => fx%token + nullify(fx%token) + nextState = ST_IN_CLOSING_TAG + else + call add_error(fx%error_stack, "Closing tag: expecting a Name") + goto 100 + end if + end select + + case (ST_IN_CLOSING_TAG) + !write(*,*)'ST_IN_CLOSING_TAG' + select case (fx%tokenType) + case (TOK_END_TAG) + call close_tag + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + deallocate(fx%name) + if (is_empty(fx%elstack)) then + if (startInCharData_) then + fx%well_formed = .true. + nextState = ST_CHAR_IN_CONTENT + else + !we're done + if (validCheck) then + call checkIdRefs + if (in_error(fx%error_stack)) goto 100 + endif + fx%well_formed = .true. + nextState = ST_MISC + fx%context = CTXT_AFTER_CONTENT + endif + else + nextState = ST_CHAR_IN_CONTENT + endif + end select + + case (ST_IN_DOCTYPE) + !write(*,*)'ST_IN_DOCTYPE' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid document name") + goto 100 + endif + fx%root_element => fx%token + nullify(fx%token) + nextState = ST_DOC_NAME + end select + + case (ST_DOC_NAME) + !write(*,*) 'ST_DOC_NAME ', str_vs(fx%token) + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=='SYSTEM') then + nextState = ST_DOC_SYSTEM + elseif (str_vs(fx%token)=='PUBLIC') then + nextState = ST_DOC_PUBLIC + endif + case (TOK_OPEN_SB) + if (present(startDTD_handler)) then + call startDTD_handler(str_vs(fx%root_element), "", "") + if (fx%state==ST_STOP) goto 100 + endif + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_IN_SUBSET + fx%inIntSubset = .true. + case (TOK_END_TAG) + if (present(startDTD_handler)) then + call startDTD_handler(str_vs(fx%root_element), "", "") + if (fx%state==ST_STOP) goto 100 + endif + wf_stack(1) = wf_stack(1) - 1 + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + fx%context = CTXT_BEFORE_CONTENT + nextState = ST_MISC + case default + call add_error(fx%error_stack, "Unexpected token") + goto 100 + end select + + case (ST_DOC_PUBLIC) + !write(*,*) 'ST_DOC_PUBLIC' + select case (fx%tokenType) + case (TOK_CHAR) + if (checkPublicId(str_vs(fx%token))) then + fx%publicId => fx%token + fx%token => null() + nextState = ST_DOC_SYSTEM + else + call add_error(fx%error_stack, "Invalid document public id") + goto 100 + endif + end select + + case (ST_DOC_SYSTEM) + !write(*,*) 'ST_DOC_SYSTEM' + select case (fx%tokenType) + case (TOK_CHAR) + fx%systemId => fx%token + fx%token => null() + nextState = ST_DOC_DECL + end select + + case (ST_DOC_DECL) + !write(*,*) 'ST_DOC_DECL' + select case (fx%tokenType) + case (TOK_OPEN_SB) + if (present(startDTD_handler)) then + if (associated(fx%publicId)) then + call startDTD_handler(str_vs(fx%root_element), & + publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + elseif (associated(fx%systemId)) then + call startDTD_handler(str_vs(fx%root_element), & + publicId="", systemId=str_vs(fx%systemId)) + else + call startDTD_handler(str_vs(fx%root_element), "", "") + endif + if (fx%state==ST_STOP) goto 100 + endif + if (associated(fx%systemId)) then + extSubsetURI => parseURI(str_vs(fx%systemId)) + deallocate(fx%systemId) + endif + if (associated(fx%publicId)) deallocate(fx%publicId) + fx%inIntSubset = .true. + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_IN_SUBSET + case (TOK_END_TAG) + if (present(startDTD_handler)) then + if (associated(fx%publicId)) then + call startDTD_handler(str_vs(fx%root_element), publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + deallocate(fx%publicId) + elseif (associated(fx%systemId)) then + call startDTD_handler(str_vs(fx%root_element), publicId="", systemId=str_vs(fx%systemId)) + else + call startDTD_handler(str_vs(fx%root_element), "", "") + endif + if (fx%state==ST_STOP) goto 100 + endif + if (associated(fx%systemId)) then + extSubsetURI => parseURI(str_vs(fx%systemId)) + if (.not.associated(extSubsetURI)) then + call add_error(fx%error_stack, "Invalid URI specified for DTD SYSTEM") + goto 100 + endif + call open_new_file(fb, extSubsetURI, iostat) + if (iostat==0) then + fx%inIntSubset=.false. + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)+1)) + wf_stack = (/0, temp_wf_stack/) + deallocate(temp_wf_stack) + inExtSubset = .true. + nextState = ST_IN_SUBSET + else + if (validCheck) then + call add_error(fx%error_stack, & + "Unable to retrieve external subset "//str_vs(fx%systemId)) + goto 100 + endif + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + fx%context = CTXT_BEFORE_CONTENT + nextState = ST_MISC + endif + call destroyURI(extSubsetURI) + else + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + fx%context = CTXT_BEFORE_CONTENT + nextState = ST_MISC + endif + if (associated(fx%systemId)) deallocate(fx%systemId) + if (associated(fx%publicId)) deallocate(fx%publicId) + case default + call add_error(fx%error_stack, "Unexpected token in DTD") + goto 100 + end select + + + case (ST_CLOSE_DOCTYPE) + !write(*,*) "ST_CLOSE_DOCTYPE" + select case (fx%tokenType) + case (TOK_END_TAG) + if (wf_stack(1)>1) then + call add_error(fx%error_stack, "Cannot end DTD while conditional section is still open") + goto 100 + endif + if (associated(extSubsetURI)) then + call open_new_file(fb, extSubsetURI, iostat) + call destroyURI(extSubsetURI) + if (iostat==0) then + fx%inIntSubset = .false. + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)+1)) + wf_stack = (/0, temp_wf_stack/) + deallocate(temp_wf_stack) + inExtSubset = .true. + nextState = ST_IN_SUBSET + else + if (validCheck) then + call add_error(fx%error_stack, & + "Unable to retrieve external subset") + goto 100 + endif + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + wf_stack(1) = wf_stack(1) - 1 + nextState = ST_MISC + fx%context = CTXT_BEFORE_CONTENT + endif + else + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + wf_stack(1) = wf_stack(1) - 1 + nextState = ST_MISC + fx%context = CTXT_BEFORE_CONTENT + endif + end select + + case (ST_IN_SUBSET) + select case(fx%tokenType) + case (TOK_ENTITY) + nextState = ST_START_PE + case default + call parseDTD + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + if (fx%state_dtd==ST_DTD_DONE) & + fx%state_dtd = ST_DTD_SUBSET + end select + + case (ST_START_PE) + !write(*,*) 'ST_START_PE' + select case (fx%tokenType) + case (TOK_NAME) + if (existing_entity(fx%forbidden_pe_list, str_vs(fx%token))) then + call add_error(fx%error_stack, & + 'Recursive entity reference') + goto 100 + endif + ent => getEntityByName(fx%xds%PEList, str_vs(fx%token)) + if (associated(ent)) then + if (ent%wfc.and.fx%xds%standalone) then + call add_error(fx%error_stack, & + "Externally declared entity used in standalone document") + goto 100 + elseif (ent%external) then + if (present(startEntity_handler)) then + call startEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", null(), .false.) +#endif + call open_new_file(fb, ent%baseURI, iostat, pe=.true.) + if (iostat/=0) then + if (validCheck) then + call add_error(fx%error_stack, & + "Unable to retrieve external parameter entity "//str_vs(fx%token)) + goto 100 + endif + if (present(skippedEntity_handler)) then + call skippedEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + ! having skipped a PE, we must now not process + ! declarations any further (unless we are declared standalone) + ! (XML section 5.1) + fx%skippedExternal = .true. + processDTD = fx%xds%standalone + else + fx%inIntSubset = .false. + if (present(startEntity_handler)) then + call startEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", null(), .false.) +#endif + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + allocate(temp_wf_stack(size(wf_stack)+1)) + temp_wf_stack = (/0, wf_stack/) + deallocate(wf_stack) + wf_stack => temp_wf_stack + if (fx%state_dtd==ST_DTD_SUBSET) & + declSepValue = size(wf_stack) + endif + else + ! Expand the entity, + if (present(startEntity_handler)) then + call startEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", nullURI, .false.) + call open_new_string(fb, & + expand_entity(fx%xds%PEList, str_vs(fx%token)), str_vs(fx%token), baseURI=nullURI, pe=.true.) +#else + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", null(), .false.) + call open_new_string(fb, & + expand_entity(fx%xds%PEList, str_vs(fx%token)), str_vs(fx%token), baseURI=null(), pe=.true.) +#endif + ! NB because we are just expanding a string here, anything + ! evaluated as a result of this string is evaluated in the + ! context of the currently open file, so has a baseURI of + ! this file + allocate(temp_wf_stack(size(wf_stack)+1)) + temp_wf_stack = (/0, wf_stack/) + deallocate(wf_stack) + wf_stack => temp_wf_stack + if (fx%state_dtd==ST_DTD_SUBSET) & + declSepValue = size(wf_stack) + endif + ! and do nothing else, carry on ... + else + ! Have we previously skipped an external entity? + if (fx%skippedExternal.and..not.fx%xds%standalone) then + if (processDTD) then + if (present(skippedEntity_handler)) then + call skippedEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + endif + else + ! If not, + call add_error(fx%error_stack, & + "Reference to undeclared parameter entity.") + goto 100 + endif + endif + nextState = ST_IN_SUBSET + + end select + + end select + + if (nextState/=ST_NULL) then + fx%state = nextState + else + call add_error(fx%error_stack, "Internal error in parser - no suitable token found") + goto 100 + endif + + end do + +100 if (associated(tempString)) deallocate(tempString) + if (associated(extSubsetURI)) call destroyURI(extSubsetURI) + call destroy_string_list(id_list) + call destroy_string_list(idref_list) + deallocate(wf_stack) + if (associated(extEntStack)) deallocate(extEntStack) + + if (fx%state==ST_STOP) return + if (.not.eof) then + ! We have encountered an error before the end of a file + if (.not.reading_main_file(fb)) then !we are parsing an entity + if (inExtSubset) then + call add_error(fx%error_stack, "Error encountered processing external subset.") + else + call add_error(fx%error_stack, "Error encountered processing entity.") + endif + call sax_error(fx, fatalError_handler) + else + call sax_error(fx, fatalError_handler) + endif + else + ! EOF of main file + if (startInChardata_) then + if (fx%well_formed) then + if (fx%state==ST_CHAR_IN_CONTENT.and.associated(fx%token)) then + if (size(fx%token)>0.and.present(characters_handler)) & + call characters_handler(str_vs(fx%token)) + endif + else + if (present(fatalError_handler)) & + call fatalError_handler("Ill-formed XML fragment") + endif + elseif (fx%well_formed.and.fx%state==ST_MISC) then + if (present(endDocument_handler)) & + call endDocument_handler() + else + call add_error(fx%error_stack, "File is not well-formed") + call sax_error(fx, fatalError_handler) + endif + endif + + contains + + subroutine parseDTD + + integer :: nextDTDState +#ifdef PGF90 + type(element_t), pointer :: nullElement + + nullElement => null() +#endif + + nextDTDState = ST_DTD_NULL + + select case (fx%state_dtd) + + case (ST_DTD_SUBSET) + !write(*,*) "ST_DTD_SUBSET" + select case (fx%tokenType) + case (TOK_CLOSE_SB) + if (.not.reading_main_file(fb)) then + call add_error(fx%error_stack, "Cannot close DOCTYPE in external entity") + return + endif + wf_stack(1) = wf_stack(1) - 1 + fx%inIntSubset = .false. + nextState = ST_CLOSE_DOCTYPE + nextDTDState = ST_DTD_DONE + case (TOK_SECTION_END) + if (wf_stack(1)==0) then + call add_error(fx%error_stack, "Unbalanced conditional section in parameter entity") + return + endif + wf_stack(1) = wf_stack(1) - 2 + nextDTDState = ST_DTD_SUBSET + case (TOK_PI_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextDTDState = ST_DTD_START_PI + case (TOK_BANG_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextDTDState = ST_DTD_BANG_TAG + case default + call add_error(fx%error_stack, "Unexpected token in document subset") + return + end select + + + case (ST_DTD_BANG_TAG) + !write(*,*) 'ST_DTD_BANG_TAG' + select case (fx%tokenType) + case (TOK_OPEN_SB) + nextDTDState = ST_DTD_START_SECTION_DECL + case (TOK_OPEN_COMMENT) + nextDTDState = ST_DTD_START_COMMENT + case (TOK_NAME) + if (str_vs(fx%token)=='ATTLIST') then + nextDTDState = ST_DTD_ATTLIST + elseif (str_vs(fx%token)=='ELEMENT') then + nextDTDState = ST_DTD_ELEMENT + elseif (str_vs(fx%token)=='ENTITY') then + nextDTDState = ST_DTD_ENTITY + elseif (str_vs(fx%token)=='NOTATION') then + nextDTDState = ST_DTD_NOTATION + endif + end select + + case (ST_DTD_START_SECTION_DECL) + !write(*,*) "ST_DTD_START_SECTION_DECL" + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=="IGNORE") then + if (fx%context/=CTXT_IN_DTD.or.reading_main_file(fb)) then + call add_error(fx%error_stack, "IGNORE section only allowed in external subset.") + return + else + ignoreDepth = 0 + fx%context = CTXT_IGNORE + nextDTDState = ST_DTD_FINISH_SECTION_DECL + endif + elseif (str_vs(fx%token)=="INCLUDE") then + if (fx%context/=CTXT_IN_DTD.or.reading_main_file(fb)) then + call add_error(fx%error_stack, "INCLUDE section only allowed in external subset.") + return + else + nextDTDState = ST_DTD_FINISH_SECTION_DECL + endif + else + call add_error(fx%error_stack, "Unknown keyword found in marked section declaration.") + endif + end select + + + case (ST_DTD_FINISH_SECTION_DECL) + !write(*,*) "ST_FINISH_SECTION_DECL" + select case (fx%tokenType) + case (TOK_OPEN_SB) + wf_stack(1) = wf_stack(1) + 1 + if (fx%context==CTXT_IGNORE) then + nextDTDState = ST_DTD_IN_IGNORE_SECTION + ignoreDepth = ignoreDepth + 1 + else + nextDTDState = ST_DTD_SUBSET + endif + end select + + + case (ST_DTD_IN_IGNORE_SECTION) + !write(*,*) "ST_IN_IGNORE_SECTION" + select case (fx%tokenType) + case (TOK_SECTION_START) + wf_stack(1) = wf_stack(1) + 2 + ignoreDepth = ignoreDepth + 1 + nextDTDState = ST_DTD_IN_IGNORE_SECTION + case (TOK_SECTION_END) + wf_stack(1) = wf_stack(1) - 2 + ignoreDepth = ignoreDepth - 1 + if (ignoreDepth==0) then + fx%context = CTXT_IN_DTD + nextDTDState = ST_DTD_SUBSET + else + nextDTDState = ST_DTD_IN_IGNORE_SECTION + endif + end select + + case (ST_DTD_START_PI) + !write(*,*)'ST_DTD_START_PI' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (nameOk) then + if (str_vs(fx%token)=='xml') then + call add_error(fx%error_stack, "XML declaration must be at start of document") + return + elseif (checkPITarget(str_vs(fx%token), fx%xds%xml_version)) then + nextDTDState = ST_DTD_PI_CONTENTS + fx%name => fx%token + fx%token => null() + else + call add_error(fx%error_stack, "Invalid PI target name") + return + endif + endif + end select + + case (ST_DTD_PI_CONTENTS) + !write(*,*)'ST_DTD_PI_CONTENTS' + if (validCheck) then + if (fx%context==CTXT_IN_DTD.and.wf_stack(1)==0) then + call add_error(fx%error_stack, & + "PI not balanced in parameter entity") + return + endif + if (len(fx%elstack)>0) then + elem => & + get_element(fx%xds%element_list, get_top_elstack(fx%elstack)) + if (associated(elem)) then + if (elem%empty) then + call add_error(fx%error_stack, "Content inside empty element") + endif + endif + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case(fx%tokenType) + case (TOK_CHAR) + if (present(processingInstruction_handler)) then + call processingInstruction_handler(str_vs(fx%name), str_vs(fx%token)) + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + nextDTDState = ST_DTD_PI_END + case (TOK_PI_END) + if (present(processingInstruction_handler)) then + call processingInstruction_handler(str_vs(fx%name), '') + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_PI_END) + !write(*,*)'ST_DTD_PI_END' + select case(fx%tokenType) + case (TOK_PI_END) + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_START_COMMENT) + !write(*,*)'ST_DTD_START_COMMENT' + select case (fx%tokenType) + case (TOK_CHAR) + fx%name => fx%token + nullify(fx%token) + nextDTDState = ST_DTD_COMMENT_END + end select + + case (ST_DTD_COMMENT_END) + !write(*,*)'ST_DTD_COMMENT_END' + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "Comment not balanced in entity") + return + endif + if (len(fx%elstack)>0) then + elem => & + get_element(fx%xds%element_list, get_top_elstack(fx%elstack)) + if (associated(elem)) then + if (elem%empty) then + call add_error(fx%error_stack, "Content inside empty element") + endif + endif + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case (fx%tokenType) + case (TOK_COMMENT_END) + if (present(comment_handler)) then + call comment_handler(str_vs(fx%name)) + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_ATTLIST) + !write(*,*) 'ST_DTD_ATTLIST' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid element name for ATTLIST") + return + endif + if (existing_element(fx%xds%element_list, str_vs(fx%token))) then + elem => get_element(fx%xds%element_list, str_vs(fx%token)) + else + elem => add_element(fx%xds%element_list, str_vs(fx%token)) + endif + nextDTDState = ST_DTD_ATTLIST_CONTENTS + end select + + case (ST_DTD_ATTLIST_CONTENTS) + !write(*,*) 'ST_DTD_ATTLIST_CONTENTS' + select case (fx%tokenType) + case (TOK_ENTITY) + !Weve found a PEref in the middle of the element contents + ! Leave DTD state as it is & expand the entity ... + nextState = ST_START_PE + case (TOK_DTD_CONTENTS) + if (processDTD) then + call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, elem, & + internal=reading_main_file(fb)) + else +#ifdef PGF90 + call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, nullElement, & + internal=reading_main_file(fb)) +#else + call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, null(), & + internal=reading_main_file(fb)) +#endif + endif + if (in_error(fx%error_stack)) return + ! Normalize attribute values in attlist + if (processDTD) then + do i = 1, size(elem%attlist%list) + if (associated(elem%attlist%list(i)%default)) then + tempString => elem%attlist%list(i)%default + elem%attlist%list(i)%default => & + normalize_attribute_text(fx, tempString) + deallocate(tempString) + if (in_error(fx%error_stack)) return + endif + enddo + endif + nextDTDState = ST_DTD_ATTLIST_END + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ATTLIST not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + call parse_dtd_attlist("", fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, elem, & + internal=reading_main_file(fb)) + else +#ifdef PGF90 + call parse_dtd_attlist("", fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, nullElement, & + internal=reading_main_file(fb)) +#else + call parse_dtd_attlist("", fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, null(), & + internal=reading_main_file(fb)) +#endif + endif + if (in_error(fx%error_stack)) return + if (processDTD) then + if (present(attributeDecl_handler)) then + call report_declarations(elem, attributeDecl_handler) + if (fx%state==ST_STOP) return + endif + endif + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_ATTLIST_END) + !write(*,*) 'ST_DTD_ATTLIST_END' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ATTLIST not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + if (present(attributeDecl_handler)) then + call report_declarations(elem, attributeDecl_handler) + if (fx%state==ST_STOP) return + endif + endif + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_ELEMENT) + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid name for ELEMENT") + return + endif + fx%name => fx%token + fx%token => null() + nextDTDState = ST_DTD_ELEMENT_CONTENTS + end select + + case (ST_DTD_ELEMENT_CONTENTS) + !write(*,*)'ST_DTD_ELEMENT_CONTENTS' + select case (fx%tokenType) + case (TOK_OPEN_PAR) + ! increment well_formedness + wf_stack(1) = wf_stack(1) + 1 + nextDTDState = ST_DTD_ELEMENT_CONTENTS + case (TOK_CLOSE_PAR) + ! increment well_formedness + wf_stack(1) = wf_stack(1) - 1 + nextDTDState = ST_DTD_ELEMENT_CONTENTS + case (TOK_ENTITY) + !Weve found a PEref in the middle of the element contents + ! Leave DTD state as it is & expand the entity ... + nextState = ST_START_PE + case (TOK_DTD_CONTENTS) + if (declared_element(fx%xds%element_list, str_vs(fx%name))) then + if (validCheck) then + call add_error(fx%error_stack, "Duplicate Element declaration") + return + else + ! Ignore contents ... + elem => null() + endif + elseif (processDTD) then + if (existing_element(fx%xds%element_list, str_vs(fx%name))) then + elem => get_element(fx%xds%element_list, str_vs(fx%name)) + else + elem => add_element(fx%xds%element_list, str_vs(fx%name)) + endif + else + elem => null() + endif + call parse_dtd_element(str_vs(fx%token), fx%xds%xml_version, fx%error_stack, & + elem, reading_main_file(fb)) + if (in_error(fx%error_stack)) return + nextDTDState = ST_DTD_ELEMENT_END + end select + + case (ST_DTD_ELEMENT_END) + !write(*,*)'ST_DTD_ELEMENT_END' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ELEMENT not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD.and.associated(elem)) then + if (present(elementDecl_handler)) then + call elementDecl_handler(str_vs(fx%name), str_vs(elem%model)) + if (fx%state==ST_STOP) return + endif + endif + deallocate(fx%name) + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_ENTITY) + !write(*,*) 'ST_DTD_ENTITY' + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=="%") then + pe = .true. + ! this will be a PE + nextDTDState = ST_DTD_ENTITY_PE + else + pe = .false. + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Illegal name for general entity") + return + endif + fx%name => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_ID + endif + end select + + case (ST_DTD_ENTITY_PE) + !write(*,*) 'ST_DTD_ENTITY_PE' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Illegal name for parameter entity") + return + endif + fx%name => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_ID + end select + + case (ST_DTD_ENTITY_ID) + !write(*,*) 'ST_DTD_ENTITY_ID' + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token) == "PUBLIC") then + nextDTDState = ST_DTD_ENTITY_PUBLIC + elseif (str_vs(fx%token) == "SYSTEM") then + nextDTDState = ST_DTD_ENTITY_SYSTEM + else + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + endif + case (TOK_CHAR) + if (reading_main_file(fb)) then + tempString => fx%token + else + tempString => expand_pe_text(fx, fx%token, fb) + endif + fx%attname => expand_entity_value_alloc(tempString, fx%xds, fx%error_stack) + if (reading_main_file(fb)) then + tempString => null() + else + deallocate(tempString) + endif + if (in_error(fx%error_stack)) return + nextDTDState = ST_DTD_ENTITY_END + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_PUBLIC) + !write(*,*) 'ST_DTD_ENTITY_PUBLIC' + select case (fx%tokenType) + case (TOK_CHAR) + if (checkPublicId(str_vs(fx%token))) then + fx%publicId => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_SYSTEM + else + call add_error(fx%error_stack, "Invalid PUBLIC id in ENTITY") + return + endif + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_SYSTEM) + !write(*,*) 'ST_DTD_ENTITY_SYSTEM' + select case (fx%tokenType) + case (TOK_CHAR) + fx%systemId => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_NDATA + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_NDATA) + !write(*,*) 'ST_DTD_ENTITY_NDATA' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ENTITY not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + call add_entity + if (in_error(fx%error_stack)) return + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + if (associated(fx%attname)) deallocate(fx%attname) + if (associated(fx%systemId)) deallocate(fx%systemId) + if (associated(fx%publicId)) deallocate(fx%publicId) + if (associated(fx%Ndata)) deallocate(fx%Ndata) + nextDTDState = ST_DTD_SUBSET + case (TOK_NAME) + if (str_vs(fx%token)=='NDATA') then + if (pe) then + call add_error(fx%error_stack, "Parameter entity cannot have NDATA declaration") + return + endif + nextDTDState = ST_DTD_ENTITY_NDATA_VALUE + else + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + endif + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_NDATA_VALUE) + !write(*,*) 'ST_DTD_ENTITY_NDATA_VALUE' + !check is a name and exists in notationlist + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid name for Notation") + return + endif + fx%Ndata => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_END + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_END) + !write(*,*) 'ST_DTD_ENTITY_END' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ENTITY not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + call add_entity + if (in_error(fx%error_stack)) return + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + if (associated(fx%attname)) deallocate(fx%attname) + if (associated(fx%systemId)) deallocate(fx%systemId) + if (associated(fx%publicId)) deallocate(fx%publicId) + if (associated(fx%Ndata)) deallocate(fx%Ndata) + nextDTDState = ST_DTD_SUBSET + case default + call add_error(fx%error_stack, "Unexpected token at end of ENTITY") + return + end select + + case (ST_DTD_NOTATION) + !write(*,*) 'ST_DTD_NOTATION' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid name for Notation") + return + endif + fx%name => fx%token + fx%token => null() + nextDTDState = ST_DTD_NOTATION_ID + case default + call add_error(fx%error_stack, "Unexpected token in NOTATION") + return + end select + + case (ST_DTD_NOTATION_ID) + !write(*,*)'ST_DTD_NOTATION_ID' + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=='SYSTEM') then + nextDTDState = ST_DTD_NOTATION_SYSTEM + elseif (str_vs(fx%token)=='PUBLIC') then + nextDTDState = ST_DTD_NOTATION_PUBLIC + else + call add_error(fx%error_stack, "Unexpected token after NOTATION") + return + endif + case default + call add_error(fx%error_stack, "Unexpected token after NOTATION") + return + end select + + case (ST_DTD_NOTATION_SYSTEM) + !write(*,*)'ST_DTD_NOTATION_SYSTEM' + select case (fx%tokenType) + case (TOK_CHAR) + fx%systemId => fx%token + fx%token => null() + nextDTDState = ST_DTD_NOTATION_END + case default + call add_error(fx%error_stack, "Unexpected token in NOTATION") + return + end select + + case (ST_DTD_NOTATION_PUBLIC) + !write(*,*)'ST_DTD_NOTATION_PUBLIC' + select case (fx%tokenType) + case (TOK_CHAR) + if (checkPublicId(str_vs(fx%token))) then + fx%publicId => fx%token + fx%token => null() + nextDTDState = ST_DTD_NOTATION_PUBLIC_2 + else + call add_error(fx%error_stack, "Invalid PUBLIC id in NOTATION") + return + endif + case default + call add_error(fx%error_stack, "Unexpected token in NOTATION") + return + end select + + case (ST_DTD_NOTATION_PUBLIC_2) + !write(*,*)'ST_DTD_NOTATION_PUBLIC_2' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "NOTATION not balanced in parameter entity") + return + endif + if (notation_exists(fx%nlist, str_vs(fx%name))) then + call add_error(fx%error_stack, "Duplicate notation declaration") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + call add_notation(fx%nlist, str_vs(fx%name), publicId=str_vs(fx%publicId)) + if (present(notationDecl_handler)) then + call notationDecl_handler(str_vs(fx%name), publicId=str_vs(fx%publicId), systemId="") + if (fx%state==ST_STOP) return + endif + endif + deallocate(fx%name) + deallocate(fx%publicId) + nextDTDState = ST_DTD_SUBSET + case (TOK_CHAR) + fx%systemId => fx%token + fx%token => null() + nextDTDState = ST_DTD_NOTATION_END + end select + + case (ST_DTD_NOTATION_END) + !write(*,*)'ST_DTD_NOTATION_END' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "NOTATION not balanced in parameter entity") + return + endif + if (notation_exists(fx%nlist, str_vs(fx%name))) then + call add_error(fx%error_stack, "Duplicate notation declaration") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + URIref => parseURI(str_vs(fx%systemId)) + if (.not.associated(URIref)) then + call add_error(fx%error_stack, "Invalid SYSTEM literal") + return + endif + if (hasFragment(URIref)) then + call destroyURI(URIref) + call add_error(fx%error_stack, "SYSTEM literal may not contain fragment") + return + endif + ! We aren't ever going to do anything with this URI, + ! since we don't do NOTATIONs. + ! Throw it away again + call destroyURI(URIref) + if (associated(fx%publicId)) then + call add_notation(fx%nlist, str_vs(fx%name), & + publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + if (present(notationDecl_handler)) then + call notationDecl_handler(str_vs(fx%name), & + publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + if (fx%state==ST_STOP) return + endif + else + call add_notation(fx%nlist, str_vs(fx%name), & + systemId=str_vs(fx%systemId)) + if (present(notationDecl_handler)) then + call notationDecl_handler(str_vs(fx%name), & + publicId="", systemId=str_vs(fx%systemId)) + if (fx%state==ST_STOP) return + endif + endif + endif + if (associated(fx%publicId)) deallocate(fx%publicId) + deallocate(fx%systemId) + deallocate(fx%name) + nextDTDState = ST_DTD_SUBSET + case default + call add_error(fx%error_stack, "Unexpected token in NOTATION") + return + end select + + end select + + if (nextDTDState==ST_DTD_NULL) then + call add_error(fx%error_stack, & + "Bad token found in DTD parsing") + else + fx%state_dtd = nextDTDState + endif + if (nextState==ST_NULL) & + nextState = ST_IN_SUBSET + + end subroutine parseDTD + + subroutine open_tag + elem => get_element(fx%xds%element_list, str_vs(fx%name)) + if (associated(elem)) then + if (validCheck) then + call checkAttributes(elem, fx%attributes) + if (.not.checkContentModel(fx%elstack, str_vs(fx%name))) then + call add_error(fx%error_stack, & + "Element "//str_vs(fx%name)//" not permitted in this context") + return + endif + else + call getDefaultAttributes(elem, fx%attributes) + endif + else + if (validCheck) then + call add_error(fx%error_stack, & + "Trying to use an undeclared element") + return + endif + endif + ! Check for namespace changes + if (namespaces_) then + call checkNamespaces(fx%attributes, fx%nsDict, & + len(fx%elstack), fx%xds, namespace_prefixes_, xmlns_uris_, & + fx%error_stack, startInCharData_, & + startPrefixMapping_handler, endPrefixMapping_handler) + if (fx%state==ST_STOP) return + endif + if (in_error(fx%error_stack)) return + call checkXmlAttributes + if (in_error(fx%error_stack)) return + if (size(extEntStack)>0) then + if (len(fx%elstack)==extEntStack(1)) & + ! This is a top-level element in the current entity + call setBase(fx%attributes, expressURI(fb%f(1)%baseURI)) + endif + if (namespaces_.and.getURIofQName(fx,str_vs(fx%name))==invalidNS) then + ! no namespace was found for the current element + if (.not.startInCharData_) then + ! but we ignore this if we are parsing an entity through DOM + call add_error(fx%error_stack, "No namespace found for current element") + return + elseif (present(startElement_handler)) then + ! Record it as having an empty URI + call startElement_handler("", & + getlocalNameofQName(str_vs(fx%name)), & + str_vs(fx%name), fx%attributes) + if (fx%state==ST_STOP) return + endif + elseif (namespaces_) then + ! Normal state of affairs + if (present(startElement_handler)) then + call startElement_handler(getURIofQName(fx, str_vs(fx%name)), & + getlocalNameofQName(str_vs(fx%name)), & + str_vs(fx%name), fx%attributes) + if (fx%state==ST_STOP) return + endif + else + ! Non-namespace aware processing + if (present(startElement_handler)) then + call startElement_handler("", "", & + str_vs(fx%name), fx%attributes) + if (fx%state==ST_STOP) return + endif + endif + if (validCheck) then + call push_elstack(fx%elstack, str_vs(fx%name), elem%cp) + else + call push_elstack(fx%elstack, str_vs(fx%name)) + endif + call reset_dict(fx%attributes) + end subroutine open_tag + + subroutine close_tag + character :: dummy + wf_stack(1) = wf_stack(1) - 1 + if (wf_stack(1)<0) then + call add_error(fx%error_stack, & + 'Ill-formed entity') + return + endif + if (str_vs(fx%name)/=get_top_elstack(fx%elstack)) then + call add_error(fx%error_stack, & + "Mismatching close tag: trying to close "//get_top_elstack(fx%elstack) & + //" with "//str_vs(fx%name)) + return + endif + if (validCheck) then + if (.not.checkContentModelToEnd(fx%elstack)) then + call add_error(fx%error_stack, & + "Failed to fulfil content model for "//str_vs(fx%name)) + return + endif + endif + dummy = pop_elstack(fx%elstack) + if (present(endElement_handler)) then + if (namespaces_.and.getURIofQName(fx,str_vs(fx%name))==invalidNS) then + ! no namespace was found for the current element, we must be + ! closing inside a DOM entity. + ! Record it as having an empty URI + call endElement_handler("", & + getlocalNameofQName(str_vs(fx%name)), & + str_vs(fx%name)) + elseif (namespaces_) then + ! Normal state of affairs + call endElement_handler(getURIofQName(fx, str_vs(fx%name)), & + getlocalnameofQName(str_vs(fx%name)), & + str_vs(fx%name)) + else + ! Non-namespace-aware processing: + call endElement_handler("", "", & + str_vs(fx%name)) + endif + if (fx%state==ST_STOP) return + endif + if (namespaces_) then + call checkEndNamespaces(fx%nsDict, len(fx%elstack), & + endPrefixMapping_handler) + if (fx%state==ST_STOP) return + endif + end subroutine close_tag + + subroutine add_entity + !Parameter or General Entity? + logical :: wfc + wfc = fb%f(1)%pe.or.inExtSubset + if (pe) then + !Does entity with this name exist? + if (.not.existing_entity(fx%xds%PEList, str_vs(fx%name))) then + ! Internal or external? + if (associated(fx%attname)) then ! it's internal + call register_internal_PE(fx%xds, & + name=str_vs(fx%name), text=str_vs(fx%attname), & + wfc=wfc, baseURI=copyURI(fb%f(1)%baseURI)) + ! FIXME need to expand value here before reporting ... + if (present(internalEntityDecl_handler)) then + call internalEntityDecl_handler('%'//str_vs(fx%name), str_vs(fx%attname)) + if (fx%state==ST_STOP) return + endif + else ! PE can't have Ndata declaration + URIref => parseURI(str_vs(fx%systemId)) + if (.not.associated(URIref)) then + call add_error(fx%error_stack, "Invalid URI specified for SYSTEM") + elseif (hasFragment(URIref)) then + call add_error(fx%error_stack, "Fragment not permitted on SYSTEM URI") + call destroyURI(URIref) + else + newURI => rebaseURI(fb%f(1)%baseURI, URIref) + call destroyURI(URIref) + if (associated(fx%publicId)) then + call register_external_PE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + wfc=wfc, baseURI=newURI) + if (present(externalEntityDecl_handler)) & + call externalEntityDecl_handler('%'//str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId)) + else + call register_external_PE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), & + wfc=wfc, baseURI=newURI) + if (present(externalEntityDecl_handler)) & + call externalEntityDecl_handler('%'//str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId="") + endif + endif + endif + ! else we ignore it + endif + else !It's a general entity + if (.not.existing_entity(fx%xds%entityList, str_vs(fx%name))) then + ! Internal or external? + if (associated(fx%attname)) then ! it's internal + call register_internal_GE(fx%xds, name=str_vs(fx%name), & + text=str_vs(fx%attname), & + wfc=wfc, baseURI=copyURI(fb%f(1)%baseURI)) + if (present(internalEntityDecl_handler)) then + call internalEntityDecl_handler(str_vs(fx%name),& + str_vs(fx%attname)) + if (fx%state==ST_STOP) return + endif + else + URIref => parseURI(str_vs(fx%systemId)) + if (.not.associated(URIref)) then + call add_error(fx%error_stack, "Invalid URI specified for SYSTEM") + elseif (hasFragment(URIref)) then + call add_error(fx%error_stack, "Fragment not permitted on SYSTEM URI") + call destroyURI(URIref) + else + newURI => rebaseURI(fb%f(1)%baseURI, URIref) + call destroyURI(URIref) + if (associated(fx%publicId).and.associated(fx%Ndata)) then + call register_external_GE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + notation=str_vs(fx%Ndata), & + wfc=wfc, baseURI=newURI) + if (present(unparsedEntityDecl_handler)) & + call unparsedEntityDecl_handler(str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + notation=str_vs(fx%Ndata)) + elseif (associated(fx%Ndata)) then + call register_external_GE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), notation=str_vs(fx%Ndata), & + wfc=wfc, baseURI=newURI) + if (present(unparsedEntityDecl_handler)) & + call unparsedEntityDecl_handler(str_vs(fx%name), publicId="", & + systemId=str_vs(fx%systemId), notation=str_vs(fx%Ndata)) + elseif (associated(fx%publicId)) then + call register_external_GE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + wfc=wfc, baseURI=newURI) + if (present(externalEntityDecl_handler)) & + call externalEntityDecl_handler(str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId)) + else + call register_external_GE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), wfc=wfc, baseURI=newURI) + if (present(externalEntityDecl_handler)) & + call externalEntityDecl_handler(str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId="") + endif + endif + endif + endif + endif + end subroutine add_entity + + subroutine getDefaultAttributes(el, dict) + type(element_t), pointer :: el + type(dictionary_t), intent(inout) :: dict + + type(attribute_t), pointer :: att + integer :: i, ind + character, pointer :: attValue(:) + + do i = 1, size(el%attlist%list) + att => el%attlist%list(i) + call get_att_index_pointer(dict, str_vs(att%name), ind, attValue) + if (.not.associated(attValue)) then + if (att%attDefault==ATT_DEFAULT & + .or.att%attDefault==ATT_FIXED) then + call add_item_to_dict(dict, & + str_vs(att%name), str_vs(att%default), & + specified=.false., declared=.true.) + endif + endif + end do + end subroutine getDefaultAttributes + + subroutine checkAttributes(el, dict) + type(element_t), pointer :: el + type(dictionary_t), intent(inout) :: dict + + integer :: i, j + + type(attribute_t), pointer :: att + type(string_list) :: s_list + character, pointer :: attValue(:), s(:) + + integer :: ind + logical, allocatable :: attributesLeft(:) + allocate(attributesLeft(getLength(dict))) + attributesLeft = .true. + + do i = 1, size(el%attlist%list) + att => el%attlist%list(i) + call get_att_index_pointer(dict, str_vs(att%name), ind, attValue) + if (associated(attValue)) attributesLeft(ind) = .false. + select case(att%attDefault) + case (ATT_REQUIRED, ATT_IMPLIED, ATT_DEFAULT) + if (.not.associated(attValue)) then + if (att%attDefault==ATT_REQUIRED) then + ! Validity Constraint: Required Attribute + call add_error(fx%error_stack, & + "REQUIRED attribute "//str_vs(att%name)//" not present") + return + elseif (att%attDefault==ATT_DEFAULT) then + if (fx%xds%standalone.and..not.att%internal) then + ! VC: Standalone document declaration" + call add_error(fx%error_stack, & + "Externally-specifid default attribute used in non-standalone document") + return + else + call add_item_to_dict(dict, & + str_vs(att%name), str_vs(att%default), & + specified=.false., declared=.true.) + endif + endif + else + select case(att%attType) + case (ATT_ID) + ! VC: ID + if (namespaces_) then + nameOk = checkNCName(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkName(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type ID must have a value which is an XML Name") + return + endif + if (registered_string(id_list, str_vs(attValue))) then + call add_error(fx%error_stack, & + "Cannot declare the same ID twice") + return + endif + call add_string(id_list, str_vs(attValue)) + call setIsId(dict, ind, .true.) + ! We don't need to check for duplicate ID & xml:ids on the same + ! element - if we are validating we'd already have noticed, + ! if we're not, it's not possible + case (ATT_IDREF) + ! VC: IDREF + if (namespaces_) then + nameOk = checkNCName(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkName(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type IDREF must have a value which is an XML Name") + return + endif + ! FIXME in a namespaced document they must match QName + if (.not.registered_string(idref_list, str_vs(attValue))) & + call add_string(idref_list, str_vs(attValue)) + case (ATT_IDREFS) + ! VC: IDREF + if (namespaces_) then + nameOk = checkNCNames(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkNames(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type IDREFS must have a value which contains only XML Names") + return + endif + call tokenize_and_add_strings(idref_list, str_vs(attValue), .true.) + case (ATT_ENTITY) + ! VC: Entity Name + if (namespaces_) then + nameOk = checkNCName(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkName(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type ENTITY must have a value which is an XML Name") + return + endif + ent => getEntityByName(fx%xds%entityList, str_vs(attValue)) + if (associated(ent)) then + if (.not.is_unparsed_entity(ent)) then + ! Validity Constraint: Entity Name + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENTITY refers to parsed entity") + return + endif + else + ! Validity Constraint: Entity Name + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENTITY refers to non-existent entity") + return + endif + case (ATT_ENTITIES) + ! VC: Entity Name + if (namespaces_) then + nameOk = checkNCNames(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkNames(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type ENTITIES must have a value which contains only XML Names") + return + endif + s_list = tokenize_to_string_list(str_vs(attValue)) + do j = 1, size(s_list%list) + s => s_list%list(j)%s + ent => getEntityByName(fx%xds%entityList, str_vs(s)) + if (associated(ent)) then + if (.not.is_unparsed_entity(ent)) then + ! Validity Constraint: Entity Name + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENTITIES refers to parsed entity "& + //str_vs(s)) + call destroy_string_list(s_list) + return + endif + else + ! Validity Constraint: Entity Name + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENTITIES refers to non-existent entity "& + //str_vs(s)) + call destroy_string_list(s_list) + return + endif + enddo + call destroy_string_list(s_list) + case (ATT_NMTOKEN) + ! VC Name Token + if (.not.checkNmtoken(str_vs(attValue), fx%xds%xml_version)) then + call add_error(fx%error_stack, & + "Attributes of type NMTOKEN must have a value which is a NMTOKEN") + return + endif + case (ATT_NMTOKENS) + ! VC: Name Token + if (.not.checkNmtokens(str_vs(attValue), fx%xds%xml_version)) then + call add_error(fx%error_stack, & + "Attributes of type NMTOKENS must have a value which contain only NMTOKENs") + return + endif + case (ATT_NOTATION) + ! VC: Notation Attributes + if (namespaces_) then + nameOk = checkNCName(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkName(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type NOTATION must have a value which is an XML Name") + return + endif + if (.not.notation_exists(fx%nlist, str_vs(attValue))) then + ! Validity Constraint: Notation Attributes + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" declared as NOTATION refers to non-existent notation "& + //str_vs(attValue)) + return + endif + if (att%attDefault==ATT_REQUIRED) then + if (.not.registered_string(att%enumerations, str_vs(attValue))) & + call add_error(fx%error_stack, & + "NOTATION attribute is not among declared values.") + endif + case (ATT_ENUM) + ! VC: Notation Attributes + if (.not.checkNmtoken(str_vs(attValue), fx%xds%xml_version)) then + call add_error(fx%error_stack, & + "Attributes of type ENUM must have a value which is an NMTOKEN") + return + endif + if (.not.registered_string(att%enumerations, str_vs(attValue))) then + ! Validity Constraint: Enumeration + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENUM refers to undeclared enumeration "& + //str_vs(attValue)) + return + endif + end select + endif + case (ATT_FIXED) + if (associated(attValue)) then + if (str_vs(att%default)//"x"/=str_vs(attValue)//"x") then + ! Validity Constraint: Fixed Attribute Default + call add_error(fx%error_stack, & + "FIXED attribute has wrong value") + return + endif + else + if (fx%xds%standalone.and..not.att%internal) then + ! VC: Standalone document declaration" + call add_error(fx%error_stack, & + "Externally-specified default attribute used in non-standalone document") + return + else + call add_item_to_dict(dict, & + str_vs(att%name), str_vs(att%default), & + specified=.false., declared=.true.) + endif + endif + end select + enddo + + if (any(attributesLeft)) & + call add_error(fx%error_stack, & + "Undeclared attributes forbidden") + + end subroutine checkAttributes + + subroutine checkXMLAttributes + integer :: ind + character, pointer :: attValue(:) + ! These must all be done with the name of the attribute, + ! not the nsURI/localname pair, in case we are + ! processing for a non-namespace aware application + if (has_key(fx%attributes, 'xml:space')) then + if (get_value(fx%attributes, 'xml:space')/='default' & + .and. get_value(fx%attributes, 'xml:space')/='preserve') then + call add_error(fx%error_stack, 'Illegal value of xml:space attribute') + return + endif + endif + call get_att_index_pointer(fx%attributes, "xml:id", ind, attValue) + if (associated(attValue)) then + ! Per xml:id spec, NCName even in non-namespace aware document + if (.not.checkNCName(str_vs(attValue), fx%xds%xml_version)) then + call add_error(fx%error_stack, & + "xml:id attributes must have values which are NCNames") + return + elseif (registered_string(id_list, str_vs(attValue))) then + call add_error(fx%error_stack, & + "xml:id attributes must be unique within a document") + return + endif + call add_string(id_list, str_vs(attValue)) + call setIsId(fx%attributes, ind, .true.) + endif + if (has_key(fx%attributes, "xml:base")) then + URIref => parseURI(get_value(fx%attributes,"xml:base")) + if (.not.associated(URIref)) then + call add_error(fx%error_stack, & + "Invalid URI reference specified for xml:base attribute") + else + call destroyURI(URIref) + endif + endif + !if (has_key(fx%attributes, 'xml:lang')) then + ! We never care about this at the SAX level. + !endif + end subroutine checkXMLAttributes + + subroutine endDTDchecks + type(element_t), pointer :: el + type(attribute_t), pointer :: att + type(entity_t), pointer :: ent + type(string_list) :: s_list + character, pointer :: s(:) + integer :: i, j, k + + if (present(FoX_endDTD_handler)) then + fx%xds_used = .true. + call FoX_endDTD_handler(fx%xds) + endif + if (present(endDTD_handler)) then + call endDTD_handler() + if (fx%state==ST_STOP) return + endif + ! Check that all notations used have been declared: + if (validCheck) then + do i = 1, size(fx%xds%entityList) + ent => getEntityByIndex(fx%xds%entityList, i) + if (str_vs(ent%notation)/="" & + .and..not.notation_exists(fx%nlist, str_vs(ent%notation))) then + ! Validity Constraint: Notation Declared + call add_error(fx%error_stack, "Attempt to use undeclared notation") + exit + endif + enddo + validLoop: do i = 1, size(fx%xds%element_list%list) + el => fx%xds%element_list%list(i) + do j = 1, size(el%attlist%list) + att => el%attlist%list(j) + ! For NOTATION, need to check enumerated as well as default ... + if (att%attType==ATT_NOTATION) then + do k = 1, size(att%enumerations%list) + s => att%enumerations%list(k)%s + if (.not.notation_exists(fx%nlist, str_vs(s))) then + ! Validity Constraint: Notation Attributes + call add_error(fx%error_stack, & + "Enumerated NOTATION in "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" refers to non-existent notation") + call destroy(s_list) + exit validLoop + endif + enddo + if (associated(att%default)) then + s_list = tokenize_to_string_list(str_vs(att%default)) + do k = 1, size(s_list%list) + s => s_list%list(k)%s + if (.not.notation_exists(fx%nlist, str_vs(s))) then + ! Validity Constraint: Notation Attributes + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as NOTATION refers to non-existent notation") + call destroy(s_list) + exit validLoop + endif + enddo + call destroy(s_list) + endif + endif + enddo + enddo validLoop + endif + end subroutine endDTDchecks + + subroutine checkIdRefs + integer :: i + character, pointer :: s(:) + do i = 1, size(idRef_list%list) + s => idRef_list%list(i)%s + if (.not.registered_string(id_list, str_vs(s))) then + call add_error(fx%error_stack, & + "Reference to undeclared ID "//str_vs(s)) + return + endif + enddo + end subroutine checkIdRefs + end subroutine sax_parse + + + subroutine sax_error(fx, error_handler) + type(sax_parser_t), intent(inout) :: fx + optional :: error_handler + interface + subroutine error_handler(msg) + character(len=*), intent(in) :: msg + end subroutine error_handler + end interface + + character, dimension(:), pointer :: errmsg + + integer :: i, m, n, n_err + n = size(fx%error_stack%stack) + n_err = n + + do i = 1, n + n_err = n_err + size(fx%error_stack%stack(i)%msg) ! + spaces + size of entityref + enddo + allocate(errmsg(n_err)) + errmsg = '' + n = 1 + do i = 1, size(fx%error_stack%stack) + m = size(fx%error_stack%stack(i)%msg) + errmsg(n:n+m-1) = fx%error_stack%stack(i)%msg + errmsg(n+m:n+m) = " " + n = n + m + 1 + enddo + ! FIXME put location information in here + if (present(error_handler)) then + call error_handler(str_vs(errmsg)) + deallocate(errmsg) + if (fx%state==ST_STOP) return + else + call FoX_error(str_vs(errmsg)) + endif + + end subroutine sax_error + + pure function URIlength(fx, qname) result(l_u) + type(sax_parser_t), intent(in) :: fx + character(len=*), intent(in) :: qName + integer :: l_u + integer :: n + n = index(QName, ':') + if (n > 0) then + l_u = len(getnamespaceURI(fx%nsDict, QName(1:n-1))) + else + l_u = len(getnamespaceURI(fx%nsDict)) + endif + end function URIlength + + pure function getURIofQName(fx, qname) result(URI) + type(sax_parser_t), intent(in) :: fx + character(len=*), intent(in) :: qName + character(len=URIlength(fx, qname)) :: URI + + integer :: n + n = index(QName, ':') + if (n > 0) then + URI = getnamespaceURI(fx%nsDict, QName(1:n-1)) + else + URI = getnamespaceURI(fx%nsDict) + endif + + end function getURIofQName + + pure function getLocalNameofQName(qname) result(localName) + character(len=*), intent(in) :: qName + character(len=len(QName)-index(QName,':')) :: localName + + localName = QName(index(QName,':')+1:) + end function getLocalNameofQName +#endif + +end module m_sax_parser diff --git a/src/xml/sax/m_sax_reader.F90 b/src/xml/sax/m_sax_reader.F90 new file mode 100644 index 0000000000..4693ff7f46 --- /dev/null +++ b/src/xml/sax/m_sax_reader.F90 @@ -0,0 +1,402 @@ +module m_sax_reader +#ifndef DUMMYLIB + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + use fox_m_fsys_format, only: operator(//) + use m_common_charset, only: XML1_0 + use m_common_error, only: error_stack, FoX_error, in_error, add_error + use m_common_io, only: setup_io, get_unit, io_err + + use FoX_utils, only: URI, parseURI, copyURI, destroyURI, & + hasScheme, getScheme, getPath + + use m_sax_xml_source, only: xml_source_t, & + get_char_from_file, push_file_chars, parse_declaration + + implicit none + private + + type file_buffer_t + !FIXME private + type(xml_source_t), pointer :: f(:) => null() + logical :: standalone = .false. + integer :: xml_version = XML1_0 + end type file_buffer_t + + public :: file_buffer_t + public :: line + public :: column + + public :: open_file + public :: close_file + + public :: open_new_file + + public :: push_chars + + public :: get_character + public :: get_all_characters + + public :: open_new_string + public :: pop_buffer_stack + + public :: parse_xml_declaration + public :: parse_text_declaration + + public :: reading_main_file + public :: reading_first_entity + +contains + + subroutine open_file(fb, iostat, file, lun, string, es) + type(file_buffer_t), intent(out) :: fb + character(len=*), intent(in), optional :: file + integer, intent(out) :: iostat + integer, intent(in), optional :: lun + character(len=*), intent(in), optional :: string + type(error_stack), intent(inout) :: es + + type(URI), pointer :: fileURI + + iostat = 0 + + call setup_io() + if (present(string)) then + if (present(file)) then + call FoX_error("Cannot specify both file and string input to open_xml") + elseif (present(lun)) then + call FoX_error("Cannot specify lun for string input to open_xml") + endif + fileURI => parseURI("") + call open_new_string(fb, string, "", baseURI=fileURI) + else + fileURI => parseURI(file) + if (.not.associated(fileURI)) then + call add_error(es, "Could not open file "//file//" - not a valid URI") + return + endif + call open_new_file(fb, fileURI, iostat, lun) + endif + call destroyURI(fileURI) + + end subroutine open_file + + + subroutine open_new_file(fb, baseURI, iostat, lun, pe) + type(file_buffer_t), intent(inout) :: fb + integer, intent(out) :: iostat + type(URI), pointer :: baseURI + integer, intent(in), optional :: lun + logical, intent(in), optional :: pe + + integer :: i + type(xml_source_t) :: f + type(xml_source_t), pointer :: temp(:) + logical :: pe_ + + if (present(pe)) then + pe_ = pe + else + pe_ = .false. + endif + + if (hasScheme(baseURI)) then + if (getScheme(baseURI)/="file") then + iostat = io_err + return + endif + endif + + call open_actual_file(f, getPath(baseURI), iostat, lun) + if (iostat==0) then + if (.not.associated(fb%f)) allocate(fb%f(0)) + ! First file + + temp => fb%f + allocate(fb%f(size(temp)+1)) + do i = 1, size(temp) + fb%f(i+1)%lun = temp(i)%lun + fb%f(i+1)%xml_version = temp(i)%xml_version + fb%f(i+1)%encoding => temp(i)%encoding + fb%f(i+1)%filename => temp(i)%filename + fb%f(i+1)%line = temp(i)%line + fb%f(i+1)%col = temp(i)%col + fb%f(i+1)%startChar = temp(i)%startChar + fb%f(i+1)%next_chars => temp(i)%next_chars + fb%f(i+1)%input_string => temp(i)%input_string + fb%f(i+1)%baseURI => temp(i)%baseURI + fb%f(i+1)%pe = temp(i)%pe + enddo + deallocate(temp) + fb%f(1)%lun = f%lun + fb%f(1)%filename => f%filename + if (pe_) then + fb%f(1)%next_chars => vs_str_alloc(" ") + else + fb%f(1)%next_chars => vs_str_alloc("") + endif + fb%f(1)%pe = pe_ + fb%f(1)%baseURI => copyURI(baseURI) + endif + + end subroutine open_new_file + + subroutine open_actual_file(f, file, iostat, lun) + type(xml_source_t), intent(out) :: f + character(len=*), intent(in) :: file + integer, intent(out) :: iostat + integer, intent(in), optional :: lun + + if (present(lun)) then + f%lun = lun + else + call get_unit(f%lun, iostat) + if (iostat/=0) return + endif + open(unit=f%lun, file=file, form="formatted", status="old", & + action="read", position="rewind", iostat=iostat) + if (iostat/=0) return + f%filename => vs_str_alloc(file) + + end subroutine open_actual_file + + subroutine close_file(fb) + type(file_buffer_t), intent(inout) :: fb + + integer :: i + + do i = 1, size(fb%f) + call close_actual_file(fb%f(i)) + enddo + + deallocate(fb%f) + + end subroutine close_file + + + subroutine close_actual_file(f) + type(xml_source_t), intent(inout) :: f + + deallocate(f%filename) + + if (f%lun>0) then + close(f%lun) + else + deallocate(f%input_string%s) + deallocate(f%input_string) + endif + + if (associated(f%encoding)) deallocate(f%encoding) + f%line = 0 + f%col = 0 + deallocate(f%next_chars) + call destroyURI(f%baseURI) + end subroutine close_actual_file + + + subroutine open_new_string(fb, string, name, baseURI, pe) + type(file_buffer_t), intent(inout) :: fb + character(len=*), intent(in) :: string + character(len=*), intent(in) :: name + type(URI), pointer :: baseURI + logical, intent(in), optional :: pe + + integer :: i + type(xml_source_t), pointer :: temp(:) + logical :: pe_ + + if (present(pe)) then + pe_ = pe + else + pe_ = .false. + endif + + if (.not.associated(fb%f)) allocate(fb%f(0)) + + temp => fb%f + allocate(fb%f(size(temp)+1)) + do i = 1, size(temp) + fb%f(i+1)%lun = temp(i)%lun + fb%f(i+1)%xml_version = temp(i)%xml_version + fb%f(i+1)%encoding => temp(i)%encoding + fb%f(i+1)%filename => temp(i)%filename + fb%f(i+1)%line = temp(i)%line + fb%f(i+1)%col = temp(i)%col + fb%f(i+1)%startChar = temp(i)%startChar + fb%f(i+1)%next_chars => temp(i)%next_chars + fb%f(i+1)%input_string => temp(i)%input_string + fb%f(i+1)%baseURI => temp(i)%baseURI + fb%f(i+1)%pe = temp(i)%pe + enddo + deallocate(temp) + + allocate(fb%f(1)%input_string) + fb%f(1)%filename => vs_str_alloc(name) + fb%f(1)%input_string%s => vs_str_alloc(string) + if (pe_) then + fb%f(1)%next_chars => vs_str_alloc(" ") + else + fb%f(1)%next_chars => vs_str_alloc("") + endif + fb%f(1)%pe = pe_ + if (associated(baseURI)) then + fb%f(1)%baseURI => copyURI(baseURI) + else + fb%f(1)%baseURI => copyURI(fb%f(2)%baseURI) + endif + + end subroutine open_new_string + + subroutine pop_buffer_stack(fb) + type(file_buffer_t), intent(inout) :: fb + + integer :: i + type(xml_source_t), pointer :: temp(:) + + call close_actual_file(fb%f(1)) + + temp => fb%f + allocate(fb%f(size(temp)-1)) + do i = 1, size(temp)-1 + fb%f(i)%lun = temp(i+1)%lun + fb%f(i)%xml_version = temp(i+1)%xml_version + fb%f(i)%encoding => temp(i+1)%encoding + fb%f(i)%filename => temp(i+1)%filename + fb%f(i)%line = temp(i+1)%line + fb%f(i)%col = temp(i+1)%col + fb%f(i)%startChar = temp(i+1)%startChar + fb%f(i)%next_chars => temp(i+1)%next_chars + fb%f(i)%input_string => temp(i+1)%input_string + fb%f(i)%baseURI => temp(i+1)%baseURI + fb%f(i)%pe = temp(i+1)%pe + enddo + deallocate(temp) + + end subroutine pop_buffer_stack + + + subroutine push_chars(fb, s) + type(file_buffer_t), intent(inout) :: fb + character(len=*), intent(in) :: s + + call push_file_chars(fb%f(1), s) + + end subroutine push_chars + + function get_character(fb, eof, es) result(string) + type(file_buffer_t), intent(inout) :: fb + logical, intent(out) :: eof + type(error_stack), intent(inout) :: es + character(len=1) :: string + + type(xml_source_t), pointer :: f + character, pointer :: temp(:) + + f => fb%f(1) + + if (size(f%next_chars)>0) then + eof = .false. + string = f%next_chars(1) + if (size(f%next_chars)>1) then + temp => vs_str_alloc(str_vs(f%next_chars(2:))) + else + temp => vs_str_alloc("") + endif + deallocate(f%next_chars) + f%next_chars => temp + else + string = get_char_from_file(f, fb%xml_version, eof, es) + endif + + end function get_character + + function get_all_characters(fb, es) result(s) + type(file_buffer_t), intent(inout) :: fb + type(error_stack), intent(inout) :: es + character, pointer :: s(:) + + logical :: eof + character :: c + character, pointer :: temp(:) + + eof = .false. + s => vs_str_alloc("") + do while (.not.eof) + c = get_character(fb, eof, es) + if (eof.or.in_error(es)) return + temp => vs_str_alloc(str_vs(s)//c) + deallocate(s) + s => temp + enddo + end function get_all_characters + + function line(fb) result(n) + type(file_buffer_t), intent(in) :: fb + integer :: n + + n = fb%f(1)%line + end function line + + function column(fb) result(n) + type(file_buffer_t), intent(in) :: fb + integer :: n + + n = fb%f(1)%col + end function column + + subroutine parse_xml_declaration(fb, xv, enc, sa, es) + type(file_buffer_t), intent(inout) :: fb + integer, intent(out) :: xv + character, pointer :: enc(:) + logical, intent(out) :: sa + type(error_stack), intent(inout) :: es + + logical :: eof + + call parse_declaration(fb%f(1), eof, es, sa) + if (eof.or.in_error(es)) then + call add_error(es, "Error parsing XML declaration") + else + fb%xml_version = fb%f(1)%xml_version + xv = fb%xml_version + enc => vs_vs_alloc(fb%f(1)%encoding) + endif + end subroutine parse_xml_declaration + + subroutine parse_text_declaration(fb, es) + type(file_buffer_t), intent(inout) :: fb + type(error_stack), intent(inout) :: es + + logical :: eof + integer :: xv + + xv = fb%f(size(fb%f))%xml_version + + call parse_declaration(fb%f(1), eof, es) + if (in_error(es)) then + call add_error(es, "Error parsing text declaration") + return + elseif (xv==XML1_0.and.fb%f(1)%xml_version/=XML1_0) then + call add_error(es, "XML 1.0 document cannot reference entities with higher version numbers") + return + endif + + end subroutine parse_text_declaration + + + function reading_main_file(fb) result(p) + type(file_buffer_t), intent(in) :: fb + logical :: p + + p = (size(fb%f)==1) + end function reading_main_file + + function reading_first_entity(fb) result(p) + type(file_buffer_t), intent(in) :: fb + logical :: p + + p = (size(fb%f)==2) + end function reading_first_entity +#endif + +end module m_sax_reader diff --git a/src/xml/sax/m_sax_tokenizer.F90 b/src/xml/sax/m_sax_tokenizer.F90 new file mode 100644 index 0000000000..97824fd7ad --- /dev/null +++ b/src/xml/sax/m_sax_tokenizer.F90 @@ -0,0 +1,1185 @@ +module m_sax_tokenizer + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: vs_str, str_vs, vs_str_alloc + use m_common_charset, only: XML_WHITESPACE, & + upperCase, isInitialNameChar + use m_common_error, only: add_error, in_error + use m_common_entities, only: entity_t, existing_entity, & + expand_entity_text, expand_char_entity, & + add_internal_entity, pop_entity_list, getEntityByName + use m_common_namecheck, only: checkName, checkCharacterEntityReference + + use m_sax_reader, only: file_buffer_t, open_new_file, pop_buffer_stack, & + push_chars, get_character, get_all_characters, & + parse_text_declaration + use m_sax_types ! everything, really +#ifdef PGF90 + use fox_m_utils_uri, only: URI +#endif + + implicit none + private + + public :: sax_tokenize + public :: normalize_attribute_text + public :: expand_pe_text + +contains + + subroutine sax_tokenize(fx, fb, eof) + type(sax_parser_t), intent(inout) :: fx + type(file_buffer_t), intent(inout) :: fb + logical, intent(out) :: eof + + character :: c, q + integer :: xv, phrase + logical :: firstChar, ws_discard + character, pointer :: tempString(:) + + xv = fx%xds%xml_version + + if (associated(fx%token)) deallocate(fx%token) + fx%token => vs_str_alloc("") + if (fx%nextTokenType/=TOK_NULL) then + eof = .false. + fx%tokenType = fx%nextTokenType + fx%nextTokenType = TOK_NULL + return + endif + fx%tokentype = TOK_NULL + + q = " " + phrase = 0 + firstChar = .true. + ws_discard = .false. + do + c = get_character(fb, eof, fx%error_stack) + if (eof) then + if (fx%state==ST_CHAR_IN_CONTENT) then + if (phrase==1) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(fx%token)//"]") + deallocate(tempString) + elseif (phrase==2) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(fx%token)//"]]") + deallocate(tempString) + endif + fx%tokenType = TOK_CHAR + endif + if (fx%tokenType/=TOK_NULL) then + ! make sure we pass back this token before eof'ing + eof = .false. + return + endif + endif + if (eof.or.in_error(fx%error_stack)) return + if (fx%inIntSubset) then + tempString => fx%xds%intSubset + fx%xds%intSubset => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + + select case (fx%state) + case (ST_MISC) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c, XML_WHITESPACE)>0) then + if (c=="<") then + ws_discard = .false. + else + call add_error(fx%error_stack, "Unexpected character found outside content") + endif + endif + else + if (c=="?") then + fx%tokenType = TOK_PI_TAG + elseif (c=="!") then + fx%tokenType = TOK_BANG_TAG + elseif (isInitialNameChar(c, xv)) then + call push_chars(fb, c) + fx%tokenType = TOK_OPEN_TAG + else + call add_error(fx%error_stack, "Unexpected character after <") + endif + endif + + case (ST_BANG_TAG) + if (firstChar) then + if (c=="-") then + phrase = 1 + elseif (c=="[") then + fx%tokenType = TOK_OPEN_SB + elseif (verify(c,upperCase)==0) then + deallocate(fx%token) + fx%token => vs_str_alloc(c) + else + call add_error(fx%error_stack, "Unexpected character after 0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + call push_chars(fb, c) + fx%tokenType = TOK_NAME + endif + + case (ST_START_PI) + ! grab until whitespace or ? + if (verify(c, XML_WHITESPACE//"?")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c=="?") call push_chars(fb, c) + endif + + case (ST_PI_CONTENTS) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c, XML_WHITESPACE)/=0) then + ws_discard = .false. + else + cycle + endif + endif + if (phrase==1) then + if (c==">") then + fx%tokenType = TOK_CHAR + fx%nextTokenType = TOK_PI_END + elseif (c=="?") then + ! The last ? didn't mean anything, but this one might. + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"?") + deallocate(tempString) + else + phrase = 0 + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"?"//c) + deallocate(tempString) + endif + elseif (c=="?") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + + case (ST_START_COMMENT) + select case(phrase) + case (0) + if (c=="-") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + case (1) + if (c=="-") then + phrase = 2 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"-"//c) + deallocate(tempString) + phrase = 0 + endif + case (2) + if (c==">") then + fx%tokenType = TOK_CHAR + fx%nextTokenType = TOK_COMMENT_END + exit + else + call add_error(fx%error_stack, & + "Expecting > after -- inside a comment.") + endif + end select + + case (ST_START_TAG) + ! grab until whitespace or /, > + if (verify(c, XML_WHITESPACE//"/>")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c==">") then + fx%nextTokenType = TOK_END_TAG + else + call push_chars(fb, c) + endif + endif + + case (ST_START_CDATA_DECLARATION) + if (firstChar) then + if (verify(c, XML_WHITESPACE)==0) then + call add_error(fx%error_stack, & + "Whitespace not allowed around CDATA in section declaration") + else + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + else + if (verify(c, XML_WHITESPACE)==0) then + call add_error(fx%error_stack, & + "Whitespace not allowed around CDATA in section declaration") + elseif (c=="[") then + fx%tokenType = TOK_NAME + if (c=="[") fx%nextTokenType = TOK_OPEN_SB + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + + + case (ST_CDATA_CONTENTS) + select case(phrase) + case (0) + if (c=="]") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + case (1) + if (c=="]") then + phrase = 2 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]"//c) + deallocate(tempString) + phrase = 0 + endif + case (2) + if (c==">") then + fx%tokenType = TOK_CHAR + fx%nextTokenType = TOK_SECTION_END + elseif (c=="]") then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]") + deallocate(tempString) + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]]"//c) + deallocate(tempString) + phrase = 0 + endif + end select + + case (ST_IN_TAG) + if (firstChar) then + if (verify(c,XML_WHITESPACE//"/>")>0) then + call add_error(fx%error_stack, & + "Whitespace required inside tag") + exit + endif + ws_discard = .true. + endif + if (ws_discard) then + if (verify(c,XML_WHITESPACE)>0) then + if (c==">") then + fx%tokenType = TOK_END_TAG + elseif (c=="/") then + phrase = 1 + ws_discard = .false. + else + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + endif + else + if (phrase==1) then + if (c==">") then + fx%tokenType = TOK_END_TAG_CLOSE + else + call add_error(fx%error_stack, & + "Unexpected character after / in element tag") + exit + endif + else + if (verify(c,XML_WHITESPACE//"=/>")==0) then + fx%tokenType = TOK_NAME + if (c=="=") then + fx%nextTokenType = TOK_EQUALS + elseif (c==">") then + fx%nextTokenType = TOK_END_TAG + else + call push_chars(fb, c) + endif + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + endif + + case (ST_ATT_NAME) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c,XML_WHITESPACE)>0) then + if (c=="=") then + fx%tokenType = TOK_EQUALS + else + call add_error(fx%error_stack, & + "Unexpected character in element tag, expected =") + endif + endif + endif + + case (ST_ATT_EQUALS) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c,XML_WHITESPACE)>0) then + if (verify(c,"'""")==0) then + q = c + ws_discard = .false. + else + call add_error(fx%error_stack, "Expecting "" or '") + endif + endif + else + if (c==q) then + fx%tokenType = TOK_CHAR + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + + case (ST_CHAR_IN_CONTENT) + if (c=="<".or.c=="&") then + if (phrase==1) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]") + deallocate(tempString) + elseif (phrase==2) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]]") + deallocate(tempString) + endif + fx%tokenType = TOK_CHAR + if (c=="<") then + call push_chars(fb, c) + elseif (c=="&") then + fx%nextTokenType = TOK_ENTITY + endif + elseif (c=="]") then + if (phrase==0) then + phrase = 1 + elseif (phrase==1) then + phrase = 2 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]") + deallocate(tempString) + endif + elseif (c==">") then + if (phrase==1) then + phrase = 0 + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]>") + deallocate(tempString) + elseif (phrase==2) then + call add_error(fx%error_stack, "]]> forbidden in character context") + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//">") + deallocate(tempString) + endif + elseif (phrase==1) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]"//c) + deallocate(tempString) + elseif (phrase==2) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]]"//c) + deallocate(tempString) + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + + case (ST_TAG_IN_CONTENT) + if (phrase==0) then + if (c=="<") then + phrase = 1 + ws_discard = .false. + elseif (c=="&") then + fx%tokenType = TOK_ENTITY + else + call add_error(fx%error_stack, "Unexpected character found in content") + endif + elseif (phrase==1) then + if (c=="?") then + fx%tokenType = TOK_PI_TAG + elseif (c=="!") then + fx%tokenType = TOK_BANG_TAG + elseif (c=="/") then + fx%tokenType = TOK_CLOSE_TAG + elseif (isInitialNameChar(c, xv)) then + call push_chars(fb, c) + fx%tokenType = TOK_OPEN_TAG + else + call add_error(fx%error_stack, "Unexpected character after <") + endif + endif + + case (ST_START_ENTITY) + if (verify(c,XML_WHITESPACE//";")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + elseif (c==";") then + fx%tokenType = TOK_NAME + else + call add_error(fx%error_stack, "Entity reference must be terminated with a ;") + endif + + case (ST_CLOSING_TAG) + if (verify(c,XML_WHITESPACE//">")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c==">") fx%nextTokenType = TOK_END_TAG + endif + + case (ST_IN_CLOSING_TAG) + if (verify(c, XML_WHITESPACE)>0) then + if (c==">") then + fx%tokenType = TOK_END_TAG + else + call add_error(fx%error_stack, "Unexpected character - expecting >") + endif + endif + + case (ST_IN_DOCTYPE, ST_DOC_NAME, ST_DOC_SYSTEM, ST_DOC_PUBLIC, & + ST_DOC_DECL, ST_CLOSE_DOCTYPE) + if (firstChar) then + if (verify(c, XML_WHITESPACE)>0) then + if (c==">") then + fx%tokenType = TOK_END_TAG + elseif (c=="[") then + fx%tokenType = TOK_OPEN_SB + else + call add_error(fx%error_stack, & + "Missing whitespace in doctype delcaration.") + endif + else + ws_discard = .true. + endif + endif + if (ws_discard) then + if (verify(c, XML_WHITESPACE)>0) then + if (verify(c, "'""")==0) then + q = c + deallocate(fx%token) + fx%token => vs_str_alloc("") + ws_discard = .false. + elseif (c=="[") then + fx%tokenType = TOK_OPEN_SB + elseif (c==">") then + fx%tokenType = TOK_END_TAG + else + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + endif + else + if (q/=" ".and.c==q) then + fx%tokenType = TOK_CHAR + elseif (q==" ".and.verify(c, ">")==0.and.(fx%state==ST_IN_DOCTYPE)) then + fx%nextTokenType = TOK_END_TAG + fx%tokenType = TOK_NAME + elseif (q==" ".and.verify(c, XML_WHITESPACE)==0) then + call push_chars(fb, c) + fx%tokenType = TOK_NAME + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + + case (ST_IN_SUBSET) + call tokenizeDTD + + case (ST_START_PE) + if (verify(c,XML_WHITESPACE//";")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + elseif (c==";") then + fx%tokenType = TOK_NAME + else + call add_error(fx%error_stack, "Entity reference must be terminated with a ;") + endif + + end select + + firstChar = .false. + if (fx%tokenType/=TOK_NULL) exit + enddo + + contains + + subroutine tokenizeDTD + + if (c=="%") then + if (fx%state_dtd==ST_DTD_START_COMMENT & + .or.fx%state_dtd==ST_DTD_START_PI & + .or.fx%state_dtd==ST_DTD_PI_CONTENTS & + .or.fx%state_dtd==ST_DTD_ENTITY & + .or.fx%state_dtd==ST_DTD_NOTATION_SYSTEM & + .or.fx%state_dtd==ST_DTD_NOTATION_PUBLIC & + .or.fx%state_dtd==ST_DTD_NOTATION_PUBLIC_2 & + .or.fx%state_dtd==ST_DTD_ENTITY_PUBLIC & + .or.fx%state_dtd==ST_DTD_ENTITY_SYSTEM) then + ! % is perfectly legitimate + continue + elseif (fx%state_dtd==ST_DTD_SUBSET) then + if (.not.fx%spaceBeforeEntity) then + fx%spaceBeforeEntity = .true. + call push_chars(fb, c) + c = " " + else + fx%spaceBeforeEntity = .false. + fx%tokenType = TOK_ENTITY + return + endif + elseif (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & + .and. q/="") then + ! We are inside a ATTLIST attvalue, so apparent PErefs arent. + continue + elseif (fx%inIntSubset) then + call add_error(fx%error_stack, & + "Parameter entity reference not permitted inside markup for internal subset") + return + elseif (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & + .or.fx%state_dtd==ST_DTD_ELEMENT_CONTENTS) then + if (.not.associated(fx%content)) then + ! content will not always be empty here; + ! if we have two PErefs bang next to each other. + fx%content => fx%token + fx%token => vs_str_alloc("") + endif + fx%tokenType = TOK_ENTITY + return + elseif (fx%state_dtd==ST_DTD_ENTITY_ID) then + ! % is ok if we are in the external subset + continue + else + if (.not.fx%spaceBeforeEntity) then + fx%spaceBeforeEntity = .true. + call push_chars(fb, c) + c = " " + else + fx%spaceBeforeEntity = .false. + fx%tokenType = TOK_ENTITY + return + endif + endif + endif + + select case(fx%state_dtd) + + case (ST_DTD_SUBSET) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c, XML_WHITESPACE)>0) then + if (c=="]") then + phrase = 1 + q = c + ws_discard = .false. + elseif (c=="<") then + phrase = 1 + q = c + ws_discard = .false. + else + call add_error(fx%error_stack, "Unexpected character found in document subset") + endif + endif + elseif (phrase==1) then + if (q=="<") then + if (c=="?") then + fx%tokenType = TOK_PI_TAG + elseif (c=="!") then + fx%tokenType = TOK_BANG_TAG + else + call add_error(fx%error_stack, "Unexpected character, expecting ! or ?") + endif + elseif (q=="]") then + if (c=="]") then + phrase = 2 + else + fx%tokenType = TOK_CLOSE_SB + call push_chars(fb, c) + if (fx%inIntSubset) then + tempString => fx%xds%intSubset + allocate(fx%xds%intSubset(size(tempString)-2)) + fx%xds%intSubset = tempString(:size(tempString)-2) + deallocate(tempString) + endif + endif + endif + elseif (phrase==2) then + if (c==">") then + fx%tokenType = TOK_SECTION_END + else + call add_error(fx%error_stack, "Unexpected character, expecting >") + endif + endif + + + case (ST_DTD_BANG_TAG) + if (firstChar) then + if (c=="-") then + phrase = 1 + elseif (c=="[") then + fx%tokenType = TOK_OPEN_SB + elseif (verify(c,upperCase)==0) then + deallocate(fx%token) + fx%token => vs_str_alloc(c) + else + call add_error(fx%error_stack, "Unexpected character after 0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + call push_chars(fb, c) + fx%tokenType = TOK_NAME + endif + + + case (ST_DTD_START_SECTION_DECL) + if (firstChar) then + ws_discard = .true. + endif + if (ws_discard) then + if (verify(c, XML_WHITESPACE)/=0) then + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + else + if (verify(c, XML_WHITESPACE//"[")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c=="[") fx%nextTokenType = TOK_OPEN_SB + endif + endif + + case (ST_DTD_FINISH_SECTION_DECL) + if (verify(c, XML_WHITESPACE)>0) then + if (c/="[") then + call add_error(fx%error_stack, & + "Unexpected token found, expecting [") + else + fx%tokenType = TOK_OPEN_SB + endif + endif + + case (ST_DTD_IN_IGNORE_SECTION) + select case(phrase) + case (0) + if (c=="<".or.c=="]") then + phrase = 1 + q = c + endif + case (1) + if ((q=="<".and.c=="!").or.(q=="]".and.c=="]")) then + phrase = 2 + else + phrase = 0 + endif + case (2) + if (q=="<".and.c=="[") then + fx%tokenType = TOK_SECTION_START + elseif (q=="]".and.c==">") then + fx%tokenType = TOK_SECTION_END + else + phrase = 0 + endif + end select + + + case (ST_DTD_START_PI) + ! grab until whitespace or ? + if (verify(c, XML_WHITESPACE//"?")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c=="?") call push_chars(fb, c) + endif + + case (ST_DTD_PI_CONTENTS) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c, XML_WHITESPACE)/=0) then + ws_discard = .false. + else + return + endif + endif + if (phrase==1) then + if (c==">") then + fx%tokenType = TOK_CHAR + fx%nextTokenType = TOK_PI_END + elseif (c=="?") then + ! The last ? didn't mean anything, but this one might. + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"?") + deallocate(tempString) + else + phrase = 0 + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"?"//c) + deallocate(tempString) + endif + elseif (c=="?") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + + case (ST_DTD_START_COMMENT) + select case(phrase) + case (0) + if (c=="-") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + case (1) + if (c=="-") then + phrase = 2 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"-"//c) + deallocate(tempString) + phrase = 0 + endif + case (2) + if (c==">") then + fx%tokenType = TOK_CHAR + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + fx%nextTokenType = TOK_COMMENT_END + else + call add_error(fx%error_stack, & + "Expecting > after -- inside a comment.") + endif + end select + + case (ST_DTD_ATTLIST, ST_DTD_ELEMENT, ST_DTD_ENTITY, & + ST_DTD_ENTITY_PE, ST_DTD_NOTATION) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c,XML_WHITESPACE)>0) then + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + elseif (verify(c,XML_WHITESPACE//">")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + if (c==">") then + fx%nextTokenType = TOK_END_TAG + else + call push_chars(fb, c) + endif + fx%tokenType = TOK_NAME + endif + + case (ST_DTD_ELEMENT_CONTENTS) + if (c==">") then + if (associated(fx%content)) then + deallocate(fx%token) + fx%token => fx%content + fx%content => null() + endif + fx%tokenType = TOK_DTD_CONTENTS + fx%nextTokenType = TOK_END_TAG + else + if (associated(fx%content)) then + deallocate(fx%token) + fx%token => vs_str_alloc(str_vs(fx%content)//c) + deallocate(fx%content) + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + if (c=="(") then + fx%tokenType = TOK_OPEN_PAR + fx%content => fx%token + fx%token => vs_str_alloc("") + elseif (c==")") then + fx%tokenType = TOK_CLOSE_PAR + fx%content => fx%token + fx%token => vs_str_alloc("") + endif + endif + + case (ST_DTD_ATTLIST_CONTENTS) + if (c==">") then + fx%tokenType = TOK_DTD_CONTENTS + fx%nextTokenType = TOK_END_TAG + elseif (associated(fx%content)) then + deallocate(fx%token) + fx%token => vs_str_alloc(str_vs(fx%content)//c) + deallocate(fx%content) + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + if (c=="'".or.c=="""") then + if (q==c) then + q = "" + else + q = c + endif + endif + + case (ST_DTD_ENTITY_ID, ST_DTD_ENTITY_PUBLIC, ST_DTD_ENTITY_SYSTEM, & + ST_DTD_ENTITY_NDATA, ST_DTD_ENTITY_END, ST_DTD_ENTITY_NDATA_VALUE, & + ST_DTD_NOTATION_ID, ST_DTD_NOTATION_SYSTEM, ST_DTD_NOTATION_PUBLIC, & + ST_DTD_NOTATION_PUBLIC_2, ST_DTD_NOTATION_END) + if (firstChar) then + if (verify(c, XML_WHITESPACE)>0) then + if (c==">") then + fx%tokenType = TOK_END_TAG + else + call add_error(fx%error_stack, "Missing whitespace in DTD.") + endif + else + ws_discard = .true. + endif + elseif (ws_discard) then + if (verify(c, XML_WHITESPACE)>0) then + if (verify(c, "'""")==0) then + q = c + deallocate(fx%token) + fx%token => vs_str_alloc("") + ws_discard = .false. + elseif (c==">") then + fx%tokenType = TOK_END_TAG + else + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + endif + else + if (q/=" ".and.c==q) then + fx%tokenType = TOK_CHAR + elseif (q==" ".and.verify(c, XML_WHITESPACE//">")==0) then + fx%tokenType = TOK_NAME + if (c==">") then + fx%nextTokenType = TOK_END_TAG + else + call push_chars(fb, c) + endif + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + + end select + end subroutine tokenizeDTD + + end subroutine sax_tokenize + + + recursive function normalize_attribute_text(fx, s_in) result(s_out) + type(sax_parser_t), intent(inout) :: fx + character, dimension(:), intent(in) :: s_in + character, dimension(:), pointer :: s_out + + character, dimension(:), pointer :: s_temp, s_temp2, s_ent, tempString + character :: dummy + integer :: i, i2, j + type(entity_t), pointer :: ent +#ifdef PGF90 + type(URI), pointer :: nullURI + nullURI => null() +#endif + + ! Condense all whitespace, only if we are validating, + ! Expand all & + ! Complain about < and & + + allocate(s_temp(size(s_in))) ! in the first instance + allocate(s_out(0)) ! in case we return early ... + s_ent => null() + tempString => null() + + i2 = 1 + i = 1 + do + if (i > size(s_in)) exit + ! Firstly, all whitespace must become 0x20 + if (verify(s_in(i),XML_WHITESPACE)==0) then + s_temp(i2) = " " + ! Then, < is always illegal + i = i + 1 + i2 = i2 + 1 + elseif (s_in(i)=='<') then + call add_error(fx%error_stack, "Illegal < found in attribute.") + goto 100 + ! Then, expand < + elseif (s_in(i)=='&') then + j = index(str_vs(s_in(i+1:)), ';') + if (j==0) then + call add_error(fx%error_stack, "Illegal & found in attribute") + goto 100 + elseif (j==1) then + call add_error(fx%error_stack, "No entity reference found") + goto 100 + endif + allocate(tempString(j-1)) + tempString = s_in(i+1:i+j-1) + if (existing_entity(fx%predefined_e_list, str_vs(tempString))) then + ! Expand immediately + s_temp(i2) = expand_entity_text(fx%predefined_e_list, str_vs(tempString)) + i = i + j + 1 + i2 = i2 + 1 + elseif (checkCharacterEntityReference(str_vs(tempString), fx%xds%xml_version)) then + ! Expand all character entities + s_temp(i2) = expand_char_entity(str_vs(tempString)) + i = i + j + 1 + i2 = i2 + 1 ! fixme + elseif (checkName(str_vs(tempString), fx%xds%xml_version)) then + ent => getEntityByName(fx%forbidden_ge_list, str_vs(tempString)) + if (associated(ent)) then + call add_error(fx%error_stack, 'Recursive entity expansion') + goto 100 + else + ent => getEntityByName(fx%xds%entityList, str_vs(tempString)) + endif + if (associated(ent)) then + if (ent%wfc.and.fx%xds%standalone) then + call add_error(fx%error_stack, 'Externally declared entity referenced in standalone document') + goto 100 + endif + !is it the right sort of entity? + if (ent%external) then + call add_error(fx%error_stack, "External entity forbidden in attribute") + goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_ge_list, str_vs(tempString), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_ge_list, str_vs(tempString), "", null(), .false.) +#endif + ! Recursively expand entity, checking for errors. + s_ent => normalize_attribute_text(fx, & + vs_str(expand_entity_text(fx%xds%entityList, str_vs(tempString)))) + dummy = pop_entity_list(fx%forbidden_ge_list) + if (in_error(fx%error_stack)) then + goto 100 + endif + allocate(s_temp2(size(s_temp)+size(s_ent)-j)) + s_temp2(:i2-1) = s_temp(:i2-1) + s_temp2(i2:i2+size(s_ent)-1) = s_ent + deallocate(s_temp) + s_temp => s_temp2 + nullify(s_temp2) + i = i + j + 1 + i2 = i2 + size(s_ent) + deallocate(s_ent) + else + s_temp(i2:i2+j) = s_in(i:i+j) + i = i + j + 1 + i2 = i2 + j + 1 + if (.not.fx%skippedExternal.or.fx%xds%standalone) then + call add_error(fx%error_stack, "Undeclared entity encountered in standalone document.") + goto 100 + endif + endif + else + call add_error(fx%error_stack, "Illegal entity reference") + goto 100 + endif + deallocate(tempString) + else + s_temp(i2) = s_in(i) + i = i + 1 + i2 = i2 + 1 + endif + enddo + + deallocate(s_out) + allocate(s_out(i2-1)) + s_out = s_temp(:i2-1) +100 deallocate(s_temp) + if (associated(s_ent)) deallocate(s_ent) + if (associated(tempString)) deallocate(tempString) + + end function normalize_attribute_text + + recursive function expand_pe_text(fx, s_in, fb) result(s_out) + type(sax_parser_t), intent(inout) :: fx + character, dimension(:), intent(in) :: s_in + type(file_buffer_t), intent(inout) :: fb + character, dimension(:), pointer :: s_out + + character, dimension(:), pointer :: s_temp, s_temp2, s_ent, tempString + character :: dummy + integer :: i, i2, j, iostat + type(entity_t), pointer :: ent +#ifdef PGF90 + type(URI), pointer :: nullURI + nullURI => null() +#endif + + ! Expand all %PE; + + allocate(s_temp(size(s_in))) ! in the first instance + allocate(s_out(0)) ! in case we return early ... + s_ent => null() + tempString => null() + s_temp2 => null() + + i2 = 1 + i = 1 + do while (i <= size(s_in)) + if (s_in(i)=='%') then + j = index(str_vs(s_in(i+1:)), ';') + if (j==0) then + call add_error(fx%error_stack, "Illegal % found in attribute") + goto 100 + elseif (j==1) then + call add_error(fx%error_stack, "No entity reference found") + goto 100 + endif + allocate(tempString(j-1)) + tempString = s_in(i+1:i+j-1) + if (checkName(str_vs(tempString), fx%xds%xml_version)) then + ent => getEntityByName(fx%forbidden_pe_list, str_vs(tempString)) + if (associated(ent)) then + call add_error(fx%error_stack, 'Recursive entity expansion') + goto 100 + endif + ent => getEntityByName(fx%xds%peList, str_vs(tempString)) + if (associated(ent)) then + if (ent%wfc.and.fx%xds%standalone) then + call add_error(fx%error_stack, & + "Externally declared entity used in standalone document") + goto 100 + elseif (str_vs(ent%notation)/="") then + call add_error(fx%error_stack, "Unparsed entity reference forbidden in entity value") + goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_pe_list, str_vs(tempString), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_pe_list, str_vs(tempString), "", null(), .false.) +#endif + ! Recursively expand entity, checking for errors. + if (ent%external) then + call open_new_file(fb, ent%baseURI, iostat) + if (iostat/=0) then + call add_error(fx%error_stack, "Unable to access external parameter entity") + goto 100 + endif + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + s_temp2 => get_all_characters(fb, fx%error_stack) + call pop_buffer_stack(fb) + if (in_error(fx%error_stack)) goto 100 + s_ent => expand_pe_text(fx, s_temp2, fb) + deallocate(s_temp2) + else + s_ent => expand_pe_text(fx, & + vs_str(expand_entity_text(fx%xds%peList, str_vs(tempString))), fb) + endif + dummy = pop_entity_list(fx%forbidden_pe_list) + if (in_error(fx%error_stack)) then + goto 100 + endif + allocate(s_temp2(size(s_temp)+size(s_ent)-j)) + s_temp2(:i2-1) = s_temp(:i2-1) + s_temp2(i2:i2+size(s_ent)-1) = s_ent + deallocate(s_temp) + s_temp => s_temp2 + s_temp2 => null() + i = i + j + 1 + i2 = i2 + size(s_ent) + deallocate(s_ent) + else + s_temp(i2:i2+j) = s_in(i:i+j) + i = i + j + 1 + i2 = i2 + j + 1 + if (.not.fx%skippedExternal.or.fx%xds%standalone) then + call add_error(fx%error_stack, "Reference to undeclared parameter entity encountered in standalone document.") + goto 100 + endif + endif + else + call add_error(fx%error_stack, "Illegal parameter entity reference") + goto 100 + endif + deallocate(tempString) + else + s_temp(i2) = s_in(i) + i = i + 1 + i2 = i2 + 1 + endif + enddo + + deallocate(s_out) + allocate(s_out(i2-1)) + s_out = s_temp(:i2-1) +100 deallocate(s_temp) + if (associated(s_temp2)) deallocate(s_temp2) + if (associated(s_ent)) deallocate(s_ent) + if (associated(tempString)) deallocate(tempString) + + end function expand_pe_text +#endif + +end module m_sax_tokenizer diff --git a/src/xml/sax/m_sax_types.F90 b/src/xml/sax/m_sax_types.F90 new file mode 100644 index 0000000000..b0148a693c --- /dev/null +++ b/src/xml/sax/m_sax_types.F90 @@ -0,0 +1,161 @@ +module m_sax_types + +#ifndef DUMMYLIB + use m_common_attrs, only: dictionary_t + use m_common_elstack, only: elstack_t + use m_common_entities, only: entity_list + use m_common_error, only: error_stack + use m_common_namespaces, only: namespacedictionary + use m_common_notations, only: notation_list + use m_common_struct, only: xml_doc_state + + use m_sax_reader, only: file_buffer_t + + implicit none + + ! Context + + integer, parameter :: CTXT_NULL = -1 + integer, parameter :: CTXT_INIT = 0 + integer, parameter :: CTXT_BEFORE_DTD = 1 + integer, parameter :: CTXT_IN_DTD = 2 + integer, parameter :: CTXT_IGNORE = 3 + integer, parameter :: CTXT_BEFORE_CONTENT = 4 + integer, parameter :: CTXT_IN_CONTENT = 5 + integer, parameter :: CTXT_AFTER_CONTENT = 6 + + ! State + + integer, parameter :: ST_STOP = -1 + integer, parameter :: ST_NULL = 0 + integer, parameter :: ST_MISC = 1 + integer, parameter :: ST_BANG_TAG = 2 + integer, parameter :: ST_START_PI = 3 + integer, parameter :: ST_PI_CONTENTS = 4 + integer, parameter :: ST_PI_END = 5 + integer, parameter :: ST_START_COMMENT = 6 + integer, parameter :: ST_COMMENT_END = 7 + integer, parameter :: ST_START_TAG = 8 + integer, parameter :: ST_START_CDATA_DECLARATION = 9 + integer, parameter :: ST_FINISH_CDATA_DECLARATION = 10 + integer, parameter :: ST_IN_TAG = 11 + integer, parameter :: ST_ATT_NAME = 12 + integer, parameter :: ST_ATT_EQUALS = 13 + integer, parameter :: ST_CHAR_IN_CONTENT = 14 + integer, parameter :: ST_CLOSING_TAG = 15 + integer, parameter :: ST_CDATA_CONTENTS = 16 + integer, parameter :: ST_IN_CLOSING_TAG = 17 + integer, parameter :: ST_TAG_IN_CONTENT = 18 + integer, parameter :: ST_CDATA_END = 19 + integer, parameter :: ST_IN_DOCTYPE = 20 + integer, parameter :: ST_DOC_NAME = 21 + integer, parameter :: ST_DOC_SYSTEM = 22 + integer, parameter :: ST_DOC_PUBLIC = 23 + integer, parameter :: ST_DOC_DECL = 24 + integer, parameter :: ST_CLOSE_DOCTYPE = 25 + integer, parameter :: ST_START_ENTITY = 26 + integer, parameter :: ST_START_PE = 27 + integer, parameter :: ST_IN_SUBSET = 28 + +! DTD states + integer, parameter :: ST_DTD_NULL = 50 + integer, parameter :: ST_DTD_SUBSET = 51 + integer, parameter :: ST_DTD_START_SECTION_DECL = 52 + integer, parameter :: ST_DTD_FINISH_SECTION_DECL = 53 + integer, parameter :: ST_DTD_IN_IGNORE_SECTION = 54 + integer, parameter :: ST_DTD_BANG_TAG = 55 + integer, parameter :: ST_DTD_START_PI = 56 + integer, parameter :: ST_DTD_PI_CONTENTS = 57 + integer, parameter :: ST_DTD_PI_END = 58 + integer, parameter :: ST_DTD_COMMENT_END = 59 + integer, parameter :: ST_DTD_START_COMMENT = 60 + integer, parameter :: ST_DTD_ATTLIST = 61 + integer, parameter :: ST_DTD_ELEMENT = 62 + integer, parameter :: ST_DTD_ENTITY = 63 + integer, parameter :: ST_DTD_NOTATION = 64 + integer, parameter :: ST_DTD_NOTATION_ID = 65 + integer, parameter :: ST_DTD_NOTATION_SYSTEM = 66 + integer, parameter :: ST_DTD_NOTATION_PUBLIC = 67 + integer, parameter :: ST_DTD_NOTATION_PUBLIC_2 = 68 + integer, parameter :: ST_DTD_NOTATION_END = 69 + integer, parameter :: ST_DTD_ENTITY_PE = 70 + integer, parameter :: ST_DTD_ENTITY_ID = 71 + integer, parameter :: ST_DTD_ENTITY_PUBLIC = 72 + integer, parameter :: ST_DTD_ENTITY_SYSTEM = 73 + integer, parameter :: ST_DTD_ENTITY_NDATA = 74 + integer, parameter :: ST_DTD_ENTITY_NDATA_VALUE = 75 + integer, parameter :: ST_DTD_ENTITY_END = 76 + integer, parameter :: ST_DTD_ATTLIST_CONTENTS = 77 + integer, parameter :: ST_DTD_ATTLIST_END = 78 + integer, parameter :: ST_DTD_ELEMENT_CONTENTS = 79 + integer, parameter :: ST_DTD_ELEMENT_END = 80 + integer, parameter :: ST_DTD_DONE = 81 + +! token types + + integer, parameter :: TOK_NULL = 0 + integer, parameter :: TOK_PI_TAG = 1 ! + integer, parameter :: TOK_COMMENT_END = 10 ! --> + integer, parameter :: TOK_SECTION_START = 11 ! + integer, parameter :: TOK_END_TAG = 13 ! > + integer, parameter :: TOK_END_TAG_CLOSE = 14 ! /> + integer, parameter :: TOK_CLOSE_TAG = 15 ! null() + character, dimension(:), pointer :: content => null() + integer :: tokenType = TOK_NULL + integer :: nextTokenType = TOK_NULL + character, dimension(:), pointer :: name => null() + character, dimension(:), pointer :: attname => null() + logical :: error = .false. + type(error_stack) :: error_stack + ! Aspects of document structure + character, dimension(:), pointer :: root_element => null() + type(elstack_t) :: elstack + type(dictionary_t) :: attributes + type(namespacedictionary) :: nsdict + type(notation_list) :: nlist + type(entity_list) :: predefined_e_list + type(entity_list) :: forbidden_pe_list + type(entity_list) :: forbidden_ge_list + character(len=1), dimension(:), pointer :: PublicId => null() + character(len=1), dimension(:), pointer :: SystemId => null() + character(len=1), dimension(:), pointer :: Ndata => null() + logical :: inIntSubset = .false. + logical :: spaceBeforeEntity = .false. + end type sax_parser_t +#endif + + type xml_t +#ifndef DUMMYLIB + type(file_buffer_t) :: fb + type(sax_parser_t) :: fx +#else + integer :: i = 0 +#endif + end type xml_t + +end module m_sax_types diff --git a/src/xml/sax/m_sax_xml_source.F90 b/src/xml/sax/m_sax_xml_source.F90 new file mode 100644 index 0000000000..21c238e6a8 --- /dev/null +++ b/src/xml/sax/m_sax_xml_source.F90 @@ -0,0 +1,438 @@ +module m_sax_xml_source + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc + use fox_m_fsys_format, only: operator(//) + use m_common_error, only: error_stack, add_error, in_error + use m_common_charset, only: XML_WHITESPACE, XML_INITIALENCODINGCHARS, & + XML_ENCODINGCHARS, XML1_0, XML1_1, isXML1_0_NameChar, & + isLegalChar, isUSASCII, allowed_encoding + use m_common_io, only: io_eor, io_eof + + use FoX_utils, only: URI + + implicit none + private + + type buffer_t + character, dimension(:), pointer :: s + integer :: pos = 1 + end type buffer_t + + type xml_source_t + !FIXME private + integer :: lun = -1 + integer :: xml_version = XML1_0 + character, pointer :: encoding(:) => null() + logical :: isUSASCII + character, pointer :: filename(:) => null() + type(URI), pointer :: baseURI => null() + integer :: line = 0 + integer :: col = 0 + integer :: startChar = 1 ! First character after XML decl + character, pointer :: next_chars(:) => null() ! pushback buffer + type(buffer_t), pointer :: input_string => null() + logical :: pe = .false. ! is this a parameter entity? + logical :: eof = .false.! need to keep track of this at the end of pes + end type xml_source_t + + public :: buffer_t + public :: xml_source_t + + public :: get_char_from_file + public :: push_file_chars + public :: parse_declaration + +contains + + + function get_char_from_file(f, xv, eof, es) result(string) + type(xml_source_t), intent(inout) :: f + integer, intent(in) :: xv + logical, intent(out) :: eof + type(error_stack), intent(inout) :: es + character(len=1) :: string + + integer :: iostat + logical :: pending + character :: c, c2 + + pending = .false. + eof = .false. + c = read_single_char(f, iostat) + if (iostat==io_eof) then + eof = .true. + return + elseif (iostat/=0) then + call add_error(es, "Error reading "//str_vs(f%filename)) + return + endif + if (.not.isLegalChar(c, f%isUSASCII, xv)) then + call add_error(es, "Illegal character found at " & + //str_vs(f%filename)//":"//f%line//":"//f%col) + return + endif + if (c==achar(13)) then + c = achar(10) + c2 = read_single_char(f, iostat) + if (iostat==io_eof) then + ! the file has just ended on a single CR. Report is as a LF. + ! Ignore the eof just now, it'll be picked up if we need to + ! perform another read. + eof = .false. + elseif (iostat/=0) then + call add_error(es, "Error reading "//str_vs(f%filename)) + return + elseif (c2/=achar(10)) then + ! then we keep c2, otherwise we'd just ignore it. + pending = .true. + endif + endif + string = c + + if (pending) then + ! we have one character left over, put in the pushback buffer + deallocate(f%next_chars) + allocate(f%next_chars(1)) + f%next_chars = c2 + endif + + if (c==achar(10)) then + f%line = f%line + 1 + f%col = 0 + else + f%col = f%col + 1 + endif + + end function get_char_from_file + + function read_single_char(f, iostat) result(c) + type(xml_source_t), intent(inout) :: f + integer, intent(out) :: iostat + character :: c + + if (f%eof) then + c = "" + iostat = io_eof + return + endif + if (f%lun==-1) then + if (f%input_string%pos>size(f%input_string%s)) then + c = "" + if (f%pe) then + iostat = 0 + else + iostat = io_eof + endif + f%eof = .true. + else + iostat = 0 + c = f%input_string%s(f%input_string%pos) + f%input_string%pos = f%input_string%pos + 1 + endif + else + read (unit=f%lun, iostat=iostat, advance="no", fmt="(a1)") c + if (iostat==io_eor) then + iostat = 0 +#ifdef FC_EOR_LF + c = achar(10) +#else + c = achar(13) +#endif + elseif (iostat==io_eof) then + if (f%pe) iostat = 0 + c = "" + f%eof = .true. + endif + endif + end function read_single_char + + subroutine rewind_source(f) + type(xml_source_t), intent(inout) :: f + + if (f%lun==-1) then + f%input_string%pos = 1 + else + rewind(f%lun) + endif + end subroutine rewind_source + + subroutine push_file_chars(f, s) + type(xml_source_t), intent(inout) :: f + character(len=*), intent(in) :: s + character, dimension(:), pointer :: nc + + nc => vs_str_alloc(s//str_vs(f%next_chars)) + deallocate(f%next_chars) + f%next_chars => nc + + end subroutine push_file_chars + + + subroutine parse_declaration(f, eof, es, standalone) + type(xml_source_t), intent(inout) :: f + logical, intent(out) :: eof + type(error_stack), intent(inout) :: es + logical, intent(out), optional :: standalone + + integer :: parse_state, xd_par + character :: c, q + character, pointer :: ch(:), ch2(:) + + integer, parameter :: XD_0 = 0 + integer, parameter :: XD_START = 1 + integer, parameter :: XD_TARGET = 2 + integer, parameter :: XD_MISC = 3 + integer, parameter :: XD_PA = 4 + integer, parameter :: XD_EQ = 5 + integer, parameter :: XD_QUOTE = 6 + integer, parameter :: XD_PV = 7 + integer, parameter :: XD_END = 8 + integer, parameter :: XD_SPACE = 9 + + integer, parameter :: xd_nothing = 0 + integer, parameter :: xd_version = 1 + integer, parameter :: xd_encoding = 2 + integer, parameter :: xd_standalone = 3 + + f%xml_version = XML1_0 + if (present(standalone)) standalone = .false. + + f%startChar = 1 + + parse_state = XD_0 + xd_par = xd_nothing + ch => null() + do + c = get_char_from_file(f, XML1_0, eof, es) + if (eof) then + call rewind_source(f) + exit + elseif (in_error(es)) then + goto 100 + endif + f%startChar = f%startChar + 1 + + select case (parse_state) + + case (XD_0) + if (c=="<") then + parse_state = XD_START + else + call rewind_source(f) + exit + endif + + case (XD_START) + if (c=="?") then + parse_state = XD_TARGET + ch => vs_str_alloc("") + else + call rewind_source(f) + exit + endif + + case (XD_TARGET) + if (isXML1_0_NameChar(c)) then + ch2 => vs_str_alloc(str_vs(ch)//c) + deallocate(ch) + ch => ch2 + elseif (verify(c, XML_WHITESPACE)==0 & + .and.str_vs(ch)=="xml") then + deallocate(ch) + parse_state = XD_MISC + else + call rewind_source(f) + deallocate(ch) + exit + endif + + case (XD_SPACE) + if (verify(c, XML_WHITESPACE)==0) then + parse_state = XD_MISC + elseif (c=="?") then + parse_state = XD_END + else + call add_error(es, & + "Missing space in XML declaration") + endif + + case (XD_MISC) + if (c=="?") then + parse_state = XD_END + elseif (isXML1_0_NameChar(c)) then + ch => vs_str_alloc(c) + parse_state = XD_PA + elseif (verify(c, XML_WHITESPACE)>0) then + call add_error(es, & + "Unexpected character in XML declaration") + endif + + case (XD_PA) + if (isXML1_0_NameChar(c)) then + ch2 => vs_str_alloc(str_vs(ch)//c) + deallocate(ch) + ch => ch2 + elseif (verify(c, XML_WHITESPACE//"=")==0) then + select case (str_vs(ch)) + + case ("version") + select case (xd_par) + case (xd_nothing) + xd_par = xd_version + case default + call add_error(es, & + "Cannot specify version twice in XML declaration") + end select + + case ("encoding") + select case (xd_par) + case (xd_nothing) + if (present(standalone)) then + call add_error(es, & + "Must specify version before encoding in XML declaration") + else + xd_par = xd_encoding + endif + case (xd_version) + xd_par = xd_encoding + case (xd_encoding) + call add_error(es, & + "Cannot specify encoding twice in XML declaration") + case (xd_standalone) + call add_error(es, & + "Cannot specify encoding after standalone in XML declaration") + end select + + case ("standalone") + if (.not.present(standalone)) & + call add_error(es, & + "Cannot specify standalone in text declaration") + select case (xd_par) + case (xd_nothing) + call add_error(es, & + "Must specify version before standalone in XML declaration") + case (xd_version, xd_encoding) + xd_par = xd_standalone + case (xd_standalone) + call add_error(es, & + "Cannot specify standalone twice in XML declaration") + end select + + case default + call add_error(es, & + "Unknown parameter "//str_vs(ch)//" in XML declaration, "//& + "expecting version, encoding or standalone") + + end select + + deallocate(ch) + if (c=="=") then + parse_state = XD_QUOTE + else + parse_state = XD_EQ + endif + else + call add_error(es, & + "Unexpected character found in XML declaration") + endif + + case (XD_EQ) + if (c=="=") then + parse_state = XD_QUOTE + elseif (verify(c, XML_WHITESPACE)>0) then + call add_error(es, & + "Unexpected character found in XML declaration; expecting ""=""") + endif + + case (XD_QUOTE) + if (verify(c, "'""")==0) then + q = c + parse_state = XD_PV + ch => vs_str_alloc("") + elseif (verify(c, XML_WHITESPACE)>0) then + call add_error(es, & + "Unexpected character found in XML declaration; expecting "" or '") + endif + + case (XD_PV) + if (c==q) then + select case (xd_par) + case (xd_version) + if (str_vs(ch)//"x"=="1.0x") then + f%xml_version = XML1_0 + deallocate(ch) + elseif (str_vs(ch)//"x"=="1.1x") then + f%xml_version = XML1_1 + deallocate(ch) + else + call add_error(es, & + "Unknown version number "//str_vs(ch)//" found in XML declaration; expecting 1.0 or 1.1") + endif + case (xd_encoding) + if (size(ch)==0) then + call add_error(es, & + "Empty value for encoding not allowed in XML declaration") + elseif (size(ch)==1.and.verify(ch(1), XML_INITIALENCODINGCHARS)>0) then + call add_error(es, & + "Invalid encoding found in XML declaration; illegal characters in encoding name") + elseif (size(ch)>1.and. & + (verify(ch(1), XML_INITIALENCODINGCHARS)>0 & + .or.verify(str_vs(ch(2:)), XML_ENCODINGCHARS)>0)) then + call add_error(es, & + "Invalid encoding found in XML declaration; illegal characters in encoding name") + elseif (.not.allowed_encoding(str_vs(ch))) then + call add_error(es, "Unknown character encoding in XML declaration") + else + f%encoding => ch + f%isUSASCII = isUSASCII(str_vs(ch)) + ch => null() + endif + case (xd_standalone) + if (str_vs(ch)//"x"=="yesx") then + standalone = .true. + deallocate(ch) + elseif (str_vs(ch)//"x"=="nox") then + standalone = .false. + deallocate(ch) + else + call add_error(es, & + "Invalid value for standalone found in XML declaration; expecting yes or no") + + endif + end select + parse_state = XD_SPACE + else + ch2 => vs_str_alloc(str_vs(ch)//c) + deallocate(ch) + ch => ch2 + endif + + case (XD_END) + if (c==">") then + exit + else + call add_error(es, & + "Unexpected character found in XML declaration; expecting >") + endif + + end select + + end do + + if (.not.associated(f%encoding)) then + if (present(standalone).or.parse_state/=XD_END) then + f%encoding => vs_str_alloc("utf-8") + else + call add_error(es, "Missing encoding in text declaration") + endif + endif + +100 if (associated(ch)) deallocate(ch) + ! if there is no XML declaraion, or if parsing caused an error, then + if (parse_state/=XD_END.or.in_error(es)) f%startChar = 1 + + end subroutine parse_declaration +#endif + +end module m_sax_xml_source diff --git a/src/xml/sax/makefile b/src/xml/sax/makefile new file mode 100644 index 0000000000..a440a53c82 --- /dev/null +++ b/src/xml/sax/makefile @@ -0,0 +1,49 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_sax.o: m_sax_operate.o +m_sax_operate.o: m_sax_parser.o m_sax_reader.o m_sax_types.o +m_sax_parser.o: m_sax_reader.o m_sax_tokenizer.o m_sax_types.o +m_sax_reader.o: m_sax_xml_source.o +m_sax_tokenizer.o: m_sax_reader.o m_sax_types.o +m_sax_types.o: m_sax_reader.o diff --git a/src/xml/utils/FoX_utils.F90 b/src/xml/utils/FoX_utils.F90 new file mode 100644 index 0000000000..560f91e59e --- /dev/null +++ b/src/xml/utils/FoX_utils.F90 @@ -0,0 +1,22 @@ +module FoX_utils + + use fox_m_utils_uuid + use fox_m_utils_uri + + implicit none + private + + public :: generate_uuid + + public :: URI + public :: parseURI + public :: rebaseURI + public :: copyURI + public :: destroyURI + public :: expressURI + public :: hasFragment + public :: hasScheme + public :: getScheme + public :: getPath + +end module FoX_utils diff --git a/src/xml/utils/fox_m_utils_mtprng.F90 b/src/xml/utils/fox_m_utils_mtprng.F90 new file mode 100644 index 0000000000..90a766d2b4 --- /dev/null +++ b/src/xml/utils/fox_m_utils_mtprng.F90 @@ -0,0 +1,360 @@ +module fox_m_utils_mtprng +#ifndef DUMMYLIB +!--------------------------------------------------------------------- +! From the Algorithmic Conjurings of Scott Robert Ladd comes... +!--------------------------------------------------------------------- +! +! mtprng.f90 (a Fortran 95 module) +! +! An implementation of the Mersenne Twister algorithm for generating +! psuedo-random sequences. +! +! History +! ------- +! 1.0.0 Initial release +! +! 1.1.0 6 February 2002 +! Updated to support algorithm revisions posted +! by Matsumoto and Nishimura on 26 January 2002 +! +! 1.5.0 12 December 2003 +! Added to hypatia project +! Minor style changes +! Tightened code +! Now state based; no static variables +! Removed mtprng_rand_real53 +! +! 2.0.0 4 January 2004 +! Corrected erroneous unsigned bit manipulations +! Doubled resolution by using 64-bit math +! Added mtprng_rand64 + +! Version for distribution with FoX +! Very small cosmetic changes to fit FoX naming scheme and +! avoid additional dependencies. +! Toby White , 2007 + +! +! ORIGINAL ALGORITHM COPYRIGHT +! ============================ +! Copyright (C) 1997,2002 Makoto Matsumoto and Takuji Nishimura. +! Any feedback is very welcome. For any question, comments, see +! http://www.math.keio.ac.jp/matumoto/emt.html or email +! matumoto@math.keio.ac.jp +!--------------------------------------------------------------------- +! +! COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: +! +! This notice applies *only* to this specific expression of this +! algorithm, and does not imply ownership or invention of the +! implemented algorithm. +! +! If you modify this file, you may insert additional notices +! immediately following this sentence. +! +! Copyright 2001, 2002, 2004 Scott Robert Ladd. +! All rights reserved, except as noted herein. +! +! This computer program source file is supplied "AS IS". Scott Robert +! Ladd (hereinafter referred to as "Author") disclaims all warranties, +! expressed or implied, including, without limitation, the warranties +! of merchantability and of fitness for any purpose. The Author +! assumes no liability for direct, indirect, incidental, special, +! exemplary, or consequential damages, which may result from the use +! of this software, even if advised of the possibility of such damage. +! +! The Author hereby grants anyone permission to use, copy, modify, and +! distribute this source code, or portions hereof, for any purpose, +! without fee, subject to the following restrictions: +! +! 1. The origin of this source code must not be misrepresented. +! +! 2. Altered versions must be plainly marked as such and must not +! be misrepresented as being the original source. +! +! 3. This Copyright notice may not be removed or altered from any +! source or altered source distribution. +! +! The Author specifically permits (without fee) and encourages the use +! of this source code for entertainment, education, or decoration. If +! you use this source code in a product, acknowledgment is not required +! but would be appreciated. +! +! Acknowledgement: +! This license is based on the wonderful simple license that +! accompanies libpng. +! +!----------------------------------------------------------------------- +! +! For more information on this software package, please visit +! Scott's web site, Coyote Gulch Productions, at: +! +! http://www.coyotegulch.com +! +!----------------------------------------------------------------------- + + implicit none + + ! Kind types for 64-, 32-, 16-, and 8-bit signed integers + integer, parameter :: INT64 = selected_int_kind(18) + integer, parameter :: INT32 = selected_int_kind(9) + integer, parameter :: INT16 = selected_int_kind(4) + integer, parameter :: INT08 = selected_int_kind(2) + + ! Kind types for IEEE 754/IEC 60559 single- and double-precision reals + integer, parameter :: IEEE32 = selected_real_kind( 6, 37 ) + integer, parameter :: IEEE64 = selected_real_kind( 15, 307 ) + + !------------------------------------------------------------------------------ + ! Everything is private unless explicitly made public + private + + public :: mtprng_state, & + mtprng_init, mtprng_init_by_array, & + mtprng_rand64, mtprng_rand, mtprng_rand_range, & + mtprng_rand_real1, mtprng_rand_real2, mtprng_rand_real3 + + !------------------------------------------------------------------------------ + ! Constants + integer(INT32), parameter :: N = 624_INT32 + integer(INT32), parameter :: M = 397_INT32 + + !------------------------------------------------------------------------------ + ! types + type mtprng_state + integer(INT32) :: mti = -1 + integer(INT64), dimension(0:N-1) :: mt + end type + +contains + !-------------------------------------------------------------------------- + ! Initializes the generator with "seed" + subroutine mtprng_init(seed, state) + + ! arguments + integer(INT32), intent(in) :: seed + type(mtprng_state), intent(out) :: state + + ! working storage + integer :: i + + ! save seed + state%mt(0) = seed + + ! Set the seed using values suggested by Matsumoto & Nishimura, using + ! a generator by Knuth. See original source for details. + do i = 1, N - 1 + state%mt(i) = iand(4294967295_INT64,1812433253_INT64 * ieor(state%mt(i-1),ishft(state%mt(i-1),-30_INT64)) + i) + end do + + state%mti = N + + end subroutine mtprng_init + + !-------------------------------------------------------------------------- + ! Initialize with an array of seeds + subroutine mtprng_init_by_array(init_key, state) + + ! arguments + integer(INT32), dimension(:), intent(in) :: init_key + type(mtprng_state), intent(out) :: state + + ! working storage + integer :: key_length + integer :: i + integer :: j + integer :: k + + call mtprng_init(19650218_INT32,state) + + i = 1 + j = 0 + key_length = size(init_key) + + do k = max(N,key_length), 0, -1 + state%mt(i) = ieor(state%mt(i),(ieor(state%mt(i-1),ishft(state%mt(i-1),-30_INT64) * 1664525_INT64))) + init_key(j) + j + + i = i + 1 + j = j + 1 + + if (i >= N) then + state%mt(0) = state%mt(N-1) + i = 1 + end if + + if (j >= key_length) j = 0 + end do + + do k = N-1, 0, -1 + state%mt(i) = ieor(state%mt(i),(ieor(state%mt(i-1),ishft(state%mt(i-1),-30_INT64) * 1566083941_INT64))) - i + + i = i + 1 + + if (i>=N) then + state%mt(0) = state%mt(N-1) + i = 1 + end if + end do + + state%mt(0) = 1073741824_INT64 ! 0x40000000, assuring non-zero initial array + + end subroutine mtprng_init_by_array + + !-------------------------------------------------------------------------- + ! Obtain the next 32-bit integer in the psuedo-random sequence + function mtprng_rand64(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + !return type + integer(INT64) :: r + + ! internal constants + integer(INT64), dimension(0:1), parameter :: mag01 = (/ 0_INT64, -1727483681_INT64 /) + + ! Period parameters + integer(INT64), parameter :: UPPER_MASK = 2147483648_INT64 + integer(INT64), parameter :: LOWER_MASK = 2147483647_INT64 + + ! Tempering parameters + integer(INT64), parameter :: TEMPERING_B = -1658038656_INT64 + integer(INT64), parameter :: TEMPERING_C = -272236544_INT64 + + ! Note: variable names match those in original example + integer(INT32) :: kk + + ! Generate N words at a time + if (state%mti >= N) then + ! The value -1 acts as a flag saying that the seed has not been set. + if (state%mti == -1) call mtprng_init(4357_INT32,state) + + ! Fill the mt array + do kk = 0, N - M - 1 + r = ior(iand(state%mt(kk),UPPER_MASK),iand(state%mt(kk+1),LOWER_MASK)) + state%mt(kk) = ieor(ieor(state%mt(kk + M),ishft(r,-1_INT64)),mag01(iand(r,1_INT64))) + end do + + do kk = N - M, N - 2 + r = ior(iand(state%mt(kk),UPPER_MASK),iand(state%mt(kk+1),LOWER_MASK)) + state%mt(kk) = ieor(ieor(state%mt(kk + (M - N)),ishft(r,-1_INT64)),mag01(iand(r,1_INT64))) + end do + + r = ior(iand(state%mt(N-1),UPPER_MASK),iand(state%mt(0),LOWER_MASK)) + state%mt(N-1) = ieor(ieor(state%mt(M-1),ishft(r,-1)),mag01(iand(r,1_INT64))) + + ! Start using the array from first element + state%mti = 0 + end if + + ! Here is where we actually calculate the number with a series of + ! transformations + r = state%mt(state%mti) + state%mti = state%mti + 1 + + r = ieor(r,ishft(r,-11)) + r = iand(4294967295_INT64,ieor(r,iand(ishft(r, 7),TEMPERING_B))) + r = iand(4294967295_INT64,ieor(r,iand(ishft(r,15),TEMPERING_C))) + r = ieor(r,ishft(r,-18)) + + end function mtprng_rand64 + + !-------------------------------------------------------------------------- + ! Obtain the next 32-bit integer in the psuedo-random sequence + function mtprng_rand(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + !return type + integer(INT32) :: r + + ! working storage + integer(INT64) :: x + + ! done + x = mtprng_rand64(state) + + if (x > 2147483647_INT64) then + r = x - 4294967296_INT64 + else + r = x + end if + + end function mtprng_rand + + !--------------------------------------------------------------------------- + ! Obtain a psuedorandom integer in the range [lo,hi] + function mtprng_rand_range(state, lo, hi) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + integer, intent(in) :: lo + integer, intent(in) :: hi + + ! return type + integer(INT32) :: r + + ! Use real value to caluclate range + r = lo + floor((hi - lo + 1.0_IEEE64) * mtprng_rand_real2(state)) + + end function mtprng_rand_range + + !-------------------------------------------------------------------------- + ! Obtain a psuedorandom real number in the range [0,1], i.e., a number + ! greater than or equal to 0 and less than or equal to 1. + function mtprng_rand_real1(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + ! return type + real(IEEE64) :: r + + ! Local constant; precalculated to avoid division below + real(IEEE64), parameter :: factor = 1.0_IEEE64 / 4294967295.0_IEEE64 + + ! compute + r = real(mtprng_rand64(state),IEEE64) * factor + + end function mtprng_rand_real1 + + !-------------------------------------------------------------------------- + ! Obtain a psuedorandom real number in the range [0,1), i.e., a number + ! greater than or equal to 0 and less than 1. + function mtprng_rand_real2(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + ! return type + real(IEEE64) :: r + + ! Local constant; precalculated to avoid division below + real(IEEE64), parameter :: factor = 1.0_IEEE64 / 4294967296.0_IEEE64 + + ! compute + r = real(mtprng_rand64(state),IEEE64) * factor + + end function mtprng_rand_real2 + + !-------------------------------------------------------------------------- + ! Obtain a psuedorandom real number in the range (0,1), i.e., a number + ! greater than 0 and less than 1. + function mtprng_rand_real3(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + ! return type + real(IEEE64) :: r + + ! Local constant; precalculated to avoid division below + real(IEEE64), parameter :: factor = 1.0_IEEE64 / 4294967296.0_IEEE64 + + r = (real(mtprng_rand64(state),IEEE64) + 0.5_IEEE64) * factor + + end function mtprng_rand_real3 + +#endif +end module fox_m_utils_mtprng diff --git a/src/xml/utils/fox_m_utils_uri.F90 b/src/xml/utils/fox_m_utils_uri.F90 new file mode 100644 index 0000000000..be6fefcdd7 --- /dev/null +++ b/src/xml/utils/fox_m_utils_uri.F90 @@ -0,0 +1,1013 @@ +module fox_m_utils_uri +#ifndef DUMMYLIB + + ! Manipulate URIs and URI references a la RFC 2396 + ! NB: ... + ! Forbidden (ASCII control) characters are not handled correctly + ! checking of reg names (not hosts) is done wrongly + ! checking of ipv6/X is untested + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + use fox_m_fsys_format, only: str_to_int_10, str_to_int_16, str + use fox_m_fsys_string, only: toLower + + implicit none + private + + type path_segment + character, pointer :: s(:) => null() + end type path_segment +#endif + + type URI + private +#ifndef DUMMYLIB + character, pointer :: scheme(:) => null() + character, pointer :: authority(:) => null() + character, pointer :: userinfo(:) => null() + character, pointer :: host(:) => null() + integer :: port = -1 + character, pointer :: path(:) => null() + type(path_segment), pointer :: segments(:) => null() + character, pointer :: query(:) => null() + character, pointer :: fragment(:) => null() +#else + integer :: i +#endif + end type URI + +#ifndef DUMMYLIB + character(len=*), parameter :: lowalpha = "abcdefghijklmnopqrstuvwxyz" + character(len=*), parameter :: upalpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + character(len=*), parameter :: alpha = lowalpha//upalpha + character(len=*), parameter :: digit = "0123456789" + character(len=*), parameter :: hexdigit = "0123456789abcdefABCDEF" + character(len=*), parameter :: alphanum = alpha//digit + character(len=*), parameter :: unreserved = alphanum//"-._~" + character(len=*), parameter :: gen_delims = ":/?#[]@" + character(len=*), parameter :: sub_delims = "!$&'()*+,;=" + character(len=*), parameter :: reserved = gen_delims//sub_delims + character(len=*), parameter :: pchar = unreserved//":@&=+$," + character(len=*), parameter :: uric_no_slash = unreserved//";?:@&=+$," + character(len=*), parameter :: uric = unreserved//reserved + character(len=*), parameter :: unwise = "{}|\^[]`" +#endif + + public :: URI + public :: parseURI + public :: expressURI + public :: isAbsoluteURI + public :: rebaseURI + public :: copyURI + public :: destroyURI + + public :: hasScheme + public :: getScheme + public :: hasAuthority + public :: getAuthority + public :: hasUserinfo + public :: getUserinfo + public :: hasHost + public :: getHost + public :: hasPort + public :: getPort + public :: getPath + public :: hasQuery + public :: getQuery + public :: hasFragment + public :: getFragment + +#ifndef DUMMYLIB + public :: dumpURI +#endif + +contains + +#ifndef DUMMYLIB + function unEscape_alloc(s) result(c) + character(len=*), intent(in) :: s + character, pointer :: c(:) + + integer :: i, j, n + character(len(s)) :: t + + c => null() + + i = 1 + j = 0 + do while (i<=len(s)) + j = j + 1 + if (s(i:i)=="%") then + if (i+2>len(s)) return + if (verify(s(i+1:i+2), hexdigit)/=0) return + n = str_to_int_16(s(i+1:i+2)) + t(j:j) = achar(n) + i = i + 3 + else + t(j:j) = s(i:i) + i = i + 1 + endif + enddo + + c => vs_str_alloc(t(:j)) + end function unEscape_alloc + + function verifyWithPctEncoding(s, chars) result(p) + character(len=*), intent(in) :: s + character(len=*), intent(in) :: chars + logical :: p + + integer :: i + + p = .false. + i = 1 + do while (i<=len(s)) + if (s(i:i)=="%") then + if (i+2>len(s)) return + if (verify(s(i+1:i+2), hexdigit)>0) return + i = i + 3 + else + if (verify(s(i:i),chars)>0) return + i = i + 1 + endif + enddo + p = .true. + end function verifyWithPctEncoding + + pure function pctEncode_len(s, chars) result(n) + character(len=*), intent(in) :: s + character(len=*), intent(in) :: chars + integer :: n + + integer :: i + n = 0 + do i = 1, len(s) + n = n + 1 + if (verify(s(i:i), unwise)==0.or.verify(s(i:i), chars)>0) n = n + 2 + enddo + + end function pctEncode_len + + function pctEncode(s, chars) result(ps) + character(len=*), intent(in) :: s + character(len=*), intent(in) :: chars + character(len=pctEncode_len(s, chars)) :: ps + + integer :: i, n + + n = 1 + do i = 1, len(s) + if (verify(s(i:i), unwise)==0.or.verify(s(i:i), chars)>0) then + ps(n:n+2) = "%"//str(iachar(s(i:i)), "x2") + n = n + 3 + else + ps(n:n) = s(i:i) + n = n + 1 + endif + enddo + + end function pctEncode + + function checkOpaquePart(part) result(p) + character(len=*), intent(in) :: part + logical :: p + + if (len(part)>0) then + p = verify(part(1:1), uric_no_slash)==0 + if (p.and.len(part)>1) & + p = verify(part(1:1), uric)==0 + endif + end function checkOpaquePart + + + function checkScheme(scheme) result(p) + character(len=*), intent(in) :: scheme + logical :: p + + p = len(scheme)>0 + if (p) then + p = verify(scheme(1:1), lowalpha//upalpha)==0 + if (p.and.len(scheme)>1) then + p = verify(scheme(2:), alphanum//"+-.")==0 + endif + endif + end function checkScheme + + function checkIpvX(host) result(p) + character(len=*), intent(in) :: host + logical :: p + + integer :: i, n1, n2 + + p = (len(host)>5).and.(host(1:1)=="[".and.host(len(host):len(host))=="]") + + if (p) then + + ! Try IPvFuture: + p = (verify(host(2:2),"Vv")==0 & + .and.verify(host(3:3),hexdigit)==0 & + .and.host(4:4)=="." & + .and.verify(host(3:3),unreserved//sub_delims//":")==0) + + if (.not.p) then ! is it IPv6? + n1 = 0 + do i = 1, 4 + n2 = index(host(n1+1:), ":") + if (n2==0.or.n2>6) return + n2 = n2 + n1 + if (verify(host(n1+1:n2-1),hexdigit)>0) return + n1 = n2 + enddo + n2 = index(host(n1+1:), ":") + if (n2==0) then + ! this must be ipv4 format + do i = 1, 3 + n2 = index(host(n1+1:), ".") + if (n2==0) return + n2 = n2 + n1 + if (verify(host(n1+1:n2-1),digit)>0) return + if (str_to_int_10(host(n1+1:n2-1))>255) return + n1 = n2 + enddo + ! Now there must be 3 or less digits followed by ] + n2 = len(host)-1 + if (verify(host(n1+1:n2-1),digit)>0) return + if (str_to_int_10(host(n1+1:n2-1))>255) return + elseif (n2<6) then + n2 = n2 + n1 + if (verify(host(n1+1:n2-1),hexdigit)>0) return + ! Now there must be 4 or less digits followed by ] + n1 = n2 + n2 = len(host) + if (n2-n1>4) return + if (verify(host(n1+1:n2-1),hexdigit)>0) return + endif + p = .true. + endif + endif + end function checkIpvX + + function checkHost(host) result(p) + character(len=*), intent(in) :: host + logical :: p + + p = checkIpvX(host) + if (.not.p) & + p = verifyWithPctEncoding(host, unreserved//sub_delims) + + end function checkHost + + function checkAuthority(authority, userinfo, host, port) result(p) + character(len=*), intent(in) :: authority + character, pointer :: userinfo(:), host(:) + integer :: port + logical :: p + + integer :: i1, i2 + + p = .true. + if (len(authority)==0) return + + i1 = index(authority, "@") + if (i1>0) then + i2 = index(authority(i1+1:), ":") + else + i2 = index(authority, ":") + endif + if (i1==0) then + userinfo => null() + else + p = verifyWithPctEncoding(authority(:i1-1), unreserved//sub_delims//":") + if (p) userinfo => unEscape_alloc(authority(:i1-1)) + endif + if (i2==0) then + i2 = len(authority)+1 + else + i2 = i1 + i2 + p = p.and.verify(authority(i2+1:), digit)==0 + if (p) port = str_to_int_10(authority(i2+1:)) + endif + p = p.and.checkHost(authority(i1+1:i2-1)) + if (p) then + host => vs_str_alloc(authority(i1+1:i2-1)) + else + if (associated(userinfo)) deallocate(userinfo) + end if + + end function checkAuthority + + function checkPathSegment(segment) result(p) + character(len=*), intent(in) :: segment + logical :: p + + integer :: i1 + + i1 = index(segment, ";") + if (i1>0) then + p = verifyWithPctEncoding(segment(:i1-1), pchar) & + .and.verifyWithPctEncoding(segment(i1+1:), pchar) + else + p = verifyWithPctEncoding(segment, unreserved//pchar) + endif + end function checkPathSegment + + function checkNonOpaquePath(path, segments) result(p) + character(len=*), intent(in) :: path + type(path_segment), pointer :: segments(:) + logical :: p + + integer :: i, i1, i2 + type(path_segment), pointer :: temp(:) + + p = .true. + i1 = index(path, "/") + if (i1==1) then + allocate(segments(1)) + segments(1)%s => vs_str_alloc("/") + else + allocate(segments(0)) + i1 = 0 + endif + + do + i2 = index(path(i1+1:), "/") + if (i2==0) then + i2 = len(path) + else + i2 = i1 + i2 + endif + if (checkPathSegment(path(i1+1:i2-1))) then + allocate(temp(size(segments)+1)) + do i = 1, size(segments) + temp(i)%s => segments(i)%s + enddo + temp(i)%s => unEscape_alloc(path(i1+1:i2)) + deallocate(segments) + segments => temp + else + do i = 1, size(segments) + deallocate(segments(i)%s) + enddo + deallocate(segments) + p = .false. + return + endif + if (i2==len(path)) exit + i1 = i2 + end do + end function checkNonOpaquePath + + function checkPath(path, segments) result(p) + character(len=*), intent(in) :: path + type(path_segment), pointer :: segments(:) + logical :: p + + p = checkNonOpaquePath(path, segments) + if (.not.p) then + p = checkOpaquePart(path) + if (p) allocate(segments(0)) + endif + + end function checkPath + + function checkQuery(query) result(p) + character(len=*), intent(in) :: query + logical :: p + + p = verifyWithPctEncoding(query, uric) + end function checkQuery + + function checkFragment(fragment) result(p) + character(len=*), intent(in) :: fragment + logical :: p + + p = verifyWithPctEncoding(fragment, uric) + end function checkFragment +#endif + + function parseURI(URIstring) result(u) + character(len=*), intent(in) :: URIstring + type(URI), pointer :: u +#ifndef DUMMYLIB + character, pointer, dimension(:) :: scheme, authority, & + userinfo, host, path, query, fragment + integer :: port + type(path_segment), pointer :: segments(:) + integer :: i1, i2, i3, i4 + logical :: p + +#endif + u => null() +#ifndef DUMMYLIB + + scheme => null() + authority => null() + userinfo => null() + host => null() + port = -1 + path => null() + segments => null() + query => null() + fragment => null() + + i1 = index(URIstring, ":") + if (i1>0) then + p = checkScheme(URIstring(:i1-1)) + if (p) then + scheme => vs_str_alloc(toLower(URIstring(:i1-1))) + else + i1 = 0 + endif + endif + ! if either i1==0 or the scheme doesn't validate, there is no scheme.. + if (len(URIstring)>=i1+3) then + if (URIstring(i1+1:i1+2)=="//") then + i2 = scan(URIstring(i1+3:), "/#?") + if (i2==0) then + i2 = len(URIstring) + 1 + else + i2 = i1 + i2 + 2 + endif + p = checkAuthority(URIstring(i1+3:i2-1), userinfo, host, port) + if (.not.p) then + call cleanUp + return + endif + authority => vs_str_alloc(URIstring(i1+3:i2-1)) + else + i2 = i1 + 1 + endif + else + i2 = i1 + 1 + endif + + if (i2>len(URIstring)) then + path => vs_str_alloc("") + allocate(segments(1)) + segments(1)%s => vs_str_alloc("") + call produceResult + return + endif + + i3 = scan(URIstring(i2:),"#?") + if (i3==0) then + i3 = len(URIstring) + 1 + else + i3 = i2 + i3 - 1 + endif + p = checkPath(URIstring(i2:i3-1), segments) + if (.not.p) then + call cleanUp + return + endif + path => unEscape_alloc(URIstring(i2:i3-1)) + + if (i3>len(URIstring)) then + call produceResult + return + endif + + if (URIstring(i3:i3)=="?") then + i4 = index(URIstring(i3+1:), "#") + if (i4==0) then + i4 = len(URIstring) + 1 + else + i4 = i3 + i4 + endif + p = checkQuery(URIstring(i3+1:i4-1)) + if (.not.p) then + call cleanUp + return + endif + query => vs_str_alloc(URIstring(i3+1:i4-1)) + else + i4 = i3 + endif + + if (i4>len(URIstring)) then + call produceResult + return + endif + + p = checkFragment(URIstring(i4+1:)) + if (.not.p) then + call cleanUp + return + endif + fragment => vs_str_alloc(URIstring(i4+1:)) + call produceResult + + contains + subroutine cleanUp + integer :: i + if (associated(scheme)) deallocate(scheme) + if (associated(authority)) deallocate(authority) + if (associated(userinfo)) deallocate(userinfo) + if (associated(host)) deallocate(host) + if (associated(path)) deallocate(path) + if (associated(query)) deallocate(query) + if (associated(fragment)) deallocate(fragment) + if (associated(segments)) then + do i = 1, size(segments) + deallocate(segments(i)%s) + enddo + deallocate(segments) + endif + end subroutine cleanUp + subroutine produceResult + allocate(u) + u%scheme => scheme + u%authority => authority + u%userinfo => userinfo + u%host => host + u%port = port + u%path => path + u%segments => segments + u%query => query + u%fragment => fragment + u%segments => segments + end subroutine produceResult +#endif + end function parseURI + + function isAbsoluteURI(u) result(p) + type(URI), intent(in) :: u + logical :: p + +#ifdef DUMMYLIB + p = .false. +#else + p = associated(u%scheme).or.associated(u%authority) + if (.not.p.and.size(u%segments(1)%s)>0) then + p = u%segments(1)%s(1)=="/" + endif +#endif + end function isAbsoluteURI + + function rebaseURI(u1, u2) result(u3) + type(URI), pointer :: u1, u2 + type(URI), pointer :: u3 + + u3 => null() +#ifndef DUMMYLIB + + if (associated(u2%scheme).or.associated(u2%authority)) then + u3 => copyURI(u2) + return + endif + + allocate(u3) + if (associated(u1%scheme)) u3%scheme => vs_vs_alloc(u1%scheme) + if (associated(u1%authority)) u3%authority => vs_vs_alloc(u1%authority) + + u3%segments => appendPaths(u1%segments, u2%segments) + u3%path => expressSegments(u3%segments) + + if (associated(u2%query)) u3%query => vs_vs_alloc(u2%query) + if (associated(u2%fragment)) u3%fragment => vs_vs_alloc(u2%fragment) +#endif + end function rebaseURI + +#ifndef DUMMYLIB + function appendPaths(seg1, seg2) result(seg3) + type(path_segment), pointer :: seg1(:), seg2(:) + type(path_segment), pointer :: seg3(:) + + type(path_segment), pointer :: temp(:) + + integer :: i, n, n2 + + if (size(seg2(1)%s)==0) then + seg3 => normalizePath(seg1) + return + elseif (seg2(1)%s(1)=="/") then + seg3 => normalizePath(seg2) + return + endif + + n = size(seg1) + size(seg2) + i = size(seg1) + if (seg1(i)%s(size(seg1(i)%s))/="/") & + n = n - 1 + + allocate(temp(n)) + n2 = 1 + do i = 1, size(seg1) + if (i==size(seg1).and.seg1(i)%s(size(seg1(i)%s))/="/") exit ! it's a file + temp(n2)%s => vs_vs_alloc(seg1(i)%s) + n2 = n2 + 1 + enddo + + do i = 1, size(seg2) + temp(n2)%s => vs_vs_alloc(seg2(i)%s) + n2 = n2 + 1 + enddo + + seg3 => normalizePath(temp) + do i = 1, size(temp) + deallocate(temp(i)%s) + enddo + deallocate(temp) + + end function appendPaths + + function normalizepath(seg1) result(seg2) + type(path_segment), pointer :: seg1(:) + type(path_segment), pointer :: seg2(:) + + integer :: i, n, n2, parents + character, pointer :: tmp(:) + + + ! If the last of the input segments are + ! equal to '.' or '..', append a slash + ! so the rest of the subroutine works. + + if ((str_vs(seg1(size(seg1))%s) == '.').or. & + (str_vs(seg1(size(seg1))%s) == '..')) then + tmp => vs_vs_alloc(seg1(size(seg1))%s) + deallocate(seg1(size(seg1))%s) + seg1(size(seg1))%s => vs_str_alloc(str_vs(tmp)//"/") + deallocate(tmp) + endif + + n = 0 + parents = 0 + do i = 1, size(seg1) + if (str_vs(seg1(i)%s)//"x"=="./x") then + continue + elseif (str_vs(seg1(i)%s)//"x"=="../x") then + if (n>0) then + n = n - 1 + else + parents = parents + 1 + endif + else + n = n + 1 + endif + enddo + + n = n + parents + allocate(seg2(n)) + + n2 = parents + do i = 1, parents + seg2(i)%s => vs_str_alloc("../") + enddo + do i = 1, size(seg1) + if (str_vs(seg1(i)%s)//"x"=="./x") then + continue + elseif (str_vs(seg1(i)%s)//"x"=="../x") then + if (n2>parents) then + if (n2<=n) deallocate(seg2(n2)%s) + n2 = n2 - 1 + endif + else + n2 = n2 + 1 + if (n2>0.and.n2<=n) & + seg2(n2)%s => vs_vs_alloc(seg1(i)%s) + endif + enddo + + end function normalizepath + + function expressSegments(seg1) result(s) + type(path_segment), pointer :: seg1(:) + character, pointer :: s(:) + + integer :: i, n + + n = 0 + + do i = 1, size(seg1) + n = n + size(seg1(i)%s) + enddo + allocate(s(n)) + n = 1 + do i = 1, size(seg1) + s(n:n+size(seg1(i)%s)-1) = seg1(i)%s + n = n + size(seg1(i)%s) + enddo + end function expressSegments + + pure function expressURI_len(u) result(n) + type(URI), intent(in) :: u + integer :: n + + n = 0 + if (associated(u%scheme)) & + n = size(u%scheme) + 1 + if (associated(u%authority)) & + n = n + pctEncode_len(str_vs(u%authority), unreserved//sub_delims//"@:") + 2 + !FIXME - I suspect that ';' as the first character of a segment should be escaped + n = n + pctEncode_len(str_vs(u%path), pchar//";"//"/") + if (associated(u%query)) & + n = n + pctEncode_len(str_vs(u%query), uric) + 1 + if (associated(u%fragment)) & + n = n + pctEncode_len(str_vs(u%fragment), uric) + 1 + + end function expressURI_len + + function expressURI(u) result(URIstring) + type(URI), intent(in) :: u + character(len=expressURI_len(u)) :: URIstring + + integer :: i, j + URIstring="" + i = 1 + if (associated(u%scheme)) then + URIstring(:size(u%scheme)+1) = str_vs(u%scheme)//":" + i = i + size(u%scheme) + 1 + endif + if (associated(u%authority)) then + j = pctEncode_len(str_vs(u%authority), unreserved//sub_delims//"@:") + URIstring(i:i+j+1) = & + "//"//pctEncode(str_vs(u%authority), unreserved//sub_delims//"@:") + i = i + j + 2 + endif + if (size(u%path)>0) then + !FIXME - I suspect that ';' as the first character of a segment should be escaped + j = pctEncode_len(str_vs(u%path), pchar//";"//"/") + URIstring(i:i+j-1) = pctEncode(str_vs(u%path), pchar//";"//"/") + i = i + j + endif + if (associated(u%query)) then + j = pctEncode_len(str_vs(u%query), uric) + URIstring(i:i+j) = "?"//pctEncode(str_vs(u%query), uric) + i = i + j + 1 + endif + if (associated(u%fragment)) then + j = pctEncode_len(str_vs(u%fragment), uric) + URIstring(i:i+j) = "#"//pctEncode(str_vs(u%fragment), uric) + endif + + end function expressURI + + subroutine dumpURI(u) + type(URI), intent(in) :: u + integer :: i + if (associated(u%scheme)) then + write(*,*) "scheme: ", str_vs(u%scheme) + else + write(*,*) "scheme UNDEFINED" + endif + if (associated(u%authority)) then + write(*,*) "authority: ", str_vs(u%authority) + else + write(*,*) "authority UNDEFINED" + endif + if (associated(u%userinfo)) then + write(*,*) "userinfo: ", str_vs(u%userinfo) + else + write(*,*) "userinfo UNDEFINED" + endif + if (associated(u%host)) then + write(*,*) "host: ", str_vs(u%host) + else + write(*,*) "host UNDEFINED" + endif + if (u%port>0) then + write(*,*) "port: ", str(u%port) + else + write(*,*) "port UNDEFINED" + endif + if (associated(u%path)) then + write(*,*) "path: ", str_vs(u%path) + else + write(*,*) "path UNDEFINED" + endif + if (associated(u%segments)) then + do i = 1, size(u%segments) + write(*,*) " segment: ", str_vs(u%segments(i)%s) + enddo + endif + if (associated(u%query)) then + write(*,*) "query: ", str_vs(u%query) + else + write(*,*) "query UNDEFINED" + endif + if (associated(u%fragment)) then + write(*,*) "fragment: ", str_vs(u%fragment) + else + write(*,*) "fragment UNDEFINED" + endif + end subroutine dumpURI +#endif + + function copyURI(u1) result(u2) + type(URI), pointer :: u1 + type(URI), pointer :: u2 +#ifndef DUMMYLIB + integer :: i + + if (.not.associated(u1)) then +#endif + u2 => null() +#ifndef DUMMYLIB + return + endif + allocate(u2) + u2%scheme => vs_vs_alloc(u1%scheme) + u2%authority => vs_vs_alloc(u1%authority) + u2%userinfo => vs_vs_alloc(u1%userinfo) + u2%host => vs_vs_alloc(u1%host) + u2%port = u1%port + u2%path => vs_vs_alloc(u1%path) + allocate(u2%segments(size(u1%segments))) + do i = 1, size(u1%segments) + u2%segments(i)%s => vs_vs_alloc(u1%segments(i)%s) + enddo + u2%query => vs_vs_alloc(u1%query) + u2%fragment => vs_vs_alloc(u1%fragment) +#endif + end function copyURI + + + subroutine destroyURI(u) + type(URI), pointer :: u +#ifndef DUMMYLIB + integer :: i + if (associated(u%scheme)) deallocate(u%scheme) + if (associated(u%authority)) deallocate(u%authority) + if (associated(u%userinfo)) deallocate(u%userinfo) + if (associated(u%host)) deallocate(u%host) + if (associated(u%path)) deallocate(u%path) + if (associated(u%segments)) then + do i = 1, size(u%segments) + deallocate(u%segments(i)%s) + enddo + deallocate(u%segments) + endif + if (associated(u%query)) deallocate(u%query) + if (associated(u%fragment)) deallocate(u%fragment) + + deallocate(u) +#endif + end subroutine destroyURI + + function hasScheme(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%scheme) +#endif + end function hasScheme + + function getScheme(u) result(s) + type(URI), pointer :: u + +#ifndef DUMMYLIB + character(len=size(u%scheme)) :: s + s = str_vs(u%scheme) +#else + character(len=1) :: s + s = "" +#endif + end function getScheme + + function hasAuthority(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%authority) +#endif + end function hasAuthority + + function getAuthority(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%authority)) :: s + s = str_vs(u%authority) +#else + character(len=1) :: s + s = "" +#endif + end function getAuthority + + function hasUserinfo(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%userinfo) +#endif + end function hasUserinfo + + function getUserinfo(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%userinfo)) :: s + s = str_vs(u%userinfo) +#else + character(len=1) :: s + s = "" +#endif + end function getUserinfo + + function hasHost(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%host) +#endif + end function hasHost + + function getHost(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%host)) :: s + s = str_vs(u%host) +#else + character(len=1) :: s + s = "" +#endif + end function getHost + + function hasPort(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = u%port > 0 +#endif + end function hasPort + + function getPort(u) result(n) + type(URI), pointer :: u + integer :: n +#ifndef DUMMYLIB + n = u%port +#else + n = 0 +#endif + end function getPort + + function getPath(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%path)) :: s + s = str_vs(u%path) +#else + character(len=1) :: s + s = "" +#endif + end function getPath + + function hasQuery(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%query) +#endif + end function hasQuery + + function getQuery(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%query)) :: s + s = str_vs(u%query) +#else + character(len=1) :: s + s = "" +#endif + end function getQuery + + function hasFragment(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%fragment) +#endif + end function hasFragment + + function getFragment(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%fragment)) :: s + s = str_vs(u%fragment) +#else + character(len=1) :: s + s = "" +#endif + end function getFragment + +end module fox_m_utils_uri diff --git a/src/xml/utils/fox_m_utils_uuid.F90 b/src/xml/utils/fox_m_utils_uuid.F90 new file mode 100644 index 0000000000..fd9c272d9a --- /dev/null +++ b/src/xml/utils/fox_m_utils_uuid.F90 @@ -0,0 +1,247 @@ +module fox_m_utils_uuid + + !This generates UUIDs according to RFC 4122 + + ! Only types 1 (time-based) and 4 (pseudo-RNG-based) are implemented. + +#ifndef DUMMYLIB + use fox_m_utils_mtprng, only : mtprng_state, mtprng_init, mtprng_rand64 +#endif + + implicit none + private + +#ifndef DUMMYLIB + integer, parameter :: i4b = selected_int_kind(9) + integer, parameter :: i8b = selected_int_kind(18) + + character, parameter :: hexdigits(0:15) = & + (/'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'/) + + type(mtprng_state), save :: rng_state + logical, save :: initialized = .false. + integer, save :: values_save ! must be default for date_and_time + integer(kind=i4b), save :: hires_count = 0 + +! clock-seq holds a random number constant for the lifetime of the program +! using this module. That's the best we can do per S 4.1.5 + integer, save :: clock_seq = 0 + +#endif + + public :: generate_uuid + +contains + + function generate_uuid(version) result(uuid) + integer, intent(in), optional :: version + character(len=36) :: uuid + +#ifdef DUMMYLIB + uuid = "" +#else + integer(kind=i8b) :: timestamp, node + integer(kind=i4b) :: clock_sequence + + integer(kind=i4b) :: time_low, time_mid, time_hi_and_version + integer(kind=i4b) :: clk_seq_hi_res, clk_seq_low + + integer :: values(8) ! must be default for date_and_time + integer(kind=i4b) :: variant, v + + + if (.not.initialized) then + ! Use the current date and time to init mtprng + ! but this gives limited varaibility, so mix + ! the result up. Can we do better? In any + ! case, this gets passed through a quick + ! generator inside mtprng_init. + call date_and_time(values=values) + values(7) = values(7)*1000+values(5)*100+values(3)*10+values(1) + values(8) = values(2)*1000+values(4)*100+values(6)*10+values(8) + call mtprng_init(int(values(7)*10000+values(8), i4b), rng_state) + clock_seq = int(mtprng_rand64(rng_state), i4b) + initialized = .true. + endif + + variant = 1 + + if (present(version)) then + v = version + else + v = 4 + endif + + select case (v) + case (0) + ! Nil UUID - S 4.1.7 + uuid = repeat('0',8)//'-'//repeat('0',4)//'-'//repeat('0',4)// & + '-'//repeat('0',4)//'-'//repeat('0',12) + return + case(1) + call date_and_time(values=values) + ! In case of too-frequent requests, we will replace time_low + ! with the count below ... + if (all(values==values_save)) then + hires_count = hires_count + 1 + else + hires_count = 0 + endif + case(2-3) + !Unimplemented + uuid = '' + return + case(4) + continue + case(5) + !Unimplemented + uuid = '' + return + case default + !Unspecified + uuid = '' + return + end select + +!4.1.4 Timestamp + + select case(v) + case(1) + timestamp = get_utc_since_1582(values) + case(4) + timestamp = ior(mtprng_rand64(rng_state), ishft(mtprng_rand64(rng_state), 28)) + end select + +!4.1.5 Clock Sequence + ! 14 bits + select case(v) + case(1) + clock_sequence = clock_seq + case(4) + clock_sequence = int(mtprng_rand64(rng_state), i4b) + end select + +!4.1.6 Node + ! 48 bits + select case(v) + case(1) + node = ior(mtprng_rand64(rng_state), ishft(mtprng_rand64(rng_state), 16)) + ! No MAC address accessible - see section 4.5 !FIXME + case(4) + node = ior(mtprng_rand64(rng_state), ishft(mtprng_rand64(rng_state), 16)) + end select + + time_low = ibits(timestamp, 0, 32) + time_mid = ibits(timestamp, 32, 16) + if (hires_count==0) then + time_hi_and_version = ior(int(ibits(timestamp, 48, 12), i4b), ishft(v, 12)) + else + time_hi_and_version = ior(hires_count, ishft(v, 12)) + endif + + clk_seq_low = ibits(clock_sequence, 0, 8) + clk_seq_hi_res = ior(ibits(clock_sequence, 8, 6), ishft(variant, 6)) + + uuid = int32ToHexOctets(time_low, 4)//"-"// & + int32ToHexOctets(time_mid, 2)//"-"// & + int32ToHexOctets(time_hi_and_version, 2)//"-"// & + int32ToHexOctets(clk_seq_hi_res, 1)// & + int32ToHexOctets(clk_seq_low, 1)//"-"// & + int64ToHexOctets(node, 6) + + contains + + function int32ToHexOctets(b, n) result(s) + integer(i4b), intent(in) :: b + integer, intent(in) :: n ! number of octets to print + character(len=2*n) :: s + + integer :: i + + do i = 0, 2*n-1 + s(2*n-i:2*n-i) = hexdigits(ibits(b, i*4, 4)) + enddo + + end function int32ToHexOctets + function int64ToHexOctets(b, n) result(s) + integer(i8b), intent(in) :: b + integer, intent(in) :: n ! number of octets to print + character(len=2*n) :: s + + integer :: i + + do i = 0, 2*n-1 + s(2*n-i:2*n-i) = hexdigits(ibits(b, i*4, 4)) + enddo + + end function int64ToHexOctets + +#endif + end function generate_uuid + +#ifndef DUMMYLIB + function get_utc_since_1582(values) result(ns) + ! This subroutine is a little broken. It only works + ! for times after 1/1/2006 and takes no account + ! of any future leapseconds. It ought to serve regardless. + + ! It returns the number of 100-ns intervals since 1582-10-15-00-00-00 + + integer, dimension(8), intent(in) :: values + integer(kind=i8b) :: ns + + integer :: days + integer :: years + + integer, parameter :: days_in_normal_year(12) = & + (/31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31/) + + ns = 23_i8b * 1000_i8b * 1000_i8b * 10_i8b ! 23 leap seconds until 24:00:00 31/12/2005 + + ! A count of the 100-nanosecond intervals since the + ! beginning of the day. + ns = ns & + ! milliseconds + + int(values(8), i8b) * 10_i8b * 1000_i8b & + ! seconds + + int(values(7), i8b) * 10_i8b * 1000_i8b * 1000_i8b & + ! minutes (with timezone adjustment) + + int(values(6) + values(4), i8b) * 10_i8b * 1000_i8b * 1000_i8b * 60_i8b & + ! hours + + int(values(5), i8b) * 10_i8b * 1000_i8b * 1000_i8b * 60_i8b * 60_i8b + + ! Number of days this year: + days = sum(days_in_normal_year(:values(2)-1)) + days = days + values(3) - 1 !add days in current month + if (values(2)>2 .and. isLeapYear(values(1))) then + days = days + 1 + endif + !That's all the time since the turn of this year + + days = days + 78 ! From the start of 15th Oct to the end of 31st Dec in 1582 + !That's the additional time before the turn of the year 1583 + + days = days + 102 ! 102 leap years from 1584 to 2000 inclusive + ! That's all the intercalataed days until 2000 + + years = values(1) - 2000 - 1 ! years since 2000 - not including this year + + days = days + years/4 - years/100 + years/400 !Add extra leap days to this total: + ! That's all out intercalated days - remaining years are all 365 days long. + + years = years + 418 ! Add the years from 1583-2000 inclusive back on. + + ! Multiply by number of time units in one day & add to today's total. + ns = ns + 864000000000_i8b * (int(days,i8b) + 365_i8b * int(years,i8b)) + + contains + function isLeapYear(y) result(p) + integer, intent(in) :: y + logical :: p + p = (mod(y,4)==0 .and. .not.mod(y,100)==0 .or. mod(y,400)==0) + end function isLeapYear + + end function get_utc_since_1582 + +#endif +end module fox_m_utils_uuid diff --git a/src/xml/utils/makefile b/src/xml/utils/makefile new file mode 100644 index 0000000000..dca3504369 --- /dev/null +++ b/src/xml/utils/makefile @@ -0,0 +1,45 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_utils.o: fox_m_utils_uuid.o fox_m_utils_uri.o +fox_m_utils_uuid.o: fox_m_utils_mtprng.o diff --git a/src/xml/wxml/FoX_wxml.F90 b/src/xml/wxml/FoX_wxml.F90 new file mode 100644 index 0000000000..41c1e5652b --- /dev/null +++ b/src/xml/wxml/FoX_wxml.F90 @@ -0,0 +1,43 @@ +module FoX_wxml + + use m_wxml_core + use m_wxml_overloads + + implicit none + private + + public :: xmlf_t + public :: xml_OpenFile + public :: xml_Close + public :: xml_NewElement + public :: xml_EndElement + public :: xml_AddXMLDeclaration + public :: xml_AddXMLStylesheet + public :: xml_AddXMLPI + public :: xml_AddComment + public :: xml_AddEntityReference + + public :: xml_AddCharacters + public :: xml_AddNewline + public :: xml_AddAttribute + public :: xml_AddPseudoAttribute + + public :: xml_DeclareNamespace + public :: xml_UndeclareNamespace + + public :: xml_AddDOCTYPE + public :: xml_AddParameterEntity + public :: xml_AddInternalEntity + public :: xml_AddExternalEntity + public :: xml_AddNotation + public :: xml_AddElementToDTD + public :: xml_AddAttlistToDTD + public :: xml_AddPEreferenceToDTD + + public :: xmlf_GetPretty_print + public :: xmlf_SetPretty_print + + public :: xmlf_Name + public :: xmlf_OpenTag + +end module FoX_wxml diff --git a/src/xml/wxml/m_wxml_core.F90 b/src/xml/wxml/m_wxml_core.F90 new file mode 100644 index 0000000000..2afd30b068 --- /dev/null +++ b/src/xml/wxml/m_wxml_core.F90 @@ -0,0 +1,1848 @@ +module m_wxml_core + +#ifndef DUMMYLIB + use fox_m_fsys_abort_flush, only: pxfabort + use fox_m_fsys_array_str, only: vs_str, str_vs, vs_str_alloc + use fox_m_fsys_string, only: toLower + use fox_m_utils_uri, only: URI, parseURI, destroyURI + use m_common_attrs, only: dictionary_t, getLength, get_key, get_value, & + hasKey, add_item_to_dict, init_dict, reset_dict, destroy_dict, & + getWhitespaceHandling, sortAttrs + use m_common_buffer, only: buffer_t, len, add_to_buffer, reset_buffer, & + dump_buffer + use m_common_charset, only: XML1_0, XML1_1, checkChars + use m_common_element, only: parse_dtd_element, parse_dtd_attlist + use m_common_elstack, only: elstack_t, len, get_top_elstack, pop_elstack, & + is_empty, init_elstack, push_elstack, destroy_elstack + use m_common_entities, only: existing_entity, is_unparsed_entity + use m_common_error, only: FoX_warning_base, FoX_error_base, FoX_fatal_base, & + error_stack, in_error, FoX_get_fatal_errors, FoX_get_fatal_warnings + use m_common_io, only: get_unit + use m_common_namecheck, only: checkEncName, checkName, checkQName, & + checkCharacterEntityReference, checkPublicId, prefixOfQName, & + localpartofQName, checkPEDef, checkPseudoAttValue, checkAttValue, checkNCName, & + likeCharacterEntityReference, checkCharacterEntityReference + use m_common_namespaces, only: namespaceDictionary, getnamespaceURI, & + initnamespaceDictionary, addDefaultNS, destroyNamespaceDictionary, & + addPrefixedNS, isPrefixInForce, checkNamespacesWriting, checkEndNamespaces + use m_common_notations, only: add_notation, notation_exists + use m_common_struct, only: xml_doc_state, init_xml_doc_state, destroy_xml_doc_state, & + register_internal_PE, register_external_PE, register_internal_GE, register_external_GE + use m_wxml_escape, only: escape_string +#ifdef PGF90 + use m_common_element, only : element_t +#endif +#endif + + implicit none + private + +#ifndef DUMMYLIB + integer, parameter :: indent_inc = 2 + ! TOHW should we let this be set? + + !Output State Machines + ! status wrt root element: + integer, parameter :: WXML_STATE_1_JUST_OPENED = 0 + !File is just opened, nothing written to it yet. + integer, parameter :: WXML_STATE_1_BEFORE_ROOT = 1 + !File has been opened, something has been written, but no root element yet. + integer, parameter :: WXML_STATE_1_DURING_ROOT = 2 + !The root element has been opened but not closed + integer, parameter :: WXML_STATE_1_AFTER_ROOT = 3 + !The root element has been opened but not closed + + ! status wrt tags: + integer, parameter :: WXML_STATE_2_OUTSIDE_TAG = 0 + !We are not within a tag. + integer, parameter :: WXML_STATE_2_INSIDE_PI = 1 + !We are inside a Processing Instruction tag + integer, parameter :: WXML_STATE_2_INSIDE_ELEMENT = 2 + !We are inside an element tag. + integer, parameter :: WXML_STATE_2_IN_CHARDATA = 3 + !We are inside deliberately-constructed text. (this is only necessary for preserve_whitespace) + + ! status wrt DTD + integer, parameter :: WXML_STATE_3_BEFORE_DTD = 0 + ! No DTD has been encountered yet. + integer, parameter :: WXML_STATE_3_DURING_DTD = 1 + ! Halfway throught outputting a DTD + integer, parameter :: WXML_STATE_3_INSIDE_INTSUBSET = 2 + !We are inside the internal subset definition + integer, parameter :: WXML_STATE_3_AFTER_DTD = 3 + ! Finished outputting a DTD +#endif + + + type xmlf_t + private +#ifdef DUMMYLIB + integer :: i = 0 +#else + type(xml_doc_state) :: xds + integer :: lun = -1 + type(buffer_t) :: buffer + type(elstack_t) :: stack + type(dictionary_t) :: dict + integer :: state_1 = -1 + integer :: state_2 = -1 + integer :: state_3 = -1 + logical :: minimize_overrun = .true. + logical :: pretty_print = .false. + logical :: canonical = .false. + integer :: indent = 0 + character, pointer :: name(:) + logical :: namespace = .true. + type(namespaceDictionary) :: nsDict +#endif + end type xmlf_t + + public :: xmlf_t + + public :: xml_OpenFile + public :: xml_NewElement + public :: xml_EndElement + public :: xml_Close + public :: xml_AddXMLDeclaration + public :: xml_AddXMLStylesheet + public :: xml_AddXMLPI + public :: xml_AddComment + public :: xml_AddCharacters + public :: xml_AddNewline + public :: xml_AddEntityReference + public :: xml_AddAttribute + public :: xml_AddPseudoAttribute + public :: xml_DeclareNamespace + public :: xml_UnDeclareNamespace + public :: xml_AddDOCTYPE + public :: xml_AddParameterEntity + public :: xml_AddInternalEntity + public :: xml_AddExternalEntity + public :: xml_AddNotation + public :: xml_AddElementToDTD + public :: xml_AddAttlistToDTD + public :: xml_AddPEreferenceToDTD + + public :: xmlf_Name + public :: xmlf_OpenTag + + public :: xmlf_SetPretty_print + public :: xmlf_GetPretty_print + + interface xml_AddCharacters + module procedure xml_AddCharacters_Ch + end interface + interface xml_AddAttribute + module procedure xml_AddAttribute_Ch + end interface + interface xml_AddPseudoAttribute + module procedure xml_AddPseudoAttribute_Ch + end interface + +#ifndef DUMMYLIB + !overload error handlers to allow file info + interface wxml_warning + module procedure wxml_warning_xf, FoX_warning_base + end interface + interface wxml_error + module procedure wxml_error_xf, FoX_error_base + end interface + interface wxml_fatal + module procedure wxml_fatal_xf, FoX_fatal_base + end interface + + ! Heuristic (approximate) target for justification of output + ! only gets used for outputting attributes + integer, parameter :: COLUMNS = 80 + + ! TOHW - This is the longest string that may be output without + ! a newline. The buffer must not be larger than this, but its size + ! can be tuned for performance. + !lowest value found so far is 4096, for NAG. We use 1024 just in case. + integer, parameter :: xml_recl = 1024 +#endif + +contains + + subroutine xml_OpenFile(filename, xf, unit, iostat, preserve_whitespace, & + pretty_print, minimize_overrun, canonical, replace, addDecl, warning, & + validate, namespace) + character(len=*), intent(in) :: filename + type(xmlf_t), intent(inout) :: xf + integer, intent(in), optional :: unit + integer, intent(out), optional :: iostat + logical, intent(in), optional :: preserve_whitespace + logical, intent(in), optional :: pretty_print + logical, intent(in), optional :: minimize_overrun + logical, intent(in), optional :: canonical + logical, intent(in), optional :: replace + logical, intent(in), optional :: addDecl + logical, intent(in), optional :: warning + logical, intent(in), optional :: validate + logical, intent(in), optional :: namespace + +#ifdef DUMMYLIB + if (present(iostat)) iostat = 0 +#else + logical :: repl, decl + integer :: iostat_ + + if (xf%lun /= -1) & + call wxml_fatal("Trying to reopen an already-open XML file") + + if (present(replace)) then + repl = replace + else + repl = .true. + endif + if (present(addDecl)) then + decl = addDecl + else + decl = .true. + endif + if (present(iostat)) iostat = 0 + + allocate(xf%name(0)) + + if (present(unit)) then + if (unit==-1) then + call get_unit(xf%lun,iostat_) + if (iostat_ /= 0) then + if (present(iostat)) iostat = iostat_ + return + endif + else + xf%lun = unit + endif + else + call get_unit(xf%lun,iostat_) + if (iostat_ /= 0) then + if (present(iostat)) iostat = iostat_ + return + endif + endif + + ! Use large I/O buffer in case the O.S./Compiler combination + ! has hard-limits by default (i.e., NAGWare f95's 1024 byte limit) + ! This is related to the maximum size of the buffer. + ! TOHW - This is the longest string that may be output without + ! a newline. The buffer must not be larger than this, but its size + ! can be tuned for performance. + + if (repl) then + ! NAG insists on unnecessary duplication of iostat etc here + if (present(iostat)) then + open(unit=xf%lun, file=filename, form="formatted", status="replace", & + action="write", recl=xml_recl, iostat=iostat) + else + open(unit=xf%lun, file=filename, form="formatted", status="replace", & + action="write", recl=xml_recl) + endif + else + if (present(iostat)) then + open(unit=xf%lun, file=filename, form="formatted", status="new", & + action="write", recl=xml_recl, iostat=iostat) + else + open(unit=xf%lun, file=filename, form="formatted", status="new", & + action="write", recl=xml_recl) + endif + endif + + call init_elstack(xf%stack) + + call init_dict(xf%dict) + !NB it can make no difference which XML version we are using + !until after we output the XML declaration. So we set it to + !1.0 for the moment & reset below. + ! Actually, this is done automatically in initializing xf%xds + call init_xml_doc_state(xf%xds) + xf%xds%documentURI => vs_str_alloc(filename) + + if (present(warning)) then + xf%xds%warning = warning + else + xf%xds%warning = .false. + endif + if (present(validate)) then + xf%xds%valid = validate + else + xf%xds%valid = .false. + endif + xf%state_1 = WXML_STATE_1_JUST_OPENED + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + xf%state_3 = WXML_STATE_3_BEFORE_DTD + + if (present(pretty_print)) then + xf%pretty_print = pretty_print + else + xf%pretty_print = .true. + endif + if (present(minimize_overrun)) then + xf%minimize_overrun = minimize_overrun + else + xf%minimize_overrun = .false. + endif + if (present(preserve_whitespace)) then + xf%pretty_print = .not.preserve_whitespace + xf%minimize_overrun = preserve_whitespace + endif + if (present(canonical)) then + xf%canonical = canonical + else + xf%canonical = .false. + endif +! FIXME interplay of above options + + xf%indent = 0 + + if (decl) then + call xml_AddXMLDeclaration(xf,encoding='UTF-8') + ! which will reset the buffer itself + else + call reset_buffer(xf%buffer, xf%lun, xf%xds%xml_version) + endif + + if (present(namespace)) then + xf%namespace = namespace + else + xf%namespace = .true. + endif + if (xf%namespace) & + call initNamespaceDictionary(xf%nsDict) +#endif + end subroutine xml_OpenFile + + + subroutine xml_AddXMLDeclaration(xf, version, encoding, standalone) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in), optional :: version + character(len=*), intent(in), optional :: encoding + logical, intent(in), optional :: standalone + +#ifndef DUMMYLIB + call check_xf(xf) + ! Don't need to call checkChars on args, everything is checked + ! fully below anyway. + + if (xf%state_1 /= WXML_STATE_1_JUST_OPENED) & + call wxml_error("Tried to put XML declaration in wrong place") + + call reset_buffer(xf%buffer, xf%lun, xf%xds%xml_version) + + call xml_AddXMLPI(xf, "xml", xml=.true.) + if (present(version)) then + if (version =="1.0") then + xf%xds%xml_version = XML1_0 + call xml_AddPseudoAttribute(xf, "version", version) + elseif (version=="1.1") then + xf%xds%xml_version = XML1_1 + call xml_AddPseudoAttribute(xf, "version", version) + else + call wxml_error("Invalid XML version.") + endif + else + call xml_AddPseudoAttribute(xf, "version", "1.0") + xf%xds%xml_version = XML1_0 + endif + if (present(encoding)) then + if (.not.checkEncName(encoding)) & + call wxml_error("Invalid encoding name: "//encoding) + if (encoding /= 'UTF-8' .and. encoding /= 'utf-8') & + call wxml_warning(xf, "Non-default encoding specified: "//encoding) + call xml_AddPseudoAttribute(xf, "encoding", encoding) + endif + if (present(standalone)) then + xf%xds%standalone_declared = .true. + xf%xds%standalone = standalone + if (standalone) then + call xml_AddPseudoAttribute(xf, "standalone", "yes") + else + call xml_AddPseudoAttribute(xf, "standalone", "no") + endif + endif + call close_start_tag(xf) + ! We have to close explicitly here to ensure nothing gets tied + ! up in the XML declaration + xf%state_1 = WXML_STATE_1_BEFORE_ROOT +#endif + end subroutine xml_AddXMLDeclaration + + + subroutine xml_AddDOCTYPE(xf, name, system, public) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in), optional :: system, public + +#ifndef DUMMYLIB + type(URI), pointer :: URIref + + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + + if (present(system)) then + URIref => parseURI(system) + if (.not.associated(URIref)) call wxml_error("xml_AddDOCTYPE: Invalid SYSTEM URI") + call destroyURI(URIref) + endif + if (present(public)) then + if (.not.checkPublicId(public)) call wxml_error("xml_AddDOCTYPE: Invalid PUBLIC ID") + endif + + if (present(public).and..not.present(system)) & + call wxml_error('xml_AddDOCTYPE: PUBLIC supplied without SYSTEM for: '//name) + + ! By having an external ID we probably render this non-standalone (unless we've said that it is in the declaration) + if (present(system).and..not.xf%xds%standalone_declared) & + xf%xds%standalone=.false. + + call close_start_tag(xf) + + if (xf%state_1 /= WXML_STATE_1_BEFORE_ROOT) & + call wxml_error("Tried to put XML DOCTYPE in wrong place: "//name) + + if (xf%state_3 /= WXML_STATE_3_BEFORE_DTD) then + call wxml_error("Tried to output more than one DOCTYPE declaration: "//name) + else + xf%state_3 = WXML_STATE_3_DURING_DTD + endif + + call add_eol(xf) + call add_to_buffer(" null() +#endif + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + + if (present(PEDef)) then + if (.not.checkChars(PEDef,xf%xds%xml_version)) call wxml_error("xml_AddParameterEntity: Invalid character in PEDef") + endif + + if (present(system)) then + URIref => parseURI(system) + if (.not.associated(URIref)) call wxml_error("xml_AddParameterEntity: Invalid SYSTEM URI") + call destroyURI(URIref) + endif + if (present(public)) then + if (.not.checkPublicId(public)) call wxml_error("xml_AddParameterEntity: Invalid PUBLIC ID") + endif + + ! By adding a parameter entity (internal or external) we make this + ! a non-standalone document. + if (.not.xf%xds%standalone_declared) & + xf%xds%standalone = .false. + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot define Parameter Entity here: "//name) + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + if (present(PEdef)) then + if (present(system) .or. present(public)) & + call wxml_fatal("Parameter entity "//name//" cannot have both a PEdef and an External ID") + else + if (.not.present(system)) & + call wxml_fatal("Parameter entity "//name//" must have either a PEdef or an External ID") + endif + if (present(PEdef)) then + if (.not.checkPEDef(PEDef, xf%xds%xml_version)) & + call wxml_fatal("Parameter entity definition is invalid: "//PEDef) + if (xf%xds%standalone) then + if (.not.checkExistingRefs()) & + call wxml_error("Tried to reference unregistered parameter entity") + else + if (.not.checkExistingRefs()) & + call wxml_warning(xf, "Reference to unknown parameter entity") + endif +#ifdef PGF90 + call register_internal_PE(xf%xds, name=name, text=PEdef, baseURI=nullURIref, wfc=.false.) +#else + call register_internal_PE(xf%xds, name=name, text=PEdef, baseURI=null(), wfc=.false.) +#endif + + else +#ifdef PGF90 + call register_external_PE(xf%xds, name=name, systemId=system, & + publicId=public, baseURI=nullURIref, wfc=.false.) +#else + call register_external_PE(xf%xds, name=name, systemId=system, & + publicId=public, baseURI=null(), wfc=.false.) +#endif + endif + + call add_eol(xf) + + call add_to_buffer(" 0) then ! FIXME what if PEdef has both " and ' in it + call add_to_buffer(" '"//PEdef//"'", xf%buffer, .true.) + else + call add_to_buffer(" """//PEdef//"""", xf%buffer, .true.) + endif + call add_to_buffer(">", xf%buffer, .false.) + else + if (present(public)) then + call add_to_buffer(" PUBLIC", xf%buffer, .false.) + call add_to_buffer(" """//public//"""", xf%buffer, .true.) + else + call add_to_buffer(" SYSTEM", xf%buffer, .false.) + endif + if (scan(system, """")/=0) then + call add_to_buffer(" '"//system//"'", xf%buffer, .true.) + else + call add_to_buffer(" """//system//"""", xf%buffer, .true.) + endif + call add_to_buffer(">", xf%buffer) + endif + + contains + function checkExistingRefs() result(p) + logical :: p + + integer :: i1, i2 + + ! Here we assume we have syntactic well-formedness as + ! checked by checkPEDef. + + p = .false. + i1 = index(PEDef, '%') + i2 = 0 + do while (i1 > 0) + i1 = i2 + i1 + i2 = index(PEDef(i1+1:),';') + if (i2 == 0) return + i2 = i1 + i2 + if (.not.existing_entity(xf%xds%PEList, PEDef(i1+1:i2-1))) & + return + i1 = index(PEDef(i2+1:), '%') + enddo + p = .true. + + end function checkExistingRefs +#endif + end subroutine xml_AddParameterEntity + + + subroutine xml_AddInternalEntity(xf, name, value) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + +#ifndef DUMMYLIB +#ifdef PGF90 + type(URI), pointer :: nullURI + nullURI => null() +#endif + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + + if (.not.checkChars(value, xf%xds%xml_version)) call wxml_error("xml_AddInternalEntity: Invalid character in value") + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot define Entity here: "//name) + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("xml_AddInternalEntity: Invalid Name: "//name) +#ifdef PGF90 + call register_internal_GE(xf%xds, name=name, text=value, baseURI=nullURI, wfc=.false.) +#else + call register_internal_GE(xf%xds, name=name, text=value, baseURI=null(), wfc=.false.) +#endif + + call add_eol(xf) + + !FIXME - valid entity values? + call add_to_buffer(" 0) then + call add_to_buffer("'"//value//"'>", xf%buffer, .true.) + else + call add_to_buffer(""""//value//""">", xf%buffer, .true.) + endif +#endif + end subroutine xml_AddInternalEntity + + + subroutine xml_AddExternalEntity(xf, name, system, public, notation) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: system + character(len=*), intent(in), optional :: public + character(len=*), intent(in), optional :: notation + +#ifndef DUMMYLIB + type(URI), pointer :: URIref +#ifdef PGF90 + type(URI), pointer :: nullURI + nullURI => null() +#endif + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + URIref => parseURI(system) + if (.not.associated(URIref)) call wxml_error("xml_AddExternalEntity: Invalid SYSTEM URI") + call destroyURI(URIref) + if (present(public)) then + if (.not.checkPublicId(public)) call wxml_error("xml_AddExternalEntity: Invalid PUBLIC ID") + endif + if (present(notation)) then + if (xf%namespace) then + if (.not.checkNCName(notation, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(notation, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + endif + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot define Entity here: "//name) + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + ! Notation only needs checked if not already registered - done above. +#ifdef PGF90 + call register_external_GE(xf%xds, name=name, & + systemID=system, publicId=public, notation=notation, & + baseURI=nullURI, wfc=.false.) +#else + call register_external_GE(xf%xds, name=name, & + systemID=system, publicId=public, notation=notation, & + baseURI=null(), wfc=.false.) +#endif + + call add_eol(xf) + + call add_to_buffer("", xf%buffer, .false.) +#endif + end subroutine xml_AddExternalEntity + + + subroutine xml_AddNotation(xf, name, system, public) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in), optional :: system + character(len=*), intent(in), optional :: public + +#ifndef DUMMYLIB + type(URI), pointer :: URIref + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + if (present(system)) then + URIref => parseURI(system) + if (.not.associated(URIref)) call wxml_error("xml_AddNotation: Invalid SYSTEM URI") + call destroyURI(URIref) + endif + if (present(public)) then + if (.not.checkPublicId(public)) call wxml_error("xml_AddNotation: Invalid PUBLIC ID") + endif + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot define Notation here: "//name) + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + if (notation_exists(xf%xds%nList, name)) & + call wxml_error("Tried to create duplicate notation: "//name) + + call add_eol(xf) + + call add_notation(xf%xds%nList, name, system, public) + call add_to_buffer(" 0) then + call add_to_buffer(" '"//system//"'", xf%buffer, .true.) + else + call add_to_buffer(" """//system//"""", xf%buffer, .true.) + endif + endif + call add_to_buffer(">", xf%buffer, .false.) +#endif + end subroutine xml_AddNotation + + + subroutine xml_AddElementToDTD(xf, name, declaration) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: declaration + +#ifndef DUMMYLIB + type(error_stack) :: stack +#ifdef PGF90 + type (element_t), pointer :: nullElement + + nullElement => null() +#endif + call check_xf(xf) + + if (.not.checkChars(declaration,xf%xds%xml_version)) call wxml_error("xml_AddElementToDTD: Invalid character in declaration") + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Element Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Element Name in DTD "//name) + endif +#ifdef PGF90 + call parse_dtd_element(declaration, xf%xds%xml_version, stack, nullElement, .true.) +#else + call parse_dtd_element(declaration, xf%xds%xml_version, stack, null(), .true.) +#endif + if (in_error(stack)) call wxml_error(xf, "Invalid ELEMENT declaration") + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot write to DTD here: xml_AddElementToDTD") + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + call add_eol(xf) + call add_to_buffer("", xf%buffer, .false.) +#endif + end subroutine xml_AddElementToDTD + + + subroutine xml_AddAttlistToDTD(xf, name, declaration) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: declaration + +#ifndef DUMMYLIB + type(error_stack) :: stack +#ifdef PGF90 + type (element_t), pointer :: nullElement + + nullElement => null() +#endif + call check_xf(xf) + + if (.not.checkChars(declaration,xf%xds%xml_version)) call wxml_error("xml_AddAttListToDTD: Invalid character in declaration") + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Attribute Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Attribute Name in DTD "//name) + endif + +#ifdef PGF90 + call parse_dtd_attlist(declaration, xf%xds%xml_version, & + validCheck=.false., namespaces=xf%namespace, stack=stack, & + elem=nullElement, internal=.true.) +#else + call parse_dtd_attlist(declaration, xf%xds%xml_version, & + validCheck=.false., namespaces=xf%namespace, stack=stack, & + elem=null(), internal=.true.) +#endif + + if (in_error(stack)) call wxml_error(xf, "Invalid ATTLIST declaration") + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot write to DTD here: xml_AddAttlistToDTD") + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + call add_eol(xf) + call add_to_buffer("", xf%buffer, .false.) +#endif + end subroutine xml_AddAttlistToDTD + + + subroutine xml_AddPEReferenceToDTD(xf, name) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + +#ifndef DUMMYLIB + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid PE Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid PE Name in DTD "//name) + endif + + call wxml_warning(xf, "Adding PEReference to DTD. Cannot guarantee well-formedness") + if (.not.existing_entity(xf%xds%PEList, name)) then + if (.not.xf%xds%standalone) then + call wxml_warning(xf, "Tried to reference possibly unregistered parameter entity in DTD: "//name) + else + call wxml_error("Tried to reference unregistered parameter entity in DTD "//name) + endif + else + if (is_unparsed_entity(xf%xds%PEList, name)) & + call wxml_error("Tried to reference unparsed parameter entity in DTD "//name) + endif + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot write to DTD here: xml_AddPEReferenceToDTD") + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + call add_eol(xf) + call add_to_buffer("%"//name//";", xf%buffer, .false.) + +#endif + end subroutine xml_AddPEReferenceToDTD + + + subroutine xml_AddXMLStylesheet(xf, href, type, title, media, charset, alternate) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: href + character(len=*), intent(in) :: type + character(len=*), intent(in), optional :: title + character(len=*), intent(in), optional :: media + character(len=*), intent(in), optional :: charset + logical, intent(in), optional :: alternate + +#ifndef DUMMYLIB + call check_xf(xf) + ! Don't bother checking name - all pseudoatts get checked anyway. + + if (xf%state_1 /= WXML_STATE_1_JUST_OPENED & + .and. xf%state_1 /= WXML_STATE_1_BEFORE_ROOT) & + call wxml_error("Cannot add stylesheet here: "//href) + + call close_start_tag(xf) + + call xml_AddXMLPI(xf, 'xml-stylesheet', xml=.true.) + call xml_AddPseudoAttribute(xf, 'href', href) + call xml_AddPseudoAttribute(xf, 'type', type) + + if (present(title)) call xml_AddPseudoAttribute(xf, 'title', title) + if (present(media)) call xml_AddPseudoAttribute(xf, 'media', media) + if (present(charset)) call xml_AddPseudoAttribute(xf, 'charset', charset) + if (present(alternate)) then + if (alternate) then + call xml_AddPseudoAttribute(xf, 'alternate', 'yes') + else + call xml_AddPseudoAttribute(xf, 'alternate', 'no') + endif + endif + if (xf%state_1 == WXML_STATE_1_JUST_OPENED) & + xf%state_1 = WXML_STATE_1_BEFORE_ROOT + xf%state_2 = WXML_STATE_2_INSIDE_PI +#endif + end subroutine xml_AddXMLStylesheet + + + subroutine xml_AddXMLPI(xf, name, data, xml, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in), optional :: data + logical, intent(in), optional :: xml + logical, intent(in), optional :: ws_significant + + logical :: xml_ +#ifndef DUMMYLIB + call check_xf(xf) + + if (present(xml)) then + xml_ = xml + else + xml_ = .false. + endif + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid PI target "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid PI target "//name) + endif + if (.not.xml_) then + if (len(name)==3.and.(toLower(name)=="xml")) & + call wxml_error("Invalid PI target "//name) + endif + + if (present(data)) then + if (.not.checkChars(data,xf%xds%xml_version)) & + call wxml_error("xml_AddXMLPI: Invalid character in data") + endif + + select case (xf%state_1) + case (WXML_STATE_1_JUST_OPENED) + xf%state_1 = WXML_STATE_1_BEFORE_ROOT + case (WXML_STATE_1_DURING_ROOT) + call close_start_tag(xf) + if (xf%pretty_print) call add_eol(xf) + case default + call close_start_tag(xf) + call add_eol(xf) + end select + call add_to_buffer("0) then + if (index(data, '?>') > 0) & + call wxml_error(xf, "Tried to output invalid PI data "//data) + call add_to_buffer(' ', xf%buffer, .false.) + call add_to_buffer(data//'?>', xf%buffer, ws_significant) + ! state_2 is now OUTSIDE_TAG from close_start_tag + else + xf%state_2 = WXML_STATE_2_INSIDE_PI + call reset_dict(xf%dict) + endif + else + xf%state_2 = WXML_STATE_2_INSIDE_PI + call reset_dict(xf%dict) + endif +#endif + end subroutine xml_AddXMLPI + + + subroutine xml_AddComment(xf, comment, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: comment + logical, intent(in), optional :: ws_significant + +#ifndef DUMMYLIB + call check_xf(xf) + if (.not.checkChars(comment,xf%xds%xml_version)) call wxml_error("xml_AddComment: Invalid character in comment") + + select case (xf%state_1) + case (WXML_STATE_1_JUST_OPENED) + xf%state_1 = WXML_STATE_1_BEFORE_ROOT + case (WXML_STATE_1_DURING_ROOT) + call close_start_tag(xf) + if (xf%pretty_print.and.xf%state_2 == WXML_STATE_2_OUTSIDE_TAG) call add_eol(xf) + case default + call close_start_tag(xf) + call add_eol(xf) + end select + + if (index(comment,'--') > 0 .or. comment(len(comment):) == '-') & + call wxml_error("Tried to output invalid comment "//comment) + + call add_to_buffer("", xf%buffer, .false.) +#endif + end subroutine xml_AddComment + + + subroutine xml_NewElement(xf, name) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + +#ifndef DUMMYLIB + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Element Name "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Element Name "//name) + endif + + select case (xf%state_1) + case (WXML_STATE_1_JUST_OPENED, WXML_STATE_1_BEFORE_ROOT) + if (xf%xds%valid) then + if (size(xf%name)==0) then + call wxml_error(xf, "No DTD specified for document") + elseif (str_vs(xf%name) /= name) then + call wxml_error(xf, "Root element name does not match DTD") + endif + endif + call close_start_tag(xf) + if (xf%state_3 /= WXML_STATE_3_BEFORE_DTD) then + select case (xf%state_3) + case (WXML_STATE_3_DURING_DTD) + call add_to_buffer('>', xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_AFTER_DTD + case (WXML_STATE_3_INSIDE_INTSUBSET) + xf%state_3 = WXML_STATE_3_AFTER_DTD + call add_eol(xf) + call add_to_buffer(']>', xf%buffer, .false.) + end select + endif + call add_eol(xf) + case (WXML_STATE_1_DURING_ROOT) + call close_start_tag(xf) + if (xf%pretty_print) call add_eol(xf) + case (WXML_STATE_1_AFTER_ROOT) + call wxml_error(xf, "Two root elements: "//name) + end select + + if (xf%namespace) then + if (len(prefixOfQName(name)) > 0) then + if (.not.isPrefixInForce(xf%nsDict, prefixOfQName(name))) & + call wxml_error(xf, "Namespace prefix not registered: "//prefixOfQName(name)) + endif + endif + + call push_elstack(xf%stack, name) + call add_to_buffer("<"//name, xf%buffer, .false.) + xf%state_2 = WXML_STATE_2_INSIDE_ELEMENT + call reset_dict(xf%dict) + xf%indent = xf%indent + indent_inc + xf%state_1 = WXML_STATE_1_DURING_ROOT +#endif + end subroutine xml_NewElement + + + subroutine xml_AddCharacters_ch(xf, chars, parsed, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: chars + logical, intent(in), optional :: parsed + logical, intent(in), optional :: ws_significant + +#ifndef DUMMYLIB + logical :: pc + + call check_xf(xf) + if (.not.checkChars(chars, xf%xds%xml_version)) call wxml_error("xml_AddCharacters: Invalid character in chars") + + if (xf%state_1 /= WXML_STATE_1_DURING_ROOT) & + call wxml_fatal("Tried to add text section in wrong place: "//chars) + + if (present(parsed)) then + pc = parsed + else + pc = .true. + endif + + call close_start_tag(xf) + + if (pc) then + call add_to_buffer(escape_string(chars, xf%xds%xml_version), xf%buffer, ws_significant) + else + ! FIXME what if we try and output two separate character events? + ! need to keep track of this ... + if (index(chars,']]>') > 0) & + call wxml_fatal("Tried to output invalid CDATA: "//chars) + call add_to_buffer("", xf%buffer, ws_significant) + endif + + xf%state_2 = WXML_STATE_2_IN_CHARDATA +#endif + end subroutine xml_AddCharacters_Ch + + + subroutine xml_AddNewline(xf) + type(xmlf_t), intent(inout) :: xf + +#ifndef DUMMYLIB + call xml_AddCharacters(xf, "") ! To ensure we are in a text section + call add_eol(xf) +#endif + end subroutine xml_AddNewline + + + subroutine xml_AddEntityReference(xf, name) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + +#ifndef DUMMYLIB + call check_xf(xf) + + if (likeCharacterEntityReference(name)) then + if (.not.checkCharacterEntityReference(name, xf%xds%xml_version)) & + call wxml_error("Invalid Character Entity Reference "//name) + elseif (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Entity Name "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Entity Name "//name) + endif + + call close_start_tag(xf) + + if (xf%state_2 /= WXML_STATE_2_OUTSIDE_TAG .and. & + xf%state_2 /= WXML_STATE_2_IN_CHARDATA) & + call wxml_fatal("Tried to add entity reference in wrong place: "//name) + + if (.not.checkCharacterEntityReference(name, xf%xds%xml_version)) then + !it's not just a unicode entity + call wxml_warning(xf, "Entity reference added - document may not be well-formed") + if (.not.existing_entity(xf%xds%entityList, name)) then + if (xf%xds%standalone) then + call wxml_error("Tried to reference unregistered entity") + else + call wxml_warning(xf, "Tried to reference unregistered entity") + endif + else + if (is_unparsed_entity(xf%xds%entityList, name)) & + call wxml_error("Tried to reference unparsed entity") + endif + endif + + call add_to_buffer('&'//name//';', xf%buffer, .false.) + xf%state_2 = WXML_STATE_2_IN_CHARDATA +#endif + end subroutine xml_AddEntityReference + + + subroutine xml_AddAttribute_Ch(xf, name, value, escape, type, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + logical, intent(in), optional :: escape + character(len=*), intent(in), optional :: type + logical, intent(in), optional :: ws_significant + +#ifndef DUMMYLIB + logical :: esc + character, pointer :: type_(:) + + if (present(type)) then + if (type/='CDATA'.and.type/='ID'.and.type/='IDREF'.and.type/='IDREFS'.and.type/='NMTOKEN'.and.type/='NMTOKENS' & + .and.type/='ENTITY'.and.type/='ENTITIES'.and.type/='NOTATION') then + call wxml_fatal("Invalid type in xml_AddAttribute: "//type) + endif + type_ => vs_str_alloc(type) + else + ! We assume CDATA, but need to worry about whether the caller cares about whitespace ... + if (present(ws_significant)) then + if (ws_significant) then + type_ => vs_str_alloc('CDATA') + else + type_ => vs_str_alloc('CDANO') ! CDAta, whitespace Not significant + endif + else + type_ => vs_str_alloc('CDAMB') ! CDAta, whitespace MayBe significant + endif + endif + + call check_xf(xf) + + if (.not.checkChars(value, xf%xds%xml_version)) call wxml_error("xml_AddAttribute: Invalid character in value") + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Attribute Name "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Attribute Name "//name) + endif + + if (present(escape)) then + esc = escape + else + esc = .true. + endif + + if (name=="xml:space") then + ! The value can only be "default" or "preserve", by 2.10 + if (.not.esc) then + if (value/="default".and.value/="preserve") & + call wxml_fatal("Invalid value for xml:space attrbute") + endif + endif + + ! FIXME when escape is false we should still do full verification + ! where possible. + ! Currently - minimal check: only extra allowed is character entity references. + ! We check they exist, and are not unparsed. + ! Ideally we would fully expand all entity references (at least for + ! a standalone document where we can) and then + ! match the resultant production against [XML]-3.3.1. This is + ! initially too much work though, so we just check simple + ! syntactic constraint. + + if (.not.esc) then + if (.not.checkAttValue(value, xf%xds%xml_version)) & + call wxml_error(xf, "Invalid attribute value: "//value) + if (index(value, '&') > 0) then + ! There are entity references + ! They should exist (unless we are not standalone) and they must not be unparsed. + if (.not.checkExistingRefsInAttValue()) then + if (xf%xds%standalone) then + call wxml_error(xf, "outputting unknown entity. Cannot guarantee validity.") + else + call wxml_warning(xf, "Warning: outputting unknown entity. Cannot guarantee validity.") + endif + endif + if (.not.checkParsedRefsInAttValue()) & + call wxml_error(xf, "Warning: outputting unknown entity. Cannot guarantee validity.") + endif + endif + + if (xf%state_2 /= WXML_STATE_2_INSIDE_ELEMENT) & + call wxml_error(xf, "attributes outside element content: "//name) + + if (hasKey(xf%dict,name)) then + call wxml_error(xf, "duplicate att name: "//name) + elseif (xf%namespace) then + if (hasKey(xf%dict, & + getnamespaceURI(xf%nsDict,prefixOfQname(name)), localpartofQname(name))) then + call wxml_error(xf, "duplicate att after namespace processing: "//name) + endif + endif + + if (xf%namespace) then + if (len(prefixOfQName(name))>0) then + if (prefixOfQName(name)/="xml".and.prefixOfQName(name)/="xmlns") then + if (.not.isPrefixInForce(xf%nsDict, prefixOfQName(name))) & + call wxml_error(xf, "namespace prefix not registered: "//prefixOfQName(name)) + endif + if (esc) then + call add_item_to_dict(xf%dict, localpartofQname(name), escape_string(value, xf%xds%xml_version), prefixOfQName(name), & + getnamespaceURI(xf%nsDict,prefixOfQname(name)), type=str_vs(type_)) + else + call add_item_to_dict(xf%dict, localpartofQname(name), value, prefixOfQName(name), & + getnamespaceURI(xf%nsDict,prefixOfQName(name)), type=str_vs(type_)) + endif + else + if (esc) then + call add_item_to_dict(xf%dict, name, escape_string(value, xf%xds%xml_version), type=str_vs(type_)) + else + call add_item_to_dict(xf%dict, name, value, type=str_vs(type_)) + endif + endif + else + if (esc) then + call add_item_to_dict(xf%dict, name, escape_string(value, xf%xds%xml_version), type=str_vs(type_)) + else + call add_item_to_dict(xf%dict, name, value, type=str_vs(type_)) + endif + endif + + !FIXME need to deallocate this when we move to better error handling + deallocate(type_) + + contains + function checkExistingRefsInAttValue() result(p) + logical :: p + + integer :: i1, i2 + + ! Here we assume we have syntactic well-formedness as + ! checked by checkAttValue. + ! We also assume we do not have simply one entity as + ! the contents - that is checked by checkAttValueEntity + + p = .false. + i1 = index(value, '&') + i2 = 0 + do while (i1 > 0) + i1 = i2 + i1 + i2 = index(value(i1+1:),';') + if (i2 == 0) return + i2 = i1 + i2 + if (.not.existing_entity(xf%xds%entityList, value(i1+1:i2-1)) .and. & + .not.checkCharacterEntityReference(value(i1+1:i2-1), xf%xds%xml_version)) & + return + i1 = index(value(i2+1:), '&') + enddo + p = .true. + + end function checkExistingRefsInAttValue + + function checkParsedRefsInAttValue() result(p) + logical :: p + + integer :: i1, i2 + + ! Here we assume we have syntactic well-formedness as + ! checked by checkAttValue. + + p = .false. + i1 = index(value, '&') + i2 = 0 + do while (i1 > 0) + i1 = i1 + i2 + i2 = index(value(i1+1:),';') + if (i2 == 0) return + i2 = i1 + i2 + if (is_unparsed_entity(xf%xds%entityList, value(i1+1:i2-1))) & + return + i1 = index(value(i2+1:), '&') + enddo + p = .true. + + end function checkParsedRefsInAttValue +#endif + end subroutine xml_AddAttribute_Ch + + + subroutine xml_AddPseudoAttribute_Ch(xf, name, value, escape, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + logical, intent(in), optional :: escape + logical, intent(in), optional :: ws_significant + +#ifndef DUMMYLIB + logical :: esc + character(len=5) :: type + + call check_xf(xf) + if (.not.checkChars(name, xf%xds%xml_version)) call wxml_error("xml_AddPseudoAttribute: Invalid character in name") + if (.not.checkChars(value, xf%xds%xml_version)) call wxml_error("xml_AddPseudoAttribute: Invalid character in value") + + if (present(escape)) then + esc = escape + else + esc = .true. + endif + if (present(ws_significant)) then + if (ws_significant) then + type='CDATA' + else + type='CDANO' ! CDAta, whitespace Not significant + endif + else + type='CDAMB' ! CDAta, whitespace MayBe significant + endif + + if (index(value, '?>') > 0) & + call wxml_error(xf, "Invalid pseudo-attribute value: "//value) + if (.not.esc) then + if (.not.checkPseudoAttValue(value, xf%xds%xml_version)) & + call wxml_error(xf, "Invalid pseudo-attribute value: "//value) + endif + + if (xf%state_2 /= WXML_STATE_2_INSIDE_PI) & + call wxml_error("PI pseudo-attribute outside PI: "//name) + + ! This is mostly ad-hoc, pseudo-attribute names are not defined anywhere. + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid pseudo-attribute name: "//name) + + if (hasKey(xf%dict,name)) & + call wxml_error(xf, "duplicate pseudo-attribute name: "//name) + + if (index(value, '?>') > 0) & + call wxml_error(xf, "Invalid pseudo-attribute data: "//value) + + if (esc) then + call add_item_to_dict(xf%dict, name, escape_string(value, xf%xds%xml_version), type=type) + else + call add_item_to_dict(xf%dict, name, value, type=type) + endif +#endif + end subroutine xml_AddPseudoAttribute_Ch + + + subroutine xml_EndElement(xf, name) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + + character :: dummy +#ifndef DUMMYLIB + call check_xf(xf) + ! No point in doing checkChars, name is compared to stack anyway. + + if (len(xf%stack) == 0) & + call wxml_fatal(xf,'Trying to close '//name//' but no tags are open.') + + if (get_top_elstack(xf%stack) /= name) & + call wxml_fatal(xf, 'Trying to close '//name//' but '//get_top_elstack(xf%stack)// & + ' is open. Either you have failed to open '//name//& + ' or you have failed to close '//get_top_elstack(xf%stack)//'.') + xf%indent = xf%indent - indent_inc + + if (xf%state_2==WXML_STATE_2_INSIDE_ELEMENT) then + if (xf%namespace) call checkNamespacesWriting(xf%dict, xf%nsDict, len(xf%stack)) + if (getLength(xf%dict) > 0) call write_attributes(xf) + if (xf%minimize_overrun) call add_eol(xf) + endif + if (xf%state_2==WXML_STATE_2_INSIDE_ELEMENT.and..not.xf%canonical) then + call add_to_buffer("/>", xf%buffer, .false.) + else + if (xf%state_2==WXML_STATE_2_INSIDE_ELEMENT) & + call add_to_buffer('>', xf%buffer, .false.) + if (xf%state_2==WXML_STATE_2_INSIDE_PI) & + call close_start_tag(xf) + if (xf%state_2==WXML_STATE_2_OUTSIDE_TAG.and.xf%pretty_print) & + call add_eol(xf) +! XLF does a weird thing here, and if pop_elstack is called as an +! argument to the add_to_buffer, it gets called twice. So we have to separate +! out get_top_... from pop_... + call add_to_buffer("", xf%buffer, .false.) + endif + dummy = pop_elstack(xf%stack) + + if (xf%namespace) call checkEndNamespaces(xf%nsDict, len(xf%stack)+1) + if (is_empty(xf%stack)) then + xf%state_1 = WXML_STATE_1_AFTER_ROOT + endif + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG +#endif + end subroutine xml_EndElement + + + subroutine xml_DeclareNamespace(xf, nsURI, prefix, xml) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: nsURI + character(len=*), intent(in), optional :: prefix + logical, intent(in), optional :: xml + +#ifndef DUMMYLIB + call check_xf(xf) + if (.not.xf%namespace) call wxml_error("Cannot declare a namespace in a non-namespaced document") + + !if (.not.checkNCName(nsURI, xf%xds%xml_version)) call wxml_error("xml_DeclareNamespace: Invalid nsURI") + if (present(prefix)) then + if (.not.checkNCName(prefix, xf%xds%xml_version)) call wxml_error("xml_DeclareNamespace: Invalid prefix") + endif + + if (xf%state_1 == WXML_STATE_1_AFTER_ROOT) & + call wxml_error(xf, "adding namespace outside element content") + + if (len(nsURI) == 0) then + if (present(prefix).and.xf%xds%xml_version==XML1_0) & + call wxml_error(xf, "prefixed namespace with empty URI forbidden in XML 1.0") + endif + + if (present(prefix)) then + call addPrefixedNS(xf%nsDict, prefix, nsURI, len(xf%stack)+1, xf%xds, xml) + else + call addDefaultNS(xf%nsDict, nsURI, len(xf%stack)+1) + endif +#endif + end subroutine xml_DeclareNamespace + + + subroutine xml_UndeclareNamespace(xf, prefix) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in), optional :: prefix + +#ifndef DUMMYLIB + call check_xf(xf) + !No need to checkChars, prefix is checked against stack + if (.not.xf%namespace) call wxml_error("Cannot declare a namespace in a non-namespaced document") + + if (present(prefix).and.xf%xds%xml_version==XML1_0) & + call wxml_error("cannot undeclare prefixed namespaces in XML 1.0") + + if (xf%state_1 == WXML_STATE_1_AFTER_ROOT) & + call wxml_error(xf, "Undeclaring namespace outside element content") + + if (present(prefix)) then + call addPrefixedNS(xf%nsDict, prefix, "", len(xf%stack)+1, xf%xds) + else + call addDefaultNS(xf%nsDict, "", len(xf%stack)+1) + endif +#endif + end subroutine xml_UndeclareNamespace + + + subroutine xml_Close(xf, empty) + type(xmlf_t), intent(inout) :: xf + logical, optional :: empty + +#ifndef DUMMYLIB + logical :: empty_ + + if (present(empty)) then + empty_ = empty + else + empty_ = .false. + endif + + if (xf%lun == -1) & + call wxml_fatal('Tried to close XML file which is not open') + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) & + call close_start_tag(xf) + + if (xf%state_3 /= WXML_STATE_3_BEFORE_DTD & + .and. xf%state_3 /= WXML_STATE_3_AFTER_DTD) then + select case (xf%state_3) + case (WXML_STATE_3_DURING_DTD) + call add_to_buffer('>', xf%buffer, .false.) + case (WXML_STATE_3_INSIDE_INTSUBSET) + call add_eol(xf) + call add_to_buffer(']>', xf%buffer, .false.) + end select + xf%state_3 = WXML_STATE_3_AFTER_DTD + endif + + do while (xf%state_1 == WXML_STATE_1_DURING_ROOT) + call xml_EndElement(xf, get_top_elstack(xf%stack)) + enddo + + if (xf%state_1 /= WXML_STATE_1_AFTER_ROOT) then + if (empty_) then + call wxml_warning(xf, 'Invalid XML document produced: No root element') + else + call wxml_error(xf, 'Invalid XML document produced: No root element') + endif + endif + + call dump_buffer(xf%buffer) + close(unit=xf%lun) + xf%lun = -1 + + call destroy_dict(xf%dict) + call destroy_elstack(xf%stack) + + if (xf%namespace) & + call destroyNamespaceDictionary(xf%nsDict) + call destroy_xml_doc_state(xf%xds) + + deallocate(xf%name) +#endif + end subroutine xml_Close + + subroutine xmlf_SetPretty_print(xf, new_value) + type(xmlf_t), intent(inout) :: xf + logical, intent(in) :: new_value +#ifndef DUMMYLIB + xf%pretty_print = new_value +#endif + end subroutine xmlf_SetPretty_print + + pure function xmlf_GetPretty_print(xf) result(value) + logical :: value + type(xmlf_t), intent(in) :: xf +#ifdef DUMMYLIB + value = .false. +#else + value = xf%pretty_print +#endif + end function xmlf_GetPretty_print + + pure function xmlf_name(xf) result(fn) + type (xmlf_t), intent(in) :: xf +#ifdef DUMMYLIB + character(len=1) :: fn + fn = " " +#else + character(len=size(xf%xds%documentURI)) :: fn + fn = str_vs(xf%xds%documentURI) +#endif + end function xmlf_name + +#ifndef DUMMYLIB + pure function xmlf_opentag_len(xf) result(n) + type (xmlf_t), intent(in) :: xf + integer :: n + + if (xf%lun == -1) then + n = 0 + elseif (is_empty(xf%stack)) then + n = 0 + else + n = len(get_top_elstack(xf%stack)) + endif + end function xmlf_opentag_len +#endif + + function xmlf_opentag(xf) result(fn) + type (xmlf_t), intent(in) :: xf +#ifdef DUMMYLIB + character(len=1) :: fn + fn = " " +#else + character(len=xmlf_opentag_len(xf)) :: fn + + if (xf%lun == -1) then + fn = '' + elseif (is_empty(xf%stack)) then + fn = '' + else + fn = get_top_elstack(xf%stack) + endif +#endif + end function xmlf_opentag + +#ifndef DUMMYLIB + + subroutine check_xf(xf) + type(xmlf_t), intent(inout) :: xf + if (xf%lun == -1) & + call wxml_fatal("Tried to manipulate an XML File which is not open") + + end subroutine check_xf + + + subroutine add_eol(xf) + type(xmlf_t), intent(inout) :: xf + + integer :: indent_level + + ! In case we still have a zero-length stack, we must make + ! sure indent_level is not less than zero. + if (xf%state_3 == WXML_STATE_3_INSIDE_INTSUBSET) then + indent_level = indent_inc + else + indent_level = xf%indent + endif + + !We must flush here (rather than just adding an eol character) + !since we don't know what the eol character is on this system. + !Flushing with a linefeed will get it automatically, though. + call dump_buffer(xf%buffer, lf=.true.) + call reset_buffer(xf%buffer, xf%lun, xf%xds%xml_version) + + if (xf%pretty_print) & + call add_to_buffer(repeat(' ',indent_level),xf%buffer, .false.) + + end subroutine add_eol + + + subroutine close_start_tag(xf) + type(xmlf_t), intent(inout) :: xf + + select case (xf%state_2) + case (WXML_STATE_2_INSIDE_ELEMENT) + if (xf%namespace) call checkNamespacesWriting(xf%dict, xf%nsDict, len(xf%stack)) + if (getLength(xf%dict) > 0) call write_attributes(xf) + if (xf%minimize_overrun) call add_eol(xf) + call add_to_buffer('>', xf%buffer, .false.) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + case (WXML_STATE_2_INSIDE_PI) + if (getLength(xf%dict) > 0) call write_attributes(xf) + call add_to_buffer('?>', xf%buffer, .false.) + if (xf%pretty_print.and.xf%state_3/=WXML_STATE_3_INSIDE_INTSUBSET) call add_eol(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + case (WXML_STATE_2_IN_CHARDATA) + continue + case (WXML_STATE_2_OUTSIDE_TAG) + continue + end select + + end subroutine close_start_tag + + + subroutine write_attributes(xf) + type(xmlf_t), intent(inout) :: xf + + integer :: i, j, size + + if (xf%state_2 /= WXML_STATE_2_INSIDE_PI .and. & + xf%state_2 /= WXML_STATE_2_INSIDE_ELEMENT) & + call wxml_fatal("Internal library error") + + if (xf%canonical) call sortAttrs(xf%dict) + + do i = 1, getLength(xf%dict) + size = len(get_key(xf%dict, i)) + len(get_value(xf%dict, i)) + 4 + if (xf%minimize_overrun.and.(len(xf%buffer) + size) > COLUMNS) then + call add_eol(xf) + else + call add_to_buffer(" ", xf%buffer, .false.) + endif + call add_to_buffer(get_key(xf%dict, i), xf%buffer, .false.) + call add_to_buffer("=", xf%buffer, .false.) + call add_to_buffer('"',xf%buffer, .false.) + j = getWhiteSpaceHandling(xf%dict, i) + if (j==0) then + call add_to_buffer(get_value(xf%dict, i), xf%buffer, .true.) + elseif (j==1) then + call add_to_buffer(get_value(xf%dict, i), xf%buffer) + else + call add_to_buffer(get_value(xf%dict, i), xf%buffer, .false.) + endif + call add_to_buffer('"', xf%buffer, .false.) + enddo + + end subroutine write_attributes + + subroutine wxml_warning_xf(xf, msg) + ! Emit warning, but carry on. + type(xmlf_t), intent(in) :: xf + character(len=*), intent(in) :: msg + + if (FoX_get_fatal_warnings()) then + write(6,'(a)') 'FoX warning made fatal' + call wxml_fatal_xf(xf, msg) + endif + + if (xf%xds%warning) then + write(6,'(a)') 'WARNING(wxml) in writing to file ', xmlf_name(xf) + write(6,'(a)') msg + endif + + end subroutine wxml_warning_xf + + + subroutine wxml_error_xf(xf, msg) + ! Emit error message, clean up file and stop. + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: msg + + if (FoX_get_fatal_errors()) then + write(6,'(a)') 'FoX error made fatal' + call wxml_fatal_xf(xf, msg) + endif + + write(6,'(a)') 'ERROR(wxml) in writing to file ', xmlf_name(xf) + write(6,'(a)') msg + + !call xml_Close(xf) + stop + + end subroutine wxml_error_xf + + + subroutine wxml_fatal_xf(xf, msg) + !Emit error message and abort with coredump. Does not try to + !close file, so should be used from anything xml_Close might + !itself call (to avoid infinite recursion!) + + type(xmlf_t), intent(in) :: xf + character(len=*), intent(in) :: msg + + write(6,'(a)') 'ERROR(wxml) in writing to file ', xmlf_name(xf) + write(6,'(a)') msg + + call pxfabort() + stop + + end subroutine wxml_fatal_xf + +#endif + +end module m_wxml_core diff --git a/src/xml/wxml/m_wxml_escape.F90 b/src/xml/wxml/m_wxml_escape.F90 new file mode 100644 index 0000000000..99bfcdab00 --- /dev/null +++ b/src/xml/wxml/m_wxml_escape.F90 @@ -0,0 +1,140 @@ +module m_wxml_escape + +#ifndef DUMMYLIB + !Ensure all characters are safe to go out into XML file. + + use fox_m_fsys_format, only: str + use m_common_charset, only: XML1_0 + use m_common_error, only: FoX_error, FoX_warning + + implicit none + private + + integer, parameter :: AMP = iachar('&') + integer, parameter :: LT = iachar('<') + integer, parameter :: GT = iachar('>') + integer, parameter :: QUOT = iachar('"') + integer, parameter :: APOS = iachar("'") + + public :: escape_string + public :: escape_string_len + +contains + + pure function escape_string_len(s) result(c) + character(len=*), intent(in) :: s + integer :: c + + integer :: i + c = len(s) + do i = 1, len(s) + select case(iachar(s(i:i))) + case (AMP) + c = c + 4 + case (LT) + c = c + 3 + case (GT) + c = c + 3 + case (QUOT) + c = c + 5 + case (APOS) + c = c + 5 + case (1:8) + c = c + 3 + case (11:12) + c = c + 4 + case (14:31) + c = c + 4 + case (127:) + c = c + 5 + ! a char can never contain more than 8 bits = 256 characters, so + ! we never need more than 3 chars to represent the int. + end select + enddo + + end function escape_string_len + + + function escape_string(s, version) result (s2) + character(len=*), intent(in) :: s + integer, intent(in) :: version + character(len=escape_string_len(s)) :: s2 + + integer :: c, i, ic + + ! We have to do it this way (with achar etc) in case the native + ! platform encoding is not ASCII + + c = 1 + do i = 1, len(s) + ic = iachar(s(i:i)) + select case (ic) + case (0) + call FoX_error("Tried to output a NUL character") + case (1:8) + if (version==XML1_0) then + call FoX_error("Tried to output a character invalid under XML 1.0") + else + s2(c:c+3) = "&#"//str(ic)//";" + c = c + 4 + endif + case(9:10) + s2(c:c) = achar(ic) + c = c + 1 + case(11:12) + if (version==XML1_0) then + call FoX_error("Tried to output a character invalid under XML 1.0") + else + s2(c:c+5) = "&#"//str(ic)//";" + c = c + 5 + endif + case(13) + s2(c:c) = achar(13) + c = c + 1 + case (14:31) + if (version==XML1_0) then + call FoX_error("Tried to output a character invalid under XML 1.0") + else + s2(c:c+5) = "&#"//str(ic)//";" + c = c + 5 + endif + case (32:126) + select case (iachar(s(i:i))) + case (AMP) + s2(c:c+4) = "&" + c = c + 5 + case (LT) + s2(c:c+3) = "<" + c = c + 4 + case (GT) + s2(c:c+3) = ">" + c = c + 4 + case (QUOT) + s2(c:c+5) = """ + c = c + 6 + case (APOS) + s2(c:c+5) = "'" + c = c + 6 + case default + s2(c:c) = achar(ic) + c = c + 1 + end select + case (127) + s2(c:c+5) = "" + c = c + 6 + case default + !TOHW we should maybe just disallow this ... + call FoX_warning("emitting non-ASCII character. Platform-dependent result!") + s2(c:c+6) = "&#"//str(ic)//";" + c = c + 6 + ! a char can never contain more than 8 bits = 256 characters, so + ! we never need more than 3 chars to represent the int. + ! We have to encode it though, because UTF-8 x7F-x9F must be + ! encoded, and if they are in the native charset they'll be > 128 + end select + enddo + + end function escape_string + +#endif +end module m_wxml_escape diff --git a/src/xml/wxml/m_wxml_overloads.F90 b/src/xml/wxml/m_wxml_overloads.F90 new file mode 100644 index 0000000000..5f6281dcb5 --- /dev/null +++ b/src/xml/wxml/m_wxml_overloads.F90 @@ -0,0 +1,1028 @@ +! This file is AUTOGENERATED!!!! +! Do not edit this file; edit m_wxml_overloads.m4 and regenerate. +! +! +module m_wxml_overloads + +#ifndef DUMMYLIB + use fox_m_fsys_format, only: str +#endif + use fox_m_fsys_realtypes, only: sp, dp + use m_wxml_core, only: xmlf_t + use m_wxml_core, only: xml_AddCharacters + use m_wxml_core, only: xml_AddAttribute + use m_wxml_core, only: xml_AddPseudoAttribute + + implicit none + private + + interface xml_AddCharacters + module procedure CharactersScalarCmplxDp + module procedure CharactersScalarCmplxSp + module procedure CharactersScalarRealDp + module procedure CharactersScalarRealSp + module procedure CharactersScalarInt + module procedure CharactersScalarLg + + module procedure CharactersArrayCmplxDp + module procedure CharactersArrayCmplxSp + module procedure CharactersArrayRealDp + module procedure CharactersArrayRealSp + module procedure CharactersArrayInt + module procedure CharactersArrayLg + module procedure CharactersArrayCh + module procedure CharactersMatrixCmplxDp + module procedure CharactersMatrixCmplxSp + module procedure CharactersMatrixRealDp + module procedure CharactersMatrixRealSp + module procedure CharactersMatrixInt + module procedure CharactersMatrixLg + module procedure CharactersMatrixCh + end interface + + interface xml_AddAttribute + module procedure AttributeScalarCmplxDp + module procedure AttributeScalarCmplxSp + module procedure AttributeScalarRealDp + module procedure AttributeScalarRealSp + module procedure AttributeScalarInt + module procedure AttributeScalarLg + + module procedure AttributeArrayCmplxDp + module procedure AttributeArrayCmplxSp + module procedure AttributeArrayRealDp + module procedure AttributeArrayRealSp + module procedure AttributeArrayInt + module procedure AttributeArrayLg + module procedure AttributeArrayCh + module procedure AttributeMatrixCmplxDp + module procedure AttributeMatrixCmplxSp + module procedure AttributeMatrixRealDp + module procedure AttributeMatrixRealSp + module procedure AttributeMatrixInt + module procedure AttributeMatrixLg + module procedure AttributeMatrixCh + end interface + + interface xml_AddPseudoAttribute + module procedure PseudoAttributeScalarCmplxDp + module procedure PseudoAttributeScalarCmplxSp + module procedure PseudoAttributeScalarRealDp + module procedure PseudoAttributeScalarRealSp + module procedure PseudoAttributeScalarInt + module procedure PseudoAttributeScalarLg + + module procedure PseudoAttributeArrayCmplxDp + module procedure PseudoAttributeArrayCmplxSp + module procedure PseudoAttributeArrayRealDp + module procedure PseudoAttributeArrayRealSp + module procedure PseudoAttributeArrayInt + module procedure PseudoAttributeArrayLg + module procedure PseudoAttributeArrayCh + module procedure PseudoAttributeMatrixCmplxDp + module procedure PseudoAttributeMatrixCmplxSp + module procedure PseudoAttributeMatrixRealDp + module procedure PseudoAttributeMatrixRealSp + module procedure PseudoAttributeMatrixInt + module procedure PseudoAttributeMatrixLg + module procedure PseudoAttributeMatrixCh + end interface + + public :: xml_AddCharacters + public :: xml_AddAttribute + public :: xml_AddPseudoAttribute + +contains + + subroutine CharactersScalarCmplxDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(dp), intent(in) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, "")) + endif +#endif + end subroutine CharactersScalarCmplxDp + + + subroutine CharactersScalarCmplxSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(sp), intent(in) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, "")) + endif +#endif + end subroutine CharactersScalarCmplxSp + + + subroutine CharactersScalarRealDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(dp), intent(in) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt)) + else + call xml_AddCharacters(xf=xf, chars=str(chars)) + endif +#endif + end subroutine CharactersScalarRealDp + + + subroutine CharactersScalarRealSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(sp), intent(in) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt)) + else + call xml_AddCharacters(xf=xf, chars=str(chars)) + endif +#endif + end subroutine CharactersScalarRealSp + + + subroutine CharactersScalarInt & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + integer, intent(in) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars)) +#endif + end subroutine CharactersScalarInt + + + subroutine CharactersScalarLg & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + logical, intent(in) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars)) +#endif + end subroutine CharactersScalarLg + + + + + subroutine CharactersArrayCmplxDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(dp), intent(in) , dimension(:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, ""), ws_significant=.false.) + endif +#endif + end subroutine CharactersArrayCmplxDp + + + subroutine CharactersArrayCmplxSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(sp), intent(in) , dimension(:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, ""), ws_significant=.false.) + endif +#endif + end subroutine CharactersArrayCmplxSp + + + subroutine CharactersArrayRealDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(dp), intent(in) , dimension(:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) + endif +#endif + end subroutine CharactersArrayRealDp + + + subroutine CharactersArrayRealSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(sp), intent(in) , dimension(:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) + endif +#endif + end subroutine CharactersArrayRealSp + + + subroutine CharactersArrayInt & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + integer, intent(in) , dimension(:) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) +#endif + end subroutine CharactersArrayInt + + + subroutine CharactersArrayLg & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + logical, intent(in) , dimension(:) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) +#endif + end subroutine CharactersArrayLg + + + subroutine CharactersArrayCh & + (xf, chars, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) , dimension(:) :: chars + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars, delimiter), ws_significant=.false.) +#endif + end subroutine CharactersArrayCh + + + subroutine CharactersMatrixCmplxDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(dp), intent(in) , dimension(:,:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, ""), ws_significant=.false.) + endif +#endif + end subroutine CharactersMatrixCmplxDp + + + subroutine CharactersMatrixCmplxSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(sp), intent(in) , dimension(:,:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, ""), ws_significant=.false.) + endif +#endif + end subroutine CharactersMatrixCmplxSp + + + subroutine CharactersMatrixRealDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(dp), intent(in) , dimension(:,:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) + endif +#endif + end subroutine CharactersMatrixRealDp + + + subroutine CharactersMatrixRealSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(sp), intent(in) , dimension(:,:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) + endif +#endif + end subroutine CharactersMatrixRealSp + + + subroutine CharactersMatrixInt & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + integer, intent(in) , dimension(:,:) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) +#endif + end subroutine CharactersMatrixInt + + + subroutine CharactersMatrixLg & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + logical, intent(in) , dimension(:,:) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) +#endif + end subroutine CharactersMatrixLg + + + subroutine CharactersMatrixCh & + (xf, chars, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) , dimension(:,:) :: chars + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars, delimiter), ws_significant=.false.) +#endif + end subroutine CharactersMatrixCh + + + + subroutine AttributeScalarCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, "")) + endif +#endif + end subroutine AttributeScalarCmplxDp + + subroutine AttributeScalarCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, "")) + endif +#endif + end subroutine AttributeScalarCmplxSp + + subroutine AttributeScalarRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt)) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value)) + endif +#endif + end subroutine AttributeScalarRealDp + + subroutine AttributeScalarRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt)) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value)) + endif +#endif + end subroutine AttributeScalarRealSp + + subroutine AttributeScalarInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value)) +#endif + end subroutine AttributeScalarInt + + subroutine AttributeScalarLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value)) +#endif + end subroutine AttributeScalarLg + + + subroutine AttributeArrayCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine AttributeArrayCmplxDp + + subroutine AttributeArrayCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine AttributeArrayCmplxSp + + subroutine AttributeArrayRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine AttributeArrayRealDp + + subroutine AttributeArrayRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine AttributeArrayRealSp + + subroutine AttributeArrayInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) , dimension(:) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine AttributeArrayInt + + subroutine AttributeArrayLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) , dimension(:) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine AttributeArrayLg + + subroutine AttributeArrayCh & + (xf, name, value, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) , dimension(:) :: value + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value, delimiter), ws_significant=.false.) +#endif + end subroutine AttributeArrayCh + + subroutine AttributeMatrixCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine AttributeMatrixCmplxDp + + subroutine AttributeMatrixCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine AttributeMatrixCmplxSp + + subroutine AttributeMatrixRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine AttributeMatrixRealDp + + subroutine AttributeMatrixRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine AttributeMatrixRealSp + + subroutine AttributeMatrixInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) , dimension(:,:) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine AttributeMatrixInt + + subroutine AttributeMatrixLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) , dimension(:,:) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine AttributeMatrixLg + + subroutine AttributeMatrixCh & + (xf, name, value, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) , dimension(:,:) :: value + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value, delimiter), ws_significant=.false.) +#endif + end subroutine AttributeMatrixCh + + + subroutine PseudoAttributeScalarCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, "")) + endif +#endif + end subroutine PseudoAttributeScalarCmplxDp + + subroutine PseudoAttributeScalarCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, "")) + endif +#endif + end subroutine PseudoAttributeScalarCmplxSp + + subroutine PseudoAttributeScalarRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt)) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value)) + endif +#endif + end subroutine PseudoAttributeScalarRealDp + + subroutine PseudoAttributeScalarRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt)) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value)) + endif +#endif + end subroutine PseudoAttributeScalarRealSp + + subroutine PseudoAttributeScalarInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value)) +#endif + end subroutine PseudoAttributeScalarInt + + subroutine PseudoAttributeScalarLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value)) +#endif + end subroutine PseudoAttributeScalarLg + + + subroutine PseudoAttributeArrayCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeArrayCmplxDp + + subroutine PseudoAttributeArrayCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeArrayCmplxSp + + subroutine PseudoAttributeArrayRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeArrayRealDp + + subroutine PseudoAttributeArrayRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeArrayRealSp + + subroutine PseudoAttributeArrayInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) , dimension(:) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine PseudoAttributeArrayInt + + subroutine PseudoAttributeArrayLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) , dimension(:) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine PseudoAttributeArrayLg + + subroutine PseudoAttributeArrayCh & + (xf, name, value, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) , dimension(:) :: value + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, delimiter), ws_significant=.false.) +#endif + end subroutine PseudoAttributeArrayCh + + subroutine PseudoAttributeMatrixCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeMatrixCmplxDp + + subroutine PseudoAttributeMatrixCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeMatrixCmplxSp + + subroutine PseudoAttributeMatrixRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeMatrixRealDp + + subroutine PseudoAttributeMatrixRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeMatrixRealSp + + subroutine PseudoAttributeMatrixInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) , dimension(:,:) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine PseudoAttributeMatrixInt + + subroutine PseudoAttributeMatrixLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) , dimension(:,:) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine PseudoAttributeMatrixLg + + subroutine PseudoAttributeMatrixCh & + (xf, name, value, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) , dimension(:,:) :: value + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, delimiter), ws_significant=.false.) +#endif + end subroutine PseudoAttributeMatrixCh + + +end module m_wxml_overloads diff --git a/src/xml/wxml/makefile b/src/xml/wxml/makefile new file mode 100644 index 0000000000..7cc8ee2884 --- /dev/null +++ b/src/xml/wxml/makefile @@ -0,0 +1,46 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_wxml.o: m_wxml_core.o m_wxml_overloads.o +m_wxml_core.o: m_wxml_escape.o +m_wxml_overloads.o: m_wxml_core.o From 346e0a9caf55c8e4247a39254fb40c4cfade0949 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 12:01:00 -0400 Subject: [PATCH 052/192] new xml parser is changed to be a static library --- src/Makefile | 19 ++++--------- src/xml/Makefile | 24 ++++++++++++++++ src/xml/common/Makefile | 59 ++++++++++++++++++++++++++++++++++++++++ src/xml/dom/FoX_dom.F90 | 4 +++ src/xml/dom/Makefile | 49 +++++++++++++++++++++++++++++++++ src/xml/fsys/Makefile | 48 ++++++++++++++++++++++++++++++++ src/xml/lib/libxml.a | Bin 0 -> 1940196 bytes src/xml/sax/Makefile | 50 ++++++++++++++++++++++++++++++++++ src/xml/utils/Makefile | 46 +++++++++++++++++++++++++++++++ src/xml/wxml/Makefile | 47 ++++++++++++++++++++++++++++++++ 10 files changed, 332 insertions(+), 14 deletions(-) create mode 100644 src/xml/Makefile create mode 100644 src/xml/common/Makefile create mode 100644 src/xml/dom/Makefile create mode 100644 src/xml/fsys/Makefile create mode 100644 src/xml/lib/libxml.a create mode 100644 src/xml/sax/Makefile create mode 100644 src/xml/utils/Makefile create mode 100644 src/xml/wxml/Makefile diff --git a/src/Makefile b/src/Makefile index 9f0cf555d5..8d3646973d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,6 +5,7 @@ templates = $(wildcard templates/*.o) xml_fort = xml-fortran/xmlparse.o \ xml-fortran/read_xml_primitives.o \ xml-fortran/write_xml_primitives.o +xml_lib = -Lxml/lib -lxml #=============================================================================== # Object Files @@ -229,17 +230,12 @@ endif all: xml xml-fortran $(program) xml: - cd xml; cd fsys; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd utils; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd common; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd wxml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd sax; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd dom; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" xml-fortran: cd xml-fortran; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" cd templates; make F90=$(F90) F90FLAGS="$(F90FLAGS)" $(program): $(objects) - $(F90) $(objects) $(templates) $(xml_fort) $(LDFLAGS) -o $@ + $(F90) $(objects) $(templates) $(xml_fort) $(xml_lib) $(LDFLAGS) -o $@ install: @install -D $(program) $(DESTDIR)$(prefix)/bin/$(program) @install -D utils/statepoint_cmp.py $(DESTDIR)$(prefix)/bin/statepoint_cmp @@ -255,12 +251,7 @@ uninstall: @rm $(DESTDIR)$(prefix)/share/man/man1/openmc.1 @rm $(DESTDIR)$(prefix)/share/doc/$(program)/copyright distclean: clean - cd xml; cd fsys; make clean - cd xml; cd utils; make clean - cd xml; cd common; make clean - cd xml; cd wxml; make clean - cd xml; cd sax; make clean - cd xml; cd dom; make clean + cd xml; make clean cd xml-fortran; make clean cd templates; make clean clean: @@ -276,7 +267,7 @@ neat: .PHONY: all xml xml-fortran install uninstall clean neat distclean %.o: %.F90 - $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml-fortran -Itemplates -c $< + $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml-fortran -Itemplates -Ixml/include -c $< #=============================================================================== # Dependencies diff --git a/src/xml/Makefile b/src/xml/Makefile new file mode 100644 index 0000000000..744da31475 --- /dev/null +++ b/src/xml/Makefile @@ -0,0 +1,24 @@ +#=============================================================================== +# Targets +#=============================================================================== + +all: + mkdir -p include + mkdir -p lib + cd fsys; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd utils; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd common; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd wxml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd sax; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd dom; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd lib; ar rcs libxml.a *.o; rm -f *.o + +clean: + cd fsys; make clean + cd utils; make clean + cd common; make clean + cd wxml; make clean + cd sax; make clean + cd dom; make clean + @rm -r -f include + @rm -r -f lib diff --git a/src/xml/common/Makefile b/src/xml/common/Makefile new file mode 100644 index 0000000000..a4874b3b59 --- /dev/null +++ b/src/xml/common/Makefile @@ -0,0 +1,59 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_common.o: m_common_attrs.o +m_common_attrs.o: m_common_element.o m_common_error.o +m_common_buffer.o: m_common_charset.o m_common_error.o +m_common_element.o: m_common_charset.o m_common_content_model.o m_common_error.o m_common_namecheck.o +m_common_elstack.o: m_common_content_model.o m_common_error.o +m_common_entities.o: m_common_charset.o m_common_error.o +m_common_entity_expand.o: m_common_entities.o m_common_error.o +m_common_entity_expand.o: m_common_namecheck.o m_common_struct.o +m_common_io.o: m_common_error.o +m_common_namecheck.o: m_common_charset.o +m_common_namespaces.o: m_common_attrs.o m_common_charset.o m_common_error.o +m_common_namespaces.o: m_common_namecheck.o m_common_struct.o +m_common_notations.o: m_common_error.o +m_common_struct.o: m_common_charset.o m_common_element.o m_common_entities.o +m_common_struct.o: m_common_notations.o diff --git a/src/xml/dom/FoX_dom.F90 b/src/xml/dom/FoX_dom.F90 index e6d8cafc62..6e1ac36a7b 100644 --- a/src/xml/dom/FoX_dom.F90 +++ b/src/xml/dom/FoX_dom.F90 @@ -1,5 +1,6 @@ module FoX_dom + use fox_common, only: countrts use fox_m_fsys_array_str use fox_m_fsys_format @@ -258,4 +259,7 @@ module FoX_dom public :: setLiveNodeLists public :: getNamespaceNodes + ! Length of array + public :: countrts + end module FoX_dom diff --git a/src/xml/dom/Makefile b/src/xml/dom/Makefile new file mode 100644 index 0000000000..1f597ddbc5 --- /dev/null +++ b/src/xml/dom/Makefile @@ -0,0 +1,49 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_dom.o: m_dom_dom.o m_dom_error.o m_dom_extras.o m_dom_parse.o m_dom_utils.o +m_dom_dom.o: m_dom_error.o +m_dom_extras.o: m_dom_dom.o m_dom_error.o +m_dom_parse.o: m_dom_dom.o m_dom_error.o +m_dom_utils.o: m_dom_dom.o m_dom_error.o diff --git a/src/xml/fsys/Makefile b/src/xml/fsys/Makefile new file mode 100644 index 0000000000..6e80265b0a --- /dev/null +++ b/src/xml/fsys/Makefile @@ -0,0 +1,48 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -I../include -c $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +fox_m_fsys_format.o: fox_m_fsys_realtypes.o fox_m_fsys_abort_flush.o +fox_m_fsys_parse_input.o: fox_m_fsys_realtypes.o +fox_m_fsys_count_parse_input.o: fox_m_fsys_realtypes.o +fox_m_fsys_string_list.o: fox_m_fsys_array_str.o diff --git a/src/xml/lib/libxml.a b/src/xml/lib/libxml.a new file mode 100644 index 0000000000000000000000000000000000000000..558e0c9a1657c4724c1243289a726ee54b81d62d GIT binary patch literal 1940196 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

>>2n1L=YEJ0_AAgXt( z`?1u!rIOdZTiuVP-fgvlCU8sj4+$_()(coTs5EyPnJ7nhxfEaIK%zU-MWTi1Pbmiy z=Pj&vD<=HORr*B|g~C#Z+D$ps(jOmO8X(8L+F;jlr1_ZaBMxW+x{jSGY|s+`I?4Op zb}qVX)%{>o6=}h)<4Ae?r1E7M8LO^iH{8Ev6mX1d3fCT7X$rg#SDFIv*HhrTvCCO^ z-r3}}C{i`jSDO{lHdD~onpP}BYNX;8``q=iiD$*k@_Z(!LASTxd?nD=jkuB@f?xu01F>ujZz3t7~pGZgtIFgxf_b zZ5;;g-38}_x=o$*jEPs@?Pgv0v((Js+rRu##k4pNQiEPZP4yadN^7L=%?9ZPU2|Qq z2*`|!D##ru-Lq0^pf(v*feT#wdU=$fE8-kt;b2rmt02aAHmHa~;08Td0NkLH+yfR{ zH|SJ)UI!nAoQ3Et6FmG^Z-)L;t0N=NUY z$WN}?SRLA-ci7uT1)j4Snj3R7{X-6`Mn=!eMVeGAPewU(H8iUU8emKw=Qi$ycq?K6 z!Fb4OXvGUP?#zIQ8yr%z6b5&p*`Z$$1%;&mH7}~qqNt+!8f~&|D6QGM#6V1U_z z9URq11rid~H&UtW=Dl~k?OIjj9CJi-H%zR!$hr8PJ5|4*Klc0u9%eUH2XoHrBq3H) z3T|07puAu*FpE;v!D}a7>>Ezj0880#g53P4g^N-rKdn|R??=nNmQwkvD-m`^kbE75 z5l1hJxdk40LrKSXOT&-&TN)nCTN-k&1=G{UP7lrFT@7t;aAQM$`>h058@1c2iWiEv zyg&f8Lp=HQLT7qIz%7=yGwAfaOFsCp%5w*+|%iAV;&dBP2QrH^9i`kmytfmg%gmGAysdrN8u4=7x8=8WrASR#Z=WNaM zVo&5E9g$|diMZ2B2M65#k|y?lF#uXIVa}ewcDqB~cDvut*>2Z6Ad9x!{XR+_Tkv`? zZk3+k?RK9C*>0Dk5p65hyt7VEy9TNg*5V=he7rZ%j$B0*xqy+kV0&p;e*S;@42W3xMw`Mm?_BgTuOVA&3$W5jc2qc3cSDdJK|ywm zLXe_|2(~)5mPQE@G-=Zf48PSV5hBTa28ms8#IdZEYSJ;>;pRE9q8&^Q#q1{sP z8aBA(lvA?ON6JNssjCPvbw=-P0pcVkp?#j1J_M�*D2| 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≈E=Q+vnMWR0)I?TBooH>shof)!EP8)tZ zsn@voI1in$_y2X@CYBFu{TST06~Vc0w}Z3aW5RV^7D%2`hU>mP2Oai%6P*2K{3ZJJ zQe3$8En~Ru+ltVkZ+&q3_5|;MxlI7)zFi2;`D_qwbAD*J?%PkG!<=7%Gv|Gyvr6Wl z=xW02WrOfk!u@p*qpzai+z)NQxgUN2XTP_FYrmVt_iw{>KRkdA`%V9=TX*(b0i3>d zg3AEQ@HJSF*to!8Xfgri+cK2z7^d!PPp}L z08ZZ)Mn`>Hqn^GcevA0}`rMx_z_~v&|L*E}ot6-;*Xen=F3K9-AoBE3CFt-vZ3xcm zbPzaw=Nf(WU1YeP4@;p#-yPue{Smx_H>3Z(&i>)%FdF=EaONx|+|H+%;rc$e6m*!g zHaK$*Gdg;1jJ90PjmgmQ_lWkj5S;re-st$x@gMJ@p8G1>oydH&U%h_w3%C2ZI5>SP z8lCWeuA$H~sHg8PQ?I@UP|tJY1UP-aH#+M3BkJkf?QZmb`w6%G4hN_2M5Cj=(@;;} z*gvEDJ|W!tmIbG86{DlR)lg60$NzHspXWvcaGo3O!IyYzsQYaq_zLh<;48sTfv*C; z489tC(cdm7``rf4pX+$_pXl{(gR}la@EP#k3(oH^d=tRO+;hG>Hx>%l>*%_it6{_S zT#bhg&yA13d2W0QPT${+zWV-YxSp%`p+n!t@4LR}TM3-L4Tal&n;NdZEull--r)58 z44l5ljK21J%5eSM;2d=5dlQ_#WB+w?pzk8#+V4YI-=&7@`mTTueLn`L?>TV#{$}*m z_paeO=X=nh@1qY~U-YdYJhgXC_(Z-Wc1bdsNvf0N$Ak`7jXJcOdP%6uyF0S zsq`Cfxc2)Vbm+SsoW7^Qx$c+2IfwhgZJtC)BKei;9Fhz7zsfvW!I`HKxZi40?~ngz z9l(!(Gw093HBTFv+f~DLZa1OBoQadVbz#nY;PkC1-1HcN%~RBH%~R6w zU6SW1=rd1KaORn6bpE$4hO6&{PNK%c(5!0G#&(NW*KhO6&A!(-BTi|IX*rF8S8 zZ+39{mKUyb4n=M=LRC;d%G1^BXo#uT_wOSNFC%%z8m@mIa4Ph}(EkLS?}PRj9sfE0 z<4e@@ec#elkvaIwAxrXKeY!^?T>I7c-C2eEU*-GmeBjLUq|pigrydHGMm_Vi0jExv z0G%GFr%t2PZhcRBp1LlNr*WL;TM6OXZ)I7RvWC|cUI{up-(Cji`8EbT3-ZhcXP(u< zZJzap>-(2Y&|#iK;LLNy=(J20S(sa>AB~)e(ng-zT7cE;!G!8y;@!8y;jjK2OpoRO&KJQth#7&-q}2h{Hd=RA*sbDlpNeVyl3!*!lFp~HEm ziE;ai^DH4;=XundpZ|Co_3XD6ICH*gbTnsM!!>6|=rHFfaOPZNbadUfp`Po04V>$q zD!rQn*FB?fox`EX7(xY5&pA{KsIL)F{{}eMy&pK|Inn6rJf|71^PCMG&T~CD=XuKL z=(%yuaJ>(?V7T6g`~v;3H#2=b&G49;Gv}X6xX#&sj{nGqdcIyKdOV_I??c`ct~z=j zGC;WhEZ&EV1ZSSfMknlt@g6f!&pe-jQ|DlS&Jom8XG;b*AKr&NmodU^U$uqXz8Zrw zPb;Gn{;#W0s2%E=XBjwk)&=N%fO_f_&*b{z-(RRAT>H}f_9E(eeZ397%ge6&Y5_R) zw}5|z`aQzcSMSdcp`Pz^XJ&T!c|EQXt~w3mes6=}dLQ^9ba*{}4bJQFy3x`5nLDT- zjlPm+iJmi?aGNu?;d6~a|k%+Io;^!Jm(m$=i5T)Fy}|$ z%y|m@TjaR{&OD*4k$Kv=B{RIetotLv{bw;xK5*uF0el(qyaLWVy@lI6{SEIVd4@oT zdFFsK&->uZk>?wuuh;1r!*$&+LMIHJTi`rT{xLfKbNt6c)bl*qo6YU>CSIMM10}LY z=M8c=&UtncuJi0FbL(cfUcbGd!+A~w=R8*%ol){Z!-uHn`F7CM_mw=S0_v}udR>>l z1M17E^~X*aGi5$!_P~7W#PUW=UfwzgxW)gzC*z2y9AuShv6)Etk;8{&yN&z^T`fg z8l3x~5qJ*N4+GBw9uH2PJ>Yp!e;J(TQkp_8CwXyj?uRM?ybU-%#~TdJpNH8BJ`g$2 zfwQlB;Db<~v#`s*0=yJB^S1(Ti29M>^nDlnCDd;RUk82^oW57V`ExsIi@5xpTUBuG zpAO*6IU1b%=L2x|dj_03_rTe2xhGuCap<=eIQwlLz*7`;I`l0FUKjc`!MlPt1E+5X z;d=h;_nzZFx}(0Q_y6@e9S=Sfe4)|N_f5-CPo3@HJSPV{8R=K^=_oIyD!Z z^I2+i!rt@z#|qT*^qN+v*u=l?Qrp8su2x%~7UC|rG?mbncz zTwm`-L5IGJ!RfmjoW5s`zWSawT)!{>Gj!1<-#d%*d={uglW&y>|% z{z2HEMZmc~tAcYMwg>0AF%+DiKYRktoIijw=L2x&jD5l7UxEB(!I`rG_&U^g1Lx-h z)4~dr%n&> znefdU=jO&aR|V(i0PVo}IY4i4&Uv_S&99#Wj7L4+zb^-$ikw@Fj(!fX1NGE74enp+ zI_HTs6V9`l_ci}9Te$yKeh#o2ob%aabi%%f_xK3){2X9cuQh7XhT zDqgs+mfriLeQgBi{7-;$Kl}^M`-B{IB02s4vc3{HU-vtJ^Yvw*a9wvjZ-*MLp9747 z4$r;C;5_#l*L6AR+f}&wE|acuB&|#jz;LNiaoO$+x$KiU+THp0WUKpJDD+{;zs~WDK1H?gx`CEfC z{|a#4C;tc@i~Nr^aQXSVTn3!yVMB1fE)NE$&T??->;1#rHP8vst7Dd6<|37qv0!1+F|L?f4n^|ip6^BZvP z&#S_9f4(p0>P^G-T)hn)?#~pBT^{bwf55pPvNmz`ybmc0&izmqocrN-aL)7TrV)MZ ztC@EV_>bzs{jc(VpguTrHa9w9U&MQ~LOt&XCN*Ld$C+o1 zaNUP{#CLt8TsVbOW-^YZyBAiAI5v!K|Rma zZS4~J>ieu+hU>cTHT;Oo;jnODjr;8^IM;nZ`-rdqU(R{DaGk>+qCdxQo%2HIaL${- zIp>bAC+zoc(eG-w_S@6&kpD~Ny~iNow%=Lc?6-f%guc4IBZT{3<@(M6=lX_?PS_9Q zJ>pT%^}W|Ap|7rQ=#7M2`%PweVd*!WaQ|6c-|XP*_cx=X{oX@8`%Tt4lE?oq`%NcY z_e0qGy8p<4diLACYr?+Nx4UrvtMnZKPTxsJC+wSgkLjqV@2YMIeRa<34A(h-V0Z(W z^QXdnHO_fIIOlw}d&JlOFR!Cp!gX%FMgNZBdVc-`9bQLi-*o!Cj_!cd_mLivdi5Qs z`^Rv7U!Os^|1A0z0;lf^aQ~J<&+R`0c!{1aKkMs*=YmdqaK7Ie0M7b}!nI%db(*1> zsE>vICh+Ru`@zeCKiMmK{uD4$k@$!tH)IhkDNKJ~;P7`aZ5N z?uWkM>}wi0=N}KwoS%TRukXRB^9ML}()D#Y*;fT{>a+&ue&`dx7lU&@g!(yO&Y>#! z1?;!R;H)1Xz*mE_-yPtb+i`H#|18|@hwG^4+|u@UeQ`hJ1?PSk3(mflf^+^`z?t(P zIQzN@PMxF!Tu$o5g0ruh;MC~>&iybZfUgJVerPw)<>BkmAmKXy(eipU%y4}@8Vw!3 z9xVYch1u$2%P<11n1{VN#AxkdEb&DfIkh+IlKYReX;?ZeSHJYzHWoFulz$@PU_SJ zr%pF;>I?_xeAa+-K3{=zKHmlK#KT-p&Y_fW-RIL}zdeWg(%4rWz}eS$@G_`>ADs1v zg{$v$@x6%p;?VgWoc-n*?)s{S`icSkRqz*4-wmAl3&FFaem6M#`U#vr$C6=$%b68A zmB6Xf3_Kg^-w>{I)9+LFM?Lq+#DMw*0rj7Pa}I|B_${M9L)J0zNY^jd@qKV!FDJk` zhd;o1y<{Eb^jZI$aGlRA$x|Qo%+ng2{f-8w{!Vb}90sS(190xojH6wC@~6PLKWl?i z|4s0pu}`LhGyfOh%>Oeu_0x@sp0i*8uL;il&A_SO6`Zfjlfe19zX|*&^m`hdbNC0G zbI3Y2(wFX&*|JZH3HQIs`P2qy{(<1!&+mZKcLzB4^EGhR7aQm1&pegEnWt_59|g|+ zwlIKi2j@B-1*h*-aIRze@h*Q_tk)CZtgivi`q#j@AI5@nKdc4ke)t-kbG|NI_rqM- z51|PzKj)tdoO7rN&N;UZ;KRW==jq_oj|YDm^E?U8x!nxlnJ2owsPjB{Iq1IuPW=%9 zd=)r#{s8B?WSr#ka9zp@*LBhRlNzY!y1Wj~xs3)djlR}`)AtBC?`O_~Q$P9S==lo@ z*Zg{)QwjCVUkjZ2{lU4frUvkh;M`Yx!Kwc}IIqWKQ(RxXzMcT*I@Sf}Ji7?jIV_Ru z>uuC?4vWB<^AI@C+uy+H8#C4A=XqNNob_*mGtU%o=2;TJ4}()b*|bO=ox>KHLl)uw zSLs_AoW5U;%GoeSVR zmr~DgIe9<)BslwhCV;mG=Nx8$m&1ATEjasn0M5R0&U881S3Pj*^Z}>N9B}Hq2hRDN z0Ox$}f^$B}XStl@MZr0T0pL6z7J@VXC&Kl*&~yGP)bqOd6`Xle&vrRWV_iyv)Awa? zp7R~RsXqyvI%|b%em&=RqMrE=gH!(jIIruBb6j8K6~K92*8!(~dvKof6Tx}TZvf{y z9s}nbeiN>9*ed5+(zz}_=a3hiIqQP6-*y3fG&tul8=U&9!FgSu0q6CSc%IA8`Xb=v zu-_VhQ>QOD>puhMoPPx8oD%pmi z0-QRxg=>DjUQ#S@b7ua`;M9);=RSEgfDZ-dKA8zl{eBBw9=`8b56;hXj)Sw`-@&tC z&WRQ!Tu1#pCq}scRi4j0IfeUQ<@HhyoH^@(^SbU3zz2czbu%8EI-i5H{wHwG=br!`^RAmG^$USh zzeWIm9h|;{0{C=r`mO?}?{49`E_#mbNBuHyZS?0Zu7Wd9%(Cd~n?<<))dA3XBA~t@ z>Sv?AA2|107g8_Tub}=}@H*h_!P(bH@Y<-K zA>6LlQq=Q0`WT%3UIAyGwDB%K^ArPTp69?h=f>dl?Fh~}_XFpgM+We@;GFY1aPITZ z!C8M2{1?pm9ysTaafO@DRn%7luLs^DfDZ=e`8*Arb1S#f`Eovc!1KfRoN(PI&E&kj zV7PuC{1S8uK<6QNLGUcAT%Ii8Pl9&S95VgA|R%)cI- z=UDpHt}o^(Al&A8!f^SRF;oIN%u^GbdD?(i!+iRJ`?olH4j%*Ozi)C+xaO2Ly?+=o z(wB1mxx{3KcanXQPPqRp{#;@KaPH69Mn``R^BvS@K>pR>yuLOWov`;D|M3y(`E!_a z*0}ZJKHLgk)qB3q?KpS~@VnrRz!R@^`rHqhgzG%@`pRy&?uS_Da6eQ6=YCiRUI=+g zu8W?hrf|(OQP#1Z;d;F{gbwp`0cW1MMn~@x-bMXHN#Q@$F{B`Ir z1g`?V3!MAv2k>W6pXq&!Kex``rLu8F}V!aCw;jBXIsbqm&z6 zJ@Xd|;4gwRe=Bh2UkuLt#{&58;QTqU6d$;p{CTq+;H)n$T=%p7oNam3$6{_TgI5Rd z0?wbKI|9zWu7bxw=OH+AX4n+HuZrN*X#`H4H^JH0TyW~_1TTl2-vsb~!FgY_ezVI@ zeh8fNsqvw!F9F{V!Ff)8C0y5WzMMqxsjY54yngS3^ZL#Fk*nu@dkJt}kJ&zU_0+EfPW>L>^^kLH z0AC5t`D_Jeo)(`(^4R%z5bl4Kef2fCRaO!9K-02td=BW2Y@2^Z@6B+??H#x?{08jzc-DJUcdjKp4V@t-7Y`(X9aLxzn#H({f+|X^}A+| z)93mg0O$OF1?T*e>~%Vv|8;Qcr`hM~sb3A8*KdmeJ`kMqnE=i_Pks^c)%Dfi_g+c3 z|5f%?%hVt8FGlatB%prUe%BZKeIK0t9ssBBMR4XnaUi<>AK=t~^q{M!enD{RKlPQX zXa45k)PD<{`jfz^fB0*s&+G0d;kqtc<+}UDaJ}wsK!?{|qQg#~*IhwyUN4QndA)Q7 z=k=24sMF_sV!=6|r@=X&m%%xoyWpHp>SGaKozIstpBTe+J{g7k&*FRvgL6I)!0DUm zctYRf;+x%Y^^Fy7eJg>}w;lMeURFJyFM@Nu{tDoEPegM1|7Crx0Ny%)&j;tvGaLlx zy59rm&odPH#^s^TOW@QQ2u__<;CV2gGvL&Di~5S-)aeXPodw|3*$zGeT zaOyM%zk&L(;M92^oH|FqsgwMS%fs`hP5_@4z>fy-(Antv1;Kf~l@H*x19+q5d!0&=r!#X~C&drCsAUHqgtQ^2wg7bVE0M7T<(*yVlaDFcFaR5IV zz_Whm`ie!rwZVB^zX{HLI|ZEA^a;FH1GZ#aN&58!veIiG@;T~5xY4mju28JzQ(1kU+{!8xBV!8xDD zuedxs-(C#h1Hk#ZJS%{|56*e+4d55Rsed1wIvIX(`B`5~xW3Nn_Z`Zjp6gN{oH`u? zblyZgbrynG$9~%szz>7-KI&=!Pj=Py%loVB0lWe@zi-kofbRn5e)tZYpC|kU&bdAQ ztINstssT=&w&2tm3(h&e3r?Nw;MBPYPMycExt!D~9>80Im&5w@3EzvkNj z9(Ud4cqR zIl=jP=5OFU_hN239e(cg0(d>-X%@iyfYWz0IM2zgx12BMd04p4^Qz4AgyA~RZ=u6^ z{tnK0zW7^o-?qZ7Z%4x?MV=n&3LW~62dD3Q;QYCZo!}iX=kLKsgWm+_+>-nr31;)B zG+h6ERTQ<^lb!A-)y&?FR!~w;JohY2-iIN{e`Be=Y4W#Qy-HkGM@nf^$Wpy zJ+21lx@uB_KZe0!6x%Gq&=QahLb2|v$(3^q2UfcrbeoObKn*)E& zJRdmcQ$e`SN9P=edd{a4IOo$Bob#Dr^mRT{4cGb1f)3}i4xIBjVRTkS+7Eqexc*$< z_lEx^uRB+u&!6i{_LrL{`+XL?3D#xU->#nP@(wun`DWod|9QzG8+|wG8$Wf4FdvNCe7o7Qv-iz+r1)TbWz?pwy0N)DEIe!68or3>H&r=T-h)U^U6+(eBJ1AJ@(2z!H9Xeeq~4>Aa9@r4wi`HoCxf5& zo}=f<6>xrEquxU|PhQt;g=_w5lBc8LdjHlHI=rrjg7dmwVs!L=cs1&IKfDv1_oXvK zk-_?1aD6`j=YF^Z&imni0(j;`PM^Moz&Zb%iCsPCSz5Txv#!jug5f&P%Fy9F>w|Ni zoxpki_XB61Ny2TO>4rC!`;a-%VV>3C%yZG`)RFyv*>F9#uNnTD%;zrjx&PB8adYPU zD}(bo-3QLk&xKa{dp&InN9!BK11Y88XkThU+|Y3HPsS&a)Ia=h+zi zYn*#Sz|Vl^kptP+XUMQ`K&ej0__9F?`v6<)XKgJ37=gGhCun3&{bA{0f`y$?B4eI S(ytf9uq4 zK2^Nu>+9VpaQZF-e-`!oz<&on3C?|f6`XkzrEz&U&tl+Qmxkcf`5ByjJ(AYxv#%oH z?5i#~`)Uc!zIuQ&&kS((^&U7smpcUh4CeU&`~rBoM_ph3ExWE`5#f40u8`}oxZ(Ob zQpWHNQvVF}eWdygz&VGt;9RfE0sPT)EhOHf}C`|~6? z@9XY?^PJ3<(dEwpo!a1Y!Fz#IXD0X@)E@xf3I07e&zau?c%e)#C-c+=XPz$L%rhDM z5c0%>GtV|~uFGlRc3sY+p6ik&v&+wQc?O*MTY__4CV@9Z{>|Xj`3C$Y)Thbf@{9wI z1?L>f2Jkn)nP&<(^Q;4Bp2OfXk>^Kn=E;*al3(Yq|ITR<;r{v46FSd>_X2MR&iZ-a zRZzbjoPC`Ge-`x*z^j61%I4mfIpSn<)QxT z;JJ`z7(rp8%)MHQ{=0=>6nv)W<@nY^=-4`^g62%)dNg3Mv^m$)eR=DoBX8y(MJt`Tl_oY>!!~4>gzzcfM*Y|6A z3Pksv(6=QxeMf?`-?>I#eHR(7KX0%UI`sVnoW9)(M*7wJ=OfbB zx5Dk`SJ#ExIs9(8{(HxNLWg-W6moNBo*Lks!z;qA?`wvu@9WT^@7v(?{TZD5Hcerd zle|a(uL#ck4TRhLO$_fL^KSti=I;&8{IkHB^Kbya3(j*UeG%6$&#NNftgk9u`_l8j z7V2YheZ2`@9egY}&*8to*;mFVT+TS?lmutaI^gW97dUk$gHz{yaQ5{bICWANbvb#i z<`3Wv!Fe9u0q4F-RV<>f^Vjp?QN#7RcwD&sJfS2w_thG3`tCIP>bu8q_5Bh$+|L)m z>H9Z0ebYV}z2C98=U#K zfHVJZ0X$1d*DtU4GT=Nn>VUJpop9|-&%-XLkM;JG-fv6?=XtmqoabS-QZ6U^ssJ7b z{f6Mo*$te1%>t*+`{2|$3eLVBf>YAMx2 zz6XuI_It!|_5B7q^t}O2-`H|)4&3KE!I|f{a65<7hBr&*T^!!yJLoXaU2x`UT;Ap3 zKI|62XMywkQ|Bv0bTp@afAgwv|8*gQ_eal%2jKjCBW1;eI$`fQ{v)k$Ux(k{tY0Z% zz4|s6ZhgCf)3=||3HzqrV=(IJTkmN%w~=0*_Vs4vgn8bQ``)(<*YCRxFnpNQj}-3b zPY?Y?;C`w4W`8E3ug;->a65+*;G9DxqZ9T#{6`hka}GzVxH;7D>NHQjXB{62K1R6i z6aBfwiH7TZrWw9U=Cc6$oX;k3&gaeNT%PmDGXwl(@aL<#dR~t$glqn3av#;&aDBhr z9y+`phk)~XTn*!VR^Wfvaxo=z4aXQ>Ty@YFDyJc?u4A*@-7&`20HaPqG z6r8?CjK2DQW4P|yGti;$AK>&|Q8&`BzORY(Uu3+;WA&oXp`dU(hoXk->w8J)Fi$;j z<{1XgIm|NpI*0j&tM3x%(0401eRI9&`sIB^^#I;BfcFGv{_(=?+@=_=?`vj4hxyln zGyhlM%%8G;^u9`f^S-?{IPd@4g0p_0aP3Rq*Ni}Ythb-^dRzrw9efu!-`A9C5Iz5k z;BnA-1DrWWfwQkQ;MCa%PMs^@>?_ktE+=(f056A}uLkgm;Jlw~(a`C0Uv(F*^VjR5 zx8ZtS^oI`j)ogI?tC&X7ee(;qzC{dI-{R1rZw+wzwgP9ry^Oy0+s|zR8-nzUbQ( zoaf2R0KOrB?*wQ5Q^M`s&Ka)f;RWb0|3Bc&pZ#Um7xTXyz=weIdY=c*b7KQI>-P)S zzVtjif_k1CcfqUUe0a2}%g^(0C^-9C1Re*St>Dag44i%a4Njd5&0HSplm};D?ZK%t z1DxmKngISSIM2gX&0QYut51dN{Pnu{%y7Lf_CbgH>PK+yt5z+d`}PuUeft@%zJsAd z-`U{wT?@{BcNu-{cdy~ zPkZ4u&l`s8=RDn^!#rcbnP(L^_u*#&{0cZf=ZSeWdj9;vZT=#L>-(nS&|&^M;LN|W zRm4~I_5JBa;r9LMUU0rYJ#2Ks-dW>6PN1IePk(3^z285CYaZRVe;Ka*{tF%Uo2k9i zXTP1n`95(S_zzxgy)Ui)y3-j6J}7{%6|VX9bE=Jo>-+94h98mlkGr7H&tbj==lkwv z9U?i^SJ$zvaJ!CO!MToojZWC>*?$Z|J=d{T$ArH6Im3&FYrl;Q|4aIPRk;0}p)WZ5 zE!oMf%Xu%Ku2&y$zJIUyhO6f}*+96??Y!LAH8EV@zqf!6_gimp?zi{B`SV>T!I|f( zaGU3*;rhP*HguRLZD-dn^Xw9?_pzP)#rGb?y107gc}}>^Q{8Yq&uc@6c{+kK&jN7z zt~dI+E}INj-;ba}-=pC4t=QG|%h!?C0eo-(p9s$U%Y@sxtukEiW7k24`S*b{|7CFI z&)Y3}U$ww_pVl6n_j`lESwBm-_NDi+i%=iy?I*n+KL@W4eg>TPv30sf&)*3=4mu;j znR5v^``Qamo%7(-N&KeE!@f#_Q>PU;?_>J~@OW_EKh*5ue7Ub$3fKATbS z=iz=i4`1vReg18Q+c|VJT=R5=4)cr$XP!^NIfo-gU+3_R;p%$^I`sVmoW3o3yMB3| z3=iN-0{A*`=HDya&h3EVdLABz4)b3DXZ}=gMbH020Ph0M>wOG3&y6MEtluJB`_l7p z2kLol`~+Sd=h%I4o`>D~MDJ??cpP-%!I^UxIQzN`PMySkoiBCrgR`%O;M5rg&hv17 z0RIA<=V9l5E)VzB+roAJdR>e(T(669(BZy{2j{+u?H}E@tZ?gF$#C_p0v-A`1*dO! zaP~XG=xe`Y4Oibu(4lWUIDH=(5WU}A!fn6#3|HU6(4lWtaQgNJ=lS#4!034j3b%QR z8m{l_OG1ZvYJoG)N^tJOT><)^sQ`k!vDF3LRC>u-_0YU z&w0Oaor7L4Um31*J_a4m`B!kxIr+$h{T@#f37Xn)eg7L{_$_(AnN7HT|6358?|<8Z z^K*=Bqud-udQo&88wBw7;Jog}3D>#l=NOX>*S=;Lo<;7X7D1n%V|)nCzRHeHI8WWT zRfOC9Qxlx~r=ii&{nHfn+&=}zB=ptKF`h77`z>MkbJA}m;dUJxg0tT=W8He4$9k0j z=jRx!!TGwdL%6P2VZS-=vDJz&`u+mW z`9CoFeZ)7(q=frWeNzgz`z<>-edEFD`-#yXF1|YrSKmF*VZW!q>Dy{@^f~tyu5+Fu zzWohX-yzVU?=*1wW}On->KmAT`pYv-6X!N4Oidw(4p^V;PgEMp2a)+^!56708cf|vI08H z|1mi87oFkq6hfW`;Jn{>6TCa>mx0#<-x|PAfWL_PE8x6;7(dhHWS&*v{C#WZz?tV( z08cy1=`&AGaONp5T-QZ^k6cyMPefl$!Kwd^(b3;8@g3^9F0E#}oc#R>yTQ4CP7BwZ z2W9_!XSnX4i_qczxdYDqGk=cD!*%%xoa=H8oa+*r>vYI-f^%+9f^%KA2-kk=L}WwT z4A=YHU51~Q`5b^g@8{2e(>K|?h>`Vu#BlXZXLu;Hw^_VLR^j%(x(GOZYa1Qyx4z-( z+t~2J;@b-P?6)U4eSZN@>*dz-JlTA=j^xh=@V4Om{H-52KgXK{&iW<7b^iMKfmR=`T1nc1=0Iz3mylZe&EbG37max0H@AgaOzwDXJ5$|x}4N03SJI5s|N56 z;QU-@5IBEd++yK6e|=y1uHm{5R~p_x_Tfh8b02;I&V5*6QNrs^-$z#!?q5g#M-RF` z>wxpTYGQQ4-gEp%3)K5ZgX-j5?B>ilOa$jS92Tzg=_UQf8{Q-Nf3~6bpu=-`J9t6l zxodRv9Jpt=&Ofvy;oQbao=1e+^CurT=ikNXsBaI$)whq~8^m`g^ttZy!Rg!k9XJ0` zn8PyRI%mzZ%5crI&hXQcXDjrX=L9(OYz(_R+z%Io+dP*H*F3)(9`bLNy~iEsGtXm7 zoiFoL6K;KL8m_+e3@;z+a3D!9S2U|Ek;Luw;8U!y9_@sz6YRB-}B(~9lqSnf%|HXaGgV9|Cg+LkA;To zz6wK!`)U(7_tkBqqy7GExc2+N@L0){GT!B9zXibA?`PmVPg1Xl=xD#1C&qBilhN>+ zk|(!td(Kw|XPz-eM|~$6uD;U@?;*YmpwBsM0;lgiqocl|l?msdzR3)qC%)-~+kT6H z)3=w=QQv-stM6dLcZu&9=(FE2IDNl1I_i7eaP>WH_!aTJ2z~ng1y0|ot0ME%pAV^+ zEpi>bE8L&kU)~?R|M?L7F8F@%Kf%v}9|yk*{(tP93w&Ku_Q!A9s-Pq&GG230q@yaO z2#Q2=IU%WN8|pPlwW)OU5ot1lme7P8SEe1>(eca}j~K6_OjS$z(1bB2=!^`83`MFn z>W`7Z@Ly{`?)lx7d(xXebksecruW?MS!b=a*M6LR_C9CdFMxk-xGd*^2Ym8f@Kob} zis3Tf_Xmz~XaCeXPQXjsm5I7p-(f^Yl(R#St@;P=ZH=N^!;|VuC8r$xpz~7{EY<@j!xb^2%liojr{6Qf9 zK5$I$iSuLIJr6kAy~=QzuHS(C`(XDm;ONf_z|o(7`tY{LV*B|8aP+g!<62(EXLrLn z{^-wPz|o&mg`U5gIFA7NOi1q}A%CO!y^IPU`J06NTBH9~ANhqs{t=_U$w&TeA)mAl zIZN9IKJrOVM8|C}Bfo>;95+mFP{_|Q{-pcJpDg5Cjr?go@;?{ycKzoGKJqJpWBq*( zIF^&`7wGiL^88c7IlWk(j|YzBXC!beKfmzdw*$w1<9x$yxjoRt|4G5^^Ad|d5B3{h z1CIU12cOjG<#b_uT7YAGUN_wOW80YzKpx}pEpUv(VNYqhR!b7Z1-JF?A3l0M74lZkzXiAT@mn7~&L3l!w-Vr3-dw|Fef%BBV|`o<9LwP< z;8+gV`SAT0dVXSkJkoGmZU>wCc&y;IJ`MvtSRV_4V|~09__t8bZw8M3+;6z_=OMvI zZ%@i-dkplTKQ97De=d01ivzX~mjlQAy2)_sPmmXDr|lMy=dxq#?*qUwUltqA`Mw?f z)tNZoyh!_F_1k)Vo8jC(B%yvGZ|m=4KJw2Ad0U>FedPZtt z&qSy9coT;m443KMOUUON`Jej8A1>s_8~LMsa7>>ZNB_~*DR{3VG*?2jYvqTXuX!?` zOq=DG7@jKlybHBln&36Xng<29{-pcx48eE2NbAWI-0H~^{COi^ZQ2Rz|6?O`0O|x^ zkfr%-!QVH0p5RtbgAZ>M{Fpqgf4<;_`I;{j-0E-g;VptcV)V2MzP(9ro8VS|yWqt} zK63{hZ}yHu`==$^&n&^8zejUN@SOWKFA#jn6Pk|^eBc7jiv?e8c&XrbKdI%*1iyKq z=2e0xEz-PN@WqDL3BKDiT7I_RrH0QF{076@Q=;Sc#9g{zb_o9aD>Yv)`1I>E?-cxv zI?V$-C`lW8$Hekv!L8j?AD$-o%k^4+Q1H>e(L7!7+i%r8LvU+1(}!pIa7XZqZ`1ni z@8{U`zO$ch2RnJNgEq@2_18SWjU(_!_S8IC@JT<>JXP?Y?yY&6;IA4U6#UkGw0yeY zzc)NX@Xz?c{(#|*;Ex+#AovT0w+jB*AZ@oz@M(u=-Y)pSLpARZ-0E5H z!#f4fK3wYw@MJdRi{;6JTm7j%JWcS657YXCf?GZ5f`?2TG6dgasMeDy_*~h;KRoVZuJ-Y@KV7W&eDFC32yaN3I2zXTE1HF`_9w6 zPVldc|FZ?3KU&Mr6Wr=)@ZpVue{Sr~_u&f#xB8oWc#Giw8l(Me72N7+6TI_WEpOlZ zx8>*5OEoX!n=WVvR{lcsD#52;qItF8R!^M|pDp+tV|ShpZxGz-Z}j2w1z$8(`?*kX ztEWlud1YGOz6WgMQ(C2YtB~(7{3 zV(?VK-}yh-pjqo+mizDB-P@S%pc30`D)yWoE_+>YyT9C&=D zjdy6vFylRvX9;e{VG4YBvEX(bqs)g_3vS0%X8Z64!R`H{R<6N2tMi(4b}<1-4qS_neuDxRvSK4@FACK z`O$(m8a`d{6Dzd*?SkKH_+r6ln}n_wyzok`r;iDi_4BWW4-mXzx|Yuo{G}P1PZIo# zTFvJOUQws{0>N7iUnO|$4O;#i!TZ!}zOQZ;Bl%ct_%Ok*_?4EwK=3)UG_MhS@0&C? zk2OW~&oI1I$lu$b<-ZX;sZsOvKKhfB!GCKX*WU{SU${u~D#0IX(tMuaA1&28n4jpLf2jzh?-()2o_~5&ZU5n%4;a!t0th3V!z+nzso4)tj1k z2tMu|&HJ12J?rNV9hzqe-u{W^lLQ~}h30bve}*42qRoz5TK#jr(%gmc|3>f@!}m4g zei1)+)$+px|Mza1Um$pDf6b>0{O+XWwKURYWz z_>~`N`L_h$VV&mR2!8FSnx~s_dt1Nu|5|fL@U;JGUMhI@x0=@pzWO`O8wC#}v86zu zMesv=Y2G3D@ZOr+`2y?bxrWJ(E$;|E&~zj#1aCGIj17Vh8>aQVB>3?sYranKPYmz3 z8|Q-8uJ*~+@gPWn)f&3z5Gr6 z6=`nAy)FM@oaPq@`Cm@d+>R$(`Ds%$ZxHgoo~n71;Gr_j+XZi!rg^}OEAlt~d+e8* z2L)etx#oF-KYy3z6@us9uelv}kr+g#01qMq#z!=NR6{jGI|KuN$5r_`0XH z{xO38VX5XdfH`7NJlUTVhW zEKgmp`E0@O|5o#bf*+TpZ$#{Sy;jfThIb12+TL0|xEDD`o0Z?QujW~ThYc?l+}&Qw z+xP0Mo?q>%d83e@zMJN&1b^lr%|93XvH_Z>rA7OBPnPDx1i$rk&5H%UzEtxX!LzT| z{2sy2uG4&l;D@=IuNHh~v#v@X^In3D!wZHF5WF_5_2dYieWT{%1^>$M8o{5e*YfuW zKE4MKP{C2^wo2TU$ z3x3cYnzsvHZ{EiX>=RuczBcbu4G=udjO*qIeuR0S!HzT8^gj5Iwp%0QI}N`_@U(}u z{0hNG7`{&M>&^4}{meL?wfm{zLj^ziF|Gds!FM#z#nuRZ=M{1Q0TbtJ2Te>9d|~D6ApDaZgU48c0=EUL*KlxTphn= z9k)4-yCm(~@4j=~CsXOeo!gK9uTN&wT$@fGd|q>H2JJP|9>LNGETcUh?P#tIGNTSn zbL{|g)Tvj{S~g!7@kRPs`XjsArLeN-gzJ?T_N15XVS66OLY$|34A-;U5o7W^t7aja zR3R{lpSdoTg45}QL!DM85|*G2OXq)8Sk|kLxgGT3)~}}jbKKf?X3e$i0V{5^2bx$Z@b)aSR`bZYMBj?Ar5&Nb`!u{kQMDmm_x zDsk@IHcFgZzlxJMa|wS5R2=1mYk4oMaad)X6IN$ryDg6U$|CY9;?$a(sbo>Ead?^Q z^!%|jS0qQiN5{^~c;}iGoa5(TsM@Mg)R?7H zeQk5Xp*DLi>6xs)hYN}C=!6T>op4srabI%=E_d8Ehmxi9np5N~bH=r5@vJl_?E~1OWS)p_5Gjcv^WEoIrV*Z4QKUnL+jAo+U=V|>sTU8eCCRD;~KXjZsq5WTc}#v zaNxw(Nham`OK3RCZF0hWl-BRsNmXZaAa_Jgs5z_8n4(d+%~{Fkg^EHGnzQH5sW`W2B>B|utillKO&whn8o4s7zx8Mjt%tlMm)5MJ_DOqj&d4*# ze4o>ba?T_b!wd6S#Rxi?B)j$E*AW%DBhJny6?wTiMXbWfCZGCc4tk0=u7IIePD4+XTi#tbIq~n~If8}zzEAcTRRoeF1#!nH=S>(?6u4xk2G>+aR74kue997% zJC(A8vSKu4Npn_za>+&|Z31gvnbj+}=ER7K5!qZ2`;02c9!{oo3gXIaLN8-M8(FMXh zkt0JQDxwn-8dJdaD>RB*o>b#g|420V)Y-H$t50yvzvv>NV`07UqMqDMc2dbqYIswl{LxixW!8?tHSf|@U&kV*3f*d@a;xRrX!1HPQ7Wj_;!G_Z?vPLYLL)}! za^yyzYkY~(bn_f)l#1wt6yz4=u!`)_6#Q(XMznbR^lvvRhEri>4Mn4<*!3Sy)oo=~ z+6bfSC#tHg`5Sk^)Md=XLSILc^>w75zK*2o>qvin9Z9npZdBYXRfvQXo{~piF!g6>s}I(HN&B$VHcik-VxG5pN@d=6)51ch_7aYBUiMea6YEVwXHrWWOn z;!GV;n3rcx^etM~OBKAsib^!$;nL;swy(r9EnYD`zM@m#pBPqTtuDvSi{JdPw!ydS>_9T6N?g+kq_8B=M zo0AZvW{b?9Ir4x5I6DtGz~o5R3U=Xg?$`wac+Y<7aT$U1p<@T;PN^sN<_&t8JXMo)5;Xg?A_}+&(3Ni^Cy13$jbCyxr34z zJnizy6=N?744zg|u0KodUU|u+v4bm0swx75$B(TT8yI}iv}u9Cik45+WZcxrlS`(M zz?7*KC4)~JQF!>Yim~G^F;XR^MH9-$PA)O_H{~NpCv%v1%n-LM^W{kM%!EBg_f1h7 zk5@{@0vSR6pPt1q2l-JPw!?YMo1LCGg!Y-!JxJ76A?(_N($*k4z&bbf{A1``wom>3 zsIAJ{r;!lctpAn`p&jWHAL|^TC9+IK;Plh|cbD{^M2zic&>wD9W={ApgtX7HyxGsw zU|$_D|NY*akBs;2d6sisVkXmX*Zh|!$n>8>=f+BZ=5g9Wqy7kR`nj6e^KJg~BP25K z_!K)gJ%PW#CjF1)JB!iR#+^$C1xp6HA| z-dhIM9PS;{6ZS7wgqd@R>bmugL^=9cZxw-P#@~7eL?I`}`LY}M8Km#L9YKHQ@g()# z{+CZXEFUyLwcWHoh5lm6U*p5?@!^Yo_`Af}?tUg?%#?J1AB~M=cL;HoKi0^rYXs@w zB?FjWY(lEqbmEs7{wGro%p+!jdg8J4Kjg#z?8Es{zgT+s(YjdtQ{wFBOQuMu>l69e z_w!xXu_n*X{E|?S=9uEVmLLvgs!MlFDFkokT4@?lB%zyUE>o;aST)H4xg+|oN{rD zqZOs)Q!gvpfD>cKkM9z_NT?>#P{PHLKusPyY0}hj5Yll|r;Hn0LBUoIOeinmpvnBb zxP-Yq8{UnW2pz<;7NHtfC&bW$;&qvmS2Ct)9CKXWgvF z+PmLymglzD%CGd{JT=Cf)x-S)-rmt4>iUFwzV+cdQ~ltL?eb%()~EeH}k9?^QFZ1D5KD^q8*ZJ_- zK75`JZ}8!bK776pU+BY|e0YlwZ}s7Af`4b~QM=%$aAQeZhv2r{uJ_@cK3rWuNFN{O zHrMJguda0AsVqdBl$UGTSv^4?d45zYmYxhBF4wfP`m=oG9Usnf!LjU)@!`cjywr!6 z`S2c*32yVZ-G_Jh@bx~t z(}&xk6O@B=CR3Dz^!-GCN-G^uR@Jt_`<-;8xUf{#W`0!#MUh2cke7Nmb%Xn7% z$k+Ms**<)p4{z|{jXr$74`1lRn|yeS4{!D1Z9cr+hj;k!^*+4Qhua}UnJ>wve=d2d z4^Q*qK_8y(!!vw%rVr2Z;f@ctYdOetjq#B$_Ti;Iyv&DJ`S5BVUgyJS`|x=_yupVz z`tbQae4!6-^5HE$yw!)d`S5lh-r>X7`|wU5ZU?z!c}O<*g_5WG@H8JD^x^3~Ji~`) z`tU3t?)Y$fe=XBB#z(%`hnM>BG9O;$!>fIGoe!Vw!{_<%1|Qz&!{__(g+9E=hqw6f zRv+Hx!`pp$hYw%x!#jPro#2(_A=!*uNS^A$(|mZ)ho}2+yEcrpo9QE;<-;8xUf{#+ z+B4E_v5$PI4=?lKRX)7hht~I;zRK`M!S~|9McU>IexBhA z1;4}aCc)bcZxQ?;9z>+ARqzRhw+a4$;q8L2GrU9aBh0wpdcn&L?-cxT!-L8zeeY{- z5fsT1JZQcb3vTBZs|C07hz)|<`N9=~+j%=XuEoCapPhF!lR8y2Gc=7~4z=%w9+hF= z3Z)&|BJYM~9DU?adT%rsjQ`!xbhDliyOzcO(f>Qs%nQH!nSD<7+1Z8Jqxr?UOsXXG zV%?bU_-(`|3)IJ4M}5?1(MO(Jo2fqPGZ=9E6%{B4!w#;?`M%%lXonG)C5sYL6&zgQ=Yhxjwz#G(ko%hoWtg-Q{A~~ zq#+#2oY|4&%*(4UO>$<=2nH%nr}z1a=?Djco&yfjYkK_EaZ_opRP7Zc({7pGWm=_o z)0}W&wc{4hDTQ_P$%#?X3I`|zDYdB-KsBJMa+^raVU*Ww<7u9|Am8`_>A-XRpAu0H zrPOR^q`ddpQfheOm*0g|mg`gbBTNx;Ym?P(;X=oq8=&31@ROYM?8V%|<(+UZvy@o} z(`A923(}X|lt)+Og_p2~+@xl;RJ(J+9jxW*^C&o!b@{bTm51^>ac&z4ugpv8q>tyF z-k}!tP9MA^xc0!9V$TN2wsqc_xjbp@9l0}CB(0fDMofrQ!mY^6&ZLx@uEF9tz6}S9 zN0e2znRg61%v|fdV0H5Av^rSp)vgLw2kpvWF-w{CHQhr+2a7d8uvp9Y6D%&EDK-3i z19{2jsg;NrYY&kEAZo zZRdJl!E}2)WGq1LV!W1sdpjVb@CpX+@0IM)LHeY?Oubi)Qj3soA z@k!P~oH_P9<``?TIhL(zHDwqpRT=g!C+U-II>U78^4t!-R_$U#xwg;jwMnz-4P(Ca zou7R{_J!F$&o0t$8sn9*Qenc4-S9>=VLRD`>-LKqs6_ikoxUyGe(@7I+z@Q;_N!aH zKfd;h*RxHuUtHFrS76C?qdIjP+kS1O@Fv=?=#h!7(Es8m`?f^;)vewiU;DLpqWy}w zg4AYezqV3%6YW=`{rYj-Kklu@qZ9X!Td4oFmBO27zqWe&#cwNaiT+o&dVhTVuLBe9 z*Ou&mZKd!g+OI_W_2cM&9Y_x~CdR+E*!b603hySgUpy;DPv%tdoQwM;Pw~)HRH^!? z=h-@XGDpofyY&SO!)o?gKAA(4_>m)|O+A&Prux)!$@Y|>e7L45-~Ax>%eU z2|QO%j!{wNhiLf87#e+755~~rGc-Z(7V!6Z^vMs-G?)ix8qI?4^pjeD2T_~oU zl&%lB@GF<3MmW_pIq(<$Gnd1iVtrOB3Scrs-ORgT$t?)p6U zT`J-Y6bq^@`BZMHEUSl3xTuHf=$Vo+q*y<0!h2DVo9IIue%wS=*!obl|Fb6A*F3jV zJ!^6w7kN9a`rBCfluoXwd>WvVZSyIOG3Qf|c0}eZG?}xIGKbHi%wZ{-?PX>3NC2N~ zt!_-_ur{4Jte!F_GT*8+Ydb&iyovEx374{Nre#}H$=c**edR_q>r}uUeqK^f!K-IM zutn#ln~M3ywdkAt@W%)G4z7v{i)>go<>8NCc@KY_p{mZkY=NGR6i+XH}+BW*$rESY||3g)Wb@Z_6M9uQCX?{u}tbB9(i*}8S zP&G-i~ zieIGZVa;13d==v14Q>V!t%P|*tlLK+#3N!oX(hVV8$Cq!BWfjfNVF3C;Ir<3^t_eW zlHp6V5{|k%jMx=lHp6V5{XuVy0K<7WvkwC{6ycg zIjUEkxZ~hEww~N^Y{~E?T8VC2iEby)H=!@lt==DPU*dp7E3qkki7gqvL@SYKCH_bH z5(nrR@I+r?<3=yHWcYg6O7Q41{W31S%w0*-31%e)HKjWGKyz|tLisnOB6{vMUERZOXhOM{k+K8! zJfZwA(xqlxQ);Ht+JS>SHxiO&=B#HSk{`32Vv`1l}SgMECQ#mDq4@aYtcyl_cfUbwPaFF>GXx@pRY zCkNC!?4xPN%=vO%732CZnQ{G>^y};_tl!*JlM5R^razZ8@pPhDfIu}9dMS=BuWrs< z!i7vv((+!^B&|NQ;Yr$JI?-gf|2%E<42i=f$j$m`UZz+|3+GBOQ*jm7zDyv6bQGW#d{IIWSpE zK6L*k`UH3rz2e+87pkss*XCeTW;l~|^-T?=)Fek2I)2-_N);Saed%p$Q+;V#Q_TX4 z^7lVbeZ}0Qu7~=G2HpO~o}T0dk8A~5^KZ%*XvQE8y7EyR{wFKnMx|X|ow;KC5b900 z({meJZ@N_)eFvVXH{8`p)SEy+FR1WCsW;tDSZ!>*=~ii?-gpn&==FpW^@c7oiF%Xq zbfVtyI%@RzT%w=0p+4#lwV&4Qq{hZxZ@N{QxZWhLH++3DYxjv~Cx0mYv~G)dqTZ+} zu^tTTZPdMSqMsIbKaJPJ?9o!tJOtbIg|p{r%t;?1ZR&+H{Y|Z$Y)%PwdEv~Qlou|} zh@7w1e>P`i$}<*n+wf&uuG^eH@I!j_#Vq(-#tS~Dby?z)27ort5|=6d3qGgmzF@2c zpVMe)&Mf$xX2+=b-kTSE&U2Tft3axio`Y&vEnb{PyZP=X*}O6^t@8XZe@~h94b75K z(s_kv`>IY7t*5P4i?$0rvy!VXqM*>ar-d{xa2OB#tA(DMo!)9ONnZ0g=3>u>_=-kN zYp4aE=jP6QK55NG%40RY*maJ>cfsck1gnjbOmhrT!CIj%2uqTcJr%5g+Eu~gB^)tW z{5@sXt2FuI1xuZ&g2jtF+hDO0|6s9XoWVlTZiB@IG^OVEocG};)wD=hzr*R)^WxO% z<&)L4L!;Cb0JCOrUw#wJTjaSfy?U}BT(8CE@@ckQ#WguVgTK6jvuRT4X=r*2gLTo1 zb8X79@`PR@aaTIwB`n&(?T8au+F92(c!AC??M&~i(K}rTr_Y+=XA58b6z-~hQ39rR4~-5cE3}NV6#f?8vfz%E?LAY zD61^uwXbu-m@l-HGv~|vuv)E~njDtxX}&bkIZ^q-cCrSqn;+7h`IZPc}_EdFa*r+P1MS>C|^qZ+TS2Q!yssG5); z_0t8Wd~cqMS>;GotW+7|%h7$RiYpVo*D~YPRTLTa&z5e=q)XO(rY5&yUJ5Gm(Nx9B z=JXAj*{h40l-e8BbeHxcrAAL!-y7YG2FWv87uK!amt732_xC8Ssq{_EchP$J>NPZK zON}g_Yf}|hTN<^(9krvZ$E?{*6_(YBm-Y1W_|XdsbC&11U&U-U`MKH+w3{lp-ury2 z_0z9Mn?^A9oY^VZ+8wQ0I}u5~W~xZ0(T<2+WaWDElNDjLlJh3Tj;%!6I=1~k8ag?Y z!|hEsQ*L~CGjy$g#LbbqmyXf3-ptL>2Fz^x&5>CrIME!bE^&{VqpcXaL~}$9EWi4$ zx+I(FR=>O6(SE8y@A}hDo4GmKfSGN-IWo)sCYqz3H%D7BbcyCD(Hv0(=$3LDyIC`< z_$Hd8jcAVa*ud5ZU7|Vqo|~inzn|XG2Fz^xy(6XfhyDnuL-^%)otg~AG2LiMk{M>;uZz{2R?!eV!3$fb17|$KVV}|b?qs^q$ zs#}JcIvgoAd#H*My#Q{itJ;UixoQz|F=-xIH`uf#H0R&5_6XkUj&6@O`?9vG6)}@3 zzN-T7jIBo?c2KxNi!el%|JBU~Kl-pKVep!atq}#;h2rX)sNLLHz*9x4+WF3}^U*-X z=BnKrFtaVy?u}lKcAKi*TQUlX+O2OZ%tBeQpBYrO+jqKp^VaSSnAw(U_eL)xyG_;Z zEg6MG?M~G0rj)0e+|c*mGvDatU$?E=t?%A=Xr^bsS`tST616+t+U+};x_PhN8!)pi zUAs4W+15D9bxO?-<-U7MMj=tV6SaG5UAs4Wxz%l}c5m#Oy>UcgqiT0ti_V%EL%ZlK z&lqmpa*!#tzKhOwJF}KjV-}r-*)_Z9EWNayQj4HKP@AyC*gQ6Y7$N#>V6WAC+cVt zPS0Yi8Cku6Y?pbs=+$G@ulJb*t?45|BH|EPCU!H#K~J^)kAxw{T-gRj)9X~dh^O8! z;f&EUS*GfnA3)y7+ApWEe$Srh-(mKj#?n4qY2<7HrfICwHGO<)ZHzfA*0LcR+?Q5G z(Ud8E9GidGmVU!lIgwJciX4VjQfXOSbHY}tdg{3g?=4ncf#!0RBhR>%i~SbsOR5Bz zclMtXY5LwP&PF9dUhB9}cQb?ZzggyeXI(a9ncskoM46{W+fx%|{(EcZedi)JW0~K8 zjYOGGlzDSWS5N6|@xILl%Hw9~+ibu_qRc1C{P$YseaEdgV>`bA8;LTXDD#OjpSXt& zZTZ2U@8ce}Q{A;M;UPH3eX>A(nCJ7=ljQVMVVMN;-1-bYW}iCF5=*r4a--^G`V}y} z+^GJhUL?`GSzTWwF(;YjM$H*n5mi~4KFf_dE-lgau34guhDFV)qgA}>DE-LL=}~L7 zg=j!!^DNYsQp>9ar9@)VxLC;pc}^YW}1^ zeYy&^TAGZ8TAOGw$TSsVwUTBk?dH4xiYzcT5GhHC21v1T7fjwA_ueAddrSV(WrrKx$GzhJ*DOnHCAd?(&W`0>jP{n zYH3DV=Z%(TEL`BYf3st5?s8MiY5B{g?R}kji%3s^mK$_Za+X;kS_9CNqCs8Otkz$& zZ&7$lV(P*WV@XUZRvht7TNcwH4SGzP_oBw6^`UOZq*WdA-do8c6=q>LJv5zWR>!23 zF>S(VAy0}$N=*q58QNj#F26rQWWLsAW|nDhArjVv-xv%D~hVwKBT7 zc-Q+80Nct$FB-{#-F%Bisz$-QBBxi*jXm7H8#V~rd7%-(FsnmuOp^e?Fipb$WGH0q z87a_kpUolIve@tv0{jO1}-OdT3)*{pYK$U+qm(jb4nU*0>%9 zDt>^q*DMs6sJ-8Pw-rN?sJ-e!wwdlw{A%w$+g$B6>*^(H?|0vA#ZV+_Z=&{6(-MeU zd_9hyvsu9}QG2QOs>hAglDXgaO+=#hejl}W-)-*NYgTPb)ZXvD+lrw`)ZRqx-I!~y zS>!BHd#U#7_a?SRC^oM4R;eMJC(G1_{-Ik}{p2rA(~TiW({u%VEL^KZ!g899*L%}P z?9j9RrUrq`DM2~u)|BslnET~B-aMQ7bw8SC!}Z>1fNA4bB1)-!m`XAa-06ukp76Mh zzH~dW#_OWZNHr626;t!w|K^2pk+;{WW2I-ox-9a>zuOo4hxZPQ{O}$v@)i;6I{S5p zncEw#242m&Xll}nr-8)cTxJ2Z!o@wGmRYHSQp-QmN!@DJZC2s>QqM4k^P6nGz?7F+ z=!|{>?p<0hik~*8?9Oj=+`@U3O)d0E1Aw7A8d@(Uje0(d_oC*r^r7zOvkKTD{Q?KM zS4`iW$a-f{lUdqbUek*6_Cy@J&hSDg;${{#>f%-HouJdFPXj#>{Jj5?L) z8(bhV(jLf`f)_2@n3m#8YAL8(abpy{S{ai~X(M6`ZLv!KX(jCf4@@i5Fs<}=E`G*G z`t?tWR9;vuAx33b%c@pX%%qrw2{xt{!%Pa9P?g4wnoG%VqIx=;pDs{8+{iyQ=38q` zi{Qri!FS)~!ML%A%pSk#C0HcUEC;qR6%LXZYqDs|CQ#N5F8tk00sUy|hVP`!53g?U z(o)-W-PlqIOwc+NTH)v>h+pZg1D1q_RjkB{0vqz6|u35(~$vWqIiOE@Xo;5t3eAohG+xuj2lR^)YbhPH-p9PWaHU)$;4L*EQ(yR2&qq9A0H*6A zGBfG|6@AI6Ivn9;*!}P>X8;ZFa&|c3{Ax~Bj29K6*Uwqcxny5dc+VtNj5D>FWVUTm zGGmN0&HUaHDvLRnWHaP+;qpSqeLve2@|o+(d`dVCL2_jEFlhswPo>+$dSeTC*GKQ! zg8vw)w$EdvDvx1|v{;o_?O;mHqvV_}vQM(0$j&WOYE@m0EJMSkkJ<++fw3Qle~iOt zi`_lOUE$}JDt0~bG`if(Tv7HvTWHc$L}NVhohUSMzOx2Ra@V$M+blG`qiTsllPEMX zrtr8}_z%S<3QeNW_!S!8ahOD*Nfeqyq2U?-z?vUqN0=Nhsj4U+JFX&UY{l5@ii#1V zhL`4#tswp?{k;(5c;3ZyT%MFWr6L9$Su%Fgs4|}eIk655pIkPnD&{HbNbGY;v&+k+ zLwOg+faGNis=f3%kW&`pfbr7rNbGY;M~xfX)yo)=yo^D$mp%t_%3>TaUiux0aZdTf ziz+KhQ~`+gR3C_u^ZG!n+}B5ZN`QATMvm%(ekB2p`k&jiMCj9ElnQ+yR>{yud`gFR zFh*|cgMOt1j{2Y5wY2Ed@-FU@pZY+oQlpRflpOD%e@W1_(Z`}npFDT?)F~AuQz~Q` zGFX-%gS(U;!(){mI3)Am9Ew?T@R;9uqSTmEWSKFzONlW&R(XL#GRMuKm}Lcz`JE@q zi8)1<5`(*x5yNAZ5IE$Q$FWL>In65@fzyT$KR%dVcv0n)ipnGMCQcb!J~cRW@G*mr zI($f_f=3V3-|1SzTYvo#NV;NVAgL-fX`g=EC(ou4vUK9y5#?>~?diYlT~e>>HGJp3 zy-uIG{f&L=xBFF}S-pGzsV|8uSw~3*0!=jd9$WU(K30~mcf7?T+n^-lljU`qd5cGH zrxuM%b^yhLw|Mjx7}?jiqZ4ArtZq9Shv`Q45Z{=+-rLGH_CR)34`c&0?9W>~J`C!C z?3f6sNm>r^ujw)yKjcg{Z7Zr&w%69hRkSW=>dLWz3`e=(s@6aB|7WY8* z#vaHnG_o)CC8uMSvl)@Px!TCi!uUqj#9*;P=FAM@&FRS#tE>49u(4`jdTfowWo18IvVZKI9sQ5z~J#T{t_5`(X?rS!R1r+mvK`kPo{gRz~CuUD@q2RHlpzGX%%C~T|xqU zT1jcqgz~YIOLT-b{r@1T;BJ~}f+7mD%-(+d@(yg~(iI^*9VHnbYzgYlUzzD-??jA= zsRjX_`{eBq`eV*T9k*g(*B+C$2GIf5xv}RDp>x^3>7vn|-mLv15g2LzI1=acGt7DR zVWz0BXd47-nZvjevV<%|MlklR#R53yp6y8K9Y81n)q1e zTy3etT%$Sv_*{Fw&A(lV$=DMLH1Q9A(Kc57@BJtJ=kL;YmM-n9C6kT+{fKe%A?-7Z zWxp&{pMQ2L3BV@p!=b2Mmg^Jv^t&i%zVz;r3;ICsn4YkIu_Dae1Xmd89WWQoF5IR* z`ath4wKovhjYM_>Kh?c6u@mS|ghVMj*N0!}!+-0;8+`cvK76?k|Imm3%ZKyNgvLtO zaX$Pc;v63vmnM@%7yHQ1G4gM4MWD^fKkFmE*2v##oU-z1RM=zrnM0iYId6|>{g?X4 z-(lqU<{t{8&FcS?kNj6g{taGyALP@i^2GAz9OCTHtp{m&HI6{L)jskM8u?2HYI$`Z zPrIvpZyOaY-{2$v7bE}L+0pX<^^qS$ zO(FZU(|KB6jX%)txx{1nbA=DT#prpxSnE;a4Ya$$@ChZ+{QnI9Vq!Euh%Q9zPtBBQ zKHBizr$zJY48QxbXuiZAGHhVSz0Xuitub#tP5Uup;0?u+v@SNG+#dz9hJZr5Dhr_ye*;ZNNa&3|S1AMTFk z&l!H_z0rKV;Wsr#^8=}a!2VqKU^E|X_!W<6uI`0t_iEy?%E{e6e5uj%@Do~(x<{qm zb%tN^$7r6mv-W4;GtvAs!#`9j*_(S^68WX!_b!W;zt8Y-E2H_}3_svS&DH%X?I!J_ z?T&m|b9E0%yMqj$@=7!xWB3iLqWSfP-}ibnf6DM>Z$$GC41fC{(R|Mo?ax>3nyY(6 z+RZlno%b|X_j`XXfam<0|+*ZEQuwlxgEiC)1?473A|->f(}$2~(?z#+8fTP%Cw?MB~t>E$4;6wbzHZiQ`l!tz~rI{(=MM@R5rGJT1nBwDP@%v z%6&?5`NXPftYxYd$u z$;Bn*@u)4QM;NA+#jRP3$E$dJ&lHa@i%0P|o|fx@%QVVRHmQWph+BCN+&0SN^t^Oz zIcG{Y1%m{}ULGlNw!U>!EGAtXug;#8wRpAmxTwXexThtpCwk+|E?v;#RlG6ftcSYe z^t*K0xUtdtjqR>8)t3GUR*cOW6{EqYi3;1cqxqg1beNB>x8IFSNv!6%!awh z#HHR|J>n9kXzKPVE@>Uj9-c6sx?pj1TTH3L_Bk$}RQ!8p!?dMwsh@PQu3~WsQ#5-Z zt)tn)6ULYI-~>}PdwNDZHpb~^=6qWTo**(Ze(eN^Y@nquisES}jA&e~gprN6jR5g@ zYOfKGyL~|Up6HLGZ7{0iX&H=YT)pWxP?2SE>}YZ-7%R{_l@@MiT}J?9(Fr(wFT29D`UqIm$`Sf0lLZNAuf z1?D`rX?YRI^W2){Cz{M-`Q3q^1-w7-CxD~fHsEMCmB!8@{xXt%}fFAVcW8mmd zDUGA^#&N*-*8|7+uLO?qAIT2V#(MUoKU*Fq0LS>Z07ttY14p|Db7E+d`TiJiw7V2I z+C7*ngVDpLte+gK^#iIL7%%;G7g2pKRdh&nUyK zUF**n!EL@@2zt<;$-vQ{4&WI7ow?DZjnjqkKNUEiYW;r|I8V7-{sC}|zr$Z>WBq7% zGH|qeFL1Ow#5@ncdeCk*aI||bHwv_|JjVGM!)=_$D=B(zKyaJy&x0O}^Iw5uoc|9v z=6lj!+K}{Tcf+MWX@c8)-w*VlKZgTHe;R>f{QnFbl!}`(gW5ChwQs8L!;2oHs9|6J?PKFz|o)d0|IJe|1tjgz%l;U0mu07 zcA%#xNV_)Q4*`zxuLF*D9|n$g-v^F%FFh!>-D`lO-8ln1d5rVJhTAwlro{q}3vToM zkDv$R{4#Ki^Ueorg;u}KuQbDZ(P8d_*|-e@j`v?@1IO|+5jdvnTEnefo385xxBlD+ zdN5sg0mpQ`;iKn$kjHfGaEOirrx(-p8{k+To&k>K;lIGKJUnoyr=N?Ajpq{JSRRf_ z_vF#;7~p94x4_YEXrQMD?G^$@yBGb;lgIq3GTi3ZN-Y+cA-FAv*BZ|8#QeGmIOf-* zz_I*13w%e?W#iUrIJ52O&-(LM!EO0@!*J=(N5IjaDTk3#+E^are=~55|0}>T{znW_ zYLp(Xg4X}xz%l-h07tv80Y|&3hkN?b?iAo?_bT9Mw{Ebfhhu8}ywh-I9OpN+Sm5`9 z+k9^XJ(%wcf%gObe+7>Dz8X0C^KZkYKVJ)O^F45cc39?nDsc2?4seYB)4(zQUjoPY z7iM^Nxk<3`oCqA_zX~|o{SR=od(03|KiZuG9PQo%9PK`Gq^AetyuxrB=TEg*;03{L zzP}85FyG$>j&c4PcnZX4$D?BVv#;UOp92K9`F;rKL4S?`j{YnHj`4p7_^#mRenUM! zcLRPUaBh-qzRv=V@&5`q+Wn~+z~OR>b}s;qb{7FhyDtJqyQ_}z{KtI%$Z#8H{-JZ; zJ`vpJ`{$qsrb{m1C-M%MzdeH8kz|n5-WKSOBe2n2X&iq4j zyqzGp&G#(OgK;hZj&Yt09P@oTaP;R_hD(2L7JOgrW?&xZL4O*7qd$YP^od+9p=JUMPr=>m@B;eOy)9@>Fpc{nTN>Cb?; zl>o=`uoO7j{Qx-H-TzciKiZuI9PQo(9POTVnx_Zz>q5hAeoZpv@FKx&IlLJ3V18W& z9P{hfz_I)^07riwH(dJjN5O6RSqys6pO=86KiQ5K2aNv&;28hAfMfh$27V;u`@6s~ z{wL+ewmS|u+Pw`p+INr z`Q8S6M~K@;hBMoa{;WTr2yXNJbHk-Sea`UGi~d{=9P|B7;28hcfn)sp=6U*u(ynbk z_Xdvf|0Qs=dmC`H`x!4>R(DNbi ze!!DPcz$BOrvgWR4m4c)bEx1p-v@yn^k*1w^ye|)82`Tk$M~n5>G_ZGKM6SBa@lx} z1dj241UTA#4LI7}sUWu9{bw>wMgw{f0t@_k3aZN8@%F6-A%fn%JH z1D*o$ae$*g=NT^jDH7b~`*_fU`Cb7W{aFJ%75v#{q!;I1fu9B(^Zl2=GeQ0a;28f8 zfTP`=M#Z)}95~u72aa~H1&($P9qsAIIG<>^jq?j8-%k~MR1$%w}T$^=ON(e&jE#A95DW;0>^y68aT%P5#W5wZtMFp;QfK`e|BuU z!-1pSD}kfkKLAI&i-4ouqH{dE80SjEZJggT`F@4qHs7xXJs9U%z%kAb0LOe^2ps)+ z(QxU{Uj(=Lz6$i9KdXVGKj)6|;(+;n6>yCIAAw{1KL9=q^7kv?82_`+jcxZz;Ar;= z;Ar<<;Ar>%fTP{K^F6y5=Muwhod0X`{TG6#>C*#KK@Z0HI^Y=R)C=^9R=>@!eGKR8 z#m@9+>&tN9y8xd69Lvw;z%gAn8E);`bloEOzS_OO?VtzK^#pKC*NGSE6Rm!$$1z-{ zs|+}%>uumX6lCMj`{!Ce>%sDHJa8T|vGS=!p8W2>(}81o_$6?(dmC`H`wH-rLI3^5 zvF$zs9PRFLktdJ&HNhY3Er%z79?Y-Nz%jor1&-yX8aVnh&v5C_9fI3@ zzZ>+RKhFS1e||Pjr>_@%VEoSnj`6<=IL5!%cu&usv}?=5p1?8w6M&=LFmSZ{G;p+g zVo7Yf`M}ZcJrg{6jB}IWHqJ+IKxlhjaGUQhf*y?Xd%!Wyfs3_1R*U(b0v!E0)Ntv~ zAi-_E4*@;s&*{L?pQnLi{NDzS@!z@B^B?2?Yv3FkoA37m$M}B%9PREs(bJE19pGs9 zCE#fHE#PSPs7pNk80Rw$w{gxj`94~3oA2j>o}D1Q<-jq{vw&m1-w7Q3dCG9<&ohGC zd~X6hJAprc2af(Yle{=!{3ip)_}>m3;~$*t>De3X4h4?!uK|vB9{`SaUk8qM&z}<8 z?j^v{?y{+#JjVHL!)=@=n0$XvaGUQPpa$o!aP%i+xb)|A!EL^u z33||%cMo-vGz>A9ksiE{y-9z}XjD-(Vta@OMm_@xXt&sK@a-# zC2;g-LZuf6jDHw7#(xoTjQ>f$^z`gcyEflP0`Cv}Zs2IQ893Vg0yx^e{Ib|~UEpZ9 zy~>lvIDccfjkA3}E$MPCZ@JC)zJ_!0z&QUDIL3JxaLo5JfTKU-47YZzKc#}(e4h+@ z(4Xsoqd#8&$N2Akg%=-;e=cy0|DS;e=~SEV{{)WlA9AIqAMKtE9PQQsN4v?>Jw0eQ z4LI68Y=$R~an3Q^#`!r@-*W}G`91>lV4O>UW1R01dTf3@3i3OXYc_7L0mu8VkAY+P z30xIBT|Y5grfYw}tv?5W9!%FUz%gBy`skSs@|dm$;FzxMulD>u3F0saIF^U=fn#~t zzS`4sAn4f#IF^S?fTP{nz|rorz|rn6t_i4(iw)Yn0yx^Ozt)q-a`=GZHow|fA#INc zZp+~lpa=8oCE%D}Yk*_<=>(4cq|Vg#r9XQJZp+W!hWDb=(4V7#qd&I*$M`P>j`9Bx zIL1Fzqy1#N1L)7zqjP~{{O<&gc9#Q3yB`BbyJytKwtGHsw0p^Qo;=3+dc$p;Kjnnb zcB9}n-+v8yFwTDfj&W`Vj`{vK;ONi4443|VCb-S_PSAt??0UUU2PX&psRWMkzZE#f z|9RjT|AXp0{fE%5&G%!0WBjKBN4viRj&}b99PJKtW7|CrINHs;xN<6i_EAV}9Mz%l-N&WUaJ1mI}51UTAV4jk>i2E0G`^WI#~F2?yg!)=_$ znSAf_8!d0S&G#J)m+k6-z%kAz1IK(H0UZ6AV7Rqw{kcSNo9|_y2lL$pj{bZM9P@p* zTfF#S{7(dq@xKlDV2Jahz%l-x14p|*nHSscaNub7cfir^W5ChwdB64aW1OcMZsUB7 z$@eP3)AZ?q8K4K_JO?<&d6Cd#^Xqw#$NR5;0^bGV`6X~HKRezUJ6%6DT&8P);MSjk zpa;`63^=Ci=RSHSf;^_H7C5HsRp3}2z66ftaR1x9^kR9K1v~@d_Iu!19^L?scE1CT zb`QQiw%y+VN4xg{N4ul%@bq9gEHm8Z*R7@;Rtj#*;T50<^Xq2dm|qVA$MUlnIQp~7 zaOuzA1-IqrZP0`Mdj4c_bdgLY2^j&>`7qupBI zXgA|NPd~;v&u|;(k9uplvjn&Kem3aAI8OzRadv@YzRv@W{>(RA`tzjVHs2S49`xrm z;ONi5`@J||{6_%C_*VkQ_}>FOlT_OJz7ROZ{}bS7w|}GO586E*INF^E9PM5X9PJKx zz|)U$&NAG_dHX(EE+n|k_tQZS#(5lYjPnfOnC~|NM}O`yT>A5X;5OeM0X^tXGjQ~0 zzdv|!!1$j69OFL`IL3b-@Z(9P&G(0ZWBlI(j&_qD^!!1)gMp*n3xT8E$-vQW+C!fH z9l_6`hBMoa{th!-jmza|4Oq;qV(+yorc`vh=I@3BvL{_hET z&H#?-y#hGey$d+neHQrHp#PLV#<5iF9eQurvpd3foDAZXg3u&+AUt} z$zz>+cAo`~c3%dLcK2`c^kbZlG2F)a9uwyi1h;X{0zDY#bAe-=?+1?Q zeF-?m`D5Ui-b? z0vzo=1sv@jzcRMn(}APiTbn(3jPv7$+c@*@tMc|o!EKxugC30YUx8zs4{Fi+xEx}7 z9pISWiNGjArA1(xr{ljm8W84mUN$cZ$#JJ@E$GCk4 z{6IR#`oI03Jw2FT#{fsW=L1K(R{%%517G&^qut|xqumq!;>lxvjWXQk*Dl*>xiNy< z{JIeI>_nnAy}tyG`LzN#rgsf+Oz)1Zo}ZZB@xTXw-74Ui-ur>0-Isu)-L=5cZs--y zAGBKt9POU-swa=MSd$5a5{JYT$=J zoaX??^ezXEb~}Kh-R)O-{-E8lz|rodz|roszj}Hw&SArCobB&k%og0nc`oR|I6nd$ z#^+~1RU+o0FHL+fur47ZJvIN z^Zka~IFB*q`60n=oF4-{80Qy(W1Q3e?%Boko(LS{{Bz)#-i5$_2627~IHvb&;Al7a zhUX93JsCLK{Vi~``v7pX`{0|NevI>S!)=_WnK-uyZsYuC(1UUQ064~Z=v$s$Oz*kC zF}+s+$MmiOJ_zF60UXo2*FR$09R?ijo(~-DJ_#J{J`Wu2HoxuJ#W=rXxQ+8H6X(@} z+c>WQJs9U+?|Aw#&IQ0R&XvG1y}tpD>0J+eFvPh}yJr{EdlYcAI|ew~y$m?oeFZq$ z{U>m=d(}S!YU6$s#(B2kHqH;4IL{T_#`#vzgK_=?aE$W{LeIY1jle39-0w|oqI z7vSmd>J!<2j9Wf%jN31OW8D4%JOlK<2OQ&;@?LDaLxH2+QNYpeLf~k(1vuI*TJ71z z{JP9=n_tUJeoYtL=GQf#2lMMz;Fw=;1IIY`e&6#4(;Ebi={*(rkr3zefMa^E1CDkd z1deuJ1devs14p}o4?O?T?i}D4=Z6fpasK;u`uxWPw{d<7^kAI-0vzMK`-h%CnBGj_ znBGF*nBFUa4~00-0*>i@1~}Sn2aa~X1CDkN?(qCayGH{@yDtF8IRDdd8|P0=oIeoU z#(6F1!8rF>@7quo6|^87@*LxH2+^MIq> zUjRqD-vZwe{M>V`w#$q|+^?_xzK`HG&OyU_kr>ALIN%uP8Ne~UcLT@tJ_j7r`#$hY zvTd&m{{fEaJ$Ri?2m6V3PX~^6Cjv*ivw)-ByMUwJqdxZRVw}$~+{XD(6XycKZJY~1 z&rXouDZu*ye;hcb_jTZy-p_$!diVa9=l}7be=u-N?|Hz{?$yB2?w!EV?h4>&cNK87 zTl$ITC&szja2x00CeF2j+c@6sH`{+;W&x8{FJxlN}CTc!O@R})_&l7z2 zX_~JP{O-#%UoZHO>6-6r`U}>d-&~`)BX~uv<`sgEbTw}fJfmLomjvJC*P3?k1w03W5)O?WO*FC8D7{RZ2 zMDrTKAAUmf`GQ~aN6p_7eBd*hCnxJq>(7VJ>hD7ZzjvAD#e$Dpsrik9AMm2)3k4tf zvgWG=pYn?4sXORT>(33V^!H(c-)F|%@b_N%oMmrl`8h)VZ8NUXB=}eDTE0W@ciz)H zl`mYhS%3O}pucAdp4*{$vEWyIqjT{~l-NjK)C42~KmUiG`hTsL=@* zHaTu8eY7}kK{9=|(kFj!qt7(@Y^Tp4eRj}idbsc&I*W9!e2z7!kkK(x6Cxu~P*q#U z4Rt!rrUomEoi8kB@3ITCM~`x@S=Y#ZxJydu2e%#f$zt^}cZ~X|FQAVcw>FQzHP@EW z2itVQYL_(6r`;+gN*;wn)r6YpvyMJn=yNuGw$kT3`fPJ%E@5?niqoQ1F?7OnS-A?s zT&++=f)uJqkU|v+Qm7(93Mmrx%CGvuhsXJsjRbx4z1ou%!)o%;UItAhiVIrV*Z&8sg? z&JB+qke(aP84z^bSDb-M9k<1q*_7nC%P7aI`LfFg)9A}=E=*J?I5c$_kY*K7rMF&( zIb4wLgtMYGE>Z%uO-_ol%w4{aztz5(lDm9qdtYbXBBh%T<}7pEW>rLT-RC{2r&%D+ zeb-%?=YFCNd&fMM=XN-?O%-HVt5~~dIP?&KlxMl0fEf=#Ry8{+7n4k zXT;u4eIXZ|t};I{RHrUxE*|XEb2--@ku!VXce1atx0lT*H`EYa_8XjQR&aqoKl_60 z3olglfT}<-=QLE*O|PZuQ3@4V3Ss4r5G+>}p+&2Ha}O0RwNIsS=8XA)ie0IuL|5Ub zxnWbmP*)UB3)ShTlYNhULyD+67A|z^ugGk!8PG|Ryg9VLUhk@X<)7j0z}sNo_GKy0 zzH!_+16quH6UeB-!@Rz*lV#=`nMROlfc<%}KO6SzV81%DpEIC}_nqdP0cCcl)b149 zoiTQ&!0tG9C(G_++MNu$lWuo{b|=m5q}rWiz0+p^e`c!&CVL_8t6f`EJD8}ro3d$( zAh#rZ{q??Aob2sa{zPQ2(rlFJ#>VQyHe4T}lC?3Xi?W0kLyb@KHJsWjA*5?W3MH`7 zt9-Ndg@i71)JDwca4T2S@s5cijI6qvdzPYd12zjN~aYO5=+OCgzq|*yd#hxV9 z=>^DI#_QM2bq?8XbSwF)*r;r$)LcgO*|&ahhMY|To?g$h?8Tgo-L~RQmnsR}innq@ zO09SsC8>p4_4#}{eX5wMmg)O#yq$1_MPZdu*R1zb5y{w${`b(=qTa~RFVmP+|~+?a*)7f?i-p4B1O7XZ@P zHY>O`sIEz63msb@@x-K%yKiCoqhed@s<6zUi)TK!?@e@| zC0v>2Rt!iBSEkZOYPhnIK9a+g54l4J=*BA7?G(<3I=lMSMz-6eU+qqPK8K*))YO;3 zXVZ(M%3nsQryy5S%nMuG{1ue=CQAHb@~+KfxOP0B`h?WEB}{F38l@!=u3W6WZqi<_ zh8v;iOzrJ!*Dl@XdU_X1L994ak8VdT%+=g<(DZFotRfbkK^fr{D_xwZ z^fz%ia{u$a6(Y(TeVvLbu;w~-w!PB|(zTVk`b}Tz+oyEqhGw0qe<+8R# ziK-9ThAr$Ot!_zM>AsBWziwo+-IdLu1zh67p#?d|txuWtJ`KUGO`1*lN)>}{LF+>c zC~L_B-8Ivq65IP(Q+kh$?@h>_jw&-cpY8RY=`Pf6Q+4b$GiL zwYPyASH7gFTQ52#FWlHk=h*wvIPYoon=I!oo{j@9V5C0qp$l=5E{Mq$qKJ|oLr<-a~7_-kKL^=Y0M4x8NgTP zxzx{Ua%)$R9fj)W@~-3l&7K`+n(#iIOvJoTHyNc81(_5hgV@!XGu{fM)OS5N72hzn^C#V$w$UeVyx*#K)jpRd-k3M3J-x58nR)$O<>-(4-qXb1-Y$AQW7DSb zeLxd+cV{k3atjymUC@4%4YGA)eglP6a<{fo7X|(hZEWp)eTYAz4s}&BPfPMglq9u; z4f+s&L?60l1z$MTO-1(4BVB?5>K#!hzOtQfzuf#))az`eUgt|)Y=+k7ghQ*So6tr@ zw4J&-+=*Eqp7D}9ojS@hTHRp-Lhe%ARe)Z%z4wf|rgM*p&iB3AdyR(I%hEop!m3&) zQ|%&^QFZ%bofwNn0+3+?;1<5+=C_3_+rs&8=_H0avfWQ@V<+9;47c8J$KTCu-1j&J zkV&!Nd(5ZzrlK1wYFw;$rTK@hrGxKBTDkqMspbZf`}5qw*ye3!XYZ8S39*j2p|@Dt zvu01Bi$ZJkMIrQ(z9JO9v}G;_H1OLyw4j@@UwfTY*9~)V)F)AW;5+MFRbKMkHFRk- zEr`6Bc&E9`dedDqr9IM3@V%VUeV7*|$Mx>RVn%N5rQP3-^G%8lxGdD(k*M6WRg5m_ zJeCwAvuL~`6UHlaSAFI>TcEltFIQ2i(AU$M>yx+uO^#JeQ~??l>qw#iZ9)NB(?FF} zN1rnHgpDpYYmeLLQ+r&vsM+L3n{MZ3kDA2gZw&A0qf-$@@l4@D;gd^>;?+M~kt zd}nb$@2nlY-F+%u;U z(CCgn-89r(NKqtT*_TDh6o;s`!wuDhLp5~zulT~TrCj#zS*Q!dbD+{sYq-9$CeQu% zriAR%Shq+NwI~;O9#y(P-D}3wyVsMnkvkf4xe5=M&Qrabjg4Fm!w<92rg#o_b~I~g zE=1<$t~N-@qlVzRxF1$Bj!W~=;o5X|*rh4yaBYU(ZJ^yuz1v8;S$cOq?K*mQA*EcU z(_9(X@c6aO7c+qx=gpnH5L4Kra$~C`tGjA{ShnifWiRAuFRxy8X@^2hYxnc*15%uu zs5dmby9pJQ3phr?@u$lr^}y!p`0LDRR+&hg?gCXNs@-~>wUIFrm~_`o&lVdaX^M1y zB4Z@7$3#8L+0?V_c9M>tkucT9G{&mYxoQdxThveycllU^`W{hwrCh^?Y`X|jU+%PG23l!4%KkxQX+GX>r9!IsmIxD zW!iF$oG!xL$VXio%;Z>?OGE9oG-T0DP*Q5tJwf;m&Y!&c$&^*Sv%`e}XWWZUcvPB` z@+f~MJ4y7>hi)-8FcOyS>V_dyyE;21cSScn`IV~mGjh~lcicC){r*zlSlaSZyPhxE z_4zf9Ti>Cs-cRa}C44~1#@S%OXp|E+y5_PHwi@;9h<9&cyvuf9$#$Dm;Hj;niB%+$F1dkq3*yQ4eJu zw@giSQ)RPS^^oiVt=x-+_6wd$a`XVG%6)NZTf1Mkr*s#Rm_2yUt%{!sQ`C(rP#K^; zq5|MeoY8|z+`0dM_Pzx^uBuvp(zFnuFaZmchdMx!R-mL%poIbn?Z647P@u)CK$<4g zCXyy0nLvsl#bl(%VW2*u^-3`+c>&$KL5D0o{onC_8u&jA zyn8e-yQ-m~F&S%YN;Jfl)YT`->gyY;tCDq%4YBJQTkC6LiMHxQq9(R1v8=Iqd91!} zSzR*HP_-=4(o|KQXt}nkzOJS!nNZI(*EJ*>l6A@DlDo2GGO?^F8A~?C=ETb?DlVQ~ zHa{L~ZK%@x!ST3bTaFVt@N7uLPAxe#*0@B85aZ{q&zo5iNi;V%HXD^?>1Tw}H@7y- zlZUnCo=ePqO0v2{i@ksPDKkUwuSSllPHah3iyB(08<#D^-7Sgwgq%;TZfr=(`MTP= zq}D>)vidU?SG6Q0soG?+>5P+3y6(E`re1f-)W+teCoP;?aniJzGiRLSX-#Nbo9mQh zR-T!sg^Ck7fA;J%Vv`pvZf!`mo>WoSP}STRn?Cilsi&MctyO`iPSMxkI6-m6bLQ7y z8YyTiE;wTR-bM0^@R7jfC&!)guk3*0PmP(q|6XIh1PZD*=F@0K3CjjuGx++3jOB%LjW?zeKb zbYp($#hb_WBX8xzzv;z~7ydX@*U`KG`(FGBCVsjQ+4x2kt@2r6;;-iTF-yGr>rK3R zjl$kP<}ok+RuiwDsj=}FX8C{H#GlCgYqRlNO#GKQe#{p<{x6yMNgQ9P@1G28Jm#C^ zm@n^}*z^mpC}`j7)^VA!w-pw?HqPTU!_fYl++br1DB$XiRSzP zoCP;(HE1N#W76&OMZ`P`9XJ6e;probbV&q`)%xW9k9i>-n1++2Z_LPxwfBErBI&U5 zTWBT%LQ-M@bDlj_2afmQmYqGHi+CGm(gt%sV-oh$ik%APm%qu}-{ez_U;ayRZ&3L~ zZbAf(g@&=1yWDHZ<@h3wRKp z$`Jg<5L{*zgZO+S1pjde{?icrSl;0c4THTVl=azXX!5ce~KKPiq2$JP22`K!TyZ}O}9 zh=@B56-4Ab-^io-R=_VZ_)_T{;85*8&aDPNz{ojQ#YQ4)4E~~NP*qj3 zgy46D;2VHT`K~c`tHvNGvEPT#9|%VsB>&V9d~OI{AA-9fcvlGi!w~$xLU3u+gXBLp z1V1kXuM5FfhTz`_!5<01Ukt(bh2II3KLjrg!D~YBn?vw>Lh#2!@a-Y^SlC&R{IL*x zRtP>n1g{UlJ3{d9hT!7IgXDZY1fPI*B8dLv5d5+byeS0#TnPT%5d4=R_+Dslg5>-_ z2rlhY5Pe+;?uOuB3&FR9;Qb+Z6zzJDJjaLN7lq)BA^2?}_`M2nJd+OiUL!s>Y_P} zyUZc0xKQ36g-n1nHdLyNE9Q0PBC<@)5`)c>#x|&ZNz3w<%Btq(s^ub>B9-&CEh?cr zHgc0@swQQZG$*t~k`qo$X11GTir33aHg;(O^EM{By0HcfmM&>*PBvFHR5rF~F5X?j zSJ^{j)AIZhwz1#{&0$6-iFxV#LQ_IF)YK$vDr>76YU%@tFhSHTbI+J#AB<`UjFkNj zz$ejAGiX|%88{^n43>_W2pd3>I?T8?W9dMPQ#mAO@x9tNVjx~{1yF?e}3H8*0_rUee5t^swZRV{H0 zz_)HGG*q>?K9ODR7=T(VX3#8&$qb&M>RMEmYLsM4PI}^1v;)#^z+S2v<###N^^GkF z<!)o^tw}&LjmsNUwKrbh#-SEVO|7m?R9~&#kHlpCMzXfK z@w!USb$Aus-e29EfV0(h+?-gFKtoZT^-h+m3T26UQ(ISGlgFVYkpz#L#)f($p_eh# zBd8|fcixt%c0fqlWGvLTUfZJFto6aV*|S_s2C%fZe6U;~K@@ayc~gD{5vA_Nf?Qq( z5hp8UHCSC+mKVwT%Zr3+^6ymEqKrk$8(7zE?o81m>!PHV=0tUCb4%T|iTdS1iPhED zqfvmUL6NW_)H1A(w&rJF=fZl5yt_pM#*5^u2bpZE$%k=|g>8cVgQBclEFNR01fk6@?3F3eb z%C+zd+;H3Sqsp|V9pU|T~82GS=I_GQnloj^}*-` z;~|4EPbGSg^)kN>f!En(c(V$hZI<$=wGw3cEzpx29Z5oFr9Xs&Y0(<;>zpZ5ZBOcM zQ?;s+NwiziCxptR6Y8lYj3D&x%BvI0D+j8Q%D>6e1fs1V_La&9RW3Gc1{B3d3!Xt< zpgCcmUtwz`(OfmtX5NEXE26JJa(Ad|>M4oRpK@UlyjwV~0$XL?)T*Cw=%VbIsq_75WnleycFRAtzZ{u>8M8xGXJL{2{_+4cFp7C0v#eEdCpV ziyp89;2kd#J@eTc>nn2j^)}w%avPV+@gejjA@t{k&|errUq!gAmDqe;L%2M{VDXy? zXFIvohg&-thxHpdBwwumsKKq?>>Lw+^H-L3Y`RAfA53X_xU5UbA^aJiMf?S~M;Xz- z7og=okLaZw&GLF=0pZNY?xic~zK{6WZxGvjor5?z?4GcKGoNco9_F*f;6nR;VPK9I z2p2gWgWGQw3q9jS#wZ1k8Cp3_Ap9u8V}u_~_z8p`L--8BClNk}@M8&|OZW!}znt*n z2(Ki3GU4rnPa(XM@DCDxC*j8vF6(@9h<;8`A^q=u!cQdJ?tvooQwe{Z=uaa2X~Ihg z|CPbT9?YX4D)t4!PbT_)!ufe{yN89?p{#{jd;T9oFSyvU#m8cuPY%H`pUa2jQIVt%S4upCer8t^8j#xX3B~)*jy=`qKegy{#jB z2H_iskC?_YhS8%we4D`^Cq6TQS^mEyoZE}{VEt7Nznl{dF7|c?ewNRPL@)Q)<7A>g z6QHF(i*UAw3yF{9>jlGTo)5QvlD|bhi}>H_qu*}$e1>qT^Y-|H!6jepx4%yGEYDgW zZsoa$aMr_ji9gHPP4uD%dz2UHkjTUBY%Ss3&uJ(8EZk%D{AI$~KEFXY>wlfWMRv)r z^#k|$a9fYmd)}b4^g9ujV-xY`dh|HqobJ!RY$vA~+;1mw!Z}}y375WuO?MgLOyB0iP4^(u?!(2W$Z?ww zmp-~2U-IGRv6{$TKHRqP4;WnZz<%i=qAx|9mH!!{XZ`=2=()acBb@a=-rVd@_dtV- zp35lR4-kGX;d2O|P55UFE+@{{7l?ii(ccw9|4pK2c^)L3<@p8SapHfjF(!$Y`Zb{d z|8QJDILmpl!9@=&=anJ!i;14)ypC{|^K*o=oc9oak<$?&ame!ua)|sUi|YD^#OHh< z)=oAMJ@a{#aOU$k@sTtwA9?OU4$%YGqqhm?c6nb@L4;m{wjLeq!_8$R=EH40I^Kud zdUTo(xAiD(aDVx(3c))=@HK?9J$%jJetYO4yd3$pdi!w*pPv#v+sO-rvz^#U3Q3pk zq*w;$I0Tm}ZvFEVgZuR`m2gvZa(mJ z{H*?OAe_hJw-CAs~`NQ~GJ`)N52;uK1T=Zb+KM;bSNI2Wu=>``}gdM+bAEa!zp&wgVb;ml_N@%bpyv+~~-LjO6Uzk=x3620WX9`_JEmt!~KJf3=# z_^=)x_u%1|=luRD1b>70aDLx65lnGNxm4k2%VjpV7|GS6}>*ps#&+TR4% zw&IZTVtal+;foMw>5n(KzZ^?K@EOF1<*y{1?YV<+w$HB<&i4Em;Y|NF;cU;*Baj&! ze*2tGINS3a!bOEv50@C+uZJs$p7XnuaMu6L#E0$nQ$)X%()}{gbGdgB{h37nBcf;i z8;G9k#j`}u{GTKGGl~E231>Tho%nElf76FsJ1;mAOmRrLu-;B4ocYf(xL^NeKHTy@ zk8sY{g~Wdr(zfNhAcX!SM1MBXR}n7!EuU`UBYHR$KRMPD&h6Aj;=}FKABmpJ@in5a z1z($Pu`EL45LuZ1V1tXE>+rMmlL`MA;d2S+`n8nstBL+vgG;)SUpuduBKot4ehuMV zzkcB3Gsm>=4-$Pn@p+DLmR~KRfKKwu@*ghB#UVJ$Kb7!hh_m{M6V7sel<)?kUqLvh zyN2*aqW=!zobG1AIo)3q-b8%fAp9D__j*4ra7ca`KbUZy_c()amVZ9smw=bm&vk^K zL-;L(v;2L8vpg>m&gFPu3`}wO%klV_g7eDpBEngos|aU#?lrizTU$@RPdL}p`-zXJ z-0II>v-)8(Q|2K%9`Tw18=2LJKZpI;eTJW=c?j@Yd z_X)zKD{AR?5}qVHax^Y*2!Gb+0S34FOcp4z$a{SFLJdbI`f&Rk*wF?b1G>}jv+_(O zoYTFG@K&Nv5`Hb=pCaU$+u%}OT;JCdJ=gaq31>N9B%Ir?@n+J~um358HzIAT{|gCc|L{@5 zIo)dr=X&~C!mlO%YYFFi`WWF{Px}mR?b+Jle-qAj_#5KGcK8Zt~|^4Naq86o(&#Ag=LuzFrXxbU+0GQ!0kEPf;5Ouxq9g2~SwUnBZ60a`xa zB%JwtFN9Aw;W_q6d~PECTZx|K`5Do(JkJx(^6Vf!ha_(<%v4<7-S-njlobBP55I&QM{$}EHCee#L4t{dXCi(@qw&|7=&g+#i zxe15JDL;FZnt;XcG}pC0+!iU@M%-rC^?RRhP@Zt9RHb3&=_Ip0h`*8c+o3Vwta@z0MoZ!Rl_i!qFxcxql zex*dcoi?@cq*!d)DW^|AwPePrr%a2*N@8;8|IG;H^9Eab6Ynb6zFs0D$;$w)l90q>mk7Bd-6@EJz1-8Ct5s-T5rSPhDCk-poQU8$9mGB< zo!vS(8GJ;52Nh5A_4D>!`?S8tL^x@Pnv&1=?>Hie)HHhFZ9?z zC3r^yBbwd|yUg9HsFk338)rs43c0!P_Ud!nb=zj?>lqHFS!@l`R`oKz41o7CS;$ zWbQ5jt}Gy%_vf?9mLTPQ`1_O$$sYt5#m7$~qiB$e%o-6DTf0yxJtN`)$#kkv)_V7( zkaBi$smR=;HI!o=eM&>hIwUxJ4dt7oG7N2wXj-L@aKB9D<$H0}j<-$>YMNmabFEd$ z)-~+4{$lzupqb*U3lC_&_Ewf+?{4YJ?HH#BUF?oEu)WFlZ7<;&Mr>iXLgs~KD?4F; z^J<*ryi%@px@LD~2oilrVimewLJtX%3ZaZ!_f@g_Qg6qGu3j4yBS%p7(KFdBrIAsm zoThJ1rY)~iqV_zBV_d0vB>5dDuI|{M)(H!?Tsi&fy z8>2sXD)m&JnLeOwTQ)PBv(dWbK@GMdeP0CmanfswD_O!=)0mq4qlVX6&5C2enN1%i@6 zFk7jZ%`WgX>SQj%rm0r@g^s&5Fpn7X$eq2ZTEh!lykjFOk*y+~5%aBpC95KIX7=55 zEQVC-hRpOC<!_ zump#%V7-@{8tHJKMR(Q8(+eh}gBb2;-HoOIFn}!Bnprr;-z9UZ;gYgGH%skQZ{A}E z=x#J!e{!Ly#G6Omhs9LQAJB(QRP{#ruwn_mOMRHgHbfsLnI4J{Q#t<^@?qc4Rxt^V za23ne2)O7W%r#_pI8=2OH_er8)6JBExbf`tjlVK-T4M%Ci#CYO%qz&b%S>UYe)51j zwP!bzg=@$-V^9GCoOABhhH#&%udtiWd50bZD4z|Ve-79=u-W^^tJZ&`>Z(%T+RmgN zYUpg223@1nL&su!P&I`oz2&f>fr7hN4llzmg(mq$LOazj?n**aVgRx3gt0!Qq$vfPEfQ8*}GN z2A)v-AVjss-Tz*>2%ezt5@|CjIo&VIfB9o@RE-W1AUNZ9WHT<8 zUY(U&g8z8AZ+I`s-6$8q1-440&7|ZMQebBBV=8zA=l#}f-X%EtdC!~k%J$7|)mm|6 zn6w>PX(c#(X$NzwC8*`T8PSz0O*Q8wK}vH^Cfm%$>%JM$jhMZ9#@6r+(elUPtQMjP z6R(F!Q57>}mly3g1J5se75_xJ2)3g($rrYl$H}*iy1m=j(MyMil}u7;Y3w9SNlK8k zG)i-)VLP8kJI;rYR_^{Yqp-?-h$Vx21;Y++5b}&z+ zsq8?4l;+6WLD-s{bv(mcnu~Vqiv?lN5dJ4rt)*GDN|3TRBtw8VD}rk>EvGGTwbtpR z*GrTd6k-}JpqkCes#k)P_E6P}kuKVOr9vp~v9EDX-Yt#J`u;gG)LZG!$x;@ehcR!(eQ4L_U7Ye&zR50MR1=hmesNZ zDaS*!s!?)e2dhWNf<_AyvJy*hxDumv-ib!QG<4R*+RlMQnYKBob1+e*tsI&JDea-! z2a?}zlh1Q%=p0C-5g?#*aHL9KItLQUNq(d|2jWkEV!eXuDtJ>7k4DR1GgA@PSnXsi z79Z3-y00LaL0>_XYE2RMsY~t`08^BV>Apg=BO~jmcIY6Bs=;PltQ4c1g#<}6|5!7g z5NgI_vl2^ixDvyTwply!E!V<|Bx(RNE>dYLGnOEwJq$DMv&jdUu|$TO@pzTKn6ZR% zlK;n;G0e9($KSp(*WIOh&gk(DNw2ib%~hTFv7Rr+ER63R(_Xf}b| z_hge-W2EfZ2X#UsP4eq?)4w+jyCTXY_eSy(%1Pe#K>q@d_-N>`3(0q9lUKAR`Mg7d7Dei@ADPJ^mWvZ+oNVe}i?Yso&(JV$avUS&>$0;(;8=986VI3{#b& zCR0_HY?ffAD1De>;=@GjoI#u&w5gF)*r4sJuZkRakz-sJdNA3=!?0)4QoE3h9<8B2 zXx3WKRA}_}kE=&%nBzb-7k93-CnRR%gSuF;2l&r=Umv zZ`d|gKc=)$_2^UjR60*=pySU-RWvMflkTSS^!!P9&}Gh~Sk5Db*|k)8tW4tfcri6h zA47ZLfIIY}ww$NAO?E0Wwetp88SX+|)45SA!|QM z^i>j>9^_fGp}G(MP*3M*I4i?L9_r;xhATrbJ&ZE9w>MP*E0Dy~Dx{u|eFWEP!k|+k zk#l|1vtA{0QR3Q zNF_fDj@^1M@i$=)a-{Y}Y4?BnZ*RZ7YRCSocI>-q$KI=Uj9rED@0V9TQTlo7C-QdL zA@(J-2A$q|LqWBFxr|cR22U;T?w8oV%U&^`bWYMzU2JDv5w-2L#b? zB#ZJKSG|!eDnqas@wGZAZzMPAU<|=F9h5hcrG#>f7H=dcmjp)?nD>BGvjw|4$H;NN zd{qGLvEuWPn);#CXf2z5XL+lWiEqsj6Lo8?(%AyU zrPd*+JoAGvNr+;eEYB#KkjD>@fvfq}2lTCvJ|t(8#ny7woa*f8Ozci(lj@}%W&bu? z*=cU8rfA1+b+%bm?Yj<0ek3b{G`s7wRD;M8?f6WV)+$9q{Q1f%z7_AJh^hIWRJa4>W?E(mj&REyWHen!M&&v?pwC+lN=|`Y2;u$W zm+~7)SWe!|d~W9{^<45Wqlx_6gTz`TxyJxN5|wYH@ zP&D}!XobF4P9b%~P2H+!)@v3Ew!laA2$D{TPfsgnj`8yh2eUmTNFI;6&Y3jX24Xfa z(FW9$rg%|bv6X?hW0t zGRmPW5}k<9K8JFjM4+HV3HC`@@fV6VNA4ZvhSj_!jmr)o6YmssS2B8M=x} z^e`)q5J2gGYEZh?d#mU!!BEn@sk;!u<4n?b6VNNuLYZ#Z@P~8o8r)&>+8{L@S(k)? zZh7@4=Pb0dH$}f_UeTM7ZDg{PbKGsA^GJ5&G-u|v8xKIT6$O8IbW99gx*abYsqk7} zeuv$A1uXw9?)Y8m8OF?lyU1Vd?x-W`r_0xyj6HmO;W8K{gHAEJ=t{o2jOon%YM#rIkK)o?EE|L0J?p7zaF z!&hxJEX&N9G|>Apk0=7`ZJ{Gnm{)YAH61%NMtsh|Rl7U)apd?NrfT2u zE~?tLN*XiKbHoca$z05WP12qFvA9;hVa)rdF$}#Nku{9r`Da@zKnvhFZA} z)ofkPO#!v?m7H3+Zb-HA@_!oT|5Mb;)uvXWr{GNy|HB(xjCgVl4m}0GJNdj3{PXW3 z!3T9G2Z}rKcOK9;J~>_3-c#t}yC}Y{0cz4pfGf-0UkLNnf5yn5uzh<7>DvCUgFvQ_ zP_cZ|M^5`oF~_}edvAOvw2Q%Hd^>v4JImdtgz-7J@3Ds^BGR_3zM-Y2uBE!Fx#q;0 z#FDDk`s9h#jSa~}gM4x{QjXBlM02e1+C=k``o`;G*VQH(V%1gk)vfha$-0K6v1Dx` zR<*3LwILa6ToPNBSk~CQJeF*XRn^xwR#znx!Ug-C>8rZNhS+tDt@SmrL|b(tQDf=r z>z36eBgth=ni}-UWMWxUQW(vNmsM0;JiBauJl5J^xz@yvJGKSinJi1j>gyU3v6E+< zD#TyNG6x#REJ`h2|2$GC!bPvLS4y}NLlmJ zR>@H8qS+VEE013$%Dgl_FJ3l3hFfuaOI>xWt|iuxSc(L$O~fYGC0k-jivk~8Utcpt z6k+vw+UcRiA!I{NFWJ}_L#CD{BEm1Zya_3vTm^ZJeViU%?6Fwc{P~xbpSxgw{IWAV z`qM(?ip4}O%Xj7gbmbSFcp20bk6lzYH-6c?ve`&r@^na*y?@5^(EHDboIiW^8L`O= z7Nc&po>WoSP}STRn?Cilsi&MctyO`iPSMxc697ErD7gO8NI_e1!4c#4E|QP@$P@zJ zQ&Yzc^0C*l1ByR2X7>JjjfuDKed}JCakq`Vy|D1)GeI7iglHLq$Z?m9KrLUo|zEXiG+>)>I{{B2yQ)v_z&Vw7F3urg}xDHZ&#^Q_sI>!HF%&s_Lr^Rid_X zNi!;dq`QYl4EIa@kwG{grRL(dGJc>#hHZS{tu}9;QpUTk)>p;mqq_1ExTFKssY6Zp zAS9nm7F^~?_?UDNUuDDo^iM#P@Z8<|@mWfhzKk09m~7I&!^halZ)xQ%Z@)|ux2{Y6 zLpI4A4$1#G{OtWU|7Qaek`ny**mQ}$I-Ws<4Lk3AK4DDyCv!hz61MkS{0f8l^S{&F zujWuJG0s+g3w;QIp!|1#RZ}c7gp&V#@U!>Z{2vI+r($)-+Dp|bvP7S}|9<)9+Aq5h zuaX`%jd^kRTvRxnR|%OJ&5F2z89Fd;No0T zbtU!@gtvv@ogw(QL+~Gm;M+rR`E+oQJhC@l5I!XYKQ9EY48dzc@LNLgyF&1vgy64- z;0K{{1m)|55d6Xrd`Sp?O9*~f2)-c%|9J@hQV6~;Ds51{jtjxh3Bi|!;O!y!mqYON zA$VU1zAFTufT|vpuM3qtVv5d702_%}lE2Se~}A^2W!NkQ_T7=m9Of+s`p+d}Yr zLhvU-@IQs%A^2Fh>_GWL@OdG4QwY8~1b;9D z|9uF45E|kjInNHkTSD+Jh2Rf_;C&(ZUqbLh&_M{2^VAUhGT_oa+ICTmH_#R}hR|n1 z@Owk>r$X>oLhxdAdV=Ji7J`2`1aA$&SBBu<3cK3;qG2LOMC2{H#4NH@?l?@Hc@?w#b zWq}dMh0J5s%S1{g7(|w-DX2UKOc$wKiW#6}b5%nn2B=L{&525kX)#$AS<;*cRECUl z&8(YBBtUpIhf;~$sHCsML=KpPvy_8ev^MB@orE{#6G9kBCR!p*VLoU{U0awaIyOiJ z$*QHEq5|?@qC*60NL-hwPsr?25NoIrMSv-~pvab{M0MSgx1;TP+FPisjLiDk*MlgEmeNqsj7j7jpu;; zO0qyIuM}GbB!dapC6}WPC7Kfr)xkAhCM%O+1)fx~;c}qDH`Rql+i5W=p}>5=?}qth z%%rKQ;cN*~3BdH=DC=s%aMI23`{2tMCIb;m33%Q+QMiPZD<8r;E0mRN_B%(Y(V-%t6OJvhgpIM zSAh{Zo3(reBp_l)lTlmMGO#yaQrFy)R3u6SpS7y)Nf>X`TzINvOC2m4t$KhxNu%GO z`afQ*+Q>XuNZ)_OAfI+5v8p@l5q*Gnr*a9A2+YAG}t{IIf zLDG$Cfg^>(H7#g3iBwRmR^|~{ws1#AWM64ZT~ta5ZTmfn*5|VR z2Ib7^ev{}ACVH7ilS5AOWQ*nV7s6#D4~rjx`6f9en&mlzaG8_0^p_IO{2K{BjOf2g zIH&t4;hgU822D^S)9oT$=CUpQy$1K|U*^B$ z5P4Yt&l1k^>?cNsL+Du#C4{p)7Zc8USV}nazk_hLw+)0d|80b`y}d;^m*YX0SCT{I z=XB=~&iSe%oYP%NIOpp=!dd?RCY<@aML5ep1M^C9`0d0YoaJ8_f`5l_e5KX1hi3_A zdHziJ`-uKMm_L$3|09>z+ejzjRt_*p$nA)NU*gij&*s|o)g;U6cQ)4kQ;RzDfs zBgbca_-YMDzTm@q4Bkcjj|XPuzn5@McPrsiq?SHmCQn5VClLN=!dVaBB>Y68-$6L1 zdyRC4aM=9D)D`xfH@KvGG=5f|+X?4%zvSa%?cq+MXM1Q7XNN=hGoPqj;1HbU+)Oyz z!^?y-pYh@xaR?u_hm#3s`VSl2>fr@-cO+6v^izRZJ+u+da^6We^WWg(|2xC~8KP(Y zFA&cBi^TcjkbJQ{Pb8e_rx@Jk%i8l)A8zgW6d%4_r51^tMf};GD+uRws|h~|JgnY6 z?&Ck+^w(GT@QDUb5uXy`b0^``2>(0bClh{@2#3ROZ>5Bvf_N+E%Y<_|9w4139KvTh zewO}9!cQf98R4v-8$%G$X z!{OK4EW){dEhYRk+-LRkal$#>&l4_9gr)zE!L1%DOup_X`WVr_LO9ERh;)u|_~k#9 zaF%}&;VgfWaF+j@gmb!25YG1VGU1%={xVp?Ve@;1$?rsii~KCl352seal%=ji+%j9 zK0ikEF{EMje>dUW?);c=&R0L--0tixonss#|LOQy{)ZCI@?S>y45Du%oYVax;WLT8 zmvAnZKM~G+?4IW$Czs1S>8#@r{0#i8{EG-@`I8}dhHx&I|7UPp?u$&hf0yWEl;TF^goUXs_}Diw)ih%f@+-Q|6@$Sia+DW9F@gO5wv_9!dafBgtI)? z8r-kv6w$Mu*AUL~{ETqc^Ir*Pc@8@|D?jVGoN%_EYY1mPpC$Yp(!GOjn}l<|j+4PN4w0u6KU?k>6V7~^2rnc0 z4TPUd_|FJu{q%?6Z-(Hf$>1M{$T=H7E9WJIvp@MF;p|VIAe{B~8sVJZgJco`hosAT zJIdgGJ1-%6wsVJY*24INQVPgwKJjRzF9|qy`R=pZT0ccpPz-{u;v1 zBm8Ec&LxC1pA_K^(SO0<{_?t;=()VQ zL+E=$==%w0{k$21Pm|684#_W<`{jglzFG&vNxvpiQ2E-bD5w-V0f z_!+34w17MKP%^vgfsnggZusVETTV<_$(&;62h-1{8GYq5YF-!$)pbsk@GVAtenRX z&T^hjIOnT@aOUF@&V2rZaL(7S2xmU~%OnmCk#jzNR!;fGfE<2&KH&=xZ|TpL$q^hv z&wBV!2)=~ylW>pa^9h67^4eS=T=8`_AO4KNR}vp_)t1j!2^W`X@%4nWoWCP{f1=+< z8VMYduMgp8`MlrYR!%#AbsW*hi2igReV>uPoahfG`VSG#`nlT2$Lgoahd*!lw-O)L z&uxUWe(oiFA<46a@DCII9N{eI8wR)fv2y<1huig<0%=@vh@M%__Yux=o=P~UTW)ZF zx)=FytDnn=52xEiIH&s=!pB2aYY&eQeh}e*Ae{Z@KBXYSA^DwvpXGCSDK2mb&URQ* zDrk0|-;N{BDOIqdXZ!iE!L1&)8GEQA`WSAt>9!Nj{&}^*EuWVRpDv>R2=Vz2;Vl0X zK0Z4QpJ$0a7Rk!<2f~@p-en-dVdYWZ)5Uev;G(w}el}kpAe{Nk_VKax_d=p)KDQfO ze1?qU>^jTp5d2Xef7zc#jwgu!M{#ZC{1xGB&#(FT*m|_fhg*C8AL7IIe8{;fd1WVT z&oc?Xg5>!K;q3R88{F#O+QX-a{z~F=H{s0ZNy3@_6~ZMAtDkW)S%zZ_eplgV@kIu= zuz<#WHmZN5qj{ewQd&ET7f&+)|Pw?6tVL%-dJTRyLb@Hs#x zzi`Oy>^IIPd=cWT{1*|<^|zI98ERYlHH0(&2MIrs=zmE#r~603Io-DnZuR+;(Zk*{ zNryu&7vX2+nMk;lrNvJpyo&HR;fo2soN(szF@yW<xCzHK zM862vmd^^pS^u9QJWBN6AiS9HKEe+q{6&NN%j@qx+?LlA89d_+CL_7KZ_7K8Z&&Ls7jr(l6vj{(y@CL#^K=_RY_m|gaefTv-&WDH(*WYIi zzAt!>!_U^==X`jZ;qyDFCjkH z5YF>IcM#6=C-!}#0`L%-zHa8T+62I1@w?6S3Ljolr0G`r@GXaGe3cLHn5gm9KK#_f zHQwdJtvqXe_;F$^IM(^_8_n}W-9Fs%>G9z$@6~)_`lNIqda#EnMh~~&%}5#op_QNg zo>i9*x8HeKR#}%wB&If=6w}AFQ%;|LYRQaKPd+6UD^ZaCKdq$X^wUp|#ipG+Lj$oz z``#3WCBYST#9z6C?fmXbj!6H|9kX9J+DQtivRt!=z;NF z>G&=;{?cpF;`mEfK8*NbeD_e;$$Wqu{%;>j5tw37-_P^DJyhqU^W*hzuKk2pPABB0fLDK|%^KU$&_}hD4evR{!ZvxAU*WwAy`|eva1d0&Y>bTPxJqOIz`1cd05hf zz_2YFTh(FfImwd*VHEHXl3hjlV8ePCBLawb9AkCtB+MxS-yCa3(s_E!`DMoX&`HolNWB z(@Mea{7FUGjg`vX&G!rI2jrhEB=6Qvm0bHq6yKvrhDy+G5;Q{><96)eiDKNQPMv+~ zl-Y{Y95=N^uCRq3PLc?h?<7d3OSQx(9-HcUp;-v&QZ4ncIVL1!kqx$a@!9hIb~bul zHfRgDH)YEgoF--qjS6^077Z@ou576eyMVp&-J}$sPH7EF!HS~%1)CAgraP4?GpKu+ zc%Ln-^o@NeFS)%UvqBdG<=q^N4(|p}zzF)O9lY;4w(? zAxT!5Q3%Iq$BEi@l|IrdjV0@=RizxaLNyXx{uGPoa)VETGGi({km_^nny)nx?J!X;d%q+oisIjH}_Ily>3WP%9uTZi2 zy+!Z-ME3q0O#DXCpm+azb^jzlcbNDM(uwrqQ}!G1r;iaMpwuMNSU04{vqFnpA6 z1@>wPeH3&-{7(iheC{`@Rz4U!=7-RKECg>1!80NFcS3N{Uyz)>!LS`t(*Rk2=`7jU zREck);frT#$nB3;QC^HD)HhIPtVL$1KsBG4m3&lDwR=$L(X%|cr?8xHZBV2r}#b=x8AEAE_;h!R$`P@mk^r$ zpFK)Vz~a{bE%M>k&#my`)^BzBaO;P(?dn=2s|SP4eVsgQ=8Wl>7mE#J-JnZ0RWL`X zI+x;`cKfNhud?}!?$k-3(7_dOUx7VLP{Y2Q?W zafpAv;r5r>powm`_50AoTFm!#&A}#&=>_XBwbzC80eR`GQI^U57UaGw9bXrsF>J5e ze&gmy^tRVfI~AME>g~{I8u<2tigcpOFe`I^232gYNVk;CasN=^{@!tawOvX~wj)l@ zcAV(Fa-QK7Y;@eMPC<{OMxNs;3U*@PS5lG2T;y)uib=VM^9Hs&-hNIQW?9m+VG;4w z!;%3yUJW5I;Rh|pS2^j+z+=J6;qq7s9`l69%=oJ48rf8Nj{C|{8{D}ady+NeE4`p& zpN;N<6?@1=spmpuEz<_aJ5=lx*h8~Z7f(7(cRW59X>nwX5zn<>t9+y}xd% z=iTM1(M}Dcxqz$gLTxjydbM%YtA@*C6?hm|y*kHLuiZ^QrdlE8s@ICEzI6}Tc&>Wx zis5E$h0U5*k0)BUwGwUzRJfitz9 zRJ^g$O0M5a$G4y>TkCgy3#40r^GhqG=jg6iO|g5QEbn1SuM428%PDl+PIWO4ZBS{< zaYI^Sta+pF?Z%(MLO0f0PzaHkbDxjiE|WPs3eeTl%+l+G8&+CWZ`EKcilod`zttkU zB+Z~DlS5GJ(4<*uo_3}@?6`dZbNU4Z@nSLrZep>JiT#<%tj-aJacG4vt!|pw-I)|e8 zG5!P38@npfSFS~UbykJn^yZr;V0|C=?{o^@dUWi`+MZvOdlvK+Y=v>d3+2`O%dtF;1!DE&f?l<) z$RxT3T6K-Dn1=dimR#fN@l^~{#ZRon@~SkVQi%~1m!g6hg%0YLMs}suNq0)7)Pn2V zPx)6{;S9RqLRt?~^9E|r=kqd(!g`lnr=Kn@Zr}BG|F4m`!IdQr^DkL9*CD`3G&PRy{K-E9n*PA5C5ND=vFmX5JBEhU!@s)$Q{*NdCdWYTr1U zqwV#MiOR}Ne`|{>26=8t`Up2~k~-w?etIMCn_rrYd49ANVt{yZ1+!-JaH@bEs#B&^ zKzXt+osoJ9ueVK3?)uf@*xgi5q6ZO?Dxe8+6rO6EYwff4i zU#G7O`)++@*!Sow!+x_|;UPGjkSlQmTam5_uFM8YvQL*0TIAgp->mLwU*99=Bqo?r z5=uwh)MVVcW8YqNR#mWly&_gb5)vYLJW+_`3@C!Oe5;%lk%WY3f+3Q#M9LFka#lnV z5~33fk(?zGMBC-8h$JLL6Ah7^B@#r^CyFEwkn%$SMDd;-c%ZCDU_@id7%%mA#Ker# zDY>%M!cEOW5%;Dd;3Y(eMd(b0MsNQU*1e>Hpz|OFnpq`14UfaKmI=zeO&5%u(h(WU zEK)O13Lo?lvL=W?O>}Anu0EE(YEvDrfS6kY{r{v%hQ*m`93cu zkIZ_{?CD%@1uLEF^Q8IShD(#{?Fd4UJ~{QKdT=ti-fV*zg$^OtcINhF%qcuFK=Wf~ z)fJt|@vGTMb)%1$-ABRa&sIBlsxw-?MO~SO%jDe2w2eh@wX$|J;Yurb9yBKTkO#co zib=)tSg>QB3!dw~>EJKjDf-3J?qWlpQq#)Xa=Hj$t|&1`FS2xY5@`lHXcp@~UM_5L zaYG-6C71{mBSE`WAl(|EzROQ~{ zj#@y-Rw&y=G&#zvOp8#NJY(AkV|&ILTW_@dZ}zT?L&oN0?vuOR36sj)H_I__bzh}r z9&ICRGr3*Q9KWtQLFrEM^jOMvRX}|9d;Y5GWeKc%A93{Pi6f}$u6|zM1ZNmj%iI&~ zG}B7{Nur13**fi1{8K}!(MAH3!AcHfS(97daLDxOeO-saj&R$h?tiCxS+l zT?iV2?FJoGD!Da8D(OEUtv;NLR8rM@SFA>BW|C<#tuFcbPnWrMTI4*LWg5|7lz` z5=;FMgK0GTyKonp{UzWC*CSWxwc$c{xg|^9J*|h>#L|)8(y{UYO#jp&+!s|0bMBP~ zg8JpjHcwzZJt$guPQykEl2dbQZ)vf*YQJ%EB>6tfwM85PjPR1F}SF5&V`O8{-rb()k=&(%H)N-tJ+V(9>lglJM)BPDI13d2z3#zfUk4-wG zN-ERYhSH~}Ev$6D0!yBh6H(SJ&o=2oLLj1-!%Y(X&;3Y>Q5cItXX3LUCR0{2Gsb*B zVo<3J-9f4>jR9rL<8N9+fmJU(b`@S_@QXHc0MSYzn#oDJbVB>CQ}Qx4d4Rje$lY42 z_>3O|1(?PJn+$OWmuz6x>1=B!F9}(Iz2v zu_Dv87X66|cUyUS!Xzi{UU*XOK# zgfy@2!>V*uFJOSKo9^N%s*pE1FrPZW{kjbgn0Y9e>-Kx|2I6+`jFPEd{yEC5pqY6E zD3C5tyU$j*FTTAM+VXeZM3>eSPl3kzkNjuVaq2mnbEgl_nCy?a@OWfp>L$389cRc> zCK(l`2^Tzqq8EjxY?P-_G8v(TVLTXv%B*kltJkZ5@D_8E@8Qdfqp7>)&UB|})xQUT02aI&nob{|5b^lzobgKIx3HQsYl$S|3 zopI6Bml(pF_Cc*Oj92%S>YjmDWB;u5q2f?u`~DegtY{UvkUsyPf>s1n=4=((d0$=? z`X|ctJ6(lfI|sDBAGR}$R_j2Ng{l|XB^X;d`kTM6Y*lf?yol6h3{Iskx~UoZszhIv z>Z=+0%F$P)`f8rOa`e?gxx&OfuKXj6OhzYh32I&$Dmjf}d7NpUl~u0~gOjAGs&v}k z7t-e7KE(!|CI=1*qocnJW!d~KxcHD|bG)eds&vM?^;qHUbL8o_F9xl!7xX&mYfQFY z$v!5?O)A98NeJZ<*|E1g9=l=N827Jxx)SdnZdIi35p7qb*PD`*f~iQ~Ernf?zE72M zdW|l^H8F8`nT2mb-Qp2V!HGvyn(tNfS|YIHu2CK_GrPc3o0GW=IkQhD6yf>3P9Y6^ ze!$gJUcD*t!HQuzBZ*gF?p&GCdMR$zFgY{(ZW3>;9CxN^j4ZiU6uecIPTm)Br+-o{ z+3F@tQ)VU<$#Vu+;62&gm7eg)GIzrFH6<)tapGy0kec))?pJjJx`Q+rSm~kaTj}ZF zMU+;nC$Ffrpf^3?fr3}v2`j-;8TlZKo|?#xDNtR&MXN(iaHIYyE_h2=hO1s}lo2wn zOl_3mFs@8(lp!;&<|#F%WYml+QyYUu*8bWUUaREk6wR%JdwSnD|d9vNwl&(_S0oE|4V_7SPJZ*R*<2t&=}N7{E4MmxTM zNO?3fwH1M3yPFB$mC3saE436do72B?&-tKJ8QC#=4}~cbVSe!~l_{kcrQ!BndnMo7 zzH9H~aqYXtMpLC|J+$od8f(A2+o&()rKG<6sz7|~gSb%JZhgm7Jwe;x&0Qb}P6k+U7y9{4zTp{gbyFp?I4f5v8kxvhxmpJ?m$R}<2TKuLtAdweMrW0tJ=KhQOJj(L?C=+oyg?gQ zNNX^s)L)8!-aM~N-YEYuZXWpu3Xm2sD^v2D@Q)Lv(MZSd`mvH>wpkbRtyQ^=5o^SvCUm0mP{17-I z^LlzF_ep~AX&a2Ag52=F(bN~U)puLxd%%FTAkX>AWEnf(J@Qvn$40LfpzI~HLz|ty z^iI$AM$`=r(Rx3=#!KFsJ_bHFqie?u3X(_j48{ zksT?KQI3L6NaO!-on#wlJyKE+S>$yQWk?>@^E~FuVww4inIqT}*1lDqzu9pETm4?c zn%TPk-*T(an7q4B8X%jqaQCYIUw$de!_57la(;sSXYQO1+5ae564~kKS&}Yz={^l< zXn6sb(6~HPI(`5h_hFc%%UYh(+VMfdNk1V|);&G^)>Zv;aIa0-l+4&x)2ArLjs3^u z6TDNs0(tDI|Lb3*UO|tP`gd3qdZ)V2yqfzOC^7sf|Zd&$_WeZ7oUBgvys z*CnLJ0eO$H9img#<4)Cs_nu!5@p>5i-KL%7@7n^7CF2wsW$7@=z?$M>~Fn9*WG-b{tv+!a4X%nvo@4 zs(K_Hhi1Dl=vT$WXUfR`R@G6F7enMqcHlGj;ZYE=Af5*x8yW>~O1^qGyy?*90oF>s z2bc=TPHd>DfIXVm2+7arPX)NI;0bOs97mzbW7L@aA9yYRs{#Irsi`RauwKVKpY`k{ zYfJ;aacd^&wBhB=6-cqjv15rhf#w%SDnOVx@6+O}lKFbfc)&@jdWZ5ovjM*`nNmu9G|alQ}y#Zt399QBTvY_>R+O6KY5#HYVU@+eXbT~7HH83CP;#{c0u8QO%zKuckt#fOiK6%M*yIigdU zR7j(*#>}TzqFTvf3qB3-)9t8X5&1Af?xcP|r)nYy!+J3bUC~^?RE~@{u)Zmanmsxw zi<+BtP!=_}>YyxY_Nky+)ZC_mIg6Som?GUt@(=VR4eslcQI3MizcX$i)7d0*?3qum zM&@$LO1^w$O6wr|?8XkfU8`T&*KSk3j^AH@i)YPt#C4#Z*VtbOF3cw=il%PG42rKP zGfoVx$|{UJ%euuQ`(GK57%ZHw6p7J&)mN+ZmDCf&t=3oirGZqJzFMc_*6OQneYH+s z>1kQKax=7rJP`VO=A|)+YU?v{q{P#2G$`3z`5sxP5=_6*prmr=$r}wyBzImZf=V72 zZ#49(3@V=T)!$^)!DqYDr6qU~979l9g_5)A2VavyNIl1PRnDl?Z>d!8N5N959_)r! z8QQ3Hz}4w&Km6T4Bz4r6oYi}~Zt4oypp+H9QJc41jTds2ZD#HlH;OmiX9ywACFVR? z&UoWJ=1K#bQOq2Kiyga7l6@7vOu19~VyS+ES-IrkQE#;4cJ<*E{Z>u?Cvjm%ozaea zX(*Lh7{lj3Vc__>Z20#G)Mse-@a^{yD;xTFLa#^j7Z`FrJ8R1UuY_+J`0Y0PSz-0& zxw3f8^qI`{1y43nSt2!ee&OXIB;lhA(Cgn%jxQV^$jTRYZ;PO&f^g|N3U3oQrGKK)3c)B^+k1IEDw^wFz^J5 zH~oZHG{ju&MoZMSk!_tat3{LnYOL*Uni{(f8W!2`Js8t$s+RQ~ z`EE+kCmu|V)n5S{S{2%gEa+ze_PlYRc63BqIx^=9cYP5gHD+U~&R8Jn`{X}rnM5mN z98!%fcge`p8O@_pc&J(ZK&!Ti%V*WtWQW-lq4_C>&EV4!b0!t30KV3Oj*8=son-x^ zz{+6pT{qMPBTnv@GyHb%Rk{_*h=q`3TlhUaS`rz_`W%y`8>Zwm0ldTpZcI7p0#>e1!%mKb1K&~tF`vb=eaDV9c z2ae(I4;-T`awy*)K>o~=$~$q7_W=WhVba)SMR$`c)Mp8z9p7k!xf!hgOLAfF&Rt6P ze+Xh^3V_U*4Q=EL;ON~cfTOtpjvjIW*hk6CgBJGNl_3Q%`8q0qBjmzcx}U=1)np7oZ+5_Ovs=t2ty=qo5tqJsa&mP&cK9$sxXI3lkm%gwlU5M62%^2wIs6yJ7^m~TWvh3zOKa0Jl$aG72hS)7x)+m$)mGwxM#$5N0%GS)c!|;VnnF3M&^-Ei7 zreKKu0y_-AU}FwG!wF6m^5EBBrG#HJQx3VavLB$52^=%gErD5I>SS{9lhLhH_87AfPlsRXvb@CVQKrN$IY$t+UU;j48ean9Q zcsKl``z96zN8B;4T`Kkey`kDC8yyAhQ`x?2YCvAk^laExSkJU>iKjU`6gXl4@-wpJ zgSwg#HFGM)cSzC2Ipcw#=Zf&?-(jBi{OTaf&~`VXp7)(moWX_U>c~2m>&lvCs z+d!|7buRXFr}yjl6dsa7VTdxF%(`7rCa%;cvof9XHFH`-v9ro)^_g>d8|5C10c>*` z?YM=<2f4GGWRtp3B6#+M+qq&~n|5xZJR0TdZDwc>ncmbcw0w|Hp8ee^jo@IfP7Sdg zqUrwpb}rlVZ0~vZ&iQvQf>!LD|F`6#{l*x29&tPe8q)hCLmq_^q4FNRYJ&BqnCkxy zy597aN^}Hf94va-oymj|HC9smXsV`DEO%(yx%sM4E1_~61uG#}JxZ!jd$ifMW}M9A znf+PH+$YJisnpez!geh!*U5r|no9RRz&TmmrS}2O5`FcKeh#;%Kz)GIk)((7Ib5?F zExh-;@^iR7@95`no%9-Au4{J57AWa8x?I=n#1%fySt`oKH)wIHn6e#e7Dr${gIi=9 zENuUUASxR?a7wjZ^2K<3xc5%kLRUUBi*0oGR_(}+vGP^lkWb*MCXE_7y(L?(Fpm7E zY8tTXdD96PqbteJ(~&_pl^ByUPrM5sbo1!tw@?4IIi4mIJhPEL)Sj>Iw>|68yue+;z zb5N%gjeULevzPr}cNcauaCcYPavD1wHWeH74LXT6qN5}lc#QPWV6OX;@)}s=lTG*B z^*<7S(<%S|KYQN-UsqABe@a^#N+AWR6i_)xg;r>7OA9HWB(y0fke1LkP<*waO=%=e zVjj?PQG-b^hvR`LR;viN*T=PbL3>p&$iwhV;T9 zAxb_{SoUX>bv+rfbmt!8(FB(GONK9)(bvE}W(WcYhGB2WS4sBPFl;OJ+}<#33pL3s z!!T+bb{Iy@fExzBStCY_YHm1X>y~5YvCg3R3U6V8o#h(hqt8us9D(Y{I0PND-JwH*O$pTBS@=hqj;+7L z3Og23{RPfcx!_M;N;Cf}K@_yNj&eoDo=-i`LBnyTN+M(X(kD<>`OigM3@4b|3AwS}| zvZ;uSk-3WaG>5Vl!_Zz-Yur2SWl*2yP_29zWTGU#*Ja{Dl~D)IVk#@Nm4ABysb7{; zO87`Yb4tlnHrM>=g_-7(PAMrem&Ld&=1V@MB*B-ajo42wn8rm+^gTB}Q`zan!@~;O zcVKrReetLLEU13pC-ru(9J9l2^fLs#uhO02t6dX?AzR>^vJd;Pk3AcXj_K+9(il&I zLAOLHrcwJC+>jxgHh)7*!N3Gl*!~$PjZv?oNjM$2#iZ?4Tw^}_9ZnUoWsvkaa^=U6 zN=wzn@TpNbPGUhx^*-g~{hS>K_xRHUSPq;qJE`Ma-!5i-;*l~=E}-#uY9miN%urN4hto(1fj0JgB&?=Yp_H8GrCFo&AMta!=M97 z!-lUt@&bxo5KZ>O!?OGN1!S{KKReM}(rI9m%_a4-adS!i>{Pzwes(5bX6a|Cm&ow* zkBz9Wtyn$Rw-o<~LRc>}>^$!x!w(%_R%|Yb;e@#)hAYe^F}$2FncbSA<*Z2)x=zu^R4Yr6++=ko@`y6{2+(#R2I5BNZe8?Lx176l75w?E}mMJS{RQw zrwNZIBlNZtD-d8*VhFYDqGf&q`c{>)Ioc|e*z-tAJD$f0OAklv9XA1eo6go6uS_?E z;u^)%{QJRpX{7Z1Jl~TE48wYC!A37IyasLXcbU;@?I7zDX$=2-`>0|1IjBK zb`3JJAyj44>kV3v$?F{m^_>FW*m)`sX7^MscP2TDr=l7~&{Pe9+JxAuyqX+zpjEna zeYPGxUyRO*9tCW$-&0W<8|+ku?ZC5qZ7_#9iA zwg+Lf2j%&w20!$zPDx9bC*M}LFMB?!s)BMeim(@5;5@e>4Ta~^h0j^L-q-Vdeml;C z(S-#)AEg$!W%oB-7im4S%e@5|VX&)JkSn)<-R8W&`V^h>@uEQOsz{ z@atP2MVStMeH0fWV|^5(-ufuw7fuirB4m9OF_(LN6jhR0A4T-d`Y5VKy*`R_*1i;4 z^6S~Af_0&=KGf38R2t3a^!)|*vJ#@O{rZ6obB8omq`DGqEQZ$D@5}pj4}AxWwFGNk zMb{EsmvSf$vXdS<;{#_;(hFwxM%J|7=b((K9->X1n$55O{lUy`o$ArcoL5@yC`)D+ zIVdwTJFDacRP;FCdK!6+0|ppQC~WV5j*G~=C_`RZMTZnJW?pDsI?NL|{X{ghpS|7V zL?+6x!s;3mH^>%^cq}!h3*XWBXmESS=@B_?HrLeuzEU(DR2MK7zaJeMY42uVr zZ12}gwkx=w6qh7{yM5RtDQ0HHB`IQNM~+KE{K6%nLcA`QgeocMl2A3i;V#Kq<&tC^ zO$6%^YG;2)hdKq8NBr~z&+KM)W4E|dprjkS-ay$5a^yN0UYlY3DzGnoj_nEiHWC!z z?A=kEjn3q3H~ckpWkbtZS6_)C%WH*NTft}Bo{23JOiAcqr~b==jK9$5l`m7I=PPn{ z#mN~On$))EqL#yLm)u)C=`>`~)prka66eJ-f#u_0c4%-7oEljUL)_sFaYbb{X8QUq z2Vo`2fWJw1EF>4^aXf^FR-Qr0sj>AiNKVzu@Hz1!IXVaV{9>QvD7ueuLYBpF1S5n~ z*#2I)YLE@x=XvZ@l*jenV;r>@kzYqgCwT|TTn%FoQV~kYhM^)OXdybUku1NX^VV$5#GA;F4suTfwk)j{{kpWvv}*LIZWIRDgCGugFZ6HrPz43by% zI!u!HpI<}PW3cDfaHAOT0blRNtfq?@4CH{XAq^O)Y8)m(V*^!N&-B5l_;jt})ALsG z>DE@5el^!eJ$^9>#zg`KJLtx2X}jux(fQvYG&-g6{4CCeM|rlsxA-k&*Q7T2i$Teb zF$lQyJ%n#JSX>48^XV{@zK=+=#jR=I?J&BqbIw*wu)|QD=`cuM)$1_Xv7zfQs5>3_ zG}K&LD}%k*(0@AvQDGy|c5H#O0Jd^_$vp$aGe>-$Rc{BKC}rB2ycZ(NfAN_ko;AQk z8^lFF%p5V2yk#cG4sPLts*<-%#T~*lR5xe{Bx7@~(cYdpGO@yY1nJ~Rwk zKt$lQ!j9YVy+bhuV~s7KZ&cH_twY*$vh<<8tLUwK`cCDP4}%3){lhR>@O`}RT!xdX zLi$csd&3}kRjM5idEBh=5$=sKv*L~I`5Q4C z@WyEQbCfs6%cFX=wj1kg65cu<;uqc+6(YnNBj&tyJfYqgRg&?>h`w3JL)ECg zG0r)#j)#S~?|Ng$Bc?cTZ>-P7o$UEb-*AZU{H0F=PjNhiLgrg3l?h!%oEW6Kdeb>N zd%oA#Yv;L47nWr(!NkGG%kvFg(HA{44u8UGqp%64{x$aSCrs_da1vCiV*#Z;}-g`(8TNyR}ix?%5H959{h5-_<{^tA9)v=z>ls+9B;SILBQ_uixPpY*Z9M z2f9Um{c)W#2Ym`ujH&0m&eS;9*}BrVg1>K_6kcY zDN~ip6Um=Q=6z?ANs)D6{zoS;^OKpY!uHQQn~^G>O!nbJ8kyL(=kUUgKRSp9iInVF zg|+p>a~^excW(GkddK_Luy^>$9TQFd>o^&{VSv>RzvzeM>lOBSsp8F z|0G?eN|)b9E8E!5d2L7k)wTw?Vw$IUcc_$6EyqHT704jgyaZX%6?;an{iy$3sd!$s zSkg@-Lo`iN`n88l!L<)f#bUYlmAt>?10^3US@D4fD-+4zm0VJCaY<##vZaY@pQGAa zlza+pz5zilWI-{*?)I3w<>oF%f07+5&0T@H zTZ=zssrD&+d=eVd6uO}UlzNK`@Go|@o7=L~XH1%8?`uCIkj^0^BXlA?p&KhCc|ltA zvj|6*ApJc^LfZWp?j;TSQaG&>rP`7m*MQ;P(%UHGROxMJ@qOWq-$1*91_$@<4gY3Y;#MN_Mo17aNNK)<~wvFbq_KzJHvVWv;()4A$H_HW8}4s{`7Wi#UJ1# z{cgETGwPb8>dXf*7R}rq#Rt#IzC9|;R#O-`UO?q%oCm6KQyAW~M|ErqV=I_-Kr!|MJ)UB=vKwmTSZ!yOwrlxzEW!v@J z@j^(u?vFdgr1P(OQkA#0;c%hK+eo>Wqnq-GLAl3n2Bn+=###@QSnECA7vA{qIB_qa z_o+^z2o@5ncIb>!tt1hAgCc-#A#~IOcF;#(#~9T^|K7iIOk&iwM6$FSZKgZXwyQNK zm-rN7Z|QcD<$`%1*>0hE>TCs`D%}pY4A8oXa^{ba)AQC&y7e(~|IV?7 zg{^pFYpQr=+nz%TJ6b@M?qL^-aSDLf?6%+X`=d5X*%PYoMJdN|kOi zeBrLR!ZJnI#g-$VOj58%8_Dt_d+6K7_2v?2B?)OV{&wOo0=ku5pfMFf+*F>vo!)R~ zoJ6!!Haf%6PV?MhV9vYNyP<=pUGuYyhKX+kOQWP#w402-*B+D4SbuH$7%YQHt=k$Q zihala<|&G{o}(j%J2Gaxe6?(n>z_?$V5+SoyzHw=SIoUS<9-QZ0* zS#1JmhVRSVLFuhg3@%A3PqlQrb89jbHV%DO9m`DIQY%A)D53_D?3hY_5U{}?;M7Y_ zmdiAwIh*eQI+o>9?ra?kJ><^92_s!`BH!5Lqjp48py`GE$KG%g5F+NL!B25_c`8jA zmmu(#N|T&j|AQWj9{E}bFiPaZBKU>lAHlLxTwzj5`hX&u@Uy%PL`6m7sIqeUFzrj8 zdieWAMJ6cAbVc0jdrXg++~K-QwiEKDF~5~aq>a~Vv+TPMdvcxG=I*o&#+-W6b2u!A zejru67BVYre?RZvE!|q4Ub{BJxHz?CJ3Aa|JA`)DOvuSU;H^>592S(!utxOtJMQgYD~GOR0DDcg8%Zvx&BI7kt8t zWY-aeSr4(A_kDxiRWE?ZwGUCaDk7RiRZxzT5PC~D;|gQDHXiPe?Zj)}KLiLL|0X>j z_4Hft#3n0GcT>8Od+1e8&C~Ek8S`}?r8cdqmUL6DJQe>6#SmX+&Gzx-yj$v3yu9I0 z(>+2NtB1vxQDUq(+oJf~`v4oE~Ea^s^ z69a}P5vk%zwt_C*%nsePSbblfUbi{InJb1^RHTj^9%uuNLD=9ZyO+v;|TcSX^LK3N)Sjl-~&zK~_FF%xITn~P)ctLWfiBtuJKL%vIs1jqDIWc^__^oVh&B)KV%hb(O&E9Br8fS(|Gn2H8yVJo3qlS329wu!7Aen$)35e%!+ zg1JDS{fgM~){p5^^PY^lu4PjY)t5n$63Kg~lADpdBaT0&XY1mY<7-c3*e%+FbbOiW zpi4AAa{b`0Y%_Oz%-s%i7o(<`>UhT7u`B3J`Ajs=cA00B=?>lM5mffx(yjP|isD&O z=}u{pIl`~KrPQYeA9nP7^r(#VHcn0}OgbJloj$r7yLf1Nq8oYA?_)IkaQ`dkBwqd- z8FRPDi1LwG`vYak?dWH{m(EGdyT9;r-g+dJbV2&mi2+u8R7rgO<7lTGmM$$w{vl%R zQX+-0Y{!pUO7dF7RN4jMY@!nJd#RR99DGiFq`UtZZY!+nKcM_sC0RKW^Y(n~h(vnA z%-%6{&B)}gFj-(D>M6dNSBkLugXi1WwMWe#Tz9Yvu`95X z7|I@K??GU;C#t)JUg_=@w_H4Qw?^X<^atH7{*XkdyOm3HxAZVqG$DT@k#D#02>hwP zB+_Z>%M$60@8_!IMqs@Vd?%bD*C7!pMCIrW==gD{`L2{9u<9|W!=}Wj$9l(@wvCC! zRPmg)J);ZT=g+6T<)c4#DCUX_Kl!%=s_PK?5KPDOxP%f|dW9YLuzzG<997uy4+wK4 z)qu}qwFg(L`AKb}+j@@5$8$-MaD&AEG z1uW(S+^VztgQbv8m_PU&2#y@(LLayj)=!JqDHz6A^w5}Ep3!eW#5Z&Xya9Gc#Zg*u zYpqr%StK*LO3p6stkqg8mvD9|O>V0t3N?%e?A!jYcfph*ViSL7+P_ojZ?b$!Jh^NN zz9Fz+%0vR_qQG2?p<$ZuyE?2U=45`7rRX8@;9F>$H+5%PSz-Gr3zWXTjc!<9F)cg3 z@DqpQKG~B}kW>P2)du97$`KRlK6#*XBYZQrG$^F(N2 zJFS2bDfb!vm+?`c1mNq*+1 zEW-Rei($mSKzmC*$vwQXLelIyW~pYk9ybS?UF<-!8;V{ZE_UdkWz~M$&1G2hiF9P4 zw;uFyGU9Xy3hr=1 z;Fz8bKjLGU2tU$84nOJuWyT7ki1@RF&I4c4q!#4JunIvE*3(; zm09VEQzSH=$Liw3_7~oz`uX4DCWj#zsj15T=bS)uMvo`ZhR5g)@dTR8a&+uw>*FoLfTQu)bbGOIb#f%k?nUx#` z=FTpJnrQCqLa52+jut{?P6AlsIoB~_SL-(FKd=BATSxyQyIRx4o42invp3sXn9Hzj zEpcX=_l-_9m$aL7rn$t{QL}lDcaLr?W|TIN@>Ut>0KFYCi8>N>;Pnq0Ps1wW?1RSH zSih0O*BhxK+qZnZvZM@X%$OzDfYEsx!A+-V>{F@HreY&$u-FazWPPDbACDfsE+0?E zT!R}dQugrie8VkBnP8tU7qyrwIM9P{HzU-ygSp!39M+DubhuA`YdON!C z2RYKh#Z7XVW;Bl_KvGRF!K6JBC7mr!I-J&q7O=V*p#^%FTU*ixBW}(UKQs& zsLP48>FmXK|JCt1I|ipR>`!flIt;za46#2&ed&RYL7F-b{##wWb>b4XX+8aULst7C zv&~TU(2ju~=5Fetmc#{_2iGxB_E|axf_?2X*q9>QYg^BpP=+CoO->$z>KNuo$3R=3 z%5ku1>JB0~=#Ig;f7~%tIBi*Ov?XD*WhVaEj-f~{^Ip-OcgN7>q?6e((8Ju?lCGFz z>u0Dt2B90)G01b1FLeyWvFRA7j(i;hJ@P3TItEJQ!y*+eSkR&A7#=ysj`MuGVGqYf z*I*a2981yn&rx~Ch|IqRDsl}^5r6bW_t4!@E1)i!d_aiev$%y&FJbw%-_7ZcTE}rD zjt)8yh&&L89FjmJR#b-uBELTAU!k?iIfH8DLZr#Nfi_x4@0)9Y%V36|BWrWR-WW?1 zhq{lsf=tEY8`7TM@?@WLID$c@^7I`mIg%Q>XUL8IcoZ=_qrbeT5If-+S!XCSyhIPP z_=Uy#g~<4TxTjf15kkg#eR6B&`^m|p?6Zs_2=+O1(W3}I-AG@~nXw2G-(qHj$*)85++Oa;72l8?!+~Y| zp?xMDSGqq7aG?4Z$7Icc-ZHqyc^KC5Df_~*o7Sj4lOAU2 zGyRhkQ74u~_L)K^d!z(?CS{+c&m?#NeWqk%`eIDUoEGXjQP|VTV^EQ|)5N=?580c@ z8<7l;ycrKGbV_E2=M!_}Z6|JRe0>)#!SFNmr9-dYD^V(iNv!musjaZ$dY_KBXJvxr@9J$0qVdb>x~hnle4|DH$+I8%B(WD46?(`>zXYnR}rtAAowzyE6}eLZ8zOYu(1Xg?ZbEZS|J<7`)_ zQ|!hKk4_ECoK4i{a(q+kyqCo+jD0G7^(bkS)O64&$q#&$RW-QWCHIhL-!k88p^|z8 z<6C}B4|RiN3mE;)A+ZFU)C!TVpAHBSNA3}IcBnZEs38H_ng`QLSB%oF1zeyWT#LgA z2pfAA6yCWrIQ|J%5bCtB{eS5EDBlEUH)bkBoy*DGECzT;$ifJMy~sa@cjnZD+gOFO z+e0{Gl3yD`C$ZA02^%ZlAtCh8)*+dW$U{P$>0sw&cuYrr1AgbZv4T0xmxI)#UGRMV z~wrlQjLK>9bbu*ec(_q37K3+G}RpeQFUtkzmf$ zD#v#s=o`S36X~;Orp~^r|@me+o%V8Ew$+jMb)*G6Dr1_#;G48GRmB1w$RuTQd zhmrAz1=h>Q`SWF((VPonk`Jx>hP|>$x3d;t<{0Tj>T+OZB%GXGmet;j_c}}%{Fi32+X(x83NOmF7g0;^2=2) zx&gPg>eu5ERlk=0^mbI>kFEOUa+zi{XVoJDC02cY5R5NPi22I|oG&$QQXnZx+eMX5}999eCosiI!C(c|d@7gn~{P48r zAs!MyoDb^|0lqv}ZqzAxKlN^@dr2}SukC;$(v@#mn?(_)LZ0SBjeXNFPvpsNOA*}H zi2lRLj#Y7ZWH$_|NUoUq1cvx^u3ZnXRzSaFXOe?1hU`)r4wGp@6n6|%j z0FB922^-hpsghwZt~vVoF38oZoh%1IXY1$bot*u=(eM${d^?>KGMg_w3^ZS3n?jqf zGrZhXXyZN@d!eusJdgyPy)L>uonlJ5M@}ix%#z`ro&;ijaCFB8a<3>tU&`3#A zuy>>%?Cj8m&)GpMATs@@fm(OIole4Gr3lV??KB@j^rgu0R`e)tw32kXc8|gRNs^V=#X>gHpzPhjR2zP7|L)Yn>GwIYT-B>`u_$JzBzznibQ0k;i;LosAud%xCP3UKX}j zzg-!%<#aOwjoN@>3%-D$|2(?Lb&_YD;0Z?u1aCG6)X}gw_n>DVYQz{9tW|9!0bL#K^??EuaAlMN(!+r%ple-webk-5Cql!Tr>*{QWK^PaPx8_oc&sD{6Q852 zELL1kZ*uV7#^;i6(VAg8#7fKnmqt$jk1YRUxZ-o=$3b}hkQZ9y>xj?&{a7NnH{)|X z1)e^j2U{y)yRSPqw*hf&Y_F>c49M~*140i21|)}5=&a*j@4-;G3Rk$yaX&ru7cN)W zZU^2llac$v^4f$eT;{l+9_qp+#XQI`2b$8@F5Wl0u`R@z0SbxCl{OX+5w z3`$mR!8Z;!;l33qFzZ^{$$!(Ot$ED1%2eZPFxyMC*bMmKa+euQU8G@JIL^YD%?768w^j1SvarwnBQE@X- zEZrmf0wnZM>t=a+YmR8*4!mI+9$S^2xHW3HaThL;-!t^*b*^jA&o4%1Q<0f1T&7i_ zhyKEK8`!6UziMtyTWB!6?&)(=UJ7>cmT~cf7q&+%aSZsg+CE(4U0`h=SLO0 zNsu91E?43mp_bFsOmyY;nHu7dO${aRnHr9@{8TIrOxYtmQHf88Cx3{$zuG(UNVk`# zZlV^9PstS|$6zf3MOI-`h$hS#i4`V)0zwB|#%rh+wJ=Co&ubw>nv|}6*=*eh&G1Zw9dFsqfG}?6JBu}(L(iv$l zXoGwkXj_~zfH6fs<~r?j5BHi~?(u=EB_1E_1V2OH+2!4n?{>8mSak$bMGyTdII`N; zE?2nB_C*iOsSQ0WPtuP%JT?IQZN*TnM2z6G+9I86233TIiR^%eG2b>(aoz*iaduV4x5B>T! zvKrG4SGdf^L=XLi8(D+xZLV;cjfo!m8`I#{$e^Q&Q={{ZJbTS|hOuadvN2s1NfTWa zX^iTs_}1SA@fTNut&QpU)44@6pBv{jx{=mZ^*|hL0dg#}t5!4aoKW#LC1uSfpGlmh? zGH0%6Apwe(Uc6XO!>z5crU*h(D6{A!G%zUa2;vhLxs&zN<|L`rc};E zjqJHdYmA<>i%YO&;1%(dNhrn=(Us2EAZ)mqokkFi}obPT#WVSnd6EvTS zq=(!DwNTO()9o#{*})&rC}fP}8|2ndrQ;bXdqh8iU;iLDvYAA28+qVQ#_^21@ev;B z8Tx*Mn^fC9s4NPhd>4OhohQapE0lEhqpzQF&vOs(oKCQVF+4gksCMjT6lHUnmTp_m zi4jdp`hl#C`9$5APt3b9rz>E|d0N(PJoA4=BXFETVf!o{>6+pqU8s{0VLD+7Mc@kx z6FYI+d`L;*odO=+Ss_LmFbn&9+B3E@(2~Dr_f=_E7Oq7SkZB7Ot+5CVQ=T-Z3m!(fG zY(Jk)p|DO`-@9=m8eiBx9dVt3S{o-%UTm|s4@^hHUxRg;AHRB|ItFQ)ik zIISZ0eC-RgWe47uYO*$lNU}?6l$BDvK4o>*fIZXUu?G3IOQ*8jCao8uRJJ^M?=~lr z=V1|c1uX)MIVD+OO>b%Pk0sNj;&{v!WfgiHaZbA+i7&-(BzkNa=&=>{9-rQOmZ7^7 zZ6kbg7pLoS!q zHUGgOFR@9G*-KV_thm_%E>#eiNI>)?A&lZlD^2Kyt^%SmN1^JF9Y(?YK6xXBkcwwI z4x$@x)N5oLwnAKF%O+wdWIIGXn6zIrw)G6FjaXUR`XUFdtYrC=Q0@ zJe0}V79QaYk8K&^dLlck_h4O5!tc09iYB>3=Jmu5mMv4O`>aWF`zMe*5KAA>tZL1oHcU$Z^U;DtQ+81Zi zqiUbMWt-a8QxMuUsW4l%5Lv;ZrUNMl73dMYaj`-}c&T z{qv;NH3tLg6m2jPqRh-BfspED#Aif+VLYign{9D+f{UlXJ1SRMW@_CWYY6j=5#nf_J%#o zwGGBlI~Gcb)KXArTAxT=ur@L3h18_$=j|x$c+@11u>i2tf57Mc;MhQHXe>|7lu%T@ z{-IB$o5qkCznk{RbgFuC+a0)XSa(FjD&a#86!E*J^3W=9v2IA z?E_?i%2P|1^F9W9Vgfn=6MP{-@esibt?1S_v;J$KeTl5^ zJNRcXMSg%55rRk&-tYhL>!EN4Q3#ehDn4@072ta{{z0xJ@s!n-gPV_S+cU2Dgtk3n z3OnlHOoF{RRK`{2`fR$s1lRUGd<`qUAD+Q%)CHb3+MNX65GwhM{fv@-R6~n7CO}T~ z4bHwZxudjEqDa}y^oKPm%881AvE0Zj$A;qjC;10fWI`=uCe%7B<|&wreSd(mc@N3v zZ93{3DVvpk+0by%%H|DUsjVWlv>&AjD9B za^=~4#-aHXBY9Wfjy0Zb5t}rwuwzWVt!m^*X8=mlB1XD+0y^=Qqj0PHX!!0y)Bi5W zGOWwHznZ$$P1K(7v!r{ACs2=zYQR@WVtu|M46B5* zS9`+l4}MXM`rDBAy85=R^6E_5$C#GO!CN+mC~^w}dmp+`qgy7U{^`s0DOL}hm5q5( z=t(~{Px5rS`etQje`xb~xRA{k_uY>agJOhysX&&Nk->RIe^3N>*)kaVSbcZ2EiyD7 z-4SU#m5hr>Mepicd|8HsVS(O+Segs7O@>q~og5y}NbSjFOq@CrA9o*SQK#SQyXEG=3H^P_(e_lG)?Jp27%zjZ2J4KaOv zhpfo6&5Z;fAVQ6@ES#Vb##&gac@U5HrkSu!ogYsmJ3INo`BQP(WiDspl4CTf^NVn~ z*`UR^+`^Yj5{YD&=`#$W-r_hOWR4==3^1%puKWOb_4vnyy8hc)#*}ea7Kii+A*1yZ zBl0^KSy{8|Um``**k`z$%hPYr9y3`|!ExS*r57^1;&gV8nQ!#K54@^{&AomzJJgr)2I6YKz{S1WjW zHKuJ}j_K9C)PAw1au-78zTq5H3Z@K8f)QD#haB3p8d#ogKn>SD|dJr zl=;vy#;LKK*7_IH_Invc-H`isCjRt&oEyJYwqvy=$f0Z_b^ue3rp0zS`2Y(L-WjSU z$F#)5mke7qa3u6dM_)!KhqTD(q3Z~91_G=1usR%OA?IY} zx0ZVCQVi};8E3W%|5)PDmv)a4^~M7u>H{NcHT#|;(hwX`XP=pjM_B1?6`mUY3j}R4 z2N2l@mWA!(w7V1)wAo9J`GDfOo;Q(u3{}{ED3x4dSFciUl~3$yKNEv%Sa6jVK?r^2 z_}5B8?DfRvquTZy+5FbFJ%<&xzZsUh?`N36FthXcZbhu`G2EuwXx@NAT?3A ziUnJ+WL#)A*W%l4?A?gIwC*PDN#DdvDAH}+T>kIS*f^0oJ<;~a7(Nz1y$Bn&CQK>A z=`nU6fC&OfEcHA}FZy`qekVh3Uze4k5ndwQou}{~a%T{mFXHi53N1pX%Q95V*hKNb z`C3uWF>wj5%eGyuIrmGzhW55~p=R(O^qsC2%-dOb2-HQ=QoL?+RAq|h+NB@Yz4mpFBt%)R< zOwl%bFqx|C!e204xe0&4aOGzF1;dqF@D~ivpA2C|#uSi3Mkl@VCxiDGnC3>Bp(Zyc zbp3v&r|{06)J;^~_?&r?FQ{S;MHmEDb1^9OZNozuU^Wjh=s6Vx!UmT|fD^EpZK#?% zKqn@-8M+j(Xgzt^FtfTZbx0!p;W0SUuYiS?Uex2WV;sEMMUVQcGxE7dWSqU7hh%V1 zmbUJ)5t13z_S=Q7bvMt3PE4c$=rp7^=6WfVowsLlIZWltn$0bE7nR9FmNH!1Or#4u ztgz!LyfqwEJj3MnI;(hq#l5T475BZk4ipz7wA*1E-JIuW=(kN1?LLn*|0r^LlbvJf zl3zjn|Er0>b*W}1GyAxUjA@twtC&wxyU931!xrSb5FGS<`dqJqq}d)>*nT%2!F#3E zuM-Q~Z>5AkIGCWLXz9YF=JWfu;uaM<;07owI63nx21$fxQeb$P$Vnig-0GtZO_E*i z71R9vsW<%lMMF3|%RWMFF7s?|aK8Ah5o-6dV$>-XIw1-D`mVrylyrxM?MrxeinTGP zU`Om6f!FG?4u-Q!yRiLuZRm4FM@Q-^)@afA=2{EeXOcvu6Bx+u7w_Xurd1BpSDUFnU6p2}v9;t+6r zhj18jy~%CBT_pEz&hE}u18y2Yy`Asp7w|auP3B= z2UPy`E(t%N^0AWc6CzZ89Cot4F)H6zHY>AJq{W>Dh3&Oa`@W*QyT_4QtH$mi{E~0? zNLxS=s*m+uphF8$^XVftt}rOsl#y)Ssi!+Cu*tN9-S)EN?!e3zKLiP6 z)nPvGFduJn{Q<=s6d(GGOH4y`G=LB;i*RXnt77&nO*3t_m%@~mxk_*45n^=K_N9%) z9+vEPLs~e&)-ci4WA4ZW#j|bZj?SOP-41hSxaoSv+!=QtZTS0=%P{+;iLoFIT9Wi2C886=BVPp14U2|(o4 z*;pvAY&dO3HiY`Kv0vZSrLj<6*>Kv7YzXydvzGMQUm6SLl?|uO$c9k*q4o1r_}2a2 z&>B02MQ!=IdgrD%5(3EwC!}V8NSIknoWw%C07?2kC~D3t7)m0Ypow^I0Lg#-4w$th z{;}^kJJs>?L5^5QYKY70LlAr)jv&o`E|>g*G{;1!eC3IxhFPZm+4Pu>yn&2Zik+18 z=8`({R&z-mc_&|TN8ZJk!H%5V%D&IN-P3sSQKCBXyIpc=Ahf`T8_3`$PonzoMJBf) z>%V7Q9MOO8L<~XNzKx+j?w|wxcSeu5fA`-XMHDsAeLx#Rd#NwEDhL_)y$zE_28ZeZ;6jy_)TB_6nAJx#vjLBk3>G*C)^$`3{c>i695V7g2{CB;OQT zA^Gn^DQv$$&m4}R)umYDrOv|-ETP02FM7zqdd~~kT7c2&ezwdDKH$UOM>~t*NGe=E z<&OVml^Zj~enl#YlSux?32`*N5)I2R$Lg=qycY08?NH^?lYJ-<13ginHptWy@52Up z>xnZ*e3H=cI7M&3NkT_?x{SmnW1ik)Ewcl%JCp}X4#15VSo z)+bO$q{F(?!+=|46mldSoQJ~oxWWYv&qLwpp}%n5RG|@{i?&#Bt!h|WRrkj<`enGJ*{^{m|ZJJvZ|x^(}RJ9qoZc z1gk-6U>sh$3N{K( z;a<~5ZJT`de9i{uv>8KgLQUDtbl(u4H%yD-`cDZ6Db_yCwKWM5wj@ zFiSmExjk7iDaGGFgGk1kgCQud#I+ACN9_bY8kcB$Zek+&kv)l2=^lLF0<&l6Z|D+% z5~|8lJb^n2^Kh=Zi4?Vc#S=2_?do7dp*2oQ0*V*-?{gA0FZ$>*2m7AtqR$OU3g&G)Hdb22SrLpIia64 zLG*j{!OBGPcZqAC>mlOFdzb%%oQ69p@Ta$9E&ioMy&VnSRWq*0(Din#_pod16@2wp zce&MjzSFz5Tis>1yWHh2C!_x&M)BG+?y>+@&Z0%`k~YB@ zv~-jGyN&+#mhLfs{=q1=^MFAbkEHKdiL35rz=`BfaXiDH6JLct#gBW7XVR(!cUuf2 zqiz$LR&Q|;J!M_Q2?rbL=r}aS!XHo~M%g|20VU!qYDnYxskz1O9_H!XxXW-ViUV72 z*a=uh3s(3Hp9TH)(Sm*xX+doze?Xy$NZt)11HOU<{qBMSF(yPYVFSFNqCi?6DxUDZ-s)m&3|dAxaTb-Zd_eM?<)ynao5UG=*9#trf2`gm1s zZT+gM=4wNy#N5@?*Tt`@Z>e1!uU@~Zx_Y&wudP{E(;TaAY+ToLIloZS++4k`p_vFR zC@m>3zi@uZveI}{wxUsF?C9bdPiscQX-hN{M< z>c$xh=go{Qsa{jvSY5ZOn&`FEt*%~GTh)l%%NE3E&6qiB#;nrD#`?zi<<-rmNcC4# z*BQPm>sHm)HzDb&`nuIM&EUVPHr`adimu{#WnER{6)g?%nmQDyxw?^`wp`X!OhQwbR$sH?9L2gOH$BT(xFJ zQ*+g-D@?9VE_3J33>OO$i~Q!!ofDak^ngo;I;w9xA-=dCqJ)&$1j7XG{?bbBkRiQ`c*CKP=>O)E30a2R>v#K;&_Bg zh}WUW^Xri%l&Yq#sb+O`oU~y>yt=lU-dM9fSkBW=J0p^RDd*{PqRZ)}D=G@7hbpMP z9C~26VRkJ;^Q-EpuIn2h8mI}BCf-;Nz$T69=lTI;gG#NAFIcu9Ueg3MuB&NUTfKVz zf|6w=M4B(0;?>n|;0AV;QKn`nA89BFjCNDSVwH7Q)YV^A7r&xlKrPf;1ZkX;_BS#kPp#_tP5*?#P3?Icp#1&7K`Dm-zU@4&hSx_-bmXURATEhBWMQuu^pyEbCQkYnrQ@8meGP zTgWPg=Do1K@v@rLtEsODv}4nPSDhK>9CM|!$zr+EmDa7IE(lg04KBpmqjA81E~#GC z(%4jUB}@w6yX3@BWgV9j-Nfpe%d26tAnV4eRnGppKhw3%n-$KFL}Ih zX|;TfJ|e^__QNBK-i-S>j|!=WVTyBfTF%TLX-BhW7DXzu zOE)jFgYnYMnl(3E0q3KSWnZG|@-x976p#Dis`z4b1k9MDGdH52m!3o#0k_+wmo8ma zx~QV^-14&dm34+Xq|V(}#tRKCm!XlzYmD`Ac4gM=(NF*3XZ(d}+QF_qC4oloG=hJ5ASFfkdjxe}EaZ{6lg-1@dAFM|ING2b4 z1=fYS%b<^i&akPurnZ*5R8`*7=0xge?(%YRl^xD#kC6s0ihj^#i!0~MtFJlHj)+Tl zMug!;KOAD~f;{ z(^z#mIhzYfHKB&&+gw%cr7`+*;NLe^0%}}{_Nm8ms2+BdO6xGFXyjqm;tQ9REGxTk zF{wFqKH{%~Wz&r}(Ri?~zHWN;`kE#lZ3q?QV_iQ}K4%@On&KW^?uNsOU^sLpJ4^6r ztKhsfz=x}Dypnn}@f3^;2j0B2&buT?IwEuJo;8^f)!Hg_=uD%DY$*J0jBDeI=TpDg zM4X4bhmlxIUCoE#WL{O%ycXiA@*GEc6F2 z`PDSepjuHciODCG$nH~ndeKA*)x4phnt}*Cy5+j4vQD|F3|Nh-x;PmVab#@2XMKJS zo;0DZh)DBQHLI$#(qB@#FyAb~-d`F@fV{;j46@_(G^i9_S$QD0Ec8kjFDqMiX(&B^ znSJfXzUf+x&vhkrjYX&THC5xj@>}72mX!{!`Q#x+7ZUN3R(S^4cD;x(BHHK; zmW|PykySq{7exxM&XZn%&(g(s)A6VRh1IrGp->i8TWn$3a-X?bi*DhnM&ghe%VKX} z+Xi89DqXPBlL*2?GP-77hT|V?_x;x1j6D`w=g62@ifkfayBaDLH_zi3xDa29YMMA6 zM24RusR;35jMv<_!FB}(4#@E%auCAqm$Im>oZ1Ke< zWnPBlHoz$B$~9Do-55ep6nr!x4vuYdo(LHr ze(b_{GJY}K+vq2`8J_EDty5*)WmUCRh&*SHAmfdor_O934&fiD5^Zl(YuwtBWy{LT zFtjZ%T?E5wY^yY%vhx;SxTF-M%!L>3V;fH~bQ?JI*tMQ=+2Z--l?(8qy~z7T#BRwy z_6i$TFweg~V|K*2K#d>HI4xovI=_A$dFUBgyRO0IcShuRQS@D0L{b8-c`Id)PSI(R z<1w2&gQH+AOMYHHwi;cKQFL2HXi=XwSx)*Mzw=Iyj0=b#>hW=TbsfBC#I|`%BUr5~*6_ndJ9XWN()B0v)@ zw2CMXNulLBX9IK%+4F96OZMbEJ5M>!j*_#mZTsF@1nJR+iC;g0xtR^jxpn%z7>Co3 z)1u-vs-8}Zj?Z}IJ1r{idv2AD!@|^}L#9?w(LmT3isKOf|7t&x8{*C3;c^JYrHgD< zF5PK)(wh@$HXXg_@TE!@X+~7KeEG|>ymKPWh{`uIjOnF|n&oI|nqJkks;1_os&x%# z*EYZB^5&DP8tR+g{^6GT=Gb}j=bss$T6q~Jr&``sj`{z_`uLm~r_VTT`m7cP=T0;C zM$;!*g&%eGlGv#A6Gt64VO&8MCi3FIX$>>|#_+`6;S;YrX#Qab*R_rLt3|S;eRvF~odQyuh$wz< z8Hay_?m@L}V?Q}2J$m$i9W43xP$K+R@#-2&bE(Szg3(_7&zYpLO^@S3Xpa8-D3yBC z{~$G$imV)M-*e%J-)%PSm@^bz&F^!kdvyO|(~dqWByGPdTb!03;rG?BwCSTfy5%k=l9r})ii z+OQ6D3;5gIX#TF1tH$cusu{F|AvR-mRdZEr#$`=Su^Egu)*ED%ar9#|(3w`xIB#(! zh3Bx^#iXiUy8^*_J5y+~8|=SvyhznS#niuId-I`MNw)o-LbKj5B(og=Lr>^;d{pWr5 zeT~tzyeIhkEfy^QO8fpZfd|6BynhKUf^|aAW?pK_zsY7u<)<|l^1hV+2w=gw0~9N| zz+}9JZqzTxKivmdMY? zcRz1R#&#Cie2f40bp~f!4Qz?UFTLI1Y-@q7wD|X5!UcZZ7vb-E{7>|+`zN8X823B) zo5ufyS6pD|a32HgYZkx#LW6UE0*rp6z1lPP^6zNYEV*-kN5`o1_W!k+EX?;+z^7(s_Sg&e-na_LQ~I{{@4)woDh6%2>#g+{H_rE*CF^J zFg@A$oE3sshTvC+;J1X}4~5{nLhuP_Z`t^q9fDsNf?pkie?0_$A_RXm1fK>|m5tAm z5PWS2o(jRg6N3LD1Rsr#G8;ehLh!l}{FV@WTL}JI2!1j;>TLWZLh$Ag{F)H_%OUt9 zA^0Cd@Hb(PQ#L;5gy5AS_;n%pw?gpWhTw<8zGvkp1YZ$?ex&g{)Z6!1Td40pYs5c|8T=`a2W98_yGQ{ z3rYXk5d7W{{8u6PBn*V`DC0Lj1g{UlZw5~M-+sK|pW_#xdS6KT--h4?7(Zm==j0Il z(h$5O1pj6T{&WcbDsU>-^>$p&aTk(S#@?q@YpYjXQL`G$8k%Ozo_WTc7^X{BH?L-x zk<(+eTV)02=@5!%k9h<1>>vn0zI4)!_ zgXIzHW7u@I4ja_yFYUa_POPP|#v_xN8=lry*InMcmfxmuQ+?eE%9B?DGzmvoP`0LK zz0H;PqD80YOwCKxtLvI#YZ|Lj3S=Le1HWOVh8MDwh4!}bE-WREMueAZ)>UWV^-Th; zSW~|q`L1c&(6pkev9W3ciI9s$f3HMNo@{-@R#X{*T824eevRmvJ6Ig*@laeryd#Cz%Acfkq|Bw$`mOw#jL3wnS{cB9k;=?LF;& z)Y(TK-F7-j`r!Y;tL zuc}>f*#@?goRkD9HEQZu_OMk`db6UMHCj`4*-}Ht-1RyTS+Tlim9LmOf4s4R*V(Ob z^kKd=(HzN@0kYbQ4bC-d0ws3BrxG`likS)t%ZZZNoP9~OrXr1)o1o$_E*g!64%pS_ zDRrQDnxZeI&eyLRwKZ2jckOmI!vZgM#4ZY-sq(+Y+TK{*KyOC8l#O+IWdQuEGHIwSwqEaTVk+Nw7XsNtlJxwj$Q^+Qon{( z+jZ&6(s_9sT@0gCgWjCBL1)v#ER|;VvS9~7_Wq0wyXnx3sq}YutFimBvEKH0%9aL= zy-jDk88N=j!?7!nj9rnnFz6*wAtZ|k%3?{#8mL^gbe!p+mS;=?3=&M&ihw-`PZ>^+ z%nW-3D;ny#ua4%IbR)9@UB>WDYp|D{ET;Gwo|s%J6ePPRnzh?8>ZH8MU@CH9quKVv zOxDJg!AIHGn4pHT>r`5|mSGl6tXs#K#-1DUOCx{K~hc5j8GXsjl$ zbs+8>XXe$HZs5iwneLRADL2<+jHL^!Q?sLz*^7|W)L4UQf{KH~=w;-t6S~)xzvAT- zA{%rk*r?P*ey?dn*+lFT>HM4;L)q=PAgjz8TAJ2oGh>bb?UjP#8{q}84Pm2h7jP>- z&DZ_YU^6<2adI)rB{#)$S)~Io@3I8Z5n=?YU16WP>THeAjv-IpdFzCku9WTN8>%xL zDANzYrn198gJ*kb9~irs(MgFhHl~?cz93ws!~x62dF%GpntWaHZX_c8fk84HXx$XN zq?ygZ>478)YC=G@Z88s~Fx5R4E)}Z^kXoeOFw$7=`0GtH;3TQhu~*m9+rMV`VT4`U&iyw`#XgntMKD6Pf9~P5Kdi&CgPWQ|wmKU#D=*&lf`Q?a z(l4k#6D>}BYW-QFa9xk-5d0@0__3JBq+hVU)`sBU3c>#xf|p|cF&q6)hu}XC!Pk;t z@C(-0^$OSebGyQ|oZB(4myOP5h3k6%fxU`!wTSAEdGq5 ze*!Scm*#8eM||phzliw}`UUyFTj6m$7dlTU{Ot<=mBKasOQ><;7o>ls!sB=@^fxN} zM1_A%;hKIWHD>&R^qUnP$8(|o1%>N+zu)2_{~r=j{C24HQ*bYIb}C%+bCg{_MEtx% zr9V;OI{lf%1b&qMWc)AmKc?`h3Qtg@!Y^3fdWFaFTto=f^Fg-=uX zF?QlCNasw2YdUu-{GE!;AxACzKOm1oJz0l)qf~l>+OF9=!m`As?yV? z@Ofe!H~0nfogQbH%cb)@L*Y8#5{nBR$#k6l? zN$|TAuK9Tzbt?EtxxQfQ@eGR-KXdWF(793JTCc7nP9&wr zFDRd5EiQD#ejcyVYy0_#!nK}Mk`c7@b-C`cxX}METfg@O@K%017JD#&_t^B0EBYrO zt?0wg6|VDbC8LR-cgvDm8te3z9^0U2@pC|@m~qZO|6ou%;8@s7yzLlzf)CK5sX8dZ8SU6TG* zi_>t3^mgiD_y@mEY9#mtwLjixhsY!ZrO-lnB2d|8a%K z@m%P?%inYwyGpO?{mgkLpCF%?Dm;!P!ska7PM3mr zDO~e&!kLD?l&jYAGtJ_3tNB@?aJm%w>jQK?Zt1kD^qS6>&NBH1%XPoPkY3k% z@|wkkf6n!)F6{D`?U0<^aCO%HgYu z{s~AY{M@Z@o$ssXGP#WUed~OO#}%D(6@He&->+~@|HuW7j+W0Xg=_j3E1XP@@L#KN zP5)+#i`?Ei+T{0Tm0s5uosmR8NiW|cDz`X^QK$cSApLSn|N4;h?_bE|GvxVi3Xda+ zlZr2@F#`q_EL~= zT$LolchUsZSLOAEzh$RuKB+vKu7eUU8UE2{z~CmA5L6i7z^sj#TE~i>wN)S%C$0pKV!@F zAw^%8t5xB;TsJOt{Aj!JO@+siMD*mx3O`HX&nsNh|MIfz^dC^TrvH0|>;Ctj3fJ^c ztu*vSZvV7$m}7C0v#ysvC|sBKkc%Cknx7d8Cl^)ZP^@sx&l-yhKSzxf`9$} zNQ7U|PClmaIGzjtFDhJ@_st(5D$b6*-|{)l;zWN6{ues)6t4OFy}~v9!#_yWGvfer z6<&hpLjS!A*L1E{__-?m4GP!%d{^O`&Qlf_eUtM3UZvONeOcj}{<$ltRHj_I-&w72 zoxV8)|ANJZpOserUsCDcfqXyhoXaRef&EJ|7t$l6&}Ykk;B&&uJc{ChRI~ur8hX=4Zm?jvrm#xWaY%GZn7KcTX!^^Z6%*>vFwmt;<)Z zKilF`uC=yY7pU~QT<=r3<}i0HDE zQn=>l%MID-KcH|;{|^e+bT0X@p(EvzdRbv{vP-&Lvl_GKTcL2B@0AKa53h=z+^le2 zUq4WILZyE~;hO)KL-0qNvh%r1;hN8h%`UyB|1*VaKKCeG)0xoX=un7A%KJ8jYx+M` zxTgP-!ZrQlu5|RzSM;YVT+_eN;=%g;oJz0j_kCA6`kJ4s6|VXDtimr){M@Z@P3IYf zQ;109vrFNc|B34zKN|m;!ZrOp3fFwTb%Ud`Nag!3g=_ki78kkgvG(T!D!rCldm#Pk z<4k^^3`u|P)sCNI6hBo8k1PB-g)dh4CWUMMFZwqm!Y}AoG$=fd=OX`&3fK1MHiaLj z(m!u;k*REzpY%!G;74>cpMOxergKP|lXLnwE+yaD78gFnZk!Ro#crG(z{PHyr|47N3jIqI zuK9fP4UQkp=UjzrKHsPC^A!D7g=;!rQFub7|E|I{Kh-y8=jS?wYkrE1tpPgHzV1-zb^EILboTQ0Df}W;u7hv#^C|QX zwK(yq>EESrEr-Vfbfo=05x`qbcCn`dc)|+kmx{h_zt1aN=lh$_7=py7mfJxa9UjM} z)Z@tt*K|H;aj7pEcdt?Db-O<2vyQ&b_k#-8`L-#%Nb$2#;hLY5K4<6)KXg7O{bpF4 z%B%UgO5xM-Ov?L>03C@>ek*{t;vM>ZH-LB9eD72Ab-g^LaLxbU6}}8~g`f9s;%wMn zX+G;e?{F>88x$T_biSkTvlRZg!Zkl_U&v1XOA6QYdljzh>lX^w^e5bG=u3UwY2`M_ z;=%f=`eJr|S{1JOxzXZ6|9>p~TUGiQASrUbQ{kG=t8dAk@8=b+^W9=`$@fW{@Ap)C zo$nJ0*ZFR_HG96j3fKAeTU_#${%Cgye(=9L`a0h^n;ow8YN^8GNFs8(%HqL%uL;3F zZE>n!o$t8+0DvFiTF*adaiRZHE6+75y>8bwJ&; zhM^zKcX9|m!{SuGnx6^(<@nV3o}zG_?>P#;7_W*xT&!?Szs};qr}R@z0ld|)5nCU? z3l1?4KCbBNerl7#b-qsr=t#cL25`yuR{?yc&9_g{*ZIDtaGme`+YP_LcJv{I$B{(j z_O@?kPk)}mb^5<6T-VoIzUAoX^cxhe`Amo4w}jw#SUgyd-w)uehK<+{0{C((&qoz~ zEze&mT=RMVR#z@v-kl29<^9ZmXHS2J!gc!JTU^Q|?f0K5z2@hLZ#()Le@x+;&pw6g zd=LAMp%cva$Pj$0#e?nl;O{zqbiVIUxXyQ;!gc$tP`EDFM=dUVw%U5UPNmoV$afTe zAy^YVe^KFjJU;FY$G`4Rx7=xP$=7yYv2RDHGI$U zqv;&d?eL3Kz9(5+_!Ro5sPvlt&lIlZ{D%M?8E5?|fVUbpVt)zXTWr0&s_5(Xb=X~w zf1U526|T#BkZ(0Y;N987x6pW;?yp5zDpFY^X&}KS!n5Wsq|X@F9y=DwCP_7Nq@$@%oh8%+72vKxUQED zg=_op1%>PSI_n39zQ|4NL#f4s_Tf^6YrS0+pd;;ftxA79vJ(9t-Q&uo>3mV)+CJQ6 zap61@R`nC5v`ntY8VsW7(_Tl3Jyw$uO`$PcWWb5lwiv9^mE9Kg(aGme&0G%F7 zXOBv+_2i@n9RIq$dP49G4`xsQ(T5za<$R06<47X>KcMi%3V%xBx?HbVT;wV19}arh z<*Uopt#HlHPZh5D`G>+6Dt?Z7#L?IMthTuDv&+i4R;Abcl=M0}nxB;l*Zf?kaGm~k zi%Y(;|NS17ev0CMyTUd9FNffFZ_Cc-vkKRIj(*gn7k(tZT@b(}zI|8#7yoUt#mNp( zn-e{ms&LK!`2jkzp70WtUfZ!Lj~PCRzLw{Fg~wICH44}1yDUB)X~*LK_@Vd*zkgTh zwfw)OaLxa(6|VK z$ZgEe9Q~z=pGgYW{7ebK8=f_EBwuOoS6iIwUAOle6t3m7DL_Zs`z-g7n zo>aJQ?|-zo@G12FtkP@x|M7E2|2SNVetz5HLPy&B_X2pUc|CSd5V!695k+6O_oo%E z^ZoEI9Y4CfT?&sQiST)+!gcyzSX}s*_WqnouiN`S6t4N1@+-%W#@}agp})nJca=)7 z{f+Mi(m!L<-xHF4S0MfP35Jj719+=}V=o5q$u|8z6n!oKLwA}NNpE$zW-0s0B?<%-~9oc zzLQ74|JliZc$}{DeOBQ*-^ssr>EDG*k=q3p7ddRU^p~piT2HPDq~BrFe=H>ZMuops z<-0Wm|I`0DK6QIJ{5KAdD>`Q?T$gvb#id+-v3%YUzz;pl(7jjD(dGTI!ZrUd{MPYv zvf^jP?;Ji=;X4(s%k`SV<0}2u|L5rF^gmO$PCxedF8!s7&RZ0&<#UR~MLyDBpQ+N% zP;}NPT+>Mh=xjRN@Uc;)*Yd3VgX2?|t6AZ3mG2i6uFG|s!ZklD`?J&kn8G#vuPR*g zv)$t3L2fMm|Gq8P|El!bKKCnJ^K-;=+4(85xX^#n(!Wrp*YsB?T+=_~`Rw#(D?E-Q z(%#o7{QtA}CGb&I*Z&iO5(S;0sEFGGQ2_)t3 zz>)v1pV@qnez?MQzIeQnZX(cQz61bAeJTJ){x=x>wVy8sdgR{-IPyRLb7%d3130F) z32@ZsfG=!5DEDZdQXiAvQaHDN&IWov;Ao%ufS&{Oixu9RFpjrcpWA>Q z`9BRf^8Xre$r-Vp}F<;7E@aql!vlRbZ zfFASxd4s-I(Z2%pGeQ5@*Ytx9P8ZteZonh zvs~2wF~Cuu_W-x(8LiK*-_Q>_n9rf~U*r1&j{0ONThlTUsLx*CvQ#P8 zqO+De3h;9QKOS(@=S+obee^iM9H1`%K9?Bu|5N(c06mt+TU_Yx0s6Cn|EqvwdcOty z9H8IpJNiKf+y8X>ukA1ta7^zR3fJjPOXsIOo&ump`$9w;7I?e!nOVH zR_RJfwfSTH*cWirX9D2Jf0n{E|J90rCD0@P8o-hN{eYw2J_R_^5AG@T*K*e>xko9S z^9%XB4mj%Ha~InVXwSa^j`S}Aj`WA@D*5R8RL7qo2Cm0-rz)KF%%cC=PtF4z(|d^v z{yT&JvVDjd9cT2i(}nsk1l%GzE%z3{u|9eLaMb4lwLv=TkNg({j{N@)cqYhw0&wKt zu5g{d{rl2GeC%eY3-kAHfTKPy0&dacI=!C(j`iKHyW9LxpWzDE`pi)Jj0Jks=Y7CY zpS0dKf7E9P;OBy#nSi4{Co5d*qy6o4pf3PE`}VQrqCVpRM}5u(9Q|P-;Hb~AJ)HTU z3^?+y037|H4shguzruBT7b^Qd0`zGA3-@%^=Q_YqpT7Z){_q&!s86-mng1UENB*k; z$NYU6aOB^R?#%x_z>)tOfERjfKmQ7F;%&(1rqks0@+vbn@3|F}3ul+w0=!-zl0N|)+pMExflsg=7lsgS@EElDKBY$fj zXa3oMBma4TW4X8-aO8iN!nGaJ_M$}7u>$DP&gbpxtWO=_7Co=yKlUj#Vnb2H$`{|SS?_J<8XkNh_R zj{I*u$W9mXe;RO1Z;ykW>CXZj<(2}Da<6j19|9cpNjXHy)%IMd{Gp%1&GI!EaFkmN zIQqk7fFu2JmUDW~1sv@Y2E5Q?$E#ZaNB+MU=*+(YaO8gj;8@??132=3Q{mcf%az^U z1A4T>l0nY;JODWA^H0Ffg7hADsLcoUxoNPCWB#rJ9QnTsIL7&J07w1<4zu~s0ezkW z9OZ5V9Od>u+~$MyISSYI)aCj#phy3o4LIs~4dBTCuLgfzuGa!R@_z|%w|Jv0FM0M1l%IN zy8Uq2Fq;q3-vT(w{X5_&x5)(`aHP#2%hxc#kCaJk z+|FuT@dBX7^j-ou>Uk^R$p1-$zxLZVfFAjO1UT|vI^0ed@_zzwOz$SZk$&n3n-9u8 z4{(&bzy)6pIP(9W!nHj!l;8F}+F9;Ez)|kAfFu8}0LS!B%CPw({Ytv=NMSurAcKM9}j`}>HaGfvnRKBbRdQ4a4 zXlH$X3pncY5a4q`pJxF_eZE(?)@Pa0XV)>dT-4`rz|lVM1CILaIo9Tb{k|c9qdu&jE|1!W)pJfWy>C*N7{Xmc9t_g6|=VQQ;|GpEXo|=DFKS~@OLltheZzlkb{NDo{ z`FoGC?O+jKT@U&INBSE8$8_BVILck+f^PyG^L_7QZGDi>%YdUkUjmN$?>EurgX3ev z07w33DO}rmp|bOAphrFX9cS}LeZ~Tg`kVuJ5bRtAIO=nw!nHojls>ltJ?b-klCwS+ z0gn1y4>;QA4!}{L*^{05*8qD3?;^l4z0UxSa^D6V<$mIV4?EsjpOC_JdRvsAM1daV-Uv9# z_2)SAzYuUt?@GWiy`!esd{FKQfTP^AT=2U9M?YT;IP$s4XUj!>RsoLs{|9iS-|Ym+ zN83~H|9_yu&3Jnh;Hb|(0Z0BlPPFwzeTD&!^rtA?)F%k^Xy-+MqdvC*j_X)f0gm*~ zxZv#y*ZNp${?6mK(}nq!t8h)9rRaa*Lhs47`Jf$!0*-cj6>y|)2OR0AoFw_1^PYJI zuID`$DxC8@i~j5SaWUX%x0_t>eNL8gwH@k|9S%}B^GCal0v!228gRDHJAhlDXWu-V z9_R4~0*?GY2ORCW_bE0XX{ze*lj9ybCze?>$}e(SCk|{OLJd;hc{cuf_q6dVT;n^6!1RttaY}4>;B#l& z{88@JfTP@d07v>a6>jG5CqR$+n^I)UMLkylj{IK%9QD~_j^v}|{!ae%3{yDARpdX( z1^)_gl-oaO>xptF1CI6JX@H~LBj?(DkpDEmk$>8G&h$e8NBR)pSP!lR{7ABej-TrR zM?G6y@H^)@>+>?;s86KWnf?yIk$xlKsE_A-n-AiT1CI85A8_RV9pG3`^}E2C|M3df zex>WFlYk!UmvaHfdaBmoqwA@~K#zQG1RU#~F964Ss&|R4Khl2$INE35`8GY;=Sjek z{xiUl{!gVgAEaLgIMQEJ=1hM-;7C8E+@>GqvHf-i;OGw}E_k&I{#zG3=R#X9rmF;S z)YGbPrq2Z&>AwRU`5#_s^FjJA07v>kA!qvE1CI9js|)^^3;u=+epi((7xj4saMb^r zi=65I4mi@Ex4@ac9&n`JW1&rtelienw8K~zJl6%^W0B1t(>o4u)aL`hk$&%sZ9Yif z1US-f1RUwJE|Gk6T+;R3xe7Pyy9yWl8W;SmOKm+-pBlhXpYfMD)6W7N>HA&oOn)5U zNWT(r%-^R0$Nb&kf`9IUue`!ppSJ-=eHyBr=^q9h=_{^uroRzzr2igpOxIpl*?bTm z=7RqlaLmU(H8vlV+Xy()zYRFj=hxbNFkj{Xj(Ub%@XWBy2jvz5j&k?;r8E6Fz>)rZ zg`4H24(L(OB`)}+tEF6%&j||Wa)G!x+^3nOYP}%uFg`4A4M*)s{&H^0M6#^XT`~1pT{~>^vKzh#roTqklxmW}^ z4<&0n0yxT@R_`pg3UIXB?SStG{GS0F?fDhpY)Y+9uV348QJ+By*Y>|r*?%O^V|kwj zIP&Rxoy{Nh83j0|E1+;KmsAzUIY7@#6SV$U1CIRf2Ar2pYWjx(M?Fh^4a0~c6 zsc@~QZvSimdgTAP3;vJaI?H_naFn}CgH4b5J{)kg!`XmizFz=1>Uqfjv-u;REWlBp za}}=bKqeAL1<+%D{T^`S-wZhB*LuKF&jr^z>$wzgi!{;sp1Q=QM?U+x;77aQF9MGG ze-1dxJwy#KYWrwE8SH|OQ@GiFo&)%4kgh1;^8vp^31U8|=S_g4o{t0W13qs6j&^(D zM%$jq|1-dm|A3op`cmLO9B}0SG2qBQ{r5H>|6QO*{+|Pm{D<6Zrwi+;iGU;huN1D+wNA;s9_W$JBY>m-WZz=TErWb1 z27EZ+zj49e037-ImfHNwfzN!v(Vh{&F9iDKfL8$C2sqm3l0VpTk^fDATfpaOz%j0V z4mk2T_>VS!jH@F6M}2Y?uJcRRr!#;a>(fgBM?QZy__V8he+cL?->&{)fLp-l#M>o*t)~i8o^us$ z#?Mk0{Ph2`^+dS~07tpE0FHjr1UT~f0&w(`UH)XtMLRqIIP!T7aMY(=;o1&lB60Lt zX7k7V8V@+~pA9(XR|VjxXWu{D{E^QwfLo-A&i6Y3M?Q@%_)9MMk~^H`J^(n%{Y2r~ zKH5*dbisG~i_IVPd;)Mx?+1XR-2Lyg`BZ`(h69fLp9dWIe+oGAKj1EB{zn3i{IdWL zfj+-cxL)s3$D3!+u>t6hrr$arce`84)%LWw5YW+A;au)8{@icS`xX5oF7)@@lgPhX z(ciCdlm8vdZ9Aado&ww=owWY{Qn=RtR>i*!=rIoW{Hx6$?fE$1DEEE9QEt1!wcIf+c_F7@{N4Y})$MjB6xR(2flIsV0lv@Zm%B=+){r?Vwf3xEMH=sxVUwogl z{&xY6`ai62t^Zew|1&_3`o9M_>c89lwp`TzXoYM3p8g(jj0bwu|2%`fF;&u+xzOMF zfGrpE>lwf?y`KX=R0y61y7A57O;z>)ud07w3P9g1;6IS)w4am$j{4+1YU_#qRtmTUe6CTr)>F5;mI6KUf6AcO=~`#t)g*(DmrY!y z>n-4q>G}e2l>6OUnJ%s8|0zDZJZ7gC{oxS6(Qj7)j`sN{;OMuhkK6pwZwCO5dY-Ou zt*7?ebAcZ9EN!y+qdva^+#);be7_HHl>4^AwOpMq?-_VC@#3S+z?+plzX1LNiB8My z-E8ZLa=!;0^*`hZo8AIGCjfpd;IjZn{`)>@^FjXO0Z0Dl0-g!{Lx3ayTNSSDwo%#P zFF=p^Rrr*%K34&b`rHY473lK_;Hb}>r=9tS0Z0D#0FM6oB;d&Zf@hrhF9sa>-v{_b zp#L*~BmeuKb>_bwaOD3!;Mo2-;W?Yn0+4$a;3)SSz|l_*Txat^xkmzyau+H*?hn04 zI2{q7M}2Mp9QC;ya2%KG{erE}Wa6vK*Aj(mxq6@f-y3)}D?`U04LqF_O~;+Ue<1zV z{8s{wdcF)e>Uq|SQctZ77m>&J}PQWdY`y$}S0{#); zn2*=K>dgNhz>)tefMcBe5OCyw!1_cx%u@bvsKQMi|c6{sB0~?dJeTeHQ=Ang3mYBmY+c$GH6|;K=_kZ#eUR z8gS(QCEysh_juFhgZ!TY9R2e{z>)tqfMYp5@hzMG#U4BUoB=q>Edd!X@{Y|1>8AjW`cHGguTZ$wU$@6<4ZNCc#7D%y8&!IL z1N;XPosQ>!1|0Rd_TRStn67&Ow}4Nh!nK}in$q(e&||;21#p!6^t;Y-KLQ-(_WO@b zKOXcM0XXu17jR71F7MfVkpEGDV|+UvaO8iX!nGY*l>HY0J^I@t@7r=wpACScKD}CO zdQ9&?z)_!90Z0DrfFu7yT5bNA-m!oq|2YcR=}j9znLtMg&|`Y<`oQLo`aBD`MbB&h zd`sb)|MO|g-Q)Q$(4$}VYP0#Ho=*Udaz6mvBEDMgHwxEspHgyn|Ip@-at{R@<({r^ z&1a~x&$&R4_Nf3I^8edYo_9q>hfqn>>>O8#1( zQkC9*3OCy)#~Son{~QeNy!Z$i_y#5Sa^R10e+xLuecs@s<-TIz z)k3Pr^SXhjF*7>;1N>3$H-Mwu%+H+de4K$-3+^7z@dlouUA`_?a{meR7*{_99OWMR zMWUVS6rUp%ZkCI)4SHQpi(KfhaiPD?h5o`Xo$YWP;An>j0LOa#MZht=k*}Qj-vK!C ze;#nG$KMAW`S07FXwUnUJr7p6Y0u>by|(8{7y5(0PUNrS*x?E{`PT!EaqLlpkM{Fs z1Ft5Z+Yq>`ocr^>9V~l~fE4h;t&gn(D(*Q@g zzc%=2x&P0=t63-=HyXHgpu}$n{wVh$z)^0y!AHye&cLfBdryyV67Azxa{DOUOz$AT zQEq|3N6VdK;9736ftM?}mB1g<8vz{UK4I`#qWC-y^wG?0(VFKV5-P8VgD&W{o zo((vr_X~yV{Q63z_dB3ReFpb**8eDloBH2k(Dy%xl%?ZOF7&f@aZc}*fLrvuPVX|n z&jkD-z%jk=C|svETj}!w&|`W(*wtB|y?fa>>eB=`=2r{gNIxRY=5rb7e>~u*|7kAx zUc1?RQ0}pSquh@HNBZ>LZ9YhUoWix;eka3%=UkviJxg8i8w@_WUbxx7tH~C8+-l&n zRDRtB{09=9_J>CSM}0mr_-MJG8hAAerQ<6DuTyeUdfWCvx%&Z*a(ncV^xAIP|94k7 z-$j3W6mZO!cLBHP8J*tVd)WG%3HZT)qdq=`Ykjo;=K($HleeeMAN45(9QCOM{B+Rg zM!->@Cls#rxm(%!d7wvq{sTDflh6+M86bCnH_`shivJ*moAw_EIPytJcecYYz|jtG z0*-d;)z{{O^#2Dq(mw|{(jUH;&F3;m?=gU*olkMWXS?8g>}~T$eZ~Qf`g{O5((m2R z=7aQ2fFu1zz>)sceQZ9Mt|AwFfeW6qugwSLmH>`&t^J(ovjIo?YXQf6`6J+{|KD8j zivG@WZv-6Wp1Qv?eL3Jrzf|G6-067!Fwmo(&${4!21vP@kB+N-4ZPYTk9`e%qbiS< z!kHV!)p3BMKC=uyT5iz5wcPU!yuVs69RmI+_Zq-a?!5*dE%yNf*K!{=@GK>_8Tg~z zHvvbv2ON-SXWec*RN;IV<7A#eKUc|}?n3{RL9f%b&cJoLUN-QBhmZnvyaoI*T^|FE z>9P(?OqZ5>n1NRd?jFxD17E7-j#0SjKPLi?a_0el1@YGP>BWGvOpPxF{7S%Y2K*|( zmjhk{_%ndl0{*7L&2sc_1FxoM_-HZkW@U#@fj`u$M+J9CUc(ss9-;+1+{+7J47WkvwmjFk( zyBsX-X{L8~1J`oB2A-wl?yqp>hH{Sr9Oce3_}nbj@B|H9%RS$~=P9`%g`0A(1{~!+ zWbnCJ$!#=nE%z}4uUB%PQ@APj9l%lUzK2LbrkxKka4pv|@RdsLFom0Pj{_X#UTE;S zS*7Yq>8Nc&6h2s)0{X_*)7$z$YpGJ_Dbu@H~Z^a_1ZLT5g4bYq<*yJV)`r)WD}GyjJ0+ z+#!2VX48>E|M}j6xA>RGlVRYujgok#f&YHA#Ip=Ml?wqKeghwWjKuQ|JY}-PXBhY- zh0ikZhZR20z`xCwe98^{2A{+i8u;iFC0=ddH=HE#Is@-LL*n%Yew4zO82F2`CH+zZ zzqCQ(%M5&)(r3AWPrP2zuQc$D=S#fNz+X~)nhpHsC6a!ffk$tY_<92$e5=Ga7!h7sQjH~;NujYZr~RwzwK|}19?*=IxGV( zR`?JDe^B8W2Cn&My5LzZxZefOcfn`4;Imxtc`kUl3%<|=uXe%f4E%0opLzq|OX2!E z=Q0z%Q66asB=`|KhlM!(9@uSN(pCpLwsuml*h>`z5~Az}pqR z%)lSJN764h@Yh#Le5HZ+epup-2ELzqkE_|h|EBPD20m8d>kWK(v*f?Qz@K|r;w=XL zlv*#l(ZF9*^Th22{*9U+^&FDuSAR}nYkNFt20p!q#M2FYteW@gZ{UAW^9PoJ|5vRy z9b(`c)Vj9@@&BHZ+{1g+@S*Y7c9p+xlBYQSa&HN&HSkY( z^9(vR%2S+v8gKqV$ALc)}c-p}4I#mM7+;M&W z`b&wc=6@W&_XdgQvx0PJJR*lEqWZ-wHe(669{2JAsn_o~_xUiriSU4wG zF{12fYvkzhW5#A=jvY1LvN9|__0j+QA^vEV<&Qk?AG+EfY4+DNraZ{R z{&1t;>t9pT-oyLbM(=%%5#P5_-?uX#-NL6+Hh+4mzvjasq`-QAq-q^0vECn@PXBke zb$QW0^ih9gy{U`8NRemt_j~Wr*J=KgHU7vt|LP4r{3+}Fv3@-MM&J8kB-NkN9L?$( z@qH-eq{&l$e|gSI{%mYw3aLgceJu|No{`5}#5w9)=Z`dr>u_V}P-3<&TSRI>Cd~J+ zd_WSl&8@lY!`-W9ZH*El0X6NrhXzS8oT)Bi>isoqhn%glsOH0Rvhhl?@p6A;`Z9kc zZ)qUnTQVh*_giAL(jR48&`H>W&YiJE-cqv160*m)eq{=BiEv|8Ias(>KuBqH_zKhdf~v4z_TMvM`-HurMi8(^wwO>f>C}wvJgE zGRvNxJ#DK0$`9AG?IWv(Y}&LbvSKj(^+y`|i&HF}Pffn1MB!=jF`-9BgIG9&Zi(D9 z%oJum{%8Z=7shJH6jyAohAeqWCTsA^OR`!+zPu#EHO!EgLnsV{8@=I$6fgbJ6+(?z z-g+Vsip6}ltxYI0j)lG$ zyEw%koBmdAvzU6c&^PdZ8SmwX4kuGKa`>`Kh_EN)*%R3N9!_wcao4m9ICi zTBhGZaeqrf~V9G=;@a5mV%qh$-^2BWYBEWF-@4lTMWzXi74Hu}R~%AN&PUA;spY8&ofNq))46MMVmrisq5;^>SaP2qHMFg}{X{pgCPW+=q=r%TqJF7xfn zYIWHZu8UtSj$hT&mB_`W@Dc^m*7~0>D#deQ3zuzSc#SUv%tu!{>bXj{I|z- zWN&;eFyXZ;jweIrQeALTtPeSCv+U|o+r5wK>M@cUNNFTD_s2x(RvZPpnl&kH5BOu; z2+?(^iDnZh3}Cm*OFr!J?URgq3-a+8Ywbf)lAK^|UCL92p_YZjUG zcx^Z;5#6ORJ69d{GX{k|Tzg!yoNdHxo6k@c$%^CW+$a z>}Hk7tvgbPNw$|dyEIbfcHnG+CX&HblNgb$p{j}+9kpkF&GkN2Zyhzcm&codO+MMG zymBqKHPw)z930{W_te*w&5IQts-3Cd*3RAINNgjWsI%Kbrzw%xy9{bhHLDo)g!ZJO z$yZTN#Mjc+tI4O@D=M0C@7^2!6A9Ht*rida;!RW55Hbu9i`Js-H8px0RBO#_K|R8a z5h-|QyQz!qCLQ?GsYkG!T2NG5Y@Z$jx1iQ(FWH(NgPO=7hua~042?g+9s{@8Wsl)q zx>7xc7P=ILv`LINsLOn!9v z^3tNqOnFITFb%8|Jw|C0eV}Mwx<4kyO%rBm;>vBM#$t4+>&kja*{JN6b%T=I%&aw+ zwRpU>%gCklOcXc7oK|2cC)!%8@^FZpcd1Dx^72RP%E4{*jSAK;8vK9JFRtEngb zHdl)50D5-S1!MJ%lrCDYBBm6$qc5l8L+2{afhbu>B{Z=(#t!6J2=R!XW}+xT%eO?C zr%5(rn0^B{C;XB0)*DD-C%KE^2$=k$_B4+ZYlS)p3M`MOk<}zvr?jKh06Y{z^M3IK zRrz8;l~`w#mDS;B%Ssx7ra@sn@kTQMO<7r#V$zzHEqTI?muJxENi!o&zIFGDQzQS9 z_anZwep;BM8MNM_?$ES84}WxGp#({^X;BY_#+M4j={g!fiW4HYCZUg3Tugn^T-))S z3iTuDat=k6z%eULvuny)XkT8cFZI%f&gff54{0L`of77lS4v-$nWSf6_3}hs6A_;> za#LfiEQ4o*lq#1 zT;J4z`jN-Ixu5x?!aLa6NF(L1*^!Nu9PQp_I+XUZ@TOA&2*~7zMf{6TAPC5wKu|yB zsucBrJ%OO^c6kCp@DZ&f#gT&-X(kku3orOaASxDK2pYNYLZZCz;s-7tB5F9551j^T z-)+spi*#bV`N9jGb2Nb<*L;XM1i5}vEIex9piT}+LS+ya%a25eqq>u3!)5gl&rrPx zNts_QLJ})6?PWwRZXNHKi-+Z`g5uN3bx1pB6%yAWNkeiEK-qE2iv#-&A3w}GBrc5H z@UWfNo?hT21%zjA#X2OYfN*+2)IlA`Uz%a~7gv1~^8V%h)$YCYN+e-qo~{2^qASN6 znJP|-9#J4Fgb}eA@0G0^u^ve-itO@|r0uCqXXYGaeo;5JGuEN?pxhitYMt#j`LMlI z@2**h?dS?yG##U<=DAQS)z}~v2z@2;9;ejCWb1+poPEYvN}(wicU*Bp}r4~B|(p{G;yA@9Td z0VW$4Z%W%g>4lS)XdVsk(>t}%syY(0tf}W<=8_$GB3sC!m(}IWkt*A*={&qguN+6| zO=Ml(i48&g_LMjL0zDn|rPnm}OZMiZq@eUIp%*2m&@Vai(vKdtM_vTc;jV1_kw=Ll zmfug(WeXPBD|uIhK;&(IN0H{IcDXLQR0;}9+s~8uv*Mzcs#y}in0o;ys)COxb!?L6kHgx<^+pM z3M&dj#bu=)&y2i)HF89THK(koYJRXZWEB;bmX?LAis0N}MX2Y=O04Ua*uBSyEJ0LMC9F%nMqD^UJCz?Idu1aDG|EB34st#@drSsVAp- zL0MJF94ol6C`ei>`jX=L#UT$TQIf~gF>3tir26Ufjv77EozAk;J2ErFoo+Kj;d9CD zr1cn?InJFw+d<{e$jk|yqnj|woo?=Io8R~(bY&%T=9X2=FC-tDQxRNHTuOfn=df?s z&yP-G7t%XNx^-oFu&8+MB5QUT`Lb}X!qPcbWvGHZp}4fXN_c2ld9bu#;e6iq%6P7% zs?;J?T%X7hzQF~Ec}NM?aEpOL>!QMv;yKpz({ep0=Hwh>4VgZhoUZEVKyhhdMVU2b z#JCZohmWih;Mk$^w}%4~A1N1~=1EzYo^n8+UTHkd!k?Vr|9nj1KiPYx|FTEU9=k3g z0{Y4KCNfTk(|wEZbND75K^}V0&llCCrXH6fm@toO`p?J3NRwJ62>AX|zDY+O|Is{p z_~XwvGERrneGc>+_$D1Ad0Hj@{HZm&EbbXgO?{&WGh(_lmQTmNJ?T%J?oLG)Ol3OO zCqvQAWa2p8455!jP`#8l3+yTqWPi1*`5*P^zuAUUsVK43P*4i;TbWfFjVLnF}t$T zGoqqQ{wOM&&lQAc1UYkX#EGX&A6^+MEV_UQ__5%;g1Hri^Mg`I*Z*3?f=$EIXn16) zAKy<#l=d`A{3-TpHZdkipC$>@(&d@AHexwkX87nYPoBf+%s9Ix9$8uV%f2>c9Ywd8 z=hi+ylAdMxDqHDF9$J2b!C33B=_b$xKcArib$&TVUL0xQOv{Hp9}m~!hW;}}UnHLO za~kFEKrm+N?@`a|J(o1SssCwo!?Y>W_Mao4s8#|w|M*$`yv{!^E{2K3gZU?jlc!Lg z;(ty39d#$__FVbM2Blmd`pJ{OrhLYnLE3 zOT3%vs0q_MS(tG{ImIs3LnG4;M`}Fvi8oU>E4>~MH;(rLJw@F_*f9#%zxBC5;g`~x z4^daqIYR$c5oS1k{Ee=h@O3Wu$Aq&!Z>tOt^&DY5LFB||GT}^rw#r;luMz#33SVa6 z7b^SliFt)QK7-m1(A&vwBtAe`+m zkqaOlqQ0eb#D#vjqQ61Wi~5)7UsCueYWwpc>QXv??ZT%o+0jYQ!GyCu{nXxbqV6U> zC%VuFUGRl2_zjBx0Yh0!kLMkE^4zcR?-VZTak}@q3!g7t@H8rltp9MXIOq`dJ)MUV z?v&nK7rfX7zsd!_(FK2iaMtIAK5(#rn>Pi}IgL|NbHAXntg50YC`3zY zlaSj$Wu*l{?o z&dD=h)4B3vW*3pf^Qb=+swgZiC@L$ZkpNCE<+*2WMUa$^->jgnQt|wtGJRnvo*pCL zv=-DFEh8(s`9f9YJfSS7Sh}6Tr<^JHaf1vDUS3vSFuQ6ljgyeh+!EHTfTpOVjGT^) zTUA)1v~f-nB(<#4-DyP6v^2OtTS@0vRj9b6vY@J>Sh+&^BDw~iRD6Ug@eEFUU`~*% zUM8Q}yco(vxY<&ul#oK=po&6qnb3zbrK*$$NbLM1h4?`yo5j9V|w;k}97!DV+IR^k4JQ-ySgS|DeG~@3s60@EHhv-V_T+ zZMpN6+3^HTO+KR!OQhHF zJfLus{@lZDeOLvZ-fvy-fa+greO{K2dwyZy+Ww~-c(c-fmcm&dw0}9^X#Z;!&c&X~ z^+$@&UxA)YqV<1P;ijH18o1VTy@890kNCX<{87(#z){abhR{QFm~sajxRyKA!1eg^ zXoZ{hIRS8#8#4Ild|71Rby7XgBWB#ph zQ|_05quhOlN`_3&uC4R^kAP#nuvXzZUw+RI((#0WYdxPe@TH1=J@7|=cpq@o^POQ* z4nNLG(RzNbaMPdtM@o8)Kc?i)H1KAHR~Y#73cu38Usd>Wh4W8VS?lvA;24)i443*a zJ(Fnqs{zM&_y@pw2u9N{SGczS>q?(TfSxZk{i}eZJ>Ld=1kj(rKj>h#sLw3Gc?pH) z|0}?`C~N!{z)|i#{4gC_e_hWWqHvaL(SOZnDB!5iXoHWgFEW9CAn+M5ihj^x${nn5 zQ*I{UDED}SkCuBP(4*XEk4coP>)96#T$h*i2EIY%?>h?Tp9AQ>w$CSkqn%$lHc_sw z?><&I^FjZ-d}1QKuJ5i_xJmyzH4eu()_13?fpW%q&PeN7s&H-pv{Wgt66o24n*Nst zeU_q+y3jWm^jiPl0X=_qO!I%th5i|!9|ZJo1CD(9PL}E5^rD{(Q@BoVnbLm(&}08= z2H?nNzQLzP@u>!SD)|6jndzOaaf54HpU*GHDR(6}yXKz2Cc%mR?3y+$$DfHY^jT*it&t zo0?1LM4R|6XD}LOP`s#2OU!!E4#{j1TFVx{LqEiJ{gRGfebkRw37?K3vX!}!H*>#w zlit(h1*@zhU1r&r{CcKB<8Nci_lPf)w7mLN$dA&B#HR2%Qcc{URgtuamwrj*8d_Lq z+IpW)P2om5DYnh}G8SXhektq~|4^|BFTFbRpr93-H?Q&2N=fli2V%pkaHa5HP$IJ_ zbuJrH3kI~fa2xF{x5gh`Bn0M0+H;3C1|q!aQpy9ima9pYy^%HJZK<^7Y!~;Dj>s=G z$QPO^>Ff*aG_+Six-feDrzNeT?~1mpP0Vwn>YdfC+m=xX6$@sSc=;Mxf)jzeXeXi# z6j*YTPki`^RztGKcxZ9B?J@jfm(ZO)a>A>|>2*RrHHjT7*p77B9`_izkA-@jiuL0qvGh`I&-96hdBa)qCcmV$gx0aL zS{X{MK;#qtwgD9irP(7wy`fG1l<(=iYQIoYZa@+%-$cw`@u%{dT(N11+#H- zlH~^yco&mE>@JH=xzXwLp%Y)C>&ow(%%a;PDamxZKPga7Kb2_~(j}JyJ|*n@J%bQo zrI^?cT)()MOFMGM-4soe9g*(@`@-*Ldvn*iu9VG5xz!lr$R^6cQ0>u&q0Nqn230b0k*TegOQ`zAAX`U z``CZY>zM80&&>2b_G={9g!^<`gooxXhldT)AzJ9fM_yzjxxDb|==6J|c~7T2NniE| z6n#MJ{FyWmRWJ6&*c!Q!r*ns{&5iss5dA&dATXijiXpU#yG5J1KXp=UOkdjYoi3j^ zNqlK5j|xwp33Yu&acOINk-V8XYHv;`i?%nj?Yr*H33XVvdo$TexMp-Of9#~55nAv6 zY#<_?o=T7D&Qw6WwU_s$1>}ha;k_>6)tNMijxfp~S!6y{yk$@cXA(mGXwivXgf2O5 zh|8Ejb@6ts_PV8bYlFnDSw`&^{xlHm)j9KdYtN!S2){N!-^ZiqnWf_;?USF?hXB*O7P_$H!~l1SZyYW_0Kmni@90hhGyrs zHeG`2cdO|lYp4zIl6I|KxOB%~=AmzqkxjrTqSQBuy5F>@@KfH&r#6*4HsrPb*z$II z+8?9$U!tNj1MGCo9`!AC@MP{wyJbbWm)XmfL#&>g4vKr?&O&y}LW?5Z!lT8*4NF9y zL-D8+JY;Y+_vqm;OpOF?%aRn1#PqAb{KcW1ep&LDh}#kMWi!!kq*$i}Ad2DiqEc8YN*{_;U#se1_pZeKkK{&4lpmf}(YIAqXr!SYtWCSa@LQFUa27sPlF8W125F*{4Lql3`3TY+#1=mk7h z%tSR!IkFMz)`Ce5TJ1@duFT#QoGX?UUQO4WhQElCDKn9A-M#c)MkL@9S0P`%~MP+@hEKG2fpc zp63>=m03A-4PE+Ud4D3EAGWwmsaR!t6&aWfLs>7|@Z261%Q*!G}K3 zN*kQ9_yC=xhU_MycmyVFxPso(l|d&qCX+h5pZHT($mBI}nyJu?8I@HxribYCJ{N!L z2-@E?jmX!Li8!VHlvflV`fg{B?7DvA#RJ^7atcnO1VqC5{G{G4pusPcoX;-}FHz*%$)79ZVT(+DET%%6j78K(h2CbkF*owk4`plCE)dPDBUk9i&@#mhY=+Jl znr*O!h8FJIQAzd-6{aW%Oq|V5g!t7~WC_4S=lyy|4MrfE)=Vu4|7N+oLJZ08*lq~6F@nrE&mfPx~Xe@Ru(M~Fn>*^&{W!~}|8%__N^hGmqS(X0b&Hd48+>Z<_S zK~VN@d0%6ukTp#9$^dEZ)+;!RX8a8DK}pDul;j(@fzXi+1yLk!L;fjjII>mbGaHOI zVkml&im}Lh?>&4eTqNar%3puc3p7OhB8{fex8QJ)TLhn# zoKmrQV$#ic<>)@qX^r&oKkLLM5PY2H22`LH-|P`&a(6xTgqR?ZBv|KdoAFPsA#@;5 zh~Zrw|BN=sl<@X;Dj{if5l;{ zzM*eb>Ut}Kp5Qn-E?!~15+@&|D=hNFW7MOvs+u;IyJabD|0F58uIjBlOzfB}<6lQb zh0B6|%prgP~Rqp*colmqox!lPV>q; z#KqSbrxVk>>JjqRg#I&6^S+V$)f2laqWkfLJ5YVfR?f?S3!-=5_-qE&fzrBMdN# z?U?D?-H)(vG1fu`A58@(d42ul<2}=@c-1^iR2KI9F;x>jIe$D+%tVS3tfnvdeF3-D z$s07ciSy7lpToW3f9@}CQv9Wsy_|>ECJaPclxYwo{Ao=PU%s4PmC9`Y+z zjvB;2;d$s5$9d>aspY}(qoaA~jaxDgExstOy*XjXXIp!7LM1EO$y8I*h~Jvd59K_G4)3_)p)b=@!+qDf9&L+w0G2*%#gz|t!9U^AEBZm5NWK*PnUqib zDbG{31tMSSd3YGX)zVy_em1x0X+QHYhj=$~yEDE{C$al3daLi#L^9PL=t>UdZCkn$ zG-|h7dE0_@AIDG;GYsNTwlY-kS7j@UeyZwJ&bj^I>pl|8Rz5knE&-9%RIffA5d)~v z6*EK$Sj7ZgDN=Ggmb{!9{Y;C}O*9$()AAhcGTac;Ro502rE_S~+b~GNLI_z+%}Ojv z<#th8NS9pO@u_nwBwZGdc5_|6-mJ@$FQLMACN(8p+nEc*B9(;DoX}d?*5*qHbK5sG zrzbRpT+QdL&FsysAzVTwtf_~Xc?lJocVZIr_7`hMh!w4J5sQcLL4JYNt@roV=5C$P zK-$*%l2B&4HDBV3homXkueWmXkQxjV6JR<|#99&BE?9hw$A}9Kj$NM^7na&_VVR5z zOJrQArW57X_FpTKSjA>y6|2^Yj3F%Az<;Un$^5{7(LV2~6tZu|=Dfa(BY&jQzKxAWXbl=V`5TT(Ju|VX6?Y2M!e9)nrvdtHW zuw8a85LrnM#m5yK=~w@6vj3}}Y--36rCk|VcND{Ao3OUt66;K7l_E@iSOlgX59j(bPLxl$Z4x^ z8b29dAmR~9Ryi*aX{Yyg$<|_l$VR%3FDOW|K;(z9HEY+&1tK!V4Qz(a7@BRcg@)#V zmJa751(7|8$Uj9R;wQa8ghz03fe4S?a>_ecAhHERw}~T6n=KIG`Xgb1h^SC>>zx;f z2tZB2j7`!AR|`atynhn%ZLvV)!*{vlb}-?TxIko>(*lv(B%eAF2s-?_uvl5m3rV!I zMkUh3?#d&>f{(pGMEw-AinuUB;BtY8BKff{5Rob21tKaT&I?3ZL`>Rjfe1e%7l_C! zu|PyHk_$vOaKQPY7KpHLdx3~lF!=%zPUVicKt$AbJKU-cUbf-7s-xz^d4b4_-urU$ z_;uo?)b{sBb26h@{c74LdTZ}8l*l;NFT~|T7jFBsn_|P zL>%ph6JNw?NDvfC7hKrlr+UNp(sP1OG%KI#b8om_08=w1coHp;@#p)aCk>(V3_54f zc@~{B={%3lS#&PBQz&C}CrMq$?|o4KucqfS=v+r2z@dNt)tVULLBOd%uI-oUJ4Ie9p--jlJ!wH4lL-#^;p5rtu9)qtj)EM~z! z>v96bkbZbtAJY^Q-6(D)3f5b@0`I@%<_ZglHky-F)1KlD&!jYyu)xHLRkMW-ldJZ& z)6c53`nB+Zc#8^&r**g?HQ8WZiewX*IMW;cVnjT}eJI6(*V}~Il6mtuze~wts%W(= z0N&a^tLM1pj@FlR0f|r&>KBTuscCdsEicLE>+9rYf4b!F%J?HwEkE%RmpMa(1&I$8 zP(nm6ift+oCZ~V&2rc$wkl0;FPh?yE$XC3wgkIIBr6~A9LQP8#?owPO*vA>M3W8Bo zZ^Pm8f)zb33KKcgw%&=uANkt(1qqT#;_SEaDZ$%hGma<+lfB3mB7@?#;7p=sF}3za z2ckKYSCk#JNKU;ZL=&{OVTCtSG2@qnNXtM`i_WcR9WV{3oL3^E)tqnCInEvWQXqPA zfAvftxw-`l5^#uG`$HFWuWAcfx9>bGxQzX@W2C+U>hQCEjvtk8>{ zMI>q6leSh91+9lS+=H$~0h@%Ss1KR9^?lm0P3Was;{yqFE4vR7t%x?>+Ch{s!EtyM z7b5!QX}yPT3+!x^!cnL7w=Bg7M+4}F8IHm?GbqeQ>v%g@xXR^#SNp1T^HWV(7BL`E zQ}a|e+Uc8({GiiUA3LfATA!g;T+vv`R*(IGXl030Fhan?8fsOarR4{fciX{iLYnTPDsi-%=q`HI`p!h#Om%0#Z= z&|}^_b4lSZ$(|^WaXd$I5!~eJvzKBQr{wZC5IJGAi44zD{a39KNgTSSSM`ub%%7sX zETNgP*~^i_pSQ;0wx-jq~)UB-2 ztLPDO=IHcgbf#L5zpE+QYAfkXWm$Y!GmFmTlKFn!Zb@g|j1nVicC!hzElP~JtAMW> zN(|QmTw_nvLM9JG;HP-n9(d)Tp65co28_wrDE*-Jf(R9k zv=?K0$!K%wk2sSBx4hZ4gVY@lQJY=Gysmlygq|*iur9Y8oL0KK>>rrC(Vfr)Nt*dz zi#5@z&7HXGAUaS@V~;Iq$ zz=+Ljgmu+nAl!5*Y=PtbWc_4EYk9qz5=yuTX6lpSH+1gC*T8X4KTAx#J`T?$j++FR#|35}wiLz*7X zBh>ttn^KGRACKVZA^j#XmBG=dshWFVyVL)+BSdsk(-lc3{pwo}rrl{BdWhcI&W>+h z`?A=8RZTQ>aXMmKk8e(3-Hef_ZW~{L0K9D?I+VwsY&qI7;%jSqx(=**ypGG#9$c3C zyN#~MGFMGBJRZ~fj90VphPg{68whg2M(rIL8X-LrfP*ur@0r#yh+!UPx zv3dFS>={%MU8-uJ%>87GNcXmgMd0RDMM<|BQdGLqq5PzIWZ_S(-)w`?J&1JMCUk;- z{bc=O#|WLGIXc7}zBL|As_BYa6%F*5N*2@wV}BGWJ~AIAZkO9-8NAh{Oc#{7dueYK0?im5Aufh zqh68szNrC@H<@dyc6&T0V72xTpjhbI`X$8}*-u<(cBtr%Id^!uT+WrSl#UzIt#|8C zrTV4=2y~n2zlvzpirvKiNpw zy*1<(uLf@0r@n2k`nL4?wms_GcCT;iRo~XLp0|jtSv${sQzrhcON)Jm)@sx2XrzH( z=cV1u#as63;|#DZDZSAr7E;q5&^;r*o9Lx2`CBaF4V|3$J85UkelXr2lTcHZMV{`_ zA!r@xBZ6zfLYBCo2EI?j0~W3HT`wm{vn4e>9?mfd`_H14oxunxNou5b zrxWwi%;+mT8^tfV1}7R5=8uIZlaX^_3pE)uJ#*llau1xQvMn z812Qyz#YR#n@!(h@6;Z7UKROn7zj+HyfavW|Ug#3(cs@#c#fpRBCx4SzfTfpMtPCc|ck&S7g zROj7^m#y#{E}e8ICRHJm-?gPn4w>oll0#;Hc}YXC4P0`GAd4<#$>o9Au5LHZbq3D( zsSsRE$)*CZ7YVRKaO?0hL;zz%1ibKp5)c)SXo2f#Bwqcn>GXKGv)|q-L8ql?s9xYw zkx;$FrGlY)k4wcv^(vPNiRx`G6&2MBT_P|BVz+Y<+ZhxoDEed4ur+J)awGJSIxeYO z;?4Z7MdyPfWv7adGFNpoK<27$3CLX4jRBdfQPm!hxvHB4_3}NsZQwe+i!63re&?}2svF6Wz5xnJRn(UR{zsy)&ABw@P7Q2kPRlgPDS zdZT7Bxr%l}5(Q9}NLH!!GJSWTahD@gYXOUjQR`1-bnK)&^9rWerGf8vA!fI({e`W( za*t#$wKWjAY(Z90=axKexM4wBQ08eZ+ni~#SKG3fd<)$CL=4+qZMt?QLjM@s8OdIT zb7^O6lL*ZY`J-rWv_5f)aDB0b#by!9ITnez@L%(D&hF|kc;mfxySpp5*e;}U$i1+i zaxY8QAls)w@k95vdIsW=TGq&ND3D?>y(shk$(`}#)Nk}_A|Liv@4@oooN#6KrvP>C>4WH=tb~`yttQqBujp%xTBG*B(KKX!&u2*(t);z zemzI2N23+7EN;%$tfmfP{B`_R8aq>iPaTY&@o-1yJBUAYJU{6K9R%%;$vDE|U5TA7 z+if=>yDgduKc1lYagOe#d24x$OS}g(g6e1<3A;kq)zBd>A#A()-3}B!A@X7P+%^*d z+rsaDJR$8cF?}ya1$eAQ3<~uX^Y&`E;%64pcGk?IYLEGUR1*=ZIoWoW=%RGkqv3Z_ zT9;0;1E?BCJDhNr)j##Ec}(v-hQK3eW}j>47=lN5(#`ockye7u>|=wPHyt6GINZ3nQ851>)x3cHdKv+`@>1- zhZttplQB7zz-}^6%Y^ux#_0HnOEuXuK1_7BH~c6O#cy&pbEdT3Wux1t^SpCBz4OaK zemuR7x#Jyr9V1n*V}^J=ftrRh?cT0Wyr!@nn}$1k9i8{PA5X92dx~(PsU}@Z^g2Eu zY`b=7cc75Av)Az>?sc?g=;4_TJ04>DUXLcH9RRocRLajKp6c~?ym+MhRxJI{1JWyg z#xrr9j+x1pZ^B3-t_C)iZ+3DHB{j*~<%Btu9ys%o+SNG}V({ZxGO9*u>RZ1*O3!Q0 zcIjR@jH+BNJ|haf&tz+q9w7<1Fwa{%e2coe-fK@H4r)Xxab=z8Y9FQV9$mFJC5t1C z?lktLTk*Ecwq9Sx%{(!xlCWkcF0QLFNv9$2NZmJ3>&9@rp_&rAC;e|df~d4;H6lQW z+bCM=kxcG9h{l~>V-T$>q*xmxAIBLZAAMPq-_sh&%`z)KE^-CkC1xFsi_DN@@=ciS zG4%a&sgHkNvnqXu=-`&HG}v4F%@MBt?=aZrz4j@h7fy{XUrp{VT>dWRrk1g)!gn;? za~Z;Jy`0D$mJ)euZ>EP_N~NP#neM6JuAEtl_vRGT`4;QQ@->aK8*0;bH?C?wN#x%9 zrpJiOVtO+(F%t7?rPgJ@Q0zC>-AhS!pspjdV0O%*_8-q^Sm9t**u)m?H1Kt}H~bkD z0KH^xXJ@UOW^{fYowXAvHtKipj`fA_~+7W z{&_^kPsPUQ zcJ;cQF)h-PuxrAPdZ&eh`|E1b8~!`RvF++gJ5T`Y^e}z0w;!R&h^;+i(~eFK(|0mb zcE_s@Kc2pD!9F@@{s8W~14Y74yKgeDa&g}u-7M3OXMxF5+LrX^8gaqfc8&;kZSn1y z)FG~Kol1Xc;D$QE^HfLMobMY(6ynz5#_+W0Q1_v~+8Na_{b5NOo^Me9TMs!tulwST zdwcv{-*w#Eqe0(2+E9vcPwN8u>+hbOKMrKpiPI9vcK-N?CK6p{A84hFIh(Na<$QW4 ztb1NR{&~&OUDijH*<+33(Tj^m0o^rxnCA3d=cqE6+<9Jm$ME4Ds7J7~e%e<1ueGkA zy&0638cQT-By9=atR?3at5&XJ)Y(&MunwRyxqWylT9i~V% z(RIG1^=u)%eH=K2i)Aq z8nHsQ<%9v0Wx3cebrM|Blj+W(2ftmZ;c{HJTM1_S$^f zy;N^LNzb5d|N6Fl>)ZBr*vOrB5hrHaRio9_^n=D2dbj@N)=iu0TW_bo{wvq=j_zu^ z_nD6}y(hOc6g)3jVU=AJte9I;w!m62FIZ|76_ylLl@x}GOV6`H^MY34{IaUjkX1I< znjf5BRUWmP3}tl+|;U~rD6FDag19P%8STT&7{ zudu{A%$iFK=UBz1R$(YqQ9QdU6g;KO3YLb7LyN46;M`zEu(T*BsZI+PRaI0LUli2$ zgA2E6K+meX7ZsLNsf5bp(({G0OM=1;AHgiOCv5h7JoDL3mE;?x=P|vl@3?VE^{p%@tD^k1>Bo;rO3%#c z0Q%(iljKOBxrO{EY#DuKMpAlf_dRyg#+>L-tVZaYhdbO%T7Kd=;g*qX3SItmNts&) zr6*pt!M+)zw#;4`qemxAzsis4wo9rq+X+Y1rZHmx@r?UJMhE3d$+zjpCNHm>({G_Y z#%`JaWQMsk#7`8>Bi?jXVLPCX!&dweC;Fo(s4@?(g$7zAX|kO!J}aGo0NTg z&J^DXC;D?wIysPc%BlIkIBn{*>8GDDE+qYRevTWr<#MdTxt!XG1D%wJ-yAON z6qhqTx!c6)GLyRthl8_G{|U*%C8#Ls6`4n9`LmhjWG#REnB>(3Wl=)9#*gb>zw`*MkUV&QCNNkf3)2?2p=;w z-MHl0FmtA|S)9HDGi$oc!3Sh(S$Z7jDIanE z9mGdR{)v2bV}-K&IJ^2C#0|zHY&qkSw_D=+bh5q~-O2JWdP4GmGt;!kgyi`)bLJ#& zblW2ocI**xX<sbv_(H2^Q0_HPdT7Zue5p^U8Q%mJ-j}NkN@(Q zbh7tM|7DMyJ$CJpThnXtuCZON?fI+J)TIXzxuEq6N{{E2fk=Bi(Ee(mrd3^Q&#=)z zn-8S>jAl~7cd6q05jT|LI!vqCb@48-p4X{{9E}9=IoK5FYg*?yGR~5mM z!Vw`F*z}B;Qy41rjF??n=@}u=iZY3)4vl97Rl30uC!R8WIE}UzU7)Cf^9ttjV5rii zD<_K{Ci8k|jx^!OQomkbmjH{64G}qje(*f9d8B_Xk@dy$OiOQs95SOgXyLs$=%sLh z)68qaap3a_KjS}4Sv;!GJh%4war7*wpQfV{4ok^Tqb1gOX#F)dk}gc0_+HCQ=IZg3 z^XwNLoPNI4&+GJacw@>8`j5wu_vA0$rvnekRX)S`iS%D%NH#t$@HlSnrXB0_EvlJnxpVpOZmVVwM!OS~;#Co6qdOUOGDgM{gpMRUWGjUp* zd?a6G4ljh+opA|!wEbBpC;2lzmW1~inQO{d>^3_wT~E&^n>M>aKAgNLvHDP)~ z3B@#3cvPYUcGn)nf2|rY5-}}KuT!7+OsdG4M2vr>iakyf@x$roUZB5-#e|(q|9Ks) zc@z=ggrDz%U*>|};)37jfeGyDL3xm7MyYX8@c-F+7x=iUs(pBR0ZI!@xJdz(0ScrDC4~Yl*MxR(0x3kGP!UW+ zlhR0Yqq|d4v8a9-STAZ^-dIuD&``O&0x^r>Ex(sF(nA0+?pINR`JrW@MGe)}Mk$F~ z>gy~!^ITvCG26)eA2%B<4(@3=Y!YsHtu=6c=OK*YrOwT5jpmDFZ{y zbXJR+GNCDUQ5BehXra?76_!=nh7Hw~Rn<0;^-HVkD(b2)uaH^cYy^xsDnXs(OQc`DW{nCnynigTw9_f?lFGr46R3fu;q}=(0O|$Wf zirVVBtsnSLU-c#p>PGY<2Ywa8OLcBUNq% zgYVAH^{}#{X3_GT(yNz2rno5OcW(bn=Y*wB&(Y>#Zwi|{mrmr!h9&ZJ`Z>p&8W$U< z5PulK?BYQ63&|(YxyQN@E(#f|8jMpil1%||%(N*u?E)gxtY>4yABD5B)|TT+sq(f* zy)K8uST5u>*98ujpGo+81N#EXF@7nPJzsFR@?Rnk!S9|o=I{GC!=nvZ75-5L19y-F|q7U11HsNfCxgH<2=UGJ0 z_PlZjE6KO#&4jZ(HxQ232-E5P8R2ZVlnZ@*{@){<`TsBByA%IG!kPbW(viTScDv19 zhW2r|$mM)_oN%_=p9p7t_7KK6gb(XeNI2_rsl!#D9(fqYa-wH_j*v{n;oGO2aMq{N z;mZH-_VLhCqL;B8^}__=tmkTv&lbn0o9J23Pl>T`_w?{RsP>2 zd~d>c6{Fw~eXwUnn*LzIna={kMOWp2zr$7ki=6%sdiYX@KkVW1wRbt5ApSCjr2L;J zobCC5RE{|Oe3$ny6_-bq&#v+?4xyKEL&awiE?KAe#e~c7qT-hmE~kqBfpF%tyJQ{? zk^5o%DgA+jGoSf{vz==RXZmuf>~Q#Ys3m+q+*i3@BRoR*TEh1y{0YKY?r#ZaKZ&~m zTlEL9GRIX87d@pYt3F>Kob7g#$0uglgl;8zac$*O<;LZFJM1Wh4Ts>Ye?Q?Yw?PUO z4xhfo;hL^>&Trd2T>U)d;eC$&%fz4k{9A;xp682kaj4u^9G?mgSGiRlzQxhkIb71k za#s+}az7@O1rC*4C=cTJxWnaATvF}d?9nfC^zAw5zwXh0)zRPP;hL^45ASmHcN2e3 z*E+&EU586$heOl#l;d-xhpXIUJ$%5?mpEK5S?<|{v)qG(2#3nGSE0}m4i`RLE@l(X z{yEp65 z?je;d4wb9z$le~VazE_hGo9Q*hx_?*Ea5D-!sDamtIESwZjFa8c5)kuKd1LIgtOdl zdwkUY@A7b!dyj{&a&jLe{w((i!ddR0q%j~n+pAFMHHZ6tI4NQ&eE-?g;hJCShx-sc z`{7j{z2zDTwG%!2{}%{nzrD%hqjGO0dX{_1{vmUyp6i_5RybVJbs+xKKmY8}KjP?L zBYLj)j**9P`1U!EaE?Rj9IpB-v5$vV5Iy(%Itb@{?DqKlcARC@L-efYpFH|&o!r0W zp#Sa^OQ3o>^3V?*?)%AOgmZrVug6F8>lZ}N`8EDvE6KO#L4>nC%Lo^invWHPv)!g1 zlAZr)gfstU!lmj`{#}G~dVfl|6hWnbfpFI6!oonG2+?;EF0QA1Ruj&A;!^`Ytj|iq zIb9DBeh~40l5pnVc4&6~-y)p(KSQ`=h3fxL!dcJ#KN9d4Qz`v)!dag#!XrdKL^#{+ zpM*0X`QC&azQ1iEoYS?#;Q_sbz^dmygfsts!a2RK5YGH3PYd{Szy2`7ng2Y8PsC#~ zgmasHJai$^b3G6~BH+*Z6cN4)NHksN5zhKtLOAPlkHb|TovM6*=vkjXdGy8Ot$u&Y zLH|ER+4b)uob~^!!&M*c?{D$&qA`~5zdT&~`xB-Ia=E`hneYOTsNFUb&UW7M$bb*) zc_QIl&r}o6e7;CH*E6>f&id?rR3Ml6oJ=_PPtPQr`CLKx0hHeB31|9CXJnU~BAolD z4-g(9{=Xue%k@iyv)pk<2Xdv0qxrJ(7>lbv*sDC(9U4iDFO@_Qb>*p<6(;?MD9FX61`r;oGx zs9cSon>}3Rwt4s}C-(-2i+xz`*9m924aaB7l^uxXxWeJWhwGVJJ^Ft+`a5#aKjhJC ze7?cMHC>N-_+&Sp{tWTo7dojQ{+4h~*BK{BDuRBH_D|3DaFsjX!(&eFMGp7v87G|O zo_b=YTzeG?l{;McOv0b)Uqv|kXPw7K%S9v6bGex1)^iGf=JQj+SVY`{Ldb) z_IcIAmpS{qN&Goqc0SprT=ZeNM>|~Qs-GX{;VSoJ5ASkvONl?roliK+ZT9%6AGUe8 z%DvXZ2b|m+i9gG|jc}IxgW|vr5^`$3|Ha{Ihre60(D;&oeh;Fb<#45+u)U=#A$l&S zt37(<|0vN*h^qR0{FFd0>v^-o#b?C?&2oaH*Ws#<_6L7Xe56aH{O8OzkEO>Y<~#k% z96lDeS^qU2eY>MypM!qqIhMchVg0`t&6I1eLZO=-F7#X;zfU-qqaS&Ev^@3@J(tHD zPR*37{&u5>tG|84!&kfX-sW)8ll|>`gtNbyg^Qrod>L?jCVbTD^j_)F zU+CytbI`By=+)1^P4sM^<)wk0*?+!DILE8&31|9$5Pm4xf8uEYf3{B%;Zupem~f{5 zoWm!Ac02s3e_rR|nvb9N@MdSXuQ_~JJ`&Dye@u8G>ABtMxWF+Hf1;1tc`t_xf41{< zk6!)o7!OzdkN5B{r+<|AbNQM_IP3pu!r9J0_W1WZ{?8LV>wixf5`;t2#rgifgtMRg zi*Tktdahw(_#BE;EvHe!S^vd^v;NBnXZrgcuKA*V@}P&Sogen_0cVFNh(FtTfN+)@ zIm4z)<*Gj%>2Tj4Dm{8#kX%Fb+`gQ4W+wjwC)n!?9Pab~NV&c5=T{lwyMRRPA19pi z>rTQszaAjGfcX5DaL%v45YF_oK4$e)yJ>!%pmk4J&gy#i(*lx!X&h(#fxazNVt0Q{0 z+f{_KKHnmo`TvG+=JN{S%xAB21O3_lhY-&6bq-hk)&7khuJ&K%;mywepCSHi{}kaY zcg1;uo}AvV6VCSENI28KNjTeQm-zvImOGtrwoeJ+O#c~&`}X;qhpT;EVt~F+3mT6aJK(S!r4Ag z5YG1bCE+Z0Tty(4?Xx@KOn<(^efv~+xZ0=6!wYw`^6H2``{#1PS?*mPpK{0NheW?C zSZnzo_xBI&;uzG2_G@k!}hikg__3&j*pMxCk=ljuwbGkl3INSf*9{*0q z{{fCHD#lyEaxnrw>{9?Jg5zcadNI2*F0O9N>q3VD?)6XQF{Uk;>%dI1v{p2db znf}KP_w(gR4_Es<>){c35XY~GKilUe!ddQDt*rr$I{@q@NKIL$+8|xpc z$xgqw!+rYOJ$fw{cX+s_>w7-#($!7;IbD5(bGnv%%IfLo`}Kr#zTZbU`^ig$9}1RQ z&%8-E%bjvbAa^R!&mf%Xn;kw8wAZ^&mDxb z+<$s}G~U>`E=cczI8}c*-QlX|W+(S-qUU(#Dvw_IcMv`E{~wQj>?Etl{W<7=;nAyK z{o2FTZol{N36B3u#Gmce=bxs^>R7yvXT!7x8C3 zA0nLf+)Oz8VeQgP{WVUz%Hg6v=gY%{a~yKRrIwFhzE%^y3rN&|1_VK~+NdZsDBIlulwIOp%=%d*qY zbhz5#2A8hWiJtTQ6NGcRt|pxIzkzV3A9T3N)qH=+!`1$O_3#d-=f8+Q+kdCagY>f8 z1C|9i+pUCfw%f&oGyUfruKNGb>GM^hXS;oyaMtH1gtOg#ML5&XTyFJOxoWo)JzVW} ziih_&Jd752X9c{u(wzb+-5+lxCKu6}i#y)&8e=_=ilaukh$K zf3G8Yw$HWARv(ee<>C&)xg0%7IMYwOCg8*6XdlAaJ|_{*_Bn%artfmN=DXHE-}P|K z$Gbhe+2!K{#Gmu=F~V8yzX)f)o%$J@Ue!nEJ&$*|*pux%t0k}x+igDKZ0D;8XZrgI zXFESiIP3o>!r9LMBAn^ZYt2lT+W7(xS36gFc$c&DB@P!mu$`|UoaH`BINSMkkH7l! zq_)7G2jW!gne#py;B2?c2xmLrL^#tw;Bd`foxgjM=-K|iCY;kX?sI`YZ2#Q}XZj@$ zSGj8c1`k*JU+&@k&i>aBf3|-I;Vk#&_CU`JI}pxx`z_&2KQ>|csQzlVJsmFoz;-)` zaMtH^!kK@9aOU$B!kN!n!ly#E`pFZ7GyNggX6mo@Kg`3`{?k2ti?jdn4)^{4G{RYK zRnp28dQNW(;cWjq2xt1=6MiVA_f^7K?&RwNxon?8!kPXv4xfm}B(6~Ve9ptwKG%78 zL=u7HOT?f3^P7aT+~-sFIp042B%JND|Ml7FFCd)lQ%g9@?I4`((@8kf|HI+FectkL zwa@q)te#3A+r?h*;&9)8K14XnJ+>ozde0?XSgZeEN%&5L-{f%BN9R57B6_ylLxdL) zpZ_DA`5*ZCKu_j#6yeP0Ji^)j7Zc9(>m087tAB3vaJBzqKJM)Qzr>&Y^Y?_a+|n=D zbg5jO*ZY{mB_DSs`z-P3S2?*?6Fs+UE4~=mjqQ08;hewg31|9$5zhAC`Nn`hr)xUl zZ2uC%nf_}IS39WvJ3Uyy>AlIL z*Yw^_^qk&bd^xZ)`_=1&vmfraDm(r8gtH%hif~Tv^@MYJzfL&QZ*jQVLH+Pw9hPb=}?7bdF`2jR@;3BuX_za*ULkNtY4{_3A6c(~fX#KW7N{ZDte@1N%r z&T_wTOLqIOBb@F32g2DtJ9K91ulC=^;lBM3C7ks+gK)OnCkbczZij2WtKHUmxY}*K zhj%%Y=_V{0-Wu3FyTyps>4-(9l!lJ(X+qR5YGDCKsfV% zf^g>ZOTwAYnA-yV+5Wo{&h(c!T=iG`FZFP>f0KvzJNsWv{Mr836V7rU_V{SL^uI)Z zAd;f>&p$o-El%#Z+k^CSyH@0IO_#6^}mmNM9w>iCcd-Rt$`VBwfWoG z{b7gi1UUP{;e@k4EGC@&VHx3U=bH$h3Q<}Q+(|goZ+~Z|oz)*EdARyRfroE$cG$<^ zzCTPQoaLVR-M|hzk)CygvwdzPoaygzxcalUzr94ycKaFOtj|9QXS+@MUZ5w_S36wg zs@*>2;cB;f51$~;f@3-H=W^6eILrO&UG_P#Guz>Q!gs+v&G%mu&h&3NT=m!Xcem96 zf418JgtI=U63%uzmvE+E?QoT=cDvWZ)oyD%yvW&YgTsA)dxr1=vculr57Jdgc+K4b z&h}|1obCBN!kPXj4%c+4AO42u*`BWu&U)_ogFqj)=Rt%s{nZXvxoXc=4_A98JY4s+ z{37vZKl}#aEVuWbKu@;A3xu=XcDgq^{b3GQ{hM9>o=o&?w=)T6eHsa8yR{I`^uKht z%2m7l*2C3qfAH{jr{^ogpY0a9FGw%TJN{tV$v z{|$$$+^3z~dx@Uy{0QN!{~+OP=hq2m`m?*O{wi1PJm16B&KG+4fYbA0hx_IIGQwGI z`~BJN_CJKP-JT$v?e-^!tNvQ9Lu&&5Y_|f!S)XGFXZ~@*nNKU>%%_WRwtqL_OuyTY zvfFv_II^vr*wM}M=^ zXMpIr|MJuIfj*pHe2Yo&iVUQ!kPY6hpQdb|6liT&EGdY zyvx~P!iGS9_W!*IXSo+UT;=vVKJ`S;_P^Gn*LA-)5&eOb?_Yf+(1-1RKjCcuUlY#s z`)myOu>B7uoYQp%;cWj;63+D94p%#<{nvW9+JC)=n~xYk#m9+1+y6PjS?-Kpn=X~B z{qa*BF6D^rdA&zpxVwG+OGMB4^?*mO{C`69%>U57Onu57|05kP`Y`_sJbL9{NA%49 zI*(q*6TY5l-*^8<&gJ{re9LiC(3A9~F4QF_hCBOLDg z;XJ|%@u=4Gm5&GdaQST}oXh*S2xt1=5Pm4}e}!`CB#rE0r$pB}29#1&aUq(3F^RtAr zp5G>%?YV|f_sUZx2^{e%QmeIC~a4-1q-u2``}hy~g9C?N@^6xxUhU)W(2^ z$hhqkt9Sb(`>XhGPP5k?9=_)p7GLGzZ4U4B@LfM@>AO7qH9r=U4_L5a^RbC;G1*cFMIgl63c%}4%`gJfke}#^b^Ecv)~0e z@W~#o>56!`@-NJR7v;cb=D>?{;ISNdxrbj+W79d`!!;ilc=)7+mR{H6tKGVse;4jy z?<-z=rM)il@HHzeKGVbZ`i#YkJ^blbi^n`%<(7N+{nuIg`5vzH3q1T2U$FFxJ-l$G z#g}@x@>%BL-@eV#H+%T!x-8!A;UBug;vF7-;++;><>AX*T-)j4M|{Q7cX{{$F0NYb z;q!VdeYb~ex_UhP7RRSA2i~6pAIO1k%7JgrfxqnG_pGz}Z}ISFUAq<9)81D<{E@>W z7B+RQ)VF$+xf8+UPrskK%)|A&zqfn%ORhe2-wj>9w7PLx{SlGK^rMcQadgqlqmMi` z5-EzvVgFArDmwO9V6%?GKXWXYcy+Yw)UjhkV|$DVon-1Iz1H8=dV=3#b6yuR_4&i_tjI6IA0Nf;;v)>=TxjLc-=pc=+;IOc;R`y~=M<7S!q;8y>GM$~ zbie-da?NEx$zOJ$tFnxZcEC-sl^m1p4}31WeEpV+=*NdIpYQRx&~5#GQ1L0_Q?~>a3#B#3PPAdWI{u=4Yf*e&jJn&a~xz^rd|A32CL$rF@srq|m%r z;{Qs{Etywx&e^eRUR(fU#S&{*z4_K#vBbR{=4WNQ`AId)Pj9qS_{4hKmg2LZ<}}$R zZ3f`BAn*V>)8VdyTwg%1BSH zG`ghP>L9K(@k(i;&kV&sC`@9B-p%rO_&#&HJ^C}qk;jsuwx0MNPN-517DVt!vdt#P zKH1h2ZoAXMA}49|OO=mtk0v^r!ME|Ei~j6{6Pk8H zQbd`-@9byT8-1N(gEs>+bSF2}f=-;ujL^qYZJm%eJSh+?H)4rsCr-QUX%|jc+tbxJ z?Y5`gIPI~gJvi;Nr+tG5X3=9$!BWwmWta_#Z;e2uvQ%|vY4V&Yg{8?kQzEg%Q?aS* zVu`+3OV5~CVm-2Y6If4&>T-WFu3yHrwg1X3Mrzw;`RQ%z#}DYlJqGkNWzbl%yfBt5 z2KQ%TQy-2c286pcMO#lS99y4wxLclVdoEo1@VZUg$5yQ|66EHb^_qpHiAMse`-Pw^ z@!Q14vc#W_9|ZTnJP(r%0ijVrgg%qHVoWSG?`Nfnr%Jjl=fTAKvcwC9b6H|@3@$Z6 z6@l1*M=FLVO}1s2w=A(K;~C_U&014*aAkA1Z5bR3S1yB_aq2R-9j7jXQ7klrSH+U> zjkcbqy^Zc>fd3&eol(eWYbcxxYS97%DY4G6jLbw7jumYN1h0y=;}3 zeS<&!;3)m$QfTmg_m3D#K#4JvDR^t71&<{*J|x*~391 zoj8@M1gBC9N=<}QsRd1yV_eqMM?m4VZCflsAC#Be9Sl49BV>E{`WN=5R7xn9E0t@7 zaCnMFS(QpD=b|Fz+f;Okm3>BaTljlTRUsxuRVwv_IklC{e|}Xed7t4AS*lX&A6k`K z7tyNJc}a8NB~q2XZ0w8rE$t=mZB?4w%Q_CbqC|~e4HRxG20CJ!{O{*TiFIX(KRzg> z(ll45!W~|Hw)}H!xb;mCwfu8jd}7N#$HY$>ya6R7867~~yCVWdDK0}(kc+lPIs%uu z%4#PnH9Xj|Wla2B{JVNb2@40mCfv7-jhD4-85dtNC__8xu=U_H-(z$09l8K&NGvyW zww|1peone?IrJf|kIzjt_(!l-g_?qd9-jLVlg6=EPeK}39ECLQICQ?GZTisJX|-nM zetPI|q(Z8H((lOJZo(!#WCH9*j4Bt2JXXuhk zLT0WHx0PT5&sI?3*2Tym)J);l>oHh>+A7?(7w(SO*hzzaDWtyNI~ZJiwxt8AB~B{J zvP7Q#XNDk~7t*i>MHGGe8+*d{4fw?ky^~U8|>moh<5QwDvfdMO(N zSLXqr*G%{r+-vFCD^DY*4eGGQd#|~PSJI6);&YTB?dg{#Ud|Z4VOgihzH!jm|+VV$?T&`qSP0qtJb9)}N4ewuLQy+^ZejW?owGOJmoyCvIhs#pm z6eRDU=)X(dR+>B)1G}e2GRAe;1(0D^U9{C1c6DLk+MKr~8HK2==cE@*9LSQS|o` z8^fi44Ps*ZTr8Oq(Yd66?mLj6G9r;wu?0m!;HNe=-xJ)}Lf9 zzik1YbQ7bC?P(v-rS`NRr_1c=08UL#CY#01BdspTpw)E-jzh2EU@}X2O6=KT_?qyu zqeDcP2()9BJw*`O(P>Z78|&z@rvo?@Uy5C0`qah^trNytUM!9!RvSg)C>>+-V%^Sc9rRKu`gmz;nk~E7%aV>O-g24sC$&rha;kHAd zmqVPcDF23e@(ZGQ@~om*GKNY)7Ro#pER->I0dfTkW!BAFf7O|ROy%F)W7P=*5b<`| z)lk^e$&vRO?ZrrgKe8$X&p48_eo)v~ap8|Mu~_n~$>>WDM<<}~AHHu7RD;Gw?Q&w1 zYceX63liV5ehhjegT_p~ZIfYEs2D=W9%rA$86MA2R4&j~6-h4jwk=N6SK-X`^n2Up zr@;j{Ll+6`3UC%FN@qnflxW)nP%LHcs7s*iO2#_WE=NlIBz@N>Hku_1lF~qgX0319 z+gH|l$<9M%?gr7$a&}2-Hp=R`jFdVTb96!ep=^l5e!^AG=LJjoOo2peafiR7t#uJyvMCT zOV2K&X#)JY!CiQ65EC5~Wc!uB6o0$=Emgh?3@Uk(9+{MTXgAdNZ2cc@Ex>4)VuR!4 z!qw=hr8AL#rk+nlA2Z{oN@qqC!mXzwG@zDSvts! z1&pckeS`eOQ#$Y~UY&Q=wk+}8`N;U-;W?>gA57=d(q&}{JMeu<>Vq=_qC85`Xmm8O zr7ZCZ{6Zwiu+twlTffR&+8D6S-$9||ngCfBQF>{WHIy$oL7OrL)R9(+S^{}Ut1P^= zwZ+ma-;uP6gb~wfrUf8*G-d7uyi^jCG(JxKR#h%dam zcsvwt{e;lF`gN3HvUoD$DBBY2LQWfVXN1XyuD5v=q)H>GQhJ-aa3#6~h{VI0NT)+= zIqC_o#I$b@0@LtAxIv48ACkvRL6q=*qPc4?T}z+%j8W7jvA0>;TqHmuUYigX!@H|) zxu3q*E~>h#vShocnZ9c7l&eHv*{r^+C&v=&O2c>c#Zoh7h3}jDx4GXKM$y*(fi97EIC`~`($o%w9w}AebQPIz>X>6>i zyExLgv~po}q^2%1e{NZ%s(N8EiYFJ z=haVHQaIV-;(IQp#qwWz+S zt}2UGcxJ2UkBeN*r_7!cJ@uop($h{an|sEY~rRwd*1wypD*?( zt%IzZsz6R|eQRoKt1qss4eFCdQ6yc`>fn{3(6;DNH2uhNEXe)(H7)v{9UAv9ovHe(b!K?XTM|HU9c>u9xSh z8LH~V6^kS&aQeKRp9mh7{NLVVQtYk=i!yJ>$SNN)rs89Af6J+ss{2Fs7t2VE9LYC1 zCflFT!T1&V#)ljxKZi}H3p({>NxlfrZGHY2JS+0q$8i}dcJdcUUeghwb2$|3_SLi&PWJ`aMtI)Nbmh_7$^?A+zU4i*|2Pm{!-kibV*w_pF_w|=+ zUw0v1b+Y_Nd3MZq#<|pGzI@RsoBa8+aRbL!yqms!OEvtz&~5#01fDR*HtX&JxR-4= zf)4;615exJ658x|$j`WR>IpQs=j878xR77l+xmGo$^}zLgZ^Xq6a3Lb?8Byx#ObFU z{{1NyH}x-0JMmXwp^X3Xs%bX-g&cSohR#O+fgJeE9C&#Sd|nQGX%4(S2Yzb~ye9`P zufJtWmpFSie1G6#f6Wim9>Qj^9P}4B`W+9l=I_C^9IZL%Z_a_=l>>h~2mZSp_!~L! z{ZJ&brT3#b@VXrMXLI18ta0S0n$Rt)yr{N%+L1?3n?Cc%qe2xGK4Hb&Gv`#GbKY28 zLE|vF2rsUVS4iIm4R`&L3K>qyMcGh&c}-oF8)7m<5)9PWRa8q@Yoyk=3*22)zYLl! zYFyq}QQ6Q?xxAt=ZfP!SG^Zx|WG6bMh7bmobuGrfvOLBqc0K9}jj(fDrm3;AabZo3A!w{zRuK&Q*^F;$L=c8- zG`Y(5XpC3bEX?(&bWAE_FwZ^AI@HwLjH$1vu3yB76Q-3_goy1M7gaYH`DVC}ZWf5>n!}C!xWr6~zeG6GAAtH! z4!JFFt1A7Ggv(}?il0k3>%WL_rhmZUz8!SORmC;l+UU_2LAD(I#9#V^s*l;H0@tEH z%YD=tTyReBX2RJ%uQ^=x`8b}FV@Kyq!bjHgsN4@aTlK8v{ck0l?eKlVnSKY?70~pmpX}t} z?(xvBKJMzZeH||8Vm~>AaF)B=<1^jyNfJH#$%&#M4&l%CxqxuCXOqJ0(55k%Mhoo@gQ2jSK{zVR#OXgop zIP17uSIaJR-JGtK=diKM+3FrLX;PH9c z@p*>mIgT14ocT-=VWf}RxxmBKKlk+TEl%!!4j22de}05;mit@6**;^02#4?ymDSFN z5YF@;b+~WO^NC(WDW6XfE~koL>G9d$86rvaY|q;WXFd;meAJ$O9nFQU}`w|evwT>O0p z(KG)C=lmS>R}#*4yUF9D>AIchIbCP( zZIU%CT@8eDx;{&|oT@!n5zhL^wt;e}9XegQ9wB4!t7kakmA%ruZQ$0Rv&(l3z?OE>QPVV`{pY6GbaF%ErJO7k$*7F~Pb6htu66nM93mmTbqV}xzaJA>B zJiOi6A?|R=N494(;Vk#}9-nT<=S8AtdrsLOjB%)*e{u9DI$Y#(dT;mWw>bJciJtjy z^yt+Nk9oM-q2I$JZrtP-#Gmc(0^w|jaR-=W89$LOm)0kLC7k_Xw*v!u_MhVkXZp(t zXaD&e;jGVU!r7iZgfsm;FkFP+W~`+2z9ZHk9?Ir?c1_x)!M;VgHP!=-yKRpe8S z&$xpE`$*NO_MA(&6j{Z8lLNoyV9Uqn|D=b%;NW+ zoaosPTM6g|`c&L>xc3?g4CY;kXKseLC zk0PA;ob7O5|3yU4`d>yk^Zz>Gtp6i~v)n<#S^w7wXZ}+T57NbcHIs0ruOXbv(Q?AM z933+);Lm!_Bb@#1(}XkqHyp0^)N=HFqGx-qBb@dB6XBfRF-HV?GJT1|Rj$_Or+T<^ z&(P@}KH1%vNBp@Ryo7L;`<%z8*zx&4qL(UG{qxU+v)x|z__RAdZxX#!naW4!QO5w2 zI^sAv!7<;JZ^bWi_yQ0Aqr(?__?g>VK1)6PXm`EL!$V<9-|XRw9NzBXXG@}Sba?oN zh=D_^Jp4COY00sp{SmbV`mVE2K@MEkM=AZij=spFSG?H6A9D2NIq-;8$Jk!%uSc0X z5lsGcy-?i4b-mV&97!$H{hh8~Ur0$pFpUaWc30Omjc@NNKoEDjnbRc6tXC-QQRA-?^gO`E3|v z>pV|)Z;=hQ{2g3cUYv<7jF90!K0LQj0 z*aT|3&4@bE?b0x$B=JaZbb|yN*mYpetj*yYE*g*btK~av(eAR;YV&mJ!VR&+uXI0# zl7ww{6!k4w@h8m+C^7mQ{N3`iND8^D^{DkiP3xx zOg4vcZ5(zCFq>P&x`5bFc zY|A9}yj{wW+W=x*^zoK04_?gYf zXkW{UzEHUBD`ExXRR6CN9&RYWVguH`&k6%4yGQsLO#YpQT0B{X!!=!7|z(ih8GgFu`4Hr@ML-O6z# z#u=tuAlvS8C!hMXw#L4H(Sc;Mds4ox^rFZc!uDf=F963x^hLR_+GNHe0;kHyo7<^& zS&$Y`#wgS^ysWO_*6)LV52>H)0+<#z)!b; z!IosRBwIEh3_my>DGTa&XKK^yv`u+mq6rlvwlt&0!U#s0!1piZ)1uPaD8o7U*r=42 zyCDD@wtP@5hCL4NhZMMXy6ZIf1+lxm_h7`_bCt$&n;~ejxI`c@P|ov5;4DiRvvS_g zamMDJ!n!nBHf2Ida{Lq!+fRsw?=mL^vbp}cP2*yzJsvS?PhB5Njo-B-xyO{Yr>{D2 z#O`nU8;e2HohPGdL#m9gg0?;99!KCZCtUiwI5?-vq>{Mi&-{ zzU|vbzNFi-p^*0Uw4Weud2umJv?|ebBZ^8p%+Vg(&a~DeeaONuN$qe>c`)yugR7;? zx{nG)dq9nnB|G9Hn(K{r;!^e;>@vTpXy=IjI?Cvc9Mv?P+vKgQvUn?Fp0}4qUmL1%*)9F{NIx35SmOcpD$SmvW{5GijGix66iXx$!7HV zA#AQ?oE^xjpsEW$I2ASy>P~yt^?914Jp(setJz!9U`gHBDmi3W*(PjUyooQz#CMPe z@t~n{!@`8_If9j;X@-QPoH8V6g~p&2pMfox1iv8v0RCb9qKH8OpckuNBlU{h%%XBeoZXN(D(n@l7gOLZITq~=K^y{UK3 z6f+T$ocnwNt$wU`yi6K~?52;U_+(PzDN_@p2WXmsp_6k6knm}9nt5~!eM3gWLBqht zQHWO;sJT!@55y94`{$qsKPNG_&oBy%K9cPQLh&LiJ2qZEevkFi}}@Za;RGL>|oBBq}GH9#6!l z6eOGa@RQDlwAD|_!IrI_ocnkzHBXRDB?*lyZLYcCHNBg#jzuM!Rww4&i6^?rS%(Ff z;Wml(lF>U`dJ6KyYa1*DKFhQ^Iq%NW#G~o$ZpFibZPE?d!8sYl5)XqSRbF7}bo)^m z7{gYp7`Jk+;cuF59>V8dB`v>7CkG# zj32eWzTJp9WetqH%#l-piA@gdnKP$K6OT|%?Ws8h4G?x=a8UJ)Wyys1S672RO6P(% zyDhIt-kw2kWlVn_W9Y11VqLiOG5e(aU&_zFY`#y2o=L{X;xa!>=V$A1C0cr>=F!7u z3QJutEMkoGfraLm#A-x^%&}@)M53;KeIb*P^n&%;t~W%|F(uGG>@X)mKW>@<1*IwlQ*w!br@@-9ncj^^hP zoS7CxKzY1lczMBef;g3LlkCMv2xFPaLFpQ!{v(}GGp4V#q5mDqQK!Oej0;~2Mm7AJ zLR`^~@}E-zgMBPx z>9Zh!kUnb$-TSNkq7jIU!H1ybYUvr9rwF8*F8_lEV|mYR$%P<{^PMk{l`0==%w_T} z=s)BfjD=})dx-0{OEJ=pNJbowy7%8|&@vnwz-Z;f66}v1YL7lSG(!d}Q_&}>SYsH| zew}3MdKuA7&3!U9W`xm6V?tM)O-ah^BbzQD0!b;+?Y8?D-Js^AITNoIfa9B~(B5Y0 zd%A&MJt$i8N!gLxtmaAgF|K*B9eIXM9?DJAYIO3t(8=p8!&jit$-B`nPqCEr z-UJ3|dN5pjZ<#~!HXSjUsOU=0E675BBqWoUw5;f~qgI_VYW28`TA3~3(Pdi|icbNb zRY@u3JNHI=X3r@(XJd3j$HBv0lQA@5UFfA-5nrhML zxAa7E7B~}u3k7&5KM=8B7ESi^ za!ANV(!{i!Y43*GPdZPD*BP7aKcXgR*Q8gSVUzuRO;Y8y`OEDqqjlX8Oas@2X*}2U z4FlH&QC`=zA7c0e(ovRN(lsa9gf6KB)62%sN&HsLFTq`F{ z5iyGvP`CC##len2wam$ppEnV`bu}rtvdH811i) z+PI#=C{#DCbp$vsv z&Dz)Gy>f+qqiqVrA6+&`<=!l^JQ8Vnh~&a7Bpw%8o$qr&)6QJ>%0hw#bWHLpE`2Ui zBuJO1vXFR!zL$l>2idh8ny+`Sl<8;17QR?+uj;nFH*rMHor-Nr;YqcP*sSQRA1-~n^4CM+yDemB!MiO17PSRVrx@)eB23&dAEQAT93EuK z3r`d@S|-{_4!3^Y=7!;HSxLFi5`TtU$0Hj}2)I{%zeGE_7rx&md@`aydC!QwQs{iI zbPBm5H~9I{Y8U_1yR8|GwzsU68Zjij2=mQ&FZ@oLZ_3;1PF`=xh@zX$)5tbHHz}Wq zy;ri%5@*y$@oE0;j9LVXYBMK)lx?_G_WV_6$?qL;h-m5AJAZZxMG~%+Oo^sk&Ft-E zXmhGuJ|}6tG*4u&sm{@cqFuP&WPHl7Dow06ameOFBRYtJ&WgE}EZzwf2<8~g)taNB zPsZfokgBuhB>xtwI?*%D{D-f-^_K0I<#|R6j~m^IHyEdzlHY!CbM(cA7YE6{jN*_g z2c6L>Prpbh+_;tXHB($U0ROa6Y>yEY!}n;1D<rRPO8V(#$!|*SAfd7E0~m>!josM%(^`!P!UhB&=;R zI5X`WS$h0iR5Z!8a?8YhP=Lwigkzh_l8r@k5`Q#r9=ZOPELWQyb4!+H#>QZLus=4Y z$1WG!u58T9v6h~qvgFKdd^8qIhGMT_>DlI!OGLfdMfgZ;f!Jkh)QF8U&p?&=!Y|P_ z0UTn3=-U(Zpi)ZrH2>p0oeUliEZz)Gfu^m{!S)vV|`XZC_$5B%8ULw?RdD z1}ciPl!fnl+2!n}ZQvx&fYVIj1mDeE@mQJ~k4C$^(0KV)$2AOl<1kKbuOD>)%nD zmS&vi$Ye08U40)1)A+a8>HW-kVVT~SUHf9mn{%}ZXpFMA33hbEE#J&(RZQr+d^B$Y zp(2YVXbEUpa;*rKQHj*E@Py$l(woV(a^oE^O43MhZ^GEgcvj7ZfifZ;EHgkQG}X|U z_h>8y{0|g5qX>!}q|Y8oFGThyO}od1#dkP2`5r1hR<;VK&}97%EME(#67B zdz-Z%r>IZTBNoW}$7=$|Ng%*T6PDF2SrgUuH0inns*ZI%*?@w4-boY&>g9k&R}(mxN78 z%ZO&nQcF5Ylk*DAlBom*)UwW`*NmQC3bWxhra;WFsWjmTe(kWS9aHT!W3OUf(8$=U zIkiKjBPMP1@5g9VZ{j2OvWIE1GeeX_FQWc8T;r737%u&*Yc@;`nM}z~IM(MtQ> z7Ef-u7Vo|err8^9 znF>a7Oxs}AAlLv~_kuu`D#a30TVvHZ;#c12+?mUJ)9-LRk-ye>q0F!INrL zLWtzOQsq-)Czlzy0Lg}UG!5Hl(F0F6Y|C+B`;^bw&bIBrEeU#XOVNXSNmo9O($;YBdd`mCUpfaZRC}%zyX(0 z02H_3<~GZbWK~k0HAmAnuM~q0vZhF?VP#q!*oC zvq6Ty&8y3_Y}@T;VOIvq!lMya20q^#9S}NsS3G?EvEYfX1z09HFekP6twi6}trWDY7ckPadCt^LI&p`p zDTY`9iwYaCOz89pC8=|!n1zjFo&NJ&0FT159Fya|$cG0TA>gVxLm+3?j##So&J4a$~WqE^zqax_Xs-!NtY{}I+_2t^h} z{pA@#He69-G99V7c4Yg>I`(%}rHm4eoVRU(HJ_}fL6o{IxWdj^PNQif<|g*U7?zu| zzk`>X(|jX&8ktu$a`@INXTUWxagwpbNK}Sf7C65}Sddw&W|%aYbxONFcrcz0*5VCC z<=z?rGguBCo|`GG6n;>acLr=szU6xp5O1v_XR)ezn3be}VqVGRg(1uRK9!lAwv`pw zjm>-$Fz*}ecxbFilK1MAP61_k(ZTIRCR3rPKCMbVg9dzoQG$lIb$HhiCenEwI~ zc4@c?kKm1zv|r+-B6)d7;MIO@gG@L)BNOD=qdu-a@ynT&R#b0Jf_f3Rb4 zW_CB+`YEnX?Lu$7GabE&vsQe-rrl4xiK=)Z%WYbdhdbsJ zgqdDTkwuWK`s8v2K|AGSWXjb|sq!5SyPdRLNwP=S83GU4H#k$7R?btd+-q*GT*dr& z>0I63svHHR3(a$9!W00Urprawu;#8SC@GzONeQ>w<_;#vz2$;60VrV0POm)3SJ%8u z+tbf9*YG+W2VHZQPc2tO-#wKw8W)(%ZgVp>+s7gRD!K#ElXW1B|E4JNYG?F9E`5sCh*3b!=*S^RRH||O+IOxLP zG+^~}?{KeR#zXa{64`x(%BH1f_beq%zPpi8(o*Fh6GP=(%iUpB(UrbsaNn|Ib?2Pq zl8h=GHk0~rj@xway<1rJBS=P@2ZfrlDD)nzaKf8#sjXVxw4>2jOqfmMtZSpUegMr( zi3KWI;O(OiQc>Ea=K`lURYvBH5w>J3z+9L#h>lldz1ylE49{T#wcQqv*a)qCItQK3LJ5L*?#Z~WL34sSwO_w0yoQ40lf z5_)niA_Y66yswXH)H8^0Hlvg5T)K2bjQ$L zDb$xL)8SMxva}E3R1ca_^fBhH#)bi1*f8LBumC4*f1KvbZ z%92ezXp?TAlNbuBx}1gy)^H-&Fl*+__W*3qrDetKQ5^6x7&KQZ`2jIm?hkLF6zLR6$KdVJrrlqt=q4;GTz_Y zs$DR;Gv0rUc!kRCf&shajIWHT^2MCyJ+Be$SCCpJ!!O<1v$Td=XMEsSraKNUYP@t; zvdeVXy{=@L)>T8Nh-$X|GcBp)cE*lLL#V3rQ=q0S9@hB9xbc(yGf`7w^R&4=;I>(% z%X!*0-Ke3oaaiaf6YDbG7$DFNcK8ul9mE#@8sl$>=U%!%l=t zzx!df$K0HpJ2|n_l#=_UA)*Jou8L`ZKpPpEwq-?sD1I2)EG#>KiRKoXN91*Zob2VZ z7v_;Z`$*Af9_g`DG0?ELqdVslBP&x!Cq2tmfSDHCG_O*!926= zvCxa_j)LB##6>+#veyrVJN8QAHsiM~8)Dj8=be|ZEVZLEstJkoIR6OmOFwVkmmW4a zyBE^RZLpB}&X~OoM))ET;`7jXlOGyL`4CR|%g5VLDYJa6IUcF-{Mihc7pL{h954^J zz9#7w?=9ZRPE1{HG(KfSjZq~HGvX<}5mV(-bD<{j>fCVa_EP=p5Yp>-&WN2kqFA(v z!^FxoftPnPhv(gmhROzigP)TN(JU3qVjD#7HsfZDs1JhnVfxG%uFni#pUjNY&uxrp zI>KmW8zCuwSwdb@z+}9{G%b>M`mqi~71QxdMpS{jQv6U^o9*Z_YIWL$%jA+Zj>(DK z-8?fC3zz+On&RL|4f)c^H7|CUJX+C?ZbCDbfy=6uL@JK|a}s6+S$e7)+tg#;$M0tRf~V_Z$hlMw_RTB17;RV;5q?nKq|eB+_Mrdq zEzvX0M-;>rH1@k7vg1m!P42h!d^oSyDiIvFIk{4v;70kw#xCj2!<5)}-*Br@ObhT= zMx|xE={?jiqkthBx}`O-mRveMX7+5divvyOVC`3W!)zP5m8Q49#!4@jpDF45*>hA8 zGTPa`W?v-TuX2%pZamYJ=k~m#kHWOGF?EvM9U+FE)hSz)#qwXB~s&Nx%*3UB9D?EH)NiRR)0 z=k(;-{aKyE1jBD$$6$hbA-3m|iVShR+c{C9stu?f{xd7K{QV5)G}6_`Qn7ix4JCeu zE4KGQ!@zg6Hq%$-=b@D{by`WrZgOd7`ET{*0vxSgMlRz2t-hqDe%Gq6mKU2d>n`&F zlmP@~{TRFL!-xcSLWw?rvDa{7 zWXUv{mFIq;kolQL7BX6MY?a|}8L^dQ>QcCf^|f@L+V&eEW`s?ugd&T+GI8$dJ0-uq znhzLs#<7}(LUVzS79Rx+HSH8g$?f}k1$qida>9n z3}dzPdNKx%B?_ldS9W0@m$#uX!?{M>xY0+r^3b>-5jjJ>BQ=YM3T=MwA3St__LLjC zaO*QN!h&xBd1LGne7n+%6))F6hf7e5u(i9TK#umI0^xemHx?L;V61M8eH(ULV=O#0 z3S)&^e~r|r!3LlH4@z2QHRT*%vU0fNqXQtb+|(g>aVa_k8I3B2`Mc4m-eY`8Yj{)t zd1J1w^%BCIjp{ukHmX8ltAl&kdHhCI$o%>)`r|PHY@}x)wl>D&}0pPdeMwxB40u! zjh|wM4j||vjt=iDi@gv7DKq#8H0X| zHdm(7%Dn*E#JRA*j#&&PGkO_@!t!RL)YgSne-kz%hgF!S%{>Kf!YZMFnpozNG2iRQ z3LqOHR+wyJ1)dH9Q%6zDwlIMJ|#m2bp$Baz^vPz-^i~ z*Yf6;4NJW!u80?FV5K5PM$Ezkb!5ojJZx0N%}zgyo11b)2hWZezzGGcu*q8s=m&5@ z=GV~Kql0{ENp$e8szi<;)1GGfUMz_aIdj-3$9yW!?Q9-tWCM#Kvd`7pRF2Rz!Vc0j z`iA$LMj`VJpP94v*>aK1}>rl39|+er|HPMe^^_Ik7>(FF6kD6Q)W}3 zi@fg{v8@yelrxN!y+eZ?`PAB~p7ge;L~bh$Bu2PpHK9=nzo<-xMHOf?hkZH^W6qd* z@P<1whfCy5+?>ND=yPGX#Ec~jA1?6*TA*FBc!C{Ek#La&Ei(M{2wK zGurO-(`Hrz**ro+C=^*7!EXqKEN?>yx!kSI@ZpTMTe2tIDkHjPn7?U~>B5FvZ$uq$ zx|h4=Y`IMf0Zn$zqA<1#&uAGzc4AIOwx60R-_-~_QCkRkPbUA84;iyN(YHgmbwE<% z3hm%ynzV3R4^H3NG@UTYG@YPn`Uq4QIZY?{X&P3d!mWEFgGMS<2p6%>NMU{@g+huX znt5*HWk8X8D?*l6EtDYn*2`49Z;D{SeGz8v>-y1_lOHSs$Dp)g#V`|2NfXKvss1(M z@%|Di#M|=*Bnn%B>b9vbXLL98iEQIAZg7UiVK6iUQDE9**p*@E1R?OId7y z&)C7exZBYlbUQpB)OY97Do@Q>q$QemT1?HZvUGlit2{N2%BCDhHwXdV<;wl2(Wr1# zb`_4ws=`sjRG6$8J|r)Rxi#<7Aj*~AQ-$)6y*Zn>#p-@xlf~-3P3*AS=oeOtO3CKM z^3DKO#_es|Y!kzpsMk#8*~wEPRDTwz*E|bFZ0dTOmw}b+^I{U)+uV*zNxMxiKKNmc z(9t0hErVu9VuZDaR)pb%X>5XRG*n>BFtezDXZ6Y#ylpPiPPaU7D|5x#Gau_f*29F0(}XUx#^ct-Zt99~<5 zTc??L&sJjL*1wB*v6C4S&atX%^plI`9fbdf^NS-Dvdo#+{HfO0ILzmb^F)5KB+z~X zd87!*a5AB>`NFDgySSVZ{Qp6cx$JCM zhCs07!eGBy89I;goT$9Oj!DtSv9e79kImYq$a(q&_V9x;Zo_qzOaYc-zgGEe??km? zPEA>)fKb5nJLf3A`QM>NXKoW#s!EH2Zp$WoF4!#mSR)U*3`CWAf?--b-ZWUIs}4oy zJ}!eHrWPQF&lo=$yd^*1S&pw6nAi%wL#I6`fofo!A8TY=DaTy! zf)1R%@{hM}i==cV)^0IhRk-(M^RsfZ`AKcUPg$aE!2G281x&UHNwQfAWy_YgFtgx| z^|!2u*gbqAvWL%>p&fdoTZ|D}RutMBg>vH!x$%a*u~y`S;yZ)6ob|#KVl*QOQCkqE zDWZ{DKqE0)=2ID=8Un7BF5vQGr-wub8#nq9(@JX%7c{BxbllXkR?HNN*FX(9ONv37 zW~)-E4pVWZqOY1gaeroHB%`k){I~*rB$MWF>qnq?a^9(#TQ6$##Ttw5)i;?BHX>l-w^hPX5+c6g16&iA&w7 z3r+3Su;{d9MX^<|m=s)&tsRVeqSMr!IJmp9bJYw9kJ#OouAYHF(^r`FGp zR5vu#H$;|HR#it9RmLl81-rbmp-$)m)X6!oV~??+DL89MGch= z%Po8JbmYVXOaf0j`J_-u!^KTYs_WvBGiIMzUK%|+Qoks2R9E~~6+uw)m2BPAA3}h@Gz#kP;N^>J7A`i5*2g3DORMYr z=N8u1H&z>ZquMa~3r{jnl-40_HC2YNs(zuh&C-VYs-}h2RmVlnsE;(%Lqc_JwODQ0 zA}1zP5|39eSsE7&=R`}&%Fdi!a!xeTgbb*yUkGKZB8ME*cnH$3b~@&$8TsrqyRxnh za;mBq!U{-gq^@#Fb>q^?g)n=aT-Mheo`$L-s%mJVeXglapIq8dy{KjxIVFwLMY zgTyr~7zO+4hRE{zrpV$-IP{`Q_>XZdxgUw33}(4!L}f{I zkX0ZeqK1%6Ad<~w0>KpxC}W6X6-!nArb?A6{>6$GtqUqDT554ERcp0%K}G-6tyHc4 z&$;)Uncv*Z4MD*wazBKb`M&ec{oQkyci(;Q&CrlFIMn6X5t+j#4I7y`GIK)KiRQnF zW5-MyH!5qayiCj*J89&kDdv9~Z^n+BG;GqyabvZ7(Ya-&g-e_uvgCN|IQnnQa8^=( zyLw29yosQ0i8~8%(+!b}!KO#$M+$N)i=xyND=m@p4<@VU0tre0-` zK8k%=Wz>xEl_j%EO6Qb>r~?&7=VpYYYtN{p7DC-B!c|z9&zoq!$#%dW%w6!CIEZVZ z=AOC}4-O^jnygLZ92^?VL8X+uEw_MMFgE}@^w2<<7pbVAzBicTLH|lB?0#s&0}l1Z zXsA3gqp*TJ@W{y(n*-xtpMC=y8viu5BjWui%Y-j9AyPmNOK2Qa6WL(uG>9K3XaJIk zlMLO(9C=G=s4FX?k(8Q!AUh{aXg?Yadd5doI^fJTg_%wVnMhGlPihOrk^Fc*i9IVz z66KYwgM=QmE6XjfpiFFouVX#T(!&LKbYCNd+0B3s&9rZFNWrixsB6)=cd>rQf5I^LY z0!)CTqG>R+Jwnq4Y6Zo)Q93k}gGqCekDgSCARUHoxwi)wrjq6iIZePXS^7T?dqmHid5N<4#UKaw$Civ zO?ObY?3JWig=US;8bb$XSLk+iqLLvU)QfsGgR?YIQ*Md-*wUzpMIz=Cr%arbH6}ze zzlQC`XAd7eaztokrYNI>IG=y0L#g~kJIpJO@a!gWe50XBvL4bwXCa}Gxa}Ac9Y@lf zos;Ozsxlq0$R0Mls<&(JQE%Ofc8a2zDuqI0c}^8#8LC6i?pz&71C*J&n7y{UAJ@;| z(2>V?v}Zwa`|;Y#8_Z~YR%S%bgd|Sh{haF6@_6mzfs%40*LHljP*UU)IASLaK@iHb zCq`zH0_a2 z^7)9&XI$oRJ2>_D*1)iuG>q#^vP^45#@={K=^T>yXO0^&Y0CI4e*^W|x$$=GWP3$r z8J#cCOn^_;XiA~og(e2}2oN8~X%cHrHboI1CX%KnQm=7)nV%HX0!k=4lOkP8y;^sM zF_`BpiphtbSNH8bsG(_Pz#V4>9kfbkN6HI|O6SnR0iE>am({i)b=el^3CSh<#CEaUq{BC&nL*0~EoD1)YwQ zs0wn~JEOM_oP3@xG(%aWB(F4nc0D+hJ?X@r11qBiJqJ>LV`*K0njW9eP~#B0_{42+ zoK-}r-RBljaBX=KXVm2pa~@2Q&n==cGk|%$<1_oJ(sS`R0gQ5}6a6 zG-AZyP}l6~6u8QxM;DglmY0V5X7tbK)3bM_f&IGK&nClmHrt@dxf6mWRc)Fa+Ip{+ zi)qy@M4Z2akkeDV3O8)OHkURXv2Tl}Gpm~~YF3*@h_?@=KTZ+84?%4r)`Y(xY8w4< znww&K+PJ2aTM%@m?F)@n5PU%wJ>q_*RqwT^MQ!sdnq8Te_EIxr%XzLA+d|ZpmfwU8 zLbUA#*?t0UKfSuyqO`OS8?ldVV#7~*#PgeBvItmrd)}8)OVmBpmMd+u$;#Lb6}wKD z*BN5hG-1cf*PLq5uIXekJlYttE{;8?L-;?9XSUVF+gHi=*VjPVp3e^}>HaxTUqf4)yx2BhY=6}p zY)iqmxVf*v|x6pV{`#x~jzX621ti zzRs*}v8Z`%vn$flrtL*;L$s+*?EZ-Qr&2ubu-e(zInk>|qD|9?30Ar7rY81hh}{E8cGHbrw%CQp-+Zyl z##j^>v+f#)6=L(m-n4^iw>H=BE#=!FHm_hDa*dyRGzMG6W(3BQ+gJZYTWH7oQ+k(g zNYT7Xt)EtumudfElWuM{kg>4}>iRiWWyjHcv0cUSSAQJ$%WQuP5nd=ZzsDG)-OI(N zt&}n<<`zeDr_*b++`i7#cjb|y+zeiO2r~E_H^`VyCy^PnR&GAXnRbvt6YWUGNn^9= z95**_78&qvk(oIKd;)1R`S$;Z$OVU%uFNE*q4KHQJxQ3aaVMoi>66RYplYi%n|FPa6ZNANEu>_XukN4I6wf^|&O*i(0f}zjw|8zaWzQq4`_48*}I~#9)*6y;Ek5`oV zh5y|Ad3Z^c|EAYy1EpWGKuB)>uqo*++cgXRY3?~l+CU%D>eYg^NK2`N?NddVxl=jh zhUGRT{{yAr3>CrLzL<}lX!A11M|yjT@amsiZpKObmi+VJR6nJ~qnNxct==dQ1T)E` zEtJ>HEr~6pzc$Icn@`t!_(LB4IC1v>E2$?l-y-&g$Np^(|H{LUp!lWIIhZ)-tK~8C zVe)ah$DX?b+h<5sn)xx=-{P@c@eiHASp z;qQ9*zSP+`4z2d3!ca1Ec6vRMcq*NvJbbqJxrqlBO6J^vUYC08ANKIA9^RJfDV3gn z9zLEpm+NHyy(db1T;ViJ_-x_k{DS!9!Y`Ce%=rZIJ3RhZd-(4?{2v~^4-FZq%GK7x z`x57J9rY6~jhv^@>r~+<3ODB`#4iz^FWj7$5dR||A+WiU`pn^ie673o=Q)? zhoA4^OFVpyhrdOf_57lEq!id-xm=zs|#d=iz_#@UK0*GabNF z=|7z~m$ynfqB(b@^3LaJ>1`L{sq~NY@M7`v>}>1DoEy{It3CD)h<)vO*4~^W)7#fP_W$wl!|8aFN@tdb zmw5O!#5oRszr^Y>=i~JD0gwGV9{wNkvtp6;W6s&>ZRhmVI>!^|e4n`5+M9E8dOO!+ zzs$qed-%uV|IQn&e{)_>Z`)DOQpN2k4?n@fbBVM5hkj%Io3#LX8}r!TDfT6ISbMWB zKyRP%*uU%HUwHV>Xnsta>ZRj7e1?bDyU{XlW+ZPGT^OMoE9lZS-J(LP)8+zL#xZ^S z4hqcXNEKa4r8WL~2K{>1H^}JIcR;ToCkJj~%o#H-)85}{ZqUr3+y8QA&`NleZsf_S zh~}8Ja6rr}=-!i@_+>dUDK-}qQaGo}Ed@Cxye?8u9*KY}^`4W@OZGWA1*KJ_te|3U zMNaN?Qjk+nR9P|82rR2AFeqv7(lQcAw{y-&k*T?3)!cn*N<@Bn?WVLO$1*N7XGeEO zUg7g3_pCP3msqFt#uxK*s2=S_c59MSnw2#@w}L|xm(P1vn2=OZgv_VZo2g_) zk&+qFncmGgvm$eI$fiL9|3=dgbEePDDWuyis~UEcY>PgpR~B$%;`)%cpgMlrvDr@d zndC%Di#&Vt)@Z3XL7POxC?s@?Y}DSHLzm(5>F(ZCZO7afNlh!4njqbMiOwQLX2H+s zDXA=;PSQ&Ya>!?KQcZGeH+KNpsxlol!Q@71^b29guPiQ0Q4Fx)d-|erlXzz>r>j(j z#Sv-Yxl!XkIfO=tHsa(>rkC{LC>6oeeot&h>@+?+BRAX0_Gn344Do8_4H zkRqDoA}2zP-`=m1G&I3J(zz4OFh4?ND4m;=Uzq0&oZ4tWU<63CBNGR)j8pbXms{!X zcnmh>SeX~a9dcMb-S5WjJZguSdNrOCzvVja9-Sr+rS_9MUd4B`J%M_u-W^SMS^7&w zc5=m6%<5~i7o8*V3|w!vt@FTQyI&q#LM74t-+8&w_z@)Dji|opkQ<3_NQ}MMvdf^g z`io(5rX~^>>1nVz92aw|GdB}++cl}i63P8?rUs2y1E4teNN5LR+>x_(LJ`l&49nXw zwoyw2A<>R!=2q|_#O!aXCsL7@OE<)F$mOkw(t}KcR;VM92@mT$yzXdv}GF^-*EzaQ^%@Pv%85SduTR-`B!tEVxW>=VjCo#p5UY#Pk=eQ9| zVMEz&C-%0Nloq7)28R|q(9zAw<|vFYG)4Z2s*(E7 zX|N*~R@e*+OK47+mo$|zwWQUj(^#oHw;%~^@y2hSM6fj-zpv9@(Wwkm)?rcMEb1?G zGZH`OkZP~`BN4SDx>($=tW2rF4%`q`bl16Sw~Q&B`dgaua*BG(Vc}x1{26H%?ed z%}KOOhc;b5hurYwI^OBkkRL-cW`%MF;Ll8)HZn7;u#~~pTQVs_8_vpTVG)g0<%J2^ z^ue=Pj_%eVeiD#kL@BGBPS3g&+8}Dfmb*di0I5Q9Ojk=j_$C?hkgLWGgh}oiwOz#6 zNwzT8(3=1$PkWCkop9AZX3=58o`j?fAWh%QLw}9SNUcpq)QM8Mq_#qXW4wQ(_*9{d z&{PC%QWY?sZ&}h9m&!CA3o%V8k!EIm!#o5lX?hR5l;+snyh$PS*W9GZvORp6GNjCn zIy~INhh54r`9smB|iLhwCx=%ldJv~`~4SC-H{gh>kwyaO8Jcvnc)4Uc!mB0k*J zZ-Hie{i&|@kmO?|8*|ajVG}0Ova-`dUTOI?>FV%MhwEBUv2eOv$4j2Um-O^CrwfJC zva<1`*Xu78&i1sV7q@>II4!Nk`6s|RPxarHmUKC}emVlDC8)T4Z{U1Oh58vH+%4}Z z4%hNt2!61<%Yb9KRszR-?*-16x>V2Oz}o@;7&u>YQu`Jh07|a@LmjUA!@~Im^-mG5 z^)%305tMg8WtIk)!59<6txa!n)^(ok+&XII|mJ@Hr zc9jPl+ttOu(a%-Dv0c>x$2imptql{G$JOTEo|3%=a{}bTYe)JwYS7$f6p36zO z>O57rt8<2M)j8Ad9+ZMT>U<11wx7QO$M*9DaP-rPuJdwYJ=lKuD{43~$2fEruH`aD z^xt5IYk6mYAFP*4fuo)!z%k#~g=>6blJQ$$&-WT@`)N+sg*mZ)z86pVLBRQ5Smh@J z=N~XuUJe}Nd=+rMM?mfG1pYJNF91hBp8yYmeP_BZ%*m~nejc6;yaVm0dJ2Je1b&fl zjb|Ng<8+zBms=bx0zaLIssCGmW8D4#9Lu{IIL56RU3cc>>S-;U^f;4mif`4d5a0^B>_Fw-utN zIRi>=+ztkgaT^XC^~?Z{dgcQUQ9fF(6~Hl`4*PFMo`}a!vBS0A%fS!&{|tB+(xEyJ=Yt0&u9qX}PkArkI4;>A zQL*-Xi;>!&4))luON6^|nC^TC6X`qy?oK{&^=EB$GF&IR5L_;Sb3D%!^BHxA!sad0>I=}t`j|Ix7z$vpTu zhpV4gJ%0WL9NT$EJ}6Ss^46J8LC{k;mlxx99B^#Uvm8JC)l{6yz#fmA*8=ZJcJcB$ zeq!Qh6WC{f{Xc;p4ZI~6k`n9hMStr5Na0#u^?wZ5qyJIBj|M-l1LwEu|8wBIfoI4> zh4mZ*e7J|_0LOewfn)sF0Ph9h zC-#e;z&p}5jl(d2UKqp|ou*_(A_)0>^r6e}Hw!daxe*0q1jB)iV+}#`7HE z8i%#g@8*I%--@YzVjlaez@Fc#{R-eXey$SEhcP~$YWg$S4Kj4*kgICfuoKw(Ba!RZp z{YQXf92Nsd`=^0pzAppEeBbl%_I&bB$<-4Uu5sh%SU61ud(3whaLo4x;Hdv?;OOTw z;OM72FFH_ibruL$osWplxnPg}W5Ch>!F&>%!v6r^y=l9)^SQvWTz?0S`F;!>^GyqJ zuJzk1o_n z0Oz4d`J=!)0{=|7mbZ?!ar(;Pf0H<*@y?XEopho<_1{Cd>!**ybzVBq<7XmpocEpw zoZo7`tyl!5ru2vHq%&}ASEGQVeYtSe|3;c|8(a+bBM51}R{_U*yi>UPsk3f^`@x>y zs-LHUqn|f{V|lj;cm10O^2x7qjdOEeT%*MC!F-nh$2i;xJd5^Gov#4L^P~R&KN{@! z<;tVP`C_?x0>^ef0r-iukLH^X9M2Q~0346|ZvhX3pU;GAd^V@iMoM3R{qgi!^F4sY zQDXh*r;Bj)6KZD6f*xRxeqxUO9o9VfCD@N7rujYq9P8yp$B$kgdK2tNfS-Q@$NAgd zJcv+o>!mmFlWDu^nG78DlnK{3sGf_!9`#%U9Q7;*j_u?*;F#|R!ZqJ2iT{^ikNLLd zlQ&9SUaXhHfMdS>fsdknG!AC~M?G_dtDdEz=TfjoJ=X$9J+}ZyJ&yq&4SL=Nj(VE# zq8=sHvqto^5zcWyJ)MDLz4QZ)dNLjV+MXwY{Yc6~<2DC4>Rjyjd0lkg2==IRHE`6q z4mj$37x);^vtM`mK#AjkdNP2IrERMJX5iz1-whn|?Z|@>CH9Z@Q-tqBygB`SE#-}X z{Yc`P?`p?h7aP`k?Ds#4cBaJnj;BBMGfudh?^LkId|!9$o3L0)Tfjb(KCAyUUJRwA z{#%HBx^UKm_FWwNmSTU5$9|+^zpvP5d+duG`=5w?mB;={$Nmtpzusg2TgSem*gxd4 zf6=k;D)w)9?6*1g`kZ4kUgV|Z#`8$v*q*b5YrPDVd?$fD_OE>4d}^rm8wHN-{O7Ib#18k9~KZG)TVOb+lY30}lhADqQu< z6#vt}9>?t^z$b#Ahk>U9f5q{Cjre~L?6F=x2afsf*PEPD;`V^`(g*k`+NR~50vz>J z3fJDuNV8lV2|y8Ch$p=zsB9OJpo@vp~`W_?U} zOnh)WKT){Gr)3LkHx}$M-(1IjsMr^RJuhKu9F}&cmAxNbP5ghu`esTRnX6z|{Vq0FLq5416-g z;Y;9HF9#2@{+INnAohJd_NM~JxD|N#e8<1W z^BSKr26?RTTW9`#%R9Q9lW9QCXPj`r^e z*L=50oIeA5>@O{N(nE>Mh59=KKY+GrdHVrJ`$@tzUyXkr*kgH@14sSO0>^RYBj95| z&k-lk2TH6T{bU0lOWRccEa9%s^S~Z;-Ul50zXKfWrPWZ%jFRjB(4hv`o6n2}j{eU9 zj{cVd$K%?|9^P?SU=qg%?a%b^+dceK5AQ!bwg1aJe1nI#;zmiy)p;#&jL*Hm@i_Q2 zaBQDZo|KD!^s@vwPtCNQjLI_RM$b6lIl$3>g@<3};lJ?k+dceo;cmNn0qn8e9&lnp zKbChmaO_{F33vU^1bg&<3vl%R0&r}%+dTeTo|Mpu{)YfZ|9QaCe+_VK=YRC@wtSLI z$!%|wJp4uvf7io%aA8vTzre#E^YEr8r}ncLIL7ChycNTjr zZ#Hl&Z=r{`=bgn5>Kp(Z^Sui=ztuQD4jl9S6gbA`kntwldgUDq9OLsQ@N~$x`6&rM zsPj_bsPkIjsPi@tpE4ogAM>pOj`@BB9P@2DG2sXEZ6kai+T5J}4&KY!9pG@iuj~+q zhs6E}@ZX(WXub3Tj`>zNesq1~BCyAC?-|El*U4Y@*dIH|mV?{#RM1li{0!jt3fDNO zo=3qR^)%r@mJ<8N^~Scq&!TM_hrYlu-?M;Y{mvDxdcqRtg<#KzboIX!I8Ola^rK7aMd$U;&Uq4qn-lbhmb$@KN~peSqvQ8&u@flzN;kP zyTJY+@c#$k*e~93{A?CKTfiRU_NB+Z8Bd-laeNM5jg667&spH-vN&G^#yR$)0R6GB`xpI zrM!m==knrlWEgPNbB5z*srZ=%_87PGfFDA6Xg$^dN1e9;$9x|buKM}=d^x=U_L%R- zz|l`j9;7I#AC1GI!rglB0UULX0gmxM6FAz}0mpW}0XRM?E>hwY=Lz&kV3fJr%%l9Jtf*Q)lG`zjOFX;SYcx zY;SJ^?+E4k-0`FSn@+P1Rlhz*vzKs=5BhHh9Q}6&j^#Q5INDDGj`eb$aMh#F4_^-U z*nVyVj&Z&dIM&yTj(=U}d<*Px+&eHgFp1-UI{N_UDXR9@V9QE7)9PJ+yu5r-kz@Gzq ztlz%_NBv&`$9mr{&z7qxy`cTkzFIKNfn{~d6& ze+)R>k;dvk8t&~Q2Y!B`w^gX0&pzvbl@0&`5E|lzo@e^ zf(xB|`TP1gRfC^S^jYKoD-XZN@l(f6C_U)#mE!+VkDo7rInl!J@bKM zzP|#_(ll=O3RgXPo%C_A&jkB-fMdS<%(Ufl+jD2(>R;pE7woaTnZU8UlYnEsmB7QG z^HSlO?|P~C8n8$I4*>5BeqI9J5%`R$sK+xZK^HQyCB+u${a_Z2;FgP#c5 zY8>_{vOB6D{r;D>!Z{8nfqj3+{zUPAyvM%Ku|HYt&+*vb;Mk87`{f?{KRNc3#Qx77 z`@M^k%R5!<_jCAJ!VeVg#$k|SuiuYzyu;Pci5@?{1l|MU{5bI8!2b_8w!?i&lFL;n z`VSV)`mr5$1CH(RIN;b0&j3CO^jrcQ^(+(4GPqx;o|RyadL96ddNu$@Juf@{=Sg}0 z4)(b2`Xz8)s?qk}x0F6m;`pHcal%#qTJe7t*rWam;F#}az)}D0z|$#j)$@dK&9`MM zRu=>>f<5N@7I2K)zky@E?aQn`E-$V>9}7I4wyB;|g{z*Sq9+3OsHYM*>RA9B^{jCG z*I9YNDu+iU-+RDMC(!w-V_zlqZ-70n2Yv}0$JGPRu>v?g9l?J;;259r!Zkj>k$iK& z9^-Q%aP)Jv<44!qZvlJsb1(4yLFXSGKXtT?(^yAz_Gk%10MzDx*9m@ zSuNbv^BCBpo|k~5p0|Oco&%${yj)BkKlS*2m~hRvWov79gv0AB4vrSi@j09Rv>uNI zJ`4C5$4?vapAGgnZ=@e-Hc9o=*^j|Ihim^`0DiC?u5|46d(iFzd+fg(fMfrC8#wmg zmX%f*#{uiHn{d^Crj&Oe*ke7O4jlbdI(~FMyb$cs&r;x6?`wgh|1W@}pH{OIFFnBiG~hVS6be`U zGo`*tz#jY8GT;-z|I@%RKJPpJ_51HW2YZaqzEuhRm~RK*Sl$u9ahxdvj(Tc^Yk5`A z4PcLY?gWl{9tMtj-f;ZuIP-TfuIG(hgYvZH(@00p!C!Fj30Pr)w zv9Ghc2PZgO$CFI(gY9jWW4}rArJqYOiSxzrqy{*SC%*>Xk@nH@J_#J_YqM}IZ}WX^ ze*Xe{tgnO5wcA+_`spiN{pfgd64;}k$-uFGF9MGK?+1>4o(7J7J_U|`es-SKuR3)+ zIYu~_3;hoVj{YN#pE~O{CyC61HxTBe*k;b^9pd(^FDCYlXgKO4y6#ELxgL-T_p}b z1AFw}12|8ywZ4V{KL_{($NzZoKN;-N|GAER9r@yPiNke#xD5PY`*{pF#`!hJPr2y% z0PHc&{{emo>Cp1-d!dnK&MPs_U4i2`Gafikfz?k1aMXE)aIKfcqH`J8qs}{k9}oT? z1dckN0^Skq`(2bM7f)SP=Oo~$vsAe1TrD~;1bfuE#Idil*#&iAkM+0;IO_kC$IqV~ z{;=qI75t$8FMwmY+Rd}Vv|LY!pH9N-jeEe+&t%8XE!J&tHrQi)&IOL;y~^>U$MGA$ z9?P{7IQn_Y@lzMr^sK|zOL<=s&gDh_A3OG1uCKv9&{*&Z8 z9_%qbrNA-H^Bq6Wil1MAJ^Hy7IL7B8$B)MODX_;l{1rI*X>v*G@*XB!%d73G2iT*| zlYwJ-a~wY!|8lTLKbHVUoxgPaXnB`|J(hPhaP+g@@l$7uA8d5E&L3U`Kj?qo>O|b| z_&x~ua5B+$l?NQ-bD?mJvmW1T!F~kzSq2=(&%1zQc{c*bmDF z^9az>3pnZ-3LN!}16~S#3WRIEo1|aN0(UkbG>iGw7 z)RTT`A`TdzLBLVZB;l@}Jg`SS6~Ix?Wx!F-Ex<888-SypcZ9ooJ_CEy)8ev3c~Q@S zz){a2;p)H6?j8(txXuqx1V5d~R>zG?fTR9pj-OuAo>ziB>VE(@&JQ;LNB!>r?+kh> z7TSVnx%7Oz%Heg^ESM*p`wK4}X}J~w?+E;M;8@-#9RH)Fyf1=1mUoL|Uq^Xx`WM(^ zocD^^?W_~y&_%eGx6Yac84lO^TOaU){wDy(auqv%^f-Gy*kie_2abMLIeu;tosWS% z`gtBWmiIl!&kFJLDcEDVnqO`W+_)Vo+>Ki&hwJ>UEBHbG0~~uT*GXW{OM2R_N`Pa0 zE_3|o{Oww>$N1a>9Q{1)_|f^>U%?*zd;lEd+^ojNZ6A7RPJbHbbm1INj6)~j=;tKh zsB@a*U)xnF*rU!XfTN$A9X}fXd%+(4JOLba{?+lL<=qVSSl(^G(a+(FY}`0LsB@5T zjprm8AHradI&&QRI_ow#8|+cfxxi7+6&^p=I(()0zaIRc|7U>rAX{zEp93Eb{D@lX z(2c_h!Zi*$4;c&g7>Bcf<8iGVIF|P+;G;m#?|@^yJSW`M^9IuVWsEbkh}j~``aypWFDj<7x-t>gQo8Z(p!SKbgQ$ z=Tyg!mbU=xvAnZ^qo3uDAHCmW71(2Y_}a18`#M@(ohUEbp8X`@}%Qe&SvsC{3N`HpbaxlMF_2KK14#V-?aKs^TnM?E8itN&0NyMGw$u^*K=_T$CA3hWPpa$W1# z>pbmxhu7I`gPXw*U&_|_eC*h(pKT6TKf!g@A@>Wu6s&$)1MdjD2XKu4iNZBLl~Uep zu*dipIrepw2d6n;--$k}p38t^z1-sX(Rsyh!5)u;e*upAKXLr%xdcKm30CxbnfcLs3ubDra;&dLj_9j@yD3&9WiU+LJZpVbc6 z^~U==el`KexP9#S(fBuAW(B)(>i``6^cU{Ne}OM`bbR<2IL2qc|FQAZ_-McWsc<*WLx7{7Qyf3q zuk*nk;~WK!@u_kAXq>MFdyKCw7j6)6Z=DeyAza})4$fZy%-Um^b2fIa&E5IDvs{U&lsNp)@%KZgr<dAFP)@0LOB@muOjXNlvd&dLs!Ib7#I zH-R7Yf4^g|<$40_asIOvIL5i16hz~r^Pf(_Ii47wEa2#8n&U_3KNVn)eyV|Ee3m(W z@f}x07pOn0FFBM`i%{^mRH-=fx_ML4g`*VCOUpJ{smx<<(&;2buM=NXnC&( zdo1tmz|qe;z;QnFwd21{TU-8Sx7k-NFOKI&0q;iJbo>bm*SM+92@coqqc|P>M8MBE zj{UKs=K`?L1p8Hv{qbVI2JBA)`==cHEU|wP?9qSA+i7P?`_Nx=`cwaHgmZk*|6z`O zSp0VYdyHqMW3S~M>2S^W6!3%jRsp|+G-t4ryo%nwU z?6F>2-$4bXq~+4@2iV`?E5y&i!rgW?#Ie`!2N>pX^)u4rCmVQY(xvU>_l}=6qGv7G z9|HC>{+COYD3{Jp&JoV~aei_haICKdjvt+$Tn_d)KlvN*`ILw1AABdPvG;o;uNK~l zwsHT}^t8j@kn;Y`;hTkjf3_|FA?8SsU`7XrTp_ypjWuci-_wEyZj_6y;x=VJQPe4hr6$H$-E!(MFrjM1yw zhZs^~Km4bO$3{-dpK4-1=i8S!&uC`BB8T7F!h$spzlj?FrA_uF?x)qh7JTXOllQaW zFv(x_%od*M@C(~m`yz+`YJbbGarjX`v3#||PZa)w!}EoI>F_HL6rB=8)%g$)>Xe2! zywAaw&vf`Ncu=Htjl&1_uzZcftB$h#O^1)=#UVx)U?@y}zLo(l8V3?B0knzIw0$nh z8hv8ZCekoTRuh^zCq7x7{QpL2!~A|I|40At7qkkp!?Cx9O&)gYuUfohhy1G!?7`oGh#WrY{44X0;5s)ZOCTbHmJ9eXeK#OT;NX7>cH zUfZ;R{T~;@*rVwqpHJFpOze$ev5og}Iqv6HFgo_3gl=@~b)K znfGEi@3HWpN6QZj=RHpWHnkT{%UVIsRr&iTkeO#2?KLv*@o=|CGh>_6VE2bYq){EX zp{UN<(Cpm^H3(`P3$q?hzjw^T)Oc2C77JT9C5tBSjv$K$n#2*t0XK=}dz!><&?K6_ zJ7F)b@I*s+qFK{@O-mSLHh2ezLufNKo;@ug2zHCXq4~REI)J7)o>Xt{SiSAA^viCh zp%xD&JaVjU)&qw)I%Lu4n-|X87_R=j`J4{)M#mQ(-poiEjyKvNu$}*Cd?x zTsSRz#gk3Dn#Tvnq|uF9X!Cb7NRcs0OQ9mis%W--75!Wl zMNzBZOXIwmS3$FPSJDu$D-_IVw1V!cpm~ZIL~IONY(ECax-mGXvb5mI;mt(H=cbS? zypd?}?oD*)F-ALCM{b$XQDJLjyE@+gsX4qoM2EMBa8%R7TfR1#oh-9c?_)Sz0~#sw z-B+pF9vVn7OU%Y7dNi1}*mGFj4Gya6N1gL$~SAW-i&2^S(E+dlUe4vPje*gD-RcC$K?EMV-Kkfal=KqfLfAjYb zGyflGNE`aU+57vL|C=#qg;Kz6rht>a{`%{htS`@ej8so5FO1}eqNSnI%4k_-G?W_} zn>{*|H#4_9H!m6~Pq96|EmT-CJGZDXKU7(gA1M!=HfD6FcSf%+osQ*$!?9gxZ0SARvtaNuq3ymUfx=CIiQnP1hm>2qeg*_2T+w>X+RJ;NCymYSSrN_6n@a9}8Dxw!na(pInh`xA5Pj!-aD@-N^IkX-#KwUhmLxu&A` zym50%l|Qpo`G*g-nf7opbUs?66#uhbv*4d!r`SaKrS_7RQVHuz6=CL1DaG(EQ>LN*MFa|$ZvR?yq>;@qe)ql|J2OQMk(k#gg!IJc;%G_Nt{ zLCy^F6D`jz$;m4%$s>Ovx}ZD~35p}6hknP2UMnI|e#xV6mE{trR5~ZOyo3szW3*Ca zgFuD@GtTgJYQ44`Gg||?vSi1{Rq|OL-Hcz;7OanQ89%&_`jJc8ar=<$U{>hpJ&$i_ z|Ez3h&SR7M`3yLZ8Oqy8mt}h%(v|lIj`n8?*K6{ph+n+soDP1@2S422)c;Mu(f{L) z|B2#%6WF8w&A`!re;N4sg~wvecY=q{0nUdQwZ9BF`hQZm#vw%eaM}R&STA^u8tY|m z8KikPj6)~jST7@iqkWlh)uZ)t5!hqgt^|(dx(7J=-{kn$dU+r0(f=2~(f>#p^0~ZN zFA)#F95~j?b->a8v%)nFA`+WD_r$xy~MyC zt#7`^#77@jy+3TC2>!e zL$t<6ZqW$%q7^6B$;WK%kJ-R6-zAQpDZDeKIN(T82|5aQ^-KYd zetzlr$rV4#!5;Oz0UZ5w;E5b1)ic9>42}}+>L~<{e)Kon)K9Vac^>RhPa8I*#QxDw zC*kU+%2?5PCfM`2sMgo9z_EW#1CH(aT;SNh>VTvDlfwCh+m-gOm%tv||J%T^eYWO? zNr}sg{*M-}{wEqM`n^Q3NBC{Qn6Y{jU;Td^qGXTq&Q|IQ(Y$ zyw=0ld-z5V-{j%1d-!G#-|FGp9Ip3<=@eeetGuPm2bAB-!Jwq`9GACu>~EEEDdh0A z!n=BSFApE+;X^$pXmghp%$@U2?ox z6_VBG9zRkl;C#QDJo4JW=d0R?*cX=BRZ|~tD5AW*Xy*zxN zhY$7eu!oQL@Y6henuqH>r*3@8JoZ%{KF`A!diY`wU+Up?9=^iES9$mv4`1uy>pgs< zhi~%m*FAi*hi~=pZ62;mN^U*2l=BXkxAE}y9v<@Wt{&dY!v}i!P!A7#_;?RL&BLd8 z_)HJi^G7#sRUZ3!9=_1S7kl_p53lp^6&}9I!`FEDS`T0E;Tt`ClZU_V;hQ~ttA}s% za9uie>#L>AQ(WH0!`pj!$iusOcrOpvd)Qq)Lp}Ck4m>qhSL(l|tb4e;jfd+q4X%C2W8c-odwKXk4A4Kh49ZdH75Z zFZ1v!51;4o>9U@((BWDyiyi)^*oRDM?HpDP^@fagxL%hzy5E4l=8+HiZ+p*(^zVDj zfMbkpS0 zW@{7MdWCCht><%w#@~({7><1$?)Hap>@VT!M`({RYr?T-!~gor$k_Uk-DvL%=&y23 zjgeOUF5TJtl(Bo{jTZEJ4B5{c9eX7FWR^K`qkG@zDVXeq^x2$%g=3YgNJJeyf3Q9r zTTo`pTtzRVYwzGsnKk#AGFLyyfe4~|lU#mBh30^Ap>@+xW?98%>u5+Si2*J#hw~wM4C%1qho*9o1oq2 zG{P6J;}+oDCTJfZx_HZG+c>&%7O{Im=4DBH&Z4#*f2di&AHpeHgWbZdVN=}Bv?nQr=o1@WJ zGqF9rud_NTSD1&iY9{s~CVgrQ?U7AQ^mBTdM{!&juFay?C%IOFVe-UN)`B;OrH@?K zz|%(d$tJN2$6n@VqwJ$qriIXk*rVj)g&L_-ThBEMxYkoNrT0|9=veX-Nz{~BnQ2P) zZqpxnGur2S-i(aCZ`O>=Lv#;uL(=N}K{TlN!;Kej;YC&I*k){q-P6W&+9fS*-wo)6 z4qRV;#V@tn=!M1>vn}ONWw($wbKtU{%upq}gi;;2WE&4v_N^JJ?A!W7)tbamRsA5# z3)1KFa-SZOYO9Ph~jh-|IU%NpL)n<&7BUs3o)^_EW+GXa` z&KbBy*WAOBGix3+q2xzuco>^!)Y|qZN7%}x<_K$tF*(YPp&^VOI^|((Ntuzjpvp)z zhuqBA=c8kveal9e727sC_Rj{#Hf{@am^AIg9=5hWwx!R%j_X54+;?mFgUmVN#oyt{ z1yzlS!p^B0J*FgDkEsuH6qJU7Eyz?1w4&N+rb3pCH!oa6wh@`ZKU}-Oe%f_MoNrPq ziRW$Uh{L0RJ9*jd2MIdIkcp11;_vq$!D@fRgM_K99X$itm4igQKK2X}{51)?=}e;I z4{DGwYIo~F;x3#?EO%xSwzB@agTyd78?ci*GoMJ*%FY}r%okLA`#GOo-`Ew?z90Pr zqa&?X|8OQ4M(vMy>@bz}{Tn-2)=obc*{#Nog=Pigf`zmYJ&(@3%t>*qmQN08?V`xM z)XwA+vl@HC?5>5MXV%ofvzBFJlEsh&))eo{@yExA8 zk`~A9CTDRxpR9Jm;yAn6?&3H{k=BLhk@|%x&i5Ny7U$Y=&n(UNYBg}pBv#9hrBi*K zW?r0&BsKQBim5MJ6PHh>zN~%y`r61B8UFP$J(ItYR~+`|c|&}~VGJ#{(4w$iwdPQ3 zBZ3u%CsM3h%L4VkHrZvIRmQ6wK-Wr@)5>}_Rr^P_F;MN-X4UCB$gWr@v|-!MMT~}4 zo>ypXC9ga)PI+}}SJ%|uyQHR!*X`HTceA*=)9Vb@ZTQMW{Ho18<89lv3t6>8=@Si* zL(Gf4OjEPKY^y&&TC4-4{bV|veOtf7t(6W}{UB#Ui?m^`$Qrs7^Q2j>JlNLjJn1Ji zcFWjot|3mdLTBuaUK(jZmtk#)qY(>K;oJ|*rxJ%L?oJHSR zlto|Y_uVX>a;$0b)YO9=+)@mtPWMcoe+vs=5KZwp@*Seq%c1`DquVCR6Ww+@Z zHi*#elP6z0+&L1_f{NMGIlk*7M~CmGbC_&?=tqu`c;pxYM-B<%x9l9c)J2C{bNqmq zHMWa^>7;-^qgNY4EV}!|yZlz1jomRzf;? zuB5Z`?DerR>qgfU(_Lvh^XsbZy?tZW(b@&w*Os*@#dO=hc2T`%qsdC^Oc#gF)PB=7p$T)wA*R6Of|4(bWP=|%$f^U*ps4fULW>m z68{~SV^&hFtfg96zx`TCpMSkMleH%s?_Qx&cSU2_0aHE*ih~x2H;WU?|!RtK{M>vQlW4-1a=b5Ub^@NI#(G z7m}Y`crx<{{yvBxHtC3#HB0!(20p}^XBZm&I76z%Gr4ZF&5TJ8b~Wp)dmd-ly&q?w zmK8F~*5+Q;}R@s{cbmjeT5Z*SD|uInc#h zTABs1AM(Qv51Umy`@kM8yY-?TxaVO9`$JGW_z2f_A9kRY*~X51?dc_XUQS6ihB8S=il?NQz|Lx@Wf+816K`q z&mqKIRDl}b^C*Pp_wkYrYu}F{#9k0vOwE>V6Zk$1A@M7v(4g!i;PL?8_v`VgJwwPj zL*q|hHtvzj6tj>$4@!KmXCZ6(xUM%5-E9u?I*H7hx97PD8asDz`WRn3ZQya)JtNCb z8ClGQS2&h@uO}xzoX043O6yFY-|)y{FPi^XCntKF3ORn>RV4y^n#3zv)}M+ z=X*J_EVZ*sx>0w}LlzVYdnL1hZ;OH7rlxNoYQ#ep`gle1Is-p=k$RoMf8WBz>$tMq zQ&?HMUt{Vq#rIV0>O1*&(`PPd5Hr=RPVl%1}@O;>WbU# zAHmr5RR}B4>uvut4=Tm}t50N1HYWi;>L)U&=HXe4zsy1ol$Vq`7u7+u~TBYd-N!~;sS%*?*FXEPO1I$`Tibd zWBM%*M!F6ae_SfQ2=#q@^kb*Qbh|xDo&*VhH&24-V<1U0{p@wAr}_Q`7QNRW?_aae zS9}Lgv+SBO#HZQSTTY|8Xy{o5Ugi7_pA*rCM3U=lWuxjWeg19cbX#t|S*FT&{$nHG zMkRET>UF1iPk{E+41HC8I6d=GotKZ4ukLR6p^`l{Blm%4H@43_f_;X6bC`SkclXH2 zo(kJjVRB@RAC)fNVy{1WuUpU;*~l5SU5$6XGK#+asYZUAfbN1YUr%oB%-564E}>Lk zPcGZ|>&fj~b4A;}ZS?PI@P&X~^1B-5yMwCDuQJ%L4&!y@q^}NR5Ja7?4&x_6cEVSO zv77CDbr^-6A1N9Z+i0FXkt?Kc>kyveXEYkPb|q`)_}od2{c3IZ8;eYRhbZH44c9mS za)$k6>f64p|2r!5h_Jfj`nG1KzU|xk_eRk?Y0ocb?6hCbpzw3uI*sMSL}RJ>tIv;U`rB25AEM**8TKOzVG+rAS{`F`@)3s2l(3; zG&=EfAPtXB>GNY;BYLCd`AOBY=W&k~;l|un-{6&#MpOcvP+z=`JGJ+m`r<9SNuM`Y z%ivzN@5?Qcsr!85TAbV%_MfATKW`HcKQx$ceegf|p7?k}tTxIgtn!P-HT09Q?kZyq z{hX}+Y6QRKZ$#(U3HD^)_j?iS_S^hk1n*D3m|R6KcF~vLB`x~eP0pe(KT5C@7Jb>x zb{BoK7CNWN{uSTqbye=^nLMhn&@RW!qzv^ZwRI8SS%Ims-%K9=MTlvVITP`^fg z9KQ%b=Pw*Sr!pE^QRSeU`bb_;{Vwa`NASSGx2+XewB+k0DSzT2W`6gaTEKYI8|rl# zi}g1h>?iY^4)$&RF0(fNn+{>}NQ;jH%&d3UK0&})(V}4{m%;p6?)U3A9Von~k+g<8 zSo-`9c(Twg9=6^734ulgCuy;c8?$>f_1rWN6et@WX8DyW}4I`fVM4Jph`>f9dB)8daU?^9SO%-UG(-+Z6>n*ejaf~{;7k8YbGD3GO?ashWZ{{NI9C z%ny!izoK`;;ND8**K5GSARvtaN zuq3yv`r)gvy&l~IM853Bx-MRy|hjATI z>g)CUR@`s6k+$4cqGEdwJ6FAD*tXL28q-v4`5R$p$}e_&S76*<8HE0%M69;%fbC&i zZc2O?LQ219CH#hWz&0wjy;zHuKdE26VH|U;>csX`wAHq9NTOe@7rSYx?2btEtF2-; z!rT5oNwj~qq0|`Lu-KmN_1mhM_SfhR*w%^d2CRdo*srSNvDzTE_o1!E>H=F=M^Je_ z5xd7y*DcN?b&j?`rPKvcA>pE0zuFxm0hz$zY2@pCs?QFLDs8Hm73#KbO&tf#I{v45}|d9 ze*cj0yI}`xw~6f%tb^I0_w+;zj%aB@5G+Dljn$b6yU}8IZ7RDN){fg%RP453OtDNW z6LGEE0ox5?o93&7#}aGT>cx34qxU3sJ^HzOLUiUt|^xzXGp zV|qnJkWpT0Kjf7b7e`8HTS;j&l5x`5?4A|T+`L(2z`I3e<`k6Y7DsGkzy1Fqa>3!0 zZXI_xRNkF%RUDZ&+?_BTN*`Qiwy(8TW&P}HD|+F4r1=ExEe^T}=cb2go|CU`bMrci zHn7icx_@ulmvwQM!le1jkRRXV=0AwG@czBx7WT~o(ci=2Y|BYshr@fbLH=9iDZlQg zwv%OlSHJEb+D9$ztHxjH1bX9^iMN$~YAy6Q%Js+l>i$}P`xA3x&wFmEq#N5obR$C8XwjnEYJfv46zFw}_wnWZ*OBz~tv= z)EHCgnc(3IJbbl>|BX27e@*(O=G&GUajJZ?JiN-oZ}sr!h;zPa=~PHc=G>fKTTmQR z<;&Ys@p9tq=Y*daH*r6=c3?# zcAdxmBM;{}JL~!TA=ZysJD|6th^NwXfrsBJey){8AhWhWe*WUIZ$(`_mH*>Be3pkV zAAMIs0kWCE3qJ z;;Hm3^zaA8&%kchk6A0Bw_7~+?WiB7@;{I`>-oU(Gt*oL~etRK-#s>&Un2adp_ZFsZUp+6=v|7M^!XGQU&!|4g#nthvzJSA}ooi63eN*_IxyigO zO-xzOv^>kr+6KKHLY#J~m-57ZX2fnX>l^g8R`^vjEH~>J^mdK#b%mCj^$U9Yw(vE@ z$-G@#sjqV^H|rAgc7*WXRVMRt;g8O>+^jdy+v|l-KF@Nqwm@&!3IE6WmYcN%di$a9 zxffb))(+_Hp>+J__(U#F=3(Kd%(vXE3DDcw!Y5sp%$EzFe7WT}+n3-O;h#JF-@=#G zSVMCzPj8Q;c{=O)d{Hvb7C!k3%k>(-eBpn%GMNX(IdiIti*ib5N6L#zbMq@QP8`%L z$jNbW&X{qTIb{`*%KXyYXtca=dSx_HoJ()2@@A5GL1`83Qcy9sg5H+X24hbr@HzBX zSTZBW+U68RN`m6tqN38g#_yC+lUG_EF^*YrZhpR_Cx@gIlt&_o9pa_(%P*SIppv4} z8HIVdMX0}_(k7elkdQ)J!)VAgZI8@|l&g}0;;4yc<9FF1HRX}qqKdKx1+hs^McIxB zO0i4c%vn2<3pb#KikzIo4i`DU5k=15@ggVh(x@UQc4<_R&6Za+sj*qgdNIz2S$U&j7mBVBx=Av^HtmJ6-(@PxLA$s z_KhwUHLpe&i)V~zdo!_kmv8I5smtQ+CmjKPfvn6Ya_ zyo_CA!etB_8LhZ&qYD(D)i)d~oUUAHCjAbLEVBNBDBf{X0CXLatoj~Nx z%q`E&i$=;TVA(_bx}7m$+dywj7;UdRx|R(zFxRx<;HI+nN7uF9PM1t+-Wcz;S$apY zgkNLm@0E^WqoNk?Yv!alWwUYIHryf|<3@+9FWp-1vu(yN!NKfu{|w;$L;$IclDeI_NeDn;HYO` zx_-(@{fJp`fN;*2mX_oB9tRwCP619!$Z`A29RIY*q+ftN>g4OZoZPrwN7px%^F0_E zw?~1ao;QK>rEs-xOV=woX?g9ZAm}KZ^F=-Vfn(hIvja+O&-d)8|FeLjo^yfoC3Ur5 zA)H^ZU9ACo)UzHq>Uo$OD<#%*DE+DbcYvdwz1b-xw&zRgYCk}@tLG%JM?F)4qn@e! zffD=wDgCMcYT&5nM&O5o{U3$9dj1ObsOJOV{8sbj>sg#wPl*1Mx8_boi8<=Y0N#PN zsr?zkT|Gr$k9y_-M?Htq^&?K|N9+Cy;jYemfuqh>fTPa6xzmDw`zZ(x7S8@rXE)%e zGjxF6lkGdvpT;2!9QB+H{0OjLDqPEJy#~Pwutz-)07pHOx${#}KN`2Eg}XXG0**Ql zlKW%W9(863SN|q6`Y#*oQD+1=>Rc>0^0EIe^rvyV2RQ0^75I^0-&F3uarLwn&i+wP zN8qUEc)9gm9P7bUjuMSp5P zPPnTl2kcQ#1#r~UO725q|GmL~AK<8G9Pnen{sQ5yo?5U+JF#2ob;54<03Q~M&}uAcM39`!5+j(WOwjCa09tkB*-cg}XY> z1CBcDfTPZ5gsXp>TM)bk_Neni;HWc;2SG}#X9)di+=_vto-2VL2lfvNclG=c>`~7f zz){cOuH=-G`e|-I2A2tUb>0aab-n-`bvETenUeaqpXlBV;p`uEb_9+(&j5Y`?W1wK z7&z*=9{5nOe+D?_`xlsadns0aDm~STVFEj`@}W-w*6B621>H?(5p$uLOJ4vl=+Y;m^QP&ptevpv3x7 z&mq9~qiw2x4)6)UZvc+@J^*|o*#94J%r}iEE0o;!)>gP1hmK&6dQJk4dd>!pdX@o4 zJ$C@dIP~F3jOb)d8lTgEW4;#wZw>aVg=@LkC#R>uKAS$P|CfQIo~wD%Ly65_*gjm;Zr@$_fVenP-2cc z2LNwJ+tj{9xccXPIn4)q)OihX)Oi_ChA45qe2S+2?*xu|o&|mo*tg`#2PId};lkax z9RnQo9CNJo!}hqIb2@O;GaLB9w2$h)L%6HwF|fz@JP#c8Jk65>O04Hl`cwZO0Y^RU zj%RCY&!;kKKT5c(Cl~BdPbF~FQ^}JFO04In^r!xp0Y^Re0Y4n?-CwPe7s-AwpQO{)HeCnX~R|t3YEC+jx&l=#UXChBxD5)PkUOXYZDQyeU zpXU1^aMbyeOfsUx_Nen@;p*QQ(SOsx9(9%hN1Zcx(nE>;ccMSlb0u)pa~JR4if!Tn&5I-drPIzJt0 z_hda?=uhKz$jJ$gdWHe#Qwp^&6YlD{6zoyYwZKu&Z$~BauN6FQ5tZ-M)IN+$~9pJd0lRnA% zXFsT?2XLNxsQ&4~T|HG`kM*?>IO_Qu8&TqXc}k-G_d1oS<*26{a9k&uCfwCC8|+cf z<-k$TKO8?ge)gK2(1SWp1CBZ`0FF9W3wL!s4fd$>W#Fi@_mqSlT+f*T9QB+B9M?&H zE8Nwy9_&%itH4oDztgQA^`mjSL^#(k>bwm&>iiRM)Y)QcvVZ%DuC)nw+y7C(QD*^g zo;qoJs{xLBehqvm*uMiD=c$eAyd5~^`+MM+Z@V)S z`DTHiLBKKJDZqK^q4{199P_;sIOe++IIics1swDJ8aPiGG~Z!oCG=yyrvS%%{}=el z;OBYZnD58HM}d96oYeV_1djQh4xA6^s{bP3nC~xv^PyPnUjdHgeIGdH8=jWX$%jz& zQw$vQtp(19KDGZ7aLo4~z%k#CguDH{X>MQ=w{z5UByiMoGH}$h2srAw0XXXUH*h|L zYPk-dp3sB&9uJ%kiE2L&IOcl|aLo5+;cgu627A=A2{`Kc0yye9E-#@U^^5_Idj61~ zuxCwLuC2f^--99v`_^E8jBqWNwW6^L?9T@K3BXa$R|N_GZNR?Ej08tL!-4M)_Bp~` zJ*8ledM*Nvdb$giCNu*dbBk-$+;1o*+UtG2^S zgu8lv4)&<$Cg7;&^3sH!L&5)Vfuo)mfFB0-{}k@(X;PN(k9rOOj(QF_Ct?3n@P8a| z)H4P6;b1>oxU1(;&{ofivN|KdKl3hYtmuYjY@*qnqOT+g`+IO=&G_z|Fg zt8iCOP?hkHdfEX;J$DLMKN`1=a}$10=gGiPXDM*hxk$MBw_bzb2Czq+D}kfVwdW@E z;Cjwmz)?@@^Ah$)(p#`$pnsi#-E??-wpg%0Y^R80Pha=cL{g(JOcKp z=UL#W=d}y09`&QgowtSaID8c3yWfS@o;m954!kGWpCnxU+r8=gG{7Ep&H#=&Z@(y! z@6q7@dElt$Q{cV8zU@5g$JNtOxO?2`4;=M81H3o6)OPY2aMaWGVltw{{*R$QwLe<8 ztLFr;$M}o`j(UD^346Ex`_ilWe;7FGc>{Pqu>VT9t7pGz_HO;7o{qp#&;7vr(|)Sw z@4!*dSHK5={h{+Y*ZS*AJ%l%XI9`)=WOXQ1s&K9nIG;Y5Gd(`{Ja zJg`3-$~6o)>Wl(E0qlPX9QE7{d??tz0{m>??*qqt52_&}O587o)1S86A;2-;Q-O~F z`wM|%zE=Rpd|v~e1%A>N(FaPbrxpFFo=(6|1U?El=9>c?^DP5@68KpH9P_;sco^*e z3>@=)2RP<75E(Jag{5%L8^ZhIE@nGNlO8P*F^4*jz%k!i;F#|X!reHm z1bfu;EO6BG$^Vye|F#LlU;xDvMarKYQn;W%5kq7E7mSe+JVUzl6ex0;L9R&QVXmOK zB7E2n9WRzBcxN3S96~_xTlMukuV3-3{$H2fs;}osm)(7TycVo@PsWNCH@wpEGwa$v z#uv>)Y(6jcx3 z^V>e(&V%Q-SI6@!>Ue%t9nY_+cz)X#&GXss@73}AiaMTORmbyd>Ue&OI-cJj Dq*C7N literal 0 HcmV?d00001 diff --git a/src/xml/sax/Makefile b/src/xml/sax/Makefile new file mode 100644 index 0000000000..c0a134055e --- /dev/null +++ b/src/xml/sax/Makefile @@ -0,0 +1,50 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_sax.o: m_sax_operate.o +m_sax_operate.o: m_sax_parser.o m_sax_reader.o m_sax_types.o +m_sax_parser.o: m_sax_reader.o m_sax_tokenizer.o m_sax_types.o +m_sax_reader.o: m_sax_xml_source.o +m_sax_tokenizer.o: m_sax_reader.o m_sax_types.o +m_sax_types.o: m_sax_reader.o diff --git a/src/xml/utils/Makefile b/src/xml/utils/Makefile new file mode 100644 index 0000000000..449bd4d91f --- /dev/null +++ b/src/xml/utils/Makefile @@ -0,0 +1,46 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_utils.o: fox_m_utils_uuid.o fox_m_utils_uri.o +fox_m_utils_uuid.o: fox_m_utils_mtprng.o diff --git a/src/xml/wxml/Makefile b/src/xml/wxml/Makefile new file mode 100644 index 0000000000..55dcc3d9cb --- /dev/null +++ b/src/xml/wxml/Makefile @@ -0,0 +1,47 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_wxml.o: m_wxml_core.o m_wxml_overloads.o +m_wxml_core.o: m_wxml_escape.o +m_wxml_overloads.o: m_wxml_core.o From b893a3f61c2fb510c50aedc56c5971eec52d0085 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 12:06:42 -0400 Subject: [PATCH 053/192] removed old makefiles --- src/xml/common/makefile | 58 ---------------------------------------- src/xml/dom/makefile | 48 --------------------------------- src/xml/fsys/makefile | 47 -------------------------------- src/xml/lib/libxml.a | Bin 1940196 -> 0 bytes src/xml/sax/makefile | 49 --------------------------------- src/xml/utils/makefile | 45 ------------------------------- src/xml/wxml/makefile | 46 ------------------------------- 7 files changed, 293 deletions(-) delete mode 100644 src/xml/common/makefile delete mode 100644 src/xml/dom/makefile delete mode 100644 src/xml/fsys/makefile delete mode 100644 src/xml/lib/libxml.a delete mode 100644 src/xml/sax/makefile delete mode 100644 src/xml/utils/makefile delete mode 100644 src/xml/wxml/makefile diff --git a/src/xml/common/makefile b/src/xml/common/makefile deleted file mode 100644 index 063c288a21..0000000000 --- a/src/xml/common/makefile +++ /dev/null @@ -1,58 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys -I../utils $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_common.o: m_common_attrs.o -m_common_attrs.o: m_common_element.o m_common_error.o -m_common_buffer.o: m_common_charset.o m_common_error.o -m_common_element.o: m_common_charset.o m_common_content_model.o m_common_error.o m_common_namecheck.o -m_common_elstack.o: m_common_content_model.o m_common_error.o -m_common_entities.o: m_common_charset.o m_common_error.o -m_common_entity_expand.o: m_common_entities.o m_common_error.o -m_common_entity_expand.o: m_common_namecheck.o m_common_struct.o -m_common_io.o: m_common_error.o -m_common_namecheck.o: m_common_charset.o -m_common_namespaces.o: m_common_attrs.o m_common_charset.o m_common_error.o -m_common_namespaces.o: m_common_namecheck.o m_common_struct.o -m_common_notations.o: m_common_error.o -m_common_struct.o: m_common_charset.o m_common_element.o m_common_entities.o -m_common_struct.o: m_common_notations.o diff --git a/src/xml/dom/makefile b/src/xml/dom/makefile deleted file mode 100644 index 98286bc783..0000000000 --- a/src/xml/dom/makefile +++ /dev/null @@ -1,48 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils -I../sax -I../wxml $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_dom.o: m_dom_dom.o m_dom_error.o m_dom_extras.o m_dom_parse.o m_dom_utils.o -m_dom_dom.o: m_dom_error.o -m_dom_extras.o: m_dom_dom.o m_dom_error.o -m_dom_parse.o: m_dom_dom.o m_dom_error.o -m_dom_utils.o: m_dom_dom.o m_dom_error.o diff --git a/src/xml/fsys/makefile b/src/xml/fsys/makefile deleted file mode 100644 index d6b81cc870..0000000000 --- a/src/xml/fsys/makefile +++ /dev/null @@ -1,47 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -fox_m_fsys_format.o: fox_m_fsys_realtypes.o fox_m_fsys_abort_flush.o -fox_m_fsys_parse_input.o: fox_m_fsys_realtypes.o -fox_m_fsys_count_parse_input.o: fox_m_fsys_realtypes.o -fox_m_fsys_string_list.o: fox_m_fsys_array_str.o diff --git a/src/xml/lib/libxml.a b/src/xml/lib/libxml.a deleted file mode 100644 index 558e0c9a1657c4724c1243289a726ee54b81d62d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1940196 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
>>2n1L=YEJ0_AAgXt( z`?1u!rIOdZTiuVP-fgvlCU8sj4+$_()(coTs5EyPnJ7nhxfEaIK%zU-MWTi1Pbmiy z=Pj&vD<=HORr*B|g~C#Z+D$ps(jOmO8X(8L+F;jlr1_ZaBMxW+x{jSGY|s+`I?4Op zb}qVX)%{>o6=}h)<4Ae?r1E7M8LO^iH{8Ev6mX1d3fCT7X$rg#SDFIv*HhrTvCCO^ z-r3}}C{i`jSDO{lHdD~onpP}BYNX;8``q=iiD$*k@_Z(!LASTxd?nD=jkuB@f?xu01F>ujZz3t7~pGZgtIFgxf_b zZ5;;g-38}_x=o$*jEPs@?Pgv0v((Js+rRu##k4pNQiEPZP4yadN^7L=%?9ZPU2|Qq z2*`|!D##ru-Lq0^pf(v*feT#wdU=$fE8-kt;b2rmt02aAHmHa~;08Td0NkLH+yfR{ zH|SJ)UI!nAoQ3Et6FmG^Z-)L;t0N=NUY z$WN}?SRLA-ci7uT1)j4Snj3R7{X-6`Mn=!eMVeGAPewU(H8iUU8emKw=Qi$ycq?K6 z!Fb4OXvGUP?#zIQ8yr%z6b5&p*`Z$$1%;&mH7}~qqNt+!8f~&|D6QGM#6V1U_z z9URq11rid~H&UtW=Dl~k?OIjj9CJi-H%zR!$hr8PJ5|4*Klc0u9%eUH2XoHrBq3H) z3T|07puAu*FpE;v!D}a7>>Ezj0880#g53P4g^N-rKdn|R??=nNmQwkvD-m`^kbE75 z5l1hJxdk40LrKSXOT&-&TN)nCTN-k&1=G{UP7lrFT@7t;aAQM$`>h058@1c2iWiEv zyg&f8Lp=HQLT7qIz%7=yGwAfaOFsCp%5w*+|%iAV;&dBP2QrH^9i`kmytfmg%gmGAysdrN8u4=7x8=8WrASR#Z=WNaM zVo&5E9g$|diMZ2B2M65#k|y?lF#uXIVa}ewcDqB~cDvut*>2Z6Ad9x!{XR+_Tkv`? zZk3+k?RK9C*>0Dk5p65hyt7VEy9TNg*5V=he7rZ%j$B0*xqy+kV0&p;e*S;@42W3xMw`Mm?_BgTuOVA&3$W5jc2qc3cSDdJK|ywm zLXe_|2(~)5mPQE@G-=Zf48PSV5hBTa28ms8#IdZEYSJ;>;pRE9q8&^Q#q1{sP z8aBA(lvA?ON6JNssjCPvbw=-P0pcVkp?#j1J_M�*D2| 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≈E=Q+vnMWR0)I?TBooH>shof)!EP8)tZ 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 S(ytf9uq4 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 Dq*C7N diff --git a/src/xml/sax/makefile b/src/xml/sax/makefile deleted file mode 100644 index a440a53c82..0000000000 --- a/src/xml/sax/makefile +++ /dev/null @@ -1,49 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_sax.o: m_sax_operate.o -m_sax_operate.o: m_sax_parser.o m_sax_reader.o m_sax_types.o -m_sax_parser.o: m_sax_reader.o m_sax_tokenizer.o m_sax_types.o -m_sax_reader.o: m_sax_xml_source.o -m_sax_tokenizer.o: m_sax_reader.o m_sax_types.o -m_sax_types.o: m_sax_reader.o diff --git a/src/xml/utils/makefile b/src/xml/utils/makefile deleted file mode 100644 index dca3504369..0000000000 --- a/src/xml/utils/makefile +++ /dev/null @@ -1,45 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_utils.o: fox_m_utils_uuid.o fox_m_utils_uri.o -fox_m_utils_uuid.o: fox_m_utils_mtprng.o diff --git a/src/xml/wxml/makefile b/src/xml/wxml/makefile deleted file mode 100644 index 7cc8ee2884..0000000000 --- a/src/xml/wxml/makefile +++ /dev/null @@ -1,46 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_wxml.o: m_wxml_core.o m_wxml_overloads.o -m_wxml_core.o: m_wxml_escape.o -m_wxml_overloads.o: m_wxml_core.o From ca5a30672ac464044b0ca20b458e610a0450aef5 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 12:07:10 -0400 Subject: [PATCH 054/192] added static library extension --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8aa7c1f976..4ecff16b3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Compiled objects and modules +*.a *.o *.mod *.log From 6524e00a77c64fd1a8612de431ebd5b1a114c059 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 12:08:35 -0400 Subject: [PATCH 055/192] added xml interface --- src/OBJECTS | 3 ++- src/xml_interface.F90 | 53 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/xml_interface.F90 diff --git a/src/OBJECTS b/src/OBJECTS index b079906751..5526dac4b7 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -59,4 +59,5 @@ string.o \ tally.o \ tally_header.o \ tally_initialize.o \ -timer_header.o +timer_header.o \ +xml_interface.o diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 new file mode 100644 index 0000000000..6eb5e0a7e8 --- /dev/null +++ b/src/xml_interface.F90 @@ -0,0 +1,53 @@ +module xml_interface + + use fox_dom + + implicit none + private + public :: open_xmldoc + public :: close_xmldoc + +contains + +!=============================================================================== +! OPEN_XMLDOC +!=============================================================================== + + subroutine open_xmldoc(ptr, filename) + + character(len=*) :: filename + type(Node), pointer :: ptr + + ptr => parseFile(trim(filename)) + + end subroutine open_xmldoc + +!=============================================================================== +! CLOSE_XMLDOC +!=============================================================================== + + subroutine close_xmldoc(ptr) + + type(Node), pointer :: ptr + + call destroy(ptr) + + end subroutine close_xmldoc + +!=============================================================================== +! GET_ELEMENT +!=============================================================================== + + subroutine get_element(inptr, elename, outptr) + + character(len=*) :: elename + type(Node), pointer :: inptr + type(Node), pointer :: outptr + type(NodeList), pointer :: tempptr + + tempptr => getElementsByTagname(inptr, elename) + outptr => item(tempptr,0) + + end subroutine get_element + +end module xml_interface From 8d8511020a1df9fafdbd345b44318ee7884fcaf3 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 14:36:12 -0400 Subject: [PATCH 056/192] made target the actual library so it doesn't recompile all the time --- src/xml/Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/xml/Makefile b/src/xml/Makefile index 744da31475..f9151178c6 100644 --- a/src/xml/Makefile +++ b/src/xml/Makefile @@ -1,8 +1,10 @@ +xml_lib = lib/libxml.a + #=============================================================================== # Targets #=============================================================================== -all: +$(xml_lib): mkdir -p include mkdir -p lib cd fsys; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" @@ -22,3 +24,9 @@ clean: cd dom; make clean @rm -r -f include @rm -r -f lib + +#=============================================================================== +# Rules +#=============================================================================== + +.PHONY: clean From e17d78384eacd937796f9db301fe035768f4b7d2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 17:31:30 -0400 Subject: [PATCH 057/192] added long integer scalar processing to XML parser --- src/xml/dom/m_dom_extras.F90 | 68 ++++++++++++++++++++++ src/xml/fsys/fox_m_fsys_parse_input.F90 | 77 +++++++++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/src/xml/dom/m_dom_extras.F90 b/src/xml/dom/m_dom_extras.F90 index f301622d5b..790561746c 100644 --- a/src/xml/dom/m_dom_extras.F90 +++ b/src/xml/dom/m_dom_extras.F90 @@ -31,6 +31,7 @@ module m_dom_extras module procedure extractDataContentIntSca module procedure extractDataContentIntArr module procedure extractDataContentIntMat + module procedure extractDataContentLongSca module procedure extractDataContentLgSca module procedure extractDataContentLgArr module procedure extractDataContentLgMat @@ -56,6 +57,7 @@ module m_dom_extras module procedure extractDataAttributeIntSca module procedure extractDataAttributeIntArr module procedure extractDataAttributeIntMat + module procedure extractDataAttributeLongSca module procedure extractDataAttributeLgSca module procedure extractDataAttributeLgArr module procedure extractDataAttributeLgMat @@ -227,6 +229,33 @@ endif end subroutine extractDataContentIntSca +subroutine extractDataContentLongSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer(8), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentLongSca + subroutine extractDataContentLgSca(arg, data, num, iostat, ex) type(DOMException), intent(out), optional :: ex type(Node), pointer :: arg @@ -863,6 +892,45 @@ endif end subroutine extractDataAttributeIntSca +subroutine extractDataAttributeLongSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + integer(8), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeLongSca + subroutine extractDataAttributeLgSca(arg, name, data, num, iostat, ex) type(DOMException), intent(out), optional :: ex type(Node), pointer :: arg diff --git a/src/xml/fsys/fox_m_fsys_parse_input.F90 b/src/xml/fsys/fox_m_fsys_parse_input.F90 index e0307e99c2..45551c1d68 100644 --- a/src/xml/fsys/fox_m_fsys_parse_input.F90 +++ b/src/xml/fsys/fox_m_fsys_parse_input.F90 @@ -16,6 +16,7 @@ module fox_m_fsys_parse_input module procedure scalartostring module procedure scalartological module procedure scalartointeger + module procedure scalartolong module procedure scalartorealsp module procedure scalartorealdp module procedure scalartocomplexsp @@ -346,6 +347,82 @@ contains #endif end subroutine scalartointeger + subroutine scalartolong(s, data, num, iostat) + character(len=*), intent(in) :: s + integer(8), intent(out) :: data + integer, intent(out), optional :: num, iostat +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + data = 0 + ij = 0 + length = 1 + loop: do i = 1, 1 + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ij Date: Mon, 22 Jul 2013 17:32:07 -0400 Subject: [PATCH 058/192] added logic to skip using fox_dom module --- src/utils/build_dependencies.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/build_dependencies.py b/src/utils/build_dependencies.py index 44a5c5ccaf..3aa22c6076 100755 --- a/src/utils/build_dependencies.py +++ b/src/utils/build_dependencies.py @@ -11,7 +11,7 @@ for src in glob.iglob('*.F90'): d = re.findall(r'\n\s*use\s+(\w+)', open(src,'r').read()) for name in d: - if name in ['mpi','hdf5','h5lt']: + if name in ['mpi','hdf5','h5lt','fox_dom']: continue if name.startswith('xml_data_'): name = name.replace('xml_data_', 'templates/') @@ -20,7 +20,7 @@ for src in glob.iglob('*.F90'): dependencies[module] = sorted(list(deps)) -for module in dependencies.keys(): +for module in sorted(dependencies.keys()): for dep in dependencies[module]: print("{0}.o: {1}.o".format(module, dep)) print('') From 31fb97e03082c6b6bf365d5e287e4d68f127887b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 17:32:39 -0400 Subject: [PATCH 059/192] started building up xml interface and processing settings XML file --- src/DEPENDENCIES | 10 +- src/input_xml.F90 | 54 +++++----- src/xml_interface.F90 | 222 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 252 insertions(+), 34 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index e5d20644b0..39ddf39e93 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -177,8 +177,8 @@ hdf5_summary.o: geometry_header.o hdf5_summary.o: global.o hdf5_summary.o: material_header.o hdf5_summary.o: mesh_header.o -hdf5_summary.o: output_interface.o hdf5_summary.o: output.o +hdf5_summary.o: output_interface.o hdf5_summary.o: string.o hdf5_summary.o: tally_header.o @@ -194,8 +194,8 @@ initialize.o: global.o initialize.o: hdf5_interface.o initialize.o: hdf5_summary.o initialize.o: input_xml.o -initialize.o: output_interface.o initialize.o: output.o +initialize.o: output_interface.o initialize.o: random_lcg.o initialize.o: source.o initialize.o: state_point.o @@ -223,6 +223,7 @@ input_xml.o: templates/materials_t.o input_xml.o: templates/plots_t.o input_xml.o: templates/settings_t.o input_xml.o: templates/tallies_t.o +input_xml.o: xml_interface.o interpolation.o: constants.o interpolation.o: endf_header.o @@ -374,3 +375,8 @@ tally_initialize.o: global.o tally_initialize.o: tally_header.o timer_header.o: constants.o + +xml_interface.o: constants.o +xml_interface.o: error.o +xml_interface.o: global.o + diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 617788e472..de0918daaf 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -48,6 +48,7 @@ contains subroutine read_settings_xml() use xml_data_settings_t + use xml_interface integer :: i ! loop index integer :: n @@ -56,6 +57,11 @@ contains character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename + type(Node), pointer :: doc + type(Node), pointer :: node_eigenvalue + logical :: found + integer :: temp_int + integer(8) :: temp_long ! Display output message message = "Reading settings XML file..." @@ -82,15 +88,16 @@ contains ! Parse settings.xml file call read_xml_file_settings_t(filename) + call open_xmldoc(doc, filename) ! Find cross_sections.xml file -- the first place to look is the ! settings.xml file. If no file is found there, then we check the ! CROSS_SECTIONS environment variable - - if (len_trim(cross_sections_) == 0 .and. run_mode /= MODE_PLOTTING) then + found = check_for_node(doc, "cross_sections") + if (.not. found .and. run_mode /= MODE_PLOTTING) then ! No cross_sections.xml file specified in settings.xml, check environment ! variable - call get_environment_variable("CROSS_SECTIONS", env_variable) + call get_environment_variable("CROSS_SECTIONS", env_variable) if (len_trim(env_variable) == 0) then message = "No cross_sections.xml file was specified in settings.xml & &or in the CROSS_SECTIONS environment variable." @@ -99,47 +106,45 @@ contains path_cross_sections = trim(env_variable) end if else - path_cross_sections = trim(cross_sections_) + call get_node_value(doc, "cross_sections", path_cross_sections) end if ! Set output directory if a path has been specified on the ! element - if (len_trim(output_path_) > 0) then - path_output = output_path_ + found = check_for_node(doc, "output_path") + if (found) then + call get_node_value(doc, "output_path", path_output) if (.not. ends_with(path_output, "/")) & path_output = trim(path_output) // "/" end if - ! Make sure that either criticality or fixed source was specified - if (eigenvalue_ % batches == 0 .and. fixed_source_ % batches == 0 & - .and. criticality_ % batches == 0) then - message = "Number of batches on or & - &tag was zero." + ! Make sure that either eigenvalue or fixed source was specified + if (.not.check_for_node(doc, "eigenvalue") .and. & + .not.check_for_node(doc, "fixed_source")) then + message = " or not specified." call fatal_error() end if - ! Check for old tag - if (criticality_ % batches > 0) then - eigenvalue_ = criticality_ - message = "The element has been deprecated and & - &replaced by ." - call warning() - end if - ! Eigenvalue information - if (eigenvalue_ % batches > 0) then + if (check_for_node(doc, "eigenvalue")) then ! Set run mode if (run_mode == NONE) run_mode = MODE_EIGENVALUE + ! Get pointer to eigenvalue XML block + call get_node_ptr(doc, "eigenvalue", node_eigenvalue) + ! Check number of particles - if (len_trim(eigenvalue_ % particles) == 0) then - message = "Need to specify number of particles per cycles." + if (.not.check_for_node(node_eigenvalue, "particles")) then + message = "Need to specify number of particles per generation." call fatal_error() end if + ! Get number of particles + call get_node_value(node_eigenvalue, "particles", temp_long) + ! If the number of particles was specified as a command-line argument, we ! don't set it here - if (n_particles == 0) n_particles = str_to_int(eigenvalue_ % particles) + if (n_particles == 0) n_particles = temp_long ! Copy batch and generation information n_batches = eigenvalue_ % batches @@ -569,6 +574,9 @@ contains #endif end if + ! Close settings XML file + call close_xmldoc(doc) + end subroutine read_settings_xml !=============================================================================== diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 6eb5e0a7e8..3d6924a8bb 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -1,11 +1,27 @@ module xml_interface + use constants, only: MAX_LINE_LEN + use error, only: fatal_error + use global, only: message use fox_dom implicit none private + public :: Node public :: open_xmldoc public :: close_xmldoc + public :: check_for_node + public :: get_node_ptr + public :: get_node_value + + integer, parameter :: ATTR_NODE = 0 + integer, parameter :: ELEM_NODE = 1 + + interface get_node_value + module procedure get_node_value_integer + module procedure get_node_value_long + module procedure get_node_value_string + end interface get_node_value contains @@ -19,6 +35,7 @@ contains type(Node), pointer :: ptr ptr => parseFile(trim(filename)) + ptr => getDocumentElement(ptr) end subroutine open_xmldoc @@ -35,19 +52,206 @@ contains end subroutine close_xmldoc !=============================================================================== -! GET_ELEMENT +! CHECK_FOR_NODE !=============================================================================== - subroutine get_element(inptr, elename, outptr) + function check_for_node(ptr, node_name) result(found) - character(len=*) :: elename - type(Node), pointer :: inptr - type(Node), pointer :: outptr - type(NodeList), pointer :: tempptr + character(len=*), intent(in) :: node_name + logical :: found + type(Node), pointer, intent(in) :: ptr - tempptr => getElementsByTagname(inptr, elename) - outptr => item(tempptr,0) + type(Node), pointer :: temp_ptr + type(NodeList), pointer :: elem_list - end subroutine get_element + ! Default that we found it and attribute + found = .true. + + ! Get the attribute node + temp_ptr => getAttributeNode(ptr, trim(node_name)) + + ! Check if node exists + if (associated(temp_ptr)) return + + ! Check for a sub-element + elem_list => getElementsByTagName(ptr, trim(node_name)) + + ! Get the length of the list + if (getLength(elem_list) == 0) then + found = .false. + return + end if + + end function check_for_node + +!=============================================================================== +! GET_NODE_PTR +!=============================================================================== + + subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) + + character(len=*), intent(in) :: node_name + logical, intent(out), optional :: found + type(Node), pointer, intent(in) :: in_ptr + type(Node), pointer, intent(out) :: out_ptr + + logical :: found_ + type(NodeList), pointer :: elem_list => null() + + ! Set found to false + found_ = .false. + + ! Check for a sub-element + elem_list => getElementsByTagName(in_ptr, trim(node_name)) + + ! Get the length of the list + if (getLength(elem_list) == 0) return + + ! Point to the new element + out_ptr => item(elem_list, 0) + found_ = .true. + + ! Check to output found + if (present(found)) found = found_ + + end subroutine get_node_ptr + +!=============================================================================== +! GET_NODE_VALUE_INTEGER +!=============================================================================== + + subroutine get_node_value_integer(ptr, node_name, result_int) + + character(len=*), intent(in) :: node_name + integer :: result_int + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_int) + else + call extractDataContent(temp_ptr, result_int) + end if + + end subroutine get_node_value_integer + +!=============================================================================== +! GET_NODE_VALUE_INTEGER +!=============================================================================== + + subroutine get_node_value_long(ptr, node_name, result_long) + + character(len=*), intent(in) :: node_name + integer(8) :: result_long + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_long) + else + call extractDataContent(temp_ptr, result_long) + end if + + end subroutine get_node_value_long + +!=============================================================================== +! GET_NODE_VALUE_STRING +!=============================================================================== + + subroutine get_node_value_string(ptr, node_name, result_str) + + character(len=*), intent(in) :: node_name + character(len=MAX_LINE_LEN) :: result_str + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_str) + else + call extractDataContent(temp_ptr, result_str) + end if + + end subroutine get_node_value_string + +!=============================================================================== +! GET_NODE +!=============================================================================== + + subroutine get_node(in_ptr, node_name, out_ptr, node_type, found) + + character(len=*), intent(in) :: node_name + integer, intent(out) :: node_type + logical, intent(out) :: found + type(Node), pointer, intent(in) :: in_ptr + type(Node), pointer, intent(out) :: out_ptr + + type(NodeList), pointer :: elem_list + + ! Default that we found it and attribute + found = .true. + node_type = ATTR_NODE + + ! Get the attribute node + out_ptr => getAttributeNode(in_ptr, trim(node_name)) + + ! Check if node exists + if (associated(out_ptr)) return + + ! Check for a sub-element + elem_list => getElementsByTagName(in_ptr, trim(node_name)) + + ! Get the length of the list + if (getLength(elem_list) == 0) then + found = .false. + return + end if + + ! Point to the new element + node_type = ELEM_NODE + out_ptr => item(elem_list, 0) + + end subroutine get_node end module xml_interface From 49d9bf6c221543dd12afee22799ffe405f8aad2e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 18:43:16 -0400 Subject: [PATCH 060/192] fixed Makefile after merge --- src/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index e76aa1788f..8d3646973d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -228,12 +228,14 @@ endif # Targets #=============================================================================== -all: xml-fortran $(program) +all: xml xml-fortran $(program) +xml: + cd xml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" xml-fortran: cd xml-fortran; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" cd templates; make F90=$(F90) F90FLAGS="$(F90FLAGS)" $(program): $(objects) - $(F90) $(objects) $(templates) $(xml_fort) $(LDFLAGS) -o $@ + $(F90) $(objects) $(templates) $(xml_fort) $(xml_lib) $(LDFLAGS) -o $@ install: @install -D $(program) $(DESTDIR)$(prefix)/bin/$(program) @install -D utils/statepoint_cmp.py $(DESTDIR)$(prefix)/bin/statepoint_cmp From a4118b73f38bb4f8ee70163e7fbad709e177f2f5 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 23:42:39 -0400 Subject: [PATCH 061/192] added array size and read array methods to interface and now read in settings through trace information --- src/input_xml.F90 | 171 ++++++++++++++++++++++++++++-------------- src/xml_interface.F90 | 170 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 281 insertions(+), 60 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index de0918daaf..d2ccf8a7db 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -50,18 +50,22 @@ contains use xml_data_settings_t use xml_interface + character(MAX_LINE_LEN) :: temp_str integer :: i ! loop index integer :: n integer :: coeffs_reqd + integer :: temp_int + integer :: temp_int_array(3) + integer(8) :: temp_long logical :: file_exists character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename type(Node), pointer :: doc - type(Node), pointer :: node_eigenvalue - logical :: found - integer :: temp_int - integer(8) :: temp_long + type(Node), pointer :: node_mode + type(Node), pointer :: node_source + type(Node), pointer :: node_dist + type(Node), pointer :: node_cutoff ! Display output message message = "Reading settings XML file..." @@ -93,8 +97,8 @@ contains ! Find cross_sections.xml file -- the first place to look is the ! settings.xml file. If no file is found there, then we check the ! CROSS_SECTIONS environment variable - found = check_for_node(doc, "cross_sections") - if (.not. found .and. run_mode /= MODE_PLOTTING) then + if (.not. check_for_node(doc, "cross_sections") .and. & + run_mode /= MODE_PLOTTING) then ! No cross_sections.xml file specified in settings.xml, check environment ! variable call get_environment_variable("CROSS_SECTIONS", env_variable) @@ -111,8 +115,7 @@ contains ! Set output directory if a path has been specified on the ! element - found = check_for_node(doc, "output_path") - if (found) then + if (check_for_node(doc, "output_path")) then call get_node_value(doc, "output_path", path_output) if (.not. ends_with(path_output, "/")) & path_output = trim(path_output) // "/" @@ -131,26 +134,28 @@ contains if (run_mode == NONE) run_mode = MODE_EIGENVALUE ! Get pointer to eigenvalue XML block - call get_node_ptr(doc, "eigenvalue", node_eigenvalue) + call get_node_ptr(doc, "eigenvalue", node_mode) ! Check number of particles - if (.not.check_for_node(node_eigenvalue, "particles")) then + if (.not.check_for_node(node_mode, "particles")) then message = "Need to specify number of particles per generation." call fatal_error() end if ! Get number of particles - call get_node_value(node_eigenvalue, "particles", temp_long) + call get_node_value(node_mode, "particles", temp_long) ! If the number of particles was specified as a command-line argument, we ! don't set it here if (n_particles == 0) n_particles = temp_long ! Copy batch and generation information - n_batches = eigenvalue_ % batches - n_inactive = eigenvalue_ % inactive + call get_node_value(node_mode, "batches", n_batches) + call get_node_value(node_mode, "inactive", n_inactive) n_active = n_batches - n_inactive - gen_per_batch = eigenvalue_ % generations_per_batch + if (check_for_node(node_mode, "generations_per_batch")) & + call get_node_value(node_mode, "generations_per_batch", & + gen_per_batch) ! Allocate array for batch keff and entropy allocate(k_generation(n_batches*gen_per_batch)) @@ -159,23 +164,29 @@ contains end if ! Fixed source calculation information - if (fixed_source_ % batches > 0) then + if (check_for_node(doc, "fixed_source")) then ! Set run mode if (run_mode == NONE) run_mode = MODE_FIXEDSOURCE + ! Get pointer to fixed_source XML block + call get_node_ptr(doc, "fixed_source", node_mode) + ! Check number of particles - if (len_trim(fixed_source_ % particles) == 0) then - message = "Need to specify number of particles per cycles." + if (.not.check_for_node(node_mode, "fixed_source")) then + message = "Need to specify number of particles per batch." call fatal_error() end if + ! Get number of particles + call get_node_value(node_mode, "particles", temp_long) + ! If the number of particles was specified as a command-line argument, we ! don't set it here - if (n_particles == 0) n_particles = str_to_int(fixed_source_ % particles) + if (n_particles == 0) n_particles = temp_long ! Copy batch information - n_batches = fixed_source_ % batches - n_active = fixed_source_ % batches + call get_node_value(node_mode, "batches", n_batches) + n_active = n_batches n_inactive = 0 gen_per_batch = 1 end if @@ -193,10 +204,15 @@ contains end if ! Copy random number seed if specified - if (seed_ > 0) seed = seed_ + if (check_for_node(doc, "seed")) call get_node_value(doc, "seed", seed) ! Energy grid methods - select case (energy_grid_) + if (check_for_node(doc, "energy_grid")) then + call get_node_value(doc, "energy_grid", temp_str) + else + temp_str = 'union' + end if + select case (trim(temp_str)) case ('nuclide') grid_method = GRID_NUCLIDE case ('union') @@ -211,13 +227,24 @@ contains ! Verbosity if (verbosity_ > 0) verbosity = verbosity_ + if (check_for_node(doc, "verbosity")) & + call get_node_value(doc, "verbosity", verbosity) ! ========================================================================== ! EXTERNAL SOURCE - if (source_ % file /= '') then + ! Get pointer to source + if (check_for_node(doc, "source")) then + call get_node_ptr(doc, "source", node_source) + else + message = "No source specified in settings XML file." + call fatal_error() + end if + + ! Check for external source file + if (check_for_node(node_source, "file")) then ! Copy path of source file - path_source = source_ % file + call get_node_value(node_source, "file", path_source) ! Check if source file exists inquire(FILE=path_source, EXIST=file_exists) @@ -228,12 +255,18 @@ contains end if else + ! Spatial distribution for external source - if (source_ % space % type /= '') then - ! Read type of spatial distribution - type = source_ % space % type + if (check_for_node(node_source, "space")) then + + ! Get pointer to spatial distribution + call get_node_ptr(node_source, "space", node_dist) + + ! Check for type of spatial distribution + if (check_for_node(node_dist, "type")) & + call get_node_value(node_dist, "type", type) call lower_case(type) - select case (type) + select case (trim(type)) case ('box') external_source % type_space = SRC_SPACE_BOX coeffs_reqd = 6 @@ -242,13 +275,13 @@ contains coeffs_reqd = 3 case default message = "Invalid spatial distribution for external source: " & - // trim(source_ % space % type) + // trim(source_ % space % type) call fatal_error() end select ! Determine number of parameters specified - if (associated(source_ % space % parameters)) then - n = size(source_ % space % parameters) + if (check_for_node(node_dist, "parameters")) then + n = get_arraysize_double(node_dist, "parameters") else n = 0 end if @@ -264,19 +297,25 @@ contains call fatal_error() elseif (n > 0) then allocate(external_source % params_space(n)) - external_source % params_space = source_ % space % parameters + call get_node_array(node_dist, "parameters", & + external_source % params_space) end if else - message = "No spatial distribution specified for external source!" + message = "No spatial distribution specified for external source." call fatal_error() end if ! Determine external source angular distribution - if (source_ % angle % type /= '') then - ! Read type of angular distribution - type = source_ % angle % type + if (check_for_node(node_source, "angle")) then + + ! Get pointer to angular distribution + call get_node_ptr(node_source, "angle", node_dist) + + ! Check for type of angular distribution + if (check_for_node(node_dist, "type")) & + call get_node_value(node_dist, "type", type) call lower_case(type) - select case (type) + select case (trim(type)) case ('isotropic') external_source % type_angle = SRC_ANGLE_ISOTROPIC coeffs_reqd = 0 @@ -292,8 +331,8 @@ contains end select ! Determine number of parameters specified - if (associated(source_ % angle % parameters)) then - n = size(source_ % angle % parameters) + if (check_for_node(node_dist, "parameters")) then + n = get_arraysize_double(node_dist, "parameters") else n = 0 end if @@ -309,7 +348,8 @@ contains call fatal_error() elseif (n > 0) then allocate(external_source % params_angle(n)) - external_source % params_angle = source_ % angle % parameters + call get_node_array(node_dist, "parameters", & + external_source % params_angle) end if else ! Set default angular distribution isotropic @@ -317,11 +357,16 @@ contains end if ! Determine external source energy distribution - if (source_ % energy % type /= '') then - ! Read type of energy distribution - type = source_ % energy % type + if (check_for_node(node_source, "energy")) then + + ! Get pointer to energy distribution + call get_node_ptr(node_source, "energy", node_dist) + + ! Check for type of energy distribution + if (check_for_node(node_dist, "type")) & + call get_node_value(node_dist, "type", type) call lower_case(type) - select case (type) + select case (trim(type)) case ('monoenergetic') external_source % type_energy = SRC_ENERGY_MONO coeffs_reqd = 1 @@ -340,8 +385,8 @@ contains end select ! Determine number of parameters specified - if (associated(source_ % energy % parameters)) then - n = size(source_ % energy % parameters) + if (check_for_node(node_dist, "parameters")) then + n = get_arraysize_double(node_dist, "parameters") else n = 0 end if @@ -357,7 +402,8 @@ contains call fatal_error() elseif (n > 0) then allocate(external_source % params_energy(n)) - external_source % params_energy = source_ % energy % parameters + call get_node_array(node_dist, "parameters", & + external_source % params_energy) end if else ! Set default energy distribution to Watt fission spectrum @@ -368,24 +414,33 @@ contains end if ! Survival biasing - call lower_case(survival_) - if (survival_ == 'true' .or. survival_ == '1') survival_biasing = .true. + if (check_for_node(doc, "survival_biasing")) then + call get_node_value(doc, "survival_biasing", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + survival_biasing = .true. + end if ! Probability tables - call lower_case(ptables_) - if (ptables_ == 'false' .or. ptables_ == '0') urr_ptables_on = .false. + if (check_for_node(doc, "ptables")) then + call get_node_value(doc, "ptables", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & + urr_ptables_on = .false. + end if ! Cutoffs - if (size(cutoff_) > 0) then - weight_cutoff = cutoff_(1) % weight - weight_survive = cutoff_(1) % weight_avg + if (check_for_node(doc, "cutoff")) then + call get_node_value(doc, "weight", weight_cutoff) + call get_node_value(doc, "weight_avg", weight_survive) end if ! Particle trace - if (associated(trace_)) then - trace_batch = trace_(1) - trace_gen = trace_(2) - trace_particle = trace_(3) + if (check_for_node(doc, "trace")) then + call get_node_array(doc, "trace", temp_int_array) + trace_batch = temp_int_array(1) + trace_gen = temp_int_array(2) + trace_particle = int(temp_int_array(3), 8) end if ! Shannon Entropy mesh diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 3d6924a8bb..44df324ce2 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -13,6 +13,9 @@ module xml_interface public :: check_for_node public :: get_node_ptr public :: get_node_value + public :: get_node_array + public :: get_arraysize_integer + public :: get_arraysize_double integer, parameter :: ATTR_NODE = 0 integer, parameter :: ELEM_NODE = 1 @@ -20,9 +23,15 @@ module xml_interface interface get_node_value module procedure get_node_value_integer module procedure get_node_value_long + module procedure get_node_value_double module procedure get_node_value_string end interface get_node_value + interface get_node_array + module procedure get_node_array_integer + module procedure get_node_array_double + end interface get_node_array + contains !=============================================================================== @@ -150,7 +159,7 @@ contains end subroutine get_node_value_integer !=============================================================================== -! GET_NODE_VALUE_INTEGER +! GET_NODE_VALUE_LONG !=============================================================================== subroutine get_node_value_long(ptr, node_name, result_long) @@ -182,6 +191,105 @@ contains end subroutine get_node_value_long +!=============================================================================== +! GET_NODE_VALUE_DOUBLE +!=============================================================================== + + subroutine get_node_value_double(ptr, node_name, result_double) + + character(len=*), intent(in) :: node_name + real(8) :: result_double + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_double) + else + call extractDataContent(temp_ptr, result_double) + end if + + end subroutine get_node_value_double + +!=============================================================================== +! GET_NODE_ARRAY_INTEGER +!=============================================================================== + + subroutine get_node_array_integer(ptr, node_name, result_int) + + character(len=*), intent(in) :: node_name + integer :: result_int(:) + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_int) + else + call extractDataContent(temp_ptr, result_int) + end if + + end subroutine get_node_array_integer + +!=============================================================================== +! GET_NODE_ARRAY_DOUBLE +!=============================================================================== + + subroutine get_node_array_double(ptr, node_name, result_double) + + character(len=*), intent(in) :: node_name + real(8) :: result_double(:) + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_double) + else + call extractDataContent(temp_ptr, result_double) + end if + + end subroutine get_node_array_double + !=============================================================================== ! GET_NODE_VALUE_STRING !=============================================================================== @@ -189,7 +297,7 @@ contains subroutine get_node_value_string(ptr, node_name, result_str) character(len=*), intent(in) :: node_name - character(len=MAX_LINE_LEN) :: result_str + character(len=*) :: result_str type(Node), pointer, intent(in) :: ptr integer :: node_type @@ -215,6 +323,64 @@ contains end subroutine get_node_value_string +!=============================================================================== +! GET_NODE_ARRAYSIZE_INTEGER +!=============================================================================== + + function get_arraysize_integer(ptr, node_name) result(n) + + character(len=*), intent(in) :: node_name + integer :: n + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Get the size + n = countrts(getTextContent(temp_ptr), 0) + + end function get_arraysize_integer + +!=============================================================================== +! GET_NODE_ARRAYSIZE_DOUBLE +!=============================================================================== + + function get_arraysize_double(ptr, node_name) result(n) + + character(len=*), intent(in) :: node_name + integer :: n + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Get the size + n = countrts(getTextContent(temp_ptr), 0.0_8) + + end function get_arraysize_double + !=============================================================================== ! GET_NODE !=============================================================================== From 308968ba40e857a3059261e59f4bd4a90e1cbd99 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 10:06:21 -0400 Subject: [PATCH 062/192] converted over settings input to new XML reader --- src/input_xml.F90 | 217 +++++++++++++++++++++++++++------------------- 1 file changed, 128 insertions(+), 89 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index d2ccf8a7db..530447162a 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -47,25 +47,29 @@ contains subroutine read_settings_xml() - use xml_data_settings_t use xml_interface character(MAX_LINE_LEN) :: temp_str - integer :: i ! loop index + integer :: i integer :: n integer :: coeffs_reqd integer :: temp_int - integer :: temp_int_array(3) + integer :: temp_int_array3(3) + integer, allocatable :: temp_int_array(:) integer(8) :: temp_long logical :: file_exists character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename - type(Node), pointer :: doc - type(Node), pointer :: node_mode - type(Node), pointer :: node_source - type(Node), pointer :: node_dist - type(Node), pointer :: node_cutoff + type(Node), pointer :: doc => null() + type(Node), pointer :: node_mode => null() + type(Node), pointer :: node_source => null() + type(Node), pointer :: node_dist => null() + type(Node), pointer :: node_cutoff => null() + type(Node), pointer :: node_entropy => null() + type(Node), pointer :: node_ufs => null() + type(Node), pointer :: node_sp => null() + type(Node), pointer :: node_output => null() ! Display output message message = "Reading settings XML file..." @@ -79,19 +83,7 @@ contains call fatal_error() end if - ! Initialize XML scalar variables - cross_sections_ = '' - output_path_ = '' - verbosity_ = 0 - energy_grid_ = 'union' - seed_ = 0_8 - source_ % file = '' - source_ % space % type = '' - source_ % angle % type = '' - source_ % energy % type = '' - ! Parse settings.xml file - call read_xml_file_settings_t(filename) call open_xmldoc(doc, filename) ! Find cross_sections.xml file -- the first place to look is the @@ -221,12 +213,11 @@ contains message = "Lethargy mapped energy grid not yet supported." call fatal_error() case default - message = "Unknown energy grid method: " // energy_grid_ + message = "Unknown energy grid method: " // temp_str call fatal_error() end select ! Verbosity - if (verbosity_ > 0) verbosity = verbosity_ if (check_for_node(doc, "verbosity")) & call get_node_value(doc, "verbosity", verbosity) @@ -263,6 +254,7 @@ contains call get_node_ptr(node_source, "space", node_dist) ! Check for type of spatial distribution + type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) call lower_case(type) @@ -275,7 +267,7 @@ contains coeffs_reqd = 3 case default message = "Invalid spatial distribution for external source: " & - // trim(source_ % space % type) + // trim(type) call fatal_error() end select @@ -312,6 +304,7 @@ contains call get_node_ptr(node_source, "angle", node_dist) ! Check for type of angular distribution + type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) call lower_case(type) @@ -326,7 +319,7 @@ contains external_source % type_angle = SRC_ANGLE_TABULAR case default message = "Invalid angular distribution for external source: " & - // trim(source_ % angle % type) + // trim(type) call fatal_error() end select @@ -363,6 +356,7 @@ contains call get_node_ptr(node_source, "energy", node_dist) ! Check for type of energy distribution + type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) call lower_case(type) @@ -380,7 +374,7 @@ contains external_source % type_energy = SRC_ENERGY_TABULAR case default message = "Invalid energy distribution for external source: " & - // trim(source_ % energy % type) + // trim(type) call fatal_error() end select @@ -437,19 +431,23 @@ contains ! Particle trace if (check_for_node(doc, "trace")) then - call get_node_array(doc, "trace", temp_int_array) - trace_batch = temp_int_array(1) - trace_gen = temp_int_array(2) - trace_particle = int(temp_int_array(3), 8) + call get_node_array(doc, "trace", temp_int_array3) + trace_batch = temp_int_array3(1) + trace_gen = temp_int_array3(2) + trace_particle = int(temp_int_array3(3), 8) end if ! Shannon Entropy mesh - if (size(entropy_) > 0) then + if (check_for_node(doc, "entropy")) then + + ! Get pointer to entropy node + call get_node_ptr(doc, "entropy", node_entropy) + ! Check to make sure enough values were supplied - if (size(entropy_(1) % lower_left) /= 3) then + if (get_arraysize_double(node_entropy, "lower_left") /= 3) then message = "Need to specify (x,y,z) coordinates of lower-left corner & &of Shannon entropy mesh." - elseif (size(entropy_(1) % upper_right) /= 3) then + elseif (get_arraysize_double(node_entropy, "upper_right") /= 3) then message = "Need to specify (x,y,z) coordinates of upper-right corner & &of Shannon entropy mesh." end if @@ -460,8 +458,10 @@ contains allocate(entropy_mesh % upper_right(3)) ! Copy values - entropy_mesh % lower_left = entropy_(1) % lower_left - entropy_mesh % upper_right = entropy_(1) % upper_right + call get_node_array(node_entropy, "lower_left", & + entropy_mesh % lower_left) + call get_node_array(node_entropy, "upper_right", & + entropy_mesh % upper_right) ! Check on values provided if (.not. all(entropy_mesh % upper_right > entropy_mesh % lower_left)) then @@ -472,10 +472,10 @@ contains ! Check if dimensions were specified -- if not, they will be calculated ! automatically upon first entry into shannon_entropy + if (check_for_node(node_entropy, "dimension")) then - if (associated(entropy_(1) % dimension)) then ! If so, make sure proper number of values were given - if (size(entropy_(1) % dimension) /= 3) then + if (get_arraysize_integer(node_entropy, "dimension") /= 3) then message = "Dimension of entropy mesh must be given as three & &integers." call fatal_error() @@ -486,7 +486,7 @@ contains allocate(entropy_mesh % dimension(3)) ! Copy dimensions - entropy_mesh % dimension = entropy_(1) % dimension + call get_node_array(node_entropy, "dimension", entropy_mesh % dimension) end if ! Turn on Shannon entropy calculation @@ -494,15 +494,19 @@ contains end if ! Uniform fission source weighting mesh - if (size(uniform_fs_) > 0) then + if (check_for_node(doc, "uniform_fs")) then + + ! Get pointer to ufs node + call get_node_ptr(doc, "uniform_fs", node_ufs) + ! Check to make sure enough values were supplied - if (size(uniform_fs_(1) % lower_left) /= 3) then + if (get_arraysize_double(node_ufs, "lower_left") /= 3) then message = "Need to specify (x,y,z) coordinates of lower-left corner & &of UFS mesh." - elseif (size(uniform_fs_(1) % upper_right) /= 3) then + elseif (get_arraysize_double(node_ufs, "upper_right") /= 3) then message = "Need to specify (x,y,z) coordinates of upper-right corner & &of UFS mesh." - elseif (size(uniform_fs_(1) % dimension) /= 3) then + elseif (get_arraysize_integer(node_ufs, "dimension") /= 3) then message = "Dimension of UFS mesh must be given as three integers." call fatal_error() end if @@ -518,11 +522,11 @@ contains allocate(ufs_mesh % dimension(3)) ! Copy dimensions - ufs_mesh % dimension = uniform_fs_(1) % dimension + call get_node_array(node_ufs, "dimension", ufs_mesh % dimension) ! Copy values - ufs_mesh % lower_left = uniform_fs_(1) % lower_left - ufs_mesh % upper_right = uniform_fs_(1) % upper_right + call get_node_array(node_ufs, "lower_left", ufs_mesh % lower_left) + call get_node_array(node_ufs, "upper_right", ufs_mesh % upper_right) ! Check on values provided if (.not. all(ufs_mesh % upper_right > ufs_mesh % lower_left)) then @@ -547,25 +551,32 @@ contains end if ! Check if the user has specified to write state points - if (size(state_point_) > 0) then + if (check_for_node(doc, "state_point")) then + + ! Get pointer to state_point node + call get_node_ptr(doc, "state_point", node_sp) + ! Determine number of batches at which to store state points - if (associated(state_point_(1) % batches)) then - n_state_points = size(state_point_(1) % batches) + if (check_for_node(node_sp, "batches")) then + n_state_points = get_arraysize_integer(node_sp, "batches") else n_state_points = 0 end if if (n_state_points > 0) then ! User gave specific batches to write state points + allocate(temp_int_array(n_state_points)) + call get_node_array(node_sp, "batches", temp_int_array) do i = 1, n_state_points - call statepoint_batch % add(state_point_(1) % batches(i)) + call statepoint_batch % add(temp_int_array(i)) end do - - elseif (state_point_(1) % interval /= 0) then + deallocate(temp_int_array) + elseif (check_for_node(node_sp, "interval")) then ! User gave an interval for writing state points - n_state_points = n_batches / state_point_(1) % interval + call get_node_value(node_sp, "interval", temp_int) + n_state_points = n_batches / temp_int do i = 1, n_state_points - call statepoint_batch % add(state_point_(1) % interval * i) + call statepoint_batch % add(temp_int * i) end do else ! If neither were specified, write state point at last batch @@ -574,12 +585,18 @@ contains end if ! Check if the user has specified to write binary source file - call lower_case(state_point_(1) % source_separate) - call lower_case(state_point_(1) % source_write) - if (state_point_(1) % source_separate == 'true' .or. & - state_point_(1) % source_separate == '1') source_separate = .true. - if (state_point_(1) % source_write == 'false' .or. & - state_point_(1) % source_write == '0') source_write = .false. + if (check_for_node(node_sp, "source_separate")) then + call get_node_value(node_sp, "source_separate", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') source_separate = .true. + end if + if (check_for_node(node_sp, "source_write")) then + call get_node_value(node_sp, "source_write", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') source_separate = .true. + end if else ! If no tag was present, by default write state point at ! last batch only @@ -589,44 +606,66 @@ contains ! Check if the user has specified to not reduce tallies at the end of every ! batch - call lower_case(no_reduce_) - if (no_reduce_ == 'true' .or. no_reduce_ == '1') reduce_tallies = .false. + if (check_for_node(doc, "no_reduce")) then + call get_node_value(doc, "no_reduce", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + reduce_tallies = .false. + end if ! Check if the user has specified to use confidence intervals for ! uncertainties rather than standard deviations - call lower_case(confidence_intervals_) - if (confidence_intervals_ == 'true' .or. & - confidence_intervals_ == '1') confidence_intervals = .true. - - ! Check for output options - if (associated(output_)) then - do i = 1, size(output_) - call lower_case(output_(i)) - select case (output_(i)) - case ('summary') - output_summary = .true. - case ('cross_sections') - output_xs = .true. - case ('tallies') - output_tallies = .true. - case ('none') - output_summary = .false. - output_xs = .false. - output_tallies = .false. - end select - end do + if (check_for_node(doc, "confidence_intervals")) then + call get_node_value(doc, "confidence_intervals", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') confidence_intervals = .true. end if - ! check for cmfd run - call lower_case(run_cmfd_) - if (run_cmfd_ == 'true' .or. run_cmfd_ == '1') then - cmfd_run = .true. -#ifndef PETSC - if (master) then - message = 'CMFD is not available, compile OpenMC with PETSc' - call fatal_error() + ! Check for output options + if (check_for_node(doc, "output")) then + + ! Get pointer to output node + call get_node_ptr(doc, "output", node_output) + + ! Check for summary option + if (check_for_node(node_output, "summary")) then + call get_node_value(node_output, "summary", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') output_summary = .true. end if + + ! Check for cross sections option + if (check_for_node(node_output, "cross_sections")) then + call get_node_value(node_output, "cross_sections", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') output_xs = .true. + end if + + ! Check for ASCII tallies output option + if (check_for_node(node_output, "tallies")) then + call get_node_value(node_output, "tallies", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'false' .or. & + trim(temp_str) == '0') output_tallies = .false. + end if + end if + + ! Check for cmfd run + if (check_for_node(doc, "run_cmfd")) then + call get_node_value(doc, "run_cmfd", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') then + cmfd_run = .true. +#ifndef PETSC + if (master) then + message = 'CMFD is not available, compile OpenMC with PETSc' + call fatal_error() + end if #endif + end if end if ! Close settings XML file From e4d26e84e6c47c25b93b4ae1b3977aba2d4b0c55 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 11:31:10 -0400 Subject: [PATCH 063/192] added capability in xml interface to get number of nodes and select a certain node index --- src/xml_interface.F90 | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 44df324ce2..cd948b64e6 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -11,6 +11,7 @@ module xml_interface public :: open_xmldoc public :: close_xmldoc public :: check_for_node + public :: get_number_nodes public :: get_node_ptr public :: get_node_value public :: get_node_array @@ -93,17 +94,39 @@ contains end function check_for_node +!=============================================================================== +! GET_NUMBER_NODES +!=============================================================================== + + function get_number_nodes(ptr, node_name) result(n_nodes) + + character(len=*), intent(in) :: node_name + integer :: n_nodes + type(Node), pointer, intent(in) :: ptr + + type(NodeList), pointer :: elem_list + + ! Check for a sub-element + elem_list => getElementsByTagName(ptr, trim(node_name)) + + ! Get the length of the list + n_nodes = getLength(elem_list) + + end function get_number_nodes + !=============================================================================== ! GET_NODE_PTR !=============================================================================== - subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) + subroutine get_node_ptr(in_ptr, node_name, out_ptr, idx, found) character(len=*), intent(in) :: node_name + integer, intent(in), optional :: idx logical, intent(out), optional :: found type(Node), pointer, intent(in) :: in_ptr type(Node), pointer, intent(out) :: out_ptr + integer :: idx_ logical :: found_ type(NodeList), pointer :: elem_list => null() @@ -117,7 +140,9 @@ contains if (getLength(elem_list) == 0) return ! Point to the new element - out_ptr => item(elem_list, 0) + idx_ = 0 + if (present(idx)) idx_ = idx - 1 + out_ptr => item(elem_list, idx_) found_ = .true. ! Check to output found @@ -148,7 +173,7 @@ contains getNodeName(ptr) // "." call fatal_error() end if - + ! Extract value if (node_type == ATTR_NODE) then call extractDataAttribute(ptr, node_name, result_int) From 8d7d24d63ee45b58260830b9bc22a7bb35bfd211 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 11:31:41 -0400 Subject: [PATCH 064/192] converted over geometry input XML reading --- src/input_xml.F90 | 150 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 44 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 530447162a..760383d577 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -15,6 +15,7 @@ module input_xml starts_with, ends_with use tally_header, only: TallyObject, TallyFilter use tally_initialize, only: add_tallies + use xml_interface implicit none save @@ -47,8 +48,6 @@ contains subroutine read_settings_xml() - use xml_interface - character(MAX_LINE_LEN) :: temp_str integer :: i integer :: n @@ -689,6 +688,8 @@ contains integer :: n_cells_in_univ integer :: coeffs_reqd integer :: mid + integer :: temp_int_array3(3) + integer, allocatable :: temp_int_array(:) real(8) :: phi, theta, psi logical :: file_exists logical :: boundary_exists @@ -697,6 +698,10 @@ contains type(Cell), pointer :: c => null() type(Surface), pointer :: s => null() type(Lattice), pointer :: lat => null() + type(Node), pointer :: doc => null() + type(Node), pointer :: node_cell => null() + type(Node), pointer :: node_surf => null() + type(Node), pointer :: node_lat => null() ! Display output message message = "Reading geometry XML file..." @@ -715,9 +720,11 @@ contains ! Parse geometry.xml file call read_xml_file_geometry_t(filename) + call open_xmldoc(doc, filename) ! Get number of tags - n_cells = size(cell_) + n_cells = 0 + n_cells = get_number_nodes(doc, "cell") ! Check for no cells if (n_cells == 0) then @@ -737,10 +744,26 @@ contains do i = 1, n_cells c => cells(i) + ! Get pointer to i-th cell node + call get_node_ptr(doc, "cell", node_cell, i) + ! Copy data into cells - c % id = cell_(i) % id - c % universe = cell_(i) % universe - c % fill = cell_(i) % fill + if (check_for_node(node_cell, "id")) then + call get_node_value(node_cell, "id", c % id) + else + message = "Must specify id of cell in geometry XML file." + call fatal_error() + end if + if (check_for_node(node_cell, "universe")) then + call get_node_value(node_cell, "universe", c % universe) + else + c % universe = NONE + end if + if (check_for_node(node_cell, "fill")) then + call get_node_value(node_cell, "fill", c % fill) + else + c % fill = NONE + end if ! Check to make sure 'id' hasn't been used if (cell_dict % has_key(c % id)) then @@ -749,7 +772,9 @@ contains end if ! Read material - word = cell_(i) % material + word = '' + if (check_for_node(node_cell, "material")) & + call get_node_value(node_cell, "material", word) call lower_case(word) select case(word) case ('void') @@ -757,7 +782,7 @@ contains case ('') ! This case is called if no material was specified - c % material = 0 + c % material = NONE case default c % material = int(str_to_int(word), 4) @@ -784,20 +809,20 @@ contains end if ! Check to make sure that surfaces were specified - if (.not. associated(cell_(i) % surfaces)) then + if (.not. check_for_node(node_cell, "surfaces")) then message = "No surfaces specified for cell " // & trim(to_str(c % id)) call fatal_error() end if ! Allocate array for surfaces and copy - n = size(cell_(i) % surfaces) + n = get_arraysize_integer(node_cell, "surfaces") c % n_surfaces = n allocate(c % surfaces(n)) - c % surfaces = cell_(i) % surfaces + call get_node_array(node_cell, "surfaces", c % surfaces) ! Rotation matrix - if (associated(cell_(i) % rotation)) then + if (check_for_node(node_cell, "rotation")) then ! Rotations can only be applied to cells that are being filled with ! another universe if (c % fill == NONE) then @@ -807,7 +832,7 @@ contains end if ! Read number of rotation parameters - n = size(cell_(i) % rotation) + n = get_arraysize_double(node_cell, "rotation") if (n /= 3) then message = "Incorrect number of rotation parameters on cell " // & to_str(c % id) @@ -815,9 +840,10 @@ contains end if ! Copy rotation angles in x,y,z directions - phi = -cell_(i) % rotation(1) * PI/180.0 - theta = -cell_(i) % rotation(2) * PI/180.0 - psi = -cell_(i) % rotation(3) * PI/180.0 + call get_node_array(node_cell, "rotation", temp_int_array3) + phi = -temp_int_array3(1) * PI/180.0_8 + theta = -temp_int_array3(2) * PI/180.0_8 + psi = -temp_int_array3(3) * PI/180.0_8 ! Calculate rotation matrix based on angles given allocate(c % rotation(3,3)) @@ -832,7 +858,7 @@ contains end if ! Translation vector - if (associated(cell_(i) % translation)) then + if (check_for_node(node_cell, "translation")) then ! Translations can only be applied to cells that are being filled with ! another universe if (c % fill == NONE) then @@ -842,7 +868,7 @@ contains end if ! Read number of translation parameters - n = size(cell_(i) % translation) + n = get_arraysize_double(node_cell, "translation") if (n /= 3) then message = "Incorrect number of translation parameters on cell " & // to_str(c % id) @@ -851,7 +877,7 @@ contains ! Copy translation vector allocate(c % translation(3)) - c % translation = cell_(i) % translation + call get_node_array(node_cell, "translation", c % translation) end if ! Add cell to dictionary @@ -860,7 +886,7 @@ contains ! For cells, we also need to check if there's a new universe -- ! also for every cell add 1 to the count of cells for the ! specified universe - universe_num = cell_(i) % universe + universe_num = c % universe if (.not. cells_in_univ_dict % has_key(universe_num)) then n_universes = n_universes + 1 n_cells_in_univ = 1 @@ -880,7 +906,8 @@ contains boundary_exists = .false. ! Get number of tags - n_surfaces = size(surface_) + n_surfaces = 0 + n_surfaces = get_number_nodes(doc, "surface") ! Check for no surfaces if (n_surfaces == 0) then @@ -894,8 +921,16 @@ contains do i = 1, n_surfaces s => surfaces(i) + ! Get pointer to i-th surface node + call get_node_ptr(doc, "surface", node_surf, i) + ! Copy data into cells - s % id = surface_(i) % id + if (check_for_node(node_surf, "id")) then + call get_node_value(node_surf, "id", s % id) + else + message = "Must specify id of surface in geometry XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (surface_dict % has_key(s % id)) then @@ -905,7 +940,9 @@ contains end if ! Copy and interpret surface type - word = surface_(i) % type + word = '' + if (check_for_node(node_surf, "type")) & + call get_node_value(node_surf, "type", word) call lower_case(word) select case(trim(word)) case ('x-plane') @@ -950,7 +987,7 @@ contains ! have been specified for the given type of surface. Then copy ! surface coordinates. - n = size(surface_(i) % coeffs) + n = get_arraysize_double(node_surf, "coeffs") if (n < coeffs_reqd) then message = "Not enough coefficients specified for surface: " // & trim(to_str(s % id)) @@ -961,11 +998,13 @@ contains call fatal_error() else allocate(s % coeffs(n)) - s % coeffs = surface_(i) % coeffs + call get_node_array(node_surf, "coeffs", s % coeffs) end if ! Boundary conditions - word = surface_(i) % boundary + word = '' + if (check_for_node(node_surf, "boundary")) & + call get_node_value(node_surf, "boundary", word) call lower_case(word) select case (trim(word)) case ('transmission', 'transmit', '') @@ -998,14 +1037,22 @@ contains ! READ LATTICES FROM GEOMETRY.XML ! Allocate lattices array - n_lattices = size(lattice_) + n_lattices = get_number_nodes(doc, "lattice") allocate(lattices(n_lattices)) do i = 1, n_lattices lat => lattices(i) + ! Get pointer to i-th lattice + call get_node_ptr(doc, "lattice", node_lat, i) + ! ID of lattice - lat % id = lattice_(i) % id + if (check_for_node(node_lat, "id")) then + call get_node_value(node_lat, "id", lat % id) + else + message = "Must specify id of lattice in geometry XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (lattice_dict % has_key(lat % id)) then @@ -1015,7 +1062,9 @@ contains end if ! Read lattice type - word = lattice_(i) % type + word = '' + if (check_for_node(node_lat, "type")) & + call get_node_value(node_lat, "type", word) call lower_case(word) select case (trim(word)) case ('rect', 'rectangle', 'rectangular') @@ -1023,12 +1072,12 @@ contains case ('hex', 'hexagon', 'hexagonal') lat % type = LATTICE_HEX case default - message = "Invalid lattice type: " // trim(lattice_(i) % type) + message = "Invalid lattice type: " // trim(word) call fatal_error() end select ! Read number of lattice cells in each dimension - n = size(lattice_(i) % dimension) + n = get_arraysize_integer(node_lat, "dimension") if (n /= 2 .and. n /= 3) then message = "Lattice must be two or three dimensions." call fatal_error() @@ -1036,27 +1085,29 @@ contains lat % n_dimension = n allocate(lat % dimension(n)) - lat % dimension = lattice_(i) % dimension + call get_node_array(node_lat, "dimension", lat % dimension) ! Read lattice lower-left location - if (size(lattice_(i) % dimension) /= size(lattice_(i) % lower_left)) then + if (size(lat % dimension) /= & + get_arraysize_double(node_lat, "lower_left")) then message = "Number of entries on must be the same as & &the number of entries on ." call fatal_error() end if allocate(lat % lower_left(n)) - lat % lower_left = lattice_(i) % lower_left + call get_node_array(node_lat, "lower_left", lat % lower_left) ! Read lattice widths - if (size(lattice_(i) % width) /= size(lattice_(i) % lower_left)) then + if (size(lat % dimension) /= & + get_arraysize_double(node_lat, "width")) then message = "Number of entries on must be the same as & &the number of entries on ." call fatal_error() end if allocate(lat % width(n)) - lat % width = lattice_(i) % width + call get_node_array(node_lat, "width", lat % width) ! Copy number of dimensions n_x = lat % dimension(1) @@ -1069,28 +1120,36 @@ contains allocate(lat % universes(n_x, n_y, n_z)) ! Check that number of universes matches size - if (size(lattice_(i) % universes) /= n_x*n_y*n_z) then + n = get_arraysize_integer(node_lat, "universes") + if (n /= n_x*n_y*n_z) then message = "Number of universes on does not match size of & &lattice " // trim(to_str(lat % id)) // "." call fatal_error() end if + allocate(temp_int_array(n)) + call get_node_array(node_lat, "universes", temp_int_array) + ! Read universes do m = 1, n_z do k = 0, n_y - 1 do j = 1, n_x - lat % universes(j, n_y - k, m) = lattice_(i) % & - universes(j + n_x*k + n_x*n_y*(m-1)) + lat % universes(j, n_y - k, m) = & + temp_int_array(j + n_x*k + n_x*n_y*(m-1)) end do end do end do + deallocate(temp_int_array) ! Read material for area outside lattice - mid = lattice_(i) % outside - if (mid == 0 .or. mid == MATERIAL_VOID) then - lat % outside = MATERIAL_VOID - else - lat % outside = mid + lat % outside = MATERIAL_VOID + if (check_for_node(node_lat, "outside")) then + call get_node_value(node_lat, "outside", mid) + if (mid == 0 .or. mid == MATERIAL_VOID) then + lat % outside = MATERIAL_VOID + else + lat % outside = mid + end if end if ! Add lattice to dictionary @@ -1098,6 +1157,9 @@ contains end do + ! Close geometry XML file + call close_xmldoc(doc) + end subroutine read_geometry_xml !=============================================================================== From 35532ab144ab25496687e080625e171a4b7e7b2e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 11:38:05 -0400 Subject: [PATCH 065/192] removed old XML reader from geometry portion on file --- src/input_xml.F90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 760383d577..338b784609 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -679,8 +679,6 @@ contains subroutine read_geometry_xml() - use xml_data_geometry_t - integer :: i, j, k, m integer :: n integer :: n_x, n_y, n_z @@ -719,7 +717,6 @@ contains end if ! Parse geometry.xml file - call read_xml_file_geometry_t(filename) call open_xmldoc(doc, filename) ! Get number of tags @@ -979,7 +976,7 @@ contains s % type = SURF_CONE_Z coeffs_reqd = 4 case default - message = "Invalid surface type: " // trim(surface_(i) % type) + message = "Invalid surface type: " // trim(word) call fatal_error() end select From 404cd634949f2f313f38640d8e46df9cb67c8f5f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 14:09:36 -0400 Subject: [PATCH 066/192] converted materials XML input section over to new xml interface --- src/input_xml.F90 | 122 +++++++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 39 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 338b784609..f486a0679d 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1166,8 +1166,6 @@ contains subroutine read_materials_xml - use xml_data_materials_t - integer :: i ! loop index for materials integer :: j ! loop index for nuclides integer :: n ! number of nuclides @@ -1176,17 +1174,23 @@ contains integer :: index_nuclide ! index in nuclides integer :: index_sab ! index in sab_tables real(8) :: val ! value entered for density + real(8) :: temp_dble ! temporary double prec. real logical :: file_exists ! does materials.xml exist? logical :: sum_density ! density is taken to be sum of nuclide densities character(12) :: name ! name of isotope, e.g. 92235.03c character(12) :: alias ! alias of nuclide, e.g. U-235.03c character(MAX_WORD_LEN) :: units ! units on density character(MAX_LINE_LEN) :: filename ! absolute path to materials.xml + character(MAX_LINE_LEN) :: temp_str ! temporary string when reading type(ListChar) :: list_names ! temporary list of nuclide names type(ListReal) :: list_density ! temporary list of nuclide densities type(Material), pointer :: mat => null() - type(nuclide_xml), pointer :: nuc => null() - type(sab_xml), pointer :: sab => null() + type(Node), pointer :: doc => null() + type(Node), pointer :: node_mat => null() + type(Node), pointer :: node_dens => null() + type(Node), pointer :: node_nuc => null() + type(Node), pointer :: node_ele => null() + type(Node), pointer :: node_sab => null() ! Display output message message = "Reading materials XML file..." @@ -1201,16 +1205,17 @@ contains end if ! Initialize default cross section variable - default_xs_ = "" + default_xs = "" ! Parse materials.xml file - call read_xml_file_materials_t(filename) + call open_xmldoc(doc, filename) ! Copy default cross section if present - default_xs = default_xs_ + if (check_for_node(doc, "default_xs")) & + call get_node_value(doc, "default_xs", default_xs) ! Allocate cells array - n_materials = size(material_) + n_materials = get_number_nodes(doc, "material") allocate(materials(n_materials)) ! Initialize count for number of nuclides/S(a,b) tables @@ -1220,8 +1225,16 @@ contains do i = 1, n_materials mat => materials(i) + ! Get pointer to i-th material node + call get_node_ptr(doc, "material", node_mat, i) + ! Copy material id - mat % id = material_(i) % id + if (check_for_node(node_mat, "id")) then + call get_node_value(node_mat, "id", mat % id) + else + message = "Must specify id of material in materials XML file" + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (material_dict % has_key(mat % id)) then @@ -1239,9 +1252,18 @@ contains ! ======================================================================= ! READ AND PARSE TAG + ! Get pointer to density element + if (check_for_node(node_mat, "density")) then + call get_node_ptr(node_mat, "density", node_dens) + else + message = "Must specify density element in material " // & + trim(to_str(mat % id)) + call fatal_error() + end if + ! Copy value and units - val = material_(i) % density % value - units = material_(i) % density % units + call get_node_value(node_dens, "value", val) + call get_node_value(node_dens, "units", units) if (units == 'sum') then ! If the user gave the units as 'sum', then the total density of the @@ -1271,7 +1293,7 @@ contains case ('atom/cm3', 'atom/cc') mat % density = 1.0e-24 * val case default - message = "Unkwown units '" // trim(material_(i) % density % units) & + message = "Unkwown units '" // trim(units) & // "' specified on material " // trim(to_str(mat % id)) call fatal_error() end select @@ -1281,101 +1303,112 @@ contains ! READ AND PARSE TAGS ! Check to ensure material has at least one nuclide - if (.not. associated(material_(i) % nuclides) .and. & - .not. associated(material_(i) % elements)) then + if (.not. check_for_node(node_mat, "nuclide") .and. & + .not. check_for_node(node_mat, "element")) then message = "No nuclides or natural elements specified on material " // & trim(to_str(mat % id)) call fatal_error() end if ! Create list of nuclides based on those specified plus natural elements - INDIVIDUAL_NUCLIDES: do j = 1, size(material_(i) % nuclides) + INDIVIDUAL_NUCLIDES: do j = 1, get_number_nodes(node_mat, "nuclide") ! Combine nuclide identifier and cross section and copy into names - nuc => material_(i) % nuclides(j) + call get_node_ptr(node_mat, "nuclide", node_nuc, j) ! Check for empty name on nuclide - if (len_trim(nuc % name) == 0) then + if (.not.check_for_node(node_nuc, "name")) then message = "No name specified on nuclide in material " // & trim(to_str(mat % id)) call fatal_error() end if ! Check for cross section - if (len_trim(nuc % xs) == 0) then + if (.not.check_for_node(node_nuc, "xs")) then if (default_xs == '') then message = "No cross section specified for nuclide in material " & // trim(to_str(mat % id)) call fatal_error() else - nuc % xs = default_xs + name = trim(default_xs) end if end if ! store full name - name = trim(nuc % name) // "." // trim(nuc % xs) + call get_node_value(node_nuc, "name", temp_str) + if (check_for_node(node_nuc, "xs")) & + call get_node_value(node_nuc, "xs", name) + name = trim(temp_str) // "." // trim(name) ! save name and density to list call list_names % append(name) ! Check if no atom/weight percents were specified or if both atom and ! weight percents were specified - if (nuc % ao == ZERO .and. nuc % wo == ZERO) then + if (.not.check_for_node(node_nuc, "ao") .and. & + .not.check_for_node(node_nuc, "wo")) then message = "No atom or weight percent specified for nuclide " // & trim(name) call fatal_error() - elseif (nuc % ao /= ZERO .and. nuc % wo /= ZERO) then + elseif (check_for_node(node_nuc, "ao") .and. & + check_for_node(node_nuc, "wo")) then message = "Cannot specify both atom and weight percents for a & &nuclide: " // trim(name) call fatal_error() end if ! Copy atom/weight percents - if (nuc % ao /= ZERO) then - call list_density % append(nuc % ao) + if (check_for_node(node_nuc, "ao")) then + call get_node_value(node_nuc, "ao", temp_dble) + call list_density % append(temp_dble) else - call list_density % append(-nuc % wo) + call get_node_value(node_nuc, "wo", temp_dble) + call list_density % append(-temp_dble) end if end do INDIVIDUAL_NUCLIDES ! ======================================================================= ! READ AND PARSE TAGS - NATURAL_ELEMENTS: do j = 1, size(material_(i) % elements) - nuc => material_(i) % elements(j) + NATURAL_ELEMENTS: do j = 1, get_number_nodes(node_mat, "element") + call get_node_ptr(node_mat, "element", node_ele, j) ! Check for empty name on natural element - if (len_trim(nuc % name) == 0) then + if (.not.check_for_node(node_ele, "name")) then message = "No name specified on nuclide in material " // & trim(to_str(mat % id)) call fatal_error() end if + call get_node_value(node_ele, "name", name) ! Check for cross section - if (len_trim(nuc % xs) == 0) then + if (.not.check_for_node(node_nuc, "xs")) then if (default_xs == '') then message = "No cross section specified for nuclide in material " & // trim(to_str(mat % id)) call fatal_error() else - nuc % xs = default_xs + temp_str = trim(default_xs) end if end if ! Check if no atom/weight percents were specified or if both atom and ! weight percents were specified - if (nuc % ao == ZERO .and. nuc % wo == ZERO) then - message = "No atom or weight percent specified for nuclide " // & + if (.not.check_for_node(node_nuc, "ao") .and. & + .not.check_for_node(node_nuc, "wo")) then + message = "No atom or weight percent specified for element " // & trim(name) call fatal_error() - elseif (nuc % ao /= ZERO .and. nuc % wo /= ZERO) then + elseif (check_for_node(node_nuc, "ao") .and. & + check_for_node(node_nuc, "wo")) then message = "Cannot specify both atom and weight percents for a & - &nuclide: " // trim(name) + &element: " // trim(name) call fatal_error() end if ! Expand element into naturally-occurring isotopes - if (nuc % ao /= ZERO) then - call expand_natural_element(nuc % name, nuc % xs, nuc % ao, & + if (check_for_node(node_nuc, "ao")) then + call get_node_value(node_nuc, "ao", temp_dble) + call expand_natural_element(name, temp_str, temp_dble, & list_names, list_density) else message = "The ability to expand a natural element based on weight & @@ -1452,7 +1485,7 @@ contains ! ======================================================================= ! READ AND PARSE TAG FOR S(a,b) DATA - n_sab = size(material_(i) % sab) + n_sab = get_number_nodes(node_mat, "sab") if (n_sab > 0) then ! Set number of S(a,b) tables mat % n_sab = n_sab @@ -1467,10 +1500,18 @@ contains do j = 1, n_sab ! Get pointer to S(a,b) table - sab => material_(i) % sab(j) + call get_node_ptr(node_mat, "sab", node_sab, j) ! Determine name of S(a,b) table - name = trim(sab % name) // "." // trim(sab % xs) + if (.not.check_for_node(node_sab, "name") .or. & + .not.check_for_node(node_sab, "xs")) then + message = "Need to specify and for S(a,b) table." + call fatal_error() + end if + call get_node_value(node_sab, "name", name) + call get_node_value(node_sab, "xs", temp_str) + + name = trim(name) // "." // trim(temp_str) mat % sab_names(j) = name ! Check that this nuclide is listed in the cross_sections.xml file @@ -1508,6 +1549,9 @@ contains n_nuclides_total = index_nuclide n_sab_tables = index_sab + ! Close materials XML file + call close_xmldoc(doc) + end subroutine read_materials_xml !=============================================================================== From 63e12ad2917427e5e40ab6a31ce474597cbfed09 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 14:10:44 -0400 Subject: [PATCH 067/192] added () from empty arg list to read_materials --- src/input_xml.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index f486a0679d..c673f3fa78 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1164,7 +1164,7 @@ contains ! for errors and placing properly-formatted data in the right data structures !=============================================================================== - subroutine read_materials_xml + subroutine read_materials_xml() integer :: i ! loop index for materials integer :: j ! loop index for nuclides From b7747d727cb6e4770ac271e5645fa598ed518cfb Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 15:10:56 -0400 Subject: [PATCH 068/192] converted tallies over up through reading in mesh --- src/input_xml.F90 | 87 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index c673f3fa78..39765c5fb6 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1577,14 +1577,21 @@ contains integer :: n_order ! Scattering order requested integer :: n_order_pos ! Position of Scattering order in score name string integer :: MT ! user-specified MT for score + integer :: iarray3(3) ! temporary integer array logical :: file_exists ! does tallies.xml file exist? + real(8) :: rarray3(3) ! temporary double prec. array character(MAX_LINE_LEN) :: filename character(MAX_WORD_LEN) :: word character(MAX_WORD_LEN) :: score_name + character(MAX_WORD_LEN) :: temp_str type(ElemKeyValueCI), pointer :: pair_list => null() type(TallyObject), pointer :: t => null() type(StructuredMesh), pointer :: m => null() type(TallyFilter), allocatable :: filters(:) ! temporary filters + type(Node), pointer :: doc => null() + type(Node), pointer :: node_mesh => null() + type(Node), pointer :: node_tal => null() + type(Node), pointer :: node_filt => null() ! Check if tallies.xml exists filename = trim(path_input) // "tallies.xml" @@ -1600,15 +1607,16 @@ contains ! Parse tallies.xml file call read_xml_file_tallies_t(filename) + call open_xmldoc(doc, filename) ! ========================================================================== ! DETERMINE SIZE OF ARRAYS AND ALLOCATE ! Check for user meshes - if (.not. associated(mesh_)) then + if (.not.check_for_node(doc, "mesh")) then n_user_meshes = 0 else - n_user_meshes = size(mesh_) + n_user_meshes = get_number_nodes(doc, "mesh") if (cmfd_run) then n_meshes = n_user_meshes + n_cmfd_meshes else @@ -1620,12 +1628,12 @@ contains if (n_meshes > 0) allocate(meshes(n_meshes)) ! Check for user tallies - if (.not. associated(tally_)) then + if (.not.check_for_node(doc, "tally")) then n_user_tallies = 0 message = "No tallies present in tallies.xml file!" call warning() else - n_user_tallies = size(tally_) + n_user_tallies = get_number_nodes(doc, "tally") end if ! Allocate tally array @@ -1634,8 +1642,12 @@ contains end if ! Check for setting - call lower_case(separate_) - if (separate_ == 'true' .or. separate_ == '1') assume_separate = .true. + if (check_for_node(doc, "assume_separate")) then + call get_node_value(doc, "assume_separate", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + assume_separate = .true. + end if ! ========================================================================== ! READ MESH DATA @@ -1643,8 +1655,16 @@ contains do i = 1, n_user_meshes m => meshes(i) - ! copy mesh id - m % id = mesh_(i) % id + ! Get pointer to mesh node + call get_node_ptr(doc, "mesh", node_mesh, i) + + ! Copy mesh id + if (check_for_node(node_mesh, "id")) then + call get_node_value(node_mesh, "id", m % id) + else + message = "Must specify id for mesh in tally XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (mesh_dict % has_key(m % id)) then @@ -1654,8 +1674,11 @@ contains end if ! Read mesh type - call lower_case(mesh_(i) % type) - select case (mesh_(i) % type) + temp_str = '' + if (check_for_node(node_mesh, "type")) & + call get_node_value(node_mesh, "type", temp_str) + call lower_case(temp_str) + select case (trim(temp_str)) case ('rect', 'rectangle', 'rectangular') m % type = LATTICE_RECT case ('hex', 'hexagon', 'hexagonal') @@ -1666,7 +1689,7 @@ contains end select ! Determine number of dimensions for mesh - n = size(mesh_(i) % dimension) + n = get_arraysize_integer(node_mesh, "dimension") if (n /= 2 .and. n /= 3) then message = "Mesh must be two or three dimensions." call fatal_error() @@ -1680,74 +1703,79 @@ contains allocate(m % upper_right(n)) ! Check that dimensions are all greater than zero - if (any(mesh_(i) % dimension <= 0)) then + call get_node_array(node_mesh, "dimension", iarray3(1:n)) + if (any(iarray3(1:n) <= 0)) then message = "All entries on the element for a tally mesh & &must be positive." call fatal_error() end if ! Read dimensions in each direction - m % dimension = mesh_(i) % dimension + m % dimension = iarray3(1:n) ! Read mesh lower-left corner location - if (m % n_dimension /= size(mesh_(i) % lower_left)) then + if (m % n_dimension /= get_arraysize_double(node_mesh, "lower_left")) then message = "Number of entries on must be the same as & &the number of entries on ." call fatal_error() end if - m % lower_left = mesh_(i) % lower_left + call get_node_array(node_mesh, "lower_left", m % lower_left) - ! Make sure either upper-right or width was specified - if (associated(mesh_(i) % upper_right) .and. & - associated(mesh_(i) % width)) then + ! Make sure both upper-right or width were specified + if (check_for_node(node_mesh, "upper_right") .and. & + check_for_node(node_mesh, "width")) then message = "Cannot specify both and on a & &tally mesh." call fatal_error() end if ! Make sure either upper-right or width was specified - if (.not. associated(mesh_(i) % upper_right) .and. & - .not. associated(mesh_(i) % width)) then + if (.not.check_for_node(node_mesh, "upper_right") .and. & + .not.check_for_node(node_mesh, "width")) then message = "Must specify either and on a & &tally mesh." call fatal_error() end if - if (associated(mesh_(i) % width)) then + if (check_for_node(node_mesh, "width")) then ! Check to ensure width has same dimensions - if (size(mesh_(i) % width) /= size(mesh_(i) % lower_left)) then + if (get_arraysize_double(node_mesh, "width") /= & + get_arraysize_double(node_mesh, "lower_left")) then message = "Number of entries on must be the same as the & &number of entries on ." call fatal_error() end if ! Check for negative widths - if (any(mesh_(i) % width < ZERO)) then + call get_node_array(node_mesh, "width", rarray3(1:n)) + if (any(rarray3(1:n) < ZERO)) then message = "Cannot have a negative on a tally mesh." call fatal_error() end if ! Set width and upper right coordinate - m % width = mesh_(i) % width + m % width = rarray3(1:n) m % upper_right = m % lower_left + m % dimension * m % width - elseif (associated(mesh_(i) % upper_right)) then + elseif (check_for_node(node_mesh, "upper_right")) then ! Check to ensure width has same dimensions - if (size(mesh_(i) % upper_right) /= size(mesh_(i) % lower_left)) then + if (get_arraysize_double(node_mesh, "upper_right") /= & + get_arraysize_double(node_mesh, "lower_left")) then message = "Number of entries on must be the same as & &the number of entries on ." call fatal_error() end if ! Check that upper-right is above lower-left - if (any(mesh_(i) % upper_right < mesh_(i) % lower_left)) then + call get_node_array(node_mesh, "upper_right", rarray3(1:n)) + if (any(rarray3(1:n) < mesh_(i) % lower_left)) then message = "The coordinates must be greater than the & & coordinates on a tally mesh." call fatal_error() end if ! Set width and upper right coordinate - m % upper_right = mesh_(i) % upper_right + m % upper_right = rarray3(1:n) m % width = (m % upper_right - m % lower_left) / m % dimension end if @@ -2350,6 +2378,9 @@ contains ! Add tally to dictionary call tally_dict % add_key(t % id, i) + ! Close XML document + call close_xmldoc(doc) + end do READ_TALLIES end subroutine read_tallies_xml From 63b03b0eb46c18864226eda4059bed50ca470089 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 16:13:38 -0400 Subject: [PATCH 069/192] added string array capability to xml interface and converted tallies XML input --- src/input_xml.F90 | 157 +++++++++++++++++++++++------------------- src/xml_interface.F90 | 64 +++++++++++++++++ 2 files changed, 152 insertions(+), 69 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 39765c5fb6..52ef5391e3 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -212,7 +212,7 @@ contains message = "Lethargy mapped energy grid not yet supported." call fatal_error() case default - message = "Unknown energy grid method: " // temp_str + message = "Unknown energy grid method: " // trim(temp_str) call fatal_error() end select @@ -424,8 +424,9 @@ contains ! Cutoffs if (check_for_node(doc, "cutoff")) then - call get_node_value(doc, "weight", weight_cutoff) - call get_node_value(doc, "weight_avg", weight_survive) + call get_node_ptr(doc, "cutoff", node_cutoff) + call get_node_value(node_cutoff, "weight", weight_cutoff) + call get_node_value(node_cutoff, "weight_avg", weight_survive) end if ! Particle trace @@ -1429,7 +1430,7 @@ contains ALL_NUCLIDES: do j = 1, mat % n_nuclides ! Check that this nuclide is listed in the cross_sections.xml file - name = list_names % get_item(j) + name = trim(list_names % get_item(j)) if (.not. xs_listing_dict % has_key(name)) then message = "Could not find nuclide " // trim(name) // & " in cross_sections.xml file!" @@ -1561,8 +1562,6 @@ contains subroutine read_tallies_xml - use xml_data_tallies_t - integer :: i ! loop over user-specified tallies integer :: j ! loop over words integer :: k ! another loop index @@ -1584,6 +1583,7 @@ contains character(MAX_WORD_LEN) :: word character(MAX_WORD_LEN) :: score_name character(MAX_WORD_LEN) :: temp_str + character(MAX_WORD_LEN), allocatable :: sarray(:) type(ElemKeyValueCI), pointer :: pair_list => null() type(TallyObject), pointer :: t => null() type(StructuredMesh), pointer :: m => null() @@ -1606,7 +1606,6 @@ contains call write_message(5) ! Parse tallies.xml file - call read_xml_file_tallies_t(filename) call open_xmldoc(doc, filename) ! ========================================================================== @@ -1684,7 +1683,7 @@ contains case ('hex', 'hexagon', 'hexagonal') m % type = LATTICE_HEX case default - message = "Invalid mesh type: " // trim(mesh_(i) % type) + message = "Invalid mesh type: " // trim(temp_str) call fatal_error() end select @@ -1768,7 +1767,7 @@ contains ! Check that upper-right is above lower-left call get_node_array(node_mesh, "upper_right", rarray3(1:n)) - if (any(rarray3(1:n) < mesh_(i) % lower_left)) then + if (any(rarray3(1:n) < m % lower_left)) then message = "The coordinates must be greater than the & & coordinates on a tally mesh." call fatal_error() @@ -1793,6 +1792,9 @@ contains ! Get pointer to tally t => tallies(i) + ! Get pointer to tally xml node + call get_node_ptr(doc, "tally", node_tal, i) + ! Set tally type to volume by default t % type = TALLY_VOLUME @@ -1805,7 +1807,12 @@ contains t % estimator = ESTIMATOR_TRACKLENGTH ! Copy material id - t % id = tally_(i) % id + if (check_for_node(node_tal, "id")) then + call get_node_value(node_tal, "id", t % id) + else + message = "Must specify id for tally in tally XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (tally_dict % has_key(t % id)) then @@ -1815,7 +1822,9 @@ contains end if ! Copy tally label - t % label = tally_(i) % label + t % label = '' + if (check_for_node(node_tal, "label")) & + call get_node_value(node_tal, "label", t % label) ! ======================================================================= ! READ DATA FOR FILTERS @@ -1824,29 +1833,45 @@ contains ! element followed by sub-elements , , etc. This checks for ! the old format and if it is present, raises an error - if (size(tally_(i) % filters) > 0) then + if (get_number_nodes(node_tal, "filters") > 0) then message = "Tally filters should be specified with multiple & &elements. Did you forget to change your element?" call fatal_error() end if - if (associated(tally_(i) % filter)) then + if (check_for_node(node_tal, "filter")) then ! Determine number of filters - n_filters = size(tally_(i) % filter) + n_filters = get_number_nodes(node_tal, "filter") ! Allocate filters array t % n_filters = n_filters allocate(t % filters(n_filters)) READ_FILTERS: do j = 1, n_filters + ! Get pointer to filter xml node + call get_node_ptr(node_tal, "filter", node_filt, j) + ! Convert filter type to lower case - call lower_case(tally_(i) % filter(j) % type) + temp_str = '' + if (check_for_node(node_filt, "type")) & + call get_node_value(node_filt, "type", temp_str) + call lower_case(temp_str) ! Determine number of bins - n_words = size(tally_(i) % filter(j) % bins) + if (check_for_node(node_filt, "bins")) then + if (trim(temp_str) == 'energy' .or. & + trim(temp_str) == 'energyout') then + n_words = get_arraysize_double(node_filt, "bins") + else + n_words = get_arraysize_integer(node_filt, "bins") + end if + else + message = "Bins not set in filter on tally " // trim(to_str(t % id)) + call fatal_error() + end if ! Determine type of filter - select case (tally_(i) % filter(j) % type) + select case (temp_str) case ('cell') ! Set type of filter t % filters(j) % type = FILTER_CELL @@ -1856,10 +1881,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('cellborn') ! Set type of filter @@ -1870,10 +1892,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('material') ! Set type of filter @@ -1884,10 +1903,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('universe') ! Set type of filter @@ -1898,10 +1914,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('surface') message = "Surface filter is not yet supported!" @@ -1915,10 +1928,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('mesh') ! Set type of filter @@ -1931,7 +1941,7 @@ contains end if ! Determine id of mesh - id = int(str_to_int(tally_(i) % filter(j) % bins(1)),4) + call get_node_value(node_filt, "bins", id) ! Get pointer to mesh if (mesh_dict % has_key(id)) then @@ -1961,10 +1971,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % real_bins(n_words)) - do k = 1, n_words - t % filters(j) % real_bins(k) = str_to_real(& - tally_(i) % filter(j) % bins(k)) - end do + call get_node_array(node_filt, "bins", t % filters(j) % real_bins) case ('energyout') ! Set type of filter @@ -1975,18 +1982,15 @@ contains ! Allocate and store bins allocate(t % filters(j) % real_bins(n_words)) - do k = 1, n_words - t % filters(j) % real_bins(k) = str_to_real(& - tally_(i) % filter(j) % bins(k)) - end do + call get_node_array(node_filt, "bins", t % filters(j) % real_bins) ! Set to analog estimator t % estimator = ESTIMATOR_ANALOG case default ! Specified tally filter is invalid, raise error - message = "Unknown filter type '" // trim(tally_(i) % & - filter(j) % type) // "' on tally " // & + message = "Unknown filter type '" // & + trim(temp_str) // "' on tally " // & trim(to_str(t % id)) // "." call fatal_error() @@ -2015,8 +2019,13 @@ contains ! ======================================================================= ! READ DATA FOR NUCLIDES - if (associated(tally_(i) % nuclides)) then - if (tally_(i) % nuclides(1) == 'all') then + if (check_for_node(node_tal, "nuclides")) then + + ! Allocate a temporary string array for nuclides and copy values over + allocate(sarray(get_arraysize_string(node_tal, "nuclides"))) + call get_node_array(node_tal, "nuclides", sarray) + + if (trim(sarray(1)) == 'all') then ! Handle special case all allocate(t % nuclide_bins(n_nuclides_total + 1)) @@ -2032,25 +2041,25 @@ contains t % all_nuclides = .true. else ! Any other case, e.g. U-235 Pu-239 - n_words = size(tally_(i) % nuclides) + n_words = get_arraysize_string(node_tal, "nuclides") allocate(t % nuclide_bins(n_words)) do j = 1, n_words ! Check if total material was specified - if (tally_(i) % nuclides(j) == 'total') then + if (trim(sarray(j)) == 'total') then t % nuclide_bins(j) = -1 cycle end if ! Check if xs specifier was given - if (ends_with(tally_(i) % nuclides(j), 'c')) then - word = tally_(i) % nuclides(j) + if (ends_with(sarray(j), 'c')) then + word = sarray(j) else if (default_xs == '') then ! No default cross section specified, search through nuclides pair_list => nuclide_dict % keys() do while (associated(pair_list)) if (starts_with(pair_list % key, & - tally_(i) % nuclides(j))) then + sarray(j))) then word = pair_list % key(1:150) exit end if @@ -2062,14 +2071,14 @@ contains ! Check if no nuclide was found if (.not. associated(pair_list)) then message = "Could not find the nuclide " // trim(& - tally_(i) % nuclides(j)) // " specified in tally " & + sarray(j)) // " specified in tally " & // trim(to_str(t % id)) // " in any material." call fatal_error() end if deallocate(pair_list) else ! Set nuclide to default xs - word = trim(tally_(i) % nuclides(j)) // "." // default_xs + word = trim(sarray(j)) // "." // default_xs end if end if @@ -2088,6 +2097,9 @@ contains t % n_nuclide_bins = n_words end if + ! Deallocate temporary string array + deallocate(sarray) + else ! No were specified -- create only one bin will be added ! for the total material. @@ -2099,18 +2111,20 @@ contains ! ======================================================================= ! READ DATA FOR SCORES - if (associated(tally_(i) % scores)) then + if (check_for_node(node_tal, "scores")) then ! Loop through scores and determine if a scatter-p# input was used ! to allow for proper pre-allocating of t % score_bins ! This scheme allows multiple scatter-p# to be requested by the user ! if so desired - n_words = size(tally_(i) % scores) + n_words = get_arraysize_string(node_tal, "scores") + allocate(sarray(n_words)) + call get_node_array(node_tal, "scores", sarray) n_new = 0 do j = 1, n_words - call lower_case(tally_(i) % scores(j)) + call lower_case(sarray(j)) ! Find if scores(j) is of the form 'scatter-p' ! If so, get the number and do a select case on that. - score_name = tally_(i) % scores(j) + score_name = trim(sarray(j)) if (starts_with(score_name,'scatter-p')) then n_order_pos = scan(score_name,'0123456789') n_order = int(str_to_int( & @@ -2123,7 +2137,7 @@ contains trim(to_str(SCATT_ORDER_MAX)) call warning() n_order = SCATT_ORDER_MAX - tally_(i) % scores(j) = SCATT_ORDER_MAX_PNSTR + sarray(j) = SCATT_ORDER_MAX_PNSTR end if n_new = n_new + n_order end if @@ -2140,7 +2154,7 @@ contains ! Get the input string in scores(l) but if scatter-n or scatter-pn ! then strip off the n, and store it as an integer to be used later ! Peform the select case on this modified (number removed) string - score_name = tally_(i) % scores(l) + score_name = sarray(l) if (starts_with(score_name,'scatter-p')) then n_order_pos = scan(score_name,'0123456789') n_order = int(str_to_int( & @@ -2171,7 +2185,7 @@ contains score_name = "scatter-n" end if - select case (score_name) + select case (trim(score_name)) case ('flux') ! Prohibit user from tallying flux for an individual nuclide if (.not. (t % n_nuclide_bins == 1 .and. & @@ -2326,14 +2340,14 @@ contains t % score_bins(j) = MT else message = "Invalid MT on : " // & - trim(tally_(i) % scores(j)) + trim(sarray(j)) call fatal_error() end if else ! Specified score was not an integer message = "Unknown scoring function: " // & - trim(tally_(i) % scores(j)) + trim(sarray(j)) call fatal_error() end if @@ -2341,6 +2355,9 @@ contains end do t % n_score_bins = n_scores t % n_user_score_bins = n_words + + ! Deallocate temporary string array of scores + deallocate(sarray) else message = "No specified on tally " // trim(to_str(t % id)) & // "." @@ -2351,8 +2368,10 @@ contains ! SET TALLY ESTIMATOR ! Check if user specified estimator - if (len_trim(tally_(i) % estimator) > 0) then - select case(tally_(i) % estimator) + if (check_for_node(node_tal, "estimator")) then + temp_str = '' + call get_node_value(node_tal, "estimator", temp_str) + select case(trim(temp_str)) case ('analog') t % estimator = ESTIMATOR_ANALOG @@ -2369,7 +2388,7 @@ contains t % estimator = ESTIMATOR_TRACKLENGTH case default - message = "Invalid estimator '" // trim(tally_(i) % estimator) & + message = "Invalid estimator '" // trim(temp_str) & // "' on tally " // to_str(t % id) call fatal_error() end select diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index cd948b64e6..d1fc88a1ce 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -17,6 +17,7 @@ module xml_interface public :: get_node_array public :: get_arraysize_integer public :: get_arraysize_double + public :: get_arraysize_string integer, parameter :: ATTR_NODE = 0 integer, parameter :: ELEM_NODE = 1 @@ -31,6 +32,7 @@ module xml_interface interface get_node_array module procedure get_node_array_integer module procedure get_node_array_double + module procedure get_node_array_string end interface get_node_array contains @@ -315,6 +317,39 @@ contains end subroutine get_node_array_double +!=============================================================================== +! GET_NODE_ARRAY_STRING +!=============================================================================== + + subroutine get_node_array_string(ptr, node_name, result_string) + + character(len=*), intent(in) :: node_name + character(len=*) :: result_string(:) + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_string) + else + call extractDataContent(temp_ptr, result_string) + end if + + end subroutine get_node_array_string + !=============================================================================== ! GET_NODE_VALUE_STRING !=============================================================================== @@ -406,6 +441,35 @@ contains end function get_arraysize_double +!=============================================================================== +! GET_NODE_ARRAYSIZE_STRING +!=============================================================================== + + function get_arraysize_string(ptr, node_name) result(n) + + character(len=*), intent(in) :: node_name + integer :: n + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Get the size + n = countrts(getTextContent(temp_ptr), 'a') + + end function get_arraysize_string + !=============================================================================== ! GET_NODE !=============================================================================== From 6d96884998bcd2ec34eada18630b2ac23bec2799 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 17:06:17 -0400 Subject: [PATCH 070/192] converted plots XML input over to new XML reader --- src/input_xml.F90 | 183 +++++++++++++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 59 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 52ef5391e3..7b2bce19ea 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -88,20 +88,22 @@ contains ! Find cross_sections.xml file -- the first place to look is the ! settings.xml file. If no file is found there, then we check the ! CROSS_SECTIONS environment variable - if (.not. check_for_node(doc, "cross_sections") .and. & - run_mode /= MODE_PLOTTING) then - ! No cross_sections.xml file specified in settings.xml, check environment - ! variable - call get_environment_variable("CROSS_SECTIONS", env_variable) - if (len_trim(env_variable) == 0) then - message = "No cross_sections.xml file was specified in settings.xml & - &or in the CROSS_SECTIONS environment variable." - call fatal_error() + if (run_mode /= MODE_PLOTTING) then + if (.not. check_for_node(doc, "cross_sections") .and. & + run_mode /= MODE_PLOTTING) then + ! No cross_sections.xml file specified in settings.xml, check environment + ! variable + call get_environment_variable("CROSS_SECTIONS", env_variable) + if (len_trim(env_variable) == 0) then + message = "No cross_sections.xml file was specified in settings.xml & + &or in the CROSS_SECTIONS environment variable." + call fatal_error() + else + path_cross_sections = trim(env_variable) + end if else - path_cross_sections = trim(env_variable) + call get_node_value(doc, "cross_sections", path_cross_sections) end if - else - call get_node_value(doc, "cross_sections", path_cross_sections) end if ! Set output directory if a path has been specified on the @@ -2397,11 +2399,11 @@ contains ! Add tally to dictionary call tally_dict % add_key(t % id, i) - ! Close XML document - call close_xmldoc(doc) - end do READ_TALLIES + ! Close XML document + call close_xmldoc(doc) + end subroutine read_tallies_xml !=============================================================================== @@ -2410,13 +2412,17 @@ contains subroutine read_plots_xml - use xml_data_plots_t - integer i, j - integer n_cols, col_id + integer n_cols, col_id, n_comp + integer, allocatable :: iarray(:) logical :: file_exists ! does plots.xml file exist? character(MAX_LINE_LEN) :: filename ! absolute path to plots.xml + character(MAX_LINE_LEN) :: temp_str type(ObjectPlot), pointer :: pl => null() + type(Node), pointer :: doc => null() + type(Node), pointer :: node_plot => null() + type(Node), pointer :: node_col => null() + type(Node), pointer :: node_mask => null() ! Check if plots.xml exists filename = trim(path_input) // "plots.xml" @@ -2431,17 +2437,25 @@ contains call write_message(5) ! Parse plots.xml file - call read_xml_file_plots_t(filename) + call open_xmldoc(doc, filename) ! Allocate plots array - n_plots = size(plot_) + n_plots = get_number_nodes(doc, "plot") allocate(plots(n_plots)) READ_PLOTS: do i = 1, n_plots pl => plots(i) + ! Get pointer to plot XML node + call get_node_ptr(doc, "plot", node_plot, i) + ! Copy data into plots - pl % id = plot_(i) % id + if (check_for_node(node_plot, "id")) then + call get_node_value(node_plot, "id", pl % id) + else + message = "Must specify plot id in plots XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (plot_dict % has_key(pl % id)) then @@ -2451,40 +2465,46 @@ contains end if ! Copy plot type - select case (plot_(i) % type) + temp_str = 'slice' + if (check_for_node(node_plot, "type")) & + call get_node_value(node_plot, "type", temp_str) + call lower_case(temp_str) + select case (trim(temp_str)) case ("slice") pl % type = PLOT_TYPE_SLICE case ("voxel") pl % type = PLOT_TYPE_VOXEL case default - message = "Unsupported plot type '" // trim(plot_(i) % type) & + message = "Unsupported plot type '" // trim(temp_str) & // "' in plot " // trim(to_str(pl % id)) call fatal_error() end select ! Set output file path + filename = "plot" + if (check_for_node(node_plot, "filename")) & + call get_node_value(node_plot, "filename", filename) select case (pl % type) case (PLOT_TYPE_SLICE) pl % path_plot = trim(path_input) // trim(to_str(pl % id)) // & - "_" // trim(plot_(i) % filename) // ".ppm" + "_" // trim(filename) // ".ppm" case (PLOT_TYPE_VOXEL) pl % path_plot = trim(path_input) // trim(to_str(pl % id)) // & - "_" // trim(plot_(i) % filename) // ".voxel" + "_" // trim(filename) // ".voxel" end select ! Copy plot pixel size if (pl % type == PLOT_TYPE_SLICE) then - if (size(plot_(i) % pixels) == 2) then - pl % pixels(1) = plot_(i) % pixels(1) - pl % pixels(2) = plot_(i) % pixels(2) + if (get_arraysize_integer(node_plot, "pixels") == 2) then + call get_node_array(node_plot, "pixels", pl % pixels(1:2)) else message = " must be length 2 in slice plot " // & trim(to_str(pl % id)) call fatal_error() end if else if (pl % type == PLOT_TYPE_VOXEL) then - if (size(plot_(i) % pixels) == 3) then - pl % pixels = plot_(i) % pixels + if (get_arraysize_integer(node_plot, "pixels") == 3) then + call get_node_array(node_plot, "pixels", pl % pixels(1:3)) else message = " must be length 3 in voxel plot " // & trim(to_str(pl % id)) @@ -2493,14 +2513,14 @@ contains end if ! Copy plot background color - if (associated(plot_(i) % background)) then + if (check_for_node(node_plot, "background")) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Background color ignored in voxel plot " // & trim(to_str(pl % id)) call warning() end if - if (size(plot_(i) % background) == 3) then - pl % not_found % rgb = plot_(i) % background + if (get_arraysize_integer(node_plot, "background") == 3) then + call get_node_array(node_plot, "background", pl % not_found % rgb) else message = "Bad background RGB " & // "in plot " // trim(to_str(pl % id)) @@ -2512,7 +2532,11 @@ contains ! Copy plot basis if (pl % type == PLOT_TYPE_SLICE) then - select case (plot_(i) % basis) + temp_str = 'xy' + if (check_for_node(node_plot, "basis")) & + call get_node_value(node_plot, "basis", temp_str) + call lower_case(temp_str) + select case (trim(temp_str)) case ("xy") pl % basis = PLOT_BASIS_XY case ("xz") @@ -2520,15 +2544,15 @@ contains case ("yz") pl % basis = PLOT_BASIS_YZ case default - message = "Unsupported plot basis '" // plot_(i) % basis & + message = "Unsupported plot basis '" // trim(temp_str) & // "' in plot " // trim(to_str(pl % id)) call fatal_error() end select end if ! Copy plotting origin - if (size(plot_(i) % origin) == 3) then - pl % origin = plot_(i) % origin + if (get_arraysize_double(node_plot, "origin") == 3) then + call get_node_array(node_plot, "origin", pl % origin) else message = "Origin must be length 3 " & // "in plot " // trim(to_str(pl % id)) @@ -2537,17 +2561,16 @@ contains ! Copy plotting width if (pl % type == PLOT_TYPE_SLICE) then - if (size(plot_(i) % width) == 2) then - pl % width(1) = plot_(i) % width(1) - pl % width(2) = plot_(i) % width(2) + if (get_arraysize_double(node_plot, "width") == 2) then + call get_node_array(node_plot, "width", pl % width(1:2)) else message = " must be length 2 in slice plot " // & trim(to_str(pl % id)) call fatal_error() end if else if (pl % type == PLOT_TYPE_VOXEL) then - if (size(plot_(i) % width) == 3) then - pl % width = plot_(i) % width + if (get_arraysize_double(node_plot, "width") == 3) then + call get_node_array(node_plot, "width", pl % width(1:3)) else message = " must be length 3 in voxel plot " // & trim(to_str(pl % id)) @@ -2556,7 +2579,11 @@ contains end if ! Copy plot color type and initialize all colors randomly - select case (plot_(i) % color) + temp_str = '' + if (check_for_node(node_plot, "color")) & + call get_node_value(node_plot, "color", temp_str) + call lower_case(temp_str) + select case (trim(temp_str)) case ("cell") pl % color_by = PLOT_COLOR_CELLS @@ -2578,13 +2605,13 @@ contains end do case default - message = "Unsupported plot color type '" // plot_(i) % color & + message = "Unsupported plot color type '" // trim(temp_str) & // "' in plot " // trim(to_str(pl % id)) call fatal_error() end select ! Copy user specified colors - if (associated(plot_(i) % col_spec_)) then + if (check_for_node(node_plot, "col_spec")) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Color specifications ignored in voxel plot " // & @@ -2592,21 +2619,34 @@ contains call warning() end if - n_cols = size(plot_(i) % col_spec_) + n_cols = get_number_nodes(node_plot, "col_spec") do j = 1, n_cols - if (size(plot_(i) % col_spec_(j) % rgb) /= 3) then + + ! Get pointer to color spec XML node + call get_node_ptr(node_plot, "col_spec", node_col, j) + + ! Check and make sure 3 values are specified for RGB + if (get_arraysize_double(node_col, "rgb") /= 3) then message = "Bad RGB " & // "in plot " // trim(to_str(pl % id)) call fatal_error() end if - col_id = plot_(i) % col_spec_(j) % id + ! Ensure that there is an id for this color specification + if (check_for_node(node_col, "id")) then + call get_node_value(node_col, "id", col_id) + else + message = "Must specify id for color specification in plot " // & + trim(to_str(pl % id)) + call fatal_error() + end if + ! Add RGB if (pl % color_by == PLOT_COLOR_CELLS) then if (cell_dict % has_key(col_id)) then col_id = cell_dict % get_key(col_id) - pl % colors(col_id) % rgb = plot_(i) % col_spec_(j) % rgb + call get_node_array(node_plot, "rgb", pl % colors(col_id) % rgb) else message = "Could not find cell " // trim(to_str(col_id)) // & " specified in plot " // trim(to_str(pl % id)) @@ -2617,7 +2657,7 @@ contains if (material_dict % has_key(col_id)) then col_id = material_dict % get_key(col_id) - pl % colors(col_id) % rgb = plot_(i) % col_spec_(j) % rgb + call get_node_array(node_plot, "rgb", pl % colors(col_id) % rgb) else message = "Could not find material " // trim(to_str(col_id)) // & " specified in plot " // trim(to_str(pl % id)) @@ -2629,7 +2669,7 @@ contains end if ! Deal with masks - if (associated(plot_(i) % mask_)) then + if (check_for_node(node_plot, "mask")) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Mask ignored in voxel plot " // & @@ -2637,23 +2677,37 @@ contains call warning() end if - select case(size(plot_(i) % mask_)) + select case(get_number_nodes(node_plot, "mask")) case default message = "Mutliple masks" // & " specified in plot " // trim(to_str(pl % id)) call fatal_error() case (0) case (1) - + + ! Get pointer to mask + call get_node_ptr(node_plot, "mask", node_mask) + + ! Determine how many components there are and allocate + n_comp = 0 + n_comp = get_arraysize_integer(node_mask, "components") + if (n_comp == 0) then + message = "Missing in mask of plot " // & + trim(to_str(pl % id)) + call fatal_error() + end if + allocate(iarray(n_comp)) + call get_node_array(node_mask, "components", iarray) + ! First we need to change the user-specified identifiers to indices ! in the cell and material arrays - do j=1,size(plot_(i) % mask_(1) % components) - col_id = plot_(i) % mask_(1) % components(j) + do j=1, n_comp + col_id = iarray(j) if (pl % color_by == PLOT_COLOR_CELLS) then if (cell_dict % has_key(col_id)) then - plot_(i) % mask_(1) % components(j) = cell_dict % get_key(col_id) + iarray(j) = cell_dict % get_key(col_id) else message = "Could not find cell " // trim(to_str(col_id)) // & " specified in the mask in plot " // trim(to_str(pl % id)) @@ -2663,7 +2717,7 @@ contains else if (pl % color_by == PLOT_COLOR_MATS) then if (material_dict % has_key(col_id)) then - plot_(i) % mask_(1) % components(j) = material_dict % get_key(col_id) + iarray(j) = material_dict % get_key(col_id) else message = "Could not find material " // trim(to_str(col_id)) // & " specified in the mask in plot " // trim(to_str(pl % id)) @@ -2675,10 +2729,18 @@ contains ! Alter colors based on mask information do j=1,size(pl % colors) - if (.not. any(j .eq. plot_(i) % mask_(1) % components)) then - pl % colors(j) % rgb = plot_(i) % mask_(1) % background + if (.not. any(j .eq. iarray)) then + if (check_for_node(node_mask, "background")) then + call get_node_array(node_mask, "background", pl % colors(j) % rgb) + else + message = "Missing in mask of plot " // & + trim(to_str(pl % id)) + call fatal_error() + end if end if end do + + deallocate(iarray) end select @@ -2689,6 +2751,9 @@ contains end do READ_PLOTS + ! Close plots XML file + call close_xmldoc(doc) + end subroutine read_plots_xml !=============================================================================== From 9143cba7886806d5c1d86ac73d121554be16b4ad Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 17:28:23 -0400 Subject: [PATCH 071/192] converted cross sections XML over to new XML reader --- src/input_xml.F90 | 81 ++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 7b2bce19ea..24ec6c2c67 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2763,15 +2763,16 @@ contains subroutine read_cross_sections_xml() - use xml_data_cross_sections_t - integer :: i ! loop index integer :: filetype ! default file type integer :: recl ! default record length integer :: entries ! default number of entries logical :: file_exists ! does cross_sections.xml exist? character(MAX_WORD_LEN) :: directory ! directory with cross sections + character(MAX_LINE_LEN) :: temp_str type(XsListing), pointer :: listing => null() + type(Node), pointer :: doc => null() + type(Node), pointer :: node_ace => null() ! Check if cross_sections.xml exists inquire(FILE=path_cross_sections, EXIST=file_exists) @@ -2785,18 +2786,12 @@ contains message = "Reading cross sections XML file..." call write_message(5) - ! Initialize variables that may go unused - directory_ = "" - filetype_ = "" - record_length_ = 0 - entries_ = 0 - ! Parse cross_sections.xml file - call read_xml_file_cross_sections_t(path_cross_sections) + call open_xmldoc(doc, path_cross_sections) - if (len_trim(directory_) > 0) then + if (check_for_node(doc, "directory")) then ! Copy directory information if present - directory = trim(directory_) + call get_node_value(doc, "directory", directory) else ! If no directory is listed in cross_sections.xml, by default select the ! directory in which the cross_sections.xml file resides @@ -2805,40 +2800,50 @@ contains end if ! determine whether binary/ascii - if (filetype_ == 'ascii') then + temp_str = '' + if (check_for_node(doc, "filetype")) & + call get_node_value(doc, "filetype", temp_str) + if (trim(temp_str) == 'ascii') then filetype = ASCII - elseif (filetype_ == 'binary') then + elseif (trim(temp_str) == 'binary') then filetype = BINARY - elseif (len_trim(filetype_) == 0) then + elseif (len_trim(temp_str) == 0) then filetype = ASCII else - message = "Unknown filetype in cross_sections.xml: " // trim(filetype_) + message = "Unknown filetype in cross_sections.xml: " // trim(temp_str) call fatal_error() end if ! copy default record length and entries for binary files - recl = record_length_ - entries = entries_ + if (filetype == BINARY) then + call get_node_value(doc, "record_length", recl) + call get_node_value(doc, "entries", entries) + end if ! Allocate xs_listings array - if (.not. associated(ace_tables_)) then + if (.not.check_for_node(doc, "ace_table")) then message = "No ACE table listings present in cross_sections.xml file!" call fatal_error() else - n_listings = size(ace_tables_) + n_listings = get_number_nodes(doc, "ace_table") allocate(xs_listings(n_listings)) end if do i = 1, n_listings listing => xs_listings(i) + ! Get pointer to ace table XML node + call get_node_ptr(doc, "ace_table", node_ace, i) + ! copy a number of attributes - listing % name = trim(ace_tables_(i) % name) - listing % alias = trim(ace_tables_(i) % alias) - listing % zaid = ace_tables_(i) % zaid - listing % awr = ace_tables_(i) % awr - listing % kT = ace_tables_(i) % temperature - listing % location = ace_tables_(i) % location + call get_node_value(node_ace, "name", listing % name) + if (check_for_node(node_ace, "alias")) & + call get_node_value(node_ace, "alias", listing % alias) + call get_node_value(node_ace, "zaid", listing % zaid) + call get_node_value(node_ace, "awr", listing % awr) + if (check_for_node(node_ace, "temperature")) & + call get_node_value(node_ace, "temperature", listing % kT) + call get_node_value(node_ace, "location", listing % location) ! determine type of cross section if (ends_with(listing % name, 'c')) then @@ -2849,24 +2854,33 @@ contains ! set filetype, record length, and number of entries listing % filetype = filetype - listing % recl = recl - listing % entries = entries + if (filetype == BINARY) then + listing % recl = recl + listing % entries = entries + end if ! determine metastable state - if (ace_tables_(i) % metastable == 0) then + if (.not.check_for_node(node_ace, "metastable")) then listing % metastable = .false. else listing % metastable = .true. end if ! determine path of cross section table - if (starts_with(ace_tables_(i) % path, '/')) then - listing % path = ace_tables_(i) % path + if (check_for_node(node_ace, "path")) then + call get_node_value(node_ace, "path", temp_str) + else + message = "Path missing for isotope " // listing % name + call fatal_error() + end if + + if (starts_with(temp_str, '/')) then + listing % path = trim(temp_str) else if (ends_with(directory,'/')) then - listing % path = trim(directory) // trim(ace_tables_(i) % path) + listing % path = trim(directory) // trim(temp_str) else - listing % path = trim(directory) // '/' // trim(ace_tables_(i) % path) + listing % path = trim(directory) // '/' // trim(temp_str) end if end if @@ -2877,6 +2891,9 @@ contains end if end do + ! Close cross sections XML file + call close_xmldoc(doc) + end subroutine read_cross_sections_xml !=============================================================================== From d74d4107814b90949bd79c6cbaafd3fa62602c36 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 18:09:38 -0400 Subject: [PATCH 072/192] converted over CMFD XML input to new XML interface --- src/cmfd_input.F90 | 301 +++++++++++++++++++++++++++------------------ 1 file changed, 183 insertions(+), 118 deletions(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index c228890d1e..df692c46e4 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -31,19 +31,24 @@ contains !=============================================================================== subroutine read_cmfd_xml() - + use error, only: fatal_error use global use output, only: write_message use string, only: lower_case - use xml_data_cmfd_t + use xml_interface use, intrinsic :: ISO_FORTRAN_ENV integer :: ng + integer, allocatable :: iarray(:) logical :: file_exists ! does cmfd.xml exist? + logical :: found character(MAX_LINE_LEN) :: filename + character(MAX_LINE_LEN) :: temp_str + type(Node), pointer :: doc => null() + type(Node), pointer :: node_mesh => null() - ! read cmfd infput file + ! read cmfd input file filename = trim(path_input) // "cmfd.xml" inquire(FILE=filename, EXIST=file_exists) if (.not. file_exists) then @@ -62,16 +67,25 @@ contains end if ! parse cmfd.xml file - call read_xml_file_cmfd_t(filename) + call open_xmldoc(doc, filename) + + ! get pointer to mesh XML node + call get_node_ptr(doc, "mesh", node_mesh, found = found) + + ! check if mesh is there + if (.not.found) then + message = "No CMFD mesh specified in CMFD XML file." + call fatal_error() + end if ! set spatial dimensions in cmfd object - cmfd % indices(1:3) = mesh_ % dimension(1:3) ! sets spatial dimensions + call get_node_array(node_mesh, "dimension", cmfd % indices(1:3)) ! get number of energy groups - if (associated(mesh_ % energy)) then - ng = size(mesh_ % energy) + if (check_for_node(node_mesh, "energy")) then + ng = get_arraysize_double(node_mesh, "energy") if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(ng)) - cmfd%egrid = mesh_ % energy + call get_node_array(node_mesh, "energy", cmfd%egrid) cmfd % indices(4) = ng - 1 ! sets energy group dimension else if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(2)) @@ -80,22 +94,26 @@ contains end if ! set global albedo - if (associated(mesh_ % albedo)) then - cmfd % albedo = mesh_ % albedo + if (check_for_node(node_mesh, "albedo")) then + call get_node_array(node_mesh, "albedo", cmfd % albedo) else cmfd % albedo = (/1.0, 1.0, 1.0, 1.0, 1.0, 1.0/) end if ! get acceleration map - if (associated(mesh_ % map)) then + if (check_for_node(node_mesh, "map")) then allocate(cmfd % coremap(cmfd % indices(1), cmfd % indices(2), & cmfd % indices(3))) - if (size(mesh_ % map) /= product(cmfd % indices(1:3))) then + if (get_arraysize_integer(node_mesh, "map") /= & + product(cmfd % indices(1:3))) then message = 'FATAL==>CMFD coremap not to correct dimensions' call fatal_error() end if - cmfd % coremap = reshape(mesh_ % map,(cmfd % indices(1:3))) + allocate(iarray(get_arraysize_integer(node_mesh, "map"))) + call get_node_array(node_mesh, "map", iarray) + cmfd % coremap = reshape(iarray,(cmfd % indices(1:3))) cmfd_coremap = .true. + deallocate(iarray) end if ! check for core map activation by printing note @@ -105,11 +123,17 @@ contains end if ! check for normalization constant - cmfd % norm = norm_ + if (check_for_node(doc, "norm")) then + call get_node_value(doc, "norm", cmfd % norm) + end if ! set feedback logical - call lower_case(feedback_) - if (feedback_ == 'true' .or. feedback_ == '1') cmfd_feedback = .true. + if (check_for_node(doc, "feedback")) then + call get_node_value(doc, "feedback", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_feedback = .true. + end if ! set balance logical ! call lower_case(balance_) @@ -121,64 +145,97 @@ contains ! cmfd_downscatter = downscatter_ ! set 2 group fix - call lower_case(run_2grp_) - if (run_2grp_ == 'true' .or. run_2grp_ == '1') cmfd_run_2grp = .true. + if (check_for_node(doc, "run_2grp")) then + call get_node_value(doc, "run_2grp", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_run_2grp = .true. + end if ! set the solver type - cmfd_solver_type = solver_(1:10) + if (check_for_node(doc, "solver")) & + call get_node_value(doc, "solver", cmfd_solver_type) - ! set monitoring - call lower_case(snes_monitor_) - call lower_case(ksp_monitor_) - call lower_case(power_monitor_) - if (snes_monitor_ == 'true' .or. snes_monitor_ == '1') & - cmfd_snes_monitor = .true. - if (ksp_monitor_ == 'true' .or. ksp_monitor_ == '1') & - cmfd_ksp_monitor = .true. - if (power_monitor_ == 'true' .or. power_monitor_ == '1') & - cmfd_power_monitor = .true. + ! set monitoring + if (check_for_node(doc, "snes_monitor")) then + call get_node_value(doc, "snes_monitor", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_snes_monitor = .true. + end if + if (check_for_node(doc, "ksp_monitor")) then + call get_node_value(doc, "ksp_monitor", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_ksp_monitor = .true. + end if + if (check_for_node(doc, "power_monitor")) then + call get_node_value(doc, "power_monitor", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_power_monitor = .true. + end if ! output logicals - call lower_case(write_balance_) - call lower_case(write_matrices_) - ! call lower_case(write_hdf5_) - if (write_balance_ == 'true' .or. write_balance_ == '1') & - cmfd_write_balance = .true. - if (write_matrices_ == 'true' .or. write_matrices_ == '1') & - cmfd_write_matrices = .true. - ! if (write_hdf5_ == 'true' .or. write_hdf5_ == '1') & - ! cmfd_write_hdf5 = .true. + if (check_for_node(doc, "write_balance")) then + call get_node_value(doc, "write_balance", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_write_balance = .true. + end if + if (check_for_node(doc, "write_matrices")) then + call get_node_value(doc, "write_matices", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_write_matrices = .true. + end if ! run an adjoint calc - call lower_case(run_adjoint_) - if (run_adjoint_ == 'true' .or. run_adjoint_ == '1') & - cmfd_run_adjoint = .true. + if (check_for_node(doc, "run_adjoint")) then + call get_node_value(doc, "run_adjoint", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_run_adjoint = .true. + end if ! batch to begin cmfd - cmfd_begin = begin_ + if (check_for_node(doc, "begin")) & + call get_node_value(doc, "begin", cmfd_begin) ! tally during inactive batches - call lower_case(inactive_) - if (inactive_ == 'false' .or. inactive_ == '0') cmfd_tally_on = .false. + if (check_for_node(doc, "inactive")) then + call get_node_value(doc, "inactive", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & + cmfd_tally_on = .false. + end if ! inactive batch flush window - cmfd_inact_flush(1) = inactive_flush_ - cmfd_inact_flush(2) = num_flushes_ + if (check_for_node(doc, "inactive_flush")) & + call get_node_value(doc, "inactive_flush", cmfd_inact_flush(1)) + if (check_for_node(doc, "num_flushes")) & + call get_node_value(doc, "num_flushes", cmfd_inact_flush(2)) ! last flush before active batches - cmfd_act_flush = active_flush_ + if (check_for_node(doc, "active_flush")) & + call get_node_value(doc, "active_flush", cmfd_act_flush) ! tolerance on keff - cmfd_keff_tol = keff_tol_ + if (check_for_node(doc, "keff_tol")) & + call get_node_value(doc, "keff_tol", cmfd_keff_tol) ! create tally objects - call create_cmfd_tally() + call create_cmfd_tally(doc) ! set number of CMFD processors and report to user - n_procs_cmfd = n_cmfd_procs_ + if (check_for_node(doc, "n_cmfd_procs")) & + call get_node_value(doc, "n_cmfd_procs", n_procs_cmfd) if (master) write(OUTPUT_UNIT,'(A,1X,I0,1X,A)') "CMFD Running on", & n_procs_cmfd," processors." + ! close CMFD XML file + call close_xmldoc(doc) + end subroutine read_cmfd_xml !=============================================================================== @@ -190,7 +247,7 @@ contains ! 3: Surface current !=============================================================================== - subroutine create_cmfd_tally() + subroutine create_cmfd_tally(doc) use error, only: fatal_error, warning use global @@ -200,20 +257,21 @@ contains use tally_header, only: TallyObject, TallyFilter use tally_initialize, only: add_tallies use xml_data_cmfd_t + use xml_interface + + type(Node), pointer :: doc integer :: i ! loop counter integer :: n ! size of arrays in mesh specification integer :: ng ! number of energy groups (default 1) integer :: n_filters ! number of filters integer :: i_filter_mesh ! index for mesh filter - character(MAX_LINE_LEN) :: filename + integer :: iarray3(3) + real(8) :: rarray3(3) type(TallyObject), pointer :: t => null() type(StructuredMesh), pointer :: m => null() type(TallyFilter) :: filters(N_FILTER_TYPES) ! temporary filters - - ! parse cmfd.xml file - filename = trim(path_input) // "cmfd.xml" - call read_xml_file_cmfd_t(filename) + type(Node), pointer :: node_mesh => null() ! set global variables if they are 0 (this can happen if there is no tally ! file) @@ -229,8 +287,11 @@ contains ! set mesh type to rectangular m % type = LATTICE_RECT + ! Get pointer to mesh XML node + call get_node_ptr(doc, "mesh", node_mesh) + ! Determine number of dimensions for mesh - n = size(mesh_ % dimension) + n = get_arraysize_integer(node_mesh, "dimension") if (n /= 2 .and. n /= 3) then message = "Mesh must be two or three dimensions." call fatal_error() @@ -244,75 +305,76 @@ contains allocate(m % upper_right(n)) ! Check that dimensions are all greater than zero - if (any(mesh_ % dimension <= 0)) then - message = "All entries on the element for a tally mesh & - &must be positive." - call fatal_error() + call get_node_array(node_mesh, "dimension", iarray3(1:n)) + if (any(iarray3(1:n) <= 0)) then + message = "All entries on the element for a tally mesh & + &must be positive." + call fatal_error() end if ! Read dimensions in each direction - m % dimension = mesh_ % dimension + m % dimension = iarray3(1:n) ! Read mesh lower-left corner location - if (m % n_dimension /= size(mesh_ % lower_left)) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() + if (m % n_dimension /= get_arraysize_double(node_mesh, "lower_left")) then + message = "Number of entries on must be the same as & + &the number of entries on ." + call fatal_error() end if - m % lower_left = mesh_ % lower_left + call get_node_array(node_mesh, "lower_left", m % lower_left) - ! Make sure either upper-right or width was specified - if (associated(mesh_ % upper_right) .and. & - associated(mesh_ % width)) then - message = "Cannot specify both and on a & - &tally mesh." - call fatal_error() + ! Make sure both upper-right or width were specified + if (check_for_node(node_mesh, "upper_right") .and. & + check_for_node(node_mesh, "width")) then + message = "Cannot specify both and on a & + &tally mesh." + call fatal_error() end if ! Make sure either upper-right or width was specified - if (.not. associated(mesh_ % upper_right) .and. & - .not. associated(mesh_ % width)) then - message = "Must specify either and on a & - &tally mesh." - call fatal_error() + if (.not.check_for_node(node_mesh, "upper_right") .and. & + .not.check_for_node(node_mesh, "width")) then + message = "Must specify either and on a & + &tally mesh." + call fatal_error() end if - if (associated(mesh_ % width)) then - ! Check to ensure width has same dimensions - if (size(mesh_ % width) /= size(mesh_ % lower_left)) then - message = "Number of entries on must be the same as the & - &number of entries on ." - call fatal_error() - end if + if (check_for_node(node_mesh, "width")) then + ! Check to ensure width has same dimensions + if (get_arraysize_double(node_mesh, "width") /= & + get_arraysize_double(node_mesh, "lower_left")) then + message = "Number of entries on must be the same as the & + &number of entries on ." + call fatal_error() + end if - ! Check for negative widths - if (any(mesh_ % width < ZERO)) then - message = "Cannot have a negative on a tally mesh." - call fatal_error() - end if + ! Check for negative widths + call get_node_array(node_mesh, "width", rarray3(1:n)) + if (any(rarray3(1:n) < ZERO)) then + message = "Cannot have a negative on a tally mesh." + call fatal_error() + end if - ! Set width and upper right coordinate - m % width = mesh_ % width - m % upper_right = m % lower_left + m % dimension * m % width + ! Set width and upper right coordinate + m % width = rarray3(1:n) + m % upper_right = m % lower_left + m % dimension * m % width - elseif (associated(mesh_ % upper_right)) then - ! Check to ensure width has same dimensions - if (size(mesh_ % upper_right) /= size(mesh_ % lower_left)) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() - end if + elseif (check_for_node(node_mesh, "upper_right")) then + ! Check to ensure width has same dimensions + if (get_arraysize_double(node_mesh, "upper_right") /= & + get_arraysize_double(node_mesh, "lower_left")) then + message = "Number of entries on must be the same as & + &the number of entries on ." + call fatal_error() + end if - ! Check that upper-right is above lower-left - if (any(mesh_ % upper_right < mesh_ % lower_left)) then - message = "The coordinates must be greater than the & - & coordinates on a tally mesh." - call fatal_error() - end if - - ! Set width and upper right coordinate - m % upper_right = mesh_ % upper_right - m % width = (m % upper_right - m % lower_left) / m % dimension + ! Check that upper-right is above lower-left + call get_node_array(node_mesh, "upper_right", rarray3(1:n)) + if (any(rarray3(1:n) < m % lower_left)) then + message = "The coordinates must be greater than the & + & coordinates on a tally mesh." + call fatal_error() + end if end if ! Set volume fraction @@ -343,13 +405,14 @@ contains t % find_filter(FILTER_MESH) = n_filters ! read and set incoming energy mesh filter - if (associated(mesh_ % energy)) then + if (check_for_node(node_mesh, "energy")) then n_filters = n_filters + 1 filters(n_filters) % type = FILTER_ENERGYIN - ng = size(mesh_ % energy) + ng = get_arraysize_double(node_mesh, "energy") filters(n_filters) % n_bins = ng - 1 allocate(filters(n_filters) % real_bins(ng)) - filters(n_filters) % real_bins = mesh_ % energy + call get_node_array(node_mesh, "energy", & + filters(n_filters) % real_bins) t % find_filter(FILTER_ENERGYIN) = n_filters end if @@ -404,13 +467,14 @@ contains t % type = TALLY_VOLUME ! read and set outgoing energy mesh filter - if (associated(mesh_ % energy)) then + if (check_for_node(node_mesh, "energy")) then n_filters = n_filters + 1 filters(n_filters) % type = FILTER_ENERGYOUT - ng = size(mesh_ % energy) + ng = get_arraysize_double(node_mesh, "energy") filters(n_filters) % n_bins = ng - 1 allocate(filters(n_filters) % real_bins(ng)) - filters(n_filters) % real_bins = mesh_ % energy + call get_node_array(node_mesh, "energy", & + filters(n_filters) % real_bins) t % find_filter(FILTER_ENERGYOUT) = n_filters end if @@ -420,7 +484,7 @@ contains t % filters = filters(1:n_filters) ! deallocate filters bins array - if (associated(mesh_ % energy)) & + if (check_for_node(node_mesh, "energy")) & deallocate(filters(n_filters) % real_bins) ! allocate macro reactions @@ -488,7 +552,8 @@ contains ! deallocate filter bins deallocate(filters(1) % int_bins) - if (associated(mesh_ % energy)) deallocate(filters(2) % real_bins) + if (check_for_node(node_mesh, "energy")) & + deallocate(filters(2) % real_bins) end do From dd9e1c97eb8580f6d2992d853f71163360d006f8 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 18:19:45 -0400 Subject: [PATCH 073/192] removed xml-fortran from build process and deleted files. Also, fixed minor bug in cmfd input --- src/DEPENDENCIES | 9 +- src/Makefile | 17 +- src/cmfd_input.F90 | 11 +- src/templates/Makefile | 38 - src/templates/cmfd.rnc | 58 - src/templates/cmfd_t.xml | 39 - src/templates/cross_sections.rnc | 23 - src/templates/cross_sections_t.xml | 26 - src/templates/geometry.rnc | 35 - src/templates/geometry_t.xml | 39 - src/templates/materials.rnc | 42 - src/templates/materials_t.xml | 44 - src/templates/plots.rnc | 31 - src/templates/plots_t.xml | 32 - src/templates/settings.rnc | 112 -- src/templates/settings_t.xml | 68 -- src/templates/tallies.rnc | 43 - src/templates/tallies_t.xml | 37 - src/utils/build_dependencies.py | 2 - src/xml-fortran/Makefile | 47 - src/xml-fortran/read_from_buffer.inc | 79 -- src/xml-fortran/read_xml_array.inc | 54 - src/xml-fortran/read_xml_primitives.f90 | 527 --------- src/xml-fortran/read_xml_scalar.inc | 40 - src/xml-fortran/read_xml_word.inc | 38 - src/xml-fortran/write_xml_primitives.f90 | 489 --------- src/xml-fortran/xml-fortran.LICENSE | 37 - src/xml-fortran/xmlparse.f90 | 1076 ------------------- src/xml-fortran/xmlreader.f90 | 1241 ---------------------- 29 files changed, 14 insertions(+), 4320 deletions(-) delete mode 100644 src/templates/Makefile delete mode 100644 src/templates/cmfd.rnc delete mode 100644 src/templates/cmfd_t.xml delete mode 100644 src/templates/cross_sections.rnc delete mode 100644 src/templates/cross_sections_t.xml delete mode 100644 src/templates/geometry.rnc delete mode 100644 src/templates/geometry_t.xml delete mode 100644 src/templates/materials.rnc delete mode 100644 src/templates/materials_t.xml delete mode 100644 src/templates/plots.rnc delete mode 100644 src/templates/plots_t.xml delete mode 100644 src/templates/settings.rnc delete mode 100644 src/templates/settings_t.xml delete mode 100644 src/templates/tallies.rnc delete mode 100644 src/templates/tallies_t.xml delete mode 100644 src/xml-fortran/Makefile delete mode 100644 src/xml-fortran/read_from_buffer.inc delete mode 100644 src/xml-fortran/read_xml_array.inc delete mode 100644 src/xml-fortran/read_xml_primitives.f90 delete mode 100644 src/xml-fortran/read_xml_scalar.inc delete mode 100644 src/xml-fortran/read_xml_word.inc delete mode 100644 src/xml-fortran/write_xml_primitives.f90 delete mode 100644 src/xml-fortran/xml-fortran.LICENSE delete mode 100644 src/xml-fortran/xmlparse.f90 delete mode 100644 src/xml-fortran/xmlreader.f90 diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 39ddf39e93..c5d01441d2 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -36,6 +36,7 @@ cmfd_execute.o: tally.o cmfd_header.o: constants.o cmfd_input.o: cmfd_message_passing.o +cmfd_input.o: constants.o cmfd_input.o: error.o cmfd_input.o: global.o cmfd_input.o: mesh_header.o @@ -44,7 +45,7 @@ cmfd_input.o: string.o cmfd_input.o: tally.o cmfd_input.o: tally_header.o cmfd_input.o: tally_initialize.o -cmfd_input.o: templates/cmfd_t.o +cmfd_input.o: xml_interface.o cmfd_jacobian_operator.o: cmfd_loss_operator.o cmfd_jacobian_operator.o: cmfd_prod_operator.o @@ -217,12 +218,6 @@ input_xml.o: random_lcg.o input_xml.o: string.o input_xml.o: tally_header.o input_xml.o: tally_initialize.o -input_xml.o: templates/cross_sections_t.o -input_xml.o: templates/geometry_t.o -input_xml.o: templates/materials_t.o -input_xml.o: templates/plots_t.o -input_xml.o: templates/settings_t.o -input_xml.o: templates/tallies_t.o input_xml.o: xml_interface.o interpolation.o: constants.o diff --git a/src/Makefile b/src/Makefile index 8d3646973d..2829226782 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,10 +1,6 @@ program = openmc prefix = /usr/local -templates = $(wildcard templates/*.o) -xml_fort = xml-fortran/xmlparse.o \ - xml-fortran/read_xml_primitives.o \ - xml-fortran/write_xml_primitives.o xml_lib = -Lxml/lib -lxml #=============================================================================== @@ -228,14 +224,11 @@ endif # Targets #=============================================================================== -all: xml xml-fortran $(program) +all: xml $(program) xml: cd xml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" -xml-fortran: - cd xml-fortran; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd templates; make F90=$(F90) F90FLAGS="$(F90FLAGS)" $(program): $(objects) - $(F90) $(objects) $(templates) $(xml_fort) $(xml_lib) $(LDFLAGS) -o $@ + $(F90) $(objects) $(xml_lib) $(LDFLAGS) -o $@ install: @install -D $(program) $(DESTDIR)$(prefix)/bin/$(program) @install -D utils/statepoint_cmp.py $(DESTDIR)$(prefix)/bin/statepoint_cmp @@ -252,8 +245,6 @@ uninstall: @rm $(DESTDIR)$(prefix)/share/doc/$(program)/copyright distclean: clean cd xml; make clean - cd xml-fortran; make clean - cd templates; make clean clean: @rm -f *.o *.mod $(program) neat: @@ -264,10 +255,10 @@ neat: #=============================================================================== .SUFFIXES: .F90 .o -.PHONY: all xml xml-fortran install uninstall clean neat distclean +.PHONY: all xml install uninstall clean neat distclean %.o: %.F90 - $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml-fortran -Itemplates -Ixml/include -c $< + $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml/include -c $< #=============================================================================== # Dependencies diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index df692c46e4..26a7a3ae0e 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -249,6 +249,7 @@ contains subroutine create_cmfd_tally(doc) + use constants, only: MAX_LINE_LEN use error, only: fatal_error, warning use global use mesh_header, only: StructuredMesh @@ -256,11 +257,11 @@ contains use tally, only: setup_active_cmfdtallies use tally_header, only: TallyObject, TallyFilter use tally_initialize, only: add_tallies - use xml_data_cmfd_t use xml_interface type(Node), pointer :: doc + character(MAX_LINE_LEN) :: temp_str integer :: i ! loop counter integer :: n ! size of arrays in mesh specification integer :: ng ! number of energy groups (default 1) @@ -393,8 +394,12 @@ contains t => cmfd_tallies(i) ! set reset property - call lower_case(reset_) - if (reset_ == 'true' .or. reset_ == '1') t % reset = .true. + if (check_for_node(doc, "reset")) then + call get_node_value(doc, "reset", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + t % reset = .true. + end if ! set up mesh filter n_filters = 1 diff --git a/src/templates/Makefile b/src/templates/Makefile deleted file mode 100644 index 232ae96090..0000000000 --- a/src/templates/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -templates = $(wildcard *.xml) -objects = $(templates:.xml=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Add include for subdirectory - -override F90FLAGS += -I../xml-fortran - -# Ignore unused variables - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) -clean: - @rm -f *.o *.mod *.out *.f90 - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .f90 .o .xml -.PHONY: all clean -.PRECIOUS: %.f90 - -%.f90: %.xml - ../xml-fortran/xmlreader $(basename $@) - -%.o: %.f90 - $(F90) $(F90FLAGS) -c $(basename $@).f90 diff --git a/src/templates/cmfd.rnc b/src/templates/cmfd.rnc deleted file mode 100644 index cbcb7709ae..0000000000 --- a/src/templates/cmfd.rnc +++ /dev/null @@ -1,58 +0,0 @@ -element cmfd { - element mesh { - (element dimension { list { xsd:int+ } } | - attribute dimension { list { xsd:int+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - ( - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) | - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } }) - ) & - (element albedo { list { xsd:double+ } } | - attribute albedo { list { xsd:double+ } }) & - (element map { list { xsd:int+ } } | - attribute map { list { xsd:int+ } })? & - (element energy { list { xsd:double+ } } | - attribute energy { list { xsd:double+ } })? - } & - - element norm { xsd:double }? & - - element feedback { xsd:boolean }? & - - element n_cmfd_procs { xsd:int }? & - - element reset { xsd:boolean }? & - - element balance { xsd:boolean }? & - - element downscatter { xsd:boolean }? & - - element run_2grp { xsd:boolean }? & - - element solver { xsd:string }? & - - element snes_monitor { xsd:boolean }? & - - element ksp_monitor { xsd:boolean }? & - - element power_monitor { xsd:boolean }? & - - element write_balance { xsd:boolean }? & - - element write_matrices { xsd:boolean }? & - - element run_adjoint { xsd:boolean }? & - - element write_hdf5 { xsd:boolean }? & - - element begin { xsd:int }? & - - element inactive { xsd:boolean }? & - - element active_flush { xsd:int }? & - - element keff_tol { xsd:double }? -} diff --git a/src/templates/cmfd_t.xml b/src/templates/cmfd_t.xml deleted file mode 100644 index c9ae3c1fdc..0000000000 --- a/src/templates/cmfd_t.xml +++ /dev/null @@ -1,39 +0,0 @@ - - diff --git a/src/templates/cross_sections.rnc b/src/templates/cross_sections.rnc deleted file mode 100644 index e82bb986fb..0000000000 --- a/src/templates/cross_sections.rnc +++ /dev/null @@ -1,23 +0,0 @@ -element cross_sections { - element ace_table { - (element name { xsd:string { maxLength = "15" } } | - attribute name { xsd:string { maxLength = "15" } }) & - (element alias { xsd:string { maxLength = "15" } } | - attribute alias { xsd:string { maxLength = "15" } })? & - (element zaid { xsd:int } | attribute zaid { xsd:int }) & - (element metastable { xsd:int } | attribute metastable { xsd:int })? & - (element awr { xsd:double } | attribute awr { xsd:double }) & - (element temperature { xsd:double } | attribute temperature { xsd:double }) & - (element path { xsd:string { maxLength = "255" } } | - attribute path { xsd:string { maxLength = "255" } }) & - (element location { xsd:int } | attribute location { xsd:int })? - }* & - - element directory { xsd:string { maxLength = "255" } }? & - - element filetype { ( "ascii" | "binary" ) } & - - element record_length { xsd:int }? & - - element entries { xsd:int }? -} \ No newline at end of file diff --git a/src/templates/cross_sections_t.xml b/src/templates/cross_sections_t.xml deleted file mode 100644 index 0852b85042..0000000000 --- a/src/templates/cross_sections_t.xml +++ /dev/null @@ -1,26 +0,0 @@ - - diff --git a/src/templates/geometry.rnc b/src/templates/geometry.rnc deleted file mode 100644 index b7975d05f3..0000000000 --- a/src/templates/geometry.rnc +++ /dev/null @@ -1,35 +0,0 @@ -element geometry { - element cell { - (element id { xsd:int } | attribute id { xsd:int }) & - (element universe { xsd:int } | attribute universe { xsd:int })? & - ( - (element fill { xsd:int } | attribute fill { xsd:int }) | - (element material { ( xsd:int | "void" ) } | - attribute material { ( xsd:int | "void" ) }) - ) & - (element surfaces { list { xsd:int+ } } | attribute surfaces { list { xsd:int+ } }) & - (element rotation { list { xsd:double+ } } | attribute rotation { list { xsd:double+ } })? & - (element translation { list { xsd:double+ } } | attribute translation { list { xsd:double+ } })? - }* - - & element surface { - (element id { xsd:int } | attribute id { xsd:int }) & - (element type { xsd:string { maxLength = "15" } } | - attribute type { xsd:string { maxLength = "15" } }) & - (element coeffs { list { xsd:double+ } } | attribute coeffs { list { xsd:double+ } }) & - (element boundary { ( "transmit" | "reflective" | "vacuum" ) } | - attribute boundary { ( "transmit" | "reflective" | "vacuum" ) })? - }* - - & element lattice { - (element id { xsd:int } | attribute id { xsd:int }) & - (element type { ( "rectangular" | "hexagonal" ) } | - attribute type { ( "rectangular" | "hexagonal" ) })? & - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | attribute lower_left { list { xsd:double+ } }) & - (element width { list { xsd:double+ } } | attribute width { list { xsd:double+ } }) & - (element universes { list { xsd:int+ } } | attribute universes { list { xsd:int+ } }) & - (element outside { xsd:int } | attribute outside { xsd:int })? - }* -} diff --git a/src/templates/geometry_t.xml b/src/templates/geometry_t.xml deleted file mode 100644 index 539a23f6b4..0000000000 --- a/src/templates/geometry_t.xml +++ /dev/null @@ -1,39 +0,0 @@ - - diff --git a/src/templates/materials.rnc b/src/templates/materials.rnc deleted file mode 100644 index d497fa745f..0000000000 --- a/src/templates/materials.rnc +++ /dev/null @@ -1,42 +0,0 @@ -element materials { - element material { - (element id { xsd:int } | attribute id { xsd:int }) & - - element density { - (element value { xsd:double } | attribute value { xsd:double })? & - (element units { xsd:string { maxLength = "10" } } | - attribute units { xsd:string { maxLength = "10" } }) - } & - - element nuclide { - (element name { xsd:string { maxLength = "7" } } | - attribute name { xsd:string { maxLength = "7" } }) & - (element xs { xsd:string { maxLength = "3" } } | - attribute xs { xsd:string { maxLength = "3" } })? & - ( - (element ao { xsd:double } | attribute ao { xsd:double }) | - (element wo { xsd:double } | attribute wo { xsd:double }) - ) - }* & - - element element { - (element name { xsd:string { maxLength = "2" } } | - attribute name { xsd:string { maxLength = "2" } }) & - (element xs { xsd:string { maxLength = "3" } } | - attribute xs { xsd:string { maxLength = "3" } })? & - ( - (element ao { xsd:double } | attribute ao { xsd:double }) | - (element wo { xsd:double } | attribute wo { xsd:double }) - ) - }* & - - element sab { - (element name { xsd:string { maxLength = "7" } } | - attribute name { xsd:string { maxLength = "7" } }) & - (element xs { xsd:string { maxLength = "3" } } | - attribute xs { xsd:string { maxLength = "3" } })? - }* - }+ & - - element default_xs { xsd:string { maxLength = "3" } }? -} diff --git a/src/templates/materials_t.xml b/src/templates/materials_t.xml deleted file mode 100644 index 99950e0351..0000000000 --- a/src/templates/materials_t.xml +++ /dev/null @@ -1,44 +0,0 @@ - - diff --git a/src/templates/plots.rnc b/src/templates/plots.rnc deleted file mode 100644 index 3c29ba4e0b..0000000000 --- a/src/templates/plots.rnc +++ /dev/null @@ -1,31 +0,0 @@ -element plots { - element plot { - (element id { xsd:int } | attribute id { xsd:int })? & - (element filename { xsd:string { maxLength = "50" } } | - attribute filename { xsd:string { maxLength = "50" } })? & - (element type { "slice" } | attribute type { "slice" })? & - (element color { ( "cell" | "mat" | "material" ) } | - attribute color { ( "cell" | "mat" | "material" ) })? & - (element origin { list { xsd:double+ } } | - attribute origin { list { xsd:double+ } })? & - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } })? & - (element basis { ( "xy" | "yz" | "xz" ) } | - attribute basis { ( "xy" | "yz" | "xz" ) })? & - (element pixels { list { xsd:int+ } } | - attribute pixels { list { xsd:int+ } })? & - (element background { list { xsd:int+ } } | - attribute background { list { xsd:int+ } })? & - element col_spec { - (element id { xsd:int } | attribute id { xsd:int }) & - (element rgb { list { xsd:int+ } } | - attribute rgb { list { xsd:int+ } }) - }* & - element mask { - (element components { list { xsd:int+ } } | - attribute components { list { xsd:int+ } }) & - (element background { list { xsd:int+ } } | - attribute background { list { xsd:int+ } }) - }* - }* -} diff --git a/src/templates/plots_t.xml b/src/templates/plots_t.xml deleted file mode 100644 index b3b86fc44e..0000000000 --- a/src/templates/plots_t.xml +++ /dev/null @@ -1,32 +0,0 @@ - - diff --git a/src/templates/settings.rnc b/src/templates/settings.rnc deleted file mode 100644 index 47dd690bcc..0000000000 --- a/src/templates/settings.rnc +++ /dev/null @@ -1,112 +0,0 @@ -element settings { - element confidence_intervals { xsd:boolean }? & - - ( - element eigenvalue { - (element batches { xsd:positiveInteger } | - attribute batches { xsd:positiveInteger }) & - (element inactive { xsd:nonNegativeInteger } | - attribute inactive { xsd:nonNegativeInteger }) & - (element particles { xsd:positiveInteger } | - attribute particles { xsd:positiveInteger }) & - (element generations_per_batch { xsd:positiveInteger } | - attribute generations_per_batch { xsd:positiveInteger })? - } | - element fixed_source { - (element batches { xsd:positiveInteger } | - attribute batches { xsd:positiveInteger }) & - (element particles { xsd:positiveInteger } | - attribute particles { xsd:positiveInteger }) - } - ) & - - element cross_sections { xsd:string { maxLength = "255" } }? & - - element cutoff { - (element weight { xsd:double } | attribute weight { xsd:double })? & - (element weight_avg { xsd:double } | attribute weight_avg { xsd:double })? - }? & - - element energy_grid { ( "nuclide" | "union" | "lethargy" ) }? & - - element entropy { - (element dimension { list { xsd:int+ } } | - attribute dimension { list { xsd:int+ } })? & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) - }? & - - element no_reduce { xsd:boolean }? & - - element output { list { - ( "summary" | "cross_sections" | "tallies" )+ } }? & - - element output_path { xsd:string { maxLength = "255" } }? & - - element ptables { xsd:boolean }? & - - element run_cmfd { xsd:boolean }? & - - element seed { xsd:positiveInteger }? & - - element source { - element file { xsd:string { maxLength = "255" } }? & - element space { - (element type { xsd:string { maxLength = "16" } } | - attribute type { xsd:string { maxLength = "16" } }) & - (element length { xsd:int } | attribute length { xsd:int })? & - (element interpolation { xsd:string { maxLength = "10" } } | - attribute interplation { xsd:string { maxLength = "10" } })? & - (element parameters { list { xsd:double+ } } | - attribute parameters { list { xsd:double+ } })? - }? & - element angle { - (element type { xsd:string { maxLength = "16" } } | - attribute type { xsd:string { maxLength = "16" } }) & - (element length { xsd:int } | attribute length { xsd:int })? & - (element interpolation { xsd:string { maxLength = "10" } } | - attribute interplation { xsd:string { maxLength = "10" } })? & - (element parameters { list { xsd:double+ } } | - attribute parameters { list { xsd:double+ } })? - }? & - element energy { - (element type { xsd:string { maxLength = "16" } } | - attribute type { xsd:string { maxLength = "16" } }) & - (element length { xsd:int } | attribute length { xsd:int })? & - (element interpolation { xsd:string { maxLength = "10" } } | - attribute interplation { xsd:string { maxLength = "10" } })? & - (element parameters { list { xsd:double+ } } | - attribute parameters { list { xsd:double+ } })? - }? - }? & - - element state_point { - ( - (element batches { list { xsd:positiveInteger+ } } | - attribute batches { list { xsd:positiveInteger+ } }) | - (element interval { xsd:positiveInteger } | - attribute interval { xsd:positiveInteger }) - ) & - (element source_separate { xsd:boolean } | - attribute source_separate { xsd:boolean })? & - (element source_write { xsd:boolean } | - attribute source_write { xsd:boolean })? - }? & - - element survival_biasing { xsd:boolean }? & - - element trace { list { xsd:positiveInteger+ } }? & - - element verbosity { xsd:positiveInteger }? & - - element uniform_fs{ - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) - }? -} diff --git a/src/templates/settings_t.xml b/src/templates/settings_t.xml deleted file mode 100644 index 3afa61745e..0000000000 --- a/src/templates/settings_t.xml +++ /dev/null @@ -1,68 +0,0 @@ - - diff --git a/src/templates/tallies.rnc b/src/templates/tallies.rnc deleted file mode 100644 index 06959276bc..0000000000 --- a/src/templates/tallies.rnc +++ /dev/null @@ -1,43 +0,0 @@ -element tallies { - element mesh { - (element id { xsd:int } | attribute id { xsd:int }) & - (element type { ( "rectangular" | "hexagonal" ) } | - attribute type { ( "rectangular" | "hexagonal" ) }) & - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - ( - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) | - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } }) - ) - }* & - - element tally { - (element id { xsd:int } | attribute id { xsd:int }) & - (element label { xsd:string { maxLength="52" } } | - attribute label { xsd:string { maxLength="52" } })? & - (element estimator { ( "analog" | "tracklength" ) } | - attribute estimator { ( "analog" | "tracklength" ) })? & - element filter { - (element type { ( "cell" | "cellborn" | "material" | "universe" | - "surface" | "mesh" | "energy" | "energyout" ) } | - attribute type { ( "cell" | "cellborn" | "material" | "universe" | - "surface" | "mesh" | "energy" | "energyout" ) }) & - (element bins { list { xsd:string { maxLength = "20" }+ } } | - attribute bins { list { xsd:string { maxLength = "20" }+ } }) & - (element groups { xsd:string { maxLength = "20" }+ } | - attribute groups { xsd:string { maxLength = "20" }+ })? - }* & - element nuclides { - list { xsd:string { maxLength = "12" }+ } - }? & - element scores { - list { xsd:string { maxLength = "20" }+ } - } - }* & - - element assume_separate { xsd:boolean }? -} diff --git a/src/templates/tallies_t.xml b/src/templates/tallies_t.xml deleted file mode 100644 index cf653968d8..0000000000 --- a/src/templates/tallies_t.xml +++ /dev/null @@ -1,37 +0,0 @@ - - diff --git a/src/utils/build_dependencies.py b/src/utils/build_dependencies.py index 3aa22c6076..fb966d028b 100755 --- a/src/utils/build_dependencies.py +++ b/src/utils/build_dependencies.py @@ -13,8 +13,6 @@ for src in glob.iglob('*.F90'): for name in d: if name in ['mpi','hdf5','h5lt','fox_dom']: continue - if name.startswith('xml_data_'): - name = name.replace('xml_data_', 'templates/') deps.add(name) if deps: dependencies[module] = sorted(list(deps)) diff --git a/src/xml-fortran/Makefile b/src/xml-fortran/Makefile deleted file mode 100644 index c0514533a3..0000000000 --- a/src/xml-fortran/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -reader = xmlreader -source = $(wildcard *.f90) -objects = $(source:.f90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unused variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(reader) -$(reader): $(objects) - $(F90) $(objects) -o $@ -clean: - @rm -f *.o *.mod $(reader) -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .f90 .o -.PHONY: all clean neat - -%.o: %.f90 - $(F90) $(F90FLAGS) -c $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -read_xml_primitives.o: xmlparse.o -write_xml_primitives.o: xmlparse.o -xmlreader.o: xmlparse.o diff --git a/src/xml-fortran/read_from_buffer.inc b/src/xml-fortran/read_from_buffer.inc deleted file mode 100644 index 0cb6ee9590..0000000000 --- a/src/xml-fortran/read_from_buffer.inc +++ /dev/null @@ -1,79 +0,0 @@ -! Part of XML-Fortran library: -! -! $Id: read_from_buffer.inc,v 1.2 2006/03/26 19:05:48 arjenmarkus Exp $ -! - character(len=*), intent(in) :: buffer - integer, intent(inout) :: ierror - - integer :: n - integer :: i - integer :: step - integer :: ierr - ! - ! First allocate an array that is surely large enough - ! Note: - ! This is not completely failsafe: with list-directed - ! input you can also use repeat counts (10000*1.0 for - ! instance). - ! - allocate( work(len(buffer)/2+1) ) - - ! - ! NOTE: - ! This is not portable!! - ! - ! read( buffer, *, iostat = ierror ) (work(n), n=1,size(work)) - ! - ! So, use a different strategy: a binary search - ! First: establish that we have at least one item to read - ! Second: do the binary search - ! -! read( buffer, *, iostat = ierr ) work(1) -! if ( ierr /= 0 ) then -! n = 0 -! else - n = 1 - do while ( n <= size(work) ) - n = 2 * n - enddo - n = n / 2 - step = n / 2 -! step = n / 2 - - do while ( step > 0 ) - read( buffer, *, iostat = ierr ) (work(i), i = 1,n) - if ( ierr /= 0 ) then - ierror = ierr ! Store the error code for later use - n = n - step - else - n = n + step - endif - step = step / 2 - enddo -! endif - - ! - ! Then allocate an array of the actual size needed - ! and copy the data - ! - ! - if ( associated( var ) ) then - deallocate( var ) - endif - ! - ! One complication: we may have one too many - ! (consequence of the binary search) - ! - read( buffer, *, iostat = ierr ) (work(i), i = 1,n) - if ( ierr < 0 ) then - n = n - 1 - endif - - allocate( var(n) ) - var(1:n) = work(1:n) - deallocate( work ) - - if ( ierror .lt. 0 ) then - ierror = 0 - endif - diff --git a/src/xml-fortran/read_xml_array.inc b/src/xml-fortran/read_xml_array.inc deleted file mode 100644 index a88086e740..0000000000 --- a/src/xml-fortran/read_xml_array.inc +++ /dev/null @@ -1,54 +0,0 @@ -! Part of XML-Fortran library: -! -! $Id: read_xml_array.inc,v 1.3 2007/02/26 20:33:38 arjenmarkus Exp $ -! - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: ierr - - ! - ! The big trick: - ! A string long enough to hold all data strings - ! - character(len=nodata*(len(data(1))+1)) :: bufferd - integer :: start - - ! - ! The value can be stored in an attribute values="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'values', buffer ) - if ( idx .gt. 0 ) then - call read_from_buffer( buffer, var, ierr ) - if ( buffer .ne. ' ' ) then - has_var = .true. - endif - else - bufferd = ' ' - start = 1 - do idx = 1,nodata - if ( data(idx) .ne. ' ' ) then - bufferd(start:) = data(idx) - start = start + len(data(idx)) + 1 - endif - enddo - call read_from_buffer( bufferd, var, ierr ) - if ( bufferd .ne. ' ' ) then - has_var = .true. - endif - endif - - if ( ierr .ne. 0 ) then - write(*,*) 'Error reading variable - tag = ', trim(tag) - has_var = .false. - endif diff --git a/src/xml-fortran/read_xml_primitives.f90 b/src/xml-fortran/read_xml_primitives.f90 deleted file mode 100644 index 1fb63d57b8..0000000000 --- a/src/xml-fortran/read_xml_primitives.f90 +++ /dev/null @@ -1,527 +0,0 @@ -! read_xml_prims.f90 - Read routines for primitive data -! -! $Id: read_xml_prims.f90,v 1.7 2007/12/07 10:38:41 arjenmarkus Exp $ -! -! Arjen Markus -! -! General information: -! This module is part of the XML-Fortran library. Its -! purpose is to help read individual items from an XML -! file into the variables that have been connected to -! the various tags. It is used by the code generated -! by the make_xml_reader program. -! -! Because the routines differ mostly by the type of the -! output variable, the body is included, to prevent -! too much repeated blocks of code with all the maintenance -! issues that causes. -! -module read_xml_primitives - use xmlparse - implicit none - - private :: read_from_buffer - private :: read_from_buffer_integers - private :: read_from_buffer_reals - private :: read_from_buffer_doubles - private :: read_from_buffer_logicals - private :: read_from_buffer_words - - interface read_from_buffer - module procedure read_from_buffer_integers - module procedure read_from_buffer_reals - module procedure read_from_buffer_doubles - module procedure read_from_buffer_logicals - module procedure read_from_buffer_words - end interface - -contains - -! skip_until_endtag -- -! Routine to read the XML file until the end tag is encountered -! -! Arguments: -! info The XML file data structure -! tag The tag in question -! attribs Array of attributes and their values -! data Array of strings, representing the data -! error Has an error occurred? -! -subroutine skip_until_endtag( info, tag, attribs, data, error ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - character(len=*), dimension(:,:), intent(inout) :: attribs - character(len=*), dimension(:), intent(inout) :: data - logical, intent(out) :: error - - integer :: noattribs - integer :: nodata - integer :: ierr - logical :: endtag - character(len=len(tag)) :: newtag - - error = .true. - do - call xml_get( info, newtag, endtag, attribs, noattribs, & - data, nodata ) - if ( xml_error(info) ) then - error = .true. - exit - endif - if ( endtag .and. newtag == tag ) then - exit - endif - enddo -end subroutine skip_until_endtag - -! read_xml_integer -- -! Routine to read a single integer from the parsed data -! -! Arguments: -! info XML parser structure -! tag The tag in question (error message only) -! endtag End tag found? (Dummy argument, actually) -! attribs Array of attributes and their values -! noattribs Number of attributes found -! data Array of strings, representing the data -! nodata Number of data strings -! var Variable to be filled -! has_var Has the variable been set? -! -subroutine read_xml_integer( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - integer, intent(inout) :: var - - include 'read_xml_scalar.inc' - -end subroutine read_xml_integer - -! read_xml_line -- -! Routine to read a single line of text from the parsed data -! -! Arguments: -! info XML parser structure -! tag The tag in question (error message only) -! endtag End tag found? (Dummy argument, actually) -! attribs Array of attributes and their values -! noattribs Number of attributes found -! data Array of strings, representing the data -! nodata Number of data strings -! var Variable to be filled -! has_var Has the variable been set? -! -subroutine read_xml_line( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - character(len=*), intent(inout) :: var - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: ierr - - ! - ! The value can be stored in an attribute value="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'value', buffer ) - if ( idx > 0 ) then - var = buffer - has_var = .true. - else - do idx = 1,nodata - if ( data(idx) /= ' ' ) then - var = data(idx) - has_var = .true. - exit - endif - enddo - endif -end subroutine read_xml_line - -! read_xml_real, ... -- -! See read_xml_integer for an explanation -! -subroutine read_xml_real( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - real, intent(inout) :: var - - include 'read_xml_scalar.inc' - -end subroutine read_xml_real - -subroutine read_xml_double( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - real(kind=kind(1.0d00)), intent(inout) :: var - - include 'read_xml_scalar.inc' - -end subroutine read_xml_double - -subroutine read_xml_logical( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - logical, intent(inout) :: var - - include 'read_xml_scalar.inc' - -end subroutine read_xml_logical - -subroutine read_xml_word( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - character(len=*), intent(inout) :: var - - include 'read_xml_word.inc' - -end subroutine read_xml_word - -! read_xml_integer_array -- -! Routine to read a one-dimensional integer array from the parsed -! ata -! -! Arguments: -! info XML parser structure -! tag The tag in question (error message only) -! endtag End tag found? (Dummy argument, actually) -! attribs Array of attributes and their values -! noattribs Number of attributes found -! data Array of strings, representing the data -! nodata Number of data strings -! var Variable to be filled -! has_var Has the variable been set? -! -subroutine read_xml_integer_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - integer, dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_integer_array - -! read_xml_line_array -- -! Routine to read an array of lines of text from the parsed data -! -! Arguments: -! info XML parser structure -! tag The tag in question (error message only) -! attribs Array of attributes and their values -! noattribs Number of attributes found -! data Array of strings, representing the data -! nodata Number of data strings -! var Variable to be filled -! has_var Has the variable been set? -! -subroutine read_xml_line_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - character(len=*), dimension(:), pointer :: var - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: idxv - integer :: ierr - logical :: started - - ! - ! The value can be stored in an attribute values="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'values', buffer ) - if ( idx > 0 ) then - allocate( var(1:1) ) - var(1) = buffer - if ( buffer /= ' ' ) then - has_var = .true. - endif - else - idxv = 0 - started = .false. - do idx = 1,nodata - if ( data(idx) /= ' ' .or. started ) then - if ( .not. started ) then - allocate( var(1:nodata-idx+1) ) - started = .true. - endif - idxv = idxv + 1 - var(idxv) = data(idx) - endif - enddo - if ( started ) then - has_var = .true. - endif - endif -end subroutine read_xml_line_array - -! read_xml_real_array, ... -- -! See read_xml_integer_array for an explanation -! -subroutine read_xml_real_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - real, dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_real_array - -subroutine read_xml_double_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - real(kind=kind(1.0d00)), dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_double_array - -subroutine read_xml_logical_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - logical, dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_logical_array - -subroutine read_xml_word_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - character(len=*), dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_word_array - -! read_from_buffer_integers -- -! Routine to read all integers from a long string -! -! Arguments: -! buffer String containing the data -! var Variable to be filled -! ierror Error flag -! -subroutine read_from_buffer_integers( buffer, var, ierror ) - integer, dimension(:), pointer :: var - integer, dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_integers - -! read_xml_from_buffer_reals, ... - -! See read_xml_from_buffer_integers for an explanation -! -subroutine read_from_buffer_reals( buffer, var, ierror ) - real, dimension(:), pointer :: var - real, dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_reals - -subroutine read_from_buffer_doubles( buffer, var, ierror ) - real(kind=kind(1.0d00)), dimension(:), pointer :: var - real(kind=kind(1.0d00)), dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_doubles - -subroutine read_from_buffer_logicals( buffer, var, ierror ) - logical, dimension(:), pointer :: var - logical, dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_logicals - -subroutine read_from_buffer_words( buffer, var, ierror ) - character(len=*), dimension(:), pointer :: var - character(len=len(var)), dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_words - -! read_xml_word_1dim, ... - -! Read an array of "words" (or ...) but from different elements -! -subroutine read_xml_integer_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - integer, dimension(:), pointer :: var - logical, intent(inout) :: has_var - - integer,dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_integer( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_integer_1dim - -subroutine read_xml_real_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - real, dimension(:), pointer :: var - logical, intent(inout) :: has_var - - real, dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_real( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_real_1dim - -subroutine read_xml_double_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - real(kind=kind(1.0d00)), dimension(:), pointer:: var - logical, intent(inout) :: has_var - - real(kind=kind(1.0d00)), dimension(:), pointer:: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_double( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_double_1dim - -subroutine read_xml_logical_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - logical, dimension(:), pointer :: var - logical, intent(inout) :: has_var - - logical, dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_logical( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_logical_1dim - -subroutine read_xml_word_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - character(len=*), dimension(:), pointer :: var - logical, intent(inout) :: has_var - - character(len=len(var)),dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_word( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_word_1dim - -subroutine read_xml_line_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - character(len=*), dimension(:), pointer :: var - logical, intent(inout) :: has_var - - character(len=len(var)),dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_line( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_line_1dim - - -end module read_xml_primitives diff --git a/src/xml-fortran/read_xml_scalar.inc b/src/xml-fortran/read_xml_scalar.inc deleted file mode 100644 index b28ba3c7a0..0000000000 --- a/src/xml-fortran/read_xml_scalar.inc +++ /dev/null @@ -1,40 +0,0 @@ -! Part of XML-Fortran library: -! -! $Id: read_xml_scalar.inc,v 1.3 2007/02/26 20:33:38 arjenmarkus Exp $ -! - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: ierr - - ! - ! The value can be stored in an attribute value="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'value', buffer ) - if ( idx .gt. 0 ) then - read( buffer, *, iostat=ierr ) var - has_var = .true. - else - do idx = 1,nodata - if ( data(idx) .ne. ' ' ) then - read( data(idx), *, iostat=ierr ) var - has_var = .true. - exit - endif - enddo - endif - - if ( ierr .ne. 0 ) then - write(*,*) 'Error reading variable - tag = ', trim(tag) - has_var = .false. - endif diff --git a/src/xml-fortran/read_xml_word.inc b/src/xml-fortran/read_xml_word.inc deleted file mode 100644 index 4c1848dcff..0000000000 --- a/src/xml-fortran/read_xml_word.inc +++ /dev/null @@ -1,38 +0,0 @@ -! Part of XML-Fortran library: -! - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: ierr - - ! - ! The value can be stored in an attribute value="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'value', buffer ) - if ( idx .gt. 0 ) then - read( buffer, *, iostat=ierr ) var - has_var = .true. - else - do idx = 1,nodata - if ( data(idx) .ne. ' ' ) then - read( data(idx), '(A)', iostat=ierr ) var - has_var = .true. - exit - endif - enddo - endif - - if ( ierr .ne. 0 ) then - write(*,*) 'Error reading variable - tag = ', trim(tag) - has_var = .false. - endif diff --git a/src/xml-fortran/write_xml_primitives.f90 b/src/xml-fortran/write_xml_primitives.f90 deleted file mode 100644 index 7d2cf90ba7..0000000000 --- a/src/xml-fortran/write_xml_primitives.f90 +++ /dev/null @@ -1,489 +0,0 @@ -! write_xml_prims.f90 - Write routines for primitive data -! -! $Id: write_xml_prims.f90,v 1.2 2007/12/27 05:13:59 arjenmarkus Exp $ -! -! Arjen Markus -! -! General information: -! This module is part of the XML-Fortran library. Its -! purpose is to write individual items to an XML -! file using the right tag. It is used by the code generated -! by the make_xml_reader program. -! -module write_xml_primitives - use xmlparse - implicit none - -! interface write_to_xml -! module procedure write_to_xml_integers -! module procedure write_to_xml_reals -! module procedure write_to_xml_doubles -! module procedure write_to_xml_logicals -! module procedure write_to_xml_words -! end interface - interface write_to_xml_word - module procedure write_to_xml_string - end interface - interface write_to_xml_line - module procedure write_to_xml_string - end interface - -contains - -! write_to_xml_integer -- -! Routine to write a single integer to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_integer( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - integer, intent(in) :: value - - character(len=100) :: indentation - - indentation = ' ' - write( info%lun, '(4a,i0,3a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>', value, '' - -end subroutine write_to_xml_integer - -! write_to_xml_integer_1dim -- -! Routine to write an array of integers to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_integer_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - integer, dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_integer( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_integer_1dim - -! write_to_xml_real -- -! Routine to write a single real value (single precision) to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_real( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real, intent(in) :: value - - character(len=100) :: indentation - character(len=12) :: buffer - - indentation = ' ' - write( buffer, '(1pg12.4)' ) value - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>', trim(adjustl(buffer)), '' - -end subroutine write_to_xml_real - -! write_to_xml_real_1dim -- -! Routine to write an array of reals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_real_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real, dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_real( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_real_1dim - -! write_to_xml_double -- -! Routine to write one real value (double precision) to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_double( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real(kind=kind(1.0d0)), intent(in) :: value - - character(len=100) :: indentation - character(len=16) :: buffer - - indentation = ' ' - write( buffer, '(1pg16.7)' ) value - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>', trim(adjustl(buffer)), '' - -end subroutine write_to_xml_double - -! write_to_xml_double_1dim -- -! Routine to write an array of double precision reals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_double_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real(kind=kind(1.0d00)), dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_double( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_double_1dim - -! write_to_xml_string -- -! Routine to write one string to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_string( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - character(len=*), intent(in) :: value - - character(len=100) :: indentation - - ! - ! NOTE: No guards against <, >, & and " yet! - ! NOTE: difference needed between words and lines? - ! - indentation = ' ' - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>', trim(value), '' - -end subroutine write_to_xml_string - -! write_to_xml_word_1dim -- -! Routine to write an array of single words to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_word_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - character(len=*), dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_string( info, tag, indent, values(i) ) - enddo -end subroutine write_to_xml_word_1dim - -! write_to_xml_string_1dim -- -! Routine to write an array of strings to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_string_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - character(len=*), dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_string( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_string_1dim - -! write_to_xml_logical -- -! Routine to write one logical to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_logical( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - logical, intent(in) :: value - - character(len=100) :: indentation - - indentation = ' ' - if ( value ) then - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>true' - else - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>false' - endif - -end subroutine write_to_xml_logical - -! write_to_xml_logical_1dim -- -! Routine to write an array of logicals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_logical_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - logical, dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_logical( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_logical_1dim - -! write_to_xml_integer_array -- -! Routine to write an array of integers to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_integer_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - integer, dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),10 - i2 = min( i + 9, size(array) ) - write( info%lun, '(a,10i12)' ) indentation(1:min(indent+4,100)), & - ( array(j) ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_integer_array - -! write_to_xml_real_array -- -! Routine to write an array of single precision reals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_real_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real, dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),10 - i2 = min( i + 9, size(array) ) - write( info%lun, '(a,10g12.4)' ) indentation(1:min(indent+4,100)), & - ( array(j) ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_real_array - -! write_to_xml_double_array -- -! Routine to write an array of double precision reals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_double_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real(kind=kind(1.0d0)), dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),5 - i2 = min( i + 4, size(array) ) - write( info%lun, '(a,5g20.7)' ) indentation(1:min(indent+4,100)), & - ( array(j) ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_double_array - -! write_to_xml_logical_array -- -! Routine to write an array of logicals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_logical_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - logical, dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),10 - i2 = min( i + 9, size(array) ) - write( info%lun, '(a,10a)' ) indentation(1:min(indent+4,100)), & - ( merge('true ', 'false ', array(j)) ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_logical_array - -! write_to_xml_word_array -- -! Routine to write an array of words to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_word_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - character(len=*), dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),10 - i2 = min( i + 9, size(array) ) - write( info%lun, '(a,20a)' ) indentation(1:min(indent+4,100)), & - ( trim(array(j)) , ' ' ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_word_array - -! write_to_xml_line_array -- -! Routine to write an array of lines to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_line_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - logical, dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array) - write( info%lun, '(a)' ) indentation(1:min(indent+4,100)), & - array(i) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_line_array - -end module write_xml_primitives diff --git a/src/xml-fortran/xml-fortran.LICENSE b/src/xml-fortran/xml-fortran.LICENSE deleted file mode 100644 index cd0ba2912e..0000000000 --- a/src/xml-fortran/xml-fortran.LICENSE +++ /dev/null @@ -1,37 +0,0 @@ -Note: The official xml-fortran repository on sourceforge - only lists the license as "BSD License". It -is assumed that this refers to the 3-clause BSD license ("modified" or "new") -that is shown below. - -The license for xml-fortran covers all source code in the xml-fortran/ -directory. - -================================================================================ - -Copyright (c) 2008 Arjen Markus -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of Arjen Markus nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL ARJEN MARKUS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/xml-fortran/xmlparse.f90 b/src/xml-fortran/xmlparse.f90 deleted file mode 100644 index eb522078a8..0000000000 --- a/src/xml-fortran/xmlparse.f90 +++ /dev/null @@ -1,1076 +0,0 @@ -!=============================================================================== -! XMLPARSE - Simple, limited XML parser in Fortran -! -! General information: -! The module reads XML files by: -! - Identifying the tag and all attributes and data belonging -! to the tag. -! - Returning to the calling subprogram to let it take care of -! the tag, attributes and data. -! - If the tag is actually an ending tag, then this is flagged -! too. -! - Handling all the data is left to the calling subprogram, -! the module merely facilitates in the parsing. -! -! Note: -! The module in its current version has a number of limitations: -! - It does not handle escape sequences (like >. to signify -! a ">" sign) -! - It does not handle tags with attributes that are spread -! over more than one line -! - The maximum length of a line is 1000 characters -! - It may report too many lines of data (empty lines) -! - No DOM support nor support for an object tree -! - It is probably not very robust in detecting malformed XML files -! -! Some questions: -! - What to do with leading blanks? -! -! Update - several ideas: -! - Introduce at least two options (via xml_options): -! - ignore_whitespace - remove leading blanks and leading and trailing -! empty lines from the PCDATA -! - no_data_truncation - consider truncation of data (more -! attributes or lines of character data than -! can be stored) a read error -! - Introduce convenience functions and subroutines: -! - xml_ok() - all is well, reading can continue -! - xml_data_trunc() - was there truncation of the data? -! - xml_find_attrib() - find an attribute by name -! -! Further ideas: -! - simple checking via a table: parent, tag, id, min, max -!=============================================================================== - -module xmlparse - - implicit none - - integer, parameter :: XML_BUFFER_LENGTH = 10000 - -!=============================================================================== -! XML_PARSE defines the data type that holds the parser information -!=============================================================================== - - type XML_PARSE - integer :: lun ! LU-number of the XML-file - integer :: level ! Indentation level (output) - integer :: lineno ! Line in file - logical :: ignore_whitespace ! Ignore leading blanks etc. - logical :: no_data_truncation ! Do not allow data truncation - logical :: too_many_attribs ! More attributes than could be stored? - logical :: too_many_data ! More lines of data than could be stored? - logical :: eof ! End of file? - logical :: error ! Invalid XML file or other error? - character(len=XML_BUFFER_LENGTH) :: line ! Buffer - end type XML_PARSE - -!=============================================================================== -! Global options -!=============================================================================== - - integer, parameter :: XML_STDOUT = -1 - integer, private :: report_lun_ = XML_STDOUT - logical, private :: report_errors_ = .false. - logical, private :: report_details_ = .false. - -!=============================================================================== -! Global data (the ampersand must come first) -!=============================================================================== - - character(len=10), dimension(2,3), save, private :: entities = & - reshape( (/ '& ', '&', & - '> ', '> ', & - '< ', '< ' /), (/2,3/) ) - -!=============================================================================== -! Auxiliary routines - private -!=============================================================================== - - private :: xml_compress_ - private :: xml_put_open_tag_ - private :: xml_put_element_ - private :: xml_put_close_tag_ - private :: xml_replace_entities_ - private :: xml_remove_tabs_ - -!=============================================================================== -! Interfaces to reporting routines -!=============================================================================== - - private :: xml_report_details_int_ - private :: xml_report_details_string_ - private :: xml_report_errors_int_ - private :: xml_report_errors_string_ - - interface xml_report_details - module procedure xml_report_details_int_ - module procedure xml_report_details_string_ - end interface - interface xml_report_errors - module procedure xml_report_errors_int_ - module procedure xml_report_errors_string_ - module procedure xml_report_errors_extern_ - end interface - -contains - -!=============================================================================== -! XML_REPORT_DETAILS_INT_ -- -! Routine to write a text with an integer value -! Arguments: -! text Text to be written -! int Integer value to be added -!=============================================================================== - -subroutine xml_report_details_int_( text, int ) - character(len=*), intent(in) :: text - integer, intent(in) :: int - - if ( report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), int - else - write(report_lun_,*) trim(text), int - endif - endif -end subroutine xml_report_details_int_ - -!=============================================================================== -! XML_REPORT_DETAILS_STRING_ -- -! Routine to write a text with a string value -! Arguments: -! text Text to be written -! string String to be added -!=============================================================================== - -subroutine xml_report_details_string_( text, string ) - character(len=*), intent(in) :: text - character(len=*), intent(in) :: string - - if ( report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), ' ', trim(string) - else - write(report_lun_,*) trim(text), ' ', trim(string) - endif - endif -end subroutine xml_report_details_string_ - -!=============================================================================== -! XML_REPORT_ERRORS_INT_ -- -! Routine to write an error message text with an integer value -! Arguments: -! text Text to be written -! int Integer value to be added -! lineno Line number in the file -!=============================================================================== - -subroutine xml_report_errors_int_( text, int, lineno ) - character(len=*), intent(in) :: text - integer, intent(in) :: int - integer, optional, intent(in) :: lineno - - if ( report_errors_ .or. report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), int - if ( present(lineno) ) then - write(*,*) ' At or near line', lineno - endif - else - write(report_lun_,*) trim(text), int - if ( present(lineno) ) then - write(report_lun_,*) ' At or near line', lineno - endif - endif - endif -end subroutine xml_report_errors_int_ - -!=============================================================================== -! XML_REPORT_ERRORS_STRING_ -- -! Routine to write an error message text with a string value -! Arguments: -! text Text to be written -! string String to be added -! lineno Line number in the file -!=============================================================================== - -subroutine xml_report_errors_string_( text, string, lineno ) - character(len=*), intent(in) :: text - character(len=*), intent(in) :: string - integer, optional, intent(in) :: lineno - - if ( report_errors_ .or. report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), ' ', trim(string) - if ( present(lineno) ) then - write(*,*) ' At or near line', lineno - endif - else - write(report_lun_,*) trim(text), ' ', trim(string) - if ( present(lineno) ) then - write(report_lun_,*) ' At or near line', lineno - endif - endif - endif -end subroutine xml_report_errors_string_ - -!=============================================================================== -! XML_REPORT_ERRORS_EXTERN_ -- -! Routine to write an error message text with a string value -! Arguments: -! info Structure holding information on the XML-file -! text Text to be written -! Note: -! This routine is meant for use by routines outside -! this module -!=============================================================================== - -subroutine xml_report_errors_extern_( info, text ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: text - - if ( report_errors_ .or. report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), ' - at or near line', info%lineno - else - write(report_lun_,*) trim(text), ' - at or near line', info%lineno - endif - end if - -end subroutine xml_report_errors_extern_ - -!=============================================================================== -! XML_OPEN -- -! Routine to open an XML file for reading or writing -! Arguments: -! info Structure holding information on the XML-file -! fname Name of the file -! mustread The file will be read (.true.) or written (.false.) -!=============================================================================== - -subroutine xml_open( info, fname, mustread ) - character(len=*), intent(in) :: fname - logical, intent(in) :: mustread - type(XML_PARSE), intent(out) :: info - - integer :: i - integer :: k - integer :: kend - integer :: ierr - logical :: opend - logical :: exists - - info%lun = 10 - info%ignore_whitespace = .false. - info%no_data_truncation = .false. - info%too_many_attribs = .false. - info%too_many_data = .false. - info%eof = .false. - info%error = .false. - info%level = -1 - info%lineno = 0 - - do i = 10,99 - inquire( unit = i, opened = opend ) - if ( .not. opend ) then - info%lun = i - inquire( file = fname, exist = exists ) - if ( .not. exists .and. mustread ) then - call xml_report_errors( 'XML_OPEN: file does not exist:', trim(fname)) - info%lun = -1 - info%error = .true. - else - open( unit = info%lun, file = fname ) - call xml_report_details( 'XML_OPEN: opened file ', trim(fname) ) - call xml_report_details( 'at LU-number: ', info%lun ) - endif - exit - endif - enddo - if ( .not. info%error .and. mustread ) then - k = 1 - do while ( k >= 1 ) - read( info%lun, '(a)', iostat = ierr ) info%line - - ! If we encounter a blank line, skip it and read the next line - if (len_trim(info%line) == 0) cycle - - call xml_remove_tabs_(info%line) - if ( ierr == 0 ) then - info%line = adjustl( info%line ) - k = index( info%line, ' appears on a single line! - ! - if ( k >= 1 ) then - kend = index( info%line, '?>' ) - if ( kend <= 0 ) then - call xml_report_errors( 'XML_OPEN: error reading file with LU-number: ', info%lun ) - call xml_report_errors( 'Line starting with ""', ' ' ) - info%error = .true. - exit - endif - endif - else - call xml_report_errors( 'XML_OPEN: error reading file with LU-number: ', info%lun ) - call xml_report_errors( 'Possibly no line starting with "' - endif -end subroutine xml_open - -!=============================================================================== -! XML_CLOSE -- -! Routine to close an XML file -! Arguments: -! info Structure holding information on the XML-file -!=============================================================================== - -subroutine xml_close( info ) - type(XML_PARSE), intent(inout) :: info - - close( info%lun ) - - ! - ! Only clean up the LU-number, so that the calling program - ! can examine the last condition - ! - call xml_report_details( 'XML_CLOSE: Closing file with LU-number ', info%lun ) - info%lun = -1 -end subroutine xml_close - -!=============================================================================== -! XML_GET -- -! Routine to get the next bit of information from an XML file -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -!=============================================================================== - -subroutine xml_get( info, tag, endtag, attribs, no_attribs, & - data, no_data ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(out) :: tag - logical, intent(out) :: endtag - character(len=*), intent(out), dimension(:,:) :: attribs - integer, intent(out) :: no_attribs - character(len=*), intent(out), dimension(:) :: data - integer, intent(out) :: no_data - - integer :: kspace - integer :: kend - integer :: kcend - integer :: keq - integer :: kfirst - integer :: ksecond - integer :: idxat - integer :: idxdat - integer :: ierr - logical :: close_bracket - logical :: comment_tag - character(len=XML_BUFFER_LENGTH) :: nextline - - ! - ! Initialise the output - ! - endtag = .false. - no_attribs = 0 - no_data = 0 - - info%too_many_attribs = .false. - info%too_many_data = .false. - - if ( info%lun < 0 ) then - call xml_report_details( 'XML_GET on closed file ', ' ' ) - return - endif - - ! - ! From the previous call or the call to xmlopen we have - ! the line that we need to parse already in memory: - ! - ! - comment_tag = .false. - close_bracket = .false. - kspace = index( info%line, ' ' ) - kend = index( info%line, '>' ) - kcend = index( info%line, '-->' ) - do while ( kend <= 0 ) - read( info%lun, '(a)', iostat = ierr ) nextline - call xml_remove_tabs_(nextline) - info%lineno = info%lineno + 1 - - if ( ierr == 0 ) then - info%line = trim(info%line) // ' ' // adjustl(nextline) - else - info%error = .true. - call xml_report_errors( 'XML_GET - end of tag not found ', & - '(buffer too small?)', info%lineno ) - call xml_close( info ) - return - endif - kend = index( info%line, '>' ) - enddo - if ( kend > kspace ) then - kend = kspace - else if (info%line(1:4) == '' ) then - endtag = .true. - tag = info%line(4:kend-1) - else if ( info%line(1:2) == '' ) - if ( keq > kend ) keq = 0 ! Guard against multiple tags - ! with attributes on one line - - ! - ! No attributes any more? - ! - if ( keq < 1 ) then - kend = index( info%line, '/>' ) - if ( kend >= 1 ) then - kend = kend + 1 ! To go beyond the ">" character - endtag = .true. - else - kend = index( info%line, '>' ) - if ( kend < 1 ) then - call xml_report_errors( 'XML_GET - wrong ending of tag ', & - trim(info%line), info%lineno ) - info%error = .true. ! Wrong ending of line! - call xml_close( info ) - return - else - close_bracket = .true. - endif - endif - if ( kend >= 1 ) then - info%line = adjustl( info%line(kend+1:) ) - endif - exit - endif - - idxat = idxat + 1 - if ( idxat <= size(attribs,2) ) then - no_attribs = idxat - attribs(1,idxat) = adjustl(info%line(1:keq-1)) ! Use adjustl() to avoid - ! multiple spaces, etc - info%line = adjustl( info%line(keq+1:) ) - - ! - ! We have almost found the start of the attribute's value - ! - kfirst = index( info%line, '"' ) - if ( kfirst < 1 ) then - call xml_report_errors( 'XML_GET - malformed attribute-value pair: ', & - trim(info%line), info%lineno ) - info%error = .true. ! Wrong form of attribute-value pair - call xml_close( info ) - return - endif - - ksecond = index( info%line(kfirst+1:), '"' ) + kfirst - if ( ksecond < 1 ) then - call xml_report_errors( 'XML_GET - malformed attribute-value pair: ', & - trim(info%line), info%lineno ) - info%error = .true. ! Wrong form of attribute-value pair - call xml_close( info ) - return - endif - - attribs(2,idxat) = info%line(kfirst+1:ksecond-1) - info%line = adjustl( info%line(ksecond+1:) ) - endif - - if ( idxat > size(attribs,2) ) then - call xml_report_errors( 'XML_GET - more attributes than could be stored: ', & - trim(info%line), info%lineno ) - info%too_many_attribs = .true. - info%line = ' ' - exit - endif - enddo - - ! - ! Now read the data associated with the current tag - ! - all the way to the next "<" character - ! - ! To do: reduce the number of data lines - empty ones - ! at the end should not count. - ! - do - if ( comment_tag ) then - kend = index( info%line, '-->' ) - else - kend = index( info%line, '<' ) - endif - idxdat = idxdat + 1 - if ( idxdat <= size(data) ) then - no_data = idxdat - if ( kend >= 1 ) then - data(idxdat) = info%line(1:kend-1) - info%line = info%line(kend:) - else - data(idxdat) = info%line - endif - else - call xml_report_errors( 'XML_GET - more data lines than could be stored: ', & - trim(info%line), info%lineno ) - info%too_many_data = .true. - exit - endif - - ! - ! No more data? Otherwise, read on - ! - if ( kend >= 1 ) then - exit - else - read( info%lun, '(a)', iostat = ierr ) info%line - call xml_remove_tabs_(info%line) - info%lineno = info%lineno + 1 - - if ( ierr < 0 ) then - call xml_report_details( 'XML_GET - end of file found - LU-number: ', & - info%lun ) - info%eof = .true. - elseif ( ierr > 0 ) then - call xml_report_errors( 'XML_GET - error reading file with LU-number ', & - info%lun, info%lineno ) - info%error = .true. - endif - if ( ierr /= 0 ) then - exit - endif - endif - enddo - - ! - ! Compress the data? - ! - if ( info%ignore_whitespace ) then - call xml_compress_( data, no_data ) - endif - - ! - ! Replace the entities, if any - ! - call xml_replace_entities_( data, no_data ) - - call xml_report_details( 'XML_GET - number of attributes: ', no_attribs ) - call xml_report_details( 'XML_GET - number of data lines: ', no_data ) - -end subroutine xml_get - -!=============================================================================== -! XML_PUT -- -! Routine to write a tag with the associated data to an XML file -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -! type Type of action: -! open - just the opening tag with attributes -! elem - complete element -! close - just the closing tag -!=============================================================================== - -subroutine xml_put(info, tag, attribs, no_attribs, & - data, no_data, type) - - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - character(len=*), intent(in), dimension(:,:) :: attribs - integer, intent(in) :: no_attribs - character(len=*), intent(in), dimension(:) :: data - integer, intent(in) :: no_data - character(len=*) :: type - - select case(type) - case('open') - call xml_put_open_tag_(info, tag, attribs, no_attribs) - case('elem') - call xml_put_element_(info, tag, attribs, no_attribs, & - data, no_data) - case('close') - call xml_put_close_tag_(info, tag) - end select - -end subroutine xml_put - -!=============================================================================== -! XML_PUT_OPEN_TAG_ -- -! Routine to write the opening tag with the attributes -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -!=============================================================================== - -subroutine xml_put_open_tag_(info, tag, attribs, no_attribs) - - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - character(len=*), intent(in), dimension(:,:) :: attribs - integer, intent(in) :: no_attribs - - integer :: i - character(len=300), parameter :: indent = ' ' - - write( info%lun, '(3a)', advance = 'no' ) & - indent(1:3*info%level), '<', adjustl(tag) - do i=1,no_attribs - if (attribs(2,i)/='') then - write( info%lun, '(5a)', advance = 'no' ) & - ' ',trim(attribs(1,i)),'="', trim(attribs(2,i)),'"' - endif - enddo - write( info%lun, '(a)' ) '>' - info%level = info%level + 1 - -end subroutine xml_put_open_tag_ - -!=============================================================================== -! XML_PUT_ELEMENT_ -- -! Routine to write the complete element -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -!=============================================================================== - -subroutine xml_put_element_(info, tag, attribs, no_attribs, & - data, no_data) - - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - character(len=*), intent(in), dimension(:,:) :: attribs - integer, intent(in) :: no_attribs - character(len=*), intent(in), dimension(:) :: data - integer, intent(in) :: no_data - - logical :: logic - character(len=1) :: aa - integer :: i, ii - - character(len=300), parameter :: indent = ' ' - - if ( (no_attribs==0 .and. no_data==0) ) then - return - else - logic = .true. - do ii = 1,no_attribs - logic = logic .and. (attribs(2,ii)=='') - enddo - do ii = 1,no_data - logic = logic .and. (data(ii)=='') - enddo - if ( logic ) then - return - else - write( info%lun, '(3a)', advance = 'no' ) & - indent(1:3*info%level), '<', adjustl(tag) - do i = 1,no_attribs - if (attribs(2,i)/='') then - write( info%lun, '(5a)', advance = 'no' ) & - ' ',trim(attribs(1,i)),'="', trim(attribs(2,i)),'"' - endif - enddo - if ( no_attribs>0 .and. no_data==0 ) then - aa='a' - elseif ( (no_attribs>0 .and. no_data>0) .or. & - (no_attribs==0 .and. no_data>0) ) then - aa='b' - else - write(*,*) no_attribs, no_data - endif - endif - endif - - select case(aa) - case('a') - write( info%lun, '(a)' ) '/>' - case('b') - write( info%lun, '(a)',advance='no' ) '>' - write( info%lun, '(2a)', advance='no') ( ' ', trim(data(i)), i=1,no_data ) - write( info%lun, '(4a)' ) ' ','' - end select - -end subroutine xml_put_element_ - -!=============================================================================== -! XML_PUT_CLOSE_TAG_ -- -! Routine to write the closing tag -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -!=============================================================================== - -subroutine xml_put_close_tag_(info, tag) - - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - - character(len=300), parameter :: indent = ' ' - - info%level = info%level - 1 - write(info%lun, '(4a)') indent(1:3*info%level), '' - -end subroutine xml_put_close_tag_ - -!=============================================================================== -! XML_COMPRESS_ -- -! Routine to remove empty lines from the character data -! Arguments: -! data Lines of character data found -! no_data (Nett) number of lines of character data -!=============================================================================== - -subroutine xml_compress_( data, no_data ) - character(len=*), intent(inout), dimension(:) :: data - integer, intent(inout) :: no_data - - integer :: i - integer :: j - logical :: empty - - j = 0 - empty = .true. - do i = 1,no_data - if ( len_trim(data(i)) /= 0 .or. .not. empty ) then - j = j + 1 - data(j) = adjustl(data(i)) - empty = .false. - endif - enddo - - no_data = j - - do i = no_data,1,-1 - if ( len_trim(data(i)) /= 0 ) then - exit - else - no_data = no_data - 1 - endif - enddo - -end subroutine xml_compress_ - -!=============================================================================== -! XML_REPLACE_ENTITIES_ -- -! Routine to replace entities such as > by their -! proper character representation -! Arguments: -! data Lines of character data found -! no_data (Nett) number of lines of character data -!=============================================================================== - -subroutine xml_replace_entities_( data, no_data ) - character(len=*), intent(inout), dimension(:) :: data - integer, intent(inout) :: no_data - - integer :: i - integer :: j - integer :: j2 - integer :: k - integer :: pos - logical :: found - - do i = 1,no_data - j = 1 - do - do k = 1,size(entities,2) - found = .false. - pos = index( data(i)(j:), trim(entities(2,k)) ) - if ( pos > 0 ) then - found = .true. - j = j + pos - 1 - j2 = j + len_trim(entities(2,k)) - data(i)(j:) = trim(entities(1,k)) // data(i)(j2:) - j = j2 - endif - enddo - if ( .not. found ) exit - enddo - enddo - -end subroutine xml_replace_entities_ - -!=============================================================================== -! XML_OPTIONS -- -! Routine to handle the parser options -! Arguments: -! info Structure holding information on the XML-file -! ignore_whitespace Ignore whitespace (leading blanks, empty lines) or not -! no_data_truncation Consider truncation of strings an error or not -! report_lun LU-number for reporting information -! report_errors Write messages about errors or not -! report_details Write messages about all kinds of actions or not -!=============================================================================== - -subroutine xml_options( info, ignore_whitespace, no_data_truncation, & - report_lun, report_errors, & - report_details ) - type(XML_PARSE), intent(inout) :: info - logical, intent(in), optional :: ignore_whitespace - logical, intent(in), optional :: no_data_truncation - - integer, intent(in), optional :: report_lun - logical, intent(in), optional :: report_errors - logical, intent(in), optional :: report_details - - if ( present(ignore_whitespace) ) then - info%ignore_whitespace = ignore_whitespace - endif - if ( present(no_data_truncation) ) then - info%no_data_truncation = no_data_truncation - endif - if ( present(report_lun) ) then - report_lun_ = report_lun - endif - if ( present(report_errors) ) then - report_errors_ = report_errors - endif - if ( present(report_details) ) then - report_details_ = report_details - endif -end subroutine xml_options - -!=============================================================================== -! XML_OK -- -! Function that returns whether all was okay or not -! Arguments: -! info Structure holding information on the XML-file -! Returns: -! .true. if there was no error, .false. otherwise -!=============================================================================== - -logical function xml_ok( info ) - type(XML_PARSE), intent(in) :: info - - xml_ok = info%eof .or. info%error .or. & - ( info%no_data_truncation .and. & - ( info%too_many_attribs .or. info%too_many_data ) ) - xml_ok = .not. xml_ok -end function xml_ok - -!=============================================================================== -! XML_ERROR -- -! Function that returns whether there was an error -! Arguments: -! info Structure holding information on the XML-file -! Returns: -! .true. if there was an error, .false. if there was none -!=============================================================================== - -logical function xml_error( info ) - type(XML_PARSE), intent(in) :: info - - xml_error = info%error .or. & - ( info%no_data_truncation .and. & - ( info%too_many_attribs .or. info%too_many_data ) ) -end function xml_error - -!=============================================================================== -! XML_DATA_TRUNC -- -! Function that returns whether data were truncated or not -! Arguments: -! info Structure holding information on the XML-file -! Returns: -! .true. if data were truncated, .false. otherwise -!=============================================================================== - -logical function xml_data_trunc( info ) - type(XML_PARSE), intent(in) :: info - - xml_data_trunc = info%too_many_attribs .or. info%too_many_data -end function xml_data_trunc - -!=============================================================================== -! XML_FIND_ATTRIB -!=============================================================================== - -integer function xml_find_attrib( attribs, no_attribs, name, value ) - character(len=*), dimension(:,:) :: attribs - integer :: no_attribs - character(len=*) :: name - character(len=*) :: value - - integer :: i - - xml_find_attrib = -1 - do i = 1,no_attribs - if ( name == attribs(1,i) ) then - value = attribs(2,i) - xml_find_attrib = i - exit - endif - enddo - -end function xml_find_attrib - -!=============================================================================== -! XML_PROCESS -- -! Routine to read the XML file as a whole and distribute processing -! the contents over three user-defined subroutines -! Arguments: -! filename Name of the file to process -! attribs Array for holding the attributes -! data Array for holding the character data -! startfunc Subroutine to handle the start of elements -! datafunc Subroutine to handle the character data -! endfunc Subroutine to handle the end of elements -! error Indicates if there was an error or not -! Note: -! The routine is declared recursive to allow inclusion of XML files -! (common with XSD schemas). This extends to the auxiliary routines. -!=============================================================================== - -recursive & -subroutine xml_process( filename, attribs, data, startfunc, datafunc, endfunc, lunrep, error ) - character(len=*) :: filename - character(len=*), dimension(:,:) :: attribs - character(len=*), dimension(:) :: data - integer :: lunrep - logical :: error - - interface - recursive subroutine startfunc( tag, attribs, error ) - character(len=*) :: tag - character(len=*), dimension(:,:) :: attribs - logical :: error - end subroutine - end interface - - interface - recursive subroutine datafunc( tag, data, error ) - character(len=*) :: tag - character(len=*), dimension(:) :: data - logical :: error - end subroutine - end interface - - interface - recursive subroutine endfunc( tag, error ) - character(len=*) :: tag - logical :: error - end subroutine - end interface - - type(XML_PARSE) :: info - character(len=80) :: tag - logical :: endtag - integer :: noattribs - integer :: nodata - - call xml_options( info, report_lun = lunrep, report_details = .false. ) - call xml_open( info, filename, .true. ) - - error = .false. - do - call xml_get( info, tag, endtag, attribs, noattribs, data, nodata ) - if ( .not. xml_ok(info) ) then - exit - endif - - if ( xml_error(info) ) then - write(lunrep,*) 'Error reading XML file!' - error = .true. - exit - endif - - if ( .not. endtag .or. noattribs /= 0 ) then - call startfunc( tag, attribs(:,1:noattribs), error ) - if ( error ) exit - - call datafunc( tag, data(1:nodata), error ) - if ( error ) exit - endif - - if ( endtag ) then - call endfunc( tag, error ) - if ( error ) exit - endif - enddo - call xml_close( info ) -end subroutine xml_process - -!=============================================================================== -! XML_REMOVE_TABS_ -- -! Routine to change any horizontal tab characters to spaces when reading a -! new line of data -! Arguments: -! line Line of character data to modify -!=============================================================================== - -subroutine xml_remove_tabs_(line) - character(len=*), intent(inout) :: line - - integer :: i - - do i = 1, len_trim(line) - if (line(i:i) == achar(9)) then - line(i:i) = ' ' - end if - end do - -end subroutine xml_remove_tabs_ - -end module xmlparse diff --git a/src/xml-fortran/xmlreader.f90 b/src/xml-fortran/xmlreader.f90 deleted file mode 100644 index 390324b637..0000000000 --- a/src/xml-fortran/xmlreader.f90 +++ /dev/null @@ -1,1241 +0,0 @@ -!=============================================================================== -! XMLREADER: -! Read an XML-file that contains a template for the data and the XML files -! that will be used in a program and generate a module with reading routines. -! -! TODO: -! - Private routines! -! - Error for unknown data types -! - Length for character items -!=============================================================================== - -program xmlreader - use XMLPARSE - implicit none - - character(len=60) :: fname - type(XML_PARSE) :: info - - character(len=80) :: tag - character(len=80) :: starttag - logical :: endtag - character(len=80), dimension(1:2,1:20) :: attribs - integer :: noattribs - character(len=1000), dimension(1:1000) :: data - integer :: nodata - integer :: i - integer :: j - integer, parameter :: notmps = 6 ! Number of temporary files needed! - integer :: ludef - integer :: ludeflt - integer :: luinit - integer :: lusubs - integer :: luprolog - integer :: lustart - integer :: luloop - integer :: luend - integer :: lumain - integer :: luwrite - integer :: luwritp - integer :: luwrv - integer :: argc - logical :: prolog_written - logical :: comp - logical :: error - logical :: begin_loop = .true. - logical :: begin_main_loop = .true. - logical :: begin_component = .false. - logical :: strict - logical :: global_type - logical :: dyn_strings - - character(len=32) :: root_name - character(len=32) :: global_name - character(len=32) :: typename - character(len=32), dimension(1:20) :: placeholder - integer :: no_placeholders - - character(len=50) :: declare - character(len=50), dimension(:,:), pointer :: types - character(len=50), dimension(:,:), pointer :: new_types - - ! TODO: arrays of integers etc, in addition to integer-arrays - see: word - integer, parameter :: notypes_predefined = 27 - character(len=50), dimension(1:4,1:notypes_predefined) :: predefined_types - integer :: notypes = notypes_predefined - data ((predefined_types(i,j) , i=1,4), j=1,notypes_predefined ) / & -'logical' ,' logical' , 'read_xml_logical', 'write_to_xml_logical', & -'logical-1dim' ,' logical, dimension(:), pointer' , 'read_xml_logical_1dim', 'write_to_xml_logical_1dim', & -'logical-array' ,' logical, dimension(:), pointer' , 'read_xml_logical_array', 'write_to_xml_logical_array', & -'logical-shape' ,' logical, dimension(SHAPE)' , 'read_xml_logical_array', 'write_to_xml_logical_array', & -'integer' ,' integer' , 'read_xml_integer', 'write_to_xml_integer', & -'integer-1dim' ,' integer, dimension(:), pointer' , 'read_xml_integer_1dim', 'write_to_xml_integer_1dim', & -'integer-array' ,' integer, dimension(:), pointer' , 'read_xml_integer_array', 'write_to_xml_integer_array', & -'integer-shape' ,' integer, dimension(SHAPE)' , 'read_xml_integer_array', 'write_to_xml_integer_array', & -'real' ,' real' , 'read_xml_real' , 'write_to_xml_real' , & -'real-1dim' ,' real, dimension(:), pointer' , 'read_xml_real_1dim', 'write_to_xml_real_1dim', & -'real-array' ,' real, dimension(:), pointer' , 'read_xml_real_array', 'write_to_xml_real_array', & -'real-shape' ,' real, dimension(SHAPE)' , 'read_xml_real_array', 'write_to_xml_real_array', & -'double' ,' real(kind=kind(1.0d0))' , 'read_xml_double', 'write_to_xml_double', & -'double-1dim' ,' real(kind=kind(1.0d0)), dimension(:), pointer' , & - 'read_xml_double_1dim', 'write_to_xml_double_1dim', & -'double-array' ,' real(kind=kind(1.0d0)), dimension(:), pointer' , & - 'read_xml_double_array', 'write_to_xml_double_array', & -'double-shape' ,' real(kind=kind(1.0d0)), dimension(SHAPE)' , & - 'read_xml_double_array', 'write_to_xml_double_array', & -'word' ,' character(len=?)' , 'read_xml_word', 'write_to_xml_word', & -'word-1dim' ,' character(len=?), dimension(:), pointer' , 'read_xml_word_1dim', 'write_to_xml_word_1dim', & -'word-array' ,' character(len=?), dimension(:), pointer' , 'read_xml_word_array', 'write_to_xml_word_array', & -'word-shape' ,' character(len=?), dimension(SHAPE)' , 'read_xml_word_array', 'write_to_xml_word_array', & -'line' ,' character(len=?)' , 'read_xml_line', 'write_to_xml_line', & -'line-1dim' ,' character(len=?), dimension(:), pointer' , 'read_xml_line_1dim', 'write_to_xml_line_1dim', & -'line-array' ,' character(len=?), dimension(:), pointer' , 'read_xml_line_array', 'write_to_xml_line_array', & -'line-shape' ,' character(len=?), dimension(SHAPE)' , 'read_xml_line_array', 'write_to_xml_line_array', & -'character' ,' character(len=?)' , 'read_xml_line', 'write_to_xml_line', & -'character-array',' character(len=?), dimension(:), pointer' , 'read_xml_line_array', 'write_to_xml_line_array', & -'character-shape',' character(len=?), dimension(SHAPE)' , 'read_xml_line_array', 'write_to_xml_line_array' / - - allocate( types(1:4,1:notypes) ) - types = predefined_types(:,1:notypes) - - ! - ! Read the global options file, if present - ! - strict = .false. - global_type = .false. - dyn_strings = .true. - call get_global_options( attribs, noattribs, strict, global_type, global_name, & - root_name, dyn_strings ) - - ! - ! Open the input file and read the name of the template. - ! Load the template into a tree and then generate it all - ! in stages - ! - argc = COMMAND_ARGUMENT_COUNT() - if (argc > 0) then - call GET_COMMAND_ARGUMENT(1, fname) - else - open(UNIT=10, FILE='xmlreader.inp') - read(UNIT=10, FMT='(a)') fname - close(UNIT=10) - end if - - open( 20, file = 'xmlreader.out' ) - call xml_options( info, report_lun = 20, report_details = .true. ) - - prolog_written = .false. - ! - ! Set the defaults - ! - global_type = .false. - global_name = fname - root_name = fname - - ludef = 21 - lusubs = 22 - luinit = 23 - luwritp = 24 - luwrv = luwritp - open( ludef, file = trim(fname)//'.f90' ) - open( lusubs, status = 'scratch' ) - open( luinit, status = 'scratch' ) - open( luwritp, status = 'scratch' ) - call open_tmp_files( 31 ) - - lumain = luloop - - ! Read the template file and act as we go along: - ! - write the declarations - ! - write the main reading routine - ! - write the individual reading routines - ! - the root element is needed because of the definition of XML files, - ! but we ignore it. - ! - call xml_open( info, trim(fname)//'.xml', .true. ) - - error = .false. - comp = .false. - no_placeholders = 0 - - call xml_get( info, starttag, endtag, attribs, noattribs, data, nodata ) - - do - call xml_get( info, tag, endtag, attribs, noattribs, data, nodata ) - write(20,*) 'tag: ',tag - write(20,*) 'attribs: ',noattribs - if ( noattribs > 0 ) then - write(20,'(4a)') ( ' ', trim(attribs(1,i)), ' = ', trim(attribs(2,i)), i=1,noattribs ) - endif - write(20,*) 'data: ',nodata - if ( nodata > 0 ) then - write(20,'(3a)') ( ' >', trim(data(i)), '<', i=1,nodata ) - endif - if ( xml_error(info) ) then - write(*,*) 'Error reading template file!' - !stop - exit - endif - - ! - ! When encountering the endtag, then close the - ! current definition - ! - if ( endtag .and. noattribs == 0 ) then - select case ( tag ) - case ( 'typedef' ) - call close_typedef( begin_component ) - case ( 'placeholder' ) - !if ( comp ) then - ! call close_placeholder - !endif - !comp = .false. - call close_placeholder - luwrv = luwritp - case default - ! - ! Have we found the end of the definition? - ! - if ( tag == starttag ) then - exit - endif - end select - - if ( xml_ok(info) ) then - cycle - else - exit - endif - - endif - - ! - ! Opening tags: dispatch on the actual tag - ! - select case( tag ) - case( 'options' ) - call set_options( attribs, noattribs, strict, global_type, & - global_name, root_name, dyn_strings ) - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - else - write(20,*) 'Options element should be the first child of the root element',& - 'Otherwise it has no effect!' - endif - - if ( strict ) then - write( lumain, '(a)' ) ' strict_ = .true.' - else - write( lumain, '(a)' ) ' strict_ = .false.' - endif - - case( 'comment', '!--' ) - ! Do nothing - - case( 'placeholder' ) - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - endif - if ( begin_loop .or. begin_main_loop ) then - begin_main_loop = .false. - call add_begin_loop( .true., .false. ) - endif - call add_placeholder(dyn_strings) - begin_component = .false. - luwrv = luwrite - - case( 'typedef' ) - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - endif - call add_typedef(dyn_strings) - - case( 'variable' ) - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - endif - if ( begin_loop .or. begin_main_loop ) then - begin_main_loop = .false. - call add_begin_loop( .true., begin_component ) - endif - call add_variable( component=comp ) - - case( 'component' ) - ! - ! Components of derived types are treated in much the - ! same way as ordinary variables - with one syntactic - ! difference - ! - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - endif - if ( begin_loop ) then - call add_begin_loop( .true., .true. ) - endif - call add_variable( component=.true. ) - begin_component = .true. - - case default - write(20,*) 'Unknown tag: ',trim(tag) - write(20,*) '-- terminating the program!' - stop - end select - end do - - ! - ! Now finish it all - ! - write( luend, '(a)' ) & - & ' if ( present(errout) ) errout = error', & - & ' call xml_close(info)', & - & 'end subroutine', & - & ' ' - write( luwritp, '(a)' ) & - & ' write(info%lun,''(a)'') ''''', & - & ' call xml_close(info)', & - & 'end subroutine', & - & ' ' - - call append_files( luprolog ) - call merge_files - - write( ludef, '(/,a)' ) & - & 'end subroutine', & - & 'end module' - - if ( error ) then - write(*,*) 'Errors found in the definition - please check!' - endif - stop -contains - -!=============================================================================== -! GET_GLOBAL_OPTIONS -- -! Routine to get the global options from the configuration file -! Arguments: -! strict Option to make the parser check for unknown tags -! global_type Option to generate an overall derived type -! global_name Name of that overall derived type (if requested) -! root_name Name of the root element of the XML file -! dyn_strings Whether to use dynamic strings or not -!=============================================================================== - -subroutine get_global_options( attribs, noattribs, strict, global_type, global_name, & - root_name, dyn_strings ) - character(len=*), dimension(:,:), intent(inout) :: attribs - integer, intent(inout) :: noattribs - logical, intent(inout) :: strict - logical, intent(inout) :: global_type - character(len=*), intent(inout) :: global_name - character(len=*), intent(inout) :: root_name - logical, intent(inout) :: dyn_strings - - character(len=20) :: tag - character(len=20), dimension(1) :: data - integer :: nodata - logical :: exists - logical :: endtag - type(XML_PARSE) :: info - - inquire( file = 'xmlreader.conf', exist = exists ) - if ( exists ) then - call xml_open( info, 'xmlreader.conf', .true. ) - do while ( xml_ok(info) ) - call xml_get( info, tag, endtag, attribs, noattribs, data, nodata ) - if ( tag == 'xmlreader' ) then - call set_options( attribs, noattribs, strict, global_type, & - global_name, root_name, dyn_strings ) - endif - enddo - call xml_close( info ) - endif -end subroutine get_global_options - -!=============================================================================== -! SET_OPTIONS -- -! Routine to set the options that influence the parser -! Arguments: -! attribs List of attributes -! noattribs Number of attributes -! strict Option to make the parser check for unknown tags -! global_type Option to generate an overall derived type -! global_name Name of that overall derived type (if requested) -! root_name Name of the root element of the XML file -! dyn_strings Whether to use dynamic strings or not -!=============================================================================== - -subroutine set_options( attribs, noattribs, strict, global_type, global_name, root_name, dyn_strings ) - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - logical, intent(inout) :: strict - logical, intent(inout) :: global_type - character(len=*), intent(inout) :: global_name - character(len=*), intent(inout) :: root_name - logical, intent(inout) :: dyn_strings - - integer :: i - - do i = 1,noattribs - select case (attribs(1,i)) - case ('strict') - if ( attribs(2,i) == 'yes' ) then - strict = .true. - else - strict = .false. - endif - case ('globaltype') - if ( attribs(2,i) == 'yes' ) then - global_type = .true. - else - global_type = .false. - endif - case ('globalname') - global_name = attribs(2,i) - case ('rootname') - root_name = attribs(2,i) - case ('dynamicstrings') - if ( attribs(2,i) == 'yes' ) then - dyn_strings = .true. - else - dyn_strings = .false. - endif - case default - write(20,*) 'Unknown option: ',trim(attribs(1,i)), ' - ignored' - end select - enddo -end subroutine set_options - -!=============================================================================== -! OPEN_TMP_FILES -- -! Routine to open the temporary files -! Arguments: -! lufirst First LU-number to use -!=============================================================================== - -subroutine open_tmp_files( lufirst ) - integer, intent(in) :: lufirst - - luprolog = lufirst - lustart = lufirst + 1 - luloop = lufirst + 2 - luend = lufirst + 3 - ludeflt = lufirst + 4 - luwrite = lufirst + 5 - open( luprolog, status = 'scratch' ) - open( lustart, status = 'scratch' ) - open( luloop, status = 'scratch' ) - open( luend, status = 'scratch' ) - open( ludeflt, status = 'scratch' ) - open( luwrite, status = 'scratch' ) -end subroutine open_tmp_files - -!=============================================================================== -! CLOSE_TMP_FILES -- -! Routine to close the temporary files -! Arguments: -! None -!=============================================================================== - -subroutine close_tmp_files - close( luprolog ) - close( lustart ) - close( luloop ) - close( luend ) - close( ludeflt ) - close( luwrite ) - - luprolog = luprolog - notmps - lustart = lustart - notmps - luloop = luloop - notmps - luend = luend - notmps - ludeflt = ludeflt - notmps - luwrite = luwrite - notmps -end subroutine close_tmp_files - -!=============================================================================== -! APPEND_TMP_FILES -- -! Routine to append the contents of the temporary files -! Arguments: -! lufirst First LU-number to use -!=============================================================================== - -subroutine append_files( lufirst ) - integer, intent(in) :: lufirst - - integer :: lu - integer :: io - character(len=120) :: line - - ! - ! If we have not written a subroutine yet, then - ! now is the time to close the overall initialisation - ! routine - ! -- no longer needed - ! - !if ( .not. contains ) then - ! write( lusubs, '(a,/)' ) 'end subroutine' - ! contains = .true. - !endif - - ! - ! Copy the contents of the scratch files - ! - do lu = lufirst,lufirst+notmps-1 - rewind( lu ) - do - read( lu, '(a)', iostat=io ) line - if ( io /= 0 ) exit - write( lusubs, '(a)' ) trim(line) - enddo - rewind( lu ) - enddo -end subroutine append_files - -!=============================================================================== -! MERGE_FILES -- -! Routine to merge all temporary files into the definite file -! Arguments: -! None -!=============================================================================== - -subroutine merge_files - - integer :: io - character(len=120) :: line - - ! - ! Copy the contents of the "subroutines" file - ! - rewind( lusubs ) - do - read( lusubs, '(a)', iostat=io ) line - if ( io /= 0 ) exit - write( ludef, '(a)' ) trim(line) - enddo - - ! - ! Copy the contents of the "write xml file subroutine" file - ! - rewind( luwritp ) - do - read( luwritp, '(a)', iostat=io ) line - if ( io /= 0 ) exit - write( ludef, '(a)' ) trim(line) - enddo - - ! - ! Copy the contents of the "initialisation subroutine" file - ! - rewind( luinit ) - do - read( luinit, '(a)', iostat=io ) line - if ( io /= 0 ) exit - write( ludef, '(a)' ) trim(line) - enddo -end subroutine merge_files - -!=============================================================================== -! WRITE_PROLOG -- -! Routine to write the beginning of the module -! Arguments: -! None -!=============================================================================== - -subroutine write_prolog - write( ludef, '(a)' ) & - & 'module xml_data_' // trim(fname), & - & ' use READ_XML_PRIMITIVES', & - & ' use WRITE_XML_PRIMITIVES', & - & ' use XMLPARSE', & - & ' implicit none', & - & ' save', & - & ' integer, private :: lurep_', & - & ' logical, private :: strict_' - - write( luprolog, '(a)' ) & - & 'subroutine read_xml_file_'//trim(fname)//'(fname, lurep, errout)' , & - & ' character(len=*), intent(in) :: fname' , & - & ' integer, intent(in), optional :: lurep' , & - & ' logical, intent(out), optional :: errout' , & - & ' ' , & - & ' type(XML_PARSE) :: info' , & - & ' logical :: error' , & - & ' character(len=80) :: tag' , & - & ' character(len=80) :: starttag' , & - & ' logical :: endtag' , & - & ' character(len=250), dimension(1:2,1:20) :: attribs' , & - & ' integer :: noattribs' , & - & ' character(len=1000), dimension(1:1000) :: data' , & - & ' integer :: nodata' - - write( lusubs, '(a)' ) & - & 'contains' - write( luinit, '(a)' ) & - & 'subroutine init_xml_file_'//trim(fname) - - write( luwritp, '(a)' ) & - & 'subroutine write_xml_file_'//trim(fname)//'(fname, lurep)' , & - & ' character(len=*), intent(in) :: fname' , & - & ' integer, intent(in), optional :: lurep' , & - & ' ' , & - & ' type(XML_PARSE) :: info' , & - & ' integer :: indent = 0', & - & ' ' , & - & ' call xml_open( info, fname, .false. )' , & - & ' call xml_options( info, report_errors=.true.)' , & - & ' if ( present(lurep) ) then' , & - & ' call xml_options( info, report_errors=.true.)' , & - & ' endif' , & - & ' write(info%lun,''(a)'') &' , & - & ' ''<' // trim(root_name) // '>''' - - write( lumain, '(a)' ) & - & ' ', & - & ' call init_xml_file_'//trim(fname), & - & ' call xml_open( info, fname, .true. )', & - & ' call xml_options( info, report_errors=.false., ignore_whitespace=.true.)', & - & ' lurep_ = 0', & - & ' if ( present(lurep) ) then', & - & ' lurep_ = lurep', & - & ' call xml_options( info, report_lun=lurep )', & - & ' endif', & - & ' do', & - & ' call xml_get( info, starttag, endtag, attribs, noattribs, &', & - & ' data, nodata)', & - & ' if ( starttag /= ''!--'' ) exit', & - & ' enddo', & - & ' if ( starttag /= "' // trim(root_name) // '" ) then', & - & ' call xml_report_errors( info, &', & - & ' ''XML-file should have root element "' // trim(root_name) // '"'')', & - & ' error = .true.', & - & ' call xml_close(info)', & - & ' return', & - & ' endif' - - call add_end_loop -end subroutine write_prolog - -!=============================================================================== -! ADD_BEGIN_LOOP -- -! Routine to write the start of the reading loop -! Arguments: -! checktag Whether code for checking the tag is required -! component Whether this is an ordinary variable or a component -! in a derived type -!=============================================================================== - -subroutine add_begin_loop( checktag, component ) - logical :: checktag - logical :: component - - if ( component ) then - write( luloop, '(a)' ) & - & ' call init_xml_type_'//trim(typename)//'(dvar)', & - & ' has_dvar = .true.' - endif - - begin_loop = .false. - - if ( component ) then - write( luloop, '(a)' ) & - & ' error = .false.' ,& - & ' att_ = 0' ,& - & ' noatt_ = noattribs+1' ,& - & ' endtag_org = endtag' ,& - & ' do', & - & ' if ( nodata /= 0 ) then' ,& - & ' noattribs = 0' ,& - & ' tag = starttag' ,& - & ' elseif ( att_ < noatt_ .and. noatt_ > 1 ) then' ,& - & ' att_ = att_ + 1' ,& - & ' if ( att_ <= noatt_-1 ) then' ,& - & ' tag = attribs(1,att_)' ,& - & ' data(1) = attribs(2,att_)' ,& - & ' noattribs = 0' ,& - & ' nodata = 1' ,& - & ' endtag = .false.' ,& - & ' else' ,& - & ' tag = starttag' ,& - & ' noattribs = 0' ,& - & ' nodata = 0' ,& - & ' endtag = .true.' ,& - & ' cycle' ,& - & ' endif' ,& - & ' else', & - & ' if ( endtag_org ) then', & - & ' return', & - & ' else', & - & ' call xml_get( info, tag, endtag, attribs, noattribs, data, nodata )' ,& - & ' if ( xml_error(info) ) then' ,& - & ' write(lurep_,*) ''Error reading input file!''',& - & ' error = .true.' ,& - & ' return' ,& - & ' endif' ,& - & ' endif' ,& - & ' endif' - else - write( luloop, '(a)' ) & - & ' error = .false.' ,& - & ' do', & - & ' call xml_get( info, tag, endtag, attribs, noattribs, data, nodata )' ,& - & ' if ( xml_error(info) ) then' ,& - & ' write(lurep_,*) ''Error reading input file!''' ,& - & ' error = .true.' ,& - & ' return' ,& - & ' endif' - endif - if ( checktag ) then - write( luloop, '(a)' ) & - & ' if ( endtag .and. tag == starttag ) then' ,& - & ' exit' ,& - & ' endif' - endif - write( luloop, '(a)' ) & - & ' if ( endtag .and. noattribs == 0 ) then' ,& - & ' if ( xml_ok(info) ) then' ,& - & ' cycle' ,& - & ' else' ,& - & ' exit' ,& - & ' endif' ,& - & ' endif' ,& - & ' select case( tag )' -end subroutine add_begin_loop - -!=============================================================================== -! ADD_END_LOOP -- -! Routine to write the end of the reading loop -! Arguments: -! None -!=============================================================================== - -subroutine add_end_loop - - write( luend, '(a)' ) & - & ' case (''comment'', ''!--'')' ,& - & ' ! Simply ignore', & - & ' case default' ,& - & ' if ( strict_ ) then', & - & ' error = .true.', & - & ' call xml_report_errors( info, &', & - & ' ''Unknown or wrongly placed tag: '' // trim(tag))',& - & ' endif' - - write( luend, '(a)' ) & - & ' end select' ,& - & ' nodata = 0' ,& - & ' if ( .not. xml_ok(info) ) exit' , & - & ' end do' -end subroutine add_end_loop - -!=============================================================================== -! ADD_VARIABLE -- -! Routine to write the definition of variables or components of -! derived types -! Arguments: -! component Whether this is an ordinary variable or a component -! in a derived type -!=============================================================================== - -subroutine add_variable( component ) - logical :: component - - integer :: idx1 - integer :: idx2 - integer :: idx3 - integer :: idx4 - integer :: idx5 - integer :: idx6 - integer :: idx7 - integer :: k - integer :: vdim - - character(len=32) :: varname - character(len=40) :: varcomp - character(len=40) :: varshape - character(len=100) :: vardefault - character(len=32) :: vartype - character(len=32) :: vartag - character(len=10) :: dim - character(len=10) :: strlength - character(len=32) :: initptr - - idx1 = xml_find_attrib( attribs, noattribs, 'name', varname ) - idx2 = xml_find_attrib( attribs, noattribs, 'type', vartype ) - strlength = "--" - idx5 = xml_find_attrib( attribs, noattribs, 'length', strlength ) - if ( idx1 <= 0 ) then - write( 20, * ) 'Variable/component found which has no name' - error = .true. - endif - if ( idx2 <= 0 ) then - write( 20, * ) 'Variable/component found which has no type - ',trim(varname) - error = .true. - else - dim = '--' - idx7 = xml_find_attrib( attribs, noattribs, 'dimension', dim ) - idx6 = xml_find_attrib( attribs, noattribs, 'shape', varshape ) - if ( idx7 >= 1 ) then - if ( dim == '1' ) then - idx3 = xml_find_attrib( types, notypes, vartype, declare ) - if ( idx3 > notypes_predefined ) then - vartype = trim(vartype) // '-array' - else - vartype = trim(vartype) // '-1dim' - endif - else - error = .true. - write(20,*) 'Dimension not supported: ',dim - endif - endif - if ( idx6 >= 1 ) then - vartype = trim(vartype) // '-shape' - vdim = 1 - if ( index(varshape, ',') > 0 ) then - vdim = 2 - endif - endif - - idx3 = xml_find_attrib( types, notypes, vartype, declare ) - if ( idx3 <= 0 ) then - write( 20, * ) & - 'Variable/component with unknown type - ',trim(varname) - error = .true. - endif - endif - - idx4 = xml_find_attrib( attribs, noattribs, 'default', vardefault ) - - if ( component ) then - varcomp = 'dvar%'//varname - else - varcomp = varname - endif - - idx1 = xml_find_attrib( attribs, noattribs, 'tag', vartag ) - if ( idx1 < 1 ) then - vartag = varname - endif - - if ( .not. error ) then - if ( index( declare, "pointer" ) > 0 ) then - initptr = " => null()" - else - initptr = "" - endif - - k = index( declare, 'SHAPE' ) - if ( k > 0 ) then - declare = declare(1:k-1) // trim(varshape) // declare(k+5:) - endif - - if ( index( declare, "?" ) <= 0 ) then - write( ludef, '(4a)' ) declare, ' :: ', trim(varname), trim(initptr) - else - if ( strlength == "--" ) then - strlength = "1" ! Hm, error is better? - endif - idx5 = index( declare, "?" ) - write( ludef, '(6a)' ) declare(1:idx5-1), trim(strlength), declare(idx5+1:), & - ' :: ', trim(varname), trim(initptr) - endif - - if ( idx6 > 0 ) then - k = index( types(2,idx3-1), '?' ) - if ( k <= 0 ) then - write( luprolog, '(3a)' ) types(2,idx3-1), ' :: ', 'p_'//trim(varname) - else - write( luprolog, '(6a)' ) types(2,idx3-1)(1:k-1), trim(strlength), & - types(2,idx3-1)(k+1:), ' :: ', 'p_'//trim(varname) - endif - endif - write( luprolog, '(3a)' ) types(2,1), ' :: ', 'has_'//trim(varname) - write( lustart, '(3a)' ) ' has_', varname, ' = .false.' - if ( dim /= '--' ) then - write( lustart, '(3a)' ) ' allocate(' // trim(varcomp), '(0))' - endif - write( luloop, '(a)' ) ' case('''//trim(vartag)//''')' - - if ( idx6 <= 0 ) then - write( luloop, '(a)' ) & - &' call '//trim(types(3,idx3))//'( &', & - &' info, tag, endtag, attribs, noattribs, data, nodata, &',& - &' ' // trim(varcomp) // ', has_'//trim(varname) // ' )' - else - write( luloop, '(a)' ) & - &' call '//trim(types(3,idx3))//'( &', & - &' info, tag, endtag, attribs, noattribs, data, nodata, &',& - &' p_' // trim(varname) // ', has_'//trim(varname) // ' )',& - &' if ( has_'//trim(varname) // ') then' - if ( vdim == 1 ) then - write( luloop, '(a)' ) & - &' if ( size('//trim(varcomp)//') <= size(p_'//trim(varname)//') ) then', & - &' '//trim(varcomp) // ' = p_'//trim(varname)//'(1:size('//trim(varcomp)//'))', & - &' else', & - &' '//trim(varcomp) // '(1:size(p_'//trim(varname)//')) = p_'//trim(varname), & - &' endif' - else - write( luloop, '(a)' ) & - &' if ( size(p_'//trim(varname)//') >= size('//trim(varcomp)//') ) then',& - &' '//trim(varcomp)//' = reshape(p_'//trim(varname)//', shape('//trim(varcomp)//'))',& - &' else',& - &' has_'//trim(varname)//' = .false.',& - &' call xml_report_errors(info, ''Incorrect number of values for '//trim(varname)//''')', & - &' endif' - endif - write( luloop, '(a)' ) & - &' deallocate( p_'//trim(varname)//' )', & - &' endif' - endif - if ( idx4 <= 0 ) then - write( luend, '(a)' ) & - &' if ( .not. has_'//trim(varname)//' ) then' - - if ( component ) then - write( luend, '(a)' ) & - &' has_dvar = .false.' - else - write( luend, '(a)' ) & - &' error = .true.' - endif - - write( luend, '(a)' ) & - &' call xml_report_errors(info, ''Missing data on '//trim(varname)//''')', & - &' endif' - else - ! - ! Note: the attribute value is supposed to have the quotes, if that - ! is relevant for the variable's type - ! - if ( component ) then - write( ludeflt, '(4a)' ) & - &' dvar%', trim(varname), ' = ', attribs(2,idx4) - else - write( luinit, '(4a)' ) & - &' ', trim(varname), ' = ', attribs(2,idx4) - endif - endif - ! - ! Write the component/variable - ! - if ( component ) then - write( luwrite, '(4a)' ) & - &' call '//trim(types(4,idx3))//'(', & - &' info, '''//trim(vartag)//''', indent+3, dvar%', trim(varname), ')' - else - write( luwrv, '(4a)' ) & - &' call '//trim(types(4,idx3))//'(', & - &' info, '''//trim(vartag)//''', indent+3, ', trim(varname), ')' - endif - endif - -end subroutine add_variable - -!=============================================================================== -! ADD_TYPEDEF -- -! Routine to write the definition and other code for a derived type -! Arguments: -! dyn_strings Whether dynamic string lengths are allowed -!=============================================================================== - -subroutine add_typedef( dyn_strings ) - logical, intent(in) :: dyn_strings - - integer :: idx1 - integer :: idx2 - - character(len=32) :: typetag - - idx1 = xml_find_attrib( attribs, noattribs, 'name', typename ) - if ( idx1 <= 0 ) then - write( 20, * ) 'Type definition found which has no name' - error = .true. - endif - - ! - ! We need a new set of temporary files - ! - call open_tmp_files( luprolog+notmps ) - - idx2 = xml_find_attrib( attribs, noattribs, 'tag', typetag ) - if ( idx1 < 1 ) then - typetag = typename - endif - - if ( .not. error ) then - write( ludef, '(/,2a)' ) 'type ',trim(typename) - write( luprolog, '(a)' ) & - & 'subroutine read_xml_type_'//trim(typename)//'_array( &' ,& - & ' info, tag, endtag, attribs, noattribs, data, nodata, &',& - & ' dvar, has_dvar )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(inout) :: tag ',& - & ' logical, intent(inout) :: endtag ',& - & ' character(len=*), dimension(:,:), intent(inout) :: attribs',& - & ' integer, intent(inout) :: noattribs',& - & ' character(len=*), dimension(:), intent(inout) :: data ',& - & ' integer, intent(inout) :: nodata ',& - & ' type('//trim(typename)//'), dimension(:), pointer :: dvar ',& - & ' logical, intent(inout) :: has_dvar ',& - & ' ' ,& - & ' integer :: newsize ',& - & ' type('//trim(typename)//'), dimension(:), pointer :: newvar',& - & ' ' ,& - & ' newsize = size(dvar) + 1' ,& - & ' allocate( newvar(1:newsize) )' ,& - & ' newvar(1:newsize-1) = dvar' ,& - & ' deallocate( dvar )' ,& - & ' dvar => newvar' ,& - & ' ' ,& - & ' call read_xml_type_'//trim(typename)// & - & '( info, tag, endtag, attribs, noattribs, data, nodata, &',& - & ' dvar(newsize), has_dvar )' ,& - & 'end subroutine read_xml_type_'//trim(typename)//'_array' ,& - & ' ' - - write( luwrite, '(a)' ) & - & 'subroutine write_xml_type_'//trim(typename)//'_array( &' ,& - & ' info, tag, indent, dvar )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(in) :: tag' ,& - & ' integer :: indent',& - & ' type('//trim(typename)//'), dimension(:) :: dvar' ,& - & ' integer :: i' ,& - & ' do i = 1,size(dvar)' ,& - & ' call write_xml_type_'//trim(typename)// & - & '( info, tag, indent, dvar(i) )' ,& - & ' enddo' ,& - & 'end subroutine write_xml_type_'//trim(typename)//'_array' ,& - & ' ', & - & 'subroutine write_xml_type_'//trim(typename)//'( &' ,& - & ' info, tag, indent, dvar )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(in) :: tag' ,& - & ' integer :: indent',& - & ' type('//trim(typename)//') :: dvar' ,& - & ' character(len=100) :: indentation' ,& - & ' indentation = '' ''' ,& - & ' write(info%lun, ''(4a)'' ) indentation(1:min(indent,100)),&',& - & ' ''<'',trim(tag), ''>''' - - write( luprolog, '(a)' ) & - & 'subroutine read_xml_type_'//trim(typename)//& - & '( info, starttag, endtag, attribs, noattribs, data, nodata, &' ,& - & ' dvar, has_dvar )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(in) :: starttag',& - & ' logical, intent(inout) :: endtag ',& - & ' character(len=*), dimension(:,:), intent(inout) :: attribs',& - & ' integer, intent(inout) :: noattribs',& - & ' character(len=*), dimension(:), intent(inout) :: data ',& - & ' integer, intent(inout) :: nodata ',& - & ' type('//trim(typename)//'), intent(inout) :: dvar' ,& - & ' logical, intent(inout) :: has_dvar ',& - & ' ' ,& - & ' integer :: att_ ',& - & ' integer :: noatt_ ',& - & ' logical :: error ',& - & ' logical :: endtag_org' - if ( dyn_strings ) then - write( luprolog, '(a)' ) & - & ' character(len=len(starttag)) :: tag ' - else - write( luprolog, '(a)' ) & - & ' character(len=80) :: tag ' - endif - - ! - ! Note: this may require a more sophisticated approach - ! when the components of the type are also pointers ... - ! - write( ludeflt, '(a)' ) & - & 'subroutine init_xml_type_'//trim(typename)//'_array( dvar ) ',& - & ' type('//trim(typename)//'), dimension(:), pointer :: dvar ',& - & ' if ( associated( dvar ) ) then' ,& - & ' deallocate( dvar )' ,& - & ' endif' ,& - & ' allocate( dvar(0) )' ,& - & 'end subroutine init_xml_type_'//trim(typename)//'_array' ,& - & 'subroutine init_xml_type_'//trim(typename)//'(dvar)' ,& - & ' type('//trim(typename)//') :: dvar ' - - begin_loop = .true. - - call add_end_loop - - ! - ! Add the names of the two new types to the list - ! - allocate( new_types(1:4,1:notypes+3) ) - new_types(:,1:notypes) = types - deallocate( types ) - types => new_types - - types(1,notypes+1) = typename - types(2,notypes+1) = ' type('//trim(typename)//')' - types(3,notypes+1) = 'read_xml_type_'//trim(typename) - types(4,notypes+1) = 'write_xml_type_'//trim(typename) - - types(1,notypes+2) = trim(typename) // '-array' - types(2,notypes+2) = ' type('//trim(typename)//'), dimension(:), pointer' - types(3,notypes+2) = 'read_xml_type_'//trim(typename)//'_array' - types(4,notypes+2) = 'write_xml_type_'//trim(typename)//'_array' - - types(1,notypes+3) = trim(typename) // '-shape' - types(2,notypes+3) = ' type('//trim(typename)//'), dimension(SHAPE)' - types(3,notypes+3) = 'read_xml_type_'//trim(typename)//'_array' - types(4,notypes+3) = 'write_xml_type_'//trim(typename)//'_array' - - notypes = notypes + 3 - - endif -end subroutine add_typedef - -!=============================================================================== -! CLOSE_TYPEDEF -- -! Routine to write the last code fragments for a derived type -! Arguments: -! component Turn off the "component" parameter -!=============================================================================== - -subroutine close_typedef( component ) - logical, intent(out) :: component - - component = .false. - write( ludef, '(a)' ) 'end type '//trim(typename) - write( luend, '(a)' ) & - & 'end subroutine read_xml_type_'//trim(typename) - write( ludeflt, '(a)' ) & - & 'end subroutine init_xml_type_'//trim(typename) - write( luwrite, '(a)' ) & - & ' write(info%lun,''(4a)'') indentation(1:min(indent,100)), &' ,& - & ' ''''', & - & 'end subroutine write_xml_type_'//trim(typename) ,& - & ' ' - call append_files( luprolog ) - call close_tmp_files - -end subroutine close_typedef - -!=============================================================================== -! ADD_PLACEHOLDER -- -! Routine to write the starting code fragments for a placeholder tag -! Arguments: -! dyn_strings Whether dynamic string lengths are allowed -!=============================================================================== - -subroutine add_placeholder( dyn_strings ) - logical, intent(in) :: dyn_strings - - integer :: idx1 - integer :: idx2 - - character(len=32) :: tag - character(len=20) :: optional - - idx1 = xml_find_attrib( attribs, noattribs, 'tag', tag ) - if ( idx1 <= 0 ) then - write( 20, * ) 'Placeholder definition found which has no tag name' - error = .true. - endif - - optional = 'no' - idx2 = xml_find_attrib( attribs, noattribs, 'optional', optional ) - - if ( optional == 'yes' ) then - if ( begin_loop ) then - call add_begin_loop( .false., .false. ) - endif - write( luloop, '(a)' ) & - ' case('''//trim(tag)//''')',& - ' ! Simply ignore the tag' - else - no_placeholders = no_placeholders + 1 - placeholder(no_placeholders) = tag - - write( luloop, '(a)' ) & - ' case('''//trim(tag)//''')',& - &' call read_xml_place_'//trim(tag)//'( info, &', & - &' tag, attribs, noattribs, data, nodata )' - comp = .false. - - ! - ! We need a new set of temporary files - ! - call open_tmp_files( luprolog+notmps ) - - ! - ! Write the first part of the routine - ! NOTE: - ! Will require an extra argument when collecting all variables - ! in one derived type - ! - write( luprolog, '(a)' ) & - & 'subroutine read_xml_place_'//trim(tag)//& - & '( info, starttag, attribs, noattribs, data, nodata )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(in) :: starttag',& - & ' character(len=*), dimension(:,:), intent(inout) :: attribs',& - & ' integer, intent(inout) :: noattribs',& - & ' character(len=*), dimension(:), intent(inout) :: data ',& - & ' integer, intent(inout) :: nodata ',& - & ' ' ,& - & ' logical :: error ',& - & ' logical :: endtag ' - if ( dyn_strings ) then - write( luprolog, '(a)' ) & - & ' character(len=len(starttag)) :: tag ' - else - write( luprolog, '(a)' ) & - & ' character(len=80) :: tag ' - endif - - begin_loop = .true. - - call add_end_loop - - write(luwrite,'(a)') & - &'subroutine write_xml_place_'//trim(tag)//'( &' ,& - &' info, indent )' ,& - &' type(XML_PARSE) :: info' ,& - &' integer :: indent',& - &' character(len=100) :: indentation' ,& - &' indentation = '' ''' ,& - &' write(info%lun, ''(4a)'' ) indentation(1:min(indent,100)),&',& - &' ''<'// trim(tag) // '>''' - write(luwritp,'(a)') & - &' call write_xml_place_'//trim(tag)//'( info, indent+3 )' - - endif -end subroutine add_placeholder - -!=============================================================================== -! CLOSE_PLACEHOLDER -- -! Routine to write the last code fragments for a placeholder -! Arguments: -! None -!=============================================================================== - -subroutine close_placeholder - - write( luend, '(a)' ) & - & 'end subroutine read_xml_place_'//trim(placeholder(no_placeholders)) - - write( luwrite, '(a)' ) & - & ' write(info%lun,''(4a)'') indentation(1:min(indent,100)), &' ,& - & ' ''''' ,& - & 'end subroutine write_xml_place_'//trim(placeholder(no_placeholders)) ,& - & ' ' - - call append_files( luprolog ) - call close_tmp_files - - no_placeholders = no_placeholders - 1 - -end subroutine close_placeholder - -end program From ab6ef0835ca96418dc38d2cf6f8af76839d1fe70 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 24 Jul 2013 12:21:59 -0400 Subject: [PATCH 074/192] added two new methods when working with repeated nodes in and XML and changed the input reading routines to use these --- src/input_xml.F90 | 77 +++++++++++++++++++++++++++++++++++-------- src/xml_interface.F90 | 41 +++++++++++++++++++---- 2 files changed, 99 insertions(+), 19 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 24ec6c2c67..ddde113bc5 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -703,6 +703,9 @@ contains type(Node), pointer :: node_cell => null() type(Node), pointer :: node_surf => null() type(Node), pointer :: node_lat => null() + type(NodeList), pointer :: node_cell_list => null() + type(NodeList), pointer :: node_surf_list => null() + type(NodeList), pointer :: node_lat_list => null() ! Display output message message = "Reading geometry XML file..." @@ -740,12 +743,15 @@ contains overlap_check_cnt = 0 end if + ! Get pointer to list of XML + call get_node_list(doc, "cell", node_cell_list) + n_universes = 0 do i = 1, n_cells c => cells(i) ! Get pointer to i-th cell node - call get_node_ptr(doc, "cell", node_cell, i) + call get_node_item(node_cell_list, i, node_cell) ! Copy data into cells if (check_for_node(node_cell, "id")) then @@ -918,11 +924,14 @@ contains ! Allocate cells array allocate(surfaces(n_surfaces)) + ! Get pointer to list of XML + call get_node_list(doc, "surface", node_surf_list) + do i = 1, n_surfaces s => surfaces(i) ! Get pointer to i-th surface node - call get_node_ptr(doc, "surface", node_surf, i) + call get_node_item(node_surf_list, i, node_surf) ! Copy data into cells if (check_for_node(node_surf, "id")) then @@ -1040,11 +1049,14 @@ contains n_lattices = get_number_nodes(doc, "lattice") allocate(lattices(n_lattices)) + ! Get pointer to list of XML + call get_node_list(doc, "lattice", node_lat_list) + do i = 1, n_lattices lat => lattices(i) ! Get pointer to i-th lattice - call get_node_ptr(doc, "lattice", node_lat, i) + call get_node_item(node_lat_list, i, node_lat) ! ID of lattice if (check_for_node(node_lat, "id")) then @@ -1194,6 +1206,10 @@ contains type(Node), pointer :: node_nuc => null() type(Node), pointer :: node_ele => null() type(Node), pointer :: node_sab => null() + type(NodeList), pointer :: node_mat_list => null() + type(NodeList), pointer :: node_nuc_list => null() + type(NodeList), pointer :: node_ele_list => null() + type(NodeList), pointer :: node_sab_list => null() ! Display output message message = "Reading materials XML file..." @@ -1225,11 +1241,14 @@ contains index_nuclide = 0 index_sab = 0 + ! Get pointer to list of XML + call get_node_list(doc, "material", node_mat_list) + do i = 1, n_materials mat => materials(i) ! Get pointer to i-th material node - call get_node_ptr(doc, "material", node_mat, i) + call get_node_item(node_mat_list, i, node_mat) ! Copy material id if (check_for_node(node_mat, "id")) then @@ -1313,10 +1332,13 @@ contains call fatal_error() end if + ! Get pointer list of XML + call get_node_list(node_mat, "nuclide", node_nuc_list) + ! Create list of nuclides based on those specified plus natural elements INDIVIDUAL_NUCLIDES: do j = 1, get_number_nodes(node_mat, "nuclide") ! Combine nuclide identifier and cross section and copy into names - call get_node_ptr(node_mat, "nuclide", node_nuc, j) + call get_node_item(node_nuc_list, j, node_nuc) ! Check for empty name on nuclide if (.not.check_for_node(node_nuc, "name")) then @@ -1372,8 +1394,11 @@ contains ! ======================================================================= ! READ AND PARSE TAGS + ! Get pointer list of XML + call get_node_list(node_mat, "element", node_ele_list) + NATURAL_ELEMENTS: do j = 1, get_number_nodes(node_mat, "element") - call get_node_ptr(node_mat, "element", node_ele, j) + call get_node_item(node_ele_list, j, node_ele) ! Check for empty name on natural element if (.not.check_for_node(node_ele, "name")) then @@ -1501,9 +1526,12 @@ contains ! Initialize i_sab_nuclides mat % i_sab_nuclides = NONE + ! Get pointer list to XML + call get_node_list(node_mat, "sab", node_sab_list) + do j = 1, n_sab ! Get pointer to S(a,b) table - call get_node_ptr(node_mat, "sab", node_sab, j) + call get_node_item(node_sab_list, j, node_sab) ! Determine name of S(a,b) table if (.not.check_for_node(node_sab, "name") .or. & @@ -1594,6 +1622,9 @@ contains type(Node), pointer :: node_mesh => null() type(Node), pointer :: node_tal => null() type(Node), pointer :: node_filt => null() + type(NodeList), pointer :: node_mesh_list => null() + type(NodeList), pointer :: node_tal_list => null() + type(NodeList), pointer :: node_filt_list => null() ! Check if tallies.xml exists filename = trim(path_input) // "tallies.xml" @@ -1653,11 +1684,14 @@ contains ! ========================================================================== ! READ MESH DATA + ! Get pointer list to XML + call get_node_list(doc, "mesh", node_mesh_list) + do i = 1, n_user_meshes m => meshes(i) ! Get pointer to mesh node - call get_node_ptr(doc, "mesh", node_mesh, i) + call get_node_item(node_mesh_list, i, node_mesh) ! Copy mesh id if (check_for_node(node_mesh, "id")) then @@ -1790,12 +1824,15 @@ contains ! ========================================================================== ! READ TALLY DATA + ! Get pointer list to XML + call get_node_list(doc, "tally", node_tal_list) + READ_TALLIES: do i = 1, n_user_tallies ! Get pointer to tally t => tallies(i) ! Get pointer to tally xml node - call get_node_ptr(doc, "tally", node_tal, i) + call get_node_item(node_tal_list, i, node_tal) ! Set tally type to volume by default t % type = TALLY_VOLUME @@ -1849,9 +1886,12 @@ contains t % n_filters = n_filters allocate(t % filters(n_filters)) + ! Get pointer list to XML + call get_node_list(node_tal, "filter", node_filt_list) + READ_FILTERS: do j = 1, n_filters ! Get pointer to filter xml node - call get_node_ptr(node_tal, "filter", node_filt, j) + call get_node_item(node_filt_list, j, node_filt) ! Convert filter type to lower case temp_str = '' @@ -2423,6 +2463,8 @@ contains type(Node), pointer :: node_plot => null() type(Node), pointer :: node_col => null() type(Node), pointer :: node_mask => null() + type(NodeList), pointer :: node_plot_list => null() + type(NodeList), pointer :: node_col_list => null() ! Check if plots.xml exists filename = trim(path_input) // "plots.xml" @@ -2443,11 +2485,14 @@ contains n_plots = get_number_nodes(doc, "plot") allocate(plots(n_plots)) + ! Get list pointer to XML + call get_node_list(doc, "plot", node_plot_list) + READ_PLOTS: do i = 1, n_plots pl => plots(i) ! Get pointer to plot XML node - call get_node_ptr(doc, "plot", node_plot, i) + call get_node_item(node_plot_list, i, node_plot) ! Copy data into plots if (check_for_node(node_plot, "id")) then @@ -2619,11 +2664,13 @@ contains call warning() end if + ! Get the number of nodes and get a list of them n_cols = get_number_nodes(node_plot, "col_spec") + call get_node_list(node_plot, "col_spec", node_col_list) do j = 1, n_cols ! Get pointer to color spec XML node - call get_node_ptr(node_plot, "col_spec", node_col, j) + call get_node_item(node_col_list, j, node_col) ! Check and make sure 3 values are specified for RGB if (get_arraysize_double(node_col, "rgb") /= 3) then @@ -2773,6 +2820,7 @@ contains type(XsListing), pointer :: listing => null() type(Node), pointer :: doc => null() type(Node), pointer :: node_ace => null() + type(NodeList), pointer :: node_ace_list => null() ! Check if cross_sections.xml exists inquire(FILE=path_cross_sections, EXIST=file_exists) @@ -2829,11 +2877,14 @@ contains allocate(xs_listings(n_listings)) end if + ! Get node list of all + call get_node_list(doc, "ace_table", node_ace_list) + do i = 1, n_listings listing => xs_listings(i) ! Get pointer to ace table XML node - call get_node_ptr(doc, "ace_table", node_ace, i) + call get_node_item(node_ace_list, i, node_ace) ! copy a number of attributes call get_node_value(node_ace, "name", listing % name) diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index d1fc88a1ce..b0c47aa083 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -8,11 +8,14 @@ module xml_interface implicit none private public :: Node + public :: NodeList public :: open_xmldoc public :: close_xmldoc public :: check_for_node public :: get_number_nodes public :: get_node_ptr + public :: get_node_list + public :: get_node_item public :: get_node_value public :: get_node_array public :: get_arraysize_integer @@ -120,15 +123,13 @@ contains ! GET_NODE_PTR !=============================================================================== - subroutine get_node_ptr(in_ptr, node_name, out_ptr, idx, found) + subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) character(len=*), intent(in) :: node_name - integer, intent(in), optional :: idx logical, intent(out), optional :: found type(Node), pointer, intent(in) :: in_ptr type(Node), pointer, intent(out) :: out_ptr - integer :: idx_ logical :: found_ type(NodeList), pointer :: elem_list => null() @@ -142,9 +143,7 @@ contains if (getLength(elem_list) == 0) return ! Point to the new element - idx_ = 0 - if (present(idx)) idx_ = idx - 1 - out_ptr => item(elem_list, idx_) + out_ptr => item(elem_list, 0) found_ = .true. ! Check to output found @@ -152,6 +151,36 @@ contains end subroutine get_node_ptr +!=============================================================================== +! GET_NODE_LIST +!=============================================================================== + + subroutine get_node_list(in_ptr, node_name, out_ptr) + + character(len=*), intent(in) :: node_name + type(Node), pointer, intent(in) :: in_ptr + type(NodeList), pointer, intent(out) :: out_ptr + + ! Check for a sub-element + out_ptr => getElementsByTagName(in_ptr, trim(node_name)) + + end subroutine get_node_list + +!=============================================================================== +! GET_NODE_ITEM +!=============================================================================== + + subroutine get_node_item(in_ptr, idx, out_ptr) + + integer, intent(in) :: idx + type(NodeList), pointer, intent(in) :: in_ptr + type(Node), pointer, intent(out) :: out_ptr + + ! Check for a sub-element + out_ptr => item(in_ptr, idx - 1) + + end subroutine get_node_item + !=============================================================================== ! GET_NODE_VALUE_INTEGER !=============================================================================== From c0772d6b485e43bdb33f1cf8b0a3fb79550d230a Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sat, 27 Jul 2013 15:08:58 -0400 Subject: [PATCH 075/192] added more compiler directives --- src/cmfd_execute.F90 | 5 ++++- src/petsc_solver.F90 | 20 ++++++-------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index a514b0cd8b..ebfaa0aea4 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -240,8 +240,10 @@ contains end if +#ifdef MPI ! Broadcast full source to all procs call MPI_BCAST(cmfd % cmfd_src, n, MPI_REAL8, 0, MPI_COMM_WORLD, mpi_err) +#endif end subroutine calc_fission_source @@ -330,9 +332,10 @@ contains end if ! Broadcast weight factors to all procs +#ifdef MPI call MPI_BCAST(cmfd % weightfactors, ng*nx*ny*nz, MPI_REAL8, 0, & MPI_COMM_WORLD, mpi_err) - +#endif end if ! Begin loop over source bank diff --git a/src/petsc_solver.F90 b/src/petsc_solver.F90 index 8318ca174b..87c288b936 100644 --- a/src/petsc_solver.F90 +++ b/src/petsc_solver.F90 @@ -196,6 +196,7 @@ contains type(Vector) :: res type(Matrix) :: jac_prec +# ifdef PETSC ! Set residual procedure call SNESSetFunction(self % snes, res % petsc_vec, jfnk_compute_residual, & ctx, petsc_err) @@ -210,6 +211,7 @@ contains ! Set up Jacobian Lags call SNESSetLagJacobian(self % snes, -2, petsc_err) call SNESSetLagPreconditioner(self % snes, -1, petsc_err) +#endif end subroutine jfnk_set_functions @@ -231,7 +233,7 @@ contains !=============================================================================== ! JFNK_COMPUTE_RESIDUAL buffer routine to user specifed residual routine !=============================================================================== - +# ifdef PETSC subroutine jfnk_compute_residual(snes, x, res, ctx, ierr) SNES :: snes @@ -248,26 +250,22 @@ contains ! for the residual and x vectors here ! Need to point an OpenMC vector to PETSc vector -#ifdef PETSC call VecGetArrayF90(x, xvec % val, ierr) call VecGetArrayF90(res, resvec % val, ierr) -#endif ! Call user residual routine call ctx % res_proc_ptr(xvec, resvec) ! Need to restore the PETSc vector -#ifdef PETSC call VecRestoreArrayF90(x, xvec % val, ierr) call VecRestoreArrayF90(res, resvec % val, ierr) -#endif end subroutine jfnk_compute_residual - +#endif !=============================================================================== ! JFNK_COMPUTE_JACOBIAN !=============================================================================== - +#ifdef PETSC subroutine jfnk_compute_jacobian(snes, x, jac_mf, jac_prec, flag, ctx, & ierr) @@ -285,23 +283,17 @@ contains ! matrix ! Need to point OpenMC vector to PETSc Vector -#ifdef PETSC call VecGetArrayF90(x, xvec % val, ierr) -#endif ! Evaluate user jacobian routine call ctx % jac_proc_ptr(xvec) ! Restore the PETSc vector -#ifdef PETSC call VecRestoreArrayF90(x, xvec % val, ierr) -#endif -#ifdef PETSC call MatAssemblyBegin(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) call MatAssemblyEnd(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) -#endif end subroutine jfnk_compute_jacobian - +#endif end module petsc_solver From 3cac0afe98f9f9d8a551b36dc95b42a0078687c9 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sat, 27 Jul 2013 15:10:45 -0400 Subject: [PATCH 076/192] changed CMFD userguide documentation --- docs/source/usersguide/input.rst | 66 +++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index d9b43c8214..2bd833cfa5 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -1131,6 +1131,22 @@ The ```` element controls what batch CMFD calculations should begin. *Default*: 1 +```` Element +--------------------- + +The ```` element sets one additional CMFD output column. Options are: + +* "balance" - prints the RMS [%] of the resdiual from the neutron balance equation + on CMFD tallies. +* "dominance" - prints the estimated dominance ratio from the CMFD iterations. + **This will only work for power iteration eigensolver**. +* "entropy" - prints the *entropy* of the CMFD predicted fission source. + **Can only be used if OpenMC entropy is active as well**. +* "source" - prints the RMS [%] between the OpenMC fission source and CMFD + fission source. + + *Default*: None + ```` Element ---------------------- @@ -1150,14 +1166,14 @@ with "true" and off with "false" *Default*: true -```` Element ----------------------- +```` Element +---------------------------- -The ```` element specifies acceptance criteria of a CMFD eigenvalue. -If the CMFD eigenvalue and OpenMC batch eigenvalue are within this tolerance, -CMFD is allowed to modify source neutron weights. +The ```` element controls when CMFD tallies are reset during +inactive batches. The integer set here is the interval at which this reset +occurs. The amout of resets is controlled with the ```` element. - *Default*: 0.005 + *Defualt*: 9999 ```` Element ------------------------- @@ -1241,14 +1257,13 @@ not impact the calculation. *Default*: 1.0 -```` Element --------------------------- +```` Element +------------------------- -The ```` element is used to set the number of processors used -for CMFD calculation. It should be less than or equal to the number of -processors used during OpenMC. +The ```` element controls the number of CMFD tally resets that +occur during inactive CMFD batches. - *Default*: 1 + *Default*: 9999 ```` Element --------------------------- @@ -1256,26 +1271,33 @@ processors used during OpenMC. The ```` element is used to view the convergence of power iteration. This option can be turned on with "true" and turned off with "false". + *Default*: false + +```` Element +------------------------- + +The ```` element can be turned on with "true" to have an adjoint +calculation be performed on the last batch when CMFD is active. *Default*: false -```` Element ---------------------------- +```` Element +-------------------------- -The ```` element is used to view the balance of OpenMC tally -residuals for every coarse mesh region and energy group. This option can be -turned on with "true" and off with "false". +The ```` element is used to view the convergence of the nonlinear SNES +function in PETSc. This option can be turned on with "true" and turned off with "false". *Default*: false -```` Element ------------------------- +```` Element +-------------------- -The ```` element can be turned on with "true" to get an -HDF5 output file of CMFD results. +The ```` element controls whether the CMFD eigenproblem is solved with +standard power iteration or nonlinear Jacobian-free Newton Krylov (JFNK). +By setting "power", power iteration is used and by setting "jfnk", JFNK is used. - *Default*: false + *Default*: power ```` Element ---------------------------- From c7e46dc462645e3560980bdadc2dfa65ea7324e4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sat, 27 Jul 2013 17:03:02 -0400 Subject: [PATCH 077/192] important CMFD information now written to statepoint, also CMFD can be restarted now --- src/cmfd_execute.F90 | 3 ++ src/cmfd_header.F90 | 6 ++++ src/hdf5_interface.F90 | 47 ++++++++++++++++++++++++++++ src/output.F90 | 2 +- src/output_interface.F90 | 67 ++++++++++++++++++++++++++++++++++++++++ src/state_point.F90 | 43 ++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 1 deletion(-) diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index ebfaa0aea4..33f63b1b2f 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -46,6 +46,9 @@ contains call fatal_error() end if + ! Save k-effective + cmfd % k_cmfd(current_batch) = cmfd % keff + ! check to perform adjoint on last batch if (current_batch == n_batches .and. cmfd_run_adjoint) then if (trim(cmfd_solver_type) == 'power') then diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 index 3d6b3fd61a..3eebb0f6d9 100644 --- a/src/cmfd_header.F90 +++ b/src/cmfd_header.F90 @@ -80,6 +80,9 @@ module cmfd_header ! Dominance ratio real(8), allocatable :: dom(:) + ! List of CMFD k + real(8), allocatable :: k_cmfd(:) + end type cmfd_type contains @@ -136,6 +139,7 @@ contains if (.not. allocated(this % balance)) allocate(this % balance(n_batches)) if (.not. allocated(this % src_cmp)) allocate(this % src_cmp(n_batches)) if (.not. allocated(this % dom)) allocate(this % dom(n_batches)) + if (.not. allocated(this % k_cmfd)) allocate(this % k_cmfd(n_batches)) ! Set everthing to 0 except weight multiply factors if feedback isnt on this % flux = ZERO @@ -155,6 +159,7 @@ contains this % balance = ZERO this % src_cmp = ZERO this % dom = ZERO + this % k_cmfd = ZERO if (entropy_on) this % entropy = ZERO end subroutine allocate_cmfd @@ -188,6 +193,7 @@ contains if (allocated(this % balance)) deallocate(this % balance) if (allocated(this % src_cmp)) deallocate(this % src_cmp) if (allocated(this % dom)) deallocate(this % dom) + if (allocated(this % k_cmfd)) deallocate(this % k_cmfd) if (allocated(this % entropy)) deallocate(this % entropy) end subroutine deallocate_cmfd diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 471a4397cb..0886fbf731 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -23,12 +23,16 @@ module hdf5_interface integer(HSIZE_T) :: dims1(1) ! dims type for 1-D array integer(HSIZE_T) :: dims2(2) ! dims type for 2-D array integer(HSIZE_T) :: dims3(3) ! dims type for 3-D array + integer(HSIZE_T) :: dims4(4) ! dims type for 4-D array type(c_ptr) :: f_ptr ! pointer to data ! Generic HDF5 write procedure interface interface hdf5_write_data module procedure hdf5_write_double module procedure hdf5_write_double_1Darray + module procedure hdf5_write_double_2Darray + module procedure hdf5_write_double_3Darray + module procedure hdf5_write_double_4Darray module procedure hdf5_write_integer module procedure hdf5_write_integer_1Darray module procedure hdf5_write_long @@ -39,6 +43,7 @@ module hdf5_interface interface hdf5_read_data module procedure hdf5_read_double module procedure hdf5_read_double_1Darray + module procedure hdf5_read_double_4Darray module procedure hdf5_read_integer module procedure hdf5_read_integer_1Darray module procedure hdf5_read_long @@ -381,6 +386,28 @@ contains end subroutine hdf5_write_double_3Darray +!=============================================================================== +! HDF5_WRITE_DOUBLE_4DARRAY writes double precision 4-D aray +!=============================================================================== + + subroutine hdf5_write_double_4Darray(group, name, buffer, length) + + integer, intent(in) :: length(4) ! length of array dimensions + integer(HID_T), intent(in) :: group ! name of group + character(*), intent(in) :: name ! name of data + real(8), intent(in) :: buffer(length(1),length(2),& ! data + length(3),length(4)) + + ! Set rank and dimensions + hdf5_rank = 4 + dims4 = length + + ! Write data + call h5ltmake_dataset_double_f(group, name, hdf5_rank, dims4, & + buffer, hdf5_err) + + end subroutine hdf5_write_double_4Darray + !=============================================================================== ! HDF5_WRITE_STRING writes string data !=============================================================================== @@ -552,6 +579,26 @@ contains end subroutine hdf5_read_double_1Darray +!=============================================================================== +! HDF5_READ_DOUBLE_4DARRAY reads double precision 4-D array +!=============================================================================== + + subroutine hdf5_read_double_4Darray(group, name, buffer, length) + + integer(HID_T), intent(in) :: group ! name of group + integer, intent(in) :: length(:) ! length of array + character(*), intent(in) :: name ! name of data + real(8), intent(inout) :: buffer(length(1), length(2),& ! read data to here + length(3), length(4)) + + ! Set dimensions of data + dims4 = length + + ! Read data + call h5ltread_dataset_double_f(group, name, buffer, dims4, hdf5_err) + + end subroutine hdf5_read_double_4Darray + !=============================================================================== ! HDF5_READ_STRING reads string data !=============================================================================== diff --git a/src/output.F90 b/src/output.F90 index 2b6b04511d..68bb46764a 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1300,7 +1300,7 @@ contains ! write out cmfd keff if it is active and other display info if (cmfd_on) then write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - cmfd % keff + cmfd % k_cmfd(current_batch) select case(trim(cmfd_display)) case('entropy') write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & diff --git a/src/output_interface.F90 b/src/output_interface.F90 index 5f421a6d25..17f14f16d5 100644 --- a/src/output_interface.F90 +++ b/src/output_interface.F90 @@ -19,6 +19,7 @@ module output_interface module procedure write_double_1Darray module procedure write_double_2Darray module procedure write_double_3Darray + module procedure write_double_4Darray module procedure write_integer module procedure write_integer_1Darray module procedure write_integer_2Darray @@ -31,6 +32,7 @@ module output_interface interface read_data module procedure read_double module procedure read_double_1Darray + module procedure read_double_4Darray module procedure read_integer module procedure read_integer_1Darray module procedure read_long @@ -367,6 +369,71 @@ contains end subroutine write_double_3Darray +!=============================================================================== +! WRITE_DOUBLE_4DARRAY writes double precision 4-D array data +!=============================================================================== + + subroutine write_double_4Darray(buffer, name, group, length) + + integer, intent(in) :: length(4) ! length of each dimension + real(8), intent(in) :: buffer(length(1),length(2),& + length(3),length(4)) + character(*), intent(in) :: name ! name of data + character(*), intent(in), optional :: group ! HDF5 group name + +#ifdef HDF5 + ! Check if HDF5 group should be created/opened + if (present(group)) then + call hdf5_open_group(group) + else + temp_group = hdf5_fh + endif + + ! Write the data + call hdf5_write_double_4Darray(temp_group, name, buffer, length) + + ! Check if HDF5 group should be closed + if (present(group)) call hdf5_close_group() +#else + message = 'Double precision 4-D array writing not currently supported' + call warning() +#endif + + end subroutine write_double_4Darray + +!=============================================================================== +! READ_DOUBLE_4DARRAY reads double precision 4-D array data +!=============================================================================== + + subroutine read_double_4Darray(buffer, name, group, length, option) + + integer, intent(in) :: length(4) ! length of each dimension + real(8), intent(inout) :: buffer(length(1),length(2),& + length(3),length(4)) + character(*), intent(in) :: name ! name of data + character(*), intent(in), optional :: group ! HDF5 group name + character(*), intent(in), optional :: option ! read option + +#ifdef HDF5 + ! Check if HDF5 group should be created/opened + if (present(group)) then + call hdf5_open_group(group) + else + temp_group = hdf5_fh + endif + + ! Write the data + call hdf5_read_double_4Darray(temp_group, name, buffer, length) + + ! Check if HDF5 group should be closed + if (present(group)) call hdf5_close_group() +#else + message = 'Double precision 4-D array reading not currently supported' + call warning() +#endif + + end subroutine read_double_4Darray + !=============================================================================== ! WRITE_INTEGER writes integer scalar data !=============================================================================== diff --git a/src/state_point.F90 b/src/state_point.F90 index 5020f8f3d0..dd3f3e785f 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -94,6 +94,27 @@ contains call write_data(k_col_tra, "k_col_tra") call write_data(k_abs_tra, "k_abs_tra") call write_data(k_combined, "k_combined", length=2) + + ! Write out CMFD info + if (cmfd_on) then + call write_data(1, "cmfd_on") + call write_data(cmfd % k_cmfd, "k_cmfd", length=current_batch, & + group="cmfd") + call write_data(cmfd % cmfd_src, "cmfd_src", & + length=(/cmfd % indices(4), cmfd % indices(1), & + cmfd % indices(2), cmfd % indices(3)/), & + group="cmfd") + if (entropy_on) call write_data(cmfd % entropy, "cmfd_entropy", & + length=current_batch, group="cmfd") + call write_data(cmfd % balance, "cmfd_balance", & + length=current_batch, group="cmfd") + call write_data(cmfd % dom, "cmfd_dominance", & + length = current_batch, group="cmfd") + call write_data(cmfd % src_cmp, "cmfd_srccmp", & + length = current_batch, group="cmfd") + else + call write_data(0, "cmfd_on") + end if end if ! Write number of meshes @@ -516,6 +537,28 @@ contains ! Take maximum of statepoint n_inactive and input n_inactive n_inactive = max(n_inactive, int_array(1)) + + ! Read in to see if CMFD was on + call read_data(int_array(1), "cmfd_on", option="collective") + + ! Write out CMFD info + if (int_array(1) == 1) then + call read_data(cmfd % k_cmfd, "k_cmfd", length=restart_batch, & + group="cmfd", option="collective") + call read_data(cmfd % cmfd_src, "cmfd_src", & + length=(/cmfd % indices(4), cmfd % indices(1), & + cmfd % indices(2), cmfd % indices(3)/), & + group="cmfd", option="collective") + if (entropy_on) call read_data(cmfd % entropy, "cmfd_entropy", & + length=restart_batch, group="cmfd", & + option="collective") + call read_data(cmfd % balance, "cmfd_balance", & + length=restart_batch, group="cmfd", option="collective") + call read_data(cmfd % dom, "cmfd_dominance", & + length = restart_batch, group="cmfd", option="collective") + call read_data(cmfd % src_cmp, "cmfd_srccmp", & + length = restart_batch, group="cmfd", option="collective") + end if end if ! Read number of meshes From 63a4574134615ec0863929d1c86badc766f2e3cd Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sat, 27 Jul 2013 17:03:54 -0400 Subject: [PATCH 078/192] Default PETSc version is 3.4.2 and current code will only compile with this version --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 4c301483a2..6c72802265 100644 --- a/src/Makefile +++ b/src/Makefile @@ -31,7 +31,7 @@ PETSC = no MPI_DIR = /opt/mpich/3.0.4-$(COMPILER) HDF5_DIR = /opt/hdf5/1.8.11-$(COMPILER) PHDF5_DIR = /opt/phdf5/1.8.11-$(COMPILER) -PETSC_DIR = /opt/petsc/3.3-p6-$(COMPILER) +PETSC_DIR = /opt/petsc/3.4.2-$(COMPILER) #=============================================================================== # Add git SHA-1 hash From e5bdb8bef9ac0d2ad8c7cd1eb5b412693b0de5f4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 29 Jul 2013 15:56:24 -0400 Subject: [PATCH 079/192] Changed how to get the number of recurring node types in file, and added comments to XML interface --- src/input_xml.F90 | 162 ++++++++++++++++++++---------------------- src/xml_interface.F90 | 82 ++++++++++----------- 2 files changed, 119 insertions(+), 125 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index ddde113bc5..2bf1abe1b5 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -725,9 +725,11 @@ contains ! Parse geometry.xml file call open_xmldoc(doc, filename) + ! Get pointer to list of XML + call get_node_list(doc, "cell", node_cell_list) + ! Get number of tags - n_cells = 0 - n_cells = get_number_nodes(doc, "cell") + n_cells = get_list_size(node_cell_list) ! Check for no cells if (n_cells == 0) then @@ -743,15 +745,12 @@ contains overlap_check_cnt = 0 end if - ! Get pointer to list of XML - call get_node_list(doc, "cell", node_cell_list) - n_universes = 0 do i = 1, n_cells c => cells(i) ! Get pointer to i-th cell node - call get_node_item(node_cell_list, i, node_cell) + call get_list_item(node_cell_list, i, node_cell) ! Copy data into cells if (check_for_node(node_cell, "id")) then @@ -911,9 +910,11 @@ contains ! applied to a surface boundary_exists = .false. + ! get pointer to list of xml + call get_node_list(doc, "surface", node_surf_list) + ! Get number of tags - n_surfaces = 0 - n_surfaces = get_number_nodes(doc, "surface") + n_surfaces = get_list_size(node_surf_list) ! Check for no surfaces if (n_surfaces == 0) then @@ -924,14 +925,11 @@ contains ! Allocate cells array allocate(surfaces(n_surfaces)) - ! Get pointer to list of XML - call get_node_list(doc, "surface", node_surf_list) - do i = 1, n_surfaces s => surfaces(i) ! Get pointer to i-th surface node - call get_node_item(node_surf_list, i, node_surf) + call get_list_item(node_surf_list, i, node_surf) ! Copy data into cells if (check_for_node(node_surf, "id")) then @@ -1045,18 +1043,18 @@ contains ! ========================================================================== ! READ LATTICES FROM GEOMETRY.XML - ! Allocate lattices array - n_lattices = get_number_nodes(doc, "lattice") - allocate(lattices(n_lattices)) - ! Get pointer to list of XML call get_node_list(doc, "lattice", node_lat_list) + ! Allocate lattices array + n_lattices = get_list_size(node_lat_list) + allocate(lattices(n_lattices)) + do i = 1, n_lattices lat => lattices(i) ! Get pointer to i-th lattice - call get_node_item(node_lat_list, i, node_lat) + call get_list_item(node_lat_list, i, node_lat) ! ID of lattice if (check_for_node(node_lat, "id")) then @@ -1233,22 +1231,22 @@ contains if (check_for_node(doc, "default_xs")) & call get_node_value(doc, "default_xs", default_xs) + ! Get pointer to list of XML + call get_node_list(doc, "material", node_mat_list) + ! Allocate cells array - n_materials = get_number_nodes(doc, "material") + n_materials = get_list_size(node_mat_list) allocate(materials(n_materials)) ! Initialize count for number of nuclides/S(a,b) tables index_nuclide = 0 index_sab = 0 - ! Get pointer to list of XML - call get_node_list(doc, "material", node_mat_list) - do i = 1, n_materials mat => materials(i) ! Get pointer to i-th material node - call get_node_item(node_mat_list, i, node_mat) + call get_list_item(node_mat_list, i, node_mat) ! Copy material id if (check_for_node(node_mat, "id")) then @@ -1336,9 +1334,9 @@ contains call get_node_list(node_mat, "nuclide", node_nuc_list) ! Create list of nuclides based on those specified plus natural elements - INDIVIDUAL_NUCLIDES: do j = 1, get_number_nodes(node_mat, "nuclide") + INDIVIDUAL_NUCLIDES: do j = 1, get_list_size(node_nuc_list) ! Combine nuclide identifier and cross section and copy into names - call get_node_item(node_nuc_list, j, node_nuc) + call get_list_item(node_nuc_list, j, node_nuc) ! Check for empty name on nuclide if (.not.check_for_node(node_nuc, "name")) then @@ -1397,8 +1395,8 @@ contains ! Get pointer list of XML call get_node_list(node_mat, "element", node_ele_list) - NATURAL_ELEMENTS: do j = 1, get_number_nodes(node_mat, "element") - call get_node_item(node_ele_list, j, node_ele) + NATURAL_ELEMENTS: do j = 1, get_list_size(node_ele_list) + call get_list_item(node_ele_list, j, node_ele) ! Check for empty name on natural element if (.not.check_for_node(node_ele, "name")) then @@ -1513,7 +1511,10 @@ contains ! ======================================================================= ! READ AND PARSE TAG FOR S(a,b) DATA - n_sab = get_number_nodes(node_mat, "sab") + ! Get pointer list to XML + call get_node_list(node_mat, "sab", node_sab_list) + + n_sab = get_list_size(node_sab_list) if (n_sab > 0) then ! Set number of S(a,b) tables mat % n_sab = n_sab @@ -1526,12 +1527,9 @@ contains ! Initialize i_sab_nuclides mat % i_sab_nuclides = NONE - ! Get pointer list to XML - call get_node_list(node_mat, "sab", node_sab_list) - do j = 1, n_sab ! Get pointer to S(a,b) table - call get_node_item(node_sab_list, j, node_sab) + call get_list_item(node_sab_list, j, node_sab) ! Determine name of S(a,b) table if (.not.check_for_node(node_sab, "name") .or. & @@ -1644,28 +1642,28 @@ contains ! ========================================================================== ! DETERMINE SIZE OF ARRAYS AND ALLOCATE + ! Get pointer list to XML + call get_node_list(doc, "mesh", node_mesh_list) + + ! Get pointer list to XML + call get_node_list(doc, "tally", node_tal_list) + ! Check for user meshes - if (.not.check_for_node(doc, "mesh")) then - n_user_meshes = 0 + n_user_meshes = get_list_size(node_mesh_list) + if (cmfd_run) then + n_meshes = n_user_meshes + n_cmfd_meshes else - n_user_meshes = get_number_nodes(doc, "mesh") - if (cmfd_run) then - n_meshes = n_user_meshes + n_cmfd_meshes - else - n_meshes = n_user_meshes - end if + n_meshes = n_user_meshes end if ! Allocate mesh array if (n_meshes > 0) allocate(meshes(n_meshes)) ! Check for user tallies - if (.not.check_for_node(doc, "tally")) then - n_user_tallies = 0 + n_user_tallies = get_list_size(node_tal_list) + if (n_user_tallies == 0) then message = "No tallies present in tallies.xml file!" call warning() - else - n_user_tallies = get_number_nodes(doc, "tally") end if ! Allocate tally array @@ -1684,14 +1682,11 @@ contains ! ========================================================================== ! READ MESH DATA - ! Get pointer list to XML - call get_node_list(doc, "mesh", node_mesh_list) - do i = 1, n_user_meshes m => meshes(i) ! Get pointer to mesh node - call get_node_item(node_mesh_list, i, node_mesh) + call get_list_item(node_mesh_list, i, node_mesh) ! Copy mesh id if (check_for_node(node_mesh, "id")) then @@ -1824,15 +1819,12 @@ contains ! ========================================================================== ! READ TALLY DATA - ! Get pointer list to XML - call get_node_list(doc, "tally", node_tal_list) - READ_TALLIES: do i = 1, n_user_tallies ! Get pointer to tally t => tallies(i) ! Get pointer to tally xml node - call get_node_item(node_tal_list, i, node_tal) + call get_list_item(node_tal_list, i, node_tal) ! Set tally type to volume by default t % type = TALLY_VOLUME @@ -1872,26 +1864,25 @@ contains ! element followed by sub-elements , , etc. This checks for ! the old format and if it is present, raises an error - if (get_number_nodes(node_tal, "filters") > 0) then - message = "Tally filters should be specified with multiple & - &elements. Did you forget to change your element?" - call fatal_error() - end if +! if (get_number_nodes(node_tal, "filters") > 0) then +! message = "Tally filters should be specified with multiple & +! &elements. Did you forget to change your element?" +! call fatal_error() +! end if - if (check_for_node(node_tal, "filter")) then - ! Determine number of filters - n_filters = get_number_nodes(node_tal, "filter") + ! Get pointer list to XML and get number of filters + call get_node_list(node_tal, "filter", node_filt_list) + n_filters = get_list_size(node_filt_list) + + if (n_filters /= 0) then ! Allocate filters array t % n_filters = n_filters allocate(t % filters(n_filters)) - ! Get pointer list to XML - call get_node_list(node_tal, "filter", node_filt_list) - READ_FILTERS: do j = 1, n_filters ! Get pointer to filter xml node - call get_node_item(node_filt_list, j, node_filt) + call get_list_item(node_filt_list, j, node_filt) ! Convert filter type to lower case temp_str = '' @@ -2453,7 +2444,7 @@ contains subroutine read_plots_xml integer i, j - integer n_cols, col_id, n_comp + integer n_cols, col_id, n_comp, n_masks integer, allocatable :: iarray(:) logical :: file_exists ! does plots.xml file exist? character(MAX_LINE_LEN) :: filename ! absolute path to plots.xml @@ -2465,6 +2456,7 @@ contains type(Node), pointer :: node_mask => null() type(NodeList), pointer :: node_plot_list => null() type(NodeList), pointer :: node_col_list => null() + type(NodeList), pointer :: node_mask_list => null() ! Check if plots.xml exists filename = trim(path_input) // "plots.xml" @@ -2481,18 +2473,18 @@ contains ! Parse plots.xml file call open_xmldoc(doc, filename) - ! Allocate plots array - n_plots = get_number_nodes(doc, "plot") - allocate(plots(n_plots)) - ! Get list pointer to XML call get_node_list(doc, "plot", node_plot_list) + ! Allocate plots array + n_plots = get_list_size(node_plot_list) + allocate(plots(n_plots)) + READ_PLOTS: do i = 1, n_plots pl => plots(i) ! Get pointer to plot XML node - call get_node_item(node_plot_list, i, node_plot) + call get_list_item(node_plot_list, i, node_plot) ! Copy data into plots if (check_for_node(node_plot, "id")) then @@ -2655,8 +2647,12 @@ contains call fatal_error() end select + ! Get the number of nodes and get a list of them + call get_node_list(node_plot, "col_spec", node_col_list) + n_cols = get_list_size(node_col_list) + ! Copy user specified colors - if (check_for_node(node_plot, "col_spec")) then + if (n_cols /= 0) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Color specifications ignored in voxel plot " // & @@ -2664,13 +2660,10 @@ contains call warning() end if - ! Get the number of nodes and get a list of them - n_cols = get_number_nodes(node_plot, "col_spec") - call get_node_list(node_plot, "col_spec", node_col_list) do j = 1, n_cols ! Get pointer to color spec XML node - call get_node_item(node_col_list, j, node_col) + call get_list_item(node_col_list, j, node_col) ! Check and make sure 3 values are specified for RGB if (get_arraysize_double(node_col, "rgb") /= 3) then @@ -2716,7 +2709,9 @@ contains end if ! Deal with masks - if (check_for_node(node_plot, "mask")) then + call get_node_list(node_plot, "mask", node_mask_list) + n_masks = get_list_size(node_mask_list) + if (n_masks /= 0) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Mask ignored in voxel plot " // & @@ -2724,16 +2719,15 @@ contains call warning() end if - select case(get_number_nodes(node_plot, "mask")) + select case(n_masks) case default message = "Mutliple masks" // & " specified in plot " // trim(to_str(pl % id)) call fatal_error() - case (0) case (1) ! Get pointer to mask - call get_node_ptr(node_plot, "mask", node_mask) + call get_list_item(node_mask_list, 1, node_mask) ! Determine how many components there are and allocate n_comp = 0 @@ -2868,23 +2862,23 @@ contains call get_node_value(doc, "entries", entries) end if + ! Get node list of all + call get_node_list(doc, "ace_table", node_ace_list) + n_listings = get_list_size(node_ace_list) + ! Allocate xs_listings array - if (.not.check_for_node(doc, "ace_table")) then + if (n_listings == 0) then message = "No ACE table listings present in cross_sections.xml file!" call fatal_error() else - n_listings = get_number_nodes(doc, "ace_table") allocate(xs_listings(n_listings)) end if - ! Get node list of all - call get_node_list(doc, "ace_table", node_ace_list) - do i = 1, n_listings listing => xs_listings(i) ! Get pointer to ace table XML node - call get_node_item(node_ace_list, i, node_ace) + call get_list_item(node_ace_list, i, node_ace) ! copy a number of attributes call get_node_value(node_ace, "name", listing % name) diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index b0c47aa083..76c174f010 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -12,10 +12,10 @@ module xml_interface public :: open_xmldoc public :: close_xmldoc public :: check_for_node - public :: get_number_nodes public :: get_node_ptr public :: get_node_list - public :: get_node_item + public :: get_list_size + public :: get_list_item public :: get_node_value public :: get_node_array public :: get_arraysize_integer @@ -41,7 +41,7 @@ module xml_interface contains !=============================================================================== -! OPEN_XMLDOC +! OPEN_XMLDOC opens and parses an XML and returns a pointer to the document !=============================================================================== subroutine open_xmldoc(ptr, filename) @@ -55,7 +55,7 @@ contains end subroutine open_xmldoc !=============================================================================== -! CLOSE_XMLDOC +! CLOSE_XMLDOC closes and destroys all memory associated with document !=============================================================================== subroutine close_xmldoc(ptr) @@ -67,7 +67,11 @@ contains end subroutine close_xmldoc !=============================================================================== -! CHECK_FOR_NODE +! CHECK_FOR_NODE checks for an attribute or sub-element node with the given +! node name. This should only be used for checking a single occurance of a +! sub-element node. To check for sub-element nodes that repeat, use +! get_node_list and get_list_size. This is to minimize number of calls +! to getElementsByTagName. !=============================================================================== function check_for_node(ptr, node_name) result(found) @@ -100,27 +104,9 @@ contains end function check_for_node !=============================================================================== -! GET_NUMBER_NODES -!=============================================================================== - - function get_number_nodes(ptr, node_name) result(n_nodes) - - character(len=*), intent(in) :: node_name - integer :: n_nodes - type(Node), pointer, intent(in) :: ptr - - type(NodeList), pointer :: elem_list - - ! Check for a sub-element - elem_list => getElementsByTagName(ptr, trim(node_name)) - - ! Get the length of the list - n_nodes = getLength(elem_list) - - end function get_number_nodes - -!=============================================================================== -! GET_NODE_PTR +! GET_NODE_PTR returns a Node pointer to an attribute or sub-element node. +! Note, this should only be used for sub-element nodes that occur only once. +! For repeated nodes, use get_node_list and then get_item_item. !=============================================================================== subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) @@ -152,7 +138,7 @@ contains end subroutine get_node_ptr !=============================================================================== -! GET_NODE_LIST +! GET_NODE_LIST is used to get a pointer to a list of sub-element nodes !=============================================================================== subroutine get_node_list(in_ptr, node_name, out_ptr) @@ -167,10 +153,24 @@ contains end subroutine get_node_list !=============================================================================== -! GET_NODE_ITEM +! GET_LIST_SIZE is used to get the number of elements from a node list !=============================================================================== - subroutine get_node_item(in_ptr, idx, out_ptr) + function get_list_size(in_ptr) result(n_size) + + integer :: n_size + type(NodeList), pointer, intent(in) :: in_ptr + + ! Get the size of the list + n_size = getLength(in_ptr) + + end function get_list_size + +!=============================================================================== +! GET_LIST_ITEM is used to select a specific item from a node list +!=============================================================================== + + subroutine get_list_item(in_ptr, idx, out_ptr) integer, intent(in) :: idx type(NodeList), pointer, intent(in) :: in_ptr @@ -179,10 +179,10 @@ contains ! Check for a sub-element out_ptr => item(in_ptr, idx - 1) - end subroutine get_node_item + end subroutine get_list_item !=============================================================================== -! GET_NODE_VALUE_INTEGER +! GET_NODE_VALUE_INTEGER returns a integer value from an attribute or node !=============================================================================== subroutine get_node_value_integer(ptr, node_name, result_int) @@ -215,7 +215,7 @@ contains end subroutine get_node_value_integer !=============================================================================== -! GET_NODE_VALUE_LONG +! GET_NODE_VALUE_LONG returns an 8-byte integer from attribute or node !=============================================================================== subroutine get_node_value_long(ptr, node_name, result_long) @@ -248,7 +248,7 @@ contains end subroutine get_node_value_long !=============================================================================== -! GET_NODE_VALUE_DOUBLE +! GET_NODE_VALUE_DOUBLE returns a double precision real from attr. or node !=============================================================================== subroutine get_node_value_double(ptr, node_name, result_double) @@ -281,7 +281,7 @@ contains end subroutine get_node_value_double !=============================================================================== -! GET_NODE_ARRAY_INTEGER +! GET_NODE_ARRAY_INTEGER returns a 1-D array of integers !=============================================================================== subroutine get_node_array_integer(ptr, node_name, result_int) @@ -314,7 +314,7 @@ contains end subroutine get_node_array_integer !=============================================================================== -! GET_NODE_ARRAY_DOUBLE +! GET_NODE_ARRAY_DOUBLE returns a 1-D array of double precision reals !=============================================================================== subroutine get_node_array_double(ptr, node_name, result_double) @@ -347,7 +347,7 @@ contains end subroutine get_node_array_double !=============================================================================== -! GET_NODE_ARRAY_STRING +! GET_NODE_ARRAY_STRING returns a 1-D array of strings !=============================================================================== subroutine get_node_array_string(ptr, node_name, result_string) @@ -380,7 +380,7 @@ contains end subroutine get_node_array_string !=============================================================================== -! GET_NODE_VALUE_STRING +! GET_NODE_VALUE_STRING returns a single string from attr. or node !=============================================================================== subroutine get_node_value_string(ptr, node_name, result_str) @@ -413,7 +413,7 @@ contains end subroutine get_node_value_string !=============================================================================== -! GET_NODE_ARRAYSIZE_INTEGER +! GET_NODE_ARRAYSIZE_INTEGER returns the size of the integer array !=============================================================================== function get_arraysize_integer(ptr, node_name) result(n) @@ -442,7 +442,7 @@ contains end function get_arraysize_integer !=============================================================================== -! GET_NODE_ARRAYSIZE_DOUBLE +! GET_NODE_ARRAYSIZE_DOUBLE returns the size of double prec. real array !=============================================================================== function get_arraysize_double(ptr, node_name) result(n) @@ -471,7 +471,7 @@ contains end function get_arraysize_double !=============================================================================== -! GET_NODE_ARRAYSIZE_STRING +! GET_NODE_ARRAYSIZE_STRING returns the size of string array !=============================================================================== function get_arraysize_string(ptr, node_name) result(n) @@ -500,7 +500,7 @@ contains end function get_arraysize_string !=============================================================================== -! GET_NODE +! GET_NODE private routine that gets a pointer to a specific node !=============================================================================== subroutine get_node(in_ptr, node_name, out_ptr, node_type, found) From be410a604344ba506e61222c090a6294ed56b57f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 29 Jul 2013 16:38:01 -0400 Subject: [PATCH 080/192] XML document is now correctly deallocated --- src/xml_interface.F90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 76c174f010..4752c08734 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -49,8 +49,8 @@ contains character(len=*) :: filename type(Node), pointer :: ptr - ptr => parseFile(trim(filename)) - ptr => getDocumentElement(ptr) + ptr => parseFile(trim(filename)) ! Pointer to the whole XML document + ptr => getDocumentElement(ptr) ! Grabs root element of XML document end subroutine open_xmldoc @@ -62,7 +62,8 @@ contains type(Node), pointer :: ptr - call destroy(ptr) + ptr => getParentNode(ptr) ! Go from root element ptr to document ptr + call destroy(ptr) ! Deallocates all nodes recursively end subroutine close_xmldoc From 49d915924c81a869361e02c1c6aa927c078ca156 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 29 Jul 2013 16:53:22 -0400 Subject: [PATCH 081/192] added documentation on new element structure --- docs/source/usersguide/input.rst | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index d9b43c8214..947534990a 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -198,19 +198,26 @@ tally data, this option can significantly improve the parallel efficiency. -------------------- The ```` element determines what output files should be written to disk -during the run. This element has no attributes or sub-elements and should be set -to a list of strings separated by spaces. Valid options are "summary", -"cross-sections", and "tallies". For example, if you want the summary and cross -sections summary file to be written, this element should be given as: +during the run. The sub-elements are described below, where "true" will write +out the file and "false" will not. - .. code-block:: xml + :cross_sections: + Writes out an ASCII summary file of the cross sections that were read in. - summary cross_sections + *Default*: false - .. note:: The tally results will be written to a binary/HDF5 state point file by - default. + :summary: + Writes out an ASCII summary file describing all of the user input files that + were read in. - *Default*: "tallies" + *Default*: false + + :tallies: + Write out an ASCII file of tally results. + + *Default*: true + + .. note:: The tally results will always be written to a binary/HDF5 state point file. ```` Element ------------------------- From 9d81de66cb03a1a800bd33ce423946c3a1ddde71 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 8 Aug 2013 18:30:23 -0400 Subject: [PATCH 082/192] fixed bug with for multigroup CMFD and fixed relative source error in power iteration --- src/cmfd_loss_operator.F90 | 2 +- src/cmfd_power_solver.F90 | 53 +++++++++++++++++++++----------------- src/cmfd_prod_operator.F90 | 2 +- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 index 22fa8dfa14..e09497bedc 100644 --- a/src/cmfd_loss_operator.F90 +++ b/src/cmfd_loss_operator.F90 @@ -410,7 +410,7 @@ contains if (cmfd_coremap) then ! Get indices from indexmap - g = mod(irow, ng) + 1 + g = mod(irow-1, ng) + 1 i = cmfd % indexmap((irow-1)/ng+1,1) j = cmfd % indexmap((irow-1)/ng+1,2) k = cmfd % indexmap((irow-1)/ng+1,3) diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 1d96ea2944..9c7eff0eda 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -19,13 +19,16 @@ module cmfd_power_solver real(8) :: stol = 1.e-8_8 ! tolerance on source real(8) :: norm_n ! current norm of source vector real(8) :: norm_o ! old norm of source vector + real(8) :: kerr ! error in keff + real(8) :: serr ! error in source logical :: adjoint_calc ! run an adjoint calculation type(Matrix) :: loss ! cmfd loss matrix type(Matrix) :: prod ! cmfd prod matrix type(Vector) :: phi_n ! new flux vector type(Vector) :: phi_o ! old flux vector - type(Vector) :: S_n ! new source vector - type(Vector) :: S_o ! old flux vector + type(Vector) :: s_n ! new source vector + type(Vector) :: s_o ! old flux vector + type(Vector) :: serr_v ! error in source type(Petsc_gmres) :: gmres ! gmres solver contains @@ -113,8 +116,9 @@ contains call phi_o % create(n) ! Set up source vectors - call S_n % create(n) - call S_o % create(n) + call s_n % create(n) + call s_o % create(n) + call serr_v % create(n) ! Set initial guess guess = ONE @@ -136,8 +140,8 @@ contains call prod % setup_petsc() call phi_n % setup_petsc() call phi_o % setup_petsc() - call S_o % setup_petsc() - call S_n % setup_petsc() + call s_o % setup_petsc() + call s_n % setup_petsc() ! Set norms to 0 norm_n = ZERO @@ -181,22 +185,22 @@ contains do i = 1, 10000 ! Compute source vector - call prod % vector_multiply(phi_o, S_o) + call prod % vector_multiply(phi_o, s_o) ! Normalize source vector - S_o % val = S_o % val / k_o + s_o % val = s_o % val / k_o ! Compute new flux vector - call gmres % solve(S_o, phi_n) + call gmres % solve(s_o, phi_n) ! Compute new source vector - call prod % vector_multiply(phi_n, S_n) + call prod % vector_multiply(phi_n, s_n) ! Compute new k-eigenvalue - k_n = sum(S_n % val) / sum(S_o % val) + k_n = sum(s_n % val) / sum(s_o % val) ! Renormalize the old source - S_o % val = S_o % val * k_o + s_o % val = s_o % val * k_o ! Check convergence call convergence(i) @@ -219,15 +223,12 @@ contains subroutine convergence(iter) - use constants, only: ONE + use constants, only: ONE, TINY_BIT use global, only: cmfd_power_monitor, master use, intrinsic :: ISO_FORTRAN_ENV integer :: iter ! iteration number - real(8) :: kerr ! error in keff - real(8) :: serr ! error in source - ! Reset convergence flag iconv = .false. @@ -235,7 +236,10 @@ contains kerr = abs(k_o - k_n)/k_n ! Calculate max error in source - serr = sqrt(ONE/dble(S_n % n) * sum(((S_n % val - S_o % val)/S_n % val)**2)) + where (s_n % val > TINY_BIT) + serr_v % val = ((s_n % val - s_o % val)/s_n % val)**2 + end where + serr = sqrt(ONE/dble(s_n % n) * sum(serr_v % val)) ! Check for convergence if(kerr < ktol .and. serr < stol) iconv = .true. @@ -315,13 +319,14 @@ contains subroutine finalize() ! Destroy all objects - call gmres % destroy() - call loss % destroy() - call prod % destroy() - call phi_n % destroy() - call phi_o % destroy() - call S_n % destroy() - call S_o % destroy() + call gmres % destroy() + call loss % destroy() + call prod % destroy() + call phi_n % destroy() + call phi_o % destroy() + call s_n % destroy() + call s_o % destroy() + call serr_v % destroy end subroutine finalize diff --git a/src/cmfd_prod_operator.F90 b/src/cmfd_prod_operator.F90 index 4c6a28fb88..c2db9fc83c 100644 --- a/src/cmfd_prod_operator.F90 +++ b/src/cmfd_prod_operator.F90 @@ -179,7 +179,7 @@ contains if (cmfd_coremap) then ! get indices from indexmap - g = mod(irow, ng) + 1 + g = mod(irow-1, ng) + 1 i = cmfd % indexmap((irow-1)/ng+1,1) j = cmfd % indexmap((irow-1)/ng+1,2) k = cmfd % indexmap((irow-1)/ng+1,3) From a72685818699fba737c85a557a8384084af3f3d2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 9 Aug 2013 13:56:48 -0400 Subject: [PATCH 083/192] fixed tally resetting on restart run and writing message for core overlay --- src/cmfd_execute.F90 | 3 +++ src/cmfd_input.F90 | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 33f63b1b2f..f9f3eb2de8 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -89,6 +89,9 @@ contains cmfd_tally_on = .true. end if + ! If this is a restart run and we are just replaying batches leave + if (restart_run .and. current_batch <= restart_batch) return + ! Check to flush cmfd tallies for active batches, no more inactive flush if (cmfd_run .and. cmfd_act_flush == current_batch) then call cmfd_tally_reset() diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 45cac6b549..a836279d51 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -136,7 +136,7 @@ contains ! Write to stdout that a core map overlay is active message = "Core Map Overlay Activated" - call write_message() + call write_message(10) end if From dedba81ead49f1c80d60cb566deec2793f9aa1b2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 15:06:52 -0400 Subject: [PATCH 084/192] if material units are sum, not value will exist -> value is moved to be read after units --- src/input_xml.F90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 2bf1abe1b5..a610015c31 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1281,8 +1281,7 @@ contains call fatal_error() end if - ! Copy value and units - call get_node_value(node_dens, "value", val) + ! Copy units call get_node_value(node_dens, "units", units) if (units == 'sum') then @@ -1317,6 +1316,9 @@ contains // "' specified on material " // trim(to_str(mat % id)) call fatal_error() end select + + ! Copy value + call get_node_value(node_dens, "value", val) end if ! ======================================================================= From 4b7d979364b66da662acab3f7423a799734a6a81 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 15:32:55 -0400 Subject: [PATCH 085/192] moved copying value before type of unit checked and initialize value to zero --- src/input_xml.F90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a610015c31..5bf877e25d 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1281,6 +1281,9 @@ contains call fatal_error() end if + ! Initialize value to zero + val = ZERO + ! Copy units call get_node_value(node_dens, "units", units) @@ -1292,6 +1295,9 @@ contains sum_density = .true. else + ! Copy value + call get_node_value(node_dens, "value", val) + ! Check for erroneous density sum_density = .false. if (val <= ZERO) then @@ -1316,9 +1322,6 @@ contains // "' specified on material " // trim(to_str(mat % id)) call fatal_error() end select - - ! Copy value - call get_node_value(node_dens, "value", val) end if ! ======================================================================= From dda6ae55d998229eb8f96caa6dcab0cdcd1f3bd5 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 15:46:30 -0400 Subject: [PATCH 086/192] changed fixed source to particle when determining if node particle exists --- src/input_xml.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 5bf877e25d..a6d6538db0 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -165,7 +165,7 @@ contains call get_node_ptr(doc, "fixed_source", node_mode) ! Check number of particles - if (.not.check_for_node(node_mode, "fixed_source")) then + if (.not.check_for_node(node_mode, "particles")) then message = "Need to specify number of particles per batch." call fatal_error() end if From 33dbb27e66aade5248212fc49b6594ce14f48010 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 16:20:29 -0400 Subject: [PATCH 087/192] fixed node pointers to natural elements that were sometimes labeled as nuclides --- src/input_xml.F90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a6d6538db0..0dd4fb3bc8 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1412,7 +1412,7 @@ contains call get_node_value(node_ele, "name", name) ! Check for cross section - if (.not.check_for_node(node_nuc, "xs")) then + if (.not.check_for_node(node_ele, "xs")) then if (default_xs == '') then message = "No cross section specified for nuclide in material " & // trim(to_str(mat % id)) @@ -1424,21 +1424,21 @@ contains ! Check if no atom/weight percents were specified or if both atom and ! weight percents were specified - if (.not.check_for_node(node_nuc, "ao") .and. & - .not.check_for_node(node_nuc, "wo")) then + if (.not.check_for_node(node_ele, "ao") .and. & + .not.check_for_node(node_ele, "wo")) then message = "No atom or weight percent specified for element " // & trim(name) call fatal_error() - elseif (check_for_node(node_nuc, "ao") .and. & - check_for_node(node_nuc, "wo")) then + elseif (check_for_node(node_ele, "ao") .and. & + check_for_node(node_ele, "wo")) then message = "Cannot specify both atom and weight percents for a & &element: " // trim(name) call fatal_error() end if ! Expand element into naturally-occurring isotopes - if (check_for_node(node_nuc, "ao")) then - call get_node_value(node_nuc, "ao", temp_dble) + if (check_for_node(node_ele, "ao")) then + call get_node_value(node_ele, "ao", temp_dble) call expand_natural_element(name, temp_str, temp_dble, & list_names, list_density) else From 455a794b43caa16e23ee02cd4cbb8519215c0ad4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 16:30:07 -0400 Subject: [PATCH 088/192] for plots, is now cell by default (according to users guide) --- src/input_xml.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 0dd4fb3bc8..8c3df4f58c 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2621,7 +2621,7 @@ contains end if ! Copy plot color type and initialize all colors randomly - temp_str = '' + temp_str = "cell" if (check_for_node(node_plot, "color")) & call get_node_value(node_plot, "color", temp_str) call lower_case(temp_str) From 5e8ac1b98a3d670abbec0dd509072a4d5925ca4c Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 16:35:34 -0400 Subject: [PATCH 089/192] added empty arg list on subroutines --- src/input_xml.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 8c3df4f58c..f1363d85a9 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1593,7 +1593,7 @@ contains ! for errors and placing properly-formatted data in the right data structures !=============================================================================== - subroutine read_tallies_xml + subroutine read_tallies_xml() integer :: i ! loop over user-specified tallies integer :: j ! loop over words @@ -2446,7 +2446,7 @@ contains ! READ_PLOTS_XML reads data from a plots.xml file !=============================================================================== - subroutine read_plots_xml + subroutine read_plots_xml() integer i, j integer n_cols, col_id, n_comp, n_masks From 43ba6f18588b2f773cd1b86705b2b28c4ae35d1e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 16:40:54 -0400 Subject: [PATCH 090/192] wrong pointer listed for reading in RGB in node --- src/input_xml.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index f1363d85a9..b27cb2aebf 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2691,7 +2691,7 @@ contains if (cell_dict % has_key(col_id)) then col_id = cell_dict % get_key(col_id) - call get_node_array(node_plot, "rgb", pl % colors(col_id) % rgb) + call get_node_array(node_col, "rgb", pl % colors(col_id) % rgb) else message = "Could not find cell " // trim(to_str(col_id)) // & " specified in plot " // trim(to_str(pl % id)) @@ -2702,7 +2702,7 @@ contains if (material_dict % has_key(col_id)) then col_id = material_dict % get_key(col_id) - call get_node_array(node_plot, "rgb", pl % colors(col_id) % rgb) + call get_node_array(node_col, "rgb", pl % colors(col_id) % rgb) else message = "Could not find material " // trim(to_str(col_id)) // & " specified in plot " // trim(to_str(pl % id)) From a321ac83f83bc7368475726767aac6bf6299fe5f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Aug 2013 08:20:34 -0400 Subject: [PATCH 091/192] Fix spacing in input_xml to conform to style guide. --- src/input_xml.F90 | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index b27cb2aebf..ece67151ce 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -90,10 +90,10 @@ contains ! CROSS_SECTIONS environment variable if (run_mode /= MODE_PLOTTING) then if (.not. check_for_node(doc, "cross_sections") .and. & - run_mode /= MODE_PLOTTING) then - ! No cross_sections.xml file specified in settings.xml, check environment - ! variable - call get_environment_variable("CROSS_SECTIONS", env_variable) + run_mode /= MODE_PLOTTING) then + ! No cross_sections.xml file specified in settings.xml, check + ! environment variable + call get_environment_variable("CROSS_SECTIONS", env_variable) if (len_trim(env_variable) == 0) then message = "No cross_sections.xml file was specified in settings.xml & &or in the CROSS_SECTIONS environment variable." @@ -116,7 +116,7 @@ contains ! Make sure that either eigenvalue or fixed source was specified if (.not.check_for_node(doc, "eigenvalue") .and. & - .not.check_for_node(doc, "fixed_source")) then + .not.check_for_node(doc, "fixed_source")) then message = " or not specified." call fatal_error() end if @@ -145,10 +145,10 @@ contains ! Copy batch and generation information call get_node_value(node_mode, "batches", n_batches) call get_node_value(node_mode, "inactive", n_inactive) - n_active = n_batches - n_inactive - if (check_for_node(node_mode, "generations_per_batch")) & - call get_node_value(node_mode, "generations_per_batch", & - gen_per_batch) + n_active = n_batches - n_inactive + if (check_for_node(node_mode, "generations_per_batch")) then + call get_node_value(node_mode, "generations_per_batch", gen_per_batch) + end if ! Allocate array for batch keff and entropy allocate(k_generation(n_batches*gen_per_batch)) @@ -220,7 +220,7 @@ contains ! Verbosity if (check_for_node(doc, "verbosity")) & - call get_node_value(doc, "verbosity", verbosity) + call get_node_value(doc, "verbosity", verbosity) ! ========================================================================== ! EXTERNAL SOURCE @@ -257,7 +257,7 @@ contains ! Check for type of spatial distribution type = '' if (check_for_node(node_dist, "type")) & - call get_node_value(node_dist, "type", type) + call get_node_value(node_dist, "type", type) call lower_case(type) select case (trim(type)) case ('box') @@ -307,7 +307,7 @@ contains ! Check for type of angular distribution type = '' if (check_for_node(node_dist, "type")) & - call get_node_value(node_dist, "type", type) + call get_node_value(node_dist, "type", type) call lower_case(type) select case (trim(type)) case ('isotropic') @@ -413,7 +413,7 @@ contains call get_node_value(doc, "survival_biasing", temp_str) call lower_case(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - survival_biasing = .true. + survival_biasing = .true. end if ! Probability tables @@ -421,7 +421,7 @@ contains call get_node_value(doc, "ptables", temp_str) call lower_case(temp_str) if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & - urr_ptables_on = .false. + urr_ptables_on = .false. end if ! Cutoffs From 3af02080023e27b6e36076fc0bf0e920b13298a2 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Aug 2013 00:31:25 -0500 Subject: [PATCH 092/192] Use new format in test_output. --- tests/test_output/settings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_output/settings.xml b/tests/test_output/settings.xml index 70116af722..fef5e2fcf1 100644 --- a/tests/test_output/settings.xml +++ b/tests/test_output/settings.xml @@ -1,7 +1,7 @@ - summary cross_sections + 10 From edfa49ef81a02b6646c1e8bbdf5c6debf24911ed Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Aug 2013 00:36:49 -0500 Subject: [PATCH 093/192] Added back RELAX NG compact schemata. --- schemas.xml | 14 ++-- src/relaxng/cmfd.rnc | 58 +++++++++++++++++ src/relaxng/cross_sections.rnc | 23 +++++++ src/relaxng/geometry.rnc | 35 ++++++++++ src/relaxng/materials.rnc | 42 ++++++++++++ src/relaxng/plots.rnc | 31 +++++++++ src/relaxng/settings.rnc | 116 +++++++++++++++++++++++++++++++++ src/relaxng/tallies.rnc | 43 ++++++++++++ 8 files changed, 355 insertions(+), 7 deletions(-) create mode 100644 src/relaxng/cmfd.rnc create mode 100644 src/relaxng/cross_sections.rnc create mode 100644 src/relaxng/geometry.rnc create mode 100644 src/relaxng/materials.rnc create mode 100644 src/relaxng/plots.rnc create mode 100644 src/relaxng/settings.rnc create mode 100644 src/relaxng/tallies.rnc diff --git a/schemas.xml b/schemas.xml index 982979001a..465b9b3980 100644 --- a/schemas.xml +++ b/schemas.xml @@ -1,10 +1,10 @@ - - - - - - - + + + + + + + diff --git a/src/relaxng/cmfd.rnc b/src/relaxng/cmfd.rnc new file mode 100644 index 0000000000..cbcb7709ae --- /dev/null +++ b/src/relaxng/cmfd.rnc @@ -0,0 +1,58 @@ +element cmfd { + element mesh { + (element dimension { list { xsd:int+ } } | + attribute dimension { list { xsd:int+ } }) & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + ( + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) | + (element width { list { xsd:double+ } } | + attribute width { list { xsd:double+ } }) + ) & + (element albedo { list { xsd:double+ } } | + attribute albedo { list { xsd:double+ } }) & + (element map { list { xsd:int+ } } | + attribute map { list { xsd:int+ } })? & + (element energy { list { xsd:double+ } } | + attribute energy { list { xsd:double+ } })? + } & + + element norm { xsd:double }? & + + element feedback { xsd:boolean }? & + + element n_cmfd_procs { xsd:int }? & + + element reset { xsd:boolean }? & + + element balance { xsd:boolean }? & + + element downscatter { xsd:boolean }? & + + element run_2grp { xsd:boolean }? & + + element solver { xsd:string }? & + + element snes_monitor { xsd:boolean }? & + + element ksp_monitor { xsd:boolean }? & + + element power_monitor { xsd:boolean }? & + + element write_balance { xsd:boolean }? & + + element write_matrices { xsd:boolean }? & + + element run_adjoint { xsd:boolean }? & + + element write_hdf5 { xsd:boolean }? & + + element begin { xsd:int }? & + + element inactive { xsd:boolean }? & + + element active_flush { xsd:int }? & + + element keff_tol { xsd:double }? +} diff --git a/src/relaxng/cross_sections.rnc b/src/relaxng/cross_sections.rnc new file mode 100644 index 0000000000..e82bb986fb --- /dev/null +++ b/src/relaxng/cross_sections.rnc @@ -0,0 +1,23 @@ +element cross_sections { + element ace_table { + (element name { xsd:string { maxLength = "15" } } | + attribute name { xsd:string { maxLength = "15" } }) & + (element alias { xsd:string { maxLength = "15" } } | + attribute alias { xsd:string { maxLength = "15" } })? & + (element zaid { xsd:int } | attribute zaid { xsd:int }) & + (element metastable { xsd:int } | attribute metastable { xsd:int })? & + (element awr { xsd:double } | attribute awr { xsd:double }) & + (element temperature { xsd:double } | attribute temperature { xsd:double }) & + (element path { xsd:string { maxLength = "255" } } | + attribute path { xsd:string { maxLength = "255" } }) & + (element location { xsd:int } | attribute location { xsd:int })? + }* & + + element directory { xsd:string { maxLength = "255" } }? & + + element filetype { ( "ascii" | "binary" ) } & + + element record_length { xsd:int }? & + + element entries { xsd:int }? +} \ No newline at end of file diff --git a/src/relaxng/geometry.rnc b/src/relaxng/geometry.rnc new file mode 100644 index 0000000000..b7975d05f3 --- /dev/null +++ b/src/relaxng/geometry.rnc @@ -0,0 +1,35 @@ +element geometry { + element cell { + (element id { xsd:int } | attribute id { xsd:int }) & + (element universe { xsd:int } | attribute universe { xsd:int })? & + ( + (element fill { xsd:int } | attribute fill { xsd:int }) | + (element material { ( xsd:int | "void" ) } | + attribute material { ( xsd:int | "void" ) }) + ) & + (element surfaces { list { xsd:int+ } } | attribute surfaces { list { xsd:int+ } }) & + (element rotation { list { xsd:double+ } } | attribute rotation { list { xsd:double+ } })? & + (element translation { list { xsd:double+ } } | attribute translation { list { xsd:double+ } })? + }* + + & element surface { + (element id { xsd:int } | attribute id { xsd:int }) & + (element type { xsd:string { maxLength = "15" } } | + attribute type { xsd:string { maxLength = "15" } }) & + (element coeffs { list { xsd:double+ } } | attribute coeffs { list { xsd:double+ } }) & + (element boundary { ( "transmit" | "reflective" | "vacuum" ) } | + attribute boundary { ( "transmit" | "reflective" | "vacuum" ) })? + }* + + & element lattice { + (element id { xsd:int } | attribute id { xsd:int }) & + (element type { ( "rectangular" | "hexagonal" ) } | + attribute type { ( "rectangular" | "hexagonal" ) })? & + (element dimension { list { xsd:positiveInteger+ } } | + attribute dimension { list { xsd:positiveInteger+ } }) & + (element lower_left { list { xsd:double+ } } | attribute lower_left { list { xsd:double+ } }) & + (element width { list { xsd:double+ } } | attribute width { list { xsd:double+ } }) & + (element universes { list { xsd:int+ } } | attribute universes { list { xsd:int+ } }) & + (element outside { xsd:int } | attribute outside { xsd:int })? + }* +} diff --git a/src/relaxng/materials.rnc b/src/relaxng/materials.rnc new file mode 100644 index 0000000000..d497fa745f --- /dev/null +++ b/src/relaxng/materials.rnc @@ -0,0 +1,42 @@ +element materials { + element material { + (element id { xsd:int } | attribute id { xsd:int }) & + + element density { + (element value { xsd:double } | attribute value { xsd:double })? & + (element units { xsd:string { maxLength = "10" } } | + attribute units { xsd:string { maxLength = "10" } }) + } & + + element nuclide { + (element name { xsd:string { maxLength = "7" } } | + attribute name { xsd:string { maxLength = "7" } }) & + (element xs { xsd:string { maxLength = "3" } } | + attribute xs { xsd:string { maxLength = "3" } })? & + ( + (element ao { xsd:double } | attribute ao { xsd:double }) | + (element wo { xsd:double } | attribute wo { xsd:double }) + ) + }* & + + element element { + (element name { xsd:string { maxLength = "2" } } | + attribute name { xsd:string { maxLength = "2" } }) & + (element xs { xsd:string { maxLength = "3" } } | + attribute xs { xsd:string { maxLength = "3" } })? & + ( + (element ao { xsd:double } | attribute ao { xsd:double }) | + (element wo { xsd:double } | attribute wo { xsd:double }) + ) + }* & + + element sab { + (element name { xsd:string { maxLength = "7" } } | + attribute name { xsd:string { maxLength = "7" } }) & + (element xs { xsd:string { maxLength = "3" } } | + attribute xs { xsd:string { maxLength = "3" } })? + }* + }+ & + + element default_xs { xsd:string { maxLength = "3" } }? +} diff --git a/src/relaxng/plots.rnc b/src/relaxng/plots.rnc new file mode 100644 index 0000000000..3c29ba4e0b --- /dev/null +++ b/src/relaxng/plots.rnc @@ -0,0 +1,31 @@ +element plots { + element plot { + (element id { xsd:int } | attribute id { xsd:int })? & + (element filename { xsd:string { maxLength = "50" } } | + attribute filename { xsd:string { maxLength = "50" } })? & + (element type { "slice" } | attribute type { "slice" })? & + (element color { ( "cell" | "mat" | "material" ) } | + attribute color { ( "cell" | "mat" | "material" ) })? & + (element origin { list { xsd:double+ } } | + attribute origin { list { xsd:double+ } })? & + (element width { list { xsd:double+ } } | + attribute width { list { xsd:double+ } })? & + (element basis { ( "xy" | "yz" | "xz" ) } | + attribute basis { ( "xy" | "yz" | "xz" ) })? & + (element pixels { list { xsd:int+ } } | + attribute pixels { list { xsd:int+ } })? & + (element background { list { xsd:int+ } } | + attribute background { list { xsd:int+ } })? & + element col_spec { + (element id { xsd:int } | attribute id { xsd:int }) & + (element rgb { list { xsd:int+ } } | + attribute rgb { list { xsd:int+ } }) + }* & + element mask { + (element components { list { xsd:int+ } } | + attribute components { list { xsd:int+ } }) & + (element background { list { xsd:int+ } } | + attribute background { list { xsd:int+ } }) + }* + }* +} diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc new file mode 100644 index 0000000000..fd39c5cb5d --- /dev/null +++ b/src/relaxng/settings.rnc @@ -0,0 +1,116 @@ +element settings { + element confidence_intervals { xsd:boolean }? & + + ( + element eigenvalue { + (element batches { xsd:positiveInteger } | + attribute batches { xsd:positiveInteger }) & + (element inactive { xsd:nonNegativeInteger } | + attribute inactive { xsd:nonNegativeInteger }) & + (element particles { xsd:positiveInteger } | + attribute particles { xsd:positiveInteger }) & + (element generations_per_batch { xsd:positiveInteger } | + attribute generations_per_batch { xsd:positiveInteger })? + } | + element fixed_source { + (element batches { xsd:positiveInteger } | + attribute batches { xsd:positiveInteger }) & + (element particles { xsd:positiveInteger } | + attribute particles { xsd:positiveInteger }) + } + ) & + + element cross_sections { xsd:string { maxLength = "255" } }? & + + element cutoff { + (element weight { xsd:double } | attribute weight { xsd:double })? & + (element weight_avg { xsd:double } | attribute weight_avg { xsd:double })? + }? & + + element energy_grid { ( "nuclide" | "union" | "lethargy" ) }? & + + element entropy { + (element dimension { list { xsd:int+ } } | + attribute dimension { list { xsd:int+ } })? & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) + }? & + + element no_reduce { xsd:boolean }? & + + element output { + (element summary { xsd:boolean } | attribute summary { xsd:boolean })? & + (element cross_sections { xsd:boolean } | + attribute cross_sections { xsd:boolean })? & + (element tallies { xsd:boolean } | attribute tallies { xsd:boolean })? + }? & + + element output_path { xsd:string { maxLength = "255" } }? & + + element ptables { xsd:boolean }? & + + element run_cmfd { xsd:boolean }? & + + element seed { xsd:positiveInteger }? & + + element source { + element file { xsd:string { maxLength = "255" } }? & + element space { + (element type { xsd:string { maxLength = "16" } } | + attribute type { xsd:string { maxLength = "16" } }) & + (element length { xsd:int } | attribute length { xsd:int })? & + (element interpolation { xsd:string { maxLength = "10" } } | + attribute interplation { xsd:string { maxLength = "10" } })? & + (element parameters { list { xsd:double+ } } | + attribute parameters { list { xsd:double+ } })? + }? & + element angle { + (element type { xsd:string { maxLength = "16" } } | + attribute type { xsd:string { maxLength = "16" } }) & + (element length { xsd:int } | attribute length { xsd:int })? & + (element interpolation { xsd:string { maxLength = "10" } } | + attribute interplation { xsd:string { maxLength = "10" } })? & + (element parameters { list { xsd:double+ } } | + attribute parameters { list { xsd:double+ } })? + }? & + element energy { + (element type { xsd:string { maxLength = "16" } } | + attribute type { xsd:string { maxLength = "16" } }) & + (element length { xsd:int } | attribute length { xsd:int })? & + (element interpolation { xsd:string { maxLength = "10" } } | + attribute interplation { xsd:string { maxLength = "10" } })? & + (element parameters { list { xsd:double+ } } | + attribute parameters { list { xsd:double+ } })? + }? + }? & + + element state_point { + ( + (element batches { list { xsd:positiveInteger+ } } | + attribute batches { list { xsd:positiveInteger+ } }) | + (element interval { xsd:positiveInteger } | + attribute interval { xsd:positiveInteger }) + ) & + (element source_separate { xsd:boolean } | + attribute source_separate { xsd:boolean })? & + (element source_write { xsd:boolean } | + attribute source_write { xsd:boolean })? + }? & + + element survival_biasing { xsd:boolean }? & + + element trace { list { xsd:positiveInteger+ } }? & + + element verbosity { xsd:positiveInteger }? & + + element uniform_fs{ + (element dimension { list { xsd:positiveInteger+ } } | + attribute dimension { list { xsd:positiveInteger+ } }) & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) + }? +} diff --git a/src/relaxng/tallies.rnc b/src/relaxng/tallies.rnc new file mode 100644 index 0000000000..06959276bc --- /dev/null +++ b/src/relaxng/tallies.rnc @@ -0,0 +1,43 @@ +element tallies { + element mesh { + (element id { xsd:int } | attribute id { xsd:int }) & + (element type { ( "rectangular" | "hexagonal" ) } | + attribute type { ( "rectangular" | "hexagonal" ) }) & + (element dimension { list { xsd:positiveInteger+ } } | + attribute dimension { list { xsd:positiveInteger+ } }) & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + ( + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) | + (element width { list { xsd:double+ } } | + attribute width { list { xsd:double+ } }) + ) + }* & + + element tally { + (element id { xsd:int } | attribute id { xsd:int }) & + (element label { xsd:string { maxLength="52" } } | + attribute label { xsd:string { maxLength="52" } })? & + (element estimator { ( "analog" | "tracklength" ) } | + attribute estimator { ( "analog" | "tracklength" ) })? & + element filter { + (element type { ( "cell" | "cellborn" | "material" | "universe" | + "surface" | "mesh" | "energy" | "energyout" ) } | + attribute type { ( "cell" | "cellborn" | "material" | "universe" | + "surface" | "mesh" | "energy" | "energyout" ) }) & + (element bins { list { xsd:string { maxLength = "20" }+ } } | + attribute bins { list { xsd:string { maxLength = "20" }+ } }) & + (element groups { xsd:string { maxLength = "20" }+ } | + attribute groups { xsd:string { maxLength = "20" }+ })? + }* & + element nuclides { + list { xsd:string { maxLength = "12" }+ } + }? & + element scores { + list { xsd:string { maxLength = "20" }+ } + } + }* & + + element assume_separate { xsd:boolean }? +} From 2b8e57b42dcde209d8f5efb59492c174b0d8da9a Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 15 Aug 2013 11:21:23 -0400 Subject: [PATCH 094/192] dependencies now sorted alphabetically in build script --- src/utils/build_dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/build_dependencies.py b/src/utils/build_dependencies.py index a2fd261f26..c88ddbfb62 100755 --- a/src/utils/build_dependencies.py +++ b/src/utils/build_dependencies.py @@ -20,7 +20,7 @@ for src in glob.iglob('*.F90'): dependencies[module] = sorted(list(deps)) -for module in dependencies.keys(): +for module in sorted(dependencies.keys()): for dep in dependencies[module]: print("{0}.o: {1}.o".format(module, dep)) print('') From 85e8ec9e24bb14bcd23b57c1d7a3780e023ba2a2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 15 Aug 2013 13:57:14 -0400 Subject: [PATCH 095/192] fixed up cmfd statepoint writing/reading, updated python statepoint file to reflect changes and incremented statepoint revision number --- src/cmfd_header.F90 | 7 +++---- src/cmfd_input.F90 | 2 +- src/constants.F90 | 2 +- src/state_point.F90 | 11 +++++++---- src/utils/statepoint.py | 17 +++++++++++++++++ 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 index 3eebb0f6d9..42e6b30ef9 100644 --- a/src/cmfd_header.F90 +++ b/src/cmfd_header.F90 @@ -91,10 +91,9 @@ contains ! ALLOCATE_CMFD allocates all data in of cmfd type !============================================================================== - subroutine allocate_cmfd(this, n_batches, entropy_on) + subroutine allocate_cmfd(this, n_batches) integer :: n_batches - logical :: entropy_on type(cmfd_type) :: this integer :: nx ! number of mesh cells in x direction @@ -135,7 +134,7 @@ contains if (.not. allocated(this % weightfactors)) allocate(this % weightfactors(ng,nx,ny,nz)) ! Allocate batchwise parameters - if (.not. allocated(this % entropy) .and. entropy_on) allocate(this % entropy(n_batches)) + if (.not. allocated(this % entropy)) allocate(this % entropy(n_batches)) if (.not. allocated(this % balance)) allocate(this % balance(n_batches)) if (.not. allocated(this % src_cmp)) allocate(this % src_cmp(n_batches)) if (.not. allocated(this % dom)) allocate(this % dom(n_batches)) @@ -160,7 +159,7 @@ contains this % src_cmp = ZERO this % dom = ZERO this % k_cmfd = ZERO - if (entropy_on) this % entropy = ZERO + this % entropy = ZERO end subroutine allocate_cmfd diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index a836279d51..de23b89b09 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -52,7 +52,7 @@ contains call time_cmfdsolve % reset() ! Allocate cmfd object - call allocate_cmfd(cmfd, n_batches, entropy_on) + call allocate_cmfd(cmfd, n_batches) end subroutine configure_cmfd diff --git a/src/constants.F90 b/src/constants.F90 index 1a4080f622..22b1e8e84e 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -11,7 +11,7 @@ module constants integer, parameter :: VERSION_RELEASE = 2 ! Revision numbers for binary files - integer, parameter :: REVISION_STATEPOINT = 9 + integer, parameter :: REVISION_STATEPOINT = 10 integer, parameter :: REVISION_PARTICLE_RESTART = 1 ! Binary file types diff --git a/src/state_point.F90 b/src/state_point.F90 index dd3f3e785f..b0d88946eb 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -98,14 +98,15 @@ contains ! Write out CMFD info if (cmfd_on) then call write_data(1, "cmfd_on") + call write_data(cmfd % indices, "indicies", length=4, group="cmfd") call write_data(cmfd % k_cmfd, "k_cmfd", length=current_batch, & group="cmfd") call write_data(cmfd % cmfd_src, "cmfd_src", & length=(/cmfd % indices(4), cmfd % indices(1), & cmfd % indices(2), cmfd % indices(3)/), & group="cmfd") - if (entropy_on) call write_data(cmfd % entropy, "cmfd_entropy", & - length=current_batch, group="cmfd") + call write_data(cmfd % entropy, "cmfd_entropy", & + length=current_batch, group="cmfd") call write_data(cmfd % balance, "cmfd_balance", & length=current_batch, group="cmfd") call write_data(cmfd % dom, "cmfd_dominance", & @@ -543,14 +544,16 @@ contains ! Write out CMFD info if (int_array(1) == 1) then + call read_data(cmfd % indices, "indicies", length=4, group="cmfd", & + option="collective") call read_data(cmfd % k_cmfd, "k_cmfd", length=restart_batch, & group="cmfd", option="collective") call read_data(cmfd % cmfd_src, "cmfd_src", & length=(/cmfd % indices(4), cmfd % indices(1), & cmfd % indices(2), cmfd % indices(3)/), & group="cmfd", option="collective") - if (entropy_on) call read_data(cmfd % entropy, "cmfd_entropy", & - length=restart_batch, group="cmfd", & + call read_data(cmfd % entropy, "cmfd_entropy", & + length=restart_batch, group="cmfd", & option="collective") call read_data(cmfd % balance, "cmfd_balance", & length=restart_batch, group="cmfd", option="collective") diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index 917ba19e18..ec43435b91 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -192,6 +192,23 @@ class StatePoint(object): self.k_abs_tra = self._get_double(path='k_abs_tra')[0] self.k_combined = self._get_double(2, path='k_combined') + # Read CMFD information + cmfd_present = self._get_int(path='cmfd_on')[0] + if cmfd_present == 1: + self.cmfd_indices = self._get_double(4, path='cmfd/indicies') + self.k_cmfd = self._get_double(self.current_batch, + path='cmfd/k_cmfd') + self.cmfd_src = self._get_double(np.product(self.cmfd_indices, + path='cmfd/cmfd_src')) + self.cmfd_entropy = self._get_double(self.current_batch, + path='cmfd/cmfd_entropy') + self.cmfd_balance = self._get_double(self.current_batch, + path='cmfd/cmfd_balance') + self.cmfd_dominance = self._get_double(self.current_batch, + path='cmfd/cmfd_dominance') + self.cmfd_srccmp = self._get_double(self.current_batch, + path='cmfd/cmfd_srccmp') + # Read number of meshes n_meshes = self._get_int(path='tallies/n_meshes')[0] From 68a73a4a83f82d70876aa5987138d832790aeeb2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 20 Aug 2013 09:12:46 -0400 Subject: [PATCH 096/192] added gmres solver for non-petsc compilation --- src/DEPENDENCIES | 61 +- src/OBJECTS | 3 +- src/cmfd_jfnk_solver.F90 | 4 +- src/cmfd_power_solver.F90 | 9 +- src/gmres_solver.F90 | 1231 +++++++++++++++++ src/input_xml.F90 | 8 +- src/matrix_header.F90 | 4 +- ...{petsc_solver.F90 => solver_interface.F90} | 333 ++++- 8 files changed, 1536 insertions(+), 117 deletions(-) create mode 100644 src/gmres_solver.F90 rename src/{petsc_solver.F90 => solver_interface.F90} (51%) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index b4c9c7980c..ab8c31b85f 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -6,13 +6,30 @@ energy_grid.o: global.o energy_grid.o: list_header.o energy_grid.o: output.o -ace_header.o: constants.o -ace_header.o: endf_header.o - list_header.o: constants.o -petsc_solver.o: matrix_header.o -petsc_solver.o: vector_header.o +particle_header.o: constants.o + +cmfd_slepc_solver.o: cmfd_loss_operator.o +cmfd_slepc_solver.o: cmfd_prod_operator.o +cmfd_slepc_solver.o: constants.o +cmfd_slepc_solver.o: global.o + +global.o: ace_header.o +global.o: bank_header.o +global.o: cmfd_header.o +global.o: constants.o +global.o: dict_header.o +global.o: geometry_header.o +global.o: hdf5_interface.o +global.o: material_header.o +global.o: mesh_header.o +global.o: particle_header.o +global.o: plot_header.o +global.o: set_header.o +global.o: source_header.o +global.o: tally_header.o +global.o: timer_header.o cmfd_loss_operator.o: constants.o cmfd_loss_operator.o: global.o @@ -29,8 +46,6 @@ particle_restart.o: physics.o particle_restart.o: random_lcg.o particle_restart.o: source.o -doppler.o: constants.o - tally_header.o: constants.o cmfd_power_solver.o: cmfd_loss_operator.o @@ -38,7 +53,7 @@ cmfd_power_solver.o: cmfd_prod_operator.o cmfd_power_solver.o: constants.o cmfd_power_solver.o: global.o cmfd_power_solver.o: matrix_header.o -cmfd_power_solver.o: petsc_solver.o +cmfd_power_solver.o: solver_interface.o cmfd_power_solver.o: vector_header.o cmfd_data.o: cmfd_header.o @@ -87,10 +102,8 @@ cmfd_prod_operator.o: constants.o cmfd_prod_operator.o: global.o cmfd_prod_operator.o: matrix_header.o -cmfd_slepc_solver.o: cmfd_loss_operator.o -cmfd_slepc_solver.o: cmfd_prod_operator.o -cmfd_slepc_solver.o: constants.o -cmfd_slepc_solver.o: global.o +ace_header.o: constants.o +ace_header.o: endf_header.o fission.o: ace_header.o fission.o: constants.o @@ -140,7 +153,7 @@ cmfd_jfnk_solver.o: cmfd_prod_operator.o cmfd_jfnk_solver.o: constants.o cmfd_jfnk_solver.o: global.o cmfd_jfnk_solver.o: matrix_header.o -cmfd_jfnk_solver.o: petsc_solver.o +cmfd_jfnk_solver.o: solver_interface.o cmfd_jfnk_solver.o: vector_header.o main.o: constants.o @@ -170,21 +183,7 @@ interpolation.o: string.o random_lcg.o: global.o -global.o: ace_header.o -global.o: bank_header.o -global.o: cmfd_header.o -global.o: constants.o -global.o: dict_header.o -global.o: geometry_header.o -global.o: hdf5_interface.o -global.o: material_header.o -global.o: mesh_header.o -global.o: particle_header.o -global.o: plot_header.o -global.o: set_header.o -global.o: source_header.o -global.o: tally_header.o -global.o: timer_header.o +doppler.o: constants.o string.o: constants.o string.o: error.o @@ -212,7 +211,11 @@ tally.o: search.o tally.o: string.o tally.o: tally_header.o -particle_header.o: constants.o +solver_interface.o: error.o +solver_interface.o: global.o +solver_interface.o: gmres_solver.o +solver_interface.o: matrix_header.o +solver_interface.o: vector_header.o output_interface.o: constants.o output_interface.o: error.o diff --git a/src/OBJECTS b/src/OBJECTS index 4a2c48a717..97a3838a94 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -25,6 +25,7 @@ fixed_source.o \ geometry.o \ geometry_header.o \ global.o \ +gmres_solver.o \ hdf5_interface.o \ hdf5_summary.o \ initialize.o \ @@ -43,7 +44,6 @@ output_interface.o \ particle_header.o \ particle_restart.o \ particle_restart_write.o \ -petsc_solver.o \ physics.o \ plot.o \ plot_header.o \ @@ -51,6 +51,7 @@ ppmlib.o \ random_lcg.o \ search.o \ set_header.o \ +solver_interface.o \ source.o \ source_header.o \ state_point.o \ diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 04f28ac426..3208e62146 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -4,7 +4,7 @@ module cmfd_jfnk_solver use cmfd_power_solver, only: cmfd_power_execute use cmfd_prod_operator, only: init_prod_matrix, build_prod_matrix use matrix_header, only: Matrix - use petsc_solver, only: Petsc_jfnk, Jfnk_ctx + use solver_interface, only: JFNKSolver, Jfnk_ctx use vector_header, only: Vector implicit none @@ -16,7 +16,7 @@ module cmfd_jfnk_solver type(Matrix) :: jac_prec ! Jacobian preconditioner object type(Matrix) :: loss ! CMFD loss matrix type(Matrix) :: prod ! CMFD production matrix - type(Petsc_jfnk) :: jfnk ! JFNK solver object + type(JFNKSolver) :: jfnk ! JFNK solver object type(Vector) :: resvec ! JFNK residual vector type(Vector) :: xvec ! JFNK solution vector diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index 9c7eff0eda..d57f386ee5 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -5,7 +5,7 @@ module cmfd_power_solver use cmfd_loss_operator, only: init_loss_matrix, build_loss_matrix use cmfd_prod_operator, only: init_prod_matrix, build_prod_matrix use matrix_header, only: Matrix - use petsc_solver, only: Petsc_gmres + use solver_interface, only: GMRESSolver use vector_header, only: Vector implicit none @@ -29,7 +29,7 @@ module cmfd_power_solver type(Vector) :: s_n ! new source vector type(Vector) :: s_o ! old flux vector type(Vector) :: serr_v ! error in source - type(Petsc_gmres) :: gmres ! gmres solver + type(GMRESSolver) :: gmres ! gmres solver contains @@ -98,6 +98,7 @@ contains subroutine init_data(adjoint) use constants, only: ONE, ZERO + use global, only: cmfd_write_matrices logical :: adjoint @@ -136,12 +137,16 @@ contains ! Setup petsc for everything call loss % assemble() call prod % assemble() +#ifdef PETSC call loss % setup_petsc() call prod % setup_petsc() call phi_n % setup_petsc() call phi_o % setup_petsc() call s_o % setup_petsc() call s_n % setup_petsc() + if (cmfd_write_matrices) call loss % write_petsc_binary('loss.bin') + if (cmfd_write_matrices) call prod % write_petsc_binary('prod.bin') +#endif ! Set norms to 0 norm_n = ZERO diff --git a/src/gmres_solver.F90 b/src/gmres_solver.F90 new file mode 100644 index 0000000000..9345f4f9a5 --- /dev/null +++ b/src/gmres_solver.F90 @@ -0,0 +1,1231 @@ +module gmres_solver + + implicit none + +contains + +subroutine pgmres ( n, im, rhs, sol, vv, eps, maxits, iout, & + aa, ja, ia, alu, jlu, ju, ierr ) + +!*****************************************************************************80 +! +!! PGMRES is an ILUT - Preconditioned GMRES solver. +! +! Discussion: +! +! This is a simple version of the ILUT preconditioned GMRES algorithm. +! The ILUT preconditioner uses a dual strategy for dropping elements +! instead of the usual level of-fill-in approach. See details in ILUT +! subroutine documentation. PGMRES uses the L and U matrices generated +! from the subroutine ILUT to precondition the GMRES algorithm. +! The preconditioning is applied to the right. The stopping criterion +! utilized is based simply on reducing the residual norm by epsilon. +! This preconditioning is more reliable than ilu0 but requires more +! storage. It seems to be much less prone to difficulties related to +! strong nonsymmetries in the matrix. We recommend using a nonzero tol +! (tol=.005 or .001 usually give good results) in ILUT. Use a large +! lfil whenever possible (e.g. lfil = 5 to 10). The higher lfil the +! more reliable the code is. Efficiency may also be much improved. +! Note that lfil=n and tol=0.0 in ILUT will yield the same factors as +! Gaussian elimination without pivoting. +! +! ILU(0) and MILU(0) are also provided for comparison purposes +! USAGE: first call ILUT or ILU0 or MILU0 to set up preconditioner and +! then call pgmres. +! +! Modified: +! +! 07 January 2004 +! +! Author: +! +! Youcef Saad +! +! Parameters: +! +! Input, integer ( kind = 4 ) N, the order of the matrix. +! +! Input, integer ( kind = 4 ) IM, the size of the Krylov subspace. IM +! should not exceed 50 in this version. This restriction can be reset by +! changing the parameter command for KMAX below. +! +! Input/output, real RHS(N), on input, the right hand side vector. +! On output, the information in this vector has been destroyed. +! +! sol == real vector of length n containing an initial guess to the +! solution on input. approximate solution on output +! +! eps == tolerance for stopping criterion. process is stopped +! as soon as ( ||.|| is the euclidean norm): +! || current residual||/||initial residual|| <= eps +! +! maxits== maximum number of iterations allowed +! +! iout == output unit number number for printing intermediate results +! if (iout <= 0) nothing is printed out. +! +! Input, real AA(*), integer ( kind = 4 ) JA(*), IA(N+1), the matrix in CSR +! Compressed Sparse Row format. +! +! +! alu,jlu== A matrix stored in Modified Sparse Row format containing +! the L and U factors, as computed by routine ilut. +! +! ju == integer ( kind = 4 ) array of length n containing the pointers to +! the beginning of each row of U in alu, jlu as computed +! by routine ILUT. +! +! on return: +! +! sol == contains an approximate solution (upon successful return). +! ierr == integer ( kind = 4 ). Error message with the following meaning. +! ierr = 0 --> successful return. +! ierr = 1 --> convergence not achieved in itmax iterations. +! ierr =-1 --> the initial guess seems to be the exact +! solution (initial residual computed was zero) +! +! work arrays: +! +! vv == work array of length n x (im+1) (used to store the Arnoli +! basis) +! + integer ( kind = 4 ), parameter :: kmax = 50 + integer ( kind = 4 ) n + + real ( kind = 8 ) aa(*) + real ( kind = 8 ) alu(*) + real ( kind = 8 ) c(kmax) + real ( kind = 8 ) eps + real ( kind = 8 ) eps1 + real ( kind = 8 ), parameter :: epsmac = 1.0D-16 + real ( kind = 8 ) gam + real ( kind = 8 ) hh(kmax+1,kmax) + integer ( kind = 4 ) i + integer ( kind = 4 ) i1 + integer ( kind = 4 ) ia(n+1) + integer ( kind = 4 ) ierr + integer ( kind = 4 ) ii + integer ( kind = 4 ) im + integer ( kind = 4 ) iout + integer ( kind = 4 ) its + integer ( kind = 4 ) j + integer ( kind = 4 ) ja(*) + integer ( kind = 4 ) jj + integer ( kind = 4 ) jlu(*) + integer ( kind = 4 ) ju(*) + integer ( kind = 4 ) k + integer ( kind = 4 ) k1 + integer ( kind = 4 ) maxits + integer ( kind = 4 ) n1 + real ( kind = 8 ) rhs(n) + real ( kind = 8 ) ro + real ( kind = 8 ) rs(kmax+1) + real ( kind = 8 ) s(kmax) + real ( kind = 8 ) sol(n) + real ( kind = 8 ) t + real ( kind = 8 ) vv(n,*) +! +! Arnoldi size should not exceed KMAX=50 in this version. +! To reset modify parameter KMAX accordingly. +! + n1 = n + 1 + its = 0 +! +! Outer loop starts here. +! Compute initial residual vector. +! + call ope ( n, sol, vv, aa, ja, ia ) + + vv(1:n,1) = rhs(1:n) - vv(1:n,1) + + do + + ro = sqrt ( ddot ( n, vv, 1, vv, 1 ) ) + + if ( 0 < iout .and. its == 0 ) then + write(iout, 199) its, ro + end if + + if ( ro == 0.0D+00 ) then + ierr = -1 + exit + end if + + t = 1.0D+00 / ro + vv(1:n,1) = vv(1:n,1) * t + + if ( its == 0 ) then + eps1 = eps * ro + end if +! +! Initialize first term of RHS of Hessenberg system. +! + rs(1) = ro + i = 0 + + 4 continue + + i = i + 1 + its = its + 1 + i1 = i + 1 + call lusol0 ( n, vv(1,i), rhs, alu, jlu, ju ) + call ope ( n, rhs, vv(1,i1), aa, ja, ia ) +! +! Modified Gram - Schmidt. +! + do j = 1, i + t = ddot ( n, vv(1,j), 1, vv(1,i1), 1 ) + hh(j,i) = t + call daxpy ( n, -t, vv(1,j), 1, vv(1,i1), 1 ) + end do + + t = sqrt ( ddot ( n, vv(1,i1), 1, vv(1,i1), 1 ) ) + hh(i1,i) = t + + if ( t /= 0.0D+00 ) then + t = 1.0D+00 / t + vv(1:n,i1) = vv(1:n,i1) * t + end if +! +! Update factorization of HH. +! + if ( i == 1 ) then + go to 121 + end if +! +! Perform previous transformations on I-th column of H. +! + do k = 2, i + k1 = k-1 + t = hh(k1,i) + hh(k1,i) = c(k1) * t + s(k1) * hh(k,i) + hh(k,i) = -s(k1) * t + c(k1) * hh(k,i) + end do + +121 continue + + gam = sqrt ( hh(i,i)**2 + hh(i1,i)**2 ) +! +! If GAMMA is zero then any small value will do. +! It will affect only residual estimate. +! + if ( gam == 0.0D+00 ) then + gam = epsmac + end if +! +! Get the next plane rotation. +! + c(i) = hh(i,i) / gam + s(i) = hh(i1,i) / gam + rs(i1) = -s(i) * rs(i) + rs(i) = c(i) * rs(i) +! +! Determine residual norm and test for convergence. +! + hh(i,i) = c(i) * hh(i,i) + s(i) * hh(i1,i) + ro = abs ( rs(i1) ) +131 format(1h ,2e14.4) + + if ( 0 < iout ) then + write(iout, 199) its, ro + end if + + if ( i < im .and. eps1 < ro ) then + go to 4 + end if +! +! Now compute solution. First solve upper triangular system. +! + rs(i) = rs(i) / hh(i,i) + + do ii = 2, i + k = i - ii + 1 + k1 = k + 1 + t = rs(k) + do j = k1, i + t = t - hh(k,j) * rs(j) + end do + rs(k) = t / hh(k,k) + end do +! +! Form linear combination of V(*,i)'s to get solution. +! + t = rs(1) + rhs(1:n) = vv(1:n,1) * t + + do j = 2, i + t = rs(j) + rhs(1:n) = rhs(1:n) + t * vv(1:n,j) + end do +! +! Call preconditioner. +! + call lusol0 ( n, rhs, rhs, alu, jlu, ju ) + + sol(1:n) = sol(1:n) + rhs(1:n) +! +! Restart outer loop when necessary. +! + if ( ro <= eps1 ) then + ierr = 0 + exit + end if + + if ( maxits < its ) then + ierr = 1 + exit + end if +! +! Else compute residual vector and continue. +! + do j = 1, i + jj = i1 - j + 1 + rs(jj-1) = -s(jj-1) * rs(jj) + rs(jj) = c(jj-1) * rs(jj) + end do + + do j = 1, i1 + t = rs(j) + if ( j == 1 ) then + t = t - 1.0D+00 + end if + call daxpy ( n, t, vv(1,j), 1, vv, 1 ) + end do + +199 format(' its =', i4, ' res. norm =', G14.6) + + end do + + return +end + +subroutine ilut ( n, a, ja, ia, lfil, tol, alu, jlu, ju, iwk, wu, wl, jr, & + jwl, jwu, ierr ) + +!*****************************************************************************80 +! +!! ILUT is an ILUT preconditioner. +! +! Discussion: +! +! This routine carries ouot incomplete LU factorization with dual +! truncation mechanism. Sorting is done for both L and U. +! +! The dual drop-off strategy works as follows: +! +! 1) Theresholding in L and U as set by TOL. Any element whose size +! is less than some tolerance (relative to the norm of current +! row in u) is dropped. +! +! 2) Keeping only the largest lenl0+lfil elements in L and the +! largest lenu0+lfil elements in U, where lenl0=initial number +! of nonzero elements in a given row of lower part of A +! and lenlu0 is similarly defined. +! +! Flexibility: one can use tol=0 to get a strategy based on keeping the +! largest elements in each row of L and U. Taking tol /= 0 but lfil=n +! will give the usual threshold strategy (however, fill-in is then +! unpredictible). +! +! A must have all nonzero diagonal elements. +! +! Modified: +! +! 07 January 2004 +! +! Author: +! +! Youcef Saad +! +! Parameters: +! +! Parameters: +! +! Input, integer ( kind = 4 ) N, the order of the matrix. +! +! Input, real A(*), integer ( kind = 4 ) JA(*), IA(N+1), the matrix in CSR +! Compressed Sparse Row format. +! +! lfil = integer ( kind = 4 ). The fill-in parameter. Each row of L and +! each row of U will have a maximum of lfil elements +! in addition to the original number of nonzero elements. +! Thus storage can be determined beforehand. +! lfil must be >= 0. +! +! iwk = integer ( kind = 4 ). The minimum length of arrays alu and jlu +! +! On return: +! +! alu,jlu = matrix stored in Modified Sparse Row (MSR) format containing +! the L and U factors together. The diagonal (stored in +! alu(1:n) ) is inverted. Each i-th row of the alu,jlu matrix +! contains the i-th row of L (excluding the diagonal entry=1) +! followed by the i-th row of U. +! +! ju = integer ( kind = 4 ) array of length n containing the pointers to +! the beginning of each row of U in the matrix alu,jlu. +! +! ierr = integer ( kind = 4 ). Error message with the following meaning. +! ierr = 0 --> successful return. +! ierr > 0 --> zero pivot encountered at step number ierr. +! ierr = -1 --> Error. input matrix may be wrong. +! (The elimination process has generated a +! row in L or U whose length is > n.) +! ierr = -2 --> The matrix L overflows the array al. +! ierr = -3 --> The matrix U overflows the array alu. +! ierr = -4 --> Illegal value for lfil. +! ierr = -5 --> zero pivot encountered. +! +! work arrays: +! +! jr,jwu,jwl, integer ( kind = 4 ) work arrays of length n. +! wu, wl, real work arrays of length n+1, and n resp. +! + integer ( kind = 4 ) n + + real ( kind = 8 ) a(*) + real ( kind = 8 ) alu(*) + real ( kind = 8 ) fact + integer ( kind = 4 ) ia(n+1) + integer ( kind = 4 ) idiag + integer ( kind = 4 ) ierr + integer ( kind = 4 ) ii + integer ( kind = 4 ) iwk + integer ( kind = 4 ) j + integer ( kind = 4 ) j1 + integer ( kind = 4 ) j2 + integer ( kind = 4 ) ja(*) + integer ( kind = 4 ) jj + integer ( kind = 4 ) jlu(*) + integer ( kind = 4 ) jpos + integer ( kind = 4 ) jr(*) + integer ( kind = 4 ) jrow + integer ( kind = 4 ) ju(*) + integer ( kind = 4 ) ju0 + integer ( kind = 4 ) jwl(n) + integer ( kind = 4 ) jwu(n) + integer ( kind = 4 ) k + integer ( kind = 4 ) len + integer ( kind = 4 ) lenl + integer ( kind = 4 ) lenl0 + integer ( kind = 4 ) lenu + integer ( kind = 4 ) lenu0 + integer ( kind = 4 ) lfil + integer ( kind = 4 ) nl + real ( kind = 8 ) s + real ( kind = 8 ) t + real ( kind = 8 ) tnorm + real ( kind = 8 ) tol + real ( kind = 8 ) wl(n) + real ( kind = 8 ) wu(n+1) + + if ( lfil < 0 ) then + ierr = -4 + return + end if +! +! Initialize JU0 (points to next element to be added to ALU, JLU) +! and pointer. +! + ju0 = n + 2 + jlu(1) = ju0 +! +! integer ( kind = 4 ) double pointer array. +! + jr(1:n) = 0 +! +! The main loop. +! + do ii = 1, n + + j1 = ia(ii) + j2 = ia(ii+1) - 1 + lenu = 0 + lenl = 0 + + tnorm = 0.0D+00 + do k = j1, j2 + tnorm = tnorm + abs ( a(k) ) + end do + tnorm = tnorm / real ( j2-j1+1, kind = 8 ) +! +! Unpack L-part and U-part of row of A in arrays WL, WU. +! + do j = j1, j2 + + k = ja(j) + t = a(j) + + if ( tol * tnorm <= abs ( t ) ) then + + if ( k < ii ) then + lenl = lenl + 1 + jwl(lenl) = k + wl(lenl) = t + jr(k) = lenl + else + lenu = lenu+1 + jwu(lenu) = k + wu(lenu) = t + jr(k) = lenu + end if + + end if + + end do + + lenl0 = lenl + lenu0 = lenu + jj = 0 + nl = 0 +! +! Eliminate previous rows. +! +150 continue + + jj = jj + 1 + + if ( lenl < jj ) then + go to 160 + end if +! +! In order to do the elimination in the correct order we need to +! exchange the current row number with the one that has +! smallest column number, among JJ, JJ+1, ..., LENL. +! + jrow = jwl(jj) + k = jj +! +! Determine the smallest column index. +! + do j = jj+1, lenl + if ( jwl(j) < jrow ) then + jrow = jwl(j) + k = j + end if + end do +! +! Exchange in JWL. +! + j = jwl(jj) + jwl(jj) = jrow + jwl(k) = j +! +! Exchange in JR. +! + jr(jrow) = jj + jr(j) = k +! +! Exchange in WL. +! + s = wl(k) + wl(k) = wl(jj) + wl(jj) = s + + if ( ii <= jrow ) then + go to 160 + end if +! +! Get the multiplier for row to be eliminated: JROW. +! + fact = wl(jj) * alu(jrow) + jr(jrow) = 0 + + if ( abs ( fact ) * wu(n+2-jrow) <= tol * tnorm ) then + go to 150 + end if +! +! Combine current row and row JROW. +! + do k = ju(jrow), jlu(jrow+1)-1 + s = fact * alu(k) + j = jlu(k) + jpos = jr(j) +! +! If fill-in element and small disregard. +! + if ( abs ( s ) < tol * tnorm .and. jpos == 0 ) then + cycle + end if + + if ( ii <= j ) then +! +! Dealing with upper part. +! + if ( jpos == 0 ) then +! +! This is a fill-in element. +! + lenu = lenu + 1 + + if ( n < lenu ) then + go to 995 + end if + + jwu(lenu) = j + jr(j) = lenu + wu(lenu) = - s + else +! +! No fill-in element. +! + wu(jpos) = wu(jpos) - s + end if + else +! +! Dealing with lower part. +! + if ( jpos == 0 ) then +! +! This is a fill-in element. +! + lenl = lenl + 1 + + if ( n < lenl ) then + go to 995 + end if + + jwl(lenl) = j + jr(j) = lenl + wl(lenl) = -s + else +! +! No fill-in element. +! + wl(jpos) = wl(jpos) - s + end if + end if + + end do + + nl = nl + 1 + wl(nl) = fact + jwl(nl) = jrow + go to 150 +! +! Update the L matrix. +! + 160 continue + + len = min ( nl, lenl0 + lfil ) + + call bsort2 ( wl, jwl, nl, len ) + + do k = 1, len + + if ( iwk < ju0 ) then + ierr = -2 + return + end if + + alu(ju0) = wl(k) + jlu(ju0) = jwl(k) + ju0 = ju0 + 1 + + end do +! +! Save pointer to beginning of row II of U. +! + ju(ii) = ju0 +! +! Reset double pointer JR to zero (L-part - except first +! JJ-1 elements which have already been reset). +! + do k = jj, lenl + jr(jwl(k)) = 0 + end do +! +! Be sure that the diagonal element is first in W and JW. +! + idiag = jr(ii) + + if ( idiag == 0 ) then + go to 900 + end if + + if ( idiag /= 1 ) then + + s = wu(1) + wu(j) = wu(idiag) + wu(idiag) = s + + j = jwu(1) + jwu(1) = jwu(idiag) + jwu(idiag) = j + + end if + + len = min ( lenu, lenu0 + lfil ) + + call bsort2 ( wu(2), jwu(2), lenu-1, len ) +! +! Update the U-matrix. +! + t = 0.0D+00 + + do k = 2, len + + if ( iwk < ju0 ) then + ierr = -3 + return + end if + + jlu(ju0) = jwu(k) + alu(ju0) = wu(k) + t = t + abs ( wu(k) ) + ju0 = ju0 + 1 + + end do +! +! Save norm in WU (backwards). Norm is in fact average absolute value. +! + wu(n+2-ii) = t / real ( len + 1, kind = 8 ) +! +! Store inverse of diagonal element of U. +! + if ( wu(1) == 0.0D+00 ) then + ierr = -5 + return + end if + + alu(ii) = 1.0D+00 / wu(1) +! +! Update pointer to beginning of next row of U. +! + jlu(ii+1) = ju0 +! +! Reset double pointer JR to zero (U-part). +! + do k = 1, lenu + jr(jwu(k)) = 0 + end do + + end do + + ierr = 0 + + return +! +! Zero pivot : +! + 900 ierr = ii + return +! +! Incomprehensible error. Matrix must be wrong. +! + 995 ierr = -1 + return +end + +subroutine amux ( n, x, y, a, ja, ia ) + +!*****************************************************************************80 +! +!! AMUX multiplies a CSR matrix A times a vector. +! +! Discussion: +! +! This routine multiplies a matrix by a vector using the dot product form. +! Matrix A is stored in compressed sparse row storage. +! +! Modified: +! +! 07 January 2004 +! +! Author: +! +! Youcef Saad +! +! Parameters: +! +! Input, integer ( kind = 4 ) N, the row dimension of the matrix. +! +! Input, real X(*), and array of length equal to the column dimension +! of A. +! +! Input, real A(*), integer ( kind = 4 ) JA(*), IA(NROW+1), the matrix in CSR +! Compressed Sparse Row format. +! +! Output, real Y(N), the product A * X. +! + integer ( kind = 4 ) n + + real ( kind = 8 ) a(*) + integer ( kind = 4 ) i + integer ( kind = 4 ) ia(*) + integer ( kind = 4 ) ja(*) + integer ( kind = 4 ) k + real ( kind = 8 ) t + real ( kind = 8 ) x(*) + real ( kind = 8 ) y(n) + + do i = 1, n +! +! Compute the inner product of row I with vector X. +! + t = 0.0D+00 + do k = ia(i), ia(i+1)-1 + t = t + a(k) * x(ja(k)) + end do + + y(i) = t + + end do + + return +end +subroutine amuxd ( n, x, y, diag, ndiag, idiag, ioff ) + +!*****************************************************************************80 +! +!! AMUXD multiplies a DIA matrix times a vector. +! +! Discussion: +! +! This routine multiplies a matrix by a vector when the original matrix +! is stored in the DIA diagonal storage format. +! +! Modified: +! +! 07 January 2004 +! +! Author: +! +! Youcef Saad +! +! Parameters: +! +! Input, integer ( kind = 4 ) N, the row dimension of the matrix. +! +! Input, real X(*), array of length equal to the column dimension of +! the A matrix. +! +! Output, real Y(N), the product A * X. +! +! Input, real DIAG(NDIAG,IDIAG), the diagonals. +! +! Input, integer ( kind = 4 ) NDIAG, the first dimension of array adiag as +! declared in the calling program. +! +! Input, integer ( kind = 4 ) IDIAG, the number of diagonals in the matrix. +! +! Input, integer ( kind = 4 ) IOFF(IDIAG), the offsets of the diagonals of +! the matrix: diag(i,k) contains the element a(i,i+ioff(k)) of the matrix. +! + integer ( kind = 4 ) idiag + integer ( kind = 4 ) n + integer ( kind = 4 ) ndiag + + real ( kind = 8 ) diag(ndiag,idiag) + integer ( kind = 4 ) i1 + integer ( kind = 4 ) i2 + integer ( kind = 4 ) io + integer ( kind = 4 ) ioff(idiag) + integer ( kind = 4 ) j + integer ( kind = 4 ) k + real ( kind = 8 ) x(n) + real ( kind = 8 ) y(n) + + y(1:n) = 0.0D+00 + + do j = 1, idiag + io = ioff(j) + i1 = max ( 1, 1 - io ) + i2 = min ( n, n - io ) + do k = i1, i2 + y(k) = y(k) + diag(k,j) * x(k+io) + end do + end do + + return +end + +subroutine daxpy ( n, da, dx, incx, dy, incy ) + +!*****************************************************************************80 +! +!! DAXPY computes constant times a vector plus a vector. +! +! Discussion: +! +! Uses unrolled loops for increments equal to one. +! +! Author: +! +! Jack Dongarra +! +! Reference: +! +! Dongarra, Moler, Bunch, Stewart, +! LINPACK User's Guide, +! SIAM, 1979. +! +! Lawson, Hanson, Kincaid, Krogh, +! Basic Linear Algebra Subprograms for Fortran Usage, +! Algorithm 539, +! ACM Transactions on Mathematical Software, +! Volume 5, Number 3, September 1979, pages 308-323. +! +! Parameters: +! +! Input, integer ( kind = 4 ) N, the number of elements in DX and DY. +! +! Input, real ( kind = 8 ) DA, the multiplier of DX. +! +! Input, real ( kind = 8 ) DX(*), the first vector. +! +! Input, integer ( kind = 4 ) INCX, the increment between successive entries of DX. +! +! Input/output, real ( kind = 8 ) DY(*), the second vector. +! On output, DY(*) has been replaced by DY(*) + DA * DX(*). +! +! Input, integer ( kind = 4 ) INCY, the increment between successive entries of DY. +! + real ( kind = 8 ) da + real ( kind = 8 ) dx(*) + real ( kind = 8 ) dy(*) + integer ( kind = 4 ) i + integer ( kind = 4 ) incx + integer ( kind = 4 ) incy + integer ( kind = 4 ) ix + integer ( kind = 4 ) iy + integer ( kind = 4 ) m + integer ( kind = 4 ) n + + if ( n <= 0 ) then + return + end if + + if ( da == 0.0D+00 ) then + return + end if +! +! Code for unequal increments or equal increments +! not equal to 1. +! + if ( incx /= 1 .or. incy /= 1 ) then + + if ( 0 <= incx ) then + ix = 1 + else + ix = ( - n + 1 ) * incx + 1 + end if + + if ( 0 <= incy ) then + iy = 1 + else + iy = ( - n + 1 ) * incy + 1 + end if + + do i = 1, n + dy(iy) = dy(iy) + da * dx(ix) + ix = ix + incx + iy = iy + incy + end do +! +! Code for both increments equal to 1. +! + else + + m = mod ( n, 4 ) + + do i = 1, m + dy(i) = dy(i) + da * dx(i) + end do + + do i = m+1, n, 4 + dy(i ) = dy(i ) + da * dx(i ) + dy(i+1) = dy(i+1) + da * dx(i+1) + dy(i+2) = dy(i+2) + da * dx(i+2) + dy(i+3) = dy(i+3) + da * dx(i+3) + end do + + end if + + return +end + +subroutine bsort2 ( w, ind, n, ncut ) + +!*****************************************************************************80 +! +!! BSORT2 returns the NCUT largest elements of an array, using bubble sort. +! +! Discussion: +! +! This routine carries out a simple bubble sort for getting the NCUT largest +! elements in modulus, in array W. IND is sorted accordingly. +! (Ought to be replaced by a more efficient sort especially +! if NCUT is not that small). +! +! Modified: +! +! 07 January 2004 +! +! Author: +! +! Youcef Saad +! +! Parameters: +! + integer ( kind = 4 ) n + + integer ( kind = 4 ) i + integer ( kind = 4 ) ind(*) + integer ( kind = 4 ) iswp + integer ( kind = 4 ) j + integer ( kind = 4 ) ncut + logical test + real ( kind = 8 ) w(n) + real ( kind = 8 ) wswp + + i = 1 + + do + + test = .false. + + do j = n-1, i, -1 + + if ( abs ( w(j) ) < abs ( w(j+1) ) ) then +! +! Swap. +! + wswp = w(j) + w(j) = w(j+1) + w(j+1) = wswp +! +! Reorder the original ind array accordingly. +! + iswp = ind(j) + ind(j) = ind(j+1) + ind(j+1) = iswp +! +! Set indicator that sequence is still unsorted. +! + test = .true. + + end if + + end do + + i = i + 1 + + if ( .not. test .or. ncut < i ) then + exit + end if + + end do + + return +end + +subroutine ope ( n, x, y, a, ja, ia ) + +!*****************************************************************************80 +! +!! OPE sparse matrix * vector multiplication +! +! Modified: +! +! 07 January 2004 +! +! Author: +! +! Youcef Saad +! +! Parameters: +! +! Input, integer ( kind = 4 ) N, the order of the matrix. +! +! Input, real X(N), the vector to be multiplied. +! +! Output, real Y(N), the product A * X. +! +! Input, real A(*), integer ( kind = 4 ) JA(*), IA(N+1), the matrix in CSR +! Compressed Sparse Row format. +! + integer ( kind = 4 ) n + + real ( kind = 8 ) a(*) + integer ( kind = 4 ) i + integer ( kind = 4 ) ia(n+1) + integer ( kind = 4 ) ja(*) + integer ( kind = 4 ) k + integer ( kind = 4 ) k1 + integer ( kind = 4 ) k2 + real ( kind = 8 ) x(n) + real ( kind = 8 ) y(n) + + do i = 1, n + k1 = ia(i) + k2 = ia(i+1) -1 + y(i) = 0.0D+00 + do k = k1, k2 + y(i) = y(i) + a(k) * x(ja(k)) + end do + end do + + return +end + +subroutine lusol0 ( n, y, x, alu, jlu, ju ) + +!*****************************************************************************80 +! +!! LUSOL0 performs a forward followed by a backward solve +! for LU matrix as produced by ILUT +! +! Modified: +! +! 07 January 2004 +! +! Author: +! +! Youcef Saad +! +! Parameters: +! +! Input, integer ( kind = 4 ) N, the order of the matrix. +! +! Input, real Y(N), the right hand side of the linear system. +! +! Output, real X(N), the solution. +! +! ALU, JLU, JU, ... +! + integer ( kind = 4 ) n + + real ( kind = 8 ) alu(*) + integer ( kind = 4 ) i + integer ( kind = 4 ) jlu(*) + integer ( kind = 4 ) ju(*) + integer ( kind = 4 ) k + real ( kind = 8 ) x(n) + real ( kind = 8 ) y(n) +! +! Forward solve +! + do i = 1, n + x(i) = y(i) + do k = jlu(i), ju(i)-1 + x(i) = x(i) - alu(k) * x(jlu(k)) + end do + end do +! +! Backward solve. +! + do i = n, 1, -1 + do k = ju(i), jlu(i+1)-1 + x(i) = x(i) - alu(k) * x(jlu(k)) + end do + x(i) = alu(i) * x(i) + end do + + return +end + +function ddot ( n, dx, incx, dy, incy ) + +!*****************************************************************************80 +! +!! DDOT forms the dot product of two vectors. +! +! Discussion: +! +! This routine uses unrolled loops for increments equal to one. +! +! Author: +! +! Jack Dongarra +! +! Reference: +! +! Dongarra, Moler, Bunch, Stewart, +! LINPACK User's Guide, +! SIAM, 1979. +! +! Parameters: +! +! Input, integer ( kind = 4 ) N, the number of entries in the vectors. +! +! Input, real ( kind = 8 ) DX(*), the first vector. +! +! Input, integer ( kind = 4 ) INCX, the increment between successive entries in X. +! +! Input, real ( kind = 8 ) DY(*), the second vector. +! +! Input, integer ( kind = 4 ) INCY, the increment between successive entries in Y. +! +! Output, real DDOT, the sum of the product of the corresponding +! entries of X and Y. +! + real ( kind = 8 ) ddot + real ( kind = 8 ) dtemp + real ( kind = 8 ) dx(*) + real ( kind = 8 ) dy(*) + integer ( kind = 4 ) i + integer ( kind = 4 ) incx + integer ( kind = 4 ) incy + integer ( kind = 4 ) ix + integer ( kind = 4 ) iy + integer ( kind = 4 ) m + integer ( kind = 4 ) n + + ddot = 0.0D+00 + dtemp = 0.0D+00 + + if ( n <= 0 ) then + return + end if +! +! Code for unequal increments or equal increments +! not equal to 1. +! + if ( incx /= 1 .or. incy /= 1 ) then + + if ( 0 <= incx ) then + ix = 1 + else + ix = ( - n + 1 ) * incx + 1 + end if + + if ( 0 <= incy ) then + iy = 1 + else + iy = ( - n + 1 ) * incy + 1 + end if + + do i = 1, n + dtemp = dtemp + dx(ix) * dy(iy) + ix = ix + incx + iy = iy + incy + end do +! +! Code for both increments equal to 1. +! + else + + m = mod ( n, 5 ) + + do i = 1, m + dtemp = dtemp + dx(i) * dy(i) + end do + + do i = m+1, n, 5 + + dtemp = dtemp + dx(i ) * dy(i ) & + + dx(i+1) * dy(i+1) & + + dx(i+2) * dy(i+2) & + + dx(i+3) * dy(i+3) & + + dx(i+4) * dy(i+4) + end do + + end if + + ddot = dtemp + + return +end + +end module gmres_solver diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 617788e472..a0731ee70b 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -557,16 +557,10 @@ contains end do end if - ! check for cmfd run + ! Check for cmfd run call lower_case(run_cmfd_) if (run_cmfd_ == 'true' .or. run_cmfd_ == '1') then cmfd_run = .true. -#ifndef PETSC - if (master) then - message = 'CMFD is not available, compile OpenMC with PETSc' - call fatal_error() - end if -#endif end if end subroutine read_settings_xml diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index fe5c2c5bcf..c034c8d2e7 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -12,8 +12,8 @@ module matrix_header integer :: nnz ! number of nonzeros in matrix integer :: n_count ! counter for length of matrix integer :: nz_count ! counter for number of non zeros - integer, private, allocatable :: row(:) ! csr row vector - integer, private, allocatable :: col(:) ! column vector + integer, allocatable :: row(:) ! csr row vector + integer, allocatable :: col(:) ! column vector real(8), allocatable :: val(:) ! matrix value vector # ifdef PETSC Mat :: petsc_mat diff --git a/src/petsc_solver.F90 b/src/solver_interface.F90 similarity index 51% rename from src/petsc_solver.F90 rename to src/solver_interface.F90 index 87c288b936..47bb0c14ab 100644 --- a/src/petsc_solver.F90 +++ b/src/solver_interface.F90 @@ -1,8 +1,14 @@ -module petsc_solver +module solver_interface + use error, only: fatal_error + use global, only: message use matrix_header, only: Matrix use vector_header, only: Vector +#ifndef PETSC + use gmres_solver, only: ilut, pgmres +#endif + implicit none private @@ -10,18 +16,44 @@ module petsc_solver # include #endif - ! PETSc GMRES solver type - type, public :: Petsc_gmres + ! GMRES solver type + type, public :: GMRESSolver #ifdef PETSC KSP :: ksp PC :: pc +#else + integer :: im ! iterations before restart + integer :: iout ! unit number for printing gmres iters + integer :: maxits ! maximum iterations + integer :: lfil ! fill in parameter + integer :: iwk ! size of MSR storage + integer, allocatable :: ju(:) ! row pointers for ILUT result + integer, allocatable :: jlu(:) ! MSR storage of j + integer, allocatable :: jr(:) ! temp work array + integer, allocatable :: jwu(:) ! temp work array + integer, allocatable :: jwl(:) ! temp work array + real(8) :: tol ! gmres tolerance + real(8) :: droptol ! ILU drop tolerance + real(8), allocatable :: alu(:) ! MSR storage of ILU A + real(8), allocatable :: vv(:) ! GMRES work array + real(8), allocatable :: wu(:) ! temp work array + real(8), allocatable :: wl(:) ! temp work array + real(8), allocatable :: rhs(:) ! copy of rhs array + type(Matrix), pointer :: A ! coefficient matrix #endif contains +#ifdef PETSC + procedure :: create => petsc_gmres_create + procedure :: set_oper => petsc_gmres_set_oper + procedure :: destroy => petsc_gmres_destroy + procedure :: solve => petsc_gmres_solve +#else procedure :: create => gmres_create procedure :: set_oper => gmres_set_oper procedure :: destroy => gmres_destroy procedure :: solve => gmres_solve - end type Petsc_gmres +#endif + end type GMRESSolver ! Derived type to contain list of data needed to be passed to procedures type, public :: Jfnk_ctx @@ -29,8 +61,8 @@ module petsc_solver procedure (jac_interface), pointer, nopass :: jac_proc_ptr end type Jfnk_ctx - ! Petsc SNES JFNK solver type - type, public :: Petsc_jfnk + ! JFNK solver type + type, public :: JFNKSolver #ifdef PETSC KSP :: ksp PC :: pc @@ -39,13 +71,18 @@ module petsc_solver SNESLineSearch :: ls #endif contains +#ifdef PETSC + procedure :: create => petsc_jfnk_create + procedure :: destroy => petsc_jfnk_destroy + procedure :: set_functions => petsc_jfnk_set_functions + procedure :: solve => petsc_jfnk_solve +#else procedure :: create => jfnk_create procedure :: destroy => jfnk_destroy procedure :: set_functions => jfnk_set_functions procedure :: solve => jfnk_solve - end type Petsc_jfnk - - integer :: petsc_err +#endif + end type JFNKSolver ! Abstract interface stating how jacobian and residual routines look abstract interface @@ -61,30 +98,28 @@ module petsc_solver end subroutine jac_interface end interface +#ifdef PETSC + integer :: petsc_err ! petsc error code +#endif + contains +#ifndef PETSC !=============================================================================== ! GMRES_CREATE sets up a PETSc GMRES solver !=============================================================================== subroutine gmres_create(self) - class(Petsc_gmres) :: self + class(GMRESSolver) :: self - integer :: ilu_levels = 5 - real(8) :: rtol = 1.0e-10_8 - real(8) :: atol = 1.0e-10_8 - -#ifdef PETSC - call KSPCreate(PETSC_COMM_WORLD, self % ksp, petsc_err) - call KSPSetTolerances(self % ksp, rtol, atol, & - PETSC_DEFAULT_DOUBLE_PRECISION, PETSC_DEFAULT_INTEGER, petsc_err) - call KSPSetType(self % ksp, KSPGMRES, petsc_err) - call KSPSetInitialGuessNonzero(self % ksp, PETSC_TRUE, petsc_err) - call KSPGetPC(self % ksp, self % pc, petsc_err) - call PCFactorSetLevels(self % pc, ilu_levels, petsc_err) - call KSPSetFromOptions(self % ksp, petsc_err) -#endif + ! Set up default values + self % im = 40 + self % iout = 0 + self % maxits = 1000 + self % lfil = 0 + self % tol = 1.e-10_8 + self % droptol = 0.0_8 end subroutine gmres_create @@ -94,15 +129,35 @@ contains subroutine gmres_set_oper(self, prec_mat, mat_in) - class(Petsc_gmres) :: self + class(GMRESSolver) :: self type(Matrix) :: prec_mat - type(Matrix) :: mat_in + type(Matrix), target :: mat_in -#ifdef PETSC - call KSPSetOperators(self % ksp, mat_in % petsc_mat, prec_mat % petsc_mat, & - SAME_NONZERO_PATTERN, petsc_err) - call KSPSetUp(self % ksp, petsc_err) -#endif + integer :: ierr + + ! Set pointer to matrix + self % A => mat_in + + ! Set up data + self % iwk = self % A % nnz + 1 + + ! Set up arrays + allocate(self % ju(self % A % n)) + allocate(self % jlu(self % iwk)) + allocate(self % alu(self % iwk)) + allocate(self % jr(self % A % n)) + allocate(self % jwu(self % A % n)) + allocate(self % jwl(self % A % n)) + allocate(self % vv(self % A % n * (self % im + 1))) + allocate(self % wu(self % A % n + 1)) + allocate(self % wl(self % A % n)) + allocate(self % rhs(self % A % n)) + + ! Perform ILU Preconditioning + call ilut(self % A % n, self % A % val, self % A % col, self % A % row, & + self % lfil, self % droptol, self % alu, self % jlu, self % ju, & + self % iwk, self % wu, self % wl, self % jr, self % jwl, & + self % jwu, ierr) end subroutine gmres_set_oper @@ -112,11 +167,19 @@ contains subroutine gmres_destroy(self) - class(Petsc_gmres) :: self + class(GMRESSolver) :: self -#ifdef PETSC - call KSPDestroy(self % ksp, petsc_err) -#endif + ! Destroy all arrays associated with GMRES solver + deallocate(self % ju) + deallocate(self % jlu) + deallocate(self % alu) + deallocate(self % jr) + deallocate(self % jwu) + deallocate(self % jwl) + deallocate(self % vv) + deallocate(self % wu) + deallocate(self % wl) + deallocate(self % rhs) end subroutine gmres_destroy @@ -126,13 +189,20 @@ contains subroutine gmres_solve(self, b, x) - class(Petsc_gmres) :: self + class(GMRESSolver) :: self type(Vector) :: b type(Vector) :: x -#ifdef PETSC - call KSPSolve(self % ksp, b % petsc_vec, x % petsc_vec, petsc_err) -#endif + integer :: ierr ! error code + + ! Copy rhs + self % rhs = b % val + + ! Perform GMRES calculation + call pgmres(self % A % n, self % im, self % rhs, x % val, self % vv, & + self % tol, self % maxits, self % iout, self % A % val, & + self % A % col, self % A % row, self % alu, self % jlu, & + self % ju, ierr) end subroutine gmres_solve @@ -142,9 +212,130 @@ contains subroutine jfnk_create(self) - class(Petsc_jfnk) :: self + class(JFNKSolver) :: self + + message = 'JFNK cannot be used without PETSc.' + call fatal_error() + + end subroutine jfnk_create + +!=============================================================================== +! JFNK_DESTROY frees all memory associated with JFNK solver +!=============================================================================== + + subroutine jfnk_destroy(self) + + class(JFNKSolver) :: self + + message = 'JFNK cannot be used without PETSc.' + call fatal_error() + + end subroutine jfnk_destroy + +!=============================================================================== +! JFNK_SET_FUNCTIONS sets user functions and matrix free objects +!=============================================================================== + + subroutine jfnk_set_functions(self, ctx, res, jac_prec) + + class(JFNKSolver) :: self + type(Jfnk_ctx) :: ctx + type(Vector) :: res + type(Matrix) :: jac_prec + + message = 'JFNK cannot be used without PETSc.' + call fatal_error() + + end subroutine jfnk_set_functions + +!=============================================================================== +! JFNK_SOLVE solves the nonlinear system +!=============================================================================== + + subroutine jfnk_solve(self, xvec) + + class(JFNKSolver) :: self + type(Vector) :: xvec + + message = 'JFNK cannot be used without PETSc.' + call fatal_error() + + end subroutine jfnk_solve + +#else +!=============================================================================== +! PETSC_GMRES_CREATE sets up a PETSc GMRES solver +!=============================================================================== + + subroutine petsc_gmres_create(self) + + class(GMRESSolver) :: self + + integer :: ilu_levels = 5 + real(8) :: rtol = 1.0e-10_8 + real(8) :: atol = 1.0e-10_8 + + call KSPCreate(PETSC_COMM_WORLD, self % ksp, petsc_err) + call KSPSetTolerances(self % ksp, rtol, atol, & + PETSC_DEFAULT_DOUBLE_PRECISION, PETSC_DEFAULT_INTEGER, petsc_err) + call KSPSetType(self % ksp, KSPGMRES, petsc_err) + call KSPSetInitialGuessNonzero(self % ksp, PETSC_TRUE, petsc_err) + call KSPGetPC(self % ksp, self % pc, petsc_err) + call PCFactorSetLevels(self % pc, ilu_levels, petsc_err) + call KSPSetFromOptions(self % ksp, petsc_err) + + end subroutine petsc_gmres_create + +!=============================================================================== +! PETSC_GMRES_SET_OPER sets the matrix opetors for the GMRES solver +!=============================================================================== + + subroutine petsc_gmres_set_oper(self, prec_mat, mat_in) + + class(GMRESSolver) :: self + type(Matrix) :: prec_mat + type(Matrix) :: mat_in + + call KSPSetOperators(self % ksp, mat_in % petsc_mat, prec_mat % petsc_mat, & + SAME_NONZERO_PATTERN, petsc_err) + call KSPSetUp(self % ksp, petsc_err) + + end subroutine petsc_gmres_set_oper + +!=============================================================================== +! PETSC_GMRES_DESTROY frees all memory associated with the GMRES solver +!=============================================================================== + + subroutine petsc_gmres_destroy(self) + + class(GMRESSolver) :: self + + call KSPDestroy(self % ksp, petsc_err) + + end subroutine petsc_gmres_destroy + +!=============================================================================== +! PETSC_GMRES_SOLVE solves the linear system +!=============================================================================== + + subroutine petsc_gmres_solve(self, b, x) + + class(GMRESSolver) :: self + type(Vector) :: b + type(Vector) :: x + + call KSPSolve(self % ksp, b % petsc_vec, x % petsc_vec, petsc_err) + + end subroutine petsc_gmres_solve + +!=============================================================================== +! PETSC_JFNK_CREATE sets up a JFNK solver using PETSc SNES +!=============================================================================== + + subroutine petsc_jfnk_create(self) + + class(JFNKSolver) :: self -#ifdef PETSC ! Turn on mf_operator option for matrix free jacobian call PetscOptionsSetValue("-snes_mf_operator", "TRUE", petsc_err) @@ -166,75 +357,68 @@ contains call PCFactorSetLevels(self % pc, 5, petsc_err) call PCSetFromOptions(self % pc, petsc_err) call KSPSetFromOptions(self % ksp, petsc_err) -#endif - end subroutine jfnk_create + end subroutine petsc_jfnk_create !=============================================================================== -! JFNK_DESTROY frees all memory associated with JFNK solver +! PETSC_JFNK_DESTROY frees all memory associated with JFNK solver !=============================================================================== - subroutine jfnk_destroy(self) + subroutine petsc_jfnk_destroy(self) - class(Petsc_jfnk) :: self + class(JFNKSolver) :: self -#ifdef PETSC call MatDestroy(self % jac_mf, petsc_err) call SNESDestroy(self % snes, petsc_err) -#endif - end subroutine jfnk_destroy + end subroutine petsc_jfnk_destroy !=============================================================================== -! JFNK_SET_FUNCTIONS sets user functions and matrix free objects +! PETSC_JFNK_SET_FUNCTIONS sets user functions and matrix free objects !=============================================================================== - subroutine jfnk_set_functions(self, ctx, res, jac_prec) + subroutine petsc_jfnk_set_functions(self, ctx, res, jac_prec) - class(Petsc_jfnk) :: self + class(JFNKSolver) :: self type(Jfnk_ctx) :: ctx type(Vector) :: res type(Matrix) :: jac_prec -# ifdef PETSC ! Set residual procedure - call SNESSetFunction(self % snes, res % petsc_vec, jfnk_compute_residual, & - ctx, petsc_err) + call SNESSetFunction(self % snes, res % petsc_vec, & + petsc_jfnk_compute_residual, ctx, petsc_err) ! Create the matrix free jacobian call MatCreateSNESMF(self % snes, self % jac_mf, petsc_err) ! Set Jacobian procedure call SNESSetJacobian(self % snes, self % jac_mf, jac_prec % petsc_mat, & - jfnk_compute_jacobian, ctx, petsc_err) + petsc_jfnk_compute_jacobian, ctx, petsc_err) ! Set up Jacobian Lags call SNESSetLagJacobian(self % snes, -2, petsc_err) call SNESSetLagPreconditioner(self % snes, -1, petsc_err) -#endif - end subroutine jfnk_set_functions + end subroutine petsc_jfnk_set_functions !=============================================================================== -! JFNK_SOLVE solves the nonlinear system +! PETSC_JFNK_SOLVE solves the nonlinear system !=============================================================================== - subroutine jfnk_solve(self, xvec) + subroutine petsc_jfnk_solve(self, xvec) - class(Petsc_jfnk) :: self + class(JFNKSolver) :: self type(Vector) :: xvec -#ifdef PETSC call SNESSolve(self % snes, PETSC_NULL_DOUBLE, xvec % petsc_vec, petsc_err) -#endif - end subroutine jfnk_solve + end subroutine petsc_jfnk_solve !=============================================================================== -! JFNK_COMPUTE_RESIDUAL buffer routine to user specifed residual routine +! PETSC_JFNK_COMPUTE_RESIDUAL buffer routine to user specifed residual routine !=============================================================================== -# ifdef PETSC - subroutine jfnk_compute_residual(snes, x, res, ctx, ierr) + + subroutine petsc_jfnk_compute_residual(snes, x, res, ctx, ierr) SNES :: snes Vec :: x @@ -260,13 +444,13 @@ contains call VecRestoreArrayF90(x, xvec % val, ierr) call VecRestoreArrayF90(res, resvec % val, ierr) - end subroutine jfnk_compute_residual -#endif + end subroutine petsc_jfnk_compute_residual + !=============================================================================== -! JFNK_COMPUTE_JACOBIAN +! PETSC_JFNK_COMPUTE_JACOBIAN buffer routine to user specified jacobian routine !=============================================================================== -#ifdef PETSC - subroutine jfnk_compute_jacobian(snes, x, jac_mf, jac_prec, flag, ctx, & + + subroutine petsc_jfnk_compute_jacobian(snes, x, jac_mf, jac_prec, flag, ctx, & ierr) SNES :: snes @@ -294,6 +478,7 @@ contains call MatAssemblyBegin(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) call MatAssemblyEnd(jac_mf, MAT_FINAL_ASSEMBLY, petsc_err) - end subroutine jfnk_compute_jacobian + end subroutine petsc_jfnk_compute_jacobian #endif -end module petsc_solver + +end module solver_interface From dd1fa2946098935393f50670fd42ce681a5f344b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 23 Aug 2013 12:34:35 -0400 Subject: [PATCH 097/192] alias is now checked with method check_for_node --- src/input_xml.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index ece67151ce..d77a0e9a2d 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2936,7 +2936,7 @@ contains ! create dictionary entry for both name and alias call xs_listing_dict % add_key(listing % name, i) - if (listing % alias /= '') then + if (check_for_node(node_ace, "alias")) then call xs_listing_dict % add_key(listing % alias, i) end if end do From e688cfa8ee871fb2688541a6c113fd30b1476a83 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 23 Aug 2013 13:19:51 -0400 Subject: [PATCH 098/192] no alias tag for sab table entry --- src/input_xml.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index d77a0e9a2d..51ada879ed 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1568,7 +1568,6 @@ contains mat % i_sab_tables(j) = index_sab call sab_dict % add_key(name, index_sab) - call sab_dict % add_key(alias, index_sab) else mat % i_sab_tables(j) = sab_dict % get_key(name) end if From bcf2cee9cae381dafc544f390fe833f38a3d7ce6 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 23 Aug 2013 13:28:40 -0400 Subject: [PATCH 099/192] removed other alias variables in sab reading --- src/input_xml.F90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 51ada879ed..4267a9727b 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1544,7 +1544,6 @@ contains end if call get_node_value(node_sab, "name", name) call get_node_value(node_sab, "xs", temp_str) - name = trim(name) // "." // trim(temp_str) mat % sab_names(j) = name @@ -1559,14 +1558,12 @@ contains ! listing index_list = xs_listing_dict % get_key(name) name = xs_listings(index_list) % name - alias = xs_listings(index_list) % alias ! If this S(a,b) table hasn't been encountered yet, we need to add its ! name and alias to the sab_dict if (.not. sab_dict % has_key(name)) then - index_sab = index_sab + 1 + index_sab = index_sab + 1 mat % i_sab_tables(j) = index_sab - call sab_dict % add_key(name, index_sab) else mat % i_sab_tables(j) = sab_dict % get_key(name) From 4b6b1a7b98902ab9313bd95d4de7c848cb1d711d Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 27 Aug 2013 15:50:57 -0400 Subject: [PATCH 100/192] re-added back in fatal error if petsc is not compiled when using cmfd --- src/input_xml.F90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a0731ee70b..6243f7a215 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -561,6 +561,10 @@ contains call lower_case(run_cmfd_) if (run_cmfd_ == 'true' .or. run_cmfd_ == '1') then cmfd_run = .true. +#ifndef PETSC + message = 'CMFD is not available, recompile OpenMC with PETSc' + call fatal_error() +#endif end if end subroutine read_settings_xml From e2d82f35a244aaa23be2b2f45f80707d1a2887c8 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 27 Aug 2013 16:11:27 -0400 Subject: [PATCH 101/192] removed built-in GMRES solver only PETSc available again --- src/DEPENDENCIES | 1 - src/cmfd_jfnk_solver.F90 | 10 +- src/cmfd_power_solver.F90 | 10 +- src/gmres_solver.F90 | 1231 ------------------------------------- src/solver_interface.F90 | 193 +----- 5 files changed, 19 insertions(+), 1426 deletions(-) delete mode 100644 src/gmres_solver.F90 diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 782f48f61a..18ff2eeb0c 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -327,7 +327,6 @@ set_header.o: list_header.o solver_interface.o: error.o solver_interface.o: global.o -solver_interface.o: gmres_solver.o solver_interface.o: matrix_header.o solver_interface.o: vector_header.o diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 3208e62146..9b14b4d1aa 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -46,19 +46,25 @@ contains call init_data() ! Initialize solver +#ifdef PETSC call jfnk % create() +#endif ! Set up residual and jacobian routines jfnk_data % res_proc_ptr => compute_nonlinear_residual jfnk_data % jac_proc_ptr => build_jacobian_matrix +#ifdef PETSC call jfnk % set_functions(jfnk_data, resvec, jac_prec) +#endif ! Stop timer for build call time_cmfdbuild % stop() ! Solve the system call time_cmfdsolve % start() +#ifdef PETSC call jfnk % solve(xvec) +#endif call time_cmfdsolve % stop() ! Extracts results to cmfd object @@ -378,7 +384,9 @@ contains call jac_prec % destroy() call xvec % destroy() call resvec % destroy() - call jfnk % destroy() +#ifdef PETSC + call jfnk % destroy() +#endif nullify(jfnk_data % res_proc_ptr) nullify(jfnk_data % jac_proc_ptr) diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index d57f386ee5..cd31789407 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -63,7 +63,9 @@ contains call time_cmfdbuild % start() ! Initialize solver +#ifdef PETSC call gmres % create() +#endif ! Initialize matrices and vectors call init_data(physical_adjoint) @@ -73,7 +75,9 @@ contains call compute_adjoint() ! Set up krylov info +#ifdef PETSC call gmres % set_oper(loss, loss) +#endif ! Stop timer for build call time_cmfdbuild % stop() @@ -196,7 +200,9 @@ contains s_o % val = s_o % val / k_o ! Compute new flux vector +#ifdef PETSC call gmres % solve(s_o, phi_n) +#endif ! Compute new source vector call prod % vector_multiply(phi_n, s_n) @@ -324,7 +330,9 @@ contains subroutine finalize() ! Destroy all objects - call gmres % destroy() +#ifdef PETSC + call gmres % destroy() +#endif call loss % destroy() call prod % destroy() call phi_n % destroy() diff --git a/src/gmres_solver.F90 b/src/gmres_solver.F90 deleted file mode 100644 index 9345f4f9a5..0000000000 --- a/src/gmres_solver.F90 +++ /dev/null @@ -1,1231 +0,0 @@ -module gmres_solver - - implicit none - -contains - -subroutine pgmres ( n, im, rhs, sol, vv, eps, maxits, iout, & - aa, ja, ia, alu, jlu, ju, ierr ) - -!*****************************************************************************80 -! -!! PGMRES is an ILUT - Preconditioned GMRES solver. -! -! Discussion: -! -! This is a simple version of the ILUT preconditioned GMRES algorithm. -! The ILUT preconditioner uses a dual strategy for dropping elements -! instead of the usual level of-fill-in approach. See details in ILUT -! subroutine documentation. PGMRES uses the L and U matrices generated -! from the subroutine ILUT to precondition the GMRES algorithm. -! The preconditioning is applied to the right. The stopping criterion -! utilized is based simply on reducing the residual norm by epsilon. -! This preconditioning is more reliable than ilu0 but requires more -! storage. It seems to be much less prone to difficulties related to -! strong nonsymmetries in the matrix. We recommend using a nonzero tol -! (tol=.005 or .001 usually give good results) in ILUT. Use a large -! lfil whenever possible (e.g. lfil = 5 to 10). The higher lfil the -! more reliable the code is. Efficiency may also be much improved. -! Note that lfil=n and tol=0.0 in ILUT will yield the same factors as -! Gaussian elimination without pivoting. -! -! ILU(0) and MILU(0) are also provided for comparison purposes -! USAGE: first call ILUT or ILU0 or MILU0 to set up preconditioner and -! then call pgmres. -! -! Modified: -! -! 07 January 2004 -! -! Author: -! -! Youcef Saad -! -! Parameters: -! -! Input, integer ( kind = 4 ) N, the order of the matrix. -! -! Input, integer ( kind = 4 ) IM, the size of the Krylov subspace. IM -! should not exceed 50 in this version. This restriction can be reset by -! changing the parameter command for KMAX below. -! -! Input/output, real RHS(N), on input, the right hand side vector. -! On output, the information in this vector has been destroyed. -! -! sol == real vector of length n containing an initial guess to the -! solution on input. approximate solution on output -! -! eps == tolerance for stopping criterion. process is stopped -! as soon as ( ||.|| is the euclidean norm): -! || current residual||/||initial residual|| <= eps -! -! maxits== maximum number of iterations allowed -! -! iout == output unit number number for printing intermediate results -! if (iout <= 0) nothing is printed out. -! -! Input, real AA(*), integer ( kind = 4 ) JA(*), IA(N+1), the matrix in CSR -! Compressed Sparse Row format. -! -! -! alu,jlu== A matrix stored in Modified Sparse Row format containing -! the L and U factors, as computed by routine ilut. -! -! ju == integer ( kind = 4 ) array of length n containing the pointers to -! the beginning of each row of U in alu, jlu as computed -! by routine ILUT. -! -! on return: -! -! sol == contains an approximate solution (upon successful return). -! ierr == integer ( kind = 4 ). Error message with the following meaning. -! ierr = 0 --> successful return. -! ierr = 1 --> convergence not achieved in itmax iterations. -! ierr =-1 --> the initial guess seems to be the exact -! solution (initial residual computed was zero) -! -! work arrays: -! -! vv == work array of length n x (im+1) (used to store the Arnoli -! basis) -! - integer ( kind = 4 ), parameter :: kmax = 50 - integer ( kind = 4 ) n - - real ( kind = 8 ) aa(*) - real ( kind = 8 ) alu(*) - real ( kind = 8 ) c(kmax) - real ( kind = 8 ) eps - real ( kind = 8 ) eps1 - real ( kind = 8 ), parameter :: epsmac = 1.0D-16 - real ( kind = 8 ) gam - real ( kind = 8 ) hh(kmax+1,kmax) - integer ( kind = 4 ) i - integer ( kind = 4 ) i1 - integer ( kind = 4 ) ia(n+1) - integer ( kind = 4 ) ierr - integer ( kind = 4 ) ii - integer ( kind = 4 ) im - integer ( kind = 4 ) iout - integer ( kind = 4 ) its - integer ( kind = 4 ) j - integer ( kind = 4 ) ja(*) - integer ( kind = 4 ) jj - integer ( kind = 4 ) jlu(*) - integer ( kind = 4 ) ju(*) - integer ( kind = 4 ) k - integer ( kind = 4 ) k1 - integer ( kind = 4 ) maxits - integer ( kind = 4 ) n1 - real ( kind = 8 ) rhs(n) - real ( kind = 8 ) ro - real ( kind = 8 ) rs(kmax+1) - real ( kind = 8 ) s(kmax) - real ( kind = 8 ) sol(n) - real ( kind = 8 ) t - real ( kind = 8 ) vv(n,*) -! -! Arnoldi size should not exceed KMAX=50 in this version. -! To reset modify parameter KMAX accordingly. -! - n1 = n + 1 - its = 0 -! -! Outer loop starts here. -! Compute initial residual vector. -! - call ope ( n, sol, vv, aa, ja, ia ) - - vv(1:n,1) = rhs(1:n) - vv(1:n,1) - - do - - ro = sqrt ( ddot ( n, vv, 1, vv, 1 ) ) - - if ( 0 < iout .and. its == 0 ) then - write(iout, 199) its, ro - end if - - if ( ro == 0.0D+00 ) then - ierr = -1 - exit - end if - - t = 1.0D+00 / ro - vv(1:n,1) = vv(1:n,1) * t - - if ( its == 0 ) then - eps1 = eps * ro - end if -! -! Initialize first term of RHS of Hessenberg system. -! - rs(1) = ro - i = 0 - - 4 continue - - i = i + 1 - its = its + 1 - i1 = i + 1 - call lusol0 ( n, vv(1,i), rhs, alu, jlu, ju ) - call ope ( n, rhs, vv(1,i1), aa, ja, ia ) -! -! Modified Gram - Schmidt. -! - do j = 1, i - t = ddot ( n, vv(1,j), 1, vv(1,i1), 1 ) - hh(j,i) = t - call daxpy ( n, -t, vv(1,j), 1, vv(1,i1), 1 ) - end do - - t = sqrt ( ddot ( n, vv(1,i1), 1, vv(1,i1), 1 ) ) - hh(i1,i) = t - - if ( t /= 0.0D+00 ) then - t = 1.0D+00 / t - vv(1:n,i1) = vv(1:n,i1) * t - end if -! -! Update factorization of HH. -! - if ( i == 1 ) then - go to 121 - end if -! -! Perform previous transformations on I-th column of H. -! - do k = 2, i - k1 = k-1 - t = hh(k1,i) - hh(k1,i) = c(k1) * t + s(k1) * hh(k,i) - hh(k,i) = -s(k1) * t + c(k1) * hh(k,i) - end do - -121 continue - - gam = sqrt ( hh(i,i)**2 + hh(i1,i)**2 ) -! -! If GAMMA is zero then any small value will do. -! It will affect only residual estimate. -! - if ( gam == 0.0D+00 ) then - gam = epsmac - end if -! -! Get the next plane rotation. -! - c(i) = hh(i,i) / gam - s(i) = hh(i1,i) / gam - rs(i1) = -s(i) * rs(i) - rs(i) = c(i) * rs(i) -! -! Determine residual norm and test for convergence. -! - hh(i,i) = c(i) * hh(i,i) + s(i) * hh(i1,i) - ro = abs ( rs(i1) ) -131 format(1h ,2e14.4) - - if ( 0 < iout ) then - write(iout, 199) its, ro - end if - - if ( i < im .and. eps1 < ro ) then - go to 4 - end if -! -! Now compute solution. First solve upper triangular system. -! - rs(i) = rs(i) / hh(i,i) - - do ii = 2, i - k = i - ii + 1 - k1 = k + 1 - t = rs(k) - do j = k1, i - t = t - hh(k,j) * rs(j) - end do - rs(k) = t / hh(k,k) - end do -! -! Form linear combination of V(*,i)'s to get solution. -! - t = rs(1) - rhs(1:n) = vv(1:n,1) * t - - do j = 2, i - t = rs(j) - rhs(1:n) = rhs(1:n) + t * vv(1:n,j) - end do -! -! Call preconditioner. -! - call lusol0 ( n, rhs, rhs, alu, jlu, ju ) - - sol(1:n) = sol(1:n) + rhs(1:n) -! -! Restart outer loop when necessary. -! - if ( ro <= eps1 ) then - ierr = 0 - exit - end if - - if ( maxits < its ) then - ierr = 1 - exit - end if -! -! Else compute residual vector and continue. -! - do j = 1, i - jj = i1 - j + 1 - rs(jj-1) = -s(jj-1) * rs(jj) - rs(jj) = c(jj-1) * rs(jj) - end do - - do j = 1, i1 - t = rs(j) - if ( j == 1 ) then - t = t - 1.0D+00 - end if - call daxpy ( n, t, vv(1,j), 1, vv, 1 ) - end do - -199 format(' its =', i4, ' res. norm =', G14.6) - - end do - - return -end - -subroutine ilut ( n, a, ja, ia, lfil, tol, alu, jlu, ju, iwk, wu, wl, jr, & - jwl, jwu, ierr ) - -!*****************************************************************************80 -! -!! ILUT is an ILUT preconditioner. -! -! Discussion: -! -! This routine carries ouot incomplete LU factorization with dual -! truncation mechanism. Sorting is done for both L and U. -! -! The dual drop-off strategy works as follows: -! -! 1) Theresholding in L and U as set by TOL. Any element whose size -! is less than some tolerance (relative to the norm of current -! row in u) is dropped. -! -! 2) Keeping only the largest lenl0+lfil elements in L and the -! largest lenu0+lfil elements in U, where lenl0=initial number -! of nonzero elements in a given row of lower part of A -! and lenlu0 is similarly defined. -! -! Flexibility: one can use tol=0 to get a strategy based on keeping the -! largest elements in each row of L and U. Taking tol /= 0 but lfil=n -! will give the usual threshold strategy (however, fill-in is then -! unpredictible). -! -! A must have all nonzero diagonal elements. -! -! Modified: -! -! 07 January 2004 -! -! Author: -! -! Youcef Saad -! -! Parameters: -! -! Parameters: -! -! Input, integer ( kind = 4 ) N, the order of the matrix. -! -! Input, real A(*), integer ( kind = 4 ) JA(*), IA(N+1), the matrix in CSR -! Compressed Sparse Row format. -! -! lfil = integer ( kind = 4 ). The fill-in parameter. Each row of L and -! each row of U will have a maximum of lfil elements -! in addition to the original number of nonzero elements. -! Thus storage can be determined beforehand. -! lfil must be >= 0. -! -! iwk = integer ( kind = 4 ). The minimum length of arrays alu and jlu -! -! On return: -! -! alu,jlu = matrix stored in Modified Sparse Row (MSR) format containing -! the L and U factors together. The diagonal (stored in -! alu(1:n) ) is inverted. Each i-th row of the alu,jlu matrix -! contains the i-th row of L (excluding the diagonal entry=1) -! followed by the i-th row of U. -! -! ju = integer ( kind = 4 ) array of length n containing the pointers to -! the beginning of each row of U in the matrix alu,jlu. -! -! ierr = integer ( kind = 4 ). Error message with the following meaning. -! ierr = 0 --> successful return. -! ierr > 0 --> zero pivot encountered at step number ierr. -! ierr = -1 --> Error. input matrix may be wrong. -! (The elimination process has generated a -! row in L or U whose length is > n.) -! ierr = -2 --> The matrix L overflows the array al. -! ierr = -3 --> The matrix U overflows the array alu. -! ierr = -4 --> Illegal value for lfil. -! ierr = -5 --> zero pivot encountered. -! -! work arrays: -! -! jr,jwu,jwl, integer ( kind = 4 ) work arrays of length n. -! wu, wl, real work arrays of length n+1, and n resp. -! - integer ( kind = 4 ) n - - real ( kind = 8 ) a(*) - real ( kind = 8 ) alu(*) - real ( kind = 8 ) fact - integer ( kind = 4 ) ia(n+1) - integer ( kind = 4 ) idiag - integer ( kind = 4 ) ierr - integer ( kind = 4 ) ii - integer ( kind = 4 ) iwk - integer ( kind = 4 ) j - integer ( kind = 4 ) j1 - integer ( kind = 4 ) j2 - integer ( kind = 4 ) ja(*) - integer ( kind = 4 ) jj - integer ( kind = 4 ) jlu(*) - integer ( kind = 4 ) jpos - integer ( kind = 4 ) jr(*) - integer ( kind = 4 ) jrow - integer ( kind = 4 ) ju(*) - integer ( kind = 4 ) ju0 - integer ( kind = 4 ) jwl(n) - integer ( kind = 4 ) jwu(n) - integer ( kind = 4 ) k - integer ( kind = 4 ) len - integer ( kind = 4 ) lenl - integer ( kind = 4 ) lenl0 - integer ( kind = 4 ) lenu - integer ( kind = 4 ) lenu0 - integer ( kind = 4 ) lfil - integer ( kind = 4 ) nl - real ( kind = 8 ) s - real ( kind = 8 ) t - real ( kind = 8 ) tnorm - real ( kind = 8 ) tol - real ( kind = 8 ) wl(n) - real ( kind = 8 ) wu(n+1) - - if ( lfil < 0 ) then - ierr = -4 - return - end if -! -! Initialize JU0 (points to next element to be added to ALU, JLU) -! and pointer. -! - ju0 = n + 2 - jlu(1) = ju0 -! -! integer ( kind = 4 ) double pointer array. -! - jr(1:n) = 0 -! -! The main loop. -! - do ii = 1, n - - j1 = ia(ii) - j2 = ia(ii+1) - 1 - lenu = 0 - lenl = 0 - - tnorm = 0.0D+00 - do k = j1, j2 - tnorm = tnorm + abs ( a(k) ) - end do - tnorm = tnorm / real ( j2-j1+1, kind = 8 ) -! -! Unpack L-part and U-part of row of A in arrays WL, WU. -! - do j = j1, j2 - - k = ja(j) - t = a(j) - - if ( tol * tnorm <= abs ( t ) ) then - - if ( k < ii ) then - lenl = lenl + 1 - jwl(lenl) = k - wl(lenl) = t - jr(k) = lenl - else - lenu = lenu+1 - jwu(lenu) = k - wu(lenu) = t - jr(k) = lenu - end if - - end if - - end do - - lenl0 = lenl - lenu0 = lenu - jj = 0 - nl = 0 -! -! Eliminate previous rows. -! -150 continue - - jj = jj + 1 - - if ( lenl < jj ) then - go to 160 - end if -! -! In order to do the elimination in the correct order we need to -! exchange the current row number with the one that has -! smallest column number, among JJ, JJ+1, ..., LENL. -! - jrow = jwl(jj) - k = jj -! -! Determine the smallest column index. -! - do j = jj+1, lenl - if ( jwl(j) < jrow ) then - jrow = jwl(j) - k = j - end if - end do -! -! Exchange in JWL. -! - j = jwl(jj) - jwl(jj) = jrow - jwl(k) = j -! -! Exchange in JR. -! - jr(jrow) = jj - jr(j) = k -! -! Exchange in WL. -! - s = wl(k) - wl(k) = wl(jj) - wl(jj) = s - - if ( ii <= jrow ) then - go to 160 - end if -! -! Get the multiplier for row to be eliminated: JROW. -! - fact = wl(jj) * alu(jrow) - jr(jrow) = 0 - - if ( abs ( fact ) * wu(n+2-jrow) <= tol * tnorm ) then - go to 150 - end if -! -! Combine current row and row JROW. -! - do k = ju(jrow), jlu(jrow+1)-1 - s = fact * alu(k) - j = jlu(k) - jpos = jr(j) -! -! If fill-in element and small disregard. -! - if ( abs ( s ) < tol * tnorm .and. jpos == 0 ) then - cycle - end if - - if ( ii <= j ) then -! -! Dealing with upper part. -! - if ( jpos == 0 ) then -! -! This is a fill-in element. -! - lenu = lenu + 1 - - if ( n < lenu ) then - go to 995 - end if - - jwu(lenu) = j - jr(j) = lenu - wu(lenu) = - s - else -! -! No fill-in element. -! - wu(jpos) = wu(jpos) - s - end if - else -! -! Dealing with lower part. -! - if ( jpos == 0 ) then -! -! This is a fill-in element. -! - lenl = lenl + 1 - - if ( n < lenl ) then - go to 995 - end if - - jwl(lenl) = j - jr(j) = lenl - wl(lenl) = -s - else -! -! No fill-in element. -! - wl(jpos) = wl(jpos) - s - end if - end if - - end do - - nl = nl + 1 - wl(nl) = fact - jwl(nl) = jrow - go to 150 -! -! Update the L matrix. -! - 160 continue - - len = min ( nl, lenl0 + lfil ) - - call bsort2 ( wl, jwl, nl, len ) - - do k = 1, len - - if ( iwk < ju0 ) then - ierr = -2 - return - end if - - alu(ju0) = wl(k) - jlu(ju0) = jwl(k) - ju0 = ju0 + 1 - - end do -! -! Save pointer to beginning of row II of U. -! - ju(ii) = ju0 -! -! Reset double pointer JR to zero (L-part - except first -! JJ-1 elements which have already been reset). -! - do k = jj, lenl - jr(jwl(k)) = 0 - end do -! -! Be sure that the diagonal element is first in W and JW. -! - idiag = jr(ii) - - if ( idiag == 0 ) then - go to 900 - end if - - if ( idiag /= 1 ) then - - s = wu(1) - wu(j) = wu(idiag) - wu(idiag) = s - - j = jwu(1) - jwu(1) = jwu(idiag) - jwu(idiag) = j - - end if - - len = min ( lenu, lenu0 + lfil ) - - call bsort2 ( wu(2), jwu(2), lenu-1, len ) -! -! Update the U-matrix. -! - t = 0.0D+00 - - do k = 2, len - - if ( iwk < ju0 ) then - ierr = -3 - return - end if - - jlu(ju0) = jwu(k) - alu(ju0) = wu(k) - t = t + abs ( wu(k) ) - ju0 = ju0 + 1 - - end do -! -! Save norm in WU (backwards). Norm is in fact average absolute value. -! - wu(n+2-ii) = t / real ( len + 1, kind = 8 ) -! -! Store inverse of diagonal element of U. -! - if ( wu(1) == 0.0D+00 ) then - ierr = -5 - return - end if - - alu(ii) = 1.0D+00 / wu(1) -! -! Update pointer to beginning of next row of U. -! - jlu(ii+1) = ju0 -! -! Reset double pointer JR to zero (U-part). -! - do k = 1, lenu - jr(jwu(k)) = 0 - end do - - end do - - ierr = 0 - - return -! -! Zero pivot : -! - 900 ierr = ii - return -! -! Incomprehensible error. Matrix must be wrong. -! - 995 ierr = -1 - return -end - -subroutine amux ( n, x, y, a, ja, ia ) - -!*****************************************************************************80 -! -!! AMUX multiplies a CSR matrix A times a vector. -! -! Discussion: -! -! This routine multiplies a matrix by a vector using the dot product form. -! Matrix A is stored in compressed sparse row storage. -! -! Modified: -! -! 07 January 2004 -! -! Author: -! -! Youcef Saad -! -! Parameters: -! -! Input, integer ( kind = 4 ) N, the row dimension of the matrix. -! -! Input, real X(*), and array of length equal to the column dimension -! of A. -! -! Input, real A(*), integer ( kind = 4 ) JA(*), IA(NROW+1), the matrix in CSR -! Compressed Sparse Row format. -! -! Output, real Y(N), the product A * X. -! - integer ( kind = 4 ) n - - real ( kind = 8 ) a(*) - integer ( kind = 4 ) i - integer ( kind = 4 ) ia(*) - integer ( kind = 4 ) ja(*) - integer ( kind = 4 ) k - real ( kind = 8 ) t - real ( kind = 8 ) x(*) - real ( kind = 8 ) y(n) - - do i = 1, n -! -! Compute the inner product of row I with vector X. -! - t = 0.0D+00 - do k = ia(i), ia(i+1)-1 - t = t + a(k) * x(ja(k)) - end do - - y(i) = t - - end do - - return -end -subroutine amuxd ( n, x, y, diag, ndiag, idiag, ioff ) - -!*****************************************************************************80 -! -!! AMUXD multiplies a DIA matrix times a vector. -! -! Discussion: -! -! This routine multiplies a matrix by a vector when the original matrix -! is stored in the DIA diagonal storage format. -! -! Modified: -! -! 07 January 2004 -! -! Author: -! -! Youcef Saad -! -! Parameters: -! -! Input, integer ( kind = 4 ) N, the row dimension of the matrix. -! -! Input, real X(*), array of length equal to the column dimension of -! the A matrix. -! -! Output, real Y(N), the product A * X. -! -! Input, real DIAG(NDIAG,IDIAG), the diagonals. -! -! Input, integer ( kind = 4 ) NDIAG, the first dimension of array adiag as -! declared in the calling program. -! -! Input, integer ( kind = 4 ) IDIAG, the number of diagonals in the matrix. -! -! Input, integer ( kind = 4 ) IOFF(IDIAG), the offsets of the diagonals of -! the matrix: diag(i,k) contains the element a(i,i+ioff(k)) of the matrix. -! - integer ( kind = 4 ) idiag - integer ( kind = 4 ) n - integer ( kind = 4 ) ndiag - - real ( kind = 8 ) diag(ndiag,idiag) - integer ( kind = 4 ) i1 - integer ( kind = 4 ) i2 - integer ( kind = 4 ) io - integer ( kind = 4 ) ioff(idiag) - integer ( kind = 4 ) j - integer ( kind = 4 ) k - real ( kind = 8 ) x(n) - real ( kind = 8 ) y(n) - - y(1:n) = 0.0D+00 - - do j = 1, idiag - io = ioff(j) - i1 = max ( 1, 1 - io ) - i2 = min ( n, n - io ) - do k = i1, i2 - y(k) = y(k) + diag(k,j) * x(k+io) - end do - end do - - return -end - -subroutine daxpy ( n, da, dx, incx, dy, incy ) - -!*****************************************************************************80 -! -!! DAXPY computes constant times a vector plus a vector. -! -! Discussion: -! -! Uses unrolled loops for increments equal to one. -! -! Author: -! -! Jack Dongarra -! -! Reference: -! -! Dongarra, Moler, Bunch, Stewart, -! LINPACK User's Guide, -! SIAM, 1979. -! -! Lawson, Hanson, Kincaid, Krogh, -! Basic Linear Algebra Subprograms for Fortran Usage, -! Algorithm 539, -! ACM Transactions on Mathematical Software, -! Volume 5, Number 3, September 1979, pages 308-323. -! -! Parameters: -! -! Input, integer ( kind = 4 ) N, the number of elements in DX and DY. -! -! Input, real ( kind = 8 ) DA, the multiplier of DX. -! -! Input, real ( kind = 8 ) DX(*), the first vector. -! -! Input, integer ( kind = 4 ) INCX, the increment between successive entries of DX. -! -! Input/output, real ( kind = 8 ) DY(*), the second vector. -! On output, DY(*) has been replaced by DY(*) + DA * DX(*). -! -! Input, integer ( kind = 4 ) INCY, the increment between successive entries of DY. -! - real ( kind = 8 ) da - real ( kind = 8 ) dx(*) - real ( kind = 8 ) dy(*) - integer ( kind = 4 ) i - integer ( kind = 4 ) incx - integer ( kind = 4 ) incy - integer ( kind = 4 ) ix - integer ( kind = 4 ) iy - integer ( kind = 4 ) m - integer ( kind = 4 ) n - - if ( n <= 0 ) then - return - end if - - if ( da == 0.0D+00 ) then - return - end if -! -! Code for unequal increments or equal increments -! not equal to 1. -! - if ( incx /= 1 .or. incy /= 1 ) then - - if ( 0 <= incx ) then - ix = 1 - else - ix = ( - n + 1 ) * incx + 1 - end if - - if ( 0 <= incy ) then - iy = 1 - else - iy = ( - n + 1 ) * incy + 1 - end if - - do i = 1, n - dy(iy) = dy(iy) + da * dx(ix) - ix = ix + incx - iy = iy + incy - end do -! -! Code for both increments equal to 1. -! - else - - m = mod ( n, 4 ) - - do i = 1, m - dy(i) = dy(i) + da * dx(i) - end do - - do i = m+1, n, 4 - dy(i ) = dy(i ) + da * dx(i ) - dy(i+1) = dy(i+1) + da * dx(i+1) - dy(i+2) = dy(i+2) + da * dx(i+2) - dy(i+3) = dy(i+3) + da * dx(i+3) - end do - - end if - - return -end - -subroutine bsort2 ( w, ind, n, ncut ) - -!*****************************************************************************80 -! -!! BSORT2 returns the NCUT largest elements of an array, using bubble sort. -! -! Discussion: -! -! This routine carries out a simple bubble sort for getting the NCUT largest -! elements in modulus, in array W. IND is sorted accordingly. -! (Ought to be replaced by a more efficient sort especially -! if NCUT is not that small). -! -! Modified: -! -! 07 January 2004 -! -! Author: -! -! Youcef Saad -! -! Parameters: -! - integer ( kind = 4 ) n - - integer ( kind = 4 ) i - integer ( kind = 4 ) ind(*) - integer ( kind = 4 ) iswp - integer ( kind = 4 ) j - integer ( kind = 4 ) ncut - logical test - real ( kind = 8 ) w(n) - real ( kind = 8 ) wswp - - i = 1 - - do - - test = .false. - - do j = n-1, i, -1 - - if ( abs ( w(j) ) < abs ( w(j+1) ) ) then -! -! Swap. -! - wswp = w(j) - w(j) = w(j+1) - w(j+1) = wswp -! -! Reorder the original ind array accordingly. -! - iswp = ind(j) - ind(j) = ind(j+1) - ind(j+1) = iswp -! -! Set indicator that sequence is still unsorted. -! - test = .true. - - end if - - end do - - i = i + 1 - - if ( .not. test .or. ncut < i ) then - exit - end if - - end do - - return -end - -subroutine ope ( n, x, y, a, ja, ia ) - -!*****************************************************************************80 -! -!! OPE sparse matrix * vector multiplication -! -! Modified: -! -! 07 January 2004 -! -! Author: -! -! Youcef Saad -! -! Parameters: -! -! Input, integer ( kind = 4 ) N, the order of the matrix. -! -! Input, real X(N), the vector to be multiplied. -! -! Output, real Y(N), the product A * X. -! -! Input, real A(*), integer ( kind = 4 ) JA(*), IA(N+1), the matrix in CSR -! Compressed Sparse Row format. -! - integer ( kind = 4 ) n - - real ( kind = 8 ) a(*) - integer ( kind = 4 ) i - integer ( kind = 4 ) ia(n+1) - integer ( kind = 4 ) ja(*) - integer ( kind = 4 ) k - integer ( kind = 4 ) k1 - integer ( kind = 4 ) k2 - real ( kind = 8 ) x(n) - real ( kind = 8 ) y(n) - - do i = 1, n - k1 = ia(i) - k2 = ia(i+1) -1 - y(i) = 0.0D+00 - do k = k1, k2 - y(i) = y(i) + a(k) * x(ja(k)) - end do - end do - - return -end - -subroutine lusol0 ( n, y, x, alu, jlu, ju ) - -!*****************************************************************************80 -! -!! LUSOL0 performs a forward followed by a backward solve -! for LU matrix as produced by ILUT -! -! Modified: -! -! 07 January 2004 -! -! Author: -! -! Youcef Saad -! -! Parameters: -! -! Input, integer ( kind = 4 ) N, the order of the matrix. -! -! Input, real Y(N), the right hand side of the linear system. -! -! Output, real X(N), the solution. -! -! ALU, JLU, JU, ... -! - integer ( kind = 4 ) n - - real ( kind = 8 ) alu(*) - integer ( kind = 4 ) i - integer ( kind = 4 ) jlu(*) - integer ( kind = 4 ) ju(*) - integer ( kind = 4 ) k - real ( kind = 8 ) x(n) - real ( kind = 8 ) y(n) -! -! Forward solve -! - do i = 1, n - x(i) = y(i) - do k = jlu(i), ju(i)-1 - x(i) = x(i) - alu(k) * x(jlu(k)) - end do - end do -! -! Backward solve. -! - do i = n, 1, -1 - do k = ju(i), jlu(i+1)-1 - x(i) = x(i) - alu(k) * x(jlu(k)) - end do - x(i) = alu(i) * x(i) - end do - - return -end - -function ddot ( n, dx, incx, dy, incy ) - -!*****************************************************************************80 -! -!! DDOT forms the dot product of two vectors. -! -! Discussion: -! -! This routine uses unrolled loops for increments equal to one. -! -! Author: -! -! Jack Dongarra -! -! Reference: -! -! Dongarra, Moler, Bunch, Stewart, -! LINPACK User's Guide, -! SIAM, 1979. -! -! Parameters: -! -! Input, integer ( kind = 4 ) N, the number of entries in the vectors. -! -! Input, real ( kind = 8 ) DX(*), the first vector. -! -! Input, integer ( kind = 4 ) INCX, the increment between successive entries in X. -! -! Input, real ( kind = 8 ) DY(*), the second vector. -! -! Input, integer ( kind = 4 ) INCY, the increment between successive entries in Y. -! -! Output, real DDOT, the sum of the product of the corresponding -! entries of X and Y. -! - real ( kind = 8 ) ddot - real ( kind = 8 ) dtemp - real ( kind = 8 ) dx(*) - real ( kind = 8 ) dy(*) - integer ( kind = 4 ) i - integer ( kind = 4 ) incx - integer ( kind = 4 ) incy - integer ( kind = 4 ) ix - integer ( kind = 4 ) iy - integer ( kind = 4 ) m - integer ( kind = 4 ) n - - ddot = 0.0D+00 - dtemp = 0.0D+00 - - if ( n <= 0 ) then - return - end if -! -! Code for unequal increments or equal increments -! not equal to 1. -! - if ( incx /= 1 .or. incy /= 1 ) then - - if ( 0 <= incx ) then - ix = 1 - else - ix = ( - n + 1 ) * incx + 1 - end if - - if ( 0 <= incy ) then - iy = 1 - else - iy = ( - n + 1 ) * incy + 1 - end if - - do i = 1, n - dtemp = dtemp + dx(ix) * dy(iy) - ix = ix + incx - iy = iy + incy - end do -! -! Code for both increments equal to 1. -! - else - - m = mod ( n, 5 ) - - do i = 1, m - dtemp = dtemp + dx(i) * dy(i) - end do - - do i = m+1, n, 5 - - dtemp = dtemp + dx(i ) * dy(i ) & - + dx(i+1) * dy(i+1) & - + dx(i+2) * dy(i+2) & - + dx(i+3) * dy(i+3) & - + dx(i+4) * dy(i+4) - end do - - end if - - ddot = dtemp - - return -end - -end module gmres_solver diff --git a/src/solver_interface.F90 b/src/solver_interface.F90 index 47bb0c14ab..b00bd33585 100644 --- a/src/solver_interface.F90 +++ b/src/solver_interface.F90 @@ -5,10 +5,6 @@ module solver_interface use matrix_header, only: Matrix use vector_header, only: Vector -#ifndef PETSC - use gmres_solver, only: ilut, pgmres -#endif - implicit none private @@ -21,25 +17,6 @@ module solver_interface #ifdef PETSC KSP :: ksp PC :: pc -#else - integer :: im ! iterations before restart - integer :: iout ! unit number for printing gmres iters - integer :: maxits ! maximum iterations - integer :: lfil ! fill in parameter - integer :: iwk ! size of MSR storage - integer, allocatable :: ju(:) ! row pointers for ILUT result - integer, allocatable :: jlu(:) ! MSR storage of j - integer, allocatable :: jr(:) ! temp work array - integer, allocatable :: jwu(:) ! temp work array - integer, allocatable :: jwl(:) ! temp work array - real(8) :: tol ! gmres tolerance - real(8) :: droptol ! ILU drop tolerance - real(8), allocatable :: alu(:) ! MSR storage of ILU A - real(8), allocatable :: vv(:) ! GMRES work array - real(8), allocatable :: wu(:) ! temp work array - real(8), allocatable :: wl(:) ! temp work array - real(8), allocatable :: rhs(:) ! copy of rhs array - type(Matrix), pointer :: A ! coefficient matrix #endif contains #ifdef PETSC @@ -47,11 +24,6 @@ module solver_interface procedure :: set_oper => petsc_gmres_set_oper procedure :: destroy => petsc_gmres_destroy procedure :: solve => petsc_gmres_solve -#else - procedure :: create => gmres_create - procedure :: set_oper => gmres_set_oper - procedure :: destroy => gmres_destroy - procedure :: solve => gmres_solve #endif end type GMRESSolver @@ -76,11 +48,6 @@ module solver_interface procedure :: destroy => petsc_jfnk_destroy procedure :: set_functions => petsc_jfnk_set_functions procedure :: solve => petsc_jfnk_solve -#else - procedure :: create => jfnk_create - procedure :: destroy => jfnk_destroy - procedure :: set_functions => jfnk_set_functions - procedure :: solve => jfnk_solve #endif end type JFNKSolver @@ -104,165 +71,7 @@ module solver_interface contains -#ifndef PETSC -!=============================================================================== -! GMRES_CREATE sets up a PETSc GMRES solver -!=============================================================================== - - subroutine gmres_create(self) - - class(GMRESSolver) :: self - - ! Set up default values - self % im = 40 - self % iout = 0 - self % maxits = 1000 - self % lfil = 0 - self % tol = 1.e-10_8 - self % droptol = 0.0_8 - - end subroutine gmres_create - -!=============================================================================== -! GMRES_SET_OPER sets the matrix opetors for the GMRES solver -!=============================================================================== - - subroutine gmres_set_oper(self, prec_mat, mat_in) - - class(GMRESSolver) :: self - type(Matrix) :: prec_mat - type(Matrix), target :: mat_in - - integer :: ierr - - ! Set pointer to matrix - self % A => mat_in - - ! Set up data - self % iwk = self % A % nnz + 1 - - ! Set up arrays - allocate(self % ju(self % A % n)) - allocate(self % jlu(self % iwk)) - allocate(self % alu(self % iwk)) - allocate(self % jr(self % A % n)) - allocate(self % jwu(self % A % n)) - allocate(self % jwl(self % A % n)) - allocate(self % vv(self % A % n * (self % im + 1))) - allocate(self % wu(self % A % n + 1)) - allocate(self % wl(self % A % n)) - allocate(self % rhs(self % A % n)) - - ! Perform ILU Preconditioning - call ilut(self % A % n, self % A % val, self % A % col, self % A % row, & - self % lfil, self % droptol, self % alu, self % jlu, self % ju, & - self % iwk, self % wu, self % wl, self % jr, self % jwl, & - self % jwu, ierr) - - end subroutine gmres_set_oper - -!=============================================================================== -! GMRES_DESTROY frees all memory associated with the GMRES solver -!=============================================================================== - - subroutine gmres_destroy(self) - - class(GMRESSolver) :: self - - ! Destroy all arrays associated with GMRES solver - deallocate(self % ju) - deallocate(self % jlu) - deallocate(self % alu) - deallocate(self % jr) - deallocate(self % jwu) - deallocate(self % jwl) - deallocate(self % vv) - deallocate(self % wu) - deallocate(self % wl) - deallocate(self % rhs) - - end subroutine gmres_destroy - -!=============================================================================== -! GMRES_SOLVE solves the linear system -!=============================================================================== - - subroutine gmres_solve(self, b, x) - - class(GMRESSolver) :: self - type(Vector) :: b - type(Vector) :: x - - integer :: ierr ! error code - - ! Copy rhs - self % rhs = b % val - - ! Perform GMRES calculation - call pgmres(self % A % n, self % im, self % rhs, x % val, self % vv, & - self % tol, self % maxits, self % iout, self % A % val, & - self % A % col, self % A % row, self % alu, self % jlu, & - self % ju, ierr) - - end subroutine gmres_solve - -!=============================================================================== -! JFNK_CREATE sets up a JFNK solver using PETSc SNES -!=============================================================================== - - subroutine jfnk_create(self) - - class(JFNKSolver) :: self - - message = 'JFNK cannot be used without PETSc.' - call fatal_error() - - end subroutine jfnk_create - -!=============================================================================== -! JFNK_DESTROY frees all memory associated with JFNK solver -!=============================================================================== - - subroutine jfnk_destroy(self) - - class(JFNKSolver) :: self - - message = 'JFNK cannot be used without PETSc.' - call fatal_error() - - end subroutine jfnk_destroy - -!=============================================================================== -! JFNK_SET_FUNCTIONS sets user functions and matrix free objects -!=============================================================================== - - subroutine jfnk_set_functions(self, ctx, res, jac_prec) - - class(JFNKSolver) :: self - type(Jfnk_ctx) :: ctx - type(Vector) :: res - type(Matrix) :: jac_prec - - message = 'JFNK cannot be used without PETSc.' - call fatal_error() - - end subroutine jfnk_set_functions - -!=============================================================================== -! JFNK_SOLVE solves the nonlinear system -!=============================================================================== - - subroutine jfnk_solve(self, xvec) - - class(JFNKSolver) :: self - type(Vector) :: xvec - - message = 'JFNK cannot be used without PETSc.' - call fatal_error() - - end subroutine jfnk_solve - -#else +#ifdef PETSC !=============================================================================== ! PETSC_GMRES_CREATE sets up a PETSc GMRES solver !=============================================================================== From 8723ed61e3ae9c9f3671b273802b9d70f837ae9f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 29 Aug 2013 14:42:37 -0400 Subject: [PATCH 102/192] fixed cmfd upper_right mesh tag and n_procs_cmfd --- src/cmfd_input.F90 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 26a7a3ae0e..3a3a2ff4bb 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -228,6 +228,7 @@ contains call create_cmfd_tally(doc) ! set number of CMFD processors and report to user + n_procs_cmfd = 1 if (check_for_node(doc, "n_cmfd_procs")) & call get_node_value(doc, "n_cmfd_procs", n_procs_cmfd) if (master) write(OUTPUT_UNIT,'(A,1X,I0,1X,A)') "CMFD Running on", & @@ -376,6 +377,10 @@ contains & coordinates on a tally mesh." call fatal_error() end if + + ! Set upper right coordinate and width + m % upper_right = rarray3(1:n) + m % width = (m % upper_right - m % lower_left) / real(m % dimension, 8) end if ! Set volume fraction From d4a217a10780142252f1143d265c4c1cdd1d1e23 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 3 Sep 2013 11:21:51 -0400 Subject: [PATCH 103/192] fixed verbosity input reading --- src/input_xml.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 4267a9727b..06c12fad31 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -69,6 +69,7 @@ contains type(Node), pointer :: node_ufs => null() type(Node), pointer :: node_sp => null() type(Node), pointer :: node_output => null() + type(Node), pointer :: node_verb => null() ! Display output message message = "Reading settings XML file..." @@ -219,8 +220,10 @@ contains end select ! Verbosity - if (check_for_node(doc, "verbosity")) & - call get_node_value(doc, "verbosity", verbosity) + if (check_for_node(doc, "verbosity")) then + call get_node_ptr(doc, "verbosity", node_verb) + call get_node_value(node_verb, "value", verbosity) + end if ! ========================================================================== ! EXTERNAL SOURCE From 4ebeb3a8bb0a968369d0eef0361651b138a0e0cd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 7 Sep 2013 21:38:41 -0400 Subject: [PATCH 104/192] Expand description of OpenMC development workflow. --- docs/source/devguide/workflow.rst | 111 ++++++++++++++++++++++++------ 1 file changed, 90 insertions(+), 21 deletions(-) diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index 40528beea3..061c440ad5 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -5,41 +5,110 @@ Development Workflow ==================== Anyone wishing to make contributions to OpenMC should be fully acquianted and -comfortable working with git_ and GitHub_. The primary means of modifying and -making contributions to OpenMC is through GitHub `pull requests`_. This is -what's known as a fork and pull development model. The steps for this are as -follows: +comfortable working with git_ and GitHub_. We assume here that you have git +installed on your system, have a GitHub account, and have setup SSH keys to be +able to create/push to repositories on GitHub. + +Overview +-------- + +Development of OpenMC relies heavily on branching; specifically, we use a +branching model sometimes referred to as `git flow`_. If you plan to contribute +to OpenMC development, we highly recommend that you read the linked blog post to +get a sense of how the branching model works. There are two main branches that +always exist: *master* and *develop*. The *master* branch is a stable branch +that contains the latest release of the code. The *develop* branch is where any +ongoing development takes place prior to a release and is not guaranteed to be +stable. When the development team decides that a release should occur, the +*develop* branch is merged into *master*. + +Trivial changes to the code may be committed directly to the *develop* branch by +a trusted developer. However, most new features should be developed on a branch +that branches off of *develop*. When the feature is completed, a `pull request`_ +is initiated on GitHub that is then reviewed by a trusted developer. If the pull +request is satisfactory, it is then merged into *develop*. Note that a trusted +developer may not review their own pull request (i.e., an independent code +review is required). + +Code Review Criteria +-------------------- + +In order to be consider suitable for for inclusion in the *develop* branch, the +following criteria must be satisfied for all proposed changes: + +- Compiles under all conditions (MPI, OpenMP, HDF5, etc.). This is checked as + part of the test suite (see `test_compile.py`_). +- Passes the regression suite. +- No memory leaks (checked with valgrind_). +- Conforms to the OpenMC `style guide`_. +- No degradation of performance or greatly increased memory usage. This is not a + hard rule -- in certain circumstances, a performance loss might be acceptable + if there are compelling reasons. +- New features/input are documented. + +Contributing +------------ + +Now that you understand the basic development workflow, let's discuss how an +individual to contribute to development. Note that this would apply to both new +features and bug fixes. The general steps for contributing are as follows: 1. Fork the main openmc repository from `mit-crpg/openmc`_. This will create a -repository with the same name under your personal account. As such, you can -commit to it as you please without disrupting other developers. + repository with the same name under your personal account. As such, you can + commit to it as you please without disrupting other developers. -2. Create a branch that you want merged back to `mit-crpg/openmc`_ and make -commits that you intend to go back. If you have made other changes that should -not be merged back, those changes should be on another branch. + .. image:: ../../img/fork.png -3. Issue a pull request from GitHub and select the branch you want merged. +2. Clone your fork of OpenMC and create a branch that branches off of *develop*: -4. The OpenMC integration manager will review your pull request and make sure it -conforms to the :ref:`devguide_styleguide`, compiles correctly, runs in parallel -correctly, does not break other features in the code, etc. Any issues with the -pull request can be discussed directly on the pull request page itself. + .. code-block:: sh -5. After the pull request has been thoroughly vetted, it is merged back into -`mit-crpg/openmc`_. + git clone git@github.com:yourusername/openmc.git + cd openmc + git checkout -b newbranch develop + +3. Make your changes on the new branch that you intend to have included in + *develop*. If you have made other changes that should not be merged back, + ensure that those changes are made on a different branch. + +4. Issue a pull request from GitHub and select the *develop* branch of + mit-crpg/openmc as the target. + + .. image:: ../../img/pullrequest.png + + At a minimum, you should describe what the changes you've made are and why + you are making them. If the changes are related to an oustanding issue, make + sure it is cross-referenced. A wise developer would also check whether their + changes do indeed pass the regression test suite. + +5. A trusted developer will review your pull request based on the criteria + above. Any issues with the pull request can be discussed directly on the pull + request page itself. + +6. After the pull request has been thoroughly vetted, it is merged back into the + *develop* branch of mit-crpg/openmc. + +Private Development +------------------- While the process above depends on the fork of the OpenMC repository being publicly available on GitHub, you may also wish to do development on a private repository for research or commercial purposes. The proper way to do this is to create a complete copy of the OpenMC repository (not a fork from GitHub). The private repository can then either be stored just locally or in conjunction with -a private repository on Github (this requires a `paid plan`_). If you want to -merge some changes you've made in your private repository back to -`mit-crpg/openmc`_ repository, simply follow the steps above with an extra step -of pulling a branch from your private repository into your public fork. +a private repository on Github (this requires a `paid plan`_). Alternatively, +`Bitbucket`_ offers private repositories for free. If you want to merge some +changes you've made in your private repository back to mit-crpg/openmc +repository, simply follow the steps above with an extra step of pulling a branch +from your private repository into a public fork. .. _git: http://git-scm.com/ .. _GitHub: https://github.com/ -.. _pull requests: https://help.github.com/articles/using-pull-requests +.. _git flow: http://nvie.com/git-model +.. _test_compile.py: https://github.com/mit-crpg/openmc/blob/develop/tests/test_compile/test_compile.py +.. _valgrind: http://valgrind.org/ +.. _style guide: http://mit-crpg.github.io/openmc/devguide/styleguide.html +.. _pull request: https://help.github.com/articles/using-pull-requests .. _mit-crpg/openmc: https://github.com/mit-crpg/openmc .. _paid plan: https://github.com/plans +.. _Bitbucket: https://bitbucket.org From d20db5f1a84a7a09409002c226ddad9fd2e1a87c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 7 Sep 2013 22:47:11 -0400 Subject: [PATCH 105/192] Added the images that I forgot before. --- docs/img/fork.png | Bin 0 -> 8601 bytes docs/img/pullrequest.png | Bin 0 -> 17703 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/img/fork.png create mode 100644 docs/img/pullrequest.png diff --git a/docs/img/fork.png b/docs/img/fork.png new file mode 100644 index 0000000000000000000000000000000000000000..f43f64242e63127dc95282c64f5b062a31a9ea96 GIT binary patch literal 8601 zcmY+Kb8y|y_we60HXAf-V<(N#*lyg|ww>HIYGYfCZR^H18#lJ?U;FvaJTuSktj_xU zv9r79_4A8@Bq|aC5&!_G(o$l|007nUH&2fM^*6@nZTt2&fiseo6a(J=`Rw+>xW5ua z2PsWw06@n67eGL2I{sfFyouEZ@RXTqhgHmfmf}Ol%#=ZKY(sL?uCe$91ydYx%x*Iq?CRE&O3c zldw3=52UXclYxxFQeX;-yWQR07ji>=I)q4;y(f55Cuir=v8#0N@w3l+Jk;zN3h)Rp zP~U_;VDO7dQvFT-mA0Y&lORyw{Z|zF&XNsxoD-R21V;ya6Z50*K z*zM@oj=#H_ShVX=WwNlUtz5T0w9W&4!`~d-acb5Pu$rZz!lr#S @IUR?AP3eBOB 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 z z!l3{jFWS*+$H2vf1KiKnPTpC1xNY}Y(&GJ;mfiC-;08@j{OVS@btXt~{PTDZ>lxN@ z#DHIBbnE}lqJYe6O|syuV++=HS-^KWQ+Baf-?Y#V)tjq~h_n5V|wdzr08 zr`dZUIH2j3UO*5611@iBa`A&|x`OxAm6%#cduu5zsdsn&`i}1#bh`}c8sGYJ+kA#C zzWPt=goGMuz+i{JMrjEE`k6Jm-dpCD<;!JK%Fj<$Jk0{Ytj=@2!L3N4-vU|N{p_&m zPgr%OEK>cm316n0TJvV1AN~+BPC1Tb?Wb_UF)~80rK2BqCWJ+bDwFJ=hoATxf3r_M zDzXoDuDlwYvQMGlhLh;mxRU)zT@P`IO%M%p<7J>k0#A^X^AEds^?! zQow?~A0@CZDV1Ku@&7eyc=5|${HeC$<>tl~_N3#RLg9>K`90A6_%!G7gH~ciu=$Fw z+Il;-3ismofmXZ3hWTjnFLXAPsy>{?$gHMhW%TOEg=wo!^I}L}_Io$*IFZ-4NmR~qw*lP2jNpM*vNNp4}Kz`u8XWu zW@(<+WIj&zcW`BXx4KLQ-x13-BjEb1M{@|Ik%auCRV}aC*odu4Qp(GvQ=}?RG?@$v z%tj>@x3x+WyE$B8A2z49d?e5Z`r579L>2|&pfvykIXJlNF6o?HC<-ZyO1KtF%(}eq z$A8#^zC&eSgzw`I8G$gYbO%^z#iKq(^S57D~+(omMa1HV(7(8<+&cX zC z;AIAX72+aQ3IHSG2Irv|FZS<;IRX?*co=9_ho@%UrAvP;GjvB+akZ_&GBx9Psjn3& z^PshwIaa*+mGsqF`Xv#&RS+stal6C!sto?lE0A0Aw^pC$=V+O~ubEB56nv`RcT{+f z$9xw-oC+}@D{ z^DOfM#g66TW0|Tt7L#f*z}!AxFc(NJQBkoUL8F-6(*N*4H*Nc@W@M87cV56rnQWeg-o`AvdR4 zj4B=3_xz}M22bIbyIQ`CJzPMyT|f1mx6K5RVx&y|vu$ZGJ9{y^VHy?M0fu zy{===O}(XxXU+Mmt(qeMm5Y(!>CN(!GiN}N5_!S#lt z^4;4-cQjeF0qoXW|D<#WJfQY6EPVNP-hz1C@=9T|q3^Dx$$kaQlNXS&7BBruy zV`EOw9G%l~$=!;bPom@Y9v52M^4)vH+im~XTkoV?Xf;825hfOimIp74pgTLgkD&eJ z;u~&#K4ZW(WCgY*rul^%oh`Vg#aJZZj^3|)xV!hjRA0V}w6PL578u3!p%xV6+uC3) zF`fp%U+z+2ElqeQG7?pky;&B_-g+_r+NU&Ka%)gJ`$p;(>ZO+Ioae3*7nj&TV645w z3_F;_0C@l&V92ovyim&t2x`+^lK=n{=R?ZsypZpyq%V4)=X1SVKQpJ?=S4if9+0YJ z+^HH&6p6jq_O_b7Eev45^IH;e{{$Mk##?+UPd+|GsZUp_=CyhFC30(>} z46^pmuFMz>qMKT4qk7N2gJ4dY7~v3N|LM8HVeg`Pl3y|1tA^zjJ_0V?3a%lNZYEf5!c z*FSXZzOR*_Q#=K--yJ$`c9CJ!z~BXpA%SU&a;fT!8d$thj2bm>!nw0Zu)?4|H(keb z9fkln-cSnK{t&qjX**+DS$B$SI*Nq{byoSY*<-KwCfn351mMW&KXf?1PIk0<0-r|E`X4ZRJB;i3`x6M5{T4lG9P5o(~Ny7i}jd(QWbQn~Q&<-HEe$FCb>qe=Lk2 zj+i-r;@IjKciit9iR&Scd~?Xkd9wRw0mMa_ldv zI*ebHjj%{gdDe55^We=o8Iowbr$=Sob3#3Y6XlBY1pXdOiJWQug!Os6w_vfR>##=+ zehDGpEwztmHnkBOkM&p*A2-k}LxNHS_r!NDpygzxeA1(y#a=Uw`FaIB-}uDO5cCA=O4`MJ!2o5iL7SP7bOt5 zx`(6@ETOr3fC6)>>u<4!`)@f?EE2JG-8(+U%bzG5I(4m8aE$s6xV(%EtnrG>6|sDc z=uYF{p0S*7iL&&l)#9t_$o*jkJJssm;z&9*JNa4lWYi^U5x>cHVmRRsulE&`J5|14 z>fX;-jW92S^sP|dpE*~;^>s3{c9h&}r<6>kxI@9ezv5#+)8p9?&qu19LJCWYtVW9q zy`qr;q7mIQ!$M{xmC$@kH2~$-L$I*^SE4^|3YQ*^sjj1_i-6)U!>Z5iL^-16zSA$3J@%U$#{-p<3*6=5k3m%_t=D1HAL%jL&*GtZrwPN@CV0_?Xoe-!%$#i3gU& z;G+(mvW`8KNZO8T^1NkI{H*&@m>662pv(+g1?TU3`$DfwWm0efd^fb`q5}oP?_HIC z5(S^8w)kg)hJu21iN_wuMXC6h-cN`_hyy%+O_ijjwMO8@401wadaBI$s{6aTVI#AT zO;g{D4U#Eh#(4V8fF~sWn$qN1x_yUDytguQVB7miRCCr@WmDn(LW1|;QZilm6pY%a z=9#<8p0ImSDF#N1M45koX{J!3aU+Rqh!*6Qh1TNy26tQX8}Vvv68a{chMAd*LtK!I z7hvb)PppqOH#eJ^iFQa^Sy-Ik-T7y_9R>ZuiLzyS6P^8W=ZRT> zoAmRQhIIEbCZ6l9=$Qy~a#5UKxAeqyFG2eorOW zcy3a{b|$TSd3cHQdjwQ5kei{a^nJLhXJtjFSk;Q1z$6r}gz>MI#6+uC;m#fG| zd!eDBo0~>PlWy@|0&cYp4SwTpgbhxHfe}$`@p!5nxQ{sIC2jpg*eKD}WhZ_qf6F0LF3)4P8m3P4(1L99Z=;QRjpjgXb= literal 0 HcmV?d00001 diff --git a/docs/img/pullrequest.png b/docs/img/pullrequest.png new file mode 100644 index 0000000000000000000000000000000000000000..8cfc71e9cd3bc5ed8d8f49f7309c5b54b8121705 GIT binary patch literal 17703 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 Ef?eNLmjD0& literal 0 HcmV?d00001 From 983022e5983b1be8ae3588911ad3779e4681ba7d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 7 Sep 2013 23:00:16 -0400 Subject: [PATCH 106/192] Added three more things to code review criteria. --- docs/source/devguide/workflow.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index 061c440ad5..9562c9584e 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -33,18 +33,21 @@ review is required). Code Review Criteria -------------------- -In order to be consider suitable for for inclusion in the *develop* branch, the +In order to be considered suitable for inclusion in the *develop* branch, the following criteria must be satisfied for all proposed changes: +- Changes have a clear purpose and are useful. - Compiles under all conditions (MPI, OpenMP, HDF5, etc.). This is checked as part of the test suite (see `test_compile.py`_). - Passes the regression suite. +- If appropriate, test cases are added to regression suite. - No memory leaks (checked with valgrind_). - Conforms to the OpenMC `style guide`_. - No degradation of performance or greatly increased memory usage. This is not a hard rule -- in certain circumstances, a performance loss might be acceptable if there are compelling reasons. - New features/input are documented. +- No unnecessary external software dependencies are introduced. Contributing ------------ From d020954a5d82d091a52dbc97f4de5437d511444e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 08:56:47 -0400 Subject: [PATCH 107/192] converted CMFD output writing to new output interface and allowed statepoint python file to read in > 1-D arrays --- src/state_point.F90 | 62 +++++++++++++++++++++-------------------- src/utils/statepoint.py | 33 +++++++++++++--------- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/state_point.F90 b/src/state_point.F90 index ceaf9021af..9ad9b8d9f2 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -20,6 +20,10 @@ module state_point use output_interface use tally_header, only: TallyObject +#ifdef MPI + use mpi +#endif + implicit none type(BinaryOutput) :: sp ! statepoint/source output file @@ -91,32 +95,32 @@ contains call sp % write_data(gen_per_batch, "gen_per_batch") call sp % write_data(k_generation, "k_generation", & length=current_batch*gen_per_batch) - call write_data(entropy, "entropy", length=current_batch*gen_per_batch) - call write_data(k_col_abs, "k_col_abs") - call write_data(k_col_tra, "k_col_tra") - call write_data(k_abs_tra, "k_abs_tra") - call write_data(k_combined, "k_combined", length=2) + call sp % write_data(entropy, "entropy", length=current_batch*gen_per_batch) + call sp % write_data(k_col_abs, "k_col_abs") + call sp % write_data(k_col_tra, "k_col_tra") + call sp % write_data(k_abs_tra, "k_abs_tra") + call sp % write_data(k_combined, "k_combined", length=2) ! Write out CMFD info if (cmfd_on) then - call write_data(1, "cmfd_on") - call write_data(cmfd % indices, "indicies", length=4, group="cmfd") - call write_data(cmfd % k_cmfd, "k_cmfd", length=current_batch, & + call sp % write_data(1, "cmfd_on") + call sp % write_data(cmfd % indices, "indicies", length=4, group="cmfd") + call sp % write_data(cmfd % k_cmfd, "k_cmfd", length=current_batch, & group="cmfd") - call write_data(cmfd % cmfd_src, "cmfd_src", & + call sp % write_data(cmfd % cmfd_src, "cmfd_src", & length=(/cmfd % indices(4), cmfd % indices(1), & cmfd % indices(2), cmfd % indices(3)/), & group="cmfd") - call write_data(cmfd % entropy, "cmfd_entropy", & + call sp % write_data(cmfd % entropy, "cmfd_entropy", & length=current_batch, group="cmfd") - call write_data(cmfd % balance, "cmfd_balance", & + call sp % write_data(cmfd % balance, "cmfd_balance", & length=current_batch, group="cmfd") - call write_data(cmfd % dom, "cmfd_dominance", & + call sp % write_data(cmfd % dom, "cmfd_dominance", & length = current_batch, group="cmfd") - call write_data(cmfd % src_cmp, "cmfd_srccmp", & + call sp % write_data(cmfd % src_cmp, "cmfd_srccmp", & length = current_batch, group="cmfd") else - call write_data(0, "cmfd_on") + call sp % write_data(0, "cmfd_on") end if end if @@ -531,27 +535,25 @@ contains n_inactive = max(n_inactive, int_array(1)) ! Read in to see if CMFD was on - call read_data(int_array(1), "cmfd_on", option="collective") + call sp % read_data(int_array(1), "cmfd_on") ! Write out CMFD info if (int_array(1) == 1) then - call read_data(cmfd % indices, "indicies", length=4, group="cmfd", & - option="collective") - call read_data(cmfd % k_cmfd, "k_cmfd", length=restart_batch, & - group="cmfd", option="collective") - call read_data(cmfd % cmfd_src, "cmfd_src", & + call sp % read_data(cmfd % indices, "indicies", length=4, group="cmfd") + call sp % read_data(cmfd % k_cmfd, "k_cmfd", length=restart_batch, & + group="cmfd") + call sp % read_data(cmfd % cmfd_src, "cmfd_src", & length=(/cmfd % indices(4), cmfd % indices(1), & cmfd % indices(2), cmfd % indices(3)/), & - group="cmfd", option="collective") - call read_data(cmfd % entropy, "cmfd_entropy", & - length=restart_batch, group="cmfd", & - option="collective") - call read_data(cmfd % balance, "cmfd_balance", & - length=restart_batch, group="cmfd", option="collective") - call read_data(cmfd % dom, "cmfd_dominance", & - length = restart_batch, group="cmfd", option="collective") - call read_data(cmfd % src_cmp, "cmfd_srccmp", & - length = restart_batch, group="cmfd", option="collective") + group="cmfd") + call sp % read_data(cmfd % entropy, "cmfd_entropy", & + length=restart_batch, group="cmfd") + call sp % read_data(cmfd % balance, "cmfd_balance", & + length=restart_batch, group="cmfd") + call sp % read_data(cmfd % dom, "cmfd_dominance", & + length = restart_batch, group="cmfd") + call sp % read_data(cmfd % src_cmp, "cmfd_srccmp", & + length = restart_batch, group="cmfd") end if end if diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index ec43435b91..5698506c92 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -152,6 +152,8 @@ class StatePoint(object): # Read statepoint revision self.revision = self._get_int(path='revision')[0] + if self.revision != 10: + raise Exception('Statepoint Revision is not consistent.') # Read OpenMC version if self._hdf5: @@ -186,28 +188,27 @@ class StatePoint(object): self.current_batch*self.gen_per_batch, path='k_generation') self.entropy = self._get_double( self.current_batch*self.gen_per_batch, path='entropy') - if self.revision >= 8: - self.k_col_abs = self._get_double(path='k_col_abs')[0] - self.k_col_tra = self._get_double(path='k_col_tra')[0] - self.k_abs_tra = self._get_double(path='k_abs_tra')[0] - self.k_combined = self._get_double(2, path='k_combined') + self.k_col_abs = self._get_double(path='k_col_abs')[0] + self.k_col_tra = self._get_double(path='k_col_tra')[0] + self.k_abs_tra = self._get_double(path='k_abs_tra')[0] + self.k_combined = self._get_double(2, path='k_combined') # Read CMFD information cmfd_present = self._get_int(path='cmfd_on')[0] if cmfd_present == 1: - self.cmfd_indices = self._get_double(4, path='cmfd/indicies') + self.cmfd_indices = self._get_int(4, path='cmfd/indicies') self.k_cmfd = self._get_double(self.current_batch, - path='cmfd/k_cmfd') - self.cmfd_src = self._get_double(np.product(self.cmfd_indices, - path='cmfd/cmfd_src')) + path='cmfd/k_cmfd') + self.cmfd_src = self._get_double(np.product(self.cmfd_indices), + path='cmfd/cmfd_src') self.cmfd_entropy = self._get_double(self.current_batch, - path='cmfd/cmfd_entropy') + path='cmfd/cmfd_entropy') self.cmfd_balance = self._get_double(self.current_batch, - path='cmfd/cmfd_balance') + path='cmfd/cmfd_balance') self.cmfd_dominance = self._get_double(self.current_batch, - path='cmfd/cmfd_dominance') + path='cmfd/cmfd_dominance') self.cmfd_srccmp = self._get_double(self.current_batch, - path='cmfd/cmfd_srccmp') + path='cmfd/cmfd_srccmp') # Read number of meshes n_meshes = self._get_int(path='tallies/n_meshes')[0] @@ -597,6 +598,12 @@ class StatePoint(object): else: return [float(v) for v in self._get_data(n, 'd', 8)] + def _get_double(self, n=1, path=None): + if self._hdf5: + return self._f[path].value + else: + return self._get_data(n, 'd', 8) + def _get_string(self, n=1, path=None): if self._hdf5: return str(self._f[path].value) From 576b45ef23c99bc14005fbb7b50f3a1c87224946 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 10:00:17 -0400 Subject: [PATCH 108/192] updated statepoint utility and cmfd results to handle printing more detalied cmfd results --- src/utils/statepoint.py | 6 +- tests/test_cmfd_feed/results.py | 24 ++ tests/test_cmfd_feed/results_true.dat | 317 ++++++++++++++++-------- tests/test_cmfd_nofeed/results.py | 24 ++ tests/test_cmfd_nofeed/results_true.dat | 121 +++++++++ 5 files changed, 392 insertions(+), 100 deletions(-) diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index 5698506c92..96b98ab671 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -199,8 +199,10 @@ class StatePoint(object): self.cmfd_indices = self._get_int(4, path='cmfd/indicies') self.k_cmfd = self._get_double(self.current_batch, path='cmfd/k_cmfd') - self.cmfd_src = self._get_double(np.product(self.cmfd_indices), + self.cmfd_src = self._get_double_array(np.product(self.cmfd_indices), path='cmfd/cmfd_src') + self.cmfd_src = np.reshape(self.cmfd_src, + tuple(self.cmfd_indices), order='F') self.cmfd_entropy = self._get_double(self.current_batch, path='cmfd/cmfd_entropy') self.cmfd_balance = self._get_double(self.current_batch, @@ -598,7 +600,7 @@ class StatePoint(object): else: return [float(v) for v in self._get_data(n, 'd', 8)] - def _get_double(self, n=1, path=None): + def _get_double_array(self, n=1, path=None): if self._hdf5: return self._f[path].value else: diff --git a/tests/test_cmfd_feed/results.py b/tests/test_cmfd_feed/results.py index 533a624558..67bbe6ca4f 100644 --- a/tests/test_cmfd_feed/results.py +++ b/tests/test_cmfd_feed/results.py @@ -53,6 +53,30 @@ outstr += 'tally 4:\n' for item in results4: outstr += "{0:12.6E}\n".format(item) +# write out cmfd answers +outstr += 'cmfd indices\n' +for item in sp.cmfd_indices: + outstr += "{0:12.6E}\n".format(item) +outstr += 'k cmfd\n' +for item in sp.k_cmfd: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd entropy\n' +for item in sp.cmfd_entropy: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd balance\n' +for item in sp.cmfd_balance: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd dominance ratio\n' +for item in sp.cmfd_dominance: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd openmc source comparison\n' +for item in sp.cmfd_srccmp: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd source\n' +cmfdsrc = np.reshape(sp.cmfd_src, np.product(sp.cmfd_indices), order='F') +for item in cmfdsrc: + outstr += "{0:12.6E}\n".format(item) + # write results to file with open('results_test.dat','w') as fh: fh.write(outstr) diff --git a/tests/test_cmfd_feed/results_true.dat b/tests/test_cmfd_feed/results_true.dat index ad4c85c761..20ed778cd2 100644 --- a/tests/test_cmfd_feed/results_true.dat +++ b/tests/test_cmfd_feed/results_true.dat @@ -1,14 +1,14 @@ k-combined: -1.167124E+00 1.217343E-02 +1.167124E+00 1.217344E-02 tally 1: 1.126891E+01 -1.275755E+01 -2.086556E+01 -4.369501E+01 +1.275756E+01 +2.086557E+01 +4.369504E+01 2.853629E+01 -8.169559E+01 -3.404506E+01 -1.165019E+02 +8.169563E+01 +3.404507E+01 +1.165020E+02 3.723908E+01 1.389881E+02 3.760048E+01 @@ -16,86 +16,86 @@ tally 1: 3.455469E+01 1.197186E+02 2.837503E+01 -8.086291E+01 -2.151789E+01 -4.648315E+01 +8.086287E+01 +2.151788E+01 +4.648312E+01 1.194541E+01 -1.432331E+01 +1.432330E+01 tally 2: -2.292173E+01 -2.650792E+01 -1.598183E+01 -1.288692E+01 -2.194143E+00 -2.510557E-01 -4.217816E+01 -8.938583E+01 +2.292174E+01 +2.650794E+01 +1.598184E+01 +1.288693E+01 +2.194144E+00 +2.510560E-01 +4.217817E+01 +8.938588E+01 2.974466E+01 -4.447038E+01 -3.939269E+00 -7.882413E-01 -5.742289E+01 +4.447040E+01 +3.939271E+00 +7.882419E-01 +5.742290E+01 1.654599E+02 -4.067581E+01 -8.308454E+01 -5.566955E+00 -1.570072E+00 -6.810586E+01 +4.067582E+01 +8.308457E+01 +5.566956E+00 +1.570073E+00 +6.810587E+01 2.331019E+02 -4.831543E+01 +4.831544E+01 1.174673E+02 -6.244999E+00 -1.967938E+00 +6.245000E+00 +1.967939E+00 7.258131E+01 2.646756E+02 5.185146E+01 -1.351425E+02 -6.622488E+00 +1.351426E+02 +6.622489E+00 2.231695E+00 7.246115E+01 2.641295E+02 -5.159542E+01 +5.159541E+01 1.339441E+02 6.702239E+00 2.277825E+00 -6.716397E+01 +6.716396E+01 2.266815E+02 -4.765063E+01 -1.141627E+02 -6.384098E+00 +4.765062E+01 +1.141626E+02 +6.384097E+00 2.063760E+00 -5.549542E+01 -1.545361E+02 +5.549541E+01 +1.545360E+02 3.940819E+01 -7.794880E+01 -5.183375E+00 -1.369483E+00 -4.155685E+01 -8.679439E+01 -2.926744E+01 -4.307145E+01 -3.938852E+00 -7.877949E-01 -2.335180E+01 -2.758241E+01 -1.622683E+01 -1.331996E+01 -2.248921E+00 -2.594934E-01 +7.794877E+01 +5.183373E+00 +1.369482E+00 +4.155684E+01 +8.679434E+01 +2.926743E+01 +4.307143E+01 +3.938851E+00 +7.877944E-01 +2.335179E+01 +2.758239E+01 +1.622682E+01 +1.331995E+01 +2.248920E+00 +2.594933E-01 tally 3: 1.537807E+01 1.193746E+01 1.044189E+00 5.627797E-02 2.861614E+01 -4.118322E+01 +4.118324E+01 1.846208E+00 1.729007E-01 -3.921462E+01 -7.723576E+01 +3.921463E+01 +7.723579E+01 2.427723E+00 2.983153E-01 -4.653207E+01 +4.653208E+01 1.089818E+02 3.128394E+00 4.945409E-01 @@ -111,15 +111,15 @@ tally 3: 1.061019E+02 2.936424E+00 4.356307E-01 -3.783939E+01 -7.188489E+01 +3.783938E+01 +7.188486E+01 2.485680E+00 3.117618E-01 2.819358E+01 -3.997588E+01 +3.997585E+01 1.875908E+00 1.772000E-01 -1.562421E+01 +1.562420E+01 1.236020E+01 1.013140E+00 5.260420E-02 @@ -160,8 +160,8 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -3.126365E+00 -4.930682E-01 +3.126366E+00 +4.930686E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -208,10 +208,10 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.456410E+00 -1.499416E+00 -2.727755E+00 -3.788867E-01 +5.456412E+00 +1.499417E+00 +2.727756E+00 +3.788869E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -256,10 +256,10 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.453480E+00 -2.790159E+00 -5.240103E+00 -1.386895E+00 +7.453481E+00 +2.790160E+00 +5.240104E+00 +1.386896E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -304,10 +304,10 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.741563E+00 -3.830225E+00 -7.137968E+00 -2.555042E+00 +8.741565E+00 +3.830226E+00 +7.137969E+00 +2.555043E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -354,8 +354,8 @@ tally 4: 0.000000E+00 9.221731E+00 4.266573E+00 -8.474116E+00 -3.604647E+00 +8.474118E+00 +3.604648E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -401,9 +401,9 @@ tally 4: 0.000000E+00 0.000000E+00 9.054937E+00 -4.124537E+00 -9.066119E+00 -4.133833E+00 +4.124536E+00 +9.066120E+00 +4.133834E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -448,8 +448,8 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.362253E+00 -3.517245E+00 +8.362251E+00 +3.517244E+00 9.086548E+00 4.156118E+00 0.000000E+00 @@ -496,10 +496,10 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -6.871573E+00 -2.369190E+00 -8.399584E+00 -3.549784E+00 +6.871571E+00 +2.369189E+00 +8.399583E+00 +3.549783E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -544,10 +544,10 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.998103E+00 -1.258194E+00 -7.145296E+00 -2.568115E+00 +4.998101E+00 +1.258193E+00 +7.145294E+00 +2.568114E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -592,10 +592,10 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.716076E+00 -3.753568E-01 -5.427593E+00 -1.484154E+00 +2.716075E+00 +3.753566E-01 +5.427591E+00 +1.484153E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -642,8 +642,8 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -3.056645E+00 -4.702349E-01 +3.056644E+00 +4.702345E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -652,3 +652,124 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 +cmfd indices +1.000000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.158665E+00 +1.173348E+00 +1.180514E+00 +1.168646E+00 +1.152485E+00 +1.154989E+00 +1.152285E+00 +1.152275E+00 +1.149476E+00 +1.154912E+00 +1.164393E+00 +1.170145E+00 +1.169161E+00 +1.167966E+00 +1.170153E+00 +1.170989E+00 +cmfd entropy +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.229801E+00 +3.226763E+00 +3.225011E+00 +3.230316E+00 +3.232043E+00 +3.228854E+00 +3.227439E+00 +3.228206E+00 +3.226749E+00 +3.222635E+00 +3.218882E+00 +3.219196E+00 +3.218057E+00 +3.220324E+00 +3.219099E+00 +3.219847E+00 +cmfd balance +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.244292E-03 +5.210436E-03 +4.212224E-03 +3.321578E-03 +3.067254E-03 +3.076634E-03 +2.601637E-03 +2.666441E-03 +2.490817E-03 +2.571411E-03 +3.013819E-03 +2.802836E-03 +2.725356E-03 +2.475281E-03 +2.279123E-03 +2.110816E-03 +cmfd dominance ratio +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.509393E-01 +5.472954E-01 +5.461377E-01 +5.518823E-01 +5.540420E-01 +5.513822E-01 +5.505071E-01 +5.498933E-01 +5.486126E-01 +5.450413E-01 +5.437663E-01 +5.421995E-01 +5.417191E-01 +5.437232E-01 +5.429525E-01 +5.428436E-01 +cmfd openmc source comparison +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.136193E-02 +1.059214E-02 +9.675100E-03 +7.296572E-03 +5.812484E-03 +6.138879E-03 +6.070297E-03 +5.791570E-03 +6.020032E-03 +7.287183E-03 +7.068423E-03 +6.380521E-03 +5.828010E-03 +4.741191E-03 +4.848114E-03 +4.686772E-03 +cmfd source +4.367444E-02 +7.866919E-02 +1.050799E-01 +1.366581E-01 +1.421824E-01 +1.393453E-01 +1.269612E-01 +1.060419E-01 +7.920472E-02 +4.218277E-02 diff --git a/tests/test_cmfd_nofeed/results.py b/tests/test_cmfd_nofeed/results.py index 533a624558..67bbe6ca4f 100644 --- a/tests/test_cmfd_nofeed/results.py +++ b/tests/test_cmfd_nofeed/results.py @@ -53,6 +53,30 @@ outstr += 'tally 4:\n' for item in results4: outstr += "{0:12.6E}\n".format(item) +# write out cmfd answers +outstr += 'cmfd indices\n' +for item in sp.cmfd_indices: + outstr += "{0:12.6E}\n".format(item) +outstr += 'k cmfd\n' +for item in sp.k_cmfd: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd entropy\n' +for item in sp.cmfd_entropy: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd balance\n' +for item in sp.cmfd_balance: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd dominance ratio\n' +for item in sp.cmfd_dominance: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd openmc source comparison\n' +for item in sp.cmfd_srccmp: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd source\n' +cmfdsrc = np.reshape(sp.cmfd_src, np.product(sp.cmfd_indices), order='F') +for item in cmfdsrc: + outstr += "{0:12.6E}\n".format(item) + # write results to file with open('results_test.dat','w') as fh: fh.write(outstr) diff --git a/tests/test_cmfd_nofeed/results_true.dat b/tests/test_cmfd_nofeed/results_true.dat index 6f00290374..be594a3fba 100644 --- a/tests/test_cmfd_nofeed/results_true.dat +++ b/tests/test_cmfd_nofeed/results_true.dat @@ -652,3 +652,124 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 +cmfd indices +1.000000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.158665E+00 +1.177323E+00 +1.179341E+00 +1.169660E+00 +1.179827E+00 +1.180220E+00 +1.177699E+00 +1.179321E+00 +1.180728E+00 +1.180996E+00 +1.186680E+00 +1.181455E+00 +1.178393E+00 +1.176470E+00 +1.172593E+00 +1.167042E+00 +cmfd entropy +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.229801E+00 +3.227279E+00 +3.224715E+00 +3.226705E+00 +3.226404E+00 +3.227329E+00 +3.227407E+00 +3.228601E+00 +3.230423E+00 +3.228093E+00 +3.226358E+00 +3.226226E+00 +3.227324E+00 +3.228594E+00 +3.229513E+00 +3.229820E+00 +cmfd balance +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.244292E-03 +5.300885E-03 +5.198276E-03 +3.759109E-03 +3.994559E-03 +3.064086E-03 +2.749389E-03 +2.388483E-03 +2.304428E-03 +2.188627E-03 +2.269076E-03 +2.016776E-03 +1.622043E-03 +1.470984E-03 +1.367144E-03 +1.402525E-03 +cmfd dominance ratio +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.509393E-01 +5.473805E-01 +5.434034E-01 +5.469278E-01 +5.460554E-01 +5.474063E-01 +5.486271E-01 +5.485095E-01 +5.490619E-01 +5.473524E-01 +5.466605E-01 +5.471154E-01 +5.480465E-01 +5.485550E-01 +5.500141E-01 +5.502154E-01 +cmfd openmc source comparison +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.136193E-02 +1.007349E-02 +8.776995E-03 +6.729971E-03 +6.921055E-03 +5.867589E-03 +4.366276E-03 +4.051899E-03 +3.406846E-03 +4.043983E-03 +4.864903E-03 +3.711687E-03 +3.124413E-03 +2.675579E-03 +2.523549E-03 +2.724884E-03 +cmfd source +4.534769E-02 +8.593793E-02 +1.059054E-01 +1.317554E-01 +1.358070E-01 +1.352224E-01 +1.290227E-01 +1.093443E-01 +7.943056E-02 +4.222668E-02 From 6e77710d308bb05fdf35a96ed65faa40732a3357 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 14:06:32 -0400 Subject: [PATCH 109/192] changed PETSc includes to modules --- src/cmfd_input.F90 | 8 +-- src/matrix_header.F90 | 12 ++--- src/solver_interface.F90 | 103 ++++++++++++++++++++------------------- src/vector_header.F90 | 12 ++--- 4 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index de23b89b09..9a768f4e3b 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -2,14 +2,14 @@ module cmfd_input use global +#ifdef PETSC + use petscsys +#endif + implicit none private public :: configure_cmfd -# ifdef PETSC -# include -# endif - contains !=============================================================================== diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index c034c8d2e7..25f1df95a5 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -1,12 +1,12 @@ module matrix_header +#ifdef PETSC + use petscmat +#endif + implicit none private -# ifdef PETSC -# include -# endif - type, public :: Matrix integer :: n ! number of rows/cols in matrix integer :: nnz ! number of nonzeros in matrix @@ -16,7 +16,7 @@ module matrix_header integer, allocatable :: col(:) ! column vector real(8), allocatable :: val(:) ! matrix value vector # ifdef PETSC - Mat :: petsc_mat + type(mat) :: petsc_mat # endif logical :: petsc_active contains @@ -296,7 +296,7 @@ contains class(Matrix) :: self #ifdef PETSC - PetscViewer :: viewer + type(PetscViewer) :: viewer call PetscViewerBinaryOpen(PETSC_COMM_WORLD, trim(filename), & FILE_MODE_WRITE, viewer, petsc_err) diff --git a/src/solver_interface.F90 b/src/solver_interface.F90 index b00bd33585..a937057d6b 100644 --- a/src/solver_interface.F90 +++ b/src/solver_interface.F90 @@ -5,18 +5,19 @@ module solver_interface use matrix_header, only: Matrix use vector_header, only: Vector +#ifdef PETSC + use petscksp + use petscsnes +#endif + implicit none private -#ifdef PETSC -# include -#endif - ! GMRES solver type type, public :: GMRESSolver #ifdef PETSC - KSP :: ksp - PC :: pc + type(ksp) :: ksp_ + type(pc) :: pc_ #endif contains #ifdef PETSC @@ -36,11 +37,11 @@ module solver_interface ! JFNK solver type type, public :: JFNKSolver #ifdef PETSC - KSP :: ksp - PC :: pc - SNES :: snes - Mat :: jac_mf - SNESLineSearch :: ls + type(ksp) :: ksp_ + type(pc) :: pc_ + type(snes) :: snes_ + type(mat) :: jac_mf + integer :: ls #endif contains #ifdef PETSC @@ -84,14 +85,14 @@ contains real(8) :: rtol = 1.0e-10_8 real(8) :: atol = 1.0e-10_8 - call KSPCreate(PETSC_COMM_WORLD, self % ksp, petsc_err) - call KSPSetTolerances(self % ksp, rtol, atol, & + call KSPCreate(PETSC_COMM_WORLD, self % ksp_, petsc_err) + call KSPSetTolerances(self % ksp_, rtol, atol, & PETSC_DEFAULT_DOUBLE_PRECISION, PETSC_DEFAULT_INTEGER, petsc_err) - call KSPSetType(self % ksp, KSPGMRES, petsc_err) - call KSPSetInitialGuessNonzero(self % ksp, PETSC_TRUE, petsc_err) - call KSPGetPC(self % ksp, self % pc, petsc_err) - call PCFactorSetLevels(self % pc, ilu_levels, petsc_err) - call KSPSetFromOptions(self % ksp, petsc_err) + call KSPSetType(self % ksp_, 'gmres', petsc_err) + call KSPSetInitialGuessNonzero(self % ksp_, PETSC_TRUE, petsc_err) + call KSPGetPC(self % ksp_, self % pc_, petsc_err) + call PCFactorSetLevels(self % pc_, ilu_levels, petsc_err) + call KSPSetFromOptions(self % ksp_, petsc_err) end subroutine petsc_gmres_create @@ -105,9 +106,9 @@ contains type(Matrix) :: prec_mat type(Matrix) :: mat_in - call KSPSetOperators(self % ksp, mat_in % petsc_mat, prec_mat % petsc_mat, & + call KSPSetOperators(self % ksp_, mat_in % petsc_mat, prec_mat % petsc_mat, & SAME_NONZERO_PATTERN, petsc_err) - call KSPSetUp(self % ksp, petsc_err) + call KSPSetUp(self % ksp_, petsc_err) end subroutine petsc_gmres_set_oper @@ -119,7 +120,7 @@ contains class(GMRESSolver) :: self - call KSPDestroy(self % ksp, petsc_err) + call KSPDestroy(self % ksp_, petsc_err) end subroutine petsc_gmres_destroy @@ -133,7 +134,7 @@ contains type(Vector) :: b type(Vector) :: x - call KSPSolve(self % ksp, b % petsc_vec, x % petsc_vec, petsc_err) + call KSPSolve(self % ksp_, b % petsc_vec, x % petsc_vec, petsc_err) end subroutine petsc_gmres_solve @@ -149,23 +150,23 @@ contains call PetscOptionsSetValue("-snes_mf_operator", "TRUE", petsc_err) ! Create the SNES context - call SNESCreate(PETSC_COMM_WORLD, self % snes, petsc_err) + call SNESCreate(PETSC_COMM_WORLD, self % snes_, petsc_err) ! Set up the GMRES solver - call SNESGetKSP(self % snes, self % ksp, petsc_err) - call KSPSetType(self % ksp, KSPGMRES, petsc_err) + call SNESGetKSP(self % snes_, self % ksp_, petsc_err) + call KSPSetType(self % ksp_, 'gmres', petsc_err) ! Apply options - call SNESGetLineSearch(self % snes, self % ls, petsc_err) - call SNESLineSearchSetType(self % ls, SNESLINESEARCHBASIC, petsc_err) - call SNESSetFromOptions(self % snes, petsc_err) + call SNESGetLineSearch(self % snes_, self % ls, petsc_err) + call SNESLineSearchSetType(self % ls, 'basic', petsc_err) + call SNESSetFromOptions(self % snes_, petsc_err) ! Set up preconditioner - call KSPGetPC(self % ksp, self % pc, petsc_err) - call PCSetType(self % pc, PCILU, petsc_err) - call PCFactorSetLevels(self % pc, 5, petsc_err) - call PCSetFromOptions(self % pc, petsc_err) - call KSPSetFromOptions(self % ksp, petsc_err) + call KSPGetPC(self % ksp_, self % pc_, petsc_err) + call PCSetType(self % pc_, 'ilu', petsc_err) + call PCFactorSetLevels(self % pc_, 5, petsc_err) + call PCSetFromOptions(self % pc_, petsc_err) + call KSPSetFromOptions(self % ksp_, petsc_err) end subroutine petsc_jfnk_create @@ -178,7 +179,7 @@ contains class(JFNKSolver) :: self call MatDestroy(self % jac_mf, petsc_err) - call SNESDestroy(self % snes, petsc_err) + call SNESDestroy(self % snes_, petsc_err) end subroutine petsc_jfnk_destroy @@ -194,19 +195,19 @@ contains type(Matrix) :: jac_prec ! Set residual procedure - call SNESSetFunction(self % snes, res % petsc_vec, & + call SNESSetFunction(self % snes_, res % petsc_vec, & petsc_jfnk_compute_residual, ctx, petsc_err) ! Create the matrix free jacobian - call MatCreateSNESMF(self % snes, self % jac_mf, petsc_err) + call MatCreateSNESMF(self % snes_, self % jac_mf, petsc_err) ! Set Jacobian procedure - call SNESSetJacobian(self % snes, self % jac_mf, jac_prec % petsc_mat, & + call SNESSetJacobian(self % snes_, self % jac_mf, jac_prec % petsc_mat, & petsc_jfnk_compute_jacobian, ctx, petsc_err) ! Set up Jacobian Lags - call SNESSetLagJacobian(self % snes, -2, petsc_err) - call SNESSetLagPreconditioner(self % snes, -1, petsc_err) + call SNESSetLagJacobian(self % snes_, -2, petsc_err) + call SNESSetLagPreconditioner(self % snes_, -1, petsc_err) end subroutine petsc_jfnk_set_functions @@ -219,7 +220,7 @@ contains class(JFNKSolver) :: self type(Vector) :: xvec - call SNESSolve(self % snes, PETSC_NULL_DOUBLE, xvec % petsc_vec, petsc_err) + call SNESSolve(self % snes_, PETSC_NULL_DOUBLE, xvec % petsc_vec, petsc_err) end subroutine petsc_jfnk_solve @@ -227,11 +228,11 @@ contains ! PETSC_JFNK_COMPUTE_RESIDUAL buffer routine to user specifed residual routine !=============================================================================== - subroutine petsc_jfnk_compute_residual(snes, x, res, ctx, ierr) + subroutine petsc_jfnk_compute_residual(snes_, x, res, ctx, ierr) - SNES :: snes - Vec :: x - Vec :: res + type(snes) :: snes_ + type(vec) :: x + type(vec) :: res integer :: ierr type(Jfnk_ctx) :: ctx @@ -259,14 +260,14 @@ contains ! PETSC_JFNK_COMPUTE_JACOBIAN buffer routine to user specified jacobian routine !=============================================================================== - subroutine petsc_jfnk_compute_jacobian(snes, x, jac_mf, jac_prec, flag, ctx, & - ierr) + subroutine petsc_jfnk_compute_jacobian(snes_, x, jac_mf, jac_prec, flag, & + ctx, ierr) - SNES :: snes - Vec :: x - Mat :: jac_mf - Mat :: jac_prec - MatStructure :: flag + type(snes) :: snes_ + type(vec) :: x + type(mat) :: jac_mf + type(mat) :: jac_prec + integer :: flag type(Jfnk_ctx) :: ctx integer :: ierr diff --git a/src/vector_header.F90 b/src/vector_header.F90 index b91f926b07..47b865ebec 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -2,19 +2,19 @@ module vector_header use constants, only: ZERO +#ifdef PETSC + use petscvec +#endif + implicit none private -# ifdef PETSC -# include -# endif - type, public :: Vector integer :: n ! number of rows/cols in matrix real(8), allocatable :: data(:) ! where vector data is stored real(8), pointer :: val(:) ! pointer to vector data # ifdef PETSC - Vec :: petsc_vec + type(vec) :: petsc_vec # endif logical :: petsc_active contains @@ -113,7 +113,7 @@ contains class(Vector) :: self #ifdef PETSC - PetscViewer :: viewer + type(PetscViewer) :: viewer call PetscViewerBinaryOpen(PETSC_COMM_WORLD, trim(filename), & FILE_MODE_WRITE, viewer, petsc_err) From 8500207b8429fceea5cf294bb765a16691fe133a Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 14:07:12 -0400 Subject: [PATCH 110/192] dominance ratio precision is loosened due to some precision difference when running with MPI --- tests/test_cmfd_feed/results.py | 2 +- tests/test_cmfd_feed/results_true.dat | 40 ++++++++++++------------- tests/test_cmfd_nofeed/results.py | 2 +- tests/test_cmfd_nofeed/results_true.dat | 40 ++++++++++++------------- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/tests/test_cmfd_feed/results.py b/tests/test_cmfd_feed/results.py index 67bbe6ca4f..f86aaa53e3 100644 --- a/tests/test_cmfd_feed/results.py +++ b/tests/test_cmfd_feed/results.py @@ -68,7 +68,7 @@ for item in sp.cmfd_balance: outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd dominance ratio\n' for item in sp.cmfd_dominance: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:10.3E}\n".format(item) outstr += 'cmfd openmc source comparison\n' for item in sp.cmfd_srccmp: outstr += "{0:12.6E}\n".format(item) diff --git a/tests/test_cmfd_feed/results_true.dat b/tests/test_cmfd_feed/results_true.dat index 20ed778cd2..0e46fd1371 100644 --- a/tests/test_cmfd_feed/results_true.dat +++ b/tests/test_cmfd_feed/results_true.dat @@ -721,26 +721,26 @@ cmfd balance 2.279123E-03 2.110816E-03 cmfd dominance ratio -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.509393E-01 -5.472954E-01 -5.461377E-01 -5.518823E-01 -5.540420E-01 -5.513822E-01 -5.505071E-01 -5.498933E-01 -5.486126E-01 -5.450413E-01 -5.437663E-01 -5.421995E-01 -5.417191E-01 -5.437232E-01 -5.429525E-01 -5.428436E-01 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 5.509E-01 + 5.473E-01 + 5.461E-01 + 5.519E-01 + 5.540E-01 + 5.514E-01 + 5.505E-01 + 5.499E-01 + 5.486E-01 + 5.450E-01 + 5.438E-01 + 5.422E-01 + 5.417E-01 + 5.437E-01 + 5.430E-01 + 5.428E-01 cmfd openmc source comparison 0.000000E+00 0.000000E+00 diff --git a/tests/test_cmfd_nofeed/results.py b/tests/test_cmfd_nofeed/results.py index 67bbe6ca4f..f86aaa53e3 100644 --- a/tests/test_cmfd_nofeed/results.py +++ b/tests/test_cmfd_nofeed/results.py @@ -68,7 +68,7 @@ for item in sp.cmfd_balance: outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd dominance ratio\n' for item in sp.cmfd_dominance: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:10.3E}\n".format(item) outstr += 'cmfd openmc source comparison\n' for item in sp.cmfd_srccmp: outstr += "{0:12.6E}\n".format(item) diff --git a/tests/test_cmfd_nofeed/results_true.dat b/tests/test_cmfd_nofeed/results_true.dat index be594a3fba..683f6a6ec3 100644 --- a/tests/test_cmfd_nofeed/results_true.dat +++ b/tests/test_cmfd_nofeed/results_true.dat @@ -721,26 +721,26 @@ cmfd balance 1.367144E-03 1.402525E-03 cmfd dominance ratio -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.509393E-01 -5.473805E-01 -5.434034E-01 -5.469278E-01 -5.460554E-01 -5.474063E-01 -5.486271E-01 -5.485095E-01 -5.490619E-01 -5.473524E-01 -5.466605E-01 -5.471154E-01 -5.480465E-01 -5.485550E-01 -5.500141E-01 -5.502154E-01 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 5.509E-01 + 5.474E-01 + 5.434E-01 + 5.469E-01 + 5.461E-01 + 5.474E-01 + 5.486E-01 + 5.485E-01 + 5.491E-01 + 5.474E-01 + 5.467E-01 + 5.471E-01 + 5.480E-01 + 5.486E-01 + 5.500E-01 + 5.502E-01 cmfd openmc source comparison 0.000000E+00 0.000000E+00 From ddbaa4a3b89e72fdb27dbdb15bf079f43b575946 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 14:18:18 -0400 Subject: [PATCH 111/192] standardized Makefile between intel and gnu compiler options --- src/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index 433095c211..2236a5d019 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,12 +41,12 @@ GIT_SHA1 = $(shell git log -1 2>/dev/null | head -n 1 | awk '{print $$2}') ifeq ($(COMPILER),gnu) F90 = gfortran - F90FLAGS := -cpp -fbacktrace + F90FLAGS := -cpp -std=f2008 -fbacktrace LDFLAGS = # Debugging ifeq ($(DEBUG),yes) - F90FLAGS += -g -Wall -pedantic -std=f2008 -fbounds-check \ + F90FLAGS += -g -Wall -pedantic -fbounds-check \ -ffpe-trap=invalid,overflow,underflow LDFLAGS += -g endif @@ -69,12 +69,12 @@ endif ifeq ($(COMPILER),intel) F90 = ifort - F90FLAGS := -fpp -warn -assume byterecl -traceback + F90FLAGS := -fpp -std08 -assume byterecl -traceback LDFLAGS = # Debugging ifeq ($(DEBUG),yes) - F90FLAGS += -g -ftrapuv -fp-stack-check -check all -fpe0 + F90FLAGS += -g -warn -ftrapuv -fp-stack-check -check all -fpe0 LDFLAGS += -g endif From 5af64724f410b0ea440a037e840ee3208abd9b32 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 16:22:09 -0400 Subject: [PATCH 112/192] added PETSc JFNK solver test --- tests/test_cmfd_jfnk/cmfd.xml | 16 + tests/test_cmfd_jfnk/geometry.xml | 43 ++ tests/test_cmfd_jfnk/materials.xml | 12 + tests/test_cmfd_jfnk/results.py | 82 +++ tests/test_cmfd_jfnk/results_true.dat | 775 +++++++++++++++++++++++++ tests/test_cmfd_jfnk/settings.xml | 32 + tests/test_cmfd_jfnk/tallies.xml | 16 + tests/test_cmfd_jfnk/test_cmfd_feed.py | 68 +++ 8 files changed, 1044 insertions(+) create mode 100644 tests/test_cmfd_jfnk/cmfd.xml create mode 100644 tests/test_cmfd_jfnk/geometry.xml create mode 100644 tests/test_cmfd_jfnk/materials.xml create mode 100644 tests/test_cmfd_jfnk/results.py create mode 100644 tests/test_cmfd_jfnk/results_true.dat create mode 100644 tests/test_cmfd_jfnk/settings.xml create mode 100644 tests/test_cmfd_jfnk/tallies.xml create mode 100644 tests/test_cmfd_jfnk/test_cmfd_feed.py diff --git a/tests/test_cmfd_jfnk/cmfd.xml b/tests/test_cmfd_jfnk/cmfd.xml new file mode 100644 index 0000000000..d97b098fd2 --- /dev/null +++ b/tests/test_cmfd_jfnk/cmfd.xml @@ -0,0 +1,16 @@ + + + + + -10 -1 -1 + 10 1 1 + 10 1 1 + 0.0 0.0 1.0 1.0 1.0 1.0 + + + 5 + balance + jfnk + true + + diff --git a/tests/test_cmfd_jfnk/geometry.xml b/tests/test_cmfd_jfnk/geometry.xml new file mode 100644 index 0000000000..57c4aa2285 --- /dev/null +++ b/tests/test_cmfd_jfnk/geometry.xml @@ -0,0 +1,43 @@ + + + + + + 0 + -1 2 -3 4 -5 6 + 1 + + + + + x-plane + 10 + vacuum + + + x-plane + -10 + vacuum + + + y-plane + 1 + reflective + + + y-plane + -1 + reflective + + + z-plane + 1 + reflective + + + z-plane + -1 + reflective + + + diff --git a/tests/test_cmfd_jfnk/materials.xml b/tests/test_cmfd_jfnk/materials.xml new file mode 100644 index 0000000000..496774661e --- /dev/null +++ b/tests/test_cmfd_jfnk/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/test_cmfd_jfnk/results.py b/tests/test_cmfd_jfnk/results.py new file mode 100644 index 0000000000..f86aaa53e3 --- /dev/null +++ b/tests/test_cmfd_jfnk/results.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +import sys +import numpy as np + +# import statepoint +sys.path.append('../../src/utils') +import statepoint + +# read in statepoint file +if len(sys.argv) > 1: + sp = statepoint.StatePoint(sys.argv[1]) +else: + sp = statepoint.StatePoint('statepoint.20.binary') +sp.read_results() + +# extract tally results and convert to vector +results1 = sp.tallies[0].results +shape1 = results1.shape +size1 = (np.product(shape1)) +results1 = np.reshape(results1, size1) +results2 = sp.tallies[1].results +shape2 = results2.shape +size2 = (np.product(shape2)) +results2 = np.reshape(results2, size2) +results3 = sp.tallies[2].results +shape3 = results3.shape +size3 = (np.product(shape3)) +results3 = np.reshape(results3, size3) +results4 = sp.tallies[3].results +shape4 = results4.shape +size4 = (np.product(shape4)) +results4 = np.reshape(results4, size4) + +# set up output string +outstr = '' + +# write out k-combined +outstr += 'k-combined:\n' +outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1]) + +# write out tally results +outstr += 'tally 1:\n' +for item in results1: + outstr += "{0:12.6E}\n".format(item) +outstr += 'tally 2:\n' +for item in results2: + outstr += "{0:12.6E}\n".format(item) +outstr += 'tally 3:\n' +for item in results3: + outstr += "{0:12.6E}\n".format(item) +outstr += 'tally 4:\n' +for item in results4: + outstr += "{0:12.6E}\n".format(item) + +# write out cmfd answers +outstr += 'cmfd indices\n' +for item in sp.cmfd_indices: + outstr += "{0:12.6E}\n".format(item) +outstr += 'k cmfd\n' +for item in sp.k_cmfd: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd entropy\n' +for item in sp.cmfd_entropy: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd balance\n' +for item in sp.cmfd_balance: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd dominance ratio\n' +for item in sp.cmfd_dominance: + outstr += "{0:10.3E}\n".format(item) +outstr += 'cmfd openmc source comparison\n' +for item in sp.cmfd_srccmp: + outstr += "{0:12.6E}\n".format(item) +outstr += 'cmfd source\n' +cmfdsrc = np.reshape(sp.cmfd_src, np.product(sp.cmfd_indices), order='F') +for item in cmfdsrc: + outstr += "{0:12.6E}\n".format(item) + +# write results to file +with open('results_test.dat','w') as fh: + fh.write(outstr) diff --git a/tests/test_cmfd_jfnk/results_true.dat b/tests/test_cmfd_jfnk/results_true.dat new file mode 100644 index 0000000000..5e78660271 --- /dev/null +++ b/tests/test_cmfd_jfnk/results_true.dat @@ -0,0 +1,775 @@ +k-combined: +1.167124E+00 1.217344E-02 +tally 1: +1.126891E+01 +1.275756E+01 +2.086557E+01 +4.369504E+01 +2.853629E+01 +8.169563E+01 +3.404507E+01 +1.165020E+02 +3.723908E+01 +1.389881E+02 +3.760048E+01 +1.416691E+02 +3.455469E+01 +1.197186E+02 +2.837503E+01 +8.086287E+01 +2.151788E+01 +4.648312E+01 +1.194541E+01 +1.432330E+01 +tally 2: +2.292174E+01 +2.650794E+01 +1.598184E+01 +1.288693E+01 +2.194144E+00 +2.510560E-01 +4.217817E+01 +8.938588E+01 +2.974466E+01 +4.447040E+01 +3.939271E+00 +7.882419E-01 +5.742290E+01 +1.654599E+02 +4.067582E+01 +8.308457E+01 +5.566956E+00 +1.570073E+00 +6.810587E+01 +2.331019E+02 +4.831544E+01 +1.174673E+02 +6.245000E+00 +1.967939E+00 +7.258131E+01 +2.646756E+02 +5.185146E+01 +1.351426E+02 +6.622489E+00 +2.231695E+00 +7.246115E+01 +2.641295E+02 +5.159541E+01 +1.339441E+02 +6.702239E+00 +2.277825E+00 +6.716396E+01 +2.266815E+02 +4.765062E+01 +1.141626E+02 +6.384097E+00 +2.063760E+00 +5.549541E+01 +1.545360E+02 +3.940819E+01 +7.794877E+01 +5.183373E+00 +1.369482E+00 +4.155684E+01 +8.679434E+01 +2.926743E+01 +4.307143E+01 +3.938851E+00 +7.877944E-01 +2.335179E+01 +2.758239E+01 +1.622682E+01 +1.331995E+01 +2.248920E+00 +2.594933E-01 +tally 3: +1.537807E+01 +1.193746E+01 +1.044189E+00 +5.627797E-02 +2.861614E+01 +4.118324E+01 +1.846208E+00 +1.729007E-01 +3.921463E+01 +7.723579E+01 +2.427723E+00 +2.983153E-01 +4.653208E+01 +1.089818E+02 +3.128394E+00 +4.945409E-01 +4.989896E+01 +1.251503E+02 +3.242191E+00 +5.330743E-01 +4.967378E+01 +1.241657E+02 +3.184751E+00 +5.164524E-01 +4.592989E+01 +1.061019E+02 +2.936424E+00 +4.356307E-01 +3.783938E+01 +7.188486E+01 +2.485680E+00 +3.117618E-01 +2.819358E+01 +3.997585E+01 +1.875908E+00 +1.772000E-01 +1.562420E+01 +1.236020E+01 +1.013140E+00 +5.260420E-02 +tally 4: +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.126366E+00 +4.930686E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.456412E+00 +1.499417E+00 +2.727756E+00 +3.788869E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.453481E+00 +2.790160E+00 +5.240104E+00 +1.386896E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.741565E+00 +3.830226E+00 +7.137969E+00 +2.555043E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.221731E+00 +4.266573E+00 +8.474118E+00 +3.604648E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.054937E+00 +4.124536E+00 +9.066120E+00 +4.133834E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.362251E+00 +3.517244E+00 +9.086548E+00 +4.156118E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.871571E+00 +2.369189E+00 +8.399583E+00 +3.549783E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.998101E+00 +1.258193E+00 +7.145294E+00 +2.568114E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.716075E+00 +3.753566E-01 +5.427591E+00 +1.484153E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.056644E+00 +4.702345E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +cmfd indices +1.000000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.158665E+00 +1.173348E+00 +1.180514E+00 +1.168646E+00 +1.152485E+00 +1.154989E+00 +1.152285E+00 +1.152275E+00 +1.149476E+00 +1.154912E+00 +1.164393E+00 +1.170145E+00 +1.169161E+00 +1.167966E+00 +1.170153E+00 +1.170989E+00 +cmfd entropy +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.229801E+00 +3.226763E+00 +3.225011E+00 +3.230316E+00 +3.232043E+00 +3.228854E+00 +3.227439E+00 +3.228206E+00 +3.226749E+00 +3.222635E+00 +3.218882E+00 +3.219196E+00 +3.218057E+00 +3.220324E+00 +3.219099E+00 +3.219847E+00 +cmfd balance +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.244292E-03 +5.210436E-03 +4.212224E-03 +3.321578E-03 +3.067254E-03 +3.076634E-03 +2.601637E-03 +2.666441E-03 +2.490817E-03 +2.571411E-03 +3.013819E-03 +2.802836E-03 +2.725356E-03 +2.475281E-03 +2.279123E-03 +2.110816E-03 +cmfd dominance ratio + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 +cmfd openmc source comparison +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.136193E-02 +1.059214E-02 +9.675100E-03 +7.296571E-03 +5.812485E-03 +6.138879E-03 +6.070297E-03 +5.791569E-03 +6.020032E-03 +7.287183E-03 +7.068423E-03 +6.380521E-03 +5.828010E-03 +4.741191E-03 +4.848115E-03 +4.686773E-03 +cmfd source +4.367444E-02 +7.866919E-02 +1.050799E-01 +1.366581E-01 +1.421824E-01 +1.393453E-01 +1.269612E-01 +1.060419E-01 +7.920472E-02 +4.218277E-02 diff --git a/tests/test_cmfd_jfnk/settings.xml b/tests/test_cmfd_jfnk/settings.xml new file mode 100644 index 0000000000..41de07f569 --- /dev/null +++ b/tests/test_cmfd_jfnk/settings.xml @@ -0,0 +1,32 @@ + + + + + + 20 + 10 + 1000 + + + + + + + + + box + -10 -1 -1 10 1 1 + + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + + true + + diff --git a/tests/test_cmfd_jfnk/tallies.xml b/tests/test_cmfd_jfnk/tallies.xml new file mode 100644 index 0000000000..b20c0ad613 --- /dev/null +++ b/tests/test_cmfd_jfnk/tallies.xml @@ -0,0 +1,16 @@ + + + + + rectangular + -10 -1 -1 + 10 1 1 + 10 1 1 + + + + + flux + + + diff --git a/tests/test_cmfd_jfnk/test_cmfd_feed.py b/tests/test_cmfd_jfnk/test_cmfd_feed.py new file mode 100644 index 0000000000..db36784888 --- /dev/null +++ b/tests/test_cmfd_jfnk/test_cmfd_feed.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +import os +from subprocess import Popen, STDOUT, PIPE, call +import filecmp +import glob + +from nose.plugins.skip import SkipTest + +from nose_mpi import NoseMPI + + +pwd = os.path.dirname(__file__) + +skipAll = False + +def setup(): + os.putenv('PWD', pwd) + os.chdir(pwd) + global skipAll + skipAll = False + +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() + output = proc.communicate()[0] + print(output) + if 'CMFD is not available' in output: + global skipAll + skipAll = True + raise SkipTest + assert returncode == 0 + +def test_created_statepoint(): + if skipAll: + raise SkipTest + statepoint = glob.glob(pwd + '/statepoint.20.*') + assert len(statepoint) == 1 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + +def test_output_exists(): + if skipAll: + raise SkipTest + assert os.path.exists(pwd + '/tallies.out') + +def test_results(): + if skipAll: + raise SkipTest + statepoint = glob.glob(pwd + '/statepoint.20.*') + 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(): + output = glob.glob(pwd + '/statepoint.20.*') + output.append(pwd + '/tallies.out') + output.append(pwd + '/results_test.dat') + for f in output: + if os.path.exists(f): + os.remove(f) + From b49e9711d4a6a797cf4015dc713e0e4331a4a102 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 16:22:38 -0400 Subject: [PATCH 113/192] reset dominance ratio to 0 in JFNK solver due to initial power iteration --- src/cmfd_jfnk_solver.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 9b14b4d1aa..4e163c5c93 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -81,8 +81,8 @@ contains subroutine init_data() - use constants, only: ONE - use global, only: cmfd, cmfd_adjoint_type + use constants, only: ZERO, ONE + use global, only: cmfd, cmfd_adjoint_type, current_batch logical :: physical_adjoint integer :: n @@ -142,6 +142,9 @@ contains ! Set up Jacobian for PETSc call jac_prec % setup_petsc() + ! Set dominance ratio to 0 + cmfd % dom(current_batch) = ZERO + end subroutine init_data !============================================================================== From 93af53d634064e603b14b942fe935b5ed2808f60 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 16:24:27 -0400 Subject: [PATCH 114/192] rename python test --- tests/test_cmfd_jfnk/{test_cmfd_feed.py => test_cmfd_jfnk.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/test_cmfd_jfnk/{test_cmfd_feed.py => test_cmfd_jfnk.py} (100%) diff --git a/tests/test_cmfd_jfnk/test_cmfd_feed.py b/tests/test_cmfd_jfnk/test_cmfd_jfnk.py similarity index 100% rename from tests/test_cmfd_jfnk/test_cmfd_feed.py rename to tests/test_cmfd_jfnk/test_cmfd_jfnk.py From 46df1146ada7cef0d97e9477bd41ba3913755fb9 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 9 Sep 2013 16:51:53 -0400 Subject: [PATCH 115/192] added some documentation on how to configure PETSc and HDF5 --- docs/source/usersguide/install.rst | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index d5ea3d9c2d..52476040b4 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -71,13 +71,27 @@ Prerequisites To compile with support for HDF5_ output (highly recommended), you will need to have HDF5 installed on your computer. The installed version will need to have been compiled with the same compiler you intend to compile - OpenMC with. + OpenMC with. HDF5_ must be built with parallel I/O features. An example + of configuring HDF5_ is listed below:: + + FC=/opt/mpich/3.0.4-gnu/bin/mpif90 CC=/opt/mpich/3.0.4-gnu/bin/mpicc \ + ./configure --prefix=/opt/hdf5/1.8.11-gnu --enable-fortran \ + --enable-fortran2003 --enable-parallel * PETSc_ for CMFD acceleration - To enable CMFD acceleration, you will need to have PETSc_ installed on - your computer. The installed version will need to have been compiled with - the same compiler you intend to compile OpenMC with. + To enable CMFD acceleration, you will need to have PETSc_ (3.4.2 or higher) + installed on your computer. The installed version will need to have been + compiled with the same compiler you intend to compile OpenMC with. OpenMC + requires PETSc_ to be configured with Fortran datatypes. An example of + configuring PETSc_ is listed below:: + + ./configure --prefix=/opt/petsc/3.4.2-gnu --download-f-blas-lapack \ + --with-mpi-dir=/opt/mpich/3.0.4-gnu/ --with-shared-libraries=0 \ + --with-fortran-datatypes + + The BLAS/LAPACK library is not required to be downloaded and can be linked + explicitly (e.g., Intel MLK library). * git_ version control software for obtaining source code From 6154c4db60eaae55ff835017eda76cde0fd4716d Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 10 Sep 2013 15:31:12 -0400 Subject: [PATCH 116/192] added more options for running test suite --- tests/run_tests.py | 53 +++++++++++++++++++++++++---- tests/test_compile/test_compile.py | 54 ++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/tests/run_tests.py b/tests/run_tests.py index 5e261fba73..e8650c52f4 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -67,22 +67,63 @@ sys.path.append(pwd) # Set list of tests, either default or from command line flags = [] -tests = ['compile', 'normal', 'debug', 'optimize', 'hdf5', 'mpi', 'phdf5', - 'petsc', 'phdf5-petsc', 'phdf5-petsc-optimize'] +tests = ['compile', 'normal', 'debug', 'optimize', + 'hdf5', 'hdf5-debug', 'hdf5-optimize', + 'mpi', 'mpi-debug', 'mpi-optimize', + 'phdf5', 'phdf5-debug', 'phdf5-optimize', + 'petsc', 'petsc-debug', 'petsc-optimize', + 'phdf5-petsc', 'phdf5-petsc-debug', 'phdf5-petsc-optimize'] if len(sys.argv) > 1: flags = [i for i in sys.argv[1:] if i.startswith('-')] tests_ = [i for i in sys.argv[1:] if not i.startswith('-')] - tests = tests_ if tests_ else tests + + # Check for special subsets of tests + tests__ = [] + for i in tests_: + + # All tests will run all the tests except for compile unless + # it is also specified on the command line. Note that specifying + # compile all-tests is the same as not specifying any args + if i == 'all-tests': + tests__ = tests + try: + idx = tests_.index('compile') # check for compile test + except ValueError: + del tests__[0] + finally: + break # don't need to check for anything else + + # This checks for any subsets of tests. The string after + # all-XXXX will be used to search through all tests. + # Specifying XXXX=normal will run tests that don't contain + # debug or optimize substring. + if i.startswith('all-'): + suffix = i.split('all-')[1] + if suffix == 'normal': + for j in tests: + if j.rfind('debug') == -1 and \ + j.rfind('optimize') == -1: + tests__.append(j) + else: + for j in tests: + if j.rfind(suffix) != -1: + tests__.append(j) + else: + tests__.append(i) # append specific test (e.g., mpi-debug) + tests = tests__ if tests__ else tests # Run tests results = [] for name in tests: if name == 'compile': run_compile() - elif name in ['normal', 'debug', 'optimize', 'hdf5']: + elif name in ['normal', 'debug', 'optimize', + 'hdf5', 'hdf5-debug', 'hdf5-optimize']: run_suite(name=name) - elif name in ['mpi', 'phdf5', 'petsc', 'phdf5-petsc', - 'phdf5-petsc-optimize']: + elif name in ['mpi', 'mpi-debug', 'mpi-optimize', + 'phdf5', 'phdf5-debug', 'phdf5-optimize', + 'petsc', 'petsc-debug', 'petsc-optimize', + 'phdf5-petsc', 'phdf5-petsc-debug', 'phdf5-petsc-optimize']: run_suite(name=name, mpi=True) # print out summary of results diff --git a/tests/test_compile/test_compile.py b/tests/test_compile/test_compile.py index 72e4dd6535..a4b3704a08 100644 --- a/tests/test_compile/test_compile.py +++ b/tests/test_compile/test_compile.py @@ -53,30 +53,84 @@ def test_mpi(): assert returncode == 0 shutil.move('openmc', 'openmc-mpi') +def test_mpi_debug(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-mpi-debug') + +def test_mpi_optimize(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'OPTIMIZE=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-mpi-optimize') + def test_hdf5(): returncode = run(['make','distclean']) returncode = run(['make', compiler, 'HDF5=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-hdf5') +def test_hdf5_debug(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'HDF5=yes', 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-hdf5-debug') + +def test_hdf5_optimize(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'HDF5=yes', 'OPTIMIZE=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-hdf5-optimize') + def test_petsc(): returncode = run(['make','distclean']) returncode = run(['make', compiler, 'MPI=yes', 'PETSC=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-petsc') +def test_petsc_debug(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'PETSC=yes', 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-petsc-debug') + +def test_petsc_optimize(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'PETSC=yes', 'OPTIMIZE=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-petsc-optimize') + def test_mpi_hdf5(): returncode = run(['make','distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-phdf5') +def test_mpi_hdf5_debug(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-phdf5-debug') + +def test_mpi_hdf5_optimize(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'OPTIMIZE=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-phdf5-optimize') + def test_mpi_hdf5_petsc(): returncode = run(['make','distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'PETSC=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-phdf5-petsc') +def test_mpi_hdf5_petsc_debug(): + returncode = run(['make','distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'PETSC=yes', 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-phdf5-petsc-debug') + def test_mpi_hdf5_petsc_optimize(): returncode = run(['make','distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'PETSC=yes', 'OPTIMIZE=yes']) From 167226fd88f8becd9099e34e3c702a2b8b6414ff Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 16 Sep 2013 15:57:18 -0400 Subject: [PATCH 117/192] updated FoX XML source --- src/xml/dom/m_dom_dom.F90 | 22 +- src/xml/dom/m_dom_utils.F90 | 12 +- src/xml/fsys/fox_m_fsys_abort_flush.F90 | 7 +- src/xml/fsys/fox_m_fsys_array_str.F90 | 14 +- src/xml/fsys/fox_m_fsys_format.F90 | 182 +++---- src/xml/fsys/fox_m_fsys_varstr.F90 | 254 +++++++++ src/xml/sax/m_sax_parser.F90 | 658 ++++++++++++------------ src/xml/sax/m_sax_reader.F90 | 11 +- src/xml/sax/m_sax_tokenizer.F90 | 302 +++++------ src/xml/sax/m_sax_types.F90 | 18 +- src/xml/utils/fox_m_utils_uri.F90 | 28 +- src/xml/wxml/FoX_wxml.F90 | 2 + src/xml/wxml/m_wxml_core.F90 | 29 ++ 13 files changed, 913 insertions(+), 626 deletions(-) create mode 100644 src/xml/fsys/fox_m_fsys_varstr.F90 diff --git a/src/xml/dom/m_dom_dom.F90 b/src/xml/dom/m_dom_dom.F90 index 6725f52433..01f6265b07 100644 --- a/src/xml/dom/m_dom_dom.F90 +++ b/src/xml/dom/m_dom_dom.F90 @@ -750,13 +750,13 @@ endif select case(np%nodeType) case (ELEMENT_NODE, ATTRIBUTE_NODE, XPATH_NAMESPACE_NODE) - call destroyElementOrAttribute(np) + call destroyElementOrAttribute(np, ex) case (DOCUMENT_TYPE_NODE) - call destroyDocumentType(np) + call destroyDocumentType(np, ex) case (ENTITY_NODE, NOTATION_NODE) - call destroyEntityOrNotation(np) + call destroyEntityOrNotation(np, ex) case (DOCUMENT_NODE) - call destroyDocument(np) + call destroyDocument(np,ex) end select call destroyNodeContents(np) deallocate(np) @@ -5846,8 +5846,7 @@ endif endif ! Switch off all GC - since this is GC! - call setGCstate(arg, .false.) - + call setGCstate(arg, .false., ex) if (arg%nodeType/=DOCUMENT_NODE) then if (getFoX_checks().or.FoX_INVALID_NODE<200) then call throw_exception(FoX_INVALID_NODE, "destroyDocument", ex) @@ -5874,9 +5873,12 @@ endif if (associated(arg%docExtras%hangingNodes%nodes)) deallocate(arg%docExtras%hangingNodes%nodes) call destroy_xml_doc_state(arg%docExtras%xds) - deallocate(arg%docExtras%xds) - deallocate(arg%docExtras%domConfig) - deallocate(arg%docExtras) + if (present(ex)) then + if (inException(ex)) return + endif + if (associated(arg%docExtras%xds)) deallocate(arg%docExtras%xds) + if (associated(arg%docExtras%domConfig)) deallocate(arg%docExtras%domConfig) + if (associated(arg%docExtras)) deallocate(arg%docExtras) call destroyAllNodesRecursively(arg, except=.true.) @@ -8491,7 +8493,7 @@ endif endif endif - elseif (.not.associated(arg, getOwnerDocument(n))) then + elseif (.not.associated(getOwnerDocument(n), target=arg)) then if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then call throw_exception(WRONG_DOCUMENT_ERR, "renameNode", ex) if (present(ex)) then diff --git a/src/xml/dom/m_dom_utils.F90 b/src/xml/dom/m_dom_utils.F90 index f0e443f21d..1234c48ad6 100644 --- a/src/xml/dom/m_dom_utils.F90 +++ b/src/xml/dom/m_dom_utils.F90 @@ -140,8 +140,11 @@ endif call normalizeDocument(startNode, ex) if (present(ex)) then ! Only possible error should be namespace error ... - if (getExceptionCode(ex)/=NAMESPACE_ERR) then - if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + ! but we should avoid turning an error of 0 into + ! an internal error! + if (inException(ex)) then + if (getExceptionCode(ex)/=NAMESPACE_ERR) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then call throw_exception(FoX_INTERNAL_ERROR, "serialize", ex) if (present(ex)) then if (inException(ex)) then @@ -150,8 +153,8 @@ endif endif endif - else - if (getFoX_checks().or.SERIALIZE_ERR<200) then + else + if (getFoX_checks().or.SERIALIZE_ERR<200) then call throw_exception(SERIALIZE_ERR, "serialize", ex) if (present(ex)) then if (inException(ex)) then @@ -160,6 +163,7 @@ endif endif endif + endif endif endif else diff --git a/src/xml/fsys/fox_m_fsys_abort_flush.F90 b/src/xml/fsys/fox_m_fsys_abort_flush.F90 index 612523bb9d..29af292fc3 100644 --- a/src/xml/fsys/fox_m_fsys_abort_flush.F90 +++ b/src/xml/fsys/fox_m_fsys_abort_flush.F90 @@ -5,6 +5,9 @@ module fox_m_fsys_abort_flush public :: pxfflush public :: pxfabort public :: pure_pxfabort + ! status values to write to stderr on termination + integer, public, parameter :: STDERR_SUCCESS_STATUS = 0 + integer, public, parameter :: STDERR_FAILURE_STATUS = 1 ! pxf.F90 - assortment of Fortran wrappers to various ! unix-y system calls. @@ -44,6 +47,7 @@ CONTAINS use f90_unix_io, only : flush #endif integer, intent(in) :: unit + integer :: i #if defined(F2003) flush(unit) @@ -52,6 +56,7 @@ CONTAINS #elif defined (FC_HAVE_FLUSH) call flush(unit) #else + i= unit ! pacify compiler continue #endif @@ -99,7 +104,7 @@ CONTAINS i=>null() Print*,i #endif - stop + stop STDERR_FAILURE_STATUS end subroutine pxfabort diff --git a/src/xml/fsys/fox_m_fsys_array_str.F90 b/src/xml/fsys/fox_m_fsys_array_str.F90 index b49554832e..88d5652568 100644 --- a/src/xml/fsys/fox_m_fsys_array_str.F90 +++ b/src/xml/fsys/fox_m_fsys_array_str.F90 @@ -31,15 +31,19 @@ contains pure function str_vs(vs) result(s) character, dimension(:), intent(in) :: vs character(len=size(vs)) :: s -#ifdef PGF90 -!PGI crashes on this use of transfer. Knob-ends. integer :: i + ! Note that we could use s = transfer(vs, s) + ! here and (in principle) this could be an O(1) + ! operation. However, this leakes memory on all + ! recent gfortrans and crashes on some versions of + ! PGF90. So the loop would need to be included with + ! an #ifdef PGF90. In any case, the looping version + ! seems to be quickers (probably due to the character + ! copying being vectorized in the loop). See: + ! http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51175 do i = 1, size(vs) s(i:i) = vs(i) enddo -#else - s = transfer(vs, s) -#endif end function str_vs diff --git a/src/xml/fsys/fox_m_fsys_format.F90 b/src/xml/fsys/fox_m_fsys_format.F90 index 448f44ebd4..6d6a6d9338 100644 --- a/src/xml/fsys/fox_m_fsys_format.F90 +++ b/src/xml/fsys/fox_m_fsys_format.F90 @@ -52,7 +52,7 @@ module fox_m_fsys_format #ifndef DUMMYLIB interface safestr -! This is for internal use only - no check is made on the validity of +! This is for internal use only - no check is made on the validity of ! any fmt input. module procedure str_string, str_string_array, str_string_matrix, & str_integer, str_integer_array, str_integer_matrix, & @@ -111,13 +111,13 @@ contains #ifndef DUMMYLIB ! NB: The len generic module procedure is used in - ! many initialisation statments (to set the + ! many initialisation statments (to set the ! length of the output string needed for the ! converted number). As of the Fortran 2008 ! spec every specific function belonging to ! a generic used in this way must be defined ! in the module before use. This is enforced - ! by at least version 7.4.4 of the Cray + ! by at least version 7.4.4 of the Cray ! Fortran compiler. Hence we put all the *_len ! functions here at the top of the file. pure function str_string_array_len(st) result(n) @@ -143,7 +143,7 @@ contains pure function str_integer_len(i) result(n) integer, intent(in) :: i integer :: n - + n = int(log10(real(max(abs(i),1)))) + 1 + dim(-i,0)/max(abs(i),1) end function str_integer_len @@ -151,7 +151,7 @@ contains pure function str_integer_base_len(i, b) result(n) integer, intent(in) :: i, b integer :: n - + n = int(log10(real(max(abs(i),1)))/log10(real(b))) & + 1 + dim(-i,0)/max(abs(i),1) @@ -161,7 +161,7 @@ contains integer, intent(in) :: i character(len=*), intent(in) :: fmt integer :: n - + select case (len(fmt)) case(0) n = 0 @@ -179,7 +179,7 @@ contains elseif (verify(fmt(2:), digit)==0) then n = str_to_int_10(fmt(2:)) else - n = 0 + n = 0 endif end select @@ -188,7 +188,7 @@ contains pure function str_integer_array_len(ia) result(n) integer, dimension(:), intent(in) :: ia integer :: n - + integer :: j n = size(ia) - 1 @@ -203,7 +203,7 @@ contains integer, dimension(:), intent(in) :: ia character(len=*), intent(in) :: fmt integer :: n - + integer :: j n = size(ia) - 1 @@ -250,7 +250,7 @@ contains pure function str_logical_len(l) result (n) logical, intent(in) :: l integer :: n - + if (l) then n = 4 else @@ -292,17 +292,17 @@ contains else e = floor(log10(abs(x))) endif - + if (x < 0.0_sp) then n = 1 else n = 0 endif - + if (len(fmt) == 0) then sig = sig_sp - n = n + sig + 2 + len(e) + n = n + sig + 2 + len(e) ! for the decimal point and the e elseif (fmt(1:1) == "s") then @@ -314,9 +314,9 @@ contains sig = max(sig, 1) sig = min(sig, digits(1.0_sp)) - if (sig > 1) n = n + 1 + if (sig > 1) n = n + 1 ! for the decimal point - + n = n + sig + 1 + len(e) elseif (fmt(1:1) == "r") then @@ -376,7 +376,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), fmt) enddo - + end function str_real_sp_array_fmt_len pure function str_real_sp_matrix_fmt_len(xa, fmt) result(n) @@ -420,17 +420,17 @@ contains else e = floor(log10(abs(x))) endif - + if (x < 0.0_dp) then n = 1 else n = 0 endif - + if (len(fmt) == 0) then sig = sig_dp - n = n + sig + 2 + len(e) + n = n + sig + 2 + len(e) ! for the decimal point and the e elseif (fmt(1:1) == "s") then @@ -442,9 +442,9 @@ contains sig = max(sig, 1) sig = min(sig, digits(1.0_dp)) - if (sig > 1) n = n + 1 + if (sig > 1) n = n + 1 ! for the decimal point - + n = n + sig + 1 + len(e) elseif (fmt(1:1) == "r") then @@ -490,7 +490,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), "") enddo - + end function str_real_dp_array_len pure function str_real_dp_array_fmt_len(xa, fmt) result(n) @@ -504,7 +504,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), fmt) enddo - + end function str_real_dp_array_fmt_len pure function str_real_dp_matrix_fmt_len(xa, fmt) result(n) @@ -558,7 +558,7 @@ contains n = size(ca) - 1 do i = 1, size(ca) - n = n + len(ca(i), fmt) + n = n + len(ca(i), fmt) enddo end function str_complex_sp_array_fmt_len @@ -590,7 +590,7 @@ contains n = len(ca, "") end function str_complex_sp_matrix_len - + pure function str_complex_dp_fmt_len(c, fmt) result(n) complex(dp), intent(in) :: c character(len=*), intent(in) :: fmt @@ -644,7 +644,7 @@ contains enddo enddo end function str_complex_dp_matrix_fmt_len - + pure function str_complex_dp_matrix_len(ca) result(n) complex(dp), dimension(:, :), intent(in) :: ca integer :: n @@ -705,7 +705,7 @@ contains ! Error is flagged by returning -1 character(len=*), intent(in) :: str integer :: n - + character(len=len(str)) :: str_l integer :: max_power, i, j @@ -739,7 +739,7 @@ contains endif enddo end function to_lower - + end function str_to_int_16 #endif @@ -747,7 +747,7 @@ contains character(len=*), intent(in) :: st #ifdef DUMMYLIB character(len=1) :: s - s = " " + s = " " #else character(len=len(st)) :: s s = st @@ -762,10 +762,10 @@ contains s = " " #else character(len=str_string_array_len(st)) :: s - + integer :: k, n character(len=1) :: d - + if (present(delimiter)) then d = delimiter else @@ -789,7 +789,7 @@ contains s = " " #else character(len=str_string_matrix_len(st)) :: s - + integer :: j, k, n character(len=1) :: d @@ -853,7 +853,7 @@ contains character :: f integer :: b, ii, j, k, n, ls - + if (len(fmt)>0) then if (fmt(1:1)=="d") then f = 'd' @@ -902,7 +902,7 @@ contains #ifdef DUMMYLIB character(len=1) :: s #else - character(len=len(ia, "d")) :: s + character(len=len(ia(:), "d")) :: s integer :: j, k, n @@ -924,7 +924,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia, fmt)) :: s + character(len=len(ia(:), fmt)) :: s integer :: j, k, n @@ -944,7 +944,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia, "d")) :: s + character(len=len(ia(:,:), "d")) :: s integer :: j, k, n @@ -954,7 +954,7 @@ contains s(n:n+len(ia(j,1))) = " "//str(ia(j,1)) n = n + len(ia(j,1)) + 1 enddo - do k = 2, size(ia, 2) + do k = 2, size(ia, 2) do j = 1, size(ia, 1) s(n:n+len(ia(j,k))) = " "//str(ia(j,k)) n = n + len(ia(j,k)) + 1 @@ -971,7 +971,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia, fmt)) :: s + character(len=len(ia(:,:), fmt)) :: s integer :: j, k, n @@ -981,7 +981,7 @@ contains s(n:n+len(ia(j,1), fmt)) = " "//str(ia(j,1), fmt) n = n + len(ia(j,1), fmt) + 1 enddo - do k = 2, size(ia, 2) + do k = 2, size(ia, 2) do j = 1, size(ia, 1) s(n:n+len(ia(j,k), fmt)) = " "//str(ia(j,k), fmt) n = n + len(ia(j,k), fmt) + 1 @@ -1000,7 +1000,7 @@ contains ! character(len=merge(4,5,l)) :: s ! And g95 (sep2007) cant resolve the generic here character(len=str_logical_len(l)) :: s - + if (l) then s="true" else @@ -1015,8 +1015,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(la)) :: s - + character(len=len(la(:))) :: s + integer :: k, n n = 1 @@ -1044,7 +1044,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(la)) :: s + character(len=len(la(:,:))) :: s integer :: j, k, n @@ -1079,12 +1079,12 @@ contains enddo #endif end function str_logical_matrix - + #ifndef DUMMYLIB ! In order to convert real numbers to strings, we need to - ! perform an internal write - but how long will the + ! perform an internal write - but how long will the ! resultant string be? We don't know & there is no way - ! to discover for an arbitrary format. Therefore, + ! to discover for an arbitrary format. Therefore, ! (if we have the capability; f95 or better) ! we assume it will be less than 100 characters, write ! it to a string of that length, then remove leading & @@ -1094,7 +1094,7 @@ contains ! If we are working with an F90-only compiler, then ! we cannot do this trick - the output string will ! always be 100 chars in length, though we will remove - ! leading whitespace. + ! leading whitespace. ! The standard Fortran format functions do not give us @@ -1104,7 +1104,7 @@ contains ! "r" which will produce output without an exponent, ! and digits after the decimal point. ! or - ! "s": which implies scientific notation, with an + ! "s": which implies scientific notation, with an ! exponent, with significant figures. ! If the integer is absent, then the precision will be ! half of the number of significant figures available @@ -1137,7 +1137,7 @@ contains real(sp) :: x_ if (sig < 1) then - s ="" + s ="" return endif @@ -1156,11 +1156,11 @@ contains enddo n = 1 do k = sig - 2, 0, -1 - ! This baroque way of taking int() ensures the optimizer + ! This baroque way of taking int() ensures the optimizer ! stores it in j without keeping a different value in cache. j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 if (j==10) then - ! This can happen if, on the previous cycle, int(x_) in + ! This can happen if, on the previous cycle, int(x_) in ! the line above gave a result approx. 1.0 less than ! expected. ! In this case we want to quit the cycle & just get 999... to the end @@ -1351,8 +1351,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa)) :: s - + character(len=len(xa(:))) :: s + integer :: j, k, n n = 1 @@ -1369,8 +1369,8 @@ contains pure function str_real_sp_array_fmt(xa, fmt) result(s) real(sp), dimension(:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa, fmt)) :: s - + character(len=len(xa(:), fmt)) :: s + integer :: j, k, n n = 1 @@ -1391,8 +1391,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa, fmt)) :: s - + character(len=len(xa(:), fmt)) :: s + if (checkFmt(fmt)) then s = safestr(xa, fmt) else @@ -1405,7 +1405,7 @@ contains pure function str_real_sp_matrix_fmt(xa, fmt) result(s) real(sp), dimension(:,:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa,fmt)) :: s + character(len=len(xa(:,:),fmt)) :: s integer :: i, j, k, n @@ -1435,7 +1435,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa,fmt)) :: s + character(len=len(xa(:,:),fmt)) :: s if (checkFmt(fmt)) then s = safestr(xa, fmt) @@ -1451,12 +1451,12 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa)) :: s + character(len=len(xa(:,:))) :: s s = safestr(xa, "") #endif end function str_real_sp_matrix - + #ifndef DUMMYLIB pure function real_dp_str(x, sig) result(s) real(dp), intent(in) :: x @@ -1467,7 +1467,7 @@ contains real(dp) :: x_ if (sig < 1) then - s ="" + s ="" return endif @@ -1490,7 +1490,7 @@ contains ! stores it in j without keeping a different value in cache. j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 if (j==10) then - ! This can happen if, on the previous cycle, int(x_) in + ! This can happen if, on the previous cycle, int(x_) in ! the line above gave a result almost exactly 1.0 less than ! expected - but FP arithmetic is not consistent. ! In this case we want to quit the cycle & just get 999... to the end @@ -1682,8 +1682,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa)) :: s - + character(len=len(xa(:))) :: s + integer :: j, k, n n = 1 @@ -1700,8 +1700,8 @@ contains pure function str_real_dp_array_fmt(xa, fmt) result(s) real(dp), dimension(:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa, fmt)) :: s - + character(len=len(xa(:), fmt)) :: s + integer :: j, k, n n = 1 @@ -1722,8 +1722,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa, fmt)) :: s - + character(len=len(xa(:), fmt)) :: s + if (checkFmt(fmt)) then s = safestr(xa, fmt) else @@ -1736,7 +1736,7 @@ contains function str_real_dp_matrix_fmt(xa, fmt) result(s) real(dp), dimension(:,:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa,fmt)) :: s + character(len=len(xa(:,:),fmt)) :: s integer :: i, j, k, n @@ -1766,7 +1766,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa,fmt)) :: s + character(len=len(xa(:,:),fmt)) :: s if (checkFmt(fmt)) then s = safestr(xa, fmt) @@ -1782,7 +1782,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa)) :: s + character(len=len(xa(:,:))) :: s s = safestr(xa, "") #endif @@ -1800,7 +1800,7 @@ contains s = " " #else character(len=len(c, fmt)) :: s - + if (checkFmt(fmt)) then s = safestr(c, fmt) else @@ -1814,7 +1814,7 @@ contains complex(sp), intent(in) :: c character(len=*), intent(in) :: fmt character(len=len(c, fmt)) :: s - + real(sp) :: re, im integer :: i re = real(c) @@ -1841,13 +1841,13 @@ contains pure function str_complex_sp_array_fmt(ca, fmt) result(s) complex(sp), dimension(:), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca, fmt)) :: s + character(len=len(ca(:), fmt)) :: s integer :: i, n - + s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) n = len(ca(1), fmt)+1 - do i = 2, size(ca) + do i = 2, size(ca) s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) n = n + len(ca(i), fmt)+1 enddo @@ -1861,7 +1861,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca, fmt)) :: s + character(len=len(ca(:), fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -1877,7 +1877,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca)) :: s + character(len=len(ca(:))) :: s s = safestr(ca, "") #endif @@ -1887,7 +1887,7 @@ contains pure function str_complex_sp_matrix_fmt(ca, fmt) result(s) complex(sp), dimension(:, :), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca, fmt)) :: s + character(len=len(ca(:,:), fmt)) :: s integer :: i, j, k, n @@ -1917,7 +1917,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca, fmt)) :: s + character(len=len(ca(:,:), fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -1933,7 +1933,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca)) :: s + character(len=len(ca(:,:))) :: s s = safestr(ca, "") #endif @@ -1947,7 +1947,7 @@ contains s = " " #else character(len=len(c, fmt)) :: s - + if (checkFmt(fmt)) then s = safestr(c, fmt) else @@ -1961,7 +1961,7 @@ contains complex(dp), intent(in) :: c character(len=*), intent(in) :: fmt character(len=len(c, fmt)) :: s - + real(dp) :: re, im integer :: i re = real(c) @@ -1988,13 +1988,13 @@ contains pure function str_complex_dp_array_fmt(ca, fmt) result(s) complex(dp), dimension(:), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca, fmt)) :: s + character(len=len(ca(:), fmt)) :: s integer :: i, n s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) n = len(ca(1), fmt)+1 - do i = 2, size(ca) + do i = 2, size(ca) s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) n = n + len(ca(i), fmt)+1 enddo @@ -2008,7 +2008,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca, fmt)) :: s + character(len=len(ca(:), fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -2024,7 +2024,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca)) :: s + character(len=len(ca(:))) :: s s = safestr(ca, "") #endif @@ -2034,7 +2034,7 @@ contains pure function str_complex_dp_matrix_fmt(ca, fmt) result(s) complex(dp), dimension(:, :), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca, fmt)) :: s + character(len=len(ca(:,:), fmt)) :: s integer :: i, j, k, n @@ -2064,7 +2064,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca, fmt)) :: s + character(len=len(ca(:,:), fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -2080,7 +2080,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca)) :: s + character(len=len(ca(:,:))) :: s s = safestr(ca, "") #endif diff --git a/src/xml/fsys/fox_m_fsys_varstr.F90 b/src/xml/fsys/fox_m_fsys_varstr.F90 new file mode 100644 index 0000000000..52efa5fde9 --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_varstr.F90 @@ -0,0 +1,254 @@ +module fox_m_fsys_varstr + implicit none + + private + + public :: varstr + public :: init_varstr + public :: destroy_varstr + public :: varstr_len + public :: is_varstr_empty + public :: set_varstr_empty + public :: is_varstr_null + public :: set_varstr_null + public :: vs_varstr_alloc + public :: move_varstr_vs + public :: move_varstr_varstr + public :: str_varstr + public :: append_varstr + public :: varstr_str + public :: varstr_vs + public :: equal_varstr_str + public :: equal_varstr_varstr + + ! Allocation step in which the data within varstr will be allocated + integer, parameter :: VARSTR_INIT_SIZE=1024 + integer, parameter :: VARSTR_ALLOC_SIZE=1024 + + ! Variable size string type. + ! It is used for token field, so that it can be grown + ! by one character at a time without incurring constant + ! penalty on allocating/deallocating vs data. + ! See functions and subroutines at the end of this module. + type varstr + private + character, dimension(:), pointer :: data + integer :: length + end type varstr + +contains + +! Initialise varstr type. The string is initialised as null (i.e. invalid) +subroutine init_varstr(vstr) + type(varstr), intent(inout) :: vstr + allocate(vstr%data(VARSTR_INIT_SIZE)) + vstr%length = -1 +end subroutine init_varstr + +! Clean up memory (leaves varstr null, and drops the data field) +subroutine destroy_varstr(vstr) + type(varstr), intent(inout) :: vstr + if (associated(vstr%data)) deallocate(vstr%data) + call set_varstr_null(vstr) +end subroutine destroy_varstr + +! Return real length of varstr +function varstr_len(vstr) result(l) + type(varstr), intent(in) :: vstr + integer :: l + if (vstr%length<0) print *, "WARNING: asking for length of null varstr" + l = vstr%length +end function varstr_len + + +! Make sure that varstr is at least size-n. +! The data will be kept (copied) if keep is true (default) +! This can be called on a null varstr, but should noty be called on +! one which was destroyed. +subroutine ensure_varstr_size(vstr,n,keep) + type(varstr), intent(inout) :: vstr + integer, intent(in) :: n + logical, optional, intent(in) :: keep + + character, pointer, dimension(:) :: new_data + integer :: new_size, old_size + logical :: keep_flag + + if (present(keep)) then + keep_flag = keep + else + keep_flag = .true. + end if + + old_size = size(vstr%data) + if (n <= old_size ) return + + new_size = old_size + ((n-old_size)/VARSTR_ALLOC_SIZE+1) * VARSTR_ALLOC_SIZE + allocate(new_data(new_size)) + + if (keep_flag) new_data(1:old_size) = vstr%data(1:old_size) + + deallocate( vstr%data ) + vstr%data => new_data +end subroutine ensure_varstr_size + +! Returns whether varstr is empty: "" +function is_varstr_empty(vstr) + type(varstr), intent(in) :: vstr + logical is_varstr_empty + is_varstr_empty = (vstr%length == 0) +end function is_varstr_empty + +! Set vstr to empty string +subroutine set_varstr_empty(vstr) + type(varstr), intent(inout) :: vstr + vstr%length = 0 +end subroutine set_varstr_empty + +! Returns whether varstr is null (i.e. invalid) +function is_varstr_null(vstr) + type(varstr), intent(in) :: vstr + logical is_varstr_null + is_varstr_null = (vstr%length < 0) +end function is_varstr_null + +! Set vstr to null +subroutine set_varstr_null(vstr) + type(varstr), intent(inout) :: vstr + vstr%length = -1 +end subroutine set_varstr_null + +! Convert varstr to newly allocated array of characters +function vs_varstr_alloc(vstr) result(vs) + type(varstr) :: vstr + character, dimension(:), pointer :: vs + + if (is_varstr_null(vstr)) then + print *, "WARNING: Converting null varstr to string... making it empty first" + call set_varstr_empty(vstr) + end if + + allocate(vs(vstr%length)) + vs = vstr%data(1:vstr%length) +end function vs_varstr_alloc + +! This call moves data from varstr to vs (i.e. vs is overwritten and vstr is made null) +subroutine move_varstr_vs(vstr,vs) + type(varstr), intent(inout) :: vstr + character, dimension(:), pointer, intent(inout) :: vs + + if (associated(vs)) deallocate(vs) + vs => vs_varstr_alloc(vstr) + call set_varstr_null(vstr) +end subroutine move_varstr_vs + +! This call moves data from varstr to varstr (src becomes null) +subroutine move_varstr_varstr(src,dst) + type(varstr), intent(inout) :: src + type(varstr), intent(inout) :: dst + character, dimension(:), pointer :: tmpdata + + tmpdata => dst%data + dst%data => src%data + src%data => tmpdata + dst%length = src%length + + call set_varstr_null(src) +end subroutine move_varstr_varstr + + +! Convert varstr to string type +function str_varstr(vstr) result(s) + type(varstr), intent(in) :: vstr + character(len=vstr%length) :: s + integer :: i + + if (is_varstr_null(vstr)) then + ! Can we really end-up here? Or will it blow on allocation with len=-1 ? + print *, "WARNING: Trying to convert null varstr to str... returning empty string" + s = "" + end if + + do i = 1, vstr%length + s(i:i) = vstr%data(i) + enddo +end function str_varstr + +! Append string to varstr +subroutine append_varstr(vstr,str) + type(varstr), intent(inout) :: vstr + character(len=*), intent(in) :: str + character, dimension(:), pointer :: tmp + integer :: i + + if (is_varstr_null(vstr)) then + print *, "WARNING: Trying to append to null varstr... making it empty first" + call set_varstr_empty(vstr) + end if + + call ensure_varstr_size(vstr,vstr%length+len(str)) + + ! Note: on a XML file with very large tokens, this loop + ! is consistently faster than equivalent 'transfer' intrinsic + do i=1,len(str) + vstr%data(vstr%length+i) = str(i:i) + end do + vstr%length = vstr%length + len(str) +end subroutine append_varstr + +! Convert string to varstr in place +subroutine varstr_str(vstr,str) + type(varstr), intent(inout) :: vstr + character(len=*), intent(in) :: str + integer :: i + + call ensure_varstr_size(vstr,len(str),.false.) + do i=1,len(str) + vstr%data(i) = str(i:i) + end do + vstr%length = len(str) +end subroutine varstr_str + +! Convert character array to varstr in place +subroutine varstr_vs(vstr,vs) + type(varstr), intent(inout) :: vstr + character, dimension(:), intent(in) :: vs + + call ensure_varstr_size(vstr,size(vs),.false.) + + vstr%length = size(vs) + vstr%data(1:size(vs)) = vs +end subroutine varstr_vs + +! Compare varstr to str (true if equal) +function equal_varstr_str(vstr,str) result(r) + type(varstr), intent(in) :: vstr + character(len=*) :: str + logical :: r + + integer :: i + + r = .false. + if ( len(str) /= varstr_len(vstr) ) return + do i=1,len(str) + if ( str(i:i) /= vstr%data(i) ) return + end do + r = .true. +end function equal_varstr_str + +! Compare varstr to varstr (true if equal) +function equal_varstr_varstr(vstr1,vstr2) result(r) + type(varstr), intent(in) :: vstr1, vstr2 + logical :: r + + integer :: i + + r = .false. + if ( varstr_len(vstr1) /= varstr_len(vstr2) ) return + do i=1,varstr_len(vstr1) + if ( vstr1%data(i) /= vstr2%data(i) ) return + end do + r = .true. +end function equal_varstr_varstr + +end module fox_m_fsys_varstr diff --git a/src/xml/sax/m_sax_parser.F90 b/src/xml/sax/m_sax_parser.F90 index 6d63fd046c..c2f2283fb8 100644 --- a/src/xml/sax/m_sax_parser.F90 +++ b/src/xml/sax/m_sax_parser.F90 @@ -5,6 +5,7 @@ module m_sax_parser use fox_m_fsys_string_list, only: string_list, destroy_string_list, & tokenize_to_string_list, registered_string, init_string_list, & add_string, tokenize_and_add_strings, destroy + use fox_m_fsys_varstr use m_common_attrs, only: init_dict, destroy_dict, reset_dict, & add_item_to_dict, has_key, get_value, get_att_index_pointer, & getLength, setIsId, setBase @@ -44,7 +45,7 @@ module m_sax_parser use m_sax_reader, only: file_buffer_t, pop_buffer_stack, open_new_string, & open_new_file, parse_xml_declaration, parse_text_declaration, & - reading_main_file, reading_first_entity + reading_main_file, reading_first_entity, add_error_position use m_sax_tokenizer, only: sax_tokenize, normalize_attribute_text, & expand_pe_text use m_sax_types ! everything, really @@ -76,7 +77,14 @@ contains nullURI => null() #endif - allocate(fx%token(0)) + call init_varstr(fx%token) + call init_varstr(fx%content) + call init_varstr(fx%name) + call init_varstr(fx%attname) + call init_varstr(fx%PublicId) + call init_varstr(fx%systemId) + call init_varstr(fx%Ndata) + call init_varstr(fx%root_element) call init_error_stack(fx%error_stack) call init_elstack(fx%elstack) @@ -123,8 +131,8 @@ contains fx%context = CTXT_NULL fx%state = ST_NULL - if (associated(fx%token)) deallocate(fx%token) - if (associated(fx%root_element)) deallocate(fx%root_element) + call destroy_varstr(fx%token) + call destroy_varstr(fx%root_element) call destroy_error_stack(fx%error_stack) call destroy_elstack(fx%elstack) @@ -140,13 +148,14 @@ contains call destroy_entity_list(fx%forbidden_pe_list) call destroy_entity_list(fx%predefined_e_list) - if (associated(fx%token)) deallocate(fx%token) - if (associated(fx%content)) deallocate(fx%content) - if (associated(fx%name)) deallocate(fx%name) - if (associated(fx%attname)) deallocate(fx%attname) - if (associated(fx%publicId)) deallocate(fx%publicId) - if (associated(fx%systemId)) deallocate(fx%systemId) - if (associated(fx%Ndata)) deallocate(fx%Ndata) + call destroy_varstr(fx%token) + call destroy_varstr(fx%content) + call destroy_varstr(fx%name) + call destroy_varstr(fx%attname) + call destroy_varstr(fx%publicId) + call destroy_varstr(fx%systemId) + call destroy_varstr(fx%Ndata) + call destroy_varstr(fx%root_element) end subroutine sax_parser_destroy @@ -373,7 +382,7 @@ contains logical :: validCheck, startInCharData_, processDTD, pe, nameOK, eof logical :: namespaces_, namespace_prefixes_, xmlns_uris_, externalEntity_ integer :: i, iostat, temp_i, nextState, ignoreDepth, declSepValue - character, pointer :: tempString(:) + character, pointer :: tempString(:), tempString2(:) character :: dummy type(element_t), pointer :: elem type(attribute_t), pointer :: attDecl @@ -387,6 +396,7 @@ contains nullURI => null() #endif tempString => null() + tempString2 => null() elem => null() attDecl => null() @@ -521,8 +531,7 @@ contains if (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & .or.fx%state_dtd==ST_DTD_ELEMENT_CONTENTS) then ! stick the token back in contents ... - fx%content => fx%token - fx%token => vs_str_alloc("") + call move_varstr_varstr(fx%token,fx%content) endif if (reading_main_file(fb)) & fx%inIntSubset = .true. @@ -565,7 +574,7 @@ contains select case (fx%state) case (ST_MISC) - !write(*,*) 'ST_MISC', str_vs(fx%token) + !write(*,*) 'ST_MISC', str_varstr(fx%token) select case (fx%tokenType) case (TOK_PI_TAG) wf_stack(1) = wf_stack(1) + 1 @@ -587,7 +596,7 @@ contains case (TOK_OPEN_COMMENT) nextState = ST_START_COMMENT case (TOK_NAME) - if (str_vs(fx%token)=='DOCTYPE') then + if (equal_varstr_str(fx%token,'DOCTYPE')) then fx%context = CTXT_IN_DTD nextState = ST_IN_DOCTYPE endif @@ -599,19 +608,18 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (nameOk) then - if (str_vs(fx%token)=='xml') then + if (equal_varstr_str(fx%token,'xml')) then call add_error(fx%error_stack, "XML declaration must be at start of document") goto 100 - elseif (checkPITarget(str_vs(fx%token), fx%xds%xml_version)) then + elseif (checkPITarget(str_varstr(fx%token), fx%xds%xml_version)) then nextState = ST_PI_CONTENTS - fx%name => fx%token - fx%token => null() - else + call move_varstr_varstr(fx%token,fx%name) + else call add_error(fx%error_stack, "Invalid PI target name") goto 100 endif @@ -631,17 +639,17 @@ contains select case(fx%tokenType) case (TOK_CHAR) if (present(processingInstruction_handler)) then - call processingInstruction_handler(str_vs(fx%name), str_vs(fx%token)) + call processingInstruction_handler(str_varstr(fx%name), str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextState = ST_PI_END case (TOK_PI_END) if (present(processingInstruction_handler)) then - call processingInstruction_handler(str_vs(fx%name), "") + call processingInstruction_handler(str_varstr(fx%name), "") if (fx%state==ST_STOP) goto 100 endif - deallocate(fx%name) + call set_varstr_null(fx%name) if (fx%context==CTXT_IN_CONTENT) then nextState = ST_CHAR_IN_CONTENT else @@ -664,8 +672,7 @@ contains !write(*,*)'ST_START_COMMENT' select case (fx%tokenType) case (TOK_CHAR) - fx%name => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%name) nextState = ST_COMMENT_END end select @@ -682,10 +689,10 @@ contains select case (fx%tokenType) case (TOK_COMMENT_END) if (present(comment_handler)) then - call comment_handler(str_vs(fx%name)) + call comment_handler(str_varstr(fx%name)) if (fx%state==ST_STOP) goto 100 endif - deallocate(fx%name) + call set_varstr_null(fx%name) if (fx%context==CTXT_IN_CONTENT) then nextState = ST_CHAR_IN_CONTENT else @@ -701,16 +708,15 @@ contains .or. fx%context==CTXT_BEFORE_CONTENT & .or. fx%context==CTXT_IN_CONTENT) then if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Illegal element name") goto 100 endif - fx%name => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%name) nextState = ST_IN_TAG elseif (fx%context == CTXT_AFTER_CONTENT) then call add_error(fx%error_stack, "Cannot open second root element") @@ -725,7 +731,7 @@ contains !write(*,*) "ST_START_CDATA_DECLARATION" select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=="CDATA") then + if (equal_varstr_str(fx%token,"CDATA")) then if (fx%context/=CTXT_IN_CONTENT) then call add_error(fx%error_stack, "CDATA section only allowed in text content.") goto 100 @@ -749,8 +755,7 @@ contains !write(*,*)'ST_CDATA_CONTENTS' select case (fx%tokenType) case (TOK_CHAR) - fx%name => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%name) nextState = ST_CDATA_END end select @@ -770,9 +775,9 @@ contains call startCdata_handler if (fx%state==ST_STOP) goto 100 endif - if (size(fx%name)>0) then + if (.not.is_varstr_empty(fx%name)) then if (present(characters_handler)) then - call characters_handler(str_vs(fx%name)) + call characters_handler(str_varstr(fx%name)) if (fx%state==ST_STOP) goto 100 endif endif @@ -780,7 +785,7 @@ contains call endCdata_handler if (fx%state==ST_STOP) goto 100 endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextState = ST_CHAR_IN_CONTENT end select @@ -789,14 +794,14 @@ contains select case (fx%tokenType) case (TOK_END_TAG) if (fx%context /= CTXT_IN_CONTENT) then - if (associated(fx%root_element)) then + if (.not.is_varstr_null(fx%root_element)) then if (validCheck) then - if (str_vs(fx%name)/=str_vs(fx%root_element)) then + if (.not.equal_varstr_varstr(fx%name,fx%root_element)) then call add_error(fx%error_stack, "Root element name does not match document name") goto 100 endif endif - deallocate(fx%root_element) + call set_varstr_null(fx%root_element) elseif (validCheck) then call add_error(fx%error_stack, "No DTD defined") goto 100 @@ -813,7 +818,7 @@ contains call open_tag if (in_error(fx%error_stack)) goto 100 if (fx%state==ST_STOP) goto 100 - deallocate(fx%name) + call set_varstr_null(fx%name) nextState = ST_CHAR_IN_CONTENT case (TOK_END_TAG_CLOSE) @@ -821,14 +826,14 @@ contains nextState = ST_CHAR_IN_CONTENT else ! only a single element in this doc - if (associated(fx%root_element)) then + if (.not.is_varstr_null(fx%root_element)) then if (validCheck) then - if (str_vs(fx%name)/=str_vs(fx%root_element)) then + if (.not.equal_varstr_varstr(fx%name,fx%root_element)) then call add_error(fx%error_stack, "Root element name does not match document name") goto 100 endif endif - deallocate(fx%root_element) + call set_varstr_null(fx%root_element) elseif (validCheck) then call add_error(fx%error_stack, "No DTD defined") goto 100 @@ -846,7 +851,7 @@ contains call close_tag if (in_error(fx%error_stack)) goto 100 if (fx%state==ST_STOP) goto 100 - deallocate(fx%name) + call set_varstr_null(fx%name) if (fx%context/=CTXT_IN_CONTENT) then fx%well_formed = .true. fx%context = CTXT_AFTER_CONTENT @@ -855,23 +860,22 @@ contains case (TOK_NAME) if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Illegal attribute name") goto 100 endif !Have we already had this dictionary item? - if (has_key(fx%attributes, str_vs(fx%token))) then + if (has_key(fx%attributes, str_varstr(fx%token))) then call add_error(fx%error_stack, "Duplicate attribute name") goto 100 endif - fx%attname => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%attname) if (associated(elem)) then - attDecl => get_attribute_declaration(elem, str_vs(fx%attname)) + attDecl => get_attribute_declaration(elem, str_varstr(fx%attname)) else attDecl => null() endif @@ -892,9 +896,11 @@ contains select case (fx%tokenType) case (TOK_CHAR) !First, expand all entities: - tempString => normalize_attribute_text(fx, fx%token) - deallocate(fx%token) - fx%token => tempString + tempString2 => vs_varstr_alloc(fx%token) + tempString => normalize_attribute_text(fx, tempString2, fb) + call varstr_vs( fx%token, tempString ) + deallocate(tempString) + deallocate(tempString2) tempString => null() !If this attribute is not CDATA, we must process further; if (associated(attDecl)) then @@ -903,23 +909,23 @@ contains temp_i = ATT_CDATA endif if (temp_i==ATT_CDATA) then - call add_item_to_dict(fx%attributes, str_vs(fx%attname), & - str_vs(fx%token), itype=ATT_CDATA, declared=associated(attDecl)) + call add_item_to_dict(fx%attributes, str_varstr(fx%attname), & + str_varstr(fx%token), itype=ATT_CDATA, declared=associated(attDecl)) else if (validCheck) then if (fx%xds%standalone.and..not.attDecl%internal & - .and.(str_vs(fx%token)//"x"/=att_value_normalize(str_vs(fx%token))//"x")) then + .and.(str_varstr(fx%token)//"x"/=att_value_normalize(str_varstr(fx%token))//"x")) then call add_error(fx%error_stack, & "Externally-declared attribute value normalization results in changed value "// & "in standalone document") goto 100 endif endif - call add_item_to_dict(fx%attributes, str_vs(fx%attname), & - att_value_normalize(str_vs(fx%token)), itype=temp_i, & + call add_item_to_dict(fx%attributes, str_varstr(fx%attname), & + att_value_normalize(str_varstr(fx%token)), itype=temp_i, & declared=.true.) endif - deallocate(fx%attname) + call set_varstr_null(fx%attname) nextState = ST_IN_TAG end select @@ -927,17 +933,17 @@ contains !write(*,*)'ST_CHAR_IN_CONTENT' select case (fx%tokenType) case (TOK_CHAR) - if (size(fx%token)>0) then + if (varstr_len(fx%token)>0) then if (validCheck) then if (elementContent(fx%elstack)) then - if (verify(str_vs(fx%token), XML_WHITESPACE)==0) then + if (verify(str_varstr(fx%token), XML_WHITESPACE)==0) then if (fx%xds%standalone.and..not.elem%internal) then call add_error(fx%error_stack, & "Externally-specified ignorable whitespace used in standalone document") goto 100 endif if (present(ignorableWhitespace_handler)) then - call ignorableWhitespace_handler(str_vs(fx%token)) + call ignorableWhitespace_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif else @@ -949,13 +955,13 @@ contains goto 100 else if (present(characters_handler)) then - call characters_handler(str_vs(fx%token)) + call characters_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif endif else if (present(characters_handler)) then - call characters_handler(str_vs(fx%token)) + call characters_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif endif @@ -998,15 +1004,15 @@ contains 'Encountered reference to undeclared entity') endif endif - ent => getEntityByName(fx%forbidden_ge_list, str_vs(fx%token)) + ent => getEntityByName(fx%forbidden_ge_list, str_varstr(fx%token)) if (associated(ent)) then call add_error(fx%error_stack, 'Recursive entity reference') goto 100 endif - ent => getEntityByName(fx%predefined_e_list, str_vs(fx%token)) + ent => getEntityByName(fx%predefined_e_list, str_varstr(fx%token)) if (associated(ent)) then if (present(startEntity_handler)) then - call startEntity_handler(str_vs(fx%token)) + call startEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif if (validCheck) then @@ -1019,15 +1025,15 @@ contains endif endif if (present(characters_handler)) then - call characters_handler(expand_entity(fx%predefined_e_list, str_vs(fx%token))) + call characters_handler(expand_entity(fx%predefined_e_list, str_varstr(fx%token))) if (fx%state==ST_STOP) goto 100 endif if (present(endEntity_handler)) then - call endEntity_handler(str_vs(fx%token)) + call endEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif - elseif (likeCharacterEntityReference(str_vs(fx%token))) then - if (checkRepCharEntityReference(str_vs(fx%token), fx%xds%xml_version)) then + elseif (likeCharacterEntityReference(str_varstr(fx%token))) then + if (checkRepCharEntityReference(str_varstr(fx%token), fx%xds%xml_version)) then if (validCheck) then if (associated(elem)) then if (.not.elem%mixed.and..not.elem%any) then @@ -1038,18 +1044,18 @@ contains endif endif if (present(characters_handler)) then - call characters_handler(expand_char_entity(str_vs(fx%token))) + call characters_handler(expand_char_entity(str_varstr(fx%token))) if (fx%state==ST_STOP) goto 100 endif - elseif (checkCharacterEntityReference(str_vs(fx%token), fx%xds%xml_version)) then + elseif (checkCharacterEntityReference(str_varstr(fx%token), fx%xds%xml_version)) then call add_error(fx%error_stack, "Unable to digest character entity reference in content, sorry.") goto 100 else call add_error(fx%error_stack, "Illegal character reference") goto 100 endif - elseif (existing_entity(fx%xds%entityList, str_vs(fx%token))) then - ent => getEntityByName(fx%xds%entityList, str_vs(fx%token)) + elseif (existing_entity(fx%xds%entityList, str_varstr(fx%token))) then + ent => getEntityByName(fx%xds%entityList, str_varstr(fx%token)) if (ent%wfc.and.fx%xds%standalone) then call add_error(fx%error_stack, & 'Externally declared entity referenced in standalone document') @@ -1063,22 +1069,22 @@ contains if (iostat/=0) then if (validCheck) then call add_error(fx%error_stack, & - "Unable to retrieve external entity "//str_vs(fx%token)) + "Unable to retrieve external entity "//str_varstr(fx%token)) goto 100 endif if (present(skippedEntity_handler)) then - call skippedEntity_handler(str_vs(fx%token)) + call skippedEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif else if (present(startEntity_handler)) then - call startEntity_handler(str_vs(fx%token)) + call startEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 - call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", nullURI, .false.) + call add_internal_entity(fx%forbidden_ge_list, str_varstr(fx%token), "", nullURI, .false.) #else - call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", null(), .false.) + call add_internal_entity(fx%forbidden_ge_list, str_varstr(fx%token), "", null(), .false.) #endif temp_wf_stack => wf_stack allocate(wf_stack(size(temp_wf_stack)+1)) @@ -1100,15 +1106,16 @@ contains endif endif if (present(startEntity_handler)) then - call startEntity_handler(str_vs(fx%token)) + call startEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 - call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", nullURI, .false.) + call add_internal_entity(fx%forbidden_ge_list, str_varstr(fx%token), "", nullURI, .false.) #else - call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", null(), .false.) + call add_internal_entity(fx%forbidden_ge_list, str_varstr(fx%token), "", null(), .false.) #endif - call open_new_string(fb, expand_entity(fx%xds%entityList, str_vs(fx%token)), str_vs(fx%token), baseURI=ent%baseURI) + call open_new_string(fb, expand_entity(fx%xds%entityList, str_varstr(fx%token)), & + str_varstr(fx%token), baseURI=ent%baseURI) temp_wf_stack => wf_stack allocate(wf_stack(size(temp_wf_stack)+1)) wf_stack = (/0, temp_wf_stack/) @@ -1118,7 +1125,7 @@ contains ! Unknown entity check standalone etc if (fx%skippedExternal.and..not.fx%xds%standalone) then if (present(skippedEntity_handler)) then - call skippedEntity_handler(str_vs(fx%token)) + call skippedEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif else @@ -1133,9 +1140,8 @@ contains !write(*,*)'ST_CLOSING_TAG' select case (fx%tokenType) case (TOK_NAME) - if (checkName(str_vs(fx%token), fx%xds%xml_version)) then - fx%name => fx%token - nullify(fx%token) + if (checkName(str_varstr(fx%token), fx%xds%xml_version)) then + call move_varstr_varstr(fx%token,fx%name) nextState = ST_IN_CLOSING_TAG else call add_error(fx%error_stack, "Closing tag: expecting a Name") @@ -1150,7 +1156,7 @@ contains call close_tag if (in_error(fx%error_stack)) goto 100 if (fx%state==ST_STOP) goto 100 - deallocate(fx%name) + call set_varstr_null(fx%name) if (is_empty(fx%elstack)) then if (startInCharData_) then fx%well_formed = .true. @@ -1175,31 +1181,30 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid document name") goto 100 endif - fx%root_element => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%root_element) nextState = ST_DOC_NAME end select case (ST_DOC_NAME) - !write(*,*) 'ST_DOC_NAME ', str_vs(fx%token) + !write(*,*) 'ST_DOC_NAME ', str_varstr(fx%token) select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=='SYSTEM') then + if (equal_varstr_str(fx%token,'SYSTEM')) then nextState = ST_DOC_SYSTEM - elseif (str_vs(fx%token)=='PUBLIC') then + elseif (equal_varstr_str(fx%token,'PUBLIC')) then nextState = ST_DOC_PUBLIC endif case (TOK_OPEN_SB) if (present(startDTD_handler)) then - call startDTD_handler(str_vs(fx%root_element), "", "") + call startDTD_handler(str_varstr(fx%root_element), "", "") if (fx%state==ST_STOP) goto 100 endif wf_stack(1) = wf_stack(1) + 1 @@ -1207,7 +1212,7 @@ contains fx%inIntSubset = .true. case (TOK_END_TAG) if (present(startDTD_handler)) then - call startDTD_handler(str_vs(fx%root_element), "", "") + call startDTD_handler(str_varstr(fx%root_element), "", "") if (fx%state==ST_STOP) goto 100 endif wf_stack(1) = wf_stack(1) - 1 @@ -1225,9 +1230,8 @@ contains !write(*,*) 'ST_DOC_PUBLIC' select case (fx%tokenType) case (TOK_CHAR) - if (checkPublicId(str_vs(fx%token))) then - fx%publicId => fx%token - fx%token => null() + if (checkPublicId(str_varstr(fx%token))) then + call move_varstr_varstr(fx%token,fx%publicId) nextState = ST_DOC_SYSTEM else call add_error(fx%error_stack, "Invalid document public id") @@ -1239,8 +1243,7 @@ contains !write(*,*) 'ST_DOC_SYSTEM' select case (fx%tokenType) case (TOK_CHAR) - fx%systemId => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%systemId) nextState = ST_DOC_DECL end select @@ -1249,39 +1252,39 @@ contains select case (fx%tokenType) case (TOK_OPEN_SB) if (present(startDTD_handler)) then - if (associated(fx%publicId)) then - call startDTD_handler(str_vs(fx%root_element), & - publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) - elseif (associated(fx%systemId)) then - call startDTD_handler(str_vs(fx%root_element), & - publicId="", systemId=str_vs(fx%systemId)) + if (.not.is_varstr_null(fx%publicId)) then + call startDTD_handler(str_varstr(fx%root_element), & + publicId=str_varstr(fx%publicId), systemId=str_varstr(fx%systemId)) + elseif (.not.is_varstr_null(fx%systemId)) then + call startDTD_handler(str_varstr(fx%root_element), & + publicId="", systemId=str_varstr(fx%systemId)) else - call startDTD_handler(str_vs(fx%root_element), "", "") + call startDTD_handler(str_varstr(fx%root_element), "", "") endif if (fx%state==ST_STOP) goto 100 endif - if (associated(fx%systemId)) then - extSubsetURI => parseURI(str_vs(fx%systemId)) - deallocate(fx%systemId) + if (.not.is_varstr_null(fx%systemId)) then + extSubsetURI => parseURI(str_varstr(fx%systemId)) + call set_varstr_null(fx%systemId) endif - if (associated(fx%publicId)) deallocate(fx%publicId) + call set_varstr_null(fx%publicId) fx%inIntSubset = .true. wf_stack(1) = wf_stack(1) + 1 nextState = ST_IN_SUBSET case (TOK_END_TAG) if (present(startDTD_handler)) then - if (associated(fx%publicId)) then - call startDTD_handler(str_vs(fx%root_element), publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) - deallocate(fx%publicId) - elseif (associated(fx%systemId)) then - call startDTD_handler(str_vs(fx%root_element), publicId="", systemId=str_vs(fx%systemId)) + if (.not.is_varstr_null(fx%publicId)) then + call startDTD_handler(str_varstr(fx%root_element), publicId=str_varstr(fx%publicId), systemId=str_varstr(fx%systemId)) + call set_varstr_null(fx%publicId) + elseif (.not.is_varstr_null(fx%systemId)) then + call startDTD_handler(str_varstr(fx%root_element), publicId="", systemId=str_varstr(fx%systemId)) else - call startDTD_handler(str_vs(fx%root_element), "", "") + call startDTD_handler(str_varstr(fx%root_element), "", "") endif if (fx%state==ST_STOP) goto 100 endif - if (associated(fx%systemId)) then - extSubsetURI => parseURI(str_vs(fx%systemId)) + if (.not.is_varstr_null(fx%systemId)) then + extSubsetURI => parseURI(str_varstr(fx%systemId)) if (.not.associated(extSubsetURI)) then call add_error(fx%error_stack, "Invalid URI specified for DTD SYSTEM") goto 100 @@ -1300,7 +1303,7 @@ contains else if (validCheck) then call add_error(fx%error_stack, & - "Unable to retrieve external subset "//str_vs(fx%systemId)) + "Unable to retrieve external subset "//str_varstr(fx%systemId)) goto 100 endif call endDTDchecks @@ -1317,8 +1320,8 @@ contains fx%context = CTXT_BEFORE_CONTENT nextState = ST_MISC endif - if (associated(fx%systemId)) deallocate(fx%systemId) - if (associated(fx%publicId)) deallocate(fx%publicId) + call set_varstr_null(fx%systemId) + call set_varstr_null(fx%publicId) case default call add_error(fx%error_stack, "Unexpected token in DTD") goto 100 @@ -1385,12 +1388,12 @@ contains !write(*,*) 'ST_START_PE' select case (fx%tokenType) case (TOK_NAME) - if (existing_entity(fx%forbidden_pe_list, str_vs(fx%token))) then + if (existing_entity(fx%forbidden_pe_list, str_varstr(fx%token))) then call add_error(fx%error_stack, & 'Recursive entity reference') goto 100 endif - ent => getEntityByName(fx%xds%PEList, str_vs(fx%token)) + ent => getEntityByName(fx%xds%PEList, str_varstr(fx%token)) if (associated(ent)) then if (ent%wfc.and.fx%xds%standalone) then call add_error(fx%error_stack, & @@ -1398,25 +1401,25 @@ contains goto 100 elseif (ent%external) then if (present(startEntity_handler)) then - call startEntity_handler('%'//str_vs(fx%token)) + call startEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", nullURI, .false.) + str_varstr(fx%token), "", nullURI, .false.) #else call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", null(), .false.) + str_varstr(fx%token), "", null(), .false.) #endif call open_new_file(fb, ent%baseURI, iostat, pe=.true.) if (iostat/=0) then if (validCheck) then call add_error(fx%error_stack, & - "Unable to retrieve external parameter entity "//str_vs(fx%token)) + "Unable to retrieve external parameter entity "//str_varstr(fx%token)) goto 100 endif if (present(skippedEntity_handler)) then - call skippedEntity_handler('%'//str_vs(fx%token)) + call skippedEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif ! having skipped a PE, we must now not process @@ -1427,15 +1430,15 @@ contains else fx%inIntSubset = .false. if (present(startEntity_handler)) then - call startEntity_handler('%'//str_vs(fx%token)) + call startEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", nullURI, .false.) + str_varstr(fx%token), "", nullURI, .false.) #else call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", null(), .false.) + str_varstr(fx%token), "", null(), .false.) #endif call parse_text_declaration(fb, fx%error_stack) if (in_error(fx%error_stack)) goto 100 @@ -1449,19 +1452,19 @@ contains else ! Expand the entity, if (present(startEntity_handler)) then - call startEntity_handler('%'//str_vs(fx%token)) + call startEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", nullURI, .false.) + str_varstr(fx%token), "", nullURI, .false.) call open_new_string(fb, & - expand_entity(fx%xds%PEList, str_vs(fx%token)), str_vs(fx%token), baseURI=nullURI, pe=.true.) + expand_entity(fx%xds%PEList, str_varstr(fx%token)), str_varstr(fx%token), baseURI=nullURI, pe=.true.) #else call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", null(), .false.) + str_varstr(fx%token), "", null(), .false.) call open_new_string(fb, & - expand_entity(fx%xds%PEList, str_vs(fx%token)), str_vs(fx%token), baseURI=null(), pe=.true.) + expand_entity(fx%xds%PEList, str_varstr(fx%token)), str_varstr(fx%token), baseURI=null(), pe=.true.) #endif ! NB because we are just expanding a string here, anything ! evaluated as a result of this string is evaluated in the @@ -1480,7 +1483,7 @@ contains if (fx%skippedExternal.and..not.fx%xds%standalone) then if (processDTD) then if (present(skippedEntity_handler)) then - call skippedEntity_handler('%'//str_vs(fx%token)) + call skippedEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif endif @@ -1500,13 +1503,16 @@ contains if (nextState/=ST_NULL) then fx%state = nextState else - call add_error(fx%error_stack, "Internal error in parser - no suitable token found") + call add_error(fx%error_stack, "Internal error in parser - no suitable token found.") goto 100 endif end do -100 if (associated(tempString)) deallocate(tempString) +100 continue + if (in_error(fx%error_stack)) call add_error_position(fx%error_stack, fb) + if (associated(tempString)) deallocate(tempString) + if (associated(tempString2)) deallocate(tempString2) if (associated(extSubsetURI)) call destroyURI(extSubsetURI) call destroy_string_list(id_list) call destroy_string_list(idref_list) @@ -1530,9 +1536,13 @@ contains ! EOF of main file if (startInChardata_) then if (fx%well_formed) then - if (fx%state==ST_CHAR_IN_CONTENT.and.associated(fx%token)) then - if (size(fx%token)>0.and.present(characters_handler)) & - call characters_handler(str_vs(fx%token)) + !Note: it used to be as follows: + !if (fx%state==ST_CHAR_IN_CONTENT.and.associated(fx%token%data)) then + !It is probably safe now not to check if token%data is allocated, as in case it is not, + !token%length should be -1... but if it crashes, you know the culprit. + if (fx%state==ST_CHAR_IN_CONTENT) then + if (varstr_len(fx%token)>0.and.present(characters_handler)) & + call characters_handler(str_varstr(fx%token)) endif else if (present(fatalError_handler)) & @@ -1601,13 +1611,13 @@ contains case (TOK_OPEN_COMMENT) nextDTDState = ST_DTD_START_COMMENT case (TOK_NAME) - if (str_vs(fx%token)=='ATTLIST') then + if (equal_varstr_str(fx%token,'ATTLIST')) then nextDTDState = ST_DTD_ATTLIST - elseif (str_vs(fx%token)=='ELEMENT') then + elseif (equal_varstr_str(fx%token,'ELEMENT')) then nextDTDState = ST_DTD_ELEMENT - elseif (str_vs(fx%token)=='ENTITY') then + elseif (equal_varstr_str(fx%token,'ENTITY')) then nextDTDState = ST_DTD_ENTITY - elseif (str_vs(fx%token)=='NOTATION') then + elseif (equal_varstr_str(fx%token,'NOTATION')) then nextDTDState = ST_DTD_NOTATION endif end select @@ -1616,7 +1626,7 @@ contains !write(*,*) "ST_DTD_START_SECTION_DECL" select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=="IGNORE") then + if (equal_varstr_str(fx%token,"IGNORE")) then if (fx%context/=CTXT_IN_DTD.or.reading_main_file(fb)) then call add_error(fx%error_stack, "IGNORE section only allowed in external subset.") return @@ -1625,7 +1635,7 @@ contains fx%context = CTXT_IGNORE nextDTDState = ST_DTD_FINISH_SECTION_DECL endif - elseif (str_vs(fx%token)=="INCLUDE") then + elseif (equal_varstr_str(fx%token,"INCLUDE")) then if (fx%context/=CTXT_IN_DTD.or.reading_main_file(fb)) then call add_error(fx%error_stack, "INCLUDE section only allowed in external subset.") return @@ -1675,18 +1685,17 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (nameOk) then - if (str_vs(fx%token)=='xml') then + if (equal_varstr_str(fx%token,'xml')) then call add_error(fx%error_stack, "XML declaration must be at start of document") return - elseif (checkPITarget(str_vs(fx%token), fx%xds%xml_version)) then + elseif (checkPITarget(str_varstr(fx%token), fx%xds%xml_version)) then nextDTDState = ST_DTD_PI_CONTENTS - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) else call add_error(fx%error_stack, "Invalid PI target name") return @@ -1717,17 +1726,17 @@ contains select case(fx%tokenType) case (TOK_CHAR) if (present(processingInstruction_handler)) then - call processingInstruction_handler(str_vs(fx%name), str_vs(fx%token)) + call processingInstruction_handler(str_varstr(fx%name), str_varstr(fx%token)) if (fx%state==ST_STOP) return endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_PI_END case (TOK_PI_END) if (present(processingInstruction_handler)) then - call processingInstruction_handler(str_vs(fx%name), '') + call processingInstruction_handler(str_varstr(fx%name), '') if (fx%state==ST_STOP) return endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_SUBSET end select @@ -1742,8 +1751,7 @@ contains !write(*,*)'ST_DTD_START_COMMENT' select case (fx%tokenType) case (TOK_CHAR) - fx%name => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_COMMENT_END end select @@ -1770,10 +1778,10 @@ contains select case (fx%tokenType) case (TOK_COMMENT_END) if (present(comment_handler)) then - call comment_handler(str_vs(fx%name)) + call comment_handler(str_varstr(fx%name)) if (fx%state==ST_STOP) return endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_SUBSET end select @@ -1782,18 +1790,18 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid element name for ATTLIST") return endif - if (existing_element(fx%xds%element_list, str_vs(fx%token))) then - elem => get_element(fx%xds%element_list, str_vs(fx%token)) + if (existing_element(fx%xds%element_list, str_varstr(fx%token))) then + elem => get_element(fx%xds%element_list, str_varstr(fx%token)) else - elem => add_element(fx%xds%element_list, str_vs(fx%token)) + elem => add_element(fx%xds%element_list, str_varstr(fx%token)) endif nextDTDState = ST_DTD_ATTLIST_CONTENTS end select @@ -1807,16 +1815,16 @@ contains nextState = ST_START_PE case (TOK_DTD_CONTENTS) if (processDTD) then - call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + call parse_dtd_attlist(str_varstr(fx%token), fx%xds%xml_version, & namespaces_, validCheck, fx%error_stack, elem, & internal=reading_main_file(fb)) else #ifdef PGF90 - call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + call parse_dtd_attlist(str_varstr(fx%token), fx%xds%xml_version, & namespaces_, validCheck, fx%error_stack, nullElement, & internal=reading_main_file(fb)) #else - call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + call parse_dtd_attlist(str_varstr(fx%token), fx%xds%xml_version, & namespaces_, validCheck, fx%error_stack, null(), & internal=reading_main_file(fb)) #endif @@ -1828,7 +1836,7 @@ contains if (associated(elem%attlist%list(i)%default)) then tempString => elem%attlist%list(i)%default elem%attlist%list(i)%default => & - normalize_attribute_text(fx, tempString) + normalize_attribute_text(fx, tempString, fb) deallocate(tempString) if (in_error(fx%error_stack)) return endif @@ -1894,16 +1902,15 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid name for ELEMENT") return endif - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_ELEMENT_CONTENTS end select @@ -1923,7 +1930,7 @@ contains ! Leave DTD state as it is & expand the entity ... nextState = ST_START_PE case (TOK_DTD_CONTENTS) - if (declared_element(fx%xds%element_list, str_vs(fx%name))) then + if (declared_element(fx%xds%element_list, str_varstr(fx%name))) then if (validCheck) then call add_error(fx%error_stack, "Duplicate Element declaration") return @@ -1932,15 +1939,15 @@ contains elem => null() endif elseif (processDTD) then - if (existing_element(fx%xds%element_list, str_vs(fx%name))) then - elem => get_element(fx%xds%element_list, str_vs(fx%name)) + if (existing_element(fx%xds%element_list, str_varstr(fx%name))) then + elem => get_element(fx%xds%element_list, str_varstr(fx%name)) else - elem => add_element(fx%xds%element_list, str_vs(fx%name)) + elem => add_element(fx%xds%element_list, str_varstr(fx%name)) endif else elem => null() endif - call parse_dtd_element(str_vs(fx%token), fx%xds%xml_version, fx%error_stack, & + call parse_dtd_element(str_varstr(fx%token), fx%xds%xml_version, fx%error_stack, & elem, reading_main_file(fb)) if (in_error(fx%error_stack)) return nextDTDState = ST_DTD_ELEMENT_END @@ -1960,11 +1967,11 @@ contains wf_stack(1) = wf_stack(1) - 1 if (processDTD.and.associated(elem)) then if (present(elementDecl_handler)) then - call elementDecl_handler(str_vs(fx%name), str_vs(elem%model)) + call elementDecl_handler(str_varstr(fx%name), str_vs(elem%model)) if (fx%state==ST_STOP) return endif endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_SUBSET end select @@ -1972,24 +1979,23 @@ contains !write(*,*) 'ST_DTD_ENTITY' select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=="%") then + if (equal_varstr_str(fx%token,"%")) then pe = .true. ! this will be a PE nextDTDState = ST_DTD_ENTITY_PE else pe = .false. if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, & "Illegal name for general entity") return endif - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_ENTITY_ID endif end select @@ -1999,17 +2005,16 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, & "Illegal name for parameter entity") return endif - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_ENTITY_ID end select @@ -2017,9 +2022,9 @@ contains !write(*,*) 'ST_DTD_ENTITY_ID' select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token) == "PUBLIC") then + if (equal_varstr_str(fx%token,"PUBLIC")) then nextDTDState = ST_DTD_ENTITY_PUBLIC - elseif (str_vs(fx%token) == "SYSTEM") then + elseif (equal_varstr_str(fx%token,"SYSTEM")) then nextDTDState = ST_DTD_ENTITY_SYSTEM else call add_error(fx%error_stack, "Unexpected token in ENTITY") @@ -2027,11 +2032,15 @@ contains endif case (TOK_CHAR) if (reading_main_file(fb)) then - tempString => fx%token + tempString => vs_varstr_alloc(fx%token) else - tempString => expand_pe_text(fx, fx%token, fb) + tempString2 => vs_varstr_alloc(fx%token) + tempString => expand_pe_text(fx, tempString2, fb) + deallocate(tempString2) endif - fx%attname => expand_entity_value_alloc(tempString, fx%xds, fx%error_stack) + tempString2 => expand_entity_value_alloc(tempString, fx%xds, fx%error_stack) + call varstr_vs( fx%attname, tempString2 ) + deallocate(tempString2) if (reading_main_file(fb)) then tempString => null() else @@ -2048,9 +2057,8 @@ contains !write(*,*) 'ST_DTD_ENTITY_PUBLIC' select case (fx%tokenType) case (TOK_CHAR) - if (checkPublicId(str_vs(fx%token))) then - fx%publicId => fx%token - fx%token => null() + if (checkPublicId(str_varstr(fx%token))) then + call move_varstr_varstr(fx%token,fx%publicId) nextDTDState = ST_DTD_ENTITY_SYSTEM else call add_error(fx%error_stack, "Invalid PUBLIC id in ENTITY") @@ -2065,8 +2073,7 @@ contains !write(*,*) 'ST_DTD_ENTITY_SYSTEM' select case (fx%tokenType) case (TOK_CHAR) - fx%systemId => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%systemId) nextDTDState = ST_DTD_ENTITY_NDATA case default call add_error(fx%error_stack, "Unexpected token in ENTITY") @@ -2090,14 +2097,14 @@ contains if (in_error(fx%error_stack)) return if (fx%state==ST_STOP) return endif - deallocate(fx%name) - if (associated(fx%attname)) deallocate(fx%attname) - if (associated(fx%systemId)) deallocate(fx%systemId) - if (associated(fx%publicId)) deallocate(fx%publicId) - if (associated(fx%Ndata)) deallocate(fx%Ndata) + call set_varstr_null(fx%name) + call set_varstr_null(fx%attname) + call set_varstr_null(fx%systemId) + call set_varstr_null(fx%publicId) + call set_varstr_null(fx%Ndata) nextDTDState = ST_DTD_SUBSET case (TOK_NAME) - if (str_vs(fx%token)=='NDATA') then + if (equal_varstr_str(fx%token,'NDATA')) then if (pe) then call add_error(fx%error_stack, "Parameter entity cannot have NDATA declaration") return @@ -2118,16 +2125,15 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid name for Notation") return endif - fx%Ndata => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%Ndata) nextDTDState = ST_DTD_ENTITY_END case default call add_error(fx%error_stack, "Unexpected token in ENTITY") @@ -2151,11 +2157,11 @@ contains if (in_error(fx%error_stack)) return if (fx%state==ST_STOP) return endif - deallocate(fx%name) - if (associated(fx%attname)) deallocate(fx%attname) - if (associated(fx%systemId)) deallocate(fx%systemId) - if (associated(fx%publicId)) deallocate(fx%publicId) - if (associated(fx%Ndata)) deallocate(fx%Ndata) + call set_varstr_null(fx%name) + call set_varstr_null(fx%attname) + call set_varstr_null(fx%systemId) + call set_varstr_null(fx%publicId) + call set_varstr_null(fx%Ndata) nextDTDState = ST_DTD_SUBSET case default call add_error(fx%error_stack, "Unexpected token at end of ENTITY") @@ -2167,16 +2173,15 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid name for Notation") return endif - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_NOTATION_ID case default call add_error(fx%error_stack, "Unexpected token in NOTATION") @@ -2187,9 +2192,9 @@ contains !write(*,*)'ST_DTD_NOTATION_ID' select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=='SYSTEM') then + if (equal_varstr_str(fx%token,'SYSTEM')) then nextDTDState = ST_DTD_NOTATION_SYSTEM - elseif (str_vs(fx%token)=='PUBLIC') then + elseif (equal_varstr_str(fx%token,'PUBLIC')) then nextDTDState = ST_DTD_NOTATION_PUBLIC else call add_error(fx%error_stack, "Unexpected token after NOTATION") @@ -2204,8 +2209,7 @@ contains !write(*,*)'ST_DTD_NOTATION_SYSTEM' select case (fx%tokenType) case (TOK_CHAR) - fx%systemId => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%systemId) nextDTDState = ST_DTD_NOTATION_END case default call add_error(fx%error_stack, "Unexpected token in NOTATION") @@ -2216,9 +2220,8 @@ contains !write(*,*)'ST_DTD_NOTATION_PUBLIC' select case (fx%tokenType) case (TOK_CHAR) - if (checkPublicId(str_vs(fx%token))) then - fx%publicId => fx%token - fx%token => null() + if (checkPublicId(str_varstr(fx%token))) then + call move_varstr_varstr(fx%token,fx%publicId) nextDTDState = ST_DTD_NOTATION_PUBLIC_2 else call add_error(fx%error_stack, "Invalid PUBLIC id in NOTATION") @@ -2239,25 +2242,24 @@ contains "NOTATION not balanced in parameter entity") return endif - if (notation_exists(fx%nlist, str_vs(fx%name))) then + if (notation_exists(fx%nlist, str_varstr(fx%name))) then call add_error(fx%error_stack, "Duplicate notation declaration") return endif endif wf_stack(1) = wf_stack(1) - 1 if (processDTD) then - call add_notation(fx%nlist, str_vs(fx%name), publicId=str_vs(fx%publicId)) + call add_notation(fx%nlist, str_varstr(fx%name), publicId=str_varstr(fx%publicId)) if (present(notationDecl_handler)) then - call notationDecl_handler(str_vs(fx%name), publicId=str_vs(fx%publicId), systemId="") + call notationDecl_handler(str_varstr(fx%name), publicId=str_varstr(fx%publicId), systemId="") if (fx%state==ST_STOP) return endif endif - deallocate(fx%name) - deallocate(fx%publicId) + call set_varstr_null(fx%name) + call set_varstr_null(fx%publicId) nextDTDState = ST_DTD_SUBSET case (TOK_CHAR) - fx%systemId => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%systemId) nextDTDState = ST_DTD_NOTATION_END end select @@ -2271,14 +2273,14 @@ contains "NOTATION not balanced in parameter entity") return endif - if (notation_exists(fx%nlist, str_vs(fx%name))) then + if (notation_exists(fx%nlist, str_varstr(fx%name))) then call add_error(fx%error_stack, "Duplicate notation declaration") return endif endif wf_stack(1) = wf_stack(1) - 1 if (processDTD) then - URIref => parseURI(str_vs(fx%systemId)) + URIref => parseURI(str_varstr(fx%systemId)) if (.not.associated(URIref)) then call add_error(fx%error_stack, "Invalid SYSTEM literal") return @@ -2292,27 +2294,27 @@ contains ! since we don't do NOTATIONs. ! Throw it away again call destroyURI(URIref) - if (associated(fx%publicId)) then - call add_notation(fx%nlist, str_vs(fx%name), & - publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + if (.not.is_varstr_null(fx%publicId)) then + call add_notation(fx%nlist, str_varstr(fx%name), & + publicId=str_varstr(fx%publicId), systemId=str_varstr(fx%systemId)) if (present(notationDecl_handler)) then - call notationDecl_handler(str_vs(fx%name), & - publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + call notationDecl_handler(str_varstr(fx%name), & + publicId=str_varstr(fx%publicId), systemId=str_varstr(fx%systemId)) if (fx%state==ST_STOP) return endif else - call add_notation(fx%nlist, str_vs(fx%name), & - systemId=str_vs(fx%systemId)) + call add_notation(fx%nlist, str_varstr(fx%name), & + systemId=str_varstr(fx%systemId)) if (present(notationDecl_handler)) then - call notationDecl_handler(str_vs(fx%name), & - publicId="", systemId=str_vs(fx%systemId)) + call notationDecl_handler(str_varstr(fx%name), & + publicId="", systemId=str_varstr(fx%systemId)) if (fx%state==ST_STOP) return endif endif endif - if (associated(fx%publicId)) deallocate(fx%publicId) - deallocate(fx%systemId) - deallocate(fx%name) + call set_varstr_null(fx%publicId) + call set_varstr_null(fx%systemId) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_SUBSET case default call add_error(fx%error_stack, "Unexpected token in NOTATION") @@ -2333,13 +2335,13 @@ contains end subroutine parseDTD subroutine open_tag - elem => get_element(fx%xds%element_list, str_vs(fx%name)) + elem => get_element(fx%xds%element_list, str_varstr(fx%name)) if (associated(elem)) then if (validCheck) then call checkAttributes(elem, fx%attributes) - if (.not.checkContentModel(fx%elstack, str_vs(fx%name))) then + if (.not.checkContentModel(fx%elstack, str_varstr(fx%name))) then call add_error(fx%error_stack, & - "Element "//str_vs(fx%name)//" not permitted in this context") + "Element '"//str_varstr(fx%name)//"' not permitted in this context") return endif else @@ -2348,7 +2350,7 @@ contains else if (validCheck) then call add_error(fx%error_stack, & - "Trying to use an undeclared element") + "Trying to use an unrecognised element '"//str_varstr(fx%name)//"'") return endif endif @@ -2368,39 +2370,39 @@ contains ! This is a top-level element in the current entity call setBase(fx%attributes, expressURI(fb%f(1)%baseURI)) endif - if (namespaces_.and.getURIofQName(fx,str_vs(fx%name))==invalidNS) then + if (namespaces_.and.getURIofQName(fx,str_varstr(fx%name))==invalidNS) then ! no namespace was found for the current element if (.not.startInCharData_) then ! but we ignore this if we are parsing an entity through DOM - call add_error(fx%error_stack, "No namespace found for current element") + call add_error(fx%error_stack, "No namespace found for current element '"//str_varstr(fx%name)//"'") return elseif (present(startElement_handler)) then ! Record it as having an empty URI call startElement_handler("", & - getlocalNameofQName(str_vs(fx%name)), & - str_vs(fx%name), fx%attributes) + getlocalNameofQName(str_varstr(fx%name)), & + str_varstr(fx%name), fx%attributes) if (fx%state==ST_STOP) return endif elseif (namespaces_) then ! Normal state of affairs if (present(startElement_handler)) then - call startElement_handler(getURIofQName(fx, str_vs(fx%name)), & - getlocalNameofQName(str_vs(fx%name)), & - str_vs(fx%name), fx%attributes) + call startElement_handler(getURIofQName(fx, str_varstr(fx%name)), & + getlocalNameofQName(str_varstr(fx%name)), & + str_varstr(fx%name), fx%attributes) if (fx%state==ST_STOP) return endif else ! Non-namespace aware processing if (present(startElement_handler)) then call startElement_handler("", "", & - str_vs(fx%name), fx%attributes) + str_varstr(fx%name), fx%attributes) if (fx%state==ST_STOP) return endif endif if (validCheck) then - call push_elstack(fx%elstack, str_vs(fx%name), elem%cp) + call push_elstack(fx%elstack, str_varstr(fx%name), elem%cp) else - call push_elstack(fx%elstack, str_vs(fx%name)) + call push_elstack(fx%elstack, str_varstr(fx%name)) endif call reset_dict(fx%attributes) end subroutine open_tag @@ -2413,37 +2415,37 @@ contains 'Ill-formed entity') return endif - if (str_vs(fx%name)/=get_top_elstack(fx%elstack)) then + if (str_varstr(fx%name)/=get_top_elstack(fx%elstack)) then call add_error(fx%error_stack, & - "Mismatching close tag: trying to close "//get_top_elstack(fx%elstack) & - //" with "//str_vs(fx%name)) + "Mismatching close tag: trying to close entity '"//get_top_elstack(fx%elstack) & + //"' with '"//str_varstr(fx%name)//"'") return endif if (validCheck) then if (.not.checkContentModelToEnd(fx%elstack)) then call add_error(fx%error_stack, & - "Failed to fulfil content model for "//str_vs(fx%name)) + "Failed to fulfil content model for "//str_varstr(fx%name)) return endif endif dummy = pop_elstack(fx%elstack) if (present(endElement_handler)) then - if (namespaces_.and.getURIofQName(fx,str_vs(fx%name))==invalidNS) then + if (namespaces_.and.getURIofQName(fx,str_varstr(fx%name))==invalidNS) then ! no namespace was found for the current element, we must be ! closing inside a DOM entity. ! Record it as having an empty URI call endElement_handler("", & - getlocalNameofQName(str_vs(fx%name)), & - str_vs(fx%name)) + getlocalNameofQName(str_varstr(fx%name)), & + str_varstr(fx%name)) elseif (namespaces_) then ! Normal state of affairs - call endElement_handler(getURIofQName(fx, str_vs(fx%name)), & - getlocalnameofQName(str_vs(fx%name)), & - str_vs(fx%name)) + call endElement_handler(getURIofQName(fx, str_varstr(fx%name)), & + getlocalnameofQName(str_varstr(fx%name)), & + str_varstr(fx%name)) else ! Non-namespace-aware processing: call endElement_handler("", "", & - str_vs(fx%name)) + str_varstr(fx%name)) endif if (fx%state==ST_STOP) return endif @@ -2460,19 +2462,19 @@ contains wfc = fb%f(1)%pe.or.inExtSubset if (pe) then !Does entity with this name exist? - if (.not.existing_entity(fx%xds%PEList, str_vs(fx%name))) then + if (.not.existing_entity(fx%xds%PEList, str_varstr(fx%name))) then ! Internal or external? - if (associated(fx%attname)) then ! it's internal + if (.not.is_varstr_null(fx%attname)) then ! it's internal call register_internal_PE(fx%xds, & - name=str_vs(fx%name), text=str_vs(fx%attname), & + name=str_varstr(fx%name), text=str_varstr(fx%attname), & wfc=wfc, baseURI=copyURI(fb%f(1)%baseURI)) ! FIXME need to expand value here before reporting ... if (present(internalEntityDecl_handler)) then - call internalEntityDecl_handler('%'//str_vs(fx%name), str_vs(fx%attname)) + call internalEntityDecl_handler('%'//str_varstr(fx%name), str_varstr(fx%attname)) if (fx%state==ST_STOP) return endif else ! PE can't have Ndata declaration - URIref => parseURI(str_vs(fx%systemId)) + URIref => parseURI(str_varstr(fx%systemId)) if (.not.associated(URIref)) then call add_error(fx%error_stack, "Invalid URI specified for SYSTEM") elseif (hasFragment(URIref)) then @@ -2481,39 +2483,39 @@ contains else newURI => rebaseURI(fb%f(1)%baseURI, URIref) call destroyURI(URIref) - if (associated(fx%publicId)) then - call register_external_PE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + if (.not.is_varstr_null(fx%publicId)) then + call register_external_PE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId), & wfc=wfc, baseURI=newURI) if (present(externalEntityDecl_handler)) & - call externalEntityDecl_handler('%'//str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId)) + call externalEntityDecl_handler('%'//str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId)) else - call register_external_PE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), & + call register_external_PE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), & wfc=wfc, baseURI=newURI) if (present(externalEntityDecl_handler)) & - call externalEntityDecl_handler('%'//str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId="") + call externalEntityDecl_handler('%'//str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId="") endif endif endif ! else we ignore it endif else !It's a general entity - if (.not.existing_entity(fx%xds%entityList, str_vs(fx%name))) then + if (.not.existing_entity(fx%xds%entityList, str_varstr(fx%name))) then ! Internal or external? - if (associated(fx%attname)) then ! it's internal - call register_internal_GE(fx%xds, name=str_vs(fx%name), & - text=str_vs(fx%attname), & + if (.not.is_varstr_null(fx%attname)) then ! it's internal + call register_internal_GE(fx%xds, name=str_varstr(fx%name), & + text=str_varstr(fx%attname), & wfc=wfc, baseURI=copyURI(fb%f(1)%baseURI)) if (present(internalEntityDecl_handler)) then - call internalEntityDecl_handler(str_vs(fx%name),& - str_vs(fx%attname)) + call internalEntityDecl_handler(str_varstr(fx%name),& + str_varstr(fx%attname)) if (fx%state==ST_STOP) return endif else - URIref => parseURI(str_vs(fx%systemId)) + URIref => parseURI(str_varstr(fx%systemId)) if (.not.associated(URIref)) then call add_error(fx%error_stack, "Invalid URI specified for SYSTEM") elseif (hasFragment(URIref)) then @@ -2522,35 +2524,35 @@ contains else newURI => rebaseURI(fb%f(1)%baseURI, URIref) call destroyURI(URIref) - if (associated(fx%publicId).and.associated(fx%Ndata)) then - call register_external_GE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & - notation=str_vs(fx%Ndata), & + if ((.not.is_varstr_null(fx%publicId)).and.(.not.is_varstr_null(fx%Ndata))) then + call register_external_GE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId), & + notation=str_varstr(fx%Ndata), & wfc=wfc, baseURI=newURI) if (present(unparsedEntityDecl_handler)) & - call unparsedEntityDecl_handler(str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & - notation=str_vs(fx%Ndata)) - elseif (associated(fx%Ndata)) then - call register_external_GE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), notation=str_vs(fx%Ndata), & + call unparsedEntityDecl_handler(str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId), & + notation=str_varstr(fx%Ndata)) + elseif (.not.is_varstr_null(fx%Ndata)) then + call register_external_GE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), notation=str_varstr(fx%Ndata), & wfc=wfc, baseURI=newURI) if (present(unparsedEntityDecl_handler)) & - call unparsedEntityDecl_handler(str_vs(fx%name), publicId="", & - systemId=str_vs(fx%systemId), notation=str_vs(fx%Ndata)) - elseif (associated(fx%publicId)) then - call register_external_GE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + call unparsedEntityDecl_handler(str_varstr(fx%name), publicId="", & + systemId=str_varstr(fx%systemId), notation=str_varstr(fx%Ndata)) + elseif (.not.is_varstr_null(fx%publicId)) then + call register_external_GE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId), & wfc=wfc, baseURI=newURI) if (present(externalEntityDecl_handler)) & - call externalEntityDecl_handler(str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId)) + call externalEntityDecl_handler(str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId)) else - call register_external_GE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), wfc=wfc, baseURI=newURI) + call register_external_GE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), wfc=wfc, baseURI=newURI) if (present(externalEntityDecl_handler)) & - call externalEntityDecl_handler(str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId="") + call externalEntityDecl_handler(str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId="") endif endif endif @@ -2802,7 +2804,9 @@ contains if (str_vs(att%default)//"x"/=str_vs(attValue)//"x") then ! Validity Constraint: Fixed Attribute Default call add_error(fx%error_stack, & - "FIXED attribute has wrong value") + "FIXED attribute has unexpected value." & + //" At Element='"//str_vs(el%name)//"' Attribute='"//str_vs(att%name)//"'" & + //" Expecting '"//str_vs(att%default)//"' Found '"//str_vs(attValue)//"'") return endif else @@ -2820,9 +2824,7 @@ contains end select enddo - if (any(attributesLeft)) & - call add_error(fx%error_stack, & - "Undeclared attributes forbidden") + if (any(attributesLeft)) call add_error(fx%error_stack, "Undeclared attributes forbidden. Element '"//str_vs(el%name)//"'") end subroutine checkAttributes diff --git a/src/xml/sax/m_sax_reader.F90 b/src/xml/sax/m_sax_reader.F90 index 4693ff7f46..72fa6584ec 100644 --- a/src/xml/sax/m_sax_reader.F90 +++ b/src/xml/sax/m_sax_reader.F90 @@ -26,6 +26,7 @@ module m_sax_reader public :: file_buffer_t public :: line public :: column + public :: add_error_position public :: open_file public :: close_file @@ -73,6 +74,7 @@ contains fileURI => parseURI(file) if (.not.associated(fileURI)) then call add_error(es, "Could not open file "//file//" - not a valid URI") + iostat=1 return endif call open_new_file(fb, fileURI, iostat, lun) @@ -169,7 +171,7 @@ contains call close_actual_file(fb%f(i)) enddo - deallocate(fb%f) + if (associated(fb%f)) deallocate(fb%f) end subroutine close_file @@ -397,6 +399,13 @@ contains p = (size(fb%f)==2) end function reading_first_entity + + subroutine add_error_position(stack, fb) + type(error_stack), intent(inout) :: stack + type(file_buffer_t), intent(in) :: fb + call add_error(stack,"(Possibly near line="//line(fb)//" col="//column(fb)//")") + end subroutine add_error_position + #endif end module m_sax_reader diff --git a/src/xml/sax/m_sax_tokenizer.F90 b/src/xml/sax/m_sax_tokenizer.F90 index 97824fd7ad..59ea631d3f 100644 --- a/src/xml/sax/m_sax_tokenizer.F90 +++ b/src/xml/sax/m_sax_tokenizer.F90 @@ -2,6 +2,8 @@ module m_sax_tokenizer #ifndef DUMMYLIB use fox_m_fsys_array_str, only: vs_str, str_vs, vs_str_alloc + use fox_m_fsys_varstr + use m_common_charset, only: XML_WHITESPACE, & upperCase, isInitialNameChar use m_common_error, only: add_error, in_error @@ -12,7 +14,7 @@ module m_sax_tokenizer use m_sax_reader, only: file_buffer_t, open_new_file, pop_buffer_stack, & push_chars, get_character, get_all_characters, & - parse_text_declaration + parse_text_declaration, add_error_position use m_sax_types ! everything, really #ifdef PGF90 use fox_m_utils_uri, only: URI @@ -39,8 +41,7 @@ contains xv = fx%xds%xml_version - if (associated(fx%token)) deallocate(fx%token) - fx%token => vs_str_alloc("") + call set_varstr_empty(fx%token) if (fx%nextTokenType/=TOK_NULL) then eof = .false. fx%tokenType = fx%nextTokenType @@ -58,13 +59,9 @@ contains if (eof) then if (fx%state==ST_CHAR_IN_CONTENT) then if (phrase==1) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(fx%token)//"]") - deallocate(tempString) + call append_varstr(fx%token,']') elseif (phrase==2) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(fx%token)//"]]") - deallocate(tempString) + call append_varstr(fx%token,']]') endif fx%tokenType = TOK_CHAR endif @@ -90,6 +87,7 @@ contains ws_discard = .false. else call add_error(fx%error_stack, "Unexpected character found outside content") + call add_error_position(fx%error_stack,fb) endif endif else @@ -102,6 +100,7 @@ contains fx%tokenType = TOK_OPEN_TAG else call add_error(fx%error_stack, "Unexpected character after <") + call add_error_position(fx%error_stack,fb) endif endif @@ -112,21 +111,20 @@ contains elseif (c=="[") then fx%tokenType = TOK_OPEN_SB elseif (verify(c,upperCase)==0) then - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token,c) else call add_error(fx%error_stack, "Unexpected character after 0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr(fx%token,c) else call push_chars(fb, c) fx%tokenType = TOK_NAME @@ -135,9 +133,7 @@ contains case (ST_START_PI) ! grab until whitespace or ? if (verify(c, XML_WHITESPACE//"?")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c=="?") call push_chars(fb, c) @@ -158,21 +154,16 @@ contains fx%nextTokenType = TOK_PI_END elseif (c=="?") then ! The last ? didn't mean anything, but this one might. - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"?") - deallocate(tempString) + call append_varstr( fx%token, '?' ) else phrase = 0 - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"?"//c) - deallocate(tempString) + call append_varstr( fx%token, '?' ) + call append_varstr( fx%token, c ) endif elseif (c=="?") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (ST_START_COMMENT) @@ -181,17 +172,14 @@ contains if (c=="-") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (1) if (c=="-") then phrase = 2 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"-"//c) - deallocate(tempString) + call append_varstr( fx%token, '-' ) + call append_varstr( fx%token, c ) phrase = 0 endif case (2) @@ -202,15 +190,14 @@ contains else call add_error(fx%error_stack, & "Expecting > after -- inside a comment.") + call add_error_position(fx%error_stack,fb) endif end select case (ST_START_TAG) ! grab until whitespace or /, > if (verify(c, XML_WHITESPACE//"/>")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c==">") then @@ -225,22 +212,21 @@ contains if (verify(c, XML_WHITESPACE)==0) then call add_error(fx%error_stack, & "Whitespace not allowed around CDATA in section declaration") + call add_error_position(fx%error_stack,fb) else - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token, c) ws_discard = .false. endif else if (verify(c, XML_WHITESPACE)==0) then call add_error(fx%error_stack, & "Whitespace not allowed around CDATA in section declaration") + call add_error_position(fx%error_stack,fb) elseif (c=="[") then fx%tokenType = TOK_NAME if (c=="[") fx%nextTokenType = TOK_OPEN_SB else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif @@ -251,17 +237,14 @@ contains if (c=="]") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (1) if (c=="]") then phrase = 2 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]"//c) - deallocate(tempString) + call append_varstr( fx%token, ']' ) + call append_varstr( fx%token, c ) phrase = 0 endif case (2) @@ -269,13 +252,10 @@ contains fx%tokenType = TOK_CHAR fx%nextTokenType = TOK_SECTION_END elseif (c=="]") then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]") - deallocate(tempString) + call append_varstr( fx%token, ']' ) else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]]"//c) - deallocate(tempString) + call append_varstr( fx%token, ']]' ) + call append_varstr( fx%token, c ) phrase = 0 endif end select @@ -285,6 +265,7 @@ contains if (verify(c,XML_WHITESPACE//"/>")>0) then call add_error(fx%error_stack, & "Whitespace required inside tag") + call add_error_position(fx%error_stack,fb) exit endif ws_discard = .true. @@ -297,8 +278,7 @@ contains phrase = 1 ws_discard = .false. else - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token,c) ws_discard = .false. endif endif @@ -308,7 +288,8 @@ contains fx%tokenType = TOK_END_TAG_CLOSE else call add_error(fx%error_stack, & - "Unexpected character after / in element tag") + "Unexpected character after '/' ("//c//") in tag for element '"//str_varstr(fx%name)//"'") + call add_error_position(fx%error_stack,fb) exit endif else @@ -322,9 +303,7 @@ contains call push_chars(fb, c) endif else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif endif @@ -338,6 +317,7 @@ contains else call add_error(fx%error_stack, & "Unexpected character in element tag, expected =") + call add_error_position(fx%error_stack,fb) endif endif endif @@ -351,28 +331,23 @@ contains ws_discard = .false. else call add_error(fx%error_stack, "Expecting "" or '") + call add_error_position(fx%error_stack,fb) endif endif else if (c==q) then fx%tokenType = TOK_CHAR else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif case (ST_CHAR_IN_CONTENT) if (c=="<".or.c=="&") then if (phrase==1) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]") - deallocate(tempString) + call append_varstr( fx%token, ']' ) elseif (phrase==2) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]]") - deallocate(tempString) + call append_varstr( fx%token, ']]' ) endif fx%tokenType = TOK_CHAR if (c=="<") then @@ -386,35 +361,28 @@ contains elseif (phrase==1) then phrase = 2 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]") - deallocate(tempString) + call append_varstr( fx%token, ']' ) endif elseif (c==">") then if (phrase==1) then phrase = 0 - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]>") - deallocate(tempString) + call append_varstr( fx%token, ']>' ) elseif (phrase==2) then call add_error(fx%error_stack, "]]> forbidden in character context") + call add_error_position(fx%error_stack,fb) else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//">") - deallocate(tempString) + call append_varstr( fx%token, '>' ) endif elseif (phrase==1) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]"//c) - deallocate(tempString) + call append_varstr( fx%token, ']' ) + call append_varstr( fx%token, c ) + phrase = 0 elseif (phrase==2) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]]"//c) - deallocate(tempString) + call append_varstr( fx%token, ']]' ) + call append_varstr( fx%token, c ) + phrase = 0 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (ST_TAG_IN_CONTENT) @@ -426,6 +394,7 @@ contains fx%tokenType = TOK_ENTITY else call add_error(fx%error_stack, "Unexpected character found in content") + call add_error_position(fx%error_stack,fb) endif elseif (phrase==1) then if (c=="?") then @@ -439,25 +408,23 @@ contains fx%tokenType = TOK_OPEN_TAG else call add_error(fx%error_stack, "Unexpected character after <") + call add_error_position(fx%error_stack,fb) endif endif case (ST_START_ENTITY) if (verify(c,XML_WHITESPACE//";")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) elseif (c==";") then fx%tokenType = TOK_NAME else call add_error(fx%error_stack, "Entity reference must be terminated with a ;") + call add_error_position(fx%error_stack,fb) endif case (ST_CLOSING_TAG) if (verify(c,XML_WHITESPACE//">")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c==">") fx%nextTokenType = TOK_END_TAG @@ -469,6 +436,7 @@ contains fx%tokenType = TOK_END_TAG else call add_error(fx%error_stack, "Unexpected character - expecting >") + call add_error_position(fx%error_stack,fb) endif endif @@ -483,6 +451,7 @@ contains else call add_error(fx%error_stack, & "Missing whitespace in doctype delcaration.") + call add_error_position(fx%error_stack,fb) endif else ws_discard = .true. @@ -492,16 +461,14 @@ contains if (verify(c, XML_WHITESPACE)>0) then if (verify(c, "'""")==0) then q = c - deallocate(fx%token) - fx%token => vs_str_alloc("") + call set_varstr_empty(fx%token) ws_discard = .false. elseif (c=="[") then fx%tokenType = TOK_OPEN_SB elseif (c==">") then fx%tokenType = TOK_END_TAG else - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token, c) ws_discard = .false. endif endif @@ -515,9 +482,7 @@ contains call push_chars(fb, c) fx%tokenType = TOK_NAME else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif @@ -526,13 +491,12 @@ contains case (ST_START_PE) if (verify(c,XML_WHITESPACE//";")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) elseif (c==";") then fx%tokenType = TOK_NAME else call add_error(fx%error_stack, "Entity reference must be terminated with a ;") + call add_error_position(fx%error_stack,fb) endif end select @@ -574,14 +538,14 @@ contains elseif (fx%inIntSubset) then call add_error(fx%error_stack, & "Parameter entity reference not permitted inside markup for internal subset") + call add_error_position(fx%error_stack,fb) return elseif (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & .or.fx%state_dtd==ST_DTD_ELEMENT_CONTENTS) then - if (.not.associated(fx%content)) then + if (is_varstr_null(fx%content)) then ! content will not always be empty here; ! if we have two PErefs bang next to each other. - fx%content => fx%token - fx%token => vs_str_alloc("") + call move_varstr_varstr(fx%token,fx%content) endif fx%tokenType = TOK_ENTITY return @@ -617,6 +581,7 @@ contains ws_discard = .false. else call add_error(fx%error_stack, "Unexpected character found in document subset") + call add_error_position(fx%error_stack,fb) endif endif elseif (phrase==1) then @@ -627,6 +592,7 @@ contains fx%tokenType = TOK_BANG_TAG else call add_error(fx%error_stack, "Unexpected character, expecting ! or ?") + call add_error_position(fx%error_stack,fb) endif elseif (q=="]") then if (c=="]") then @@ -647,6 +613,7 @@ contains fx%tokenType = TOK_SECTION_END else call add_error(fx%error_stack, "Unexpected character, expecting >") + call add_error_position(fx%error_stack,fb) endif endif @@ -658,21 +625,20 @@ contains elseif (c=="[") then fx%tokenType = TOK_OPEN_SB elseif (verify(c,upperCase)==0) then - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token,c) else call add_error(fx%error_stack, "Unexpected character after 0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else call push_chars(fb, c) fx%tokenType = TOK_NAME @@ -685,15 +651,12 @@ contains endif if (ws_discard) then if (verify(c, XML_WHITESPACE)/=0) then - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token, c) ws_discard = .false. endif else if (verify(c, XML_WHITESPACE//"[")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c=="[") fx%nextTokenType = TOK_OPEN_SB @@ -705,6 +668,7 @@ contains if (c/="[") then call add_error(fx%error_stack, & "Unexpected token found, expecting [") + call add_error_position(fx%error_stack,fb) else fx%tokenType = TOK_OPEN_SB endif @@ -737,9 +701,7 @@ contains case (ST_DTD_START_PI) ! grab until whitespace or ? if (verify(c, XML_WHITESPACE//"?")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c=="?") call push_chars(fb, c) @@ -760,21 +722,16 @@ contains fx%nextTokenType = TOK_PI_END elseif (c=="?") then ! The last ? didn't mean anything, but this one might. - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"?") - deallocate(tempString) + call append_varstr( fx%token, '?' ) else phrase = 0 - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"?"//c) - deallocate(tempString) + call append_varstr( fx%token, '?' ) + call append_varstr( fx%token, c ) endif elseif (c=="?") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (ST_DTD_START_COMMENT) @@ -783,29 +740,25 @@ contains if (c=="-") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (1) if (c=="-") then phrase = 2 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"-"//c) - deallocate(tempString) + call append_varstr( fx%token, '-' ) + call append_varstr( fx%token, c ) phrase = 0 endif case (2) if (c==">") then fx%tokenType = TOK_CHAR - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) fx%nextTokenType = TOK_COMMENT_END else call add_error(fx%error_stack, & "Expecting > after -- inside a comment.") + call add_error_position(fx%error_stack,fb) endif end select @@ -814,14 +767,11 @@ contains if (firstChar) ws_discard = .true. if (ws_discard) then if (verify(c,XML_WHITESPACE)>0) then - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token, c) ws_discard = .false. endif elseif (verify(c,XML_WHITESPACE//">")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else if (c==">") then fx%nextTokenType = TOK_END_TAG @@ -833,31 +783,24 @@ contains case (ST_DTD_ELEMENT_CONTENTS) if (c==">") then - if (associated(fx%content)) then - deallocate(fx%token) - fx%token => fx%content - fx%content => null() + if (.not.is_varstr_null(fx%content)) then + call move_varstr_varstr(fx%content,fx%token) endif fx%tokenType = TOK_DTD_CONTENTS fx%nextTokenType = TOK_END_TAG else - if (associated(fx%content)) then - deallocate(fx%token) - fx%token => vs_str_alloc(str_vs(fx%content)//c) - deallocate(fx%content) + if (.not.is_varstr_null(fx%content)) then + call move_varstr_varstr(fx%content,fx%token) + call append_varstr(fx%token, c) else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr(fx%token, c) endif if (c=="(") then fx%tokenType = TOK_OPEN_PAR - fx%content => fx%token - fx%token => vs_str_alloc("") + call move_varstr_varstr(fx%token,fx%content) elseif (c==")") then fx%tokenType = TOK_CLOSE_PAR - fx%content => fx%token - fx%token => vs_str_alloc("") + call move_varstr_varstr(fx%token,fx%content) endif endif @@ -865,14 +808,11 @@ contains if (c==">") then fx%tokenType = TOK_DTD_CONTENTS fx%nextTokenType = TOK_END_TAG - elseif (associated(fx%content)) then - deallocate(fx%token) - fx%token => vs_str_alloc(str_vs(fx%content)//c) - deallocate(fx%content) + elseif (.not.is_varstr_null(fx%content)) then + call move_varstr_varstr(fx%content,fx%token) + call append_varstr(fx%token, c) else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif if (c=="'".or.c=="""") then if (q==c) then @@ -892,6 +832,7 @@ contains fx%tokenType = TOK_END_TAG else call add_error(fx%error_stack, "Missing whitespace in DTD.") + call add_error_position(fx%error_stack,fb) endif else ws_discard = .true. @@ -900,14 +841,12 @@ contains if (verify(c, XML_WHITESPACE)>0) then if (verify(c, "'""")==0) then q = c - deallocate(fx%token) - fx%token => vs_str_alloc("") + call set_varstr_empty(fx%token) ws_discard = .false. elseif (c==">") then fx%tokenType = TOK_END_TAG else - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str( fx%token, c ) ws_discard = .false. endif endif @@ -922,9 +861,7 @@ contains call push_chars(fb, c) endif else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif @@ -934,9 +871,10 @@ contains end subroutine sax_tokenize - recursive function normalize_attribute_text(fx, s_in) result(s_out) + recursive function normalize_attribute_text(fx, s_in, fb) result(s_out) type(sax_parser_t), intent(inout) :: fx character, dimension(:), intent(in) :: s_in + type(file_buffer_t), intent(in) :: fb character, dimension(:), pointer :: s_out character, dimension(:), pointer :: s_temp, s_temp2, s_ent, tempString @@ -968,16 +906,19 @@ contains i = i + 1 i2 = i2 + 1 elseif (s_in(i)=='<') then - call add_error(fx%error_stack, "Illegal < found in attribute.") + call add_error(fx%error_stack, "Illegal '<' found in attribute.") + call add_error_position(fx%error_stack,fb) goto 100 ! Then, expand < elseif (s_in(i)=='&') then j = index(str_vs(s_in(i+1:)), ';') if (j==0) then - call add_error(fx%error_stack, "Illegal & found in attribute") + call add_error(fx%error_stack, "Illegal '&' found in attribute") + call add_error_position(fx%error_stack,fb) goto 100 elseif (j==1) then call add_error(fx%error_stack, "No entity reference found") + call add_error_position(fx%error_stack,fb) goto 100 endif allocate(tempString(j-1)) @@ -996,6 +937,7 @@ contains ent => getEntityByName(fx%forbidden_ge_list, str_vs(tempString)) if (associated(ent)) then call add_error(fx%error_stack, 'Recursive entity expansion') + call add_error_position(fx%error_stack,fb) goto 100 else ent => getEntityByName(fx%xds%entityList, str_vs(tempString)) @@ -1003,11 +945,13 @@ contains if (associated(ent)) then if (ent%wfc.and.fx%xds%standalone) then call add_error(fx%error_stack, 'Externally declared entity referenced in standalone document') + call add_error_position(fx%error_stack,fb) goto 100 endif !is it the right sort of entity? if (ent%external) then call add_error(fx%error_stack, "External entity forbidden in attribute") + call add_error_position(fx%error_stack,fb) goto 100 endif #ifdef PGF90 @@ -1017,7 +961,7 @@ contains #endif ! Recursively expand entity, checking for errors. s_ent => normalize_attribute_text(fx, & - vs_str(expand_entity_text(fx%xds%entityList, str_vs(tempString)))) + vs_str(expand_entity_text(fx%xds%entityList, str_vs(tempString))),fb) dummy = pop_entity_list(fx%forbidden_ge_list) if (in_error(fx%error_stack)) then goto 100 @@ -1037,11 +981,13 @@ contains i2 = i2 + j + 1 if (.not.fx%skippedExternal.or.fx%xds%standalone) then call add_error(fx%error_stack, "Undeclared entity encountered in standalone document.") + call add_error_position(fx%error_stack,fb) goto 100 endif endif else call add_error(fx%error_stack, "Illegal entity reference") + call add_error_position(fx%error_stack,fb) goto 100 endif deallocate(tempString) @@ -1090,10 +1036,12 @@ contains if (s_in(i)=='%') then j = index(str_vs(s_in(i+1:)), ';') if (j==0) then - call add_error(fx%error_stack, "Illegal % found in attribute") + call add_error(fx%error_stack, "Illegal '%' found in attribute") + call add_error_position(fx%error_stack,fb) goto 100 elseif (j==1) then call add_error(fx%error_stack, "No entity reference found") + call add_error_position(fx%error_stack,fb) goto 100 endif allocate(tempString(j-1)) @@ -1102,6 +1050,7 @@ contains ent => getEntityByName(fx%forbidden_pe_list, str_vs(tempString)) if (associated(ent)) then call add_error(fx%error_stack, 'Recursive entity expansion') + call add_error_position(fx%error_stack,fb) goto 100 endif ent => getEntityByName(fx%xds%peList, str_vs(tempString)) @@ -1109,9 +1058,11 @@ contains if (ent%wfc.and.fx%xds%standalone) then call add_error(fx%error_stack, & "Externally declared entity used in standalone document") + call add_error_position(fx%error_stack,fb) goto 100 elseif (str_vs(ent%notation)/="") then call add_error(fx%error_stack, "Unparsed entity reference forbidden in entity value") + call add_error_position(fx%error_stack,fb) goto 100 endif #ifdef PGF90 @@ -1124,6 +1075,7 @@ contains call open_new_file(fb, ent%baseURI, iostat) if (iostat/=0) then call add_error(fx%error_stack, "Unable to access external parameter entity") + call add_error_position(fx%error_stack,fb) goto 100 endif call parse_text_declaration(fb, fx%error_stack) @@ -1156,11 +1108,13 @@ contains i2 = i2 + j + 1 if (.not.fx%skippedExternal.or.fx%xds%standalone) then call add_error(fx%error_stack, "Reference to undeclared parameter entity encountered in standalone document.") + call add_error_position(fx%error_stack,fb) goto 100 endif endif else call add_error(fx%error_stack, "Illegal parameter entity reference") + call add_error_position(fx%error_stack,fb) goto 100 endif deallocate(tempString) diff --git a/src/xml/sax/m_sax_types.F90 b/src/xml/sax/m_sax_types.F90 index b0148a693c..eb123c781f 100644 --- a/src/xml/sax/m_sax_types.F90 +++ b/src/xml/sax/m_sax_types.F90 @@ -9,6 +9,8 @@ module m_sax_types use m_common_notations, only: notation_list use m_common_struct, only: xml_doc_state + use fox_m_fsys_varstr, only: varstr + use m_sax_reader, only: file_buffer_t implicit none @@ -124,16 +126,16 @@ module m_sax_types integer :: state_dtd = ST_DTD_SUBSET logical :: well_formed = .false. logical :: skippedExternal = .false. - character, dimension(:), pointer :: token => null() - character, dimension(:), pointer :: content => null() + type(varstr) :: token + type(varstr) :: content integer :: tokenType = TOK_NULL integer :: nextTokenType = TOK_NULL - character, dimension(:), pointer :: name => null() - character, dimension(:), pointer :: attname => null() + type(varstr) :: name + type(varstr) :: attname logical :: error = .false. type(error_stack) :: error_stack ! Aspects of document structure - character, dimension(:), pointer :: root_element => null() + type(varstr) :: root_element type(elstack_t) :: elstack type(dictionary_t) :: attributes type(namespacedictionary) :: nsdict @@ -141,9 +143,9 @@ module m_sax_types type(entity_list) :: predefined_e_list type(entity_list) :: forbidden_pe_list type(entity_list) :: forbidden_ge_list - character(len=1), dimension(:), pointer :: PublicId => null() - character(len=1), dimension(:), pointer :: SystemId => null() - character(len=1), dimension(:), pointer :: Ndata => null() + type(varstr) :: PublicId + type(varstr) :: SystemId + type(varstr) :: Ndata logical :: inIntSubset = .false. logical :: spaceBeforeEntity = .false. end type sax_parser_t diff --git a/src/xml/utils/fox_m_utils_uri.F90 b/src/xml/utils/fox_m_utils_uri.F90 index be6fefcdd7..a3f1d70ff6 100644 --- a/src/xml/utils/fox_m_utils_uri.F90 +++ b/src/xml/utils/fox_m_utils_uri.F90 @@ -321,7 +321,7 @@ contains p = .true. i1 = index(path, "/") - if (i1==1) then + if (i1==1) then ! absolute path allocate(segments(1)) segments(1)%s => vs_str_alloc("/") else @@ -385,9 +385,10 @@ contains end function checkFragment #endif - function parseURI(URIstring) result(u) - character(len=*), intent(in) :: URIstring + function parseURI(inURIstring) result(u) + character(len=*), intent(in) :: inURIstring type(URI), pointer :: u + character(len=len_trim(inURIstring)) :: URIstring #ifndef DUMMYLIB character, pointer, dimension(:) :: scheme, authority, & userinfo, host, path, query, fragment @@ -398,6 +399,7 @@ contains #endif u => null() + URIstring = trim(inURIString) #ifndef DUMMYLIB scheme => null() @@ -410,6 +412,18 @@ contains query => null() fragment => null() + if (len(URIstring)>3) then + ! is this a M$ windoze absolute path ? eg of the form "C:/path_segments" + if ((scan(URIstring(1:1),alpha)>0).and.(URIstring(2:3)==':/') ) then + ! no point in attempting to decode as a uri, it contains only a windows path + scheme => vs_str_alloc("file") + path => unEscape_alloc(URIstring) + allocate(segments(1)) + segments(1)%s => vs_str_alloc("") + call produceResult + return + end if + end if i1 = index(URIstring, ":") if (i1>0) then p = checkScheme(URIstring(:i1-1)) @@ -460,6 +474,13 @@ contains call cleanUp return endif + if (len(URIstring(i2:i3-1))>3) then + ! is this a M$ windoze absolute path with a unix root ? eg of the form "/C:/path_segments" + if ( (URIstring(i2:i2)=='/').and.(scan(URIstring(i2+1:i2+1),alpha)>0).and.(URIstring(i2+2:i2+3)==':/') ) then + ! ignore the root slash (which would otherwise make sense on most systems) to yield a representation in windows canonical form + i2 = i2+1 + end if + end if path => unEscape_alloc(URIstring(i2:i3-1)) if (i3>len(URIstring)) then @@ -522,7 +543,6 @@ contains u%host => host u%port = port u%path => path - u%segments => segments u%query => query u%fragment => fragment u%segments => segments diff --git a/src/xml/wxml/FoX_wxml.F90 b/src/xml/wxml/FoX_wxml.F90 index 41c1e5652b..cf4def0a77 100644 --- a/src/xml/wxml/FoX_wxml.F90 +++ b/src/xml/wxml/FoX_wxml.F90 @@ -36,6 +36,8 @@ module FoX_wxml public :: xmlf_GetPretty_print public :: xmlf_SetPretty_print + public :: xmlf_GetExtendedData + public :: xmlf_SetExtendedData public :: xmlf_Name public :: xmlf_OpenTag diff --git a/src/xml/wxml/m_wxml_core.F90 b/src/xml/wxml/m_wxml_core.F90 index 2afd30b068..7e905c2ab5 100644 --- a/src/xml/wxml/m_wxml_core.F90 +++ b/src/xml/wxml/m_wxml_core.F90 @@ -87,6 +87,9 @@ module m_wxml_core integer :: state_1 = -1 integer :: state_2 = -1 integer :: state_3 = -1 + ! Holder for extra information for other writers. See + ! table with getter and setter below: + integer :: extended_data = 0 logical :: minimize_overrun = .true. logical :: pretty_print = .false. logical :: canonical = .false. @@ -128,6 +131,8 @@ module m_wxml_core public :: xmlf_SetPretty_print public :: xmlf_GetPretty_print + public :: xmlf_SetExtendedData + public :: xmlf_GetExtendedData interface xml_AddCharacters module procedure xml_AddCharacters_Ch @@ -1653,6 +1658,30 @@ contains #endif end function xmlf_GetPretty_print +! xf%extended data is an integer so that writers +! can change there behaviour depending on some +! stored information. Currently only used for +! wcml 'validate' argument (which is intended to +! check some of the more troublesome aspects of +! the CML schema + subroutine xmlf_SetExtendedData(xf, new_value) + type(xmlf_t), intent(inout) :: xf + integer, intent(in) :: new_value +#ifndef DUMMYLIB + xf%extended_data = new_value +#endif + end subroutine xmlf_SetExtendedData + + pure function xmlf_GetExtendedData(xf) result(value) + integer :: value + type(xmlf_t), intent(in) :: xf +#ifdef DUMMYLIB + value = .false. +#else + value = xf%extended_data +#endif + end function xmlf_GetExtendedData + pure function xmlf_name(xf) result(fn) type (xmlf_t), intent(in) :: xf #ifdef DUMMYLIB From f945e20dcb12b4a89c1fd74a8f4cc50dea654894 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 21 Sep 2013 16:50:44 -0400 Subject: [PATCH 118/192] 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 c67423342e88c6565723ee43cabc6dc6ef18da7a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 21 Sep 2013 16:48:47 -0400 Subject: [PATCH 119/192] PEP8 fixes in run_tests.py and test_compile.py. --- tests/run_tests.py | 6 ++--- tests/test_compile/test_compile.py | 39 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/tests/run_tests.py b/tests/run_tests.py index b10225f7fa..30d330e5d5 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -87,11 +87,11 @@ if len(sys.argv) > 1: if i == 'all-tests': tests__ = tests try: - idx = tests_.index('compile') # check for compile test + idx = tests_.index('compile') # check for compile test except ValueError: del tests__[0] finally: - break # don't need to check for anything else + break # don't need to check for anything else # This checks for any subsets of tests. The string after # all-XXXX will be used to search through all tests. @@ -109,7 +109,7 @@ if len(sys.argv) > 1: if j.rfind(suffix) != -1: tests__.append(j) else: - tests__.append(i) # append specific test (e.g., mpi-debug) + tests__.append(i) # append specific test (e.g., mpi-debug) tests = tests__ if tests__ else tests # Run tests diff --git a/tests/test_compile/test_compile.py b/tests/test_compile/test_compile.py index bc3b2391d6..997f243d7b 100644 --- a/tests/test_compile/test_compile.py +++ b/tests/test_compile/test_compile.py @@ -59,18 +59,21 @@ def test_mpi(): assert returncode == 0 shutil.move('openmc', 'openmc-mpi') + def test_mpi_debug(): - returncode = run(['make','distclean']) + returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'DEBUG=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-mpi-debug') + def test_mpi_optimize(): - returncode = run(['make','distclean']) + returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'OPTIMIZE=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-mpi-optimize') + def test_omp(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'OPENMP=yes']) @@ -84,36 +87,43 @@ def test_hdf5(): assert returncode == 0 shutil.move('openmc', 'openmc-hdf5') + def test_hdf5_debug(): - returncode = run(['make','distclean']) + returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'HDF5=yes', 'DEBUG=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-hdf5-debug') + def test_hdf5_optimize(): - returncode = run(['make','distclean']) + returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'HDF5=yes', 'OPTIMIZE=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-hdf5-optimize') + def test_petsc(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'PETSC=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-petsc') + def test_petsc_debug(): - returncode = run(['make','distclean']) + returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'PETSC=yes', 'DEBUG=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-petsc-debug') + def test_petsc_optimize(): - returncode = run(['make','distclean']) - returncode = run(['make', compiler, 'MPI=yes', 'PETSC=yes', 'OPTIMIZE=yes']) + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'PETSC=yes', + 'OPTIMIZE=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-petsc-optimize') + def test_mpi_omp(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'OPENMP=yes']) @@ -127,36 +137,43 @@ def test_omp_hdf5(): assert returncode == 0 shutil.move('openmc', 'openmc-omp-hdf5') + def test_mpi_hdf5(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-phdf5') + def test_mpi_hdf5_debug(): - returncode = run(['make','distclean']) + returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'DEBUG=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-phdf5-debug') + def test_mpi_hdf5_optimize(): - returncode = run(['make','distclean']) + returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'OPTIMIZE=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-phdf5-optimize') + def test_mpi_hdf5_petsc(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'PETSC=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-phdf5-petsc') + def test_mpi_hdf5_petsc_debug(): - returncode = run(['make','distclean']) - returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'PETSC=yes', 'DEBUG=yes']) + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'PETSC=yes', + 'DEBUG=yes']) assert returncode == 0 shutil.move('openmc', 'openmc-phdf5-petsc-debug') + def test_mpi_hdf5_petsc_optimize(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'PETSC=yes', From 0fcde07bc554028da5985f6624b47dd5b61e788b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 21 Sep 2013 16:52:48 -0400 Subject: [PATCH 120/192] Modify build_dependencies.py to skip petsc modules. --- src/utils/build_dependencies.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/build_dependencies.py b/src/utils/build_dependencies.py index c88ddbfb62..19ac978857 100755 --- a/src/utils/build_dependencies.py +++ b/src/utils/build_dependencies.py @@ -11,7 +11,8 @@ for src in glob.iglob('*.F90'): d = re.findall(r'\n\s*use\s+(\w+)', open(src, 'r').read()) for name in d: - if name in ['mpi', 'hdf5', 'h5lt']: + if name in ['mpi', 'hdf5', 'h5lt', 'petscsys', 'petscmat', 'petscksp', + 'petscsnes', 'petscvec']: continue if name.startswith('xml_data_'): name = name.replace('xml_data_', 'templates/') From ca0ace41cbc6b25aff6f1b05545d7853f4743067 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 21 Sep 2013 17:32:53 -0400 Subject: [PATCH 121/192] 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 32c03c44ae666702f4252c82249771763f1d6551 Mon Sep 17 00:00:00 2001 From: Tuomas Viitanen Date: Wed, 25 Sep 2013 16:48:48 +0300 Subject: [PATCH 122/192] Added a check for the name of the ACE data to be read. --- src/ace.F90 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ace.F90 b/src/ace.F90 index c37a546c01..209f5daa33 100644 --- a/src/ace.F90 +++ b/src/ace.F90 @@ -243,6 +243,15 @@ contains ! Read first line of header read(UNIT=in, FMT='(A10,2G12.0,1X,A10)') name, awr, kT, date_ + + ! Check that correct xs was found + ! (if XS listing (cross_sections.xml) is broken, this may not be the case) + + if( trim(adjustl(name)) /= trim(adjustl(listing % name)) ) then + message = "XS listing entry " // trim(listing % name) // " did not & + &match ACE data, " // trim(name) // " found instead." + call fatal_error() + end if ! Read more header and NXS and JXS read(UNIT=in, FMT=100) comment, mat, & From e8792cdb442c41c884bfecec171e47749e518ded Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 25 Sep 2013 14:46:45 -0400 Subject: [PATCH 123/192] fixed addition conflict of threads from merge --- src/DEPENDENCIES | 1 - src/input_xml.F90 | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index d2bb688c99..e958cc62ac 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -197,7 +197,6 @@ initialize.o: global.o initialize.o: hdf5_interface.o initialize.o: hdf5_summary.o initialize.o: input_xml.o -initialize.o: omp_lib.o initialize.o: output.o initialize.o: output_interface.o initialize.o: random_lcg.o diff --git a/src/input_xml.F90 b/src/input_xml.F90 index ed7b845eca..deeae3c01e 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -226,10 +226,10 @@ contains end if ! Number of OpenMP threads - if (threads_ /= NONE) then + if (check_for_node(doc, "threads")) then #ifdef OPENMP if (n_threads == NONE) then - n_threads = threads_ + call get_node_value(doc, "threads", n_threads) if (n_threads < 1) then message = "Invalid number of threads: " // to_str(n_threads) call fatal_error() From dccd186369af3aa3cdc58849b25d8101931f9adc Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 26 Sep 2013 11:56:01 -0400 Subject: [PATCH 124/192] added old version of fsys_format since there is a bug in the pre-release version --- src/xml/fsys/fox_m_fsys_format.F90 | 182 ++++++++++++++--------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/src/xml/fsys/fox_m_fsys_format.F90 b/src/xml/fsys/fox_m_fsys_format.F90 index 6d6a6d9338..448f44ebd4 100644 --- a/src/xml/fsys/fox_m_fsys_format.F90 +++ b/src/xml/fsys/fox_m_fsys_format.F90 @@ -52,7 +52,7 @@ module fox_m_fsys_format #ifndef DUMMYLIB interface safestr -! This is for internal use only - no check is made on the validity of +! This is for internal use only - no check is made on the validity of ! any fmt input. module procedure str_string, str_string_array, str_string_matrix, & str_integer, str_integer_array, str_integer_matrix, & @@ -111,13 +111,13 @@ contains #ifndef DUMMYLIB ! NB: The len generic module procedure is used in - ! many initialisation statments (to set the + ! many initialisation statments (to set the ! length of the output string needed for the ! converted number). As of the Fortran 2008 ! spec every specific function belonging to ! a generic used in this way must be defined ! in the module before use. This is enforced - ! by at least version 7.4.4 of the Cray + ! by at least version 7.4.4 of the Cray ! Fortran compiler. Hence we put all the *_len ! functions here at the top of the file. pure function str_string_array_len(st) result(n) @@ -143,7 +143,7 @@ contains pure function str_integer_len(i) result(n) integer, intent(in) :: i integer :: n - + n = int(log10(real(max(abs(i),1)))) + 1 + dim(-i,0)/max(abs(i),1) end function str_integer_len @@ -151,7 +151,7 @@ contains pure function str_integer_base_len(i, b) result(n) integer, intent(in) :: i, b integer :: n - + n = int(log10(real(max(abs(i),1)))/log10(real(b))) & + 1 + dim(-i,0)/max(abs(i),1) @@ -161,7 +161,7 @@ contains integer, intent(in) :: i character(len=*), intent(in) :: fmt integer :: n - + select case (len(fmt)) case(0) n = 0 @@ -179,7 +179,7 @@ contains elseif (verify(fmt(2:), digit)==0) then n = str_to_int_10(fmt(2:)) else - n = 0 + n = 0 endif end select @@ -188,7 +188,7 @@ contains pure function str_integer_array_len(ia) result(n) integer, dimension(:), intent(in) :: ia integer :: n - + integer :: j n = size(ia) - 1 @@ -203,7 +203,7 @@ contains integer, dimension(:), intent(in) :: ia character(len=*), intent(in) :: fmt integer :: n - + integer :: j n = size(ia) - 1 @@ -250,7 +250,7 @@ contains pure function str_logical_len(l) result (n) logical, intent(in) :: l integer :: n - + if (l) then n = 4 else @@ -292,17 +292,17 @@ contains else e = floor(log10(abs(x))) endif - + if (x < 0.0_sp) then n = 1 else n = 0 endif - + if (len(fmt) == 0) then sig = sig_sp - n = n + sig + 2 + len(e) + n = n + sig + 2 + len(e) ! for the decimal point and the e elseif (fmt(1:1) == "s") then @@ -314,9 +314,9 @@ contains sig = max(sig, 1) sig = min(sig, digits(1.0_sp)) - if (sig > 1) n = n + 1 + if (sig > 1) n = n + 1 ! for the decimal point - + n = n + sig + 1 + len(e) elseif (fmt(1:1) == "r") then @@ -376,7 +376,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), fmt) enddo - + end function str_real_sp_array_fmt_len pure function str_real_sp_matrix_fmt_len(xa, fmt) result(n) @@ -420,17 +420,17 @@ contains else e = floor(log10(abs(x))) endif - + if (x < 0.0_dp) then n = 1 else n = 0 endif - + if (len(fmt) == 0) then sig = sig_dp - n = n + sig + 2 + len(e) + n = n + sig + 2 + len(e) ! for the decimal point and the e elseif (fmt(1:1) == "s") then @@ -442,9 +442,9 @@ contains sig = max(sig, 1) sig = min(sig, digits(1.0_dp)) - if (sig > 1) n = n + 1 + if (sig > 1) n = n + 1 ! for the decimal point - + n = n + sig + 1 + len(e) elseif (fmt(1:1) == "r") then @@ -490,7 +490,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), "") enddo - + end function str_real_dp_array_len pure function str_real_dp_array_fmt_len(xa, fmt) result(n) @@ -504,7 +504,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), fmt) enddo - + end function str_real_dp_array_fmt_len pure function str_real_dp_matrix_fmt_len(xa, fmt) result(n) @@ -558,7 +558,7 @@ contains n = size(ca) - 1 do i = 1, size(ca) - n = n + len(ca(i), fmt) + n = n + len(ca(i), fmt) enddo end function str_complex_sp_array_fmt_len @@ -590,7 +590,7 @@ contains n = len(ca, "") end function str_complex_sp_matrix_len - + pure function str_complex_dp_fmt_len(c, fmt) result(n) complex(dp), intent(in) :: c character(len=*), intent(in) :: fmt @@ -644,7 +644,7 @@ contains enddo enddo end function str_complex_dp_matrix_fmt_len - + pure function str_complex_dp_matrix_len(ca) result(n) complex(dp), dimension(:, :), intent(in) :: ca integer :: n @@ -705,7 +705,7 @@ contains ! Error is flagged by returning -1 character(len=*), intent(in) :: str integer :: n - + character(len=len(str)) :: str_l integer :: max_power, i, j @@ -739,7 +739,7 @@ contains endif enddo end function to_lower - + end function str_to_int_16 #endif @@ -747,7 +747,7 @@ contains character(len=*), intent(in) :: st #ifdef DUMMYLIB character(len=1) :: s - s = " " + s = " " #else character(len=len(st)) :: s s = st @@ -762,10 +762,10 @@ contains s = " " #else character(len=str_string_array_len(st)) :: s - + integer :: k, n character(len=1) :: d - + if (present(delimiter)) then d = delimiter else @@ -789,7 +789,7 @@ contains s = " " #else character(len=str_string_matrix_len(st)) :: s - + integer :: j, k, n character(len=1) :: d @@ -853,7 +853,7 @@ contains character :: f integer :: b, ii, j, k, n, ls - + if (len(fmt)>0) then if (fmt(1:1)=="d") then f = 'd' @@ -902,7 +902,7 @@ contains #ifdef DUMMYLIB character(len=1) :: s #else - character(len=len(ia(:), "d")) :: s + character(len=len(ia, "d")) :: s integer :: j, k, n @@ -924,7 +924,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia(:), fmt)) :: s + character(len=len(ia, fmt)) :: s integer :: j, k, n @@ -944,7 +944,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia(:,:), "d")) :: s + character(len=len(ia, "d")) :: s integer :: j, k, n @@ -954,7 +954,7 @@ contains s(n:n+len(ia(j,1))) = " "//str(ia(j,1)) n = n + len(ia(j,1)) + 1 enddo - do k = 2, size(ia, 2) + do k = 2, size(ia, 2) do j = 1, size(ia, 1) s(n:n+len(ia(j,k))) = " "//str(ia(j,k)) n = n + len(ia(j,k)) + 1 @@ -971,7 +971,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia(:,:), fmt)) :: s + character(len=len(ia, fmt)) :: s integer :: j, k, n @@ -981,7 +981,7 @@ contains s(n:n+len(ia(j,1), fmt)) = " "//str(ia(j,1), fmt) n = n + len(ia(j,1), fmt) + 1 enddo - do k = 2, size(ia, 2) + do k = 2, size(ia, 2) do j = 1, size(ia, 1) s(n:n+len(ia(j,k), fmt)) = " "//str(ia(j,k), fmt) n = n + len(ia(j,k), fmt) + 1 @@ -1000,7 +1000,7 @@ contains ! character(len=merge(4,5,l)) :: s ! And g95 (sep2007) cant resolve the generic here character(len=str_logical_len(l)) :: s - + if (l) then s="true" else @@ -1015,8 +1015,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(la(:))) :: s - + character(len=len(la)) :: s + integer :: k, n n = 1 @@ -1044,7 +1044,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(la(:,:))) :: s + character(len=len(la)) :: s integer :: j, k, n @@ -1079,12 +1079,12 @@ contains enddo #endif end function str_logical_matrix - + #ifndef DUMMYLIB ! In order to convert real numbers to strings, we need to - ! perform an internal write - but how long will the + ! perform an internal write - but how long will the ! resultant string be? We don't know & there is no way - ! to discover for an arbitrary format. Therefore, + ! to discover for an arbitrary format. Therefore, ! (if we have the capability; f95 or better) ! we assume it will be less than 100 characters, write ! it to a string of that length, then remove leading & @@ -1094,7 +1094,7 @@ contains ! If we are working with an F90-only compiler, then ! we cannot do this trick - the output string will ! always be 100 chars in length, though we will remove - ! leading whitespace. + ! leading whitespace. ! The standard Fortran format functions do not give us @@ -1104,7 +1104,7 @@ contains ! "r" which will produce output without an exponent, ! and digits after the decimal point. ! or - ! "s": which implies scientific notation, with an + ! "s": which implies scientific notation, with an ! exponent, with significant figures. ! If the integer is absent, then the precision will be ! half of the number of significant figures available @@ -1137,7 +1137,7 @@ contains real(sp) :: x_ if (sig < 1) then - s ="" + s ="" return endif @@ -1156,11 +1156,11 @@ contains enddo n = 1 do k = sig - 2, 0, -1 - ! This baroque way of taking int() ensures the optimizer + ! This baroque way of taking int() ensures the optimizer ! stores it in j without keeping a different value in cache. j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 if (j==10) then - ! This can happen if, on the previous cycle, int(x_) in + ! This can happen if, on the previous cycle, int(x_) in ! the line above gave a result approx. 1.0 less than ! expected. ! In this case we want to quit the cycle & just get 999... to the end @@ -1351,8 +1351,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:))) :: s - + character(len=len(xa)) :: s + integer :: j, k, n n = 1 @@ -1369,8 +1369,8 @@ contains pure function str_real_sp_array_fmt(xa, fmt) result(s) real(sp), dimension(:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa(:), fmt)) :: s - + character(len=len(xa, fmt)) :: s + integer :: j, k, n n = 1 @@ -1391,8 +1391,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:), fmt)) :: s - + character(len=len(xa, fmt)) :: s + if (checkFmt(fmt)) then s = safestr(xa, fmt) else @@ -1405,7 +1405,7 @@ contains pure function str_real_sp_matrix_fmt(xa, fmt) result(s) real(sp), dimension(:,:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa(:,:),fmt)) :: s + character(len=len(xa,fmt)) :: s integer :: i, j, k, n @@ -1435,7 +1435,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:,:),fmt)) :: s + character(len=len(xa,fmt)) :: s if (checkFmt(fmt)) then s = safestr(xa, fmt) @@ -1451,12 +1451,12 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:,:))) :: s + character(len=len(xa)) :: s s = safestr(xa, "") #endif end function str_real_sp_matrix - + #ifndef DUMMYLIB pure function real_dp_str(x, sig) result(s) real(dp), intent(in) :: x @@ -1467,7 +1467,7 @@ contains real(dp) :: x_ if (sig < 1) then - s ="" + s ="" return endif @@ -1490,7 +1490,7 @@ contains ! stores it in j without keeping a different value in cache. j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 if (j==10) then - ! This can happen if, on the previous cycle, int(x_) in + ! This can happen if, on the previous cycle, int(x_) in ! the line above gave a result almost exactly 1.0 less than ! expected - but FP arithmetic is not consistent. ! In this case we want to quit the cycle & just get 999... to the end @@ -1682,8 +1682,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:))) :: s - + character(len=len(xa)) :: s + integer :: j, k, n n = 1 @@ -1700,8 +1700,8 @@ contains pure function str_real_dp_array_fmt(xa, fmt) result(s) real(dp), dimension(:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa(:), fmt)) :: s - + character(len=len(xa, fmt)) :: s + integer :: j, k, n n = 1 @@ -1722,8 +1722,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:), fmt)) :: s - + character(len=len(xa, fmt)) :: s + if (checkFmt(fmt)) then s = safestr(xa, fmt) else @@ -1736,7 +1736,7 @@ contains function str_real_dp_matrix_fmt(xa, fmt) result(s) real(dp), dimension(:,:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa(:,:),fmt)) :: s + character(len=len(xa,fmt)) :: s integer :: i, j, k, n @@ -1766,7 +1766,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:,:),fmt)) :: s + character(len=len(xa,fmt)) :: s if (checkFmt(fmt)) then s = safestr(xa, fmt) @@ -1782,7 +1782,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:,:))) :: s + character(len=len(xa)) :: s s = safestr(xa, "") #endif @@ -1800,7 +1800,7 @@ contains s = " " #else character(len=len(c, fmt)) :: s - + if (checkFmt(fmt)) then s = safestr(c, fmt) else @@ -1814,7 +1814,7 @@ contains complex(sp), intent(in) :: c character(len=*), intent(in) :: fmt character(len=len(c, fmt)) :: s - + real(sp) :: re, im integer :: i re = real(c) @@ -1841,13 +1841,13 @@ contains pure function str_complex_sp_array_fmt(ca, fmt) result(s) complex(sp), dimension(:), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca(:), fmt)) :: s + character(len=len(ca, fmt)) :: s integer :: i, n - + s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) n = len(ca(1), fmt)+1 - do i = 2, size(ca) + do i = 2, size(ca) s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) n = n + len(ca(i), fmt)+1 enddo @@ -1861,7 +1861,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:), fmt)) :: s + character(len=len(ca, fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -1877,7 +1877,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:))) :: s + character(len=len(ca)) :: s s = safestr(ca, "") #endif @@ -1887,7 +1887,7 @@ contains pure function str_complex_sp_matrix_fmt(ca, fmt) result(s) complex(sp), dimension(:, :), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca(:,:), fmt)) :: s + character(len=len(ca, fmt)) :: s integer :: i, j, k, n @@ -1917,7 +1917,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:,:), fmt)) :: s + character(len=len(ca, fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -1933,7 +1933,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:,:))) :: s + character(len=len(ca)) :: s s = safestr(ca, "") #endif @@ -1947,7 +1947,7 @@ contains s = " " #else character(len=len(c, fmt)) :: s - + if (checkFmt(fmt)) then s = safestr(c, fmt) else @@ -1961,7 +1961,7 @@ contains complex(dp), intent(in) :: c character(len=*), intent(in) :: fmt character(len=len(c, fmt)) :: s - + real(dp) :: re, im integer :: i re = real(c) @@ -1988,13 +1988,13 @@ contains pure function str_complex_dp_array_fmt(ca, fmt) result(s) complex(dp), dimension(:), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca(:), fmt)) :: s + character(len=len(ca, fmt)) :: s integer :: i, n s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) n = len(ca(1), fmt)+1 - do i = 2, size(ca) + do i = 2, size(ca) s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) n = n + len(ca(i), fmt)+1 enddo @@ -2008,7 +2008,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:), fmt)) :: s + character(len=len(ca, fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -2024,7 +2024,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:))) :: s + character(len=len(ca)) :: s s = safestr(ca, "") #endif @@ -2034,7 +2034,7 @@ contains pure function str_complex_dp_matrix_fmt(ca, fmt) result(s) complex(dp), dimension(:, :), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca(:,:), fmt)) :: s + character(len=len(ca, fmt)) :: s integer :: i, j, k, n @@ -2064,7 +2064,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:,:), fmt)) :: s + character(len=len(ca, fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -2080,7 +2080,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:,:))) :: s + character(len=len(ca)) :: s s = safestr(ca, "") #endif From 4907ed2df222bcddbbfd986ff8dbbfcdb9cb63b8 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sun, 29 Sep 2013 15:07:42 -0400 Subject: [PATCH 125/192] added intent attribute on all procedure args --- src/cmfd_data.F90 | 18 +++--- src/cmfd_execute.F90 | 41 +++++++------- src/cmfd_header.F90 | 6 +- src/cmfd_input.F90 | 2 +- src/cmfd_jfnk_solver.F90 | 16 +++--- src/cmfd_loss_operator.F90 | 112 ++++++++++++++++++------------------- src/cmfd_power_solver.F90 | 12 ++-- src/cmfd_prod_operator.F90 | 40 ++++++------- src/matrix_header.F90 | 86 ++++++++++++++-------------- src/solver_interface.F90 | 82 +++++++++++++-------------- src/vector_header.F90 | 26 ++++----- 11 files changed, 221 insertions(+), 220 deletions(-) diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index 0671c79eb6..d0e80aaa90 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -767,15 +767,15 @@ contains use global, only: cmfd, cmfd_hold_weights real(8) :: get_reflector_albedo ! reflector albedo - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: l ! iteration counter for leakages + integer, intent(in) :: i ! iteration counter for x + integer, intent(in) :: j ! iteration counter for y + integer, intent(in) :: k ! iteration counter for z + integer, intent(in) :: g ! iteration counter for groups + integer, intent(in) :: l ! iteration counter for leakages - integer :: shift_idx ! parameter to shift index by +1 or -1 - real(8) :: current(12) ! partial currents for all faces of mesh cell - real(8) :: albedo ! the albedo + integer :: shift_idx ! parameter to shift index by +1 or -1 + real(8) :: current(12) ! partial currents for all faces of mesh cell + real(8) :: albedo ! the albedo ! Get partial currents from object current = cmfd%current(:,g,i,j,k) @@ -798,7 +798,7 @@ contains end function get_reflector_albedo !=============================================================================== -! FIX_NEUTRON_BALANCE +! FIX_NEUTRON_BALANCE is a method to adjust parameters to have perfect balance !=============================================================================== subroutine fix_neutron_balance() diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 17c8157f41..7a35a12144 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -254,7 +254,7 @@ contains end subroutine calc_fission_source !=============================================================================== -! CMFD_REWEIGHT +! CMFD_REWEIGHT performs weighting of particles in the source bank !=============================================================================== subroutine cmfd_reweight(new_weights) @@ -271,17 +271,18 @@ contains use mpi #endif - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups - integer :: i ! iteration counter - integer :: ijk(3) ! spatial bin location - integer :: e_bin ! energy bin of source particle - integer :: n_groups ! number of energy groups - logical :: outside ! any source sites outside mesh - logical :: in_mesh ! source site is inside mesh - logical :: new_weights ! calcualte new weights + logical, intent(in) :: new_weights ! calcualte new weights + + integer :: nx ! maximum number of cells in x direction + integer :: ny ! maximum number of cells in y direction + integer :: nz ! maximum number of cells in z direction + integer :: ng ! maximum number of energy groups + integer :: i ! iteration counter + integer :: ijk(3) ! spatial bin location + integer :: e_bin ! energy bin of source particle + integer :: n_groups ! number of energy groups + logical :: outside ! any source sites outside mesh + logical :: in_mesh ! source site is inside mesh type(StructuredMesh), pointer :: m ! point to mesh real(8), allocatable :: egrid(:) ! energy grid @@ -389,13 +390,13 @@ contains use global, only: cmfd, cmfd_coremap integer :: matidx ! the index location in matrix - integer :: i ! current x index - integer :: j ! current y index - integer :: k ! current z index - integer :: g ! current group index - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: ng ! maximum number of energy groups + integer, intent(in) :: i ! current x index + integer, intent(in) :: j ! current y index + integer, intent(in) :: k ! current z index + integer, intent(in) :: g ! current group index + integer, intent(in) :: nx ! maximum number of cells in x direction + integer, intent(in) :: ny ! maximum number of cells in y direction + integer, intent(in) :: ng ! maximum number of energy groups ! Check if coremap is used if (cmfd_coremap) then @@ -413,7 +414,7 @@ contains end function get_matrix_idx !=============================================================================== -! CMFD_TALLY_RESET +! CMFD_TALLY_RESET resets all cmfd tallies !=============================================================================== subroutine cmfd_tally_reset() diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 index 42e6b30ef9..3d7786ab8e 100644 --- a/src/cmfd_header.F90 +++ b/src/cmfd_header.F90 @@ -93,8 +93,8 @@ contains subroutine allocate_cmfd(this, n_batches) - integer :: n_batches - type(cmfd_type) :: this + integer, intent(in) :: n_batches ! number of batches in calc + type(cmfd_type), intent(inout) :: this ! cmfd instance integer :: nx ! number of mesh cells in x direction integer :: ny ! number of mesh cells in y direction @@ -169,7 +169,7 @@ contains subroutine deallocate_cmfd(this) - type(cmfd_type) :: this + type(cmfd_type), intent(inout) :: this ! cmfd instance if (allocated(this % egrid)) deallocate(this % egrid) if (allocated(this % totalxs)) deallocate(this % totalxs) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 9a768f4e3b..a3a497b10f 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -228,7 +228,7 @@ contains integer :: ng ! number of energy groups (default 1) integer :: n_filters ! number of filters integer :: i_filter_mesh ! index for mesh filter - character(MAX_LINE_LEN) :: filename + character(MAX_LINE_LEN) :: filename ! name of cmfd file type(TallyObject), pointer :: t => null() type(StructuredMesh), pointer :: m => null() type(TallyFilter) :: filters(N_FILTER_TYPES) ! temporary filters diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 4e163c5c93..958a4c3e56 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -30,7 +30,7 @@ contains use global, only: time_cmfdbuild, time_cmfdsolve - logical, optional :: adjoint ! adjoint calculation + logical, intent(in), optional :: adjoint ! adjoint calculation ! Check for adjoint adjoint_calc = .false. @@ -84,8 +84,8 @@ contains use constants, only: ZERO, ONE use global, only: cmfd, cmfd_adjoint_type, current_batch - logical :: physical_adjoint - integer :: n + logical :: physical_adjoint ! physical adjoing calculation logical + integer :: n ! size of matrices ! Set up all matrices call init_loss_matrix(loss) @@ -153,8 +153,8 @@ contains subroutine init_jacobian_matrix() - integer :: nnz - integer :: n + integer :: nnz ! number of nonzeros in matrix + integer :: n ! dimension of matrix ! Get length of matrix and number of nonzeros total in loss matrix nnz = loss % nnz @@ -179,7 +179,7 @@ contains use constants, only: ONE - type(Vector) :: x + type(Vector), intent(in) :: x ! solution vector integer :: i ! loop counter for jacobian rows integer :: jjac ! loop counter for jacobian cols @@ -267,8 +267,8 @@ contains use global, only: cmfd_write_matrices - type(Vector) :: x - type(Vector) :: res + type(Vector), intent(in) :: x ! solution vector + type(Vector), intent(out) :: res ! residual vector character(len=25) :: filename integer :: n diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 index e09497bedc..6e9917ac5b 100644 --- a/src/cmfd_loss_operator.F90 +++ b/src/cmfd_loss_operator.F90 @@ -16,7 +16,7 @@ contains subroutine init_loss_matrix(loss_matrix) - type(Matrix) :: loss_matrix ! cmfd loss matrix + type(Matrix), intent(inout) :: loss_matrix ! cmfd loss matrix integer :: nx ! maximum number of x cells integer :: ny ! maximum number of y cells @@ -68,12 +68,12 @@ contains function preallocate_loss_matrix(nx, ny, nz, ng, n) result(nnz) - integer, intent(in) :: nx ! maximum number of x cells - integer, intent(in) :: ny ! maximum number of y cells - integer, intent(in) :: nz ! maximum number of z cells - integer, intent(in) :: ng ! maximum number of groups - integer, intent(in) :: n ! total length of matrix - integer :: nnz ! number of nonzeros + integer, intent(in) :: nx ! maximum number of x cells + integer, intent(in) :: ny ! maximum number of y cells + integer, intent(in) :: nz ! maximum number of z cells + integer, intent(in) :: ng ! maximum number of groups + integer, intent(in) :: n ! total length of matrix + integer :: nnz ! number of nonzeros integer :: i ! iteration counter for x integer :: j ! iteration counter for y @@ -181,39 +181,39 @@ contains subroutine build_loss_matrix(loss_matrix, adjoint) - type(Matrix) :: loss_matrix ! cmfd loss matrix - logical, optional :: adjoint ! set up the adjoint + type(Matrix), intent(inout) :: loss_matrix ! cmfd loss matrix + logical, intent(in), optional :: adjoint ! set up the adjoint - integer :: nxyz(3,2) ! single vector containing bound. locations - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: l ! iteration counter for leakages - integer :: h ! energy group when doing scattering - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups - integer :: neig_mat_idx ! matrix index of neighbor cell - integer :: scatt_mat_idx ! matrix index for h-->g scattering terms - integer :: bound(6) ! vector for comparing when looking for bound - integer :: xyz_idx ! index for determining if x,y or z leakage - integer :: dir_idx ! index for determining - or + face of cell - integer :: neig_idx(3) ! spatial indices of neighbour - integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: irow ! iteration counter over row - logical :: adjoint_calc ! is this a physical adjoint calculation? - real(8) :: totxs ! total macro cross section - real(8) :: scattxsgg ! scattering macro cross section g-->g - real(8) :: scattxshg ! scattering macro cross section h-->g - real(8) :: dtilde(6) ! finite difference coupling parameter - real(8) :: dhat(6) ! nonlinear coupling parameter - real(8) :: hxyz(3) ! cell lengths in each direction - real(8) :: jn ! direction dependent leakage coeff to neig - real(8) :: jo(6) ! leakage coeff in front of cell flux - real(8) :: jnet ! net leakage from jo - real(8) :: val ! temporary variable before saving to + integer :: nxyz(3,2) ! single vector containing bound. locations + integer :: i ! iteration counter for x + integer :: j ! iteration counter for y + integer :: k ! iteration counter for z + integer :: g ! iteration counter for groups + integer :: l ! iteration counter for leakages + integer :: h ! energy group when doing scattering + integer :: nx ! maximum number of x cells + integer :: ny ! maximum number of y cells + integer :: nz ! maximum number of z cells + integer :: ng ! maximum number of groups + integer :: neig_mat_idx ! matrix index of neighbor cell + integer :: scatt_mat_idx ! matrix index for h-->g scattering terms + integer :: bound(6) ! vector for comparing when looking for bound + integer :: xyz_idx ! index for determining if x,y or z leakage + integer :: dir_idx ! index for determining - or + face of cell + integer :: neig_idx(3) ! spatial indices of neighbour + integer :: shift_idx ! parameter to shift index by +1 or -1 + integer :: irow ! iteration counter over row + logical :: adjoint_calc ! is this a physical adjoint calculation? + real(8) :: totxs ! total macro cross section + real(8) :: scattxsgg ! scattering macro cross section g-->g + real(8) :: scattxshg ! scattering macro cross section h-->g + real(8) :: dtilde(6) ! finite difference coupling parameter + real(8) :: dhat(6) ! nonlinear coupling parameter + real(8) :: hxyz(3) ! cell lengths in each direction + real(8) :: jn ! direction dependent leakage coeff to neig + real(8) :: jo(6) ! leakage coeff in front of cell flux + real(8) :: jnet ! net leakage from jo + real(8) :: val ! temporary variable before saving to ! Check for adjoint adjoint_calc = .false. @@ -366,14 +366,14 @@ contains subroutine indices_to_matrix(g, i, j, k, matidx, ng, nx, ny) - integer :: matidx ! the index location in matrix - integer :: i ! current x index - integer :: j ! current y index - integer :: k ! current z index - integer :: g ! current group index - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: ng ! maximum number of groups + integer, intent(out) :: matidx ! the index location in matrix + integer, intent(in) :: i ! current x index + integer, intent(in) :: j ! current y index + integer, intent(in) :: k ! current z index + integer, intent(in) :: g ! current group index + integer, intent(in) :: nx ! maximum number of x cells + integer, intent(in) :: ny ! maximum number of y cells + integer, intent(in) :: ng ! maximum number of groups ! Check if coremap is used if (cmfd_coremap) then @@ -396,15 +396,15 @@ contains subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: irow ! iteration counter over row (0 reference) - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups + integer, intent(out) :: i ! iteration counter for x + integer, intent(out) :: j ! iteration counter for y + integer, intent(out) :: k ! iteration counter for z + integer, intent(out) :: g ! iteration counter for groups + integer, intent(in) :: irow ! iteration counter over row (0 reference) + integer, intent(in) :: nx ! maximum number of x cells + integer, intent(in) :: ny ! maximum number of y cells + integer, intent(in) :: nz ! maximum number of z cells + integer, intent(in) :: ng ! maximum number of groups ! Check for core map if (cmfd_coremap) then diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index cd31789407..c1309b72cd 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -41,11 +41,11 @@ contains use global, only: cmfd_adjoint_type, time_cmfdbuild, time_cmfdsolve - real(8), optional :: k_tol ! tolerance on keff - real(8), optional :: s_tol ! tolerance on source - logical, optional :: adjoint ! adjoint calc + real(8), intent(in), optional :: k_tol ! tolerance on keff + real(8), intent(in), optional :: s_tol ! tolerance on source + logical, intent(in), optional :: adjoint ! adjoint calc - logical :: physical_adjoint = .false. + logical :: physical_adjoint = .false. ! physical adjoint default false ! Set tolerances if present if (present(k_tol)) ktol = k_tol @@ -104,7 +104,7 @@ contains use constants, only: ONE, ZERO use global, only: cmfd_write_matrices - logical :: adjoint + logical, intent(in) :: adjoint ! adjoint calcualtion integer :: n ! problem size real(8) :: guess ! initial guess @@ -238,7 +238,7 @@ contains use global, only: cmfd_power_monitor, master use, intrinsic :: ISO_FORTRAN_ENV - integer :: iter ! iteration number + integer, intent(in) :: iter ! iteration number ! Reset convergence flag iconv = .false. diff --git a/src/cmfd_prod_operator.F90 b/src/cmfd_prod_operator.F90 index c2db9fc83c..e6d055ced8 100644 --- a/src/cmfd_prod_operator.F90 +++ b/src/cmfd_prod_operator.F90 @@ -16,7 +16,7 @@ contains subroutine init_prod_matrix(prod_matrix) - type(Matrix) :: prod_matrix + type(Matrix), intent(inout) :: prod_matrix ! production matrix integer :: nx ! maximum number of x cells integer :: ny ! maximum number of y cells @@ -50,8 +50,8 @@ contains subroutine build_prod_matrix(prod_matrix, adjoint) - type(Matrix) :: prod_matrix - logical, optional :: adjoint + type(Matrix), intent(inout) :: prod_matrix ! production matrix + logical, intent(in), optional :: adjoint ! adjoint calculation logical integer :: i ! iteration counter for x integer :: j ! iteration counter for y @@ -135,14 +135,14 @@ contains subroutine indices_to_matrix(g, i, j, k, matidx, ng, nx, ny) - integer :: matidx ! the index location in matrix - integer :: i ! current x index - integer :: j ! current y index - integer :: k ! current z index - integer :: g ! current group index - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: ng ! maximum number of groups + integer, intent(out) :: matidx ! the index location in matrix + integer, intent(in) :: i ! current x index + integer, intent(in) :: j ! current y index + integer, intent(in) :: k ! current z index + integer, intent(in) :: g ! current group index + integer, intent(in) :: nx ! maximum number of x cells + integer, intent(in) :: ny ! maximum number of y cells + integer, intent(in) :: ng ! maximum number of groups ! check if coremap is used if (cmfd_coremap) then @@ -165,15 +165,15 @@ contains subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: irow ! iteration counter over row (0 reference) - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups + integer, intent(out) :: i ! iteration counter for x + integer, intent(out) :: j ! iteration counter for y + integer, intent(out) :: k ! iteration counter for z + integer, intent(out) :: g ! iteration counter for groups + integer, intent(in) :: irow ! iteration counter over row (0 reference) + integer, intent(in) :: nx ! maximum number of x cells + integer, intent(in) :: ny ! maximum number of y cells + integer, intent(in) :: nz ! maximum number of z cells + integer, intent(in) :: ng ! maximum number of groups ! check for core map if (cmfd_coremap) then diff --git a/src/matrix_header.F90 b/src/matrix_header.F90 index 25f1df95a5..591eeaaed1 100644 --- a/src/matrix_header.F90 +++ b/src/matrix_header.F90 @@ -43,9 +43,9 @@ contains subroutine matrix_create(self, n, nnz) - integer :: n - integer :: nnz - class(Matrix) :: self + integer, intent(in) :: n ! dimension of matrix + integer, intent(in) :: nnz ! number of nonzeros + class(Matrix), intent(inout) :: self ! matrix instance ! Preallocate vectors if (.not.allocated(self % row)) allocate(self % row(n+1)) @@ -71,7 +71,7 @@ contains subroutine matrix_destroy(self) - class(Matrix) :: self + class(Matrix), intent(inout) :: self ! matrix instance #ifdef PETSC if (self % petsc_active) call MatDestroy(self % petsc_mat, petsc_err) @@ -89,9 +89,9 @@ contains subroutine matrix_add_value(self, col, val) - integer :: col - real(8) :: val - class(Matrix) :: self + integer, intent(in) :: col ! col location in matrix + real(8), intent(in) :: val ! value to store in matrix + class(Matrix), intent(inout) :: self ! matrix instance ! Record the data self % col(self % nz_count) = col @@ -112,7 +112,7 @@ contains subroutine matrix_new_row(self) - class(Matrix) :: self + class(Matrix), intent(inout) :: self ! matrix instance ! Record the current number of nonzeros self % row(self % n_count) = self % nz_count @@ -132,7 +132,7 @@ contains subroutine matrix_assemble(self) - class(Matrix) :: self + class(Matrix), intent(inout) :: self ! matrix instance integer :: i integer :: first @@ -158,12 +158,12 @@ contains recursive subroutine sort_csr(col, val, first, last) - integer :: col(:) - integer :: first - integer :: last - real(8) :: val(:) + integer :: col(:) ! column vector to sort + integer :: first ! first value in sort + integer :: last ! last value in sort + real(8) :: val(:) ! value vector to be sorted like columns - integer :: mid + integer :: mid ! midpoint value if (first < last) then call split(col, val, first, last, mid) @@ -179,18 +179,18 @@ contains subroutine split(col, val, low, high, mid) - integer :: col(:) - integer :: low - integer :: high - integer :: mid - real(8) :: val(:) + integer :: col(:) ! column vector to sort + integer :: low ! low index to sort + integer :: high ! high index to sort + integer :: mid ! middle of sort + real(8) :: val(:) ! value vector to be sorted like columns - integer :: left - integer :: right - integer :: iswap - integer :: pivot - real(8) :: rswap - real(8) :: val0 + integer :: left ! contains left value in sort + integer :: right ! contains right value in sort + integer :: iswap ! temporary interger swap + integer :: pivot ! pivotting variable for columns + real(8) :: rswap ! temporary real swap + real(8) :: val0 ! pivot for value vector left = low right = high @@ -232,14 +232,14 @@ contains end subroutine split !=============================================================================== -! MATRIX_GET_ROW +! MATRIX_GET_ROW is a method to get row and checks for PETSc C indexing use !=============================================================================== function matrix_get_row(self, i) result(row) - class(Matrix) :: self - integer :: i - integer :: row + class(Matrix), intent(in) :: self ! matrix instance + integer, intent(in) :: i ! row to get + integer :: row ! index in col where row begins row = self % row(i) @@ -248,14 +248,14 @@ contains end function matrix_get_row !=============================================================================== -! MATRIX_GET_COL +! MATRIX_GET_COL is a method to get column and checks for PETSc C index use !=============================================================================== function matrix_get_col(self, i) result(col) - class(Matrix) :: self - integer :: i - integer :: col + class(Matrix), intent(in) :: self ! matrix instance + integer, intent(in) :: i ! index from row vector + integer :: col ! the actual column col = self % col(i) @@ -269,7 +269,7 @@ contains subroutine matrix_setup_petsc(self) - class(Matrix) :: self + class(Matrix), intent(inout) :: self ! matrix instance ! change indices to c notation self % row = self % row - 1 @@ -292,11 +292,11 @@ contains subroutine matrix_write_petsc_binary(self, filename) - character(*) :: filename - class(Matrix) :: self + character(*), intent(in) :: filename ! file name to write to + class(Matrix), intent(in) :: self ! matrix instance #ifdef PETSC - type(PetscViewer) :: viewer + type(PetscViewer) :: viewer ! a petsc viewer instance call PetscViewerBinaryOpen(PETSC_COMM_WORLD, trim(filename), & FILE_MODE_WRITE, viewer, petsc_err) @@ -312,7 +312,7 @@ contains subroutine matrix_transpose(self) - class(Matrix) :: self + class(Matrix), intent(inout) :: self ! matrix instance #ifdef PETSC call MatTranspose(self % petsc_mat, MAT_REUSE_MATRIX, self % petsc_mat, & @@ -330,12 +330,12 @@ contains use constants, only: ZERO use vector_header, only: Vector - class(Matrix) :: self - type(Vector) :: vec_in - type(Vector) :: vec_out + class(Matrix), intent(in) :: self ! matrix instance + type(Vector), intent(in) :: vec_in ! vector to multiply matrix against + type(Vector), intent(inout) :: vec_out ! resulting vector - integer :: i - integer :: j + integer :: i ! row iteration counter + integer :: j ! column iteration counter ! Begin loop around rows ROWS: do i = 1, self % n diff --git a/src/solver_interface.F90 b/src/solver_interface.F90 index a937057d6b..113e3119af 100644 --- a/src/solver_interface.F90 +++ b/src/solver_interface.F90 @@ -16,8 +16,8 @@ module solver_interface ! GMRES solver type type, public :: GMRESSolver #ifdef PETSC - type(ksp) :: ksp_ - type(pc) :: pc_ + type(ksp) :: ksp_ ! Krylov linear solver instance + type(pc) :: pc_ ! Preconditioner instance #endif contains #ifdef PETSC @@ -37,11 +37,11 @@ module solver_interface ! JFNK solver type type, public :: JFNKSolver #ifdef PETSC - type(ksp) :: ksp_ - type(pc) :: pc_ - type(snes) :: snes_ - type(mat) :: jac_mf - integer :: ls + type(ksp) :: ksp_ ! Krylov linear solver instance + type(pc) :: pc_ ! Preconditioner instance + type(snes) :: snes_ ! Nonlinear solver instance + type(mat) :: jac_mf ! Matrix free jacobian instance + integer :: ls ! Line search instance #endif contains #ifdef PETSC @@ -56,13 +56,13 @@ module solver_interface abstract interface subroutine res_interface(x, res) import :: Vector - type(Vector) :: x - type(Vector) :: res + type(Vector), intent(in) :: x ! solution vector + type(Vector), intent(out) :: res ! residual vector end subroutine res_interface subroutine jac_interface(x) import :: Vector - type(Vector) :: x + type(Vector), intent(in) :: x ! solution vector end subroutine jac_interface end interface @@ -79,7 +79,7 @@ contains subroutine petsc_gmres_create(self) - class(GMRESSolver) :: self + class(GMRESSolver), intent(inout) :: self ! GMRES solver instance integer :: ilu_levels = 5 real(8) :: rtol = 1.0e-10_8 @@ -102,9 +102,9 @@ contains subroutine petsc_gmres_set_oper(self, prec_mat, mat_in) - class(GMRESSolver) :: self - type(Matrix) :: prec_mat - type(Matrix) :: mat_in + class(GMRESSolver), intent(inout) :: self ! GMRES solver instanace + type(Matrix), intent(inout) :: prec_mat ! preconditioner matrix + type(Matrix), intent(inout) :: mat_in ! coefficient matrix call KSPSetOperators(self % ksp_, mat_in % petsc_mat, prec_mat % petsc_mat, & SAME_NONZERO_PATTERN, petsc_err) @@ -118,7 +118,7 @@ contains subroutine petsc_gmres_destroy(self) - class(GMRESSolver) :: self + class(GMRESSolver), intent(inout) :: self ! GMRES solver instance call KSPDestroy(self % ksp_, petsc_err) @@ -130,9 +130,9 @@ contains subroutine petsc_gmres_solve(self, b, x) - class(GMRESSolver) :: self - type(Vector) :: b - type(Vector) :: x + class(GMRESSolver), intent(inout) :: self ! GMRES solver instance + type(Vector), intent(inout) :: b ! right hand side vector + type(Vector), intent(inout) :: x ! solution vector call KSPSolve(self % ksp_, b % petsc_vec, x % petsc_vec, petsc_err) @@ -144,7 +144,7 @@ contains subroutine petsc_jfnk_create(self) - class(JFNKSolver) :: self + class(JFNKSolver), intent(inout) :: self ! JFNK solver instance ! Turn on mf_operator option for matrix free jacobian call PetscOptionsSetValue("-snes_mf_operator", "TRUE", petsc_err) @@ -176,7 +176,7 @@ contains subroutine petsc_jfnk_destroy(self) - class(JFNKSolver) :: self + class(JFNKSolver), intent(inout) :: self ! JFNK solver instance call MatDestroy(self % jac_mf, petsc_err) call SNESDestroy(self % snes_, petsc_err) @@ -189,10 +189,10 @@ contains subroutine petsc_jfnk_set_functions(self, ctx, res, jac_prec) - class(JFNKSolver) :: self - type(Jfnk_ctx) :: ctx - type(Vector) :: res - type(Matrix) :: jac_prec + class(JFNKSolver), intent(inout) :: self ! JFNK solver instance + type(Jfnk_ctx), intent(inout) :: ctx ! JFNK context instance + type(Vector), intent(inout) :: res ! residual vector + type(Matrix), intent(inout) :: jac_prec ! preconditioner matrix ! Set residual procedure call SNESSetFunction(self % snes_, res % petsc_vec, & @@ -217,8 +217,8 @@ contains subroutine petsc_jfnk_solve(self, xvec) - class(JFNKSolver) :: self - type(Vector) :: xvec + class(JFNKSolver), intent(inout) :: self ! JFNK instance + type(Vector), intent(inout) :: xvec ! solution vector call SNESSolve(self % snes_, PETSC_NULL_DOUBLE, xvec % petsc_vec, petsc_err) @@ -230,14 +230,14 @@ contains subroutine petsc_jfnk_compute_residual(snes_, x, res, ctx, ierr) - type(snes) :: snes_ - type(vec) :: x - type(vec) :: res - integer :: ierr - type(Jfnk_ctx) :: ctx + type(snes), intent(inout) :: snes_ ! PETSc SNES object + type(vec), intent(inout) :: x ! PETSc solution vector + type(vec), intent(inout) :: res ! PETSc residual vector + integer, intent(inout) :: ierr ! error code + type(Jfnk_ctx), intent(inout) :: ctx ! JFNK context instance - type(Vector) :: xvec - type(Vector) :: resvec + type(Vector) :: xvec ! solution vector + type(Vector) :: resvec ! residual vector ! We need to use the x and res that come from PETSc because the pointer ! location changes and therefore we can not use module variables @@ -263,15 +263,15 @@ contains subroutine petsc_jfnk_compute_jacobian(snes_, x, jac_mf, jac_prec, flag, & ctx, ierr) - type(snes) :: snes_ - type(vec) :: x - type(mat) :: jac_mf - type(mat) :: jac_prec - integer :: flag - type(Jfnk_ctx) :: ctx - integer :: ierr + type(snes), intent(inout) :: snes_ ! PETSc snes instance + type(vec), intent(inout) :: x ! PETSc solution vector + type(mat), intent(inout) :: jac_mf ! PETSc matrix free jacobian + type(mat), intent(inout) :: jac_prec ! PETSc matrix jacobian precond. + integer, intent(inout) :: flag ! unused madatory flag + type(Jfnk_ctx), intent(inout) :: ctx ! JFNK context instance + integer, intent(inout) :: ierr ! error code - type(Vector) :: xvec + type(Vector) :: xvec ! solution vector ! Again, we use the vector that comes from Petsc to build the Jacobian ! matrix diff --git a/src/vector_header.F90 b/src/vector_header.F90 index 47b865ebec..3d1bd9afae 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -14,9 +14,9 @@ module vector_header real(8), allocatable :: data(:) ! where vector data is stored real(8), pointer :: val(:) ! pointer to vector data # ifdef PETSC - type(vec) :: petsc_vec + type(vec) :: petsc_vec ! PETSc vector # endif - logical :: petsc_active + logical :: petsc_active ! Logical if PETSc is being used contains procedure :: create => vector_create procedure :: destroy => vector_destroy @@ -25,7 +25,7 @@ module vector_header procedure :: write_petsc_binary => vector_write_petsc_binary end type Vector - integer :: petsc_err + integer :: petsc_err ! petsc error code contains @@ -35,8 +35,8 @@ contains subroutine vector_create(self, n) - integer :: n - class(Vector), target :: self + integer, intent(in) :: n ! size of vector + class(Vector), intent(inout), target :: self ! vector instance ! Preallocate vector if (.not.allocated(self % data)) allocate(self % data(n)) @@ -59,7 +59,7 @@ contains subroutine vector_destroy(self) - class(Vector) :: self + class(Vector), intent(inout) :: self ! vector instance #ifdef PETSC if (self % petsc_active) call VecDestroy(self % petsc_vec, petsc_err) @@ -76,9 +76,9 @@ contains subroutine vector_add_value(self, idx, val) - integer :: idx - real(8) :: val - class(Vector) :: self + integer, intent(in) :: idx ! index location in vector + real(8), intent(in) :: val ! value to add + class(Vector), intent(inout) :: self ! vector instance self % val(idx) = val @@ -90,7 +90,7 @@ contains subroutine vector_setup_petsc(self) - class(Vector) :: self + class(Vector), intent(inout) :: self ! vector instance ! Link to PETSc #ifdef PETSC @@ -109,11 +109,11 @@ contains subroutine vector_write_petsc_binary(self, filename) - character(*) :: filename - class(Vector) :: self + character(*), intent(in) :: filename ! name of file to write to + class(Vector), intent(in) :: self ! vector instance #ifdef PETSC - type(PetscViewer) :: viewer + type(PetscViewer) :: viewer ! PETSc viewer instance call PetscViewerBinaryOpen(PETSC_COMM_WORLD, trim(filename), & FILE_MODE_WRITE, viewer, petsc_err) From 80b58c2d0f4cf4701b2f456dc34d4d8a9cd23faf Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sun, 29 Sep 2013 15:16:51 -0400 Subject: [PATCH 126/192] added space between args --- src/cmfd_input.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index a3a497b10f..d893d7bc74 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -35,7 +35,7 @@ contains ! Split up procs # ifdef PETSC - call MPI_COMM_SPLIT(MPI_COMM_WORLD,color,0,new_comm,mpi_err) + call MPI_COMM_SPLIT(MPI_COMM_WORLD, color, 0, new_comm, mpi_err) # endif ! assign to PETSc @@ -43,7 +43,7 @@ contains PETSC_COMM_WORLD = new_comm ! Initialize PETSc on all procs - call PetscInitialize(PETSC_NULL_CHARACTER,mpi_err) + call PetscInitialize(PETSC_NULL_CHARACTER, mpi_err) # endif ! Initialize timers From 7747f798452b4498a3516a2c22f8dee41eca0967 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sun, 29 Sep 2013 15:43:49 -0400 Subject: [PATCH 127/192] updated statepoint specs for rev. 9 and rev. 10 --- docs/source/devguide/statepoint.rst | 538 ++++++++++++++++++++++++++++ 1 file changed, 538 insertions(+) diff --git a/docs/source/devguide/statepoint.rst b/docs/source/devguide/statepoint.rst index 2b486f12e0..9862a45282 100644 --- a/docs/source/devguide/statepoint.rst +++ b/docs/source/devguide/statepoint.rst @@ -4,6 +4,544 @@ State Point Binary File Specifications ====================================== +----------- +Revision 10 +----------- + +**integer(4) FILETYPE_STATEPOINT** + + Flags whether this file is a statepoint file or a particle restart file. + +**integer(4) REVISION_STATEPOINT** + + Revision of the binary state point file. Any time a change is made in the + format of the state-point file, this integer is incremented. + +**integer(4) VERSION_MAJOR** + + Major version number for OpenMC + +**integer(4) VERSION_MINOR** + + Minor version number for OpenMC + +**integer(4) VERSION_RELEASE** + + Release version number for OpenMC + +**character(19) time_stamp** + + Date and time the state point was written. + +**character(255) path** + + Absolute path to directory containing input files. + +**integer(8) seed** + + Pseudo-random number generator seed. + +**integer(4) run_mode** + + run mode used. The modes are described in constants.F90. + +**integer(8) n_particles** + + Number of particles used per generation. + +**integer(4) n_batches** + + Total number of batches (active + inactive). + +**integer(4) current_batch** + + The number of batches already simulated. + +if (run_mode == MODE_EIGENVALUE) + + **integer(4) n_inactive** + + Number of inactive batches + + **integer(4) gen_per_batch** + + Number of generations per batch for criticality calculations + + *do i = 1, current_batch \* gen_per_batch* + + **real(8) k_generation(i)** + + k-effective for the i-th total generation + + *do i = 1, current_batch \* gen_per_batch* + + **real(8) entropy(i)** + + Shannon entropy for the i-th total generation + + **real(8) k_col_abs** + + Sum of product of collision/absorption estimates of k-effective + + **real(8) k_col_tra** + + Sum of product of collision/track-length estimates of k-effective + + **real(8) k_abs_tra** + + Sum of product of absorption/track-length estimates of k-effective + + **real(8) k_combined(2)** + + Mean and standard deviation of a combined estimate of k-effective + + **integer(4) cmfd_on** + + Flag that cmfd is on + + if (cmfd_on) + + **integer(4) cmfd % indices** + + Indices for cmfd mesh (i,j,k,g) + + **real(8) cmfd % k_cmfd(1:current_batch)** + + CMFD eigenvalues + + **real(8) cmfd % src(1:I,1:J,1:K,1:G)** + + CMFD fission source + + **real(8) cmfd % entropy(1:current_batch)** + + CMFD estimate of Shannon entropy + + **real(8) cmfd % balance(1:current_batch)** + + RMS of the residual neutron balance equation on CMFD mesh + + **real(8) cmfd % dom(1:current_batch)** + + CMFD estimate of dominance ratio + + **real(8) cmfd % scr_cmp(1:current_batch)** + + RMS comparison of difference between OpenMC and CMFD fission source + +**integer(4) n_meshes** + + Number of meshes in tallies.xml file + +*do i = 1, n_meshes* + + **integer(4) meshes(i) % id** + + Unique ID of mesh. + + **integer(4) meshes(i) % type** + + Type of mesh. + + **integer(4) meshes(i) % n_dimension** + + Number of dimensions for mesh (2 or 3). + + **integer(4) meshes(i) % dimension(:)** + + Number of mesh cells in each dimension. + + **real(8) meshes(i) % lower_left(:)** + + Coordinates of lower-left corner of mesh. + + **real(8) meshes(i) % upper_right(:)** + + Coordinates of upper-right corner of mesh. + + **real(8) meshes(i) % width(:)** + + Width of each mesh cell in each dimension. + +**integer(4) n_tallies** + +*do i = 1, n_tallies* + + **integer(4) tallies(i) % id** + + Unique ID of tally. + + **integer(4) tallies(i) % n_realizations** + + Number of realizations for the i-th tally. + + **integer(4) size(tallies(i) % scores, 1)** + + Total number of score bins for the i-th tally + + **integer(4) size(tallies(i) % scores, 2)** + + Total number of filter bins for the i-th tally + + **integer(4) tallies(i) % n_filters** + + *do j = 1, tallies(i) % n_filters* + + **integer(4) tallies(i) % filter(j) % type** + + Type of tally filter. + + **integer(4) tallies(i) % filter(j) % n_bins** + + Number of bins for filter. + + **integer(4)/real(8) tallies(i) % filter(j) % bins(:)** + + Value for each filter bin of this type. + + **integer(4) tallies(i) % n_nuclide_bins** + + Number of nuclide bins. If none are specified, this is just one. + + *do j = 1, tallies(i) % n_nuclide_bins* + + **integer(4) tallies(i) % nuclide_bins(j)** + + Values of specified nuclide bins + + **integer(4) tallies(i) % n_score_bins** + + Number of scoring bins. + + *do j = 1, tallies(i) % n_score_bins* + + **integer(4) tallies(i) % score_bins(j)** + + Values of specified scoring bins (e.g. SCORE_FLUX). + + *do j = 1, tallies(i) % n_score_bins* + + **integer(4) tallies(i) % scatt_order(j)** + + Scattering Order specified scoring bins. + + **integer(4) tallies(i) % n_score_bins** + + Number of scoring bins without accounting for those added by + the scatter-pn command. + +**integer(4) n_realizations** + + Number of realizations for global tallies. + +**integer(4) N_GLOBAL_TALLIES** + + Number of global tally scores + +*do i = 1, N_GLOBAL_TALLIES* + + **real(8) global_tallies(i) % sum** + + Accumulated sum for the i-th global tally + + **real(8) global_tallies(i) % sum_sq** + + Accumulated sum of squares for the i-th global tally + +**integer(4) tallies_on** + + Flag indicated if tallies are present in the file. + +if (tallies_on > 0) + + *do i = 1, n_tallies* + + *do k = 1, size(tallies(i) % scores, 2)* + + *do j = 1, size(tallies(i) % scores, 1)* + + **real(8) tallies(i) % scores(j,k) % sum** + + Accumulated sum for the j-th score and k-th filter of the + i-th tally + + **real(8) tallies(i) % scores(j,k) % sum_sq** + + Accumulated sum of squares for the j-th score and k-th + filter of the i-th tally + +if (run_mode == MODE_EIGENVALUE) + + *do i = 1, n_particles* + + **real(8) source_bank(i) % wgt** + + Weight of the i-th source particle + + **real(8) source_bank(i) % xyz(1:3)** + + Coordinates of the i-th source particle. + + **real(8) source_bank(i) % uvw(1:3)** + + Direction of the i-th source particle + + **real(8) source_bank(i) % E** + + Energy of the i-th source particle. + +---------- +Revision 9 +---------- + +**integer(4) FILETYPE_STATEPOINT** + + Flags whether this file is a statepoint file or a particle restart file. + +**integer(4) REVISION_STATEPOINT** + + Revision of the binary state point file. Any time a change is made in the + format of the state-point file, this integer is incremented. + +**integer(4) VERSION_MAJOR** + + Major version number for OpenMC + +**integer(4) VERSION_MINOR** + + Minor version number for OpenMC + +**integer(4) VERSION_RELEASE** + + Release version number for OpenMC + +**character(19) time_stamp** + + Date and time the state point was written. + +**character(255) path** + + Absolute path to directory containing input files. + +**integer(8) seed** + + Pseudo-random number generator seed. + +**integer(4) run_mode** + + run mode used. The modes are described in constants.F90. + +**integer(8) n_particles** + + Number of particles used per generation. + +**integer(4) n_batches** + + Total number of batches (active + inactive). + +**integer(4) current_batch** + + The number of batches already simulated. + +if (run_mode == MODE_EIGENVALUE) + + **integer(4) n_inactive** + + Number of inactive batches + + **integer(4) gen_per_batch** + + Number of generations per batch for criticality calculations + + *do i = 1, current_batch \* gen_per_batch* + + **real(8) k_generation(i)** + + k-effective for the i-th total generation + + *do i = 1, current_batch \* gen_per_batch* + + **real(8) entropy(i)** + + Shannon entropy for the i-th total generation + + **real(8) k_col_abs** + + Sum of product of collision/absorption estimates of k-effective + + **real(8) k_col_tra** + + Sum of product of collision/track-length estimates of k-effective + + **real(8) k_abs_tra** + + Sum of product of absorption/track-length estimates of k-effective + + **real(8) k_combined(2)** + + Mean and standard deviation of a combined estimate of k-effective + +**integer(4) n_meshes** + + Number of meshes in tallies.xml file + +*do i = 1, n_meshes* + + **integer(4) meshes(i) % id** + + Unique ID of mesh. + + **integer(4) meshes(i) % type** + + Type of mesh. + + **integer(4) meshes(i) % n_dimension** + + Number of dimensions for mesh (2 or 3). + + **integer(4) meshes(i) % dimension(:)** + + Number of mesh cells in each dimension. + + **real(8) meshes(i) % lower_left(:)** + + Coordinates of lower-left corner of mesh. + + **real(8) meshes(i) % upper_right(:)** + + Coordinates of upper-right corner of mesh. + + **real(8) meshes(i) % width(:)** + + Width of each mesh cell in each dimension. + +**integer(4) n_tallies** + +*do i = 1, n_tallies* + + **integer(4) tallies(i) % id** + + Unique ID of tally. + + **integer(4) tallies(i) % n_realizations** + + Number of realizations for the i-th tally. + + **integer(4) size(tallies(i) % scores, 1)** + + Total number of score bins for the i-th tally + + **integer(4) size(tallies(i) % scores, 2)** + + Total number of filter bins for the i-th tally + + **integer(4) tallies(i) % n_filters** + + *do j = 1, tallies(i) % n_filters* + + **integer(4) tallies(i) % filter(j) % type** + + Type of tally filter. + + **integer(4) tallies(i) % filter(j) % n_bins** + + Number of bins for filter. + + **integer(4)/real(8) tallies(i) % filter(j) % bins(:)** + + Value for each filter bin of this type. + + **integer(4) tallies(i) % n_nuclide_bins** + + Number of nuclide bins. If none are specified, this is just one. + + *do j = 1, tallies(i) % n_nuclide_bins* + + **integer(4) tallies(i) % nuclide_bins(j)** + + Values of specified nuclide bins + + **integer(4) tallies(i) % n_score_bins** + + Number of scoring bins. + + *do j = 1, tallies(i) % n_score_bins* + + **integer(4) tallies(i) % score_bins(j)** + + Values of specified scoring bins (e.g. SCORE_FLUX). + + *do j = 1, tallies(i) % n_score_bins* + + **integer(4) tallies(i) % scatt_order(j)** + + Scattering Order specified scoring bins. + + **integer(4) tallies(i) % n_score_bins** + + Number of scoring bins without accounting for those added by + the scatter-pn command. + +**integer(4) n_realizations** + + Number of realizations for global tallies. + +**integer(4) N_GLOBAL_TALLIES** + + Number of global tally scores + +*do i = 1, N_GLOBAL_TALLIES* + + **real(8) global_tallies(i) % sum** + + Accumulated sum for the i-th global tally + + **real(8) global_tallies(i) % sum_sq** + + Accumulated sum of squares for the i-th global tally + +**integer(4) tallies_on** + + Flag indicated if tallies are present in the file. + +if (tallies_on > 0) + + *do i = 1, n_tallies* + + *do k = 1, size(tallies(i) % scores, 2)* + + *do j = 1, size(tallies(i) % scores, 1)* + + **real(8) tallies(i) % scores(j,k) % sum** + + Accumulated sum for the j-th score and k-th filter of the + i-th tally + + **real(8) tallies(i) % scores(j,k) % sum_sq** + + Accumulated sum of squares for the j-th score and k-th + filter of the i-th tally + +if (run_mode == MODE_EIGENVALUE) + + *do i = 1, n_particles* + + **real(8) source_bank(i) % wgt** + + Weight of the i-th source particle + + **real(8) source_bank(i) % xyz(1:3)** + + Coordinates of the i-th source particle. + + **real(8) source_bank(i) % uvw(1:3)** + + Direction of the i-th source particle + + **real(8) source_bank(i) % E** + + Energy of the i-th source particle. + ---------- Revision 8 ---------- From 0f95311f4766d0af59590f40af3f8ee80d359d57 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sun, 29 Sep 2013 15:48:37 -0400 Subject: [PATCH 128/192] clarified HDF5 installation --- docs/source/usersguide/install.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index aa257592b7..84f7633c2f 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -71,13 +71,15 @@ Prerequisites To compile with support for HDF5_ output (highly recommended), you will need to have HDF5 installed on your computer. The installed version will need to have been compiled with the same compiler you intend to compile - OpenMC with. HDF5_ must be built with parallel I/O features. An example - of configuring HDF5_ is listed below:: + OpenMC with. HDF5_ must be built with parallel I/O features if you intend + to use HDF5_ with MPI. An example of configuring HDF5_ is listed below:: FC=/opt/mpich/3.0.4-gnu/bin/mpif90 CC=/opt/mpich/3.0.4-gnu/bin/mpicc \ ./configure --prefix=/opt/hdf5/1.8.11-gnu --enable-fortran \ --enable-fortran2003 --enable-parallel + You may omit '--enable-parallel' if you want to compile HDF5_ in serial. + * PETSc_ for CMFD acceleration To enable CMFD acceleration, you will need to have PETSc_ (3.4.2 or higher) From 99d9118ecaf04d083fea2fccdfda1ee3fc35fe72 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sun, 29 Sep 2013 17:21:10 -0400 Subject: [PATCH 129/192] updated omp tests for all combinations --- tests/run_tests.py | 35 +++++++----- tests/test_compile/test_compile.py | 87 ++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 15 deletions(-) diff --git a/tests/run_tests.py b/tests/run_tests.py index 30d330e5d5..4edc59630b 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -67,12 +67,17 @@ sys.path.append(pwd) # Set list of tests, either default or from command line flags = [] -tests = ['compile', 'normal', 'debug', 'optimize', 'hdf5', 'hdf5-debug', - 'hdf5-optimize', 'mpi', 'mpi-debug', 'mpi-optimize', 'omp', - 'phdf5', 'phdf5-debug', 'phdf5-optimize', 'mpi-omp', 'omp-hdf5', - 'petsc', 'petsc-debug', 'petsc-optimize', 'phdf5-petsc', - 'phdf5-petsc-debug', 'phdf5-petsc-optimize', - 'omp-phdf5-petsc-optimize'] +tests = ['compile', 'normal', 'debug', 'optimize', + 'omp', 'omp-debug', 'omp-optimize', + 'hdf5', 'hdf5-debug', 'hdf5-optimize', + 'omp-hdf5', 'omp-hdf5-debug', 'omp-hdf5-optimize', + 'mpi', 'mpi-debug', 'mpi-optimize', + 'mpi-omp', 'mpi-omp-debug', 'mpi-omp-optimize', + 'phdf5', 'phdf5-debug', 'phdf5-optimize', + 'phdf5-omp', 'phdf5-omp-debug', 'phdf5-omp-optimize', + 'petsc', 'petsc-debug', 'petsc-optimize', + 'phdf5-petsc', 'phdf5-petsc-debug', 'phdf5-petsc-optimize', + 'omp-phdf5-petsc', 'omp-phdf5-petsc-debug', 'omp-phdf5-petsc-optimize'] if len(sys.argv) > 1: flags = [i for i in sys.argv[1:] if i.startswith('-')] tests_ = [i for i in sys.argv[1:] if not i.startswith('-')] @@ -117,13 +122,19 @@ results = [] for name in tests: if name == 'compile': run_compile() - elif name in ['normal', 'debug', 'optimize', 'omp' - 'hdf5', 'hdf5-debug', 'hdf5-optimize', 'omp-hdf5']: + elif name in ['normal', 'debug', 'optimize', + 'hdf5', 'hdf5-debug', 'hdf5-optimize', + 'omp', 'omp-debug', 'omp-optimize', + 'omp-hdf5', 'omp-hdf5-debug', 'omp-hdf5-optimize']: run_suite(name=name) - elif name in ['mpi', 'mpi-debug', 'mpi-optimize', 'mpi-omp', 'phdf5', - 'phdf5-debug', 'phdf5-optimize', 'petsc', 'petsc-debug', - 'petsc-optimize', 'phdf5-petsc', 'phdf5-petsc-debug', - 'phdf5-petsc-optimize', 'omp-phdf5-petsc-optimize']: + elif name in ['mpi', 'mpi-debug', 'mpi-optimize', + 'mpi-omp', 'mpi-omp-debug', 'mpi-omp-optimize', + 'phdf5', 'phdf5-debug', 'phdf5-optimize', + 'phdf5-omp', 'phdf5-omp-debug', 'phdf5-omp-optimize', + 'petsc', 'petsc-debug', 'petsc-optimize', + 'phdf5-petsc', 'phdf5-petsc-debug', 'phdf5-petsc-optimize', + 'omp-phdf5-petsc', 'omp-phdf5-petsc-debug', + 'omp-phdf5-petsc-optimize']: run_suite(name=name, mpi=True) # print out summary of results diff --git a/tests/test_compile/test_compile.py b/tests/test_compile/test_compile.py index 997f243d7b..ced2a86321 100644 --- a/tests/test_compile/test_compile.py +++ b/tests/test_compile/test_compile.py @@ -81,6 +81,20 @@ def test_omp(): shutil.move('openmc', 'openmc-omp') +def test_omp_debug(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'OPENMP=yes', 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-omp-debug') + + +def test_omp_optimize(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'OPENMP=yes', 'OPTIMIZE=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-omp-optimize') + + def test_hdf5(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'HDF5=yes']) @@ -102,6 +116,27 @@ def test_hdf5_optimize(): shutil.move('openmc', 'openmc-hdf5-optimize') +def test_omp_hdf5(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'OPENMP=yes', 'HDF5=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-omp-hdf5') + + +def test_omp_hdf5_debug(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'OPENMP=yes', 'HDF5=yes', 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-omp-hdf5-debug') + + +def test_omp_hdf5_optimize(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'OPENMP=yes', 'HDF5=yes', 'OPTIMIZE=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-omp-hdf5-optimize') + + def test_petsc(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'PETSC=yes']) @@ -131,11 +166,18 @@ def test_mpi_omp(): shutil.move('openmc', 'openmc-mpi-omp') -def test_omp_hdf5(): +def test_mpi_omp_debug(): returncode = run(['make', 'distclean']) - returncode = run(['make', compiler, 'MPI=yes', 'OPENMP=yes', 'HDF5=yes']) + returncode = run(['make', compiler, 'MPI=yes', 'OPENMP=yes', 'DEBUG=yes']) assert returncode == 0 - shutil.move('openmc', 'openmc-omp-hdf5') + shutil.move('openmc', 'openmc-mpi-omp-debug') + + +def test_mpi_omp_optimize(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'OPENMP=yes', 'OPTIMIZE=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-mpi-omp-optimize') def test_mpi_hdf5(): @@ -159,6 +201,29 @@ def test_mpi_hdf5_optimize(): shutil.move('openmc', 'openmc-phdf5-optimize') +def test_mpi_omp_hdf5(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'OPENMP=yes', 'HDF5=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-phdf5-omp') + + +def test_mpi_omp_hdf5_debug(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'OPENMP=yes', 'HDF5=yes', + 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-phdf5-omp-debug') + + +def test_mpi_omp_hdf5_optimize(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'OPENMP=yes', 'HDF5=yes', + 'OPTIMIZE=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-phdf5-omp-optimize') + + def test_mpi_hdf5_petsc(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'HDF5=yes', 'PETSC=yes']) @@ -182,6 +247,22 @@ def test_mpi_hdf5_petsc_optimize(): shutil.move('openmc', 'openmc-phdf5-petsc-optimize') +def test_mpi_omp_hdf5_petsc(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'OMP=yes', 'HDF5=yes', + 'PETSC=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-omp-phdf5-petsc') + + +def test_mpi_omp_hdf5_petsc_debug(): + returncode = run(['make', 'distclean']) + returncode = run(['make', compiler, 'MPI=yes', 'OMP=yes', 'HDF5=yes', + 'PETSC=yes', 'DEBUG=yes']) + assert returncode == 0 + shutil.move('openmc', 'openmc-omp-phdf5-petsc-debug') + + def test_mpi_omp_hdf5_petsc_optimize(): returncode = run(['make', 'distclean']) returncode = run(['make', compiler, 'MPI=yes', 'OMP=yes', 'HDF5=yes', From f1780faea2daf48744d356a41314e336d59ab214 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sun, 29 Sep 2013 17:31:44 -0400 Subject: [PATCH 130/192] dont add compile test when specifying all-omp --- tests/run_tests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run_tests.py b/tests/run_tests.py index 4edc59630b..cbb80d2686 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -112,6 +112,8 @@ if len(sys.argv) > 1: else: for j in tests: if j.rfind(suffix) != -1: + if suffix == 'omp' and j == 'compile': + continue tests__.append(j) else: tests__.append(i) # append specific test (e.g., mpi-debug) From be6a8d22ecb1c37151c86b380b84318848b7c15d Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 2 Oct 2013 21:21:05 -0400 Subject: [PATCH 131/192] 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 132/192] 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 c71ef57ddc4bf0dc5632fb259ccee8e56caa8855 Mon Sep 17 00:00:00 2001 From: Nick Horelik Date: Tue, 8 Oct 2013 17:58:56 -0400 Subject: [PATCH 133/192] Fixed bug in statepoint.py --- src/utils/statepoint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index 917ba19e18..df0629a2b2 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -309,7 +309,8 @@ class StatePoint(object): if self._hdf5: path = 'tallies/tally{0}/results'.format(i+1) data = self._f[path].value - t.results = np.dstack((data['sum'], data['sum_sq'])) + t.results = np.column_stack((data['sum'], data['sum_sq'])) + t.results.shape = (t.total_filter_bins, t.total_score_bins, 2) else: t.results = np.array(self._get_double(2*n)) t.results.shape = (t.total_filter_bins, t.total_score_bins, 2) From 260d051167ca60b896ab59c530a4d5f6f7f77c08 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 9 Oct 2013 16:13:59 -0400 Subject: [PATCH 134/192] changed dummy runtests to nosetests and fixed exception handling --- tests/run_tests.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/run_tests.py b/tests/run_tests.py index cbb80d2686..3da02c8998 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -22,7 +22,7 @@ def run_compile(): os.remove(exe) # run compile test - result = nose.run(argv=['run_tests.py', 'test_compile'] + flags) + result = nose.run(argv=['nosetests', 'test_compile'] + flags) if not result: print('Did not pass compile tests.') results.append(('compile', result)) @@ -35,7 +35,7 @@ def run_suite(name=None, mpi=False): # Set arguments list. Note that the first argument is a dummy argument (the # script name). It's not actually recursively calling run_tests.py - argv = ['run_tests.py', '--exclude', 'test_compile'] + flags + argv = ['nosetests', '--exclude', 'test_compile'] + flags # Add MPI plugin if set if mpi: @@ -48,11 +48,12 @@ def run_suite(name=None, mpi=False): os.chdir(pwd) os.rename(pwd + '/../src/openmc-' + name, pwd + '/../src/openmc') result = nose.run(argv=argv, addplugins=plugins) - finally: os.rename(pwd + '/../src/openmc', pwd + '/../src/openmc-' + name) - if not result: - print('Did not pass ' + name + ' tests') - results.append((name, result)) + except OSError: + result = False + if not result: + print('Did not pass ' + name + ' tests') + results.append((name, result)) # set mpiexec path if 'COMPILER' in os.environ: From 3f47b31784748f9aa5bdf325275cfe69416c37b0 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 9 Oct 2013 16:20:19 -0400 Subject: [PATCH 135/192] added more info if compiler is not found --- tests/run_tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run_tests.py b/tests/run_tests.py index 3da02c8998..ec0091f162 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -51,6 +51,7 @@ def run_suite(name=None, mpi=False): os.rename(pwd + '/../src/openmc', pwd + '/../src/openmc-' + name) except OSError: result = False + print('No OpenMC executable found for ' + name + ' tests') if not result: print('Did not pass ' + name + ' tests') results.append((name, result)) From 1bfbde7cf07625ab153b6b44e44cd3e389b53c84 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 9 Oct 2013 19:42:36 -0400 Subject: [PATCH 136/192] PEP8 fixes on Python scripts. --- tests/run_tests.py | 5 +++-- tests/test_cmfd_feed/results.py | 26 +++++++++++----------- tests/test_cmfd_feed/test_cmfd_feed.py | 11 ++++++--- tests/test_cmfd_jfnk/results.py | 26 +++++++++++----------- tests/test_cmfd_jfnk/test_cmfd_jfnk.py | 11 ++++++--- tests/test_cmfd_nofeed/results.py | 26 +++++++++++----------- tests/test_cmfd_nofeed/test_cmfd_nofeed.py | 11 ++++++--- 7 files changed, 66 insertions(+), 50 deletions(-) diff --git a/tests/run_tests.py b/tests/run_tests.py index ec0091f162..49975c218a 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -79,7 +79,8 @@ tests = ['compile', 'normal', 'debug', 'optimize', 'phdf5-omp', 'phdf5-omp-debug', 'phdf5-omp-optimize', 'petsc', 'petsc-debug', 'petsc-optimize', 'phdf5-petsc', 'phdf5-petsc-debug', 'phdf5-petsc-optimize', - 'omp-phdf5-petsc', 'omp-phdf5-petsc-debug', 'omp-phdf5-petsc-optimize'] + 'omp-phdf5-petsc', 'omp-phdf5-petsc-debug', + 'omp-phdf5-petsc-optimize'] if len(sys.argv) > 1: flags = [i for i in sys.argv[1:] if i.startswith('-')] tests_ = [i for i in sys.argv[1:] if not i.startswith('-')] @@ -138,7 +139,7 @@ for name in tests: 'petsc', 'petsc-debug', 'petsc-optimize', 'phdf5-petsc', 'phdf5-petsc-debug', 'phdf5-petsc-optimize', 'omp-phdf5-petsc', 'omp-phdf5-petsc-debug', - 'omp-phdf5-petsc-optimize']: + 'omp-phdf5-petsc-optimize']: run_suite(name=name, mpi=True) # print out summary of results diff --git a/tests/test_cmfd_feed/results.py b/tests/test_cmfd_feed/results.py index f86aaa53e3..26bcb36d9f 100644 --- a/tests/test_cmfd_feed/results.py +++ b/tests/test_cmfd_feed/results.py @@ -34,7 +34,7 @@ results4 = np.reshape(results4, size4) # set up output string outstr = '' - + # write out k-combined outstr += 'k-combined:\n' outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1]) @@ -42,41 +42,41 @@ outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1]) # write out tally results outstr += 'tally 1:\n' for item in results1: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 2:\n' for item in results2: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 3:\n' for item in results3: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 4:\n' for item in results4: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) # write out cmfd answers outstr += 'cmfd indices\n' for item in sp.cmfd_indices: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'k cmfd\n' for item in sp.k_cmfd: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd entropy\n' for item in sp.cmfd_entropy: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd balance\n' for item in sp.cmfd_balance: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd dominance ratio\n' for item in sp.cmfd_dominance: - outstr += "{0:10.3E}\n".format(item) + outstr += "{0:10.3E}\n".format(item) outstr += 'cmfd openmc source comparison\n' for item in sp.cmfd_srccmp: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd source\n' cmfdsrc = np.reshape(sp.cmfd_src, np.product(sp.cmfd_indices), order='F') for item in cmfdsrc: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) # write results to file -with open('results_test.dat','w') as fh: +with open('results_test.dat', 'w') as fh: fh.write(outstr) diff --git a/tests/test_cmfd_feed/test_cmfd_feed.py b/tests/test_cmfd_feed/test_cmfd_feed.py index db36784888..59bcad866b 100644 --- a/tests/test_cmfd_feed/test_cmfd_feed.py +++ b/tests/test_cmfd_feed/test_cmfd_feed.py @@ -14,17 +14,19 @@ pwd = os.path.dirname(__file__) skipAll = False + def setup(): os.putenv('PWD', pwd) os.chdir(pwd) global skipAll skipAll = False + 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() @@ -36,6 +38,7 @@ def test_run(): raise SkipTest assert returncode == 0 + def test_created_statepoint(): if skipAll: raise SkipTest @@ -43,11 +46,13 @@ def test_created_statepoint(): assert len(statepoint) == 1 assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + def test_output_exists(): if skipAll: raise SkipTest assert os.path.exists(pwd + '/tallies.out') + def test_results(): if skipAll: raise SkipTest @@ -55,9 +60,10 @@ def test_results(): 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') + os.rename('results_test.dat', 'results_error.dat') assert compare + def teardown(): output = glob.glob(pwd + '/statepoint.20.*') output.append(pwd + '/tallies.out') @@ -65,4 +71,3 @@ def teardown(): for f in output: if os.path.exists(f): os.remove(f) - diff --git a/tests/test_cmfd_jfnk/results.py b/tests/test_cmfd_jfnk/results.py index f86aaa53e3..26bcb36d9f 100644 --- a/tests/test_cmfd_jfnk/results.py +++ b/tests/test_cmfd_jfnk/results.py @@ -34,7 +34,7 @@ results4 = np.reshape(results4, size4) # set up output string outstr = '' - + # write out k-combined outstr += 'k-combined:\n' outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1]) @@ -42,41 +42,41 @@ outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1]) # write out tally results outstr += 'tally 1:\n' for item in results1: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 2:\n' for item in results2: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 3:\n' for item in results3: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 4:\n' for item in results4: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) # write out cmfd answers outstr += 'cmfd indices\n' for item in sp.cmfd_indices: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'k cmfd\n' for item in sp.k_cmfd: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd entropy\n' for item in sp.cmfd_entropy: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd balance\n' for item in sp.cmfd_balance: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd dominance ratio\n' for item in sp.cmfd_dominance: - outstr += "{0:10.3E}\n".format(item) + outstr += "{0:10.3E}\n".format(item) outstr += 'cmfd openmc source comparison\n' for item in sp.cmfd_srccmp: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd source\n' cmfdsrc = np.reshape(sp.cmfd_src, np.product(sp.cmfd_indices), order='F') for item in cmfdsrc: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) # write results to file -with open('results_test.dat','w') as fh: +with open('results_test.dat', 'w') as fh: fh.write(outstr) diff --git a/tests/test_cmfd_jfnk/test_cmfd_jfnk.py b/tests/test_cmfd_jfnk/test_cmfd_jfnk.py index db36784888..59bcad866b 100644 --- a/tests/test_cmfd_jfnk/test_cmfd_jfnk.py +++ b/tests/test_cmfd_jfnk/test_cmfd_jfnk.py @@ -14,17 +14,19 @@ pwd = os.path.dirname(__file__) skipAll = False + def setup(): os.putenv('PWD', pwd) os.chdir(pwd) global skipAll skipAll = False + 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() @@ -36,6 +38,7 @@ def test_run(): raise SkipTest assert returncode == 0 + def test_created_statepoint(): if skipAll: raise SkipTest @@ -43,11 +46,13 @@ def test_created_statepoint(): assert len(statepoint) == 1 assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + def test_output_exists(): if skipAll: raise SkipTest assert os.path.exists(pwd + '/tallies.out') + def test_results(): if skipAll: raise SkipTest @@ -55,9 +60,10 @@ def test_results(): 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') + os.rename('results_test.dat', 'results_error.dat') assert compare + def teardown(): output = glob.glob(pwd + '/statepoint.20.*') output.append(pwd + '/tallies.out') @@ -65,4 +71,3 @@ def teardown(): for f in output: if os.path.exists(f): os.remove(f) - diff --git a/tests/test_cmfd_nofeed/results.py b/tests/test_cmfd_nofeed/results.py index f86aaa53e3..26bcb36d9f 100644 --- a/tests/test_cmfd_nofeed/results.py +++ b/tests/test_cmfd_nofeed/results.py @@ -34,7 +34,7 @@ results4 = np.reshape(results4, size4) # set up output string outstr = '' - + # write out k-combined outstr += 'k-combined:\n' outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1]) @@ -42,41 +42,41 @@ outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1]) # write out tally results outstr += 'tally 1:\n' for item in results1: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 2:\n' for item in results2: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 3:\n' for item in results3: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'tally 4:\n' for item in results4: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) # write out cmfd answers outstr += 'cmfd indices\n' for item in sp.cmfd_indices: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'k cmfd\n' for item in sp.k_cmfd: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd entropy\n' for item in sp.cmfd_entropy: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd balance\n' for item in sp.cmfd_balance: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd dominance ratio\n' for item in sp.cmfd_dominance: - outstr += "{0:10.3E}\n".format(item) + outstr += "{0:10.3E}\n".format(item) outstr += 'cmfd openmc source comparison\n' for item in sp.cmfd_srccmp: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) outstr += 'cmfd source\n' cmfdsrc = np.reshape(sp.cmfd_src, np.product(sp.cmfd_indices), order='F') for item in cmfdsrc: - outstr += "{0:12.6E}\n".format(item) + outstr += "{0:12.6E}\n".format(item) # write results to file -with open('results_test.dat','w') as fh: +with open('results_test.dat', 'w') as fh: fh.write(outstr) diff --git a/tests/test_cmfd_nofeed/test_cmfd_nofeed.py b/tests/test_cmfd_nofeed/test_cmfd_nofeed.py index feb4a0657e..07176a8f2e 100644 --- a/tests/test_cmfd_nofeed/test_cmfd_nofeed.py +++ b/tests/test_cmfd_nofeed/test_cmfd_nofeed.py @@ -13,17 +13,19 @@ pwd = os.path.dirname(__file__) skipAll = False + def setup(): os.putenv('PWD', pwd) os.chdir(pwd) global skipAll skipAll = False + 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() @@ -35,6 +37,7 @@ def test_run(): raise SkipTest assert returncode == 0 + def test_created_statepoint(): if skipAll: raise SkipTest @@ -42,11 +45,13 @@ def test_created_statepoint(): assert len(statepoint) == 1 assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + def test_output_exists(): if skipAll: raise SkipTest assert os.path.exists(pwd + '/tallies.out') + def test_results(): if skipAll: raise SkipTest @@ -54,9 +59,10 @@ def test_results(): 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') + os.rename('results_test.dat', 'results_error.dat') assert compare + def teardown(): output = glob.glob(pwd + '/statepoint.20.*') output.append(pwd + '/tallies.out') @@ -64,4 +70,3 @@ def teardown(): for f in output: if os.path.exists(f): os.remove(f) - From 241b6e567c4b920c30a96c321d3c4801255c9088 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 9 Oct 2013 21:20:55 -0400 Subject: [PATCH 137/192] residual vector should be intent inout --- src/cmfd_jfnk_solver.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmfd_jfnk_solver.F90 b/src/cmfd_jfnk_solver.F90 index 958a4c3e56..7bd21a8758 100644 --- a/src/cmfd_jfnk_solver.F90 +++ b/src/cmfd_jfnk_solver.F90 @@ -267,8 +267,8 @@ contains use global, only: cmfd_write_matrices - type(Vector), intent(in) :: x ! solution vector - type(Vector), intent(out) :: res ! residual vector + type(Vector), intent(in) :: x ! solution vector + type(Vector), intent(inout) :: res ! residual vector character(len=25) :: filename integer :: n From f7aacaf0a7054b0b2b63bf405f79650e269a31c1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 9 Oct 2013 21:31:13 -0400 Subject: [PATCH 138/192] Change intent in res_interface to match previous commit. --- src/solver_interface.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_interface.F90 b/src/solver_interface.F90 index 113e3119af..b629a0789f 100644 --- a/src/solver_interface.F90 +++ b/src/solver_interface.F90 @@ -56,8 +56,8 @@ module solver_interface abstract interface subroutine res_interface(x, res) import :: Vector - type(Vector), intent(in) :: x ! solution vector - type(Vector), intent(out) :: res ! residual vector + type(Vector), intent(in) :: x ! solution vector + type(Vector), intent(inout) :: res ! residual vector end subroutine res_interface subroutine jac_interface(x) From bfc172f26485487bd780a37d233d0f4961df4bca Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 10 Oct 2013 00:02:26 -0400 Subject: [PATCH 139/192] 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 140/192] 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 141/192] 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 142/192] 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 9025c729c720fef5511a96205f1af079b700be07 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 16 Oct 2013 23:03:40 -0400 Subject: [PATCH 143/192] removed Popen wait and use communicate then get return code --- tests/test_basic/test_basic.py | 2 +- tests/test_cmfd_feed/test_cmfd_feed.py | 2 +- tests/test_cmfd_jfnk/test_cmfd_jfnk.py | 2 +- tests/test_cmfd_nofeed/test_cmfd_nofeed.py | 2 +- tests/test_compile/test_compile.py | 2 +- .../test_confidence_intervals.py | 2 +- tests/test_density_atombcm/test_density_atombcm.py | 2 +- tests/test_density_atomcm3/test_density_atomcm3.py | 2 +- tests/test_density_kgm3/test_density_kgm3.py | 2 +- tests/test_density_sum/test_density_sum.py | 2 +- .../test_eigenvalue_genperbatch.py | 2 +- .../test_eigenvalue_no_inactive.py | 2 +- tests/test_energy_grid/test_energy_grid.py | 2 +- tests/test_entropy/test_entropy.py | 2 +- tests/test_filter_cell/test_filter_cell.py | 2 +- tests/test_filter_cellborn/test_filter_cellborn.py | 2 +- tests/test_filter_energy/test_filter_energy.py | 2 +- tests/test_filter_energyout/test_filter_energyout.py | 2 +- .../test_filter_group_transfer.py | 2 +- tests/test_filter_material/test_filter_material.py | 2 +- tests/test_filter_mesh_2d/test_filter_mesh_2d.py | 2 +- tests/test_filter_mesh_3d/test_filter_mesh_3d.py | 2 +- tests/test_filter_universe/test_filter_universe.py | 2 +- tests/test_fixed_source/test_fixed_source.py | 2 +- tests/test_lattice/test_lattice.py | 2 +- tests/test_lattice_multiple/test_lattice_multiple.py | 2 +- tests/test_natural_element/test_natural_element.py | 2 +- tests/test_output/test_output.py | 2 +- tests/test_plot_background/test_plot_background.py | 2 +- tests/test_plot_basis/test_plot_basis.py | 2 +- tests/test_plot_colspec/test_plot_colspec.py | 2 +- tests/test_plot_mask/test_plot_mask.py | 2 +- tests/test_ptables_off/test_ptables_off.py | 2 +- tests/test_reflective_cone/test_reflective_cone.py | 2 +- .../test_reflective_cylinder/test_reflective_cylinder.py | 2 +- tests/test_reflective_plane/test_reflective_plane.py | 2 +- tests/test_reflective_sphere/test_reflective_sphere.py | 2 +- tests/test_rotation/test_rotation.py | 2 +- tests/test_salphabeta/test_salphabeta.py | 2 +- .../test_salphabeta_multiple/test_salphabeta_multiple.py | 2 +- tests/test_score_MT/test_score_MT.py | 2 +- tests/test_score_absorption/test_score_absorption.py | 2 +- tests/test_score_current/test_score_current.py | 2 +- tests/test_score_events/test_score_events.py | 2 +- tests/test_score_fission/test_score_fission.py | 2 +- tests/test_score_flux/test_score_flux.py | 2 +- tests/test_score_kappafission/test_score_kappafission.py | 2 +- tests/test_score_nufission/test_score_nufission.py | 2 +- tests/test_score_nuscatter/test_score_nuscatter.py | 2 +- tests/test_score_scatter/test_score_scatter.py | 2 +- tests/test_score_scatter_n/test_score_scatter_n.py | 2 +- tests/test_score_scatter_pn/test_score_scatter_pn.py | 2 +- tests/test_score_total/test_score_total.py | 2 +- tests/test_seed/test_seed.py | 2 +- tests/test_source_angle_mono/test_source_angle_mono.py | 2 +- .../test_source_energy_maxwell.py | 2 +- tests/test_source_energy_mono/test_source_energy_mono.py | 2 +- tests/test_source_point/test_source_point.py | 2 +- tests/test_statepoint_batch/test_statepoint_batch.py | 2 +- .../test_statepoint_interval/test_statepoint_interval.py | 2 +- tests/test_statepoint_restart/test_statepoint_restart.py | 8 ++++---- .../test_statepoint_sourcesep.py | 2 +- tests/test_survival_biasing/test_survival_biasing.py | 2 +- tests/test_tally_assumesep/test_tally_assumesep.py | 2 +- tests/test_trace/test_trace.py | 2 +- tests/test_translation/test_translation.py | 2 +- tests/test_uniform_fs/test_uniform_fs.py | 2 +- tests/test_universe/test_universe.py | 2 +- tests/test_void/test_void.py | 2 +- 69 files changed, 72 insertions(+), 72 deletions(-) diff --git a/tests/test_basic/test_basic.py b/tests/test_basic/test_basic.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_basic/test_basic.py +++ b/tests/test_basic/test_basic.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_cmfd_feed/test_cmfd_feed.py b/tests/test_cmfd_feed/test_cmfd_feed.py index 59bcad866b..1388ca85a5 100644 --- a/tests/test_cmfd_feed/test_cmfd_feed.py +++ b/tests/test_cmfd_feed/test_cmfd_feed.py @@ -29,9 +29,9 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() output = proc.communicate()[0] print(output) + returncode = proc.returncode if 'CMFD is not available' in output: global skipAll skipAll = True diff --git a/tests/test_cmfd_jfnk/test_cmfd_jfnk.py b/tests/test_cmfd_jfnk/test_cmfd_jfnk.py index 59bcad866b..1388ca85a5 100644 --- a/tests/test_cmfd_jfnk/test_cmfd_jfnk.py +++ b/tests/test_cmfd_jfnk/test_cmfd_jfnk.py @@ -29,9 +29,9 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() output = proc.communicate()[0] print(output) + returncode = proc.returncode if 'CMFD is not available' in output: global skipAll skipAll = True diff --git a/tests/test_cmfd_nofeed/test_cmfd_nofeed.py b/tests/test_cmfd_nofeed/test_cmfd_nofeed.py index 07176a8f2e..be7ec194a5 100644 --- a/tests/test_cmfd_nofeed/test_cmfd_nofeed.py +++ b/tests/test_cmfd_nofeed/test_cmfd_nofeed.py @@ -28,9 +28,9 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() output = proc.communicate()[0] print(output) + returncode = proc.returncode if 'CMFD is not available' in output: global skipAll skipAll = True diff --git a/tests/test_compile/test_compile.py b/tests/test_compile/test_compile.py index ced2a86321..8d411abd91 100644 --- a/tests/test_compile/test_compile.py +++ b/tests/test_compile/test_compile.py @@ -273,8 +273,8 @@ def test_mpi_omp_hdf5_petsc_optimize(): def run(commands): proc = Popen(commands, stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode return returncode diff --git a/tests/test_confidence_intervals/test_confidence_intervals.py b/tests/test_confidence_intervals/test_confidence_intervals.py index a0dda8b468..a8372834f7 100644 --- a/tests/test_confidence_intervals/test_confidence_intervals.py +++ b/tests/test_confidence_intervals/test_confidence_intervals.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_density_atombcm/test_density_atombcm.py b/tests/test_density_atombcm/test_density_atombcm.py index d13f404722..2fde1e6312 100644 --- a/tests/test_density_atombcm/test_density_atombcm.py +++ b/tests/test_density_atombcm/test_density_atombcm.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_density_atomcm3/test_density_atomcm3.py b/tests/test_density_atomcm3/test_density_atomcm3.py index 860f858d34..74a644b59d 100644 --- a/tests/test_density_atomcm3/test_density_atomcm3.py +++ b/tests/test_density_atomcm3/test_density_atomcm3.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_density_kgm3/test_density_kgm3.py b/tests/test_density_kgm3/test_density_kgm3.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_density_kgm3/test_density_kgm3.py +++ b/tests/test_density_kgm3/test_density_kgm3.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_density_sum/test_density_sum.py b/tests/test_density_sum/test_density_sum.py index d1bc7d3a69..fcafcf0f1f 100644 --- a/tests/test_density_sum/test_density_sum.py +++ b/tests/test_density_sum/test_density_sum.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_eigenvalue_genperbatch/test_eigenvalue_genperbatch.py b/tests/test_eigenvalue_genperbatch/test_eigenvalue_genperbatch.py index eb4e206c1b..24077bd591 100644 --- a/tests/test_eigenvalue_genperbatch/test_eigenvalue_genperbatch.py +++ b/tests/test_eigenvalue_genperbatch/test_eigenvalue_genperbatch.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_eigenvalue_no_inactive/test_eigenvalue_no_inactive.py b/tests/test_eigenvalue_no_inactive/test_eigenvalue_no_inactive.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_eigenvalue_no_inactive/test_eigenvalue_no_inactive.py +++ b/tests/test_eigenvalue_no_inactive/test_eigenvalue_no_inactive.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_energy_grid/test_energy_grid.py b/tests/test_energy_grid/test_energy_grid.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_energy_grid/test_energy_grid.py +++ b/tests/test_energy_grid/test_energy_grid.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_entropy/test_entropy.py b/tests/test_entropy/test_entropy.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_entropy/test_entropy.py +++ b/tests/test_entropy/test_entropy.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_cell/test_filter_cell.py b/tests/test_filter_cell/test_filter_cell.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_cell/test_filter_cell.py +++ b/tests/test_filter_cell/test_filter_cell.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_cellborn/test_filter_cellborn.py b/tests/test_filter_cellborn/test_filter_cellborn.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_cellborn/test_filter_cellborn.py +++ b/tests/test_filter_cellborn/test_filter_cellborn.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_energy/test_filter_energy.py b/tests/test_filter_energy/test_filter_energy.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_energy/test_filter_energy.py +++ b/tests/test_filter_energy/test_filter_energy.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_energyout/test_filter_energyout.py b/tests/test_filter_energyout/test_filter_energyout.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_energyout/test_filter_energyout.py +++ b/tests/test_filter_energyout/test_filter_energyout.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_group_transfer/test_filter_group_transfer.py b/tests/test_filter_group_transfer/test_filter_group_transfer.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_group_transfer/test_filter_group_transfer.py +++ b/tests/test_filter_group_transfer/test_filter_group_transfer.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_material/test_filter_material.py b/tests/test_filter_material/test_filter_material.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_material/test_filter_material.py +++ b/tests/test_filter_material/test_filter_material.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_mesh_2d/test_filter_mesh_2d.py b/tests/test_filter_mesh_2d/test_filter_mesh_2d.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_mesh_2d/test_filter_mesh_2d.py +++ b/tests/test_filter_mesh_2d/test_filter_mesh_2d.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_mesh_3d/test_filter_mesh_3d.py b/tests/test_filter_mesh_3d/test_filter_mesh_3d.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_mesh_3d/test_filter_mesh_3d.py +++ b/tests/test_filter_mesh_3d/test_filter_mesh_3d.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_filter_universe/test_filter_universe.py b/tests/test_filter_universe/test_filter_universe.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_filter_universe/test_filter_universe.py +++ b/tests/test_filter_universe/test_filter_universe.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_fixed_source/test_fixed_source.py b/tests/test_fixed_source/test_fixed_source.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_fixed_source/test_fixed_source.py +++ b/tests/test_fixed_source/test_fixed_source.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_lattice/test_lattice.py b/tests/test_lattice/test_lattice.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_lattice/test_lattice.py +++ b/tests/test_lattice/test_lattice.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_lattice_multiple/test_lattice_multiple.py b/tests/test_lattice_multiple/test_lattice_multiple.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_lattice_multiple/test_lattice_multiple.py +++ b/tests/test_lattice_multiple/test_lattice_multiple.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_natural_element/test_natural_element.py b/tests/test_natural_element/test_natural_element.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_natural_element/test_natural_element.py +++ b/tests/test_natural_element/test_natural_element.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_output/test_output.py b/tests/test_output/test_output.py index 9024d6fb19..dc48dc37b9 100644 --- a/tests/test_output/test_output.py +++ b/tests/test_output/test_output.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_summary_exists(): diff --git a/tests/test_plot_background/test_plot_background.py b/tests/test_plot_background/test_plot_background.py index a34b5bbfb7..d8fc18f755 100644 --- a/tests/test_plot_background/test_plot_background.py +++ b/tests/test_plot_background/test_plot_background.py @@ -17,8 +17,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path, '-p'], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_plot_exists(): diff --git a/tests/test_plot_basis/test_plot_basis.py b/tests/test_plot_basis/test_plot_basis.py index fe0a13ccac..86a4159200 100644 --- a/tests/test_plot_basis/test_plot_basis.py +++ b/tests/test_plot_basis/test_plot_basis.py @@ -17,8 +17,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path, '-p'], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_plots_exists(): diff --git a/tests/test_plot_colspec/test_plot_colspec.py b/tests/test_plot_colspec/test_plot_colspec.py index a34b5bbfb7..d8fc18f755 100644 --- a/tests/test_plot_colspec/test_plot_colspec.py +++ b/tests/test_plot_colspec/test_plot_colspec.py @@ -17,8 +17,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path, '-p'], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_plot_exists(): diff --git a/tests/test_plot_mask/test_plot_mask.py b/tests/test_plot_mask/test_plot_mask.py index fe0a13ccac..86a4159200 100644 --- a/tests/test_plot_mask/test_plot_mask.py +++ b/tests/test_plot_mask/test_plot_mask.py @@ -17,8 +17,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path, '-p'], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_plots_exists(): diff --git a/tests/test_ptables_off/test_ptables_off.py b/tests/test_ptables_off/test_ptables_off.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_ptables_off/test_ptables_off.py +++ b/tests/test_ptables_off/test_ptables_off.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_reflective_cone/test_reflective_cone.py b/tests/test_reflective_cone/test_reflective_cone.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_reflective_cone/test_reflective_cone.py +++ b/tests/test_reflective_cone/test_reflective_cone.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_reflective_cylinder/test_reflective_cylinder.py b/tests/test_reflective_cylinder/test_reflective_cylinder.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_reflective_cylinder/test_reflective_cylinder.py +++ b/tests/test_reflective_cylinder/test_reflective_cylinder.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_reflective_plane/test_reflective_plane.py b/tests/test_reflective_plane/test_reflective_plane.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_reflective_plane/test_reflective_plane.py +++ b/tests/test_reflective_plane/test_reflective_plane.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_reflective_sphere/test_reflective_sphere.py b/tests/test_reflective_sphere/test_reflective_sphere.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_reflective_sphere/test_reflective_sphere.py +++ b/tests/test_reflective_sphere/test_reflective_sphere.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_rotation/test_rotation.py b/tests/test_rotation/test_rotation.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_rotation/test_rotation.py +++ b/tests/test_rotation/test_rotation.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_salphabeta/test_salphabeta.py b/tests/test_salphabeta/test_salphabeta.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_salphabeta/test_salphabeta.py +++ b/tests/test_salphabeta/test_salphabeta.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_salphabeta_multiple/test_salphabeta_multiple.py b/tests/test_salphabeta_multiple/test_salphabeta_multiple.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_salphabeta_multiple/test_salphabeta_multiple.py +++ b/tests/test_salphabeta_multiple/test_salphabeta_multiple.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_MT/test_score_MT.py b/tests/test_score_MT/test_score_MT.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_MT/test_score_MT.py +++ b/tests/test_score_MT/test_score_MT.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_absorption/test_score_absorption.py b/tests/test_score_absorption/test_score_absorption.py index b49faa8177..335d649dd7 100644 --- a/tests/test_score_absorption/test_score_absorption.py +++ b/tests/test_score_absorption/test_score_absorption.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_current/test_score_current.py b/tests/test_score_current/test_score_current.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_current/test_score_current.py +++ b/tests/test_score_current/test_score_current.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_events/test_score_events.py b/tests/test_score_events/test_score_events.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_events/test_score_events.py +++ b/tests/test_score_events/test_score_events.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_fission/test_score_fission.py b/tests/test_score_fission/test_score_fission.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_fission/test_score_fission.py +++ b/tests/test_score_fission/test_score_fission.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_flux/test_score_flux.py b/tests/test_score_flux/test_score_flux.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_flux/test_score_flux.py +++ b/tests/test_score_flux/test_score_flux.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_kappafission/test_score_kappafission.py b/tests/test_score_kappafission/test_score_kappafission.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_kappafission/test_score_kappafission.py +++ b/tests/test_score_kappafission/test_score_kappafission.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_nufission/test_score_nufission.py b/tests/test_score_nufission/test_score_nufission.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_nufission/test_score_nufission.py +++ b/tests/test_score_nufission/test_score_nufission.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_nuscatter/test_score_nuscatter.py b/tests/test_score_nuscatter/test_score_nuscatter.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_nuscatter/test_score_nuscatter.py +++ b/tests/test_score_nuscatter/test_score_nuscatter.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_scatter/test_score_scatter.py b/tests/test_score_scatter/test_score_scatter.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_scatter/test_score_scatter.py +++ b/tests/test_score_scatter/test_score_scatter.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_scatter_n/test_score_scatter_n.py b/tests/test_score_scatter_n/test_score_scatter_n.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_scatter_n/test_score_scatter_n.py +++ b/tests/test_score_scatter_n/test_score_scatter_n.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_scatter_pn/test_score_scatter_pn.py b/tests/test_score_scatter_pn/test_score_scatter_pn.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_scatter_pn/test_score_scatter_pn.py +++ b/tests/test_score_scatter_pn/test_score_scatter_pn.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_score_total/test_score_total.py b/tests/test_score_total/test_score_total.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_score_total/test_score_total.py +++ b/tests/test_score_total/test_score_total.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_seed/test_seed.py b/tests/test_seed/test_seed.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_seed/test_seed.py +++ b/tests/test_seed/test_seed.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_source_angle_mono/test_source_angle_mono.py b/tests/test_source_angle_mono/test_source_angle_mono.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_source_angle_mono/test_source_angle_mono.py +++ b/tests/test_source_angle_mono/test_source_angle_mono.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_source_energy_maxwell/test_source_energy_maxwell.py b/tests/test_source_energy_maxwell/test_source_energy_maxwell.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_source_energy_maxwell/test_source_energy_maxwell.py +++ b/tests/test_source_energy_maxwell/test_source_energy_maxwell.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_source_energy_mono/test_source_energy_mono.py b/tests/test_source_energy_mono/test_source_energy_mono.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_source_energy_mono/test_source_energy_mono.py +++ b/tests/test_source_energy_mono/test_source_energy_mono.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_source_point/test_source_point.py b/tests/test_source_point/test_source_point.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_source_point/test_source_point.py +++ b/tests/test_source_point/test_source_point.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_statepoint_batch/test_statepoint_batch.py b/tests/test_statepoint_batch/test_statepoint_batch.py index 57d00dbe8f..ed4f7b2a7a 100644 --- a/tests/test_statepoint_batch/test_statepoint_batch.py +++ b/tests/test_statepoint_batch/test_statepoint_batch.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_statepoints_exist(): diff --git a/tests/test_statepoint_interval/test_statepoint_interval.py b/tests/test_statepoint_interval/test_statepoint_interval.py index 5c3c87fbc5..263ab37907 100644 --- a/tests/test_statepoint_interval/test_statepoint_interval.py +++ b/tests/test_statepoint_interval/test_statepoint_interval.py @@ -20,8 +20,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_statepoints_exist(): diff --git a/tests/test_statepoint_restart/test_statepoint_restart.py b/tests/test_statepoint_restart/test_statepoint_restart.py index 7a880d59b0..544c9197ec 100644 --- a/tests/test_statepoint_restart/test_statepoint_restart.py +++ b/tests/test_statepoint_restart/test_statepoint_restart.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): @@ -45,8 +45,8 @@ def test_restart_form1(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path, '-r', statepoint[0]], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint_form1(): @@ -72,8 +72,8 @@ def test_restart_form2(): else: proc = Popen([openmc_path, '--restart', statepoint[0]], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint_form2(): @@ -94,8 +94,8 @@ def test_restart_serial(): openmc_path = pwd + '/../../src/openmc' proc = Popen([openmc_path, '--restart', statepoint[0]], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint_serial(): diff --git a/tests/test_statepoint_sourcesep/test_statepoint_sourcesep.py b/tests/test_statepoint_sourcesep/test_statepoint_sourcesep.py index 61191c57ae..ca1c9966a4 100644 --- a/tests/test_statepoint_sourcesep/test_statepoint_sourcesep.py +++ b/tests/test_statepoint_sourcesep/test_statepoint_sourcesep.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_statepoint_exists(): diff --git a/tests/test_survival_biasing/test_survival_biasing.py b/tests/test_survival_biasing/test_survival_biasing.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_survival_biasing/test_survival_biasing.py +++ b/tests/test_survival_biasing/test_survival_biasing.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_tally_assumesep/test_tally_assumesep.py b/tests/test_tally_assumesep/test_tally_assumesep.py index 62c4d7316a..151df366bd 100644 --- a/tests/test_tally_assumesep/test_tally_assumesep.py +++ b/tests/test_tally_assumesep/test_tally_assumesep.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_trace/test_trace.py b/tests/test_trace/test_trace.py index d618dfc5e8..a518ab7731 100644 --- a/tests/test_trace/test_trace.py +++ b/tests/test_trace/test_trace.py @@ -19,9 +19,9 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() stdout = proc.communicate()[0] print(stdout) + returncode = proc.returncode assert returncode == 0 assert stdout.find('Simulating Particle 453') != -1 diff --git a/tests/test_translation/test_translation.py b/tests/test_translation/test_translation.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_translation/test_translation.py +++ b/tests/test_translation/test_translation.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_uniform_fs/test_uniform_fs.py b/tests/test_uniform_fs/test_uniform_fs.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_uniform_fs/test_uniform_fs.py +++ b/tests/test_uniform_fs/test_uniform_fs.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_universe/test_universe.py b/tests/test_universe/test_universe.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_universe/test_universe.py +++ b/tests/test_universe/test_universe.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): diff --git a/tests/test_void/test_void.py b/tests/test_void/test_void.py index fe8344c15c..64d20b6115 100644 --- a/tests/test_void/test_void.py +++ b/tests/test_void/test_void.py @@ -19,8 +19,8 @@ def test_run(): stderr=STDOUT, stdout=PIPE) else: proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) - returncode = proc.wait() print(proc.communicate()[0]) + returncode = proc.returncode assert returncode == 0 def test_created_statepoint(): From 59b703a5a799198ab5136907269f0ef7b9412ede Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 17 Oct 2013 08:49:50 -0400 Subject: [PATCH 144/192] bumped version to 0.5.3 --- src/constants.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants.F90 b/src/constants.F90 index 5587722869..e9999c0bdd 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -8,7 +8,7 @@ module constants ! OpenMC major, minor, and release numbers integer, parameter :: VERSION_MAJOR = 0 integer, parameter :: VERSION_MINOR = 5 - integer, parameter :: VERSION_RELEASE = 2 + integer, parameter :: VERSION_RELEASE = 3 ! Revision numbers for binary files integer, parameter :: REVISION_STATEPOINT = 10 From 78865f2f67c89d89181112bde1e52f577b98f394 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 21 Oct 2013 16:32:22 -0400 Subject: [PATCH 145/192] when loading statepoints, file should be opened in parallel --- src/output_interface.F90 | 6 ++++++ src/state_point.F90 | 17 +---------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/output_interface.F90 b/src/output_interface.F90 index 8137fb63d8..f7b342e506 100644 --- a/src/output_interface.F90 +++ b/src/output_interface.F90 @@ -1644,6 +1644,12 @@ contains call h5dclose_f(dset, hdf5_err) if (present(group)) call hdf5_close_group(self % hdf5_grp) +# elif MPI + + ! Write out tally buffer + call MPI_FILE_READ(self % unit_fh, buffer, n1*n2, MPI_TALLYRESULT, & + MPI_STATUS_IGNORE, mpiio_err) + #else ! Read tally result diff --git a/src/state_point.F90 b/src/state_point.F90 index 9ad9b8d9f2..3793923bb2 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -474,7 +474,7 @@ contains call write_message(1) ! Open file for reading - call sp % file_open(path_state_point, 'r') + call sp % file_open(path_state_point, 'r', serial = .false.) ! Read filetype call sp % read_data(int_array(1), "filetype") @@ -705,13 +705,6 @@ contains end do TALLY_RESULTS end if - -#ifdef MPI - ! If using MPI, file needs to be closed and reopened in parallel - ! If serial, we cannot close the file or we will lose our file position - call sp % file_close() -# endif - end if ! Read source if in eigenvalue mode @@ -739,14 +732,6 @@ contains ! Open source file call sp % file_open(filename, 'r', serial = .false.) - else - -#ifdef MPI - ! Reopen statepoint file in parallel, but only if MPI - ! We will compute the position where the source begins - call sp % file_open(path_state_point, 'r', serial = .false.) -#endif - end if ! Write out source From 4edef2dd2d7bd7714e1a03de8af00929f1e36f7c Mon Sep 17 00:00:00 2001 From: walshjon Date: Wed, 23 Oct 2013 14:26:22 -0700 Subject: [PATCH 146/192] removed extraneous forced recalculation of S(a,b) xs --- src/ace_header.F90 | 5 +++-- src/cross_section.F90 | 11 ++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/ace_header.F90 b/src/ace_header.F90 index d075c22889..5e2f39a007 100644 --- a/src/ace_header.F90 +++ b/src/ace_header.F90 @@ -214,8 +214,9 @@ module ace_header real(8) :: kappa_fission ! microscopic energy-released from fission ! Information for S(a,b) use - integer :: index_sab ! index in sab_tables (zero means no table) - real(8) :: elastic_sab ! microscopic elastic scattering on S(a,b) table + integer :: index_sab ! index in sab_tables (zero means no table) + integer :: last_index_sab = 0 ! index in sab_tables last used by this nuclide + real(8) :: elastic_sab ! microscopic elastic scattering on S(a,b) table ! Information for URR probability table use logical :: use_ptable ! in URR range with probability tables? diff --git a/src/cross_section.F90 b/src/cross_section.F90 index 4c9ffb8c88..299033590d 100644 --- a/src/cross_section.F90 +++ b/src/cross_section.F90 @@ -93,6 +93,8 @@ contains ! Calculate microscopic cross section for this nuclide if (p % E /= micro_xs(i_nuclide) % last_E) then call calculate_nuclide_xs(i_nuclide, i_sab, p % E) + else if (i_sab /= micro_xs(i_nuclide) % last_index_sab) then + call calculate_nuclide_xs(i_nuclide, i_sab, p % E) end if ! ======================================================================== @@ -235,13 +237,8 @@ contains end if end if - ! Set last evaluated energy -- if we're in S(a,b) region, force - ! re-calculation of cross-section - if (i_sab == 0) then - micro_xs(i_nuclide) % last_E = E - else - micro_xs(i_nuclide) % last_E = ZERO - end if + micro_xs(i_nuclide) % last_E = E + micro_xs(i_nuclide) % last_index_sab = i_sab end subroutine calculate_nuclide_xs From 2e45b2f57797bf675e64ae413b5593b6ebb5b724 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 19 Oct 2013 11:21:20 -0400 Subject: [PATCH 147/192] Update release notes and conf.py. --- docs/source/conf.py | 2 +- docs/source/releasenotes/index.rst | 1 + docs/source/releasenotes/notes_0.5.3.rst | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f3553c33e1..8752b48986 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,7 +48,7 @@ copyright = u'2011-2013, Massachusetts Institute of Technology' # The short X.Y version. version = "0.5" # The full version, including alpha/beta/rc tags. -release = "0.5.2" +release = "0.5.3" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/releasenotes/index.rst b/docs/source/releasenotes/index.rst index 876d953ff8..9164fa5943 100644 --- a/docs/source/releasenotes/index.rst +++ b/docs/source/releasenotes/index.rst @@ -10,6 +10,7 @@ bugs fixed, and known issues for each successive release. .. toctree:: :maxdepth: 1 + notes_0.5.3 notes_0.5.2 notes_0.5.1 notes_0.5.0 diff --git a/docs/source/releasenotes/notes_0.5.3.rst b/docs/source/releasenotes/notes_0.5.3.rst index fb7b1aa79a..c9c7aee38d 100644 --- a/docs/source/releasenotes/notes_0.5.3.rst +++ b/docs/source/releasenotes/notes_0.5.3.rst @@ -21,12 +21,20 @@ the problem at hand (mostly on the number of nuclides in the problem). New Features ------------ -- Special run mode --tallies removed. +- Output interface enhanced to allow multiple files handles to be opened +- Particle restart file linked to output interface - Particle restarts and state point restarts are both identified with the -r command line flag. -- New regression test suite. -- All memory leaks fixed. -- Shared-memory parallelism with OpenMP. +- Particle instance no longer global, passed to all physics routines +- Physics routines refactored to rely less on global memory, more arguments + passed in +- CMFD routines refactored and now can compute dominance ratio on the fly +- PETSc 3.4.2 or higher must be used and compiled with fortran datatype support +- Memory leaks fixed except for ones from xml-fortran package +- Test suite enhanced to test output with different compiler options +- Description of OpenMC development workflow added +- OpenMP shared-memory parallelism added +- Special run mode --tallies removed. --------- Bug Fixes @@ -35,7 +43,7 @@ Bug Fixes - 2b1e8a_: Normalize direction vector after reflecting particle. - 5853d2_: Set blank default for cross section listing alias. - e178c7_: Fix infinite loop with words greater than 80 characters in write_message. -- c18a6e_: Chcek for valid secondary mode on S(a,b) tables. +- c18a6e_: Check for valid secondary mode on S(a,b) tables. - 82c456_: Fix bug where last process could have zero particles. .. _2b1e8a: https://github.com/mit-crpg/openmc/commit/2b1e8a From 96f2035254eb006a99cd3f25f4c8fe4dfe1e3f47 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 6 Nov 2013 12:41:14 +0100 Subject: [PATCH 148/192] Formatting changes from previous merge. --- src/ace.F90 | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/ace.F90 b/src/ace.F90 index 209f5daa33..d54aa83c28 100644 --- a/src/ace.F90 +++ b/src/ace.F90 @@ -243,15 +243,14 @@ contains ! Read first line of header read(UNIT=in, FMT='(A10,2G12.0,1X,A10)') name, awr, kT, date_ - - ! Check that correct xs was found - ! (if XS listing (cross_sections.xml) is broken, this may not be the case) - - if( trim(adjustl(name)) /= trim(adjustl(listing % name)) ) then - message = "XS listing entry " // trim(listing % name) // " did not & - &match ACE data, " // trim(name) // " found instead." - call fatal_error() - end if + + ! Check that correct xs was found -- if cross_sections.xml is broken, the + ! location of the table may be wrong + if(adjustl(name) /= adjustl(listing % name)) then + message = "XS listing entry " // trim(listing % name) // " did not & + &match ACE data, " // trim(name) // " found instead." + call fatal_error() + end if ! Read more header and NXS and JXS read(UNIT=in, FMT=100) comment, mat, & From 4fc6643e0ac9175ed9fa063194f80e3305b784d0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 7 Nov 2013 10:04:27 +0100 Subject: [PATCH 149/192] 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 150/192] 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 151/192] 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 152/192] 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 153/192] 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 From 99c76e9377f738330108890b9c07ee5ad2a20b42 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 7 Nov 2013 13:10:01 -0500 Subject: [PATCH 154/192] fixed description of CMFD mesh --- docs/source/usersguide/input.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index 9ec51ca21e..3b389c4381 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -1196,13 +1196,9 @@ with "false". ```` Element ------------------ -If a structured mesh is desired as a filter for a tally, it must be specified in -a separate element with the tag name ````. This element has the following +The CMFD mesh is a structured Cartesian mesh. This element has the following attributes/sub-elements: - :type: - The type of structured mesh. Only "rectangular" is currently supported. - :lower_left: The lower-left corner of the structured mesh. If only two coordinate are given, it is assumed that the mesh is an x-y mesh. From bda4fd0cb6e0762e84a65ec97bcf5b5f5a661c33 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 14 Nov 2013 08:59:06 -0500 Subject: [PATCH 155/192] fixed preallocation of cmfd loss matrix when not using coremap overlay --- src/cmfd_loss_operator.F90 | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 index 6e9917ac5b..69be017644 100644 --- a/src/cmfd_loss_operator.F90 +++ b/src/cmfd_loss_operator.F90 @@ -26,8 +26,10 @@ contains integer :: nnz ! number of nonzeros in matrix integer :: n_i ! number of interior cells integer :: n_c ! number of corner cells - integer :: n_s ! number side cells + integer :: n_s ! number of side cells + integer :: n_e ! number of edge cells integer :: nz_c ! number of non-zero corner cells + integer :: nz_e ! number of non-zero edge cells integer :: nz_s ! number of non-zero side cells integer :: nz_i ! number of non-zero interior cells @@ -48,13 +50,16 @@ contains if (cmfd_coremap) then nnz = preallocate_loss_matrix(nx, ny, nz, ng, n) else ! structured Cartesian grid - n_c = 4 ! define # of corners - n_s = 2*(nx + ny) - 8 ! define # of sides - n_i = nx*ny - (n_c + n_S) ! define # of interiors - nz_c = ng*n_c*(3 + ng - 1) ! define # nonzero corners - nz_s = ng*n_s*(4 + ng - 1) ! define # nonzero sides - nz_i = ng*n_i*(5 + ng - 1) ! define # nonzero interiors - nnz = nz_c + nz_s + nz_i + n_c = 8 ! define # of corners + n_e = 4*(nx - 2) + 4*(ny - 2) + 4*(nz - 2) ! define # of edges + n_s = 2*(nx - 2)*(ny - 2) + 2*(nx - 2)*(nz - 2) & + + 2*(ny - 2)*(nz - 2) ! define # of sides + n_i = nx*ny*nz - (n_c + n_e + n_s) ! define # of interiors + nz_c = ng*n_c*(4 + ng - 1) ! define # nonzero corners + nz_e = ng*n_e*(5 + ng - 1) ! define # nonzero edges + nz_s = ng*n_s*(6 + ng - 1) ! define # nonzero sides + nz_i = ng*n_i*(7 + ng - 1) ! define # nonzero interiors + nnz = nz_c + nz_e + nz_s + nz_i end if ! Configure loss matrix From 740b0072cb23651f0a65fb38251fe017963ca883 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 14 Nov 2013 19:11:31 -0500 Subject: [PATCH 156/192] Particle track data should be threadprivate. --- src/track_output.F90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/track_output.F90 b/src/track_output.F90 index 4b02dfe9a2..8b9c17b89a 100644 --- a/src/track_output.F90 +++ b/src/track_output.F90 @@ -14,6 +14,7 @@ module track_output integer, private :: n_tracks ! total number of tracks real(8), private, allocatable :: coords(:,:) ! track coordinates +!$omp threadprivate(n_tracks, coords) contains From a70fdd1b868f493fd4a58cd07d6a467aba24eec8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 14 Nov 2013 19:14:21 -0500 Subject: [PATCH 157/192] Set default for p % write_track. --- src/particle_header.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/particle_header.F90 b/src/particle_header.F90 index b9762960ed..e1c696ff16 100644 --- a/src/particle_header.F90 +++ b/src/particle_header.F90 @@ -77,7 +77,7 @@ module particle_header integer :: n_collision ! # of collisions ! Track output - logical :: write_track + logical :: write_track = .false. contains procedure :: initialize => initialize_particle From ef004c457941b430fc84ee6c2ad23baba7d12b98 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 14 Nov 2013 19:18:47 -0500 Subject: [PATCH 158/192] Update description of in user's guide. --- docs/source/usersguide/input.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index 3b389c4381..1ae061f4c8 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -636,9 +636,10 @@ Each ```` element can have the following attributes or sub-elements: --------------------- The ```` can be used to represent repeating structures (e.g. fuel pins -in an assembly) or other geometry which naturally fits into a two-dimensional -structured mesh. Each cell within the lattice is filled with a specified -universe. A ```` accepts the following attributes or sub-elements: +in an assembly) or other geometry which naturally fits into a two- or +three-dimensional structured mesh. Each cell within the lattice is filled with a +specified universe. A ```` accepts the following attributes or +sub-elements: :id: A unique integer that can be used to identify the surface. @@ -650,18 +651,19 @@ universe. A ```` accepts the following attributes or sub-elements: *Default*: rectangular :dimension: - Two integers representing the number of lattice cells in the x- and y- - directions, respectively. + Two or three integers representing the number of lattice cells in the x- and + y- (and z-) directions, respectively. *Default*: None :lower_left: - The coordinates of the lower-left corner of the lattice. + The coordinates of the lower-left corner of the lattice. If the lattice is + two-dimensional, only the x- and y-coordinates are specified. *Default*: None :width: - The width of the lattice cell in the x- and y- directions. + The width of the lattice cell in the x- and y- (and z-) directions. *Default*: None From b83cdf207d235c95c4430e83f79a0751aa2a55e5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 14 Nov 2013 19:54:26 -0500 Subject: [PATCH 159/192] Fix bugs in particle track output. --- src/input_xml.F90 | 2 +- src/track_output.F90 | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 14b49292ee..9bedbc8b3c 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -414,7 +414,7 @@ contains 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) + track_identifiers(1:3,i) = track_(3*(i-1)+1 : 3*(i-1)+3) end do end if diff --git a/src/track_output.F90 b/src/track_output.F90 index 8b9c17b89a..021cbb8ca6 100644 --- a/src/track_output.F90 +++ b/src/track_output.F90 @@ -24,7 +24,6 @@ contains subroutine initialize_particle_track() n_tracks = 0 - allocate(coords(1,1)) end subroutine initialize_particle_track !=============================================================================== @@ -34,13 +33,17 @@ contains subroutine write_particle_track(p) type(Particle), intent(in) :: p - real(8), allocatable :: new_coords(:, :) + real(8), allocatable :: new_coords(:, :) - ! Add another column to coords. + ! Add another column to coords n_tracks = n_tracks + 1 - allocate(new_coords(3, n_tracks)) - new_coords(:, 1:n_tracks-1) = coords - call move_alloc(FROM=new_coords, TO=coords) + if (allocated(coords)) then + allocate(new_coords(3, n_tracks)) + new_coords(:, 1:n_tracks-1) = coords + call move_alloc(FROM=new_coords, TO=coords) + else + allocate(coords(3,1)) + end if ! Write current coordinates into the newest column. coords(:, n_tracks) = p % coord0 % xyz From 250b5f412fb275545852fc7438f3359869ec28b7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 14 Nov 2013 21:08:14 -0500 Subject: [PATCH 160/192] Update list of publications. --- docs/source/publications.rst | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/docs/source/publications.rst b/docs/source/publications.rst index d757b9c3ce..a7100fc18b 100644 --- a/docs/source/publications.rst +++ b/docs/source/publications.rst @@ -4,6 +4,53 @@ Publications ============ +- Benoit Forget, Sheng Xu, and Kord Smith, "Direct Doppler broadening in Monte + Carlo simulations using the multipole representation," *Ann. Nucl. Energy*, + **64**, 78--85 (2014). ``_ + +- Andrew Siegel, Kord Smith, Kyle Felker, Paul Romano, Benoit Forget, and Peter + Beckman, "Improved cache performance in Monte Carlo transport calculations + using energy banding," *Comput. Phys. Commun.* + (2013). ``_ + +- Jonathan A. Walsh, Benoit Forget, and Kord S. Smith, "Validation of OpenMC + Reactor Physics Simulations with the B&W 1810 Series Benchmarks," + *Trans. Am. Nucl. Soc.*, **109**, 1301--1304 (2013). + +- Bryan R. Herman, Benoit Forget, and Kord Smith, "Utilizing CMFD in OpenMC to + Estimate Dominance Ratio and Adjoint," *Trans. Am. Nucl. Soc.*, **109**, + 1389-1392 (2013). + +- Timothy P. Burke, Brian C. Kiedrowski, and William R. Martin, "Flux and + Reaction Rate Kernel Density Estimators in OpenMC," *Trans. Am. Nucl. Soc.*, + **109**, 683-686 (2013). + +- Paul K. Romano, Benoit Forget, Kord Smith, and Andrew Siegel, "On the user of + tally servers in Monte Carlo simulations of light-water reactors," + *Proc. Joint International Conference on Supercomputing in Nuclear + Applications and Monte Carlo*, Paris, France, Oct. 27--31 (2013). + +- Paul K. Romano, Nicholas E. Horelik, Bryan R. Herman, Adam G. Nelson, Benoit + Forget, and Kord Smith, "OpenMC: A State-of-the-Art Monte Carlo Code for + Research and Development," *Proc. Joint International Conference on + Supercomputing in Nuclear Applications and Monte Carlo*, Paris, France, + Oct. 27--31 (2013). + +- Kyle G. Felker, Andrew R. Siegel, Kord S. Smith, Paul K. Romano, and Benoit + Forget, "The energy band memory server algorithm for parallel Monte Carlo + calculations," *Proc. Joint International Conference on Supercomputing in + Nuclear Applications and Monte Carlo*, Paris, France, Oct. 27--31 (2013). + +- John R. Tramm and Andrew R. Siegel, "Memory Bottlenecks and Memory Contention + in Multi-Core Monte Carlo Transport Codes," *Proc. Joint International + Conference on Supercomputing in Nuclear Applications and Monte Carlo*, Paris, + France, Oct. 27--31 (2013). + +- Andrew R. Siegel, Kord Smith, Paul K. Romano, Benoit Forget, and Kyle Felker, + "Multi-core performance studies of a Monte Carlo neutron transport code," + *Int. J. High Perform. Comput. Appl.* + (2013). ``_ + - Paul K. Romano, Andrew R. Siegel, Benoit Forget, and Kord Smith, "Data decomposition of Monte Carlo particle transport simulations via tally servers," *J. Comput. Phys.*, **252**, 20--36 From 686ccb913d53b76aba530629fbc367c0b99f1c17 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 19 Nov 2013 19:35:26 -0500 Subject: [PATCH 161/192] Remove 'subject to change' on 0.5.3 release notes. --- docs/source/releasenotes/notes_0.5.3.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/source/releasenotes/notes_0.5.3.rst b/docs/source/releasenotes/notes_0.5.3.rst index c9c7aee38d..6d93f9aece 100644 --- a/docs/source/releasenotes/notes_0.5.3.rst +++ b/docs/source/releasenotes/notes_0.5.3.rst @@ -4,10 +4,6 @@ Release Notes for OpenMC 0.5.3 ============================== -.. note:: - These release notes are for an upcoming release of OpenMC and are still - subject to change. - ------------------- System Requirements ------------------- From 6d04cdd905c3dd2022ee535bbf2be4bf3c9c6ec8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 20 Nov 2013 18:42:26 -0500 Subject: [PATCH 162/192] Fix typo in list of publications. --- docs/source/publications.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/publications.rst b/docs/source/publications.rst index a7100fc18b..c17e652c7f 100644 --- a/docs/source/publications.rst +++ b/docs/source/publications.rst @@ -25,7 +25,7 @@ Publications Reaction Rate Kernel Density Estimators in OpenMC," *Trans. Am. Nucl. Soc.*, **109**, 683-686 (2013). -- Paul K. Romano, Benoit Forget, Kord Smith, and Andrew Siegel, "On the user of +- Paul K. Romano, Benoit Forget, Kord Smith, and Andrew Siegel, "On the use of tally servers in Monte Carlo simulations of light-water reactors," *Proc. Joint International Conference on Supercomputing in Nuclear Applications and Monte Carlo*, Paris, France, Oct. 27--31 (2013). From 772da9ba6bd0b0add9d70913f01aae3e595586c3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 29 Nov 2013 11:26:32 -0500 Subject: [PATCH 163/192] Added statepoint.py source site processing example in user's guide. Closes #221 on github. --- docs/source/usersguide/processing.rst | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index 780fe37938..043041ef42 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -391,3 +391,51 @@ 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. + +---------------------- +Source Site Processing +---------------------- + +For eigenvalue problems, OpenMC will store information on the fission source +sites in the statepoint file by default. For each source site, the weight, +position, sampled direction, and sampled energy are stored. To extract this data +from a statepoint file, the statepoint.py Python module can be used. Below is an +example of an interactive ipython session using the statepoint.py Python module: + +.. code-block:: python + + In [1]: import statepoint + + In [2]: sp = statepoint.StatePoint('statepoint.100.h5') + + In [3]: sp.read_source() + + In [4]: len(sp.source) + Out[4]: 1000 + + In [5]: sp.source[0:10] + Out[5]: + [, + , + , + , + , + , + , + , + , + ] + + In [6]: site = sp.source[0] + + In [7]: site.weight + Out[7]: 1.0 + + In [8]: site.xyz + Out[8]: array([ 2.21980946, -8.92686048, 87.93720485]) + + In [9]: site.uvw + Out[9]: array([ 0.06740523, 0.50612814, 0.85982024]) + + In [10]: site.E + Out[10]: 0.93292326356564159 From 0d866a08a7526872b35a8e5cb157c068b33b8612 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 29 Nov 2013 12:12:54 -0500 Subject: [PATCH 164/192] Modify XML input parsing instructions in developer's guide. --- docs/source/devguide/index.rst | 2 +- docs/source/devguide/xml-fortran.rst | 40 ---------------------------- docs/source/devguide/xml-parsing.rst | 38 ++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 41 deletions(-) delete mode 100644 docs/source/devguide/xml-fortran.rst create mode 100644 docs/source/devguide/xml-parsing.rst diff --git a/docs/source/devguide/index.rst b/docs/source/devguide/index.rst index 1eb7436d03..1ceba324c1 100644 --- a/docs/source/devguide/index.rst +++ b/docs/source/devguide/index.rst @@ -15,6 +15,6 @@ as debugging. structures styleguide workflow - xml-fortran + xml-parsing statepoint voxel diff --git a/docs/source/devguide/xml-fortran.rst b/docs/source/devguide/xml-fortran.rst deleted file mode 100644 index 456cd662f1..0000000000 --- a/docs/source/devguide/xml-fortran.rst +++ /dev/null @@ -1,40 +0,0 @@ -.. _devguide_xml-fortran: - -========================= -xml-fortran Input Parsing -========================= - -OpenMC relies on the xml-fortran package for reading and intrepreting the XML -input files for geometry, materials, settings, tallies, etc. The use of an XML -format makes writing input files considerably more flexible than would otherwise -be possible. - -With the xml-fortran package, extending the user input files to include new tags -is fairly straightforward. A "template" file exists for each diferent type of -input file that tells xml-fortran what to expect in a file. These template files -can be found in the src/templates directory. The steps for modifying/adding -input are as follows: - -1. Add a ````` tag to the desired template file, -e.g. src/templates/geometry_t.xml. See the `xml-fortran documentation`_ for a -description of the acceptable fields. - -2. In the input_xml module, any input given in your new tag will be read -automatically through a call to, e.g. read_xml_file_geometry_t. Whatever -variable name you specified should have the data available. - -3. Add code in the appropriate subroutine to check the variable for any possible -errors. - -4. Add a variable in OpenMC to copy the temporary variable into if there are no -errors. - -A set of `RELAX NG`_ schemata exists that enables real-time validation of input -files when using the GNU Emacs text editor. You should also modify the RELAX NG -schema for the template you changed (e.g. src/templates/geometry.rnc) so that -those who use Emacs can confirm whether their input is valid before they -run. You will need to be familiar with RELAX NG `compact syntax`_. - -.. _xml-fortran documentation: http://xml-fortran.sourceforge.net/documentation.html -.. _RELAX NG: http://relaxng.org/ -.. _compact syntax: http://relaxng.org/compact-tutorial-20030326.html diff --git a/docs/source/devguide/xml-parsing.rst b/docs/source/devguide/xml-parsing.rst new file mode 100644 index 0000000000..8310d53e4e --- /dev/null +++ b/docs/source/devguide/xml-parsing.rst @@ -0,0 +1,38 @@ +.. _devguide_xml-parsing: + +================= +XML Input Parsing +================= + +OpenMC relies on the FoX_ Fortran XML library for reading and intrepreting the +XML input files for geometry, materials, settings, tallies, etc. The use of an +XML format makes writing input files considerably more flexible than would +otherwise be possible. + +With the FoX library, extending the user input files to include new tags is +fairly straightforward. The steps for modifying/adding input are as follows: + +1. Add appropriate calls to procedures from the `xml_interface module`_, such as +``check_for_node``, ``get_node_value``, and ``get_node_array``. All input +reading is performed in the `input_xml module`_. + +2. Make sure that your input can be categorized as one of the datatypes from +`XML Schema Part 2`_ and that parsing of the data appropriately reflects +this. For example, for a boolean_ value, true can be represented either by "true" +or by "1". + +3. Add code to check the variable for any possible errors. + +A set of `RELAX NG`_ schemata exists that enables real-time validation of input +files when using the GNU Emacs text editor. You should also modify the RELAX NG +schema for the file you changed (e.g. src/relaxng/geometry.rnc) so that +those who use Emacs can confirm whether their input is valid before they +run. You will need to be familiar with RELAX NG `compact syntax`_. + +.. _FoX: https://github.com/andreww/fox +.. _xml_interface module: https://github.com/mit-crpg/openmc/blob/develop/src/xml_interface.F90 +.. _input_xml module: https://github.com/mit-crpg/openmc/blob/develop/src/input_xml.F90 +.. _XML Schema Part 2: http://www.w3.org/TR/xmlschema-2/ +.. _boolean: http://www.w3.org/TR/xmlschema-2/#boolean +.. _RELAX NG: http://relaxng.org/ +.. _compact syntax: http://relaxng.org/compact-tutorial-20030326.html From 152c4d4b2f4d89c5077e9b49cb050f256ebe08e6 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 29 Nov 2013 12:18:54 -0500 Subject: [PATCH 165/192] Removed errors related to xml-fortran from troubleshooting guide. --- docs/source/usersguide/troubleshoot.rst | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/docs/source/usersguide/troubleshoot.rst b/docs/source/usersguide/troubleshoot.rst index 49cf41c8de..a13272aa65 100644 --- a/docs/source/usersguide/troubleshoot.rst +++ b/docs/source/usersguide/troubleshoot.rst @@ -19,25 +19,6 @@ you are using a compiler that does not support type-bound procedures from Fortran 2003. This affects any version of gfortran prior to 4.6. Downloading and installing the latest gfortran_ compiler should resolve this problem. -Fatal Error: Wrong module version '4' (expected '9') for file 'xml_data_cmfd_t.mod' opened at (1) -************************************************************************************************* - -The `.mod` modules files that are created by gfortran are versioned and -sometimes are usually not backwards compatible. If gfortran is upgraded and the -modules files for xml-fortran source files are not deleted, this error may -occur. To fix this, clear out all module and object files with :program:`make -distclean` and then recompiling. - -Fatal Error: File 'xml_data_cmfd_t.mod' opened at (1) is not a GFORTRAN module file -*********************************************************************************** - -When OpenMC compiles, the first thing it needs to do is compile source in the -xml-fortran subdirectory. If you compiled everything with a compiler other than -gfortran, performed a :program:`make clean`, and then tried to :program:`make` -with gfortran, the xml-fortran modules would have been compiled with a different -compiler. To fix this, try clearing out all module and object files with -:program:`make distclean` and then recompiling. - gfortran: unrecognized option '-cpp' ************************************ From acb16a3e884b387bd2bf1eb75e25876a334ab9fe Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 29 Nov 2013 13:47:46 -0500 Subject: [PATCH 166/192] Prevent unused variable warning in cmfd_input. --- src/cmfd_input.F90 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index ac3d59824e..120db5f649 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -20,7 +20,9 @@ contains use cmfd_header, only: allocate_cmfd +#ifdef PETSC integer :: new_comm ! new mpi communicator +#endif integer :: color ! color group of processor ! Read in cmfd input file @@ -34,17 +36,17 @@ contains end if ! Split up procs -# ifdef PETSC +#ifdef PETSC call MPI_COMM_SPLIT(MPI_COMM_WORLD, color, 0, new_comm, mpi_err) -# endif +#endif ! assign to PETSc -# ifdef PETSC +#ifdef PETSC PETSC_COMM_WORLD = new_comm ! Initialize PETSc on all procs call PetscInitialize(PETSC_NULL_CHARACTER, mpi_err) -# endif +#endif ! Initialize timers call time_cmfd % reset() From 740b60e72ef4c2d8ae9ffff59e252bcadf894282 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Thu, 5 Dec 2013 20:07:14 -0500 Subject: [PATCH 167/192] sample_external_source now checks to make sure the sampled xyz exists within the problem domain. If it does not, the source site is resampled. --- src/DEPENDENCIES | 1 + src/source.F90 | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index b4fe997bbc..579bf26fb1 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -329,6 +329,7 @@ solver_interface.o: vector_header.o source.o: bank_header.o source.o: constants.o source.o: error.o +source.o: geometry.o source.o: geometry_header.o source.o: global.o source.o: math.o diff --git a/src/source.F90 b/src/source.F90 index 2082ad6dda..3d4de842a0 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -3,6 +3,7 @@ module source use bank_header, only: Bank use constants use error, only: fatal_error + use geometry, only: find_cell use geometry_header, only: BASE_UNIVERSE use global use math, only: maxwell_spectrum, watt_spectrum @@ -73,6 +74,8 @@ contains real(8) :: p_max(3) ! maximum coordinates of source real(8) :: a ! Arbitrary parameter 'a' real(8) :: b ! Arbitrary parameter 'b' + logical :: found ! Does the source particle exist within geometry? + type(Particle) :: p ! Temporary particle for using find_cell ! Set weight to one by default site % wgt = ONE @@ -80,11 +83,24 @@ contains ! Sample position select case (external_source % type_space) case (SRC_SPACE_BOX) - ! Coordinates sampled uniformly over a box - p_min = external_source % params_space(1:3) - p_max = external_source % params_space(4:6) - r = (/ (prn(), i = 1,3) /) - site % xyz = p_min + r*(p_max - p_min) + ! Set particle defaults + call p % initialize() + ! Repeat sampling source location until a good site has been found + found = .false. + do while (.not.found) + ! Coordinates sampled uniformly over a box + p_min = external_source % params_space(1:3) + p_max = external_source % params_space(4:6) + r = (/ (prn(), i = 1,3) /) + site % xyz = p_min + r*(p_max - p_min) + + ! Fill p with needed data + p % coord0 % xyz = site % xyz + + ! Now search to see if location exists in geometry + call find_cell(p, found) + + end do case (SRC_SPACE_POINT) ! Point source @@ -146,7 +162,7 @@ contains end subroutine sample_external_source !=============================================================================== -! GET_SOURCE_PARTICLE returns the next source particle +! GET_SOURCE_PARTICLE returns the next source particle !=============================================================================== subroutine get_source_particle(p, index_source) From c3cd394f7a27968b2eeab61427cc4840746df139 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Thu, 5 Dec 2013 20:13:25 -0500 Subject: [PATCH 168/192] Prettied up the comments. --- src/source.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source.F90 b/src/source.F90 index 3d4de842a0..57870206de 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -75,7 +75,7 @@ contains real(8) :: a ! Arbitrary parameter 'a' real(8) :: b ! Arbitrary parameter 'b' logical :: found ! Does the source particle exist within geometry? - type(Particle) :: p ! Temporary particle for using find_cell + type(Particle) :: p ! Temporary particle for using find_cell ! Set weight to one by default site % wgt = ONE From 7cc648036b6771ce1d8645d172ddecf56d8a0b10 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Mon, 9 Dec 2013 09:42:00 -0500 Subject: [PATCH 169/192] Added limit to number of external source resamples. --- src/constants.F90 | 20 ++++++++++++-------- src/source.F90 | 10 +++++++++- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/constants.F90 b/src/constants.F90 index e9999c0bdd..564e0baedd 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -20,7 +20,7 @@ module constants FILETYPE_PARTICLE_RESTART = -2 ! ============================================================================ - ! ADJUSTABLE PARAMETERS + ! ADJUSTABLE PARAMETERS ! NOTE: This is the only section of the constants module that should ever be ! adjusted. Modifying constants in other sections may cause the code to fail. @@ -50,6 +50,10 @@ module constants integer, parameter :: MAX_WORD_LEN = 150 integer, parameter :: MAX_FILE_LEN = 255 + ! Maximum number of external source spatial resamples to encounter before an + ! error is thrown. + integer, parameter :: MAX_EXTSRC_RESAMPLES = 10000 + ! ============================================================================ ! PHYSICAL CONSTANTS @@ -110,9 +114,9 @@ module constants ! Surface types integer, parameter :: & - SURF_PX = 1, & ! Plane parallel to x-plane - SURF_PY = 2, & ! Plane parallel to y-plane - SURF_PZ = 3, & ! Plane parallel to z-plane + SURF_PX = 1, & ! Plane parallel to x-plane + SURF_PY = 2, & ! Plane parallel to y-plane + SURF_PZ = 3, & ! Plane parallel to z-plane SURF_PLANE = 4, & ! Arbitrary plane SURF_CYL_X = 5, & ! Cylinder along x-axis SURF_CYL_Y = 6, & ! Cylinder along y-axis @@ -143,7 +147,7 @@ module constants ELECTRON = 3 ! Angular distribution type - integer, parameter :: & + integer, parameter :: & ANGLE_ISOTROPIC = 1, & ! Isotropic angular distribution ANGLE_32_EQUI = 2, & ! 32 equiprobable bins ANGLE_TABULAR = 3 ! Tabular angular distribution @@ -275,7 +279,7 @@ module constants SCORE_KAPPA_FISSION = -12, & ! fission energy production rate SCORE_CURRENT = -13, & ! partial current SCORE_EVENTS = -14 ! number of events - + ! Maximum scattering order supported integer, parameter :: SCATT_ORDER_MAX = 10 character(len=*), parameter :: SCATT_ORDER_MAX_PNSTR = "scatter-p10" @@ -322,7 +326,7 @@ module constants ! Source angular distribution types integer, parameter :: & - SRC_ANGLE_ISOTROPIC = 1, & ! Isotropic angular + SRC_ANGLE_ISOTROPIC = 1, & ! Isotropic angular SRC_ANGLE_MONO = 2, & ! Monodirectional source SRC_ANGLE_TABULAR = 3 ! Tabular distribution @@ -332,7 +336,7 @@ module constants SRC_ENERGY_MAXWELL = 2, & ! Maxwell fission spectrum SRC_ENERGY_WATT = 3, & ! Watt fission spectrum SRC_ENERGY_TABULAR = 4 ! Tabular distribution - + ! ============================================================================ ! MISCELLANEOUS CONSTANTS diff --git a/src/source.F90 b/src/source.F90 index 57870206de..4fcdd0074a 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -76,6 +76,7 @@ contains real(8) :: b ! Arbitrary parameter 'b' logical :: found ! Does the source particle exist within geometry? type(Particle) :: p ! Temporary particle for using find_cell + integer, save :: num_resamples = 0 ! Number of resamples encountered ! Set weight to one by default site % wgt = ONE @@ -99,7 +100,14 @@ contains ! Now search to see if location exists in geometry call find_cell(p, found) - + if (.not. found) then + num_resamples = num_resamples + 1 + if (num_resamples == MAX_EXTSRC_RESAMPLES) then + message = "Maximum number of external source spatial resamples & + &reached!" + call fatal_error() + end if + end if end do case (SRC_SPACE_POINT) From 8884fb9e3f1657b2b5e0c5b4e8e4a3af5adfbe68 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Tue, 10 Dec 2013 05:58:11 -0500 Subject: [PATCH 170/192] Added changes to allow for the entire list of zaids to be checked. --- src/ace.F90 | 30 +++++++----- src/ace_header.F90 | 113 +++++++++++++++++++++++---------------------- src/output.F90 | 66 ++++++++++++++------------ 3 files changed, 112 insertions(+), 97 deletions(-) diff --git a/src/ace.F90 b/src/ace.F90 index d54aa83c28..d5fa012d36 100644 --- a/src/ace.F90 +++ b/src/ace.F90 @@ -115,9 +115,9 @@ contains ! search through the list of nuclides for one which has a matching zaid sab => sab_tables(mat % i_sab_tables(k)) - ! Loop through nuclides and find match + ! Loop through nuclides and find match FIND_NUCLIDE: do j = 1, mat % n_nuclides - if (nuclides(mat % nuclide(j)) % zaid == sab % zaid) then + if (any(sab % zaid == nuclides(mat % nuclide(j)) % zaid)) then mat % i_sab_nuclides(k) = j exit FIND_NUCLIDE end if @@ -161,16 +161,16 @@ contains mat % i_sab_tables(m) = temp_table end do SORT_SAB end if - + ! Deallocate temporary arrays for names of nuclides and S(a,b) tables if (allocated(mat % names)) deallocate(mat % names) if (allocated(mat % sab_names)) deallocate(mat % sab_names) end do MATERIAL_LOOP2 - + ! Avoid some valgrind leak errors call already_read % clear() - + end subroutine read_xs !=============================================================================== @@ -253,7 +253,7 @@ contains end if ! Read more header and NXS and JXS - read(UNIT=in, FMT=100) comment, mat, & + read(UNIT=in, FMT=100) comment, mat, & (zaids(i), awrs(i), i=1,16), NXS, JXS 100 format(A70,A10/4(I7,F11.0)/4(I7,F11.0)/4(I7,F11.0)/4(I7,F11.0)/& ,8I9/8I9/8I9/8I9/8I9/8I9) @@ -277,7 +277,7 @@ contains ACCESS='direct', RECL=record_length) ! Read all header information - read(UNIT=in, REC=location) name, awr, kT, date_, & + read(UNIT=in, REC=location) name, awr, kT, date_, & comment, mat, (zaids(i), awrs(i), i=1,16), NXS, JXS ! determine table length @@ -333,7 +333,15 @@ contains sab % name = name sab % awr = awr sab % kT = kT - sab % zaid = zaids(1) + ! Find sab % n_zaid + do i = 1, 16 + if (zaids(i) == 0) then + sab % n_zaid = i - 1 + exit + end if + end do + allocate(sab % zaid(sab % n_zaid)) + sab % zaid = zaids(1: sab % n_zaid) call read_thermal_data(sab) end select @@ -406,7 +414,7 @@ contains integer :: LNU ! type of nu data (polynomial or tabular) integer :: NC ! number of polynomial coefficients integer :: NR ! number of interpolation regions - integer :: NE ! number of energies + integer :: NE ! number of energies integer :: NPCR ! number of delayed neutron precursor groups integer :: LED ! location of energy distribution locators integer :: LDIS ! location of all energy distributions @@ -809,7 +817,7 @@ contains LED = JXS(10) - ! Loop over all reactions + ! Loop over all reactions do i = 1, NXS(5) rxn => nuc % reactions(i+1) ! skip over elastic scattering rxn % has_energy_dist = .true. @@ -840,7 +848,7 @@ contains integer :: LDIS ! location of all energy distributions integer :: LNW ! location of next energy distribution if multiple - integer :: LAW ! secondary energy distribution law + integer :: LAW ! secondary energy distribution law integer :: NR ! number of interpolation regions integer :: NE ! number of incoming energies integer :: IDAT ! location of first energy distribution for given MT diff --git a/src/ace_header.F90 b/src/ace_header.F90 index 5e2f39a007..26fbf4cbca 100644 --- a/src/ace_header.F90 +++ b/src/ace_header.F90 @@ -16,7 +16,7 @@ module ace_header integer, allocatable :: type(:) ! type of distribution integer, allocatable :: location(:) ! location of each table real(8), allocatable :: data(:) ! angular distribution data - + ! Type-Bound procedures contains procedure :: clear => distangle_clear ! Deallocates DistAngle @@ -35,7 +35,7 @@ module ace_header ! For reactions that may have multiple energy distributions such as (n.2n), ! this pointer allows multiple laws to be stored type(DistEnergy), pointer :: next => null() - + ! Type-Bound procedures contains procedure :: clear => distenergy_clear ! Deallocates DistEnergy @@ -57,7 +57,7 @@ module ace_header logical :: has_energy_dist ! Energy distribution present? type(DistAngle) :: adist ! Secondary angular distribution type(DistEnergy), pointer :: edist => null() ! Secondary energy distribution - + ! Type-Bound procedures contains procedure :: clear => reaction_clear ! Deallocates Reaction @@ -76,7 +76,7 @@ module ace_header logical :: multiply_smooth ! multiply by smooth cross section? real(8), allocatable :: energy(:) ! incident energies real(8), allocatable :: prob(:,:,:) ! actual probabibility tables - + ! Type-Bound procedures contains procedure :: clear => urrdata_clear ! Deallocates UrrData @@ -137,7 +137,7 @@ module ace_header ! Reactions integer :: n_reaction ! # of reactions type(Reaction), pointer :: reactions(:) => null() - + ! Type-Bound procedures contains procedure :: clear => nuclide_clear ! Deallocates Nuclide @@ -147,12 +147,13 @@ module ace_header ! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off ! of light isotopes such as water, graphite, Be, etc !=============================================================================== - + type SAlphaBeta - character(10) :: name ! name of table, e.g. lwtr.10t - integer :: zaid ! Z and A identifier, e.g. 6012 for Carbon-12 - real(8) :: awr ! weight of nucleus in neutron masses - real(8) :: kT ! temperature in MeV (k*T) + character(10) :: name ! name of table, e.g. lwtr.10t + real(8) :: awr ! weight of nucleus in neutron masses + real(8) :: kT ! temperature in MeV (k*T) + integer :: n_zaid ! Number of valid zaids + integer, allocatable :: zaid(:) ! List of valid Z and A identifiers, e.g. 6012 ! threshold for S(a,b) treatment (usually ~4 eV) real(8) :: threshold_inelastic @@ -164,7 +165,7 @@ module ace_header integer :: n_inelastic_mu ! # of outgoing angles for inelastic integer :: secondary_mode ! secondary mode (equal/skewed) real(8), allocatable :: inelastic_e_in(:) - real(8), allocatable :: inelastic_sigma(:) + real(8), allocatable :: inelastic_sigma(:) real(8), allocatable :: inelastic_e_out(:,:) real(8), allocatable :: inelastic_mu(:,:,:) @@ -240,125 +241,125 @@ module ace_header !=============================================================================== ! DISTANGLE_CLEAR resets and deallocates data in Reaction. -!=============================================================================== - +!=============================================================================== + subroutine distangle_clear(this) - + class(DistAngle), intent(inout) :: this ! The DistAngle object to clear - + if (allocated(this % energy)) & deallocate(this % energy, this % type, this % location, this % data) - - end subroutine distangle_clear + + end subroutine distangle_clear !=============================================================================== ! DISTENERGY_CLEAR resets and deallocates data in DistEnergy. -!=============================================================================== - +!=============================================================================== + recursive subroutine distenergy_clear(this) - + class(DistEnergy), intent(inout) :: this ! The DistEnergy object to clear - + ! Clear p_valid call this % p_valid % clear() - + if (allocated(this % data)) & deallocate(this % data) - + if (associated(this % next)) then ! recursively clear this item call this % next % clear() deallocate(this % next) end if - + end subroutine distenergy_clear - + !=============================================================================== ! REACTION_CLEAR resets and deallocates data in Reaction. -!=============================================================================== - +!=============================================================================== + subroutine reaction_clear(this) - + class(Reaction), intent(inout) :: this ! The Reaction object to clear - + if (allocated(this % sigma)) & deallocate(this % sigma) - + if (associated(this % edist)) then call this % edist % clear() deallocate(this % edist) end if - + call this % adist % clear() - - end subroutine reaction_clear - + + end subroutine reaction_clear + !=============================================================================== ! URRDATA_CLEAR resets and deallocates data in Reaction. -!=============================================================================== - +!=============================================================================== + subroutine urrdata_clear(this) - + class(UrrData), intent(inout) :: this ! The UrrData object to clear - + if (allocated(this % energy)) & deallocate(this % energy, this % prob) - - end subroutine urrdata_clear + + end subroutine urrdata_clear !=============================================================================== ! NUCLIDE_CLEAR resets and deallocates data in Nuclide. -!=============================================================================== - +!=============================================================================== + subroutine nuclide_clear(this) - + class(Nuclide), intent(inout) :: this ! The Nuclide object to clear - + integer :: i ! Loop counter - + if (allocated(this % grid_index)) & deallocate(this % grid_index) - + if (allocated(this % energy)) & deallocate(this % total, this % elastic, this % fission, & this % nu_fission, this % absorption) if (allocated(this % heating)) & deallocate(this % heating) - + if (allocated(this % index_fission)) & deallocate(this % index_fission) - + if (allocated(this % nu_t_data)) & deallocate(this % nu_t_data) - + if (allocated(this % nu_p_data)) & deallocate(this % nu_p_data) - + if (allocated(this % nu_d_data)) & deallocate(this % nu_d_data) - + if (allocated(this % nu_d_precursor_data)) & deallocate(this % nu_d_precursor_data) - + if (associated(this % nu_d_edist)) then do i = 1, size(this % nu_d_edist) call this % nu_d_edist(i) % clear() end do deallocate(this % nu_d_edist) end if - + if (associated(this % urr_data)) then call this % urr_data % clear() deallocate(this % urr_data) end if - + if (associated(this % reactions)) then do i = 1, size(this % reactions) call this % reactions(i) % clear() end do deallocate(this % reactions) end if - - end subroutine nuclide_clear + + end subroutine nuclide_clear end module ace_header diff --git a/src/output.F90 b/src/output.F90 index c60b96bf26..e4fcfecef5 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -126,7 +126,7 @@ contains ! print header based on level select case (header_level) case (1) - write(UNIT=unit_, FMT='(/3(1X,A/))') repeat('=', 75), & + write(UNIT=unit_, FMT='(/3(1X,A/))') repeat('=', 75), & repeat('=', n) // '> ' // trim(line) // ' <' // & repeat('=', m), repeat('=', 75) case (2) @@ -180,7 +180,7 @@ contains end subroutine print_usage !=============================================================================== -! WRITE_MESSAGE displays an informational message to the log file and the +! WRITE_MESSAGE displays an informational message to the log file and the ! standard output stream. !=============================================================================== @@ -215,7 +215,7 @@ contains ! Determine last space in current line last_space = index(message(i_start+1:i_start+line_wrap), & ' ', BACK=.true.) - if (last_space == 0) then + if (last_space == 0) then i_end = min(length + 1, i_start+line_wrap) - 1 write(ou, fmt='(1X,A)') message(i_start+1:i_end) else @@ -1030,6 +1030,7 @@ contains integer :: size_sab ! memory used by S(a,b) table integer :: unit_ ! unit to write to + integer :: i ! Loop counter for parsing through sab % zaid ! set default unit for writing information if (present(unit)) then @@ -1040,7 +1041,12 @@ contains ! Basic S(a,b) table information write(unit_,*) 'S(a,b) Table ' // trim(sab % name) - write(unit_,*) ' zaid = ' // trim(to_str(sab % zaid)) + write(unit_,'(A)',advance="no") ' zaids = ' + do i = 1, sab % n_zaid - 1 + ! This could use some fancy handling for if the line becomes too long + write(unit_,'(A)',advance="no") trim(to_str(sab % zaid(sab % n_zaid))) // ', ' + end do + write(unit_,*) trim(to_str(sab % zaid(sab % n_zaid))) write(unit_,*) ' awr = ' // trim(to_str(sab % awr)) write(unit_,*) ' kT = ' // trim(to_str(sab % kT)) @@ -1242,7 +1248,7 @@ contains end select end if write(UNIT=ou, FMT=*) - + write(UNIT=ou, FMT='(2X,A9,3X)', ADVANCE='NO') "=========" write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" if (entropy_on) write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" @@ -1272,7 +1278,7 @@ contains if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & entropy(overall_gen) - if (overall_gen - n_inactive*gen_per_batch > 1) then + if (overall_gen - n_inactive*gen_per_batch > 1) then write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5," +/-",F8.5)', ADVANCE='NO') & keff, keff_std end if @@ -1300,7 +1306,7 @@ contains entropy(current_batch*gen_per_batch) ! write out accumulated k-effective if after first active batch - if (overall_gen - n_inactive*gen_per_batch > 1) then + if (overall_gen - n_inactive*gen_per_batch > 1) then write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5," +/-",F8.5)', ADVANCE='NO') & keff, keff_std else @@ -1310,7 +1316,7 @@ contains ! write out cmfd keff if it is active and other display info if (cmfd_on) then write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - cmfd % k_cmfd(current_batch) + cmfd % k_cmfd(current_batch) select case(trim(cmfd_display)) case('entropy') write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & @@ -1349,7 +1355,7 @@ contains ! Plot id write(ou,100) "Plot ID:", trim(to_str(pl % id)) - + ! Plot type if (pl % type == PLOT_TYPE_SLICE) then write(ou,100) "Plot Type:", "Slice" @@ -1387,7 +1393,7 @@ contains trim(to_str(pl % pixels(2))) else if (pl % type == PLOT_TYPE_VOXEL) then write(ou,100) "Voxels:", trim(to_str(pl % pixels(1))) // " " // & - trim(to_str(pl % pixels(2))) // " " // trim(to_str(pl % pixels(3))) + trim(to_str(pl % pixels(2))) // " " // trim(to_str(pl % pixels(3))) end if write(ou,*) @@ -1539,7 +1545,7 @@ contains write(ou,100) 'Cell ID','No. Overlap Checks' - do i = 1, n_cells + do i = 1, n_cells write(ou,101) cells(i) % id, overlap_check_cnt(i) if (overlap_check_cnt(i) < 10) num_sparse = num_sparse + 1 end do @@ -1573,7 +1579,7 @@ contains integer :: k ! loop index for scoring bins integer :: n ! loop index for nuclides integer :: l ! loop index for user scores - integer :: type ! type of tally filter + integer :: type ! type of tally filter integer :: indent ! number of spaces to preceed output integer :: filter_index ! index in results array for filters integer :: score_index ! scoring bin index @@ -1749,13 +1755,13 @@ contains score_name = 'P' // trim(to_str(t % scatt_order(k))) // & ' Scattering Moment' end if - write(UNIT=UNIT_TALLY, FMT='(1X,2A,1X,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(1X,2A,1X,A,"+/- ",A)') & repeat(" ", indent), score_name, & to_str(t % results(score_index,filter_index) % sum), & trim(to_str(t % results(score_index,filter_index) % sum_sq)) else if (t % score_bins(k) == SCORE_SCATTER_PN) then score_name = "Scattering Rate" - write(UNIT=UNIT_TALLY, FMT='(1X,2A,1X,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(1X,2A,1X,A,"+/- ",A)') & repeat(" ", indent), score_name, & to_str(t % results(score_index,filter_index) % sum), & trim(to_str(t % results(score_index,filter_index) % sum_sq)) @@ -1763,7 +1769,7 @@ contains score_index = score_index + 1 score_name = 'P' // trim(to_str(n_order)) // & ' Scattering Moment' - write(UNIT=UNIT_TALLY, FMT='(1X,2A,1X,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(1X,2A,1X,A,"+/- ",A)') & repeat(" ", indent), score_name, & to_str(t % results(score_index,filter_index) % sum), & trim(to_str(t % results(score_index,filter_index) % sum_sq)) @@ -1775,7 +1781,7 @@ contains else score_name = score_names(abs(t % score_bins(k))) end if - write(UNIT=UNIT_TALLY, FMT='(1X,2A,1X,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(1X,2A,1X,A,"+/- ",A)') & repeat(" ", indent), score_name, & to_str(t % results(score_index,filter_index) % sum), & trim(to_str(t % results(score_index,filter_index) % sum_sq)) @@ -1813,8 +1819,8 @@ contains integer :: i_filter_ein ! index for incoming energy filter integer :: i_filter_surf ! index for surface filter integer :: n ! number of incoming energy bins - integer :: len1 ! length of string - integer :: len2 ! length of string + integer :: len1 ! length of string + integer :: len2 ! length of string integer :: filter_index ! index in results array for filters logical :: print_ebin ! should incoming energy bin be displayed? character(MAX_LINE_LEN) :: string @@ -1864,14 +1870,14 @@ contains mesh_indices_to_bin(m, (/ i-1, j, k /) + 1, .true.) matching_bins(i_filter_surf) = IN_RIGHT filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Left", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) matching_bins(i_filter_surf) = OUT_RIGHT filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Incoming Current from Left", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) @@ -1881,14 +1887,14 @@ contains mesh_indices_to_bin(m, (/ i, j, k /) + 1, .true.) matching_bins(i_filter_surf) = IN_RIGHT filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Incoming Current from Right", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) matching_bins(i_filter_surf) = OUT_RIGHT filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Right", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) @@ -1898,14 +1904,14 @@ contains mesh_indices_to_bin(m, (/ i, j-1, k /) + 1, .true.) matching_bins(i_filter_surf) = IN_FRONT filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Back", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) matching_bins(i_filter_surf) = OUT_FRONT filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Incoming Current from Back", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) @@ -1915,14 +1921,14 @@ contains mesh_indices_to_bin(m, (/ i, j, k /) + 1, .true.) matching_bins(i_filter_surf) = IN_FRONT filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Incoming Current from Front", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) matching_bins(i_filter_surf) = OUT_FRONT filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Front", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) @@ -1932,14 +1938,14 @@ contains mesh_indices_to_bin(m, (/ i, j, k-1 /) + 1, .true.) matching_bins(i_filter_surf) = IN_TOP filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Bottom", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) matching_bins(i_filter_surf) = OUT_TOP filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Incoming Current from Bottom", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) @@ -1949,14 +1955,14 @@ contains mesh_indices_to_bin(m, (/ i, j, k /) + 1, .true.) matching_bins(i_filter_surf) = IN_TOP filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Incoming Current from Top", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) matching_bins(i_filter_surf) = OUT_TOP filter_index = sum((matching_bins(1:t%n_filters) - 1) * t % stride) + 1 - write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & + write(UNIT=UNIT_TALLY, FMT='(5X,A,T35,A,"+/- ",A)') & "Outgoing Current to Top", & to_str(t % results(1,filter_index) % sum), & trim(to_str(t % results(1,filter_index) % sum_sq)) From ee1edca155ab185f664aa6805ee81c671af0ac53 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 10 Dec 2013 09:06:40 -0500 Subject: [PATCH 171/192] fixed source_write option in input reading --- src/input_xml.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 05f4ae0c56..8cd93f0e10 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -635,8 +635,8 @@ contains if (check_for_node(node_sp, "source_write")) then call get_node_value(node_sp, "source_write", temp_str) call lower_case(temp_str) - if (trim(temp_str) == 'true' .or. & - trim(temp_str) == '1') source_separate = .true. + if (trim(temp_str) == 'false' .or. & + trim(temp_str) == '0') source_write = .false. end if else ! If no tag was present, by default write state point at From b6395729a4ba6e33b321fd08635d8f73baeb307f Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Tue, 10 Dec 2013 11:51:40 -0500 Subject: [PATCH 172/192] Added better printing of the new zaid list for cross_sections.out; now it will go to a new line before hitting the 80-char limit. --- src/output.F90 | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/output.F90 b/src/output.F90 index e4fcfecef5..1507aa4cd5 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1028,9 +1028,10 @@ contains type(SAlphaBeta), pointer :: sab integer, optional :: unit - integer :: size_sab ! memory used by S(a,b) table - integer :: unit_ ! unit to write to - integer :: i ! Loop counter for parsing through sab % zaid + integer :: size_sab ! memory used by S(a,b) table + integer :: unit_ ! unit to write to + integer :: i ! Loop counter for parsing through sab % zaid + integer :: char_count ! Counter for the number of characters on a line ! set default unit for writing information if (present(unit)) then @@ -1041,12 +1042,30 @@ contains ! Basic S(a,b) table information write(unit_,*) 'S(a,b) Table ' // trim(sab % name) - write(unit_,'(A)',advance="no") ' zaids = ' - do i = 1, sab % n_zaid - 1 - ! This could use some fancy handling for if the line becomes too long - write(unit_,'(A)',advance="no") trim(to_str(sab % zaid(sab % n_zaid))) // ', ' + write(unit_,'(A)',advance="no") ' zaids = ' + ! Initialize the counter based on the above string + char_count = 11 + do i = 1, sab % n_zaid + ! Deal with a line thats too long + if (char_count >= 73) then ! 73 = 80 - (5 ZAID chars + 1 space + 1 comma) + ! End the line + write(unit_,*) "" + ! Add 11 leading blanks + write(unit_,'(A)', advance="no") " " + ! reset the counter to 11 + char_count = 11 + end if + if (i < sab % n_zaid) then + ! Include a comma + write(unit_,'(A)',advance="no") trim(to_str(sab % zaid(i))) // ", " + char_count = char_count + len(trim(to_str(sab % zaid(i)))) + 2 + else + ! Don't include a comma, since we are all done + write(unit_,'(A)',advance="no") trim(to_str(sab % zaid(i))) + end if + end do - write(unit_,*) trim(to_str(sab % zaid(sab % n_zaid))) + write(unit_,*) "" ! Move to next line write(unit_,*) ' awr = ' // trim(to_str(sab % awr)) write(unit_,*) ' kT = ' // trim(to_str(sab % kT)) From 0a05d4594aeb57a0179048c3bb4ccdd00a990b31 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 Dec 2013 16:53:11 -0500 Subject: [PATCH 173/192] Fix bug in doppler broadening (not used). --- src/doppler.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doppler.F90 b/src/doppler.F90 index 12fb34bbfe..e888eb2b18 100644 --- a/src/doppler.F90 +++ b/src/doppler.F90 @@ -87,7 +87,7 @@ contains ! ======================================================================= ! EXTEND CROSS SECTION TO 0 ASSUMING 1/V SHAPE - if (k == 1 .and. a >= 4.0) then + if (k == 1 .and. a >= -4.0) then ! Since x = 0, this implies that a = -y F_b = F_a a = -y @@ -101,7 +101,7 @@ contains end if ! ======================================================================= - ! EVALUATE FIRST TERM FROM x(k) - y = 0 to -4 + ! EVALUATE FIRST TERM FROM x(k) - y = 0 to 4 k = i b = ZERO From 1bdf693d798b7b4597d65bee5753296bb8829823 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 Dec 2013 22:33:59 -0500 Subject: [PATCH 174/192] Fix use statements in several cmfd modules. --- src/cmfd_data.F90 | 2 +- src/cmfd_execute.F90 | 12 +++++++----- src/cmfd_power_solver.F90 | 2 ++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index d0e80aaa90..8dd0fa35ff 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -934,7 +934,7 @@ contains subroutine compute_effective_downscatter() use constants, only: ZERO, CMFD_NOACCEL - use global, only: cmfd, cmfd_downscatter + use global, only: cmfd integer :: nx ! number of mesh cells in x direction integer :: ny ! number of mesh cells in y direction diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 7a35a12144..6234d9fcf9 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -117,9 +117,9 @@ contains subroutine process_cmfd_options() +#ifdef PETSC use global, only: cmfd_snes_monitor, cmfd_ksp_monitor, mpi_err -#ifdef PETSC ! Check for snes monitor if (cmfd_snes_monitor) call PetscOptionsSetValue("-snes_monitor", & "stdout", mpi_err) @@ -138,9 +138,10 @@ contains subroutine calc_fission_source() use constants, only: CMFD_NOACCEL, ZERO, TWO - use global, only: cmfd, cmfd_coremap, master, mpi_err, entropy_on, & - current_batch + use global, only: cmfd, cmfd_coremap, master, entropy_on, current_batch + #ifdef MPI + use global, only: mpi_err use mpi #endif @@ -261,13 +262,14 @@ contains use constants, only: ZERO, ONE use error, only: warning, fatal_error - use global, only: n_particles, meshes, source_bank, work, & - n_user_meshes, message, cmfd, master, mpi_err + use global, only: meshes, source_bank, work, n_user_meshes, message, & + cmfd, master use mesh_header, only: StructuredMesh use mesh, only: count_bank_sites, get_mesh_indices use search, only: binary_search #ifdef MPI + use global, only: mpi_err use mpi #endif diff --git a/src/cmfd_power_solver.F90 b/src/cmfd_power_solver.F90 index c1309b72cd..f2761d0a1f 100644 --- a/src/cmfd_power_solver.F90 +++ b/src/cmfd_power_solver.F90 @@ -102,7 +102,9 @@ contains subroutine init_data(adjoint) use constants, only: ONE, ZERO +#ifdef PETSC use global, only: cmfd_write_matrices +#endif logical, intent(in) :: adjoint ! adjoint calcualtion From ca4ec3b3cd1e2dd6d1a07e461b2f5ef504262fc3 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Wed, 11 Dec 2013 09:47:49 -0500 Subject: [PATCH 175/192] Added ZrH as the top reflector of the test_salphabeta_multiple test in order to include an S(a,b) library which can be applied to multiple problems in our test suite. --- tests/test_salphabeta_multiple/geometry.xml | 6 ++++-- tests/test_salphabeta_multiple/materials.xml | 18 +++++++++++++++++- .../test_salphabeta_multiple/results_true.dat | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tests/test_salphabeta_multiple/geometry.xml b/tests/test_salphabeta_multiple/geometry.xml index 2d427d8cb3..63f69f7438 100644 --- a/tests/test_salphabeta_multiple/geometry.xml +++ b/tests/test_salphabeta_multiple/geometry.xml @@ -44,9 +44,11 @@ - + - + + + diff --git a/tests/test_salphabeta_multiple/materials.xml b/tests/test_salphabeta_multiple/materials.xml index 97ff56c05f..6e975bde97 100644 --- a/tests/test_salphabeta_multiple/materials.xml +++ b/tests/test_salphabeta_multiple/materials.xml @@ -1,7 +1,8 @@ - + 70c @@ -111,4 +112,19 @@ + + + + + + + + + + + + + + + diff --git a/tests/test_salphabeta_multiple/results_true.dat b/tests/test_salphabeta_multiple/results_true.dat index f26a309aae..bbd61cdb53 100644 --- a/tests/test_salphabeta_multiple/results_true.dat +++ b/tests/test_salphabeta_multiple/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.013747E+00 3.067630E-02 +9.761880E-01 9.170415E-03 From 549bdad2e371e6afb78353b7dec8914bacbdaee8 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sat, 14 Dec 2013 10:18:35 -0500 Subject: [PATCH 176/192] Added ability to read in continuous secondary energy distributions for inelastic S(a,b) collisions. Still have to implement new collision physics in physics.F90 --- src/ace.F90 | 83 ++++++++++++++++++++++++++++++++-------------- src/ace_header.F90 | 24 ++++++++++++-- src/constants.F90 | 3 +- 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/src/ace.F90 b/src/ace.F90 index d5fa012d36..ff8582470d 100644 --- a/src/ace.F90 +++ b/src/ace.F90 @@ -1195,15 +1195,10 @@ contains integer :: NE_out ! number of outgoing energies integer :: NMU ! number of outgoing angles integer :: JXS4 ! location of elastic energy table + real(8), allocatable :: LOCC(:) ! Location of inelastic data - ! read secondary energy mode for inelastic scattering and check + ! read secondary energy mode for inelastic scattering table % secondary_mode = NXS(7) - if (table % secondary_mode /= SAB_SECONDARY_EQUAL .and. & - table % secondary_mode /= SAB_SECONDARY_SKEWED) then - message = "Unsupported secondary mode on S(a,b) table " // & - trim(adjustl(table % name)) // ": " // to_str(table % secondary_mode) - call fatal_error() - end if ! read number of inelastic energies and allocate arrays NE_in = int(XSS(JXS(1))) @@ -1221,29 +1216,67 @@ contains ! allocate space for outgoing energy/angle for inelastic ! scattering - NE_out = NXS(4) - NMU = NXS(3) + 1 - table % n_inelastic_e_out = NE_out - table % n_inelastic_mu = NMU - allocate(table % inelastic_e_out(NE_out, NE_in)) - allocate(table % inelastic_mu(NMU, NE_out, NE_in)) + if (table % secondary_mode == SAB_SECONDARY_EQUAL .or. & + table % secondary_mode == SAB_SECONDARY_SKEWED) then + NMU = NXS(3) + 1 + table % n_inelastic_mu = NMU + NE_out = NXS(4) + table % n_inelastic_e_out = NE_out + allocate(table % inelastic_e_out(NE_out, NE_in)) + allocate(table % inelastic_mu(NMU, NE_out, NE_in)) + else if (table % secondary_mode == SAB_SECONDARY_CONT) then + NMU = NXS(3) - 1 + table % n_inelastic_mu = NMU + allocate(table % inelastic_data(NE_in)) + allocate(LOCC(NE_in)) + ! NE_out will be determined later + end if ! read outgoing energy/angle distribution for inelastic scattering - lc = JXS(3) - 1 - do i = 1, NE_in - do j = 1, NE_out - ! read outgoing energy - table % inelastic_e_out(j,i) = XSS(lc + 1) + if (table % secondary_mode == SAB_SECONDARY_EQUAL .or. & + table % secondary_mode == SAB_SECONDARY_SKEWED) then + lc = JXS(3) - 1 + do i = 1, NE_in + do j = 1, NE_out + ! read outgoing energy + table % inelastic_e_out(j,i) = XSS(lc + 1) - ! read outgoing angles for this outgoing energy - do k = 1, NMU - table % inelastic_mu(k,j,i) = XSS(lc + 1 + k) + ! read outgoing angles for this outgoing energy + do k = 1, NMU + table % inelastic_mu(k,j,i) = XSS(lc + 1 + k) + end do + + ! advance pointer + lc = lc + 1 + NMU end do - - ! advance pointer - lc = lc + 1 + NMU end do - end do + else if (table % secondary_mode == SAB_SECONDARY_CONT) then + ! Get the location pointers to each Ein's DistEnergySAB data + LOCC = get_real(NE_in) + ! Get the number of outgoing energies and allocate space accordingly + do i = 1, NE_in + NE_out = XSS(XSS_index + i - 1) + table % inelastic_data(i) % n_e_out = NE_out + allocate(table % inelastic_data(i) % e_out (NE_out)) + allocate(table % inelastic_data(i) % e_out_pdf (NE_out)) + allocate(table % inelastic_data(i) % e_out_cdf (NE_out)) + allocate(table % inelastic_data(i) % mu (NMU, NE_out)) + end do + + ! Now we can fill the inelastic_data(i) attributes + do i = 1, NE_in + XSS_index = LOCC(i) + NE_out = table % inelastic_data(i) % n_e_out + do j = 1, NE_out + table % inelastic_data(i) % e_out(j) = XSS(XSS_index + 1) + table % inelastic_data(i) % e_out_pdf(j) = XSS(XSS_index + 2) + table % inelastic_data(i) % e_out_cdf(j) = XSS(XSS_index + 3) + table % inelastic_data(i) % mu(:, j) = & + XSS(XSS_index + 4: XSS_index + 4 + NMU - 1) + XSS_index = XSS_index + 4 + NMU - 1 + end do + end do + end if ! read number of elastic energies and allocate arrays JXS4 = JXS(4) diff --git a/src/ace_header.F90 b/src/ace_header.F90 index 26fbf4cbca..e5bf1bb3a4 100644 --- a/src/ace_header.F90 +++ b/src/ace_header.F90 @@ -32,7 +32,7 @@ module ace_header type(Tab1) :: p_valid ! probability of law validity real(8), allocatable :: data(:) ! energy distribution data - ! For reactions that may have multiple energy distributions such as (n.2n), + ! For reactions that may have multiple energy distributions such as (n,2n), ! this pointer allows multiple laws to be stored type(DistEnergy), pointer :: next => null() @@ -143,6 +143,20 @@ module ace_header procedure :: clear => nuclide_clear ! Deallocates Nuclide end type Nuclide +!=============================================================================== +! DISTENERGYSAB contains the secondary energy/angle distributions for inelastic +! thermal scattering collisions which utilize a continuous secondary energy +! representation. +!=============================================================================== + + type DistEnergySab + integer :: n_e_out + real(8), allocatable :: e_out(:) + real(8), allocatable :: e_out_pdf(:) + real(8), allocatable :: e_out_cdf(:) + real(8), allocatable :: mu(:,:) + end type DistEnergySab + !=============================================================================== ! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off ! of light isotopes such as water, graphite, Be, etc @@ -163,11 +177,17 @@ module ace_header integer :: n_inelastic_e_in ! # of incoming E for inelastic integer :: n_inelastic_e_out ! # of outgoing E for inelastic integer :: n_inelastic_mu ! # of outgoing angles for inelastic - integer :: secondary_mode ! secondary mode (equal/skewed) + integer :: secondary_mode ! secondary mode (equal/skewed/continuous) real(8), allocatable :: inelastic_e_in(:) real(8), allocatable :: inelastic_sigma(:) + ! The following are used only if secondary_mode is 0 or 1 real(8), allocatable :: inelastic_e_out(:,:) real(8), allocatable :: inelastic_mu(:,:,:) + ! The following is used only if secondary_mode is 3 + ! The different implementation is necessary because the continuous + ! representation has a variable number of outgoing energy points for each + ! incoming energy + type(DistEnergySab), allocatable :: inelastic_data(:) ! One for each Ein ! Elastic scattering data integer :: elastic_mode ! elastic mode (discrete/exact) diff --git a/src/constants.F90 b/src/constants.F90 index 564e0baedd..6fea8cbc16 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -155,7 +155,8 @@ module constants ! Secondary energy mode for S(a,b) inelastic scattering integer, parameter :: & SAB_SECONDARY_EQUAL = 0, & ! Equally-likely outgoing energy bins - SAB_SECONDARY_SKEWED = 1 ! Skewed outgoing energy bins + SAB_SECONDARY_SKEWED = 1, & ! Skewed outgoing energy bins + SAB_SECONDARY_CONT = 2 ! Continuous, linear-linear interpolation ! Elastic mode for S(a,b) elastic scattering integer, parameter :: & From 8f5ce5ed5ca6ff4913c7e8b80def71c673fd98a2 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 15 Dec 2013 13:45:18 -0500 Subject: [PATCH 177/192] Corrected an error message in physics.F90:sample_energy, which referred to Law 44 instead of the correct Law 61. --- src/physics.F90 | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/physics.F90 b/src/physics.F90 index fb1f891943..5771377c86 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -47,7 +47,7 @@ contains if (verbosity >= 10 .or. trace) then message = " " // trim(reaction_name(p % event_MT)) // " with " // & trim(adjustl(nuclides(p % event_nuclide) % name)) // & - ". Energy = " // trim(to_str(p % E * 1e6_8)) // " eV." + ". Energy = " // trim(to_str(p % E * 1e6_8)) // " eV." call write_message() end if @@ -158,7 +158,7 @@ contains call fatal_error() end if - ! Find atom density + ! Find atom density i_nuclide = mat % nuclide(i) atom_density = mat % atom_density(i) @@ -209,7 +209,7 @@ contains i_reaction = nuc % index_fission(1) return end if - + ! Get grid index and interpolatoin factor and sample fission cdf i_grid = micro_xs(i_nuclide) % index_grid f = micro_xs(i_nuclide) % interp_factor @@ -226,7 +226,7 @@ contains if (i_grid < rxn % threshold) cycle ! add to cumulative probability - prob = prob + ((ONE - f)*rxn%sigma(i_grid - rxn%threshold + 1) & + prob = prob + ((ONE - f)*rxn%sigma(i_grid - rxn%threshold + 1) & + f*(rxn%sigma(i_grid - rxn%threshold + 2))) ! Create fission bank sites if fission occus @@ -382,7 +382,7 @@ contains if (i_grid < rxn % threshold) cycle ! add to cumulative probability - prob = prob + ((ONE - f)*rxn%sigma(i_grid - rxn%threshold + 1) & + prob = prob + ((ONE - f)*rxn%sigma(i_grid - rxn%threshold + 1) & + f*(rxn%sigma(i_grid - rxn%threshold + 2))) end do @@ -513,7 +513,7 @@ contains f = ZERO else i = binary_search(sab % elastic_e_in, sab % n_elastic_e_in, E) - f = (E - sab%elastic_e_in(i)) / & + f = (E - sab%elastic_e_in(i)) / & (sab%elastic_e_in(i+1) - sab%elastic_e_in(i)) end if @@ -563,7 +563,7 @@ contains f = ZERO else i = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E) - f = (E - sab%inelastic_e_in(i)) / & + f = (E - sab%inelastic_e_in(i)) / & (sab%inelastic_e_in(i+1) - sab%inelastic_e_in(i)) end if @@ -974,7 +974,7 @@ contains E_cm = E ! determine outgoing energy in lab - E = E_cm + (E_in + TWO * mu * (A+ONE) * sqrt(E_in * E_cm)) & + E = E_cm + (E_in + TWO * mu * (A+ONE) * sqrt(E_in * E_cm)) & / ((A+ONE)*(A+ONE)) ! determine outgoing angle in lab @@ -1037,7 +1037,7 @@ contains r = ONE else i = binary_search(rxn % adist % energy, n, E) - r = (E - rxn % adist % energy(i)) / & + r = (E - rxn % adist % energy(i)) / & (rxn % adist % energy(i+1) - rxn % adist % energy(i)) end if @@ -1166,7 +1166,7 @@ contains end if end function rotate_angle - + !=============================================================================== ! SAMPLE_ENERGY samples an outgoing energy distribution, either for a secondary ! neutron from a collision or for a prompt/delayed fission neutron @@ -1322,7 +1322,7 @@ contains ! ======================================================================= ! CONTINUOUS TABULAR DISTRIBUTION - ! read number of interpolation regions and incoming energies + ! read number of interpolation regions and incoming energies NR = int(edist % data(1)) NE = int(edist % data(2 + 2*NR)) if (NR == 1) then @@ -1348,7 +1348,7 @@ contains r = ONE else i = binary_search(edist % data(lc+1:lc+NE), NE, E_in) - r = (E_in - edist%data(lc+i)) / & + r = (E_in - edist%data(lc+i)) / & (edist%data(lc+i+1) - edist%data(lc+i)) end if @@ -1452,7 +1452,7 @@ contains ! ======================================================================= ! MAXWELL FISSION SPECTRUM - ! read number of interpolation regions and incoming energies + ! read number of interpolation regions and incoming energies NR = int(edist % data(1)) NE = int(edist % data(2 + 2*NR)) @@ -1484,7 +1484,7 @@ contains ! ======================================================================= ! EVAPORATION SPECTRUM - ! read number of interpolation regions and incoming energies + ! read number of interpolation regions and incoming energies NR = int(edist % data(1)) NE = int(edist % data(2 + 2*NR)) @@ -1565,7 +1565,7 @@ contains call fatal_error() end if - ! read number of interpolation regions and incoming energies + ! read number of interpolation regions and incoming energies NR = int(edist % data(1)) NE = int(edist % data(2 + 2*NR)) if (NR > 0) then @@ -1587,7 +1587,7 @@ contains r = ONE else i = binary_search(edist % data(lc+1:lc+NE), NE, E_in) - r = (E_in - edist%data(lc+i)) / & + r = (E_in - edist%data(lc+i)) / & (edist%data(lc+i+1) - edist%data(lc+i)) end if @@ -1714,11 +1714,11 @@ contains if (.not. present(mu_out)) then ! call write_particle_restart() - message = "Law 44 called without giving mu_out as argument." + message = "Law 61 called without giving mu_out as argument." call fatal_error() end if - ! read number of interpolation regions and incoming energies + ! read number of interpolation regions and incoming energies NR = int(edist % data(1)) NE = int(edist % data(2 + 2*NR)) if (NR > 0) then @@ -1740,7 +1740,7 @@ contains r = ONE else i = binary_search(edist % data(lc+1:lc+NE), NE, E_in) - r = (E_in - edist%data(lc+i)) / & + r = (E_in - edist%data(lc+i)) / & (edist%data(lc+i+1) - edist%data(lc+i)) end if From 1dd13d80641407ca8217635f3a8ecbdb93908e16 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 15 Dec 2013 14:30:29 -0500 Subject: [PATCH 178/192] Added code to physics.F90%sab_scatter() to sample continuous secondary energy distributions; still have to test. --- src/physics.F90 | 190 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 146 insertions(+), 44 deletions(-) diff --git a/src/physics.F90 b/src/physics.F90 index 5771377c86..372d67bd3e 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -490,13 +490,24 @@ contains integer :: k ! outgoing cosine bin integer :: n_energy_out ! number of outgoing energy bins real(8) :: f ! interpolation factor - real(8) :: r ! used for skewed sampling + real(8) :: r ! used for skewed sampling & continuous real(8) :: E_ij ! outgoing energy j for E_in(i) real(8) :: E_i1j ! outgoing energy j for E_in(i+1) real(8) :: mu_ijk ! outgoing cosine k for E_in(i) and E_out(j) real(8) :: mu_i1jk ! outgoing cosine k for E_in(i+1) and E_out(j) real(8) :: prob ! probability for sampling Bragg edge type(SAlphaBeta), pointer, save :: sab => null() + ! Following are needed only for SAB_SECONDARY_CONT scattering + integer :: l ! sampled incoming E bin (is i or i + 1) + real(8) :: E_i_1, E_i_J ! endpoints on outgoing grid i + real(8) :: E_i1_1, E_i1_J ! endpoints on outgoing grid i+1 + real(8) :: E_1, E_J ! endpoints interpolated between i and i+1 + real(8) :: E_l_j, E_l_j1 ! adjacent E on outgoing grid l + real(8) :: p_l_j, p_l_j1 ! adjacent p on outgoing grid l + real(8) :: c_j, c_j1 ! cumulative probability + real(8) :: frac ! interpolation factor on outgoing energy + real(8) :: r1 ! RNG for outgoing energy + !$omp threadprivate(sab) ! Get pointer to S(a,b) table @@ -554,8 +565,7 @@ contains ! Outgoing energy is same as incoming energy -- no need to do anything else - ! Determine number of outgoing energy and angle bins - n_energy_out = sab % n_inelastic_e_out + ! Perform inelastic calculations ! Get index and interpolation factor for inelastic grid if (E < sab % inelastic_e_in(1)) then @@ -570,54 +580,146 @@ contains ! Now that we have an incoming energy bin, we need to determine the ! outgoing energy bin. This will depend on the "secondary energy ! mode". If the mode is 0, then the outgoing energy bin is chosen from a - ! set of equally-likely bins. However, if the mode is 1, then the first + ! set of equally-likely bins. If the mode is 1, then the first ! two and last two bins are skewed to have lower probabilities than the ! other bins (0.1 for the first and last bins and 0.4 for the second and - ! second to last bins, relative to a normal bin probability of 1) + ! second to last bins, relative to a normal bin probability of 1). + ! Finally, if the mode is 2, then a continuous distribution (with + ! accompanying PDF and CDF is utilized) - if (sab % secondary_mode == SAB_SECONDARY_EQUAL) then - ! All bins equally likely - j = 1 + int(prn() * n_energy_out) - elseif (sab % secondary_mode == SAB_SECONDARY_SKEWED) then - r = prn() * (n_energy_out - 3) - if (r > ONE) then - ! equally likely N-4 middle bins - j = int(r) + 2 - elseif (r > 0.6) then - ! second to last bin has relative probability of 0.4 - j = n_energy_out - 1 - elseif (r > 0.5) then - ! last bin has relative probability of 0.1 - j = n_energy_out - elseif (r > 0.1) then - ! second bin has relative probability of 0.4 - j = 2 - else - ! first bin has relative probability of 0.1 - j = 1 + if ((sab % secondary_mode == SAB_SECONDARY_EQUAL) .or. & + (sab % secondary_mode == SAB_SECONDARY_SKEWED)) then + if (sab % secondary_mode == SAB_SECONDARY_EQUAL) then + ! All bins equally likely + + j = 1 + int(prn() * sab % n_inelastic_e_out) + elseif (sab % secondary_mode == SAB_SECONDARY_SKEWED) then + ! Distribution skewed away from edge points + + ! Determine number of outgoing energy and angle bins + n_energy_out = sab % n_inelastic_e_out + + r = prn() * (n_energy_out - 3) + if (r > ONE) then + ! equally likely N-4 middle bins + j = int(r) + 2 + elseif (r > 0.6) then + ! second to last bin has relative probability of 0.4 + j = n_energy_out - 1 + elseif (r > 0.5) then + ! last bin has relative probability of 0.1 + j = n_energy_out + elseif (r > 0.1) then + ! second bin has relative probability of 0.4 + j = 2 + else + ! first bin has relative probability of 0.1 + j = 1 + end if end if + + ! Determine outgoing energy corresponding to E_in(i) and E_in(i+1) + E_ij = sab % inelastic_e_out(j,i) + E_i1j = sab % inelastic_e_out(j,i+1) + + ! Outgoing energy + E = (1 - f)*E_ij + f*E_i1j + + ! Sample outgoing cosine bin + k = 1 + int(prn() * sab % n_inelastic_mu) + + ! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1) + mu_ijk = sab % inelastic_mu(k,j,i) + mu_i1jk = sab % inelastic_mu(k,j,i+1) + + ! Cosine of angle between incoming and outgoing neutron + mu = (1 - f)*mu_ijk + f*mu_i1jk + + else if (sab % secondary_mode == SAB_SECONDARY_CONT) then + ! Continuous secondary energy - this is to be similar to + ! Law 61 interpolation on outgoing energy + + ! Sample between ith and (i+1)th bin + r = prn() + if (f > r) then + l = i + 1 + else + l = i + end if + + ! Determine endpoints on grid i + n_energy_out = sab % inelastic_data(i) % n_e_out + E_i_1 = sab % inelastic_data(i) % e_out(1) + E_i_J = sab % inelastic_data(i) % e_out(n_energy_out) + + ! Determine endpoints on grid i + 1 + n_energy_out = sab % inelastic_data(i + 1) % n_e_out + E_i1_1 = sab % inelastic_data(i + 1) % e_out(1) + E_i1_J = sab % inelastic_data(i + 1) % e_out(n_energy_out) + + E_1 = E_i_1 + f * (E_i1_1 - E_i_1) + E_J = E_i_J + f * (E_i1_J - E_i_J) + + ! Determine outgoing energy bin + ! (First reset n_energy_out to the right value) + n_energy_out = sab % inelastic_data(l) % n_e_out + r1 = prn() + c_j = sab % inelastic_data(l) % e_out_cdf(1) + do j = 1, n_energy_out - 1 + c_j1 = sab % inelastic_data(l) % e_out_cdf(j + 1) + if (r1 < c_j1) exit + c_j = c_j1 + end do + + ! check to make sure k is <= n_energy_out - 1 + j = min(j, n_energy_out - 1) + + ! Get the data to interpolate between + E_l_j = sab % inelastic_data(l) % e_out(j) + p_l_j = sab % inelastic_data(l) % e_out_pdf(j) + + ! Next part assumes linear-linear interpolation in standard + E_l_j1 = sab % inelastic_data(l) % e_out(j + 1) + p_l_j1 = sab % inelastic_data(l) % e_out_pdf(j + 1) + + ! Find secondary energy (variable E) + frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j) + if (frac == ZERO) then + E = E_l_j + (r1 - c_j) / p_l_j + else + E = E_l_j + (sqrt(max(ZERO, p_l_j * p_l_j + & + TWO * frac * (r1 - c_j))) - p_l_j) / frac + end if + + ! Now interpolate between incident energy bins i and i + 1 + if (l == i) then + E = E_1 + (E - E_i_1) * (E_J - E_1) / (E_i_J - E_i_1) + else + E = E_1 + (E - E_i1_1) * (E_J - E_1) / (E_i1_J - E_i1_1) + end if + + ! Find correlated angular distribution for closest outgoing energy bin + if (r1 - c_j < c_j1 - r1) then + j = j + else + j = j + 1 + end if + + ! Sample outgoing cosine bin + k = 1 + int(prn() * sab % n_inelastic_mu) + + ! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1) + mu_ijk = sab % inelastic_data(i) % mu(k, j) + mu_i1jk = sab % inelastic_data(i) % mu(k, j) + + ! Cosine of angle between incoming and outgoing neutron + mu = (ONE - f) * mu_ijk + f * mu_i1jk + else message = "Invalid secondary energy mode on S(a,b) table " // & trim(sab % name) - end if - - ! Determine outgoing energy corresponding to E_in(i) and E_in(i+1) - E_ij = sab % inelastic_e_out(j,i) - E_i1j = sab % inelastic_e_out(j,i+1) - - ! Outgoing energy - E = (1 - f)*E_ij + f*E_i1j - - ! Sample outgoing cosine bin - k = 1 + int(prn() * sab % n_inelastic_mu) - - ! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1) - mu_ijk = sab % inelastic_mu(k,j,i) - mu_i1jk = sab % inelastic_mu(k,j,i+1) - - ! Cosine of angle between incoming and outgoing neutron - mu = (1 - f)*mu_ijk + f*mu_i1jk - end if + end if ! (inelastic secondary energy treatment) + end if ! (elastic or inelastic) ! change direction of particle uvw = rotate_angle(uvw, mu) From 56d3d382826d9a3df29e60f51d6e5abcdf211f98 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 15 Dec 2013 15:58:55 -0500 Subject: [PATCH 179/192] Modified code after some testing to not perform interpolation on Ein and Eout when finding mu; instead the closest distribution is chosen, as is done for file 4 angular distributions in sample_angle. --- src/physics.F90 | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/physics.F90 b/src/physics.F90 index 372d67bd3e..2d3cfeb39f 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -698,7 +698,7 @@ contains E = E_1 + (E - E_i1_1) * (E_J - E_1) / (E_i1_J - E_i1_1) end if - ! Find correlated angular distribution for closest outgoing energy bin + ! Find angular distribution for closest outgoing energy bin if (r1 - c_j < c_j1 - r1) then j = j else @@ -708,12 +708,9 @@ contains ! Sample outgoing cosine bin k = 1 + int(prn() * sab % n_inelastic_mu) - ! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1) - mu_ijk = sab % inelastic_data(i) % mu(k, j) - mu_i1jk = sab % inelastic_data(i) % mu(k, j) - - ! Cosine of angle between incoming and outgoing neutron - mu = (ONE - f) * mu_ijk + f * mu_i1jk + ! Will use mu from the randomly chosen incoming and closest outgoing + ! energy bins + mu = sab % inelastic_data(l) % mu(k, j) else message = "Invalid secondary energy mode on S(a,b) table " // & From b64644407e5c04aaefe865ceb3a43be288e2b72c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 17 Dec 2013 21:19:42 -0500 Subject: [PATCH 180/192] Two small fixes in installation instructions. --- docs/source/usersguide/install.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index c05782e71d..164b0151be 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -78,7 +78,7 @@ Prerequisites ./configure --prefix=/opt/hdf5/1.8.11-gnu --enable-fortran \ --enable-fortran2003 --enable-parallel - You may omit '--enable-parallel' if you want to compile HDF5_ in serial. + You may omit ``--enable-parallel`` if you want to compile HDF5_ in serial. * PETSc_ for CMFD acceleration @@ -93,7 +93,7 @@ Prerequisites --with-fortran-datatypes The BLAS/LAPACK library is not required to be downloaded and can be linked - explicitly (e.g., Intel MLK library). + explicitly (e.g., Intel MKL library). * git_ version control software for obtaining source code From 96999e00c6eb2ab1172c590268bc79155fea9d84 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Dec 2013 11:18:58 -0500 Subject: [PATCH 181/192] Make sure uvw is initialized when sampling source position. --- src/source.F90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/source.F90 b/src/source.F90 index 4fcdd0074a..231a23cc5c 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -97,6 +97,7 @@ contains ! Fill p with needed data p % coord0 % xyz = site % xyz + p % coord0 % uvw = [ ONE, ZERO, ZERO ] ! Now search to see if location exists in geometry call find_cell(p, found) From d28750c34fdec59b3ce955865299eafe980cba16 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Dec 2013 11:48:47 -0500 Subject: [PATCH 182/192] Fix bug in convert_xsdir.py. --- src/utils/convert_xsdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/convert_xsdir.py b/src/utils/convert_xsdir.py index fa29f46eca..32315f1ed5 100755 --- a/src/utils/convert_xsdir.py +++ b/src/utils/convert_xsdir.py @@ -71,7 +71,7 @@ class Xsdir(object): # Handle continuation lines while words[-1] == '+': extraWords = self.f.readline().split() - words = words + extraWords + words = words[:-1] + extraWords assert len(words) >= 7 # Create XsdirTable object and add to line From d0028d388dc68b61efa520ac8ddc937efa8eb6a3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Dec 2013 17:10:31 -0500 Subject: [PATCH 183/192] Allow mixed ascii/binary cross_sections.xml. --- src/input_xml.F90 | 14 +++++++++++++- src/relaxng/cross_sections.rnc | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 8cd93f0e10..63fb2163e3 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2938,7 +2938,19 @@ contains end if ! set filetype, record length, and number of entries - listing % filetype = filetype + if (check_for_node(node_ace, "filetype")) then + temp_str = '' + call get_node_value(node_ace, "filetype", temp_str) + if (temp_str == 'ascii') then + listing % filetype = ASCII + else if (temp_str == 'binary') then + listing % filetype = BINARY + end if + else + listing % filetype = filetype + end if + + ! Set record length and entries for binary files if (filetype == BINARY) then listing % recl = recl listing % entries = entries diff --git a/src/relaxng/cross_sections.rnc b/src/relaxng/cross_sections.rnc index e82bb986fb..75c0ea29c2 100644 --- a/src/relaxng/cross_sections.rnc +++ b/src/relaxng/cross_sections.rnc @@ -10,7 +10,9 @@ element cross_sections { (element temperature { xsd:double } | attribute temperature { xsd:double }) & (element path { xsd:string { maxLength = "255" } } | attribute path { xsd:string { maxLength = "255" } }) & - (element location { xsd:int } | attribute location { xsd:int })? + (element location { xsd:int } | attribute location { xsd:int })? & + (element filetype { ( "ascii" | "binary" ) } | + attribute filetype { ( "ascii" | "binary" ) })? }* & element directory { xsd:string { maxLength = "255" } }? & From 4a8157f28081eb9b470e408bda3db1cb65b71dff Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Fri, 20 Dec 2013 07:10:38 -0500 Subject: [PATCH 184/192] Included explicit integer casting in the reading of thermal data. --- src/ace.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ace.F90 b/src/ace.F90 index ff8582470d..aa6de5ab64 100644 --- a/src/ace.F90 +++ b/src/ace.F90 @@ -1195,7 +1195,7 @@ contains integer :: NE_out ! number of outgoing energies integer :: NMU ! number of outgoing angles integer :: JXS4 ! location of elastic energy table - real(8), allocatable :: LOCC(:) ! Location of inelastic data + integer(8), allocatable :: LOCC(:) ! Location of inelastic data ! read secondary energy mode for inelastic scattering table % secondary_mode = NXS(7) @@ -1252,10 +1252,10 @@ contains end do else if (table % secondary_mode == SAB_SECONDARY_CONT) then ! Get the location pointers to each Ein's DistEnergySAB data - LOCC = get_real(NE_in) + LOCC = get_int(NE_in) ! Get the number of outgoing energies and allocate space accordingly do i = 1, NE_in - NE_out = XSS(XSS_index + i - 1) + NE_out = int(XSS(XSS_index + i - 1)) table % inelastic_data(i) % n_e_out = NE_out allocate(table % inelastic_data(i) % e_out (NE_out)) allocate(table % inelastic_data(i) % e_out_pdf (NE_out)) From 4bda7f2054fe8174a6eacf72d367499307227a89 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Fri, 20 Dec 2013 09:44:09 -0500 Subject: [PATCH 185/192] Updated theory manual to incorporate the new thermal scattering sampling technique --- docs/source/methods/physics.rst | 64 ++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/docs/source/methods/physics.rst b/docs/source/methods/physics.rst index c79144dfe3..fc82dc1a98 100644 --- a/docs/source/methods/physics.rst +++ b/docs/source/methods/physics.rst @@ -790,6 +790,7 @@ outgoing angle is \mu = \frac{1}{A} \ln \left ( \xi_4 e^A + (1 - \xi_4) e^{-A} \right ). +.. _ace-law-61: ACE Law 61 - Correlated Energy and Angle Distribution +++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -952,7 +953,7 @@ as v_n \bar{\sigma} (v_n, T) = \int d\mathbf{v}_T v_r \sigma(v_r) M (\mathbf{v}_T) - + where :math:`v_n` is the magnitude of the velocity of the neutron, :math:`\bar{\sigma}` is an effective cross section, :math:`T` is the temperature of the target material, :math:`\mathbf{v}_T` is the velocity of the target @@ -1321,7 +1322,7 @@ given analytically by \mu = 1 - \frac{E_i}{E} -where :math:`E_i` is the energy of the Bragg edge that scattered the neutron. +where :math:`E_i` is the energy of the Bragg edge that scattered the neutron. Outgoing Angle for Incoherent Elastic Scattering ------------------------------------------------ @@ -1348,18 +1349,24 @@ where the interpolation factor is defined as Outgoing Energy and Angle for Inelastic Scattering -------------------------------------------------- -On each |sab| table, there is a correlated angle-energy secondary distribution -for neutron thermal inelastic scattering. While the documentation for the ACE -format implies that there are a series of equiprobable outgoing energies, the -outgoing energies may have non-uniform probability distribution. In particular, -if the thermal data were processed with :math:`iwt = 0` in NJOY, then the first -and last outgoing energies have a relative probability of 1, the second and -second to last energies have a relative probability of 4, and all other energies -have a relative probability of 10. The procedure to determine the outgoing +Each |sab| table provides a correlated angle-energy secondary distribution for +neutron thermal inelastic scattering. There are three representations used +in the ACE thermal scattering data: equiprobable discrete outgoing +energies, non-uniform yet still discrete outgoing energies, and continuous +outgoing energies with corresponding probability and cumulative distribution +functions provided in tabular format. These three representations all +represent the angular distribution in a common format, using a series of +discrete equiprobable outgoing cosines. + +Equi-Probable Outgoing Energies ++++++++++++++++++++++++++++++++ + +If the thermal data was processed with :math:`iwt = 1` in NJOY, then the +outgoing energy spectra is represented in the ACE data as a set of discrete and +equiprobable outgoing energies. The procedure to determine the outgoing energy and angle is as such. First, the interpolation factor is determined from -equation :eq:`sab-interpolation-factor`. Then, an outgoing energy bin is sampled -either from a uniform distribution or from the aforementioned skewed -distribution. The outgoing energy is then interpolated between values +equation :eq:`sab-interpolation-factor`. Then, an outgoing energy bin is +sampled from ths uniform distribution and then interpolated between values corresponding to neighboring incoming energies: .. math:: @@ -1380,6 +1387,37 @@ uniformly and then the final cosine is interpolated on the incoming energy grid: where :math:`\mu_{i,j,k}` is the k-th outgoing cosine corresponding to the j-th outgoing energy and the i-th incoming energy. +Skewed Equi-Probable Outgoing Energies +++++++++++++++++++++++++++++++++++++++ + +If the thermal data was processed with :math:`iwt=0` in NJOY, then the +outgoing energy spectra is represented in the ACE data according to the +following: the first and last outgoing energies have a relative probability of +1, the second and second-to-last energies have a relative probability of 4, and +all other energies have a relative probability of 10. The procedure to +determine the outgoing energy and angle is similar to the method discussed +above, except that the sampled probability distribution is now skewed +accordingly. + +Continuous Outgoing Energies +++++++++++++++++++++++++++++ + +If the thermal data was processed with :math:`iwt=2` in NJOY, then the +outgoing energy spectra is represented by a continuous outgoing energy spectra +in tabular form with linear-linear interpolation. The sampling of the outgoing +energy portion of this format is very similar to :ref:`ACE Law 61`, +but the sampling of the correlated angle is performed as it was in the other +two representations discussed in this sub-section. In the Law 61 algorithm, +we found an interpolation factor :math:`f`, statistically sampled an incoming +energy bin :math:`\ell`, and sampled an outgoing energy bin :math:`j` based on +the tabulated cumulative distribution function. Once the outgoing energy has +been determined with equation :eq:`ace-law-4-energy`, we then need to decide +which angular distribution data to use. Like the linear-linear interpolation +case in Law 61, the angular distribution closest to the sampled value of the +cumulative distribution function for the outgoing energy is utilized. The +actual algorithm utilized to sample the outgoing angle is shown in equation +:eq:`inelastic-angle`. + .. _probability_tables: ---------------------------------------------- From 402df187428ebf1f985b6ac73cd2252ff0ca434d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 22 Dec 2013 20:41:49 -0500 Subject: [PATCH 186/192] Fixed a typo in theory documentation. --- docs/source/methods/physics.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/methods/physics.rst b/docs/source/methods/physics.rst index fc82dc1a98..54dc913455 100644 --- a/docs/source/methods/physics.rst +++ b/docs/source/methods/physics.rst @@ -1363,10 +1363,10 @@ Equi-Probable Outgoing Energies If the thermal data was processed with :math:`iwt = 1` in NJOY, then the outgoing energy spectra is represented in the ACE data as a set of discrete and -equiprobable outgoing energies. The procedure to determine the outgoing -energy and angle is as such. First, the interpolation factor is determined from +equiprobable outgoing energies. The procedure to determine the outgoing energy +and angle is as such. First, the interpolation factor is determined from equation :eq:`sab-interpolation-factor`. Then, an outgoing energy bin is -sampled from ths uniform distribution and then interpolated between values +sampled from a uniform distribution and then interpolated between values corresponding to neighboring incoming energies: .. math:: From 5db982406428e40e09154b530b0de7ee7b7ed00b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 22 Dec 2013 20:57:36 -0500 Subject: [PATCH 187/192] Fix particle track bug when using Intel compiler. --- src/input_xml.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 63fb2163e3..3ce3c9d0d4 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -474,7 +474,8 @@ contains allocate(temp_int_array(n_tracks)) call get_node_array(doc, "track", temp_int_array) - ! Reshape into track_identifiers -- note automatic array allocation + ! Reshape into track_identifiers + allocate(track_identifiers(3, n_tracks/3)) track_identifiers = reshape(temp_int_array, [3, n_tracks/3]) end if From 73c542e2016e7dc7290973671bf2151e9518d88c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 26 Dec 2013 22:44:03 -0500 Subject: [PATCH 188/192] Display number of OpenMP threads under title banner. --- src/output.F90 | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/output.F90 b/src/output.F90 index 1507aa4cd5..677c35396c 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -31,6 +31,10 @@ contains subroutine title() +#ifdef OPENMP + use omp_lib +#endif + write(UNIT=OUTPUT_UNIT, FMT='(/11(A/))') & ' .d88888b. 888b d888 .d8888b.', & ' d88P" "Y88b 8888b d8888 d88P Y88b', & @@ -46,25 +50,31 @@ contains ! Write version information write(UNIT=OUTPUT_UNIT, FMT=*) & - ' Copyright: 2011-2013 Massachusetts Institute of Technology' + ' Copyright: 2011-2013 Massachusetts Institute of Technology' write(UNIT=OUTPUT_UNIT, FMT=*) & - ' License: http://mit-crpg.github.io/openmc/license.html' - write(UNIT=OUTPUT_UNIT, FMT='(6X,"Version:",7X,I1,".",I1,".",I1)') & + ' License: http://mit-crpg.github.io/openmc/license.html' + write(UNIT=OUTPUT_UNIT, FMT='(6X,"Version:",8X,I1,".",I1,".",I1)') & VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE #ifdef GIT_SHA1 - write(UNIT=OUTPUT_UNIT, FMT='(6X,"Git SHA1:",6X,A)') GIT_SHA1 + write(UNIT=OUTPUT_UNIT, FMT='(6X,"Git SHA1:",7X,A)') GIT_SHA1 #endif ! Write the date and time - write(UNIT=OUTPUT_UNIT, FMT='(6X,"Date/Time:",5X,A)') & + write(UNIT=OUTPUT_UNIT, FMT='(6X,"Date/Time:",6X,A)') & time_stamp() #ifdef MPI ! Write number of processors - write(UNIT=OUTPUT_UNIT, FMT='(6X,"MPI Processes:",1X,A)') & + write(UNIT=OUTPUT_UNIT, FMT='(6X,"MPI Processes:",2X,A)') & trim(to_str(n_procs)) #endif +#ifdef OPENMP + ! Write number of OpenMP threads + write(UNIT=OUTPUT_UNIT, FMT='(6X,"OpenMP Threads:",1X,A)') & + trim(to_str(omp_get_max_threads())) +#endif + end subroutine title !=============================================================================== From f3f024bbc60f499b5ca38f4e4f156f1f2df1a95f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 26 Dec 2013 23:17:03 -0500 Subject: [PATCH 189/192] Avoid passing temporary arrays to write_data. --- src/hdf5_summary.F90 | 5 +++-- src/state_point.F90 | 6 +++--- src/track_output.F90 | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/hdf5_summary.F90 b/src/hdf5_summary.F90 index 915e6af070..c9e367200c 100644 --- a/src/hdf5_summary.F90 +++ b/src/hdf5_summary.F90 @@ -100,6 +100,7 @@ contains integer :: i, j, k, m integer :: n_x, n_y, n_z + integer :: length(3) integer, allocatable :: lattice_universes(:,:,:) type(Cell), pointer :: c => null() type(Surface), pointer :: s => null() @@ -312,8 +313,8 @@ contains end do end do end do - call su % write_data(lattice_universes, "universes", & - length=(/n_x, n_y, n_z/), & + length = [n_x, n_y, n_z] + call su % write_data(lattice_universes, "universes", length=length, & group="geometry/lattices/lattice " // trim(to_str(lat % id))) deallocate(lattice_universes) diff --git a/src/state_point.F90 b/src/state_point.F90 index 3793923bb2..e13cd8f486 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -464,6 +464,7 @@ contains character(19) :: current_time integer :: i integer :: j + integer :: length(4) integer :: int_array(3) integer, allocatable :: temp_array(:) real(8) :: real_array(3) @@ -542,10 +543,9 @@ contains call sp % read_data(cmfd % indices, "indicies", length=4, group="cmfd") call sp % read_data(cmfd % k_cmfd, "k_cmfd", length=restart_batch, & group="cmfd") + length = cmfd % indices([4,1,2,3]) call sp % read_data(cmfd % cmfd_src, "cmfd_src", & - length=(/cmfd % indices(4), cmfd % indices(1), & - cmfd % indices(2), cmfd % indices(3)/), & - group="cmfd") + length=length, group="cmfd") call sp % read_data(cmfd % entropy, "cmfd_entropy", & length=restart_batch, group="cmfd") call sp % read_data(cmfd % balance, "cmfd_balance", & diff --git a/src/track_output.F90 b/src/track_output.F90 index 021cbb8ca6..79a2809066 100644 --- a/src/track_output.F90 +++ b/src/track_output.F90 @@ -56,6 +56,7 @@ contains subroutine finalize_particle_track(p) type(Particle), intent(in) :: p + integer :: length(2) character(MAX_FILE_LEN) :: fname type(BinaryOutput) :: binout @@ -69,7 +70,8 @@ contains // '.binary' #endif call binout % file_create(fname) - call binout % write_data(coords, 'coordinates', length=(/3, n_tracks/)) + length = [3, n_tracks] + call binout % write_data(coords, 'coordinates', length=length) call binout % file_close() deallocate(coords) end subroutine finalize_particle_track From 9a0e50574cfb2c8b7220c4ede790e65d61bcf776 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 27 Dec 2013 16:33:25 -0500 Subject: [PATCH 190/192] Fix bug causing CMFD to report incorrect results when using threads. --- src/cmfd_input.F90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 120db5f649..6f259f9497 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -577,7 +577,9 @@ contains end do ! Put cmfd tallies into active tally array and turn tallies on +!$omp parallel call setup_active_cmfdtallies() +!$omp end parallel tallies_on = .true. end subroutine create_cmfd_tally From 415e5da764cdc6d05dc43402b958c9e74d359048 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 2 Jan 2014 15:44:48 -0500 Subject: [PATCH 191/192] Added description of UFS method in documentation. --- docs/source/methods/eigenvalue.rst | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/source/methods/eigenvalue.rst b/docs/source/methods/eigenvalue.rst index a5ba0bf459..fe99ba22ec 100644 --- a/docs/source/methods/eigenvalue.rst +++ b/docs/source/methods/eigenvalue.rst @@ -108,6 +108,40 @@ at plots of :math:`k_{eff}` and the Shannon entropy. A number of methods have been proposed (see e.g. [Romano]_, [Ueki]_), but each of these is not without problems. +--------------------------- +Uniform Fission Site Method +--------------------------- + +Generally speaking, the variance of a Monte Carlo tally will be inversely +proportional to the number of events that score to the tally. In a reactor +problem, this implies that regions with low relative power density will have +higher variance that regions with high relative power density. One method to +circumvent the uneven distribution of relative errors is the uniform fission +site (UFS) method introduced by [Sutton]_. In this method, the portion of the +problem containing fissionable material is subdivided into a number of cells +(typically using a structured mesh). Rather than producing + +.. math:: + + m = \frac{w}{k} \frac{\nu\Sigma_f}{\Sigma_t} + +fission sites at each collision where :math:`w` is the weight of the neutron, +:math:`k` is the previous-generation estimate of the neutron multiplication +factor, :math:`\nu\Sigma_f` is the neutron production cross section, and +:math:`\Sigma_t` is the total cross section, in the UFS method we produce + +.. math:: + + m_{UFS} = \frac{w}{k} \frac{\nu\Sigma_f}{\Sigma_t} \frac{v_i}{s_i} + +fission sites at each collision where :math:`v_i` is the fraction of the total +volume occupied by cell :math:`i` and :math:`s_i` is the fraction of the fission +source contained in cell :math:`i`. To ensure that no bias is introduced, the +weight of each fission site stored in the fission bank is :math:`s_i/v_i` rather +than unity. By ensuring that the expected number of fission sites in each mesh +cell is constant, the collision density across all cells, and hence the variance +of tallies, is more uniform than it would be otherwise. + .. _Shannon entropy: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-06-3737_entropy.pdf .. [Lieberoth] J. Lieberoth, "A Monte Carlo Technique to Solve the Static @@ -119,5 +153,9 @@ problems. *Proc. International Conference on Mathematics, Computational Methods, and Reactor Physics*, Saratoga Springs, New York (2009). +.. [Sutton] Daniel J. Kelly, Thomas M. Sutton, and Stephen C. Wilson, "MC21 + Analysis of the Nuclear Energy Agency Monte Carlo Performance Benchmark + Problem," *Proc. PHYSOR 2012*, Knoxville, Tennessee, Apr. 15--20 (2012). + .. [Ueki] Taro Ueki, "On-the-Fly Judgments of Monte Carlo Fission Source Convergence," *Trans. Am. Nucl. Soc.*, **98**, 512 (2008). From 0f628689132f503060c06f1419d7c3c487d0f9a3 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Fri, 3 Jan 2014 14:20:54 -0500 Subject: [PATCH 192/192] Made some revisions to plot_mesh_tally and statepoint for usability purposes. --- src/utils/plot_mesh_tally.py | 340 ++++++++++++++++++++--------------- src/utils/statepoint.py | 33 ++-- 2 files changed, 208 insertions(+), 165 deletions(-) diff --git a/src/utils/plot_mesh_tally.py b/src/utils/plot_mesh_tally.py index 04b6b4592a..c70b291653 100755 --- a/src/utils/plot_mesh_tally.py +++ b/src/utils/plot_mesh_tally.py @@ -16,123 +16,161 @@ from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as Naviga import numpy as np class AppForm(QMainWindow): - def __init__(self, parent=None): + def __init__(self, argv, parent=None): QMainWindow.__init__(self, parent) # Read data from source or leakage fraction file - self.get_file_data() - self.main_frame = QWidget() - self.setCentralWidget(self.main_frame) - - # Create the Figure, Canvas, and Axes - self.dpi = 100 - self.fig = Figure((5.0, 15.0), dpi=self.dpi) - self.canvas = FigureCanvas(self.fig) - self.canvas.setParent(self.main_frame) - self.axes = self.fig.add_subplot(111) - - # Create the navigation toolbar, tied to the canvas - self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) + self.all_good = False + while not self.all_good: + if len(argv) > 1: + cl_file = str(argv[1]) + else: + cl_file = None + self.get_file_data(cl_file) + # Check that there are any mesh tallies at all + if len(self.tally_ids) != 0: + self.all_good = True + else: + # if there are not, the user will be given the choice to choose + # another file (but only if using interactive chooser) + if cl_file is None: + choice = QMessageBox.critical(None, "Invalid StatePoint File", + "File Does Not Contain Mesh " + + "Tallies!" + + "\nSelect Another File Or Quit", + QMessageBox.Retry, + QMessageBox.Abort) + if choice == QMessageBox.Abort: + self.all_good = False + break + else: + print("Invalid StatePoint File; File Does Not Contain " + + "Mesh Tallies!") + self.all_good = False + break - # Grid layout at bottom - self.grid = QGridLayout() - # Overall layout - self.vbox = QVBoxLayout() - self.vbox.addWidget(self.canvas) - self.vbox.addWidget(self.mpl_toolbar) - self.vbox.addLayout(self.grid) - self.main_frame.setLayout(self.vbox) + if self.all_good: + # Set maximum colorbar value by maximum tally data value + self.maxvalue = self.datafile.tallies[0].results.max() - # Tally selections - label_tally = QLabel("Tally:") - self.tally = QComboBox() - self.tally.addItems([(str(i + 1)) for i in range(self.n_tallies)]) - self.connect(self.tally, SIGNAL('activated(int)'), - self._update) - self.connect(self.tally, SIGNAL('activated(int)'), - self.populate_boxes) - self.connect(self.tally, SIGNAL('activated(int)'), - self.on_draw) + self.main_frame = QWidget() + self.setCentralWidget(self.main_frame) - # Planar basis - label_basis = QLabel("Basis:") - self.basis = QComboBox() - self.basis.addItems(['xy', 'yz', 'xz']) + # Create the Figure, Canvas, and Axes + self.dpi = 100 + self.fig = Figure((5.0, 15.0), dpi=self.dpi) + self.canvas = FigureCanvas(self.fig) + self.canvas.setParent(self.main_frame) + self.axes = self.fig.add_subplot(111) - # Update window when 'Basis' selection is changed - self.connect(self.basis, SIGNAL('activated(int)'), - self._update) - self.connect(self.basis, SIGNAL('activated(int)'), - self.populate_boxes) - self.connect(self.basis, SIGNAL('activated(int)'), - self.on_draw) + # Create the navigation toolbar, tied to the canvas + self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) - # Axial level within selected basis - label_axial_level = QLabel("Axial Level:") - self.axial_level = QComboBox() - self.connect(self.axial_level, SIGNAL('activated(int)'), - self.on_draw) - - # Add Option to plot mean or uncertainty - label_mean = QLabel("Mean or Uncertainty:") - self.mean = QComboBox() - self.mean.addItems(['Mean','Absolute Uncertainty', - 'Relative Uncertainty']) - - # Update window when mean selection is changed - self.connect(self.mean, SIGNAL('activated(int)'), - self.on_draw) - + # Grid layout at bottom + self.grid = QGridLayout() - self.label_filters = QLabel("Filter options:") + # Overall layout + self.vbox = QVBoxLayout() + self.vbox.addWidget(self.canvas) + self.vbox.addWidget(self.mpl_toolbar) + self.vbox.addLayout(self.grid) + self.main_frame.setLayout(self.vbox) - # Labels for all possible filters - self.labels = {'cell': 'Cell: ', 'cellborn': 'Cell born: ', - 'surface': 'Surface: ', 'material': 'Material', - 'universe': 'Universe: ', 'energyin': 'Energy in: ', - 'energyout': 'Energy out: '} - - # Empty reusable labels - self.qlabels = {} - for j in range(8): - self.nextLabel = QLabel - self.qlabels[j] = self.nextLabel - - # Reusable comboboxes labelled with filter names - self.boxes = {} - for key in self.labels.keys(): - self.nextBox = QComboBox() - self.connect(self.nextBox, SIGNAL('activated(int)'), + # Tally selections + label_tally = QLabel("Tally:") + self.tally = QComboBox() + # Only show options for the tallies with meshes + self.tally.addItems([str(i + 1) for i in self.tally_ids]) + self.connect(self.tally, SIGNAL('activated(int)'), + self._update) + self.connect(self.tally, SIGNAL('activated(int)'), + self.populate_boxes) + self.connect(self.tally, SIGNAL('activated(int)'), self.on_draw) - self.boxes[key] = self.nextBox - # Combobox to select among scores - self.score_label = QLabel("Score:") - self.scoreBox = QComboBox() - for item in self.tally_scores[0]: - self.scoreBox.addItems(str(item)) - self.connect(self.scoreBox, SIGNAL('activated(int)'), - self.on_draw) + # Planar basis + label_basis = QLabel("Basis:") + self.basis = QComboBox() + self.basis.addItems(['xy', 'yz', 'xz']) - # Fill layout - self.grid.addWidget(label_tally, 0, 0) - self.grid.addWidget(self.tally, 0, 1) - self.grid.addWidget(label_basis, 1, 0) - self.grid.addWidget(self.basis, 1, 1) - self.grid.addWidget(label_axial_level, 2, 0) - self.grid.addWidget(self.axial_level, 2, 1) - self.grid.addWidget(label_mean, 3, 0) - self.grid.addWidget(self.mean, 3, 1) - self.grid.addWidget(self.label_filters, 4, 0) + # Update window when 'Basis' selection is changed + self.connect(self.basis, SIGNAL('activated(int)'), + self._update) + self.connect(self.basis, SIGNAL('activated(int)'), + self.populate_boxes) + self.connect(self.basis, SIGNAL('activated(int)'), + self.on_draw) - self._update() - self.populate_boxes() - self.on_draw() + # Axial level within selected basis + label_axial_level = QLabel("Axial Level:") + self.axial_level = QComboBox() + self.connect(self.axial_level, SIGNAL('activated(int)'), + self.on_draw) - def get_file_data(self): + # Add Option to plot mean or uncertainty + label_mean = QLabel("Mean or Uncertainty:") + self.mean = QComboBox() + self.mean.addItems(['Mean','Absolute Uncertainty', + 'Relative Uncertainty']) + + # Update window when mean selection is changed + self.connect(self.mean, SIGNAL('activated(int)'), + self.on_draw) + + + self.label_filters = QLabel("Filter options:") + + # Labels for all possible filters + self.labels = {'cell': 'Cell: ', 'cellborn': 'Cell born: ', + 'surface': 'Surface: ', 'material': 'Material', + 'universe': 'Universe: ', 'energyin': 'Energy in: ', + 'energyout': 'Energy out: '} + + # Empty reusable labels + self.qlabels = {} + for j in range(8): + self.nextLabel = QLabel + self.qlabels[j] = self.nextLabel + + # Reusable comboboxes labelled with filter names + self.boxes = {} + for key in self.labels.keys(): + self.nextBox = QComboBox() + self.connect(self.nextBox, SIGNAL('activated(int)'), + self.on_draw) + self.boxes[key] = self.nextBox + + # Combobox to select among scores + self.score_label = QLabel("Score:") + self.scoreBox = QComboBox() + for item in self.tally_scores[0]: + self.scoreBox.addItems(str(item)) + self.connect(self.scoreBox, SIGNAL('activated(int)'), + self.on_draw) + + # Fill layout + self.grid.addWidget(label_tally, 0, 0) + self.grid.addWidget(self.tally, 0, 1) + self.grid.addWidget(label_basis, 1, 0) + self.grid.addWidget(self.basis, 1, 1) + self.grid.addWidget(label_axial_level, 2, 0) + self.grid.addWidget(self.axial_level, 2, 1) + self.grid.addWidget(label_mean, 3, 0) + self.grid.addWidget(self.mean, 3, 1) + self.grid.addWidget(self.label_filters, 4, 0) + + self._update() + self.populate_boxes() + self.on_draw() + + def get_file_data(self, cl_file=None): # Get data file name from "open file" browser - filename = QFileDialog.getOpenFileName(self, 'Select statepoint file', '.') + if cl_file is None: + filename = QFileDialog.getOpenFileName(self, + 'Select statepoint file', '.') + else: + filename = cl_file # Create StatePoint object and read in data self.datafile = StatePoint(str(filename)) @@ -141,32 +179,31 @@ class AppForm(QMainWindow): self.setWindowTitle('Core Map Tool : ' + str(self.datafile.path)) - # Set maximum colorbar value by maximum tally data value - self.maxvalue = self.datafile.tallies[0].results.max() - self.labelList = [] # Read mesh dimensions -# for mesh in self.datafile.meshes: -# self.nx, self.ny, self.nz = mesh.dimension + # for mesh in self.datafile.meshes: + # self.nx, self.ny, self.nz = mesh.dimension - # Read filter types from statepoint file + # Find which tallies have meshes so the rest can be ignored, + # and for these tallies read the filter and score types + self.tally_ids = [] self.n_tallies = len(self.datafile.tallies) self.tally_list = [] - for tally in self.datafile.tallies: - self.filter_types = [] - for f in tally.filters: - self.filter_types.append(f) - self.tally_list.append(self.filter_types) - - # Read score types from statepoint file self.tally_scores = [] - for tally in self.datafile.tallies: - self.score_types = [] - for s in tally.scores: - self.score_types.append(s) - self.tally_scores.append(self.score_types) -# print 'self.tally_scores = ', self.tally_scores + for itally, tally in enumerate(self.datafile.tallies): + if 'mesh' in tally.filters: + # Then we have a good tally, store the ID, filters and + # scores + self.tally_ids.append(itally) + self.filter_types = [] + for f in tally.filters: + self.filter_types.append(f) + self.tally_list.append(self.filter_types) + self.score_types = [] + for s in tally.scores: + self.score_types.append(s) + self.tally_scores.append(self.score_types) def on_draw(self): """ Redraws the figure @@ -178,70 +215,73 @@ class AppForm(QMainWindow): axial_level = self.axial_level.currentIndex() + 1 is_mean = self.mean.currentIndex() + # get current tally index + tally_id = self.tally_ids[self.tally.currentIndex()] + # Create spec_list spec_list = [] - for tally in self.datafile.tallies[self.tally.currentIndex()].filters.values(): + for tally in self.datafile.tallies[tally_id].filters.values(): if tally.type == 'mesh': continue index = self.boxes[tally.type].currentIndex() spec_list.append((tally.type, index)) - + # Take is_mean and convert it to an index of the score score_loc = is_mean if score_loc > 1: score_loc = 1 - + if self.basis.currentText() == 'xy': matrix = np.zeros((self.nx, self.ny)) for i in range(self.nx): for j in range(self.ny): - matrix[i,j] = self.datafile.get_value(self.tally.currentIndex(), - spec_list + [('mesh', (i, j, axial_level))], + matrix[i,j] = self.datafile.get_value(tally_id, + spec_list + [('mesh', (i + 1, j + 1, axial_level))], self.scoreBox.currentIndex())[score_loc] # Calculate relative uncertainty from absolute, if # requested if is_mean == 2: # Take care to handle zero means when normalizing - mean_val = self.datafile.get_value(self.tally.currentIndex(), - spec_list + [('mesh', (i, j, axial_level))], + mean_val = self.datafile.get_value(tally_id, + spec_list + [('mesh', (i + 1, j + 1, axial_level))], self.scoreBox.currentIndex())[0] if mean_val > 0.0: matrix[i,j] = matrix[i,j] / mean_val else: matrix[i,j] = 0.0 - + elif self.basis.currentText() == 'yz': matrix = np.zeros((self.ny, self.nz)) for i in range(self.ny): for j in range(self.nz): - matrix[i,j] = self.datafile.get_value(self.tally.currentIndex(), - spec_list + [('mesh', (axial_level, i, j))], + matrix[i,j] = self.datafile.get_value(tally_id, + spec_list + [('mesh', (axial_level, i + 1, j + 1))], self.scoreBox.currentIndex())[score_loc] # Calculate relative uncertainty from absolute, if # requested if is_mean == 2: # Take care to handle zero means when normalizing - mean_val = self.datafile.get_value(self.tally.currentIndex(), - spec_list + [('mesh', (axial_level, i, j))], + mean_val = self.datafile.get_value(tally_id, + spec_list + [('mesh', (axial_level, i + 1, j + 1))], self.scoreBox.currentIndex())[0] if mean_val > 0.0: matrix[i,j] = matrix[i,j] / mean_val else: matrix[i,j] = 0.0 - + else: matrix = np.zeros((self.nx, self.nz)) for i in range(self.nx): for j in range(self.nz): - matrix[i,j] = self.datafile.get_value(self.tally.currentIndex(), - spec_list + [('mesh', (i, axial_level, j))], + matrix[i,j] = self.datafile.get_value(tally_id, + spec_list + [('mesh', (i + 1, axial_level, j + 1))], self.scoreBox.currentIndex())[score_loc] # Calculate relative uncertainty from absolute, if # requested if is_mean == 2: # Take care to handle zero means when normalizing - mean_val = self.datafile.get_value(self.tally.currentIndex(), - spec_list + [('mesh', (i, axial_level, j))], + mean_val = self.datafile.get_value(tally_id, + spec_list + [('mesh', (i + 1, axial_level, j + 1))], self.scoreBox.currentIndex())[0] if mean_val > 0.0: matrix[i,j] = matrix[i,j] / mean_val @@ -255,8 +295,8 @@ class AppForm(QMainWindow): # Make figure, set up color bar self.axes = self.fig.add_subplot(111) - cax = self.axes.imshow(matrix, vmin=0.0, vmax=matrix.max(), - interpolation="nearest") + cax = self.axes.imshow(matrix.transpose(), vmin=0.0, vmax=matrix.max(), + interpolation="nearest", origin='lower') self.fig.colorbar(cax) self.axes.set_xticks([]) @@ -271,9 +311,11 @@ class AppForm(QMainWindow): ''' # print 'Calling _update...' + # get current tally index + tally_id = self.tally_ids[self.tally.currentIndex()] + self.mesh = self.datafile.meshes[ - self.datafile.tallies[ - self.tally.currentIndex()].filters['mesh'].bins[0] - 1] + self.datafile.tallies[tally_id].filters['mesh'].bins[0] - 1] self.nx, self.ny, self.nz = self.mesh.dimension @@ -289,8 +331,7 @@ class AppForm(QMainWindow): self.axial_level.addItems([str(i+1) for i in range(self.ny)]) # Determine maximum value from current tally data set - self.maxvalue = self.datafile.tallies[ - self.tally.currentIndex()].results.max() + self.maxvalue = self.datafile.tallies[tally_id].results.max() # print self.maxvalue # Clear and hide old filter labels @@ -307,6 +348,9 @@ class AppForm(QMainWindow): def populate_boxes(self): # print 'Calling populate_boxes...' + # get current tally index + tally_id = self.tally_ids[self.tally.currentIndex()] + n = 5 labels = {'cell': 'Cell : ', 'cellborn': 'Cell born: ', @@ -317,8 +361,7 @@ class AppForm(QMainWindow): # For each filter in newly-selected tally, name a label and fill the # relevant combobox with options for element in self.tally_list[self.tally.currentIndex()]: - nextFilter = self.datafile.tallies[ - self.tally.currentIndex()].filters[element] + nextFilter = self.datafile.tallies[tally_id].filters[element] if element == 'mesh': continue @@ -337,7 +380,7 @@ class AppForm(QMainWindow): elif element == 'energyin' or element == 'energyout': for i in range(nextFilter.length): - text = (str(nextFilter.bins[i]) + ' to ' + + text = (str(nextFilter.bins[i]) + ' to ' + str(nextFilter.bins[i+1])) combobox.addItem(text) @@ -351,9 +394,10 @@ class AppForm(QMainWindow): def main(): app = QApplication(sys.argv) - form = AppForm() - form.show() - app.exec_() + form = AppForm(app.arguments()) + if form.all_good: + form.show() + app.exec_() if __name__ == "__main__": diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index ec9d9edf78..430abee8b8 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -1,7 +1,6 @@ #!/usr/bin/env python2 import struct -from math import sqrt from collections import OrderedDict import numpy as np @@ -10,10 +9,10 @@ import scipy.stats filter_types = {1: 'universe', 2: 'material', 3: 'cell', 4: 'cellborn', 5: 'surface', 6: 'mesh', 7: 'energyin', 8: 'energyout'} -score_types = {-1: 'flux', +score_types = {-1: 'flux', -2: 'total', -3: 'scatter', - -4: 'nu-scatter', + -4: 'nu-scatter', -5: 'scatter-n', -6: 'scatter-pn', -7: 'transport', @@ -276,7 +275,7 @@ class StatePoint(object): f.bins = self._get_int(path=base+'bins') else: f.bins = self._get_int(f.length, path=base+'bins') - + base = 'tallies/tally' + str(i+1) + '/' # Read nuclide bins @@ -379,7 +378,7 @@ class StatePoint(object): Calculates the sample mean and standard deviation of the mean for each tally bin. """ - + # Determine number of realizations n = self.n_realizations @@ -387,14 +386,14 @@ class StatePoint(object): for i in range(len(self.global_tallies)): # Get sum and sum of squares s, s2 = self.global_tallies[i] - + # Calculate sample mean and replace value s /= n self.global_tallies[i,0] = s # Calculate standard deviation if s != 0.0: - self.global_tallies[i,1] = t_value*sqrt((s2/n - s*s)/(n-1)) + self.global_tallies[i,1] = t_value*np.sqrt((s2/n - s*s)/(n-1)) # Regular tallies for t in self.tallies: @@ -402,14 +401,14 @@ class StatePoint(object): for j in range(t.results.shape[1]): # Get sum and sum of squares s, s2 = t.results[i,j] - + # Calculate sample mean and replace value s /= n t.results[i,j,0] = s # Calculate standard deviation if s != 0.0: - t.results[i,j,1] = t_value*sqrt((s2/n - s*s)/(n-1)) + t.results[i,j,1] = t_value*np.sqrt((s2/n - s*s)/(n-1)) def get_value(self, tally_index, spec_list, score_index): """Returns a tally score given a list of filters to satisfy. @@ -458,7 +457,7 @@ class StatePoint(object): filter_index += value*t.filters[f_type].stride else: filter_index += f_index*t.filters[f_type].stride - + # Return the desired result from Tally.results. This could be the sum and # sum of squares, or it could be mean and stdev if self.generate_stdev() # has been called already. @@ -531,7 +530,7 @@ class StatePoint(object): for i in range(n_filters): # compute indices for filter combination - filters[:,n_filters - i - 1] = np.floor((np.arange(n_bins) % + filters[:,n_filters - i - 1] = np.floor((np.arange(n_bins) % np.prod(filtmax[0:i+2]))/(np.prod(filtmax[0:i+1]))) + 1 # append in dictionary bin with filter @@ -544,14 +543,14 @@ class StatePoint(object): dims.reverse() dims = np.asarray(dims) if score_str == 'current': - dims += 1 - meshmax[1:4] = dims + dims += 1 + meshmax[1:4] = dims mesh_bins = np.zeros((n_bins,3)) - mesh_bins[:,2] = np.floor(((filters[:,n_filters - i - 1] - 1) % + mesh_bins[:,2] = np.floor(((filters[:,n_filters - i - 1] - 1) % np.prod(meshmax[0:2]))/(np.prod(meshmax[0:1]))) + 1 - mesh_bins[:,1] = np.floor(((filters[:,n_filters - i - 1] - 1) % + mesh_bins[:,1] = np.floor(((filters[:,n_filters - i - 1] - 1) % np.prod(meshmax[0:3]))/(np.prod(meshmax[0:2]))) + 1 - mesh_bins[:,0] = np.floor(((filters[:,n_filters - i - 1] - 1) % + mesh_bins[:,0] = np.floor(((filters[:,n_filters - i - 1] - 1) % np.prod(meshmax[0:4]))/(np.prod(meshmax[0:3]))) + 1 data.update({'mesh':zip(mesh_bins[:,0],mesh_bins[:,1], mesh_bins[:,2])}) @@ -576,7 +575,7 @@ class StatePoint(object): def _get_data(self, n, typeCode, size): return list(struct.unpack('={0}{1}'.format(n,typeCode), self._f.read(n*size))) - + def _get_int(self, n=1, path=None): if self._hdf5: return [int(v) for v in self._f[path].value]

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