!! !! @HEADER !! !!!!********************************************************************** !! !! Zoltan Toolkit for Load-balancing, Partitioning, Ordering and Coloring !! Copyright 2012 Sandia Corporation !! !! Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, !! the U.S. Government retains certain rights in this software. !! !! Redistribution and use in source and binary forms, with or without !! modification, are permitted provided that the following conditions are !! met: !! !! 1. Redistributions of source code must retain the above copyright !! notice, this list of conditions and the following disclaimer. !! !! 2. 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. !! !! 3. Neither the name of the Corporation nor the names of the !! contributors may be used to endorse or promote products derived from !! this software without specific prior written permission. !! !! THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "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 SANDIA CORPORATION OR THE !! 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. !! !! Questions? Contact Karen Devine kddevin@sandia.gov !! Erik Boman egboman@sandia.gov !! !!!!********************************************************************** !! !! @HEADER !! module dr_input use zoltan use dr_const use mpi_h implicit none private public :: read_cmd_file, check_inp, brdcst_cmd_info, gen_par_filename, & PARIO_INFO, NEMESIS_FILE, CHACO_FILE, MM_FILE !-------------------------------------------------------------------------- ! Purpose: Determine file types for command files and read in the parallel ! ExodusII command file. ! Taken from nemesis utilites nem_spread and nem_join. !-------------------------------------------------------------------------- ! Author(s): Matthew M. St.John (9226) ! Translated to Fortran by William F. Mitchell !-------------------------------------------------------------------------- !-------------------------------------------------------------------------- ! Revision History: ! 14 April 1999: Date of creation. ! 02 September 1999: Fortran translation !-------------------------------------------------------------------------- integer(Zoltan_INT), parameter :: NEMESIS_FILE = 0, & CHACO_FILE = 1, & MM_FILE = 2 integer(Zoltan_INT), parameter :: MAX_INPUT_STR_LN = 4096 ! maximum string length ! for read_string() ! Structure used to store the information necessary for parallel I/O. type PARIO_INFO integer(Zoltan_INT) :: init_dist_pins integer(Zoltan_INT) :: dsk_list_cnt integer(Zoltan_INT), pointer :: dsk_list(:) integer(Zoltan_INT) :: rdisk integer(Zoltan_INT) :: num_dsk_ctrlrs ! The number of disk controllers. integer(Zoltan_INT) :: pdsk_add_fact ! The offset from zero used by the ! the target machine. integer(Zoltan_INT) :: zeros ! 1 - if the target machine uses leading zeros when ! designating the disk number (eg - the paragon ! uses /pfs/io_01) ! 0 - if it does not (eg - the tflop uses ! /pfs/tmp_1) integer(Zoltan_INT) :: file_type ! input file type ! The root location of the parallel disks character(len=FILENAME_MAX+1) :: pdsk_root ! The subdirectory to write files to character(len=FILENAME_MAX+1) :: pdsk_subdir ! The base name of the input file. character(len=FILENAME_MAX+1) :: pexo_fname end type PARIO_INFO contains function lowercase(string) character(len=*), intent(in) :: string character(len=len(string)) :: lowercase ! returns the string converted to lower case integer, parameter :: int_A = iachar("A"), & int_Z = iachar("Z"), & a_A_diff = iachar("a") - iachar("A") integer :: i, int_char do i=1,len(string) int_char = iachar(string(i:i)) if (int_char >= int_A .and. int_char <= int_Z) then lowercase(i:i) = achar(int_char + a_A_diff) else lowercase(i:i) = string(i:i) endif end do end function lowercase !*************************************************************************** !*************************************************************************** logical function read_cmd_file(filename, prob, pio_info) character(len=*) :: filename type(PROB_INFO) :: prob type(PARIO_INFO) :: pio_info ! ! * This function reads the ASCII parallel-exodus command file. ! * ! * Input ! * ----- ! * filename - The name of the command file. ! * pio_info - parallel I/O information. ! ! I'm really coping out here. This was written for debugging and initial ! testing of the Fortran interface, and doesn't need the full capability ! of reading the command files. So currently it assumes the command file ! looks like the Chaco test files that existed at the time it was written. ! WFM 9/1/99 ! local declarations integer, parameter :: file_cmd = 11 character(len=MAX_INPUT_STR_LN + 1) :: inp_line, command, temp_string integer :: iostat logical :: more_params !**************************** BEGIN EXECUTION ***************************** ! Open the file open(unit=file_cmd,file=filename,action='read',iostat=iostat) if (iostat /= 0) then read_cmd_file = .false. return endif ! assume no more than 15 parameters allocate(prob%params(0:15)) prob%num_params = 1 prob%params(0)%str(0) = "DEBUG_MEMORY" prob%params(0)%str(1) = "1" ! Begin parsing the input file do ! while not end of data read(unit=file_cmd,fmt="(a)",iostat=iostat) inp_line if (iostat /= 0) exit ! end of data ! skip any line that is a comment if (inp_line == '') cycle if (inp_line(1:1) == '#') cycle ! find what is before the equal sign command = inp_line(1:index(inp_line,"=")-1) ! if there is a tab, take what is before the tab if (index(command," ") /= 0) then ! "tab" command = command(1:index(command," ")-1) ! "tab" endif ! ****** File Name ****** if (lowercase(trim(command)) == "file name") then ! assumes there is one blank between "=" and the file name pio_info%pexo_fname = trim(inp_line(index(inp_line,"=")+2:)) endif !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! hacks to allow more of input file to be read (KDD, 10/2000) ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (lowercase(trim(command)) == "decomposition method") then ! assumes there is one blank between "=" and the method prob%method = trim(inp_line(index(inp_line,"=")+2:)) endif if (lowercase(trim(command)) == "file type") then ! assumes there is one blank between "=" and the file type if (lowercase(trim(inp_line(index(inp_line,"=")+2:))) == "chaco") then pio_info%file_type = CHACO_FILE else if (lowercase(trim(inp_line(index(inp_line,"=")+2:))) == "matrixmarket") then pio_info%file_type = MM_FILE else print *, "Error: zfdrive can read only Chaco or MatrixMarket format files." read_cmd_file = .false. endif endif if ((lowercase(trim(command)) == "zoltan parameters").or. & (lowercase(trim(command)) == "zoltan parameter")) then ! assumes there is one blank between "=" and the parameter name temp_string = lowercase(trim(inp_line(index(inp_line,"=")+2:))) ! assumes no blanks between second "=" and the parameter name ! skip the input line if there are no parameters specified on it. if (index(temp_string,"=").gt.0) then more_params = .true. do while (more_params) if (index(temp_string, ",").gt.0) then more_params = .true. prob%params(prob%num_params)%str(1) = & temp_string(index(temp_string,"=")+1:index(temp_string,",")-1) else more_params = .false. prob%params(prob%num_params)%str(1) = & temp_string(index(temp_string,"=")+1:) endif prob%params(prob%num_params)%str(0) = & temp_string(1:index(temp_string,"=")-1) prob%num_params = prob%num_params+1 if (more_params) then temp_string = temp_string(index(temp_string,",")+1:) do while (temp_string(1:1).eq." ") !skip white space temp_string = temp_string(2:) enddo endif enddo endif endif if (lowercase(trim(command)) == "test multi callbacks") then ! assumes there is one blank between "=" and the input value Test_Multi_Callbacks = iachar(trim(inp_line(index(inp_line,"= ")+2:))) - iachar('0') endif if (lowercase(trim(command)) == "test graph callbacks") then Test_Graph_Callbacks = iachar(trim(inp_line(index(inp_line,"= ")+2:))) - iachar('0') endif if (lowercase(trim(command)) == "test hypergraph callbacks") then Test_Hypergraph_Callbacks = iachar(trim(inp_line(index(inp_line,"= ")+2:))) - iachar('0') endif if (lowercase(trim(command)) == "test local partitions") then ! assumes there is one blank between "=" and the input value Test_Local_Partitions = iachar(trim(inp_line(index(inp_line,"=")+2:))) - iachar('0') endif if (lowercase(trim(command)) == "test generate files") then ! assumes there is one blank between "=" and the input value Test_Gen_Files = iachar(trim(inp_line(index(inp_line,"= ")+2:))) - iachar('0') endif if (lowercase(trim(command)) == "test drops") then ! assumes there is one blank between "=" and the input value Test_Drops = iachar(trim(inp_line(index(inp_line,"= ")+2:))) - iachar('0') endif if (lowercase(trim(command)) == "zdrive action") then ! assumes there is one blank between "=" and the input value Driver_Action = iachar(trim(inp_line(index(inp_line,"= ")+2:))) - iachar('0') endif !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! end of hacks to allow more of input file to be read (KDD, 10/2000) ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Added JDT 3/2007 to save the zoltanparams file name ! Note: we assume a good format here: ! zoltanparams file = thefile.name if (lowercase(trim(command)) == "zoltanparams file") then prob%ztnPrm_file = lowercase(trim(inp_line(index(inp_line,"=")+2:))) endif ! ! The other commands are not processed. In the initial tests they either ! always have the same value (in which case they are set after this loop) ! or they do not appear. end do ! while not end of data ! Assume parallel disk info is number=0 pio_info%num_dsk_ctrlrs = 0 ! Close the command file close(file_cmd) read_cmd_file = .true. end function read_cmd_file !*************************************************************************** !*************************************************************************** !*************************************************************************** logical function check_inp(prob, pio_info) type(PROB_INFO) :: prob type(PARIO_INFO) :: pio_info !**************************** BEGIN EXECUTION ***************************** ! check for the parallel Nemesis file for proc 0 if (len_trim(pio_info%pexo_fname) <= 0) then print *, "fatal: must specify file base name" check_inp = .false. return endif ! Not supporting NEMESIS ! default file type is nemesis ! if (pio_info->file_type < 0) pio_info->file_type = NEMESIS_FILE; ! !#ifndef ZOLTAN_NEMESIS ! ! * if compiling without the ZOLTAN_NEMESIS flag (i.e., not linking with ! * Nemesis library), can't use NEMESIS_FILE file type. ! ! ! if (pio_info->file_type == NEMESIS_FILE) { ! Gen_Error(0, "fatal: must link with Nemesis libraries for Nemesis " ! "file types"); ! return 0; ! } !#endif !ZOLTAN_NEMESIS !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! Check the parallel IO specifications !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! check that there is a list of disks, or a number of raids if ((pio_info%dsk_list_cnt <= 0) .and. (pio_info%num_dsk_ctrlrs < 0)) then pio_info%num_dsk_ctrlrs = 0 ! default to single directory endif ! default is not to have preceeding 0's in the disk names if (pio_info%zeros < 0) pio_info%zeros = 0 ! most systems that we deal with start their files systems with 1 not 0 if (pio_info%pdsk_add_fact < 0) pio_info%pdsk_add_fact = 1 ! ! * if there are parallel disks, then the root and subdir locations must ! * be specified ! if (pio_info%num_dsk_ctrlrs > 0 .or. pio_info%dsk_list_cnt > 0) then if (len_trim(pio_info%pdsk_root) == 0) then print *, "fatal: must specify parallel disk root name" check_inp = .false. return endif if (len_trim(pio_info%pdsk_subdir) == 0) then print *, "fatal: must specify parallel disk subdirectory" check_inp = .false. return endif else if (len_trim(pio_info%pdsk_root) == 0) then pio_info%pdsk_root = "." ! default is execution directory endif endif !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! Check the Zoltan specifications !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! ! * Make sure a load-balancing method was provided. ! if (len_trim(prob%method) == 0) then print *, "fatal: load balance method must be specified" check_inp = .false. return endif check_inp = .true. end function check_inp !*************************************************************************** !*************************************************************************** !*************************************************************************** subroutine brdcst_cmd_info(Proc, prob, pio_info) integer(Zoltan_INT) :: Proc type(PROB_INFO) :: prob type(PARIO_INFO) :: pio_info ! local declarations integer(Zoltan_INT) :: ctrl_id integer(Zoltan_INT) :: size integer(Zoltan_INT) :: ierr, i !**************************** BEGIN EXECUTION ***************************** call MPI_Bcast(Test_Multi_Callbacks, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(Test_Local_Partitions, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(Test_Drops, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(Test_Gen_Files, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(Driver_Action, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%dsk_list_cnt, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%rdisk, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%num_dsk_ctrlrs, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%pdsk_add_fact, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%zeros, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%file_type, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%pdsk_root, len(pio_info%pdsk_root), MPI_CHARACTER, & 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%pdsk_subdir, len(pio_info%pdsk_root), MPI_CHARACTER, & 0, MPI_COMM_WORLD, ierr) call MPI_Bcast(pio_info%pexo_fname, len(pio_info%pdsk_root), MPI_CHARACTER, & 0, MPI_COMM_WORLD, ierr) if(pio_info%dsk_list_cnt > 0) then if(Proc /= 0) then allocate(pio_info%dsk_list(0:pio_info%dsk_list_cnt-1)) endif call MPI_Bcast(pio_info%dsk_list, pio_info%dsk_list_cnt, MPI_INTEGER, & 0, MPI_COMM_WORLD, ierr) endif ! broadcast the param file name call MPI_Bcast(prob%ztnPrm_file, len(prob%ztnPrm_file), MPI_CHARACTER, & 0, MPI_COMM_WORLD, ierr) ! and broadcast the problem specifications call MPI_Bcast(prob%method, len(prob%method), MPI_CHARACTER, 0,MPI_COMM_WORLD,ierr) call MPI_Bcast(prob%num_params, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) if (prob%num_params > 0) then size = len(prob%params(0)%str(0)) if (Proc /= 0) then allocate(prob%params(0:prob%num_params-1)) endif do i=0,prob%num_params-1 call MPI_Bcast(prob%params(i)%str(0), size, MPI_CHARACTER, 0, & MPI_COMM_WORLD, ierr) call MPI_Bcast(prob%params(i)%str(1), size, MPI_CHARACTER, 0, & MPI_COMM_WORLD, ierr) end do endif ! now calculate where the file for this processor is if(pio_info%dsk_list_cnt <= 0) then if (pio_info%num_dsk_ctrlrs > 0) then ctrl_id = mod(Proc,pio_info%num_dsk_ctrlrs) pio_info%rdisk = ctrl_id + pio_info%pdsk_add_fact endif else ctrl_id = mod(Proc,pio_info%dsk_list_cnt) pio_info%rdisk = pio_info%dsk_list(ctrl_id) endif end subroutine brdcst_cmd_info !*************************************************************************** !*************************************************************************** !*************************************************************************** subroutine gen_par_filename(scalar_fname, par_fname, pio_info, proc_for, nprocs) character(len=*) :: scalar_fname, par_fname type(PARIO_INFO) :: pio_info integer(Zoltan_INT) :: proc_for, nprocs !---------------------------------------------------------------------------- ! * ! * Author(s): Gary Hennigan (1421) ! Translated to Fortran by William F. Mitchell ! *---------------------------------------------------------------------------- ! * Function which generates the name of a parallel file for a ! * particular processor. The function does this by appending ! * "N.p" to the end of the input parameter "scalar_fname", where: ! * ! * N - The number of processors utilized ! * p - The processor ID. ! * ! * In addition, the location of the parallel disk system is prepended ! * to each file name. ! *--------------------------------------------------------------------------- ! * Example: ! * ! * scalar_fname = "Parallel-exoII-" (Input) ! * par_fname = "/raid/io_01/tmp/rf_crew/Parallel-exoII-8.0" (Output) ! * ! * where, for this example: ! * ! * N = 8 processors ! * p = 0 particular processor ID ! *--------------------------------------------------------------------------- ! * Revision History: ! * ! * 05 November 1993: Date of Creation ! 02 September 1999: Fortran translation ! *--------------------------------------------------------------------------- ! ! Local variables integer(Zoltan_INT) :: iTemp1 integer(Zoltan_INT) :: iMaxDigit, iMyDigit character(len=FILENAME_MAX) :: cTemp character(len=6) :: frmat character(len=32) :: nproc_str, myproc_str character(len=2) :: rdisk_str !************************ EXECUTION BEGINS ****************************** ! ! * Find out the number of digits needed to specify the processor ID. ! * This allows numbers like 01-99, i.e., prepending zeros to the ! * name to preserve proper alphabetic sorting of the files. ! iMaxDigit = 0 iTemp1 = nprocs do while (iTemp1 >= 1) iTemp1 = iTemp1/10 iMaxDigit = iMaxDigit + 1 end do iMyDigit = 0 iTemp1 = proc_for do while (iTemp1 >= 1) iTemp1 = iTemp1/10 iMyDigit = iMyDigit + 1 end do ! create the character strings containing the numbers frmat="" write(frmat,"(a2,i1,a1)") "(I",iMaxDigit,")" write(nproc_str,frmat) nprocs frmat="" write(frmat,"(a2,i1,a1,i1,a1)") "(I",iMaxDigit,".",iMaxDigit,")" write(myproc_str,frmat) proc_for ! create the filename with the digit suffixes cTemp = trim(scalar_fname)//"."//trim(nproc_str)//"."//trim(myproc_str) ! ! * Finally, generate the complete file specification for the parallel ! * file used by this processor. ! if (pio_info%num_dsk_ctrlrs > 0) then if(pio_info%zeros /= 0) then write(rdisk_str,"(I2.2)") pio_info%rdisk else write(rdisk_str,"(I2)") pio_info%rdisk rdisk_str = adjustl(rdisk_str) endif par_fname = trim(pio_info%pdsk_root)//trim(rdisk_str)//"/"//& trim(pio_info%pdsk_subdir)//trim(cTemp) else par_fname = trim(pio_info%pdsk_root)//"/"//trim(cTemp) endif end subroutine gen_par_filename end module dr_input