!*********************************************************************** !* GNU Lesser General Public License !* !* This file is part of the GFDL Flexible Modeling System (FMS). !* !* FMS is free software: you can redistribute it and/or modify it under !* the terms of the GNU Lesser General Public License as published by !* the Free Software Foundation, either version 3 of the License, or (at !* your option) any later version. !* !* FMS is distributed in the hope that it will be useful, but WITHOUT !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License !* for more details. !* !* You should have received a copy of the GNU Lesser General Public !* License along with FMS. If not, see . !*********************************************************************** module column_diagnostics_mod use mpp_io_mod, only: mpp_io_init, mpp_open, MPP_ASCII, & MPP_OVERWR, MPP_SEQUENTIAL, & MPP_MULTI, mpp_close use fms_mod, only: fms_init, mpp_pe, mpp_root_pe, & file_exist, check_nml_error, & error_mesg, FATAL, NOTE, WARNING, & close_file, open_namelist_file, & stdlog, write_version_number use time_manager_mod, only: time_manager_init, month_name, & get_date, time_type use constants_mod, only: constants_init, PI, RADIAN use mpp_mod, only: input_nml_file !------------------------------------------------------------------- implicit none private !--------------------------------------------------------------------- ! module to locate and mark desired diagnostic columns ! ! !-------------------------------------------------------------------- !--------------------------------------------------------------------- !----------- ****** VERSION NUMBER ******* --------------------------- ! Include variable "version" to be written to log file. #include !--------------------------------------------------------------------- !------- interfaces -------- public column_diagnostics_init, & initialize_diagnostic_columns, & column_diagnostics_header, & close_column_diagnostics_units !private !-------------------------------------------------------------------- !---- namelist ----- real :: crit_xdistance = 4.0 ! model grid points must be within crit_xdistance in ! longitude of the requested diagnostics point ! coordinates in order to be flagged as the desired ! point ! [ degrees ] real :: crit_ydistance = 4.0 ! model grid points must be within crit_ydistance in ! latitude of the requested diagnostics point ! coordinates in order to be flagged as the desired ! point ! [ degrees ] namelist / column_diagnostics_nml / & crit_xdistance, & crit_ydistance !-------------------------------------------------------------------- !-------- public data ----- !-------------------------------------------------------------------- !------ private data ------ logical :: module_is_initialized = .false. !------------------------------------------------------------------- !------------------------------------------------------------------- contains !#################################################################### subroutine column_diagnostics_init !-------------------------------------------------------------------- ! column_diagnostics_init is the constructor for ! column_diagnostics_mod. !-------------------------------------------------------------------- !-------------------------------------------------------------------- ! local variables: ! integer :: unit, ierr, io !-------------------------------------------------------------------- ! local variables: ! ! unit unit number for nml file ! ierr error return flag ! io error return code ! !--------------------------------------------------------------------- !-------------------------------------------------------------------- ! if routine has already been executed, return. !-------------------------------------------------------------------- if (module_is_initialized) return !--------------------------------------------------------------------- ! verify that all modules used by this module have been initialized. !---------------------------------------------------------------------- call mpp_io_init call fms_init call time_manager_init call constants_init !--------------------------------------------------------------------- ! read namelist. !--------------------------------------------------------------------- #ifdef INTERNAL_FILE_NML read (input_nml_file, column_diagnostics_nml, iostat=io) ierr = check_nml_error (io, 'column_diagnostics_nml') #else if (file_exist('input.nml')) then unit = open_namelist_file ( ) ierr=1; do while (ierr /= 0) read (unit, nml=column_diagnostics_nml, iostat=io, end=10) ierr = check_nml_error (io, 'column_diagnostics_nml') enddo 10 call close_file (unit) endif #endif !--------------------------------------------------------------------- ! write version number and namelist to logfile. !--------------------------------------------------------------------- call write_version_number("COLUMN_DIAGNOSTICS_MOD", version) if (mpp_pe() == mpp_root_pe()) then unit = stdlog() write (unit, nml=column_diagnostics_nml) endif !-------------------------------------------------------------------- module_is_initialized = .true. end subroutine column_diagnostics_init !#################################################################### subroutine initialize_diagnostic_columns & (module, num_diag_pts_latlon, num_diag_pts_ij, & global_i , global_j , global_lat_latlon, & global_lon_latlon, lonb_in, latb_in, & do_column_diagnostics, & diag_lon, diag_lat, diag_i, diag_j, diag_units) !--------------------------------------------------------------------- ! initialize_diagnostic_columns returns the (i, j, lat, lon) coord- ! inates of any diagnostic columns that are located on the current ! processor. !---------------------------------------------------------------------- !--------------------------------------------------------------------- character(len=*), intent(in) :: module integer, intent(in) :: num_diag_pts_latlon, & num_diag_pts_ij integer, dimension(:), intent(in) :: global_i, global_j real , dimension(:), intent(in) :: global_lat_latlon, & global_lon_latlon real, dimension(:,:), intent(in) :: lonb_in, latb_in logical, dimension(:,:), intent(out) :: do_column_diagnostics integer, dimension(:), intent(inout) :: diag_i, diag_j real , dimension(:), intent(out) :: diag_lat, diag_lon integer, dimension(:), intent(out) :: diag_units !--------------------------------------------------------------------- !--------------------------------------------------------------------- ! intent(in) variables: ! ! module module calling this subroutine ! num_diag_pts_latlon number of diagnostic columns specified ! by lat-lon coordinates ! num_diag_pts_ij number of diagnostic columns specified ! by global (i,j) coordinates ! global_i specified global i coordinates ! global_j specified global j coordinates ! global_lat_latlon specified global lat coordinates ! global_lon_latlon specified global lon coordinates ! ! intent(out) variables: ! ! do_column_diagnostics is a diagnostic column in this jrow ? ! diag_i processor i indices of diagnstic columns ! diag_j processor j indices of diagnstic columns ! diag_lat latitudes of diagnostic columns ! [ degrees ] ! diag_lon longitudes of diagnostic columns ! [ degrees ] ! diag_units unit number for each diagnostic column ! !--------------------------------------------------------------------- !-------------------------------------------------------------------- ! local variables: real, dimension(size(diag_i,1)) :: global_lat, global_lon real, dimension(size(latb_in,1)-1, size(latb_in,2)-1) :: & distance, distance_x, distance_y, & distance_x2, distance2 real, dimension(size(latb_in,1), size(latb_in,2)) :: latb_deg real, dimension(size(lonb_in,1), size(lonb_in,2)) :: lonb_deg real :: dellat, dellon real :: latb_max, latb_min, lonb_max, lonb_min integer :: num_diag_pts integer :: i, j, nn real :: ref_lat real :: current_distance character(len=8) :: char character(len=32) :: filename logical :: allow_ij_input logical :: open_file !-------------------------------------------------------------------- ! local variables: ! ! global_lat latitudes for all diagnostic columns [ degrees ] ! global_lon longitudes for all diagnostic columns ! [ degrees ] ! num_diag_pts total number of diagnostic columns ! i, j, nn do loop indices ! char character string for diaganostic column index ! filename filename for output file for diagnostic column ! !--------------------------------------------------------------------- if (.not. module_is_initialized) call column_diagnostics_init !-------------------------------------------------------------------- ! save the input lat and lon fields. define the delta of latitude ! and longitude. !-------------------------------------------------------------------- latb_deg = latb_in*RADIAN lonb_deg = lonb_in*RADIAN dellat = latb_in(1,2) - latb_in(1,1) dellon = lonb_in(2,1) - lonb_in(1,1) latb_max = MAXVAL (latb_deg(:,:)) latb_min = MINVAL (latb_deg(:,:)) lonb_max = MAXVAL (lonb_deg(:,:)) lonb_min = MINVAL (lonb_deg(:,:)) if (lonb_min < 10.0 .or. lonb_max > 350.) then lonb_min = 0. lonb_max = 360.0 endif allow_ij_input = .true. ref_lat = latb_in(1,1) do i =2,size(latb_in,1) if (latb_in(i,1) /= ref_lat) then allow_ij_input = .false. exit endif end do if ( .not. allow_ij_input .and. num_diag_pts_ij /= 0) then call error_mesg ('column_diagnostics_mod', & 'cannot specify column diagnostics column with (i,j) & &coordinates when using cubed sphere -- must specify & & lat/lon coordinates', FATAL) endif !---------------------------------------------------------------------- ! initialize column_diagnostics flag and diag unit numbers. define ! total number of diagnostic columns. !---------------------------------------------------------------------- do_column_diagnostics = .false. diag_units(:) = -1 diag_i(:) = -99 diag_j(:) = -99 diag_lat(:) = -999. diag_lon(:) = -999. num_diag_pts = size(diag_i(:)) !-------------------------------------------------------------------- ! define an array of lat-lon values for all diagnostic columns. !-------------------------------------------------------------------- do nn = 1, num_diag_pts_latlon global_lat(nn) = global_lat_latlon(nn) global_lon(nn) = global_lon_latlon(nn) end do do nn = 1, num_diag_pts_ij global_lat(nn+num_diag_pts_latlon) = & ((-0.5*acos(-1.0) + 0.5*dellat) + & (global_j (nn)-1) *dellat)*RADIAN global_lon(nn+num_diag_pts_latlon) = (0.5*dellon + & (global_i (nn)-1)*dellon)*RADIAN end do !---------------------------------------------------------------------- ! loop over all diagnostic points to check for their presence on ! this processor. !---------------------------------------------------------------------- do nn=1,num_diag_pts open_file = .false. !---------------------------------------------------------------------- ! verify that the values of lat and lon are valid. !---------------------------------------------------------------------- if (global_lon(nn) >= 0. .and. global_lon(nn) <= 360.0) then else call error_mesg ('column_diagnostics_mod', & ' invalid longitude', FATAL) endif if (global_lat(nn) >= -90.0 .and. global_lat(nn) <= 90.0) then else call error_mesg ('column_diagnostics_mod', & ' invalid latitude', FATAL) endif !-------------------------------------------------------------------- ! if the desired diagnostics column is within the current ! processor's domain, define the total and coordinate distances from ! each of the processor's grid points to the diagnostics point. !-------------------------------------------------------------------- if (global_lat(nn) .ge. latb_min .and. & global_lat(nn) .le. latb_max) then if (global_lon(nn) .ge. lonb_min .and.& global_lon(nn) .le. lonb_max) then do j=1,size(latb_deg,2) - 1 do i=1,size(lonb_deg,1) - 1 distance_y(i,j) = ABS(global_lat(nn) - latb_deg(i,j)) distance_x(i,j) = ABS(global_lon(nn) - lonb_deg(i,j)) distance_x2(i,j) = ABS((global_lon(nn)-360.) - & lonb_deg(i,j)) distance(i,j) = (global_lat(nn) - latb_deg(i,j))**2 + & (global_lon(nn) - lonb_deg(i,j))**2 distance2(i,j) = (global_lat(nn) - latb_deg(i,j))**2 + & ((global_lon(nn)-360.) - & lonb_deg(i,j))**2 end do end do !-------------------------------------------------------------------- ! find the grid point on the processor that is within the specified ! critical distance and also closest to the requested diagnostics ! column. save the (i,j) coordinates and (lon,lat) of this model ! grid point. set a flag indicating that a disgnostics file should ! be opened on this processor for this diagnostic point. !-------------------------------------------------------------------- current_distance = distance(1,1) do j=1,size(latb_deg,2) - 1 do i=1,size(lonb_deg,1) - 1 if (distance_x(i,j) <= crit_xdistance .and. & distance_y(i,j) <= crit_ydistance ) then if (distance(i,j) < current_distance) then current_distance = distance(i,j) do_column_diagnostics(i,j) = .true. diag_j(nn) = j diag_i(nn) = i diag_lon(nn) = lonb_deg(i,j) diag_lat(nn) = latb_deg(i,j) open_file = .true. endif endif !--------------------------------------------------------------------- ! check needed because of the 0.0 / 360.0 longitude periodicity. !--------------------------------------------------------------------- if (distance_x2(i,j) <= crit_xdistance .and. & distance_y(i,j) <= crit_ydistance ) then if (distance2(i,j) < current_distance) then current_distance = distance2(i,j) do_column_diagnostics(i,j) = .true. diag_j(nn) = j diag_i(nn) = i diag_lon(nn) = lonb_deg(i,j) diag_lat(nn) = latb_deg(i,j) open_file = .true. endif endif end do end do !-------------------------------------------------------------------- ! if the point has been found on this processor, open a diagnostics ! file. !-------------------------------------------------------------------- if (open_file) then write (char, '(i2)') nn filename = trim(module) // '_point' // & trim(adjustl(char)) // '.out' call mpp_open (diag_units(nn), filename, & form=MPP_ASCII, & action=MPP_OVERWR, & access=MPP_SEQUENTIAL, & threading=MPP_MULTI, nohdrs=.true.) endif ! (open_file) endif endif end do !--------------------------------------------------------------------- end subroutine initialize_diagnostic_columns !#################################################################### subroutine column_diagnostics_header & (module, diag_unit, Time, nn, diag_lon, & diag_lat, diag_i, diag_j) !-------------------------------------------------------------------- ! column_diagnostics_header writes out information concerning ! time and location of following data into the column_diagnostics ! output file. !-------------------------------------------------------------------- !-------------------------------------------------------------------- character(len=*), intent(in) :: module type(time_type), intent(in) :: Time integer, intent(in) :: diag_unit integer, intent(in) :: nn real, dimension(:), intent(in) :: diag_lon, diag_lat integer, dimension(:), intent(in) :: diag_i, diag_j !-------------------------------------------------------------------- ! intent(in) variables ! ! module module name calling this subroutine ! Time current model time [ time_type ] ! diag_unit unit number for column_diagnostics output ! nn index of diagnostic column currently active ! diag_lon longitude of current diagnostic column [ degrees ] ! diag_lat latitude of current diagnostic column [ degrees ] ! diag_i i coordinate of current diagnostic column ! diag_j j coordinate of current diagnostic column ! !--------------------------------------------------------------------- !-------------------------------------------------------------------- ! local variables: integer :: year, month, day, hour, minute, second character(len=8) :: mon character(len=64) :: header !-------------------------------------------------------------------- ! local variables: ! ! year, month, day, hour, minute, seconds ! integers defining the current time ! mon character string for the current month ! header title for the output ! !-------------------------------------------------------------------- if (.not. module_is_initialized) call column_diagnostics_init !-------------------------------------------------------------------- ! convert the time type to a date and time for printing. convert ! month to a character string. !-------------------------------------------------------------------- call get_date (Time, year, month, day, hour, minute, second) mon = month_name(month) !--------------------------------------------------------------------- ! write timestamp and column location information to the diagnostic ! columns output unit. !--------------------------------------------------------------------- write (diag_unit,'(a)') ' ' write (diag_unit,'(a)') ' ' write (diag_unit,'(a)') & '======================================================' write (diag_unit,'(a)') ' ' header = ' PRINTING ' // module // ' DIAGNOSTICS' write (diag_unit,'(a)') header write (diag_unit,'(a)') ' ' write (diag_unit,'(a, i6,2x, a,i4,i4,i4,i4)') ' time stamp:', & year, trim(mon), day, & hour, minute, second write (diag_unit,'(a, i4)') & ' DIAGNOSTIC POINT COORDINATES, point #', nn write (diag_unit,'(a)') ' ' write (diag_unit,'(a,f8.3,a,f8.3)') ' longitude = ', & diag_lon(nn), ' latitude = ', diag_lat(nn) write (diag_unit,'(a, i6, a,i6,a,i6)') & ' on processor # ', mpp_pe(), & ' : processor i =', diag_i(nn), & ' , processor j =', diag_j(nn) write (diag_unit,'(a)') ' ' !--------------------------------------------------------------------- end subroutine column_diagnostics_header !###################################################################### subroutine close_column_diagnostics_units (diag_units) !--------------------------------------------------------------------- ! close_column_diagnostics_units closes any open column_diagnostics ! files associated with the calling module. !---------------------------------------------------------------------- !---------------------------------------------------------------------- integer, dimension(:), intent(in) :: diag_units !---------------------------------------------------------------------- !-------------------------------------------------------------------- ! intent(in) variable: ! ! diag_units array of column diagnostic unit numbers ! !-------------------------------------------------------------------- !-------------------------------------------------------------------- ! local variable integer :: nn ! do loop index !-------------------------------------------------------------------- ! close the unit associated with each diagnostic column. !-------------------------------------------------------------------- do nn=1, size(diag_units(:)) if (diag_units(nn) /= -1) then call mpp_close (diag_units(nn)) endif end do !--------------------------------------------------------------------- end subroutine close_column_diagnostics_units !##################################################################### end module column_diagnostics_mod