program vector_plot ! !******************************************************************************* ! !! VECTOR_PLOT displays velocity vector plots. ! ! ! Modified: ! ! 29 April 2002 ! ! Author: ! ! John Burkardt ! implicit none ! character ( len = 100 ) file_name integer i real magnify integer node_num character ( len = 11 ) s_of_i real, allocatable, dimension ( : ) :: u character ( len = 80 ) uv_file real uv_max real, allocatable, dimension ( : ) :: v real x_max real x_min character ( len = 80 ) xy_file real, allocatable, dimension ( : ) :: x integer xy_dim integer xy_lines real, allocatable, dimension ( : ) :: y real y_max real y_min ! call timestamp ( ) write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'VECTOR_PLOT' write ( *, '(a)' ) ' Display a vector field ( U(X,Y), V(X,Y) )' ! ! Get the XY data file name. ! write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'What is the name of the XY data file?' read ( *, '(a)' ) xy_file ! ! Scan the XY data file, counting lines and data values. ! call data_size ( xy_file, xy_lines, xy_dim ) if ( xy_dim /= 2 ) then write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'VECTOR_PLOT - Fatal error!' write ( *, '(a)' ) ' The XY data file does not contain 2D data.' stop end if write ( *, '(a)' ) ' ' write ( *, '(a)' ) ' The file "' // trim ( xy_file ) // '" contains ' & // trim ( s_of_i ( xy_lines ) ) // ' pairs of data values.' ! ! Read in X and Y values from the XY data file. ! node_num = xy_lines allocate ( x(1:node_num) ) allocate ( y(1:node_num) ) call data_r2_read ( xy_file, node_num, x, y ) ! ! Extract some interesting data. ! x_min = minval ( x(1:node_num) ) x_max = maxval ( x(1:node_num) ) y_min = minval ( y(1:node_num) ) y_max = maxval ( y(1:node_num) ) write ( *, '(a)' ) ' ' write ( *, '(a,g14.6)' ) ' X minimum : ', x_min write ( *, '(a,g14.6)' ) ' X maximum : ', x_max write ( *, '(a,g14.6)' ) ' Y minimum : ', y_min write ( *, '(a,g14.6)' ) ' Y maximum : ', y_max ! ! Get the UV file name. ! write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'What is the name of the UV data file?' read ( *, '(a)' ) uv_file ! ! Read in U, V. ! allocate ( u(1:node_num) ) allocate ( v(1:node_num) ) call data_r2_read ( uv_file, node_num, u, v ) uv_max = maxval ( sqrt ( u(1:node_num)**2 + v(1:node_num)**2 ) ) write ( *, '(a)' ) ' ' write ( *, '(a,g14.6)' ) ' UV maximum = ' , uv_max ! ! Create the vector plot. ! file_name = 'vector_plot.ps' magnify = 2.0E+00 call vector_field ( node_num, x, y, u, v, file_name, magnify ) ! ! Deallocate the arrays. ! deallocate ( u ) deallocate ( v ) deallocate ( x ) deallocate ( y ) write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'VECTOR_PLOT' write ( *, '(a)' ) ' Normal end of execution.' stop end subroutine ch_cap ( c ) ! !******************************************************************************* ! !! CH_CAP capitalizes a single character. ! ! ! Modified: ! ! 19 July 1998 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input/output, character C, the character to capitalize. ! implicit none ! character c integer itemp ! itemp = ichar ( c ) if ( 97 <= itemp .and. itemp <= 122 ) then c = char ( itemp - 32 ) end if return end function ch_eqi ( c1, c2 ) ! !******************************************************************************* ! !! CH_EQI is a case insensitive comparison of two characters for equality. ! ! ! Examples: ! ! CH_EQI ( 'A', 'a' ) is .TRUE. ! ! Modified: ! ! 28 July 2000 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, character C1, C2, the characters to compare. ! ! Output, logical CH_EQI, the result of the comparison. ! implicit none ! character c1 character c1_cap character c2 character c2_cap logical ch_eqi ! c1_cap = c1 c2_cap = c2 call ch_cap ( c1_cap ) call ch_cap ( c2_cap ) if ( c1_cap == c2_cap ) then ch_eqi = .true. else ch_eqi = .false. end if return end function ch_is_digit ( c ) ! !******************************************************************************* ! !! CH_IS_DIGIT returns .TRUE. if a character is a decimal digit. ! ! ! Modified: ! ! 09 August 1999 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, character C, the character to be analyzed. ! ! Output, logical CH_IS_DIGIT, .TRUE. if C is a digit, .FALSE. otherwise. ! implicit none ! character c logical ch_is_digit ! if ( lge ( c, '0' ) .and. lle ( c, '9' ) ) then ch_is_digit = .true. else ch_is_digit = .false. end if return end subroutine ch_to_digit ( c, digit ) ! !******************************************************************************* ! !! CH_TO_DIGIT returns the integer value of a base 10 digit. ! ! ! Example: ! ! C DIGIT ! --- ----- ! '0' 0 ! '1' 1 ! ... ... ! '9' 9 ! ' ' 0 ! 'X' -1 ! ! Modified: ! ! 04 August 1999 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, character C, the decimal digit, '0' through '9' or blank ! are legal. ! ! Output, integer DIGIT, the corresponding integer value. If C was ! 'illegal', then DIGIT is -1. ! implicit none ! character c integer digit ! if ( lge ( c, '0' ) .and. lle ( c, '9' ) ) then digit = ichar ( c ) - 48 else if ( c == ' ' ) then digit = 0 else digit = -1 end if return end subroutine data_r2_read ( file_name, n, x, y ) ! !******************************************************************************* ! !! DATA_R2_READ reads a data set of pairs of real numbers stored in a file. ! ! ! Discussion: ! ! The data set can be thought of as a real M by 2 array. ! ! Each row of the array corresponds to one data "item". ! ! The data is stored in a file, one row (pair of values) at a time. ! ! Each row (pair of values) begins on a new line. ! ! Blank lines and comment lines (beginning with '#') are ignored. ! ! Individual data values should be separated by spaces or commas. ! ! Modified: ! ! 15 April 2002 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, character ( len = * ) FILE_NAME, the name of the file to read. ! ! Input, integer N, the number of data items. ! ! Output, real X(N), Y(N), the data values. ! implicit none ! integer n ! character ( len = * ) file_name integer ierror integer input integer ios integer last integer length character ( len = 80 ) line integer n2 real x(n) real y(n) ! call get_unit ( input ) open ( unit = input, file = file_name, status = 'old' ) x(1:n) = huge ( x(1) ) y(1:n) = huge ( y(1) ) n2 = 0 do read ( input, '(a)', iostat = ios ) line if ( ios /= 0 ) then exit end if if ( len_trim ( line ) == 0 ) then else if ( line(1:1) == '#' ) then else n2 = n2 + 1 last = 0 call s_to_r ( line(last+1:), x(n2), ierror, length ) if ( ierror /= 0 ) then exit end if last = last + length call s_to_r ( line(last+1:), y(n2), ierror, length ) if ( ierror /= 0 ) then exit end if if ( n2 == n ) then exit end if end if end do close ( unit = input ) return end subroutine data_size ( file_name, m, n ) ! !******************************************************************************* ! !! DATA_SIZE counts the size of a data set stored in a file. ! ! ! Discussion: ! ! Blank lines and comment lines (which begin with '#') are ignored). ! ! All other lines are assumed to represent data items. ! ! This routine assumes that each data line contains exactly the ! same number of values, which are separated by spaces. ! ! (This means this routine cannot handle cases where a data item ! extends over more than one line, or cases where data is squeezed ! together with no spaces, or where commas are used as separators, ! but with space on both sides.) ! ! Modified: ! ! 11 April 2002 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, character ( len = * ) FILE_NAME, the name of the file to read. ! ! Output, integer M, the number of nonblank, noncomment lines. ! ! Output, integer N, the number of values per line. ! implicit none ! character ( len = * ) file_name integer input integer ios character ( len = 80 ) line integer m integer n integer n_max integer n_min integer n_word ! m = 0 n_max = - huge ( n_max ) n_min = huge ( n_min ) call get_unit ( input ) open ( unit = input, file = file_name, status = 'old' ) do read ( input, '(a)', iostat = ios ) line if ( ios /= 0 ) then exit end if if ( len_trim ( line ) == 0 ) then else if ( line(1:1) == '#' ) then else m = m + 1 call word_count ( line, n_word ) n_max = max ( n_max, n_word ) n_min = min ( n_min, n_word ) end if end do if ( n_max /= n_min ) then write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'DATA_SIZE - Fatal error!' write ( *, '(a)' ) ' Number of words per line varies.' write ( *, '(a,i6)' ) ' Minimum is ', n_min write ( *, '(a,i6)' ) ' Maximum is ', n_max n = 0 else n = n_min end if close ( unit = input ) return end subroutine digit_to_ch ( digit, c ) ! !******************************************************************************* ! !! DIGIT_TO_CH returns the character representation of a decimal digit. ! ! ! Example: ! ! DIGIT C ! ----- --- ! 0 '0' ! 1 '1' ! ... ... ! 9 '9' ! 17 '*' ! ! Modified: ! ! 04 August 1999 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, integer DIGIT, the digit value between 0 and 9. ! ! Output, character C, the corresponding character, or '*' if DIGIT ! was illegal. ! implicit none ! character c integer digit ! if ( 0 <= digit .and. digit <= 9 ) then c = char ( digit + 48 ) else c = '*' end if return end subroutine get_unit ( iunit ) ! !******************************************************************************* ! !! GET_UNIT returns a free FORTRAN unit number. ! ! ! Discussion: ! ! A "free" FORTRAN unit number is an integer between 1 and 99 which ! is not currently associated with an I/O device. A free FORTRAN unit ! number is needed in order to open a file with the OPEN command. ! ! Modified: ! ! 02 March 1999 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Output, integer IUNIT. ! ! If IUNIT = 0, then no free FORTRAN unit could be found, although ! all 99 units were checked (except for units 5 and 6). ! ! Otherwise, IUNIT is an integer between 1 and 99, representing a ! free FORTRAN unit. Note that GET_UNIT assumes that units 5 and 6 ! are special, and will never return those values. ! implicit none ! integer i integer ios integer iunit logical lopen iunit = 0 do i = 1, 99 if ( i /= 5 .and. i /= 6 ) then inquire ( unit = i, opened = lopen, iostat = ios ) if ( ios == 0 ) then if ( .not. lopen ) then iunit = i return end if end if end if end do return end function s_of_i ( i ) ! !******************************************************************************* ! !! S_OF_I converts an integer to a left-justified string. ! ! ! Examples: ! ! I S ! ! 1 1 ! -1 -1 ! 0 0 ! 1952 1952 ! 123456 123456 ! ! Modified: ! ! 13 February 2002 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, integer I, an integer to be converted. ! ! Output, character ( len = 11 ) S_OF_I, the representation of the ! integer. The integer will be left-justified. ! implicit none ! character c integer i integer idig integer ihi integer ilo integer ipos integer ival integer j character ( len = 11 ) s character ( len = 11 ) s_of_i ! s = ' ' ilo = 1 ihi = 11 ! ! Make a copy of the integer. ! ival = i ! ! Handle the negative sign. ! if ( ival < 0 ) then if ( ihi <= 1 ) then s(1:1) = '*' return end if ival = - ival s(1:1) = '-' ilo = 2 end if ! ! The absolute value of the integer goes into S(ILO:IHI). ! ipos = ihi ! ! Find the last digit, strip it off, and stick it into the string. ! do idig = mod ( ival, 10 ) ival = ival / 10 if ( ipos < ilo ) then do j = 1, ihi s(j:j) = '*' end do return end if call digit_to_ch ( idig, c ) s(ipos:ipos) = c ipos = ipos - 1 if ( ival == 0 ) then exit end if end do ! ! Shift the string to the left. ! s(ilo:ilo+ihi-ipos-1) = s(ipos+1:ihi) s(ilo+ihi-ipos:ihi) = ' ' s_of_i = s return end subroutine s_to_r ( s, r, ierror, lchar ) ! !******************************************************************************* ! !! S_TO_R reads a real number from a string. ! ! ! Discussion: ! ! This routine will read as many characters as possible until it reaches ! the end of the string, or encounters a character which cannot be ! part of the real number. ! ! Legal input is: ! ! 1 blanks, ! 2 '+' or '-' sign, ! 2.5 spaces ! 3 integer part, ! 4 decimal point, ! 5 fraction part, ! 6 'E' or 'e' or 'D' or 'd', exponent marker, ! 7 exponent sign, ! 8 exponent integer part, ! 9 exponent decimal point, ! 10 exponent fraction part, ! 11 blanks, ! 12 final comma or semicolon. ! ! with most quantities optional. ! ! Examples: ! ! S R ! ! '1' 1.0 ! ' 1 ' 1.0 ! '1A' 1.0 ! '12,34,56' 12.0 ! ' 34 7' 34.0 ! '-1E2ABCD' -100.0 ! '-1X2ABCD' -1.0 ! ' 2E-1' 0.2 ! '23.45' 23.45 ! '-4.2E+2' -420.0 ! '17d2' 1700.0 ! '-14e-2' -0.14 ! 'e2' 100.0 ! '-12.73e-9.23' -12.73 * 10.0**(-9.23) ! ! Modified: ! ! 12 February 2001 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, character ( len = * ) S, the string containing the ! data to be read. Reading will begin at position 1 and ! terminate at the end of the string, or when no more ! characters can be read to form a legal real. Blanks, ! commas, or other nonnumeric data will, in particular, ! cause the conversion to halt. ! ! Output, real R, the real value that was read from the string. ! ! Output, integer IERROR, error flag. ! ! 0, no errors occurred. ! ! 1, 2, 6 or 7, the input number was garbled. The ! value of IERROR is the last type of input successfully ! read. For instance, 1 means initial blanks, 2 means ! a plus or minus sign, and so on. ! ! Output, integer LCHAR, the number of characters read from ! the string to form the number, including any terminating ! characters such as a trailing comma or blanks. ! implicit none ! character c logical ch_eqi integer ierror integer ihave integer isgn integer iterm integer jbot integer jsgn integer jtop integer lchar integer nchar integer ndig real r real rbot real rexp real rtop character ( len = * ) s character, parameter :: TAB = char ( 9 ) ! nchar = len_trim ( s ) ierror = 0 r = 0.0E+00 lchar = - 1 isgn = 1 rtop = 0.0E+00 rbot = 1.0E+00 jsgn = 1 jtop = 0 jbot = 1 ihave = 1 iterm = 0 do lchar = lchar + 1 c = s(lchar+1:lchar+1) ! ! Blank or TAB character. ! if ( c == ' ' .or. c == TAB ) then if ( ihave == 2 ) then else if ( ihave == 6 .or. ihave == 7 ) then iterm = 1 else if ( ihave > 1 ) then ihave = 11 end if ! ! Comma. ! else if ( c == ',' .or. c == ';' ) then if ( ihave /= 1 ) then iterm = 1 ihave = 12 lchar = lchar + 1 end if ! ! Minus sign. ! else if ( c == '-' ) then if ( ihave == 1 ) then ihave = 2 isgn = - 1 else if ( ihave == 6 ) then ihave = 7 jsgn = - 1 else iterm = 1 end if ! ! Plus sign. ! else if ( c == '+' ) then if ( ihave == 1 ) then ihave = 2 else if ( ihave == 6 ) then ihave = 7 else iterm = 1 end if ! ! Decimal point. ! else if ( c == '.' ) then if ( ihave < 4 ) then ihave = 4 else if ( ihave >= 6 .and. ihave <= 8 ) then ihave = 9 else iterm = 1 end if ! ! Exponent marker. ! else if ( ch_eqi ( c, 'E' ) .or. ch_eqi ( c, 'D' ) ) then if ( ihave < 6 ) then ihave = 6 else iterm = 1 end if ! ! Digit. ! else if ( ihave < 11 .and. lge ( c, '0' ) .and. lle ( c, '9' ) ) then if ( ihave <= 2 ) then ihave = 3 else if ( ihave == 4 ) then ihave = 5 else if ( ihave == 6 .or. ihave == 7 ) then ihave = 8 else if ( ihave == 9 ) then ihave = 10 end if call ch_to_digit ( c, ndig ) if ( ihave == 3 ) then rtop = 10.0E+00 * rtop + real ( ndig ) else if ( ihave == 5 ) then rtop = 10.0E+00 * rtop + real ( ndig ) rbot = 10.0E+00 * rbot else if ( ihave == 8 ) then jtop = 10 * jtop + ndig else if ( ihave == 10 ) then jtop = 10 * jtop + ndig jbot = 10 * jbot end if ! ! Anything else is regarded as a terminator. ! else iterm = 1 end if ! ! If we haven't seen a terminator, and we haven't examined the ! entire string, go get the next character. ! if ( iterm == 1 .or. lchar+1 >= nchar ) then exit end if end do ! ! If we haven't seen a terminator, and we have examined the ! entire string, then we're done, and LCHAR is equal to NCHAR. ! if ( iterm /= 1 .and. lchar+1 == nchar ) then lchar = nchar end if ! ! Number seems to have terminated. Have we got a legal number? ! Not if we terminated in states 1, 2, 6 or 7! ! if ( ihave == 1 .or. ihave == 2 .or. ihave == 6 .or. ihave == 7 ) then ierror = ihave return end if ! ! Number seems OK. Form it. ! if ( jtop == 0 ) then rexp = 1.0E+00 else if ( jbot == 1 ) then rexp = 10.0E+00**( jsgn * jtop ) else rexp = jsgn * jtop rexp = rexp / jbot rexp = 10.0E+00**rexp end if end if r = isgn * rexp * rtop / rbot return end subroutine vector_field ( n, x, y, u, v, file_name, magnify ) ! !******************************************************************************* ! !! VECTOR_FIELD plots a vector field. ! ! ! Modified: ! ! 16 February 2002 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, integer N, the number of points. ! ! Input, real X(N), Y(N), the location of the points at which vector ! values are known. ! ! Input, real U(N), V(N), the X and Y components of the vector quantity. ! ! Input, character ( len = * ) FILE_NAME, the name of the file. ! ! Input, real MAGNIFY, a magnification factor for the vectors. ! implicit none ! integer n ! real area real blue character ( len = * ) file_name real green integer i integer ierror integer iunit real magnify real red real scale real u(n) real v(n) real vscale real x(n) real xmax real xmax2 real xmin real xmin2 real xscale real y(n) real ymax real ymax2 real ymin real ymin2 ! ! Open the file. ! call get_unit ( iunit ) call ps_file_open ( file_name, iunit, ierror ) if ( ierror /= 0 ) then write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'VECTOR_FIELD' write ( *, '(a,i6)' ) ' File creation error ', ierror return end if call ps_file_head ( file_name ) ! ! Define the size of the page. ! xmin = minval ( x(1:n) ) ymin = minval ( y(1:n) ) xmax = maxval ( x(1:n) ) ymax = maxval ( y(1:n) ) area = ( xmax - xmin ) * ( ymax - ymin ) vscale = maxval ( sqrt ( u(1:n)**2 + v(1:n)**2 ) ) if ( vscale == 0.0E+00 ) then write ( *, * ) ' ' write ( *, * ) 'VECTOR_FIELD - Fatal error!' write ( *, * ) ' The vector field is null.' return end if xscale = sqrt ( area / real ( n ) ) scale = magnify * sqrt ( area / real ( n ) ) / vscale ! ! Move the boundaries out far enough to catch a typical vector. ! xmax = xmax + xscale xmin = xmin - xscale ymax = ymax + xscale ymin = ymin - xscale ! ! Adjust symmetrically so that X and Y ranges are equal. ! if ( xmax - xmin > ymax - ymin ) then xmax2 = xmax xmin2 = xmin ymax2 = ymax + 0.5 * ( ( xmax - xmin ) - ( ymax - ymin ) ) ymin2 = ymin + 0.5 * ( ( xmax - xmin ) - ( ymax - ymin ) ) else xmax2 = xmax + 0.5 * ( ( ymax - ymin ) - ( xmax - xmin ) ) xmin2 = xmin + 0.5 * ( ( ymax - ymin ) - ( xmax - xmin ) ) ymax2 = ymax ymin2 = ymin end if call ps_page_head ( xmin2, ymin2, xmax2, ymax2 ) ! ! Specify the line color. ! red = 1.0E+00 green = 0.0E+00 blue = 0.0E+00 call ps_color_line_set ( red, green, blue ) ! ! Draw the vector field. ! do i = 1, n call ps_arrow ( x(i), y(i), x(i) + scale * u(i), y(i) + scale * v(i) ) end do ! ! Close up the page and the file. ! call ps_page_tail call ps_file_tail call ps_file_close ( iunit ) return end subroutine word_count ( s, nword ) ! !******************************************************************************* ! !! WORD_COUNT counts the number of "words" in a string. ! ! ! Modified: ! ! 14 April 1999 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, character ( len = * ) S, the string to be examined. ! ! Output, integer NWORD, the number of "words" in the string. ! Words are presumed to be separated by one or more blanks. ! implicit none ! logical blank integer i integer lens integer nword character ( len = * ) s ! nword = 0 lens = len ( s ) if ( lens <= 0 ) then return end if blank = .true. do i = 1, lens if ( s(i:i) == ' ' ) then blank = .true. else if ( blank ) then nword = nword + 1 blank = .false. end if end do return end