!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! GNU General Public License !!
!! !!
!! This file is part of the Flexible Modeling System (FMS). !!
!! !!
!! FMS is free software; you can redistribute it and/or modify !!
!! it and are expected to follow the terms of the GNU General Public !!
!! License as published by the Free Software Foundation. !!
!! !!
!! 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 General Public License !!
!! along with FMS; if not, write to: !!
!! Free Software Foundation, Inc. !!
!! 59 Temple Place, Suite 330 !!
!! Boston, MA 02111-1307 USA !!
!! or see: !!
!! http://www.gnu.org/licenses/gpl.txt !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
module station_data_mod
!
! Giang Nong
!
!
! This module is used for outputing model results in a list
! of stations (not gridded arrrays). The user needs to supply
! a list of stations with lat, lon values of each station.
! Data at a single point (I,J) that is closest to each station will
! be written to a file. No interpolation is made when a station
! is between two or more grid points.
! In the output file, a 3D field will have a format of array(n1,n2) and
! a 2D field is array(n1) where n1 is number of stations and n2 is number
! of vertical levels or depths.
!
!
! Here are some basic steps of how to use station_data_mod
!1/Call data_station_init
! user needs to supply 2 tables: list_stations and station_data_table as follows:
! example of list of stations (# sign means comment)
! # station_id lat lon
! station_1 20.4 100.8
! example of station_data_table (# sign means comment)
! # General descriptor
! Am2p14 station data
!# start time (should be the same as model's initial time)
! 19800101
!# file inforamtion
!# filename, output_frequency, frequency_unit, time_axis_unit
! "ocean_day" 1 "days" "hours"
!# field information
!# module field_name filename time_method pack
! Ice_mod temperature ocean_day . true. 2
! Ice_mod pressure ocean_day .false. 2
! 2/
! Call register_station_field to register each field that needs to be written to a file, the call
! register_station_field returns a field_id that will be used later in send_station_data
! 3/
! Call send_station_data will send data at each station in the list
! to a file
! 4/ Finally, call station_data_end after the last time step.
!
use axis_utils_mod, only: nearest_index
use mpp_io_mod, only : mpp_open, MPP_RDONLY, MPP_ASCII, mpp_close,MPP_OVERWR,MPP_NETCDF, &
mpp_write_meta, MPP_SINGLE, mpp_write, fieldtype,mpp_flush
use fms_mod, only : error_mesg, FATAL, WARNING, stdlog, write_version_number,&
mpp_pe, lowercase, stdout, close_file
use mpp_mod, only : mpp_npes, mpp_sync, mpp_root_pe, mpp_send, mpp_recv, mpp_max, &
mpp_get_current_pelist
use mpp_domains_mod,only: domain2d, mpp_get_compute_domain
use diag_axis_mod, only : diag_axis_init
use diag_output_mod,only: write_axis_meta_data, write_field_meta_data,diag_fieldtype,done_meta_data
use diag_manager_mod,only : get_date_dif, DIAG_SECONDS, DIAG_MINUTES, DIAG_HOURS, &
DIAG_DAYS, DIAG_MONTHS, DIAG_YEARS
use diag_util_mod,only : diag_time_inc
use time_manager_mod, only: operator(>),operator(>=),time_type,get_calendar_type,NO_CALENDAR,set_time, &
set_date, increment_date, increment_time
implicit none
private
integer, parameter :: max_fields_per_file = 150
integer, parameter :: max_files = 31
integer :: num_files = 0
integer :: num_stations = 0
integer :: max_stations = 20
integer :: max_output_fields = 100
integer :: num_output_fields = 0
real :: EMPTY = 0.0
real :: MISSING = 1.E20
logical :: module_is_initialized = .false.
logical :: need_write_axis = .true.
integer :: base_year, base_month, base_day, base_hour, base_minute, base_second
type (time_type) :: base_time
character (len=10) :: time_unit_list(6) = (/'seconds ', 'minutes ', &
'hours ', 'days ', 'months ', 'years '/)
integer, parameter :: EVERY_TIME = 0
integer, parameter :: END_OF_RUN = -1
character(len=128) :: version = ''
character(len=128) :: tagname = ''
character(len=256) :: global_descriptor
character (len = 7) :: avg_name = 'average'
integer :: total_pe
integer :: lat_axis, lon_axis
integer, allocatable:: pelist(:)
character(len=32) :: pelist_name
type file_type
character(len=128) :: name
integer :: output_freq
integer :: output_units
integer :: time_units
integer :: fields(max_fields_per_file)
integer :: num_fields
integer :: file_unit
integer :: time_axis_id, time_bounds_id
type (time_type) :: last_flush
type(fieldtype) :: f_avg_start, f_avg_end, f_avg_nitems, f_bounds
end type file_type
type station_type
character(len=128) :: name
real :: lat, lon
integer :: id
integer :: global_i, global_j ! index of global grid
integer :: local_i, local_j ! index on the current PE
logical :: need_compute ! true if the station present in this PE
end type station_type
type group_field_type
integer :: output_file
integer :: num_station ! number of stations on this PE
integer, pointer :: station_id(:) =>null() ! id of station on this PE
character(len=128) :: output_name, module_name,long_name, units
logical :: time_average,time_max,time_min, time_ops, register
integer :: pack, axes(2), num_axes
character(len=8) :: time_method !could be: true, false, max, min, mean, ...
real, pointer :: buffer(:, :)=>null()
integer :: counter,nlevel
type(time_type) :: last_output, next_output
type(fieldtype) :: f_type
end type group_field_type
type global_field_type
real, pointer :: buffer(:,:)=>null()
integer :: counter
end type global_field_type
type(global_field_type),save :: global_field
type (file_type),save :: files(max_files)
type(group_field_type),allocatable,save :: output_fields(:)
type (station_type),allocatable :: stations(:)
type(diag_fieldtype),save :: diag_field
public register_station_field, send_station_data, station_data_init, station_data_end
interface register_station_field
module procedure register_station_field2d
module procedure register_station_field3d
end interface
interface send_station_data
module procedure send_station_data_2d
module procedure send_station_data_3d
end interface
contains
!
!
! station_data_init()
!
!
! read in lat. lon of each station
! create station_id based on lat, lon
! read station_data_table, initialize output_fields and output files
!
!
subroutine station_data_init()
character(len=128) :: station_name
real :: lat, lon
integer :: iunit,nfiles,nfields,time_units,output_freq_units,j,station_id,io_status,logunit
logical :: init_verbose
character(len=128) :: record
type file_part_type
character(len=128) :: name
integer :: output_freq
character(len=10) :: output_freq_units
integer :: format ! must always be 1 for netcdf files
character(len=10) :: time_unit
end type file_part_type
type field_part_type
character(len=128) :: module_name,field_name,file_name
character(len=8) :: time_method
integer :: pack
end type field_part_type
type(file_part_type) :: record_files
type(field_part_type) :: record_fields
namelist /station_data_nml/ max_output_fields, max_stations,init_verbose
if (module_is_initialized) return
init_verbose = .false.
total_pe = mpp_npes()
allocate(pelist(total_pe))
call mpp_get_current_pelist(pelist, pelist_name)
! read namelist
call mpp_open(iunit, 'input.nml',form=MPP_ASCII,action=MPP_RDONLY)
read(iunit,station_data_nml,iostat=io_status)
logunit = stdlog()
write(logunit, station_data_nml)
if (io_status > 0) then
call error_mesg('station_data_init', 'Error reading station_data_nml',FATAL)
endif
call mpp_close (iunit)
allocate(output_fields(max_output_fields), stations(max_stations))
! read list of stations
if(init_verbose) then
logunit = stdout()
write(logunit, *) ' '
write(logunit, *) '****** Summary of STATION information from list_stations ********'
write(logunit, *) ' '
write(logunit, *) 'station name ', ' latitude ', ' longitude '
write(logunit, *) ' '
endif
call mpp_open(iunit, 'list_stations',form=MPP_ASCII,action=MPP_RDONLY)
do while (num_stations 0) then
stations(station_id)%name = station_name
else
call error_mesg('station_data_init','station DUPLICATED in file list_stations', FATAL)
endif
logunit = stdout()
if( init_verbose.and. mpp_pe() == mpp_root_pe()) &
write(logunit,1)stations(station_id)%name,stations(station_id)%lat,stations(station_id)%lon
1 format(1x,A18, 1x,F8.2,4x,F8.2)
75 continue
enddo
call error_mesg('station_data_init','max_stations exceeded, increase it via namelist', FATAL)
76 continue
call mpp_close (iunit)
logunit = stdout()
if(init_verbose) write(logunit, *)'*****************************************************************'
! read station_data table
call mpp_open(iunit, 'station_data_table',form=MPP_ASCII,action=MPP_RDONLY)
! Read in the global file labeling string
read(iunit, *, end = 99, err=99) global_descriptor
! Read in the base date
read(iunit, *, end = 99, err = 99) base_year, base_month, base_day, &
base_hour, base_minute, base_second
if (get_calendar_type() /= NO_CALENDAR) then
base_time = set_date(base_year, base_month, base_day, base_hour, &
base_minute, base_second)
else
! No calendar - ignore year and month
base_time = set_time(base_hour*3600+base_minute*60+base_second, base_day)
base_year = 0
base_month = 0
end if
nfiles=0
do while (nfiles <= max_files)
read(iunit,'(a)',end=86,err=85) record
if (record(1:1) == '#') cycle
read(record,*,err=85,end=85)record_files%name,record_files%output_freq, &
record_files%output_freq_units,record_files%format,record_files%time_unit
if(record_files%format /= 1) cycle !avoid reading field part
time_units = 0
output_freq_units = 0
do j = 1, size(time_unit_list(:))
if(record_files%time_unit == time_unit_list(j)) time_units = j
if(record_files%output_freq_units == time_unit_list(j)) output_freq_units = j
end do
if(time_units == 0) &
call error_mesg('station_data_init',' check time unit in station_data_table',FATAL)
if(output_freq_units == 0) &
call error_mesg('station_data_init',', check output_freq in station_data_table',FATAL)
call init_file(record_files%name,record_files%output_freq, output_freq_units,time_units)
85 continue
enddo
call error_mesg('station_data_init','max_files exceeded, increase max_files', FATAL)
86 continue
rewind(iunit)
nfields=0
do while (nfields <= max_output_fields)
read(iunit,'(a)',end=94,err=93) record
if (record(1:1) == '#') cycle
read(record,*,end=93,err=93) record_fields
if (record_fields%pack .gt. 8 .or.record_fields%pack .lt. 1) cycle !avoid reading file part
nfields=nfields+1
call init_output_field(record_fields%module_name,record_fields%field_name, &
record_fields%file_name,record_fields%time_method,record_fields%pack)
93 continue
enddo
call error_mesg('station_data_init','max_output_fields exceeded, increase it via nml ', FATAL)
94 continue
call close_file(iunit)
call check_duplicate_output_fields
call write_version_number (version, tagname)
module_is_initialized = .true.
return
99 continue
call error_mesg('station_data_init','error reading station_datatable',FATAL)
end subroutine station_data_init
!----------------------------------------------------------------------
subroutine check_duplicate_output_fields()
! pair(output_name and output_file) should be unique in data_station_table, ERROR1
! pair(module_name and output_name) should be unique in data_station_table, ERROR2
integer :: i, j, tmp_file
character(len=128) :: tmp_name, tmp_module
if(num_output_fields <= 1) return
do i = 1, num_output_fields-1
tmp_name = trim(output_fields(i)%output_name)
tmp_file = output_fields(i)%output_file
tmp_module = trim(output_fields(i)%module_name)
do j = i+1, num_output_fields
if((tmp_name == trim(output_fields(j)%output_name)).and. &
(tmp_file == output_fields(j)%output_file)) &
call error_mesg (' ERROR1 in station_data_table:', &
&' module/field '//tmp_module//'/'//tmp_name//' duplicated', FATAL)
if((tmp_name == trim(output_fields(j)%output_name)).and. &
(tmp_module == trim(output_fields(j)%module_name))) &
call error_mesg (' ERROR2 in station_data_table:', &
&' module/field '//tmp_module//'/'//tmp_name//' duplicated', FATAL)
enddo
enddo
end subroutine check_duplicate_output_fields
!----------------------------------------------------------------------
function get_station_id(lat,lon)
integer :: get_station_id, i
real, intent(in):: lat,lon
! each station should have distinct lat and lon
get_station_id = -1
do i = 1, num_stations
if(stations(i)%lat == lat .and. stations(i)%lon == lon) return
enddo
num_stations = num_stations + 1
stations(num_stations)%id = num_stations
stations(num_stations)%lat = lat
stations(num_stations)%lon = lon
stations(num_stations)%need_compute = .false.
stations(num_stations)%global_i = -1; stations(num_stations)%global_j = -1
stations(num_stations)%local_i = -1 ; stations(num_stations)%local_j = -1
get_station_id = num_stations
end function get_station_id
!----------------------------------------------------------------------
subroutine init_file(filename, output_freq, output_units, time_units)
character(len=*), intent(in) :: filename
integer, intent(in) :: output_freq, output_units, time_units
character(len=128) :: time_units_str
real, dimension(1) :: tdata
num_files = num_files + 1
if(num_files >= max_files) &
call error_mesg('station_data, init_file', ' max_files exceeded, incease max_files', FATAL)
files(num_files)%name = trim(filename)
files(num_files)%output_freq = output_freq
files(num_files)%output_units = output_units
files(num_files)%time_units = time_units
files(num_files)%num_fields = 0
files(num_files)%last_flush = base_time
files(num_files)%file_unit = -1
!---- register axis_id and time boundaries id
write(time_units_str, 11) trim(time_unit_list(files(num_files)%time_units)), base_year, &
base_month, base_day, base_hour, base_minute, base_second
11 format(a, ' since ', i4.4, '-', i2.2, '-', i2.2, ' ', i2.2, ':', i2.2, ':', i2.2)
files(num_files)%time_axis_id = diag_axis_init ('Time', tdata, time_units_str, 'T', &
'Time' , set_name=trim(filename))
files(num_files)%time_bounds_id = diag_axis_init('nv',(/1.,2./),'none','N','vertex number',&
set_name=trim(filename))
end subroutine init_file
!--------------------------------------------------------------------------
subroutine init_output_field(module_name,field_name,file_name,time_method,pack)
character(len=*), intent(in) :: module_name, field_name, file_name
character(len=*), intent(in) :: time_method
integer, intent(in) :: pack
integer :: out_num, file_num,num_fields, method_selected, l1
character(len=8) :: t_method
! Get a number for this output field
num_output_fields = num_output_fields + 1
if(num_output_fields > max_output_fields) &
call error_mesg('station_data', 'max_output_fields exceeded, increase it via nml', FATAL)
out_num = num_output_fields
file_num = find_file(file_name)
if(file_num < 0) &
call error_mesg('station_data,init_output_field', 'file '//trim(file_name) &
//' is NOT found in station_data_table', FATAL)
! Insert this field into list of fields of this file
files(file_num)%num_fields = files(file_num)%num_fields + 1
if(files(file_num)%num_fields > max_fields_per_file) &
call error_mesg('station_data, init_output_field', 'max_fields_per_file exceeded ', FATAL)
num_fields = files(file_num)%num_fields
files(file_num)%fields(num_fields) = out_num
output_fields(out_num)%output_name = trim(field_name)
output_fields(out_num)%module_name = trim(module_name)
output_fields(out_num)%counter = 0
output_fields(out_num)%output_file = file_num
output_fields(out_num)%pack = pack
output_fields(out_num)%time_average = .false.
output_fields(out_num)%time_min = .false.
output_fields(out_num)%time_max = .false.
output_fields(out_num)%time_ops = .false.
output_fields(out_num)%register = .false.
t_method = lowercase(time_method)
select case (trim(t_method))
case('.true.')
output_fields(out_num)%time_average = .true.
output_fields(out_num)%time_method = 'mean'
case('mean')
output_fields(out_num)%time_average = .true.
output_fields(out_num)%time_method = 'mean'
case('average')
output_fields(out_num)%time_average = .true.
output_fields(out_num)%time_method = 'mean'
case('avg')
output_fields(out_num)%time_average = .true.
output_fields(out_num)%time_method = 'mean'
case('.false.')
output_fields(out_num)%time_average = .false.
output_fields(out_num)%time_method = 'point'
case ('max')
call error_mesg('station_data, init_output_field','time_method MAX is not supported',&
FATAL)
output_fields(out_num)%time_max = .true.
output_fields(out_num)%time_method = 'max'
l1 = len_trim(output_fields(out_num)%output_name)
if(output_fields(out_num)%output_name(l1-2:l1) /= 'max') &
output_fields(out_num)%output_name = trim(field_name)//'_max'
case ('min')
call error_mesg('station_data, init_output_field','time_method MIN is not supported',&
FATAL)
output_fields(out_num)%time_min = .true.
output_fields(out_num)%time_method = 'min'
l1 = len_trim(output_fields(out_num)%output_name)
if(output_fields(out_num)%output_name(l1-2:l1) /= 'min') &
output_fields(out_num)%output_name = trim(field_name)//'_min'
case default
call error_mesg('station_data, init_output_field', 'error in time_method of field '&
//trim(field_name), FATAL)
end select
if (files(file_num)%output_freq == EVERY_TIME) &
output_fields(out_num)%time_average = .false.
output_fields(out_num)%time_ops = output_fields(out_num)%time_min.or.output_fields(out_num)%time_max &
.or.output_fields(out_num)%time_average
output_fields(out_num)%time_method = trim(time_method)
end subroutine init_output_field
!--------------------------------------------------------------------------
function find_file(name)
integer :: find_file
character(len=*), intent(in) :: name
integer :: i
find_file = -1
do i = 1, num_files
if(trim(files(i)%name) == trim(name)) then
find_file = i
return
end if
end do
end function find_file
!
!
! register_station_field (module_name,fieldname,glo_lat,glo_lon,levels,init_time,
! domain,longname,units)
!
!
! This function is similar to register_diag_field of diag_manager_mod. All arguments
! are inputs that user needs to supply, some are optional. The names of input args are
! self-describing.
levels is absent for 2D fields.
! Note that pair (module_name, fieldname) must be unique in the
! station_data_table or a fatal error will occur.
! A field id is returned from this call that will be used later in send_station_data.
!
!
!--------------------------------------------------------------------------
function register_station_field2d (module_name,fieldname,glo_lat,glo_lon,init_time, &
domain,longname,units)
integer :: register_station_field2d
character(len=*), intent(in) :: module_name, fieldname
real,dimension(:), intent(in) :: glo_lat,glo_lon
type(domain2d), intent(in) :: domain
type(time_type), intent(in) :: init_time
character(len=*), optional, intent(in) :: longname, units
real :: levels(1:1)
levels = 0.
register_station_field2d = register_station_field3d (module_name,fieldname,glo_lat,glo_lon,&
levels,init_time,domain,longname,units)
end function register_station_field2d
!--------------------------------------------------------------------------
function register_station_field3d (module_name,fieldname,glo_lat,glo_lon,levels,init_time, &
domain,longname,units)
! write field meta data on ROOT PE only
! allocate buffer
integer :: register_station_field3d
character(len=*), intent(in) :: module_name, fieldname
real,dimension(:), intent(in) :: glo_lat,glo_lon,levels !in X,Y,Z direction respectively
type(domain2d), intent(in) :: domain
type(time_type), intent(in) :: init_time
character(len=*), optional, intent(in) :: longname,units
integer :: i,ii, nlat, nlon,nlevel, isc, iec, jsc, jec
character(len=128) :: error_msg
integer :: local_num_stations ! number of stations on this PE
integer :: out_num ! position of this field in array output_fields
integer :: file_num, freq, output_units, outunit
real, allocatable :: station_values(:), level_values(:)
character(len=128) :: longname2,units2
if(PRESENT(longname)) then
longname2 = longname
else
longname2 = fieldname
endif
if(PRESENT(units)) then
units2 = units
else
units2 = "none"
endif
nlat = size(glo_lat); nlon = size(glo_lon); nlevel=size(levels)
allocate(station_values(num_stations), level_values(nlevel))
do i = 1, nlevel
level_values(i) = real(i)
enddo
! determine global index of this field in all stations
outunit = stdout()
do i = 1,num_stations
station_values(i) = real(i)
if(stations(i)%latglo_lat(nlat)) then
write(error_msg,'(F9.3)') stations(i)%lat
write(outunit,*) 'Station with latitude '//trim(error_msg)//' outside global latitude values'
call error_mesg ('register_station_field', 'latitude out of range', FATAL)
endif
if(stations(i)%longlo_lon(nlon)) then
write(error_msg,'(F9.3)') stations(i)%lon
write(outunit,*) 'Station with longitude '//trim(error_msg)//' outside global longitude values'
call error_mesg ('register_station_field', 'longitude out of range', FATAL)
endif
stations(i)%global_i = nearest_index(stations(i)%lon, glo_lon)
stations(i)%global_j = nearest_index(stations(i)%lat, glo_lat)
if(stations(i)%global_i<0 .or. stations(i)%global_j<0) &
call error_mesg ('register_station_field', 'Error in global index of station',FATAL)
enddo
! determine local index of this field in all stations , local index starts from 1
call mpp_get_compute_domain(domain, isc,iec,jsc,jec)
local_num_stations = 0
do i = 1,num_stations
if(isc<=stations(i)%global_i .and. iec>= stations(i)%global_i .and. &
jsc<=stations(i)%global_j .and. jec>= stations(i)%global_j) then
stations(i)%need_compute = .true.
stations(i)%local_i = stations(i)%global_i - isc + 1
stations(i)%local_j = stations(i)%global_j - jsc + 1
local_num_stations = local_num_stations +1
endif
enddo
! get the position of this field in the array output_fields
out_num = find_output_field(module_name, fieldname)
if(out_num < 0 .and. mpp_pe() == mpp_root_pe()) then
call error_mesg ('register_station_field', &
'module/field_name '//trim(module_name)//'/'//&
trim(fieldname)//' NOT found in station_data table', WARNING)
register_station_field3d = out_num
return
endif
if(local_num_stations>0) then
allocate(output_fields(out_num)%station_id(local_num_stations))
allocate(output_fields(out_num)%buffer(local_num_stations,nlevel))
output_fields(out_num)%buffer = EMPTY
! fill out list of available stations in this PE
ii=0
do i = 1,num_stations
if(stations(i)%need_compute) then
ii = ii+ 1
if(ii>local_num_stations) call error_mesg ('register_station_field', &
'error in determining local_num_station', FATAL)
output_fields(out_num)%station_id(ii)=stations(i)%id
endif
enddo
endif
output_fields(out_num)%num_station = local_num_stations
if( mpp_pe() == mpp_root_pe()) then
allocate(global_field%buffer(num_stations,nlevel))
global_field%buffer = MISSING
endif
output_fields(out_num)%register = .true.
output_fields(out_num)%output_name = fieldname
file_num = output_fields(out_num)%output_file
output_fields(out_num)%last_output = init_time
freq = files(file_num)%output_freq
output_units = files(file_num)%output_units
output_fields(out_num)%next_output = diag_time_inc(init_time, freq, output_units)
register_station_field3d = out_num
output_fields(out_num)%long_name = longname2
output_fields(out_num)%units = units2
output_fields(out_num)%nlevel = nlevel
! deal with axes
output_fields(out_num)%axes(1) = diag_axis_init('Stations',station_values,'station number', 'X')
if(nlevel == 1) then
output_fields(out_num)%num_axes = 1
else
output_fields(out_num)%num_axes = 2
output_fields(out_num)%axes(2) = diag_axis_init('Levels',level_values,'level number', 'Y' )
endif
if(need_write_axis) then
lat_axis = diag_axis_init('Latitude', stations(1:num_stations)%lat,'station latitudes', 'n')
lon_axis = diag_axis_init('Longitude',stations(1:num_stations)%lon, 'station longitudes', 'n')
endif
need_write_axis = .false.
! call mpp_sync()
end function register_station_field3d
!-------------------------------------------------------------------------
function find_output_field(module_name, field_name)
integer find_output_field
character(len=*), intent(in) :: module_name, field_name
integer :: i
find_output_field = -1
do i = 1, num_output_fields
if(trim(output_fields(i)%module_name) == trim(module_name) .and. &
lowercase(trim(output_fields(i)%output_name)) == &
lowercase(trim(field_name))) then
find_output_field = i
return
endif
end do
end function find_output_field
!-------------------------------------------------------------------------
subroutine opening_file(file)
! open file, write axis meta_data for all files (only on ROOT PE,
! do nothing on other PEs)
integer, intent(in) :: file
character(len=128) :: time_units
integer :: j,field_num,num_axes,axes(5),k
logical :: time_ops
integer :: time_axis_id(1),time_bounds_id(1)
write(time_units, 11) trim(time_unit_list(files(file)%time_units)), base_year, &
base_month, base_day, base_hour, base_minute, base_second
11 format(a, ' since ', i4.4, '-', i2.2, '-', i2.2, ' ', i2.2, ':', i2.2, ':', i2.2)
call mpp_open(files(file)%file_unit, files(file)%name, action=MPP_OVERWR, &
form=MPP_NETCDF, threading=MPP_SINGLE, fileset=MPP_SINGLE)
call mpp_write_meta (files(file)%file_unit, 'title', cval=trim(global_descriptor))
time_ops = .false.
do j = 1, files(file)%num_fields
field_num = files(file)%fields(j)
if(output_fields(field_num)%time_ops) then
time_ops = .true.
exit
endif
enddo
!write axis meta data
do j = 1, files(file)%num_fields
field_num = files(file)%fields(j)
num_axes = output_fields(field_num)%num_axes
axes(1:num_axes) = output_fields(field_num)%axes(1:num_axes)
do k = 1,num_axes
if(axes(k)<0) &
call error_mesg ('station_data opening_file','output_name '// &
trim(output_fields(field_num)%output_name)// &
' has axis_id = -1', FATAL)
enddo
axes(num_axes + 1) = lat_axis
axes(num_axes + 2) = lon_axis
axes(num_axes + 3) = files(file)%time_axis_id
! need in write_axis: name, unit,long_name
call write_axis_meta_data(files(file)%file_unit, axes(1:num_axes + 3), time_ops)
if(time_ops) then
axes(num_axes + 4) = files(file)%time_bounds_id
call write_axis_meta_data(files(file)%file_unit, axes(1:num_axes + 4))
endif
end do
!write field meta data
do j = 1, files(file)%num_fields
field_num = files(file)%fields(j)
num_axes = output_fields(field_num)%num_axes
axes(1:num_axes) = output_fields(field_num)%axes(1:num_axes)
num_axes = num_axes + 1
axes(num_axes) = files(file)%time_axis_id
diag_field = write_field_meta_data(files(file)%file_unit,output_fields(field_num)%output_name, &
axes(1:num_axes),output_fields(field_num)%units, &
output_fields(field_num)%long_name,time_method=output_fields(field_num)%time_method,&
pack=output_fields(field_num)%pack)
output_fields(field_num)%f_type = diag_field%Field
end do
if(time_ops) then
time_axis_id(1) = files(file)%time_axis_id
time_bounds_id(1) = files(file)%time_bounds_id
diag_field=write_field_meta_data(files(file)%file_unit, avg_name // '_T1',time_axis_id, &
time_units,"Start time for average period", pack=1)
files(file)%f_avg_start = diag_field%Field
diag_field=write_field_meta_data(files(file)%file_unit,avg_name // '_T2' ,time_axis_id, &
time_units,"End time for average period", pack=1)
files(file)%f_avg_end = diag_field%Field
diag_field=write_field_meta_data(files(file)%file_unit,avg_name // '_DT' ,time_axis_id, &
time_units,"Length of average period", pack=1)
files(file)%f_avg_nitems = diag_field%Field
diag_field=write_field_meta_data(files(file)%file_unit, 'Time_bounds', (/time_bounds_id,time_axis_id/), &
trim(time_unit_list(files(file)%time_units)), &
'Time axis boundaries', pack=1)
files(file)%f_bounds = diag_field%Field
endif
call done_meta_data(files(file)%file_unit)
end subroutine opening_file
!
!
! send_station_data(field_id, data, time)
!
!
! data should have the size of compute domain(isc:iec,jsc:jec)
! time is model's time
! field_id is returned from register_station_field
! only data at stations will be be sent to root_pe which, in turn, sends to output file
!
!
!-------------------------------------------------------------------------
subroutine send_station_data_2d(field_id, data, time)
integer, intent(in) :: field_id
real, intent(in) :: data(:,:)
type(time_type), intent(in) :: time
real :: data3d(size(data,1),size(data,2),1)
data3d(:,:,1) = data
call send_station_data_3d(field_id, data3d, time)
end subroutine send_station_data_2d
!-------------------------------------------------------------------------
subroutine send_station_data_3d(field_id, data, time)
integer, intent(in) :: field_id
real, intent(in) :: data(:,:,:)
type(time_type), intent(in) :: time
integer :: freq,units,file_num,local_num_stations,i,ii, max_counter
integer :: index_x, index_y, station_id
integer, allocatable :: station_ids(:) ! root_pe only, to receive local station_ids
real, allocatable :: tmp_buffer(:,:) ! root_pe only, to receive local buffer from each PE
if (.not.module_is_initialized) &
call error_mesg ('send_station_data_3d',' station_data NOT initialized', FATAL)
if(field_id < 0) return
file_num = output_fields(field_id)%output_file
if( mpp_pe() == mpp_root_pe() .and. files(file_num)%file_unit < 0) then
call opening_file(file_num)
endif
freq = files(file_num)%output_freq
units = files(file_num)%output_units
! compare time with next_output
if (time > output_fields(field_id)%next_output .and. freq /= END_OF_RUN) then ! time to write out
! ALL PEs, including root PE, must send data to root PE
call mpp_send(output_fields(field_id)%num_station,plen=1,to_pe=mpp_root_pe())
if(output_fields(field_id)%num_station > 0) then
call mpp_send(output_fields(field_id)%station_id(1),plen=size(output_fields(field_id)%station_id),&
to_pe=mpp_root_pe())
call mpp_send(output_fields(field_id)%buffer(1,1),plen=size(output_fields(field_id)%buffer),&
to_pe=mpp_root_pe())
endif
! get max_counter if the field is averaged
if(output_fields(field_id)%time_average) then
max_counter = output_fields(field_id)%counter
call mpp_max(max_counter, pelist)
endif
! receive local data from all PEs
if(mpp_pe() == mpp_root_pe()) then
do i = 1,size(pelist)
call mpp_recv(local_num_stations,glen=1,from_pe=pelist(i))
if(local_num_stations> 0) then
allocate(station_ids(local_num_stations))
allocate(tmp_buffer(local_num_stations,output_fields(field_id)%nlevel))
call mpp_recv(station_ids(1), glen=size(station_ids), from_pe=pelist(i))
call mpp_recv(tmp_buffer(1,1),glen=size(tmp_buffer), from_pe=pelist(i))
do ii = 1,local_num_stations
global_field%buffer(station_ids(ii),:) = tmp_buffer(ii,:)
enddo
deallocate(station_ids, tmp_buffer)
endif
enddo
! send global_buffer content to file
if(output_fields(field_id)%time_average) then
if(max_counter == 0 ) &
call error_mesg ('send_station_data','counter=0 for averaged field '// &
output_fields(field_id)%output_name, FATAL)
global_field%buffer = global_field%buffer/real(max_counter)
endif
! check if global_field contains any missing values
if(any(global_field%buffer == MISSING)) &
call error_mesg ('send_station_data','Global_field contains MISSING, field '// &
output_fields(field_id)%output_name, FATAL)
call station_data_out(file_num,field_id,global_field%buffer,output_fields(field_id)%next_output)
global_field%buffer = MISSING
endif
call mpp_sync()
! clear buffer, increment next_output time and reset counter on ALL PEs
if(output_fields(field_id)%num_station>0) output_fields(field_id)%buffer = EMPTY
output_fields(field_id)%last_output = output_fields(field_id)%next_output
output_fields(field_id)%next_output = diag_time_inc(output_fields(field_id)%next_output,&
freq, units)
output_fields(field_id)%counter = 0; max_counter = 0
endif
! accumulate buffer only
do i = 1 , output_fields(field_id)%num_station
station_id = output_fields(field_id)%station_id(i)
index_x = stations(station_id)%local_i; index_y = stations(station_id)%local_j
if(index_x>size(data,1) .or. index_y>size(data,2)) &
call error_mesg ('send_station_data','local index out of range for field '// &
output_fields(field_id)%output_name, FATAL)
if(output_fields(field_id)%time_average) then
output_fields(field_id)%buffer(i,:) = output_fields(field_id)%buffer(i,:) + &
data(index_x,index_y,:) ! accumulate buffer
else ! not average
output_fields(field_id)%buffer(i,:) = data(index_x,index_y,:)
endif
enddo
if(output_fields(field_id)%time_average) &
output_fields(field_id)%counter = output_fields(field_id)%counter + 1
end subroutine send_station_data_3d
!------------------------------------------------------------------------
subroutine station_data_out(file, field, data, time,final_call_in)
integer, intent(in) :: file, field
real, intent(inout) :: data(:, :)
type(time_type), intent(in) :: time
logical, optional, intent(in):: final_call_in
logical :: final_call
integer :: i, num
real :: dif, time_data(2, 1, 1), dt_time(1, 1, 1), start_dif, end_dif
final_call = .false.
if(present(final_call_in)) final_call = final_call_in
dif = get_date_dif(time, base_time, files(file)%time_units)
call mpp_write(files(file)%file_unit,output_fields(field)%f_type, data, dif)
start_dif = get_date_dif(output_fields(field)%last_output, base_time,files(file)%time_units)
end_dif = dif
do i = 1, files(file)%num_fields
num = files(file)%fields(i)
if(output_fields(num)%time_ops) then
if(num == field) then
time_data(1, 1, 1) = start_dif
call mpp_write(files(file)%file_unit, files(file)%f_avg_start, &
time_data(1:1,:,:), dif)
time_data(2, 1, 1) = end_dif
call mpp_write(files(file)%file_unit, files(file)%f_avg_end, &
time_data(2:2,:,:), dif)
dt_time(1, 1, 1) = end_dif - start_dif
call mpp_write(files(file)%file_unit, files(file)%f_avg_nitems, &
dt_time(1:1,:,:), dif)
! Include boundary variable for CF compliance
call mpp_write(files(file)%file_unit, files(file)%f_bounds, &
time_data(1:2,:,:), dif)
exit
endif
end if
end do
if(final_call) then
if(time >= files(file)%last_flush) then
call mpp_flush(files(file)%file_unit)
files(file)%last_flush = time
endif
else
if(time > files(file)%last_flush) then
call mpp_flush(files(file)%file_unit)
files(file)%last_flush = time
endif
endif
end subroutine station_data_out
!
!
! station_data_end(time)
!
!
! Must be called after the last time step to write the buffer content
!
!
!-----------------------------------------------------------------------------
subroutine station_data_end(time)
type(time_type), intent(in) :: time !model's time
integer :: freq, max_counter, local_num_stations
integer :: file, nfield, field, pe, col
integer, allocatable :: station_ids(:) ! root_pe only, to receive local station_ids
real, allocatable :: tmp_buffer(:,:) ! root_pe only, to receive local buffer from each PE
do file = 1, num_files
freq = files(file)%output_freq
do nfield = 1, files(file)%num_fields
field = files(file)%fields(nfield)
if(.not. output_fields(field)%register) cycle
if(time >= output_fields(field)%next_output .or. freq == END_OF_RUN) then
! ALL PEs, including root PE, must send data to root PE
call mpp_send(output_fields(field)%num_station,plen=1,to_pe=mpp_root_pe())
if(output_fields(field)%num_station > 0) then
call mpp_send(output_fields(field)%station_id(1),plen=size(output_fields(field)%station_id),&
to_pe=mpp_root_pe())
call mpp_send(output_fields(field)%buffer(1,1),plen=size(output_fields(field)%buffer),&
to_pe=mpp_root_pe())
endif
! get max_counter if the field is averaged
if(output_fields(field)%time_average) then
max_counter = output_fields(field)%counter
call mpp_max(max_counter, pelist)
endif
! only root PE receives local data from all PEs
if(mpp_pe() == mpp_root_pe()) then
do pe = 1,size(pelist)
call mpp_recv(local_num_stations,glen=1,from_pe=pelist(pe))
if(local_num_stations> 0) then
allocate(station_ids(local_num_stations))
allocate(tmp_buffer(local_num_stations,output_fields(field)%nlevel))
call mpp_recv(station_ids(1), glen=size(station_ids), from_pe=pelist(pe))
call mpp_recv(tmp_buffer(1,1),glen=size(tmp_buffer),from_pe=pelist(pe))
do col = 1,local_num_stations
global_field%buffer(station_ids(col),:) = tmp_buffer(col,:)
enddo
deallocate(station_ids, tmp_buffer)
endif
enddo
! send global_buffer content to file
if(output_fields(field)%time_average)then
if(max_counter == 0 )&
call error_mesg ('send_station_end','counter=0 for averaged field '// &
output_fields(field)%output_name, FATAL)
global_field%buffer = global_field%buffer/real(max_counter)
endif
! check if global_field contains any missing values
if(any(global_field%buffer == MISSING)) &
call error_mesg ('send_station_end','Global_field contains MISSING, field '// &
output_fields(field)%output_name, FATAL)
call station_data_out(file,field,global_field%buffer,output_fields(field)%next_output,.true.)
global_field%buffer = MISSING
endif
call mpp_sync()
endif
!deallocate field buffer
if(output_fields(field)%num_station>0) &
deallocate(output_fields(field)%buffer, output_fields(field)%station_id)
enddo ! nfield
enddo ! file
if(mpp_pe() == mpp_root_pe()) deallocate(global_field%buffer)
end subroutine station_data_end
!-----------------------------------------------------------------------------------
end module station_data_mod