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 e9688e761..8887e1fc9 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 707049b1a..e1c7f12c3 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 c241153e7..dfe7390f1 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 e0f0bd5bb..43f802545 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 dd28f9bf9..f8a474c54 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 07c7f5475..e3bd96afb 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 e1c7f12c3..ba8eac3fa 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 000000000..7c836809e --- /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 1af6e2d30..15afeb4cb 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 7c836809e..a5b33b725 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 b71354cca..fcabd989e 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 b8302226d..e2114c8b4 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 15afeb4cb..39d8156fd 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 ba8eac3fa..a5f331853 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 a5b33b725..1907d1ec0 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 zcmXtfcRbba`~U0Mnb~`knN8V3l93siSrOSJE9)E-$_mMjkUc}TLv{$+J9`t(G0t($ z`Mtb9-`^h|9*_HRUiY}}Ydo*#b=~g`^fV|)Sx5l@pwQA(GXwy99QX&2;DIBU0+nF^ z-~zPN9y|@m+HV`F;+gg3JKG4l+`|-~m$o!N@?(tA9lKp|PFg6Q%Y(NlNnoJ+6L4or zBP3ojxV;h=7ABtduIYrcW3eMG4w8`ZC+S%PYpIr|feb{8uJ>eU;%?ynJ98I9yV_Y~ z{@%u5d1|`#LNx0Zw*A$bx}#eAsR-$$IraHcw9eyYxh;id=Brcaai6U?6FE_S)Wr_5 z&CD4!Q^nf27|v#12`{3?h{*#DkP}68+P>{n!}Y2LBiZo9nVLHGb2`C~*zszEBk7ef zf63SYaVGQs8=Z&HhNz2p0Ufq4AhpozZ|f-eP_e?c^fx2M*oLso;CzV`Lnq9v03(Dc zme2NV1e`0DPJHa88?_~Yt$0%B|E5GbP_01*&J94NAtpHM7LZyTydsl3*d zX0DWfOICb_NtVcV!Rx;r%doQy`af@C!;atvr{|Q&Bff^^hf8-rfHl5|>uDPwMDw8V za0gJ_9zr>FaAoXxyM828^!zJR?A+gvTx8cX>HIfS%X>bxWztS81iZs-K3N`3fEb8n zCAr>3{&I28387tM`;7rCxV3z$zqVX7pwv8A(lB?KVwbqWwz*UGGIRGi!9mmw${Uv8 zfnUA;PYynm&cZfajMFk-YoPm1z&})wC+6aGdEx91Qzm{N{(sYu{5Oqz-i@(Tr-Wmp zw0+L|RDd<~NaXUovy+;VDhAis8hpMq-SC=Eo!F~u4H6GwQlPrtnf=3lRYc95O3$Ca zuw%&pj3xw#P!Jo0S1DiRUKkJnsi}LjcL7U?Ey+TMEes#%_4rRJ{jrw9Hnz_!m*2)< z^6eoob;Qal%HO(ogBR#TiZC8MCWN)$q;zIk-BiN^kL1U6t-tE>Co~}+4nKyz-|!~M zHQK>VF9*4Lc@Y3nQKS{A0<@PV2K5Pzi&q6UKC?8J29Ic~vHv1M??36QIqd}cfY@w; zYrCLU4-&S?jR2R9!sKrNDoJx620zy=WlT5xWT79`fm;*E%IKk(Y$dW0Ti=rbfjd?|B%Z#*>Ui@jj*@mGAOb`5z{cHSgB@VXDzzDGKvl2mU+yTiq*3;ZK(0CxmaMmiXPl;8*$$n3^`z_hL-uH!) z)U*U)F0O|LD`{&@o=&g_R+2>S$`of(Y7&)Rwm1zsLDx6z=br) zmucnk_tC)~WXfL4{mNSui?RO+mhxJ#eq%;?Vl}L>Hn&g)P1xZ09%JO!EBymhQG(E! zepCA=G%d*n6%l?`mJ0=-d&9=X-!1O*GTUQ>AYTpN=Hl(FeIB#rhRTXk*H32QFu{W? z$EWr&$12M&`+BA7-c~-s0|Q(j0jSIB9Ej`(;^rolw6W)CaG)%?`Dpo||{u@skeRd;|^;Dr~eMJY(+^!yZHQjk0N zN5MbpW$JV2_@PHKC6X5QS-tl9gAT zPtj>PHwAuiy23p#R|3v7_ef=-8-%%*}IhrNJ zbtohMR0$%s8Q z&L`$?*bC=gS~j0_^&(3Q`vVnV$39JS4Fb?14p{Q|u}fjFq3RxRHaOb#i=n|xpx3(A z*g?DC`|^_e{=o;2DupTEmZRb(mi)C|ag9*)KfU|&lhOd=4|<~Ouq6rFw+EXC7wFdb z)~3DAu=9oUg-4MjEni4)lLH6@9BD_fw&U$ntPuL;Bc7@`LGg@o&x;So7g*OEI@~vk zt#D(BqftL@HJv>{v{b++5z)K^l1k16*Sm`~6|!Z8a-R!B(8u+Z{#BQ8qyWJW8lr2j z!UwME#~_owk-1HHlsYOB$W#7eaS~2eWhQW^xOfKcjC@G0(pP3qPhUQ5Cj0@)qA$+0 z07oaiODX+v_uvj1)6ggM?E5TCJ;uxB{X5&WsZ3^8a^mUKqTvOvESJhae0(5`BI+8Z z$b;cH)(W5v5kmD`NM*I*evDW4ktUQY2eeG7+EyA&5mcsjt*ue>IY2sh&vEOoQb2or zCDmnXa8<;FC(g2j8!GD|Hoq^Fym5QZVKS^u23EmA+QL$){7DWCnB z^KF@O-*;6lwo$3JURZc+6MUGSH`=-)>!|Wzf{QS};`VN_lq(4vKvKv4pE6yek{?uj zD2f{Uf*jQyz)ycg1bcV_ObUl=x8a%8d=#lPn1Sz{abvK`sHVI#W^!@171W#KB3s;d zylM(gOs-k~Bn#`dw8y4-NzRY-6eZqK2|i8Tcng1;+Lu)`EUKsZs+Uh*0V-oI*XC)z z2Of7uAZ`0)H)V3@hsU^l-}9+mQt@Pz7JJ9wWa!4Z*bLnG@~-7`c@az4;|k@ovlGrwes2Pszb^?q72vvn=s<)5$inyUF*cxMCcVCoMHY z=H2^%D){;53vHY5xX(WUFp$p^)O^z{W2dok3v2|>vVUWZiG8d!AIXbV5BSXdz9|NU4wxP_nZ_=A@r z&8ayNU1Nc+!JcLX5~7{wdEezIFn*o{A5m7*q#(o@tY8Io9@s%MfZgtCzs?gvG@~W% zf++~y*jhe;b9n28gEyvTrD)u}(UOxi#eOs?KAF8eL#r%ZX2Kc)Ur3xkow#?Z6|_s|pf57J%EJpr{lRcPBy~Rz z!}lPp4x;)+@MT%vxIdJ$Ry$@>V*;XtZM;~YxSs>hl>8x43=iXkVl(fR98Hrw^4NM)gAv3dc|rc)1<(v(J>|iLX@}5K zV5CV-?eiz>aJ{vg&fMAW$)7?2PZSG-Cgxnty{|0dT;(UvkS`sjXvpL|isldqZ;jW^MlU+M-GegP zwr2e2G<|6&$(WCRz#%Y|Q*oF*&`Z>@4<+tYgC{hm7uXzaNTmmUuiz({X6(Pk04`j# zzkG0J^oY>Z)NClsPW&Gx)#GwlbS%w4`pEd*gKd#%8IFyXue|`11^e1_`yYef7$sSO z?Y8V)s;@co8f>HEOKG!R6!zB! ztIwZ1cago9TWKv2xfk=NyV*IxCrhafA*AZhW+;|C&;OpIw;H2EGfb&e?Y`Dg z+WOmfcn3U{4Sn3~i9}C(<4o!kih)@EG`+I<4KG;R{c@8n|D(~xJ;(Sbd zE3bCw`KZkwPCgXZ(`VcWx}`639kgFNPtbIG*V|M>FNASqXJTz3Uu+l? zVJ~ne$$t)t!2iTiOcWyaurSzTwUe>mt@mrFt%g(jxe7Z~Ser-41 z#P}c5=RKHc(1rllF%y3X+d}4uYkA9Y9J=VO;cb_CrC$A#aZDrWiU^sE${_8R@1EvC zAkLy`-j1JYnEaP3_?#VqhUR0lq^A*4VbX~C3F=|zWO0UYz08HVJae2Uo}U`u`-O%# zRi%TGaPpcA_&n3R(IDwmfA?3$(n`-m-Z@#x_kZjjWV7B3v53F+b1w*LafjhO$}M4{ zr&xhJDk)toqUMo%&XZ94%9}i>Hwv(}qDc&P>vbD`CJ)I8o+s@V&qr2PvYsw_XF;VMha6~_!ZSfyQ6bVOt$ap^wTDCTpG^3resvt@Gp{tlL!Cp6T!8oK8uHV4s3QBJ-y<*HNk;a+qsP6-Tktd#)FYMtGVo;_SOWk^8_WZs99FBAE^mNF zii)m$0u8G@U)F0F@vN*(NG~lj#{{5AuhclTcuA%k0YiaCYf1>+YL3=yh3g)`F~VVd z8R|;>&+BbhS2$+y6=Y3<2;bEnCQZZ?T6y$2VxD$nxLdV2lh1R3AWQNd*BY!kP0oZu zgYCu63om^+7|VrwE!9lGi|LjW{hDM6VE64cs$&VCh5C2y!xOgoju@-`4@n6Y&&T=P zI~VBzspTWXqsEU7__9u_7N5B)Iqyf<;`m)|{H(j0{-o?ksImh zf$w+6fosC11_IvDJ+rHEZlCmXZ8`{SAgMw+TQtDuEJi5E&U)}%CP66UoW4UqDoue! z>}AuOjq)2i&#M$!xksH?ic0-t6iZX;z6}}uHq+mpRqSGFxFLFT;kTs`oR^3ChGX`4 zCJZytRQR*|p?byD3G#f2)PvvC$7-RK6yom*#ERR-gmjFCCYu*A9(e(czomIUGF(UNY zQ+sta(F=_C0>0$pe}Y$&Ghhb|Wzz~GK67q(lzMoP5hV)GK2Msi+yHbMMfDQgZ?k`p zq_BuQU`#PtKW3jn2Nkz0ww$*g!rPCc-(Oo04{R1%1m4oxHX{g-Dnel@#9jt3b3RX; zKf~~7@JSHs(UXaoq5;xDXeW}QA!n7Oj5(5f|A>)i=^HMw2@19EDC`CL5FwPY)U7Dw za3*Z6;H;i9P&#gf*kUAXm47A^5mxZI_ zM%zo#`h<=^3Y9;ByyDg0ac?C$yj(i7J(Q8`&KY$K9sTlA+4sTyC}Xc=$S&iSBjM)! z_!za>C*^KrdU@r?QbK=XCuYC(e|XJJ(H$yB=Ty_FW&V6d1yIRvtmtO#{O__9ulF{D zRfx7e4ShdD`4<&`@jC{i74jJIq&5;pt`#!qmx1DhJ;iGF=@0MQ{2j3Gue2$f9|;IA zL68|Hq1p`|#6v_|NSp|BzgPN8L5#9P*7*LaM=DFb&khX=5xD>n)MP8^in7<@U$r9& z@utx1egdovn>{eO5LcJo+U`w8@okRnhTDghAu*q4dEB7AgY-0f{Npt->4V?$07IB3 z4;yE3`KASr*&f+~>3TdhJxCTSIdUcE8u4ypqtw$Q-AI6$6Wl!48sSWyGCISp*Qo9d zD?8W&?D&doEB{92iI{lr#!!bplWa7x_?*R^ApZI-a~985>|P8qf>@d=ml?Y))JOrf zL_FQ2aC(ty4j-NiR>3TNTonj`)u0|qomaT#WH10`jqUK?EgR9@l+zI<7I4Mqu9yRXecPQK)OZF99uuikyzOZ|yJ7g<2Ne!^ji6Vt5tg~UA$ zb4-7cC@d6dv#Gn=q7-X>t8j;wXiJ-~{XtE8%JFoC>IJ*=F13ySPg!Z%vG^5CYL5i zMY?-*qm@>ZP!{|TyTjc6T&Vd4}I^eG%2Ze%M35v<&{0=M{cZ#HRFBR z%B~>pH9`w`E|c<(2Nk6WlI~DrjG}^LlUMJ zXg66srifz;{*Ao0JvF69oVzU%TMEH^4vOFD{6bo@3gF@wW3OF?8R#y+qz*2!K(I?y ziu-C2{9ampSDG3{{9cL09|+3)l3Kfm4CYWch~Y!_fh=9~J7|r4`b6pB;9 zu*z6K1tBrN&|q<3$eHl=E*~6N&46vB-`{<6&{P)`vH7Kj7!3G3E5negp>-!gt&Zds z(?mMmbcIbH3Q`KUGb;Ks_m@}uav(zqraCQ@zd#1RL;V_*s_8PIx$(p6HGYt|b1(R*bj9j-4t3d>-521_~}(8fP*lrUlyN543NPZ<_wP2}Di( z+>y|q@#3MVShN@okAbGWaqZNKU@+F-P)km2{LQvLygBcLNZO-%HO?tlcymlxw#It7 z;O5u17d8ZG`Q9{F#%|Og@dnms{u7hdzVwmlQ!E9s@2#f{UdRbP+K|`B!t5Pi{&r`* zbjv;?w3VzEf)*Rv+&lWvCV)=GIU}!{FseP+cLbWWgb>6M8h*TEr{#J6f`Y|D=F$TC zooqnukNn;tAO2CNJ*|YMmmeb?0|suKm6&sPG{2-I;E#Kx!2wCw8=^2a zoT6VZn-2s4jcXA75RA;zo%>Mx8Qxt_epL1G=8J>4oNcl@iCuF10oX#dW#6`~A)n2f z^C7UbGUoJcS?Si3?kiHeInxJw#ue}zcT2=tLa8Xr944J@-eqX6W8-cC!}Yw*1Ji+8 zAxDaLaPhuxYGXdO9sdgg@HW3x#*d`!wDzvby?Gt+)vEHY5WkM#mjY(3o{OMJDAR%F zSon!t!tsP(rv%VN=!m-y(z5RP)2+)|h`aJz1jN2gb5PfJ4CbXbB|6`Ofr)_shibQX zfDcbmh!&DE#6|8vJ99?}SE)-po`jmK6AK%lGrg-r^{E#qK^Fyn9Y9?5;2#XR-zjZF zINg-dmQ9<`U(X%Oa1&gF1fLdcuezhRI<5*W8RSbk-}@fx{_rw0DW)a(L04}fqGa`x zKd^1bdk~~;YC*h>FOK%4zn_OC9UDI?9Wu*dV4D!!Zt@Lw{MeiK z^VoW%7^Zh4wGxF&IJ+|I@moZtl|MZW87J$fwQySOMa7Hk^`|_Oyb&Nt@bq|+gN7gw z#azx&&ivc5Px~>4r24Fbb&1V#n3C;v5d-A!&H)xQF)@%n>acXggBo5$JV=vSpnbEV zyDLL3V)uJL<92!drJ<9$imxllX3!5p{^`Nz`*mVpC#iF)Ku!;F22gX1>L^d*q9emx zdft1G%YU(z98-wY*MrcZa(Eg$*2b$0vqnUa$l%Kx%xtuWtXpf)hC^7%6R0}rJCE5l}? zRG{5Z_(Y-hxEroKQ1xo3o3|x;6)NfNenccDB$$6~KabZ#)C$Ef95vbEG6FRiAbKB; zd)H<)nrpWTgw^^H%I@Uii=o9Q#NA9!t=$}OympZQL610BUSjTe{Ckza7Q(V&6+<`! zaa?rp&zJFRe%fgc{~)ep0W2}`7S#VPl2P^Yw{4;STCLcode)X-GvWa=b|9|y_)nsI z*Ak_uesnF-==@ebn&?nbq@IP5r-ia;2pf*gZkpRwLE3=LN;$A_2u1&zrmg_{Lf(2< zJbn8~n9Aru6bo9j=Ho1bo7GoEK>gw1O<pNL zB{bYJ86zMxL7y0;du;V2on|S(*ihs>lKb^~=Jsmz4@W`l0Om_(R@0BL$&C#V*>SWw z;Qzf1fC*Xz(nda z{UmvNbEj)vtuDEnLyhy4t%pD03fdqUGR1!n9pP0MYO3Wb0@xlQ8_{TNV8~f5r(jrE zEJGwW8;5)pP+WxU*9QKt7l8c(2m))P@2zh=kWBS!-dKE$~$EG;r@2KqQ zG1^02Z?9CNglVm86zWIbCHv-f#Jr)O8Uw_Nf_N0v0A;edHB6xTvKXgEsgqLVcGuX_ zoMmB-BLj~z$YZ7IC_DCT9^CZh2LLAh)H2Mk z7FG-B(l48g`%u}RSS9s`itx(3n3G?pE&kzJA<8bi9=gNLd#C`(qN{%@NT|)*<)3(Ii3gr>Yy*}7>3m1LSgu@ z$bLNxS?{v6^BL0-*JNL>@;9N&Aqh;QRVKj2eRGeL5}v!^g4+(T{5E67TIrZFr%{Wd z4mg>S*@^)v5=(3A;MH^8vxLsS`zutXRQh@;77%tkgXF1+gH}X6HLGacWW1I3uIpI% zyipE^LgBR0QK6G;yr4*lzKhZqf~MEwaU;>ujC;f*txx>CLk}X*3xLXy zJm&FBncLZqI%EdHpQw=WGjlk0_}5axvoiLd_4&~9DwVg7e<>Xdenci{xCWVbu;KlE zg-)yw=TG>yxwXHgTpEp?q$k2}vDW-dO-U&M|KN=07qYGTLR(a4wprWfsm9kZ=_C~O zX{}<6Y=RYZDSX^^qFEE4n8VaCXDLY_@|eH>8c80dFD3aon!$zT>qYMQc5)gy_Z_-j zi}5#z5|wQoaDM@=w^+c{b;ReOHZ^i;WUQaFgWHASf!a@dZA20No z-4^7VaC7`&0`Jsmk(BNOBG@~#429nc+PNPb?$-OhxgZ#cMICEpe7iGI(B5ueQMrKf zkp99R@f($RsIy`quG_K3qLau*I+b3tm>mY-SrRlp0Rsb-I9k7hh$R?_P+pzkXHgjU z3~~GHN=S8bw&oJkICpE82Z{eu)IT=+tiH9|)_~`TmoU|ZGjRvroWy{A#9gI6+vBP8 zmZ46Vi#X`LY-cGW&2-FzpMXcUt+)V>iU%Hx6hQ}X51$r>&pp+gl-hUeWzSQQyd+ed z*6Y|#CmmA{9YZ+pG}m6^!fJqAH%e|T`J)Nxx-QC<`pjOV)ddU(nZ(zMIt#UO`#rxE z;SX%z@%r$rXy*_6J-?Z^3i(v^JD-2jW_9nUH5|=0YgWh2!1L25s8EtAoV|Kz)b`e| zVllbx)Y6cGtr4t}W2&)HmAt^UvU+(HlN07Qwn6Ny@{6qB!K9v~i36 zPMsLBW2Rg7D>))*xs|V7vixJRuso-~qPBHc&7Th%z991VRS*C0qV#;UzHr}JFTZ$W zXxBwStpo@H6mB@;Ft8XfEd*Glcebk(g7`*;R5g#Rr=QbOV;Ubs4O@7ioI^%#NQS&Q zNsDbq&QPo-hhtn1@2leOmULUK4chFWLIM7wN0Qe4$6)fBhnKs7Et%8cMm6Y_I@`>- z*@}F;`TCv4!&!oT=Y@$$h4gi1=8d(~>mTb1eoH1}Y$ll*)qN$nvG~P~ACkRc z#JyzYn9@vsWd5SGRo@h+Vj;tGp?CEY(zS2AsBO1EY%KE^0VV$-Ik2}M;ZL_3ozdj( zn$dLm*6Qv&Mg3l`=oppWy8DvF-^3ks!it>VYa2dajgUD;5_FLad)z;S)+Hyc&Y+5G zTd&;{-jfQgkU3WI93kB{mNBurDC0!{7R~&&QS&qVTXjuJNc6v1w?D7%RC#{-9_Q-$ zt;sCMV=3jk`vQi|@@>2P^o>jfb_c%UYQ;vlkj!J`A?_UyXNw+wo$Gi&O?znUx=r-4 z^mVm>2#gl^dN|^mid*gNXEtvoQJM$aafzJ-FCA~$xtV>FdwfH416ZgFro>C5^W~xMNZ8xt|)aDf6pi3O)h!U`60qyCOboU%O4|T~I7$==zF6(s;g9X@yUh8pSgLzioc#)*JWZs(D@Q^oF%Yw0&dxut_j>d=IuR; zPEX?-5%)4@9xGS+allte;(t3Itlk`hH=+x#&|pVM6#J8iK2b5z!jLBooEaCX4WeRY za_xIkUzG0nD6Q4}#%f#Ww}g&_o;?Ko8aQXgfoM^@PAploa=PH)FG)Nk5ux}OTk%Hd zId6vR1n*#VAJ7LPG#T47&qs^_aF{-h z_GtmZHwCBBnG83E^@h&lG`HsXIC)77>K)0ii#tAN`X<#&d8|#P#{HVfD7$bWIDRq=Np(?GAy>9Sk(8#NgPq&d7Dpd0+widj9(Ts`es>U7P)WCfGR ze=_At?0Te61N`glI+9}*<(_$IK?OQ3^uCGB%uZ%UxVlGSi+kuOKa>6byQ}unP4$M3)mvj2DbOnE`G)0>7k); zyCu4Y07u8pGJpNghaI0us024%9S{}#GW5Fj&1A8R8)N|aTd_iq3f^l{cVJ-b*sKO6 z0pjzjoEO0r_tkK@G(}ZGD+G7DkftzYKj3tX zBHn*PYqu-fT6z@UT{%KcO~onB+KjM!2DnzY(6fY0M!+Wy)s-*)HTwAwM@>t5SEVb% zIw|>g@+DFljBv$>mm+$+Pe3lD=Wp9{V=f?9U2Ef^NP;zi0r}PUCJf#D<1;Y_l;OeJ zG0L*ycPN&-r_d;#=&t*HsXQ?p2T#e7f?oyAhnPTV{oSHa2e7zyseke{Lx%R{dj_s$ zKep*}t)7Vwx~cep7y0NzkguVY#fXhQSQ1+q$GsTYp1zG5xJy98x6kK*pr7)v&f&HA zC=}$Fbt^Ze86>l3h(pJmZ8)K31-p09smy`Hry6|7N0HXwp>pJA1mxe1Rl`;4jmdXQ@*mP3V!6r))C zy{K2OL%ww>cq&ApWPd+uT}iL~fVZ_@9SBb@?<>qvXi{QZ>q-jcXTN4tXRi6?|Mu>1 zMpcV3&0jR>Wn0f4zKinhQRYH%7DwQfH<$%Qx1r^w&ZD>czLESUBcmizZ@i=WHY~hzr51h5CF!MVy%GyJTUFKo{H0-WKZm#I*YB z&fiS8$4}*1ToC%12|3~;GxO1IU%bFR%}ul?CmfaJO+J1awTrDMg$<3zj8yEhgTc44 z`KPS`h8BsU%lw7hm=IUFG%zFo%Et274reFd>EauDP8mX)+}#>jwLZ-D$gTIcc4NfcKI=4qo&@z|JkV9 zmzr0aLTKWD^9i^=-c_))Gha+i(pV~rEqN~!mE)cG-O4P5oSn_2Gt!p?wtzSzGd|3j zV)&Kv?Jx(G>_QJDM{84%?-nRZu^xs?nBRM_AenoadRP;gkpbz#f?)w0vjJu%vFiL;P z2nkNv3WQ_(lE)I9$*U`uUx<61z3Jse*mgCyH>kV<+ku)7bBaSqF(WTXZCi0{BUL3M zg+C|mFfyVzAB=y~u_=Eo)rIJw5$x9;-o_Ig1O4tFWAbP@fDoNC5;Obay79mYl>^gi zO|<`<)0g4qIELyovkrVn7m)4s=iQq1mc}&cu@+`EIZke2nuTV6psLF~a!q%)Xm*zv z>REGGTu(GqznM3`0RU|HKCDAn^jp?JY*06v_XEHcs^`AwxL~VMCG<49jvtR%}cWB*|{n{m?zS)!0#5q!4c!r3wAWX1EBml~Q^C2l?Jv*h_2eMgB=&4vWS zTf7V}2S}>)A+^a&#t;6kE}8l+;#}UfwSko`lNnrcpW!99$u-94xf#Mqd>hja>}I%G zNXO}AphzrP#;QtIC}}W}S|PW}y_>C@--lkT|JZYFv2GslkS)piMyP0a_!gOS{iP^&z7}eON(!JuoxoBI;eJkJdk3W@F;c%4?p~_K4-0y94);(9<8Q*&9 zpz=qP47(PzfM8=0Cs=+?8%a zk8jP5VUh%YS0|OlXO#_fXbe6nCt>spnJA8+o5sib%sk8;s4l7(geo2DnE4l;x-8^L zj#JI?4?N#{>v-?Pvy}PU=^eh-Q7!EXv4FlqiFBMyV}fz#lv2hRCi|anVMPINlZ|-AZytBO$O{QLE~x@!gD&t}){3?b zZ|>W4^T0jK8BVnX^suNe>N1|JxO{o+IXq9swyk%Z|Is0C-KVv++wN-%5rxLNGUk2r(pl2GwAQ=% zFxEu1W%_Rud9mrV^R5GcKDlmmM+ij~VBpGWV1Ck$;SZ?LrH5=c#$l{r|i)E%ZKKBfi! z=vlvH2-{fSK!WYHf7jQy1KcD~Pwm03+Cud4xI7H8U2xB8^N6srQq&q-RpHhiAo3s& zZf##9(E7k(x-(dmh1_Q(XM+ZfPD$K8uBih~G6%XHOELrIyGp{sGNjG}j$~7`wJ3;w zk$%x-rFtuAQ?=SuVA%Wjp2Oe@#)nz2l9RhdvZ^jCIO1Vr9XIcl#ix*b8`H&PepcgG zr<$?;_+vKqITi^XgH(`Jm%CL7)E7Xznerfkk)%bSUAZ?Cry9kngtxX?dDjYe$y zTi=1c{gvZ_(f{PH6pBa)bGDf%IX%t~$K)(keg;E=o#;Vebpt;FHA(10*onKMn zz&*XZ`^8qGY6dq}>>>`7`mML!*D2f*<8bmET$jfR*V8*o%c5?-YJYh4EUDz#Xw_mi zi$4;%xmyKur>XrOy>r3*01>n2&URm0%r~+i6d$fP5x7O6&v70hv0SisJRLc{J%tZY zT~m0AV9ET;tt3qT2&6;w36#et^$(Rjxz`mQlaM{7#rZukvPP59-^rsQqqfb}u0|p3 zri{pd4?UAeZ`m}R_`?N5(U0MriP#*Q2d7tZvfbY10>%YXIlIux%IY-NUz{-xAG4Nt zr6(01>gI`kxPf>$#w_fM%hP#2j2SdoxoC94J2RAtdo6GZPYRw|De|Bm&$W%=&1m@^ zaFHXVe>C3FZEhWy<~lU6wjofVuu^A#*M(zgnQb-DPgl?6eBG;cg|D!#=3qi?t_}%+ zPXV^2udN((y(BRhXzPbw?$l{=dD`+vpj|{ThalJWP=^xi-s~O9K&R}R4y$`j!ny5X zdm1BLuW_UGSe0)$<|@aOCkcal<6t4vTP!oMqy+u~ciVzXNHJi~v-l8q#cK1kvZy(K zfNo8KA*N?3jN~bHS-(9LqykO42)1tx;_4p5%8MVR+B3X=GSp@3o7vELqaSg9v4$x5 zx1@fDe(JE3$c*+uqSZi`zurC0T}{0oev_VBG`fTq!WnOj-DQaWnF1XW9VTE5gw7lQ zw&%rg*s7>a&MQ8azZ2{tv4voaYAZBbpkFueACqSeSuXVL(dKwPd1)zaXd@QY*T?-b z%V-x$cXl*}BS~+UKG41(Nw?0CRJ^H$ndLn@*$>{#8xIAYtoHA)+;IJrRIn8PMCH=z z@@^WRRJ7rD3f^evn*Lp-bt>Q6k50dInZo6#2Ur6O0_RHFj#F@PuYitfu-JmD?%Ic% zmMH%jf*NF;c054rC{KQCB6%PGkxAq&2HEpuXJs#glG!1u@G!6X5&Py^s?9HNzmR(t zgkO9l*+-N39~URs)Vm*2b`UWEM6cuuV$BZt>-{y6C2aiioK$Owg{4Z3h8C^ zU#_c#u8@+3$igNV(;osSjcd~z?s9ae#Sc@hG6iD|bQ=XPu_UZ)(<>4D`auVYAuJNA zEX(b2WhyuHWUJc-_^R%E8hOjMf8hA%CWu8olh4H?B?8Ozw$~j;%KL)oWv$&RS_4Ln z^PvM(EkrVf{MfyGPdT|P6zAdwt(RbGT}XroDK&VMq)=0eZrogbSMsCJWtjX)@*l+! zhe}Eu&k7Xy&_x-XzG&urWPfmRfGO;I|=v4HtW=K8o zYt^5lbAHZATsIX+B&Z14)9h)b9!YCx6vXF9oiuZW{wey3bo43hxxKi!ES7Pu5a);* zR{Mo{vQ@7@<%#{PP>8n*0pY`1;JZDi~**t6oy?4YV!dMURoi6@lEi!yMz&22QZt^c_09H=gl3H6|KY-~cHr zflH8-3zDUc;XP(GxU>1S2{+)QXqhqIzR#f2aVaF0CZ<#|PEE~>(fq?F>Lj=rpPZQX zI%b^y)JyWW4^pr2(2VJgQ((&!Y^+%F<3Osl_=lrSIeT-n#pE-lSN?`A^EVDeyP z41-^btLRD{8?pP45M3ba<|EgFEz*h!FZn0o`DzDBa~3CDkQcVj zhfkbppvQgCVZX4>hRZC1B%THtV~k(E3fw)ftA@G2Ogrg0l)r^@QFqq>p%Rt&$r)soN#1`Wa=^O~5A$ zHGjaDs3k=8aQldJG=|OMO~Ed-PWNj&0W>B&xQ-y?-Fd4RmGV+zSVXcU=skDU%HpUKww1)9)zal6ZVJ>=}&`cwRjV7I_}8y z(SYG!kV7vUM|^Q_@>rAzky9S-fr~D8v#{5zbKN%fbX2jtTk7Yo{QR-U*ujV+?7<}` zhBB7{vUz&E&#fJ~w1K?yYOjE$m6!LSw+uW~R(nCd+-<;1cGg>ak-oJ_?MUWf%zajs4;3fr=4kiJNiKfznc#`K(jO7r5@F`Mn_Fij1J2Cj4=J@3M z9G$^0IxmtiJw33Z4n0U-eYIA>YCq35xd8vJdV2V0qBVe`vOF-Qxqph43T_${Y06Ob zXEd{qi0)M6(lPMqJui(r=1alJ-y@+(j6S|br!ru0*S$WB-OFy}XZlxrM#ToncY2X2 z79Vp{h!aO&uvn(Z=F6Dl?5){CE{bp2dA&8WxVe#$7D=)(xk}mrz3|6jdu&Vt9?}D& zXp_6|^2t&;O2>-yaE5B@q)gp8d6HbB&xAk4hStPO?42W|b7@e2#OwMo%5V0cVxw_& z(vW-sBeln{@V5#MFw@V6hW9ipHG)zC(?07LdMdm$YIgoJ#dB;-gfXP-X|1{=S7z6K z5T4{6PQk^V~P8fW&bs*%%wmh4NBkoRgP`@_dNH zCpIN|Q}-g(aBVn>5QR;4MN1o5W1^tvM+xy70H0Wba4Sf2(t&T9Xx}v&op~B#HP1ZV znLh3>ry-xc!5X|uY_(?nD+QvjhCOUObC>uG{Hj&aWuLD4rs+~M+fTUX$C@{ETT_wR zEHd* zf~YVS!&NqPj{HC9{5x=dDCGHQRH&Ahj6GQ(D34BxxQgTu^Q-LSVNO20tjL^=DqiMe zX)8@_C?sk=AqF#EpyY5Rd0&jnx$P6eOc(Qxz^SPmyEAnICfHFOi0$`j@5d~Q`Y5bz zIQZ)AGVj&^NiUfwDqaKSmb1U^exk0$^XOHVI%n>+eHY#SBl=C~@N*;cf(IIsZn3(1 z$GV?^o)1-QainLbTbh;J;d?UV)MZ2RP3>_O20urtBC#LXmVDDLLK^bGC}P_JMiFg9 zHpV{+g-?w=G~vCDPdVLA-q;&=0!Tt}SIthWX&l0oA42*D2Onz?G_3Kv z<9!*4d5*XG9)`}rsu_rKdX+F9X>FjY<=n7KI22NyCYPtFb@_R%wW}L@IAQp^*XZ(zCH*~HPq)0uuX^7Fu;v3 zToK>GB-kk9&mfftnzJn(!kkk7#H2z;>VF(<@C#Md%?xmU`>6ku763k)!$V^e`sgn| zTc&5Ao>4rMS_In!42+;9+(~)4NEVeby=r*Em_a&)&P(#tsAIWM!yp6sGBXy~Fa8g8 zfNF<_<$zFX#W%2;?se&P>p)6IXWCBqti}1jbF(Po!{u9D>eCb2q}5{@zG0d1XUs6= zr-P}Mo?SKfT73;4h2z6m@j#;L5p*19m5;?^z)x@W!#Wj8CEOugLf#+%VuTDf61s29=CMJfIVyzrZ8}PkMPXsSvM{+lQ<{)teVB*1 zV1b6fjkck~@k$%p;Cq77^eGS)-;U=H0x5$q2T~?C%kEEAvc>`c1t7{P+MOdljGPq7 z@Y4!JYCi?IMWdM`HVm_#HT>IhIJH2dZQ6}@Ufd6szG2{_AzR0pplqvYG`8;aQeP2X zig)tP1$l1FFk4D05O&@B$OhhssW^Is%nFzMkvd95&T4vtHa$A@)SH0Cbz@j)xWdYP zSj4>iSJ0x^vT+wK7ub{|H)Wa#jG4pfJ{=D_T0#=X7%A{Ud{v13ZldC==It_b zRhqZ<^LSip&mXKR)dVO3uxo%~B7b{_+2Ri>#^|PH6)mJcN=p}f zt2u>wN&EEPvHOt7`px3`GK1jnkntgDyhxI9{g-u9bC!hDT0_rtZkSwMo;HiS3lnLQ zfdF3hHMQdZ?*&k~3$iFefr8i_J|tuDtq0Bzaom4jA@uovdEG=`lW9oMZ{yj1H0g9jdEpk4fJ3>MoQ7Ukj~cl`0(ppUIEX z1i2I2j7_+$pjAbazL9XmLr;8=i-^dBs0fROynPJ$b6F{_FsxrU5oJjg~Tl?6l3C1 zPu>-^fe5TO0sG|g<98^dj7NGxpNCUhdwse<*j;;gNCwH`NEhRjPHC z9F)!JymvTifB$H;JN=Zxf(_W!OhcT`{8*am5ThNq?%CJG1CMY!4r33x;=U4i$1L%Y z?D)KBo%;iO`2$V2GVM-9=+E|1nQejw`Udo=_e>F^v*f8(=X zz)XAK@@?NiJ1JhX#TAr^7f^lu_(CWR(-8J)#im&6(|zvZq<1I2v(ju16eHZ4_s(#~ z``S0!Hd@5<*ln6%hld{wZG9BFcT%sP51QH-idOGH=@xO_#XO_12llux$_}vSOWu0~ zr%g(DIw}?yF?;+*5&^P)Q&oj5-z|WF5r>;J_X)|Rv$cG+4G5o;0g4fI1{!#DZjU1) zba(Zw;HD206H9EuQv|jSjsIyy&bfJ5^{g4nbn+-MxRld+!Sm`^4_doH^%L!*z7SNp)gQ z{$6n{$Y7wQGm_;|uR8-sVrla&HnYuZz=nm4elD;{M2@NaK6vQ@`9y!kBIGme8GnQ3dsl0By2b*X9$Uysss@y1t@T3nw1+^(CV^vkb!~ z%?tC!!AFGeHOVILLQQy4qqHY|BkOc9NZk{b{+t&7e%Z91&>HvUaSufRofK`p(gCtl z6KuE$PT8>F+m#B26FKW9uO^SMJr#QQ?pl4w`E(6nsu zk&0fe#b*oq6PhHamcm$Ytj80OzY}c>wMXYo>azvoL6%@4nftGxTpe zsh!v&)1R^FLq3!De8cjYAFy1|4SS;GuVnfVXV|OPP}9HF+2WWmP369~pMlqvN-b4SV>Z@-7^6P`a z5yRs2;auVKJLJq1k4w2$cfC3#iRpONi)1O(r!W&TmOCp*RoCn@GLMn(7#67&W`Kha ziPmTW!Nf`2&tUA@Hq&lHK0GhMmm}|C#J=JQM|VQO%n_lDujD!DEcHc+#6g4bV>rM1 zwUIl5Iv5tdV8l$kWVY;;>%jePco#K{S1b0gv4(Q;hDngX?o$g8B!J7=k6ltV65MnV zU|qR_2Xe0k+V%DS&I!)_KoKg0wjN9*%=@6?^w7APT!EC4HuZsSB5rU1G_5EU?S0Qn#$ zpKX8ZRy7%Oo;8x}^y%5bRqK!%ONMCBU>vtSqTd$LrUOVle=DvnIQcQsx5DJQgd}HJ zQRnxcXY&CIP?(0>n0yeCCwdy`Mj9@I8~NWqh1UKCBYit{VuxpL8`wqW^E2>n+jqzA~xeL!e)e6%7SYs)Gv*7A!`Y>&A$df~altp{}kTYzW!7?EUV)`=6AcH|8g$*{<-N9ZPA)ERP0E+VxkB@EKoCu^WDEGlpnx#P z-jICQ{7;U{3&CUjtTLWK+Q-!Znq9c!z%;VsJ@RZ<%3dvyq%`}ift*oYN@s*^x-r5l zQ_R8Z{+6*>{lvLSVBjwxoEX_5?n-dH1q1ZpUB#LOTy=a6uVVi3cKF=*+gJOE?b`|V zdvI{2JRV88cP`jwsZqva|ItNeg zvDF&Rnf3_6yTNieYWR~H^ut1jDn8~{y~YPzyHr2i>5l}cK{wqf5CD}A2DE_7Y^ac& zn*tn_A{^IOOCcL%<1>x3N*OO(L??YPIOfa_G4Z>9JI^x|yxOut#zRxIk&J+W0FC2^xT>*0^6`d(pJ&Jv{Y@2ggzLl|9@i3D6d|PN#~2g zPDJ9giW4xpL}OIhLDX&;sW2h`D?OnO@^Te_Fz@+M)xXLxHYtEV6?H|9N{7A2fF#kh*^mFGthsO=*)0MTd z%OFGJC+Obpe7>cQE=S%BApva>(fT&&#jP*0rDoXg-+ORzEtt<3FCbtKe6y@xtpV8h z(^~g?XBU9stgDDIMC_-}V}L<)YVi0lf)lu}1hMlZFo9XW>GdppS`@^_EZ1dq3Z#93 zrizV9&%Yj$$pw;p+={III8-Jzi3&f#f(oH-C&G-fgr%+#Dzk^3+)D652|1y2( zLMmcqJZeTlzg1;(F{kLrU=rz!6$Rp|1%INV1(frrB4Q&5pU^wJnF-|f^IsUhn%=$I zPP?AV)-TWbdu!DFX&`C8SWmc@!83LDOYW}r4g)}jA%m{R^0yLFj(Ie3F9+u7R8n5r z9Zi`cZbdo`cVMMU;UxY`0`#xHnvHeWjNy~Tv%m|}_5ebacqrKH+7%NYqOF>ynvBu| z>Ad%kxizK0{eEnrON$P${ZgjT{El;*9XYoS1GZECpA}^6>`A6&4jwi07iW(`sg1iA zDb&XkpZ_Wp)@)mjJ{^L2P@uR>i3;Xe@iS_KlR7??t%yKa5`rFn0xkA``xc40PKB5? zEz7E4bl1`gn>q|m`BR2qt*hH`z)SI&b*A_Uh`es16^1dwaYx{K<~-rV!N{@+i|?=i zcQM3`-0>-=snG+wXzR?Sjt-3uG9Q?mE1|+i^L>bS5wOBFWOQ|&6>>WTkVo0_Q5Z(^ zJzDV$V)N>{rM+qJul`*|&XT_20QZt@O)6)g?rM~q?5$&>e$g~}pZ_(OTO`*W9CXMc zs98+c(3dEHGbi!8(!RT0&!}uem3#k#LQ31_66QJwA$p@t>6+{s8bp5)+BNnS1=yps zE5HVggrmLfff7%WeYKOT*w%Jvy1T4OYqBgSERIABIBFNH6vDqDO*FG*`GX0@pkn}? zf8n=rE2kRV@Vb9~oMSDB679{khQPE&!L4B>_el^8kdNYCf&JTz4gOf!BJ}}}ny|2; z=YExbbpGo!Rop9{`Gn>O&37#ozbK^X`01IY-LJc)=i$=~T{}BgLZsCn+S5@Y+)!{e zCQ0n=ZtOc2+*PU|xkaKuJq@j1scOIFm%sWsxi5qGG4Ri`=jejtch}N)!sVroy#URz z^dp@S;294;rGSN}AT&lwMQn!nBueLc0Ad02#5gk83?6rsk~aJW%AfN#?O|+er*jUJ zv&tr7*!g+K5e(lRsx7(~xs=+1?>$qQBwxLH<555%aO*}uHmh_`tJ*{(pfD1) z2E|xynz;%4FgiEnc&doFBZv$}1K*W+ZH}5O3!y68PbthaOZ&t*Q$?(`o|Nyqf@)Cy zlYRjQO(MM>yiEx5j)RACC=w5s<~P=Y&W;8hy5H2zYS8{l{)hb$9Y1Rbvu0Mpc}+GA zo?$ZZ*#+=^^dV>f$PK`CQmCP$mw0eh%4cjk4Z~8=w?@BCp-D#z8KGKk{-5E|Ur>~O zXx0Ln-xP?<8~?C34lNC@g0p^Xp4VZc^`=juzWGfr@Eo(ZDE=6Ev%rrrnzA$dgIfsw z8NFn%UHVySNxA;`Yf%d0aeM7$xxWqmXk$Tje&yaZ__9TRxPC|6a5==y)m(hilSa+J z0e$>t1=(mK>ax7VP25&vdB#^LMg~?l(|#NCQrUOEPKdnoXWjJ-chwKtbk9t{(Z%W( zr24)F7UoWaWc(Qz#9 zLR4`Fsg%RPa_jTNVeqcsSNmhq0eZlFn(DusCm?Y@ZF!7M9?-tD;D(W)_?-8xh7_DN z7nxv}352&%{3%In#~Pa0$ZgMjer!J{HY*k$9?BoomL)+I5!R3}!L`5?XlnZ5Z?V08 zQwQQ7n#u=5=MB42_e>eee(bSG&dSa#2?2q%KAms2^Y!oNTwnadJ`{65>eAlvD^p67 z;KVi0ZDUO@Db_ z<>z(4y?hAlBg%)%P@X^WAczODLn-Bt)OjJ&hyRR6vjxnuH}j3v@ZFXuZ1Bq`V-t~? zCo)_gy5OX{ISpY5=TpYV+=T&aoug_!s^FsyWNyhevsJ`cbb?>d7(No zctB$@g+4W%R`UvK7|RQF2Pzgg3L>MASr8RjcAG7p(uQZEQNMV7oQ=@F=d{x-KE2} z*<5g%rMtFohF??r2ip6400bzVfn*Y@&?@qI7DMKU_m(52&F_9$o3YxL(`_~3n2bLC zvxE!ovXnP1Dl5JTA$g8@xZK1sOy?7R<}36qX}$`Xi%QGI5ziMoaRI{cY5syc=V0Oa zv07MoVdL7OLeBNcP_R)2#1Vu$MQw`v?f4kn(GU1#F6f?B_FK^#m)@tsdq1NOa{biU zDm@Lpzh}7*cdUvNb17Z&G(0+qDKfIH+nh{|at^3bkT>eY9`|cN>-BVKn>t)FQ|rY3 zTV?EmCiz0gbbBt#j|~eXbL<a)`XTB^O+e+Q;?PP^uq5II z&iC|;R3#o-np9_Z$$^Jq|AI3|eb19t)w{-F$T2?IwkX;sCU&x$K zk&mB6)VKnMbpHLU7>t0kJm!xBtW{rpuF|Y+nTYYp{H>*JHlKr6B^IMeP|s0bzC`LR z{o*p_gHj#OR8Hpx7@f;krONe^540eDKcJ2Utdr{bcI!1hn_)D^9$@2?H z-;i>nZbaM(DcYp`iG^59N+rg%)P1R%e;#80J99Rs7!-OuPBZ8xhrHW}WKqdfr`I-`FiqPbf)?0_{1MQ-jJEaG6(B|XhWI%!!|#@-iml8^Yb_=?aE@He5ao) zDSelu15_JJN-W1?e&)N`+Bq6$HwjkzZ-ac%M_W;Ji^0Q)%Hiye4vq@#&`C<5GekE=M8ssZr4aOVn zLrek;DLrg{F?Ynf=!5!-T!OXxAtck3<#?5;_@uS(^q)@i&y8RGXqcZ~5};w+&$mk| zJmtOp*q**9a94ECT8V%Bms}n!YyM;%*qf5#yJAZP=5q47HOPFe9Tp}%RYmsoM`Mzt zh|=xU+a7w#AU6(5U;EhXUCj-C%ZYvvp8b!0In&XwOGPCkKPG8K)>^OYV|j~PaR-{8 zF^uf@^C5W+G6Z)glMXQ7<*}}=Fly^Q;D8IjACv94EMb|DKLbu z8pV3ATKni7O#ZWWV{nwvQ2q8E;O|5k_Rpmh0vGcAaCel;17c@}bxOwe8-p=dh(Jxh z5w&uUt*cqrm<{r;*uNZ^uJyNj0q%!<9=-c!+?oEWZj3TYHb9Mk7qHa+j@~ZGuq?=t z9#lg+dZS)VC&8H{wG?6D0)Es)1d`v2Bm?@z5em8b%Un`ZblSNrlts*sA0y5Thv2CU zKLCQ){b{1f2;`j@M$Vg{eL=q47QI`vUkDA?=p*tTYqxcvF16qoF-w=v!q;v*&n|`T z$&;Numq~J333*pPRr3vhyTR!slhHlAS@zwDtnN&3N>3h9V9o1%a%+|*oIlJEtdaZ0 z9r;CKO_6x#K%gkSBz6D@quK_rZ!>Adt2j)QuHC1|_ovXm8TCicp`U*`dj{xd@Um62 z>Q5%g&aE6^v}FD2gGvx>0zbkMttDR6reP-oy!yiI#vk)xjLOzcZXT}j<{wMQqiJ`@ zSE&Bx76?uZGe5MEE|7nIUwlsWB+R}ZmFab=T7O6gkXu3qlnB_6UMjalcs^kalR*tmvMe_r2DTV z%^Aw(57_~%)#4mYSrjO8>?qHx?I>c87lYVW62{u*u1up~cYxqOAsYKMaAhXj|4aJS zgYGrhpDHyqdX|{~S_6&fdn7r0;G%p}MpN?w3w%Dmu$<8@$Z(iH`;a|S31-fiYVLk(6Rkh{+hvW&uaxttrsRDE9Pok%ttT%ubMHkYB!u8` zMwFUf$iBILwzdDQndK5t(ltCB&F^2T#lat-niM|6)Kbq-BRjZx+~9xu5Vgjc08BmG zS&}40o_!Z}GoH8p5w2?f@v5B8WC7_P1ACsu*Om3-Zl|z5qh8)dcnQ~OSeUu^3brI35orZD$hwY;D)jyu?RI$~U-yq&VuX*wn{w#yGi$6SIp@)hjdw1zAk| zx`CV@CZ^cI`sG8_YP--tK-FpQQL}zIUj2FQjVelg4fv1+eTqY~4(W*hQE`hjpC3dt zPB!=+NpgBYVaQt5`EG?Rl%*^-ggIV6&Us_Oa2c}2K8p3WUOUEfYv>;-!De$+ALx<7 z4Z;VYkW=jQIxrIkXqo?Eau$j6oPt6-0E$NREBGIK8*7B0Y1@czm+ddKeXr160<^^= zDnx0=WfcqBuI=Ql{?L0CE?pya@fla&<`eCbsG;FGpq<=dZd!ez=n=l@Kg$Kg zY&7`eiBa!d2#RFgnS!mE2?;?~6Kx>KA~M$U=olPro{chu^_*!2&Hx(>6U%X((e7IR zuEaw8wCbmEp@}1fy&SQv`GJ`9`8;11w^I0Qg@xe{1cohejjOIHnWci8FP4WlX*H>G&?$Kch~9e7rjP1sX5Og(DS6 zU43M$)B}@6$8?NWBIii=*y0OTUG_pBV&ozldY5!ZdyHq3L*LWa_&;UTJH#A;#0sC$_@g8Hyq?_h+vU!3 ztWr7wn}iZC4$wXeP?@y;t4uXsS8?3vAhny?;&)xDC5(;l=tZBcv=5Mfp&yp>sK?8j z0K};UHVu@Uk~7%QVq zx^XX+rXwVpj*jQcK>V?V%Xs5H;_Y+u=7XeT3*qD8qrN>^C&6qLDDrHhlYu!eHaiiH zw?hOU89I2(;iDDFa8mZvUry9<{qz2ZVk7fuF>?a(NOl;|Doi*ng( z9%VB3L?GO48m(eJO_i5o{SeonUN#xXhiB^tH9N#bXmUJ%u0xvr^CeH?kq2>|58@_3 z6Ae^}x^Bv$vkY-!_xL6K@%ll(4AJB&w3WqKd?Kb@S?Edgd&l<7&)P`0a>ZJ)$pwSv zJ#zmoFnj>vJ}Z~t=Awsm`#jC-3N*&t6XW;R53gKG!@i&p?o~4UzZSrt3lbqhsk1h? zxpd&B>7u{Jwt$ShBeccnvPAv|2kC9xEoJ|5Ryq%6pGb9N7_Q&R=FjDBGA|VfE!`;6 zyOmo+9=LRfboC)5vSjT%{sGa}xOJJ*OJ5VK)~t9J?F|VCUv~h-7Q%p%nEjP?&qp`! zTQeH}G!y&y32m0k%Ah}!77B3n2!=#q(mSPg@Qa>IYS5h?=be*xz z>}=f}QK62J9177gi3SV!%zyBN1(sWce}<#AN`Olrw7&YAnrFKcS8rS**W17*H-`^a z+PDG}@ zG?V;z@xv<%;#F`7%YUDIu5>h(oTfUEuQ!W#e>U(r=W`ucfUjB%E(NH{c_~!Q|Ky|e zJQ$Q)mCX5@Z6i=dNX^}v9(@c7mh_GoY>QGrqS1Y1qp-yiy6Wuhb6b%e1i)4}eh(XC z<3-%`9FkSS4OT;x$NH@}&%8dewJ5jQ!0N9COTz;Y^FfBGAZ>CBp_XBrhE$T_>9 zzeO+gL8|yfJkhU)x~{~yZQe_#a`IPf*VoV7`8>xKIT?IFC$Z8^Fb)qbLYm=t9nRw@ zvF?9)c9^vyL4X#qJ}^cg7pl@n#efm+*U}UlNW6`+VLIJg)^vWJ>d`@sT7n8PMvr&6 z|E80857?~UEmYSa4`hH{mp1Mu6!5QDD)-nzAM{AhOYmtRla%t_eJNPCDoAziDr`jp z|NcwRK1!&bGI}_1gfc{)uX`LJQ|zrQ@8wXCtVMz^9v@K;H_xfzUmYP^K7g1 zv;}zYSq!#A48?&09{J{*ym_)hT(J2aTqRn=$dH%F(mtExjl#6!=M552)!v=q6qMQg0%Hi2o6r2)A%1J zm!wTCyYY4ARDENkWAiGydSfs6#tTUI#s*-z>V@&!@j9INU%gcPa}K=jfU)FZR&aH$ z?bqrav=(Z=_U3ISyU(@HeycL|W;qd%1$RMc2E2vvlh`nvdz4qOOsN~OnaQuIsIY<4#U|9xE z*~@_?3|p*^h3CcE0U75?JmiB#<_BXIEHf~pUnKfa@MI|G^uJWP11z+cm^pbO2JM37 z7{p(9E&o2zJVda5JA)1Mah)4|SSedTT4@~w?o)=<$3NKc_xV=%L1f4L8DzIDC)-~8 z*4sBEb2YV?ZXBt@&PRJ!wwX%(Lp!W&w6cD*_Q15Wx}Wy#xNJw@R0X1Kkr1xCAc~rg zr5&XrR(BB0txq384|Zoh&^{`Rd}2jvn#Hf>gW&wo=Gp0zMB1LPka}>Xil!2}PmlDs zRa1Fg+7K9lPjY|Z!lzjR;Q`S4Bdc)xm`TN^ePAma)Sk}H*fngv9Hkc>)|!S>V{Xm+ z>ONd*z*2KXPubJ+%9WIc z15Zu(LVd7f1E?D0*d?*=$sHNA$Z{%&rqNW36RaVj?&LbcazS1N9lJX{eE_d%+%IW8 zzBe0lxEP26(fmYJgxFOaV(^;J2;ADZP9Mp$#B_ICdL|GxvOAqkCa}=CCTth5fk?z`@bFmcUcYT!vuQMfVg~Z_RG%cTW!z6h2N;yCAZV&TD!<8Z#4^w+yx2N(4vW! z9>{9fuP^zP=L!_^4rO~3e)`v}{SHfPgBvM+zerSGXx}9r6DlvB!@TPtRa+rzxClMC zDWy@Idn$;R^R4bVBPYJ`;6^rzYH`qCK+k|If2QtA-aN_SugdoPjrs*}w0^)qqO82~2BM=| z80FM8SE^K0EAbRL^T!sJM~_uBQeE=U!bZ)9C1jW2p-=$*cJ@{*>Xa*W%9;37&K z!ikk#YO-{&U@akC&tvO%$pEqX!}F}IoFpd-j;Be0pNXP6E18G-Z^Q>=$)Af7dS1MK zna7_Kb2;^x7p@ch)Avjd&@k&gEu@5myn*xsL37EeP<+v_tvws#SL9~N1em}?K24;$ z6Zc>v-t7w@l!`=Lb?3SQcElcyIlG8=hR3db=jQ|2$f9VMfQZgwm#(|&Djg#UxpFV@ z>7QT6;k;1N@k=9#sA%sF&y%d$+vTSM(i%ukk(8bcxCJg2D5%>T4P=LW4OlZ!$!f9q zMa}%laxXp0`-SerN<=H9>9wCeV`O$br>_!ut3tiwP~PNvRF4i#5_4I@y*Fq?3@3)@ z;SIk03CctPgj%=sQ%t(JccqB%rYT2T6r4Ma^rE#M#7542Ittq$`evw{DoZOL+f3Sf z{z`Y`{CJd!{ehe$9%KVMAmG==W8enL$E$x>6!@g?3r0rrhzu`4V>@@+Rry2kcSWup zc1TGlKQG3hQbBcx!leZDgxs_j)kBGke$-MKi@Fa^L=>JK^65klJv@ai<#E?4i?<+4 ztP*G!+5dk<5=up6tLK$*4IjJ$`ON!>$`IIv|O!}4-QA5^<8Gd z`j`cANHIL;fwk>iN%r^MvkGHUEEb)XiH9NI&IEi~?c{ zbDp;sFpunWtO+zsu{!Cxz`7*v>-pB;p+HFAKMD<6gXvCrYtRtf;?KQfZAij_)ctK- zS!>v<+38~A=F3~o0;yd2QQc|^eRB1ZfR{WQV{)%sjp11KHhz?oj?~M!N6dG-^}f1t z8K)C4<{2u_{lntn>%yPGmi+Nspd6(I<;U5ic!Ks`Nggzzb>%wd{eHA)z%T@JEU+PX zZXGy}0yW}`muZl0i%s6WXXxYcwI*VlDsE+=A$L{oPqf|v{o%o-#!}$3t7@5%Ydqyj zE{rMu@)%4HHa`w{9J=jZdMc2R;N=0C9Z2sNf+U|&xnx1SS$~iZPMeF2eJA<_%rk${JXc8Qb$DzipE zvg8@5zcJ%6aikh#{Zou2J(5e`!kV*3H5(&K?X*r^kZO7YdMvxV_r-7M;8fF?>G5H6 zX|=oDj;jmpeDhwA-IMHyZwf;sSatfC+M(H9ygVM4Y4<7P@Jk*LHDK&NFYP~Po&Kew z1h3)0pv1?WjZd0RQ}M$LbBOe#qYocBcF96?HrB0WQGj*1Fvh>2Rsfd8cLjhpy* z?31b-t>hjLNKp@@0|l~L_rqGz&^@{5oF0)T6PQ?ZK1?n!(G&}Bu04=^3V!dG#~|UqrU|Bf)aetaCrJwv8cM?p|{O*a}Kb4 z-J0`7rfMw^grRwBz;Vg?B?|?L+^}~G#Ok`a+u9^^8RWxZDnQ={5NhqY8E++r7!E>S z31XMtYbjWr((%TVs)G0}d$|`QVzja?ejB*Y`|$a2yh<(`&JUEL_=oqd*Cu8Ma+`>} z_kTs8PgSJUiI1pbf?f|HYP%mBb08;A>a+U6CY6UoS#nC^&>2JnlL*cLLVaJQK&ED=~*DC#GYABWydHd7K zpSjOxe=dM5V}}`g{Hpwm@!`bS&f!*R%6Txa18|+F&ZF`Sz+5fyHs3D$xck<{YfNG_qjA;@tUgr1Svc3e7Qu4@M~b^iH9K z_pQpDxhwCChc+VsIqEGP!n;s8k*1?AE2lJaH1gLak1&zuf?->Kp{Z=rU~c{M3RJg( zC&O1(n2jHDZjoJUSs8^HdHSQ$zv?ZQA;KRGECv=z3JF)_%L~KS(o|}jJW#qm)Pp}Y zSp%0x+ZTNRD|imlTnp;8%?S;)`$pxFARk*P-jKIHKE}myTl^s?GC5I>lquVualO7AmWaek;31w(ttRd3y`|20jWjNEwicFuVxFj zM=VP!Xww=AvBlsTaMkwaL%UheF${Di0^~mH-9AYD_8&vlk*Q6nSG#mCe?xEEl*KG6 z-bzSIfOGjmJyBY{I9JgG#icnGrpgdbWKuWIa_@89b@oqwWDUG^8W)Pol*pwe1P$kX zdXJ4pA|^-8CpUe{thi&VU+%2ZO=f8st}9Iq5(Ko*X24<+pd4w)5B@QuHn5qN;FGKS zEg5ai;vCb$ok-A_PapleGxFuIM{1>r!6!CTgqlcx`Rh>54Mi25Nb${ey8CJVx2|?O^j(x&~>| zyjLhPiMx=2w2JNMxRAP|QP8o`d)keS-;gF#2j&xhwS*>U-gxRdKD+pC0&}2}#gU^fUt^JAAb21kzyXJ1H5g4|}r#qDf(#O_Ts9_AT zK>!jPM10zxgxZS-iY%nz1xf2!rljCi4NJ>H>y#SuZwVcEV=gk5{wb;V6oXls!7iRQ zhxvZs%`DlNxG5}#xD(_VbU%qNVW$C+vU?4AGpR0K>FL^pCsFwz8OPEUSN>HNsO-;`9koW?y1^fX1a;0EHy zJ5xUFX*z>T&+}RYK9YtM`DW8R3A;xTW)UT zYJ1V+ctmQ~nm>W%fBB^stoAl5=x4c$-)W#1Z7IEvHd%Mcrc*!*-0>$jN} zFdir%n9UP0)@e_ndM$1+;w&3+(4~0tuFJ3Ct=+4=W)$Xka=WrhV`;g?d|d=KNK{H| z$h)e~z+;D9uIy-uq1V%Jitl4TJpH(&6#Ee{jaLmJuM4p#;>z2y#OHKw zNLg8bdz*9G$5mh+u%MH+TrAq2VavI<-M(bVdSW){UOKC7bUUdudx90Ntl#O6rUQD# z27Upp(|UR*5O6MEd;+q3#iyO6UVfep~ zpo;h7Axxvc8w6ubenHbs9i>Scj)O@F{6&?osJZa~idJd*NWkxKGj1xgxIePJBJMtf(Txn$1br{fegv=q_&dn#`?gXElhsosQT zH>ee?vtKP5jH@LHV?lj)XbhJ1E91CEo`v_t=;y7B-LnO0eyuOGt}no~=VjwR+Vsf9%j;+X z%8;F8VPPD7C&oZ>em6Q-io9tJSX4y1A3qohUfPY;{=|r(qK~F|OaATvZ5iLvfq*GV z3X4b6Xa-22)!)5QK8S5+?nMA^my9)i>bqdDXU9-^3~rHUyt)c^G)hsXDcccG3|r2M zQ2zTazw&Qn{yw+oZn|w_d}%DQd@{{bC%QmW!_Sf(T29ms72GGQYV6Dm#sHc~K(zqy zYd}RG1A@SeuY_Y~E3 zDZTAB3!YPz-|u-^Iu+nN!Ba_qP%+f-M=#Z*F1fbZKy@Z@)bLfXq~EbMk`_%OITAxcv+4{VASEAoOv+Al*F#6_$UscML2Df{K%f@(7PyUj~o`kD-9v))q<-5?o zkqxdJD*+a9qE~a>GZuOO0ryz7lmK6W2~ztJm~(C>CD7@0INg!fAT;u_@@9bVnUI5H z-dBO&>p8oAdkl=fM@c_b_m@5-{P!+~-Wo0Wy^rUeIiPe;hUZVzXQtsGu)udufoD}% zdE)v`K4Submeh*#IMG?Oi{K&#~vT9T-3CO`Fods(E<4N%bx9-R#LCU zmclqBn-i^PW7*jz>Lj&n)z9V6*ym==P_DdRlneRBug_EtnQ%oGjuXnr!r5Y z-wM2@r8cDUgt`?@nZ5t_5+nS9W#p>>(NL_GV(yM?c>BszWEu@NGwGFUChW)xnSNh< z(`JL5Y6$;hTM9~g1efM}zQCS`c0XG8w+n+TM78B=WO)j?zk^b>l(YjA< zouwd88hleYvajzZ>qFogUwP627$v`vKP0sdJX~D3q-2eZljMgF zSFnNy(vWKpY8)9|?$;`NE%hId%I8}-2AE5ySs^_jQq4^=iuNCM$c`X!zxIXKfL@FR zNHX#UoCxYY!Cj>+A$@4NLzgP{US ztdka>7iY`yg^H4owvajw;G@h&E&e@ZSk%vhF7ge@$Y{kFV|hv;+SPOJ7G=}(xE}>$ zW+l%a3z(X-!NWAw6mcdG)JKHIM?CLRJ0<9QP#QjUYGi|a|IMvon+GE*c^35~ z1_q)x_yE%Nhm8MIoHp8W;y1sSjjQnl)M9qf^yceEPA%Fc@?Ygd`&E$8A6b}MLuUU- zxkwQsu{N@Ub_q4DOUMe{s^$#obGpvHIbU#(IG*)5eR~;1tW;-h$JBNr8|K*e)iFf* z`r{({!5!Ptma|`t+}3a;rLbo`0i+a4clOwCt)QzQC9sdchlencW5jorY7N&*rroDs zJ=c9jX$5qC0@FkRi{n~`-aF*4u+=UgUld4qj&>>@O~n&NUPgXCQjz5`3l%?Jd-)PH z%stoL(KESuL#TSCae(m6`-)Z6N?*GkXny3E1@s#z&g>%wv`ltltORpp<8g5Ow zGQ3J*k;d8Ovp36Hh#`}c32=7o?B@9T^^d=nPk?8Yq!=DSr?$wLTJWU^oKB!ppK)=> zKd7tDK*j$Vkw6NIY{F;4qo)dYn<5qXH!( ZOPvQ@6B2T8{EcUP%2zqHctN05hNK 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%0AH;1+ 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

>rPy^m{&64m%@njLjSUXMiR*^4~rjsnKI$dJksZ zNyc2{WI`5s6-=k6+Ep${p&idhcAnNr#C}zyFgzt72#4A^UXMm}Q+~4tHgDEqbF3Kp zi9B^)Wt-L~`L?JWd=QspdU5*B7|}9*Crd6Xl+HA`Wy$O6WA?(;HsI(lW%W;UW#$5q z+y;DWmt1(5(jJizeKXmS4B@7~?|;hUWAr>^Xls|~!H2Xm3Q*QC|Ecj*SDvNO2UAs3 z0-H$`PLieU>$6Ih^;aC)c4qq>6GxU|SDV&l-G^Ni=HjHigJuLHv&4Uq8L2=>vHzK< z&>j>1zy`N$Q=qx{DeDx8`g&hhhz=9Nw&$!I(i+7^5Ro)rPZe=}=Zu7zKEqAc(fIPv z0$cgJkEV=U^f#sS7r)k5!cdQ)`I`^9MuiLw9dyTcH> zkK*u?ZX7;rgUtMMGHnHV>O%#g?Y-kLz!ZmGaQf(^SoXwpWRra6R3W$4aX3P7$W;np z5yvYnHQdR$+ZV0^j98Mcj_!fq@9ZbBO3ale?z(4D>J^%KVG`P7u6IcWzWUO8qlZ>g8+XwWy5*9QlC@R1$ur$q(JHg5yI{SB+I(J$O&2B#sr z9M8qs>|j2GYn1)Bv{_tbRA7mHcj)-@GW{PZKRE<(Zb=X$vF`IrK&d<$!`jKYeKk)3 z6fPx}Jd|d>cQ$>Y`Zwn9W}`+HGav}O!pTR=;DH_3aJ3`<`p};_c5T_zK$x4M>$?~G z1bNX&@tl~hjax;7WYQL5uq^Q(cyoz~cb=vA%}ndYd7*JggA9dNTw0`V=A)KeCyL8>#R-I>r zyRe95n3XsaaK%Z;Xpw-+(ke^(qR|KZ+fw&#Tx0z?x(IO!T_vTVKJ+KzJiXca zU3wepx6c*~j`KvftoCXC4~_?3PD1^kaJBHYXHcnycYI7qQ#-$QXczq_a1B0Qd@@8t zpjLsqKgoB)H^5`CTCK;wKrX3OenpjNqngje6soRZ?QEE&sy})6C&jh{7X}_XVvdS; zsxah&5x8(3Z2w}#e9+~<)xYefd-vh@HWF8j1OP6Cp8qX>zO~?&jROGN)<2cOvX)vI zk%OvO3cSGFow^4SY+vcR;Iywj@6}DcB6f%?#dLl9wj{h2772r13hc-t&Og49qbdy* zx(g4q`tj5tYIy5ZtS4^i{nI1}sNVj-@c4l7bQZHvB73A3$F{Pn+j;jxf7J!&-Flk` zCP|acI3@%*zuj0kp#c89S)hQ|By$ryB$wKdnMCrgK_)U@Ym>qGoky{9d3i3Dp4l9k zlod?hD8pa4K{;BAC9~5G`-qJ`?HR&1!#MBVy^+fXvh~TXaK0BGM-Bl7>38t&Y+K2I z>ua7Li|cEIdSR!Y^)!UQwYB=w!L2YE7SX_6iS>tO=jt~LK8+ek2670n+Ty9X$O5pbo3nI7mazfKpV=0DZF&;; za3&nwsdE;|Z9+t%-e~im>kAkR3O?CO1{o8;0AW`7=NmHbMM$5kv9S?8*^XJ8iwT<( zaZZWqGB-B6K?>|b`bpF<7ZawxNzEuj- zvZ|N!JKnT7v~Uk+;Ou5A&Z+5ijY{)I`Bz;qXB(W?iM~3(v-;iV>1KW$^Bj(KY5jea z?gP|_+nG4~?-mam^;8W?v)!Qu(H;DIi_2Y+`zuPLq6e&N5)+^{4yrK}k0T`uMKGCa zF@Xyif!Z%%f7rNoXy-Gx7JNu2cV4{)FOSGZTF{ya0G9vCqpd;(;}$C72wYc>@rb=A zwdmIcO$eiXXl0z8$?=^P1%X|qdbA9XdPod@$a%o@jT;=!KBW2&63;A7@;q3_>$Nan zTUg7;@o;%%e$8#p54Kb~F5+-!EI4~_e|(oVkSp}4wi}<+CyG|`kjQU~hfT`t^q$({ z7u`;ggO(L`3+z6fgg1i(BxD)XUnaLdK_51E-P^vMwfV}|Vt4Q8V>_IjNR+v9L6$Hh zM+cj*oxsaIjXB&Zl$$)1wPq@D67=JKe_Y}udrE?RjQeXHaT}xr#HMLPz2&396w9HJ zm^o5Nsk+GR3W3DXRG@IAk#gFAo5axVKUqsM1;lX0EPK*Hdj>Y5Ap8O+mp19viI;Du z34V#bqMk)xnWqxNJr!axMCJ#VYbtT_jvG4@WH+iFUoIJI<59i4C^v9(CqpVYCKlU{ zzQH*jDPx};zo3JiddlT6sYdIzb`blNE@$0l4as9t@l9@)EkLFFII_zU`E`W%gs+3? zFoV?MC`T)>UBGqZov<(F!_c@eE7r2~ho5&X12v@R;dT+EWVF$TrG*33S%P^~&22bc2+~O#+^te)}{@;EV_c{=f z@XkrI$yaoRxI<2#b{s4BUN5qm*yDc(rHQ(iM*LF@(aU~&@(u-kxK0_Hnb6x}hk^soV;)egex$;|BD4OXxTt0P^HVt=C_0dQLGuvCGYbN>Wk; z?`Ao|r#xj#s5C-EOwP)Y%M5f@#YX*&G}YGllXTt@>O&Ow?BO+=ma zIoT=MvghqK5nq=Qs)~j(Xo6nkj|Ns2QV9uv9^tzU@japxjXA;SXRcAFLkd?m88{}* z-=rp!ZlqIIYrnx%Eqxc`#RsCpKh}?eaCjXX|Bb#bunO|&fdbF2aKXSteeVr+5t-GU zVT_g4>)-5g5o;ct2#VrFZVu^Pnb+?d?2k98MBT%bC3jMD_VTl-e$E_=j}V$0r21}w z$7EjQwO^A`W4_&TIqM^)zsj8wJ+WY~U@70-GEY-acYW7`ZP6$CNq|4Hiihc)eExF_ zMVU^?J^?QBdv+R_jpg84Sy!W^7b%y&2xyNPPvH*IIDiisop3)(Bn2=#G(aun1h9F> z1s73!om7g+NoOh)X^!rvZJBQ9`IV=@iuI+Ay;NRZrjfBgw@hS3EwOi#GC6VgT;b|^ z@YniW3J{(nC((4vJ7y3RJG3*p!uZRayDtR)zZPKa3y`6_gIOctc)Q$qr_Nk*@PaY8 zn<-W@$MvadP?`O4z7+#&WdG?YmZMtYgqf-UpN}_lNkrs^f?z$!K~qFFBn8WOh7nz- zpS77*FpFJTyap|=gRm0^XGrSaV~8H7^~p>Ml?e^#3iqNo8!s2xDEEfh!)QjCtMCWx zT~e8(VlYo#;%7svT?vBdMBb5EEG!SZEyr(_IZ*%)*5WXGn6phL6IU2#n)D-E_p@~k zR)PWG2c?}l7=^Arh>QA8P7Krn!7^N5r;5FY*Z(r=sTu=!A~Sx zp_;ftfeT(osdMsvm=I1Mbm1G!dfEFtL{>sNzo-2XA1*Q^=uxnXSWDbZ5O%ZTn>=eZ z+-Ow4m9sm}nC(o3*MROXSGGp&eMHbq@Sv|HXU%@`%<%wOrw}QQRY?kCD{nd~m zwn$jPhs_7;T7dGYv*2{Q@6CwWP*qUSLRunzktn{?cxp`p$6L5ibJL(&$z=oBu54Dc zkNUslqyC8~C|&J-Y;`$Qxc&C&cEK7bNP1*2?DeVc8USDm_0=&dL=nR#C*qamlycdQ zV~pI23rQv;SagvM>#0Y$htMHrJO;!|pNak1rz@%?nay^Ug@^#t+IDdqPBu=hzU5a+E{_f}VA`??{R)%Mu2QG;l zUeuy2vqucT25L`DbNhb@hi(>a0)R-FUvBb(9F;Gw&)Kcb#*kj$QxU&VLs^8YyIJX?4AznR>Xt(vze`>o7QvmLZ$G#_;gfv z#{2ItfWGkYeB`)q7j}4$h3|)dvqu}CHNh*FbXs|X`#Yyl@7YvIXP|VGG_&s{rLSHU zZ%8=g8)FNTtR&BSHRZ7Ewu)0kP_sJmr;@C=T>(b6&6u19eU(G{GFZ`ScT92?c>_4< zdj=LmlW!ScXtdI+hS9|6o$ACS}Vk7Ikyf1&xCUm)?+jzK8$Z>PKd!E>_ zrjSTBTlO1dXh}~>TB{@2T3%UFz`WJ!Nt(`QL)&#c85UiI62Cb2Wq3OCJW1SrPA|j2 zI7up{AeYKqdh}+btIf;yK0N6rus@MwJg|5>Id#0n=CK+1KA9}9y*n;WM(Ysj)bn;C z^a7#R!MABs1b zrabE)yJGsw%o3YK+uB9NOzuZ^FKAs#t6*d9r-aCMiWAj`s)QtyCewlsk+C(k_m5y1 zx{sMFMP-8nhmKy~KN^3?2XfPTId5y~qbzw>5i(Eu{kGCBG?TGS#`enNp4(AsazNfO zXmTD?qGTl|C`*;$yOsfdYZa|YW-;<><=~{@hq0Ua7KE-DH;rkM?5i}F^ZpuRm1tV^?H!Dn6rrE56@=s43ig0dJ1o$KA*P7<`M&V#}{k()e;?;oLr zzNJ;N0vGZLMM6rTey*3S{*xk}B$nU%8f=gHVe1oI=JvkEqBs7O<8cMp`2Lud$2HJ8 zHItvoPrHR95C@92|1Ds@T=QYDGB!K#xzc2w=6()X#z7JDoRZH;DxprcPd(BXnyX`C z5L;)U{oDGo6jEA9*vuIx7(xRue=^eiwtu#OuX+;tBRkj{rS$1tL9wz;LFwI<0a+1a z(Dv70Be!a6@URlm4h8?0bnjrtxzzIkJ10kl?VO}P%9UoH= z?sdzt-%~XhqSV6~QNoXv2M#@4Rg@p`BhJD{>Q4XEMSqX ze+)`L0%=~k(F?Cq<+!B|bL%Z1Fc-Z!JL;K#8sO#hFlua1i9``*5EcS~EKh?D|S zLx>nih=eGOAkrm0Gn6POh!QF>lz>W#bPPy~lyrB;&^64=?~I@Cdfz`;i#zw+9nX%l z&vTM=py@d@ViQHeocFof&%`9 zFy$0ZLL|Ori7DjW?zpbGEkDEVst2`6_nSjSucls|RpO1KF?d#ZXN>O4sr5a1X%Exq z9?~+#(}rEcc?#$nNdEfcs=BhylMiE{{7TM3(-q{JsoX|(M|^jqEQM|0u(I4k(^42ud?1h1zP^2 ze8AWdP+4=$E^jeA@QeO(r8{C6`JQU_AWgRUFwJP8KGV<72-h;)Wb>PQN(Sx8$loA?jF%mv5e| z6nuTpW9#PTP^8+S1^yBzz7p+E#$T@Xb%;fzBXn&w}@`v^;t2sB!j4I6|PA| zYaN5Fd=Y_lU$bs5skp+P4}hjV{rEvsFb#k2v@zcuQp#lbrB^B^rxi)*-(p%~8&Yql znhHZK7hEF*4q)^;c-C)?u+fOKP4Hc=@S={XWw6s!M5oRvU5F7DcL z$e7Rc*Nh(*5o0~o*Vm6l7_4m156SXV3WFTM#gWH>JFTFSo6 ztvhXZM%%eD%>te`1Hbu*#9USO=tvNLu()+zajo7nOlH2wgP|NUmB_TF{>xse5I@An@!m{XS z2Z`^{Q~}=)dcEmJ`{x?GZe*elo? zao^owPPAFr^>7P8(Z5@%1Ccfq0^AfTrhgDIQ8A4MX`y5Wlt)8X=5-%Y9@qR-O&J|{ zCI0~Ix=-hz;M-t2hOPc@&+eWD*=nm6Wgwj|Tho)Npy*Xu#QdM&8{Ru&2X8me#qC9; zdBG?^H82+J>_lMuNqIZi0o+zTp(2^2)9~%}*STWVrkU3G?JQra<_)!Cr;l7t^n~+< zG*d9DrNNsfUj<`I%J^)NWPcX_j!ln;h*U+G6tQBrORR4)H~yO+BU6xP5mYGl%YS` z(q*Vls3pdgqz}hqzDll~oO&D`WM3@ta5>E8(n-F^~FbQ)Z98w+jv2ervRqg9K z@n);OxuDnR16yym*|3$6@i!C2aOqW{0aKY6yJN^8^wn+SAU$o`R&0gi&=V#YXn(Us zBlil~%>`8|I+th;DM^nQnqV*a+ibmwl;;9=$Nz2ze3=n-e~ln#mj$CG7n?Mo<@#Wk zbe~y2nGJDvJVluk<;wd0^*&)6YM<@PPM8V0}P{5jLX~~bj|fBN>MPI3KIL-VZ`Sbuk^DmwJ#`S2Oc7gzW~g)`1QJ0Vmppe4)jcyk zeYy_|FCnuYCh|U&T=aKWS3~H>M2p#c7f`-Xo7t#W^xW5>c6KqAhb;NtT!rAa^z2I| zx$?zducBDw8Pj7a+)WdswZcGdd0>+EJ{sZHr0(YYnoE)noTpVUt#y1s-eoI(-%QIz z@>lPQY;Px9kh=SeUxupgYbHhaRPxA=ECLP?gASBwM*=W>Yy0Cv^plP1sfX6a#dZ;o z{L~H0kbi@?0(5Zkn{rX$W&j_8RbPOf6YJR1lS;C6l!hZD@dhu_=`w{(BBcS_uA0`N z$A!s{=Ep{ap^teM>!TjsfXNIW`0$&1?xU+mUa2U+2=T`0rC6W|0@ok{4$K{9?btuz zq)&ydbe3IP-qkYx*M3qaL*i$~&&aBt`3+0iq)yuA)}aTi;^Vc(-^R8hiti*{b}gZn zxz*SS_5(a`jPmJAX|SXAWGgIF6GLk}TW2Cp-Sk4Hx_>)e*_%N7TA3VeIsNu00G^9a z`be?)8+#{%t*>9(eyccgEuNjFTf7le5MS9+m|+X4+}J39UM#!9#DM3u#tgIt3asOH zG43j@?2-;?JgHE@Lba>eav0=mtXC!20N;Us!HR{0xs}MbYMvoB%mpg3c1q|bN80nu z<;Om9%*t-^>yurKnEI{$Q30RKrE2dMDR64*Dq+?62HUGm3v*RiNgOQwj>US@hp)Lw zHrG=>5@@xPPZ}sB1pFhO?mnK6t*p~v@%t+zGoOn0H$1;HImr!I`V_}jT#zIJHwxtY zgxRRq#wBpPDn*_1Ob(GL{qiQ_Qd&{&wB(8dbD#@fPNTXpTh!#`t{5>7y<{S}!pw1w zEcuvvkw|P-x&C{o>ED?v+__6}IbR>0h_%DsbaTPRnpE^X zPfU8BmA_Xy(wd5^>B-#P)MI)^T?i_0VbDmKtVg~jMDw4^)Sp&M|CTeZ4GwvM)bYvn zl=?0D7B>0IWv+@ohRf;k`XilMD&7Fa^7N=PTuBur+!n z%XgpbPV?-;)_=VfFrnme?;J~_{UCV0B5r`JC@e8!?#uz+x~vW+~QI zG-OtW(!`yg@8evi6BHUeqMJiDCpFF)bCs3q)=1UW+j&fJV}{gWF0NVKF^S87MV9_j z0XLsF^8yFNrq*fKVEvNmjt}D><4;Q*Uu{1~LGu(&iNbo*!U``QuB|x}`es3TC$_Ga$`=}x_nndV!-yK=9 z@P}Xj6jz0{Gt|>S^|quk_Af%Xou?c=MBUoo`V;17wczGOKNqXNQ=xh{XzY5J_Y+Y> z4JK=ZCa%Xzp@ows@X1umze%&>VEuz7&%S&xIa1L1?D%4HN@OO7f~V?5W%&>|%*36< zL`vcEr(z6&$(GU3S+2KBOxMVSR6j^kHJc?V?)a54{*WfG*Wc0OE{gO!)7te8 zK;{_rQ$plMqe~Rx$vZykgFIGA@}ZQzg*o)ArnsqBZz5tCWm?dTRTi&bSt$iq*jayr zRHIk)hu@%X_&tuhjGW~ZATe^7k3m>RnM&&D`l`=pUE|@+)ZJ+!hfZm4R~gvrR3*QQ z{<9L&#wl+X>&({nCB9*iBGP!IFhp$Hd1OKC&pZej0pkGE&#t=~-> zk&?>29eXwOq~eCX+ry7^Gd>YhcKV9?Btq-*sKB|Y6W5!?I+nJ$jDfJtjK35bi;u3{W2>j40<(39pl#5s{=134 zTU=XX!9$29&JceFYPNWbpC+koxd6E1oZDN&V zO-tt|RaOdJtIWv}_(toteDLuCRH}u2);zR&7A3Yi{f_#Y6&rsH8}xGQ+9T0GYhhsy_(K^zFStb_ZmoVUu#zs3{qMt zpDQ*AmeFAosd=t_(S!7(G@Gcvcj-m?NJ|n{H5!S`+XatcQQos8mh#tH&-L9|*)aGd z4joMI_v=%!(1s5juOWVGAev_hI0CNK71tn0CHFP=wfHRV?%^RVF)T(ZyAnLt@9+SM z(Nz~(2Br+umE|tMpaw2GYq?%v3N5# zNPW9#_JDGWK;dg;VCd*TQlwNCvIp$ao@kdZsH(qFU)~chd(%3&)nic}v*~GfvBGAw z9VrD-UU_|Sn5{hS^sXevozetLf>SVQZ%}V@sSDRU_x8ea@fCkl>l7hjNuf`e z&M*B>$QPnlLvR%O3)5BG(@|G%GsiC_=!$MI*9JW^O?=LpOfo&Ue$aWsrTvlHaLuGjg`>?DiX zryf#w47?B&lEC~wt5nOXS+WaU5iLdXo^+spq${ha%amS&B;}=#Thr67{odm!8A?ZR zRCuInq$V|ynYg(9!Vmt0a>ECAKzyT>08zV)8}OqwbtO~w9AP$laE4I(!vEh)kyHvj z&XZ9>&d@yRK8hL~D^g@EJ3**Kgp%FJsXjXc?=vJu2*lAWy3N?nw@5L6?Y-+J^W7K8 ziHUB9=Ete8C16%(1`l2ZJ_S8ZI4R}zKX#M)6E6l$142081B{Dg|ECDSYJIYGE{x9| z`+;_(emR-vJpHGK9`6KUT@0Rwyn|!rG!s6zbZnkISNcqTCEHCwsc2l>)jya(mpK;)js@pGlCoOJ8{;W8(uVuXG%9DirboqM^17?2Eia6 z#z~vFSx)U^&5YOQQ?u1IpRqeUO#5i{WmAP`C$FFyP0H0T|C1L936xx&wxk>Ic=1@mJ)aXIb)E)h&(ZPEF@1(d z8juM#9NKc%#3kLD)@(In*a=067UuN}tl7jP@t8-!q5XxvH`3Gd#U z=q+FmtozjehU=`TYZAhZ{1K&}xsG1Xo}}PqZW()B_)iXh{GJ-i*KS(;TfhgAbdmT< zaCjDA!O$>+eh+7wnj=B<4Zbx#oi68iWgg^PQxmZ3oXI6_Ap=)u!Jmkeb_+G|GW4lA z@hH(fes*N6dOG!Gb_z-E`anAdjcU(E9KB1n-AKqd3XRV(dgmto(1>(Df@I4gge%}N zSd-)<0y_DwSW-T1H)6~;q?p6$GlS|x4u|iw@|yyXL#xJ zw@2{xK$0T6zlB~F#cDwF>!d$%Xxd!SXBSL@-%W(i;MUf!x{`%eH1oX|+Sj@V|1)qF zj%jv#c6zjJ3mJHSt+rVCQ}XCa4*Tc;sikraFrW-&y@NtMQENT@9%)}Jim=$OTbHnB zH0f&&-!zJS`S)TeN-g12p{0CS(%6HR&>!D}=s#!ih%PFE|1vaKe-ytd zW=aRbJAZH)!I*)ALL4aqJ2rhw3JuEf)m(O(S-i)jNABU`5;oeOTR?Z{{`&ec?}Jq5 z-vPeyKY9*JYblV@@Nm+966$|xM7mN>I=vF{qQ%H;u=#t4ksBre&s5*Y!6>&o8YA}g}(+lpzm`;b0Ov!w4wQBIDIE(1px>uEO6OO-m}I$<9O;S z-gM41=l&`vKy|3HKkUlYmEUd|l>UJ-x*S->51x&r2I%pE>N0*r=*R(D50VnvnisJM z!1lVPs@(VvU(-1^D~fjKR(z;UFyl|K2-F<=j&^b!BfQ-uI{rlH4@9_nuacLMuV8GK zkg-(Xde!H_{Tun-T}6^~7L7`WRg7$=4{ zdUZAMqr=y8kL=fqV5@2VC#bi5J$HZh<-J_@PeAy0aF?)N_W2vqT5+oSVvYi|1j4Po z!4skY-Xq`I7!RoRH7eqcsW}G;LS-3KQ`Dcu12{Lc3-eDr&_?F0Tlk=?htsc_Cx?vR zTmXb8SlZ@M;*e-)&jF!JbwG_4MxJ@!YE`_Z-V)5p{dY&evtkKG%!Ih7)@u&U`~8=p zGc4P{8$~%mhd*4sm}2;gOz@Xc0&Vbp{+Cz2c9`CZM(tEiw*E*s$@u}9ya!>>STrF5 zR5&i`V}$CrMjT`ln;T9>ONe{B}j+AAi*~86>nJm^#UBbZi2~v)P?r{`)^i=8Nh?8dJ_evi&{S zT`MvDwd1@^D}S<=L%S2=dDPEIGf2^ORX0>{{YHk z+A4W*Jkz;7&vUu{jIX_>C?p`_vNniS-Bpb-LL|FWk$~^X{Q8XIbsR$WJL%~3$4jvf zKQj+Ysr8$B_!$&QPAnaPiK?w`7oVcj=d|G&L=+Kar+w`m%>u)@9c}UN6CPbiC}gg= z-u=GAU&7UPv$BhFGT?AY5+#A!r-Qn|z!}WKY+{<}E{-FT^WQ4<9t!o-niX9hPKfrt zCp!{H3ZL;dItj{m=1!{^JhgE-EBCoC2*_8HXm9R72E_`XO_{_G4$_OSOYH5nk2Go} zwGp1vpmnKO1`{w!-NsjA@8*&h6}rB7zw~zfE)R$RWdA&Xy|aFwE5x>=H%Bm81&|cr zbK>>&Hq<9P&hC`mRjB)sJ8_7QAy7gB2`Jgy%!xRnv%O#fXwF(~d-A&H0Bs$b_C5_J z`03VqAR`r3KEqiPTDqU}2J-H6Dfnhi`i0mp#*a!@r4AnL=`PgzASRt8roxGqzw{59 zL}igXY0%S!-Qxg+_tw@10kiT!n1g1D5Ct3GlN};e&a|vYa8D78r!F(aE_#BABf>*< z>H(Q}Z=F;*okNFt<8-~B#Fh$?%Sp_XYHE0)k4Z;Br7G~Gfd*gN?~KL28K4wTY%V9i z*3}_?(>NxL4~6?68gS|z>qgg&*hP>)UQrBQFU_=x znnWe{th8O4SGNw+{I!IxKAAEH4T3AIPwfAs$cOl1CQt~11iq)=G=BV(#VRd?hIHOH-ORuipzL?QSyI3tPBUwA5MHASD$YNQ}Y;t z1VZ(Dsjc1&5HcQz*cU$`Svf)xnvv?h6Q!764h(6?bUR%6*Y zC3?wxoU7LH*5Hy@5ekLaK3r6voLgFlE$l0yWb?602I$ZG-}QbngCmXWEI)CD!;fbIS*smAq#Qx2oA_bFmZQYAjG~Fma#tDjp_h7)9Rudcx2Wfz)X=n=bZ$Q`Mhpj$p z1e}`N&tYGm9%OO?ykn2NNX(+a|Gd<;|iD^1oDJ>Ujrg{9yih=NT)g0LP2QF>d#^|MX)(_zlnev485_`C~+s_A-8 z#M2k-!1E`Tz3>O^F;*&su1tc%@d1KxU|3osa<#M2n{u{B=HisdwY9ddhQaFK<`>|% ziqEhX#hTAD@%A_3=Bwe@8p?eOn-{JN5f@+k^+ecjd_35^RxKoVsxQjEakIK5iG8CX z)2FU>MI4|Dlu4)@-Meb^l3C65PPKopvl8N6QzO$jKZh04yi5 zXA*5Kn7)C~ob&beHgGr=+?$?y&_8+_Shw?{?-t$5B^1Ke6zlkLWWnU+LU~rx15x(1 za3V0Lo6sc)omA%#?x=VCk)ue3VcR4z;ji)E+RKgaX4#X{Hct|hNgx9f(+xt&_FPn{ zQ`-9GZr(UY`rvHS&Qh?)*}W=&y7!2w)uo50na|G2>SX$mjThU`Y)|Qk$mUlMO!Z%E ziQ=Hu4ERq)vTu)-G#7s4oS;liOt09!^wPMMd}+&w?$QTnJ2K=DbFTKZw3GK?`WU~H zKOJrOub8sa?y!vTVZSjHk>v8et6KV&@E=m2aRSw#bi{L*xyNFIpb}gl-vQ=03DL1>|OW3tQ65r^uV7$Q70R0AQyHclqrdCRdO4cIgdF+))=)_5jN<3jx< zHG-CGbx&*a4B~os`r2vaRSOX?HgWzHqB8x?%dN1+pFZ{tDZnF$p;wbWX7c!eN)W$v z9|N?cdF6gz`C}Z4*}Vt3kH0*9p>MaQm{q;PZPVpOj-IG21@T3Z2==i^{yxG-Co^c- z%jotA1uEO_^h^dNYjL1(EGv6YD%SA-+0QWRx;5|c&e9|M7+7x`%Er4J{XWk#yN=$` zRZ>M8*o`>Rcc4+cah|}x3!K4Mu~(raqrBtS)|dU!)?`1BUazFcv5 zItSpb{6Ck|R4mFJvhUc3-8^(}`H5b^<#D78I7Yr&=q5u+GeuB=f2<7ycKk~f2=N8U z>vQ-@w{)PKUS*ts^?ueYXn6@YmyS1|P z>vfO(v`=Zm*j0dPV!;^@T1^^&B!dwbZd^qNihGR=yU*GW%;Ln|)^sCFN7LeRS?MJ4 zvh4c6!s7q43&+;y^GCWUdcnHBkN8&FF;*`GF5kW2!=vkkX5kgNtef{~V1jjDIN2Xw zEhuo2t)&1jA}4Byf*1*dHI)@maNjC763hE*vR_t5nugtNUftX6tKD5X4q=k{Lj4u% zTYk8vC(6zMJ{$P(PY$u{E?`>MHU!925h~A}dIvTt;~89;5=NHJ?D~A@*8_Pl*MC_1 zg7)9S_?-v_p#-1qf;w8qGb}DlCvt^na?hS!xPP)$|BH&Ww5Y{{2o7{*S@9XeqQvg& zeOIe|;_Vlsdz|b6p%nAVG>MjAa05VZ?`}6N1LlYrw@aVD&T^8*POB+R92*m5SER@~ z>PDHVAY`9>Sce~G&6jRIQ~pyu5>fM8*{3>mK)gWjUzQsFiwt<16H8uEe#j;-uDfcT z;8-u_^O~N=by>z`i0ixT(Krnj7QKtI?t#-}(NxhcuTEPkf!|gk)uh7AX*J908#4{Gc z)YIMm2Vo3TH<0o9yz22+n}-R6Bim0Cmt^0>X}r0afPTaiub%#l3fS-at>qL~#MTUA za_=g8q*=?|UuAlP-HDAMLYAUK0jb!1f*Huie~4jVSs>Uo!;F9=uM_QFjhOH~h_g<0 zrfD(@330vnn6_OClTsMc)V=#O(b?9RmtU{0s%qq)rI>sRDch*e5q5vr;FEK7PEs%{y|%bunqaymGm~NbY628k$z~-#6qU0X~u5 zKIC{6k!XBcsEbKGbGK^Sc4Q&KeJw*Rwz~zD3RZ3gl%J)3AI1?7m6DDH35Jt0lM;XG zN0)1t;yyRCrngDG3JtkXUOV58Jlt8h$CaXQB+9Nrw3u({8wBMhQyxUgxRio$(MNoQ z+|fN{reiKmX2B<2yPYQwuS%b-soD)Iy}{yOn5MTb_fJR^^_0A)`PZ0v_ADEfl zVLr(&+v9yG$}aX_0Vgs1xC%475~Dj^ZOmj*LoUgwtczjDKmWs7IO_a_!@C}d%#BAZ zIj&PH@4#1^|1){TsKH*`{is0OP{JlLee3k!lKb^bEPUm=Ucu1W&B@i=k#phUgs*!0 zXG2JhWQ*Ory-PCUIcS?N1LM{w3;5T=$*<4mCd@R@ixaq7rJd(g%^D^S8(g*!l*Utt zrG*bDJ_&rfSo7%5oyYw~OV_)lBV+=Z0TQeePYtn0ZroZsbLyyiMBlKnsrAP%74Ca- zpomgFZZ|eA?cPsgIni+rc+VOh&1GaP?yD7fE{88&02aZ>2SPS6s79XYIY96h#dyJJ zyNLJ*;O*3V7ED5(FYgxQ~pfqPyXBU~+1aNBoQDXJ$lv#TAGKB`Dhdbi~dhF^Ie>l+l;`Ry~> zOs@cPLd4H+{+0%45B}n2?p#`Y#GRzW{FpXF{*jc`##GI>?cBc1>c5VmO|EzQ;hlp5 zrW~T|0e}*hw)rfm>A3D5n$_T%?e|L3v0=AE5p2;?x`@D?t!nQ**mu?0w{l6}c(nq< zh8+JgQS=@@*kH%kCMKu>9Qm*Jl@KBgC($`1nQnJ{nqWj&9C!{3XtxRTKHKFobZ1j8 z`a+D$gXgaeqobdgP2{<_1If_-GtGUl-synMs2vHmsLdf&#aGw|tArF+>>CRVz&7&danTPY&rZKNFZ+625(HGDozfZQmb z*uJoKUk8IID|Jz~mg@wYM$h$S@HVfG!`3P{eT!q4*a$sUk4=-D3I0)yp!DyGF%V^^ zBPPSeIeIM;Me0@PaLE(YMk&GdHKTnh_LA=f2_G?9xYm=YLKaJ4Xcvi|fDM^Q!%kr* z&z;Uhz)sVG2Lg9+_s{m$APTRzubw^Y2BWTayNW*g+~AWzD*3;ZGV)80-FCN2Pf)NR zek&9cgkixwI`nYf%9&gFFw2LDlKE3UZfEUbccp=VWE zH-V7h&^}WY@;=OdKA8h?KOfufL(&cxfN8kZUZj6y5ckMf_l zUby+2e`!oNy?}ZMLGm(bcoJ@;eh1+lmVOF;?qQ#1SkN9zk!sGeLEN2bU34lCvEJ(Z8U;Ce0% zsk)G8h~+9f98nsdsyX)9b%O=nJojU)FZVcvtrSNQWaM*4n<@Swc_iW5`>Rm>IQRug z*gNc%_6>QmUctD7xwtg;2|;(yfbvQNZ{4n&MYM6&2p%0u(E24^*_)%6@+6(tl6^3t zNYP$seHw=#;}5(GAwN(8kz2J%6I3@#Y@J)%3r^y*(ARG0k;?g4I16mLZr0IG6^g4i zOHPv1pJ`l#iIrCgU0;6_^|7SsB!lZQxHy;;{J?AEYz&XXA#QlaAcyvXB3|y$)gh?Y zWcB+u_T8B6y<+Yl=X!22s&S-RJvMUCqcE+w)(Q5FCg*WiW){Po?%DjI!qz%+$Wk;7 zuclSBp{uQjj~KV-+440DIEGz1*>5Ka=-N6u+SzV3z8hLU-*B!eCr$4BDvq?Eg zT<`8w^(HQdF}Xd*=wRO>N_2Nj?-X{f%lL|ySWr}tgoCFDRR0Os3&j2L(gOSm-hM+F z@1!bu!;)pM;WbG^Qe}TLJSQG}DiG4=D%;e?&W)4+@fB>6oJB&J{>sgw4@{NaYVB+a zum?`ez?agUM8Eon`IV$Y%%}{68Xw1J!)vcB{e7@fX*x~6WD7ykdISh!s~cxx zCyb}ZDjswU{~3nou3g@usZUSieYUAzSy>z9!@uvIe$r`Yve+T*OB0CU7nD`LQ@p7w zf5eD-*t+yRMuCa~lINK|C9%AnnpzjIzuH%s$<4a7dP!}8i@F-~F69v|p*5=RM%~IA zW!kR%hvhYu*fPU?*L&S961MFO3a-#7!PY|2e}BR}Y07voNgOUgXl+fb)PNu_00&-F zN$^y%dn`C*Ltg~^q)!6$7>^RywaQUbqWsrKYEAcj2(6*60-Fxygth8cR1IN!_r&Mb zvOgj8=b`9WxyuXB0KS^s(DuN^ycc8PD(hGXNfQh-kxZRHZkcsH{eW=Vi@;LdO^RUR zos}jLNo)1&{Ta>5yP1{WiEEYfnR3~L6_iUVm^+HvJu*n@&`UU>+5CCbP7c*y11<>Z zFl5$YiX38QcM4laHiz~BmErG0Z^GhCoyzZWeA^-J9tFM=>m%n{x1Rk?{GLZl@PbFN z2WXOSs8E@c|9S3c(fqmjaKGbl=?BflhboXN#i}4A$=UbV{e6eqL;|5*_U}>4?dQ!F zbM~ZK>4?2<>N3&}*yXDz|1pH4i9RrU`}p4!2?i;qfc%+aaSmmy@j32wU^ zuHr*)*-(Ed=xO~Oj_vW(I!#Tq>pt1ki#Ixf9@5miO6~@^s`S-P&Xi34$U4u zAj358Hpi?&(WM9tiA7B_y~Khwb!%8@jbAY>RpGa}-y~&;dkV|9=O+u%o9S-f;puo1 z6V6R8auJfyLAnrnYxgzvfEKBaQ$Oabz$8XSA;rD!1iZn zqllbq^b&$1B5~kOhjUQL1t%s2N+PHzr%oh^R$A$MjD+^i7~n_2AkI%!T&TI_m|&Vr zaGO9=t_%-eK&6h5bKF1nz*8xGoL*(oJahd(jY&|Ju8X9_JY>Aj!q)w0UCi{biW-`} zMs(OWzmOT|Bz^mLyEQK}uAUAR&KcIomHTX37Rq0E=0AaU=DRMNHBf$zzq7iMP+^V_ zsOi~ADUs!0y2iND8NxGgXUg6!G1-!!2O(+uVGx<8KNWnx$pkN069&W5KuXT#FJ%bV z*hq^veO~Se8$rBs`0Q%;+(Y>jBC(?@nulEr)U9`6E_K1{rYpcLY|v|IPC zrm)&Ur+|IN?c%0Yt9E$3E-I?Fu#W8Om+B6wmV2_aPk+xvkU(i{z|WDr?v8FbQ;^s1 zjuCgdKwemPn`59z<|H2FN5DIKE*4y1%l)G@Z|CklTyxt`B#MzgV&e7G@)nPM>x1$c zCdkq~+29T(Bni0IxX_o4qPlMv?D=$%u{Pd{dRdad0{jo)VD-t1x{D_XI3blKU#q>? zUZ*L;%eYqo`zG8y=Pe;*2^G<@l`b4P(+dB@@#U8KX13ubX0-i2#i(dZ$ zPXqoS3_*3<@c<|Hnyxamox39cp3QI&Ea`}0jL+Nt^IM;m>fdpW;I1)yN}zI~nEF1Z zUR3zlG)noequXZ&xi!a9!eJps0I`E_?OvM_&RD}S-JF+OE}EBXW0Pe9SPc*G#Cqd~ z{Lf_F3D}%UsOZ$+v`gXFAxy-Sq_9BHJcvs5TgiWeEw)e0+JY2;0T&{`Vo(>%hHZ?tJ0i(V^>G$EXp>kh*<10f20O2UHbt{U|l5zl+NHO4SqTF?z#Axy`likjz#H zV)+`rzW;lh#U6XuIS_T&JEYqltn=mr{Sm2 zg})XRhq3EYUz*7vlYcQtQUOj{FFLLBJFhkP$>P0XMlQuNxj(n(H%&eV%T#$8RfYQo zi|%TZP0f(SGeNgWKuCV3&xK!#o+WSpNMb_097Er0gofd}5nm&+9 zgKx;jtt~t(o!{WO{=jyJo}p1mgkl0GOe?G8|)~ zb!bUe=ePS*d~@U=7`r^vsQZh2dIk^fDa&^;#B(YX%*}G!Qk0OFagai}(^J$?7xh$+ zU=GPUJX_D_u#s#uBIJ2C1|Fm)HHHc26 zf%NEsYbIZUpHY2>H_%T){J36~N7wp-FqD4xDW2QG7*%nq`|FVgN;&|>w%NGMQ0YA~XVdkX#{(#^AXp4v)d`Y#(@3W;PyjveAqMoKZ z^L!5&S{w3=fS@*z@X?dW;^=+-Ph2*c)%7M|{G(qsO;znT_HV>ZPb!TMeP9ZrTvrK_ zT6i$DBG(ne79-@ahoE!=hiU%+Z*b#0{{>wF1>do{{zd=UK~H89w%np`D5aXps{aOZ zrHTpFzW}mHRM&CqfkboeQ0P;pr4q?AkmMQS$I9>jHNSI$6|DSH&vwfvv#Q{HOS5h zrm6&y8+s0wa%JMC(sRPStlaRD%_a-@J-rMN@bNnq0S_8%ObGtCouI;dO>e%HQ1Jxn zi-B7?4MIj?kjU-)>9>DD-kp0Q!JbpOM4$03WVQ4s$ubeMtEZdgA$;A821~A$-sl}hv-yA+IPk~5rZ&|GYu)2@}ZKbq_Jv$rXIU=WfZ!#e%Y=mGwK*;27b^GD z9C25!qrBGlYQohXL^YPn4p&k+53UW_(9=QOya4pN=!W5UMMh;s66R5#pPmg z&;9RGwUSk#6?FB#!zG+nyJt^v@P{DfcA0dGEuy(4oaCUO(B#=A#tA_bKzlFAli;L! z)NfyPd-X0;rnfMHD<}(l_7k=)Ud=KgOdJqeZ+pCtbPB|oMJQ*UEzI%%QMTEzY7VI~ zfx=V%oF(>=tSsyfXh?Padh&xO?DK}o+FshX^+lBFVQZJ*j`qzYY)mM5TDzF_33phw z1-ql}<-G(o|LLh$E*xUcW#o{g+%O-6lu;kS3;wC6=6K@ca#o?Q-&#G{ygBzW`JvOJBk8|w5V9S)ONC^)G*e%Q96g=Gs~&Qx zT@m!OodVTaUtdy0fX^A@o%zD~qEoDo#qn456yae5#)WDv3-?SP4ratzNNy^9;F5~Qi$*?KW_zLKM`o}Aw+j}vy9L|6OmE^JE%`yad3?CbsAFSz#6?u;5u{n@<;hQAagh0m1d z>mlS9GT_)eGe6-eWU43KFVC_u=y;(6;r(mLSLLb*k3+G%Sl`5o%37Nk(k){OQINFT zC^;Q1Zi;2NZjdB~{Iq{okZrsZ_BtPAnQWkbK$Zv{9BQ0MBr@{)w*=m|aGgYF?qm8N zZ}+?2#lmG-VY_?Kxdv%uC+@Ln#2BGF)L^XHrECt1%~2+|tc5Et0Mc19Qn04iq29GQ z5$L)>U9!ML^!Glh1cAeq^1Th+<<@+uiJQS9+s%PO_L>bFOL8Cw8QQlPy%cuMo7t5x z<#xAv@BE0d@=zEAsnUZqDCdpmaqHOWIYATA;5eUMsCD$zN=xTXnw&uTvz@dX7xOmo z-J>e6NR9tm8Ta^mH}uml_)Hn!-S&L)bo=Z1n%ok-Gd+akcDV`%%;0}g&8VbD~0Kfw_AhBZ5Ll^*>!cCeb{}+oy=>>N+ZGwHSi$BQZjHz+^1)`A zX~pe(1Z23eb=zdmr0lFkJbxmp-AS27aRrjh$opN&n{cts9DgQz z>3lrP5coMofr)k8xu^Yw-18I5ZAdd4AxpE(MsJK2aYd(r(vy&9;c!FE=TzLMNDf4} zHp{cDNcT%y^%8ER6$ZXv6#N6VT~lO%LG6w;MV|t{JZUSkDH&5-;#a8iyUm*Q9gCqd z2CXA(&ifTmIMji!`!Q0%91X>EJDm*p=ML_qjLf9fQ=WYb~BVx0B4eec3KC$&RERD_Ei&t)15U zb`n=Cjvrs0r11p_)w(H!+*aKXgrJ0*W=SH(YKRJ^GKlufy}4^xoEVq&hO#*XmqD~l zSCsjhn0!(+c76;iye}-z?o?_$X&XQS)vFQ{i|40%ITn`2C{K-{Z9k0>`Wwn$br~=2 zJ}2)a$l%Ns`cl6Sq~%b-Il~@;FCm)e9sM=g0ihQJ#b(SP{=M8l zPr3)jh{?Y5JRcXd$2ACd5Ay#MT`Nxl$q@5fh(gFgEt5iN;Lk>(Ac`!_OR8>!@0wkU zGwYc=-H(=~?x9)x8<90J*HIgf(O=1#4|j|YyM8e9Kt`suZoQBC4Ol)w`} zg7k(l8cU(1;cDn$)ZNKKa`((M?QZCtJ(tc2RSuot3?t#WRiR%AYmGqi#97Z59e5RQ z46P(B_TUj4dOE2aSaJNvzE zv-QV5{edbkP2mSwH`6-ABdit#J{m5PJT@_*ho*@&GO}6v-Lls$A@f+?0_z*X!+@jN zPR&+tw>31_72_eoW2%j~T=c8XI-8Exyao%xhYvL<3`AE@>LAY7emy+oa}6ZK75Hb= z5A1wUBs>cU_2~MP6e{(4Z-HcD>&px0-b-qM_W5$i5^CsO`xJH^qfX4s`LyRaq>QVR zY<|V*DmUwn532fG;X!Z1AD*@XZ_^Dy-xUsrXQk>&+`DGq?mp<$XOG+T)$o1_fY%j* zkg3Fqv;N%^^YtpPzVBUCru*zmpqnJS^>LgKVHKo{4*8omh`Vo<`e6}Z?%RMImrtM5 zs99<3S}fOp$o*dZF9bl)@0N-eyFE_lm%58j5r^@Jn#Agag^W7yR<1*!jZ?1+@+wyj zuSuQVOyBp~uZYVA#JUq#^Sfb3!ZXy8kSc91Lh|hJ0Tq%BxxZVX4#$CtJJXe&@}p<_$* zj}Sw;t?_PT9%&8QTUV&F)GX*v=n$na!no&xRNJ>FP6MDl%Ro%YmZJ?$Co~aKDhwSV zB*vqMaz};6+X}kqrx=9POaIBV*@IsqNf!l9{i>{MGzYt_OC!*N&^t1K_|Sap!&5<= z4$A`J34zHyKA^chGw!(LDnB>;^l8(dFH4h{_s^ok#LLKd4wQoGPM*bCLQqGRERK!% z@cHe9Ixy>2IJC*cVrUa*Bf?VWv{3gHz5Ua5!i4pNla6_85tNSjhQ{oh*3z0Tr*3hn(nCx$0o(ZlCxW8ltx_CxgN#Z_Ip1r^rM-0afw8ot@) z%h^EhRkAqrH+dt8&#kT*iiwf%Gy{c|ydJ^wNzsak2{UIiX6VSf7k2j;hH}9=wEQwl zoZkK%tPoywp7nm~z~xC&QKi`-9UdmMBCdO{xylMcsm&^2W0C@pcFlcjZ17%7Mc@YCYISfkXPYIt*ez=Mj^6LxoF>Bv229{fP*We|@Eu z@hNTrURr1*lh5ou8U#ua!}v9dL%M_D>_h%zhRu&N*mAI`K+(E4C;?La*IEaJQNHu* zh0vy36UKb_%ClQ~p;Qn%v1P&r7`5zd@n_KL4-!Gu;-mkwvea`*=%L`EW_3{h;{7!r zv+cSGUr)lp@#?u8JT5lIUErefXWj4L7+VrH#z)2qP{XLs2I-cP z?oL6vJEp$v{T%P{{o)@Cd#-D(v)0n2muztSJBf&FHDqjw*}hV?s)9K0v{$8f~;`OuM&57yoXSc@!

xL)+;dLL%xTq8mf#G7g&G4VOTHW`fB(^XIm`-ud4 z`6nBWbwu~Bhul>XG7mAEq(KvYFPc{?R(y~*1amdN{)h;;Ed10Ec0JUEw{-Y0-UmB9 zUGIw=2T<4eL~djw(zqRYg-<7sn0BZn69UJVTotEqk2XT9(~#s*1qDQc20T)8rB4|U z!O@DF#KHgY8?oii%A-~$Lg+uL%wEon$XUV>j0hPd|Er6~ zNb?4HQjab=Z@D{aZ5iZr4#+*o|D;HwL%^N3S%hJg5S{I+kHQAeHk+OVM#0D9V7%81 z-3G#cc%xSC0>xzMn({UF#X}tJ2vstWK(Q})|GRPd)DfCc?V3Fg}Vu^*Lg77u+jExlkKhMp96(o)I@1ogQO1GM6Gp;k4TOs~C-o&1kOy9SW16cDszU{1iDBuNJtc-0 z=|?2)$8M+M0ZR|u2mtbBuZr!!zas(ib73A5HKq%sDSKyJ&WedvAB+p4e=%{R_7M&O z4Vr&Ur)%JxrmJUlBg2FN-a{b9rv)0HM%fW zVCiS@yMH&Ok2hqyv^X~CMal?5>+H4c+qY7_yv}#$Ui+2Jk1Uhrn^&8ob0uI=VMt&V z*u&p;k_k!^;>Q<{udGCsBGZi^L;^$vfX#poCIn)-_fS6}WU##cR0{3eHytdW0-j(F zaz_1j3umZqg{6p;!Na`@JV8@JU{s4XpZ}n|7bUwUt zZ_F0VIiqGlIiADv{qdERpjigfyNTj}n zAKa57rZ?6%Fr~7Qt@6YY7OT3POVIE*P}ew-d?)fKgyN@iPN=w$RS)1;uSK}F?1dN? zV<9T3_oQ5KgajzK4nPsamjGA{x`i)#C?Hji6+h76q{F#d%@8~lw-pXXCdQ^;yi?fH zrN0S**H4X1zs1tk1+HJ3zDM5TL2KeU66Dv;-C97RB0pSNyfW0K7@B0H=uYy*0!c5A zNn~OJMFp%iL0OC-@;);=&s&7-d*0SphriS~_5;I){*9ql1t0E^yC=6vz>bCTEak@* zU|5&`sieC$DWP-4Et6XtctguSvXJ`-&~Q-C>AcU&$)7D&gka3Um`-v<6f*?3E_+OT z14!HrflsMe0uBX~(lu7)%SBGPd!d-TbjZZU&avvtO4aL_qp8?g^!Oo6K>jlN5mk@#Bok;1NCP)&Ww0=|-jcjN&6W{cHidh- z%u~&uJf%)^(Ryc1>TUhmF#q^OcwTF!&({muUwiP=@pV0y&B-0$#fU&J6yd%$`MGNA zw?~fzKV-~aSafeQoTk41^=3Qj|c}~+-x81 zL!(_fomS{%sa!p-nls0QdIJ(8Cv^7?2lx;wE6U#L!nbF6e|JH*xWHQzfd}tN)hx4B zbkH!X3Y}fiV|j=IftcJhLs5dQ-7I3dOQ_eo+gtFyNeOxts&=4Diw@~g>iWIPV&Ek3 z{gq!ZA(?l=D!JTVNVmft;;Zo8uXtSL)FkRyYXQPI$O*~~f~oz4hs z_AESo>kK=J6$+>iH)Y({=Y$Irq_FaTdUtYKK8Hy~ny!xc2Ui9%0YYVH7Aja)P4oo1 zvl?==BGn*Nmdvh0(kw-Nmo*o5Ear39?blorMk7w)5blWvV6*=YE)7u628%NwXOnAx z;^acwABd;Vwnh6g)smmR)Tbq|!t9Op(FgyfV3Uo~&^GDCQCk18`BZ6kAM}l}KN9>* z)*a61W6`$It@zj&N8{)>aoU3isCY>N|FuHPk9h8zrPAx*B1cNoxl7Fm0)awS*;=_7 zb_7pW+Z`PwfFBz{!z{XD+#SFZWJ5mN+JN-U79ZONpc?VPVLQvLw=1n;^rQ7>kV~MMQ;JG8lv+;7$Xq< zC1$~OHG5#73N`$pzQqQGeav;=)aW{MOWk2}=PKmOjj*;ifjt{!(wr#Q0jCX2-!i z)|yqHVQYsUwcXmT4xZ;XnfEuO%QGVXSz3kPQ1wE)05b>g>&9beTcSuqc&-M(r1anY zcM6pz3=W1kRqmS)3?k@`4$Q9ZaFh)rVqP3F|M=nO4emSw2kigp%KDZ>JEz`)WFpvE z`szncSWhhp1t7lvaLG@FjJNY@Q7BS8rfH2q}=JBJ&e+F#a9PM(AiB!CvCv%XrREByW zk~y{cwvhYtnQk|P3goz92)Ttk$bQq6@*U>Gxk>{Px2(D@CN;r2V)vBI7l;G-A`%{Z za0H$27b&Osq$o#7BCR!C>sqlv`QmZ^Rd?HgEb0zH`dFi_Z04!mlMTt4Ueh-yh1RSG zOmZ(g4Uj*`fm^mec?g5Vwm7Zb@2w4TR5aUCDh|o7UV%WIXqfaLx21Qz_vSuIo*A+{O`VB6>`PsM)1zn?-7yV=TzhGU-CUk;efUn zS-0|3K@sR^@Z22p;Z44+>27#zWg&RSyhsr`S74sr+6u17+wzAzzUP@#vu`QrS`QUm zUTl5~CIOin=EwKo23+nnm~|pw&^rh zadY#;ym}g1>v*gWBIE?Mz@UJ-u;-VM^qg7vQD12PfDGJy-leQ&*KcpN=+PSo@SGtk zY7j4`*R0g{JNyhBY-pHA3KIgrCXZU3xE{57Ya9nMSK+~804K593@qwR@=PTYt}t+J z?szfB-KXLXF2a4YGHNE}wRN%Q<>{*=wl^DHuC~9s)Bz@Udfarn^u5(sAYG#${f$&l z#7lC51VZ~*KZDJgNvsP+t2bV!V-MQFGOlCGdts8B&d{$&jGWQXQ>6IS{7bc5oo|ab z6~gwx3$F_bc%@Nx9tf{m&HFTumwsbfa#uN7jz}QYV9r^o-g|s(BxbmQe$Ea4d*}(4 zvEcB|Pkj^GhpXbSPFDUSD=z$t_F9!l7`Lef0WDm|ioxIpwu;)x_F+n|h3rvZ;EvQo zp%tSoeIE&6FE{``js);A`hXOVN;3SKbJg?N(qfPb(Duxno7cH;fP&y$+X#~u@}d$r*wI@J8P zGp}SXxa&+5f2=vz(8AY(*Vuh9(8ns7qjbNIkr0B^DMlagv4Cva$Q*3D-sEbGxAM%k zdG?Y@u}}Hgwon*y0Mp4$jlwVP$J5p_(9Mu(DcxKB24*_3>k=|h@QYzV81X;1kyK6z zQQK3Kd0GzC>U+e^PuzjkI}Q&__?FwnusL4%8GlwgF6Jir;txCPi-<>r^PlkyVkFO!SZMKLhZb@CA@Nq?)CE5R5$|*E>$} z8{}%Nx*(&I8b0&g9k=35@0dl@_M#@&GKL|oFP&0inMye<v*+-kLShGfjn`m#L z&5i=EXOzG?>=bB~6pTYSHLJyKe!!MFCwd3$E8+E0E!ppjZntFP1&#piVq~Y&Zo{7G?!YDSwOJRb+VbJ_)%990 zuH1H$$v>bS4a}fdT3l%8pd9pn);06B3WAf3S`G6d`)>>RIzQ@b3c0j>8_XDAw!tfX zlEu{aGl$0FQI!?G!f#YB?Kd}GQ7mtPctAajTXfHPU-2U1dQWD+-frt1DH)wl9FE1- z?J+7Wb_5-@`VkhSxs6Uj`jl(S{8pR%Q68G(CkUj${xD}7?FFj89< z9IFvXqF zSZmN@(3X*xnZiQtORft5p;eP0=m0+zZ&wkqKG3)qUzgU&HE`v#>=O60Yk|(-g`!xLGp$pH4u|$Xs36udCJYVR(fFl49 z1@4b_SDMo-`E_PbnIit=HJbD((ZJ+)cJl9fT(*}gXnWuL&-!t>%Oh8)O0Tb}FA~U} zz9EA~KofKz)o|ou#M!oAC-|)1Nyvs^l)&h{UMRzL9ZHBb>*7_K09tY;*V|`J^8>pB zGEITn;=D6+@0q{f2j2Y(z~Hp`?qgxqA{O{Rw%?=p2@vRji#IlU?b=Ocm4jR?C3`Y6 zoq3v~euvlgu){Hn*=rYLt!U;vhHe=h{#PX4Tqew+?nn4`^VnWN*vZRxnvbdmxbOm9zk`N z52W9wsT6*rQ`*wFK{<6Ai*I)wgm(jx*}dwGYUz&sk<~~+>Y%`P2voo@kugZRab18j z%Q-jSHu6=YpG%%5G4!L@pb<~vRwmfqG_|!;Wx7k#H7fjH79bjrQbx&cUyW-UGnDg_ zM}o_xEjZqwK@<{0c3p!EGFMeSHZ!fYGKY%O3S9X}j0EEUr%Jt3 zt!7Q+J20=qacpp-VH!BW3 zeFlxQzjM_A9T^{}G+(mzH-o%kPg*LYyUK{EJFVFdZdDd>Sn|2%mU&x-nxAv6%gp04 zjZh89z9}z(exjVnRybY<8sE15Ic*iQ*hB>d2mL!^xMVD_#F*~tcN}-=U+86{NSnGc z@qFbj*-S*;$jL$3j1+g5bKA+YrRD1g>lDtP7bWJ}GDwuxd!EmglXa3Rr)JKinyafO=Bly;Gwlf_w_Bx~XXF31?@ z<=>}p%A(DP)M~^_-#xWy)Ig@|?PcrC2tfHxPk=xPRGpDhtHaIun>j^p8r|G_Ye73JGur@=d-^Kh?LnvYw^hjByX?=FWf72|K)Bx9%TtbB#%f{JKpCuD{-Cna1v zf>Pjh#*GsT_(uTKkOM3->DIKDFj->o_?F;#OROYk)^4Lfy(BQP&aOYakAnKr)|wMW zr~0W?t_?w+)*P3HAIAsC03g~6SlD*(Z8t#xUH4Md?Rcth^ZTaQbyS|=W4L4xf0VnZ zBJ{6w!u`kXgiEe5nT)7B&6BpCwIIop806h)&(-kAK>9m2v?Aa;3;7?r7~og=BXx6W zbXOf+#TNGPuxuK`!F(WDoG6tr)bhLcJpJl&f55|zHyMJU4!6Hh@o*1#lS&G>;%|Yb zk~W++V|IOceDzglU!HL6E~YNld_a^ZK7Vey_&)yf=H?PZ?xrf(AiUp`)rRQ}tEap*s;0_|3%hZ$Ap8taWI zOYAM2%Q>G?b(qKVrEm;xUdku3Ec6HQYKm7sql$Cyb`tMxEHbV9JnRynDSx-(%2VAx z$oZL$XJF!${#DLwcK(3}1tn-J?w`_BPoeh2lCa6XK$=RG$cVa(6_PLE_)971hZ?4p z1a*q1)as}u(B=XL-UKxol6FG&)rm=VWBr+Ab~p_dfUkenf^}932(hV?gsFg?rGoM} zXMLM-LjpHngkmHrxDB9k1n$HQOlp4GnXQ$7$wpfMbSOky<69#G8n++s?1SCZ=EpNw z4UG-A91#u^`zolQxOdC1I=13OgsvymsktN|`?v7rBa`xJ*z2M;3yV*dYEvAwxJuO& z-F4IaBtyh&`Neuua`RGiu+D8>D8EHI9YFXRu)Mxgt@$PJ+Bak zi+Esl1v;iMX~y*?5Lf<|`x#NVP*X?Vp|oIB+-kwus~;!t5?<+(;{8X)g6rL|Pg-)Z z&x-^&ok!*jlNWvcgmGw*BvEDH}H*e}z!!o&njF38e&p_{8 zzIFT$6TKc~aWbCqnAIQ!2Lhp2)f@+hJKoRuUxfqzP&y*jU+ABL=bW{rQgZzI%{aF# zTsYOxN29X(EUG z(e6_UeSesbd%XP2x1&VuZk+NEa8=*dpN;oVzx((Hb6*it*_XW4ID*8exnpmFu>e>3 zU$M5SHhb2o%vV;Ug65;Cx8Rjc5Jk0W!Z-8arGi^La`$)r8$Ux&#;KT^dTk!NJSRkJ zT*v|tZ&SPiGTA8A=TZ((FdL(cLCM>a5dmZP@IJ}b%pXAgm$&f+3qg zrORD=YDe(AaD576|6(JnyNY8^#{KmyM0}0|8_+=Fx z`NFr#SZ^|P8~tworHP!c%k-Bj%k1odZw%R`!S_qaKgaKvOH8I^st-WT|^(j^{df7bnU z^5tCty2A9$Eb5e*7lgYo`RJ1M8U?fm;4yVk7JE2lS%XP zE@MA%=v$AmZ`wc7M1N5rDPl6HW`CV4%vf$Y^*MgO=WM$T-gkc`zi}1`8A9AtzD;30 z4=8mS%Jt7qs9R?Tlm~R7{}im*S8e;H%;hu&XeZDARuddFRZCx$W5kGO{Zub`v3MTZ z8MDb}si^ zerUwVT_?Jne0NDJm(*MW!wObe3$^_(-@nu*)k&vN6C>(|Zc?LA>mxdSGCCo)YB>S; zR~YL_!~(Q$3yshLp#-FA$=n-6PV@Tu!)o532MQ=|kfH=E z1Jr4Eio2c~d13a~iDhlnK_d%AfyAwW&wC4k!+tW30;4iGoiC$BIZb6~YASaH+n8u~ z1VW$VzY_|y2l~qMPq)0`;15T~rn>b4 zV(**w2MsnFSgirMy3@EG7vFZgPa>m)O!6v+zh&T==K< z(Tlr^qaOwWqd~#^|I&J?*4}QN)dlaxImu|s-RNTRRyF)y5x~-GWyKwx`SG{|{phtf ze#~s^EugP|v&!tyH(+>7WK~6neh*jpxgljVikLn3GLws~RnTnvHu>D+vgK$0Lt5PU zUAo{ON~=>O&{htBILxv4*r2K7{dCLYRl5EQLp6?$O64~I@)?qKC+lV6`fe{tTlHgM zzQF^+8)J+=Od0PqN@}5NBTU|ByB6 z7&DA!*r8dHA)kD)xS+D83Z-th`M7VWf238>7b-D~t;K2N0f6%}&0ezTfThI*!)0m-1;oDGX@wg-J!l zXk)>#`5stno;i96JeOHO8$(=&`YMRn?4Y1mmkEdUIRbr;OiiV4yu0{~6j zZ)h%^5>mrReYU;n%yBh&?7$F@;6f@{Vurn>5G_9=S66Eu)py^Auzd|>6j&X_ss6h9 z(dDpapu1NY3vmlH{*q&SNe9V;O9PO*MtlH)@*0CP_VZV8OpnE#I+UgQ4YX~+SqY&e z4rgzKSY^mXMs>_O|L2_jiaSy99e`Nj4-1-BDX!oP*e!Dv2@{oPS`1;!+X*cd&*Xa7;>zaD@cX7)T0ei|r$r~ik!v-S z?u$SBU>G@AWf@p*(nKD!upROJo9*k9>hBbH@-12MA0N;8KcD10bd_8>+$Go5gmR*JClZMrl`x(`p+R=#+7%3aO0 zIMu{X@AeFjp5ruWy>9xMk24U}dM-o(>iHYz@Wk@(XuK>s@((6iWVp@#}EK!~3h~VaKyn8JS+A8{oc|E>2-Ze+u_UVtY zNXlPF&(;hN3|(%W3K_A#_Iw#HiTAqga>&+1pf`J+Q*C zZ)CR3k?h}ue)#c0<+Kx_Fz}upm!lGrrD2zP3g@1VkSrK!~(pIn9#WZs;$t zW1a%taIR`{9m_?}LA=SUO-I_;l_H2)Ym8>!0^iq%cm_0@;-nk<$eg5K^O(wJiyZo@ zJ*IrWu_6b^d+QoY1T4x;x@Se)y)OBT_JFJ&-+#>F%R$R#0Qq>Gp3`=s*a+Tj!YpRw z{knZ)zssjtymGJK;iI8!8V#<#2hSLyg@_{ixh%J23y>BRPjXG8>|jUA*#zpc*okln zzBQA~4z{F(pW_EQu8>W zW+tE4*+$w6lHUGR=>KW_Lbc1b${p{y7*Em-!Kb0jm#PdIVt^LMN&z^I6GRJhv>i#J znhxF2B5I+Cw^fWGBIU}@#mS8PHi&Azy^naCTw9`O#gfNpv~qrtrZ`dJz%x6mlEm20 zT0ZCH*3hpsP$^Jbiqz-w%hF6WI1PZ;iN&yNJn3Z4fjil*F&&!g@oc`7YeqY|4oZ^W z=`r;#4vFWXG&yqh)5o2xe(EQ`HEU!Y2(_0JRdCYwx7rcKxZ$ku?S-1^gamNYqC@d1 zrSd68iVg{|Oi<6ZQ}mkuybqwS;{yL2spji*#A$bi*Lx6e50?=Y@B8Q z*^YADGmzSHv1?`|guc=IbES4h#yjXoCO66F$*0v#fYx_IwTBYf zIk>2?2m0$*o4xvQ;VXGaEEsExAL6E5)~CCl1EFq=qfKSwtr}Yt^#_dIq{^)bnon}7 z?=(c57Hpq~aM=&(jlCyId&I~vR}QBUFYjC$s!%A7s~l^7aAWT*s$iu=W}=E2=e0Pav9?b}zK*35qKg{CI;FFusEAT@E40EQNz-IH&_U>1=*zKXuMJj5$ z`KNSIj+mCyVBjU?Rx=s%d!OIRyjjt!)083d7LX^^-d*PYb;_8aQK-1&8KfRG^w--K zi_MNs0qvy4v&S;eI^rBHIMtH>-}Nbdgtel8_J{rvEfkh}%D(8nVK+IRK|vcOYjmDa z(@V$Vx`L{7e+`$n(d@Z5Q=j?8g63xOEbZ%gHkT}0$>|C^V>Iz&U}u~fS*hX9jofz4 zS?gWxn7QvH5)FS3asF>;^+oquplr9w+ndqEUsqDt2y?%j5#0L{?U<2?z3;yzpDQM4 z74efMc1ig(Gct6&mxI*nVVyB8mjr4E?ON0Hdh3OVL(S_dx=p|2i?q1eG98e@M6BgM_c4owK9HEE{;Qyj3vxm zfxEves*0*ap}oZyH(w<<KqRy(d=_mPv|f(kY+NRN=9>B6p{?)T4NLSv95 zxVZ%wNSw|4k)1DPbkP>eYr6gr zrBb)OH1oECqw7sx&1B|@GV&I!95kHc%p8e-jc1j13wWj3*gN}weO(Uz+aCsrf5e!f zdwAEpp*P)?^4X^2Yv76AFn`j+yqhJPDDNFB16#946uac?eT%u!y{Tf*uSoJgzD97_ z)FSXyOM{$3o>Ef5i?hhNAP*x0@MG*|d=P={gbxQ(goG0X=;Lc?*y=;yCt{F%!DtYF z0UND3lZ2WYo<*P{JW5}gDyHwe$uR`;Q-|4jR{kud220(EO+ezXZ{OKuJB_Vz`y3rQC?Z3p3`1bBG9G%7}n&nkUW> zy^@U6cZO<`arTpZ@GE_nc?ZDkcsCn=OIpIquP7DLMO$ zrN%c|7q08Q(d3&tYcope!hd)cBzvI2H^TlJ)kPKOj}iEV|N5q)KX((_}OL zovMATH}+iQ^rAC)0lUuRwWOC?d57w$gf+cA#K&)g+wH^)~RE zke?N{7Lj$A0g+{3CLI?H%?naRL`NNDg73!Z5Qmfdco-x@xJ`5l>NMW0);=*ekp;}f zM=ch*b#vdjG?J?(*5Su(eqP({OiyBZLomX$uM)$PBPk(&<(bA~4wlm{v4&{kH6BsB zz9M}N$|1CVwoz5-()#vHB6rf_U*hgRja^2ez4(S(@IuPMmWZ>O_CA?sA}+y8xtLq| z+v?$BpWUDO^@&+blZu^VSc5)XPup`c{0HJ4a=Fs44@pl8hNjHtM`5i96p{~I?^jsk z*xOCK_#&v35lXY4wcTd4ZoA67nGh?*FxC@ zN`A6y5sDcZsGE`SmgM9mNlS#{gs)rz%hUM z7b^dQA3!mUy4!=(O*q2}L#ibZ#M0>bZ^??vNT+5S1N6z$12OF}{d?fw()EHFv+D~m`i+pLf>{pwKI zUD@5&>`taXyLRz?TgIJJ1L2)|b9h5_KXExY!Yb$Fe8HIQ$#y4{FW47GtqjL_F%33y ze~3EV3noh}Q%tGu6nHKUpi6>+L)ltrUr8e1n{Y=jVl{hPHFe2SSNMlvPwxlM17*pr z*Q}l1O;i1yiofNd2~Jc9ueL{zB~#k*;So3Oq7tQE*^Gi16fGBpLsD^$z^2k!AqpJW z!c&abkh9=@xc^g$H;g?FP6f}fX zn356>V)HbpdXVu%lo=~9eV=)mNJWLBB46yCmel=p3->kYoC6!i7xyZxvq@$C#qFsL zDhj+IxWRqM(PZ>nw;?-2#noxL?3v26!s)|cZ-2TPcWqEtG}38sh>77obgq{ z-o1lVZJ&{$ECC}-L8H~Wr6Y)jM=5`T<%sT}V){BGCx|=wD_0rSyYW2RI~0nUld={-oP?Adu~_F%Tjax<#yZ!{nbZbLnas z_20;&7$B6$x-u?z&SDlOvEOCxU>AO7#L}um<@LTtq!H{vpHMn^u*ziX7DQuU){%z@BPWPP8 z^OJmzc(7}*DdLps#Z%@cG+-gB zhxPx%)~LVEP_xF#98GQ4;7v`3LMqi9Tpdl4z>9~4HaSv^j9b=mObqG{i{dsH6D#-b*jj3~TVQ$DKBNlMPdVJ^M zh=J~pGu?l{Ppt=N$UUV5v*6ImA6#-jxDB2s+7iXVU0>JI%h)rAJd2t7KNM0@q`Za;Ug)pb|9VDW)P6_RjHO}t&$__IOfsnM!+2fG?7U89`VWLU-kgI zGlNr5(MvmK>M!Y41NNPpd8PiGEz!2(Qbr1Lt{Zc&$)f9yH?<}68;4ZrGQl<1wE{wy zr#IUZ!d1nihbA@`A3Z`*QonLS3Jj=(2g46T}?iC(dnc5$;Z_ck;ZR1cD#`s6`0K9Q;0Jo!^nw41_WZhG_%6Bfy^o5`>G z=2XC1{l=a#RE)tq%~HxQ$-V1^J4nmS``t1%)V*_X1=5XAlQ7yzP@*pSmxyfMg|6ws z7zd>O+V%(B5!>tvat0(xueb-ru88-%-UY^lFlwu$l08wYCfYA>Il$IWl~W8UH%&Z1G)bfkfxiD=({ z&%6t4c7_;rJ(xtCWJ^8Hx)TS3FB0MQ-$2kX@QvDfcCT5;uV68RxYQ}O$!*1)U-9co z(oqYAR#1e)E4>fVMBCT>=p))%(w&yRE(NLM8t6BAWr5 z$Z|YS5o++8eK6cs?);G$Gf*40U!Z6;yiJ!fhI_gzOk*1AC=RXc!aI68_Y^@ITAm}2wIP-IFWsi)^a25etWV?NrJV#dRzaO>O>9m zD{b&Ab0Aju1~%(X1eyv--Mnvj!`A7&@G)&>JETQm$=nZ(%+ak}K(*kn^EhKtPG{{U zYcny_m8vxCe}bp}{^xe#5>$x>@Eon9 zE6wZjGU8zR_c5y-Z9pxotieYx*Jxsd4}_tr#dQ|avo~2X>T1Js_0W{jhgQzNx-?9VTE- zmYetDk0yPLoUZRC0(Y2%XS#ar^XVTl6g)7WaCMbZ2 z*WbpF-K*>bG7~f2U<(ZE?B`qB4v*UK3~PE5P(k|3JkPnNYZ5CFpqp%qu;^9wMV8k% z2E>?m_SieZODl9Bbw+iB0yY~0f$`$S)8(H@F!R&oTSUkK7-HbTOF4p&?%O{4KIbLz zhP>OA-gkNNje@0lQAQrd&vI06Mq8fCL17cT<8g0lrh4)wH{PJ;o~U<$JEm|)hT2nL zRlzigVmD=HZ|#9SCBSlz&x*4r@FGxev%po3IIWZ-v!tH>;uMxxwVu2b!nSljeva9MA#A90C{B9ibMq zp!ymw5B@c3Wri-;>Cms*3DU8>seZ&deHL-{)_{Z<1r9P+LJD}wnu!GH8D zP8)tt3TC}WgL@Zje!jslU~Gh|8;YMf+7}uZ7@1#s7T+l!2KJl`oL76{zK3d>N8VMf zi*VWFQ)%+MzaCc+ROL}rG$jXtdcvh)uW(a9jMz|qsi);h@WX)6e?OZici&`B?B!hv zLEUg$FT3Xkzc4Dk{zCslzu#Mth-4_)wMGvrqO*IvxOBW3=UN_16(>=ryJ2EnQ$Y?d zIAd2DN|p%WlXQrQlQ^-X{*FIx+1}KN<6^?oNaYP*tEgf5EPRMY|gA* zp==^9tx+vc+&v@TUCp?idcf=->staNG;1bH3AY6G?v`$Gb)Z7(7>i6tzV23a2d zVXXRFK8y-a_?w`BP!rN0n&>JJB%B5)#LupL2RKZ?2skJ207P#e^0Z$*ig=P5Mi~hN z)o*>(6FP}+rLvWnA?HY2)B~4*4O4DJt2KE`u>*FW64Z&_aR3eILRf zb~L-k6-#zch1yy6jghgjg>l=mZGIq~F{JY)6G(!WsL{s+j-UCiw(c1Tg-2EH_3JjB zXd=TkmQK#^9@WC=-#$O60}2WL1IfoH9?;gi;HSlh9Vysj9@OcKzUT3ej;7vwtfmD` zOGh{RkDW1V3bDsR#cIsBnT+8o+VdDM76x{DEEo}SZW|G&6&-o$9D8`Q#xGvg{^u(8 ze}J8#?BPG{Jt6JPAF?PKYKlv~rd$Vz+=e0uGz$ivSN6A>T z90BIjKV|?9e$O2B-?Z7!jHA#WkKt9%j+v~wRco{zFh1QL(4eg6uT>q%C~Ixw7){|)!Qdi~)NaOeIl4$<-u z#yPyWC-o%)4_G6AOYmmOu2b;7ZF${lxK`l3{-u(O(SGIfR9+KzM0f+@iG>hGd5N(V z$9HH+7KhJOJn*PeE6K`W!>5!~odSKW$MNIG`YaA%m6-{jkTsr@N@gZWE8;rc?H6w9 z+;(;ESuJt08#vcWvWI^$sPJE!>?1tbqfp=)UnBqSxcgzmoT&}rDHdtoJY_7Ql!z_H zDYvaw|5cIk8M`0ShP%kwcfbfdmKuvs$`t=;={Ux#G&KF4$0OYw+VYZ0EtpOp(z2}q zHNo&w<7wZN1hmu#GsC~<4(sg$&x(nS{NhqyeRDkR`x^B$_4pkLWK%5g-*iNe(*=Um z+YnqV68b6YK+j=EvwA(}rO6BXl{)V+!8`pik*2PNDy}dbHnt>ds81r8&3sk%y2kyB z*7NC2oka+23HW=*dU}`Y?K%=HTLXJiM(0FEM{YOg!E!g|ay+;Cbkw+I^afCd8zhkB zzeNEr7HbAYHD-yOm9v^IQL>J5E}zMg-Ay_L1saQIn^lO5&j;wxNer8g;c+RN06wlK?xVKVkC>}&l zQ{s|FvGjLj&8@xdqzl?C9rNuoyTO83&yg6kA7ZdMfUk$alQMFz)Vgo~PNB~8C%UlkFFd5yy=w_wGDYpvr zjvEg5(J$}Rtey0{tyPk9arzb~(bwN(sNtU+Oku@eTe);|-wva=8Ag5m`g|Oy-9=pB zs$|w8naQsKAP`RJyMNdNC7<^Puy~S*5+b->LZOZRsV+5tz@HiG3>-O=rnz%Ttw8j~ z$w1^Cm0SA9=IpC%rqaZd9jzNnb!r7og~PTToe?gq`K=oR!z~(|B;k1qz?siq0!@&_ zKH7$^fE{-bTTz=^jpsQUxa_?O)qyjp+kIpMq$%j6 zdc}C6;O1n!vs2ezq4N!;(kwe(7KQBOcAxp#wy2>yqx}VAJM4me6W=Fp8SC)I3LSsy3!9=P-*oaY7so9gW;X>ppC!aLGjBQ7!W6BuTCsD#|}%xCY~2%FTu2`O`l4?f#ka$ht>n}^ylV` z$BNa*%nL9#O+t)8)8FLngZm|$X@nBjSVUjG$U2REF!EH+m%5SLv?!eg*?05TL9hMaI9PcL>PMxu7Wq<{$&wi+EZSsmR_Qw)iY=Zgr%M4o!k&TR;;q zW}S5;kwj8~Y#jmbsdJ(SE=v~^qwjqN#XE#g{QEIXc8cR1OBZN&1$0b&2b0bA*Dp4q zZU=fsmccVKy;W64ua|aAA+2dp*xlC^mU5^3ny5r_%FoyhmQ7FC&_=aXpDu;kLNS+Q zYk{~Jw?$c9G~GA441I&FC;eMQl-I1NfK<2t%rJ(T$WtgAER6rHDV^S}8o3gh{OK=$ zM<*vg>6fiBbX#kL<($*-(9MZ}ou7Z{e5gXBcR!ZQ@7YSs<<*9S*nz|cd)M0xHVDi1 zJom*g^WT=SZD;3#xef&@dv=y(q5@EE$VyHQSc3`0YUV_vy?kIas1LAX0c<1){WJH< zmo8O6Ps>TEn9N%o$)0pi;5PAe<((goO6l@;w`4xCHIypKO8#4&CEJ!hbmF3rpzP4| zetpv?&728b?CUMLxMav(6Kful>-<;QP_U#O#0oyuJYZyq|4)8F|D;}jmpw)enBxMB)2_gq| zyb$F^?Tn4PGlDvN1yY#kl(f|duiKIV8H<;7E58jo4*!i zAu}xdb0@gA^`+)MmRX@ZQF zHlf*#Pi#9XAnQnJHMMSK<@1X22F~thMrd>^iJW;w^VynLMXM^lHZg!~P5S@~Fc8q% z&=tOpQ78xUfS}n3vRdunn8G=Yk-ww2pocLN6{Hg2` zo19t0WnOvaDdO^P#FHz4+N!(7>GHlq%7c#|p?%ilm1Prun8SNi+K8~KcEFlI47g%q z9gk2)e>|2oi{@9q2^?KoD1oh8dy(1LNaInSA|aiZ4rkQo zKOH^8e|5*Xn$6CVk2eZichbw!CQ>m@C@OhC1^M*zg5YVE3EkcOZJ{D?P>a(Wd%U4w zyOKt+=cv?jy)!u89$#tI0m)H!jaw#$=jAE6yc6=ioUuqqet`RunDmLFJWpD(PG?jX zJuQvcPVy9J;hU4wfwuQ6AB%lxzuSmotp$B+&PCZ|DO zdiv`VQrH5@GRtIRyeXKW`Ie8D>Gza9z2)P6ujD&dl`|nL5|PY^(k@SOgD_z&uDonn z}3P^{{b{X7P0>YqkbAq3@M7p7`N^2P(x}=h9Qk{ z4+HMzab(lvb<3ofq+rVgwB7o~pRen#I2GGWqzN|W6b=QhX$Q1s3BI=etxNyto_>7x zoHpQ1z@QEUk@qwM)|Te+AtphG8_?W%?^)?+J%DAcPXnyh{4}4hF0PKHCMy&Z7bq`# zHh9;CHQwKyuky~P5etC3c|LUNziAY_91kn zr->8aUgYugoKKa;nU(^3D4uMu3ZlSDqW6zliLAFR|EYwEnJtomGD)s4nAG#ABTkZ! zNw{@KNf3w}LXlR+5P>_vH$H+ZDFu6fHzp08-^ygC_rBk}s!I5}IATHELw&@bPF_}_ z85FcY05mDy0l=+wsm9?tk3_}Mk{5mmkDNOjVn=TrfSnEjus;9%@MA!6HU6`1BLkT? zA~ib|_77|iNv6pzharS+?P!6c;%xQiQ}b*qd7>Ng?LXx3@T7U9SMi~9>^h#YF!?ZH zAzTRYz(9ss8M9yc|TX?REN+S{^7PcVbJHiI_6@?I#g{K z&gzWaDiJuXFf{=Wy1gm$#1srMMf>7)I%fz#{4~I;r_D3XIqKGNI#U@ZBhZ+jYz0BW z&GrPkdJU0EZWuJ2_sB8oU*_+^AsU-Qn#ga?byw^B7)VFQt;u=UVNBc8<0*+*d}Eh? zAA3hFoROYMEzIDO85r_#%`1)2(tc2!cx*J7}fM3PF>MTZURLX#^HJR$sZ zW(G7=(jaFpkVsrLF2%<-c?~+rHWj{VT;8Gn#5BseNb8eGP(c#^H;WZPL6?5mK z;M9*EVv3yanhRf)Mhnl(i?UWzPxH|p@57blcbRJJw4Nv^G<&4s7Hi={o5}i=qA~`` z9cSnkhUieQG=iliD8tuTsXx&#b>+YI8348Aj}ay96MAiAm_GT0{)Z$r>ow)N5Vu? ze9hW$De%ih6lY1vxC)<=AfVE*Lx52t4f72 z{F46!NA|PX&*#RbTQ|+2nC9O}cu7(I3xn$Uy^-A)YHl9?7M{?~4BTG%eoH2MwXxdW>!HjfSW5M=MniSe=uXyF- z^73db_sqoM?44aTP_Lv)cHB*J?t*pe62*_^|#w9Jb zrpCa8x4dn7SJQ2(vm<25wZDL+b;Z(E3lHdJrE6@G@(P3do_g4ecNDnQJ;|(yo@&C7 zeNmHN2S~9CKsvYube_5c;7d=zuW1rEV&CUyvH@)@f!QoC*GbILUIO$Z_Xd0k1q7{L z$|LN4OB;Mi)-C2KLpdtrEvs!`AFk<`gkI#+A2eZemO~Le3{vrbAtnRiEB}ut<}S)4 zd1JOFn8X=BG4{NC;r?!EN|W1Df5Pra9lJ{U3TR7L#%NK z-{<-7GfwuC8o?rH8gu2@2R`(PnawbRELRegC+*qEO#M;f&+i>~on5a&PlWrw=oRcI zwex@*c$*vayj{Y7&S#!w)C>PoCLt7Ei4q*P+nhFx3D|n- zRT9VZP{gms{fIvcIRP0*(u zo@`#(Ci{+5%Ygy&yfujzT6*E|g$g>GhcVr^WB`6% zB{{_!hYzoGKih+>$xG=7*t)uLgiy#|$#jz23PkpDTkbFoDi_N4=Ko5q?4NcTixE$* zdG$q|4Z|tLa4%MG^Cvc52lPzRTW2UMw;<+uu6M&ah*|+2)Ta9NC@S!6(4#mByh#jx zux#S8cHj2Z(~3{G=OW{^Gvceps^_AYli2!}4hTlZE4w9VHKqib4Kb`7NxYphRtWUE z(LER%9~e2bBAH7U(j9X62;aEbW`hUl0i^t56wBXw2z2bX)=RemKJmRRx~-Nqy1LilmNT>Nzc z+@PeBdy>4t=yk5=_t;EYjk)Nm|A0Ug-?dbgOg0TlSPxbUum}!nk1EGdCsvM?$DLyWZHgtZ+kO}CKB%w z!_)parRg%_agYJ#>h_D#UV$0iZJOMBu)XS)UzEabegC1-KmgJv z0(4RuI%gaSojkLj`R3Y|(Oo4RXVn#$d(2(p`>FjMoxrG3^zwehsT;W;BpFiVR1@H} zV$}UGSbWIkLQJG*M)-XYLM*f*;VXH0g#rQM@^y^f z@6z=TY1+wxqUJ)`aey_P3*w{U;@EKMJV=G!EGHJBRM>;v0~1Hu&cj3bxRdIH8#S{mB*#4Sp|8OlfuEo(ZDTsltAJ0CN7 zCmyZMQV^pdqnJEU(U6pGPB1tq(AY%lFYx@H6={DQ1E|uG!HnpJ?JMA|8{3shSCpnjvaDeV=_O3v#=Zd|%{hly zjMR9Ug+T{O1(_Z*Q`5^UsQZ;iSE|-ykZQ?qytd=9ZQ~qKV;2CL`6+O%%Ufp#t>-7` z*>*U=;p|^h`;W(k2LEXoi&%N)$jvE@NPLeX8a{p5a=Y5)K5KB{XG_^aDc=_5i_s)A31 zLp^P6x)(A+HltnpNS2>Va^&sayI4|QQ5Op@H=LBuA%n$R6qjiLg+@KKGz zCt)F|Q0RwseMVSMj+-JhNp?|E6x&1U}0j$MsozN3(Cau)ft2A+zy7PnC-A zP5r^>D^zRaAd*v70DOFDPIFads_=yOzBw~K zw7>idMqaTU)y7lwfM-FEVLS(X`< zXC)kGs;QAW0iK7J5%7I-^sjo@@4MI;5xa)i(4Pl#>e|^}K)-H={9hBQ?|m8iBl&UGFY%~ z!q4fQWmv{sA->-&7B zpUij_48=&e;Wd|=?g-&>}goE8vV=9#Etk z_a->j4wFxMu=w^cNR#>psf{OxM$sRo=-aa3cZO2~Hsr)R_}(56DF~e7v&{R*`F4L z-hZ{)N`z6UUjQe48%lB9Rsh`<0I>vSt!r?rZP*{-3M-TyWv@zE+Yw|(H-Y(=*D!5E z!s{2&Hq)W)*G%tyTdHae8pN&HHVf40U>|M6%N7A;ELK|cRAyDZh_E>9*^VSoy;PzG zW+NUVR#?N~)P54C=Ak^K=CsrviW~)~Z;HS;fj1E{cm6v)@*=Hd8RF+?LX&8-qT_JT zl<56g9W(BKIeeN*82gMn@RkoluWuF?TYM$;H10hxX%Np!WiPOO{ZOrlFJ*KI>h0#J zFUVu%&$peH2C_;z0EEd3P%JwKbQG9Lw@%nc4lvsX);Lb}en&3Zq;N9-Jsm~lF5KJY z|7#Q+ui5%n#Z6R=={6OizzIC!SV0HrW$-Z~wUtgmy;!Hu46&n{svDK+r1btbaawN? z=gA!#p|({{<;_>2qGO9UK;Ou4;RKwlE(@ql+BfUp_LQG#q?7tvzXD5_u8G)Nj}#a5 z^-8a~5PHmJ!8 z44{TG?8)A=q7uM~dwc7eK@}mq^sA22#6?PD_~m$u=iyf9%9F4ARw^ItHH_FDa;Pu- z=qBh3j`eQk(_l|_6?(zk7|O3~g$>O-NlN)H?=)=N46(g`*M$rp04k6mpTmDVV*MDi zEUo8GEfua`FH8QG@@Eh~C^TS%5gWe~!)Y2V-9wYOoq_ufark=wTm9~xVpP#PU7Nbe zSiHeMk&OG^W09^bH^X@~wUV~~$vL`dP;qI;5H%JaLm<&-#IxqY8ywUEWYPYU6}ZEN zX3`3W0>T0GBcT>usX(pJe|dRPdw+SOg5@@@xE!M{#qpZQ)1sSI1r4^Lkra4aZb0sYuGH);=%d;0{4$g>6Ds-TqK5yH^ zKSCOPTc6)Hl~UKj;PUnSw{$J5Ri!nQi2o9ngekLsNMQMsFClnW>Na8dj5eE)i`^r` z0o7>3vW~g8Z+6G>CwJA))apWRPOjP}k>t_(r|s8nT27>_EgqSu{6KD(@p(2YDB76K zf_bQvH7QN-QQrqL+;J(?4IZf^QdQ^Y$j@JK1XV_GPL;dH1P>npCk#{Bj(blVIO%r_ zx9&5mh4UzJndCcOe5bHoYJOIoXR?Ib56cU&zx7=m!eX_k^K0%1KpTI?@HuAwy1EB7 z0u9`H7hcnm^yxwM9nf~$s#utM^orT14v8`;`pO|HzP~xgMGh-G__^)ES;ClhkMCrZ zO&W$5!tE_KHpF*Oa#~HS&PyJF(xk(PH3O@Fr^Jnqtxj})5!e-i%Z#kbm=hHYG9Kq1 zEFPca1XTV_?Cz{blN;S!%fy(-A~rgL`kCr|rAY6~SgMj)%Iu9hgnF~w)LNe5o4<+H ziLD-|e9_(V?7mk0_m`#*k?~I-8X45FuO(w80xvS|SNscdCYW7O>QnWecmCRf?cd2|M~F!I{j4h_@{rKVI&*}D`^*^6rF_GR z)Ab(Z)yVlex#{TDxEHTJD_5?E`=4wad$qsaQ(jX(#Dem~(^ieo+H`)9GYKiaYHn@y z^naz?NvXN<*9b?k1TdnWqpwzLp7`-)7y{FR% zZK>lO&YAh0;`#dQD69GNayb0QW?7ttIo3#EsZ0a0f`;a&+_fM$MY3xe@7%zgURCdr zM^VAwY~{bd(>S=_MilTVLnyTVb0G&CUzUsOQ@QeovAcCoIK9^0YC|XQjI>2kECkBh zr)dz4)xbkdp2DNK7vdjyGT3%H3X&=ln-gGgB<6&bxCJ7g0>7$5(NtWL^PFb+FX}w2 z0K?l`rn<2gAFX7DvmM2cK>ba)#c8yf>gxW5U*>$sX1)Op6|YQvb4U3^DXm~!1(Pba>*jAuM~yWa6GojUPH zuK8J5ltRGoefOSZy6y?Bh)AD#!D0=JH7fAQZ_~sj9+Il$LHK5@&2cTL8;bAe**smH z>KYQvbcD~`@SfyU{?%iMr9I4a`*MEA45Idhl(n{su-7SDQ^i5(b?R?*E%h?1B$)+i z=K^dIpTv{&hRm#YIBO4jUr5&H7TGMG8zW@{JXyQHlU+RL@MH=yzUK^Sx45DTFUUx0 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 d9b43c821..fcd4bb73c 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 7105e9f7d..94b8a71be 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 a45704f39..79e6ecf71 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 ce5c457e8..7038544da 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 45abaf9a2..4ce051213 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 504630d79..c10891f26 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 9ffea5008..02e1c3b99 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 3afa61745..3903a3514 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 94b8a71be..8d6fea037 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 8d6fea037..f51f87685 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 e8cc541f4..48ef958f4 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 b07990675..6ba5516d5 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 7038544da..3d54173be 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 c10891f26..77b323ce6 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 000000000..c4c218520 --- /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 8b6764200..35ecb3aa6 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 47dd690bc..f5f723b8d 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 eb8981f38..d07653d2a 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 77b323ce6..76c48ec21 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 02e1c3b99..3e7443703 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 f51f87685..bd0d19a1a 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 326860e5e..56ae873a4 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 ad9c0ada2..cecb38719 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 57ab50c7f..9afc67684 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 79e6ecf71..b2b445bdb 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 05cb7df6e..c4a9c3dbe 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 c4c218520..bc11497fb 100644 --- a/src/particle_track.F90 +++ b/src/particle_track.F90 @@ -5,8 +5,22 @@ module particle_track !use output_interface only: file_create, file_close, write_double1Darray use string, only: to_str +#ifdef HDF5 + use hdf5 +#endif + implicit none +#ifdef HDF5 + integer, private :: hdf5_err ! HDF5 error code + integer(HID_T), private :: track_dset ! dataset handle + integer(HID_T), private :: track_dspace ! master dataspace handle + integer(HID_T), private :: track_selspace ! selected dataspace handle + integer(HID_T), private :: track_fh ! HDF5 file handle + integer(HID_T), private :: cparms ! chunk parameters + integer(HSIZE_T), private :: n_tracks ! total number of tracks +#endif + contains !=============================================================================== @@ -19,13 +33,35 @@ contains subroutine initialize_particle_track() character(MAX_FILE_LEN) :: filename + ! Replace the following with '#ifdef HDF5'. +#if 0 + integer(HSIZE_T) :: dims(2), max_dims(2), i + integer(HID_T) :: cparms + integer(HID_T) :: dapl +#endif filename = trim(path_output) // 'track_' // trim(to_str(current_batch)) & - // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) & - // '.binary' - open(UNIT=UNIT_TRACK, FILE=filename, STATUS='replace', & - ACCESS='stream') - !file_create(filename, 'serial') + // '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) + + ! Replace the following with '#ifdef HDF5'. +#if 0 + filename = filename // '.h5' + call h5fcreate_f(filename, H5F_ACC_TRUNC_F, track_fh, hdf5_err) + dims = (/3, 1/) + i = 3 + max_dims = (/i, H5S_UNLIMITED_F/) + call h5screate_simple_f(2, dims, track_dspace, hdf5_err, max_dims) + call h5pcreate_f(H5P_DATASET_CREATE_F, cparms, hdf5_err) + call h5pset_chunk_f(cparms, 2, dims, hdf5_err) + call h5pset_fill_value_f(cparms, H5T_NATIVE_DOUBLE, 0, hdf5_err) + call h5dcreate_f(track_fh, 'coordinates', H5T_NATIVE_DOUBLE, track_dspace, & + track_dset, hdf5_err, cparms) + n_tracks = 1 +#else + filename = filename // '.binary' + open(UNIT=UNIT_TRACK, FILE=filename, ACTION="write", & + STATUS='replace', ACCESS='stream') +#endif end subroutine initialize_particle_track @@ -38,8 +74,40 @@ contains !=============================================================================== subroutine write_particle_track() + ! Replace the following with '#ifdef HDF5'. +#if 0 + integer(HSIZE_T) :: dims(2), max_dims(2), offset(2), i + integer(HSIZE_T), parameter :: cnt(2) = (/3, 1/), write_dims(2) = (/3, 1/) + real(8) :: coords(3, 1) + integer(HSIZE_T) :: test + + i = 3 + dims = (/i, n_tracks/) + call h5dget_space_f(track_dset, track_dspace, hdf5_err) + call h5dset_extent_f(track_dset, dims, hdf5_err) + call h5dget_space_f(track_dset, track_dspace, hdf5_err) + i = 1 + offset = (/i, n_tracks/) + call h5sselect_hyperslab_f(track_dspace, H5S_SELECT_SET_F, offset, cnt, & + hdf5_err) + !call h5sget_select_npoints_f(track_dspace, test, hdf5_err) + print *, test + coords(:, 1) = p % coord0 % xyz + print *, coords + if (n_tracks<20) then + call h5dwrite_f(track_dset, H5T_NATIVE_DOUBLE, coords, & + write_dims, hdf5_err, file_space_id=track_dspace) + end if + !call h5sselect_hyperslab_f(track_dspace, H5S_SELECT_SET_F, offset, dims, & + ! hdf5_err) + !i = 3 + n_tracks = n_tracks + 1 + !dims = (/i, n_tracks/) + !call h5sset_extent_simple_f(track_dspace, 2, dims, max_dims, hdf5_err) + !call h5dset_extent_f(track_dset, dims, hdf5_err) +#else write(UNIT_TRACK) p % coord0 % xyz - !write_double1Darray(p % coord0 % xyz, 'coordinates', 3) +#endif end subroutine write_particle_track !=============================================================================== @@ -49,8 +117,22 @@ contains !=============================================================================== subroutine finalize_particle_track() + ! Replace the following with '#ifdef HDF5'. +#if 0 + integer(HSIZE_T) :: dims(2), max_dims(2), i + + call h5sget_simple_extent_dims_f(track_dspace, dims, max_dims, hdf5_err) + !i = 3 + !dims = (/i, dims(2)-1/) + !call h5dextend_f(track_dset, dims, hdf5_err) + + call h5dclose_f(track_dset, hdf5_err) + call h5sclose_f(track_dspace, hdf5_err) + call h5pclose_f(cparms, hdf5_err) + call h5fclose_f(track_fh, hdf5_err) +#else close(UNIT=UNIT_TRACK) - !file_close('serial') +#endif end subroutine finalize_particle_track end module particle_track From 52233d0173494368ebe87210e748cf5403a382c3 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 8 Jul 2013 15:10:50 -0400 Subject: [PATCH 015/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 bd0d19a1a..c12041214 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 ea81f9ce9..064acd707 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 c4a9c3dbe..05cb7df6e 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 bc11497fb..852a07e10 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 1907d1ec0..f0b1f23e5 100755 --- a/src/utils/track.py +++ b/src/utils/track.py @@ -5,7 +5,7 @@ Usage information can be obtained by running 'track.py --help': usage: track.py [-h] [-o OUT] IN [IN ...] - Convert binary particle track file to a .pvtp file. + Convert particle track file to a .pvtp file. positional arguments: IN Input particle track data filename(s). @@ -26,9 +26,8 @@ import vtk def _parse_args(): # Create argument parser. parser = argparse.ArgumentParser( - description='Convert binary particle track file to a .pvtp file.') - parser.add_argument('input', metavar='IN', type=argparse.FileType('rb'), - nargs='+', + description='Convert particle track file to a .pvtp file.') + parser.add_argument('input', metavar='IN', type=str, nargs='+', help='Input particle track data filename(s).') parser.add_argument('-o', '-out', '--out', metavar='OUT', type=str, dest='out', @@ -39,8 +38,15 @@ def _parse_args(): def main(): - # Parse commandline argumetns. + # Parse commandline arguments. args = _parse_args() + + # Check input file extensions. + for fname in args.input: + if len(fname) > 3 and fname[-3:] == '.h5': continue + if len(fname) > 7 and fname[-7:] == '.binary': continue + raise ValueError("Input file names must either end with '.h5' or " + "'.binary'.") # Make sure that the output filename ends with '.pvtp'. if not args.out: @@ -48,19 +54,28 @@ def main(): elif os.path.splitext(args.out)[1] != '.pvtp': args.out = ''.join([args.out, '.pvtp']) + # Import HDF library if HDF files are present + for fname in args.input: + if fname[-3:] != '.h5': continue + import h5py + break + points = vtk.vtkPoints() cells = vtk.vtkCellArray() j = 0 k = 0 - for fin in args.input: - track = fin.read() - coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) - for i in range(len(track)/24)] - n_points = len(coords) - - for triplet in coords: - points.InsertNextPoint(triplet) - + for fname in args.input: + if len(fname) > 7 and fname[-7:] == '.binary': + track = open(fname, 'rb').read() + coords = [struct.unpack("ddd", track[24*i : 24*(i+1)]) + for i in range(len(track)/24)] + n_points = len(coords) + for triplet in coords: points.InsertNextPoint(triplet) + elif fname[-3:] == '.h5': + coords = h5py.File(fname).get('coordinates') + n_points = coords.shape[0] + for i in range(n_points): points.InsertNextPoint(coords[i,:]) + # Create VTK line and assign points to line. line = vtk.vtkPolyLine() line.GetPointIds().SetNumberOfIds(n_points) From 06c3bd3690140c4a7778f01df410c748e69f0619 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 9 Jul 2013 11:04:04 -0400 Subject: [PATCH 016/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 852a07e10..c0d7894ce 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 38f52a5db9b5c750cab77f5ac568aa471f90ea05 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 10:42:42 -0400 Subject: [PATCH 017/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 05cb7df6e..9f0cf555d 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 000000000..53df0326a --- /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 000000000..5707d4524 --- /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 000000000..eaa7e684f --- /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 000000000..33a2cdb48 --- /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 000000000..0bf518362 --- /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 000000000..d46a95ec2 --- /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 000000000..1b5df709e --- /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 000000000..5eecbbfdc --- /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 000000000..ccfca15dc --- /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 000000000..071455b29 --- /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 000000000..565700710 --- /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 000000000..0a1f1c1a8 --- /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 000000000..a15a25dbc --- /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 000000000..a796732e7 --- /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 000000000..e3bdff60c --- /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 000000000..ee404da35 --- /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 000000000..ec92b5291 --- /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 000000000..063c288a2 --- /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 000000000..e6d8cafc6 --- /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 000000000..6725f5243 --- /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 000000000..c6fdae418 --- /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 000000000..f301622d5 --- /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 000000000..72e683732 --- /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 000000000..f0e443f21 --- /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 000000000..98286bc78 --- /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 000000000..612523bb9 --- /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 000000000..b49554832 --- /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 000000000..b158d26fa --- /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 000000000..448f44ebd --- /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 000000000..e0307e99c --- /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 000000000..ecf407483 --- /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 000000000..afec6f080 --- /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 000000000..d6b81cc87 --- /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 000000000..17dc2280e --- /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 000000000..531618ad2 --- /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 000000000..6d63fd046 --- /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 000000000..4693ff7f4 --- /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 000000000..97824fd7a --- /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 000000000..b0148a693 --- /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 000000000..21c238e6a --- /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 000000000..a440a53c8 --- /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 000000000..560f91e59 --- /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 000000000..90a766d2b --- /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 000000000..be6fefcdd --- /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 000000000..fd9c272d9 --- /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 000000000..dca350436 --- /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 000000000..41c1e5652 --- /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 000000000..2afd30b06 --- /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 000000000..99bfcdab0 --- /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 000000000..5f6281dcb --- /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 000000000..7cc8ee288 --- /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 018/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 9f0cf555d..8d3646973 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 000000000..744da3147 --- /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 000000000..a4874b3b5 --- /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 e6d8cafc6..6e1ac36a7 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 000000000..1f597ddbc --- /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 000000000..6e80265b0 --- /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 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 S(ytf9uq4 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 Dq*C7N literal 0 HcmV?d00001 diff --git a/src/xml/sax/Makefile b/src/xml/sax/Makefile new file mode 100644 index 000000000..c0a134055 --- /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 000000000..449bd4d91 --- /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 000000000..55dcc3d9c --- /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 019/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 063c288a2..000000000 --- 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 98286bc78..000000000 --- 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 d6b81cc87..000000000 --- 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 a440a53c8..000000000 --- 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 dca350436..000000000 --- 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 7cc8ee288..000000000 --- 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 020/192] added static library extension --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8aa7c1f97..4ecff16b3 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 021/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 b07990675..5526dac4b 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 000000000..6eb5e0a7e --- /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 022/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 744da3147..f9151178c 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 023/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 f301622d5..790561746 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 e0307e99c..45551c1d6 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 024/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 44a5c5cca..3aa22c607 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 025/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 e5d20644b..39ddf39e9 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 617788e47..de0918daa 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 6eb5e0a7e..3d6924a8b 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 026/192] fixed Makefile after merge --- src/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index e76aa1788..8d3646973 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 027/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 de0918daa..d2ccf8a7d 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 3d6924a8b..44df324ce 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 028/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 d2ccf8a7d..530447162 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 029/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 44df324ce..cd948b64e 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 030/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 530447162..760383d57 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 031/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 760383d57..338b78460 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 032/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 338b78460..f486a0679 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 033/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 f486a0679..c673f3fa7 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 034/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 c673f3fa7..39765c5fb 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 035/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 39765c5fb..52ef5391e 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 cd948b64e..d1fc88a1c 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 036/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 52ef5391e..7b2bce19e 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 037/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 7b2bce19e..24ec6c2c6 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 038/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 c228890d1..df692c46e 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 039/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 39ddf39e9..c5d01441d 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 8d3646973..282922678 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 df692c46e..26a7a3ae0 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 232ae9609..000000000 --- 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 cbcb7709a..000000000 --- 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 c9ae3c1fd..000000000 --- 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 e82bb986f..000000000 --- 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 0852b8504..000000000 --- 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 b7975d05f..000000000 --- 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 539a23f6b..000000000 --- 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 d497fa745..000000000 --- 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 99950e035..000000000 --- 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 3c29ba4e0..000000000 --- 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 b3b86fc44..000000000 --- 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 47dd690bc..000000000 --- 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 3afa61745..000000000 --- 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 06959276b..000000000 --- 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 cf653968d..000000000 --- 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 3aa22c607..fb966d028 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 c0514533a..000000000 --- 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 0cb6ee959..000000000 --- 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 a88086e74..000000000 --- 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 1fb63d57b..000000000 --- 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 b28ba3c7a..000000000 --- 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 4c1848dcf..000000000 --- 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 7d2cf90ba..000000000 --- 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 cd0ba2912..000000000 --- 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 eb522078a..000000000 --- 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 390324b63..000000000 --- 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 040/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 24ec6c2c6..ddde113bc 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 d1fc88a1c..b0c47aa08 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 e5bdb8bef9ac0d2ad8c7cd1eb5b412693b0de5f4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 29 Jul 2013 15:56:24 -0400 Subject: [PATCH 041/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 ddde113bc..2bf1abe1b 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 b0c47aa08..76c174f01 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 042/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 76c174f01..4752c0873 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 043/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 d9b43c821..947534990 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 dedba81ead49f1c80d60cb566deec2793f9aa1b2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 15:06:52 -0400 Subject: [PATCH 044/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 2bf1abe1b..a610015c3 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 045/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 a610015c3..5bf877e25 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 046/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 5bf877e25..a6d6538db 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 047/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 a6d6538db..0dd4fb3bc 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 048/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 0dd4fb3bc..8c3df4f58 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 049/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 8c3df4f58..f1363d85a 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 050/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 f1363d85a..b27cb2aeb 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 051/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 b27cb2aeb..ece67151c 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 b03786d5363c6b8442b56e8d067d1f9440225bef Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Aug 2013 23:39:07 -0500 Subject: [PATCH 052/192] Simple implementation of random number streams. Setup separate streams for tracking and tallies. --- src/random_lcg.F90 | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/random_lcg.F90 b/src/random_lcg.F90 index 3130d42a5..12b85c2c7 100644 --- a/src/random_lcg.F90 +++ b/src/random_lcg.F90 @@ -5,8 +5,13 @@ module random_lcg private save + ! Random number streams + integer, parameter :: N_STREAMS = 2 + integer, parameter :: STREAM_TRACKING = 1 + integer, parameter :: STREAM_TALLIES = 2 + integer(8) :: prn_seed0 ! original seed - integer(8) :: prn_seed ! current seed + integer(8) :: prn_seed(N_STREAMS) ! current seed integer(8) :: prn_mult ! multiplication factor, g integer(8) :: prn_add ! additive factor, c integer :: prn_bits ! number of bits, M @@ -14,11 +19,14 @@ module random_lcg integer(8) :: prn_mask ! 2^M - 1 integer(8) :: prn_stride ! stride between particles real(8) :: prn_norm ! 2^(-M) + integer :: stream ! current RNG stream public :: prn public :: initialize_prng public :: set_particle_seed public :: prn_skip + public :: prn_set_stream + public :: STREAM_TRACKING, STREAM_TALLIES contains @@ -33,12 +41,12 @@ contains ! This algorithm uses bit-masking to find the next integer(8) value to be ! used to calculate the random number - prn_seed = iand(prn_mult*prn_seed + prn_add, prn_mask) + prn_seed(stream) = iand(prn_mult*prn_seed(stream) + prn_add, prn_mask) ! Once the integer is calculated, we just need to divide by 2**m, ! represented here as multiplying by a pre-calculated factor - pseudo_rn = prn_seed * prn_norm + pseudo_rn = prn_seed(stream) * prn_norm end function prn @@ -51,8 +59,13 @@ contains use global, only: seed + integer :: i + + stream = STREAM_TRACKING prn_seed0 = seed - prn_seed = prn_seed + do i = 1, N_STREAMS + prn_seed(i) = prn_seed0 + i - 1 + end do prn_mult = 2806196910506780709_8 prn_add = 1_8 prn_bits = 63 @@ -72,7 +85,11 @@ contains integer(8), intent(in) :: id - prn_seed = prn_skip_ahead(id*prn_stride, prn_seed0) + integer :: i + + do i = 1, N_STREAMS + prn_seed(i) = prn_skip_ahead(id*prn_stride, prn_seed0 + i - 1) + end do end subroutine set_particle_seed @@ -84,7 +101,7 @@ contains integer(8), intent(in) :: n ! number of seeds to skip - prn_seed = prn_skip_ahead(n, prn_seed) + prn_seed(stream) = prn_skip_ahead(n, prn_seed(stream)) end subroutine prn_skip @@ -149,4 +166,18 @@ contains end function prn_skip_ahead +!=============================================================================== +! PRN_SET_STREAM changes the random number stream. If random numbers are needed +! in routines not used directly for tracking (e.g. physics), this allows the +! numbers to be generated without affecting reproducibility of the physics. +!=============================================================================== + + subroutine prn_set_stream(i) + + integer, intent(in) :: i + + stream = i + + end subroutine prn_set_stream + end module random_lcg From 3af02080023e27b6e36076fc0bf0e920b13298a2 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Aug 2013 00:31:25 -0500 Subject: [PATCH 053/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 70116af72..fef5e2fcf 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 054/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 982979001..465b9b398 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 000000000..cbcb7709a --- /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 000000000..e82bb986f --- /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 000000000..b7975d05f --- /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 000000000..d497fa745 --- /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 000000000..3c29ba4e0 --- /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 000000000..fd39c5cb5 --- /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 000000000..06959276b --- /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 dd1fa2946098935393f50670fd42ce681a5f344b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 23 Aug 2013 12:34:35 -0400 Subject: [PATCH 055/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 ece67151c..d77a0e9a2 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 056/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 d77a0e9a2..51ada879e 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 057/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 51ada879e..4267a9727 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 8723ed61e3ae9c9f3671b273802b9d70f837ae9f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 29 Aug 2013 14:42:37 -0400 Subject: [PATCH 058/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 26a7a3ae0..3a3a2ff4b 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 059/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 4267a9727..06c12fad3 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 167226fd88f8becd9099e34e3c702a2b8b6414ff Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 16 Sep 2013 15:57:18 -0400 Subject: [PATCH 060/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 6725f5243..01f6265b0 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 f0e443f21..1234c48ad 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 612523bb9..29af292fc 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 b49554832..88d565256 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 448f44ebd..6d6a6d933 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 000000000..52efa5fde --- /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 6d63fd046..c2f2283fb 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 4693ff7f4..72fa6584e 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 97824fd7a..59ea631d3 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 b0148a693..eb123c781 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 be6fefcdd..a3f1d70ff 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 41c1e5652..cf4def0a7 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 2afd30b06..7e905c2ab 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 061/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 933dabb93..0c23afa91 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 ef7625b3b..46ae8e9c1 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 b15fabfb1..f803d4c2d 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 201de518e..b9762960e 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 5bc392b0f..4053dd1d7 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 25e1553a8..bbf86d4ad 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 2c535e290..4a22e8cca 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -67,14 +67,14 @@ contains micro_xs % last_E = ZERO ! Prepare to write out particle track. - if (write_track) then + if (p % write_track) then call initialize_particle_track() endif do while (p % alive) ! Write particle track. - if (write_track) call write_particle_track(p) + if (p % write_track) call write_particle_track(p) if (check_overlaps) call check_cell_overlap(p) @@ -200,7 +200,7 @@ contains end do ! Finish particle track output. - if (write_track) then + if (p % write_track) then call write_particle_track(p) call finalize_particle_track(p) endif From ca0ace41cbc6b25aff6f1b05545d7853f4743067 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 21 Sep 2013 17:32:53 -0400 Subject: [PATCH 062/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 0c23afa91..2ece570ae 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 4053dd1d7..5681a4de4 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 4a22e8cca..e8d3f7b1a 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 063/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 c37a546c0..209f5daa3 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 064/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 d2bb688c9..e958cc62a 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 ed7b845ec..deeae3c01 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 065/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 6d6a6d933..448f44ebd 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 be6a8d22ecb1c37151c86b380b84318848b7c15d Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 2 Oct 2013 21:21:05 -0400 Subject: [PATCH 066/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 f0b1f23e5..5fe20ce53 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 067/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 5681a4de4..4b02dfe9a 100644 --- a/src/track_output.F90 +++ b/src/track_output.F90 @@ -33,18 +33,13 @@ contains subroutine write_particle_track(p) type(Particle), intent(in) :: p - real(8) :: old_coords(3, n_tracks) - - ! Save the coordinates gathered in previous calls. - old_coords = coords + real(8), allocatable :: new_coords(:, :) ! Add another column to coords. n_tracks = n_tracks + 1 - deallocate(coords) - allocate(coords(3, n_tracks)) - - ! Put the old coordinates back into the array. - coords(:, 1:n_tracks-1) = old_coords + allocate(new_coords(3, n_tracks)) + new_coords(:, 1:n_tracks-1) = coords + call move_alloc(FROM=new_coords, TO=coords) ! Write current coordinates into the newest column. coords(:, n_tracks) = p % coord0 % xyz From bfc172f26485487bd780a37d233d0f4961df4bca Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 10 Oct 2013 00:02:26 -0400 Subject: [PATCH 068/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 5fe20ce53..a2dca34e1 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 000000000..f3566ab17 --- /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 000000000..7befbddf5 --- /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 000000000..420bc5949 --- /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 000000000..d652d97e3 --- /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 000000000..6ded87a0e --- /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 069/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 d652d97e3..4600c8931 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 070/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 159c689e9..527ec6b30 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 071/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 c3e706b7e..2082ad6dd 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 347ca24ee522ab8a9e5c07d85765ac273b7889aa Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 18 Oct 2013 17:27:03 -0400 Subject: [PATCH 072/192] added more options for writing source bank at end of batches --- src/eigenvalue.F90 | 8 +++- src/global.F90 | 5 +++ src/input_xml.F90 | 55 ++++++++++++++++++++++---- src/state_point.F90 | 77 ++++++++++++++++++++++++++++-------- src/templates/settings_t.xml | 9 ++++- 5 files changed, 128 insertions(+), 26 deletions(-) diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index f5375bf3a..62ce1c47b 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -17,7 +17,7 @@ module eigenvalue use random_lcg, only: prn, set_particle_seed, prn_skip use search, only: binary_search use source, only: get_source_particle - use state_point, only: write_state_point + use state_point, only: write_state_point, write_source_point use string, only: to_str use tally, only: synchronize_tallies, setup_active_usertallies, & reset_result @@ -222,6 +222,12 @@ contains call write_state_point() end if + ! Write out source point if it's been specified for this batch + if ((sourcepoint_batch % contains(current_batch) .or. source_latest) .and. & + source_write) then + call write_source_point() + end if + if (master .and. current_batch == n_batches) then ! Make sure combined estimate of k-effective is calculated at the last ! batch in case no state point is written diff --git a/src/global.F90 b/src/global.F90 index f12171b25..aba56232b 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -189,6 +189,7 @@ module global ! Write source at end of simulation logical :: source_separate = .false. logical :: source_write = .true. + logical :: source_latest = .false. ! ============================================================================ ! PARALLEL PROCESSING VARIABLES @@ -359,6 +360,10 @@ module global integer :: n_state_points = 0 type(SetInt) :: statepoint_batch + ! Information about source points to be written + integer :: n_source_points = 0 + type(SetInt) :: sourcepoint_batch + ! Various output options logical :: output_summary = .false. logical :: output_xs = .false. diff --git a/src/input_xml.F90 b/src/input_xml.F90 index f50a4557c..3a8a1fac6 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -530,14 +530,6 @@ contains n_state_points = 1 call statepoint_batch % add(n_batches) 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. else ! If no tag was present, by default write state point at ! last batch only @@ -545,6 +537,53 @@ contains call statepoint_batch % add(n_batches) end if + ! Check if the user has specified to write state points + if (size(source_point_) > 0) then + ! Determine number of batches at which to store state points + if (associated(state_point_(1) % batches)) then + n_source_points = size(source_point_(1) % batches) + else + n_source_points = 0 + end if + + if (n_source_points > 0) then + ! User gave specific batches to write state points + do i = 1, n_source_points + call sourcepoint_batch % add(source_point_(1) % batches(i)) + end do + + elseif (source_point_(1) % interval /= 0) then + ! User gave an interval for writing state points + n_source_points = n_batches / source_point_(1) % interval + do i = 1, n_source_points + call sourcepoint_batch % add(source_point_(1) % interval * i) + end do + else + ! If neither were specified, write state point at last batch + n_source_points = 1 + call sourcepoint_batch % add(n_batches) + end if + + ! Check if the user has specified to write binary source file + call lower_case(source_point_(1) % source_separate) + call lower_case(source_point_(1) % source_write) + call lower_case(source_point_(1) % overwrite_latest) + if (source_point_(1) % source_separate == 'true' .or. & + source_point_(1) % source_separate == '1') source_separate = .true. + if (source_point_(1) % source_write == 'false' .or. & + source_point_(1) % source_write == '0') source_write = .false. + if (source_point_(1) % overwrite_latest == 'true' .or. & + source_point_(1) % overwrite_latest == '1') source_latest = .true. + else + ! If no tag was present, by default we keep source bank in + ! statepoint file and write it out at statepoints intervals + source_separate = .false. + n_source_points = n_state_points + do i = 1, n_state_points + call sourcepoint_batch % add(statepoint_batch % get_item(i)) + end do + end if + ! Check if the user has specified to not reduce tallies at the end of every ! batch call lower_case(no_reduce_) diff --git a/src/state_point.F90 b/src/state_point.F90 index 9ad9b8d9f..ae546cc10 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -280,14 +280,42 @@ contains end if - ! Check for eigenvalue calculation - if (run_mode == MODE_EIGENVALUE .and. source_write) then + end subroutine write_state_point - ! Check for writing source out separately +!=============================================================================== +! WRITE_SOURCE_POINT +!=============================================================================== + + subroutine write_source_point() + + type(BinaryOutput) :: sp + character(MAX_FILE_LEN) :: filename + + ! Check to write out source for a specified batch + if (sourcepoint_batch % contains(current_batch)) then + + ! Create or open up file if (source_separate) then - ! Set filename for source - filename = trim(path_output) // 'source.' // & + ! Set filename + filename = trim(path_output) // 'source.' // trim(to_str(current_batch)) +#ifdef HDF5 + filename = trim(filename) // '.h5' +#else + filename = trim(filename) // '.binary' +#endif + + ! Write message for new file creation + message = "Creating source file " // trim(filename) // "..." + call write_message() + + ! Create separate source file + call sp % file_create(filename, serial = .false.) + + else + + ! Set filename for state point + filename = trim(path_output) // 'statepoint.' // & trim(to_str(current_batch)) #ifdef HDF5 filename = trim(filename) // '.h5' @@ -295,16 +323,7 @@ contains filename = trim(filename) // '.binary' #endif - ! Write message - message = "Creating source file " // trim(filename) // "..." - call write_message(1) - - ! Create source file - call sp % file_create(filename, serial = .false.) - - else - - ! Reopen state point file in parallel + ! Reopen statepoint file in parallel call sp % file_open(filename, 'w', serial = .false.) end if @@ -317,7 +336,33 @@ contains end if - end subroutine write_state_point + ! Also check to write source separately in overwritten file + if (source_latest) then + + ! Set filename + filename = trim(path_output) // 'source' +#ifdef HDF5 + filename = trim(filename) // '.h5' +#else + filename = trim(filename) // '.binary' +#endif + + ! Write message for new file creation + message = "Creating source file " // trim(filename) // "..." + call write_message() + + ! Always create this file because it will be overwritten + call sp % file_create(filename, serial = .false.) + + ! Write out source + call sp % write_source_bank() + + ! Close file + call sp % file_close() + + end if + + end subroutine write_source_point !=============================================================================== ! WRITE_TALLY_RESULTS_NR diff --git a/src/templates/settings_t.xml b/src/templates/settings_t.xml index 8f1cc7b00..60b959c67 100644 --- a/src/templates/settings_t.xml +++ b/src/templates/settings_t.xml @@ -38,9 +38,15 @@ + + + + + - + + @@ -57,6 +63,7 @@ + From eae7e9322402f33cc847eb8c63ba7ecb5d23df54 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 18 Oct 2013 17:30:33 -0400 Subject: [PATCH 073/192] default source point behavior is set to same batches as statepoint --- 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 3a8a1fac6..41dac3f06 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -560,8 +560,10 @@ contains end do else ! If neither were specified, write state point at last batch - n_source_points = 1 - call sourcepoint_batch % add(n_batches) + n_source_points = n_state_points + do i = 1, n_state_points + call sourcepoint_batch % add(statepoint_batch % get_item(i)) + end do end if ! Check if the user has specified to write binary source file From 21338d3de4d67fdef71ff6da7c846590309a4b6f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 18 Oct 2013 17:37:07 -0400 Subject: [PATCH 074/192] updated settings schema --- src/templates/settings.rnc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/templates/settings.rnc b/src/templates/settings.rnc index cd4ed4beb..fda258385 100644 --- a/src/templates/settings.rnc +++ b/src/templates/settings.rnc @@ -83,6 +83,15 @@ element settings { }? & element state_point { + ( + (element batches { list { xsd:positiveInteger+ } } | + attribute batches { list { xsd:positiveInteger+ } }) | + (element interval { xsd:positiveInteger } | + attribute interval { xsd:positiveInteger }) + ) + }? & + + element source_point { ( (element batches { list { xsd:positiveInteger+ } } | attribute batches { list { xsd:positiveInteger+ } }) | @@ -92,7 +101,9 @@ element settings { (element source_separate { xsd:boolean } | attribute source_separate { xsd:boolean })? & (element source_write { xsd:boolean } | - attribute source_write { xsd:boolean })? + attribute source_write { xsd:boolean })? & + (element overwrite_latest { xsd:boolean} | + attribute overwrite_latest {xsd:boolean})? }? & element survival_biasing { xsd:boolean }? & From 8a9c76e4aca5b5323ae4d2b4795b5c810cb2d061 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 18 Oct 2013 17:42:49 -0400 Subject: [PATCH 075/192] added new filetype and wrote this in source file --- src/constants.F90 | 3 ++- src/state_point.F90 | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/constants.F90 b/src/constants.F90 index 558772286..d72d85523 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -17,7 +17,8 @@ module constants ! Binary file types integer, parameter :: & FILETYPE_STATEPOINT = -1, & - FILETYPE_PARTICLE_RESTART = -2 + FILETYPE_PARTICLE_RESTART = -2, & + FILETYPE_SOURCE = -3 ! ============================================================================ ! ADJUSTABLE PARAMETERS diff --git a/src/state_point.F90 b/src/state_point.F90 index ae546cc10..a6c21704d 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -312,6 +312,9 @@ contains ! Create separate source file call sp % file_create(filename, serial = .false.) + ! Write file type + call sp % write_data(FILETYPE_SOURCE, "filetype") + else ! Set filename for state point @@ -354,6 +357,9 @@ contains ! Always create this file because it will be overwritten call sp % file_create(filename, serial = .false.) + ! Write file type + call sp % write_data(FILETYPE_SOURCE, "filetype") + ! Write out source call sp % write_source_bank() From 154c8d632fdf77bb561ffa9a3875be477514a1d4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 18 Oct 2013 17:56:29 -0400 Subject: [PATCH 076/192] a specific source file can now be specified on command line --- src/global.F90 | 1 + src/initialize.F90 | 36 ++++++++++++++++++++++++++++++++++++ src/state_point.F90 | 4 ++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/global.F90 b/src/global.F90 index aba56232b..f5ce1dbcc 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -261,6 +261,7 @@ module global character(MAX_FILE_LEN) :: path_cross_sections ! Path to cross_sections.xml character(MAX_FILE_LEN) :: path_source = '' ! Path to binary source character(MAX_FILE_LEN) :: path_state_point ! Path to binary state point + character(MAX_FILE_LEN) :: path_source_point ! Path to binary source point character(MAX_FILE_LEN) :: path_particle_restart ! Path to particle restart character(MAX_FILE_LEN) :: path_output = '' ! Path to output directory diff --git a/src/initialize.F90 b/src/initialize.F90 index dea901fb6..09e446e38 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -363,8 +363,44 @@ contains case (FILETYPE_PARTICLE_RESTART) path_particle_restart = argv(i) particle_restart_run = .true. + case default + message = "Unrecognized file after restart flag." + call fatal_error() end select + ! If its a restart run check for additional source file + if (restart_run) then + + ! Increment arg + i = i + 1 + + ! Check if it has extension we can read + if ((ends_with(argv(i), '.binary') .or. & + ends_with(argv(i), '.h5'))) then + + ! Check file type is a source file + call sp % file_open(argv(i), 'r', serial = .false.) + call sp % read_data(filetype, 'filetype') + call sp % file_close() + if (filetype /= FILETYPE_SOURCE) then + message = "Second file after restart flag must be a source file" + call fatal_error() + end if + + ! It is a source file + path_source_point = argv(i) + + else + + ! Source is in statepoint file + path_source_point = path_state_point + + ! Set argument back + i = i - 1 + + end if + end if + case ('-g', '-geometry-debug', '--geometry-debug') check_overlaps = .true. diff --git a/src/state_point.F90 b/src/state_point.F90 index a6c21704d..836f96c00 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -793,9 +793,9 @@ contains else #ifdef MPI - ! Reopen statepoint file in parallel, but only if MPI + ! Open file determined form initialization 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.) + call sp % file_open(path_source_point, 'r', serial = .false.) #endif end if From 728c906cc4a1d59bd68714ba50f43fdd0648c1a7 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 21 Oct 2013 17:18:50 -0400 Subject: [PATCH 077/192] fixed some restarting and printing issues --- src/initialize.F90 | 2 +- src/state_point.F90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/initialize.F90 b/src/initialize.F90 index 09e446e38..6b2023a9b 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -369,7 +369,7 @@ contains end select ! If its a restart run check for additional source file - if (restart_run) then + if (restart_run .and. i + 1 <= argc) then ! Increment arg i = i + 1 diff --git a/src/state_point.F90 b/src/state_point.F90 index cf3c052b9..74d0f6054 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -307,7 +307,7 @@ contains ! Write message for new file creation message = "Creating source file " // trim(filename) // "..." - call write_message() + call write_message(1) ! Create separate source file call sp % file_create(filename, serial = .false.) @@ -352,7 +352,7 @@ contains ! Write message for new file creation message = "Creating source file " // trim(filename) // "..." - call write_message() + call write_message(1) ! Always create this file because it will be overwritten call sp % file_create(filename, serial = .false.) From ed3de400e6b5e07f5083f840ccd2e909df278c7b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 21 Oct 2013 17:21:22 -0400 Subject: [PATCH 078/192] change where source separate is specified in tests --- tests/test_statepoint_sourcesep/settings.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_statepoint_sourcesep/settings.xml b/tests/test_statepoint_sourcesep/settings.xml index 4f5b7691f..fba449e2d 100644 --- a/tests/test_statepoint_sourcesep/settings.xml +++ b/tests/test_statepoint_sourcesep/settings.xml @@ -1,10 +1,11 @@ - + + - 10 + 15 5 1000 From ba9d5753dbe9175e19aa12ca4431c0e512d3a184 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 22 Oct 2013 09:21:43 -0400 Subject: [PATCH 079/192] put source sep test back to 10 batches --- tests/test_statepoint_sourcesep/settings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_statepoint_sourcesep/settings.xml b/tests/test_statepoint_sourcesep/settings.xml index fba449e2d..98abc5acf 100644 --- a/tests/test_statepoint_sourcesep/settings.xml +++ b/tests/test_statepoint_sourcesep/settings.xml @@ -5,7 +5,7 @@ - 15 + 10 5 1000 From b4122c7319d22cbabe5ffd4796eb5e216c83a98c Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 22 Oct 2013 09:22:08 -0400 Subject: [PATCH 080/192] when opening separate source file, read filetype first --- src/state_point.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/state_point.F90 b/src/state_point.F90 index 74d0f6054..306fb8f37 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -783,6 +783,9 @@ contains ! Open source file call sp % file_open(filename, 'r', serial = .false.) + ! Read file type + call sp % read_data(int_array(1), "filetype") + 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 081/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 d075c2288..5e2f39a00 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 4c9ffb8c8..299033590 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 96f2035254eb006a99cd3f25f4c8fe4dfe1e3f47 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 6 Nov 2013 12:41:14 +0100 Subject: [PATCH 082/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 209f5daa3..d54aa83c2 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 083/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 64e97ce21..cac8d5908 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 084/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 c12041214..780fe3793 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 085/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 7befbddf5..bbf880d96 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 086/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 4600c8931..0e7674154 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 087/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 420bc5949..ef74341db 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 740b0072cb23651f0a65fb38251fe017963ca883 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 14 Nov 2013 19:11:31 -0500 Subject: [PATCH 088/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 4b02dfe9a..8b9c17b89 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 089/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 b9762960e..e1c696ff1 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 b83cdf207d235c95c4430e83f79a0751aa2a55e5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 14 Nov 2013 19:54:26 -0500 Subject: [PATCH 090/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 14b49292e..9bedbc8b3 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 8b9c17b89..021cbb8ca 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 091/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 d757b9c3c..a7100fc18 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 092/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 c9c7aee38..6d93f9aec 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 093/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 a7100fc18..c17e652c7 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 094/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 780fe3793..043041ef4 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 095/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 1eb7436d0..1ceba324c 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 456cd662f..000000000 --- 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 000000000..8310d53e4 --- /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 096/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 49cf41c8d..a13272aa6 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 097/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 ac3d59824..120db5f64 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 098/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 b4fe997bb..579bf26fb 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 2082ad6dd..3d4de842a 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 099/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 3d4de842a..57870206d 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 100/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 e9999c0bd..564e0baed 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 57870206d..4fcdd0074 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 9058b7b490524e0164fdad880fbc9938753006d9 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Mon, 9 Dec 2013 12:24:20 -0500 Subject: [PATCH 101/192] Fixed non-(current)standard usage of L(I) for Laws 4, 44, 61, 67. Error will be thrown. --- src/ace.F90 | 83 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/src/ace.F90 b/src/ace.F90 index d54aa83c2..4673898c0 100644 --- a/src/ace.F90 +++ b/src/ace.F90 @@ -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 @@ -406,7 +406,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 +809,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 +840,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 @@ -934,6 +934,7 @@ contains integer :: NEa ! number of energies for Watt 'a' integer :: NRb ! number of interpolation regions for Watt 'b' integer :: NEb ! number of energies for Watt 'b' + real(8), allocatable :: L(:) ! locations of distributions for each Ein ! initialize length length = 0 @@ -958,6 +959,22 @@ contains ! Continuous tabular distribution NR = int(XSS(lc + 1)) NE = int(XSS(lc + 2 + 2*NR)) + ! Before progressing, check to see if data set uses L(I) values + ! in a way inconsistent with the current form of the ACE Format Guide + ! (MCNP5 Manual, Vol 3) + allocate(L(NE)) + L = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1)) + do i = 1,NE + ! Now check to see if L(i) is equal to any other entries + ! If so, then we must exit + if (count(L == L(i)) > 1) then + message = "Invalid usage of L(I) in ACE data; & + &Consider using more recent data set." + call fatal_error() + end if + end do + deallocate(L) + ! Continue with finding data length length = length + 2 + 2*NR + 2*NE do i = 1,NE ! determine length @@ -1000,6 +1017,22 @@ contains ! Kalbach-Mann correlated scattering NR = int(XSS(lc + 1)) NE = int(XSS(lc + 2 + 2*NR)) + ! Before progressing, check to see if data set uses L(I) values + ! in a way inconsistent with the current form of the ACE Format Guide + ! (MCNP5 Manual, Vol 3) + allocate(L(NE)) + L = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1)) + do i = 1,NE + ! Now check to see if L(i) is equal to any other entries + ! If so, then we must exit + if (count(L == L(i)) > 1) then + message = "Invalid usage of L(I) in ACE data; & + &Consider using more recent data set." + call fatal_error() + end if + end do + deallocate(L) + ! Continue with finding data length length = length + 2 + 2*NR + 2*NE do i = 1,NE NP = int(XSS(lc + length + 2)) @@ -1014,6 +1047,22 @@ contains ! Correlated energy and angle distribution NR = int(XSS(lc + 1)) NE = int(XSS(lc + 2 + 2*NR)) + ! Before progressing, check to see if data set uses L(I) values + ! in a way inconsistent with the current form of the ACE Format Guide + ! (MCNP5 Manual, Vol 3) + allocate(L(NE)) + L = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1)) + do i = 1,NE + ! Now check to see if L(i) is equal to any other entries + ! If so, then we must exit + if (count(L == L(i)) > 1) then + message = "Invalid usage of L(I) in ACE data; & + &Consider using more recent data set." + call fatal_error() + end if + end do + deallocate(L) + ! Continue with finding data length length = length + 2 + 2*NR + 2*NE do i = 1,NE ! outgoing energy distribution @@ -1046,6 +1095,22 @@ contains ! Laboratory energy-angle law NR = int(XSS(lc + 1)) NE = int(XSS(lc + 2 + 2*NR)) + ! Before progressing, check to see if data set uses L(I) values + ! in a way inconsistent with the current form of the ACE Format Guide + ! (MCNP5 Manual, Vol 3) + allocate(L(NE)) + L = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1)) + do i = 1,NE + ! Now check to see if L(i) is equal to any other entries + ! If so, then we must exit + if (count(L == L(i)) > 1) then + message = "Invalid usage of L(I) in ACE data; & + &Consider using more recent data set." + call fatal_error() + end if + end do + deallocate(L) + ! Continue with finding data length NMU = int(XSS(lc + 4 + 2*NR + 2*NE)) length = 4 + 2*(NR + NE + NMU) @@ -1188,7 +1253,7 @@ contains integer :: NMU ! number of outgoing angles integer :: JXS4 ! location of elastic energy table - ! 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 From 8fe5014b6d5f16fe3498d4e8e172431e00e9c055 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Mon, 9 Dec 2013 12:31:27 -0500 Subject: [PATCH 102/192] Updated troubleshooting documentation to reflect the new error message for the invalid L(I) usage. --- docs/source/usersguide/troubleshoot.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/source/usersguide/troubleshoot.rst b/docs/source/usersguide/troubleshoot.rst index a13272aa6..ba7e4405c 100644 --- a/docs/source/usersguide/troubleshoot.rst +++ b/docs/source/usersguide/troubleshoot.rst @@ -79,6 +79,14 @@ with the :envvar:`CROSS_SECTIONS` environment variable. It is recommended to add a line in your ``.profile`` or ``.bash_profile`` setting the :envvar:`CROSS_SECTIONS` environment variable. +ERROR: Invalid usage of L(I) in ACE data; Consider using more recent data set. +****************************************************************************** + +The cross-sections requested in ``materials.xml`` do not conform to the current +standard format. This typically happens with fissionable nuclides in a ``.6*c`` +library as distributed with MCNP. Please try a newer library such as any from +the ``.7*c`` set. + Geometry Debugging ****************** @@ -107,8 +115,8 @@ have many particles travelling through them there will not be many locations where overlaps are checked for in that region. The user should refer to the output after a geometry debug run to see how many checks were performed in each cell, and then adjust the number of starting particles or starting source -distributions accordingly to achieve good coverage. - +distributions accordingly to achieve good coverage. + ERROR: After particle __ crossed surface __ it could not be located in any cell and it did not leak. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 8884fb9e3f1657b2b5e0c5b4e8e4a3af5adfbe68 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Tue, 10 Dec 2013 05:58:11 -0500 Subject: [PATCH 103/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 d54aa83c2..d5fa012d3 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 5e2f39a00..26fbf4cbc 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 c60b96bf2..e4fcfecef 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 104/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 05f4ae0c5..8cd93f0e1 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 105/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 e4fcfecef..1507aa4cd 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 106/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 12fb34bbf..e888eb2b1 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 107/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 d0e80aaa9..8dd0fa35f 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 7a35a1214..6234d9fcf 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 c1309b72c..f2761d0a1 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 108/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 2d427d8cb..63f69f743 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 97ff56c05..6e975bde9 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 f26a309aa..bbd61cdb5 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 d6957a25240abd28b399dc78a2532b60dafbbc02 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 12 Dec 2013 22:54:42 -0500 Subject: [PATCH 109/192] Started writing script to download ACE files from NNDC site. --- data/get_nndc_data.py | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100755 data/get_nndc_data.py diff --git a/data/get_nndc_data.py b/data/get_nndc_data.py new file mode 100755 index 000000000..7bb3b2c6e --- /dev/null +++ b/data/get_nndc_data.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +from __future__ import print_function +import tarfile +import urllib2 + +baseUrl = 'http://www.nndc.bnl.gov/endf/b7.1/aceFiles/' +files = ['ENDF-B-VII.1-neutron-293.6K.tar.gz', + 'ENDF-B-VII.1-neutron-300K.tar.gz', + 'ENDF-B-VII.1-neutron-900K.tar.gz', + 'ENDF-B-VII.1-neutron-1500K.tar.gz', + 'ENDF-B-VII.1-tsl.tar.gz'] +block_size = 16384 + +# ============================================================================== +# DOWNLOAD FILES FROM NNDC SITE + +filesComplete = [] +for f in files: + # Establish connection to URL + url = baseUrl + f + print('Downloading {0}... '.format(f), end='') + req = urllib2.urlopen(url) + + # Get file size from header + file_size = int(req.info().getheaders("Content-Length")[0]) + downloaded = 0 + + # Copy file to disk + with open(f, 'wb') as fh: + while True: + chunk = req.read(block_size) + if not chunk: break + fh.write(chunk) + downloaded += len(chunk) + status = "{0:10} [{1:3.2f}%]".format(downloaded, downloaded * 100. / file_size) + print(status + chr(8)*len(status), end='') + filesComplete.append(f) + +# ============================================================================== +# EXTRACT FILES FROM TGZ + +for f in files: + if not f in filesComplete: + continue + + with tarfile.open(f, 'r') as tgz: + tgz.extractall(path='nndc') From ec805b8b6f2439c37d70c0c1b9fe955068fd4172 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 13 Dec 2013 11:40:04 -0500 Subject: [PATCH 110/192] XML only looks at first generation of children for tagname --- src/xml/dom/FoX_dom.F90 | 1 + src/xml/dom/m_dom_dom.F90 | 161 ++++++++++++++++++++++++++++++++++++++ src/xml_interface.F90 | 10 +-- 3 files changed, 167 insertions(+), 5 deletions(-) diff --git a/src/xml/dom/FoX_dom.F90 b/src/xml/dom/FoX_dom.F90 index 6e1ac36a7..4e2fc1e9c 100644 --- a/src/xml/dom/FoX_dom.F90 +++ b/src/xml/dom/FoX_dom.F90 @@ -74,6 +74,7 @@ module FoX_dom public :: createAttribute public :: createEntityReference public :: getElementsByTagName + public :: getChildrenByTagName public :: getElementById public :: importNode diff --git a/src/xml/dom/m_dom_dom.F90 b/src/xml/dom/m_dom_dom.F90 index 01f6265b0..ec5b788d9 100644 --- a/src/xml/dom/m_dom_dom.F90 +++ b/src/xml/dom/m_dom_dom.F90 @@ -349,6 +349,7 @@ module m_dom_dom public :: createEntityReference public :: createEmptyEntityReference public :: getElementsByTagName + public :: getChildrenByTagName public :: importNode public :: createElementNS public :: createAttributeNS @@ -6908,6 +6909,166 @@ endif end function getElementsByTagName + function getChildrenByTagName(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) .and. .not. associated(getParentNode(this), treeroot)) 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 getChildrenByTagName + function importNode(doc , arg, deep , ex)result(np) type(DOMException), intent(out), optional :: ex type(Node), pointer :: doc diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 4752c0873..870b4288b 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -72,7 +72,7 @@ contains ! 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. +! to getChildrenByTagName. !=============================================================================== function check_for_node(ptr, node_name) result(found) @@ -94,7 +94,7 @@ contains if (associated(temp_ptr)) return ! Check for a sub-element - elem_list => getElementsByTagName(ptr, trim(node_name)) + elem_list => getChildrenByTagName(ptr, trim(node_name)) ! Get the length of the list if (getLength(elem_list) == 0) then @@ -124,7 +124,7 @@ contains found_ = .false. ! Check for a sub-element - elem_list => getElementsByTagName(in_ptr, trim(node_name)) + elem_list => getChildrenByTagName(in_ptr, trim(node_name)) ! Get the length of the list if (getLength(elem_list) == 0) return @@ -149,7 +149,7 @@ contains type(NodeList), pointer, intent(out) :: out_ptr ! Check for a sub-element - out_ptr => getElementsByTagName(in_ptr, trim(node_name)) + out_ptr => getChildrenByTagName(in_ptr, trim(node_name)) end subroutine get_node_list @@ -525,7 +525,7 @@ contains if (associated(out_ptr)) return ! Check for a sub-element - elem_list => getElementsByTagName(in_ptr, trim(node_name)) + elem_list => getChildrenByTagName(in_ptr, trim(node_name)) ! Get the length of the list if (getLength(elem_list) == 0) then From fecc07709aa8095e05e1e351d9b3196bc00f66cd Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 13 Dec 2013 11:41:11 -0500 Subject: [PATCH 111/192] added flag for compiling XML on BGQ --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 03c370e07..05fa826f4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -218,7 +218,7 @@ endif ifeq ($(MACHINE),bluegene) F90 = /bgsys/drivers/ppcfloor/comm/xl/bin/mpixlf2003 - F90FLAGS = -WF,-DNO_F2008,-DMPI -O3 + F90FLAGS = -WF,-DNO_F2008,-DMPI,-DRESTRICTED_ASSOCIATED_BUG -O3 LDFLAGS = -lmpich.cnkf90 endif @@ -233,7 +233,7 @@ endif ifeq ($(MACHINE),bluegeneq) F90 = mpixlf2003 - F90FLAGS = -WF,-DNO_F2008,-DMPI -O5 + F90FLAGS = -WF,-DNO_F2008,-DMPI,-DRESTRICTED_ASSOCIATED_BUG -O5 endif #=============================================================================== From 549bdad2e371e6afb78353b7dec8914bacbdaee8 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sat, 14 Dec 2013 10:18:35 -0500 Subject: [PATCH 112/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 d5fa012d3..ff8582470 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 26fbf4cbc..e5bf1bb3a 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 564e0baed..6fea8cbc1 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 113/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 fb1f89194..5771377c8 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 114/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 5771377c8..372d67bd3 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 115/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 372d67bd3..2d3cfeb39 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 116/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 c05782e71..164b0151b 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 6ba3c48c5af0a42950b3469d65b6dff5d1211c3d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Dec 2013 10:54:02 -0500 Subject: [PATCH 117/192] Check for file already downloaded. Rename xsdir. --- data/get_nndc_data.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/data/get_nndc_data.py b/data/get_nndc_data.py index 7bb3b2c6e..0f943d684 100755 --- a/data/get_nndc_data.py +++ b/data/get_nndc_data.py @@ -1,6 +1,8 @@ #!/usr/bin/env python from __future__ import print_function +import os +import os.path import tarfile import urllib2 @@ -19,22 +21,33 @@ filesComplete = [] for f in files: # Establish connection to URL url = baseUrl + f - print('Downloading {0}... '.format(f), end='') req = urllib2.urlopen(url) # Get file size from header - file_size = int(req.info().getheaders("Content-Length")[0]) + file_size = int(req.info().getheaders('Content-Length')[0]) downloaded = 0 + # Check if file already downloaded + if os.path.exists(f): + if os.path.getsize(f) == file_size: + print('Skipping ' + f) + continue + else: + overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(f)) + if overwrite.lower().startswith('n'): + continue + # Copy file to disk + print('Downloading {0}... '.format(f), end='') with open(f, 'wb') as fh: while True: chunk = req.read(block_size) if not chunk: break fh.write(chunk) downloaded += len(chunk) - status = "{0:10} [{1:3.2f}%]".format(downloaded, downloaded * 100. / file_size) + status = '{0:10} [{1:3.2f}%]'.format(downloaded, downloaded * 100. / file_size) print(status + chr(8)*len(status), end='') + print('') filesComplete.append(f) # ============================================================================== @@ -44,5 +57,11 @@ for f in files: if not f in filesComplete: continue + # Extract files with tarfile.open(f, 'r') as tgz: tgz.extractall(path='nndc') + + # Give xsdir a unique name + xsdir = 'nndc/xsdir' + if os.path.exists(xsdir): + os.rename(xsdir, xsdir + '_' + f.strip('.tar.gz')) From 96999e00c6eb2ab1172c590268bc79155fea9d84 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Dec 2013 11:18:58 -0500 Subject: [PATCH 118/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 4fcdd0074..231a23cc5 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 119/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 fa29f46ec..32315f1ed 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 120/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 8cd93f0e1..63fb2163e 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 e82bb986f..75c0ea29c 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 121/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 ff8582470..aa6de5ab6 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 122/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 c79144dfe..fc82dc1a9 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 123/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 fc82dc1a9..54dc91345 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 124/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 63fb2163e..3ce3c9d0d 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 125/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 1507aa4cd..677c35396 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 126/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 915e6af07..c9e367200 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 3793923bb..e13cd8f48 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 021cbb8ca..79a280906 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 127/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 120db5f64..6f259f949 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 128/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 a5ba0bf45..fe99ba22e 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 129/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 04b6b4592..c70b29165 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 ec9d9edf7..430abee8b 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] From be6b7ea90db74750558d66afbe6e23aebd4130d5 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 6 Jan 2014 09:41:48 -0500 Subject: [PATCH 130/192] added check if source is written to statepoint file that the statepoint will exist --- src/input_xml.F90 | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index e8d602c35..cc12d0c41 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -57,6 +57,7 @@ contains integer, allocatable :: temp_int_array(:) integer(8) :: temp_long logical :: file_exists + logical :: check character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename @@ -612,13 +613,13 @@ contains call statepoint_batch % add(n_batches) end if - ! Check if the user has specified to write state points + ! Check if the user has specified to write source points if (check_for_node(doc, "source_point")) then ! Get pointer to source_point node call get_node_ptr(doc, "source_point", node_sp) - ! Determine number of batches at which to store state points + ! Determine number of batches at which to store source points if (check_for_node(node_sp, "batches")) then n_source_points = get_arraysize_integer(node_sp, "batches") else @@ -626,7 +627,7 @@ contains end if if (n_source_points > 0) then - ! User gave specific batches to write state points + ! User gave specific batches to write source points allocate(temp_int_array(n_source_points)) call get_node_array(node_sp, "batches", temp_int_array) do i = 1, n_source_points @@ -634,14 +635,14 @@ contains end do deallocate(temp_int_array) elseif (check_for_node(node_sp, "interval")) then - ! User gave an interval for writing state points + ! User gave an interval for writing source points call get_node_value(node_sp, "interval", temp_int) n_source_points = n_batches / temp_int do i = 1, n_source_points call sourcepoint_batch % add(temp_int * i) end do else - ! If neither were specified, write state point at last batch + ! If neither were specified, write source points with state points n_source_points = n_state_points do i = 1, n_state_points call sourcepoint_batch % add(statepoint_batch % get_item(i)) @@ -658,8 +659,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 if (check_for_node(node_sp, "overwrite_latest")) then call get_node_value(node_sp, "overwrite_latest", temp_str) @@ -677,6 +678,21 @@ contains end do end if + ! If source is not seperate and is to be written out in the statepoint file, + ! make sure that the sourcepoint batch numbers are contained in the + ! statepoint list + if (.not. source_separate) then + check = .true. + do i = 1, n_source_points + check = statepoint_batch % contains(sourcepoint_batch % get_item(i)) + if (.not. check) then + message = 'Sourcepoint batches are not a subset& + & of statepoint batches.' + call fatal_error() + end if + end do + end if + ! Check if the user has specified to not reduce tallies at the end of every ! batch if (check_for_node(doc, "no_reduce")) then From 036eb081c10349900b29bd17eec0cfed2dc6ab2d Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 6 Jan 2014 09:57:45 -0500 Subject: [PATCH 131/192] added new tests for sourcepoint --- tests/test_sourcepoint_batch/geometry.xml | 8 ++++ tests/test_sourcepoint_batch/materials.xml | 9 ++++ tests/test_sourcepoint_batch/results.py | 32 +++++++++++++ tests/test_sourcepoint_batch/results_true.dat | 3 ++ tests/test_sourcepoint_batch/settings.xml | 19 ++++++++ .../test_sourcepoint_batch.py | 44 +++++++++++++++++ tests/test_sourcepoint_interval/geometry.xml | 8 ++++ tests/test_sourcepoint_interval/materials.xml | 9 ++++ tests/test_sourcepoint_interval/results.py | 32 +++++++++++++ .../results_true.dat | 3 ++ tests/test_sourcepoint_interval/settings.xml | 19 ++++++++ .../test_sourcepoint_interval.py | 44 +++++++++++++++++ tests/test_sourcepoint_latest/geometry.xml | 8 ++++ tests/test_sourcepoint_latest/materials.xml | 9 ++++ tests/test_sourcepoint_latest/results.py | 25 ++++++++++ .../test_sourcepoint_latest/results_test.dat | 2 + .../test_sourcepoint_latest/results_true.dat | 3 ++ tests/test_sourcepoint_latest/settings.xml | 18 +++++++ .../test_sourcepoint_latest.py | 48 +++++++++++++++++++ 19 files changed, 343 insertions(+) create mode 100644 tests/test_sourcepoint_batch/geometry.xml create mode 100644 tests/test_sourcepoint_batch/materials.xml create mode 100644 tests/test_sourcepoint_batch/results.py create mode 100644 tests/test_sourcepoint_batch/results_true.dat create mode 100644 tests/test_sourcepoint_batch/settings.xml create mode 100644 tests/test_sourcepoint_batch/test_sourcepoint_batch.py create mode 100644 tests/test_sourcepoint_interval/geometry.xml create mode 100644 tests/test_sourcepoint_interval/materials.xml create mode 100644 tests/test_sourcepoint_interval/results.py create mode 100644 tests/test_sourcepoint_interval/results_true.dat create mode 100644 tests/test_sourcepoint_interval/settings.xml create mode 100644 tests/test_sourcepoint_interval/test_sourcepoint_interval.py create mode 100644 tests/test_sourcepoint_latest/geometry.xml create mode 100644 tests/test_sourcepoint_latest/materials.xml create mode 100644 tests/test_sourcepoint_latest/results.py create mode 100644 tests/test_sourcepoint_latest/results_test.dat create mode 100644 tests/test_sourcepoint_latest/results_true.dat create mode 100644 tests/test_sourcepoint_latest/settings.xml create mode 100644 tests/test_sourcepoint_latest/test_sourcepoint_latest.py diff --git a/tests/test_sourcepoint_batch/geometry.xml b/tests/test_sourcepoint_batch/geometry.xml new file mode 100644 index 000000000..612e46132 --- /dev/null +++ b/tests/test_sourcepoint_batch/geometry.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/test_sourcepoint_batch/materials.xml b/tests/test_sourcepoint_batch/materials.xml new file mode 100644 index 000000000..1f85510e0 --- /dev/null +++ b/tests/test_sourcepoint_batch/materials.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/test_sourcepoint_batch/results.py b/tests/test_sourcepoint_batch/results.py new file mode 100644 index 000000000..9b0f577eb --- /dev/null +++ b/tests/test_sourcepoint_batch/results.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +import sys + +# 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.8.binary') +sp.read_results() +sp.read_source() + +# 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 xyz +xyz = sp.source[0].xyz +for i in xyz: + outstr += "{0:12.6E} ".format(i) +outstr += "\n" + +# write results to file +with open('results_test.dat','w') as fh: + fh.write(outstr) diff --git a/tests/test_sourcepoint_batch/results_true.dat b/tests/test_sourcepoint_batch/results_true.dat new file mode 100644 index 000000000..748df0351 --- /dev/null +++ b/tests/test_sourcepoint_batch/results_true.dat @@ -0,0 +1,3 @@ +k-combined: +0.000000E+00 0.000000E+00 +-9.438655E-02 -4.436810E+00 -2.416825E+00 diff --git a/tests/test_sourcepoint_batch/settings.xml b/tests/test_sourcepoint_batch/settings.xml new file mode 100644 index 000000000..c816fe700 --- /dev/null +++ b/tests/test_sourcepoint_batch/settings.xml @@ -0,0 +1,19 @@ + + + + + + + + 10 + 5 + 1000 + + + + + -4 -4 -4 4 4 4 + + + + diff --git a/tests/test_sourcepoint_batch/test_sourcepoint_batch.py b/tests/test_sourcepoint_batch/test_sourcepoint_batch.py new file mode 100644 index 000000000..78d202e40 --- /dev/null +++ b/tests/test_sourcepoint_batch/test_sourcepoint_batch.py @@ -0,0 +1,44 @@ +#!/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) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +def test_statepoint_exists(): + statepoint = glob.glob(pwd + '/statepoint.*') + assert len(statepoint) == 5 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + +def test_results(): + statepoint = glob.glob(pwd + '/statepoint.8.*') + 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.*') + output.append(pwd + '/results_test.dat') + for f in output: + if os.path.exists(f): + os.remove(f) diff --git a/tests/test_sourcepoint_interval/geometry.xml b/tests/test_sourcepoint_interval/geometry.xml new file mode 100644 index 000000000..612e46132 --- /dev/null +++ b/tests/test_sourcepoint_interval/geometry.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/test_sourcepoint_interval/materials.xml b/tests/test_sourcepoint_interval/materials.xml new file mode 100644 index 000000000..1f85510e0 --- /dev/null +++ b/tests/test_sourcepoint_interval/materials.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/test_sourcepoint_interval/results.py b/tests/test_sourcepoint_interval/results.py new file mode 100644 index 000000000..9b0f577eb --- /dev/null +++ b/tests/test_sourcepoint_interval/results.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +import sys + +# 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.8.binary') +sp.read_results() +sp.read_source() + +# 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 xyz +xyz = sp.source[0].xyz +for i in xyz: + outstr += "{0:12.6E} ".format(i) +outstr += "\n" + +# write results to file +with open('results_test.dat','w') as fh: + fh.write(outstr) diff --git a/tests/test_sourcepoint_interval/results_true.dat b/tests/test_sourcepoint_interval/results_true.dat new file mode 100644 index 000000000..748df0351 --- /dev/null +++ b/tests/test_sourcepoint_interval/results_true.dat @@ -0,0 +1,3 @@ +k-combined: +0.000000E+00 0.000000E+00 +-9.438655E-02 -4.436810E+00 -2.416825E+00 diff --git a/tests/test_sourcepoint_interval/settings.xml b/tests/test_sourcepoint_interval/settings.xml new file mode 100644 index 000000000..d0b8fa71d --- /dev/null +++ b/tests/test_sourcepoint_interval/settings.xml @@ -0,0 +1,19 @@ + + + + + + + + 10 + 5 + 1000 + + + + + -4 -4 -4 4 4 4 + + + + diff --git a/tests/test_sourcepoint_interval/test_sourcepoint_interval.py b/tests/test_sourcepoint_interval/test_sourcepoint_interval.py new file mode 100644 index 000000000..78d202e40 --- /dev/null +++ b/tests/test_sourcepoint_interval/test_sourcepoint_interval.py @@ -0,0 +1,44 @@ +#!/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) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +def test_statepoint_exists(): + statepoint = glob.glob(pwd + '/statepoint.*') + assert len(statepoint) == 5 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + +def test_results(): + statepoint = glob.glob(pwd + '/statepoint.8.*') + 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.*') + output.append(pwd + '/results_test.dat') + for f in output: + if os.path.exists(f): + os.remove(f) diff --git a/tests/test_sourcepoint_latest/geometry.xml b/tests/test_sourcepoint_latest/geometry.xml new file mode 100644 index 000000000..612e46132 --- /dev/null +++ b/tests/test_sourcepoint_latest/geometry.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/test_sourcepoint_latest/materials.xml b/tests/test_sourcepoint_latest/materials.xml new file mode 100644 index 000000000..1f85510e0 --- /dev/null +++ b/tests/test_sourcepoint_latest/materials.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/test_sourcepoint_latest/results.py b/tests/test_sourcepoint_latest/results.py new file mode 100644 index 000000000..8ff10971c --- /dev/null +++ b/tests/test_sourcepoint_latest/results.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +import sys + +# 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.10.binary') +sp.read_results() + +# 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 results to file +with open('results_test.dat','w') as fh: + fh.write(outstr) diff --git a/tests/test_sourcepoint_latest/results_test.dat b/tests/test_sourcepoint_latest/results_test.dat new file mode 100644 index 000000000..00a65f913 --- /dev/null +++ b/tests/test_sourcepoint_latest/results_test.dat @@ -0,0 +1,2 @@ +k-combined: +3.011353E-01 2.854556E-03 diff --git a/tests/test_sourcepoint_latest/results_true.dat b/tests/test_sourcepoint_latest/results_true.dat new file mode 100644 index 000000000..748df0351 --- /dev/null +++ b/tests/test_sourcepoint_latest/results_true.dat @@ -0,0 +1,3 @@ +k-combined: +0.000000E+00 0.000000E+00 +-9.438655E-02 -4.436810E+00 -2.416825E+00 diff --git a/tests/test_sourcepoint_latest/settings.xml b/tests/test_sourcepoint_latest/settings.xml new file mode 100644 index 000000000..36c5c7a0d --- /dev/null +++ b/tests/test_sourcepoint_latest/settings.xml @@ -0,0 +1,18 @@ + + + + + + + 10 + 5 + 1000 + + + + + -4 -4 -4 4 4 4 + + + + diff --git a/tests/test_sourcepoint_latest/test_sourcepoint_latest.py b/tests/test_sourcepoint_latest/test_sourcepoint_latest.py new file mode 100644 index 000000000..d26533843 --- /dev/null +++ b/tests/test_sourcepoint_latest/test_sourcepoint_latest.py @@ -0,0 +1,48 @@ +#!/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) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +def test_statepoint_exists(): + statepoint = glob.glob(pwd + '/statepoint.10.*') + assert len(statepoint) == 1 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + source = glob.glob(pwd + '/source.*') + assert len(source) == 1 + assert source[0].endswith('binary') or source[0].endswith('h5') + +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(): + output = glob.glob(pwd + '/statepoint.10.*') + output += glob.glob(pwd + '/source.*') + output.append(pwd + '/results_test.dat') + for f in output: + if os.path.exists(f): + os.remove(f) From ad017aa5dee0f271e40e500fc1574f91a4e59c1e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 6 Jan 2014 10:37:50 -0500 Subject: [PATCH 132/192] added test for restarting with a sourcepoint --- tests/test_sourcepoint_restart/geometry.xml | 8 + tests/test_sourcepoint_restart/materials.xml | 9 + tests/test_sourcepoint_restart/results.py | 44 + .../test_sourcepoint_restart/results_true.dat | 2412 +++++++++++++++++ tests/test_sourcepoint_restart/settings.xml | 19 + tests/test_sourcepoint_restart/tallies.xml | 23 + .../test_sourcepoint_restart.py | 133 + 7 files changed, 2648 insertions(+) create mode 100644 tests/test_sourcepoint_restart/geometry.xml create mode 100644 tests/test_sourcepoint_restart/materials.xml create mode 100644 tests/test_sourcepoint_restart/results.py create mode 100644 tests/test_sourcepoint_restart/results_true.dat create mode 100644 tests/test_sourcepoint_restart/settings.xml create mode 100644 tests/test_sourcepoint_restart/tallies.xml create mode 100644 tests/test_sourcepoint_restart/test_sourcepoint_restart.py diff --git a/tests/test_sourcepoint_restart/geometry.xml b/tests/test_sourcepoint_restart/geometry.xml new file mode 100644 index 000000000..612e46132 --- /dev/null +++ b/tests/test_sourcepoint_restart/geometry.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/test_sourcepoint_restart/materials.xml b/tests/test_sourcepoint_restart/materials.xml new file mode 100644 index 000000000..1f85510e0 --- /dev/null +++ b/tests/test_sourcepoint_restart/materials.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/test_sourcepoint_restart/results.py b/tests/test_sourcepoint_restart/results.py new file mode 100644 index 000000000..d0b3a55e0 --- /dev/null +++ b/tests/test_sourcepoint_restart/results.py @@ -0,0 +1,44 @@ +#!/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.7.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) + +# 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) + +# write results to file +with open('results_test.dat','w') as fh: + fh.write(outstr) diff --git a/tests/test_sourcepoint_restart/results_true.dat b/tests/test_sourcepoint_restart/results_true.dat new file mode 100644 index 000000000..f2b97af34 --- /dev/null +++ b/tests/test_sourcepoint_restart/results_true.dat @@ -0,0 +1,2412 @@ +k-combined: +0.000000E+00 0.000000E+00 +tally 1: +3.000000E-03 +5.000000E-06 +1.350207E-03 +9.349751E-07 +-7.677912E-05 +4.241317E-07 +-3.387424E-04 +1.343262E-07 +1.215119E-03 +9.127703E-07 +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.131510E-04 +1.880396E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +9.000000E-06 +1.889613E-04 +3.570639E-08 +2.320049E-04 +5.382627E-08 +1.491658E-03 +2.225044E-06 +1.817087E-03 +2.362232E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +4.000000E-05 +1.075518E-03 +3.528151E-06 +2.716562E-03 +1.166700E-05 +2.288359E-03 +2.867176E-06 +3.411472E-03 +8.255676E-06 +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.009839E-04 +9.059133E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +1.700000E-05 +3.530317E-04 +1.159034E-06 +2.030571E-03 +4.217108E-06 +9.628222E-04 +4.775997E-07 +1.817087E-03 +2.362232E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.000000E-06 +4.849895E-04 +9.196474E-07 +3.794711E-04 +4.964654E-07 +8.024923E-04 +3.245502E-07 +1.237485E-03 +9.676259E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +1.789439E-04 +3.202091E-08 +-4.519686E-04 +2.042756E-07 +-2.540910E-04 +6.456222E-08 +6.243342E-04 +3.897931E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.500000E-05 +1.464813E-03 +1.309819E-06 +8.835792E-04 +6.990079E-07 +3.177051E-03 +5.159010E-06 +3.366739E-03 +5.697495E-06 +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.009839E-04 +9.059133E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +5.000000E-06 +1.881176E-03 +2.067359E-06 +2.892742E-04 +1.089921E-07 +-9.095343E-04 +4.184955E-07 +1.237485E-03 +9.676259E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.365012E-04 +8.770346E-07 +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.121671E-04 +9.744828E-08 +1.000000E-03 +1.000000E-06 +8.504018E-04 +7.231831E-07 +5.847747E-04 +3.419615E-07 +2.618879E-04 +6.858528E-08 +6.243342E-04 +3.897931E-07 +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.243342E-04 +3.897931E-07 +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.009839E-04 +9.059133E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.009839E-04 +9.059133E-08 +1.000000E-03 +1.000000E-06 +9.742209E-04 +9.491064E-07 +9.236596E-04 +8.531471E-07 +8.502670E-04 +7.229539E-07 +3.009839E-04 +9.059133E-08 +9.000000E-03 +4.500000E-05 +5.336900E-03 +1.424486E-05 +3.093810E-03 +5.161998E-06 +2.879582E-03 +4.229500E-06 +4.002256E-03 +8.501473E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.600000E-02 +1.280000E-04 +4.224331E-03 +9.782839E-06 +3.223102E-03 +5.736299E-06 +2.099435E-03 +2.209094E-06 +7.346629E-03 +2.710118E-05 +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.121671E-04 +9.744828E-08 +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.365012E-04 +8.770346E-07 +2.000000E-03 +4.000000E-06 +4.929028E-04 +2.429532E-07 +-6.043220E-05 +3.652051E-09 +2.687444E-04 +7.222357E-08 +0.000000E+00 +0.000000E+00 +4.000000E-03 +1.000000E-05 +3.193650E-03 +5.924989E-06 +2.012845E-03 +2.064950E-06 +1.030603E-03 +6.338801E-07 +3.656540E-03 +7.357018E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +1.000000E-05 +5.813261E-04 +2.815138E-07 +-5.070667E-04 +2.458625E-07 +-5.126590E-04 +1.937439E-07 +2.162803E-03 +2.798572E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.500000E-02 +1.130000E-04 +3.838937E-03 +7.695404E-06 +3.258655E-03 +8.032841E-06 +7.822538E-04 +5.238913E-06 +7.034462E-03 +2.505476E-05 +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.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.200000E-02 +7.400000E-05 +3.703109E-03 +7.450207E-06 +1.172561E-03 +6.983603E-06 +9.775789E-04 +3.396069E-06 +5.183826E-03 +1.446969E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.600000E-02 +1.300000E-04 +5.461842E-03 +1.700217E-05 +-1.194197E-03 +2.155631E-06 +-4.418175E-04 +1.630461E-07 +7.647613E-03 +2.954714E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.000000E-05 +2.288086E-03 +4.056180E-06 +2.803000E-03 +6.212022E-06 +2.253444E-03 +3.299961E-06 +3.043389E-03 +5.316010E-06 +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.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +4.100000E-05 +-1.469649E-03 +6.515270E-06 +3.164874E-03 +5.070423E-06 +-7.834805E-04 +2.163764E-06 +3.991073E-03 +8.036254E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +6.578244E-04 +4.327330E-07 +1.490994E-04 +2.223064E-08 +-2.750809E-04 +7.566948E-08 +6.243342E-04 +3.897931E-07 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.500000E-05 +4.192244E-04 +2.440829E-06 +2.290352E-04 +2.955670E-08 +8.117866E-05 +3.786185E-09 +4.292057E-03 +9.213941E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.100000E-02 +7.300000E-05 +4.050236E-03 +2.014829E-05 +3.589950E-03 +7.394738E-06 +3.301506E-03 +7.271497E-06 +5.574275E-03 +2.054932E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +8.000000E-06 +6.865027E-04 +3.148594E-06 +1.123135E-03 +7.553928E-07 +-6.937330E-04 +5.371067E-07 +1.226302E-03 +7.521585E-07 +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.009839E-04 +9.059133E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.500000E-05 +4.720935E-03 +1.123308E-05 +2.531138E-03 +3.881873E-06 +1.207797E-03 +1.104982E-06 +3.991073E-03 +8.036254E-06 +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.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.805904E-03 +3.261288E-06 +2.000000E-03 +4.000000E-06 +1.904158E-03 +3.625820E-06 +1.722222E-03 +2.966050E-06 +1.472450E-03 +2.168109E-06 +0.000000E+00 +0.000000E+00 +2.200000E-02 +2.440000E-04 +8.997789E-03 +4.069446E-05 +6.038380E-03 +2.892796E-05 +3.188711E-03 +1.706565E-05 +1.071337E-02 +5.765023E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.121671E-04 +9.744828E-08 +1.000000E-03 +1.000000E-06 +9.994298E-04 +9.988600E-07 +9.982900E-04 +9.965829E-07 +9.965814E-04 +9.931746E-07 +0.000000E+00 +0.000000E+00 +1.100000E-02 +6.100000E-05 +1.446955E-03 +5.203867E-06 +1.233625E-03 +3.439950E-06 +2.123440E-03 +6.107272E-06 +3.366739E-03 +5.697495E-06 +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.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +1.600000E-05 +3.062793E-03 +9.380701E-06 +2.224463E-03 +4.948238E-06 +2.136221E-03 +4.563441E-06 +3.009839E-03 +9.059133E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.009839E-04 +9.059133E-08 +1.000000E-03 +1.000000E-06 +1.071043E-04 +1.147134E-08 +-4.827930E-04 +2.330891E-07 +-1.575849E-04 +2.483301E-08 +3.009839E-04 +9.059133E-08 +8.000000E-03 +3.400000E-05 +3.469885E-03 +1.204729E-05 +1.706287E-04 +4.770906E-06 +6.093070E-04 +3.706122E-07 +3.656540E-03 +7.357018E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.009839E-04 +9.059133E-08 +1.000000E-03 +1.000000E-06 +9.276723E-04 +8.605758E-07 +7.908638E-04 +6.254655E-07 +6.043224E-04 +3.652056E-07 +3.009839E-04 +9.059133E-08 +1.500000E-02 +1.130000E-04 +7.760317E-03 +3.043383E-05 +3.486730E-03 +6.079687E-06 +3.192886E-03 +5.300269E-06 +5.206192E-03 +1.357459E-05 +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.121671E-04 +9.744828E-08 +1.000000E-03 +1.000000E-06 +-2.723974E-04 +7.420035E-08 +-3.886995E-04 +1.510873E-07 +3.580662E-04 +1.282114E-07 +3.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +1.620000E-04 +3.109478E-03 +5.403087E-06 +7.420753E-04 +5.610775E-06 +-1.972647E-03 +2.210831E-06 +7.056828E-03 +2.499410E-05 +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.253181E-04 +4.803845E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.203936E-03 +1.449461E-06 +2.000000E-03 +4.000000E-06 +1.876080E-03 +3.519675E-06 +1.640561E-03 +2.691440E-06 +1.316649E-03 +1.733565E-06 +0.000000E+00 +0.000000E+00 +2.000000E-03 +2.000000E-06 +1.656132E-03 +1.374995E-06 +1.062492E-03 +5.867156E-07 +3.772098E-04 +1.191478E-07 +3.054572E-03 +4.820460E-06 +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.131510E-04 +1.880396E-07 +1.000000E-03 +1.000000E-06 +-5.333309E-04 +2.844418E-07 +-7.333726E-05 +5.378353E-09 +4.207423E-04 +1.770241E-07 +6.243342E-04 +3.897931E-07 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.000000E-05 +1.583270E-03 +3.252679E-06 +-3.064030E-04 +4.947979E-06 +1.080830E-03 +8.869004E-07 +2.452604E-03 +3.008634E-06 +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.131510E-04 +1.880396E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.200000E-02 +9.000000E-05 +5.530475E-03 +1.529320E-05 +2.705859E-03 +5.488988E-06 +3.266400E-03 +5.350316E-06 +7.112744E-03 +3.142384E-05 +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.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.100000E-02 +2.250000E-04 +7.403253E-03 +3.016107E-05 +5.420662E-03 +1.983684E-05 +2.936582E-03 +4.922875E-06 +8.238398E-03 +3.592572E-05 +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.253181E-04 +4.803845E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.100000E-02 +6.100000E-05 +3.184470E-03 +6.981764E-06 +3.311201E-03 +5.530952E-06 +2.002853E-03 +2.093961E-06 +3.968707E-03 +8.234052E-06 +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.131510E-04 +1.880396E-07 +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.365012E-04 +8.770346E-07 +1.000000E-03 +1.000000E-06 +9.677184E-04 +9.364790E-07 +9.047184E-04 +8.185155E-07 +8.140422E-04 +6.626647E-07 +0.000000E+00 +0.000000E+00 +1.000000E-02 +6.800000E-05 +1.266540E-03 +1.529518E-06 +-3.692635E-04 +9.672879E-07 +1.689168E-03 +1.866327E-06 +4.035806E-03 +1.215361E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.200000E-02 +8.000000E-05 +3.410989E-03 +6.495638E-06 +8.888873E-04 +4.027331E-06 +6.828019E-04 +1.205374E-06 +4.894025E-03 +1.211286E-05 +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.243342E-04 +3.897931E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.100000E-02 +7.300000E-05 +3.976139E-03 +9.939638E-06 +1.462256E-03 +2.640635E-06 +2.026283E-03 +3.803255E-06 +4.604224E-03 +1.067567E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.029518E-04 +8.153220E-07 +1.000000E-03 +1.000000E-06 +9.531896E-04 +9.085703E-07 +8.628555E-04 +7.445196E-07 +7.353151E-04 +5.406883E-07 +0.000000E+00 +0.000000E+00 +3.000000E-03 +9.000000E-06 +1.969150E-03 +3.877552E-06 +1.225946E-03 +1.502944E-06 +1.367753E-03 +1.870749E-06 +1.504920E-03 +2.264783E-06 +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.009839E-04 +9.059133E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.600000E-05 +-3.423632E-04 +2.476095E-06 +2.144575E-03 +2.456203E-06 +-1.035289E-03 +3.066348E-06 +3.678906E-03 +6.769426E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +4.000000E-05 +5.417577E-03 +1.889555E-05 +2.293131E-03 +3.303280E-06 +6.729020E-04 +3.229421E-07 +5.206192E-03 +1.357459E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.100000E-02 +7.300000E-05 +7.262770E-03 +3.815108E-05 +4.265119E-03 +1.234667E-05 +2.317314E-03 +2.894105E-06 +3.355556E-03 +5.998148E-06 +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.019679E-04 +3.623653E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +9.000000E-06 +5.774620E-04 +3.334623E-07 +3.789825E-04 +1.436277E-07 +-1.360725E-04 +1.851572E-08 +1.805904E-03 +3.261288E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +3.400000E-05 +-1.376386E-04 +3.479848E-08 +-1.136484E-03 +8.968777E-07 +-6.910241E-04 +3.879057E-07 +3.344373E-03 +6.674880E-06 +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.131510E-04 +1.880396E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.100000E-02 +7.300000E-05 +3.617203E-03 +1.222945E-05 +1.595056E-03 +1.290135E-06 +-6.608603E-04 +1.154300E-06 +4.024623E-03 +1.056015E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +-2.171894E-04 +4.717122E-08 +-4.292432E-04 +1.842497E-07 +3.001713E-04 +9.010283E-08 +3.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +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.000000E-03 +1.700000E-05 +3.376537E-03 +6.867510E-06 +1.576049E-03 +1.242043E-06 +6.774572E-04 +3.539001E-07 +2.787137E-03 +5.137331E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.300000E-02 +8.900000E-05 +3.214925E-03 +6.383584E-06 +1.681554E-03 +1.482668E-06 +1.811720E-03 +1.724962E-06 +4.269691E-03 +9.774105E-06 +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.009839E-04 +9.059133E-08 +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.243342E-04 +3.897931E-07 +1.000000E-03 +1.000000E-06 +8.016563E-05 +6.426528E-09 +-4.903602E-04 +2.404531E-07 +-1.189605E-04 +1.415159E-08 +0.000000E+00 +0.000000E+00 +7.000000E-03 +3.700000E-05 +6.291973E-04 +9.253012E-07 +-8.619079E-04 +3.776625E-07 +4.994451E-04 +1.549611E-07 +3.690089E-03 +7.039749E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.000000E-06 +1.439409E-03 +1.103682E-06 +6.555229E-04 +5.306118E-07 +7.043819E-05 +4.155385E-07 +1.226302E-03 +7.521585E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +4.295537E-04 +1.845164E-07 +-2.232254E-04 +4.982957E-08 +-4.461813E-04 +1.990778E-07 +3.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +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.000000E-03 +4.000000E-06 +1.440797E-04 +2.075896E-08 +1.581861E-03 +2.502285E-06 +7.101262E-04 +5.042792E-07 +6.019679E-04 +3.623653E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +9.000000E-06 +2.304111E-03 +5.308928E-06 +1.405559E-03 +1.975595E-06 +8.468624E-04 +7.171759E-07 +2.441421E-03 +3.141818E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +2.900000E-05 +4.675157E-03 +1.278192E-05 +2.215224E-03 +3.397777E-06 +1.088961E-03 +2.486687E-06 +3.344373E-03 +6.674880E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +4.000000E-06 +-2.660370E-04 +7.077569E-08 +-7.919915E-04 +6.272506E-07 +2.842468E-04 +8.079625E-08 +9.141350E-04 +4.598136E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +6.738077E-05 +4.540168E-09 +-4.931897E-04 +2.432361E-07 +-1.003064E-04 +1.006137E-08 +3.009839E-04 +9.059133E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +4.000000E-06 +1.768784E-03 +3.128598E-06 +1.358416E-03 +1.845293E-06 +8.583748E-04 +7.368072E-07 +1.226302E-03 +7.521585E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +5.000000E-06 +1.382401E-03 +1.135211E-06 +9.435101E-04 +9.475181E-07 +1.233939E-03 +9.782026E-07 +1.226302E-03 +7.521585E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +9.843457E-04 +9.689366E-07 +9.534048E-04 +9.089808E-07 +9.079028E-04 +8.242875E-07 +3.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 2: +2.302093E-01 +2.649911E-02 +2.511941E-01 +3.155135E-02 +1.462955E+00 +1.070191E+00 +1.627347E+01 +1.324129E+02 diff --git a/tests/test_sourcepoint_restart/settings.xml b/tests/test_sourcepoint_restart/settings.xml new file mode 100644 index 000000000..5fee23d67 --- /dev/null +++ b/tests/test_sourcepoint_restart/settings.xml @@ -0,0 +1,19 @@ + + + + + + + + 10 + 5 + 1000 + + + + + -4 -4 -4 4 4 4 + + + + diff --git a/tests/test_sourcepoint_restart/tallies.xml b/tests/test_sourcepoint_restart/tallies.xml new file mode 100644 index 000000000..1704f56e1 --- /dev/null +++ b/tests/test_sourcepoint_restart/tallies.xml @@ -0,0 +1,23 @@ + + + + + rectangular + 5 3 4 + -10. -5. 0. + 10. 4. 9. + + + + + + + scatter-P3 nu-fission + + + + + fission absorption total flux + + + \ No newline at end of file diff --git a/tests/test_sourcepoint_restart/test_sourcepoint_restart.py b/tests/test_sourcepoint_restart/test_sourcepoint_restart.py new file mode 100644 index 000000000..a4387405b --- /dev/null +++ b/tests/test_sourcepoint_restart/test_sourcepoint_restart.py @@ -0,0 +1,133 @@ +#!/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) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +def test_created_statepoint(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + assert len(statepoint) == 1 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + sourcepoint = glob.glob(pwd + '/source.7.*') + assert len(sourcepoint) == 1 + assert sourcepoint[0].endswith('binary') or sourcepoint[0].endswith('h5') + +def test_results(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + 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 test_restart_form1(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + openmc_path = pwd + '/../../src/openmc' + if int(NoseMPI.mpi_np) > 0: + proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path, + '-r', statepoint[0]], + stderr=STDOUT, stdout=PIPE) + else: + proc = Popen([openmc_path, '-r', statepoint[0]], stderr=STDOUT, stdout=PIPE) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +def test_created_statepoint_form1(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + assert len(statepoint) == 1 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + sourcepoint = glob.glob(pwd + '/source.7.*') + assert len(sourcepoint) == 1 + assert sourcepoint[0].endswith('binary') or sourcepoint[0].endswith('h5') + +def test_results_form1(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + 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 test_restart_form2(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + sourcepoint = glob.glob(pwd + '/source.7.*') + openmc_path = pwd + '/../../src/openmc' + if int(NoseMPI.mpi_np) > 0: + proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path, + '--restart', statepoint[0], source[0]], + stderr=STDOUT, stdout=PIPE) + else: + proc = Popen([openmc_path, '--restart', statepoint[0]], + stderr=STDOUT, stdout=PIPE) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +def test_created_statepoint_form2(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + assert len(statepoint) == 1 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + sourcepoint = glob.glob(pwd + '/source.7.*') + assert len(sourcepoint) == 1 + assert sourcepoint[0].endswith('binary') or sourcepoint[0].endswith('h5') + +def test_results_form2(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + 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 test_restart_serial(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + openmc_path = pwd + '/../../src/openmc' + proc = Popen([openmc_path, '--restart', statepoint[0]], + stderr=STDOUT, stdout=PIPE) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +def test_created_statepoint_serial(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + assert len(statepoint) == 1 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + sourcepoint = glob.glob(pwd + '/source.7.*') + assert len(sourcepoint) == 1 + assert sourcepoint[0].endswith('binary') or sourcepoint[0].endswith('h5') + +def test_results_serial(): + statepoint = glob.glob(pwd + '/statepoint.7.*') + 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.7.*') + output += glob.glob(pwd + '/sourcepoint.7.*') + output.append(pwd + '/results_test.dat') + for f in output: + if os.path.exists(f): + os.remove(f) From a3726724ecf9d287707d87d7863c84dbf5f3ef13 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 8 Jan 2014 23:15:59 -0500 Subject: [PATCH 133/192] Ability to generate PDF documentation. --- .gitignore | 1 + docs/Makefile | 20 +++++++++++++++--- docs/{img => source/_images}/3dcore.png | Bin docs/{img => source/_images}/3dgeomplot.png | Bin docs/{img => source/_images}/Tracks.png | Bin docs/{img => source/_images}/atr.png | Bin docs/{img => source/_images}/fluxplot.png | Bin docs/{img => source/_images}/fork.png | Bin docs/{img => source/_images}/halfspace.svg | 0 docs/{img => source/_images}/master-slave.png | Bin .../_images}/nearest-neighbor-example.png | Bin .../_images}/nearest-neighbor.png | Bin docs/{img => source/_images}/openmc.png | Bin .../{img => source/_images}/plotmeshtally.png | Bin docs/{img => source/_images}/pullrequest.png | Bin docs/{img => source/_images}/union.svg | 0 docs/{img => source/_images}/uniongrid.svg | 0 docs/source/conf.py | 4 +++- docs/source/devguide/workflow.rst | 4 ++-- docs/source/methods/cross_sections.rst | 2 +- docs/source/methods/geometry.rst | 4 ++-- docs/source/methods/parallelization.rst | 6 +++--- docs/source/usersguide/processing.rst | 12 +++++------ 23 files changed, 35 insertions(+), 18 deletions(-) rename docs/{img => source/_images}/3dcore.png (100%) rename docs/{img => source/_images}/3dgeomplot.png (100%) rename docs/{img => source/_images}/Tracks.png (100%) rename docs/{img => source/_images}/atr.png (100%) rename docs/{img => source/_images}/fluxplot.png (100%) rename docs/{img => source/_images}/fork.png (100%) rename docs/{img => source/_images}/halfspace.svg (100%) rename docs/{img => source/_images}/master-slave.png (100%) rename docs/{img => source/_images}/nearest-neighbor-example.png (100%) rename docs/{img => source/_images}/nearest-neighbor.png (100%) rename docs/{img => source/_images}/openmc.png (100%) rename docs/{img => source/_images}/plotmeshtally.png (100%) rename docs/{img => source/_images}/pullrequest.png (100%) rename docs/{img => source/_images}/union.svg (100%) rename docs/{img => source/_images}/uniongrid.svg (100%) diff --git a/.gitignore b/.gitignore index 023c4f2c1..9f720f989 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ src/openmc # Documentation builds docs/build +docs/source/_images/*.pdf # xml-fortran reader src/xml-fortran/xmlreader diff --git a/docs/Makefile b/docs/Makefile index 8909256f3..89c71dc07 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -6,13 +6,19 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build +IMAGEDIR = source/_images # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest +# SVG to PDF conversion +SVG2PDF = inkscape +PDFS = $(patsubst %.svg,%.pdf,$(wildcard $(IMAGEDIR)/*.svg)) + + +.PHONY: help images clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @@ -33,8 +39,16 @@ help: @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" +# Pattern rule for converting SVG to PDF +%.pdf: %.svg + $(SVG2PDF) -f $< -A $@ + +# Rule to build PDFs +images: $(PDFS) + clean: -rm -rf $(BUILDDIR)/* + -rm $(PDFS) html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @@ -91,14 +105,14 @@ epub: @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." -latex: +latex: images $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." -latexpdf: +latexpdf: images $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf diff --git a/docs/img/3dcore.png b/docs/source/_images/3dcore.png similarity index 100% rename from docs/img/3dcore.png rename to docs/source/_images/3dcore.png diff --git a/docs/img/3dgeomplot.png b/docs/source/_images/3dgeomplot.png similarity index 100% rename from docs/img/3dgeomplot.png rename to docs/source/_images/3dgeomplot.png diff --git a/docs/img/Tracks.png b/docs/source/_images/Tracks.png similarity index 100% rename from docs/img/Tracks.png rename to docs/source/_images/Tracks.png diff --git a/docs/img/atr.png b/docs/source/_images/atr.png similarity index 100% rename from docs/img/atr.png rename to docs/source/_images/atr.png diff --git a/docs/img/fluxplot.png b/docs/source/_images/fluxplot.png similarity index 100% rename from docs/img/fluxplot.png rename to docs/source/_images/fluxplot.png diff --git a/docs/img/fork.png b/docs/source/_images/fork.png similarity index 100% rename from docs/img/fork.png rename to docs/source/_images/fork.png diff --git a/docs/img/halfspace.svg b/docs/source/_images/halfspace.svg similarity index 100% rename from docs/img/halfspace.svg rename to docs/source/_images/halfspace.svg diff --git a/docs/img/master-slave.png b/docs/source/_images/master-slave.png similarity index 100% rename from docs/img/master-slave.png rename to docs/source/_images/master-slave.png diff --git a/docs/img/nearest-neighbor-example.png b/docs/source/_images/nearest-neighbor-example.png similarity index 100% rename from docs/img/nearest-neighbor-example.png rename to docs/source/_images/nearest-neighbor-example.png diff --git a/docs/img/nearest-neighbor.png b/docs/source/_images/nearest-neighbor.png similarity index 100% rename from docs/img/nearest-neighbor.png rename to docs/source/_images/nearest-neighbor.png diff --git a/docs/img/openmc.png b/docs/source/_images/openmc.png similarity index 100% rename from docs/img/openmc.png rename to docs/source/_images/openmc.png diff --git a/docs/img/plotmeshtally.png b/docs/source/_images/plotmeshtally.png similarity index 100% rename from docs/img/plotmeshtally.png rename to docs/source/_images/plotmeshtally.png diff --git a/docs/img/pullrequest.png b/docs/source/_images/pullrequest.png similarity index 100% rename from docs/img/pullrequest.png rename to docs/source/_images/pullrequest.png diff --git a/docs/img/union.svg b/docs/source/_images/union.svg similarity index 100% rename from docs/img/union.svg rename to docs/source/_images/union.svg diff --git a/docs/img/uniongrid.svg b/docs/source/_images/uniongrid.svg similarity index 100% rename from docs/img/uniongrid.svg rename to docs/source/_images/uniongrid.svg diff --git a/docs/source/conf.py b/docs/source/conf.py index 8752b4898..24c26bcd8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -121,7 +121,7 @@ html_title = "OpenMC Documentation" # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = '../img/openmc.png' +html_logo = '_images/openmc.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 @@ -188,6 +188,8 @@ latex_documents = [ u'Massachusetts Institute of Technology', 'manual'), ] +latex_elements = {'preamble': '\\usepackage{enumitem}\\setlistdepth{9}'} + # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index 9562c9584..f3793628a 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -60,7 +60,7 @@ features and bug fixes. The general steps for contributing are as follows: repository with the same name under your personal account. As such, you can commit to it as you please without disrupting other developers. - .. image:: ../../img/fork.png + .. image:: ../_images/fork.png 2. Clone your fork of OpenMC and create a branch that branches off of *develop*: @@ -77,7 +77,7 @@ features and bug fixes. The general steps for contributing are as follows: 4. Issue a pull request from GitHub and select the *develop* branch of mit-crpg/openmc as the target. - .. image:: ../../img/pullrequest.png + .. image:: ../_images/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 diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index c564896c7..a193dde96 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -80,7 +80,7 @@ dashed box would need to be stored on a per-nuclide basis, and the union grid would need to be stored once. This method is also referred to as *double indexing* and is available as an option in Serpent (see paper by Leppanen_). -.. figure:: ../../img/uniongrid.svg +.. figure:: ../_images/uniongrid.* :width: 600px :align: center :figclass: align-center diff --git a/docs/source/methods/geometry.rst b/docs/source/methods/geometry.rst index d4824f5d1..1515ffa89 100644 --- a/docs/source/methods/geometry.rst +++ b/docs/source/methods/geometry.rst @@ -45,7 +45,7 @@ surface by a combination of the unique ID of the surface and a positive/negative sign. The following illustration shows an example of an ellipse with unique ID 1 dividing space into two half-spaces. -.. figure:: ../../img/halfspace.svg +.. figure:: ../_images/halfspace.* :align: center :figclass: align-center @@ -60,7 +60,7 @@ half-space references whose intersection defines the region. The region is then assigned a material defined elsewhere. The following illustration shows an example of a cell defined as the intersection of an ellipse and two planes. -.. figure:: ../../img/union.svg +.. figure:: ../_images/union.* :align: center :figclass: align-center diff --git a/docs/source/methods/parallelization.rst b/docs/source/methods/parallelization.rst index 7f0b9bd6e..66ead52a2 100644 --- a/docs/source/methods/parallelization.rst +++ b/docs/source/methods/parallelization.rst @@ -65,7 +65,7 @@ in the case of an eigenvalue calculation). This idea is illustrated in .. _figure-master-slave: -.. figure:: ../../img/master-slave.png +.. figure:: ../_images/master-slave.png :align: center :figclass: align-center @@ -122,7 +122,7 @@ needed. This concept is illustrated in :ref:`Figure 2 .. _figure-nearest-neighbor: -.. figure:: ../../img/nearest-neighbor.png +.. figure:: ../_images/nearest-neighbor.png :align: center :figclass: align-center @@ -203,7 +203,7 @@ communicated between adjacent nodes. .. _figure-neighbor-example: -.. figure:: ../../img/nearest-neighbor-example.png +.. figure:: ../_images/nearest-neighbor-example.png :align: center :figclass: align-center diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index 043041ef4..fc426ba2f 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -38,7 +38,7 @@ running OpenMC with the -plot or -p command-line option (See Plotting in 2D -------------- -.. image:: ../../img/atr.png +.. image:: ../_images/atr.png :height: 200px After running OpenMC to obtain PPM files, images should be saved to another @@ -58,7 +58,7 @@ Ubuntu: ``sudo apt-get install imagemagick``). Images are then converted like: Plotting in 3D -------------- -.. image:: ../../img/3dgeomplot.png +.. image:: ../_images/3dgeomplot.png :height: 200px The binary VOXEL files output by OpenMC can not be viewed directly by any @@ -162,7 +162,7 @@ tasks will be described here in the following sections. Plotting in 2D -------------- -.. image:: ../../img/plotmeshtally.png +.. image:: ../_images/plotmeshtally.png :height: 200px For simple viewing of 2D slices of a mesh plot, the utility plot_mesh_tally.py @@ -170,7 +170,7 @@ is provided. This utility provides an interactive GUI to explore and plot mesh tallies for any scores and filter bins. It requires statepoint.py, as well as `PyQt `_. -.. image:: ../../img/fluxplot.png +.. image:: ../_images/fluxplot.png :height: 200px Alternatively, the user can write their own Python script to manipulate the data @@ -249,7 +249,7 @@ two heatmaps in the previous figure. Plotting in 3D -------------- -.. image:: ../../img/3dcore.png +.. image:: ../_images/3dcore.png :height: 200px As with 3D plots of the geometry, meshtally data needs to be put into a standard @@ -357,7 +357,7 @@ and dumped to MATLAB in one step. Particle Track Visualization ---------------------------- -.. image:: ../../img/Tracks.png +.. image:: ../_images/Tracks.png :height: 200px OpenMC can dump particle tracks—the position of particles as they are From c439c3dc5d533c74ca7df25759009eb89d81af9a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 14 Jan 2014 20:34:29 -0500 Subject: [PATCH 134/192] Change OPENMP macro to _OPENMP which is automatically defined when compiling with OpenMP. --- src/Makefile | 6 +++--- src/eigenvalue.F90 | 4 ++-- src/global.F90 | 6 +++--- src/initialize.F90 | 6 +++--- src/input_xml.F90 | 2 +- src/output.F90 | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Makefile b/src/Makefile index 03c370e07..00abca489 100644 --- a/src/Makefile +++ b/src/Makefile @@ -181,17 +181,17 @@ endif ifeq ($(OPENMP),yes) ifeq ($(COMPILER),intel) - F90FLAGS += -openmp -DOPENMP + F90FLAGS += -openmp LDFLAGS += -openmp endif ifeq ($(COMPILER),gnu) - F90FLAGS += -fopenmp -DOPENMP + F90FLAGS += -fopenmp LDFLAGS += -fopenmp endif ifeq ($(COMPILER),ibm) - F90FLAGS += -qsmp=omp -WF,-DOPENMP + F90FLAGS += -qsmp=omp LDFLAGS += -qsmp=omp endif endif diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index f5375bf3a..4047f815f 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -166,7 +166,7 @@ contains subroutine finalize_generation() -#ifdef OPENMP +#ifdef _OPENMP ! Join the fission bank from each thread into one global fission bank call join_bank_from_threads() #endif @@ -821,7 +821,7 @@ contains end subroutine replay_batch_history -#ifdef OPENMP +#ifdef _OPENMP !=============================================================================== ! JOIN_BANK_FROM_THREADS !=============================================================================== diff --git a/src/global.F90 b/src/global.F90 index 637ea315e..c3dbf0aa2 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -158,7 +158,7 @@ module global ! Source and fission bank type(Bank), allocatable, target :: source_bank(:) type(Bank), allocatable, target :: fission_bank(:) -#ifdef OPENMP +#ifdef _OPENMP type(Bank), allocatable, target :: master_fission_bank(:) #endif integer(8) :: n_bank ! # of sites in fission bank @@ -205,7 +205,7 @@ module global integer :: MPI_BANK ! MPI datatype for fission bank integer :: MPI_TALLYRESULT ! MPI datatype for TallyResult -#ifdef OPENMP +#ifdef _OPENMP integer :: n_threads = NONE ! number of OpenMP threads integer :: thread_id ! ID of a given thread #endif @@ -438,7 +438,7 @@ contains !$omp parallel if (allocated(fission_bank)) deallocate(fission_bank) !$omp end parallel -#ifdef OPENMP +#ifdef _OPENMP if (allocated(master_fission_bank)) deallocate(master_fission_bank) #endif if (allocated(source_bank)) deallocate(source_bank) diff --git a/src/initialize.F90 b/src/initialize.F90 index b75fa20ef..9f495e4ac 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -26,7 +26,7 @@ module initialize use mpi #endif -#ifdef OPENMP +#ifdef _OPENMP use omp_lib #endif @@ -372,7 +372,7 @@ contains ! Read number of threads i = i + 1 -#ifdef OPENMP +#ifdef _OPENMP ! Read and set number of OpenMP threads n_threads = str_to_int(argv(i)) if (n_threads < 1) then @@ -830,7 +830,7 @@ contains call fatal_error() end if -#ifdef OPENMP +#ifdef _OPENMP ! If OpenMP is being used, each thread needs its own private fission ! bank. Since the private fission banks need to be combined at the end of a ! generation, there is also a 'master_fission_bank' that is used to collect diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 3ce3c9d0d..ae4e4b1b2 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -228,7 +228,7 @@ contains ! Number of OpenMP threads if (check_for_node(doc, "threads")) then -#ifdef OPENMP +#ifdef _OPENMP if (n_threads == NONE) then call get_node_value(doc, "threads", n_threads) if (n_threads < 1) then diff --git a/src/output.F90 b/src/output.F90 index 677c35396..3e31ad718 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -31,7 +31,7 @@ contains subroutine title() -#ifdef OPENMP +#ifdef _OPENMP use omp_lib #endif @@ -69,7 +69,7 @@ contains trim(to_str(n_procs)) #endif -#ifdef OPENMP +#ifdef _OPENMP ! Write number of OpenMP threads write(UNIT=OUTPUT_UNIT, FMT='(6X,"OpenMP Threads:",1X,A)') & trim(to_str(omp_get_max_threads())) From b142ff6458d4515a2e4f9d72ae0b7ee3bfb67da6 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 14 Jan 2014 21:26:40 -0500 Subject: [PATCH 135/192] Move calculation of number of threads outside of .omp parallel. --- src/initialize.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/initialize.F90 b/src/initialize.F90 index 9f495e4ac..c1fbf40cd 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -836,8 +836,9 @@ contains ! generation, there is also a 'master_fission_bank' that is used to collect ! the sites from each thread. + n_threads = omp_get_max_threads() + !$omp parallel - n_threads = omp_get_num_threads() thread_id = omp_get_thread_num() if (thread_id == 0) then From cbf37e0916c6f878b3af07f8a89f3dd13570ca0e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 14 Jan 2014 21:44:40 -0500 Subject: [PATCH 136/192] Happy new year 2014! --- LICENSE | 2 +- docs/source/conf.py | 2 +- docs/source/license.rst | 2 +- man/man1/openmc.1 | 2 +- src/output.F90 | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/LICENSE b/LICENSE index f8d9cd7c0..0b9c35137 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011-2013 Massachusetts Institute of Technology +Copyright (c) 2011-2014 Massachusetts Institute of Technology Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/docs/source/conf.py b/docs/source/conf.py index 24c26bcd8..7fb622631 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -39,7 +39,7 @@ master_doc = 'index' # General information about the project. project = u'OpenMC' -copyright = u'2011-2013, Massachusetts Institute of Technology' +copyright = u'2011-2014, Massachusetts Institute of Technology' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/source/license.rst b/docs/source/license.rst index a7d7f2976..e7f4b3a69 100644 --- a/docs/source/license.rst +++ b/docs/source/license.rst @@ -4,7 +4,7 @@ License Agreement ================= -Copyright © 2011-2013 Massachusetts Institute of Technology +Copyright © 2011-2014 Massachusetts Institute of Technology Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/man/man1/openmc.1 b/man/man1/openmc.1 index cac8d5908..db91d7a8f 100644 --- a/man/man1/openmc.1 +++ b/man/man1/openmc.1 @@ -46,7 +46,7 @@ to locate ACE format cross section libraries if the user has not specified the tag in .I settings.xml\fP. .SH LICENSE -Copyright \(co 2011-2013 Massachusetts Institute of Technology. +Copyright \(co 2011-2014 Massachusetts Institute of Technology. .PP Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/src/output.F90 b/src/output.F90 index 3e31ad718..ee0a8391c 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -50,7 +50,7 @@ contains ! Write version information write(UNIT=OUTPUT_UNIT, FMT=*) & - ' Copyright: 2011-2013 Massachusetts Institute of Technology' + ' Copyright: 2011-2014 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:",8X,I1,".",I1,".",I1)') & From 55d257351c7b731aec57b415b3270d0e205f30f3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Jan 2014 19:00:12 -0500 Subject: [PATCH 137/192] Converted plot_mesh_tally.py to use Tkinter rather than PyQt. --- src/utils/plot_mesh_tally.py | 512 +++++++++++++++-------------------- 1 file changed, 213 insertions(+), 299 deletions(-) diff --git a/src/utils/plot_mesh_tally.py b/src/utils/plot_mesh_tally.py index c70b29165..d22e0e0b4 100755 --- a/src/utils/plot_mesh_tally.py +++ b/src/utils/plot_mesh_tally.py @@ -1,269 +1,241 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python -'''Python script to plot tally data generated by OpenMC.''' +"""Python script to plot tally data generated by OpenMC.""" +import os import sys -from statepoint import * -# Color intensity dependent on individual score? - -from PyQt4.QtCore import * -from PyQt4.QtGui import * -import matplotlib.pyplot as plt +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigureCanvas +from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg as NavigationToolbar from matplotlib.figure import Figure -from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar +import matplotlib.pyplot as plt import numpy as np -class AppForm(QMainWindow): - def __init__(self, argv, parent=None): - QMainWindow.__init__(self, parent) +from statepoint import * + +if sys.version_info[0] < 3: + import Tkinter as tk +else: + import tkinter as tk +import tkFileDialog +import tkFont +import tkMessageBox +import ttk + +class MeshPlotter(tk.Frame): + def __init__(self, parent, filename): + tk.Frame.__init__(self, parent) + + self.labels = {'cell': 'Cell:', 'cellborn': 'Cell born:', + 'surface': 'Surface:', 'material': 'Material:', + 'universe': 'Universe:', 'energyin': 'Energy in:', + 'energyout': 'Energy out:'} + + self.filterBoxes = {} # Read data from source or leakage fraction file - 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 + self.get_file_data(filename) + # Set up top-level window + top = self.winfo_toplevel() + top.title('Mesh Tally Plotter: ' + filename) + top.rowconfigure(0, weight=1) + top.columnconfigure(0, weight=1) + self.grid(sticky=tk.W+tk.N) - if self.all_good: - # Set maximum colorbar value by maximum tally data value - self.maxvalue = self.datafile.tallies[0].results.max() + # Create widgets and draw to screen + self.create_widgets() + self.update() - self.main_frame = QWidget() - self.setCentralWidget(self.main_frame) + def create_widgets(self): + figureFrame = tk.Frame(self) + figureFrame.grid(row=0, column=0) - # 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 Figure and Canvas + self.dpi = 100 + self.fig = Figure((5.0, 5.0), dpi=self.dpi) + self.canvas = FigureCanvas(self.fig, master=figureFrame) + self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) - # Create the navigation toolbar, tied to the canvas - self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) + # Create the navigation toolbar, tied to the canvas + self.mpl_toolbar = NavigationToolbar(self.canvas, figureFrame) + self.mpl_toolbar.update() + self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1) - # Grid layout at bottom - self.grid = QGridLayout() + # Create frame for comboboxes + self.selectFrame = tk.Frame(self) + self.selectFrame.grid(row=1, column=0, sticky=tk.W+tk.E) - # 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) + # Tally selection + labelTally = tk.Label(self.selectFrame, text='Tally:') + labelTally.grid(row=0, column=0, sticky=tk.W) + self.tallyBox = ttk.Combobox(self.selectFrame, state='readonly') + self.tallyBox['values'] = [self.datafile.tallies[i].id + for i in self.meshTallies] + self.tallyBox.current(0) + self.tallyBox.grid(row=0, column=1, sticky=tk.W+tk.E) + self.tallyBox.bind('<>', self.update) - # 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) + # Planar basis selection + labelBasis = tk.Label(self.selectFrame, text='Basis:') + labelBasis.grid(row=1, column=0, sticky=tk.W) + self.basisBox = ttk.Combobox(self.selectFrame, state='readonly') + self.basisBox['values'] = ('xy', 'yz', 'xz') + self.basisBox.current(0) + self.basisBox.grid(row=1, column=1, sticky=tk.W+tk.E) + self.basisBox.bind('<>', self.update) - # Planar basis - label_basis = QLabel("Basis:") - self.basis = QComboBox() - self.basis.addItems(['xy', 'yz', 'xz']) + # Axial level + labelAxial = tk.Label(self.selectFrame, text='Axial level:') + labelAxial.grid(row=2, column=0, sticky=tk.W) + self.axialBox = ttk.Combobox(self.selectFrame, state='readonly') + self.axialBox.grid(row=2, column=1, sticky=tk.W+tk.E) + self.axialBox.bind('<>', self.redraw) - # 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) + # Option for mean/uncertainty + labelMean = tk.Label(self.selectFrame, text='Mean/Uncertainty:') + labelMean.grid(row=3, column=0, sticky=tk.W) + self.meanBox = ttk.Combobox(self.selectFrame, state='readonly') + self.meanBox['values'] = ('Mean', 'Absolute uncertainty', + 'Relative uncertainty') + self.meanBox.current(0) + self.meanBox.grid(row=3, column=1, sticky=tk.W+tk.E) + self.meanBox.bind('<>', self.update) - # 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) + # Scores + labelScore = tk.Label(self.selectFrame, text='Score:') + labelScore.grid(row=4, column=0, sticky=tk.W) + self.scoreBox = ttk.Combobox(self.selectFrame, state='readonly') + self.scoreBox.grid(row=4, column=1, sticky=tk.W+tk.E) + self.scoreBox.bind('<>', self.redraw) - # Add Option to plot mean or uncertainty - label_mean = QLabel("Mean or Uncertainty:") - self.mean = QComboBox() - self.mean.addItems(['Mean','Absolute Uncertainty', - 'Relative Uncertainty']) + # Filter label + font = tkFont.Font(weight='bold') + labelFilters = tk.Label(self.selectFrame, text='Filters:', font=font) + labelFilters.grid(row=5, column=0, sticky=tk.W) - # 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 - if cl_file is None: - filename = QFileDialog.getOpenFileName(self, - 'Select statepoint file', '.') + def update(self, event=None): + if not event: + widget = None else: - filename = cl_file + widget = event.widget - # Create StatePoint object and read in data - self.datafile = StatePoint(str(filename)) - self.datafile.read_results() - self.datafile.generate_stdev() + tally_id = self.meshTallies[self.tallyBox.current()] + selectedTally = self.datafile.tallies[tally_id] - self.setWindowTitle('Core Map Tool : ' + str(self.datafile.path)) + # Get mesh for selected tally + self.mesh = self.datafile.meshes[selectedTally.filters['mesh'].bins[0] - 1] - self.labelList = [] + # Get mesh dimensions + self.nx, self.ny, self.nz = self.mesh.dimension - # Read mesh dimensions - # for mesh in self.datafile.meshes: - # self.nx, self.ny, self.nz = mesh.dimension + # Repopulate comboboxes baesd on current basis selection + text = self.basisBox['values'][self.basisBox.current()] + if text == 'xy': + self.axialBox['values'] = [str(i+1) for i in range(self.nz)] + elif text == 'yz': + self.axialBox['values'] = [str(i+1) for i in range(self.nx)] + else: + self.axialBox['values'] = [str(i+1) for i in range(self.ny)] + self.axialBox.current(0) - # 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 = [] - 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) + # If update() was called by a change in the basis combobox, we don't + # need to repopulate the filters + if widget == self.basisBox: + self.redraw() + return - def on_draw(self): - """ Redraws the figure - """ + # Update scores + self.scoreBox['values'] = selectedTally.scores # self.tally_scores[self.tallyBox.current()] + self.scoreBox.current(0) -# print 'Calling on_draw...' - # Get selected basis, axial_level and stage - basis = self.basis.currentIndex() + 1 - axial_level = self.axial_level.currentIndex() + 1 - is_mean = self.mean.currentIndex() + # Remove any filter labels/comboboxes that exist + for row in range(6, self.selectFrame.grid_size()[1]): + for w in self.selectFrame.grid_slaves(row=row): + w.grid_forget() + w.destroy() - # get current tally index - tally_id = self.tally_ids[self.tally.currentIndex()] + # create a label/combobox for each filter in selected tally + count = 0 + for filterType in selectedTally.filters: + if filterType == 'mesh': + continue + count += 1 + + # Create label and combobox for this filter + label = tk.Label(self.selectFrame, text=self.labels[filterType]) + label.grid(row=count+6, column=0, sticky=tk.W) + combobox = ttk.Combobox(self.selectFrame, state='readonly') + self.filterBoxes[filterType] = combobox + + # Set combobox items + f = selectedTally.filters[filterType] + if filterType in ['energyin', 'energyout']: + combobox['values'] = ['{0} to {1}'.format(*f.bins[i:i+2]) + for i in range(f.length)] + else: + combobox['values'] = [str(i) for i in f.bins] + + combobox.current(0) + combobox.grid(row=count+6, column=1, sticky=tk.W+tk.E) + combobox.bind('<>', self.redraw) + + self.redraw() + + def redraw(self, event=None): + basis = self.basisBox.current() + 1 + axial_level = self.axialBox.current() + 1 + is_mean = self.meanBox.current() + + # Get selected tally + tally_id = self.meshTallies[self.tallyBox.current()] + selectedTally = self.datafile.tallies[tally_id] # Create spec_list spec_list = [] - for tally in self.datafile.tallies[tally_id].filters.values(): - if tally.type == 'mesh': + for f in selectedTally.filters.values(): + if f.type == 'mesh': continue - index = self.boxes[tally.type].currentIndex() - spec_list.append((tally.type, index)) + index = self.filterBoxes[f.type].current() + spec_list.append((f.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': + text = self.basisBox['values'][self.basisBox.current()] + if text == '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(tally_id, spec_list + [('mesh', (i + 1, j + 1, axial_level))], - self.scoreBox.currentIndex())[score_loc] - # Calculate relative uncertainty from absolute, if - # requested + self.scoreBox.current())[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(tally_id, spec_list + [('mesh', (i + 1, j + 1, axial_level))], - self.scoreBox.currentIndex())[0] + self.scoreBox.current())[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': + elif text == '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(tally_id, spec_list + [('mesh', (axial_level, i + 1, j + 1))], - self.scoreBox.currentIndex())[score_loc] - # Calculate relative uncertainty from absolute, if - # requested + self.scoreBox.current())[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(tally_id, spec_list + [('mesh', (axial_level, i + 1, j + 1))], - self.scoreBox.currentIndex())[0] + self.scoreBox.current())[0] if mean_val > 0.0: matrix[i,j] = matrix[i,j] / mean_val else: @@ -275,20 +247,18 @@ class AppForm(QMainWindow): for j in range(self.nz): 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 + self.scoreBox.current())[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(tally_id, spec_list + [('mesh', (i + 1, axial_level, j + 1))], - self.scoreBox.currentIndex())[0] + self.scoreBox.current())[0] if mean_val > 0.0: matrix[i,j] = matrix[i,j] / mean_val else: matrix[i,j] = 0.0 -# print spec_list # Clear the figure self.fig.clear() @@ -296,7 +266,7 @@ class AppForm(QMainWindow): # Make figure, set up color bar self.axes = self.fig.add_subplot(111) cax = self.axes.imshow(matrix.transpose(), vmin=0.0, vmax=matrix.max(), - interpolation="nearest", origin='lower') + interpolation='none', origin='lower') self.fig.colorbar(cax) self.axes.set_xticks([]) @@ -305,100 +275,44 @@ class AppForm(QMainWindow): # Draw canvas self.canvas.draw() + + def get_file_data(self, filename): + # Create StatePoint object and read in data + self.datafile = StatePoint(filename) + self.datafile.read_results() + self.datafile.generate_stdev() - def _update(self): - '''Updates widget to display new relevant comboboxes and figure data - ''' -# print 'Calling _update...' + # Find which tallies are mesh tallies + self.meshTallies = [] + for itally, tally in enumerate(self.datafile.tallies): + if 'mesh' in tally.filters: + self.meshTallies.append(itally) - # get current tally index - tally_id = self.tally_ids[self.tally.currentIndex()] + if not self.meshTallies: + tkMessageBox.showerror("Invalid StatePoint File", + "File does not contain mesh tallies!") + sys.exit(1) + - self.mesh = self.datafile.meshes[ - self.datafile.tallies[tally_id].filters['mesh'].bins[0] - 1] +if __name__ == '__main__': + # Hide root window + root = tk.Tk() + root.withdraw() - self.nx, self.ny, self.nz = self.mesh.dimension + # If no filename given as command-line argument, open file dialog + if len(sys.argv) < 2: + filename = tkFileDialog.askopenfilename(title='Select statepoint file', + initialdir='.') + else: + filename = sys.argv[1] - # Clear axial level combobox - self.axial_level.clear() + if filename: + # Check to make sure file exists + if not os.path.isfile(filename): + tkMessageBox.showerror("File not found", + "Could not find regular file: " + filename) + sys.exit(1) - # Repopulate axial level combobox based on current basis selection - if (self.basis.currentText() == 'xy'): - self.axial_level.addItems([str(i+1) for i in range(self.nz)]) - elif (self.basis.currentText() == 'yz'): - self.axial_level.addItems([str(i+1) for i in range(self.nx)]) - else: - 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[tally_id].results.max() -# print self.maxvalue - - # Clear and hide old filter labels - for item in self.labelList: - item.clear() - - # Clear and hide old filter boxes - for j in self.labels: - self.boxes[j].clear() - self.boxes[j].setParent(None) - - self.update() - - 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: ', - 'surface': 'Surface: ', - 'material': 'Material: ', - 'universe': 'Universe: '} - - # 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[tally_id].filters[element] - if element == 'mesh': - continue - - label = QLabel(self.labels[element]) - self.labelList.append(label) - combobox = self.boxes[element] - self.grid.addWidget(label, n, 0) - self.grid.addWidget(combobox, n, 1) - n += 1 - -# print element - if element in ['cell', 'cellborn', 'surface', 'material', 'universe']: - combobox.addItems([str(i) for i in nextFilter.bins]) -# for i in nextFilter.bins: -# print i - - elif element == 'energyin' or element == 'energyout': - for i in range(nextFilter.length): - text = (str(nextFilter.bins[i]) + ' to ' + - str(nextFilter.bins[i+1])) - combobox.addItem(text) - - self.scoreBox.clear() - for item in self.tally_scores[self.tally.currentIndex()]: - self.scoreBox.addItem(str(item)) - self.grid.addWidget(self.score_label, n, 0) - self.grid.addWidget(self.scoreBox, n, 1) - - - -def main(): - app = QApplication(sys.argv) - form = AppForm(app.arguments()) - if form.all_good: - form.show() - app.exec_() - - -if __name__ == "__main__": - main() + app = MeshPlotter(root, filename) + root.deiconify() + root.mainloop() From 39a97d6be5dd34fc4a87123f89c891412e1e5e54 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Jan 2014 19:49:44 -0500 Subject: [PATCH 138/192] Some PEP8 fixes. --- src/utils/plot_mesh_tally.py | 39 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/utils/plot_mesh_tally.py b/src/utils/plot_mesh_tally.py index d22e0e0b4..fcd661375 100755 --- a/src/utils/plot_mesh_tally.py +++ b/src/utils/plot_mesh_tally.py @@ -5,8 +5,8 @@ import os import sys -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigureCanvas -from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg as NavigationToolbar +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg from matplotlib.figure import Figure import matplotlib.pyplot as plt import numpy as np @@ -22,6 +22,7 @@ import tkFont import tkMessageBox import ttk + class MeshPlotter(tk.Frame): def __init__(self, parent, filename): tk.Frame.__init__(self, parent) @@ -54,11 +55,11 @@ class MeshPlotter(tk.Frame): # Create the Figure and Canvas self.dpi = 100 self.fig = Figure((5.0, 5.0), dpi=self.dpi) - self.canvas = FigureCanvas(self.fig, master=figureFrame) + self.canvas = FigureCanvasTkAgg(self.fig, master=figureFrame) self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) # Create the navigation toolbar, tied to the canvas - self.mpl_toolbar = NavigationToolbar(self.canvas, figureFrame) + self.mpl_toolbar = NavigationToolbar2TkAgg(self.canvas, figureFrame) self.mpl_toolbar.update() self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1) @@ -96,7 +97,7 @@ class MeshPlotter(tk.Frame): labelMean = tk.Label(self.selectFrame, text='Mean/Uncertainty:') labelMean.grid(row=3, column=0, sticky=tk.W) self.meanBox = ttk.Combobox(self.selectFrame, state='readonly') - self.meanBox['values'] = ('Mean', 'Absolute uncertainty', + self.meanBox['values'] = ('Mean', 'Absolute uncertainty', 'Relative uncertainty') self.meanBox.current(0) self.meanBox.grid(row=3, column=1, sticky=tk.W+tk.E) @@ -124,7 +125,8 @@ class MeshPlotter(tk.Frame): selectedTally = self.datafile.tallies[tally_id] # Get mesh for selected tally - self.mesh = self.datafile.meshes[selectedTally.filters['mesh'].bins[0] - 1] + self.mesh = self.datafile.meshes[ + selectedTally.filters['mesh'].bins[0] - 1] # Get mesh dimensions self.nx, self.ny, self.nz = self.mesh.dimension @@ -146,7 +148,7 @@ class MeshPlotter(tk.Frame): return # Update scores - self.scoreBox['values'] = selectedTally.scores # self.tally_scores[self.tallyBox.current()] + self.scoreBox['values'] = selectedTally.scores self.scoreBox.current(0) # Remove any filter labels/comboboxes that exist @@ -209,7 +211,7 @@ class MeshPlotter(tk.Frame): 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(tally_id, + matrix[i, j] = self.datafile.get_value(tally_id, spec_list + [('mesh', (i + 1, j + 1, axial_level))], self.scoreBox.current())[score_loc] # Calculate relative uncertainty from absolute, if requested @@ -219,15 +221,15 @@ class MeshPlotter(tk.Frame): spec_list + [('mesh', (i + 1, j + 1, axial_level))], self.scoreBox.current())[0] if mean_val > 0.0: - matrix[i,j] = matrix[i,j] / mean_val + matrix[i, j] = matrix[i, j] / mean_val else: - matrix[i,j] = 0.0 + matrix[i, j] = 0.0 elif text == '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(tally_id, + matrix[i, j] = self.datafile.get_value(tally_id, spec_list + [('mesh', (axial_level, i + 1, j + 1))], self.scoreBox.current())[score_loc] # Calculate relative uncertainty from absolute, if requested @@ -237,15 +239,15 @@ class MeshPlotter(tk.Frame): spec_list + [('mesh', (axial_level, i + 1, j + 1))], self.scoreBox.current())[0] if mean_val > 0.0: - matrix[i,j] = matrix[i,j] / mean_val + matrix[i, j] = matrix[i, j] / mean_val else: - matrix[i,j] = 0.0 + 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(tally_id, + matrix[i, j] = self.datafile.get_value(tally_id, spec_list + [('mesh', (i + 1, axial_level, j + 1))], self.scoreBox.current())[score_loc] # Calculate relative uncertainty from absolute, if requested @@ -255,10 +257,9 @@ class MeshPlotter(tk.Frame): spec_list + [('mesh', (i + 1, axial_level, j + 1))], self.scoreBox.current())[0] if mean_val > 0.0: - matrix[i,j] = matrix[i,j] / mean_val + matrix[i, j] = matrix[i, j] / mean_val else: - matrix[i,j] = 0.0 - + matrix[i, j] = 0.0 # Clear the figure self.fig.clear() @@ -275,7 +276,7 @@ class MeshPlotter(tk.Frame): # Draw canvas self.canvas.draw() - + def get_file_data(self, filename): # Create StatePoint object and read in data self.datafile = StatePoint(filename) @@ -292,7 +293,7 @@ class MeshPlotter(tk.Frame): tkMessageBox.showerror("Invalid StatePoint File", "File does not contain mesh tallies!") sys.exit(1) - + if __name__ == '__main__': # Hide root window From 65a25edf63b762a95608f859fbcbd886a19a5453 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Wed, 22 Jan 2014 14:54:10 -0500 Subject: [PATCH 139/192] Reverted to python2 and added None Available text when no Filters are available. --- src/utils/plot_mesh_tally.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/utils/plot_mesh_tally.py b/src/utils/plot_mesh_tally.py index fcd661375..fb5b8cb83 100755 --- a/src/utils/plot_mesh_tally.py +++ b/src/utils/plot_mesh_tally.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """Python script to plot tally data generated by OpenMC.""" @@ -182,6 +182,12 @@ class MeshPlotter(tk.Frame): combobox.grid(row=count+6, column=1, sticky=tk.W+tk.E) combobox.bind('<>', self.redraw) + # If There are no filters, leave a 'None available' message + if count == 0: + count += 1 + label = tk.Label(self.selectFrame, text="None Available") + label.grid(row=count+6, column=0, sticky=tk.W) + self.redraw() def redraw(self, event=None): From 7cf22c6c323258f03b758f1c05968160ca6a227c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 27 Jan 2014 20:36:31 -0500 Subject: [PATCH 140/192] Check for empty datapath in xsdir for conversion. --- src/utils/convert_xsdir.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils/convert_xsdir.py b/src/utils/convert_xsdir.py index 32315f1ed..edee1a66c 100755 --- a/src/utils/convert_xsdir.py +++ b/src/utils/convert_xsdir.py @@ -35,8 +35,12 @@ class Xsdir(object): words = line.split() if words: if words[0].lower().startswith('datapath'): - index = line.index('=') - self.datapath = line[index+1:].strip() + if '=' in words[0]: + index = line.index('=') + self.datapath = line[index+1:].strip() + else: + if len(line.strip()) > 8: + self.datapath = line[8:].strip() else: self.f.seek(0) From ad1235db60cf90bd39c38473908d126131659dce Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 27 Jan 2014 20:37:48 -0500 Subject: [PATCH 141/192] Update one publication in documentation. --- docs/source/publications.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/publications.rst b/docs/source/publications.rst index c17e652c7..df7ca639b 100644 --- a/docs/source/publications.rst +++ b/docs/source/publications.rst @@ -48,8 +48,8 @@ Publications - 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). ``_ + *Int. J. High Perform. Comput. Appl.*, **28** (1), 87--96 + (2014). ``_ - Paul K. Romano, Andrew R. Siegel, Benoit Forget, and Kord Smith, "Data decomposition of Monte Carlo particle transport simulations via tally From 854c86aa429d8c99612cec8ada56896fa3ada60a Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 31 Jan 2014 10:35:23 -0500 Subject: [PATCH 142/192] a custom source can now be read in from file --- src/source.F90 | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/source.F90 b/src/source.F90 index 231a23cc5..4ec358776 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -1,16 +1,17 @@ module source - use bank_header, only: Bank + use bank_header, only: Bank use constants - use error, only: fatal_error - use geometry, only: find_cell - use geometry_header, only: BASE_UNIVERSE + 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 - use output, only: write_message - use particle_header, only: Particle - use random_lcg, only: prn, set_particle_seed - use string, only: to_str + use math, only: maxwell_spectrum, watt_spectrum + use output, only: write_message + use output_interface, only: BinaryOutput + use particle_header, only: Particle + use random_lcg, only: prn, set_particle_seed + use string, only: to_str #ifdef MPI use mpi @@ -28,8 +29,9 @@ contains integer(8) :: i ! loop index over bank sites integer(8) :: id ! particle id - + integer(8) :: itmp ! temporary integer type(Bank), pointer :: src => null() ! source bank site + type(BinaryOutput) :: sp ! statepoint/source binary file message = "Initializing source particles..." call write_message(6) @@ -38,8 +40,17 @@ contains ! Read the source from a binary file instead of sampling from some ! assumed source distribution - message = 'This feature is currently disabled and will be added back in.' - call fatal_error() + ! Open the binary file + call sp % file_open(path_source, 'r', serial = .false.) + + ! Read the file type + call sp % read_data(itmp, "filetype") + + ! Read in the source bank + call sp % read_source_bank() + + ! Close file + call sp % file_close() else ! Generation source sites from specified distribution in user input From e795b49f5c302b34b84161be382b11490bc2791c Mon Sep 17 00:00:00 2001 From: nhorelik Date: Fri, 31 Jan 2014 11:28:39 -0500 Subject: [PATCH 143/192] Updated data processing and viz documentation to include examples --- docs/source/_images/3dba.png | Bin 0 -> 15783 bytes docs/source/usersguide/input.rst | 2 +- docs/source/usersguide/processing.rst | 106 +++++++++++++++++++++++--- 3 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 docs/source/_images/3dba.png diff --git a/docs/source/_images/3dba.png b/docs/source/_images/3dba.png new file mode 100644 index 0000000000000000000000000000000000000000..69de65414492577ac5c9aa1e00382239093dc180 GIT binary patch literal 15783 zcmZX*2{hDS_&+`~woD|;AQZ`1#x98zD%%WMreTnMCqgPalgOH7?3IWa8Pp)M?_0K# zB`P61*~`B4zdqmZ`Tu_B{G2mq&b;6EKF@RSeV*sOp4Yvi4D_^^;XH5%1j3BgR>wjh zFh214d;$iZ&^sZ^DZ3%l z6ce?Edhu7zl5}~D$(iYsgkb0;Q^k`G0@n&f3sdpeo|bSgg(os@XwqvXXao$@d8Ynk z3Nt-{q)qDMJOw|M$C79l+OymyayDpt|F+d3b^ERVxv;9#m22ChtFLZ<-Y(pJTfnZR zr9}_1ZXZ8b{?VQuvioM$ORO=IU7HRfESN#SH)bZ8!yxL-8W18JlFZI%Lze)DkaUH7 zkG}=cO6B2r2n8ZQOrAifp&*g8R0tLdK}$7>6L65zjFyjciuLFqiI9ZjU;p(jOV(b4 z3m_osq5pMctAzno&p8+^p(GgLe_omY|F7zrCbb~yFf}YxO(^XDC{Pn3{9m0D|5x=I z91Cqk67T|aMYo~qFo+OPa>L~Rh*gIj8!f=r_`kOO_Xa{ew&lM!6X1;ioKqDh2nB-` zU=t_;V^PPZXVnHyrd*DD$`UI}t@V=MZKT^5;XfC$0i8gQVA*IPsDFN&iDT`JWBvIT zi0Dtw=}+9MnXmZLQNRp|5ogO;UwZ6$F%0J0oOn$|VceJ>Yx9i>fo8htBK*X?yloP~zcqU+Ir5pD|mwfF!^!6AX?co zn;l3bhXRSB?2J+i#**uYb`F+Gz!){nL{dLGNpN>KMCISxRdZeynfAcn%EQ9^mBl+o z5fNuq1S&a#C4V7Eq1t1dinQ|xG=Ue>|1>yEKjy!O_H}dR+!}cbf++)c0z3cOwgCN! zr*A#bO8De(VXZs@=#-+YrE;ziPaVGgE}JtLic5eCy4cS`LngS-KWaK|ZXYr($i_F2 zVg?MpF7!-`UA_N>MZxFZ$z!cNvBEj#_vg0W4mf=}#m{Z0m!+h<+Zeu!XXa;$m*|yZ z<4JpB)wq7g=)cxxG8l_Qrm315A-K#}{fc$+SMf7oR@K>dPAwS-*~}`sf8w!QNx##= z@OXT$DJ@=gZK&tG0wVR6MiVZB9T-o(otUp9g4CFtj31a?k}2&N5gh&#vevq^BBqp- zrzZ*KOn|5`fWDWei{ei$`vSSacUwmWY4r!Z_&dp#Ti{9T6n$|N`SPvnO$9o>+app) zt6gn6io6t?1{7%}zi>WgTIb^K5>}-$*^dh>Wq*-&^O&|f?$&8^-su4+&-I|&$4{SY z<48%>CRH;#VBP*$QD@dq7MG!^qhPO0A#{>Y51KdCl|B z32No@&*3fNF6*z3Y+bugSPJnMv75uDluBBx7|bK+1yA~sOL4KX zzQ`T2n0r^u16Vj%*NJpoTwL5M-$f--&b4fA_(rx&=|qYn`GyHA(GV8f9LSZwg%K{F zNHH_WSKUGtIVbPHW?j5FbJc9xsnx~(b!eaG=4;CGOOTTz30(}AscZ%)$K=L+b(E;U zP00u@dr!QH1-0nsU2V%9VXSG)U|75jf%MKq#4A!k{rL?=9VueA^&Hq;QMmC)uZYn6HV$a=^3 zsA+OeIoK!o>{i;leO@6y`}5R6T~uaRikUdy)McydsMKG|N0FOCG{}YvNwxt~(~04= zSi*h>W5~gmeRirf$A#R;P(2AjrJ&g*&!Fh4Ru%Pg!KQzle*9X!C&zo{5ku-%amxse z8{FLkR_+y#jpU;}belw{cF8Ty!8>+w`gfEpJ0os3d}fHtGYwGrX?%2HziIK)lebMm z>ERI~s*mJojD30o(+&LxI-A|=olbWSf4*XWfjako%f5QATysSwLxJiE^Eyi@TSQ2b zlgDD&B#144@jt^^EkA$IFFy|DY@7^+S1Pf#_!F}0L1F60pCaqg{uKGy6qIJv!e+B* zv^iUX^&T!Je^Z>&PFq_lk@BGhlY)i+HZ)hCRNg?f{Y-N&M3#P5e2wp4*@?iLll!0I zaopr2J3~GX+zT(XF30a*mk1y?Xm+L;^3>y(ZJvw2w)QvBDaNCuA|9OKUyJtomZ!1* z9N|)gZM-mt}G&DW7E&Sj?+W? ztVhp&YrRUtZkpCbL_l*F4z=t@m#dvVYQn47*O`ZZ&Qj`2zGgN?HaViV>ZNLBxKTn4 zm!#>_?lxO~wDIvkx5GO=F9ou54U=-DiauJU^iMC(%?5HwG3rP%OS?vyx>(h-_XM@h zD^SZaa0PDaJb}Ll+vjD*w^(yqwMNjhcjZb?C_Nte!ntCnQ{mTk6BYfvaoXpByu$jW zaSiU=)`lCLL;WOtSgrONhLaKqyy8$OWie)s_eH&o_#Xv}OYpRKiE9C5;|N(xbP}#C z3~{Ai{S2M#G`BrVTJ^J}*qK^ZjP2$i-_MhQIxt+kN>0muBL@55u?mE@(PxSr(4tVc^0uu|$#W>np`MKgF5x z#(V3SY+RX#g}Il6voE`?ZS-j>!T$L+<^o!yyA{t%P7bfJ>)IgJ_G_9Lu1CYg8_Skc zJSiU;#f_fVPj)zvHBCpPX*1U_> zW2T=50{ebT^cpEAXys(?{ARRqTG%Io(Sqcub0KET;){1txZAF0=+-bP{03&&s55t- z|HTyHaAl8&qD!W>pKMh{?gqR_has^#*R*E!P;dP;e6V7w>oX6<<%3n7bx}o_FXxQ^ zj&*6zL%kc$w2A9sy{JR)9UcDsd>7;nAL6jEbP*7Y&wBLZqV=+$h8Ol%U$We)Yi!P; zuv#v@ecQ%T%oP8&9!(`BVp#5QG4MZ!84QarvY7T2o6%A8^-(Jg%{Vl2=ahE9CAzZs z+Qs;_f(kS8f#LuGPehYFO<3zE5?$eHOLz4fT@hbZg;S%aA8yX<*VkH73VwJ**dn`&~QSrAP{RY6@&X&$x|d7)i&SuBhM zGpgt3#!%Q4b?UZOCoI4Dfs%vlb5Y@4X@1+Zv16#`rRN$9w@Hm zx#aI~69p~b8@18UiK?$7!TKX6Hmyu)elN}>xj19z5J6~iAPeM3!U-iID7p?N|JeKv(L5Ydjauh z1KwS&l;^~l2RkqeAg+*98)`$MT-U-C0U z6FgSz>XoQX%+>;x<(}m&OH;4iWx2x?nTJLkj0(*ALC@yuzvuUNZ)0{D*)slE>!CHY zX-rb?o8ra!m)TFXjSV^6K>x5IKmPj2;?6iPr>Z|1sZu0b$2xynhu!JxnUug3;;Fiv zAZm8!#o_*+mJS3qMaDcz9(G1EyCMlEQ4ygu_G3t6gZwj%w-Ql&;L4+oG8;24Cq~Ot zIb6lwVNKD^74_b|AlBu*IMp=B(=#!uL#1wHbZ)DC(FZO{k)MX1m&R=@rC)z^h_mg5 zPo%7k#;2v!8oWGD_>bo*mwzWEf&9;ARTxh#`Zz9LmhlipsTC~1!Z22050l`QE=m;K z>(5-y_C2neLIvVmi)rKcEnn(U6_*b$ovx)BN&nattpleh9$u=d@vv?om-}p9ZJIaw z=(|tVXywFnYW+~y>Wq`cq&$z#9~-RROrfu7=+_I`tylYz{$-3W{%M83$oo6d8<+qQ z>ApR(6g4>&qwAnCda3cHdmC^c32QEFLP&D~#;HAR(8zgLyMcqNr zYRBs1_EVnM{vDpY)}XH*o&rGqhwqw9%wZ|;0V`Ae;>+c48lG2kDL5_sB+erCGXr=v z6TNTd`Ol@8S-N;;VrI5)(&!jTJv)MEUlO&8)1?k3Vp?o8*0Bin9HAnU6+52Xz4)(< z=Mrb?au%jL%=VGA$wp5vKMDQ0Y8csoFL=Q1@#ta2z)E>=-ox+v4Z)(dQ889nv`Hb$ z3Gu)9)0jPbZ#VaTQBx<5D^X%b8<-d3i!+0ZbSXwyJm%fuQo#b7OOo}iOQ&_N+$5LO zjM!RIlnzroe%s3R-b_?GN99PTy~AsCM=D4A`*V%9qgI&BDwVSZ{{GIh*_3s7$CfQz z)8#@DE)|ofP&}RVx=BZ%yKZ@b=HAJ)EAZcZNuyf`Znf%-=nCB8yF%*2+X9y<_MDxU z{`Bo-N*7488$TV|h^e!jitT@;$MfTHjuUk->!_d2m^kjjhFOdLP57~roYt6bdnG^7 zelxoCIYz^Xw$_t;tI z=%`m`KC#~Rzw?SX3bw)f(v#uaEP^g{+r_qGS?Nb8(HGH(yBr1rh>qgP%{$b3YljQT zYW$W;k*$_0E;TNSe!9>M8x1S0A&*i-?z>M`)qO;r>YqpAQ_KMaGwi=K7nJgZ{zgks zgsk2ZN7Zb~{q^*gE*80cdhjq^Pgt|WDyM8!edS)aFVX;I_I2pu&F2Df+>?=6c$i~# zH7cvsi6d6pv97hMx<2TQXu&*AyHTKif7uC7(R^@XlUNRxq0X)1 zZ)wh0w;Gy_kx=y!X>#Aa@0^uxpbMk;eRAnH`qVA1CFPpU>4DD|uRtulIz^L=#QZ%j zD+@J>i|kijQT5wx9lhDg3h9Kl8DLl^5Y(Ky`30)34brIdER%BiY2y#JnCS~AT*gDd zCh)3I%+e2uK|LHE+TYlFG2C!}<18Rh$gz5pPIc4V;c3M+coNs*g_f@09+s*HzZ_W4 zX^l_-LZS1v+O6$(+)ndM%(pC%TYct8&-7I;IuVa)60NGs$u?-Ha-0D~OYp`9<1bUv zG8rY&>cuRM|9-h^Cxq^k|1M8>m9*6ZpOCX~A!>u^udh>`Z?gVPMXav#z-r|#V|W=p z*(J9X+j`Is$R1vz6!q00FBW;S3Y;^PrTbb-l%?4JEOL-dR^RA#(mz-f_Tg7E>0~pA z{V0e@H;fds@zc2V`sh!9kL15-krU#wfiBxK{gy5qCGRb{$tIS&ur&I9|8T$JVC|uK zBy%j=6rfGiQ_Wvql$Pf+o~$TUT?2eaZgPJtJ^_>YI`y`Z8WrD9&w&wp=7)TYd*J?+ z$hn33z@1#%FGT6u;3gYU|EA;WcO^_mtk|wAm;YT#;k`WOE?p%0e4|;kyiW+h(-<~U z?)&OvKLP)ASIXj&Uo2L<$l&&4!RrjvG9rGGae=F00b1RIbt& z*JP0l4gY#K2mUSi^H4bM;92s?---nTGJ=S^QW;RX+}4`uU(Y@^99}?^dFFbEchX=wAF#=+hZ_>7iS1+<^NBp4a*pk;QRL!%z!gjo|6*So zv*c@>bxG^MM~OXP(K*eCrW?%H_<6nUJaTW+#=$F3JjGDxcNl^A8L3Toz~Jv)zNV7? z#O%~Vu98P+0pg^kVWys1woa_9&(Si!5oQLg7Qd1o>c~qGCinFGOCYzX?rzGHTr(fU%H!yY!LYg8$j>#KRvi^5b5QA>y;(V{Dk+~d{0{Pf( z!&a28Q}EX((GXXuUV+12B0vFRCZ`mVoo|?0#^^k)qW;Au8Wj4BLu23BI>%3IQY!bt zN^3n6%aU-k8z77Wh8CgC&6v)DD9y~W=Z4cY(~u5E{KSZ$N{cXlqO~eUx~F2Xt3vvk zChH`z_w6him2Dc)=HvR)d!A$tP=I1qU9;^xZekgl5HGBFk_ zcDwKml!-tH)z(IZXG58?mmdHYp=|DWRUsQ1{WfYYL&yjXlr|Fi0CCg!Ub@fc46f(9 zUbeA0Qn~Ukb+l1fZhmLqG4sCiw?d~4Q1l!cnhRY>Kk|*Y=OAgMhf$oFf5q4UpDbng z)H8R2xJAA}!!1Chhry))^{6OO(19fAB*4|41a96P51uuB9qJ*SEkL}~e%N}3+&|~H zS@#g}{&u(UoNhFG_-5Z&Kf8H99{U=~bYgSTop3W$P{2B!62J;M9;XwXhY8^ zTgu;W5c)o$N?JZctNeclV-|*==Rne#GomYRhI84hu)$YNGbFIGx*Nh22}DMbQPHGu$3k`O|p-2V~e z$rg9}Jl<;mZ-**ofbNw`+{rX~nmgn8r_5`wnW*)td1z_(d7Cz6w1(*5BD9pKkZsKh z#wZzv7)KX}NnIDxrn5#oa&`G3UP-I2L-v1LA4tP((U#3>4g*F-GK0r36LV51F0aps z5Aq|#!_(?7mAHp z`q)fp?a_3Q>51S8xD?>rA%>RMg$Ot-u>cxNMrEHghpiNCvR4R$gjedl`zvC&XXsCw zv$z4ju_q&mY`#6*!C&`z#(0U?-4T0#Ts+P6)r~9Ad{C5YTMmgnSf7a2fj|Tk05Y&h?%#XB1LCk~0x3vcuKr#@^w@HCofXMOE6Q3!AfXVq!|R9x@2=gNc}9~T-oZ&Mu8>qR*bGta0bIo z2T|7!r$`~#7ETjo(<;Hzv64z3kFwWQ{xX7JXHUQgFMeHYQy{o| z5E|wl#^50da9{wKOSb0f=r=k>kKTW-3^O|+4$&raEJ5gf=^zw>NEia5hSKb;_7`Ke zPTD(I**AjIU$L2>&YD?0UiZq_5d@&p0bnDH@6n(LJ1b;(Q3a5Z$`epDhxd{@JMC+ zEFdchtRhvki;SNHm7Nlc4u5OdJ-R?OpG|}IDVGv4ZDv88^u@^mh&xY@Ec*7ev@(m7 zX4x*7qf1QlJpt6E~@KvHezpK|G=8L)CyY^d>c4|!-q zJAU?Gqr~Crj`ZBWpSZXw^>6na>}%b6kis2TE$%0pgY(6(Vx2+y4?euZ5?LSk5f)O{ zcC40cy%-7i=a2f`;sTevRdxdFbh2_$f6N445Q1%qm>3h``bqvrtE!BS4cGVs$jf}* zVn0`51kz;O0lkda>}jHl`75H`ddM&)uIfQxl&BZ2e@+c)W4Ep*{^eD~pA+MOTk5sJ zuLAdWRfBgdOS4eBrMnh6`eS~=1vE(rnWXExrLtW767;nuc19DGs}ucQ@lUjY`>KJm zaB+84c~w}cqC0pdAF&^D`DWCrf7-O&-miw5FN5FKM&DiCc_FUC;_KJRnKoSR)wzp; z3Lu=rEEcCC>;m7%->F+Ya`|)RSxK0 z$}WLlvWltrBmqnDEXK6QmR1~H?qSFXO!|SW8RX&v?#?cMi9p9>zy74gP7GPkt{bBR z;X%43V%}h*#-X~SRn6;{ceZq=?q=>d)$1J6OtOI#_@i~jXsHJ0V6`x(%YCb>(mrds z6XayV&=PTaEJQ>YEx<^43ZOu6>;U}^o2HY`xMJKyoGOP32wQ{5-rQ} zIEa$@tsow?!!8pXtO|%%lOYhS%d0KkMqP8S;H;GoL34jB`ccs?vIK$9{U|hQkI|&S z*6LrSm46_WsJXE>QE-w6B1*)tl<$qan_OJF#w_CA&vXd+djS#2!@f;vh*{0q zl+)-UUi@O;Tg*YqC`ca+}}Z&uzRYs%H!X~a<0`F zD@}4d3y6W3nRk3H({m#tUU=UMgJ{#e z6iN<6kVZ}?wPr#HxTUf_h_N*VbC4(`8!zDG5eRWsLoGcH$^L{zbbJYs3@3f3QH|m_ z$z=f&#*W~bP`5hgSvT5 z*OIPo%|k&_&M>!S;cI&(bWs}^Es^X7`Llu=t3mR1Y{tKcZ|I(4(OJq?;s!phq9D>L z*K7M4TqJ-A(pv8hM>{<29a!>C^V~vHoetWeMt;-NVq;Q$6r2fO^$i%)B!@m_Ic)Qf!`&Mk&a3y5E&6@V!`wr{r1`88~AEB}iTs3pXodQ5^< z2ibYhbO(LPUB&EY&W+OmCyLtpff26#^U(C4vy0+aBhzn8S0H@_xcng&l6e8q_0aCI z@RZyQ#LhB+7Mc>}dzNcve@yNdk7=pGfG;aKe~0$SA{9S84wo$O`PW^3<-jp0IBhTV z*gxY5?) z?hQ;J#o8H?^srFH3b)#?zjVhmszN7@MidHQmX;yS$bI~Juny!!mUXF-axF@{{;nzN zla=g4fFxiB-yoc-#oIbr+je z#kf;2EYXh@>{(2bOOR9#O7skGRoSVu_>)!3z(bMgD?E*CaZ@WZspbBfD{ha0>tW`E zEDI*(bdnw#22GD8ng9o(ZK>X?RkGvSLm~_J)*l9dc)xZUpvnO027RIQHm#2qree1l z-;A9i;0g@Lg^I3}u1_}E`u%?$zHv9S_&z49u`z-jrYl(?p74Obxi+tu_}S;%o#C!B z*u_yAffPB>yAbbheh#ZkOli=dv|AA6&-~0aV7a98ha$#T@qThryi;i$t~cEKc#dJv zlbLt69ZmASqPnGzy*9ewz1F~QQKO{Q(r537?6hJn(idQAsNKk-p^nRhnMKqo-ngKS zZHt^W8#%l&%2E?}?{Y+X728rgJDF&mqnB5uk>fk4o7wo#@9Y)KZvSymedZ2M_T-j=#azh1xu5DDoz zf()%ets+I?ud4U;08kM_Z_>+XJtd?5dG4+r{n{A!i~msHQVp`#sEe@X%l-K*j$>HR z@_X@TO*G4uD({)pB3W6q12VWZ%M9KbuJmTC38X0 z@!kRkiWm4tsclTUR3A66tqi4{cE$9#Q zq6c=zQ-Uy-8pj+&rXG}xV{Qny7{;2uEsoib8-XRju=e*Q08$!>bc3*S7>_deQ^~>pF02h zG8IN8p14afzuH$U#s~zQKp4mYXiLBRRPl;~)PkLNA?AE&pHJ&;L5f?w=pmLe1~t@T zw(;s@3awaEi3-2B7JsTvx-jrIlz~~H)!(ig5ndyE9TOUxF1#&*7j$lDs{)9tABVuR z-D@O!;o4~XcAXR6fFwOlwxt=dHIZ%l!QGMHc8--O9oC8l==RzC71)MJlGoSVYGQnI z)o*0LODM2Qc+0YCrtBxFTZFdgOe~WWqI)x)KAWFX_8r3c&FJc9al3K}rY@VzR^@+> z6^DxO%@~$XAWZ3;v!QO2DG`U(X&#bkCJIADiZj1wSI~+)``T3BIFmM=zlhtR-I||y zz`w`77r)7WeF;*NHS2k1YhJ%LtSzIZ8NYXn<%DC_RbU0_bs(?dq~IQ;$mE2^%zAS!n^qVKw{OMZ9C48rV7uF(Qm9p<{OI4pi7 zFlNn9dL{w=tYjq&>MJ%;t3n?aNYXc>rTo;P#kzn(uY&|0>rB2W#dSIY3AR2`AYoG@ z`~7ox&oz9i>rWLzzt4kD0PMql08{UN(UIJxpSM_-riGA*sqK#--uP}sD(TGg?N?9@ zZF?xF&X~e$q9qe&Y*O0DDh=X9mfEl7V2@Z2eYm+R81ws)P0iI30c#f_kfa`#4MQB) zyV6~tAr1Dd9XUab^=&pRH5m_y5h7qr)`s1!QoOESJ+e$QAWa!`n@GCI7b|oi#F&J8 zAx|q@XMtIh*Rzf+H`lT4*s%MdxKpsDz2|}00L0eLfp)A?7nK-?^eTpA!myhL`mThjjnm*Fi zty1wrTk+(dEb}7jOnjQ#UQxNf!791m8b&1GvVF=&tAg(CtR9}fFOPV% z*Z+ z9#$5F-2g>+a!Ge(t*Tc~OU=>wj-DUM+h3PT^|RdCKRX?{MO^^5AQyi)ctyK`cbxcF z^}dgYh}xvy6_XF#PH|C6oS6|fvM=lLoxP;oon^Pz&HY~Ca0Z(nUqmUx~OBXJvXqGshi(uVA|eV?WKt_l{YxJsPls{ZI4b zTc02Z&ha!JlQ9oaUl*Z3%+;`Wwh&Rx&g1f!tvVW@8E}18o6HCw{_nNWF@vOrnqX$O z29Gsp3F347thv1AJ2@^Kph1Zy9J9`uj}?f%zV2qYh~ z3-k~ZN13f3Ab@4$>&_4w$UN>?7{DX%`;3_W8y#3P5Q2%H&ECE`dn{94Pp8};7-#(q z6@$}8K>)_)38k0GZ@wU+@5TB*t&a!Sk5Y2oJXR0xvVv}nH1jBWf>QZjRATgrV@*Uj z=~sm6&I(J&8X%4!WlfzbAxrV9{{N!hyej<7W*G>E@z{uuw~;fq^Tofp25;!`e5ix# z!NlmRx&QGR1a@luqf6sY2L3Y@@EQr>(J{E{RE!QoA(&SjkNsQd(r`n-)!$6uGBNfs zT{@HoFDqaKY4sdR`i$_0Mx>D0MSVO0!CklRJV4-wu^6Ag5R*hgPDPv{}`1COaXndm*uwu zJ}_4h%HOt1sN2NmW$K*C2N=-mKP`hu6)J+L&Ebw=cr0r%L%p3>c+NFow)L59u+Tu7 z!t8rX%Ye{CJea?~8XNR7vBy^D_3$?KIHM#s8WI1Aj?A`wJ5~n;&PXBfue2eaH$kjp z!@$~Nll*A7C2fx9@idS$)$mLH7({x;1~OX%!UXtT^w1&~Z{w`5`^l$xh}sFjJ`C-4 zzwzM@I2iQZySrmWW$T_u;Xly-)4?)6MT7zOJ$Gextz;Mz08zdi640svra&%^+1m2q zzSY5jyOe~Q$BARy?pHFrxXf%VdoVfYk;@5mrcmk`>cfY}Yd@{GfYDn+JLrpPT3(to z_G@pz!G!B&C{76wZS>rBsQ4^d&Bu>D0uOIX0hnK(j=SA1SQ7X=*R1&DaYLHulYh^E z9Q)<^hFOprjY*t4vh#Qu1-t;Q0=XsVlnBRCG)-tma?C2z!Slu;zA5)6&VZQzT3L71 z*2{f!xZlsFz^SWt+=GC_2-sWH)dl#DdGpmypF7@fzO*{hm^6Eb)8sYp&_UI1DC%qQ zK`q*nD8(J;R5x4r123yd{@ZK_ivLZGJEm~04!>7aodQvJcAQ=&CalM;TB1@sb_te! z(h$Hw4Zgd~*6n_GI@8OOxalB*HhVg!|3pz+s+y<%nf1s9hkP$tHhmG6DDx66S4&{b`LAN(2sd|=4>p9)5)a^K(oj#%z-5lH>YcW*XKjaf;B zRE=0G{44v&Q?FEC9Zn3k$>fU#4Da{WV6(JqY9>65U+Y^1-}ZX=R8~COhCdRcB>Z|( zr3c$W(CwaB0v-v6MNfSoT7EZS620q(T<&3fgbtAHxNErlg_f#zCZD1`9NYbY*#5TA zw-nb_QX^sXy3LmBW{XMR5WqcMqPGkGZLXgC*FLOz>(zbX5ErqbN@d{)3*p>pYp$;S zO4z&D;0-zsqBI89qvZAAL90=5(g)*) zRNPau!|SL&nKfUJY8>-+ho7h1j2sUAc(8i;LiY~)^xxvtzr8t1uCiY`|Ly+^84Elc z%4sik%!_`Mira0+VoVRLI%;kG7y4Vdbp)sDBVUolkeeZvEx&*CjNi(uqavu);nY^l*N39%I|Vc zby$zAAJ5&Hy-tQWNqdGG-GW2^OYfaoW5IdGM{N_DbY!a=ec}We)!tyvuPPd^HFq=H zZ?nAcKKvrX+}m%k7+`#2S^1tH%}B_R^!TOr^oIHw;#EE^&+T`%Q|5{#qCx|M4LYS6 zv0oyZdq`B9kF*&Zy=#P+r^U`sY#G6!OlMHq)rt37R(q3H<7OvCV+widjHA;Z1O%eT zID1~X2@g2GxHDKpu?;?ZDxRtDqFa#FEfMzI*bcD{==Yi8yTTU_h;j8QOU%_V=a`iHr0Hr`(#6JU6)G(U^0A4^(flFGrp@PDZTHU=vy`!Q zSxLLX)mXmMm`1M77+ga1-(t4FyS3?f&KXuyX|an2^f=Q{s-zb!g!16QfWPxzsfW0W zVfU=h-z&Yr>))jGJloQKCn@$+*s_596=bqIJrLjlTZJ=rez52)F}L<3fHDH|bNS^-b_4 zZOEn!;om2uK5*Vhq)&h+U(oXK(#5am=|}{q!y97s6)UG0A*3AQ=W{_~L$A#0-(0(Q zRJZRo;`pVl;%S!s<7r0?25*8|*f#qGM4OGnA4Lfre2EueZdA5s?!4R1Og>pyN}Cqf zIx7jb?-Cmaxew<)yeKP@FY`eF`igNLwZ(JDl@-;WX?EY7A2dNWN3VjkWYLx=<)sua zF5Y?B0li@l@WqJeC`4hcvyLlbGP?Va+WoC1&JXF>_h9_4lHiT=JKgte_Iy&(cy<@8 zCgy~Z>*foZ+Z*pDZ>4cZWFmiZxwdwwr_Oy2MnpCpM2%2UdoFVVAsRrV{!?3*p2S(Sv4@+a z7O20|GJPMjpJ(~#$sVU!Iww#Vma-okBfdo^o>;M%N=5-%7Yylzd&gp{+6p+6?az>Z zqO|4zE$!=zY%&VnwF&)E;oKRQQ4fbu^0mHK@3lF~=-}69`}d}Gj7q<^T}F=Zhq=fe z1IGHo-*bLQx5nN*Wc%xEB4?Z-;Aa!$sFmpg6Jb&vPq2I^QS0w8`zLJTb?ZQo$Rmt< zo@B;$(D<0(*8{VeM2EHX{HpK^7Z@JQ)&NKGoiH%~xunwE10O!qH090ol;GG`3jXCq z{k$j3fh;t_-_K~FCAEN->F-_lj*JiY6aDlXe=sM&N$1@z26o3KrJ9%SGRFMiW}sG6 zO&(jJEj!BU?FEpclQC-FHWCy7MQ%=TC?Wx0P(!2)_^GuCYkI zcdd%Tw{aRAs6?Pu)4kG{y5tAx`O<7VWo-_vV^={8OSQRfCU!7M#}%Zq&tCRg)gB9^ z^{MgQd=hT7`g5+P`9m!Ss2wuAX-F{ipRP*FNfYwruPSy_=GY4-Q7;ayA(F520d>>Z z>|5s;SA0LH5uQRc5CN1>Cefb02Wy3{*~aT$CwH0XcO8@Ug`C^u&Gu?s8=l_gnl)ON z{hz=*10bnL)75)nGyWZ3176SShWE6#>uu7yYF9hVi0q(>dh3V0|5vy4s6BDy-)qETD6@+#_*fJV-d|y9`}+P zUsf#uwb0*~VO)BURT3dIdRTN-*(5Fb*s4p@E0X65wr#wv9-VR1Obxu%PRU_(Dgo|& zV!bPybG(WwN0*Ym+>ihY!N>hJGufPJ-(<#Z{Qns22~DX4+yX4%+Y_o(6(iSmp1!jz zCEL*wGbhg{TLQnaZ(=iDx#KPgzl$-XuOJCB2Gds3aWVato4heB4 zs21D-(*!B{-e%b%ZEWDRv`Oq^YiHsWK#b%W)0n(1mNSewW3*wB`fWe%eG`qBinl=$ zSki=H@u{~hqY*p3q0#uZ<3g*f<@HzFQL|?klC)Stujd5ilmK6l4-Gq0?B`jMc4so6 z5guQw`v_X;Nz{Q&icM6#$TNd6SNku$Jfx zo$kpDP$7Y_7T%Y1)c8vl~AUq*=AIYBjW5Gsy zdz;a>YC6M7`rzpDuwN$>`=tq7&-mAR`}5G=3jb(rK~V5ipQIzTUNLUfsC^Gc^Z=hn1zXHooq-WUa$Vow_NFKj$>#fH}&7! zjVHP6K(k-O_g_ar+7~TD(TdknreC+mAMYX!JHr(c5pI)gpauEfH2ijh9ZHMAJyX^N@f3|-zN7ns@%vS% zi}NIA(LtJzGXS6PRFj0?Iz(s^S|a~x{P}7^pJZLKb@{ea?DNug&Q4b#7hph8HTN;f zBW?r2ppsSqxpX)r;lC~U^j4-dhXAN;e?Ht)lLyfYA?SfB*7~`*MZ6AJe8>dbJPf|M z9D$Gu75uMhquk))V`msN-x9!1aQ6NM;7{29be#{20Z?G@riZK)khBOc&&>l|4v2y@ z;JgrXSl5>^eK%*Z6-jW(abtlt7Zfj$NMVqmSOyorFoFPbd~NucAP&;U zsKyO0-^W$Lt5{76MT#ca5;ST4BR(pR9Xy*I?Be&c|KE<-CP&G6+H7`bF{v;U3b^y= z$tarh44jq2K^HL4>D!MHevGzQ==Zwq) z*tI@z1o|H{IK@Voy^iwaXPogj2UQR-5)AS5X`lln4K)!+aZd>3=i8=4Z-2`#fP8_V NHT2Zq-LMY*{{WX%y`_ * [1]_ `Scipy `_ @@ -41,6 +41,55 @@ Plotting in 2D .. image:: ../_images/atr.png :height: 200px +See below for a simple example of a plots xml file that demonstrates the +capabilities of 2D slice plots. Here we assume that there is a ``geometry.xml`` +file containing 7 cells. + +.. code-block:: xml + + + + + + myplot + 0 0 + 10 10 + 2000 2000 + 0 0 0 + + + + + + + 1 3 4 5 6 + + + + + + +In this example, OpenMC will produce a plot named ``myplot.ppm`` when run in +plotting mode. The picture will be on the xy-plane, depicting the rectangle +between points (-5,-5) and (5,5) with 2000 pixels along each dimension. The +color of each pixel is determined by placing a particle at the center of that +pixel and using OpenMC's internal ``find_cell`` routine (the same one used for +particle tracking during simulation) to determine the cell and material at that +location. In this example, pixels are 10/2000=0.005 cm wide, so points will be +at (-4.9975,-4.9975), (-4.9950,-4.9975), (-4.9925,-4.9975), etc. This is pointed +out to demonstrate that this plot may miss any features smaller than 0.005 cm, +since they could exist between pixel centers. More pixels can be used to resolve +finer features, but could result in larger files. + +The ``background``, ``col_spec``, and ``mask`` elements define how to set pixel +colors based on the cell ids at each pixel center. In this example, RGB colors +are specified for cells 1,2,3,4, and 7, a random color will be assigned to cells +5 and 6, and a black background color (``rgb="0 0 0"``) will be applied to +locations where no cell is defined. However, the ``mask`` element here says that +only cells 1,3,4,5, and 6 should be displayed, with other cells taking a white +color (``rgb="255 255 255"``), which overrides the ``col_spec`` for cell 2 and +the random color assigned to cell 7. + After running OpenMC to obtain PPM files, images should be saved to another format before using them elsewhere. This cuts down the size of the file by orders of magnitude. Most image viewers and editors that can view PPM images @@ -53,7 +102,7 @@ Ubuntu: ``sudo apt-get install imagemagick``). Images are then converted like: .. code-block:: sh - convert plot.ppm plot.png + convert myplot.ppm myplot.png Plotting in 3D -------------- @@ -61,10 +110,37 @@ Plotting in 3D .. image:: ../_images/3dgeomplot.png :height: 200px +See below for a simple example of a plots xml file that demonstrates the +capabilities of 3D voxel plots. + +.. code-block:: xml + + + + + + myplot + 0 0 0 + 10 10 10 + 500 500 500 + + + + +Voxel plots are built the same way 2D slice plots are, by determining the cell +or material id of a particle at the center of each voxel. In this example, the +space covered is the cube between the points (-5,-5,-5) and (5,5,5), with voxel +centers 10/500 = 0.02 cm apart. The binary VOXEL files that are produced do not +specify any color - instead containing only material or cell ids (material id +in this example) - and thus the ``background``, ``col_spec``, and ``mask`` +elements are not used. If no cell is found at a voxel center, an id of -1 is +stored. + The binary VOXEL files output by OpenMC can not be viewed directly by any existing viewers. In order to view them, they must be converted into a standard -mesh format that can be viewed in ParaView, Visit, etc. The provided utility -voxel.py accomplishes this for SILO: +mesh format that can be viewed in ParaView, Visit, etc. This typically will +compress the size of the file significantly. The provided utility voxel.py +accomplishes this for SILO: .. code-block:: sh @@ -88,13 +164,21 @@ or Users can process the binary into any other format if desired by following the example of voxel.py. For the binary file structure, see :ref:`devguide_voxel`. +Once processed into a standard 3D file format, colors and masks can be defined +using the stored id numbers to better explore the geometry. The process for +doing this will depend on the 3D viewer, but should be straightforward. + +.. image:: ../_images/3dba.png + :height: 200px + .. note:: 3D voxel plotting can be very computer intensive for the viewing program (Visit, Paraview, etc.) if the number of voxels is large (>10 million or so). Thus if you want an accurate picture that renders smoothly, consider using only one voxel in a certain direction. For - instance, the 3D pin lattice figure above was generated with a - 500x500x1 voxel mesh, which allows for resolution of the cylinders - without wasting too many voxels on the axial dimension. + instance, the 3D pin lattice figure at the beginning of this section + was generated with a 500x500x1 voxel mesh, which allows for resolution + of the cylinders without wasting too many voxels on the axial + dimension. ------------------- From cbe6560ba1d6e85d47f35a5645dfa0fac928ec8d Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 31 Jan 2014 11:34:07 -0500 Subject: [PATCH 144/192] message written when reading in source --- src/source.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/source.F90 b/src/source.F90 index 4ec358776..85ef87d65 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -40,6 +40,9 @@ contains ! Read the source from a binary file instead of sampling from some ! assumed source distribution + message = 'Reading source file from ' // trim(path_source) // '...' + call write_message(6) + ! Open the binary file call sp % file_open(path_source, 'r', serial = .false.) From 5d0b98835d2ca1101b4d11a273bfa1ff12530ff7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 17 Feb 2014 17:30:02 -0500 Subject: [PATCH 145/192] Extract files to subdirectories. Add pre-generated cross_sections.xml. --- data/cross_sections_nndc.xml | 1716 ++++++++++++++++++++++++++++++++++ data/get_nndc_data.py | 17 +- 2 files changed, 1727 insertions(+), 6 deletions(-) create mode 100644 data/cross_sections_nndc.xml diff --git a/data/cross_sections_nndc.xml b/data/cross_sections_nndc.xml new file mode 100644 index 000000000..2e421d7ab --- /dev/null +++ b/data/cross_sections_nndc.xml @@ -0,0 +1,1716 @@ + + + ascii + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/get_nndc_data.py b/data/get_nndc_data.py index 0f943d684..c9279add6 100755 --- a/data/get_nndc_data.py +++ b/data/get_nndc_data.py @@ -2,7 +2,8 @@ from __future__ import print_function import os -import os.path +import shutil +import subprocess import tarfile import urllib2 @@ -31,6 +32,7 @@ for f in files: if os.path.exists(f): if os.path.getsize(f) == file_size: print('Skipping ' + f) + filesComplete.append(f) continue else: overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(f)) @@ -58,10 +60,13 @@ for f in files: continue # Extract files + suffix = f[f.rindex('-') + 1:].rstrip('.tar.gz') with tarfile.open(f, 'r') as tgz: - tgz.extractall(path='nndc') + print('Extracting {0}...'.format(f)) + tgz.extractall(path='nndc/' + suffix) - # Give xsdir a unique name - xsdir = 'nndc/xsdir' - if os.path.exists(xsdir): - os.rename(xsdir, xsdir + '_' + f.strip('.tar.gz')) +# ============================================================================== +# COPY CROSS_SECTIONS.XML + +print('Copying cross_sections_nndc.xml...') +shutil.copyfile('cross_sections_nndc.xml', 'nndc/cross_sections.xml') From 6edd3934fbb3071f5f98c005a72ba0c4171b17ae Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 17 Feb 2014 17:46:10 -0500 Subject: [PATCH 146/192] Update installation guide with information about NNDC data. --- docs/source/usersguide/install.rst | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 164b0151b..ff33d9a6c 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -264,10 +264,27 @@ Cross Section Configuration In order to run a simulation with OpenMC, you will need cross section data for each nuclide in your problem. Since OpenMC uses ACE format cross sections, you -can use nuclear data that was processed with NJOY, such as that distributed with -MCNP_ or Serpent_. The TALYS-based evaluated nuclear data library, TENDL_, is +can use nuclear data that was processed with NJOY_, such as that distributed +with MCNP_ or Serpent_. Several sources provide free processed ACE data as +described below. The TALYS-based evaluated nuclear data library, TENDL_, is also openly available in ACE format. +Using ENDF/B-VII.1 Cross Sections from NNDC +------------------------------------------- + +The NNDC_ provides ACE data from the ENDF/B-VII.1 neutron and thermal scattering +sublibraries at four temperatures processed using NJOY_. To use this data with +OpenMC, a script is provided with OpenMC that will automatically download, +extract, and set up a confiuration file: + +.. code-block:: sh + + cd openmc/data + python get_nndc_data.py + +At this point, you should set the :envvar:`CROSS_SECTIONS` environment variable +to the absolute path of the file ``openmc/data/nndc/cross_sections.xml``. + Using JEFF Cross Sections from OECD/NEA --------------------------------------- @@ -314,6 +331,8 @@ distribution to the location of the Serpent cross sections. Then, either set the environment variable to the absolute path of the ``cross_sections_serpent.xml`` file. +.. _NJOY: http://t2.lanl.gov/nis/codes.shtml +.. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html .. _NEA: http://www.oecd-nea.org .. _JEFF: http://www.oecd-nea.org/dbdata/jeff/ .. _here: http://www.oecd-nea.org/dbdata/pubs/jeff312-cd.html From 1fb2bb96f6c31ac33bfde36b33a48929906eebf1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 18 Feb 2014 17:47:20 -0500 Subject: [PATCH 147/192] Updated data/readme.rst. --- data/readme.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/readme.rst b/data/readme.rst index d975723b5..03685491d 100644 --- a/data/readme.rst +++ b/data/readme.rst @@ -15,6 +15,9 @@ work with a few common cross section sources. - **cross_sections_ascii.xml** -- This file matches ENDF/B-VII.0 cross sections distributed with MCNP5 / MCNP6 beta. +- **cross_sections_nndc.xml** -- This file matches ENDF/B-VII.1 cross sections + distributed from the `NNDC website`_. + - **cross_sections_serpent.xml** -- This file matches ENDF/B-VII.0 cross sections distributed with Serpent 1.1.7. @@ -31,3 +34,4 @@ element in your settings.xml, or set the CROSS_SECTIONS environment variable to the full path of the cross_sections.xml file. .. _user's guide: http://mit-crpg.github.io/openmc/usersguide/install.html#cross-section-configuration +.. _NNDC website: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html From b102b71809a3f9baf62046ef8e1bd291bedad8e5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 18 Feb 2014 17:55:40 -0500 Subject: [PATCH 148/192] Added HDF5 files to .gitignore. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9f720f989..2dae450d0 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ src/templates/*.f90 # Test results error file results_error.dat + +# HDF5 files +*.h5 From a96c6ad47463aa26cbbc13c01c9b99f46b5f4f75 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 19 Feb 2014 18:02:18 -0500 Subject: [PATCH 149/192] Ask user to delete downloaded tgz files. --- data/get_nndc_data.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/data/get_nndc_data.py b/data/get_nndc_data.py index c9279add6..0fd1a9889 100755 --- a/data/get_nndc_data.py +++ b/data/get_nndc_data.py @@ -4,6 +4,7 @@ from __future__ import print_function import os import shutil import subprocess +import sys import tarfile import urllib2 @@ -70,3 +71,19 @@ for f in files: print('Copying cross_sections_nndc.xml...') shutil.copyfile('cross_sections_nndc.xml', 'nndc/cross_sections.xml') + +# ============================================================================== +# PROMPT USER TO DELETE .TAR.GZ FILES + +# Ask user to delete +if sys.version_info[0] < 3: + response = raw_input('Delete *.tar.gz files? ([y]/n) ') +else: + response = input('Delete *.tar.gz files? ([y]/n) ') + +# Delete files if requested +if not response or response.lower().startswith('y'): + for f in files: + if os.path.exists(f): + print('Removing {0}...'.format(f)) + os.remove(f) From 3f6913ba6ed110f324d1a0afabe4b68a368f4dd1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 19 Feb 2014 18:12:38 -0500 Subject: [PATCH 150/192] Make get_nndc_data.py compatible with Python 3. --- data/get_nndc_data.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/data/get_nndc_data.py b/data/get_nndc_data.py index 0fd1a9889..2e76401a4 100755 --- a/data/get_nndc_data.py +++ b/data/get_nndc_data.py @@ -6,7 +6,11 @@ import shutil import subprocess import sys import tarfile -import urllib2 + +try: + from urllib.request import urlopen +except ImportError: + from urllib2 import urlopen baseUrl = 'http://www.nndc.bnl.gov/endf/b7.1/aceFiles/' files = ['ENDF-B-VII.1-neutron-293.6K.tar.gz', @@ -23,7 +27,7 @@ filesComplete = [] for f in files: # Establish connection to URL url = baseUrl + f - req = urllib2.urlopen(url) + req = urlopen(url) # Get file size from header file_size = int(req.info().getheaders('Content-Length')[0]) @@ -36,7 +40,10 @@ for f in files: filesComplete.append(f) continue else: - overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(f)) + if sys.version_info[0] < 3: + overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(f)) + else: + overwrite = input('Overwrite {0}? ([y]/n) '.format(f)) if overwrite.lower().startswith('n'): continue From 6dda6baeb3adee9497f70c09482d4d4e39314356 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 20 Feb 2014 16:32:28 -0500 Subject: [PATCH 151/192] updated documentation --- docs/source/usersguide/input.rst | 49 +++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index 28ba30c23..362197439 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -348,8 +348,10 @@ attributes/sub-elements: The ```` element indicates at what batches a state point file should be written. A state point file can be used to restart a run or to get -tally results at any batch. This element has the following -attributes/sub-elements: +tally results at any batch. The default behavior when using this tag is to +write out the source bank in the state_point file. This behavior can be +customized by using the ```` element. This element has the +following attributes/sub-elements: :batches: A list of integers separated by spaces indicating at what batches a state @@ -364,19 +366,52 @@ attributes/sub-elements: *Default*: None +```` Element +-------------------------- + +The ```` element indicates at what batches the source bank +should be written. The source bank can be either written out within a state +point file or separately in a source point file. This element has the following +attributes/sub-elements: + + :batches: + A list of integers separated by spaces indicating at what batches a state + point file should be written. It should be noted that if source_separate + tag is not set to "true", this list must be a subset of state point batches. + + *Default*: Last batch only + + :interval: + A single integer :math:`n` indicating that a state point should be written + every :math:`n` batches. This option can be given in lieu of listing + batches explicitly. It should be noted that if source_separate tag is not + set to "true", this value should produce a list of batches that is a subset + of state point batches. + + *Default*: None + :source_separate: - If this element is set to "true", a separate binary source file will be + If this element is set to "true", a separate binary source point file will be written. Otherwise, the source sites will be written in the state point directly. *Default*: false - :source_write: If this element is set to "false", source sites are not written - to the state point file. This can substantially reduce the size of state - points if large numbers of particles per batch are used. + :source_write: + If this element is set to "false", source sites are not written + to the state point or source point file. This can substantially reduce the + size of state points if large numbers of particles per batch are used. *Default*: true + :overwrite_latest: + If this element is set to "true", a source point file containing + the source bank will be written out to a separate file named + ``source.binary`` or ``source.h5`` depending on if HDF5 is enabled. + This file will be overwritten at every single batch so that the latest + source bank will be available. It should be noted that a user can set both + this element to "true" and specify batches to write a permanent source bank. + ```` Element ------------------------------ @@ -1052,7 +1087,7 @@ sub-elements: the PNG format can often times reduce the file size by orders of magnitude without any loss of image quality. Likewise, high-resolution voxel files produced by OpenMC can be quite large, - but the equivalent SILO files will be significantly smaller. + but the equivalent SILO files will by significantly smaller. *Default*: "slice" From 9acc8ff366969813276d71bc59eb451e13bca7fa Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 20 Feb 2014 17:48:13 -0500 Subject: [PATCH 152/192] deleted unused data file --- tests/test_sourcepoint_latest/results_test.dat | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 tests/test_sourcepoint_latest/results_test.dat diff --git a/tests/test_sourcepoint_latest/results_test.dat b/tests/test_sourcepoint_latest/results_test.dat deleted file mode 100644 index 00a65f913..000000000 --- a/tests/test_sourcepoint_latest/results_test.dat +++ /dev/null @@ -1,2 +0,0 @@ -k-combined: -3.011353E-01 2.854556E-03 From e3a45bce3b090949ad2b4901f64ca48f55af3a1a Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 20 Feb 2014 18:32:51 -0500 Subject: [PATCH 153/192] updated results file for source point latest test --- tests/test_sourcepoint_latest/results_true.dat | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_sourcepoint_latest/results_true.dat b/tests/test_sourcepoint_latest/results_true.dat index 748df0351..00a65f913 100644 --- a/tests/test_sourcepoint_latest/results_true.dat +++ b/tests/test_sourcepoint_latest/results_true.dat @@ -1,3 +1,2 @@ k-combined: -0.000000E+00 0.000000E+00 --9.438655E-02 -4.436810E+00 -2.416825E+00 +3.011353E-01 2.854556E-03 From e63746cdb8247308e9cd5a64ba07029cbf402b7b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 21 Feb 2014 10:21:32 -0500 Subject: [PATCH 154/192] added cleanup script to remove all binary files from directories --- tests/cleanup.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 tests/cleanup.bash diff --git a/tests/cleanup.bash b/tests/cleanup.bash new file mode 100755 index 000000000..d23b166a2 --- /dev/null +++ b/tests/cleanup.bash @@ -0,0 +1,14 @@ +# This simple script ensures that all binary +# output files have been deleted in all the +# folders. This can occur if a previous error +# occurred and the test suite was rerun without +# deleting left over binary files. This will +# cause an assertion error in some of the +# tests. +for i in ./*; do + if [ -d "$i" ]; then + cd $i + rm -f *.binary *.h5 + cd .. + fi +done From d1a42b79ce8b6c485ed80b025d5df70aa8bf0d26 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 21 Feb 2014 10:50:18 -0500 Subject: [PATCH 155/192] fixed deleting binary files --- tests/test_sourcepoint_restart/test_sourcepoint_restart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_sourcepoint_restart/test_sourcepoint_restart.py b/tests/test_sourcepoint_restart/test_sourcepoint_restart.py index a4387405b..9b2e3185f 100644 --- a/tests/test_sourcepoint_restart/test_sourcepoint_restart.py +++ b/tests/test_sourcepoint_restart/test_sourcepoint_restart.py @@ -126,7 +126,7 @@ def test_results_serial(): def teardown(): output = glob.glob(pwd + '/statepoint.7.*') - output += glob.glob(pwd + '/sourcepoint.7.*') + output += glob.glob(pwd + '/source.7.*') output.append(pwd + '/results_test.dat') for f in output: if os.path.exists(f): From 411aea322dddb42ca66975e600d7a7de6490e971 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 21 Feb 2014 14:14:56 -0500 Subject: [PATCH 156/192] restructured sourcepoint restart test --- tests/test_sourcepoint_restart/results.py | 2 +- .../test_sourcepoint_restart/results_true.dat | 3378 ++++++++--------- tests/test_sourcepoint_restart/settings.xml | 4 +- .../test_sourcepoint_restart.py | 46 +- 4 files changed, 1713 insertions(+), 1717 deletions(-) diff --git a/tests/test_sourcepoint_restart/results.py b/tests/test_sourcepoint_restart/results.py index d0b3a55e0..f4c32a893 100644 --- a/tests/test_sourcepoint_restart/results.py +++ b/tests/test_sourcepoint_restart/results.py @@ -11,7 +11,7 @@ import statepoint if len(sys.argv) > 1: sp = statepoint.StatePoint(sys.argv[1]) else: - sp = statepoint.StatePoint('statepoint.7.binary') + sp = statepoint.StatePoint('statepoint.10.binary') sp.read_results() # extract tally results and convert to vector diff --git a/tests/test_sourcepoint_restart/results_true.dat b/tests/test_sourcepoint_restart/results_true.dat index f2b97af34..ad94d7645 100644 --- a/tests/test_sourcepoint_restart/results_true.dat +++ b/tests/test_sourcepoint_restart/results_true.dat @@ -1,16 +1,16 @@ k-combined: -0.000000E+00 0.000000E+00 +3.011353E-01 2.854556E-03 tally 1: -3.000000E-03 -5.000000E-06 -1.350207E-03 -9.349751E-07 --7.677912E-05 -4.241317E-07 --3.387424E-04 -1.343262E-07 -1.215119E-03 -9.127703E-07 +8.000000E-03 +1.600000E-05 +6.192275E-04 +1.370333E-06 +-8.662964E-04 +7.279501E-07 +-1.041312E-05 +3.860351E-07 +4.821963E-03 +5.791161E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -41,16 +41,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -3.000000E-03 -9.000000E-06 -1.889613E-04 -3.570639E-08 -2.320049E-04 -5.382627E-08 -1.491658E-03 -2.225044E-06 -1.817087E-03 -2.362232E-06 +1.100000E-02 +3.300000E-05 +4.387087E-03 +7.410082E-06 +1.860042E-03 +2.867148E-06 +3.399756E-03 +5.784483E-06 +5.116596E-03 +6.752240E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -59,6 +59,18 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +3.028119E-04 +9.169503E-08 +1.000000E-03 +1.000000E-06 +5.438971E-04 +2.958240E-07 +-5.626399E-05 +3.165637E-09 +-4.136011E-04 +1.710658E-07 +6.013419E-04 +3.616120E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -69,6 +81,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +5.000000E-03 +1.300000E-05 +3.577754E-03 +6.496319E-06 +1.890975E-03 +1.788514E-06 +9.937593E-04 +4.944395E-07 +1.803302E-03 +1.626040E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -139,38 +161,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -4.000000E-05 -1.075518E-03 -3.528151E-06 -2.716562E-03 -1.166700E-05 -2.288359E-03 -2.867176E-06 -3.411472E-03 -8.255676E-06 +1.600000E-02 +6.600000E-05 +5.883798E-03 +1.472349E-05 +5.964243E-03 +1.758637E-05 +4.341846E-03 +6.731059E-06 +7.631087E-03 +1.536471E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -201,56 +201,56 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +1.300000E-02 +3.900000E-05 +1.678255E-03 +1.856663E-06 +3.802861E-03 +6.462034E-06 +-4.813541E-05 +2.987521E-06 +5.422032E-03 +6.871180E-06 +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.028119E-04 +9.169503E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 -1.700000E-05 -3.530317E-04 -1.159034E-06 -2.030571E-03 -4.217108E-06 -9.628222E-04 -4.775997E-07 -1.817087E-03 -2.362232E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -2.000000E-06 -4.849895E-04 -9.196474E-07 -3.794711E-04 -4.964654E-07 -8.024923E-04 -3.245502E-07 -1.237485E-03 -9.676259E-07 +5.000000E-06 +1.710047E-03 +2.156886E-06 +7.353287E-04 +1.688467E-06 +1.623935E-03 +1.552163E-06 +2.439928E-03 +1.509909E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -321,16 +321,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -7.000000E-03 -2.500000E-05 -1.464813E-03 -1.309819E-06 -8.835792E-04 -6.990079E-07 -3.177051E-03 -5.159010E-06 -3.366739E-03 -5.697495E-06 +1.400000E-02 +4.200000E-05 +4.680073E-03 +4.832992E-06 +9.241957E-04 +9.879528E-07 +2.560088E-03 +5.615638E-06 +7.279260E-03 +1.195027E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -339,8 +339,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -3.009839E-04 -9.059133E-08 +5.992726E-04 +1.795675E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -361,18 +361,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -3.000000E-03 -5.000000E-06 -1.881176E-03 -2.067359E-06 -2.892742E-04 -1.089921E-07 --9.095343E-04 -4.184955E-07 -1.237485E-03 -9.676259E-07 -0.000000E+00 -0.000000E+00 +9.000000E-03 +1.900000E-05 +4.178982E-03 +4.155519E-06 +3.346853E-03 +6.203486E-06 +1.267269E-03 +3.209807E-06 +3.940659E-03 +3.581639E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -381,6 +379,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +3.028119E-04 +9.169503E-08 0.000000E+00 0.000000E+00 0.000000E+00 @@ -401,16 +401,16 @@ tally 1: 0.000000E+00 3.121671E-04 9.744828E-08 -1.000000E-03 -1.000000E-06 -8.504018E-04 -7.231831E-07 -5.847747E-04 -3.419615E-07 -2.618879E-04 -6.858528E-08 -6.243342E-04 -3.897931E-07 +6.000000E-03 +1.200000E-05 +-2.469438E-05 +1.110126E-06 +6.006714E-04 +6.607712E-07 +1.988388E-03 +1.233934E-06 +3.924085E-03 +4.427425E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -481,16 +481,26 @@ tally 1: 7.229539E-07 3.009839E-04 9.059133E-08 -9.000000E-03 -4.500000E-05 -5.336900E-03 -1.424486E-05 -3.093810E-03 -5.161998E-06 -2.879582E-03 -4.229500E-06 -4.002256E-03 -8.501473E-06 +2.200000E-02 +1.140000E-04 +1.396444E-02 +4.152030E-05 +9.394828E-03 +1.953841E-05 +7.823718E-03 +1.254396E-05 +1.002065E-02 +2.139199E-05 +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.056238E-04 +3.667801E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -511,26 +521,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.600000E-02 -1.280000E-04 -4.224331E-03 -9.782839E-06 -3.223102E-03 -5.736299E-06 -2.099435E-03 -2.209094E-06 -7.346629E-03 -2.710118E-05 +3.200000E-02 +2.140000E-04 +1.152532E-02 +2.810551E-05 +8.134203E-03 +1.622011E-05 +3.372833E-03 +2.907244E-06 +1.577229E-02 +5.159641E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -561,896 +561,16 @@ tally 1: 7.222357E-08 0.000000E+00 0.000000E+00 -4.000000E-03 -1.000000E-05 -3.193650E-03 -5.924989E-06 -2.012845E-03 -2.064950E-06 -1.030603E-03 -6.338801E-07 -3.656540E-03 -7.357018E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -1.000000E-05 -5.813261E-04 -2.815138E-07 --5.070667E-04 -2.458625E-07 --5.126590E-04 -1.937439E-07 -2.162803E-03 -2.798572E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.500000E-02 -1.130000E-04 -3.838937E-03 -7.695404E-06 -3.258655E-03 -8.032841E-06 -7.822538E-04 -5.238913E-06 -7.034462E-03 -2.505476E-05 -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.121671E-04 -9.744828E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.200000E-02 -7.400000E-05 -3.703109E-03 -7.450207E-06 -1.172561E-03 -6.983603E-06 -9.775789E-04 -3.396069E-06 -5.183826E-03 -1.446969E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.600000E-02 -1.300000E-04 -5.461842E-03 -1.700217E-05 --1.194197E-03 -2.155631E-06 --4.418175E-04 -1.630461E-07 -7.647613E-03 -2.954714E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -2.000000E-05 -2.288086E-03 -4.056180E-06 -2.803000E-03 -6.212022E-06 -2.253444E-03 -3.299961E-06 -3.043389E-03 -5.316010E-06 -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.121671E-04 -9.744828E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -4.100000E-05 --1.469649E-03 -6.515270E-06 -3.164874E-03 -5.070423E-06 --7.834805E-04 -2.163764E-06 -3.991073E-03 -8.036254E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -6.578244E-04 -4.327330E-07 -1.490994E-04 -2.223064E-08 --2.750809E-04 -7.566948E-08 -6.243342E-04 -3.897931E-07 -0.000000E+00 -0.000000E+00 -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.000000E-03 -2.500000E-05 -4.192244E-04 -2.440829E-06 -2.290352E-04 -2.955670E-08 -8.117866E-05 -3.786185E-09 -4.292057E-03 -9.213941E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.100000E-02 -7.300000E-05 -4.050236E-03 -2.014829E-05 -3.589950E-03 -7.394738E-06 -3.301506E-03 -7.271497E-06 -5.574275E-03 -2.054932E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -8.000000E-06 -6.865027E-04 -3.148594E-06 -1.123135E-03 -7.553928E-07 --6.937330E-04 -5.371067E-07 -1.226302E-03 -7.521585E-07 -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.009839E-04 -9.059133E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -2.500000E-05 -4.720935E-03 -1.123308E-05 -2.531138E-03 -3.881873E-06 -1.207797E-03 -1.104982E-06 -3.991073E-03 -8.036254E-06 -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.121671E-04 -9.744828E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.805904E-03 -3.261288E-06 -2.000000E-03 -4.000000E-06 -1.904158E-03 -3.625820E-06 -1.722222E-03 -2.966050E-06 -1.472450E-03 -2.168109E-06 -0.000000E+00 -0.000000E+00 -2.200000E-02 -2.440000E-04 -8.997789E-03 -4.069446E-05 -6.038380E-03 -2.892796E-05 -3.188711E-03 -1.706565E-05 -1.071337E-02 -5.765023E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.121671E-04 -9.744828E-08 -1.000000E-03 -1.000000E-06 -9.994298E-04 -9.988600E-07 -9.982900E-04 -9.965829E-07 -9.965814E-04 -9.931746E-07 -0.000000E+00 -0.000000E+00 -1.100000E-02 -6.100000E-05 -1.446955E-03 -5.203867E-06 -1.233625E-03 -3.439950E-06 -2.123440E-03 -6.107272E-06 -3.366739E-03 -5.697495E-06 -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.121671E-04 -9.744828E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -1.600000E-05 -3.062793E-03 -9.380701E-06 -2.224463E-03 -4.948238E-06 -2.136221E-03 -4.563441E-06 -3.009839E-03 -9.059133E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.009839E-04 -9.059133E-08 -1.000000E-03 -1.000000E-06 -1.071043E-04 -1.147134E-08 --4.827930E-04 -2.330891E-07 --1.575849E-04 -2.483301E-08 -3.009839E-04 -9.059133E-08 -8.000000E-03 -3.400000E-05 -3.469885E-03 -1.204729E-05 -1.706287E-04 -4.770906E-06 -6.093070E-04 -3.706122E-07 -3.656540E-03 -7.357018E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.009839E-04 -9.059133E-08 -1.000000E-03 -1.000000E-06 -9.276723E-04 -8.605758E-07 -7.908638E-04 -6.254655E-07 -6.043224E-04 -3.652056E-07 -3.009839E-04 -9.059133E-08 -1.500000E-02 -1.130000E-04 -7.760317E-03 -3.043383E-05 -3.486730E-03 -6.079687E-06 -3.192886E-03 -5.300269E-06 -5.206192E-03 -1.357459E-05 -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.121671E-04 -9.744828E-08 -1.000000E-03 -1.000000E-06 --2.723974E-04 -7.420035E-08 --3.886995E-04 -1.510873E-07 -3.580662E-04 -1.282114E-07 -3.121671E-04 -9.744828E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.800000E-02 -1.620000E-04 -3.109478E-03 -5.403087E-06 -7.420753E-04 -5.610775E-06 --1.972647E-03 -2.210831E-06 -7.056828E-03 -2.499410E-05 -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.253181E-04 -4.803845E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.203936E-03 -1.449461E-06 -2.000000E-03 -4.000000E-06 -1.876080E-03 -3.519675E-06 -1.640561E-03 -2.691440E-06 -1.316649E-03 -1.733565E-06 -0.000000E+00 -0.000000E+00 -2.000000E-03 -2.000000E-06 -1.656132E-03 -1.374995E-06 -1.062492E-03 -5.867156E-07 -3.772098E-04 -1.191478E-07 -3.054572E-03 -4.820460E-06 -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.131510E-04 -1.880396E-07 -1.000000E-03 -1.000000E-06 --5.333309E-04 -2.844418E-07 --7.333726E-05 -5.378353E-09 -4.207423E-04 -1.770241E-07 -6.243342E-04 -3.897931E-07 -0.000000E+00 -0.000000E+00 -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.000000E-03 -2.000000E-05 -1.583270E-03 -3.252679E-06 --3.064030E-04 -4.947979E-06 -1.080830E-03 -8.869004E-07 -2.452604E-03 -3.008634E-06 -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.131510E-04 -1.880396E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.200000E-02 -9.000000E-05 -5.530475E-03 -1.529320E-05 -2.705859E-03 -5.488988E-06 -3.266400E-03 -5.350316E-06 -7.112744E-03 -3.142384E-05 -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.121671E-04 -9.744828E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.100000E-02 -2.250000E-04 -7.403253E-03 -3.016107E-05 -5.420662E-03 -1.983684E-05 -2.936582E-03 -4.922875E-06 -8.238398E-03 -3.592572E-05 -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.253181E-04 -4.803845E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.100000E-02 -6.100000E-05 -3.184470E-03 -6.981764E-06 -3.311201E-03 -5.530952E-06 -2.002853E-03 -2.093961E-06 -3.968707E-03 -8.234052E-06 -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.131510E-04 -1.880396E-07 -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.365012E-04 -8.770346E-07 -1.000000E-03 -1.000000E-06 -9.677184E-04 -9.364790E-07 -9.047184E-04 -8.185155E-07 -8.140422E-04 -6.626647E-07 -0.000000E+00 -0.000000E+00 1.000000E-02 -6.800000E-05 -1.266540E-03 -1.529518E-06 --3.692635E-04 -9.672879E-07 -1.689168E-03 -1.866327E-06 -4.035806E-03 -1.215361E-05 +2.400000E-05 +7.364203E-03 +1.356715E-05 +3.578025E-03 +3.952934E-06 +4.991388E-04 +8.277617E-07 +6.660384E-03 +1.078466E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1482,15 +602,15 @@ tally 1: 0.000000E+00 0.000000E+00 1.200000E-02 -8.000000E-05 -3.410989E-03 -6.495638E-06 -8.888873E-04 -4.027331E-06 -6.828019E-04 -1.205374E-06 -4.894025E-03 -1.211286E-05 +4.800000E-05 +7.631639E-04 +2.262064E-06 +-3.448925E-04 +1.205043E-06 +-3.259600E-03 +4.482377E-06 +5.459930E-03 +8.074061E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1499,8 +619,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -6.243342E-04 -3.897931E-07 +5.989596E-04 +1.793791E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1521,16 +641,26 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -1.100000E-02 -7.300000E-05 -3.976139E-03 -9.939638E-06 -1.462256E-03 -2.640635E-06 -2.026283E-03 -3.803255E-06 -4.604224E-03 -1.067567E-05 +2.600000E-02 +1.820000E-04 +5.527216E-03 +9.741352E-06 +6.494815E-03 +1.331417E-05 +2.487729E-04 +9.009137E-06 +1.212394E-02 +3.763410E-05 +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.111267E-04 +2.768274E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1549,28 +679,40 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -9.029518E-04 -8.153220E-07 +0.000000E+00 +0.000000E+00 +2.600000E-02 +1.400000E-04 +8.583494E-03 +1.976212E-05 +2.895567E-03 +1.210757E-05 +3.612948E-03 +7.374439E-06 +1.090179E-02 +2.581708E-05 +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.982887E-04 +8.897613E-08 1.000000E-03 1.000000E-06 -9.531896E-04 -9.085703E-07 -8.628555E-04 -7.445196E-07 -7.353151E-04 -5.406883E-07 +9.407668E-04 +8.850421E-07 +8.275632E-04 +6.848608E-07 +6.703954E-04 +4.494300E-07 +5.965773E-04 +3.559045E-07 0.000000E+00 0.000000E+00 -3.000000E-03 -9.000000E-06 -1.969150E-03 -3.877552E-06 -1.225946E-03 -1.502944E-06 -1.367753E-03 -1.870749E-06 -1.504920E-03 -2.264783E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1579,278 +721,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -3.009839E-04 -9.059133E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -2.600000E-05 --3.423632E-04 -2.476095E-06 -2.144575E-03 -2.456203E-06 --1.035289E-03 -3.066348E-06 -3.678906E-03 -6.769426E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -4.000000E-05 -5.417577E-03 -1.889555E-05 -2.293131E-03 -3.303280E-06 -6.729020E-04 -3.229421E-07 -5.206192E-03 -1.357459E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.100000E-02 -7.300000E-05 -7.262770E-03 -3.815108E-05 -4.265119E-03 -1.234667E-05 -2.317314E-03 -2.894105E-06 -3.355556E-03 -5.998148E-06 -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.019679E-04 -3.623653E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -9.000000E-06 -5.774620E-04 -3.334623E-07 -3.789825E-04 -1.436277E-07 --1.360725E-04 -1.851572E-08 -1.805904E-03 -3.261288E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -3.400000E-05 --1.376386E-04 -3.479848E-08 --1.136484E-03 -8.968777E-07 --6.910241E-04 -3.879057E-07 -3.344373E-03 -6.674880E-06 -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.131510E-04 -1.880396E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.100000E-02 -7.300000E-05 -3.617203E-03 -1.222945E-05 -1.595056E-03 -1.290135E-06 --6.608603E-04 -1.154300E-06 -4.024623E-03 -1.056015E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 --2.171894E-04 -4.717122E-08 --4.292432E-04 -1.842497E-07 -3.001713E-04 -9.010283E-08 -3.121671E-04 -9.744828E-08 -0.000000E+00 -0.000000E+00 -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.000000E-03 -1.700000E-05 -3.376537E-03 -6.867510E-06 -1.576049E-03 -1.242043E-06 -6.774572E-04 -3.539001E-07 -2.787137E-03 -5.137331E-06 +2.700000E-02 +1.750000E-04 +1.012358E-02 +2.467953E-05 +1.474435E-03 +4.801327E-06 +4.424179E-04 +1.661987E-06 +1.306491E-02 +3.990010E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1882,15 +762,1135 @@ tally 1: 0.000000E+00 0.000000E+00 1.300000E-02 -8.900000E-05 -3.214925E-03 -6.383584E-06 -1.681554E-03 -1.482668E-06 -1.811720E-03 -1.724962E-06 -4.269691E-03 -9.774105E-06 +3.900000E-05 +2.894580E-03 +7.413903E-06 +4.782406E-03 +8.519965E-06 +4.310430E-03 +7.496343E-06 +6.648334E-03 +9.824958E-06 +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.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +8.600000E-05 +8.003652E-04 +9.381590E-06 +3.457615E-03 +5.280629E-06 +-1.063124E-03 +2.234064E-06 +8.804401E-03 +1.982338E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +6.578244E-04 +4.327330E-07 +1.490994E-04 +2.223064E-08 +-2.750809E-04 +7.566948E-08 +6.243342E-04 +3.897931E-07 +0.000000E+00 +0.000000E+00 +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.000000E-02 +9.400000E-05 +4.167281E-04 +3.806472E-06 +3.031990E-03 +4.457402E-06 +-2.246079E-05 +6.478142E-07 +1.000360E-02 +2.105336E-05 +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.982887E-04 +8.897613E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.900000E-02 +9.700000E-05 +6.458852E-03 +2.279404E-05 +3.627440E-03 +7.908704E-06 +3.946071E-03 +7.531590E-06 +8.885213E-03 +2.445611E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.200000E-02 +3.200000E-05 +5.376132E-03 +1.377498E-05 +3.290467E-03 +4.921168E-06 +1.422861E-03 +3.610761E-06 +4.838152E-03 +5.829692E-06 +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.016549E-04 +1.809943E-07 +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.056238E-04 +3.667801E-07 +1.000000E-03 +1.000000E-06 +3.227815E-04 +1.041879E-07 +-3.437182E-04 +1.181422E-07 +-4.000974E-04 +1.600779E-07 +0.000000E+00 +0.000000E+00 +2.700000E-02 +1.690000E-04 +1.259754E-02 +3.887684E-05 +3.514255E-03 +9.445635E-06 +1.359344E-03 +2.045441E-06 +1.362156E-02 +4.087828E-05 +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.156499E-04 +2.795463E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.805904E-03 +3.261288E-06 +2.000000E-03 +4.000000E-06 +1.904158E-03 +3.625820E-06 +1.722222E-03 +2.966050E-06 +1.472450E-03 +2.168109E-06 +0.000000E+00 +0.000000E+00 +3.900000E-02 +3.490000E-04 +1.656780E-02 +6.023774E-05 +1.361639E-02 +5.348538E-05 +9.429236E-03 +3.947382E-05 +1.761092E-02 +7.600968E-05 +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.028119E-04 +9.169503E-08 +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.121671E-04 +9.744828E-08 +1.000000E-03 +1.000000E-06 +9.994298E-04 +9.988600E-07 +9.982900E-04 +9.965829E-07 +9.965814E-04 +9.931746E-07 +0.000000E+00 +0.000000E+00 +2.600000E-02 +1.600000E-04 +8.920548E-03 +2.629362E-05 +5.151221E-03 +8.876114E-06 +5.653525E-03 +1.067857E-05 +1.116368E-02 +2.969235E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.209415E-03 +5.437558E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +1.800000E-05 +3.041714E-03 +1.129968E-05 +4.102928E-03 +6.714470E-06 +2.016161E-03 +6.122339E-06 +3.916134E-03 +9.516316E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.009839E-04 +9.059133E-08 +1.000000E-03 +1.000000E-06 +1.071043E-04 +1.147134E-08 +-4.827930E-04 +2.330891E-07 +-1.575849E-04 +2.483301E-08 +3.009839E-04 +9.059133E-08 +2.300000E-02 +1.150000E-04 +7.537245E-03 +2.829767E-05 +2.103092E-03 +9.662063E-06 +2.183497E-03 +3.744408E-06 +1.057909E-02 +2.415941E-05 +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.982887E-04 +8.897613E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.209420E-03 +9.158466E-07 +1.000000E-03 +1.000000E-06 +9.276723E-04 +8.605758E-07 +7.908638E-04 +6.254655E-07 +6.043224E-04 +3.652056E-07 +3.009839E-04 +9.059133E-08 +2.500000E-02 +1.510000E-04 +1.154954E-02 +3.543399E-05 +4.217931E-03 +7.652974E-06 +1.816814E-03 +6.073065E-06 +9.720055E-03 +2.272026E-05 +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.121671E-04 +9.744828E-08 +1.000000E-03 +1.000000E-06 +-2.723974E-04 +7.420035E-08 +-3.886995E-04 +1.510873E-07 +3.580662E-04 +1.282114E-07 +3.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +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.200000E-02 +3.560000E-04 +1.287791E-02 +4.221690E-05 +4.013355E-03 +1.949190E-05 +1.990533E-03 +9.786357E-06 +1.727222E-02 +6.016647E-05 +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.125620E-03 +1.382988E-06 +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.112371E-03 +2.274717E-06 +4.000000E-03 +8.000000E-06 +3.826961E-03 +7.325612E-06 +3.495526E-03 +6.132335E-06 +3.033406E-03 +4.680820E-06 +0.000000E+00 +0.000000E+00 +1.300000E-02 +4.300000E-05 +2.528669E-03 +1.367031E-05 +3.965235E-03 +4.071050E-06 +-4.149583E-04 +1.307826E-06 +7.258235E-03 +1.093717E-05 +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.114397E-04 +2.770157E-07 +2.000000E-03 +2.000000E-06 +-1.131644E-03 +6.424199E-07 +-3.637017E-05 +6.744918E-09 +7.827543E-04 +3.080768E-07 +9.271460E-04 +4.814882E-07 +0.000000E+00 +0.000000E+00 +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.000000E-02 +8.800000E-05 +5.382539E-03 +1.017579E-05 +2.414790E-03 +7.567027E-06 +3.961581E-03 +6.537588E-06 +9.680345E-03 +2.211393E-05 +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.159629E-04 +2.797346E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.200000E-02 +1.240000E-04 +7.269348E-03 +1.995179E-05 +2.992857E-03 +7.453704E-06 +4.698724E-03 +6.277902E-06 +1.253456E-02 +4.253483E-05 +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.121671E-04 +9.744828E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-02 +5.100000E-04 +1.664620E-02 +7.437727E-05 +9.397450E-03 +4.322575E-05 +6.836310E-03 +1.231705E-05 +1.905990E-02 +7.550592E-05 +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.122996E-03 +1.372865E-06 +1.000000E-03 +1.000000E-06 +8.132758E-04 +6.614176E-07 +4.921264E-04 +2.421884E-07 +1.248736E-04 +1.559342E-08 +9.084356E-04 +8.252553E-07 +0.000000E+00 +0.000000E+00 +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.100000E-02 +9.500000E-05 +6.491403E-03 +1.250504E-05 +2.902964E-03 +5.648898E-06 +2.149247E-03 +2.668074E-06 +8.776336E-03 +1.780557E-05 +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.131510E-04 +1.880396E-07 +1.000000E-03 +1.000000E-06 +-6.320900E-04 +3.995377E-07 +9.930661E-05 +9.861803E-09 +3.167755E-04 +1.003467E-07 +1.239313E-03 +9.687296E-07 +1.000000E-03 +1.000000E-06 +9.677184E-04 +9.364790E-07 +9.047184E-04 +8.185155E-07 +8.140422E-04 +6.626647E-07 +0.000000E+00 +0.000000E+00 +3.000000E-02 +2.020000E-04 +7.960428E-03 +1.748184E-05 +1.857609E-03 +3.751885E-06 +3.621909E-03 +7.252984E-06 +1.365034E-02 +4.336505E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.900000E-02 +1.970000E-04 +5.726309E-03 +1.204288E-05 +-2.562602E-03 +8.796860E-06 +-5.408669E-04 +1.879390E-06 +1.269835E-02 +3.424162E-05 +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.226228E-04 +4.787693E-07 +1.000000E-03 +1.000000E-06 +-1.650338E-04 +2.723616E-08 +-4.591458E-04 +2.108148E-07 +2.363135E-04 +5.584407E-08 +2.982887E-04 +8.897613E-08 +0.000000E+00 +0.000000E+00 +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.100000E-02 +1.070000E-04 +7.115739E-03 +1.595026E-05 +3.891656E-03 +4.927392E-06 +4.624860E-03 +6.824558E-06 +8.816934E-03 +1.684675E-05 +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.982887E-04 +8.897613E-08 +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.029518E-04 +8.153220E-07 +1.000000E-03 +1.000000E-06 +9.531896E-04 +9.085703E-07 +8.628555E-04 +7.445196E-07 +7.353151E-04 +5.406883E-07 +0.000000E+00 +0.000000E+00 +8.000000E-03 +1.800000E-05 +1.619128E-03 +7.769465E-06 +2.613435E-03 +3.211760E-06 +1.405519E-03 +3.351048E-06 +4.513046E-03 +5.346316E-06 +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.009839E-04 +9.059133E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.600000E-02 +1.760000E-04 +3.827326E-03 +1.821256E-05 +8.165880E-03 +1.737446E-05 +3.271322E-03 +1.161207E-05 +1.089998E-02 +2.472251E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.197919E-03 +7.175165E-07 +1.000000E-03 +1.000000E-06 +7.256235E-04 +5.265295E-07 +2.897942E-04 +8.398069E-08 +-1.332798E-04 +1.776351E-08 +3.028119E-04 +9.169503E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.400000E-02 +5.800000E-05 +8.561790E-03 +2.265799E-05 +4.539886E-03 +5.757407E-06 +1.780257E-03 +1.445049E-06 +9.412238E-03 +1.952619E-05 +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.006709E-04 +9.040301E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.900000E-02 +9.700000E-05 +1.187186E-02 +4.638870E-05 +5.669447E-03 +1.326414E-05 +2.818347E-03 +3.568113E-06 +6.363682E-03 +9.079681E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.198545E-03 +7.182698E-07 +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.013419E-04 +3.616120E-07 +1.000000E-03 +1.000000E-06 +9.751330E-04 +9.508843E-07 +9.263265E-04 +8.580808E-07 +8.553972E-04 +7.317044E-07 +0.000000E+00 +0.000000E+00 +1.000000E-02 +3.800000E-05 +5.100191E-03 +1.213804E-05 +2.526446E-03 +3.184717E-06 +1.120621E-03 +2.386039E-06 +4.520264E-03 +6.456267E-06 +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.062947E-04 +4.571831E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.700000E-02 +6.300000E-05 +3.401093E-03 +4.752976E-06 +-1.430942E-04 +2.289223E-06 +5.850560E-04 +2.116365E-06 +7.249747E-03 +1.181285E-05 +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.138219E-04 +2.784426E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.700000E-02 +9.900000E-05 +3.395904E-03 +1.353897E-05 +2.976877E-03 +2.251417E-06 +-1.066663E-04 +2.608803E-06 +7.331520E-03 +1.571258E-05 +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.989596E-04 +1.793791E-07 +2.000000E-03 +2.000000E-06 +7.367234E-04 +9.571208E-07 +4.356812E-04 +9.323439E-07 +1.039334E-03 +6.364636E-07 +1.505322E-03 +1.521066E-06 +1.000000E-03 +1.000000E-06 +9.879014E-04 +9.759491E-07 +9.639237E-04 +9.291489E-07 +9.285017E-04 +8.621153E-07 +0.000000E+00 +0.000000E+00 +1.900000E-02 +8.700000E-05 +2.777211E-03 +1.700824E-05 +4.035787E-03 +9.429368E-06 +2.980203E-05 +1.242805E-06 +1.000369E-02 +2.268829E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.900000E-02 +1.030000E-04 +7.734629E-03 +1.531457E-05 +5.093139E-03 +7.117666E-06 +5.058900E-03 +5.942325E-06 +7.269012E-03 +1.354949E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1921,16 +1921,256 @@ tally 1: 1.415159E-08 0.000000E+00 0.000000E+00 -7.000000E-03 -3.700000E-05 -6.291973E-04 -9.253012E-07 --8.619079E-04 -3.776625E-07 -4.994451E-04 -1.549611E-07 -3.690089E-03 -7.039749E-06 +1.200000E-02 +4.600000E-05 +1.345552E-03 +1.661602E-06 +-1.840162E-03 +1.071811E-06 +6.885070E-04 +5.102453E-07 +5.789780E-03 +8.745856E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +1.600000E-05 +6.633314E-03 +1.270264E-05 +4.628987E-03 +9.066793E-06 +2.905876E-03 +6.021797E-06 +4.223000E-03 +4.683691E-06 +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.013419E-04 +3.616120E-07 +1.000000E-03 +1.000000E-06 +4.295537E-04 +1.845164E-07 +-2.232254E-04 +4.982957E-08 +-4.461813E-04 +1.990778E-07 +1.207033E-03 +8.982334E-07 +2.000000E-03 +4.000000E-06 +1.368640E-03 +1.873175E-06 +5.907660E-04 +3.490045E-07 +1.853769E-04 +3.436459E-08 +0.000000E+00 +0.000000E+00 +6.000000E-03 +1.000000E-05 +3.102529E-03 +2.944216E-06 +3.553054E-03 +4.061545E-06 +2.423850E-03 +1.659791E-06 +2.405752E-03 +1.989485E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.965773E-04 +3.559045E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.200000E-02 +5.000000E-05 +7.180171E-03 +1.778928E-05 +2.622812E-03 +2.636835E-06 +1.108650E-03 +9.983372E-07 +6.665077E-03 +1.320251E-05 +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.028119E-04 +9.169503E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.200000E-02 +1.300000E-04 +1.098015E-02 +3.419417E-05 +5.083853E-03 +6.677488E-06 +2.962020E-03 +3.751133E-06 +1.115370E-02 +3.284251E-05 +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.982887E-04 +8.897613E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +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.000000E-03 +1.000000E-05 +1.904880E-03 +1.700404E-06 +-5.545137E-04 +1.781015E-06 +-9.756510E-05 +6.625846E-07 +3.325684E-03 +2.829537E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1963,134 +2203,14 @@ tally 1: 0.000000E+00 2.000000E-03 2.000000E-06 -1.439409E-03 -1.103682E-06 -6.555229E-04 -5.306118E-07 -7.043819E-05 -4.155385E-07 -1.226302E-03 -7.521585E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -4.295537E-04 -1.845164E-07 --2.232254E-04 -4.982957E-08 --4.461813E-04 -1.990778E-07 -3.121671E-04 -9.744828E-08 -0.000000E+00 -0.000000E+00 -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.000000E-03 -4.000000E-06 -1.440797E-04 -2.075896E-08 -1.581861E-03 -2.502285E-06 -7.101262E-04 -5.042792E-07 -6.019679E-04 -3.623653E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -9.000000E-06 -2.304111E-03 -5.308928E-06 -1.405559E-03 -1.975595E-06 -8.468624E-04 -7.171759E-07 -2.441421E-03 -3.141818E-06 +5.259036E-04 +2.147833E-07 +-6.778250E-04 +2.773263E-07 +-5.470874E-04 +2.096746E-07 +6.016549E-04 +1.809943E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -2122,57 +2242,25 @@ tally 1: 0.000000E+00 0.000000E+00 7.000000E-03 -2.900000E-05 -4.675157E-03 -1.278192E-05 -2.215224E-03 -3.397777E-06 -1.088961E-03 -2.486687E-06 -3.344373E-03 -6.674880E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -4.000000E-06 --2.660370E-04 -7.077569E-08 --7.919915E-04 -6.272506E-07 -2.842468E-04 -8.079625E-08 -9.141350E-04 -4.598136E-07 -0.000000E+00 -0.000000E+00 +1.700000E-05 +5.770569E-03 +1.116420E-05 +4.074122E-03 +5.715629E-06 +2.739484E-03 +2.864833E-06 +3.630946E-03 +2.739811E-06 +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.028119E-04 +9.169503E-08 0.000000E+00 0.000000E+00 0.000000E+00 @@ -2193,6 +2281,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +1.000000E-02 +2.600000E-05 +3.940359E-03 +1.345990E-05 +4.066207E-03 +4.701591E-06 +2.653786E-03 +1.896780E-06 +3.626422E-03 +2.904169E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -2201,16 +2299,18 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +8.948660E-04 +8.007852E-07 1.000000E-03 1.000000E-06 -6.738077E-05 -4.540168E-09 --4.931897E-04 -2.432361E-07 --1.003064E-04 -1.006137E-08 -3.009839E-04 -9.059133E-08 +8.049124E-04 +6.478840E-07 +4.718259E-04 +2.226197E-07 +9.635598E-05 +9.284475E-09 +9.084356E-04 +8.252553E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -2221,116 +2321,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 +4.000000E-03 4.000000E-06 -1.768784E-03 -3.128598E-06 -1.358416E-03 -1.845293E-06 -8.583748E-04 -7.368072E-07 -1.226302E-03 -7.521585E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -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.000000E-03 -5.000000E-06 -1.382401E-03 -1.135211E-06 -9.435101E-04 -9.475181E-07 -1.233939E-03 -9.782026E-07 -1.226302E-03 -7.521585E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -9.843457E-04 -9.689366E-07 -9.534048E-04 -9.089808E-07 -9.079028E-04 -8.242875E-07 -3.121671E-04 -9.744828E-08 +2.476562E-03 +1.946838E-06 +9.202571E-04 +1.346950E-06 +4.114154E-04 +1.017884E-06 +1.812898E-03 +9.066599E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -2402,11 +2402,11 @@ tally 1: 0.000000E+00 0.000000E+00 tally 2: -2.302093E-01 -2.649911E-02 -2.511941E-01 -3.155135E-02 -1.462955E+00 -1.070191E+00 -1.627347E+01 -1.324129E+02 +5.656024E-01 +6.399997E-02 +6.163195E-01 +7.599803E-02 +3.586752E+00 +2.573838E+00 +3.990119E+01 +3.185144E+02 diff --git a/tests/test_sourcepoint_restart/settings.xml b/tests/test_sourcepoint_restart/settings.xml index 5fee23d67..c405186fb 100644 --- a/tests/test_sourcepoint_restart/settings.xml +++ b/tests/test_sourcepoint_restart/settings.xml @@ -1,8 +1,8 @@ - - + + 10 diff --git a/tests/test_sourcepoint_restart/test_sourcepoint_restart.py b/tests/test_sourcepoint_restart/test_sourcepoint_restart.py index 9b2e3185f..0eaf1198c 100644 --- a/tests/test_sourcepoint_restart/test_sourcepoint_restart.py +++ b/tests/test_sourcepoint_restart/test_sourcepoint_restart.py @@ -24,49 +24,49 @@ def test_run(): assert returncode == 0 def test_created_statepoint(): - statepoint = glob.glob(pwd + '/statepoint.7.*') - assert len(statepoint) == 1 + statepoint = glob.glob(pwd + '/statepoint.*') + assert len(statepoint) == 2 assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') sourcepoint = glob.glob(pwd + '/source.7.*') assert len(sourcepoint) == 1 assert sourcepoint[0].endswith('binary') or sourcepoint[0].endswith('h5') def test_results(): - statepoint = glob.glob(pwd + '/statepoint.7.*') + 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 + os.remove(statepoint[0]) def test_restart_form1(): statepoint = glob.glob(pwd + '/statepoint.7.*') + sourcepoint = glob.glob(pwd + '/source.7.*') openmc_path = pwd + '/../../src/openmc' if int(NoseMPI.mpi_np) > 0: proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path, - '-r', statepoint[0]], + '-r', statepoint[0], sourcepoint[0]], stderr=STDOUT, stdout=PIPE) else: - proc = Popen([openmc_path, '-r', statepoint[0]], stderr=STDOUT, stdout=PIPE) + proc = Popen([openmc_path, '-r', statepoint[0], sourcepoint[0]], stderr=STDOUT, stdout=PIPE) print(proc.communicate()[0]) returncode = proc.returncode assert returncode == 0 def test_created_statepoint_form1(): - statepoint = glob.glob(pwd + '/statepoint.7.*') + statepoint = glob.glob(pwd + '/statepoint.10.*') assert len(statepoint) == 1 assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') - sourcepoint = glob.glob(pwd + '/source.7.*') - assert len(sourcepoint) == 1 - assert sourcepoint[0].endswith('binary') or sourcepoint[0].endswith('h5') def test_results_form1(): - statepoint = glob.glob(pwd + '/statepoint.7.*') + 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 + os.remove(statepoint[0]) def test_restart_form2(): statepoint = glob.glob(pwd + '/statepoint.7.*') @@ -74,50 +74,46 @@ def test_restart_form2(): openmc_path = pwd + '/../../src/openmc' if int(NoseMPI.mpi_np) > 0: proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path, - '--restart', statepoint[0], source[0]], + '--restart', statepoint[0], sourcepoint[0]], stderr=STDOUT, stdout=PIPE) else: - proc = Popen([openmc_path, '--restart', statepoint[0]], + proc = Popen([openmc_path, '--restart', statepoint[0], sourcepoint[0]], stderr=STDOUT, stdout=PIPE) print(proc.communicate()[0]) returncode = proc.returncode assert returncode == 0 def test_created_statepoint_form2(): - statepoint = glob.glob(pwd + '/statepoint.7.*') + statepoint = glob.glob(pwd + '/statepoint.10.*') assert len(statepoint) == 1 assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') - sourcepoint = glob.glob(pwd + '/source.7.*') - assert len(sourcepoint) == 1 - assert sourcepoint[0].endswith('binary') or sourcepoint[0].endswith('h5') def test_results_form2(): - statepoint = glob.glob(pwd + '/statepoint.7.*') + 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 + os.remove(statepoint[0]) def test_restart_serial(): statepoint = glob.glob(pwd + '/statepoint.7.*') + sourcepoint = glob.glob(pwd + '/source.7.*') openmc_path = pwd + '/../../src/openmc' - proc = Popen([openmc_path, '--restart', statepoint[0]], + proc = Popen([openmc_path, '--restart', statepoint[0], sourcepoint[0]], stderr=STDOUT, stdout=PIPE) print(proc.communicate()[0]) returncode = proc.returncode assert returncode == 0 def test_created_statepoint_serial(): - statepoint = glob.glob(pwd + '/statepoint.7.*') + statepoint = glob.glob(pwd + '/statepoint.10.*') assert len(statepoint) == 1 assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') - sourcepoint = glob.glob(pwd + '/source.7.*') - assert len(sourcepoint) == 1 - assert sourcepoint[0].endswith('binary') or sourcepoint[0].endswith('h5') def test_results_serial(): - statepoint = glob.glob(pwd + '/statepoint.7.*') + statepoint = glob.glob(pwd + '/statepoint.10.*') call(['python', 'results.py', statepoint[0]]) compare = filecmp.cmp('results_test.dat', 'results_true.dat') if not compare: @@ -125,8 +121,8 @@ def test_results_serial(): assert compare def teardown(): - output = glob.glob(pwd + '/statepoint.7.*') - output += glob.glob(pwd + '/source.7.*') + output = glob.glob(pwd + '/statepoint.*') + output += glob.glob(pwd + '/source.*') output.append(pwd + '/results_test.dat') for f in output: if os.path.exists(f): From 1c8eb1920db91a590f1cc533ed8cfa5ec41b41dc Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sat, 22 Feb 2014 15:14:26 -0500 Subject: [PATCH 157/192] fixed integer type for filetype during read source --- src/source.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source.F90 b/src/source.F90 index 85ef87d65..8e83b2ee9 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -29,7 +29,7 @@ contains integer(8) :: i ! loop index over bank sites integer(8) :: id ! particle id - integer(8) :: itmp ! temporary integer + integer(4) :: itmp ! temporary integer type(Bank), pointer :: src => null() ! source bank site type(BinaryOutput) :: sp ! statepoint/source binary file From f247f0c7cd0f5cb7fb22ef98598fea9f9090d50e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sat, 22 Feb 2014 17:26:31 -0500 Subject: [PATCH 158/192] added test for read source --- tests/test_source_file/geometry.xml | 8 ++ tests/test_source_file/materials.xml | 9 ++ tests/test_source_file/results.py | 25 +++++ tests/test_source_file/results_true.dat | 2 + tests/test_source_file/settings.xml | 19 ++++ tests/test_source_file/test_source_file.py | 103 +++++++++++++++++++++ 6 files changed, 166 insertions(+) create mode 100644 tests/test_source_file/geometry.xml create mode 100644 tests/test_source_file/materials.xml create mode 100644 tests/test_source_file/results.py create mode 100644 tests/test_source_file/results_true.dat create mode 100644 tests/test_source_file/settings.xml create mode 100644 tests/test_source_file/test_source_file.py diff --git a/tests/test_source_file/geometry.xml b/tests/test_source_file/geometry.xml new file mode 100644 index 000000000..612e46132 --- /dev/null +++ b/tests/test_source_file/geometry.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/test_source_file/materials.xml b/tests/test_source_file/materials.xml new file mode 100644 index 000000000..1f85510e0 --- /dev/null +++ b/tests/test_source_file/materials.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/test_source_file/results.py b/tests/test_source_file/results.py new file mode 100644 index 000000000..8ff10971c --- /dev/null +++ b/tests/test_source_file/results.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +import sys + +# 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.10.binary') +sp.read_results() + +# 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 results to file +with open('results_test.dat','w') as fh: + fh.write(outstr) diff --git a/tests/test_source_file/results_true.dat b/tests/test_source_file/results_true.dat new file mode 100644 index 000000000..ae37050f7 --- /dev/null +++ b/tests/test_source_file/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +2.977307E-01 2.848670E-03 diff --git a/tests/test_source_file/settings.xml b/tests/test_source_file/settings.xml new file mode 100644 index 000000000..98abc5acf --- /dev/null +++ b/tests/test_source_file/settings.xml @@ -0,0 +1,19 @@ + + + + + + + + 10 + 5 + 1000 + + + + + -4 -4 -4 4 4 4 + + + + diff --git a/tests/test_source_file/test_source_file.py b/tests/test_source_file/test_source_file.py new file mode 100644 index 000000000..ede988be5 --- /dev/null +++ b/tests/test_source_file/test_source_file.py @@ -0,0 +1,103 @@ +#!/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__) + +settings1=""" + + + + + + + 10 + 5 + 1000 + + + + + -4 -4 -4 4 4 4 + + + + +""" + +settings2 = """ + + + + 10 + 5 + 1000 + + + + source.10.{0} + + + +""" + +def setup(): + os.putenv('PWD', pwd) + os.chdir(pwd) + +def test_run1(): + openmc_path = pwd + '/../../src/openmc' +# if int(NoseMPI.mpi_np) > 0: + if False: + proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path], + stderr=STDOUT, stdout=PIPE) + else: + proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +def test_statepoint_exists(): + statepoint = glob.glob(pwd + '/statepoint.10.*') + assert len(statepoint) == 1 + assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5') + source = glob.glob(pwd + '/source.10.*') + assert len(statepoint) == 1 + assert source[0].endswith('binary') or source[0].endswith('h5') + +def test_run2(): + openmc_path = pwd + '/../../src/openmc' + source = glob.glob(pwd + '/source.10.*') + with open('settings.xml','w') as fh: + fh.write(settings2.format(source[0].split('.')[2])) +# if int(NoseMPI.mpi_np) > 0: + if False: + proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path], + stderr=STDOUT, stdout=PIPE) + else: + proc = Popen([openmc_path], stderr=STDOUT, stdout=PIPE) + print(proc.communicate()[0]) + returncode = proc.returncode + assert returncode == 0 + +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(): + with open('settings.xml','w') as fh: + fh.write(settings1) + output = glob.glob(pwd + '/statepoint.10.*') + output += glob.glob(pwd + '/source.10.*') + output.append(pwd + '/results_test.dat') + for f in output: + if os.path.exists(f): + os.remove(f) From dcc40c0b7d14f7034dba13719d490223c4c0300c Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Sat, 22 Feb 2014 17:33:13 -0500 Subject: [PATCH 159/192] updated documentation of read source from file --- docs/source/usersguide/input.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index 362197439..6aac10613 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -264,7 +264,9 @@ attributes/sub-elements: :file: If this attribute is given, it indicates that the source is to be read from - a binary source file whose path is given by the value of this element + a binary source file whose path is given by the value of this element. Note, + the number of source sites needs to be the same as the number of particles + simulated in a fission source generation. *Default*: None From 4cc8369dce9ff44d3007ef747979ca021cf0463d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 4 Mar 2014 17:53:25 -0500 Subject: [PATCH 160/192] Started release notes for 0.5.4. --- docs/source/releasenotes/index.rst | 1 + docs/source/releasenotes/notes_0.5.4.rst | 61 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 docs/source/releasenotes/notes_0.5.4.rst diff --git a/docs/source/releasenotes/index.rst b/docs/source/releasenotes/index.rst index 9164fa594..d649b1403 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.4 notes_0.5.3 notes_0.5.2 notes_0.5.1 diff --git a/docs/source/releasenotes/notes_0.5.4.rst b/docs/source/releasenotes/notes_0.5.4.rst new file mode 100644 index 000000000..1471e1c8e --- /dev/null +++ b/docs/source/releasenotes/notes_0.5.4.rst @@ -0,0 +1,61 @@ +.. _notes_0.5.4: + +============================== +Release Notes for OpenMC 0.5.4 +============================== + +.. note:: + These release notes are for an upcoming release of OpenMC and are still + subject to change. + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- New XML parsing backend (FoX) +- Ability to write particle track files +- Handle lost particles more gracefully (via particle track files) +- Source sites outside geometry are resampled +- Multiple random number generator streams +- plot_mesh_tally.py utility converted to use Tkinter rather than PyQt +- Script added to download ACE data from NNDC +- Mixed ASCII/binary cross_sections.xml now allowed + +--------- +Bug Fixes +--------- + +- 32c03c_: Check for valid data in cross_sections.xml +- c71ef5_: Fix bug in statepoint.py +- 8884fb_: Check for all ZAIDs for S(a,b) tables +- b38af0_: Fix XML reading on multiple levels of input +- d28750_: Fix bug in convert_xsdir.py + +.. _32c03c: https://github.com/mit-crpg/openmc/commit/32c03c +.. _c71ef5: https://github.com/mit-crpg/openmc/commit/c71ef5 +.. _8884fb: https://github.com/mit-crpg/openmc/commit/8884fb +.. _b38af0: https://github.com/mit-crpg/openmc/commit/b38af0 +.. _d28750: https://github.com/mit-crpg/openmc/commit/d28750 + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Nick Horelik `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Tuomas Viitanen `_ +- `Jon Walsh `_ From 571ba896c90ff2d71de14e74c53e18c5850af283 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 4 Mar 2014 18:59:06 -0500 Subject: [PATCH 161/192] fixed mistake --- docs/source/usersguide/input.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index 362197439..106f7ae92 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -1087,7 +1087,7 @@ sub-elements: the PNG format can often times reduce the file size by orders of magnitude without any loss of image quality. Likewise, high-resolution voxel files produced by OpenMC can be quite large, - but the equivalent SILO files will by significantly smaller. + but the equivalent SILO files will be significantly smaller. *Default*: "slice" From 3f218c431b5cd8e80bc44452b3262d324e9ac404 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 4 Mar 2014 19:28:46 -0500 Subject: [PATCH 162/192] Mention how to local install in documentation. Closes #247. --- docs/source/quickinstall.rst | 7 ++++++- docs/source/usersguide/install.rst | 10 +++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 2cee56987..ccbfabebe 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -48,7 +48,12 @@ following commands in a terminal: sudo make install This will build an executable named ``openmc`` and install it (by default in -/usr/local/bin). +/usr/local/bin). If you do not have administrator privileges, the last command +can be replaced with a local install, e.g. + +.. code-block:: sh + + make install -e prefix=$HOME/.local .. _GitHub: https://github.com/mit-crpg/openmc .. _git: http://git-scm.com diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index ff33d9a6c..2084878b7 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -189,7 +189,15 @@ the root directory of the source code: sudo make install This will build an executable named ``openmc`` and install it (by default in -/usr/local/bin). +/usr/local/bin). If you do not have administrative privileges, you can install +OpenMC locally by replacing the last command with: + +.. code-block:: sh + + make install -e prefix=$HOME/.local + +The ``prefix`` variable can be changed to any path for which you have +write-access. Compiling on Windows -------------------- From 4835a4f632f9a379ef40cfb96bc2beaeffb06f6e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 4 Mar 2014 21:10:42 -0500 Subject: [PATCH 163/192] check for present source bank before running calculation --- src/initialize.F90 | 8 +++++++- src/state_point.F90 | 35 ++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/initialize.F90 b/src/initialize.F90 index 8a4b22e90..52869a281 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -390,7 +390,7 @@ contains ! It is a source file path_source_point = argv(i) - else + else ! Different option is specified not a source file ! Source is in statepoint file path_source_point = path_state_point @@ -399,6 +399,12 @@ contains i = i - 1 end if + + else ! No command line arg after statepoint + + ! Source is assumed to be in statepoint file + path_source_point = path_state_point + end if case ('-g', '-geometry-debug', '--geometry-debug') diff --git a/src/state_point.F90 b/src/state_point.F90 index 046d748be..e31e7dea9 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -275,6 +275,13 @@ contains end if + ! Indicate where source bank is stored in statepoint + if (source_separate) then + call sp % write_data(0, "source_present") + else + call sp % write_data(1, "source_present") + end if + ! Close the file for serial writing call sp % file_close() @@ -518,6 +525,7 @@ contains integer :: length(4) integer :: int_array(3) integer, allocatable :: temp_array(:) + logical :: source_present real(8) :: real_array(3) type(TallyObject), pointer :: t => null() @@ -758,30 +766,35 @@ contains end if end if + ! Check for source in statepoint if needed + call sp % read_data(int_array(1), "source_present") + if (int_array(1) == 1) then + source_present = .true. + else + source_present = .false. + end if + + ! Check to make sure source bank is present + if (path_source_point == path_state_point .and. .not. source_present) then + message = "Source bank must be contained in statepoint restart file" + call fatal_error() + end if + ! Read source if in eigenvalue mode if (run_mode == MODE_EIGENVALUE) then ! Check if source was written out separately - if (source_separate) then + if (.not. source_present) then ! Close statepoint file call sp % file_close() - ! Set filename for source - filename = trim(path_output) // 'source.' // & - trim(to_str(restart_batch)) -#ifdef HDF5 - filename = trim(filename) // '.h5' -#else - filename = trim(filename) // '.binary' -#endif - ! Write message message = "Loading source file " // trim(filename) // "..." call write_message(1) ! Open source file - call sp % file_open(filename, 'r', serial = .false.) + call sp % file_open(path_source_point, 'r', serial = .false.) ! Read file type call sp % read_data(int_array(1), "filetype") From 6962b32de9164f615f58952e631afae52ca0af44 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 4 Mar 2014 21:50:06 -0500 Subject: [PATCH 164/192] simplified cleanup script --- tests/cleanup.bash | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/cleanup.bash b/tests/cleanup.bash index d23b166a2..0b792f9c7 100755 --- a/tests/cleanup.bash +++ b/tests/cleanup.bash @@ -5,10 +5,4 @@ # deleting left over binary files. This will # cause an assertion error in some of the # tests. -for i in ./*; do - if [ -d "$i" ]; then - cd $i - rm -f *.binary *.h5 - cd .. - fi -done +find . \( -name "*.binary" -o -name "*.h5" \) -exec rm -f {} \; From fb5ede7d9330e5e3f34614bb00fe795e02c3d34f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 4 Mar 2014 21:50:50 -0500 Subject: [PATCH 165/192] remove compile from all-tests option --- tests/run_tests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run_tests.py b/tests/run_tests.py index 49975c218..0669a99d0 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -117,6 +117,8 @@ if len(sys.argv) > 1: if j.rfind(suffix) != -1: if suffix == 'omp' and j == 'compile': continue + if j == 'compile': + continue tests__.append(j) else: tests__.append(i) # append specific test (e.g., mpi-debug) From b1c288012b438abd0cda44dd9daaca52be34fba4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 4 Mar 2014 21:54:50 -0500 Subject: [PATCH 166/192] removed source from separate and write tags --- src/input_xml.F90 | 8 ++++---- tests/test_sourcepoint_latest/settings.xml | 2 +- tests/test_sourcepoint_restart/settings.xml | 2 +- tests/test_statepoint_sourcesep/settings.xml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 9fffea5d3..03ad982cb 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -670,14 +670,14 @@ contains end if ! Check if the user has specified to write binary source file - if (check_for_node(node_sp, "source_separate")) then - call get_node_value(node_sp, "source_separate", temp_str) + if (check_for_node(node_sp, "separate")) then + call get_node_value(node_sp, "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) + if (check_for_node(node_sp, "write")) then + call get_node_value(node_sp, "write", temp_str) call lower_case(temp_str) if (trim(temp_str) == 'false' .or. & trim(temp_str) == '0') source_write = .false. diff --git a/tests/test_sourcepoint_latest/settings.xml b/tests/test_sourcepoint_latest/settings.xml index 36c5c7a0d..5056db472 100644 --- a/tests/test_sourcepoint_latest/settings.xml +++ b/tests/test_sourcepoint_latest/settings.xml @@ -1,7 +1,7 @@ - + 10 diff --git a/tests/test_sourcepoint_restart/settings.xml b/tests/test_sourcepoint_restart/settings.xml index c405186fb..4e9b12d24 100644 --- a/tests/test_sourcepoint_restart/settings.xml +++ b/tests/test_sourcepoint_restart/settings.xml @@ -2,7 +2,7 @@ - + 10 diff --git a/tests/test_statepoint_sourcesep/settings.xml b/tests/test_statepoint_sourcesep/settings.xml index 98abc5acf..17d4ee2e2 100644 --- a/tests/test_statepoint_sourcesep/settings.xml +++ b/tests/test_statepoint_sourcesep/settings.xml @@ -2,7 +2,7 @@ - + 10 From 4ca97dde96d71fc27168628b7c80790be96e1950 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 4 Mar 2014 23:32:41 -0500 Subject: [PATCH 167/192] added source present to statepoint.py and incremented version --- src/constants.F90 | 2 +- src/utils/statepoint.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/constants.F90 b/src/constants.F90 index 05327153d..909146014 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -11,7 +11,7 @@ module constants integer, parameter :: VERSION_RELEASE = 3 ! Revision numbers for binary files - integer, parameter :: REVISION_STATEPOINT = 10 + integer, parameter :: REVISION_STATEPOINT = 11 integer, parameter :: REVISION_PARTICLE_RESTART = 1 ! Binary file types diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index 430abee8b..0bfb66716 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -151,7 +151,7 @@ class StatePoint(object): # Read statepoint revision self.revision = self._get_int(path='revision')[0] - if self.revision != 10: + if self.revision != 11: raise Exception('Statepoint Revision is not consistent.') # Read OpenMC version @@ -342,6 +342,12 @@ class StatePoint(object): if not self._results: self.read_results() + # Source bank present + source_present = self._get_int(path='source_present')[0] + if source_present != 1: + print('Source bank not present.') + return + # For HDF5 state points, copy entire bank if self._hdf5: source_sites = self._f['source_bank'].value From 76cbf0ffaaf0d26db89b89befaba1460f94c7120 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 13:34:59 -0500 Subject: [PATCH 168/192] moved source_present data to the end of meta data section of statepoint writing --- src/state_point.F90 | 42 ++++++++++++++++++++--------------------- src/utils/statepoint.py | 14 ++++++++++---- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/state_point.F90 b/src/state_point.F90 index e31e7dea9..4cf62c7c1 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -232,6 +232,13 @@ contains end if + ! Indicate where source bank is stored in statepoint + if (source_separate) then + call sp % write_data(0, "source_present") + else + call sp % write_data(1, "source_present") + end if + ! Check for the no-tally-reduction method if (.not. reduce_tallies) then ! If using the no-tally-reduction method, we need to collect tally @@ -275,13 +282,6 @@ contains end if - ! Indicate where source bank is stored in statepoint - if (source_separate) then - call sp % write_data(0, "source_present") - else - call sp % write_data(1, "source_present") - end if - ! Close the file for serial writing call sp % file_close() @@ -730,6 +730,20 @@ contains end do TALLY_METADATA + ! Check for source in statepoint if needed + call sp % read_data(int_array(1), "source_present") + if (int_array(1) == 1) then + source_present = .true. + else + source_present = .false. + end if + + ! Check to make sure source bank is present + if (path_source_point == path_state_point .and. .not. source_present) then + message = "Source bank must be contained in statepoint restart file" + call fatal_error() + end if + ! Read tallies to master if (master) then @@ -766,20 +780,6 @@ contains end if end if - ! Check for source in statepoint if needed - call sp % read_data(int_array(1), "source_present") - if (int_array(1) == 1) then - source_present = .true. - else - source_present = .false. - end if - - ! Check to make sure source bank is present - if (path_source_point == path_state_point .and. .not. source_present) then - message = "Source bank must be contained in statepoint restart file" - call fatal_error() - end if - ! Read source if in eigenvalue mode if (run_mode == MODE_EIGENVALUE) then diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index 0bfb66716..d64ad0966 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -298,6 +298,13 @@ class StatePoint(object): f.stride = stride stride *= f.length + # Source bank present + source_present = self._get_int(path='source_present')[0] + if source_present == 1: + self.source_present = True + else: + self.source_present = False + # Set flag indicating metadata has already been read self._metadata = True @@ -342,10 +349,9 @@ class StatePoint(object): if not self._results: self.read_results() - # Source bank present - source_present = self._get_int(path='source_present')[0] - if source_present != 1: - print('Source bank not present.') + # Check if source bank is in statepoint + if not self.source_present: + print('Source not in statepoint file.') return # For HDF5 state points, copy entire bank From 23bd929683c66ae4abd4379f66f6606391c2cbfd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 5 Mar 2014 16:55:40 -0500 Subject: [PATCH 169/192] Update settings.xml RELAX NG schema. --- src/relaxng/settings.rnc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc index 0e4942e35..fb6a8b153 100644 --- a/src/relaxng/settings.rnc +++ b/src/relaxng/settings.rnc @@ -102,10 +102,10 @@ element settings { (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 separate { xsd:boolean } | + attribute separate { xsd:boolean })? & + (element write { xsd:boolean } | + attribute write { xsd:boolean })? & (element overwrite_latest { xsd:boolean} | attribute overwrite_latest {xsd:boolean})? }? & From 1abdcff45880c28d58f5d3882f680dede4c46aa4 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 5 Mar 2014 17:11:52 -0500 Subject: [PATCH 170/192] Make batches/interval sub-attribute optional in settings.rnc. --- src/relaxng/settings.rnc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc index fb6a8b153..58699a2a2 100644 --- a/src/relaxng/settings.rnc +++ b/src/relaxng/settings.rnc @@ -101,7 +101,7 @@ element settings { attribute batches { list { xsd:positiveInteger+ } }) | (element interval { xsd:positiveInteger } | attribute interval { xsd:positiveInteger }) - ) & + )? & (element separate { xsd:boolean } | attribute separate { xsd:boolean })? & (element write { xsd:boolean } | From 40fb29b819a13f23bcf5b958d79499a48d1a8cac Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 17:21:22 -0500 Subject: [PATCH 171/192] removed check variable to address @paulromano comment --- src/input_xml.F90 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 03ad982cb..fffd4a54e 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -58,7 +58,6 @@ contains integer(8) :: temp_long integer :: n_tracks logical :: file_exists - logical :: check character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename @@ -702,10 +701,9 @@ contains ! make sure that the sourcepoint batch numbers are contained in the ! statepoint list if (.not. source_separate) then - check = .true. do i = 1, n_source_points - check = statepoint_batch % contains(sourcepoint_batch % get_item(i)) - if (.not. check) then + if (.not. statepoint_batch % contains(sourcepoint_batch % & + get_item(i))) then message = 'Sourcepoint batches are not a subset& & of statepoint batches.' call fatal_error() From b73dfd352e453586a53da69afbc35f9a8401e554 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 17:22:20 -0500 Subject: [PATCH 172/192] formatted source_present lines as per PEP8 --- src/utils/statepoint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index d64ad0966..47809d472 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -301,9 +301,9 @@ class StatePoint(object): # Source bank present source_present = self._get_int(path='source_present')[0] if source_present == 1: - self.source_present = True + self.source_present = True else: - self.source_present = False + self.source_present = False # Set flag indicating metadata has already been read self._metadata = True From fbb692f351393998febb0c5452977ca8e75f9c9e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 17:28:52 -0500 Subject: [PATCH 173/192] removed extension from cleanup --- tests/{cleanup.bash => cleanup} | 2 ++ 1 file changed, 2 insertions(+) rename tests/{cleanup.bash => cleanup} (96%) diff --git a/tests/cleanup.bash b/tests/cleanup similarity index 96% rename from tests/cleanup.bash rename to tests/cleanup index 0b792f9c7..288c81025 100755 --- a/tests/cleanup.bash +++ b/tests/cleanup @@ -1,3 +1,5 @@ +#!/bin/bash + # This simple script ensures that all binary # output files have been deleted in all the # folders. This can occur if a previous error From 17793dfdcfdbbe97ecc2320876898b425c622782 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 17:42:45 -0500 Subject: [PATCH 174/192] check filetype before reading in source --- src/source.F90 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/source.F90 b/src/source.F90 index 8e83b2ee9..2d3f74ec9 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -49,6 +49,12 @@ contains ! Read the file type call sp % read_data(itmp, "filetype") + ! Check to make sure this is a source file + if (itmp /= FILETYPE_SOURCE) then + message = "Specified starting source file not a source file type." + call fatal_error() + end if + ! Read in the source bank call sp % read_source_bank() From 0ea86d60d0bfe44dae47597d26dc5e95db139419 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 17:44:39 -0500 Subject: [PATCH 175/192] converted source_separate to new separate tag --- tests/test_source_file/settings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_source_file/settings.xml b/tests/test_source_file/settings.xml index 98abc5acf..17d4ee2e2 100644 --- a/tests/test_source_file/settings.xml +++ b/tests/test_source_file/settings.xml @@ -2,7 +2,7 @@ - + 10 From 83ac6bc92530c8abb6d57fc8ec4492261505b9be Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 17:54:25 -0500 Subject: [PATCH 176/192] noseMPI uncommented --- tests/test_source_file/test_source_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_source_file/test_source_file.py b/tests/test_source_file/test_source_file.py index ede988be5..a4b2a4e2d 100644 --- a/tests/test_source_file/test_source_file.py +++ b/tests/test_source_file/test_source_file.py @@ -3,7 +3,7 @@ import os from subprocess import Popen, STDOUT, PIPE, call import filecmp -#from nose_mpi import NoseMPI +from nose_mpi import NoseMPI import glob pwd = os.path.dirname(__file__) From f357e370bc951717088fddd992891334baf524a9 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 17:55:44 -0500 Subject: [PATCH 177/192] uncommented mpi capability --- tests/test_source_file/test_source_file.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_source_file/test_source_file.py b/tests/test_source_file/test_source_file.py index a4b2a4e2d..9752d89b4 100644 --- a/tests/test_source_file/test_source_file.py +++ b/tests/test_source_file/test_source_file.py @@ -51,8 +51,7 @@ def setup(): def test_run1(): openmc_path = pwd + '/../../src/openmc' -# if int(NoseMPI.mpi_np) > 0: - if False: + if int(NoseMPI.mpi_np) > 0: proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path], stderr=STDOUT, stdout=PIPE) else: From 604abf205c9c0b4602813e09abe18cadb29c7bf3 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 5 Mar 2014 17:56:01 -0500 Subject: [PATCH 178/192] uncommented mpi capability in run 2 --- tests/test_source_file/test_source_file.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_source_file/test_source_file.py b/tests/test_source_file/test_source_file.py index 9752d89b4..a1b4986e5 100644 --- a/tests/test_source_file/test_source_file.py +++ b/tests/test_source_file/test_source_file.py @@ -73,8 +73,7 @@ def test_run2(): source = glob.glob(pwd + '/source.10.*') with open('settings.xml','w') as fh: fh.write(settings2.format(source[0].split('.')[2])) -# if int(NoseMPI.mpi_np) > 0: - if False: + if int(NoseMPI.mpi_np) > 0: proc = Popen([NoseMPI.mpi_exec, '-np', NoseMPI.mpi_np, openmc_path], stderr=STDOUT, stdout=PIPE) else: From 7def6a881a1d2ab6d152f8c1cc241184db0da22f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 6 Mar 2014 17:02:04 -0500 Subject: [PATCH 179/192] Change source_separate to separate in test_source_file.py. --- tests/test_source_file/test_source_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_source_file/test_source_file.py b/tests/test_source_file/test_source_file.py index a1b4986e5..85a33c031 100644 --- a/tests/test_source_file/test_source_file.py +++ b/tests/test_source_file/test_source_file.py @@ -12,7 +12,7 @@ settings1=""" - + 10 From b34cfe7bac6553a415bdd7ffd8e0a5dc735195fe Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 6 Mar 2014 17:20:11 -0500 Subject: [PATCH 180/192] Updated release notes to include two latest PRs. --- docs/source/releasenotes/notes_0.5.4.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/releasenotes/notes_0.5.4.rst b/docs/source/releasenotes/notes_0.5.4.rst index 1471e1c8e..174064e35 100644 --- a/docs/source/releasenotes/notes_0.5.4.rst +++ b/docs/source/releasenotes/notes_0.5.4.rst @@ -29,6 +29,8 @@ New Features - plot_mesh_tally.py utility converted to use Tkinter rather than PyQt - Script added to download ACE data from NNDC - Mixed ASCII/binary cross_sections.xml now allowed +- Expanded options for writing source bank +- Re-enabled ability to use source file as starting source --------- Bug Fixes From 40008dbaa49e007a0db2469dd620b69c772b307c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 6 Mar 2014 19:57:15 -0500 Subject: [PATCH 181/192] Updated release notes for 0.5.4. --- docs/source/releasenotes/notes_0.5.4.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/source/releasenotes/notes_0.5.4.rst b/docs/source/releasenotes/notes_0.5.4.rst index 174064e35..97e01b0fd 100644 --- a/docs/source/releasenotes/notes_0.5.4.rst +++ b/docs/source/releasenotes/notes_0.5.4.rst @@ -4,10 +4,6 @@ Release Notes for OpenMC 0.5.4 ============================== -.. note:: - These release notes are for an upcoming release of OpenMC and are still - subject to change. - ------------------- System Requirements ------------------- @@ -21,16 +17,17 @@ the problem at hand (mostly on the number of nuclides in the problem). New Features ------------ -- New XML parsing backend (FoX) +- Source sites outside geometry are resampled +- XML-Fortran backend replaced by FoX XML - Ability to write particle track files - Handle lost particles more gracefully (via particle track files) -- Source sites outside geometry are resampled - Multiple random number generator streams -- plot_mesh_tally.py utility converted to use Tkinter rather than PyQt +- Mesh tally plotting utility converted to use Tkinter rather than PyQt - Script added to download ACE data from NNDC - Mixed ASCII/binary cross_sections.xml now allowed - Expanded options for writing source bank - Re-enabled ability to use source file as starting source +- S(a,b) recalculation avoided when same nuclide and S(a,b) table are accessed --------- Bug Fixes @@ -41,12 +38,14 @@ Bug Fixes - 8884fb_: Check for all ZAIDs for S(a,b) tables - b38af0_: Fix XML reading on multiple levels of input - d28750_: Fix bug in convert_xsdir.py +- cf567c_: ENDF/B-VI data checked for compatibility .. _32c03c: https://github.com/mit-crpg/openmc/commit/32c03c .. _c71ef5: https://github.com/mit-crpg/openmc/commit/c71ef5 .. _8884fb: https://github.com/mit-crpg/openmc/commit/8884fb .. _b38af0: https://github.com/mit-crpg/openmc/commit/b38af0 .. _d28750: https://github.com/mit-crpg/openmc/commit/d28750 +.. _cf567c: https://github.com/mit-crpg/openmc/commit/cf567c ------------ Contributors From 943e66b59b325491fd24d9430a498ffa0e7002fa Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 6 Mar 2014 20:02:45 -0500 Subject: [PATCH 182/192] bumped version to 0.5.4 --- src/constants.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants.F90 b/src/constants.F90 index 909146014..ef0d7c8ef 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 = 3 + integer, parameter :: VERSION_RELEASE = 4 ! Revision numbers for binary files integer, parameter :: REVISION_STATEPOINT = 11 From 729a608e5ea884d77883ca87f64cc003f1647aa8 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Fri, 7 Mar 2014 14:41:54 -0500 Subject: [PATCH 183/192] Fixed p_valid sampling of energy distributions. --- src/physics.F90 | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/physics.F90 b/src/physics.F90 index 2d3cfeb39..8c98e0b79 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -1331,19 +1331,17 @@ contains ! SAMPLE ENERGY DISTRIBUTION IF THERE ARE MULTIPLE if (associated(edist % next)) then - if (edist % p_valid % n_regions > 0) then - p_valid = interpolate_tab1(edist % p_valid, E_in) + p_valid = interpolate_tab1(edist % p_valid, E_in) - if (prn() > p_valid) then - if (edist % law == 44 .or. edist % law == 61) then - call sample_energy(edist%next, E_in, E_out, mu_out) - elseif (edist % law == 66) then - call sample_energy(edist%next, E_in, E_out, A=A, Q=Q) - else - call sample_energy(edist%next, E_in, E_out) - end if - return + if (prn() > p_valid) then + if (edist % law == 44 .or. edist % law == 61) then + call sample_energy(edist%next, E_in, E_out, mu_out) + elseif (edist % law == 66) then + call sample_energy(edist%next, E_in, E_out, A=A, Q=Q) + else + call sample_energy(edist%next, E_in, E_out) end if + return end if end if From 2dd5ae6d9360a776c03cbfffe7fdd680e8c6fab5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 7 Mar 2014 18:07:01 -0500 Subject: [PATCH 184/192] Updated test results for three tests. The prompt neutron energy distribution for Pu-240 second-chance fission (MT=20) was affected by the bug in #249 for three tests. --- tests/test_natural_element/results_true.dat | 2 +- tests/test_salphabeta_multiple/results_true.dat | 2 +- tests/test_void/results_true.dat | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_natural_element/results_true.dat b/tests/test_natural_element/results_true.dat index 335f10c5a..9b2e933ab 100644 --- a/tests/test_natural_element/results_true.dat +++ b/tests/test_natural_element/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.012443E+00 2.162448E-02 +1.010190E+00 1.740589E-02 diff --git a/tests/test_salphabeta_multiple/results_true.dat b/tests/test_salphabeta_multiple/results_true.dat index bbd61cdb5..3165faa41 100644 --- a/tests/test_salphabeta_multiple/results_true.dat +++ b/tests/test_salphabeta_multiple/results_true.dat @@ -1,2 +1,2 @@ k-combined: -9.761880E-01 9.170415E-03 +9.701793E-01 8.482149E-03 diff --git a/tests/test_void/results_true.dat b/tests/test_void/results_true.dat index a64938586..e569957b7 100644 --- a/tests/test_void/results_true.dat +++ b/tests/test_void/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.010313E+00 3.162080E-02 +1.006312E+00 3.000112E-02 From 59f94b5cdfdcad1968d3bbb64677e93e81060665 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 10 Mar 2014 16:56:40 -0400 Subject: [PATCH 185/192] Fix typo 'indicies' affecting CMFD data in statepoint. --- src/cmfd_loss_operator.F90 | 2 +- src/state_point.F90 | 4 ++-- src/utils/statepoint.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 index 69be01764..b89f0f577 100644 --- a/src/cmfd_loss_operator.F90 +++ b/src/cmfd_loss_operator.F90 @@ -396,7 +396,7 @@ contains end subroutine indices_to_matrix !=============================================================================== -! MATRIX_TO_INDICES converts a matrix index to spatial and group indicies +! MATRIX_TO_INDICES converts a matrix index to spatial and group indices !=============================================================================== subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) diff --git a/src/state_point.F90 b/src/state_point.F90 index 4cf62c7c1..36eeffb39 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -104,7 +104,7 @@ contains ! Write out CMFD info if (cmfd_on) then call sp % write_data(1, "cmfd_on") - call sp % write_data(cmfd % indices, "indicies", length=4, group="cmfd") + call sp % write_data(cmfd % indices, "indices", length=4, group="cmfd") call sp % write_data(cmfd % k_cmfd, "k_cmfd", length=current_batch, & group="cmfd") call sp % write_data(cmfd % cmfd_src, "cmfd_src", & @@ -599,7 +599,7 @@ contains ! Write out CMFD info if (int_array(1) == 1) then - call sp % read_data(cmfd % indices, "indicies", length=4, group="cmfd") + call sp % read_data(cmfd % indices, "indices", 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]) diff --git a/src/utils/statepoint.py b/src/utils/statepoint.py index 47809d472..9613421e0 100644 --- a/src/utils/statepoint.py +++ b/src/utils/statepoint.py @@ -195,7 +195,7 @@ class StatePoint(object): # Read CMFD information cmfd_present = self._get_int(path='cmfd_on')[0] if cmfd_present == 1: - self.cmfd_indices = self._get_int(4, path='cmfd/indicies') + self.cmfd_indices = self._get_int(4, path='cmfd/indices') self.k_cmfd = self._get_double(self.current_batch, path='cmfd/k_cmfd') self.cmfd_src = self._get_double_array(np.product(self.cmfd_indices), From 3a980225abbc22fd70d9bf2113d0882b90d52a37 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 18 Mar 2014 08:49:32 -0400 Subject: [PATCH 186/192] removed filename from message, path_source_point instead --- src/state_point.F90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/state_point.F90 b/src/state_point.F90 index 36eeffb39..9703c3009 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -517,7 +517,6 @@ contains subroutine load_state_point() - character(MAX_FILE_LEN) :: filename character(MAX_FILE_LEN) :: path_temp character(19) :: current_time integer :: i @@ -790,7 +789,7 @@ contains call sp % file_close() ! Write message - message = "Loading source file " // trim(filename) // "..." + message = "Loading source file " // trim(path_source_point) // "..." call write_message(1) ! Open source file From e1d812727747ff0b159a099606a7afc0ad433c56 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 18 Mar 2014 08:50:44 -0400 Subject: [PATCH 187/192] if user wants overwrite latest, source must be separate --- src/input_xml.F90 | 5 ++++- tests/test_sourcepoint_latest/settings.xml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index fffd4a54e..76b1f696f 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -685,7 +685,10 @@ contains call get_node_value(node_sp, "overwrite_latest", temp_str) call lower_case(temp_str) if (trim(temp_str) == 'true' .or. & - trim(temp_str) == '1') source_latest = .true. + trim(temp_str) == '1') then + source_latest = .true. + source_separate = .true. + end if end if else ! If no tag was present, by default we keep source bank in diff --git a/tests/test_sourcepoint_latest/settings.xml b/tests/test_sourcepoint_latest/settings.xml index 5056db472..fa7f07dfa 100644 --- a/tests/test_sourcepoint_latest/settings.xml +++ b/tests/test_sourcepoint_latest/settings.xml @@ -1,7 +1,7 @@ - + 10 From b552c2e6520e71bc6b4226f8dabb96f12138d0cd Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 25 Mar 2014 17:43:48 -0400 Subject: [PATCH 188/192] source present should only be written by master --- src/state_point.F90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/state_point.F90 b/src/state_point.F90 index 9703c3009..6884620e2 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -230,13 +230,13 @@ contains end do TALLY_METADATA - end if + ! Indicate where source bank is stored in statepoint + if (source_separate) then + call sp % write_data(0, "source_present") + else + call sp % write_data(1, "source_present") + end if - ! Indicate where source bank is stored in statepoint - if (source_separate) then - call sp % write_data(0, "source_present") - else - call sp % write_data(1, "source_present") end if ! Check for the no-tally-reduction method From 20f77e4e6f5a3f95349a865900a5548804044744 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 25 Mar 2014 17:47:22 -0400 Subject: [PATCH 189/192] Updated release notes with one more bug fix. --- docs/source/releasenotes/notes_0.5.4.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/releasenotes/notes_0.5.4.rst b/docs/source/releasenotes/notes_0.5.4.rst index 97e01b0fd..a69354aa0 100644 --- a/docs/source/releasenotes/notes_0.5.4.rst +++ b/docs/source/releasenotes/notes_0.5.4.rst @@ -39,6 +39,7 @@ Bug Fixes - b38af0_: Fix XML reading on multiple levels of input - d28750_: Fix bug in convert_xsdir.py - cf567c_: ENDF/B-VI data checked for compatibility +- 6b9461_: Fix p_valid sampling inside of sample_energy .. _32c03c: https://github.com/mit-crpg/openmc/commit/32c03c .. _c71ef5: https://github.com/mit-crpg/openmc/commit/c71ef5 @@ -46,6 +47,7 @@ Bug Fixes .. _b38af0: https://github.com/mit-crpg/openmc/commit/b38af0 .. _d28750: https://github.com/mit-crpg/openmc/commit/d28750 .. _cf567c: https://github.com/mit-crpg/openmc/commit/cf567c +.. _6b9461: https://github.com/mit-crpg/openmc/commit/6b9461 ------------ Contributors From 40a5d6d294cc71951251f6fa5da0c5d9252c8196 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 25 Mar 2014 22:58:30 -0400 Subject: [PATCH 190/192] set default cmfd display to rms balance --- src/global.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global.F90 b/src/global.F90 index c27f34f9e..515b774bf 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -359,7 +359,7 @@ module global logical :: cmfd_tally_on = .true. ! CMFD display info - character(len=25) :: cmfd_display + character(len=25) :: cmfd_display = 'balance' ! Information about state points to be written integer :: n_state_points = 0 From 9ea6e9bb8d8e10f02366abb9990242486e580752 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 25 Mar 2014 22:58:56 -0400 Subject: [PATCH 191/192] updated statepoint revision documentation --- docs/source/devguide/statepoint.rst | 290 ++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) diff --git a/docs/source/devguide/statepoint.rst b/docs/source/devguide/statepoint.rst index 9862a4528..5f05c92c8 100644 --- a/docs/source/devguide/statepoint.rst +++ b/docs/source/devguide/statepoint.rst @@ -4,6 +4,296 @@ State Point Binary File Specifications ====================================== +----------- +Revision 11 +----------- + +**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:G,1:I,1:J,1:K)** + + 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) source_present** + + Flag indicated if source bank is present in the file + +**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 and source_present) + + *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 10 ----------- From ffe4e1682339066a9b9797d1f1ba41b420da4d52 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 25 Mar 2014 23:05:02 -0400 Subject: [PATCH 192/192] added default value to overwrite latest --- docs/source/usersguide/input.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index 83eb2a3d8..eebe6bfc1 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -414,6 +414,8 @@ attributes/sub-elements: source bank will be available. It should be noted that a user can set both this element to "true" and specify batches to write a permanent source bank. + *Default*: false + ```` Element ------------------------------

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