From 1304a2086f4fe070cf7c5092dad7b0acbfb8143c Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Sun, 20 Sep 2020 13:35:03 -0600 Subject: [PATCH 01/16] First pass at hash table module and test function --- src/hash/hist_hash_table.F90 | 367 +++++++++++++++++++++++++++++++++++ test/Makefile | 28 +++ test/test_hash.F90 | 10 + 3 files changed, 405 insertions(+) create mode 100644 src/hash/hist_hash_table.F90 create mode 100644 test/Makefile create mode 100644 test/test_hash.F90 diff --git a/src/hash/hist_hash_table.F90 b/src/hash/hist_hash_table.F90 new file mode 100644 index 0000000..cec877e --- /dev/null +++ b/src/hash/hist_hash_table.F90 @@ -0,0 +1,367 @@ +!!XXgoldyXX: To do, statistics output +module hist_hash_table + + implicit none + private + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! + ! Hashing. + ! + ! Accelerate processing (e.g., of outfld) by using a hash function of + ! the field name + ! + ! Note: the hashing logic will fail if any of the following are true: + ! + ! 1) The lower bound on the dimension of 'masterlist' is less than 1. + ! + ! 2) 'outfld' is called with field names that are not defined on + ! masterlist. This applies to both initial/branch and restart + ! runs. + ! + ! 3) An inconsistency between a field's file active flag + ! 'masterlist(ff)%actflag(t)' and active fields read from + ! restart files. + ! + ! 4) Invoking function 'gen_hash_key' before the primary and + ! secondary hash tables have been created + ! (routine bld_outfld_hash_tbls). + ! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ! + ! User definable constants for hash and overflow tables. + ! Define size of primary hash table (specified as 2**size). + ! +! integer, parameter :: tbl_hash_pri_sz_lg2 = 16 + ! + ! Define size of overflow hash table % of primary hash table. + ! +! integer, parameter :: tbl_hash_oflow_percent = 20 + ! + ! Do *not* modify the parameters below. + ! +! integer, parameter :: tbl_hash_pri_sz = 2**tbl_hash_pri_sz_lg2 +! integer, parameter :: tbl_hash_oflow_sz = tbl_hash_pri_sz * (tbl_hash_oflow_percent/100.0_r8) + ! + ! The primary and overflow tables are organized to mimimize space (read: + ! try to maximimze cache line usage). + ! + ! gen_hash_key(fieldname) will return an index on the interval + ! [0 ... tbl_hash_pri_sz-1]. + ! + ! + ! Primary: + ! gen_hash_key(fieldname)-------+ +----------+ + ! | | -ii | 1 ------>tbl_hash_oflow(ii) + ! | +----------+ + ! +--> | ff | 2 ------>masterlist(ff) + ! +----------+ + ! | | ... + ! +----------+ + ! | | tbl_hash_pri_sz + ! +----------+ + ! + ! Overflow (if tbl_hash_pri() < 0): + ! tbl_hash_pri(gen_hash_key(fieldname)) + ! | + ! | +----------+ + ! | | 1 | 1 (one entry on O.F. chain) + ! | +----------+ + ! | | ff_m | 2 + ! | +----------+ + ! +---------> | 3 | 3 (three entries on chain) + ! +----------+ + ! | ff_x | 4 + ! +----------+ + ! | ff_y | 5 + ! +----------+ + ! | ff_z | 6 + ! +----------+ + ! | | ... + ! +----------+ + ! | | tbl_hash_oflow_sz + ! +----------+ + ! + ! + ! + ! Constants used in hashing function gen_hash_key. + ! Note: if the constants in table 'tbl_gen_hash_key' below are modified, + ! changes are required to routine 'gen_hash_key' because of specific + ! logic in the routine that optimizes character strings of length 8. + ! + + type, abstract, public :: hist_hashable_t + ! The hashable type is a base type that contains a hash key. + integer, private :: hash_key_len = 0 + contains + procedure :: key_len + procedure(hist_hashable_get_key), deferred :: key + end type hist_hashable_t + + type, public, extends(hist_hashable_t) :: hist_hashable_char_t + character(len=:), private, allocatable :: name + contains + procedure :: key => hist_hashable_char_get_key + end type hist_hashable_char_t + + integer, parameter :: gen_hash_key_offset = 21467 ! z'000053db' + + integer, parameter :: tbl_max_idx = 15 + integer, parameter, dimension(0:tbl_max_idx) :: tbl_gen_hash_key = & + (/ 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 17, 13, 11, 7, 3, 1 /) + + integer, parameter :: table_factor_size = 8 ! Table size / # entries + integer, parameter :: table_overflow_factor = 4 ! # entries / Overflow size + + type :: hash_overflow_entry + ! An entry for a hash overflow table + integer, pointer :: hentry(:) => NULL() + end type hash_overflow_entry + + type, public :: hist_hash_table_t + ! hist_hash_table_t contains all information to build and use a hash table + ! It also keeps track of statistics such as collision frequency and size + integer, private :: table_size = -1 + integer, private :: overflow_size = -1 + integer, private :: key_offset = gen_hash_key_offset + integer, private, allocatable :: primary_table(:) + integer, private :: next_overflow_index = 1 + type(hash_overflow_entry), private, allocatable :: overflow_table(:) + ! Statistics + integer, private :: num_keys = 0 + integer, private :: num_key_collisions = 0 + integer, private :: max_collision = 0 + contains + procedure :: initialize => hash_table_initialize_table + procedure :: key_hash => hash_table_key_hash + procedure :: add_hash_key => hash_table_add_hash_key + procedure :: table_index => hash_table_table_index + end type hist_hash_table_t + + ! Abstract interface for key procedure of hist_hashable class + abstract interface + function hist_hashable_get_key(hashable) + import :: hist_hashable_t + class(hist_hashable_t), intent(in) :: hashable + character(len=:), allocatable :: hist_hashable_get_key + end function hist_hashable_get_key + end interface + +CONTAINS + + !####################################################################### + integer function key_len(this) + class(hist_hashable_t), intent(in) :: this + + key_len = this%hash_key_len + end function key_len + + !####################################################################### + + function hist_hashable_char_get_key(hashable) + ! Return the hashable char class key (name) + class(hist_hashable_char_t), intent(in) :: hashable + character(len=:), allocatable :: hist_hashable_char_get_key + + hist_hashable_char_get_key = hashable%name + end function hist_hashable_char_get_key + + !####################################################################### + + subroutine hash_table_initialize_table(this, tbl_size, ovflw_size, key_off) + ! Initialize this table. + ! Dummy arguments + class(hist_hash_table_t) :: this + integer, intent(in) :: tbl_size ! new table size + integer, intent(in) :: ovflw_size ! overflow table size + integer, optional, intent(in) :: key_off ! key offset + ! Local variable + integer :: index + + ! Clear this table so it can be initialized + if (allocated(this%primary_table)) then + deallocate(this%primary_table) + end if + if (allocated(this%overflow_table)) then + do index = 1, size(this%overflow_table, 1) + if (associated(this%overflow_table(index)%hentry)) then + deallocate(this%overflow_table(index)%hentry) + nullify(this%overflow_table(index)%hentry) + end if + end do + deallocate(this%overflow_table) + end if + this%table_size = tbl_size + allocate(this%primary_table(this%table_size)) + this%primary_table = 0 + this%overflow_size = ovflw_size + allocate(this%overflow_table(this%overflow_size)) + if (present(key_off)) then + this%key_offset = key_off + end if + end subroutine hash_table_initialize_table + + !####################################################################### + + integer function hash_table_key_hash(this, string) result(hash_key) + ! + !----------------------------------------------------------------------- + ! + ! Purpose: Generate a hash key on the interval [0 .. tbl_hash_pri_sz-1] + ! given a character string. + ! + ! Algorithm is a variant of perl's internal hashing function. + ! + !----------------------------------------------------------------------- + ! + ! + ! Arguments: + ! + class(hist_hash_table_t) :: this + character(len=*), intent(in) :: string + ! + ! Local. + ! + integer :: hash + integer :: index + integer :: ind_fact + integer :: hash_fact + + hash = this%key_offset + ind_fact = 0 + do index = 1, len(string) + ind_fact = ind_fact + 1 + if (ind_fact > tbl_max_idx) then + ind_fact = 1 + end if + hash_fact = tbl_gen_hash_key(ind_fact) + hash = ieor(hash, (ichar(string(index:index)) * hash_fact)) + end do + + hash_key = iand(hash, this%table_size - 1) + + end function hash_table_key_hash + + !####################################################################### + + integer function hash_table_table_index(this, hash_arr, key, & + errmsg) result(indx) + ! + !----------------------------------------------------------------------- + ! + ! Purpose: Return the the index of in + ! + ! If the object is not found, return -1. + ! + ! is an array of hashable objects + ! For correct usage, all of the keys in + ! MUST have been added to table. + ! + !----------------------------------------------------------------------- + ! + ! Arguments: + ! + class(hist_hash_table_t) :: this + class(hist_hashable_t), intent(in) :: hash_arr(:) + character(len=*), intent(in) :: key + character(len=*), optional, intent(out) :: errmsg + ! + ! Local. + ! + integer :: hash_key + integer :: ovflw_cind ! location of number of overflow entries + integer :: ind_ovflw ! Index of overflow chain in overflow table + integer :: num_ovflw ! Number of entries on overflow chain + integer :: test_ind ! Test index for overflow entries + integer, pointer :: ovflw(:) ! Overflow chain + character(len=*), parameter :: subname = 'HASH_TABLE_TABLE_INDEX' + + errmsg = '' + hash_key = this%key_hash(key) + indx = this%primary_table(hash_key) + if (indx < 0) then + ovflw_cind = abs(indx) + ovflw => this%overflow_table(ovflw_cind)%hentry + if (.not. associated(ovflw)) then + if (present(errmsg)) then + write(errmsg, *) subname, ": Empty overflow entry for '", & + trim(key), "'" + end if + else + num_ovflw = size(ovflw, 1) + do ind_ovflw = 1, num_ovflw + test_ind = ovflw(ind_ovflw) + if (hash_arr(test_ind)%key() == key) then + indx = test_ind + exit + end if + end do + end if + end if + + if ((indx <= 0) .and. present(errmsg)) then + if (indx == 0) then + write(errmsg, *) subname, ": No entry for '", trim(key), "'" + else if (len_trim(errmsg) == 0) then + write(errmsg, *) subname, ": No overflow entry for '", & + trim(key), "'" + end if ! No else, we have already recorded an error + else if ((hash_arr(test_ind)%key() /= key) .and. present(errmsg)) then + write(errmsg, *) subname, ": error finding field '", trim(key), "'" + end if + + end function hash_table_table_index + + !####################################################################### + + subroutine hash_table_add_hash_key(this, key, keyval) + ! + !----------------------------------------------------------------------- + ! + ! Purpose: Add to this hash table using key, + ! + ! + !----------------------------------------------------------------------- + + ! Dummy arguments: + class(hist_hash_table_t) :: this + character(len=*), intent(in) :: key + integer, intent(in) :: keyval + ! Local variables + integer :: hash_ind + integer :: ovflw_ind + integer :: ovflw_len + integer, pointer :: curr_ovflw(:) + character(len=*), parameter :: subname = 'HASH_TABLE_ADD_HASH_KEY' + + hash_ind = this%key_hash(key) + if (this%primary_table(hash_ind) == 0) then + ! No entry, add the entry here + this%primary_table(hash_ind) = keyval + else if (this%primary_table(hash_ind) < 0) then + ! We already have a collision here, add a new one + ovflw_ind = abs(hash_ind) + curr_ovflw => this%overflow_table(ovflw_ind)%hentry + ovflw_len = size(curr_ovflw, 1) + 1 + allocate(this%overflow_table(ovflw_ind)%hentry(ovflw_len)) + this%overflow_table(ovflw_ind)%hentry(1:ovflw_len-1) = curr_ovflw(:) + this%overflow_table(ovflw_ind)%hentry(ovflw_len) = keyval + this%max_collision = MAX(this%max_collision, ovflw_len) + else + ! We have a new collision, create an overflow entry + ovflw_ind = this%next_overflow_index + this%next_overflow_index = this%next_overflow_index + 1 + ovflw_len = 2 + allocate(this%overflow_table(ovflw_ind)%hentry(ovflw_len)) + this%overflow_table(ovflw_ind)%hentry(1) = this%primary_table(hash_ind) + this%overflow_table(ovflw_ind)%hentry(ovflw_len) = keyval + this%max_collision = MAX(this%max_collision, ovflw_len) + this%num_key_collisions = this%num_key_collisions + 1 + this%primary_table(hash_ind) = -ovflw_ind + end if + this%num_keys = this%num_keys + 1 + + end subroutine hash_table_add_hash_key + +end module hist_hash_table diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..99754f5 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,28 @@ +SHELL = /bin/sh + +INCFLAG = -I +INCPATH += $(INCFLAG). +FCFLAGS += -g + +SRCPATH = ../src +HASHPATH = $(SRCPATH)/hash + +# Make sure we have a log file +ifeq ($(LOGFILE),) +LOGFILE := hist_test.log +endif + +# TARGETS + +hist_hash_table.o: $(HASHPATH)/hist_hash_table.F90 + @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) + @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) + +test_hash_table: test_hash.F90 hist_hash_table.o + @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) + @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) + +# CLEAN +clean: + @rm -f *.o *.mod + @rm -f test_hash_table diff --git a/test/test_hash.F90 b/test/test_hash.F90 new file mode 100644 index 0000000..35c7716 --- /dev/null +++ b/test/test_hash.F90 @@ -0,0 +1,10 @@ +program test_hash + use hist_hash_table, only: hist_hashable_t, hist_hashable_char_t + use hist_hash_table, only: hist_hash_table_t + + type(hist_hashable_char_t), allocatable :: hash_chars(:) + type(hist_hash_table_t) :: hash_table + + call hash_table%initialize(10, 5) + +end program test_hash From 401d231f00baa9ab1494dc72d829209af45c32c8 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Sun, 20 Sep 2020 22:45:55 -0600 Subject: [PATCH 02/16] New table format --- src/hash/hist_hash_table.F90 | 271 ++++++++++++++++++++--------------- test/test_hash.F90 | 41 +++++- 2 files changed, 193 insertions(+), 119 deletions(-) diff --git a/src/hash/hist_hash_table.F90 b/src/hash/hist_hash_table.F90 index cec877e..4e6fac3 100644 --- a/src/hash/hist_hash_table.F90 +++ b/src/hash/hist_hash_table.F90 @@ -93,9 +93,7 @@ module hist_hash_table type, abstract, public :: hist_hashable_t ! The hashable type is a base type that contains a hash key. - integer, private :: hash_key_len = 0 contains - procedure :: key_len procedure(hist_hashable_get_key), deferred :: key end type hist_hashable_t @@ -105,6 +103,17 @@ module hist_hash_table procedure :: key => hist_hashable_char_get_key end type hist_hashable_char_t + public :: new_hashable_char + + type, public, extends(hist_hashable_t) :: hist_hashable_int_t + integer, private :: value + contains + procedure :: key => hist_hashable_int_get_key + procedure :: val => hist_hashable_int_get_val + end type hist_hashable_int_t + + public :: new_hashable_int + integer, parameter :: gen_hash_key_offset = 21467 ! z'000053db' integer, parameter :: tbl_max_idx = 15 @@ -114,29 +123,30 @@ module hist_hash_table integer, parameter :: table_factor_size = 8 ! Table size / # entries integer, parameter :: table_overflow_factor = 4 ! # entries / Overflow size - type :: hash_overflow_entry - ! An entry for a hash overflow table - integer, pointer :: hentry(:) => NULL() - end type hash_overflow_entry + type :: table_entry_t + ! Any table entry contains a key and a value + class(hist_hashable_t), pointer :: entry_value => NULL() + type(table_entry_t), pointer :: next => NULL() + contains + final :: finalize_table_entry + end type table_entry_t type, public :: hist_hash_table_t ! hist_hash_table_t contains all information to build and use a hash table ! It also keeps track of statistics such as collision frequency and size - integer, private :: table_size = -1 - integer, private :: overflow_size = -1 - integer, private :: key_offset = gen_hash_key_offset - integer, private, allocatable :: primary_table(:) - integer, private :: next_overflow_index = 1 - type(hash_overflow_entry), private, allocatable :: overflow_table(:) - ! Statistics - integer, private :: num_keys = 0 - integer, private :: num_key_collisions = 0 - integer, private :: max_collision = 0 + integer, private :: table_size = -1 + integer, private :: overflow_size = -1 + integer, private :: key_offset = gen_hash_key_offset + type(table_entry_t), private, allocatable :: primary_table(:) + ! Statistics + integer, private :: num_keys = 0 + integer, private :: num_key_collisions = 0 + integer, private :: max_collision = 0 contains procedure :: initialize => hash_table_initialize_table procedure :: key_hash => hash_table_key_hash procedure :: add_hash_key => hash_table_add_hash_key - procedure :: table_index => hash_table_table_index + procedure :: table_value => hash_table_table_value end type hist_hash_table_t ! Abstract interface for key procedure of hist_hashable class @@ -151,11 +161,13 @@ end function hist_hashable_get_key CONTAINS !####################################################################### - integer function key_len(this) - class(hist_hashable_t), intent(in) :: this - key_len = this%hash_key_len - end function key_len + function new_hashable_char(name_in) + type(hist_hashable_char_t) :: new_hashable_char + character(len=*), intent(in) :: name_in + + new_hashable_char%name = name_in + end function new_hashable_char !####################################################################### @@ -169,12 +181,55 @@ end function hist_hashable_char_get_key !####################################################################### - subroutine hash_table_initialize_table(this, tbl_size, ovflw_size, key_off) + function new_hashable_int(value_in) + type(hist_hashable_int_t) :: new_hashable_int + integer, intent(in) :: value_in + + new_hashable_int%value = value_in + end function new_hashable_int + + !####################################################################### + + function hist_hashable_int_get_key(hashable) + ! Return the hashable int class key (value ==> string) + class(hist_hashable_int_t), intent(in) :: hashable + character(len=:), allocatable :: hist_hashable_int_get_key + + character(len=32) :: key_str + + write(key_str, '(i0)') hashable%val() + hist_hashable_int_get_key = trim(key_str) + end function hist_hashable_int_get_key + + !####################################################################### + + integer function hist_hashable_int_get_val(hashable) + ! Return the hashable int class value + class(hist_hashable_int_t), intent(in) :: hashable + + hist_hashable_int_get_val = hashable%value + end function hist_hashable_int_get_val + + !####################################################################### + + subroutine finalize_table_entry(te) + type(table_entry_t) :: te + + nullify(te%entry_value) ! We do not own the memory + if (associated(te%next)) then + deallocate(te%next) ! This should invoke finalize recursively + nullify(te%next) + end if + + end subroutine finalize_table_entry + + !####################################################################### + + subroutine hash_table_initialize_table(this, tbl_size, key_off) ! Initialize this table. ! Dummy arguments class(hist_hash_table_t) :: this integer, intent(in) :: tbl_size ! new table size - integer, intent(in) :: ovflw_size ! overflow table size integer, optional, intent(in) :: key_off ! key offset ! Local variable integer :: index @@ -183,20 +238,8 @@ subroutine hash_table_initialize_table(this, tbl_size, ovflw_size, key_off) if (allocated(this%primary_table)) then deallocate(this%primary_table) end if - if (allocated(this%overflow_table)) then - do index = 1, size(this%overflow_table, 1) - if (associated(this%overflow_table(index)%hentry)) then - deallocate(this%overflow_table(index)%hentry) - nullify(this%overflow_table(index)%hentry) - end if - end do - deallocate(this%overflow_table) - end if this%table_size = tbl_size allocate(this%primary_table(this%table_size)) - this%primary_table = 0 - this%overflow_size = ovflw_size - allocate(this%overflow_table(this%overflow_size)) if (present(key_off)) then this%key_offset = key_off end if @@ -230,7 +273,7 @@ integer function hash_table_key_hash(this, string) result(hash_key) hash = this%key_offset ind_fact = 0 - do index = 1, len(string) + do index = 1, len_trim(string) ind_fact = ind_fact + 1 if (ind_fact > tbl_max_idx) then ind_fact = 1 @@ -245,120 +288,116 @@ end function hash_table_key_hash !####################################################################### - integer function hash_table_table_index(this, hash_arr, key, & - errmsg) result(indx) + function hash_table_table_value(this, key, errmsg) result(tbl_val) ! !----------------------------------------------------------------------- ! - ! Purpose: Return the the index of in + ! Purpose: Return the the key value of ! - ! If the object is not found, return -1. - ! - ! is an array of hashable objects - ! For correct usage, all of the keys in - ! MUST have been added to table. + ! If the object is not found, return NULL ! !----------------------------------------------------------------------- ! ! Arguments: ! - class(hist_hash_table_t) :: this - class(hist_hashable_t), intent(in) :: hash_arr(:) - character(len=*), intent(in) :: key - character(len=*), optional, intent(out) :: errmsg + class(hist_hash_table_t) :: this + character(len=*), intent(in) :: key + character(len=*), optional, intent(out) :: errmsg + class(hist_hashable_t), pointer :: tbl_val ! ! Local. ! - integer :: hash_key - integer :: ovflw_cind ! location of number of overflow entries - integer :: ind_ovflw ! Index of overflow chain in overflow table - integer :: num_ovflw ! Number of entries on overflow chain - integer :: test_ind ! Test index for overflow entries - integer, pointer :: ovflw(:) ! Overflow chain - character(len=*), parameter :: subname = 'HASH_TABLE_TABLE_INDEX' + integer :: hash_key + type(table_entry_t), pointer :: next_ptr + character(len=*), parameter :: subname = 'HASH_TABLE_TABLE_INDEX' errmsg = '' + nullify(tbl_val) hash_key = this%key_hash(key) - indx = this%primary_table(hash_key) - if (indx < 0) then - ovflw_cind = abs(indx) - ovflw => this%overflow_table(ovflw_cind)%hentry - if (.not. associated(ovflw)) then - if (present(errmsg)) then - write(errmsg, *) subname, ": Empty overflow entry for '", & - trim(key), "'" - end if - else - num_ovflw = size(ovflw, 1) - do ind_ovflw = 1, num_ovflw - test_ind = ovflw(ind_ovflw) - if (hash_arr(test_ind)%key() == key) then - indx = test_ind + if (this%primary_table(hash_key)%entry_value%key() == trim(key)) then + tbl_val => this%primary_table(hash_key)%entry_value + else + next_ptr => this%primary_table(hash_key)%next + do + if (associated(next_ptr)) then + if (next_ptr%entry_value%key() == trim(key)) then + tbl_val => next_ptr%entry_value exit end if - end do - end if + next_ptr => next_ptr%next + else + exit + end if + end do end if - if ((indx <= 0) .and. present(errmsg)) then - if (indx == 0) then - write(errmsg, *) subname, ": No entry for '", trim(key), "'" - else if (len_trim(errmsg) == 0) then - write(errmsg, *) subname, ": No overflow entry for '", & - trim(key), "'" - end if ! No else, we have already recorded an error - else if ((hash_arr(test_ind)%key() /= key) .and. present(errmsg)) then - write(errmsg, *) subname, ": error finding field '", trim(key), "'" + if ((.not. associated(tbl_val)) .and. present(errmsg)) then + write(errmsg, *) subname, ": No entry for '", trim(key), "'" end if - end function hash_table_table_index + end function hash_table_table_value !####################################################################### - subroutine hash_table_add_hash_key(this, key, keyval) + subroutine hash_table_add_hash_key(this, newval, errmsg) ! !----------------------------------------------------------------------- ! - ! Purpose: Add to this hash table using key, + ! Purpose: Add to this hash table using its key + ! Its key must not be an empty string + ! It is an error to try to add a key more than once ! ! !----------------------------------------------------------------------- ! Dummy arguments: - class(hist_hash_table_t) :: this - character(len=*), intent(in) :: key - integer, intent(in) :: keyval + class(hist_hash_table_t) :: this + class(hist_hashable_t), pointer :: newval + character(len=*), optional, intent(out) :: errmsg ! Local variables - integer :: hash_ind - integer :: ovflw_ind - integer :: ovflw_len - integer, pointer :: curr_ovflw(:) - character(len=*), parameter :: subname = 'HASH_TABLE_ADD_HASH_KEY' - - hash_ind = this%key_hash(key) - if (this%primary_table(hash_ind) == 0) then - ! No entry, add the entry here - this%primary_table(hash_ind) = keyval - else if (this%primary_table(hash_ind) < 0) then - ! We already have a collision here, add a new one - ovflw_ind = abs(hash_ind) - curr_ovflw => this%overflow_table(ovflw_ind)%hentry - ovflw_len = size(curr_ovflw, 1) + 1 - allocate(this%overflow_table(ovflw_ind)%hentry(ovflw_len)) - this%overflow_table(ovflw_ind)%hentry(1:ovflw_len-1) = curr_ovflw(:) - this%overflow_table(ovflw_ind)%hentry(ovflw_len) = keyval - this%max_collision = MAX(this%max_collision, ovflw_len) + integer :: hash_ind + integer :: ovflw_len + character(len=:), allocatable :: newkey + type(table_entry_t), pointer :: next_ptr + type(table_entry_t), pointer :: new_entry + character(len=*), parameter :: subname = 'HASH_TABLE_ADD_HASH_KEY' + + newkey = newval%key() + hash_ind = this%key_hash(newkey) + ! Check for this entry + if (associated(this%table_value(newkey))) then + if (present(errmsg)) then + write(errmsg, *) subname, " ERROR: key, '", newkey, & + "' already in table" + end if else - ! We have a new collision, create an overflow entry - ovflw_ind = this%next_overflow_index - this%next_overflow_index = this%next_overflow_index + 1 - ovflw_len = 2 - allocate(this%overflow_table(ovflw_ind)%hentry(ovflw_len)) - this%overflow_table(ovflw_ind)%hentry(1) = this%primary_table(hash_ind) - this%overflow_table(ovflw_ind)%hentry(ovflw_len) = keyval - this%max_collision = MAX(this%max_collision, ovflw_len) - this%num_key_collisions = this%num_key_collisions + 1 - this%primary_table(hash_ind) = -ovflw_ind + ASSOCIATE(tbl_entry => this%primary_table(hash_ind)) + if (associated(tbl_entry%entry_value)) then + ! We have a collision, make a new entry + allocate(new_entry) + new_entry%entry_value => newval + ! Now, find a spot + if (associated(tbl_entry%next)) then + ovflw_len = 1 + next_ptr => tbl_entry%next + do + if (associated(next_ptr%next)) then + ovflw_len = ovflw_len + 1 + next_ptr => next_ptr%next + else + exit + end if + end do + ovflw_len = ovflw_len + 1 + next_ptr%next => new_entry + else + this%num_key_collisions = this%num_key_collisions + 1 + tbl_entry%next => new_entry + ovflw_len = 1 + end if + this%max_collision = MAX(this%max_collision, ovflw_len) + end if + END ASSOCIATE end if this%num_keys = this%num_keys + 1 diff --git a/test/test_hash.F90 b/test/test_hash.F90 index 35c7716..c178111 100644 --- a/test/test_hash.F90 +++ b/test/test_hash.F90 @@ -1,10 +1,45 @@ -program test_hash +module test_hash_utils use hist_hash_table, only: hist_hashable_t, hist_hashable_char_t - use hist_hash_table, only: hist_hash_table_t + use hist_hash_table, only: new_hashable_char + + implicit none + private + + public :: new_hash_char_array + +CONTAINS + + subroutine new_hash_char_array(keys, hash_arr) + character(len=*), intent(in) :: keys(:) + type(hist_hashable_char_t), allocatable, intent(out) :: hash_arr(:) + + integer :: index + + allocate(hash_arr(size(keys, 1))) + do index = 1, size(hash_arr, 1) + hash_arr(index) = new_hashable_char(keys(index)) + end do + end subroutine new_hash_char_array + +end module test_hash_utils + +program test_hash + use hist_hash_table, only: hist_hashable_char_t, hist_hash_table_t + use test_hash_utils, only: new_hash_char_array + character(len=10) :: hash_names(4) = (/ 'foo ', 'bar ', & + 'foobar ', 'big daddy ' /) type(hist_hashable_char_t), allocatable :: hash_chars(:) type(hist_hash_table_t) :: hash_table + integer :: index + character(len=1024) :: errors + character(len=128) :: errmsg - call hash_table%initialize(10, 5) + errors = '' + call new_hash_char_array(hash_names, hash_chars) + call hash_table%initialize(10) + do index = 1, size(hash_chars, 1) + call hash_table%add_hash_key(hash_chars(index)) + end do end program test_hash From 67584b1d5ebf6dc7828c3364014d06b9a815b5fb Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Mon, 21 Sep 2020 16:09:21 -0600 Subject: [PATCH 03/16] Hash table passing decent set of tests --- src/hash/hist_hash_table.F90 | 145 +++++++++++++++++++++++++---------- test/test_hash.F90 | 104 ++++++++++++++++++------- 2 files changed, 179 insertions(+), 70 deletions(-) diff --git a/src/hash/hist_hash_table.F90 b/src/hash/hist_hash_table.F90 index 4e6fac3..26fb812 100644 --- a/src/hash/hist_hash_table.F90 +++ b/src/hash/hist_hash_table.F90 @@ -91,6 +91,10 @@ module hist_hash_table ! logic in the routine that optimizes character strings of length 8. ! + ! Public interfaces + public :: new_hashable_char + public :: new_hashable_int + type, abstract, public :: hist_hashable_t ! The hashable type is a base type that contains a hash key. contains @@ -103,8 +107,6 @@ module hist_hash_table procedure :: key => hist_hashable_char_get_key end type hist_hashable_char_t - public :: new_hashable_char - type, public, extends(hist_hashable_t) :: hist_hashable_int_t integer, private :: value contains @@ -112,8 +114,6 @@ module hist_hash_table procedure :: val => hist_hashable_int_get_val end type hist_hashable_int_t - public :: new_hashable_int - integer, parameter :: gen_hash_key_offset = 21467 ! z'000053db' integer, parameter :: tbl_max_idx = 15 @@ -149,7 +149,7 @@ module hist_hash_table procedure :: table_value => hash_table_table_value end type hist_hash_table_t - ! Abstract interface for key procedure of hist_hashable class + ! Abstract interface for key procedure of hist_hashable_t class abstract interface function hist_hashable_get_key(hashable) import :: hist_hashable_t @@ -158,16 +158,47 @@ function hist_hashable_get_key(hashable) end function hist_hashable_get_key end interface + !! Private interfaces + private :: have_error ! Has a called routine detected an error? + private :: clear_optstring ! Clear a string, if present + CONTAINS !####################################################################### - function new_hashable_char(name_in) - type(hist_hashable_char_t) :: new_hashable_char - character(len=*), intent(in) :: name_in + logical function have_error(errmsg) + ! Return .true. iff is present and contains text + character(len=*), optional, intent(in) :: errmsg + + have_error = present(errmsg) + if (have_error) then + have_error = len_trim(errmsg) > 0 + end if + end function have_error + + !####################################################################### + + subroutine clear_optstring(str) + ! clear if it is present + character(len=*), optional, intent(inout) :: str + + if (present(str)) then + str = '' + end if + end subroutine clear_optstring + + !####################################################################### + + subroutine new_hashable_char(name_in, new_obj) + character(len=*), intent(in) :: name_in + type(hist_hashable_char_t), pointer :: new_obj - new_hashable_char%name = name_in - end function new_hashable_char + if (associated(new_obj)) then + deallocate(new_obj) + end if + allocate(new_obj) + new_obj%name = name_in + end subroutine new_hashable_char !####################################################################### @@ -181,12 +212,16 @@ end function hist_hashable_char_get_key !####################################################################### - function new_hashable_int(value_in) - type(hist_hashable_int_t) :: new_hashable_int - integer, intent(in) :: value_in + subroutine new_hashable_int(val_in, new_obj) + integer, intent(in) :: val_in + type(hist_hashable_int_t), pointer :: new_obj - new_hashable_int%value = value_in - end function new_hashable_int + if (associated(new_obj)) then + deallocate(new_obj) + end if + allocate(new_obj) + new_obj%value = val_in + end subroutine new_hashable_int !####################################################################### @@ -231,14 +266,13 @@ subroutine hash_table_initialize_table(this, tbl_size, key_off) class(hist_hash_table_t) :: this integer, intent(in) :: tbl_size ! new table size integer, optional, intent(in) :: key_off ! key offset - ! Local variable - integer :: index ! Clear this table so it can be initialized if (allocated(this%primary_table)) then deallocate(this%primary_table) end if - this%table_size = tbl_size + ! Avoid too-large tables + this%table_size = ishft(1, MIN(tbl_size, bit_size(1) - 2)) allocate(this%primary_table(this%table_size)) if (present(key_off)) then this%key_offset = key_off @@ -247,7 +281,7 @@ end subroutine hash_table_initialize_table !####################################################################### - integer function hash_table_key_hash(this, string) result(hash_key) + integer function hash_table_key_hash(this, string, errmsg) result(hash_key) ! !----------------------------------------------------------------------- ! @@ -261,8 +295,10 @@ integer function hash_table_key_hash(this, string) result(hash_key) ! ! Arguments: ! - class(hist_hash_table_t) :: this - character(len=*), intent(in) :: string + class(hist_hash_table_t) :: this + character(len=*), intent(in) :: string + character(len=*), optional, intent(out) :: errmsg + character(len=*), parameter :: subname = 'HASH_TABLE_KEY_HASH' ! ! Local. ! @@ -282,7 +318,17 @@ integer function hash_table_key_hash(this, string) result(hash_key) hash = ieor(hash, (ichar(string(index:index)) * hash_fact)) end do - hash_key = iand(hash, this%table_size - 1) + hash_key = iand(hash, this%table_size - 1) + 1 + if ((hash_key < 1) .or. (hash_key > this%table_size)) then + if (present(errmsg)) then + write(errmsg, '(2a,2(i0,a))') subname, ' ERROR: Key Hash, ', & + hash_key, ' out of bounds, [1, ', this%table_size, ']' + else + write(6, '(2a,2(i0,a))') subname, ' ERROR: Key Hash, ', & + hash_key, ' out of bounds, [1, ', this%table_size, ']' + STOP 1 + end if + end if end function hash_table_key_hash @@ -311,28 +357,38 @@ function hash_table_table_value(this, key, errmsg) result(tbl_val) type(table_entry_t), pointer :: next_ptr character(len=*), parameter :: subname = 'HASH_TABLE_TABLE_INDEX' - errmsg = '' + call clear_optstring(errmsg) nullify(tbl_val) - hash_key = this%key_hash(key) - if (this%primary_table(hash_key)%entry_value%key() == trim(key)) then - tbl_val => this%primary_table(hash_key)%entry_value - else - next_ptr => this%primary_table(hash_key)%next - do - if (associated(next_ptr)) then - if (next_ptr%entry_value%key() == trim(key)) then - tbl_val => next_ptr%entry_value - exit - end if - next_ptr => next_ptr%next + hash_key = this%key_hash(key, errmsg=errmsg) + ASSOCIATE(tbl_entry => this%primary_table(hash_key)) + if (have_error(errmsg)) then + errmsg = trim(errmsg)//', called from '//subname + else if (associated(tbl_entry%entry_value)) then + if (tbl_entry%entry_value%key() == trim(key)) then + tbl_val => tbl_entry%entry_value else - exit + next_ptr => tbl_entry%next + do + if (associated(next_ptr)) then + if (associated(next_ptr%entry_value)) then + if (next_ptr%entry_value%key() == trim(key)) then + tbl_val => next_ptr%entry_value + exit + end if + end if + next_ptr => next_ptr%next + else + exit + end if + end do end if - end do - end if + end if + END ASSOCIATE if ((.not. associated(tbl_val)) .and. present(errmsg)) then - write(errmsg, *) subname, ": No entry for '", trim(key), "'" + if (.not. have_error(errmsg)) then ! Still need to test for empty + write(errmsg, *) subname, ": No entry for '", trim(key), "'" + end if end if end function hash_table_table_value @@ -352,7 +408,7 @@ subroutine hash_table_add_hash_key(this, newval, errmsg) ! Dummy arguments: class(hist_hash_table_t) :: this - class(hist_hashable_t), pointer :: newval + class(hist_hashable_t), target :: newval character(len=*), optional, intent(out) :: errmsg ! Local variables integer :: hash_ind @@ -362,10 +418,13 @@ subroutine hash_table_add_hash_key(this, newval, errmsg) type(table_entry_t), pointer :: new_entry character(len=*), parameter :: subname = 'HASH_TABLE_ADD_HASH_KEY' + call clear_optstring(errmsg) newkey = newval%key() - hash_ind = this%key_hash(newkey) + hash_ind = this%key_hash(newkey, errmsg=errmsg) ! Check for this entry - if (associated(this%table_value(newkey))) then + if (have_error(errmsg)) then + errmsg = trim(errmsg)//', called from '//subname + else if (associated(this%table_value(newkey))) then if (present(errmsg)) then write(errmsg, *) subname, " ERROR: key, '", newkey, & "' already in table" @@ -396,6 +455,8 @@ subroutine hash_table_add_hash_key(this, newval, errmsg) ovflw_len = 1 end if this%max_collision = MAX(this%max_collision, ovflw_len) + else + tbl_entry%entry_value => newval end if END ASSOCIATE end if diff --git a/test/test_hash.F90 b/test/test_hash.F90 index c178111..b7f5de6 100644 --- a/test/test_hash.F90 +++ b/test/test_hash.F90 @@ -1,45 +1,93 @@ module test_hash_utils - use hist_hash_table, only: hist_hashable_t, hist_hashable_char_t - use hist_hash_table, only: new_hashable_char + use hist_hash_table, only: hist_hashable_char_t implicit none private - public :: new_hash_char_array - -CONTAINS - - subroutine new_hash_char_array(keys, hash_arr) - character(len=*), intent(in) :: keys(:) - type(hist_hashable_char_t), allocatable, intent(out) :: hash_arr(:) - - integer :: index - - allocate(hash_arr(size(keys, 1))) - do index = 1, size(hash_arr, 1) - hash_arr(index) = new_hashable_char(keys(index)) - end do - end subroutine new_hash_char_array + type, public :: hash_object_t + type(hist_hashable_char_t), pointer :: item => NULL() + type(hash_object_t), pointer :: next => NULL() + end type hash_object_t end module test_hash_utils program test_hash - use hist_hash_table, only: hist_hashable_char_t, hist_hash_table_t - use test_hash_utils, only: new_hash_char_array + use hist_hash_table, only: hist_hashable_t, hist_hash_table_t + use hist_hash_table, only: new_hashable_char + use test_hash_utils, only: hash_object_t character(len=10) :: hash_names(4) = (/ 'foo ', 'bar ', & 'foobar ', 'big daddy ' /) - type(hist_hashable_char_t), allocatable :: hash_chars(:) - type(hist_hash_table_t) :: hash_table - integer :: index - character(len=1024) :: errors - character(len=128) :: errmsg + + type(hash_object_t), pointer :: hash_chars => NULL() + type(hash_object_t), pointer :: next_ptr => NULL() + class(hist_hashable_t), pointer :: test_ptr => NULL() + type(hist_hash_table_t) :: hash_table + integer :: index + integer :: errcnt = 0 + integer, parameter :: max_errs = 8 + integer, parameter :: err_size = 128 + character(len=err_size) :: errors(max_errs) errors = '' - call new_hash_char_array(hash_names, hash_chars) - call hash_table%initialize(10) - do index = 1, size(hash_chars, 1) - call hash_table%add_hash_key(hash_chars(index)) + call hash_table%initialize(4) + do index = 1, size(hash_names, 1) + if (associated(hash_chars)) then + allocate(next_ptr%next) + next_ptr => next_ptr%next + else + allocate(hash_chars) + next_ptr => hash_chars + end if + call new_hashable_char(hash_names(index), next_ptr%item) + call hash_table%add_hash_key(next_ptr%item, errmsg=errors(errcnt + 1)) + if (len_trim(errors(errcnt + 1)) > 0) then + errcnt = errcnt + 1 + end if + if (errcnt > max_errs) then + exit + end if end do + if (errcnt == 0) then + ! We have populated the table, let's do some tests + ! First, make sure we can find existing entries + do index = 1, size(hash_names, 1) + test_ptr => hash_table%table_value(hash_names(index), & + errmsg=errors(errcnt + 1)) + if (len_trim(errors(errcnt + 1)) > 0) then + errcnt = errcnt + 1 + end if + if (errcnt > max_errs) then + exit + end if + end do + ! Next, make sure we do not find a non-existent entry + test_ptr => hash_table%table_value(trim(hash_names(1))//'_oops', & + errmsg=errors(errcnt + 1)) + if (len_trim(errors(errcnt + 1)) == 0) then + errcnt = errcnt + 1 + write(errors(errcnt), *) "ERROR: Found an entry for '", & + trim(hash_names(1))//'_oops', "'" + end if + ! Finally, make sure we get an error if we try to add a duplicate key + call hash_table%add_hash_key(hash_chars%next%item, & + errmsg=errors(errcnt + 1)) + if (len_trim(errors(errcnt + 1)) == 0) then + errcnt = errcnt + 1 + write(errors(errcnt), *) "ERROR: Allowed duplicate entry for '", & + hash_chars%next%item%key(), "'" + end if + end if + + if (errcnt > 0) then + write(6, '(a,i0,a)') 'FAIL, ', errcnt, ' errors found' + do index = 1, errcnt + write(6, *) trim(errors(index)) + end do + STOP 1 + else + STOP 0 + end if + end program test_hash From f56a5dd1c15875c1f4fb92b772fd65712b5af5c6 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Mon, 21 Sep 2020 22:05:23 -0600 Subject: [PATCH 04/16] Start on field info DDT --- src/hist_field.F90 | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/hist_field.F90 diff --git a/src/hist_field.F90 b/src/hist_field.F90 new file mode 100644 index 0000000..714e6a2 --- /dev/null +++ b/src/hist_field.F90 @@ -0,0 +1,20 @@ +module hist_field + ! Module containing DDTs for history fields and associated routines + + implicit none + private + + type, public :: hist_field_info_t + ! Field metadata + character(len=:), private :: diagnostic_name = '' ! Name on history file + character(len=:), private :: standard_name = '' + character(len=:), private :: long_name = '' + character(len=:), private :: units = '' +! type kind rank? + ! dimensions? + type(hist_field_info_t), private :: next => NULL() + end type hist_field_info_t + +CONTAINS + +end module hist_field From d8294ebafde62686b1797a8242b740c7118d5098 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Tue, 22 Sep 2020 21:31:37 -0600 Subject: [PATCH 05/16] Some progress on buffer and field DDTs --- src/hash/hist_hash_table.F90 | 92 +-------------------------------- src/hash/hist_hashable.F90 | 98 ++++++++++++++++++++++++++++++++++++ src/hist_buffer.F90 | 67 ++++++++++++++++++++++++ test/Makefile | 6 ++- test/test_hash.F90 | 6 +-- 5 files changed, 175 insertions(+), 94 deletions(-) create mode 100644 src/hash/hist_hashable.F90 create mode 100644 src/hist_buffer.F90 diff --git a/src/hash/hist_hash_table.F90 b/src/hash/hist_hash_table.F90 index 26fb812..e7533df 100644 --- a/src/hash/hist_hash_table.F90 +++ b/src/hash/hist_hash_table.F90 @@ -1,6 +1,8 @@ !!XXgoldyXX: To do, statistics output module hist_hash_table + use hist_hashable, only: hist_hashable_t + implicit none private @@ -91,29 +93,6 @@ module hist_hash_table ! logic in the routine that optimizes character strings of length 8. ! - ! Public interfaces - public :: new_hashable_char - public :: new_hashable_int - - type, abstract, public :: hist_hashable_t - ! The hashable type is a base type that contains a hash key. - contains - procedure(hist_hashable_get_key), deferred :: key - end type hist_hashable_t - - type, public, extends(hist_hashable_t) :: hist_hashable_char_t - character(len=:), private, allocatable :: name - contains - procedure :: key => hist_hashable_char_get_key - end type hist_hashable_char_t - - type, public, extends(hist_hashable_t) :: hist_hashable_int_t - integer, private :: value - contains - procedure :: key => hist_hashable_int_get_key - procedure :: val => hist_hashable_int_get_val - end type hist_hashable_int_t - integer, parameter :: gen_hash_key_offset = 21467 ! z'000053db' integer, parameter :: tbl_max_idx = 15 @@ -149,15 +128,6 @@ module hist_hash_table procedure :: table_value => hash_table_table_value end type hist_hash_table_t - ! Abstract interface for key procedure of hist_hashable_t class - abstract interface - function hist_hashable_get_key(hashable) - import :: hist_hashable_t - class(hist_hashable_t), intent(in) :: hashable - character(len=:), allocatable :: hist_hashable_get_key - end function hist_hashable_get_key - end interface - !! Private interfaces private :: have_error ! Has a called routine detected an error? private :: clear_optstring ! Clear a string, if present @@ -189,64 +159,6 @@ end subroutine clear_optstring !####################################################################### - subroutine new_hashable_char(name_in, new_obj) - character(len=*), intent(in) :: name_in - type(hist_hashable_char_t), pointer :: new_obj - - if (associated(new_obj)) then - deallocate(new_obj) - end if - allocate(new_obj) - new_obj%name = name_in - end subroutine new_hashable_char - - !####################################################################### - - function hist_hashable_char_get_key(hashable) - ! Return the hashable char class key (name) - class(hist_hashable_char_t), intent(in) :: hashable - character(len=:), allocatable :: hist_hashable_char_get_key - - hist_hashable_char_get_key = hashable%name - end function hist_hashable_char_get_key - - !####################################################################### - - subroutine new_hashable_int(val_in, new_obj) - integer, intent(in) :: val_in - type(hist_hashable_int_t), pointer :: new_obj - - if (associated(new_obj)) then - deallocate(new_obj) - end if - allocate(new_obj) - new_obj%value = val_in - end subroutine new_hashable_int - - !####################################################################### - - function hist_hashable_int_get_key(hashable) - ! Return the hashable int class key (value ==> string) - class(hist_hashable_int_t), intent(in) :: hashable - character(len=:), allocatable :: hist_hashable_int_get_key - - character(len=32) :: key_str - - write(key_str, '(i0)') hashable%val() - hist_hashable_int_get_key = trim(key_str) - end function hist_hashable_int_get_key - - !####################################################################### - - integer function hist_hashable_int_get_val(hashable) - ! Return the hashable int class value - class(hist_hashable_int_t), intent(in) :: hashable - - hist_hashable_int_get_val = hashable%value - end function hist_hashable_int_get_val - - !####################################################################### - subroutine finalize_table_entry(te) type(table_entry_t) :: te diff --git a/src/hash/hist_hashable.F90 b/src/hash/hist_hashable.F90 new file mode 100644 index 0000000..5ec3636 --- /dev/null +++ b/src/hash/hist_hashable.F90 @@ -0,0 +1,98 @@ +module hist_hashable + + implicit none + private + + ! Public interfaces + public :: new_hashable_char + public :: new_hashable_int + + type, abstract, public :: hist_hashable_t + ! The hashable type is a base type that contains a hash key. + contains + procedure(hist_hashable_get_key), deferred :: key + end type hist_hashable_t + + type, public, extends(hist_hashable_t) :: hist_hashable_char_t + character(len=:), private, allocatable :: name + contains + procedure :: key => hist_hashable_char_get_key + end type hist_hashable_char_t + + type, public, extends(hist_hashable_t) :: hist_hashable_int_t + integer, private :: value + contains + procedure :: key => hist_hashable_int_get_key + procedure :: val => hist_hashable_int_get_val + end type hist_hashable_int_t + + ! Abstract interface for key procedure of hist_hashable_t class + abstract interface + function hist_hashable_get_key(hashable) + import :: hist_hashable_t + class(hist_hashable_t), intent(in) :: hashable + character(len=:), allocatable :: hist_hashable_get_key + end function hist_hashable_get_key + end interface + +CONTAINS + + !####################################################################### + + subroutine new_hashable_char(name_in, new_obj) + character(len=*), intent(in) :: name_in + type(hist_hashable_char_t), pointer :: new_obj + + if (associated(new_obj)) then + deallocate(new_obj) + end if + allocate(new_obj) + new_obj%name = name_in + end subroutine new_hashable_char + + !####################################################################### + + function hist_hashable_char_get_key(hashable) + ! Return the hashable char class key (name) + class(hist_hashable_char_t), intent(in) :: hashable + character(len=:), allocatable :: hist_hashable_char_get_key + + hist_hashable_char_get_key = hashable%name + end function hist_hashable_char_get_key + + !####################################################################### + + subroutine new_hashable_int(val_in, new_obj) + integer, intent(in) :: val_in + type(hist_hashable_int_t), pointer :: new_obj + + if (associated(new_obj)) then + deallocate(new_obj) + end if + allocate(new_obj) + new_obj%value = val_in + end subroutine new_hashable_int + + !####################################################################### + + function hist_hashable_int_get_key(hashable) + ! Return the hashable int class key (value ==> string) + class(hist_hashable_int_t), intent(in) :: hashable + character(len=:), allocatable :: hist_hashable_int_get_key + + character(len=32) :: key_str + + write(key_str, '(i0)') hashable%val() + hist_hashable_int_get_key = trim(key_str) + end function hist_hashable_int_get_key + + !####################################################################### + + integer function hist_hashable_int_get_val(hashable) + ! Return the hashable int class value + class(hist_hashable_int_t), intent(in) :: hashable + + hist_hashable_int_get_val = hashable%value + end function hist_hashable_int_get_val + +end module hist_hashable diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 new file mode 100644 index 0000000..50900c3 --- /dev/null +++ b/src/hist_buffer.F90 @@ -0,0 +1,67 @@ +module hist_buffer + + use hist_hashable, only: hist_hashable_t + + implicit none + private + + ! Processing flag indices + integer, parameter :: hist_proc_last = 1 ! Save last sample + integer, parameter :: hist_proc_average = 2 ! Average samples + integer, parameter :: hist_proc_stddev = 3 ! Standard deviation of samples + integer, parameter :: hist_proc_min = 4 ! Minimum of samples + integer, parameter :: hist_proc_max = 5 ! Maximum of samples + ! Time sampling flag indices + !!XXgoldyXX: Todo: decide on sampling types + + type, abstract, public :: hist_buffer_t + ! hist_buffer_t is an abstract base class for hist_outfld buffers + class(hist_hashable_t), pointer, private :: field_info => NULL() + integer, private :: vol = -1 ! For host output + class(hist_buffer_t), pointer :: next + contains + procedure :: field => get_field_info + procedure :: volume => get_volume + procedure :: clear => clear_buffer + end type hist_buffer_t + +! Accumulation count? +! Accumulation method +! Normalized value method + + type, public, extends(hist_buffer_t) :: hist_buffer_1dreal_inst_t(bkind) + kind :: bkind + real(bkind), pointer :: data(:) + end type hist_buffer_1dreal_inst_t + + type, public, extends(hist_buffer_t) :: hist_buffer_1dreal_avg_t(bkind) + kind :: bkind + real(bkind), pointer :: avg(:) ! Running average + end type hist_buffer_1dreal_avg_t + + type, public, extends(hist_buffer_t) :: hist_buffer_1dreal_var_t(bkind) + kind :: bkind + real(bkind), pointer :: avg(:) ! Running average + real(bkind), pointer :: var(:) ! Running 'variance^2' + end type hist_buffer_1dreal_var_t + +CONTAINS + + function get_field_info(this) + class(hist_buffer_t), intent(in) :: this + class(hist_hashable_t), pointer :: get_field_info + + get_field_info => this%field_info + end function get_field_info + + !####################################################################### + + integer function get_volume(this) + class(hist_buffer_t), intent(in) :: this + + get_volume = this%vol + end function get_volume + + !####################################################################### + +end module hist_buffer diff --git a/test/Makefile b/test/Makefile index 99754f5..78cd088 100644 --- a/test/Makefile +++ b/test/Makefile @@ -18,7 +18,11 @@ hist_hash_table.o: $(HASHPATH)/hist_hash_table.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) -test_hash_table: test_hash.F90 hist_hash_table.o +hist_hashable.o: $(HASHPATH)/hist_hashable.F90 + @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) + @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) + +test_hash_table: test_hash.F90 hist_hashable.o hist_hash_table.o @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) diff --git a/test/test_hash.F90 b/test/test_hash.F90 index b7f5de6..4c812cb 100644 --- a/test/test_hash.F90 +++ b/test/test_hash.F90 @@ -1,5 +1,5 @@ module test_hash_utils - use hist_hash_table, only: hist_hashable_char_t + use hist_hashable, only: hist_hashable_char_t implicit none private @@ -12,8 +12,8 @@ module test_hash_utils end module test_hash_utils program test_hash - use hist_hash_table, only: hist_hashable_t, hist_hash_table_t - use hist_hash_table, only: new_hashable_char + use hist_hashable, only: hist_hashable_t, new_hashable_char + use hist_hash_table, only: hist_hash_table_t use test_hash_utils, only: hash_object_t character(len=10) :: hash_names(4) = (/ 'foo ', 'bar ', & From 38c51793dd2b0b5d5fe9bd0e050948474c6e4eb6 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Wed, 23 Sep 2020 21:42:37 -0600 Subject: [PATCH 06/16] Possible working field and buffer structure --- src/hist_buffer.F90 | 154 +++++++++++++++++++++++++++++++++----- src/hist_field.F90 | 99 ++++++++++++++++++++++-- test/Makefile | 14 +++- test/test_hist_buffer.F90 | 41 ++++++++++ 4 files changed, 282 insertions(+), 26 deletions(-) create mode 100644 test/test_hist_buffer.F90 diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 index 50900c3..19c2786 100644 --- a/src/hist_buffer.F90 +++ b/src/hist_buffer.F90 @@ -1,6 +1,7 @@ module hist_buffer - use hist_hashable, only: hist_hashable_t + use ISO_FORTRAN_ENV, only: REAL64, REAL32 + use hist_hashable, only: hist_hashable_t implicit none private @@ -16,34 +17,95 @@ module hist_buffer type, abstract, public :: hist_buffer_t ! hist_buffer_t is an abstract base class for hist_outfld buffers - class(hist_hashable_t), pointer, private :: field_info => NULL() + class(hist_hashable_t), pointer :: field_info => NULL() integer, private :: vol = -1 ! For host output class(hist_buffer_t), pointer :: next contains - procedure :: field => get_field_info - procedure :: volume => get_volume - procedure :: clear => clear_buffer + procedure :: field => get_field_info + procedure :: volume => get_volume + procedure(hist_buff_clear), deferred :: clear end type hist_buffer_t -! Accumulation count? -! Accumulation method -! Normalized value method - - type, public, extends(hist_buffer_t) :: hist_buffer_1dreal_inst_t(bkind) - kind :: bkind - real(bkind), pointer :: data(:) - end type hist_buffer_1dreal_inst_t - + type, public, extends(hist_buffer_t) :: hist_buffer_1dreal32_inst_t + integer :: num_samples = 0 + real(REAL32), pointer :: data(:) + CONTAINS + procedure :: clear => buff_1dreal32_inst_clear + procedure :: accumulate => buff_1dreal32_inst_accum + procedure :: norm_value => buff_1dreal32_inst_value + end type hist_buffer_1dreal32_inst_t + + type, public, extends(hist_buffer_t) :: hist_buffer_1dreal64_inst_t + integer :: num_samples = 0 + real(REAL64), pointer :: data(:) + CONTAINS + procedure :: clear => buff_1dreal64_inst_clear + procedure :: accumulate => buff_1dreal64_inst_accum + procedure :: norm_value => buff_1dreal64_inst_value + end type hist_buffer_1dreal64_inst_t + +!!XXgoldyXX: v debug only +#if 0 +!!XXgoldyXX: ^ debug only type, public, extends(hist_buffer_t) :: hist_buffer_1dreal_avg_t(bkind) - kind :: bkind - real(bkind), pointer :: avg(:) ! Running average + integer, kind :: bkind + integer :: num_samples = 0 + real(bkind), pointer :: avg(:) => NULL() ! Running average + CONTAINS + procedure(hist_buff_clear) :: clear => buff_1dreal_avg_clear + procedure(hist_buff_accum) :: accumulate => buff_1dreal_avg_accum + procedure(hist_buff_value) :: norm_value => buff_1dreal_avg_value end type hist_buffer_1dreal_avg_t type, public, extends(hist_buffer_t) :: hist_buffer_1dreal_var_t(bkind) - kind :: bkind - real(bkind), pointer :: avg(:) ! Running average - real(bkind), pointer :: var(:) ! Running 'variance^2' + integer, kind :: bkind + integer :: num_samples = 0 + real(bkind), pointer :: avg(:) => NULL() ! Running average + real(bkind), pointer :: var(:) => NULL() ! Running 'variance^2' + CONTAINS + procedure(hist_buff_clear) :: clear => buff_1dreal_var_clear + procedure(hist_buff_accum) :: accumulate => buff_1dreal_var_accum + procedure(hist_buff_value) :: norm_value => buff_1dreal_var_value end type hist_buffer_1dreal_var_t +!!XXgoldyXX: v debug only +#endif +!!XXgoldyXX: ^ debug only + + ! Abstract interface for clear procedure of hist_buffer_t class + abstract interface + subroutine hist_buff_clear(this) + import :: hist_buffer_t + class(hist_buffer_t), intent(inout) :: this + end subroutine hist_buff_clear + end interface + +!!XXgoldyXX: v debug only +#if 0 +!!XXgoldyXX: ^ debug only + ! Abstract interface for accumulate procedure of hist_buffer_t class + abstract interface + logical function hist_buff_accum_1dreal(this, field, errmsg) + import :: hist_buffer_1dreal_t + + class(hist_buffer_1dreal_t(bkind)), intent(inout) :: this + real(bkind), intent(in) :: field(:) + character(len=*), optional, intent(out) :: errmsg + end function hist_buff_accum_1dreal + end interface + + ! Abstract interface for norm_value procedure of hist_buffer_t class + abstract interface + subroutine hist_buff_value_1dreal(buffer, norm_val, errmsg) + import :: hist_buffer_1dreal_t + + class(hist_buffer_1dreal_t(bkind)), intent(inout) :: buffer + real(bkind), intent(inout) :: field(:) + character(len=*), optional, intent(out) :: errmsg + end subroutine hist_buff_value + end interface +!!XXgoldyXX: v debug only +#endif +!!XXgoldyXX: ^ debug only CONTAINS @@ -64,4 +126,58 @@ end function get_volume !####################################################################### + subroutine buff_1dreal32_inst_clear(this) + class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + + this%num_samples = 0 + end subroutine buff_1dreal32_inst_clear + + !####################################################################### + + subroutine buff_1dreal64_inst_clear(this) + class(hist_buffer_1dreal64_inst_t), intent(inout) :: this + + this%num_samples = 0 + end subroutine buff_1dreal64_inst_clear + + !####################################################################### + + logical function buff_1dreal32_inst_accum(this, errmsg) result(accum_ok) + class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + character(len=*), optional, intent(out) :: errmsg + + errmsg = 'Not implemented' + accum_ok = .false. + end function buff_1dreal32_inst_accum + + !####################################################################### + + logical function buff_1dreal64_inst_accum(this, errmsg) result(accum_ok) + class(hist_buffer_1dreal64_inst_t), intent(inout) :: this + character(len=*), optional, intent(out) :: errmsg + + errmsg = 'Not implemented' + accum_ok = .false. + end function buff_1dreal64_inst_accum + + !####################################################################### + + subroutine buff_1dreal32_inst_value(this, norm_val, errmsg) + class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + real(REAL32), intent(inout) :: norm_val(:) + character(len=*), optional, intent(out) :: errmsg + + errmsg = 'Not implemented' + end subroutine buff_1dreal32_inst_value + + !####################################################################### + + subroutine buff_1dreal64_inst_value(this, norm_val, errmsg) + class(hist_buffer_1dreal64_inst_t), intent(inout) :: this + real(REAL64), intent(inout) :: norm_val(:) + character(len=*), optional, intent(out) :: errmsg + + errmsg = 'Not implemented' + end subroutine buff_1dreal64_inst_value + end module hist_buffer diff --git a/src/hist_field.F90 b/src/hist_field.F90 index 714e6a2..e6ef117 100644 --- a/src/hist_field.F90 +++ b/src/hist_field.F90 @@ -1,20 +1,107 @@ module hist_field ! Module containing DDTs for history fields and associated routines + use hist_hashable, only: hist_hashable_t + use hist_buffer, only: hist_buffer_t + implicit none private - type, public :: hist_field_info_t + public :: hist_new_field ! Allocate a hist_field_info_t object + public :: hist_get_field ! Get field from a buffer + + type, public, extends(hist_hashable_t) :: hist_field_info_t ! Field metadata - character(len=:), private :: diagnostic_name = '' ! Name on history file - character(len=:), private :: standard_name = '' - character(len=:), private :: long_name = '' - character(len=:), private :: units = '' + character(len=:), allocatable, private :: diag_name ! History file name + character(len=:), allocatable, private :: standard_name + character(len=:), allocatable, private :: long_name + character(len=:), allocatable, private :: units ! type kind rank? ! dimensions? - type(hist_field_info_t), private :: next => NULL() + type(hist_field_info_t), pointer :: next => NULL() + class(hist_buffer_t), pointer :: buffers => NULL() + contains + procedure :: key => hist_field_info_get_key + final :: finalize_field end type hist_field_info_t CONTAINS + !####################################################################### + + function hist_field_info_get_key(hashable) + ! Return the hashable field info class key (diag_name) + class(hist_field_info_t), intent(in) :: hashable + character(len=:), allocatable :: hist_field_info_get_key + + hist_field_info_get_key = hashable%diag_name + end function hist_field_info_get_key + + !####################################################################### + + subroutine hist_new_field(new_field, diag_name_in, std_name_in, & + long_name_in, units_in) + type(hist_field_info_t), pointer :: new_field + character(len=*), intent(in) :: diag_name_in + character(len=*), intent(in) :: std_name_in + character(len=*), intent(in) :: long_name_in + character(len=*), intent(in) :: units_in + + if (associated(new_field)) then + deallocate(new_field) + end if + allocate(new_field) + new_field%diag_name = diag_name_in + new_field%standard_name = std_name_in + new_field%long_name = long_name_in + new_field%units = units_in + end subroutine hist_new_field + + !####################################################################### + + subroutine hist_get_field(buffer, field) + ! Retrieve + class(hist_buffer_t), intent(in) :: buffer + type(hist_field_info_t), pointer :: field + + select type(finfo => buffer%field_info) + type is (hist_field_info_t) + field => finfo + end select + end subroutine hist_get_field + + !####################################################################### + + subroutine finalize_field(this) + ! Dummy Argument + type(hist_field_info_t) :: this + ! Local Variables + class(hist_buffer_t), pointer :: next_buf + + if (allocated(this%diag_name)) then + deallocate(this%diag_name) + end if + if (allocated(this%standard_name)) then + deallocate(this%standard_name) + end if + if (allocated(this%long_name)) then + deallocate(this%long_name) + end if + if (allocated(this%units)) then + deallocate(this%units) + end if + ! We are not in charge of the field chain so just nullify + nullify(this%next) + ! We are in charge of the buffers so get rid of them. + do + next_buf => this%buffers + if (associated(next_buf)) then + this%buffers => next_buf%next + deallocate(next_buf) + else + exit + end if + end do + end subroutine finalize_field + end module hist_field diff --git a/test/Makefile b/test/Makefile index 78cd088..9f10855 100644 --- a/test/Makefile +++ b/test/Makefile @@ -22,11 +22,23 @@ hist_hashable.o: $(HASHPATH)/hist_hashable.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) +hist_field.o: $(SRCPATH)/hist_field.F90 hist_buffer.o hist_hashable.o + @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) + @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) + +hist_buffer.o: $(SRCPATH)/hist_buffer.F90 hist_hashable.o + @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) + @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) + test_hash_table: test_hash.F90 hist_hashable.o hist_hash_table.o @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) +test_hist_buffer: test_hist_buffer.F90 hist_hashable.o hist_buffer.o hist_field.o + @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) + @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) + # CLEAN clean: @rm -f *.o *.mod - @rm -f test_hash_table + @rm -f test_hash_table, test_hist_buffer diff --git a/test/test_hist_buffer.F90 b/test/test_hist_buffer.F90 new file mode 100644 index 0000000..f20fdcb --- /dev/null +++ b/test/test_hist_buffer.F90 @@ -0,0 +1,41 @@ +#if 0 +module test_hist_buffer_utils + use hist_field, only: hist_field_info_t + use hist_buffer, only: hist_buffer_t + + implicit none + private + + public :: get_field + +CONTAINS + +end module test_hist_buffer_utils +#endif + +program test_hist_buffer + use hist_field, only: hist_field_info_t + use hist_field, only: hist_new_field, hist_get_field + + type(hist_field_info_t), pointer :: my_fields => NULL() + integer :: index + integer :: errcnt = 0 + integer, parameter :: max_errs = 8 + integer, parameter :: err_size = 128 + character(len=err_size) :: errors(max_errs) + + errors = '' + call hist_new_field(my_fields, 'U', 'eastward_wind', 'Meridional Wind', & + 'm s-1') + + if (errcnt > 0) then + write(6, '(a,i0,a)') 'FAIL, ', errcnt, ' errors found' + do index = 1, errcnt + write(6, *) trim(errors(index)) + end do + STOP 1 + else + STOP 0 + end if + +end program test_hist_buffer From 51f13ea48e2e6bf37bac42fad6dbd548a48852ef Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Thu, 24 Sep 2020 21:45:57 -0600 Subject: [PATCH 07/16] Progress on API but have to deal with kind overlap --- src/hist_api.F90 | 208 ++++++++++++++++++++++++++++++++++++++++++++ src/hist_buffer.F90 | 148 +++++++++++++++---------------- src/hist_field.F90 | 34 ++++---- test/Makefile | 11 ++- 4 files changed, 307 insertions(+), 94 deletions(-) create mode 100644 src/hist_api.F90 diff --git a/src/hist_api.F90 b/src/hist_api.F90 new file mode 100644 index 0000000..2ff5acf --- /dev/null +++ b/src/hist_api.F90 @@ -0,0 +1,208 @@ +module hist_api + + use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 + use hist_hashable, only: hist_hashable_t + use hist_field, only: hist_field_info_t + use hist_buffer, only: hist_buffer_t, buffer_factory + use hist_buffer, only: hist_buffer_1dreal32_inst_t + use hist_buffer, only: hist_buffer_1dreal64_inst_t + + implicit none + private + + ! Public API interfaces + public :: hist_new_field ! Allocate a hist_field_info_t object + public :: hist_new_buffer ! Create a new field buffer + public :: hist_buffer_accumulate ! Accumulate a new field state +! public :: hist_buffer_norm_value ! Return current normalized field state +! public :: hist_buffer_clear ! Clear buffer accumulation state +! public :: hist_buffer_accum_type ! String value of accumulation type + + ! Interfaces for public interfaces + interface hist_buffer_accumulate + module procedure hist_buffer_accumulate_1dreal32 + module procedure hist_buffer_accumulate_1dreal64 + end interface hist_buffer_accumulate + +! interface hist_buffer_norm_value +! module procedure hist_buffer_norm_value_1dreal32 +! module procedure hist_buffer_norm_value_1dreal64 +! end interface hist_buffer_norm_value + +CONTAINS + + !####################################################################### + + logical function have_error(errmsg) + ! Return .true. iff is present and contains text + character(len=*), optional, intent(in) :: errmsg + + have_error = present(errmsg) + if (have_error) then + have_error = len_trim(errmsg) > 0 + end if + end function have_error + + !####################################################################### + + subroutine hist_new_field(new_field, diag_name_in, std_name_in, & + long_name_in, units_in, errmsg) + type(hist_field_info_t), pointer :: new_field + character(len=*), intent(in) :: diag_name_in + character(len=*), intent(in) :: std_name_in + character(len=*), intent(in) :: long_name_in + character(len=*), intent(in) :: units_in + character(len=*), optional, intent(out) :: errmsg + + integer :: astat + character(len=*), parameter :: subname = 'hist_new_field' + + errmsg = '' + if (associated(new_field)) then + deallocate(new_field, stat=astat) + if ((astat /= 0) .and. present(errmsg)) then + write(errmsg, *) subname, ' Unable to deallocate ' + end if + end if + if (.not. have_error(errmsg)) then + allocate(new_field, stat=astat) + if ((astat /= 0) .and. present(errmsg)) then + write(errmsg, *) subname, ' Unable to allocate ' + end if + end if + if (.not. have_error(errmsg)) then + call hist_field_initialize(new_field, diag_name_in, std_name_in, & + long_name_in, units_in, errmsg) + if (have_error(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), *) ', called from ', subname + end if + end if + end subroutine hist_new_field + + !####################################################################### + + subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & + accum_type, output_vol, buffer, errmsg, block_ind, block_sizes) + ! Dummy arguments + class(hist_hashable_t), pointer :: field + integer, intent(in) :: buff_shape(:) + integer, intent(in) :: buff_kind + integer, intent(in) :: horiz_axis_ind + character(len=*), intent(in) :: accum_type + integer, intent(in) :: output_vol + class(hist_buffer_t), pointer, intent(out) :: buffer + character(len=*), optional, intent(out) :: errmsg + integer, optional, intent(in) :: block_ind + integer, optional, intent(in) :: block_sizes(:) + + ! Local variables + integer :: rank + character(len=8) :: kind_string + character(len=3) :: accum_string + character(len=16) :: bufftype_string + integer, parameter :: max_rank = 2 + integer, parameter :: good_kinds(2) = (/ REAL32, REAL64 /) + character(len=*), parameter :: subname = 'hist_new_buffer' + character(len=*), parameter :: errhead = subname//' ERROR: ' + + ! Initialize output and local variables + nullify(buffer) + if (present(errmsg)) then + errmsg = '' + end if + rank = SIZE(buff_shape, 1) + !! Some sanity checks + ! Check kind + if (.not. ANY(good_kinds == buff_kind)) then + write(errmsg(len_trim(errmsg)+1:), '(2a,i0,a)') errhead, & + 'buff_type, ', buff_kind, ' not supported' + end if + select case (buff_kind) + case (REAL32) + kind_string = 'real32' + case (REAL64) + kind_string = 'real64' + case (INT32) + kind_string = 'int32' + case (INT64) + kind_string = 'int64' + case default + kind_string = '' + end select + ! Check horiz_axis_ind + if ((horiz_axis_ind < 1) .or. (horiz_axis_ind > rank)) then + write(errmsg(len_trim(errmsg)+1:), '(2a,i0,a)') errhead, & + 'horiz_axis_ind outside of valid range, [1, ', rank, ']' + end if + ! Check for (proper) block structured buffer + if (present(block_ind) .and. present(block_sizes)) then + if ((block_ind < 1) .or. (block_ind > rank)) then + write(errmsg(len_trim(errmsg)+1:), '(2a,i0,a)') errhead, & + 'block_ind outside of valid range, [1, ', rank, ']' + else if (block_ind == horiz_axis_ind) then + write(errmsg(len_trim(errmsg)+1:), '(2a)') errhead, & + 'block_ind cannot be the same as horiz_axis_ind' + end if + else if (present(block_ind)) then + if (present(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), '(2a)') errhead, & + 'block_sizes required if block_ind is present' + end if + else if (present(block_sizes)) then + if (present(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), '(2a)') errhead, & + 'block_ind required if block_sizes is present' + end if + end if ! No else, we just do not have a blocked buffer + ! Check accumulation type + select case(trim(accum_type)) + case ('I', 'i', 'lst') + accum_string = 'lst' + case ('A', 'a', 'avg') + accum_string = 'avg' + case ('M', 'm', 'min') + accum_string = 'min' + case ('X', 'x', 'max') + accum_string = 'max' + case ('S', 's', 'var') + accum_string = 'var' + case default + if (present(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), '(4a)') errhead, & + "Unknown accumulation operator type, '", trim(accum_type), "'" + end if + end select + ! First, sort by rank + select case (rank) + case (1) + ! sort by kind (already checked above) + if (buff_kind == REAL32) then + buffer => buffer_factory('real32_1_') + else if (buff_kind == REAL64) then + buffer => buffer_factory('real64_1_') + end if + case default + ! Over max rank currently handled + if (present(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), '(2a,i0)') errhead, & + 'buffers have a max rank of ', max_rank + end if + end select + + end subroutine hist_new_buffer + + !####################################################################### + + subroutine hist_buffer_accumulate_1dreal32(buffer, field) + class(hist_buffer_1dreal32_inst_t), intent(inout) :: buffer + real(REAL32), intent(in) :: field(:) + end subroutine hist_buffer_accumulate_1dreal32 + + !####################################################################### + + subroutine hist_buffer_accumulate_1dreal64(buffer, field) + class(hist_buffer_1dreal64_inst_t), intent(inout) :: buffer + real(REAL64), intent(in) :: field(:) + end subroutine hist_buffer_accumulate_1dreal64 + +end module hist_api diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 index 19c2786..0a41459 100644 --- a/src/hist_buffer.F90 +++ b/src/hist_buffer.F90 @@ -1,11 +1,13 @@ module hist_buffer - - use ISO_FORTRAN_ENV, only: REAL64, REAL32 + use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 use hist_hashable, only: hist_hashable_t implicit none private + ! Public interfaces + public :: buffer_factory + ! Processing flag indices integer, parameter :: hist_proc_last = 1 ! Save last sample integer, parameter :: hist_proc_average = 2 ! Average samples @@ -44,33 +46,6 @@ module hist_buffer procedure :: norm_value => buff_1dreal64_inst_value end type hist_buffer_1dreal64_inst_t -!!XXgoldyXX: v debug only -#if 0 -!!XXgoldyXX: ^ debug only - type, public, extends(hist_buffer_t) :: hist_buffer_1dreal_avg_t(bkind) - integer, kind :: bkind - integer :: num_samples = 0 - real(bkind), pointer :: avg(:) => NULL() ! Running average - CONTAINS - procedure(hist_buff_clear) :: clear => buff_1dreal_avg_clear - procedure(hist_buff_accum) :: accumulate => buff_1dreal_avg_accum - procedure(hist_buff_value) :: norm_value => buff_1dreal_avg_value - end type hist_buffer_1dreal_avg_t - - type, public, extends(hist_buffer_t) :: hist_buffer_1dreal_var_t(bkind) - integer, kind :: bkind - integer :: num_samples = 0 - real(bkind), pointer :: avg(:) => NULL() ! Running average - real(bkind), pointer :: var(:) => NULL() ! Running 'variance^2' - CONTAINS - procedure(hist_buff_clear) :: clear => buff_1dreal_var_clear - procedure(hist_buff_accum) :: accumulate => buff_1dreal_var_accum - procedure(hist_buff_value) :: norm_value => buff_1dreal_var_value - end type hist_buffer_1dreal_var_t -!!XXgoldyXX: v debug only -#endif -!!XXgoldyXX: ^ debug only - ! Abstract interface for clear procedure of hist_buffer_t class abstract interface subroutine hist_buff_clear(this) @@ -79,34 +54,6 @@ subroutine hist_buff_clear(this) end subroutine hist_buff_clear end interface -!!XXgoldyXX: v debug only -#if 0 -!!XXgoldyXX: ^ debug only - ! Abstract interface for accumulate procedure of hist_buffer_t class - abstract interface - logical function hist_buff_accum_1dreal(this, field, errmsg) - import :: hist_buffer_1dreal_t - - class(hist_buffer_1dreal_t(bkind)), intent(inout) :: this - real(bkind), intent(in) :: field(:) - character(len=*), optional, intent(out) :: errmsg - end function hist_buff_accum_1dreal - end interface - - ! Abstract interface for norm_value procedure of hist_buffer_t class - abstract interface - subroutine hist_buff_value_1dreal(buffer, norm_val, errmsg) - import :: hist_buffer_1dreal_t - - class(hist_buffer_1dreal_t(bkind)), intent(inout) :: buffer - real(bkind), intent(inout) :: field(:) - character(len=*), optional, intent(out) :: errmsg - end subroutine hist_buff_value - end interface -!!XXgoldyXX: v debug only -#endif -!!XXgoldyXX: ^ debug only - CONTAINS function get_field_info(this) @@ -134,41 +81,52 @@ end subroutine buff_1dreal32_inst_clear !####################################################################### - subroutine buff_1dreal64_inst_clear(this) - class(hist_buffer_1dreal64_inst_t), intent(inout) :: this + subroutine buff_1dreal32_inst_accum(this, field, errmsg) + class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + real(REAL32), intent(in) :: field(:) + character(len=*), optional, intent(out) :: errmsg - this%num_samples = 0 - end subroutine buff_1dreal64_inst_clear + if (present(errmsg)) then + errmsg = 'Not implemented' + end if + this%num_samples = 1 + + end subroutine buff_1dreal32_inst_accum !####################################################################### - logical function buff_1dreal32_inst_accum(this, errmsg) result(accum_ok) + subroutine buff_1dreal32_inst_value(this, norm_val, errmsg) class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + real(REAL32), intent(inout) :: norm_val(:) character(len=*), optional, intent(out) :: errmsg - errmsg = 'Not implemented' - accum_ok = .false. - end function buff_1dreal32_inst_accum + if (present(errmsg)) then + errmsg = 'Not implemented' + end if + + end subroutine buff_1dreal32_inst_value !####################################################################### - logical function buff_1dreal64_inst_accum(this, errmsg) result(accum_ok) + subroutine buff_1dreal64_inst_clear(this) class(hist_buffer_1dreal64_inst_t), intent(inout) :: this - character(len=*), optional, intent(out) :: errmsg - errmsg = 'Not implemented' - accum_ok = .false. - end function buff_1dreal64_inst_accum + this%num_samples = 0 + + end subroutine buff_1dreal64_inst_clear !####################################################################### - subroutine buff_1dreal32_inst_value(this, norm_val, errmsg) - class(hist_buffer_1dreal32_inst_t), intent(inout) :: this - real(REAL32), intent(inout) :: norm_val(:) + subroutine buff_1dreal64_inst_accum(this, field, errmsg) + class(hist_buffer_1dreal64_inst_t), intent(inout) :: this + real(REAL64), intent(in) :: field(:) character(len=*), optional, intent(out) :: errmsg - errmsg = 'Not implemented' - end subroutine buff_1dreal32_inst_value + if (present(errmsg)) then + errmsg = 'Not implemented' + end if + + end subroutine buff_1dreal64_inst_accum !####################################################################### @@ -177,7 +135,45 @@ subroutine buff_1dreal64_inst_value(this, norm_val, errmsg) real(REAL64), intent(inout) :: norm_val(:) character(len=*), optional, intent(out) :: errmsg - errmsg = 'Not implemented' + if (present(errmsg)) then + errmsg = 'Not implemented' + end if + end subroutine buff_1dreal64_inst_value + !####################################################################### + + function buffer_factory(buffer_type) result(newbuf) + ! Create a new buffer based on . + ! has a format typekind_rank_accum + ! Where: + ! is a lowercase string representation of a + ! supported kind from the ISO_FORTRAN_ENV module. + ! is the rank of the buffer (no leading zeros) + ! is the accumulation type, one of: + ! lst: Store the last value collected + ! avg: Accumulate running average + ! var: Accumulate standard deviation + ! min: Accumulate smallest value + ! max: Accumulate largest value + ! Arguments + class(hist_buffer_t), pointer :: newbuf + character(len=*), intent(in) :: buffer_type + ! Local variables + ! For buffer allocation + type(hist_buffer_1dreal32_inst_t), pointer :: real32_1_in => NULL() + type(hist_buffer_1dreal64_inst_t), pointer :: real64_1_in => NULL() + + + ! Create new buffer + select case (trim(buffer_type)) + case ('real32_1_inst') + allocate(real32_1_in) + newbuf => real32_1_in + case ('real64_1_inst') + allocate(real64_1_in) + newbuf => real64_1_in + end select + end function buffer_factory + end module hist_buffer diff --git a/src/hist_field.F90 b/src/hist_field.F90 index e6ef117..82a3b77 100644 --- a/src/hist_field.F90 +++ b/src/hist_field.F90 @@ -7,7 +7,7 @@ module hist_field implicit none private - public :: hist_new_field ! Allocate a hist_field_info_t object + public :: hist_field_initialize public :: hist_get_field ! Get field from a buffer type, public, extends(hist_hashable_t) :: hist_field_info_t @@ -39,23 +39,24 @@ end function hist_field_info_get_key !####################################################################### - subroutine hist_new_field(new_field, diag_name_in, std_name_in, & - long_name_in, units_in) - type(hist_field_info_t), pointer :: new_field - character(len=*), intent(in) :: diag_name_in - character(len=*), intent(in) :: std_name_in - character(len=*), intent(in) :: long_name_in - character(len=*), intent(in) :: units_in + subroutine hist_field_initialize(field, diag_name_in, std_name_in, & + long_name_in, units_in, errmsg) - if (associated(new_field)) then - deallocate(new_field) + type(hist_field_info_t), pointer :: field + character(len=*), intent(in) :: diag_name_in + character(len=*), intent(in) :: std_name_in + character(len=*), intent(in) :: long_name_in + character(len=*), intent(in) :: units_in + character(len=*), optional, intent(out) :: errmsg + + if (present(errmsg)) then + errmsg = '' end if - allocate(new_field) - new_field%diag_name = diag_name_in - new_field%standard_name = std_name_in - new_field%long_name = long_name_in - new_field%units = units_in - end subroutine hist_new_field + field%diag_name = diag_name_in + field%standard_name = std_name_in + field%long_name = long_name_in + field%units = units_in + end subroutine hist_field_initialize !####################################################################### @@ -64,6 +65,7 @@ subroutine hist_get_field(buffer, field) class(hist_buffer_t), intent(in) :: buffer type(hist_field_info_t), pointer :: field + nullify(field) select type(finfo => buffer%field_info) type is (hist_field_info_t) field => finfo diff --git a/test/Makefile b/test/Makefile index 9f10855..10a24cd 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,6 +7,9 @@ FCFLAGS += -g SRCPATH = ../src HASHPATH = $(SRCPATH)/hash +HASHOBJS = hist_hashable.o hist_hash_table.o +HISTOBJS = hist_buffer.o hist_field.o hist_api.o + # Make sure we have a log file ifeq ($(LOGFILE),) LOGFILE := hist_test.log @@ -22,11 +25,15 @@ hist_hashable.o: $(HASHPATH)/hist_hashable.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) +hist_buffer.o: $(SRCPATH)/hist_buffer.F90 hist_hashable.o + @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) + @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) + hist_field.o: $(SRCPATH)/hist_field.F90 hist_buffer.o hist_hashable.o @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) -hist_buffer.o: $(SRCPATH)/hist_buffer.F90 hist_hashable.o +hist_api.o: $(SRCPATH)/hist_api.F90 hist_field.o hist_buffer.o @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) @@ -34,7 +41,7 @@ test_hash_table: test_hash.F90 hist_hashable.o hist_hash_table.o @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) -test_hist_buffer: test_hist_buffer.F90 hist_hashable.o hist_buffer.o hist_field.o +test_hist_buffer: test_hist_buffer.F90 hist_hashable.o $(HISTOBJS) @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) From 24c6fe67f427bfd2a4183149e0f038d10f5d33c4 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Fri, 25 Sep 2020 16:20:38 -0600 Subject: [PATCH 08/16] Able to create 1dreal buffers --- src/hist_api.F90 | 94 ++++++++++++++++++++++++++----------- src/hist_buffer.F90 | 67 ++++++++++++++++++++++++-- src/hist_field.F90 | 98 ++++++++++++++++++++++++++++++--------- test/Makefile | 8 ++-- test/test_hist_buffer.F90 | 6 +-- 5 files changed, 216 insertions(+), 57 deletions(-) diff --git a/src/hist_api.F90 b/src/hist_api.F90 index 2ff5acf..fe990c9 100644 --- a/src/hist_api.F90 +++ b/src/hist_api.F90 @@ -46,12 +46,15 @@ end function have_error !####################################################################### subroutine hist_new_field(new_field, diag_name_in, std_name_in, & - long_name_in, units_in, errmsg) + long_name_in, units_in, type_in, errmsg) + use hist_field, only: hist_field_initialize + type(hist_field_info_t), pointer :: new_field character(len=*), intent(in) :: diag_name_in character(len=*), intent(in) :: std_name_in character(len=*), intent(in) :: long_name_in character(len=*), intent(in) :: units_in + character(len=*), intent(in) :: type_in character(len=*), optional, intent(out) :: errmsg integer :: astat @@ -72,7 +75,7 @@ subroutine hist_new_field(new_field, diag_name_in, std_name_in, & end if if (.not. have_error(errmsg)) then call hist_field_initialize(new_field, diag_name_in, std_name_in, & - long_name_in, units_in, errmsg) + long_name_in, units_in, type_in, errmsg) if (have_error(errmsg)) then write(errmsg(len_trim(errmsg)+1:), *) ', called from ', subname end if @@ -84,7 +87,7 @@ end subroutine hist_new_field subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & accum_type, output_vol, buffer, errmsg, block_ind, block_sizes) ! Dummy arguments - class(hist_hashable_t), pointer :: field + class(hist_hashable_t), pointer :: field_base integer, intent(in) :: buff_shape(:) integer, intent(in) :: buff_kind integer, intent(in) :: horiz_axis_ind @@ -96,39 +99,70 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & integer, optional, intent(in) :: block_sizes(:) ! Local variables - integer :: rank - character(len=8) :: kind_string - character(len=3) :: accum_string - character(len=16) :: bufftype_string - integer, parameter :: max_rank = 2 - integer, parameter :: good_kinds(2) = (/ REAL32, REAL64 /) - character(len=*), parameter :: subname = 'hist_new_buffer' - character(len=*), parameter :: errhead = subname//' ERROR: ' + integer :: rank + character(len=8) :: kind_string + character(len=3) :: accum_string + character(len=16) :: bufftype_string + type(hist_field_info_t), pointer :: field + character(len=:), allocatable :: type_str + integer, parameter :: max_rank = 2 + character(len=*), parameter :: subname = 'hist_new_buffer' + character(len=*), parameter :: errhead = subname//' ERROR: ' ! Initialize output and local variables nullify(buffer) + nullify(field) if (present(errmsg)) then errmsg = '' end if rank = SIZE(buff_shape, 1) !! Some sanity checks - ! Check kind - if (.not. ANY(good_kinds == buff_kind)) then - write(errmsg(len_trim(errmsg)+1:), '(2a,i0,a)') errhead, & - 'buff_type, ', buff_kind, ' not supported' + ! We can select on the field's type string but not its kind string + ! because we do not know the kind value for the kind string + select type (field_base) + type is (hist_field_info_t) + field => field_base + class default + if (present(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), '(2a)') errhead, & + 'Input, , is not of type, hist_field_info_t' + end if + end select + if (associated(field)) then + type_str = field%type() + else + type_str = 'unknown' end if - select case (buff_kind) - case (REAL32) - kind_string = 'real32' - case (REAL64) - kind_string = 'real64' - case (INT32) - kind_string = 'int32' - case (INT64) - kind_string = 'int64' + select case (type_str) + case ('integer') + select case (buff_kind) + case (INT32) + kind_string = 'int32' + case (INT64) + kind_string = 'int64' + case default + kind_string = '' + end select + case ('real') + select case(buff_kind) + case (REAL32) + kind_string = 'real32' + case (REAL64) + kind_string = 'real64' + case default + kind_string = '' + end select case default kind_string = '' + if (present(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), '(4a)') errhead, & + "type, '", type_str, ' is not supported' + end if end select + if ((len_trim(kind_string) == 0) .and. present(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), '(2a,i0,2a)') errhead, & + "kind = ", buff_kind, " is not supported for type ", type_str + end if ! Check horiz_axis_ind if ((horiz_axis_ind < 1) .or. (horiz_axis_ind > rank)) then write(errmsg(len_trim(errmsg)+1:), '(2a,i0,a)') errhead, & @@ -172,14 +206,15 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & "Unknown accumulation operator type, '", trim(accum_type), "'" end if end select + ! We now know what sort of buffer we need ! First, sort by rank select case (rank) case (1) ! sort by kind (already checked above) if (buff_kind == REAL32) then - buffer => buffer_factory('real32_1_') + bufftype_string = 'real32_1_'//trim(accum_string) else if (buff_kind == REAL64) then - buffer => buffer_factory('real64_1_') + bufftype_string = 'real64_1_'//trim(accum_string) end if case default ! Over max rank currently handled @@ -188,6 +223,13 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & 'buffers have a max rank of ', max_rank end if end select + buffer => buffer_factory(trim(bufftype_string)) + if (associated(buffer)) then + call buffer%initialize(output_vol, horiz_axis_ind, buff_shape) + else if (present(errmsg)) then + write(errmsg(len_trim(errmsg)+1:), '(4a)') errhead, & + 'buffer (', trim(bufftype_string), ') not created' + end if end subroutine hist_new_buffer diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 index 0a41459..3ccf420 100644 --- a/src/hist_buffer.F90 +++ b/src/hist_buffer.F90 @@ -21,32 +21,38 @@ module hist_buffer ! hist_buffer_t is an abstract base class for hist_outfld buffers class(hist_hashable_t), pointer :: field_info => NULL() integer, private :: vol = -1 ! For host output + integer, private :: horiz_axis_ind = 0 class(hist_buffer_t), pointer :: next contains procedure :: field => get_field_info procedure :: volume => get_volume + procedure :: horiz_axis_index + procedure :: init_buffer + procedure(hist_buff_init), deferred :: initialize procedure(hist_buff_clear), deferred :: clear end type hist_buffer_t type, public, extends(hist_buffer_t) :: hist_buffer_1dreal32_inst_t integer :: num_samples = 0 - real(REAL32), pointer :: data(:) + real(REAL32), pointer :: data(:) => NULL() CONTAINS procedure :: clear => buff_1dreal32_inst_clear procedure :: accumulate => buff_1dreal32_inst_accum procedure :: norm_value => buff_1dreal32_inst_value + procedure :: initialize => init_buff_1dreal32 end type hist_buffer_1dreal32_inst_t type, public, extends(hist_buffer_t) :: hist_buffer_1dreal64_inst_t integer :: num_samples = 0 - real(REAL64), pointer :: data(:) + real(REAL64), pointer :: data(:) => NULL() CONTAINS procedure :: clear => buff_1dreal64_inst_clear procedure :: accumulate => buff_1dreal64_inst_accum procedure :: norm_value => buff_1dreal64_inst_value + procedure :: initialize => init_buff_1dreal64 end type hist_buffer_1dreal64_inst_t - ! Abstract interface for clear procedure of hist_buffer_t class + ! Abstract interfaces for hist_buffer_t class abstract interface subroutine hist_buff_clear(this) import :: hist_buffer_t @@ -54,6 +60,16 @@ subroutine hist_buff_clear(this) end subroutine hist_buff_clear end interface + abstract interface + subroutine hist_buff_init(this, volume_in, horiz_axis_in, shape_in) + import :: hist_buffer_t + class(hist_buffer_t), intent(inout) :: this + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + integer, intent(in) :: shape_in(:) + end subroutine hist_buff_init + end interface + CONTAINS function get_field_info(this) @@ -73,6 +89,25 @@ end function get_volume !####################################################################### + integer function horiz_axis_index(this) + class(hist_buffer_t), intent(in) :: this + + horiz_axis_index = this%horiz_axis_ind + end function horiz_axis_index + + !####################################################################### + + subroutine init_buffer(this, volume_in, horiz_axis_in) + class(hist_buffer_t), intent(inout) :: this + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + + this%vol = volume_in + this%horiz_axis_ind = horiz_axis_in + end subroutine init_buffer + + !####################################################################### + subroutine buff_1dreal32_inst_clear(this) class(hist_buffer_1dreal32_inst_t), intent(inout) :: this @@ -81,6 +116,19 @@ end subroutine buff_1dreal32_inst_clear !####################################################################### + subroutine init_buff_1dreal32(this, volume_in, horiz_axis_in, shape_in) + class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + integer, intent(in) :: shape_in(:) + + call init_buffer(this, volume_in, horiz_axis_in) + allocate(this%data(shape_in(1))) + + end subroutine init_buff_1dreal32 + + !####################################################################### + subroutine buff_1dreal32_inst_accum(this, field, errmsg) class(hist_buffer_1dreal32_inst_t), intent(inout) :: this real(REAL32), intent(in) :: field(:) @@ -117,6 +165,19 @@ end subroutine buff_1dreal64_inst_clear !####################################################################### + subroutine init_buff_1dreal64(this, volume_in, horiz_axis_in, shape_in) + class(hist_buffer_1dreal64_inst_t), intent(inout) :: this + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + integer, intent(in) :: shape_in(:) + + call init_buffer(this, volume_in, horiz_axis_in) + allocate(this%data(shape_in(1))) + + end subroutine init_buff_1dreal64 + + !####################################################################### + subroutine buff_1dreal64_inst_accum(this, field, errmsg) class(hist_buffer_1dreal64_inst_t), intent(inout) :: this real(REAL64), intent(in) :: field(:) diff --git a/src/hist_field.F90 b/src/hist_field.F90 index 82a3b77..6f246ef 100644 --- a/src/hist_field.F90 +++ b/src/hist_field.F90 @@ -11,17 +11,23 @@ module hist_field public :: hist_get_field ! Get field from a buffer type, public, extends(hist_hashable_t) :: hist_field_info_t - ! Field metadata - character(len=:), allocatable, private :: diag_name ! History file name - character(len=:), allocatable, private :: standard_name - character(len=:), allocatable, private :: long_name - character(len=:), allocatable, private :: units + ! Field metadata (note: all strings should be lowercase) + character(len=:), allocatable, private :: diag_file_name + character(len=:), allocatable, private :: field_standard_name + character(len=:), allocatable, private :: field_long_name + character(len=:), allocatable, private :: field_units + character(len=:), allocatable, private :: field_type ! type kind rank? ! dimensions? type(hist_field_info_t), pointer :: next => NULL() class(hist_buffer_t), pointer :: buffers => NULL() contains - procedure :: key => hist_field_info_get_key + procedure :: key => hist_field_info_get_key + procedure :: diag_name => field_get_diag_name + procedure :: standard_name => field_get_standard_name + procedure :: long_name => field_get_long_name + procedure :: units => field_get_units + procedure :: type => field_get_type final :: finalize_field end type hist_field_info_t @@ -30,32 +36,34 @@ module hist_field !####################################################################### function hist_field_info_get_key(hashable) - ! Return the hashable field info class key (diag_name) + ! Return the hashable field info class key (diag_file_name) class(hist_field_info_t), intent(in) :: hashable character(len=:), allocatable :: hist_field_info_get_key - hist_field_info_get_key = hashable%diag_name + hist_field_info_get_key = hashable%diag_file_name end function hist_field_info_get_key !####################################################################### subroutine hist_field_initialize(field, diag_name_in, std_name_in, & - long_name_in, units_in, errmsg) + long_name_in, units_in, type_in, errmsg) type(hist_field_info_t), pointer :: field character(len=*), intent(in) :: diag_name_in character(len=*), intent(in) :: std_name_in character(len=*), intent(in) :: long_name_in character(len=*), intent(in) :: units_in + character(len=*), intent(in) :: type_in character(len=*), optional, intent(out) :: errmsg if (present(errmsg)) then errmsg = '' end if - field%diag_name = diag_name_in - field%standard_name = std_name_in - field%long_name = long_name_in - field%units = units_in + field%diag_file_name = diag_name_in + field%field_standard_name = std_name_in + field%field_long_name = long_name_in + field%field_units = units_in + field%field_type = type_in end subroutine hist_field_initialize !####################################################################### @@ -74,23 +82,71 @@ end subroutine hist_get_field !####################################################################### + function field_get_diag_name(this) result(info) + class(hist_field_info_t), intent(in) :: this + character(len=:), allocatable :: info + + info = this%diag_file_name + end function field_get_diag_name + + !####################################################################### + + function field_get_standard_name(this) result(info) + class(hist_field_info_t), intent(in) :: this + character(len=:), allocatable :: info + + info = this%field_standard_name + end function field_get_standard_name + + !####################################################################### + + function field_get_long_name(this) result(info) + class(hist_field_info_t), intent(in) :: this + character(len=:), allocatable :: info + + info = this%field_long_name + end function field_get_long_name + + !####################################################################### + + function field_get_units(this) result(info) + class(hist_field_info_t), intent(in) :: this + character(len=:), allocatable :: info + + info = this%field_units + end function field_get_units + + !####################################################################### + + function field_get_type(this) result(info) + class(hist_field_info_t), intent(in) :: this + character(len=:), allocatable :: info + + info = this%field_type + end function field_get_type + + !####################################################################### + subroutine finalize_field(this) ! Dummy Argument type(hist_field_info_t) :: this ! Local Variables class(hist_buffer_t), pointer :: next_buf - if (allocated(this%diag_name)) then - deallocate(this%diag_name) + if (allocated(this%diag_file_name)) then + deallocate(this%diag_file_name) + end if + if (allocated(this%field_standard_name)) then + deallocate(this%field_standard_name) end if - if (allocated(this%standard_name)) then - deallocate(this%standard_name) + if (allocated(this%field_long_name)) then + deallocate(this%field_long_name) end if - if (allocated(this%long_name)) then - deallocate(this%long_name) + if (allocated(this%field_units)) then + deallocate(this%field_units) end if - if (allocated(this%units)) then - deallocate(this%units) + if (allocated(this%field_type)) then + deallocate(this%field_type) end if ! We are not in charge of the field chain so just nullify nullify(this%next) diff --git a/test/Makefile b/test/Makefile index 10a24cd..1e6d601 100644 --- a/test/Makefile +++ b/test/Makefile @@ -17,11 +17,11 @@ endif # TARGETS -hist_hash_table.o: $(HASHPATH)/hist_hash_table.F90 +hist_hashable.o: $(HASHPATH)/hist_hashable.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) -hist_hashable.o: $(HASHPATH)/hist_hashable.F90 +hist_hash_table.o: $(HASHPATH)/hist_hash_table.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) @@ -37,7 +37,7 @@ hist_api.o: $(SRCPATH)/hist_api.F90 hist_field.o hist_buffer.o @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) -test_hash_table: test_hash.F90 hist_hashable.o hist_hash_table.o +test_hash_table: test_hash.F90 $(HASHOBJS) @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) @@ -48,4 +48,4 @@ test_hist_buffer: test_hist_buffer.F90 hist_hashable.o $(HISTOBJS) # CLEAN clean: @rm -f *.o *.mod - @rm -f test_hash_table, test_hist_buffer + @rm -f test_hash_table test_hist_buffer diff --git a/test/test_hist_buffer.F90 b/test/test_hist_buffer.F90 index f20fdcb..b26f138 100644 --- a/test/test_hist_buffer.F90 +++ b/test/test_hist_buffer.F90 @@ -14,8 +14,8 @@ end module test_hist_buffer_utils #endif program test_hist_buffer - use hist_field, only: hist_field_info_t - use hist_field, only: hist_new_field, hist_get_field + use hist_field, only: hist_field_info_t, hist_get_field + use hist_api, only: hist_new_field type(hist_field_info_t), pointer :: my_fields => NULL() integer :: index @@ -26,7 +26,7 @@ program test_hist_buffer errors = '' call hist_new_field(my_fields, 'U', 'eastward_wind', 'Meridional Wind', & - 'm s-1') + 'm s-1', 'real') if (errcnt > 0) then write(6, '(a,i0,a)') 'FAIL, ', errcnt, ' errors found' From 156448a4636f66a58b2d0ab2899140c64132ceec Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Fri, 25 Sep 2020 22:53:49 -0600 Subject: [PATCH 09/16] Fleshed out init call --- src/hist_api.F90 | 3 +- src/hist_buffer.F90 | 69 +++++++++++++++++++++++++++++++++------------ test/Makefile | 2 +- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/hist_api.F90 b/src/hist_api.F90 index fe990c9..fe9d657 100644 --- a/src/hist_api.F90 +++ b/src/hist_api.F90 @@ -225,7 +225,8 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & end select buffer => buffer_factory(trim(bufftype_string)) if (associated(buffer)) then - call buffer%initialize(output_vol, horiz_axis_ind, buff_shape) + call buffer%initialize(field_base, output_vol, horiz_axis_ind, & + buff_shape, block_sizes, block_ind) else if (present(errmsg)) then write(errmsg(len_trim(errmsg)+1:), '(4a)') errhead, & 'buffer (', trim(bufftype_string), ') not created' diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 index 3ccf420..75fa1ac 100644 --- a/src/hist_buffer.F90 +++ b/src/hist_buffer.F90 @@ -19,17 +19,19 @@ module hist_buffer type, abstract, public :: hist_buffer_t ! hist_buffer_t is an abstract base class for hist_outfld buffers - class(hist_hashable_t), pointer :: field_info => NULL() - integer, private :: vol = -1 ! For host output - integer, private :: horiz_axis_ind = 0 - class(hist_buffer_t), pointer :: next + class(hist_hashable_t), pointer :: field_info => NULL() + integer, private :: vol = -1 ! For host output + integer, private :: horiz_axis_ind = 0 + character(len=:), allocatable, private :: accum_str + class(hist_buffer_t), pointer :: next contains - procedure :: field => get_field_info - procedure :: volume => get_volume - procedure :: horiz_axis_index - procedure :: init_buffer - procedure(hist_buff_init), deferred :: initialize - procedure(hist_buff_clear), deferred :: clear + procedure :: field => get_field_info + procedure :: volume => get_volume + procedure :: horiz_axis_index + procedure :: init_buffer + procedure :: accum_string + procedure(hist_buff_init), deferred :: initialize + procedure(hist_buff_sub_noargs), deferred :: clear end type hist_buffer_t type, public, extends(hist_buffer_t) :: hist_buffer_1dreal32_inst_t @@ -54,19 +56,24 @@ module hist_buffer ! Abstract interfaces for hist_buffer_t class abstract interface - subroutine hist_buff_clear(this) + subroutine hist_buff_sub_noargs(this) import :: hist_buffer_t class(hist_buffer_t), intent(inout) :: this - end subroutine hist_buff_clear + end subroutine hist_buff_sub_noargs end interface abstract interface - subroutine hist_buff_init(this, volume_in, horiz_axis_in, shape_in) + subroutine hist_buff_init(this, field_in, volume_in, horiz_axis_in, & + shape_in, block_sizes_in, block_ind_in) import :: hist_buffer_t + import :: hist_hashable_t class(hist_buffer_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in integer, intent(in) :: shape_in(:) + integer, intent(in) :: block_sizes_in(:) + integer, intent(in) :: block_ind_in end subroutine hist_buff_init end interface @@ -97,17 +104,31 @@ end function horiz_axis_index !####################################################################### - subroutine init_buffer(this, volume_in, horiz_axis_in) + subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, & + block_sizes_in, block_ind_in) class(hist_buffer_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in + integer, intent(in) :: block_sizes_in(:) + integer, intent(in) :: block_ind_in + this%field_info => field_in this%vol = volume_in this%horiz_axis_ind = horiz_axis_in end subroutine init_buffer !####################################################################### + function accum_string(this) result(ac_str) + class(hist_buffer_t), intent(in) :: this + character(len=:), allocatable :: ac_str + + ac_str = this%accum_str + end function accum_string + + !####################################################################### + subroutine buff_1dreal32_inst_clear(this) class(hist_buffer_1dreal32_inst_t), intent(inout) :: this @@ -116,13 +137,19 @@ end subroutine buff_1dreal32_inst_clear !####################################################################### - subroutine init_buff_1dreal32(this, volume_in, horiz_axis_in, shape_in) + subroutine init_buff_1dreal32(this, field_in, volume_in, horiz_axis_in, & + shape_in, block_sizes_in, block_ind_in) class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in integer, intent(in) :: shape_in(:) + integer, intent(in) :: block_sizes_in(:) + integer, intent(in) :: block_ind_in - call init_buffer(this, volume_in, horiz_axis_in) + call init_buffer(this, field_in, volume_in, horiz_axis_in, & + block_sizes_in, block_ind_in) + this%accum_str = 'last sampled value' allocate(this%data(shape_in(1))) end subroutine init_buff_1dreal32 @@ -165,13 +192,19 @@ end subroutine buff_1dreal64_inst_clear !####################################################################### - subroutine init_buff_1dreal64(this, volume_in, horiz_axis_in, shape_in) + subroutine init_buff_1dreal64(this, field_in, volume_in, horiz_axis_in, & + shape_in, block_sizes_in, block_ind_in) class(hist_buffer_1dreal64_inst_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in integer, intent(in) :: shape_in(:) + integer, intent(in) :: block_sizes_in(:) + integer, intent(in) :: block_ind_in - call init_buffer(this, volume_in, horiz_axis_in) + call init_buffer(this, field_in, volume_in, horiz_axis_in, & + block_sizes_in, block_ind_in) + this%accum_str = 'last sampled value' allocate(this%data(shape_in(1))) end subroutine init_buff_1dreal64 diff --git a/test/Makefile b/test/Makefile index 1e6d601..5f7b5ab 100644 --- a/test/Makefile +++ b/test/Makefile @@ -41,7 +41,7 @@ test_hash_table: test_hash.F90 $(HASHOBJS) @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) -test_hist_buffer: test_hist_buffer.F90 hist_hashable.o $(HISTOBJS) +test_hist_buffer: test_hist_buffer.F90 $(HISTOBJS) hist_hashable.o @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) From d9a93a97c8077b374c6e0efc0bd020774b71e8fb Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Sat, 26 Sep 2020 17:47:12 -0600 Subject: [PATCH 10/16] Hook up buffer, better error handler --- src/hist_api.F90 | 156 ++++++++++++++++++-------------------- src/hist_buffer.F90 | 27 +++---- src/util/hist_utils.F90 | 147 +++++++++++++++++++++++++++++++++++ test/Makefile | 9 ++- test/test_hist_buffer.F90 | 54 +++++-------- 5 files changed, 260 insertions(+), 133 deletions(-) create mode 100644 src/util/hist_utils.F90 diff --git a/src/hist_api.F90 b/src/hist_api.F90 index fe9d657..661f0db 100644 --- a/src/hist_api.F90 +++ b/src/hist_api.F90 @@ -1,6 +1,7 @@ module hist_api use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 + use hist_utils, only: hist_error_messages use hist_hashable, only: hist_hashable_t use hist_field, only: hist_field_info_t use hist_buffer, only: hist_buffer_t, buffer_factory @@ -33,77 +34,72 @@ module hist_api !####################################################################### - logical function have_error(errmsg) - ! Return .true. iff is present and contains text - character(len=*), optional, intent(in) :: errmsg + logical function have_error(errors) + ! Return .true. iff is present and contains error messages + type(hist_error_messages), optional, intent(inout) :: errors - have_error = present(errmsg) + have_error = present(errors) if (have_error) then - have_error = len_trim(errmsg) > 0 + have_error = errors%num_errors() > 0 end if end function have_error !####################################################################### - subroutine hist_new_field(new_field, diag_name_in, std_name_in, & - long_name_in, units_in, type_in, errmsg) + function hist_new_field(diag_name_in, std_name_in, long_name_in, units_in, & + type_in, errors) result(new_field) use hist_field, only: hist_field_initialize - type(hist_field_info_t), pointer :: new_field - character(len=*), intent(in) :: diag_name_in - character(len=*), intent(in) :: std_name_in - character(len=*), intent(in) :: long_name_in - character(len=*), intent(in) :: units_in - character(len=*), intent(in) :: type_in - character(len=*), optional, intent(out) :: errmsg + type(hist_field_info_t), pointer :: new_field + character(len=*), intent(in) :: diag_name_in + character(len=*), intent(in) :: std_name_in + character(len=*), intent(in) :: long_name_in + character(len=*), intent(in) :: units_in + character(len=*), intent(in) :: type_in + type(hist_error_messages), optional, intent(inout) :: errors - integer :: astat + integer :: astat + character(len=128) :: errmsg character(len=*), parameter :: subname = 'hist_new_field' - errmsg = '' - if (associated(new_field)) then - deallocate(new_field, stat=astat) - if ((astat /= 0) .and. present(errmsg)) then - write(errmsg, *) subname, ' Unable to deallocate ' - end if - end if - if (.not. have_error(errmsg)) then + if (.not. have_error(errors)) then allocate(new_field, stat=astat) - if ((astat /= 0) .and. present(errmsg)) then - write(errmsg, *) subname, ' Unable to allocate ' + if ((astat /= 0) .and. present(errors)) then + call errors%new_error(subname//' Unable to allocate ') end if end if - if (.not. have_error(errmsg)) then + if (.not. have_error(errors)) then call hist_field_initialize(new_field, diag_name_in, std_name_in, & long_name_in, units_in, type_in, errmsg) - if (have_error(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), *) ', called from ', subname + if (len_trim(errmsg) > 0) then + call errors%new_error(', called from '//subname) end if end if - end subroutine hist_new_field + end function hist_new_field !####################################################################### subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & - accum_type, output_vol, buffer, errmsg, block_ind, block_sizes) + accum_type, output_vol, buffer, errors, block_ind, block_sizes) ! Dummy arguments - class(hist_hashable_t), pointer :: field_base - integer, intent(in) :: buff_shape(:) - integer, intent(in) :: buff_kind - integer, intent(in) :: horiz_axis_ind - character(len=*), intent(in) :: accum_type - integer, intent(in) :: output_vol - class(hist_buffer_t), pointer, intent(out) :: buffer - character(len=*), optional, intent(out) :: errmsg - integer, optional, intent(in) :: block_ind - integer, optional, intent(in) :: block_sizes(:) + class(hist_field_info_t), pointer :: field + integer, intent(in) :: buff_shape(:) + integer, intent(in) :: buff_kind + integer, intent(in) :: horiz_axis_ind + character(len=*), intent(in) :: accum_type + integer, intent(in) :: output_vol + class(hist_buffer_t), pointer, intent(out) :: buffer + type(hist_error_messages), optional, intent(inout) :: errors + integer, optional, intent(in) :: block_ind + integer, optional, intent(in) :: block_sizes(:) ! Local variables integer :: rank character(len=8) :: kind_string character(len=3) :: accum_string character(len=16) :: bufftype_string - type(hist_field_info_t), pointer :: field + class(hist_hashable_t), pointer :: field_base + class(hist_buffer_t), pointer :: buff_ptr character(len=:), allocatable :: type_str integer, parameter :: max_rank = 2 character(len=*), parameter :: subname = 'hist_new_buffer' @@ -111,23 +107,12 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & ! Initialize output and local variables nullify(buffer) - nullify(field) - if (present(errmsg)) then - errmsg = '' - end if + nullify(field_base) + nullify(buff_ptr) rank = SIZE(buff_shape, 1) !! Some sanity checks ! We can select on the field's type string but not its kind string ! because we do not know the kind value for the kind string - select type (field_base) - type is (hist_field_info_t) - field => field_base - class default - if (present(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') errhead, & - 'Input, , is not of type, hist_field_info_t' - end if - end select if (associated(field)) then type_str = field%type() else @@ -154,38 +139,38 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & end select case default kind_string = '' - if (present(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), '(4a)') errhead, & - "type, '", type_str, ' is not supported' + if (present(errors)) then + call errors%new_error(errhead//"type, '"//type_str, & + errstr2="' is not supported") end if end select - if ((len_trim(kind_string) == 0) .and. present(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), '(2a,i0,2a)') errhead, & - "kind = ", buff_kind, " is not supported for type ", type_str + if ((len_trim(kind_string) == 0) .and. present(errors)) then + call errors%new_error(errhead//"kind = ", errint1=buff_kind, & + errstr2=" is not supported for type "//type_str) end if ! Check horiz_axis_ind if ((horiz_axis_ind < 1) .or. (horiz_axis_ind > rank)) then - write(errmsg(len_trim(errmsg)+1:), '(2a,i0,a)') errhead, & - 'horiz_axis_ind outside of valid range, [1, ', rank, ']' + call errors%new_error(errhead//'horiz_axis_ind outside of ', & + errstr2='valid range, [1, ', errint2=rank, errstr3=']') end if ! Check for (proper) block structured buffer if (present(block_ind) .and. present(block_sizes)) then if ((block_ind < 1) .or. (block_ind > rank)) then - write(errmsg(len_trim(errmsg)+1:), '(2a,i0,a)') errhead, & - 'block_ind outside of valid range, [1, ', rank, ']' + call errors%new_error(errhead//'block_ind outside of ', & + errstr2='valid range, [1, ', errint2=rank, errstr3=']') else if (block_ind == horiz_axis_ind) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') errhead, & - 'block_ind cannot be the same as horiz_axis_ind' + call errors%new_error(errhead//'block_ind cannot be the same ', & + errstr2='as horiz_axis_ind') end if else if (present(block_ind)) then - if (present(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') errhead, & - 'block_sizes required if block_ind is present' + if (present(errors)) then + call errors%new_error(errhead, & + errstr2='block_sizes required if block_ind is present') end if else if (present(block_sizes)) then - if (present(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') errhead, & - 'block_ind required if block_sizes is present' + if (present(errors)) then + call errors%new_error(errhead, & + errstr2='block_ind required if block_sizes is present') end if end if ! No else, we just do not have a blocked buffer ! Check accumulation type @@ -201,9 +186,9 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & case ('S', 's', 'var') accum_string = 'var' case default - if (present(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), '(4a)') errhead, & - "Unknown accumulation operator type, '", trim(accum_type), "'" + if (present(errors)) then + call errors%new_error(errhead//"Unknown accumulation operator", & + errstr2=" type, '"//trim(accum_type)//"'") end if end select ! We now know what sort of buffer we need @@ -218,18 +203,27 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & end if case default ! Over max rank currently handled - if (present(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), '(2a,i0)') errhead, & - 'buffers have a max rank of ', max_rank + if (present(errors)) then + call errors%new_error(errhead//'buffers have a max rank of ', & + errint1=max_rank) end if end select buffer => buffer_factory(trim(bufftype_string)) if (associated(buffer)) then + field_base => field call buffer%initialize(field_base, output_vol, horiz_axis_ind, & buff_shape, block_sizes, block_ind) - else if (present(errmsg)) then - write(errmsg(len_trim(errmsg)+1:), '(4a)') errhead, & - 'buffer (', trim(bufftype_string), ') not created' + ! Add this buffer to its field (field should be there if buffer is) + if (associated(field%buffers)) then + buff_ptr => field%buffers + field%buffers => buffer + buffer%next => buff_ptr + else + field%buffers => buffer + end if + else if (present(errors)) then + call errors%new_error(errhead//'buffer ('//trim(bufftype_string), & + errstr2=') not created') end if end subroutine hist_new_buffer diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 index 75fa1ac..3a293fc 100644 --- a/src/hist_buffer.F90 +++ b/src/hist_buffer.F90 @@ -8,12 +8,6 @@ module hist_buffer ! Public interfaces public :: buffer_factory - ! Processing flag indices - integer, parameter :: hist_proc_last = 1 ! Save last sample - integer, parameter :: hist_proc_average = 2 ! Average samples - integer, parameter :: hist_proc_stddev = 3 ! Standard deviation of samples - integer, parameter :: hist_proc_min = 4 ! Minimum of samples - integer, parameter :: hist_proc_max = 5 ! Maximum of samples ! Time sampling flag indices !!XXgoldyXX: Todo: decide on sampling types @@ -72,8 +66,8 @@ subroutine hist_buff_init(this, field_in, volume_in, horiz_axis_in, & integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in integer, intent(in) :: shape_in(:) - integer, intent(in) :: block_sizes_in(:) - integer, intent(in) :: block_ind_in + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in end subroutine hist_buff_init end interface @@ -110,8 +104,8 @@ subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, & class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in - integer, intent(in) :: block_sizes_in(:) - integer, intent(in) :: block_ind_in + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in this%field_info => field_in this%vol = volume_in @@ -144,8 +138,8 @@ subroutine init_buff_1dreal32(this, field_in, volume_in, horiz_axis_in, & integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in integer, intent(in) :: shape_in(:) - integer, intent(in) :: block_sizes_in(:) - integer, intent(in) :: block_ind_in + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in call init_buffer(this, field_in, volume_in, horiz_axis_in, & block_sizes_in, block_ind_in) @@ -199,8 +193,8 @@ subroutine init_buff_1dreal64(this, field_in, volume_in, horiz_axis_in, & integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in integer, intent(in) :: shape_in(:) - integer, intent(in) :: block_sizes_in(:) - integer, intent(in) :: block_ind_in + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in call init_buffer(this, field_in, volume_in, horiz_axis_in, & block_sizes_in, block_ind_in) @@ -259,12 +253,13 @@ function buffer_factory(buffer_type) result(newbuf) type(hist_buffer_1dreal64_inst_t), pointer :: real64_1_in => NULL() + nullify(newbuf) ! Create new buffer select case (trim(buffer_type)) - case ('real32_1_inst') + case ('real32_1_lst') allocate(real32_1_in) newbuf => real32_1_in - case ('real64_1_inst') + case ('real64_1_lst') allocate(real64_1_in) newbuf => real64_1_in end select diff --git a/src/util/hist_utils.F90 b/src/util/hist_utils.F90 new file mode 100644 index 0000000..a7eb5b7 --- /dev/null +++ b/src/util/hist_utils.F90 @@ -0,0 +1,147 @@ +module hist_utils + + implicit none + private + + type :: hist_error_entry + character(len=:), allocatable :: error_message + type(hist_error_entry), pointer :: next => NULL() + contains + final :: finalize_error_entry + end type hist_error_entry + + type, public :: hist_error_messages + integer, private :: length = 0 + logical, private :: alloc_error = .false. + integer, private :: max_len = -1 ! no limit + type(hist_error_entry), private, pointer :: error_messages => NULL() + type(hist_error_entry), private, pointer :: tail => NULL() + contains + procedure :: num_errors => hist_utils_num_errors + procedure :: new_error => hist_utils_new_error + procedure :: output => hist_utils_print_errors + final :: finalize_hist_error_messages + end type hist_error_messages + +CONTAINS + + !####################################################################### + + subroutine finalize_error_entry(this) + ! Dummy Argument + type(hist_error_entry) :: this + ! Local argument + type(hist_error_entry), pointer :: next + + if (allocated(this%error_message)) then + deallocate(this%error_message) + end if + + next => this%next + nullify(this%next) + if (associated(next)) then + deallocate(next) + end if + end subroutine finalize_error_entry + + !####################################################################### + + integer function hist_utils_num_errors(this) + ! Dummy Argument + class(hist_error_messages) :: this + + hist_utils_num_errors = this%length + end function hist_utils_num_errors + + !####################################################################### + + subroutine hist_utils_new_error(this, errstr1, errint1, errstr2, errint2, & + errstr3) + ! Dummy Arguments + class(hist_error_messages) :: this + character(len=*), intent(in) :: errstr1 + integer, optional, intent(in) :: errint1 + character(len=*), optional, intent(in) :: errstr2 + integer, optional, intent(in) :: errint2 + character(len=*), optional, intent(in) :: errstr3 + + ! Local variables + integer :: aerr + type(hist_error_entry), pointer :: new_error + character(len=:), allocatable :: temp_str + character(len=8) :: intstr + + temp_str = trim(errstr1) + if (present(errint1)) then + write(intstr, '(i0)') errint1 + temp_str = trim(temp_str)//intstr + end if + if (present(errstr2)) then + temp_str = trim(temp_str)//errstr2 + end if + if (present(errint2)) then + write(intstr, '(i0)') errint2 + temp_str = trim(temp_str)//intstr + end if + if (present(errstr3)) then + temp_str = trim(temp_str)//errstr3 + end if + allocate(new_error, stat=aerr) + ! We can't really report an error here so just trap and record + if (aerr == 0) then + new_error%error_message = trim(temp_str) + if (.not. associated(this%error_messages)) then + this%error_messages => new_error + end if + if (associated(this%tail)) then + this%tail%next => new_error + end if + this%tail => new_error + this%length = this%length + 1 + else + this%alloc_error = .true. + end if + end subroutine hist_utils_new_error + + !####################################################################### + + subroutine hist_utils_print_errors(this, unit, header) + ! Dummy Arguments + class(hist_error_messages) :: this + integer, intent(in) :: unit + character(len=*), optional, intent(in) :: header + + type(hist_error_entry), pointer :: err_ptr + + err_ptr => this%error_messages + if (present(header) .and. (this%num_errors() > 0)) then + write(unit, '(a)') trim(header) + end if + if (this%alloc_error) then + write(unit, '(a)') 'ERROR: Allocation error in error-handling system' + end if + do + if (associated(err_ptr)) then + write(unit, '(a)') trim(err_ptr%error_message) + err_ptr => err_ptr%next + else + exit + end if + end do + end subroutine hist_utils_print_errors + + !####################################################################### + + subroutine finalize_hist_error_messages(this) + ! Dummy Argument + type(hist_error_messages) :: this + + if (associated(this%error_messages)) then + deallocate(this%error_messages) + end if + nullify(this%error_messages) + nullify(this%tail) + this%length = 0 + end subroutine finalize_hist_error_messages + +end module hist_utils diff --git a/test/Makefile b/test/Makefile index 5f7b5ab..acdd5f0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -6,6 +6,7 @@ FCFLAGS += -g SRCPATH = ../src HASHPATH = $(SRCPATH)/hash +UTILPATH = $(SRCPATH)/util HASHOBJS = hist_hashable.o hist_hash_table.o HISTOBJS = hist_buffer.o hist_field.o hist_api.o @@ -17,6 +18,10 @@ endif # TARGETS +hist_utils.o: $(UTILPATH)/hist_utils.F90 + @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) + @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) + hist_hashable.o: $(HASHPATH)/hist_hashable.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) @@ -25,7 +30,7 @@ hist_hash_table.o: $(HASHPATH)/hist_hash_table.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) -hist_buffer.o: $(SRCPATH)/hist_buffer.F90 hist_hashable.o +hist_buffer.o: $(SRCPATH)/hist_buffer.F90 hist_hashable.o hist_utils.o @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) @@ -41,7 +46,7 @@ test_hash_table: test_hash.F90 $(HASHOBJS) @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) -test_hist_buffer: test_hist_buffer.F90 $(HISTOBJS) hist_hashable.o +test_hist_buffer: test_hist_buffer.F90 $(HISTOBJS) hist_hashable.o hist_utils.o @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) diff --git a/test/test_hist_buffer.F90 b/test/test_hist_buffer.F90 index b26f138..01b6809 100644 --- a/test/test_hist_buffer.F90 +++ b/test/test_hist_buffer.F90 @@ -1,38 +1,24 @@ -#if 0 -module test_hist_buffer_utils - use hist_field, only: hist_field_info_t - use hist_buffer, only: hist_buffer_t - - implicit none - private - - public :: get_field - -CONTAINS - -end module test_hist_buffer_utils -#endif - program test_hist_buffer - use hist_field, only: hist_field_info_t, hist_get_field - use hist_api, only: hist_new_field - - type(hist_field_info_t), pointer :: my_fields => NULL() - integer :: index - integer :: errcnt = 0 - integer, parameter :: max_errs = 8 - integer, parameter :: err_size = 128 - character(len=err_size) :: errors(max_errs) - - errors = '' - call hist_new_field(my_fields, 'U', 'eastward_wind', 'Meridional Wind', & - 'm s-1', 'real') - - if (errcnt > 0) then - write(6, '(a,i0,a)') 'FAIL, ', errcnt, ' errors found' - do index = 1, errcnt - write(6, *) trim(errors(index)) - end do + use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 + use hist_utils, only: hist_error_messages + use hist_buffer, only: hist_buffer_t + use hist_field, only: hist_field_info_t, hist_get_field + use hist_api, only: hist_new_field, hist_new_buffer + + class(hist_field_info_t), pointer :: my_fields => NULL() + class(hist_buffer_t), pointer :: buffer => NULL() + class(hist_buffer_t), pointer :: buff_ptr => NULL() + type(hist_error_messages) :: errors + + my_fields => hist_new_field('U', 'eastward_wind', 'Meridional Wind', & + 'm s-1', 'real', errors=errors) + call hist_new_buffer(my_fields, (/ 5 /), REAL32, 1, 'lst', 1, buffer, & + errors=errors) + + if (errors%num_errors() > 0) then + write(6, '(a,i0,a)') 'FAIL, ', errors%num_errors(), ' errors found' + call errors%output(6) + flush(6) STOP 1 else STOP 0 From 731e7cb1c5f241b9230b72dc39e722f61fd2aafc Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Sun, 27 Sep 2020 17:18:33 -0600 Subject: [PATCH 11/16] Better logging --- CODE_OF_CONDUCT.md | 84 ++++++++ src/hist_api.F90 | 148 +++++++------- src/hist_buffer.F90 | 316 ++++++++++++++++++++--------- src/util/hist_msg_handler.F90 | 368 ++++++++++++++++++++++++++++++++++ src/util/hist_utils.F90 | 147 -------------- test/Makefile | 6 +- test/test_hist_buffer.F90 | 12 +- 7 files changed, 754 insertions(+), 327 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 src/util/hist_msg_handler.F90 delete mode 100644 src/util/hist_utils.F90 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..25a5c89 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,84 @@ +# Contributor Code of Conduct +_The Contributor Code of Conduct is for participants in our software projects and community._ + +## Our Pledge +We, as contributors, creators, stewards, and maintainers (participants), of the history_output utilitys pledge to make participation in our software, system or hardware project and community a safe, productive, welcoming and inclusive experience for everyone. +All participants are required to abide by this Code of Conduct. +This includes respectful treatment of everyone regardless of age, body size, disability, ethnicity, gender identity or expression, level of experience, nationality, political affiliation, veteran status, pregnancy, genetic information, physical appearance, race, religion, or sexual orientation, as well as any other characteristic protected under applicable US federal or state law. + +## Our Standards +Examples of behaviors that contribute to a positive environment include: + +* All participants are treated with respect and consideration, valuing a diversity of views and opinions +* Be considerate, respectful, and collaborative +* Communicate openly with respect for others, critiquing ideas rather than individuals and gracefully accepting criticism +* Acknowledging the contributions of others +* Avoid personal attacks directed toward other participants +* Be mindful of your surroundings and of your fellow participants +* Alert UCAR staff and suppliers/vendors if you notice a dangerous situation or someone in distress +* Respect the rules and policies of the project and venue + +Examples of unacceptable behavior include, but are not limited to: + +* Harassment, intimidation, or discrimination in any form +* Physical, verbal, or written abuse by anyone to anyone, including repeated use of pronouns other than those requested +* Unwelcome sexual attention or advances +* Personal attacks directed at other guests, members, participants, etc. +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Alarming, intimidating, threatening, or hostile comments or conduct +* Inappropriate use of nudity and/or sexual images +* Threatening or stalking anyone, including a participant +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Scope +This Code of Conduct applies to all spaces managed by the Project whether they be physical, online or face-to-face. +This includes project code, code repository, associated web pages, documentation, mailing lists, project websites and wiki pages, issue tracker, meetings, telecons, events, project social media accounts, and any other forums created by the project team which the community uses for communication. +In addition, violations of this Code of Conduct outside these spaces may affect a person's ability to participate within them. +Representation of a project may be further defined and clarified by project maintainers. + +## Community Responsibilities +Everyone in the community is empowered to respond to people who are showing unacceptable behavior. +They can talk to them privately or publicly. +Anyone requested to stop unacceptable behavior is expected to comply immediately. +If the behavior continues concerns may be brought to the project administrators or to any other party listed in the [Reporting](#reporting) section below. + +## Project Administrator Responsibilities +Project administrators are responsible for clarifying the standards of acceptable behavior and are encouraged to model appropriate behavior and provide support when people in the community point out inappropriate behavior. +Project administrator(s) are normally the ones that would be tasked to carry out the actions in the [Consequences](#consequences) section below. + +Project administrators are also expected to keep this Code of Conduct updated with the main one housed at UCAR, as listed below in the [Attribution](#attribution) section. + +## Reporting +Instances of unacceptable behavior can be brought to the attention of the project administrator(s) who may take any action as outlined in the [Consequences](#consequences) section below. +However, making a report to a project administrator is not considered an 'official report' to UCAR. + +Instances of unacceptable behavior may also be reported directly to UCAR pursuant to [UCAR's Harassment Reporting and Complaint Procedure](https://www2.fin.ucar.edu/procedures/hr/harassment-reporting-and-complaint-procedure), or anonymously through [UCAR's EthicsPoint Hotline](https://www2.fin.ucar.edu/ethics/anonymous-reporting). + +Complaints received by UCAR will be handled pursuant to the procedures outlined in UCAR's Harassment Reporting and Complaint Procedure. +Complaints to UCAR will be held as confidential as practicable under the circumstances, and retaliation against a person who initiates a complaint or an inquiry about inappropriate behavior will not be tolerated. + +Any Contributor can use these reporting methods even if they are not directly affiliated with UCAR. +The Frequently Asked Questions (FAQ) page for reporting is [here](https://www2.fin.ucar.edu/procedures/hr/reporting-faqs). + +## Consequences +Upon receipt of a complaint, the project administrator(s) may take any action deemed necessary and appropriate under the circumstances. +Such action can include things such as: removing, editing, or rejecting comments, commits, code, wiki edits, email, issues, and other contributions that are not aligned to this Code of Conduct, or banning temporarily or permanently any contributor for other behaviors that are deemed inappropriate, threatening, offensive, or harmful. +Project administrators also have the right to report violations to UCAR HR and/or UCAR's Office of Diversity, Equity and Inclusion (ODEI), as well as a participant's home institution and/or law enforcement. +In the event an incident is reported to UCAR, UCAR will follow its Harassment Reporting and Complaint Procedure. + +## Process for Changes +All UCAR managed projects are required to adopt this Contributor Code of Conduct. +Adoption is assumed even if not expressly stated in the repository. +Projects should fill in sections where prompted with project-specific information, including, project name and adoption date. + +Projects that adopt this Code of Conduct need to stay up to date with UCAR's Contributor Code of Conduct, linked with a DOI in the [Attribution](#attribution) section below. +Projects can make limited substantive changes to the Code of Conduct, however, the changes must be limited in scope and may not contradict the UCAR Contributor Code of Conduct. + +## Attribution +This Code of Conduct was originally adapted from the [Contributor Covenant](http://contributor-covenant.org/version/1/4), version 1.4. +We then aligned it with the UCAR Participant Code of Conduct, which also borrows from the American Geophysical Union (AGU) Code of Conduct. +The UCAR Participant Code of Conduct applies to both UCAR employees as well as participants in activities run by UCAR. +The original version of this for all software projects that have strong management from UCAR or UCAR staff is available on the UCAR website at https://doi.org/10.5065/6w2c-a132. +The date that it was adopted by this project was 2020-04-08 and replaces the previous version. +When responding to complaints, UCAR HR and ODEI will do so based on the latest published version. +Therefore, any project-specific changes should follow the [Process for Changes](#process-for-changes) section above. diff --git a/src/hist_api.F90 b/src/hist_api.F90 index 661f0db..863180f 100644 --- a/src/hist_api.F90 +++ b/src/hist_api.F90 @@ -1,12 +1,11 @@ module hist_api - use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 - use hist_utils, only: hist_error_messages - use hist_hashable, only: hist_hashable_t - use hist_field, only: hist_field_info_t - use hist_buffer, only: hist_buffer_t, buffer_factory - use hist_buffer, only: hist_buffer_1dreal32_inst_t - use hist_buffer, only: hist_buffer_1dreal64_inst_t + use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 + use hist_hashable, only: hist_hashable_t + use hist_field, only: hist_field_info_t + use hist_buffer, only: hist_buffer_t, buffer_factory + use hist_buffer, only: hist_buff_1dreal32_lst_t + use hist_buffer, only: hist_buff_1dreal64_lst_t implicit none private @@ -34,45 +33,35 @@ module hist_api !####################################################################### - logical function have_error(errors) - ! Return .true. iff is present and contains error messages - type(hist_error_messages), optional, intent(inout) :: errors - - have_error = present(errors) - if (have_error) then - have_error = errors%num_errors() > 0 - end if - end function have_error - - !####################################################################### - function hist_new_field(diag_name_in, std_name_in, long_name_in, units_in, & type_in, errors) result(new_field) - use hist_field, only: hist_field_initialize + use hist_msg_handler, only: hist_have_error, hist_log_messages, ERROR + use hist_field, only: hist_field_initialize - type(hist_field_info_t), pointer :: new_field - character(len=*), intent(in) :: diag_name_in - character(len=*), intent(in) :: std_name_in - character(len=*), intent(in) :: long_name_in - character(len=*), intent(in) :: units_in - character(len=*), intent(in) :: type_in - type(hist_error_messages), optional, intent(inout) :: errors + type(hist_field_info_t), pointer :: new_field + character(len=*), intent(in) :: diag_name_in + character(len=*), intent(in) :: std_name_in + character(len=*), intent(in) :: long_name_in + character(len=*), intent(in) :: units_in + character(len=*), intent(in) :: type_in + type(hist_log_messages), optional, intent(inout) :: errors integer :: astat character(len=128) :: errmsg character(len=*), parameter :: subname = 'hist_new_field' - if (.not. have_error(errors)) then + if (.not. hist_have_error(errors)) then allocate(new_field, stat=astat) if ((astat /= 0) .and. present(errors)) then call errors%new_error(subname//' Unable to allocate ') end if end if - if (.not. have_error(errors)) then + if (.not. hist_have_error(errors)) then call hist_field_initialize(new_field, diag_name_in, std_name_in, & long_name_in, units_in, type_in, errmsg) - if (len_trim(errmsg) > 0) then - call errors%new_error(', called from '//subname) + if (hist_have_error(errors)) then + call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname=subname) end if end if end function hist_new_field @@ -81,20 +70,24 @@ end function hist_new_field subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & accum_type, output_vol, buffer, errors, block_ind, block_sizes) + use hist_msg_handler, only: hist_log_messages, hist_add_error + use hist_msg_handler, only: hist_add_alloc_error, ERROR + ! Dummy arguments - class(hist_field_info_t), pointer :: field - integer, intent(in) :: buff_shape(:) - integer, intent(in) :: buff_kind - integer, intent(in) :: horiz_axis_ind - character(len=*), intent(in) :: accum_type - integer, intent(in) :: output_vol - class(hist_buffer_t), pointer, intent(out) :: buffer - type(hist_error_messages), optional, intent(inout) :: errors - integer, optional, intent(in) :: block_ind - integer, optional, intent(in) :: block_sizes(:) + class(hist_field_info_t), pointer :: field + integer, intent(in) :: buff_shape(:) + integer, intent(in) :: buff_kind + integer, intent(in) :: horiz_axis_ind + character(len=*), intent(in) :: accum_type + integer, intent(in) :: output_vol + class(hist_buffer_t), pointer, intent(out) :: buffer + type(hist_log_messages), optional, intent(inout) :: errors + integer, optional, intent(in) :: block_ind + integer, optional, intent(in) :: block_sizes(:) ! Local variables integer :: rank + integer :: line_loc character(len=8) :: kind_string character(len=3) :: accum_string character(len=16) :: bufftype_string @@ -103,7 +96,6 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & character(len=:), allocatable :: type_str integer, parameter :: max_rank = 2 character(len=*), parameter :: subname = 'hist_new_buffer' - character(len=*), parameter :: errhead = subname//' ERROR: ' ! Initialize output and local variables nullify(buffer) @@ -139,39 +131,35 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & end select case default kind_string = '' - if (present(errors)) then - call errors%new_error(errhead//"type, '"//type_str, & - errstr2="' is not supported") - end if + call hist_add_error(subname, "type, '"//type_str, & + errstr2="' is not supported", errors=errors) end select if ((len_trim(kind_string) == 0) .and. present(errors)) then - call errors%new_error(errhead//"kind = ", errint1=buff_kind, & - errstr2=" is not supported for type "//type_str) + call errors%new_error("kind = ", errint1=buff_kind, & + errstr2=" is not supported for type "//type_str, subname=subname) end if ! Check horiz_axis_ind if ((horiz_axis_ind < 1) .or. (horiz_axis_ind > rank)) then - call errors%new_error(errhead//'horiz_axis_ind outside of ', & - errstr2='valid range, [1, ', errint2=rank, errstr3=']') + call hist_add_error(subname, 'horiz_axis_ind outside of ', & + errstr2='valid range, [1, ', errint2=rank, errstr3=']', & + errors=errors) end if ! Check for (proper) block structured buffer if (present(block_ind) .and. present(block_sizes)) then if ((block_ind < 1) .or. (block_ind > rank)) then - call errors%new_error(errhead//'block_ind outside of ', & - errstr2='valid range, [1, ', errint2=rank, errstr3=']') + call hist_add_error(subname, 'block_ind outside of ', & + errstr2='valid range, [1, ', errint2=rank, errstr3=']', & + errors=errors) else if (block_ind == horiz_axis_ind) then - call errors%new_error(errhead//'block_ind cannot be the same ', & - errstr2='as horiz_axis_ind') + call hist_add_error(subname, 'block_ind cannot be the same ', & + errstr2='as horiz_axis_ind', errors=errors) end if else if (present(block_ind)) then - if (present(errors)) then - call errors%new_error(errhead, & - errstr2='block_sizes required if block_ind is present') - end if + call hist_add_error(subname, & + 'block_sizes required if block_ind is present', errors=errors) else if (present(block_sizes)) then - if (present(errors)) then - call errors%new_error(errhead, & - errstr2='block_ind required if block_sizes is present') - end if + call hist_add_error(subname, & + 'block_ind required if block_sizes is present', errors=errors) end if ! No else, we just do not have a blocked buffer ! Check accumulation type select case(trim(accum_type)) @@ -186,10 +174,9 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & case ('S', 's', 'var') accum_string = 'var' case default - if (present(errors)) then - call errors%new_error(errhead//"Unknown accumulation operator", & - errstr2=" type, '"//trim(accum_type)//"'") - end if + call hist_add_error(subname, & + "Unknown accumulation operator type, '", & + errstr2=trim(accum_type)//"'", errors=errors) end select ! We now know what sort of buffer we need ! First, sort by rank @@ -203,16 +190,15 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & end if case default ! Over max rank currently handled - if (present(errors)) then - call errors%new_error(errhead//'buffers have a max rank of ', & - errint1=max_rank) - end if + call hist_add_error(subname, 'buffers have a max rank of ', & + errint1=max_rank, errors=errors) end select - buffer => buffer_factory(trim(bufftype_string)) + buffer => buffer_factory(trim(bufftype_string), logger=errors) + line_loc = __LINE__ - 1 if (associated(buffer)) then field_base => field call buffer%initialize(field_base, output_vol, horiz_axis_ind, & - buff_shape, block_sizes, block_ind) + buff_shape, block_sizes, block_ind, logger=errors) ! Add this buffer to its field (field should be there if buffer is) if (associated(field%buffers)) then buff_ptr => field%buffers @@ -221,9 +207,13 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & else field%buffers => buffer end if - else if (present(errors)) then - call errors%new_error(errhead//'buffer ('//trim(bufftype_string), & - errstr2=') not created') + else + call hist_add_error(subname, 'buffer ('//trim(bufftype_string), & + errstr2=') not created', errors=errors) + if (present(errors)) then + call errors%add_stack_frame(ERROR, __FILE__, line_loc, & + subname=subname) + end if end if end subroutine hist_new_buffer @@ -231,15 +221,15 @@ end subroutine hist_new_buffer !####################################################################### subroutine hist_buffer_accumulate_1dreal32(buffer, field) - class(hist_buffer_1dreal32_inst_t), intent(inout) :: buffer - real(REAL32), intent(in) :: field(:) + class(hist_buff_1dreal32_lst_t), intent(inout) :: buffer + real(REAL32), intent(in) :: field(:) end subroutine hist_buffer_accumulate_1dreal32 !####################################################################### subroutine hist_buffer_accumulate_1dreal64(buffer, field) - class(hist_buffer_1dreal64_inst_t), intent(inout) :: buffer - real(REAL64), intent(in) :: field(:) + class(hist_buff_1dreal64_lst_t), intent(inout) :: buffer + real(REAL64), intent(in) :: field(:) end subroutine hist_buffer_accumulate_1dreal64 end module hist_api diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 index 3a293fc..81f4416 100644 --- a/src/hist_buffer.F90 +++ b/src/hist_buffer.F90 @@ -16,6 +16,8 @@ module hist_buffer class(hist_hashable_t), pointer :: field_info => NULL() integer, private :: vol = -1 ! For host output integer, private :: horiz_axis_ind = 0 + integer, allocatable, private :: field_shape(:) + integer, allocatable, private :: num_samples(:) character(len=:), allocatable, private :: accum_str class(hist_buffer_t), pointer :: next contains @@ -25,54 +27,65 @@ module hist_buffer procedure :: init_buffer procedure :: accum_string procedure(hist_buff_init), deferred :: initialize - procedure(hist_buff_sub_noargs), deferred :: clear + procedure(hist_buff_sub_log), deferred :: clear end type hist_buffer_t - type, public, extends(hist_buffer_t) :: hist_buffer_1dreal32_inst_t - integer :: num_samples = 0 + type, public, extends(hist_buffer_t) :: hist_buff_1dreal32_lst_t real(REAL32), pointer :: data(:) => NULL() CONTAINS - procedure :: clear => buff_1dreal32_inst_clear - procedure :: accumulate => buff_1dreal32_inst_accum - procedure :: norm_value => buff_1dreal32_inst_value + procedure :: clear => buff_1dreal32_lst_clear + procedure :: accumulate => buff_1dreal32_lst_accum + procedure :: norm_value => buff_1dreal32_lst_value procedure :: initialize => init_buff_1dreal32 - end type hist_buffer_1dreal32_inst_t + end type hist_buff_1dreal32_lst_t - type, public, extends(hist_buffer_t) :: hist_buffer_1dreal64_inst_t - integer :: num_samples = 0 + type, public, extends(hist_buff_1dreal32_lst_t) :: hist_buff_1dreal32_avg_t + CONTAINS + procedure :: accumulate => buff_1dreal32_avg_accum + procedure :: norm_value => buff_1dreal32_avg_value + procedure :: initialize => init_buff_avg_1dreal32 + end type hist_buff_1dreal32_avg_t + + type, public, extends(hist_buffer_t) :: hist_buff_1dreal64_lst_t real(REAL64), pointer :: data(:) => NULL() CONTAINS - procedure :: clear => buff_1dreal64_inst_clear - procedure :: accumulate => buff_1dreal64_inst_accum - procedure :: norm_value => buff_1dreal64_inst_value + procedure :: clear => buff_1dreal64_lst_clear + procedure :: accumulate => buff_1dreal64_lst_accum + procedure :: norm_value => buff_1dreal64_lst_value procedure :: initialize => init_buff_1dreal64 - end type hist_buffer_1dreal64_inst_t + end type hist_buff_1dreal64_lst_t ! Abstract interfaces for hist_buffer_t class abstract interface - subroutine hist_buff_sub_noargs(this) - import :: hist_buffer_t - class(hist_buffer_t), intent(inout) :: this - end subroutine hist_buff_sub_noargs + subroutine hist_buff_sub_log(this, logger) + use hist_msg_handler, only: hist_log_messages + import :: hist_buffer_t + class(hist_buffer_t), intent(inout) :: this + type(hist_log_messages), optional, intent(inout) :: logger + end subroutine hist_buff_sub_log end interface abstract interface subroutine hist_buff_init(this, field_in, volume_in, horiz_axis_in, & - shape_in, block_sizes_in, block_ind_in) - import :: hist_buffer_t - import :: hist_hashable_t - class(hist_buffer_t), intent(inout) :: this - class(hist_hashable_t), pointer :: field_in - integer, intent(in) :: volume_in - integer, intent(in) :: horiz_axis_in - integer, intent(in) :: shape_in(:) - integer, optional, intent(in) :: block_sizes_in(:) - integer, optional, intent(in) :: block_ind_in + shape_in, block_sizes_in, block_ind_in, logger) + use hist_msg_handler, only: hist_log_messages + import :: hist_buffer_t + import :: hist_hashable_t + class(hist_buffer_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + integer, intent(in) :: shape_in(:) + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in + type(hist_log_messages), optional, intent(inout) :: logger end subroutine hist_buff_init end interface CONTAINS + !####################################################################### + function get_field_info(this) class(hist_buffer_t), intent(in) :: this class(hist_hashable_t), pointer :: get_field_info @@ -98,18 +111,33 @@ end function horiz_axis_index !####################################################################### - subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, & - block_sizes_in, block_ind_in) - class(hist_buffer_t), intent(inout) :: this - class(hist_hashable_t), pointer :: field_in - integer, intent(in) :: volume_in - integer, intent(in) :: horiz_axis_in - integer, optional, intent(in) :: block_sizes_in(:) - integer, optional, intent(in) :: block_ind_in + subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & + block_sizes_in, block_ind_in, logger) + use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error + + ! Dummy arguments + class(hist_buffer_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + integer, intent(in) :: shape_in(:) + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + integer :: astat + character(len=*), parameter :: subname = 'init_buffer' this%field_info => field_in this%vol = volume_in this%horiz_axis_ind = horiz_axis_in + allocate(this%field_shape(size(shape_in, 1)), stat=astat) + if (astat == 0) then + this%field_shape(:) = shape_in(:) + else + call hist_add_alloc_error('field_shape', __FILE__, __LINE__ - 4, & + subname=subname, errors=logger) + end if end subroutine init_buffer !####################################################################### @@ -123,35 +151,55 @@ end function accum_string !####################################################################### - subroutine buff_1dreal32_inst_clear(this) - class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + subroutine buff_1dreal32_lst_clear(this, logger) + use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error + + ! Dummy arguments + class(hist_buff_1dreal32_lst_t), intent(inout) :: this + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + integer :: aerr + character(len=*), parameter :: subname = 'buff_1dreal32_lst_clear' this%num_samples = 0 - end subroutine buff_1dreal32_inst_clear + if (.not. associated(this%data)) then + allocate(this%data(this%field_shape(1)), stat=aerr) + if (aerr /= 0) then + call hist_add_alloc_error('data', __FILE__, __LINE__ - 2, & + subname=subname, errors=logger) + end if + end if + this%data = 0.0_REAL32 + + end subroutine buff_1dreal32_lst_clear !####################################################################### subroutine init_buff_1dreal32(this, field_in, volume_in, horiz_axis_in, & - shape_in, block_sizes_in, block_ind_in) - class(hist_buffer_1dreal32_inst_t), intent(inout) :: this - class(hist_hashable_t), pointer :: field_in - integer, intent(in) :: volume_in - integer, intent(in) :: horiz_axis_in - integer, intent(in) :: shape_in(:) - integer, optional, intent(in) :: block_sizes_in(:) - integer, optional, intent(in) :: block_ind_in - - call init_buffer(this, field_in, volume_in, horiz_axis_in, & - block_sizes_in, block_ind_in) + shape_in, block_sizes_in, block_ind_in, logger) + use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error + + ! Dummy arguments + class(hist_buff_1dreal32_lst_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + integer, intent(in) :: shape_in(:) + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in + type(hist_log_messages), optional, intent(inout) :: logger + + call init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & + block_sizes_in, block_ind_in, logger=logger) + call this%clear(logger=logger) this%accum_str = 'last sampled value' - allocate(this%data(shape_in(1))) end subroutine init_buff_1dreal32 !####################################################################### - subroutine buff_1dreal32_inst_accum(this, field, errmsg) - class(hist_buffer_1dreal32_inst_t), intent(inout) :: this + subroutine buff_1dreal32_lst_accum(this, field, errmsg) + class(hist_buff_1dreal32_lst_t), intent(inout) :: this real(REAL32), intent(in) :: field(:) character(len=*), optional, intent(out) :: errmsg @@ -160,78 +208,146 @@ subroutine buff_1dreal32_inst_accum(this, field, errmsg) end if this%num_samples = 1 - end subroutine buff_1dreal32_inst_accum + end subroutine buff_1dreal32_lst_accum !####################################################################### - subroutine buff_1dreal32_inst_value(this, norm_val, errmsg) - class(hist_buffer_1dreal32_inst_t), intent(inout) :: this - real(REAL32), intent(inout) :: norm_val(:) - character(len=*), optional, intent(out) :: errmsg + subroutine buff_1dreal32_lst_value(this, norm_val, errmsg) + class(hist_buff_1dreal32_lst_t), intent(inout) :: this + real(REAL32), intent(inout) :: norm_val(:) + character(len=*), optional, intent(out) :: errmsg + + if (present(errmsg)) then + errmsg = 'Not implemented' + end if + + end subroutine buff_1dreal32_lst_value + + !####################################################################### + + subroutine init_buff_avg_1dreal32(this, field_in, volume_in, & + horiz_axis_in, shape_in, block_sizes_in, block_ind_in, logger) + use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error + + ! Dummy arguments + class(hist_buff_1dreal32_avg_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + integer, intent(in) :: shape_in(:) + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in + type(hist_log_messages), optional, intent(inout) :: logger + + call init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & + block_sizes_in, block_ind_in, logger=logger) + call this%clear(logger=logger) + this%accum_str = 'average of sampled values' + + end subroutine init_buff_avg_1dreal32 + + !####################################################################### + + subroutine buff_1dreal32_avg_accum(this, field, errmsg) + class(hist_buff_1dreal32_avg_t), intent(inout) :: this + real(REAL32), intent(in) :: field(:) + character(len=*), optional, intent(out) :: errmsg if (present(errmsg)) then errmsg = 'Not implemented' end if + this%num_samples = 1 - end subroutine buff_1dreal32_inst_value + end subroutine buff_1dreal32_avg_accum !####################################################################### - subroutine buff_1dreal64_inst_clear(this) - class(hist_buffer_1dreal64_inst_t), intent(inout) :: this + subroutine buff_1dreal32_avg_value(this, norm_val, errmsg) + class(hist_buff_1dreal32_avg_t), intent(inout) :: this + real(REAL32), intent(inout) :: norm_val(:) + character(len=*), optional, intent(out) :: errmsg + + if (present(errmsg)) then + errmsg = 'Not implemented' + end if + + end subroutine buff_1dreal32_avg_value + + !####################################################################### + + subroutine buff_1dreal64_lst_clear(this, logger) + use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error + + ! Dummy arguments + class(hist_buff_1dreal64_lst_t), intent(inout) :: this + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + integer :: aerr + character(len=*), parameter :: subname = 'buff_1dreal64_lst_clear' this%num_samples = 0 + if (.not. associated(this%data)) then + allocate(this%data(this%field_shape(1)), stat=aerr) + if (aerr /= 0) then + call hist_add_alloc_error('data', __FILE__, __LINE__ - 1, & + subname=subname, errors=logger) + end if + end if + this%data = 0.0_REAL64 - end subroutine buff_1dreal64_inst_clear + end subroutine buff_1dreal64_lst_clear !####################################################################### subroutine init_buff_1dreal64(this, field_in, volume_in, horiz_axis_in, & - shape_in, block_sizes_in, block_ind_in) - class(hist_buffer_1dreal64_inst_t), intent(inout) :: this - class(hist_hashable_t), pointer :: field_in - integer, intent(in) :: volume_in - integer, intent(in) :: horiz_axis_in - integer, intent(in) :: shape_in(:) - integer, optional, intent(in) :: block_sizes_in(:) - integer, optional, intent(in) :: block_ind_in - - call init_buffer(this, field_in, volume_in, horiz_axis_in, & - block_sizes_in, block_ind_in) + shape_in, block_sizes_in, block_ind_in, logger) + use hist_msg_handler, only: hist_log_messages + + class(hist_buff_1dreal64_lst_t), intent(inout) :: this + class(hist_hashable_t), pointer :: field_in + integer, intent(in) :: volume_in + integer, intent(in) :: horiz_axis_in + integer, intent(in) :: shape_in(:) + integer, optional, intent(in) :: block_sizes_in(:) + integer, optional, intent(in) :: block_ind_in + type(hist_log_messages), optional, intent(inout) :: logger + + call init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & + block_sizes_in, block_ind_in, logger=logger) + call this%clear(logger=logger) this%accum_str = 'last sampled value' - allocate(this%data(shape_in(1))) end subroutine init_buff_1dreal64 !####################################################################### - subroutine buff_1dreal64_inst_accum(this, field, errmsg) - class(hist_buffer_1dreal64_inst_t), intent(inout) :: this - real(REAL64), intent(in) :: field(:) - character(len=*), optional, intent(out) :: errmsg + subroutine buff_1dreal64_lst_accum(this, field, errmsg) + class(hist_buff_1dreal64_lst_t), intent(inout) :: this + real(REAL64), intent(in) :: field(:) + character(len=*), optional, intent(out) :: errmsg if (present(errmsg)) then errmsg = 'Not implemented' end if - end subroutine buff_1dreal64_inst_accum + end subroutine buff_1dreal64_lst_accum !####################################################################### - subroutine buff_1dreal64_inst_value(this, norm_val, errmsg) - class(hist_buffer_1dreal64_inst_t), intent(inout) :: this - real(REAL64), intent(inout) :: norm_val(:) - character(len=*), optional, intent(out) :: errmsg + subroutine buff_1dreal64_lst_value(this, norm_val, errmsg) + class(hist_buff_1dreal64_lst_t), intent(inout) :: this + real(REAL64), intent(inout) :: norm_val(:) + character(len=*), optional, intent(out) :: errmsg if (present(errmsg)) then errmsg = 'Not implemented' end if - end subroutine buff_1dreal64_inst_value + end subroutine buff_1dreal64_lst_value !####################################################################### - function buffer_factory(buffer_type) result(newbuf) + function buffer_factory(buffer_type, logger) result(newbuf) ! Create a new buffer based on . ! has a format typekind_rank_accum ! Where: @@ -244,24 +360,40 @@ function buffer_factory(buffer_type) result(newbuf) ! var: Accumulate standard deviation ! min: Accumulate smallest value ! max: Accumulate largest value + + use hist_msg_handler, only: hist_log_messages, ERROR, VERBOSE + use hist_msg_handler, only: hist_add_error + ! Arguments - class(hist_buffer_t), pointer :: newbuf - character(len=*), intent(in) :: buffer_type + class(hist_buffer_t), pointer :: newbuf + character(len=*), intent(in) :: buffer_type + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + character(len=*), parameter :: subname = 'buffer_factory' ! For buffer allocation - type(hist_buffer_1dreal32_inst_t), pointer :: real32_1_in => NULL() - type(hist_buffer_1dreal64_inst_t), pointer :: real64_1_in => NULL() + integer :: aerr + type(hist_buff_1dreal32_lst_t), pointer :: real32_1_in => NULL() + type(hist_buff_1dreal64_lst_t), pointer :: real64_1_in => NULL() nullify(newbuf) ! Create new buffer select case (trim(buffer_type)) case ('real32_1_lst') - allocate(real32_1_in) - newbuf => real32_1_in + allocate(real32_1_in, stat=aerr) + if (aerr == 0) then + newbuf => real32_1_in + end if case ('real64_1_lst') - allocate(real64_1_in) - newbuf => real64_1_in + allocate(real64_1_in, stat=aerr) + if (aerr == 0) then + newbuf => real64_1_in + end if + case default + call hist_add_error(subname, & + "Invalid or unsupported buffer type, '", & + errstr2=trim(buffer_type), errstr3="'", errors=logger) end select end function buffer_factory diff --git a/src/util/hist_msg_handler.F90 b/src/util/hist_msg_handler.F90 new file mode 100644 index 0000000..04f97f0 --- /dev/null +++ b/src/util/hist_msg_handler.F90 @@ -0,0 +1,368 @@ +module hist_msg_handler + + !! Class with methods to collect log and error messages for future handling + !! By default, a object only logs errors + !! Use the to change the logging level + !! The logger stops collecting non-ERROR messages after an error is logged + !! Use the , or methods to + !! record new messages + !! Use the method to write the messages to a Fortran unit + + implicit none + private + + ! Public convenience interfaces + public :: hist_have_error + public :: hist_add_message + public :: hist_add_error + public :: hist_add_alloc_error + + ! Message collection levels + ! A lower value is a higher priority + ! Only collect messages with a level lower or equal to the priority level + integer, public, parameter :: ERROR = 0 + integer, public, parameter :: WARNING = 1 + integer, public, parameter :: INFO = 2 + integer, public, parameter :: VERBOSE = 3 + integer, public, parameter :: DEBUG = 4 + integer, public, parameter :: MAX_LEVEL = DEBUG + + type :: hist_log_entry + integer :: message_level = ERROR + character(len=:), allocatable :: log_message + type(hist_log_entry), pointer :: next => NULL() + contains + final :: finalize_log_entry + end type hist_log_entry + + type, public :: hist_log_messages + integer, private :: length = 0 + integer, private :: error_count = 0 + logical, private :: alloc_error = .false. + integer :: message_level = ERROR + integer, private :: max_len = -1 ! no limit + type(hist_log_entry), private, pointer :: log_messages => NULL() + type(hist_log_entry), private, pointer :: tail => NULL() + contains + procedure :: num_errors => hist_msgs_num_errors + procedure :: num_messages => hist_msgs_num_messages + procedure :: new_error => hist_msgs_new_error + procedure :: new_message => hist_msgs_new_message + procedure :: add_alloc_error => hist_msgs_new_alloc_error + procedure :: add_stack_frame => hist_add_stack_frame + procedure :: output => hist_msgs_print_log + final :: finalize_hist_log_messages + end type hist_log_messages + + character(len=8) :: MSG_HEAD(ERROR:DEBUG) = (/ 'ERROR ', 'WARNING ', & + 'INFO ', 'VERBOSE ', 'DEBUG ' /) + +CONTAINS + + !####################################################################### + + logical function hist_have_error(errors) + ! Return .true. iff is present and contains error messages + type(hist_log_messages), optional, intent(inout) :: errors + + hist_have_error = present(errors) + if (hist_have_error) then + hist_have_error = errors%num_errors() > 0 + end if + end function hist_have_error + + !####################################################################### + + subroutine hist_add_message(subname, msg_level, msgstr1, msgint1, msgstr2, & + msgint2, msgstr3, logger) + ! Record a new error message if is present + character(len=*), intent(in) :: subname + integer, intent(in) :: msg_level + character(len=*), intent(in) :: msgstr1 + integer, optional, intent(in) :: msgint1 + character(len=*), optional, intent(in) :: msgstr2 + integer, optional, intent(in) :: msgint2 + character(len=*), optional, intent(in) :: msgstr3 + type(hist_log_messages), optional, intent(inout) :: logger + + if (present(logger)) then + call logger%new_message(msg_level, msgstr1, msgint1=msgint1, & + msgstr2=msgstr2, msgint2=msgint2, msgstr3=msgstr3, & + subname=subname) + end if + end subroutine hist_add_message + + !####################################################################### + + subroutine hist_add_error(subname, errstr1, errint1, errstr2, errint2, & + errstr3, errors) + ! Record a new error message if is present + character(len=*), intent(in) :: subname + character(len=*), intent(in) :: errstr1 + integer, optional, intent(in) :: errint1 + character(len=*), optional, intent(in) :: errstr2 + integer, optional, intent(in) :: errint2 + character(len=*), optional, intent(in) :: errstr3 + type(hist_log_messages), optional, intent(inout) :: errors + + if (present(errors)) then + call errors%new_error(errstr1, errint1=errint1, errstr2=errstr2, & + errint2=errint2, errstr3=errstr3, subname=subname) + end if + end subroutine hist_add_error + + !####################################################################### + + subroutine hist_add_alloc_error(fieldname, filename, line, subname, errors) + ! Dummy Arguments + character(len=*), intent(in) :: fieldname + character(len=*), intent(in) :: filename + integer, intent(in) :: line + character(len=*), optional, intent(in) :: subname + type(hist_log_messages), optional, intent(inout) :: errors + + if (present(errors)) then + call errors%add_alloc_error(fieldname, filename, line, subname=subname) + end if + end subroutine hist_add_alloc_error + + !####################################################################### + + subroutine finalize_log_entry(this) + ! Dummy Argument + type(hist_log_entry) :: this + ! Local argument + type(hist_log_entry), pointer :: next + + if (allocated(this%log_message)) then + deallocate(this%log_message) + end if + + next => this%next + nullify(this%next) + if (associated(next)) then + deallocate(next) + end if + end subroutine finalize_log_entry + + !####################################################################### + + integer function hist_msgs_num_messages(this) + ! Dummy Argument + class(hist_log_messages) :: this + + hist_msgs_num_messages = this%length + end function hist_msgs_num_messages + + !####################################################################### + + integer function hist_msgs_num_errors(this) + ! Dummy Argument + class(hist_log_messages) :: this + + hist_msgs_num_errors = this%error_count + end function hist_msgs_num_errors + + !####################################################################### + + subroutine hist_msgs_new_error(this, errstr1, & + errint1, errstr2, errint2, errstr3, subname) + ! Dummy Arguments + class(hist_log_messages) :: this + character(len=*), intent(in) :: errstr1 + integer, optional, intent(in) :: errint1 + character(len=*), optional, intent(in) :: errstr2 + integer, optional, intent(in) :: errint2 + character(len=*), optional, intent(in) :: errstr3 + character(len=*), optional, intent(in) :: subname + + call this%new_message(ERROR, errstr1, msgint1=errint1, msgstr2=errstr2, & + msgint2=errint2, msgstr3=errstr3, subname=subname) + this%error_count = this%error_count + 1 + + end subroutine hist_msgs_new_error + + !####################################################################### + + subroutine hist_msgs_new_message(this, msg_level, msgstr1, & + msgint1, msgstr2, msgint2, msgstr3, subname) + ! Dummy Arguments + class(hist_log_messages) :: this + integer, intent(in) :: msg_level + character(len=*), intent(in) :: msgstr1 + integer, optional, intent(in) :: msgint1 + character(len=*), optional, intent(in) :: msgstr2 + integer, optional, intent(in) :: msgint2 + character(len=*), optional, intent(in) :: msgstr3 + character(len=*), optional, intent(in) :: subname + + ! Local variables + integer, parameter :: num_strs = 3 + integer :: aerr + integer :: msg_lvl + integer :: msg_len + type(hist_log_entry), pointer :: new_log_entry + character(len=256) :: msg_strs(num_strs) + character(len=16) :: int_strs(num_strs - 1) + + if ((msg_level < ERROR) .or. (msg_level > MAX_LEVEL)) then + if (present(subname)) then + call hist_add_message(subname, WARNING, 'msg_level, ', & + msgint1=msg_level, msgstr2=' out of range, using ERROR', & + logger=this) + else + call hist_add_message('hist_msgs_new_message', WARNING, & + 'msg_level, ', msgint1=msg_level, & + msgstr2=' out of range, using ERROR', logger=this) + end if + msg_lvl = ERROR + else + msg_lvl = msg_level + end if + ! Only output if message level is appropriate: + ! No errors: check message level + ! Errors: only log errors + if (((this%num_errors() == 0) .and. & + (msg_lvl <= this%message_level)) .or. & + (msg_lvl == ERROR)) then + ! First, calculate the message length + msg_strs(:) = '' + int_strs(:) = '' + msg_len = len_trim(MSG_HEAD(msg_lvl)) + len_trim(msgstr1) + if (present(subname)) then + write(msg_strs(1), '(3a)') " in ", trim(subname), ": " + else + write(msg_strs(1), '(3a)') ": " + end if + msg_len = msg_len + len_trim(msg_strs(1)) + if (present(msgint1)) then + write(int_strs(1), '(i0)') msgint1 + msg_len = msg_len + len_trim(int_strs(1)) + end if + if (present(msgint2)) then + write(int_strs(2), '(i0)') msgint2 + msg_len = msg_len + len_trim(int_strs(2)) + end if + if (present(msgstr2)) then + msg_strs(2) = trim(msgstr2) + msg_len = msg_len + len_trim(msgstr2) + end if + if (present(msgstr3)) then + msg_strs(3) = trim(msgstr3) + msg_len = msg_len + len_trim(msgstr3) + end if + allocate(new_log_entry, stat=aerr) + ! We can't really report an error here so just trap and record + if (aerr == 0) then + allocate(character(len=msg_len) :: new_log_entry%log_message, & + stat=aerr) + if (aerr == 0) then + write(new_log_entry%log_message, '(7a)') & + trim(MSG_HEAD(msg_lvl)), trim(msg_strs(1)), & + trim(msgstr1), trim(int_strs(1)), trim(msg_strs(2)), & + trim(int_strs(2)), trim(msg_strs(3)) + else + ! Hail Mary attempt + new_log_entry%log_message = 'Message allocation error' + this%alloc_error = .true. + end if + if (.not. associated(this%log_messages)) then + this%log_messages => new_log_entry + end if + if (associated(this%tail)) then + this%tail%next => new_log_entry + end if + this%tail => new_log_entry + this%length = this%length + 1 + else + this%alloc_error = .true. + end if + end if + end subroutine hist_msgs_new_message + + !####################################################################### + + subroutine hist_add_stack_frame(this, msg_level, filename, line, subname) + ! Dummy Arguments + class(hist_log_messages) :: this + integer, intent(in) :: msg_level + character(len=*), intent(in) :: filename + integer, intent(in) :: line + character(len=*), optional, intent(in) :: subname + + if (present(subname)) then + call this%new_message(msg_level, & + 'called from '//trim(subname)//' @'//trim(filename)//':', & + msgint1=line) + else + call this%new_message(msg_level, 'called from '//trim(filename)//':', & + msgint1=line) + end if + + end subroutine hist_add_stack_frame + + !####################################################################### + + subroutine hist_msgs_new_alloc_error(this, fieldname, filename, line, & + subname) + ! Dummy Arguments + class(hist_log_messages) :: this + character(len=*), intent(in) :: fieldname + character(len=*), intent(in) :: filename + integer, intent(in) :: line + character(len=*), optional, intent(in) :: subname + + if (present(subname)) then + call this%new_message(ERROR, & + "Unable to allocate '"//trim(fieldname)//"'", & + msgstr2=' at '//trim(subname)//' @'//trim(filename)//':', & + msgint2=line) + else + call this%new_message(ERROR, & + "Unable to allocate '"//trim(fieldname)//"'", & + msgstr2=' at '//trim(filename)//':', msgint2=line) + end if + end subroutine hist_msgs_new_alloc_error + + !####################################################################### + + subroutine hist_msgs_print_log(this, unit, header) + ! Dummy Arguments + class(hist_log_messages) :: this + integer, intent(in) :: unit + character(len=*), optional, intent(in) :: header + + type(hist_log_entry), pointer :: msg_ptr + + msg_ptr => this%log_messages + if (present(header) .and. (this%num_errors() > 0)) then + write(unit, '(a)') trim(header) + end if + if (this%alloc_error) then + write(unit, '(a)') 'ERROR: Allocation error in error-handling system' + end if + do + if (associated(msg_ptr)) then + write(unit, '(a)') trim(msg_ptr%log_message) + msg_ptr => msg_ptr%next + else + exit + end if + end do + end subroutine hist_msgs_print_log + + !####################################################################### + + subroutine finalize_hist_log_messages(this) + ! Dummy Argument + type(hist_log_messages) :: this + + if (associated(this%log_messages)) then + deallocate(this%log_messages) + end if + nullify(this%log_messages) + nullify(this%tail) + this%length = 0 + end subroutine finalize_hist_log_messages + +end module hist_msg_handler diff --git a/src/util/hist_utils.F90 b/src/util/hist_utils.F90 deleted file mode 100644 index a7eb5b7..0000000 --- a/src/util/hist_utils.F90 +++ /dev/null @@ -1,147 +0,0 @@ -module hist_utils - - implicit none - private - - type :: hist_error_entry - character(len=:), allocatable :: error_message - type(hist_error_entry), pointer :: next => NULL() - contains - final :: finalize_error_entry - end type hist_error_entry - - type, public :: hist_error_messages - integer, private :: length = 0 - logical, private :: alloc_error = .false. - integer, private :: max_len = -1 ! no limit - type(hist_error_entry), private, pointer :: error_messages => NULL() - type(hist_error_entry), private, pointer :: tail => NULL() - contains - procedure :: num_errors => hist_utils_num_errors - procedure :: new_error => hist_utils_new_error - procedure :: output => hist_utils_print_errors - final :: finalize_hist_error_messages - end type hist_error_messages - -CONTAINS - - !####################################################################### - - subroutine finalize_error_entry(this) - ! Dummy Argument - type(hist_error_entry) :: this - ! Local argument - type(hist_error_entry), pointer :: next - - if (allocated(this%error_message)) then - deallocate(this%error_message) - end if - - next => this%next - nullify(this%next) - if (associated(next)) then - deallocate(next) - end if - end subroutine finalize_error_entry - - !####################################################################### - - integer function hist_utils_num_errors(this) - ! Dummy Argument - class(hist_error_messages) :: this - - hist_utils_num_errors = this%length - end function hist_utils_num_errors - - !####################################################################### - - subroutine hist_utils_new_error(this, errstr1, errint1, errstr2, errint2, & - errstr3) - ! Dummy Arguments - class(hist_error_messages) :: this - character(len=*), intent(in) :: errstr1 - integer, optional, intent(in) :: errint1 - character(len=*), optional, intent(in) :: errstr2 - integer, optional, intent(in) :: errint2 - character(len=*), optional, intent(in) :: errstr3 - - ! Local variables - integer :: aerr - type(hist_error_entry), pointer :: new_error - character(len=:), allocatable :: temp_str - character(len=8) :: intstr - - temp_str = trim(errstr1) - if (present(errint1)) then - write(intstr, '(i0)') errint1 - temp_str = trim(temp_str)//intstr - end if - if (present(errstr2)) then - temp_str = trim(temp_str)//errstr2 - end if - if (present(errint2)) then - write(intstr, '(i0)') errint2 - temp_str = trim(temp_str)//intstr - end if - if (present(errstr3)) then - temp_str = trim(temp_str)//errstr3 - end if - allocate(new_error, stat=aerr) - ! We can't really report an error here so just trap and record - if (aerr == 0) then - new_error%error_message = trim(temp_str) - if (.not. associated(this%error_messages)) then - this%error_messages => new_error - end if - if (associated(this%tail)) then - this%tail%next => new_error - end if - this%tail => new_error - this%length = this%length + 1 - else - this%alloc_error = .true. - end if - end subroutine hist_utils_new_error - - !####################################################################### - - subroutine hist_utils_print_errors(this, unit, header) - ! Dummy Arguments - class(hist_error_messages) :: this - integer, intent(in) :: unit - character(len=*), optional, intent(in) :: header - - type(hist_error_entry), pointer :: err_ptr - - err_ptr => this%error_messages - if (present(header) .and. (this%num_errors() > 0)) then - write(unit, '(a)') trim(header) - end if - if (this%alloc_error) then - write(unit, '(a)') 'ERROR: Allocation error in error-handling system' - end if - do - if (associated(err_ptr)) then - write(unit, '(a)') trim(err_ptr%error_message) - err_ptr => err_ptr%next - else - exit - end if - end do - end subroutine hist_utils_print_errors - - !####################################################################### - - subroutine finalize_hist_error_messages(this) - ! Dummy Argument - type(hist_error_messages) :: this - - if (associated(this%error_messages)) then - deallocate(this%error_messages) - end if - nullify(this%error_messages) - nullify(this%tail) - this%length = 0 - end subroutine finalize_hist_error_messages - -end module hist_utils diff --git a/test/Makefile b/test/Makefile index acdd5f0..f77cc20 100644 --- a/test/Makefile +++ b/test/Makefile @@ -18,7 +18,7 @@ endif # TARGETS -hist_utils.o: $(UTILPATH)/hist_utils.F90 +hist_msg_handler.o: $(UTILPATH)/hist_msg_handler.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) @@ -30,7 +30,7 @@ hist_hash_table.o: $(HASHPATH)/hist_hash_table.F90 @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) -hist_buffer.o: $(SRCPATH)/hist_buffer.F90 hist_hashable.o hist_utils.o +hist_buffer.o: $(SRCPATH)/hist_buffer.F90 hist_hashable.o hist_msg_handler.o @echo "${FC} -c ${FCFLAGS} ${INCPATH} $^" 2>&1 >> $(LOGFILE) @${FC} -c ${FCFLAGS} ${INCPATH} $^ 2>&1 >> $(LOGFILE) @@ -46,7 +46,7 @@ test_hash_table: test_hash.F90 $(HASHOBJS) @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) -test_hist_buffer: test_hist_buffer.F90 $(HISTOBJS) hist_hashable.o hist_utils.o +test_hist_buffer: test_hist_buffer.F90 $(HISTOBJS) hist_hashable.o hist_msg_handler.o @echo "${FC} ${FCFLAGS} ${INCPATH} -o $@ $^" 2>&1 >> $(LOGFILE) @${FC} ${FCFLAGS} ${INCPATH} -o $@ $^ 2>&1 >> $(LOGFILE) diff --git a/test/test_hist_buffer.F90 b/test/test_hist_buffer.F90 index 01b6809..f4df67b 100644 --- a/test/test_hist_buffer.F90 +++ b/test/test_hist_buffer.F90 @@ -1,14 +1,14 @@ program test_hist_buffer - use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 - use hist_utils, only: hist_error_messages - use hist_buffer, only: hist_buffer_t - use hist_field, only: hist_field_info_t, hist_get_field - use hist_api, only: hist_new_field, hist_new_buffer + use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 + use hist_msg_handler, only: hist_log_messages + use hist_buffer, only: hist_buffer_t + use hist_field, only: hist_field_info_t, hist_get_field + use hist_api, only: hist_new_field, hist_new_buffer class(hist_field_info_t), pointer :: my_fields => NULL() class(hist_buffer_t), pointer :: buffer => NULL() class(hist_buffer_t), pointer :: buff_ptr => NULL() - type(hist_error_messages) :: errors + type(hist_log_messages) :: errors my_fields => hist_new_field('U', 'eastward_wind', 'Meridional Wind', & 'm s-1', 'real', errors=errors) From 24fa73708ccf0e745741622f3ddf59e967a44db7 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Tue, 29 Sep 2020 21:51:54 -0600 Subject: [PATCH 12/16] Buffer tests pass for last 32bit and 64bit as well as average 32bit --- src/hist_api.F90 | 174 ++++++++++++++++-- src/hist_buffer.F90 | 335 +++++++++++++++++++++++++++------- src/hist_field.F90 | 6 +- src/util/hist_msg_handler.F90 | 6 +- test/test_hist_buffer.F90 | 123 ++++++++++++- 5 files changed, 552 insertions(+), 92 deletions(-) diff --git a/src/hist_api.F90 b/src/hist_api.F90 index 863180f..20f78fa 100644 --- a/src/hist_api.F90 +++ b/src/hist_api.F90 @@ -1,11 +1,11 @@ module hist_api - use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 - use hist_hashable, only: hist_hashable_t - use hist_field, only: hist_field_info_t - use hist_buffer, only: hist_buffer_t, buffer_factory - use hist_buffer, only: hist_buff_1dreal32_lst_t - use hist_buffer, only: hist_buff_1dreal64_lst_t + use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 + use hist_hashable, only: hist_hashable_t + use hist_field, only: hist_field_info_t + use hist_buffer, only: hist_buffer_t, buffer_factory + use hist_buffer, only: hist_buff_1dreal32_lst_t + use hist_buffer, only: hist_buff_1dreal64_lst_t implicit none private @@ -14,7 +14,7 @@ module hist_api public :: hist_new_field ! Allocate a hist_field_info_t object public :: hist_new_buffer ! Create a new field buffer public :: hist_buffer_accumulate ! Accumulate a new field state -! public :: hist_buffer_norm_value ! Return current normalized field state + public :: hist_buffer_norm_value ! Return current normalized field state ! public :: hist_buffer_clear ! Clear buffer accumulation state ! public :: hist_buffer_accum_type ! String value of accumulation type @@ -24,10 +24,10 @@ module hist_api module procedure hist_buffer_accumulate_1dreal64 end interface hist_buffer_accumulate -! interface hist_buffer_norm_value -! module procedure hist_buffer_norm_value_1dreal32 -! module procedure hist_buffer_norm_value_1dreal64 -! end interface hist_buffer_norm_value + interface hist_buffer_norm_value + module procedure hist_buffer_norm_value_1dreal32 + module procedure hist_buffer_norm_value_1dreal64 + end interface hist_buffer_norm_value CONTAINS @@ -220,16 +220,156 @@ end subroutine hist_new_buffer !####################################################################### - subroutine hist_buffer_accumulate_1dreal32(buffer, field) - class(hist_buff_1dreal32_lst_t), intent(inout) :: buffer - real(REAL32), intent(in) :: field(:) + subroutine hist_buffer_accumulate_1dreal32(buffer, field, cols_or_block, & + cole, logger) + use hist_msg_handler, only: hist_log_messages, hist_add_error + + ! Dummy arguments + class(hist_buffer_t), target, intent(inout) :: buffer + real(REAL32), intent(in) :: field(:) + integer, intent(in) :: cols_or_block + integer, optional, intent(in) :: cole + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + class(hist_buff_1dreal32_lst_t), pointer :: buff32 + class(hist_buff_1dreal64_lst_t), pointer :: buff64 + character(len=:), allocatable :: buff_typestr + character(len=*), parameter :: subname = 'hist_buffer_accumulate_1dreal32' + + select type(buffer) + class is (hist_buff_1dreal32_lst_t) + buff32 => buffer + call buff32%accumulate(field, cols_or_block, cole, logger) + class is (hist_buff_1dreal64_lst_t) + ! Do we want to accumulate 32bit data into 64bit buffers? + buff64 => buffer + call buff64%accumulate(real(field, REAL64), cols_or_block, cole, & + logger) + class default + buff_typestr = buffer%buffer_type() + call hist_add_error(subname, "unsupported buffer type, '", & + errstr2=buff_typestr, errstr3="'", errors=logger) + end select + end subroutine hist_buffer_accumulate_1dreal32 !####################################################################### - subroutine hist_buffer_accumulate_1dreal64(buffer, field) - class(hist_buff_1dreal64_lst_t), intent(inout) :: buffer - real(REAL64), intent(in) :: field(:) + subroutine hist_buffer_accumulate_1dreal64(buffer, field, cols_or_block, & + cole, logger) + use hist_msg_handler, only: hist_log_messages, hist_add_error + + ! Dummy arguments + class(hist_buffer_t), target, intent(inout) :: buffer + real(REAL64), intent(in) :: field(:) + integer, intent(in) :: cols_or_block + integer, optional, intent(in) :: cole + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + type(hist_buff_1dreal32_lst_t), pointer :: buff32 + type(hist_buff_1dreal64_lst_t), pointer :: buff64 + character(len=:), allocatable :: buff_typestr + character(len=*), parameter :: subname = 'hist_buffer_accumulate_1dreal64' + + select type(buffer) + class is (hist_buff_1dreal32_lst_t) + ! Squeeze 64 bit data into 32 bit buffers + buff32 => buffer + call buff32%accumulate(real(field, REAL32), cols_or_block, cole, & + logger) + class is (hist_buff_1dreal64_lst_t) + buff64 => buffer + call buff64%accumulate(field, cols_or_block, cole, logger) + class default + buff_typestr = buffer%buffer_type() + call hist_add_error(subname, "unsupported buffer type, '", & + errstr2=buff_typestr, errstr3="'", errors=logger) + end select + end subroutine hist_buffer_accumulate_1dreal64 + !####################################################################### + + subroutine hist_buffer_norm_value_1dreal32(buffer, norm_val, default_val, & + logger) + use hist_msg_handler, only: hist_log_messages, hist_add_error + + ! Dummy arguments + class(hist_buffer_t), target, intent(inout) :: buffer + real(REAL32), intent(inout) :: norm_val(:) + real(REAL32), optional, intent(in) :: default_val + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + class(hist_buff_1dreal32_lst_t), pointer :: buff32 + class(hist_buff_1dreal64_lst_t), pointer :: buff64 + real(REAL64), allocatable :: norm_val64(:) + character(len=:), allocatable :: buff_typestr + character(len=*), parameter :: subname = 'hist_buffer_norm_value_1dreal32' + + select type(buffer) + class is (hist_buff_1dreal32_lst_t) + buff32 => buffer + call buff32%norm_value(norm_val, default_val=default_val, & + logger=logger) + class is (hist_buff_1dreal64_lst_t) + ! Truncate 64bit buffer into 32bit output + buff64 => buffer + allocate(norm_val64(size(norm_val, 1))) + if (present(default_val)) then + call buff64%norm_value(norm_val64, & + default_val=REAL(default_val, REAL64), logger=logger) + else + call buff64%norm_value(norm_val64, logger=logger) + end if + norm_val(:) = REAL(norm_val64(:), REAL32) + class default + buff_typestr = buffer%buffer_type() + call hist_add_error(subname, "unsupported buffer type, '", & + errstr2=buff_typestr, errstr3="'", errors=logger) + end select + + end subroutine hist_buffer_norm_value_1dreal32 + + !####################################################################### + + subroutine hist_buffer_norm_value_1dreal64(buffer, norm_val, default_val, & + logger) + use hist_msg_handler, only: hist_log_messages, hist_add_error + + ! Dummy arguments + class(hist_buffer_t), target, intent(inout) :: buffer + real(REAL64), intent(inout) :: norm_val(:) + real(REAL64), optional, intent(in) :: default_val + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + type(hist_buff_1dreal32_lst_t), pointer :: buff32 + type(hist_buff_1dreal64_lst_t), pointer :: buff64 + real(REAL32), allocatable :: norm_val32(:) + character(len=:), allocatable :: buff_typestr + character(len=*), parameter :: subname = 'hist_buffer_norm_value_1dreal64' + + select type(buffer) + class is (hist_buff_1dreal32_lst_t) + ! Do we want to read out 32bit buffers into 64bit data? + buff32 => buffer + allocate(norm_val32(size(norm_val, 1))) + if (present(default_val)) then + call buffer%norm_value(norm_val32, & + default_val=REAL(default_val, REAL32), logger=logger) + else + call buffer%norm_value(norm_val32, logger=logger) + end if + norm_val(:) = REAL(norm_val32(:), REAL64) + class is (hist_buff_1dreal64_lst_t) + buff64 => buffer + call buffer%norm_value(norm_val, default_val=default_val, & + logger=logger) + class default + buff_typestr = buffer%buffer_type() + call hist_add_error(subname, "unsupported buffer type, '", & + errstr2=buff_typestr, errstr3="'", errors=logger) + end select + + end subroutine hist_buffer_norm_value_1dreal64 + end module hist_api diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 index 81f4416..b8eb086 100644 --- a/src/hist_buffer.F90 +++ b/src/hist_buffer.F90 @@ -16,18 +16,25 @@ module hist_buffer class(hist_hashable_t), pointer :: field_info => NULL() integer, private :: vol = -1 ! For host output integer, private :: horiz_axis_ind = 0 + integer, private :: rank = 0 integer, allocatable, private :: field_shape(:) integer, allocatable, private :: num_samples(:) + integer, allocatable, private :: block_begs(:) + integer, allocatable, private :: block_ends(:) character(len=:), allocatable, private :: accum_str - class(hist_buffer_t), pointer :: next + character(len=:), allocatable, private :: buff_type + class(hist_buffer_t), pointer :: next => NULL() contains - procedure :: field => get_field_info - procedure :: volume => get_volume - procedure :: horiz_axis_index - procedure :: init_buffer - procedure :: accum_string - procedure(hist_buff_init), deferred :: initialize - procedure(hist_buff_sub_log), deferred :: clear + procedure :: field => get_field_info + procedure :: volume => get_volume + procedure :: horiz_axis_index + procedure :: init_buffer + procedure :: accum_string + procedure :: buffer_type + procedure :: clear => hist_buff_clear + procedure :: check_status + procedure :: has_blocks + procedure(hist_buff_init), deferred :: initialize end type hist_buffer_t type, public, extends(hist_buffer_t) :: hist_buff_1dreal32_lst_t @@ -108,12 +115,68 @@ integer function horiz_axis_index(this) horiz_axis_index = this%horiz_axis_ind end function horiz_axis_index + !####################################################################### + + logical function check_status(this, logger, filename, line) + ! Check to see if this buffer is properly initialized + use hist_msg_handler, only: hist_log_messages, hist_add_error, ERROR + + ! Dummy arguments + class(hist_buffer_t), intent(inout) :: this + type(hist_log_messages), optional, intent(inout) :: logger + character(len=*), optional, intent(in) :: filename + integer, optional, intent(in) :: line + ! Local variable + character(len=*), parameter :: subname = 'check_status' + + check_status = .true. + if ( (this%horiz_axis_index() < 1) .or. & + (.not. allocated(this%field_shape)) .or. & + (.not. allocated(this%num_samples))) then + check_status = .false. + call hist_add_error(subname, & + "buffer not properly initialized '", errors=logger) + if (present(filename) .and. present(line) .and. present(logger)) then + call logger%add_stack_frame(ERROR, filename, line) + end if + end if + + end function check_status + + !####################################################################### + + logical function has_blocks(this) + ! Dummy argument + class(hist_buffer_t), intent(inout) :: this + + has_blocks = allocated(this%block_begs) .and. allocated(this%block_ends) + + end function has_blocks + + !####################################################################### + + subroutine hist_buff_clear(this, logger) + use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error + + ! Dummy arguments + class(hist_buffer_t), intent(inout) :: this + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + integer :: aerr + character(len=*), parameter :: subname = 'hist_buff_clear' + + if (this%check_status(logger, __FILE__, __LINE__ + 1)) then + this%num_samples = 0 + end if + + end subroutine hist_buff_clear !####################################################################### subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & block_sizes_in, block_ind_in, logger) use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error + use hist_msg_handler, only: hist_add_error ! Dummy arguments class(hist_buffer_t), intent(inout) :: this @@ -126,17 +189,33 @@ subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & type(hist_log_messages), optional, intent(inout) :: logger ! Local variables integer :: astat + integer :: hsize character(len=*), parameter :: subname = 'init_buffer' - this%field_info => field_in - this%vol = volume_in - this%horiz_axis_ind = horiz_axis_in - allocate(this%field_shape(size(shape_in, 1)), stat=astat) - if (astat == 0) then - this%field_shape(:) = shape_in(:) + ! Sanity check + this%rank = SIZE(shape_in, 1) + if ((horiz_axis_in < 1) .or. (horiz_axis_in > this%rank)) then + call hist_add_error(subname, & + "horiz_axis_in must be between 1 and '", errint1=this%rank, & + errors=logger) else - call hist_add_alloc_error('field_shape', __FILE__, __LINE__ - 4, & - subname=subname, errors=logger) + this%field_info => field_in + this%vol = volume_in + this%horiz_axis_ind = horiz_axis_in + allocate(this%field_shape(size(shape_in, 1)), stat=astat) + if (astat == 0) then + this%field_shape(:) = shape_in(:) + else + call hist_add_alloc_error('field_shape', __FILE__, __LINE__ - 4, & + subname=subname, errors=logger) + end if + ! Allocate num_samples + hsize = this%field_shape(this%horiz_axis_ind) + allocate(this%num_samples(hsize), stat=astat) + if (astat /= 0) then + call hist_add_alloc_error('num_samples', __FILE__, __LINE__ - 2, & + subname=subname, errors=logger) + end if end if end subroutine init_buffer @@ -151,6 +230,20 @@ end function accum_string !####################################################################### + function buffer_type(this) result(bt_str) + class(hist_buffer_t), intent(in) :: this + character(len=:), allocatable :: bt_str + + if (allocated(this%buff_type)) then + bt_str = this%buff_type + else + bt_str = 'UNKNOWN' + end if + + end function buffer_type + + !####################################################################### + subroutine buff_1dreal32_lst_clear(this, logger) use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error @@ -161,7 +254,7 @@ subroutine buff_1dreal32_lst_clear(this, logger) integer :: aerr character(len=*), parameter :: subname = 'buff_1dreal32_lst_clear' - this%num_samples = 0 + call hist_buff_clear(this, logger) if (.not. associated(this%data)) then allocate(this%data(this%field_shape(1)), stat=aerr) if (aerr /= 0) then @@ -193,33 +286,66 @@ subroutine init_buff_1dreal32(this, field_in, volume_in, horiz_axis_in, & block_sizes_in, block_ind_in, logger=logger) call this%clear(logger=logger) this%accum_str = 'last sampled value' + this%buff_type = 'hist_buff_1dreal32_lst_t' end subroutine init_buff_1dreal32 !####################################################################### - subroutine buff_1dreal32_lst_accum(this, field, errmsg) - class(hist_buff_1dreal32_lst_t), intent(inout) :: this - real(REAL32), intent(in) :: field(:) - character(len=*), optional, intent(out) :: errmsg + subroutine buff_1dreal32_lst_accum(this, field, cols_or_block, cole, logger) + use hist_msg_handler, only: hist_log_messages + ! Dummy arguments + class(hist_buff_1dreal32_lst_t), intent(inout) :: this + real(REAL32), intent(in) :: field(:) + integer, intent(in) :: cols_or_block + integer, optional, intent(in) :: cole + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + integer :: col_beg_use + integer :: col_end_use - if (present(errmsg)) then - errmsg = 'Not implemented' + if (this%has_blocks()) then + ! For a blocked field, is a block index + col_beg_use = this%block_begs(cols_or_block) + col_end_use = this%block_ends(cols_or_block) + else + ! Non blocked, is the first column index + col_beg_use = cols_or_block + if (present(cole)) then + col_end_use = cole + else + col_end_use = col_beg_use + & + this%field_shape(this%horiz_axis_ind) - 1 + end if end if - this%num_samples = 1 + + this%data(col_beg_use:col_end_use) = field(:) + this%num_samples(col_beg_use:col_end_use) = 1 end subroutine buff_1dreal32_lst_accum !####################################################################### - subroutine buff_1dreal32_lst_value(this, norm_val, errmsg) - class(hist_buff_1dreal32_lst_t), intent(inout) :: this - real(REAL32), intent(inout) :: norm_val(:) - character(len=*), optional, intent(out) :: errmsg + subroutine buff_1dreal32_lst_value(this, norm_val, default_val, logger) + use hist_msg_handler, only: hist_log_messages - if (present(errmsg)) then - errmsg = 'Not implemented' - end if + ! Dummy arguments + class(hist_buff_1dreal32_lst_t), intent(inout) :: this + real(REAL32), intent(inout) :: norm_val(:) + real(REAL32), optional, intent(in) :: default_val + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variable + integer :: ind1 + integer :: nacc + + do ind1 = 1, this%field_shape(1) + nacc = this%num_samples(ind1) + if (nacc > 0) then + norm_val(ind1) = this%data(ind1) + else if (present(default_val)) then + norm_val(ind1) = default_val + end if + end do end subroutine buff_1dreal32_lst_value @@ -243,33 +369,69 @@ subroutine init_buff_avg_1dreal32(this, field_in, volume_in, & block_sizes_in, block_ind_in, logger=logger) call this%clear(logger=logger) this%accum_str = 'average of sampled values' + this%buff_type = 'hist_buff_1dreal32_avg_t' end subroutine init_buff_avg_1dreal32 !####################################################################### - subroutine buff_1dreal32_avg_accum(this, field, errmsg) - class(hist_buff_1dreal32_avg_t), intent(inout) :: this - real(REAL32), intent(in) :: field(:) - character(len=*), optional, intent(out) :: errmsg - - if (present(errmsg)) then - errmsg = 'Not implemented' + subroutine buff_1dreal32_avg_accum(this, field, cols_or_block, cole, logger) + use hist_msg_handler, only: hist_log_messages + ! Dummy arguments + class(hist_buff_1dreal32_avg_t), intent(inout) :: this + real(REAL32), intent(in) :: field(:) + integer, intent(in) :: cols_or_block + integer, optional, intent(in) :: cole + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + integer :: col_beg_use + integer :: col_end_use + integer :: index + + if (this%has_blocks()) then + ! For a blocked field, is a block index + col_beg_use = this%block_begs(cols_or_block) + col_end_use = this%block_ends(cols_or_block) + else + ! Non blocked, is the first column index + col_beg_use = cols_or_block + if (present(cole)) then + col_end_use = cole + else + col_end_use = col_beg_use + & + this%field_shape(this%horiz_axis_ind) - 1 + end if end if - this%num_samples = 1 + + do index = col_beg_use, col_end_use + this%data(index) = this%data(index) + field(index - col_beg_use + 1) + this%num_samples(index) = this%num_samples(index) + 1 + end do end subroutine buff_1dreal32_avg_accum !####################################################################### - subroutine buff_1dreal32_avg_value(this, norm_val, errmsg) - class(hist_buff_1dreal32_avg_t), intent(inout) :: this - real(REAL32), intent(inout) :: norm_val(:) - character(len=*), optional, intent(out) :: errmsg + subroutine buff_1dreal32_avg_value(this, norm_val, default_val, logger) + use hist_msg_handler, only: hist_log_messages - if (present(errmsg)) then - errmsg = 'Not implemented' - end if + ! Dummy arguments + class(hist_buff_1dreal32_avg_t), intent(inout) :: this + real(REAL32), intent(inout) :: norm_val(:) + real(REAL32), optional, intent(in) :: default_val + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variable + integer :: ind1 + integer :: nacc + + do ind1 = 1, this%field_shape(1) + nacc = this%num_samples(ind1) + if (nacc > 0) then + norm_val(ind1) = this%data(ind1) / real(nacc, REAL32) + else if (present(default_val)) then + norm_val(ind1) = default_val + end if + end do end subroutine buff_1dreal32_avg_value @@ -285,7 +447,7 @@ subroutine buff_1dreal64_lst_clear(this, logger) integer :: aerr character(len=*), parameter :: subname = 'buff_1dreal64_lst_clear' - this%num_samples = 0 + call hist_buff_clear(this, logger) if (.not. associated(this%data)) then allocate(this%data(this%field_shape(1)), stat=aerr) if (aerr /= 0) then @@ -316,32 +478,67 @@ subroutine init_buff_1dreal64(this, field_in, volume_in, horiz_axis_in, & block_sizes_in, block_ind_in, logger=logger) call this%clear(logger=logger) this%accum_str = 'last sampled value' + this%buff_type = 'hist_buff_1dreal64_lst_t' end subroutine init_buff_1dreal64 !####################################################################### - subroutine buff_1dreal64_lst_accum(this, field, errmsg) - class(hist_buff_1dreal64_lst_t), intent(inout) :: this - real(REAL64), intent(in) :: field(:) - character(len=*), optional, intent(out) :: errmsg + subroutine buff_1dreal64_lst_accum(this, field, cols_or_block, cole, logger) + use hist_msg_handler, only: hist_log_messages + ! Dummy arguments + class(hist_buff_1dreal64_lst_t), intent(inout) :: this + real(REAL64), intent(in) :: field(:) + integer, intent(in) :: cols_or_block + integer, optional, intent(in) :: cole + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + integer :: col_beg_use + integer :: col_end_use - if (present(errmsg)) then - errmsg = 'Not implemented' + if (this%has_blocks()) then + ! For a blocked field, is a block index + col_beg_use = this%block_begs(cols_or_block) + col_end_use = this%block_ends(cols_or_block) + else + ! Non blocked, is the first column index + col_beg_use = cols_or_block + if (present(cole)) then + col_end_use = cole + else + col_end_use = col_beg_use + & + this%field_shape(this%horiz_axis_ind) - 1 + end if end if + this%data(col_beg_use:col_end_use) = field(:) + this%num_samples(col_beg_use:col_end_use) = 1 + end subroutine buff_1dreal64_lst_accum !####################################################################### - subroutine buff_1dreal64_lst_value(this, norm_val, errmsg) - class(hist_buff_1dreal64_lst_t), intent(inout) :: this - real(REAL64), intent(inout) :: norm_val(:) - character(len=*), optional, intent(out) :: errmsg + subroutine buff_1dreal64_lst_value(this, norm_val, default_val, logger) + use hist_msg_handler, only: hist_log_messages, ERROR, VERBOSE + ! Dummy arguments + class(hist_buff_1dreal64_lst_t), intent(inout) :: this + real(REAL64), intent(inout) :: norm_val(:) + real(REAL64), optional, intent(in) :: default_val + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variable + integer :: ind1 + integer :: nacc + + do ind1 = 1, this%field_shape(1) + nacc = this%num_samples(ind1) + if (nacc > 0) then + norm_val(ind1) = this%data(ind1) + else if (present(default_val)) then + norm_val(ind1) = default_val + end if + end do - if (present(errmsg)) then - errmsg = 'Not implemented' - end if + norm_val(:) = this%data(:) end subroutine buff_1dreal64_lst_value @@ -373,22 +570,28 @@ function buffer_factory(buffer_type, logger) result(newbuf) character(len=*), parameter :: subname = 'buffer_factory' ! For buffer allocation integer :: aerr - type(hist_buff_1dreal32_lst_t), pointer :: real32_1_in => NULL() - type(hist_buff_1dreal64_lst_t), pointer :: real64_1_in => NULL() + type(hist_buff_1dreal32_lst_t), pointer :: real32_1_lst => NULL() + type(hist_buff_1dreal32_avg_t), pointer :: real32_1_avg => NULL() + type(hist_buff_1dreal64_lst_t), pointer :: real64_1_lst => NULL() nullify(newbuf) ! Create new buffer select case (trim(buffer_type)) case ('real32_1_lst') - allocate(real32_1_in, stat=aerr) + allocate(real32_1_lst, stat=aerr) + if (aerr == 0) then + newbuf => real32_1_lst + end if + case ('real32_1_avg') + allocate(real32_1_avg, stat=aerr) if (aerr == 0) then - newbuf => real32_1_in + newbuf => real32_1_avg end if case ('real64_1_lst') - allocate(real64_1_in, stat=aerr) + allocate(real64_1_lst, stat=aerr) if (aerr == 0) then - newbuf => real64_1_in + newbuf => real64_1_lst end if case default call hist_add_error(subname, & diff --git a/src/hist_field.F90 b/src/hist_field.F90 index 6f246ef..5b64838 100644 --- a/src/hist_field.F90 +++ b/src/hist_field.F90 @@ -75,9 +75,9 @@ subroutine hist_get_field(buffer, field) nullify(field) select type(finfo => buffer%field_info) - type is (hist_field_info_t) - field => finfo - end select + type is (hist_field_info_t) + field => finfo + end select end subroutine hist_get_field !####################################################################### diff --git a/src/util/hist_msg_handler.F90 b/src/util/hist_msg_handler.F90 index 04f97f0..6f229e7 100644 --- a/src/util/hist_msg_handler.F90 +++ b/src/util/hist_msg_handler.F90 @@ -234,7 +234,7 @@ subroutine hist_msgs_new_message(this, msg_level, msgstr1, & else write(msg_strs(1), '(3a)') ": " end if - msg_len = msg_len + len_trim(msg_strs(1)) + msg_len = msg_len + len_trim(msg_strs(1)) + 1 ! Keep space at end if (present(msgint1)) then write(int_strs(1), '(i0)') msgint1 msg_len = msg_len + len_trim(int_strs(1)) @@ -257,8 +257,8 @@ subroutine hist_msgs_new_message(this, msg_level, msgstr1, & allocate(character(len=msg_len) :: new_log_entry%log_message, & stat=aerr) if (aerr == 0) then - write(new_log_entry%log_message, '(7a)') & - trim(MSG_HEAD(msg_lvl)), trim(msg_strs(1)), & + write(new_log_entry%log_message, '(8a)') & + trim(MSG_HEAD(msg_lvl)), trim(msg_strs(1)), ' ', & trim(msgstr1), trim(int_strs(1)), trim(msg_strs(2)), & trim(int_strs(2)), trim(msg_strs(3)) else diff --git a/test/test_hist_buffer.F90 b/test/test_hist_buffer.F90 index f4df67b..ebc120a 100644 --- a/test/test_hist_buffer.F90 +++ b/test/test_hist_buffer.F90 @@ -1,19 +1,136 @@ program test_hist_buffer use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 - use hist_msg_handler, only: hist_log_messages + use hist_msg_handler, only: hist_log_messages, hist_add_error + use hist_msg_handler, only: ERROR, VERBOSE use hist_buffer, only: hist_buffer_t use hist_field, only: hist_field_info_t, hist_get_field use hist_api, only: hist_new_field, hist_new_buffer + use hist_api, only: hist_buffer_accumulate, hist_buffer_norm_value + implicit none + + integer :: index + integer, parameter :: num_cols = 5 + character(len=256) :: errmsg class(hist_field_info_t), pointer :: my_fields => NULL() + class(hist_field_info_t), pointer :: fld_ptr => NULL() class(hist_buffer_t), pointer :: buffer => NULL() class(hist_buffer_t), pointer :: buff_ptr => NULL() type(hist_log_messages) :: errors + real(REAL32) :: field32(5), test_val32 + real(REAL64) :: field64(5), test_val64 + errors%message_level = VERBOSE + ! Create some fields and some buffers my_fields => hist_new_field('U', 'eastward_wind', 'Meridional Wind', & 'm s-1', 'real', errors=errors) - call hist_new_buffer(my_fields, (/ 5 /), REAL32, 1, 'lst', 1, buffer, & - errors=errors) + my_fields%next => hist_new_field('T', 'temperature', 'Temperature', & + 'K', 'real', errors=errors) + fld_ptr => my_fields%next + call hist_new_buffer(my_fields, (/ num_cols /), REAL32, 1, 'lst', 1, & + buffer, errors=errors) + call hist_new_buffer(my_fields, (/ num_cols /), REAL32, 1, 'avg', 1, & + buffer, errors=errors) + call hist_new_buffer(fld_ptr, (/ num_cols /), REAL64, 1, 'lst', 1, & + buffer, errors=errors) + ! Put some data into the buffers + do index = 1, num_cols + field32(index) = 2.0_real32 * real(index, real32) + field64(index) = (2.0_real64 * real(index, real64)) - 1.0_real64 + end do + ! Do some accumulation + do index = 1, 2 + buff_ptr => my_fields%buffers + do + if (associated(buff_ptr) .and. (errors%num_errors() == 0)) then + call hist_buffer_accumulate(buff_ptr, field32, 1, logger=errors) + if (errors%num_errors() > 0) then + call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname='my_fields accumulate') + exit + else + buff_ptr => buff_ptr%next + end if + else + exit + end if + end do + buff_ptr => fld_ptr%buffers + if (associated(buff_ptr) .and. (errors%num_errors() == 0)) then + call hist_buffer_accumulate(buff_ptr, field64, 1, logger=errors) + if (errors%num_errors() > 0) then + call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname='my_fields accumulate') + end if + end if + field32(:) = field32(:) * 2.0_real32 + field64(:) = field64(:) * 3.0_real64 + end do + ! Read out and check answers + buff_ptr => my_fields%buffers + do + if (associated(buff_ptr) .and. (errors%num_errors() == 0)) then + field32(:) = 0.0_real32 + call hist_buffer_norm_value(buff_ptr, field32, logger=errors) + if (errors%num_errors() > 0) then + call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname='my_fields accumulate') + exit + else + ! Check answers + if (buff_ptr%buffer_type() == 'hist_buff_1dreal32_lst_t') then + do index = 1, num_cols + test_val32 = 4.0_real32 * real(index, real32) + if (field32(index) /= test_val32) then + write(errmsg, '(a,i0,2(a,f8.3))') 'field(', index, & + ') = ', field32(index), ' /= ', test_val32 + call hist_add_error('hist_buff_1dreal32_lst_t check', & + trim(errmsg), errors=errors) + end if + end do + else if (buff_ptr%buffer_type() == 'hist_buff_1dreal32_avg_t') then + do index = 1, num_cols + test_val32 = 3.0_real32 * real(index, real32) + if (field32(index) /= test_val32) then + write(errmsg, '(a,i0,2(a,f8.2))') 'field(', index, & + ') = ', field32(index), ' /= ', test_val32 + call hist_add_error('hist_buff_1dreal32_avg_t check', & + trim(errmsg), errors=errors) + end if + end do + else + call hist_add_error('answer check, unknown buffer type', & + buff_ptr%buffer_type(), errors=errors) + end if + buff_ptr => buff_ptr%next + end if + else + exit + end if + end do + field64(:) = 0.0_real64 + buff_ptr => fld_ptr%buffers + call hist_buffer_norm_value(buff_ptr, field64, logger=errors) + if (errors%num_errors() > 0) then + call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname='my_fields accumulate') + else + ! Check answers + if (buff_ptr%buffer_type() == 'hist_buff_1dreal64_lst_t') then + do index = 1, num_cols + test_val64 = 6.0_real64 * real(index, real64) - 3.0_real64 + if (field64(index) /= test_val64) then + write(errmsg, '(a,i0,2(a,f8.3))') 'field(', index, & + ') = ', field64(index), ' /= ', test_val64 + call hist_add_error('hist_buff_1dreal32_lst_t check', & + trim(errmsg), errors=errors) + end if + end do + else + call hist_add_error('answer check, unknown buffer type', & + buff_ptr%buffer_type(), errors=errors) + end if + end if if (errors%num_errors() > 0) then write(6, '(a,i0,a)') 'FAIL, ', errors%num_errors(), ' errors found' From 70c73db6f08e9d8ad34be9dc70b351f47aa0299e Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Wed, 30 Sep 2020 17:55:01 -0600 Subject: [PATCH 13/16] Add hist_field_accumulate to cycle through buffers --- src/hist_api.F90 | 107 +++++++++++++++++++++++++++++++++++--- src/hist_field.F90 | 30 +++++------ test/test_hist_buffer.F90 | 36 +++++-------- 3 files changed, 128 insertions(+), 45 deletions(-) diff --git a/src/hist_api.F90 b/src/hist_api.F90 index 20f78fa..139ab39 100644 --- a/src/hist_api.F90 +++ b/src/hist_api.F90 @@ -1,9 +1,6 @@ module hist_api use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 - use hist_hashable, only: hist_hashable_t - use hist_field, only: hist_field_info_t - use hist_buffer, only: hist_buffer_t, buffer_factory use hist_buffer, only: hist_buff_1dreal32_lst_t use hist_buffer, only: hist_buff_1dreal64_lst_t @@ -13,12 +10,18 @@ module hist_api ! Public API interfaces public :: hist_new_field ! Allocate a hist_field_info_t object public :: hist_new_buffer ! Create a new field buffer + public :: hist_field_accumulate ! Accumulate a new field state in all buffs public :: hist_buffer_accumulate ! Accumulate a new field state public :: hist_buffer_norm_value ! Return current normalized field state ! public :: hist_buffer_clear ! Clear buffer accumulation state ! public :: hist_buffer_accum_type ! String value of accumulation type ! Interfaces for public interfaces + interface hist_field_accumulate + module procedure hist_field_accumulate_1dreal32 + module procedure hist_field_accumulate_1dreal64 + end interface hist_field_accumulate + interface hist_buffer_accumulate module procedure hist_buffer_accumulate_1dreal32 module procedure hist_buffer_accumulate_1dreal64 @@ -36,7 +39,7 @@ module hist_api function hist_new_field(diag_name_in, std_name_in, long_name_in, units_in, & type_in, errors) result(new_field) use hist_msg_handler, only: hist_have_error, hist_log_messages, ERROR - use hist_field, only: hist_field_initialize + use hist_field, only: hist_field_initialize, hist_field_info_t type(hist_field_info_t), pointer :: new_field character(len=*), intent(in) :: diag_name_in @@ -50,16 +53,16 @@ function hist_new_field(diag_name_in, std_name_in, long_name_in, units_in, & character(len=128) :: errmsg character(len=*), parameter :: subname = 'hist_new_field' - if (.not. hist_have_error(errors)) then + if (.not. hist_have_error(errors=errors)) then allocate(new_field, stat=astat) if ((astat /= 0) .and. present(errors)) then call errors%new_error(subname//' Unable to allocate ') end if end if - if (.not. hist_have_error(errors)) then + if (.not. hist_have_error(errors=errors)) then call hist_field_initialize(new_field, diag_name_in, std_name_in, & long_name_in, units_in, type_in, errmsg) - if (hist_have_error(errors)) then + if (hist_have_error(errors=errors)) then call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & subname=subname) end if @@ -72,6 +75,9 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & accum_type, output_vol, buffer, errors, block_ind, block_sizes) use hist_msg_handler, only: hist_log_messages, hist_add_error use hist_msg_handler, only: hist_add_alloc_error, ERROR + use hist_hashable, only: hist_hashable_t + use hist_buffer, only: hist_buffer_t, buffer_factory + use hist_field, only: hist_field_info_t ! Dummy arguments class(hist_field_info_t), pointer :: field @@ -220,9 +226,93 @@ end subroutine hist_new_buffer !####################################################################### + subroutine hist_field_accumulate_1dreal32(field, data, cols_or_block, & + cole, logger) + use hist_msg_handler, only: hist_log_messages, hist_have_error, ERROR + use hist_msg_handler, only: hist_add_message, VERBOSE + use hist_field, only: hist_field_info_t + use hist_buffer, only: hist_buffer_t + + ! Dummy arguments + class(hist_field_info_t), intent(inout) :: field + real(REAL32), intent(in) :: data(:) + integer, intent(in) :: cols_or_block + integer, optional, intent(in) :: cole + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + class(hist_buffer_t), pointer :: buff_ptr + character(len=:), allocatable :: buff_typestr + character(len=*), parameter :: subname = 'hist_field_accumulate_1dreal32' + + buff_ptr => field%buffers + do + if (associated(buff_ptr) .and. & + (.not. hist_have_error(errors=logger))) then + call hist_buffer_accumulate(buff_ptr, data, cols_or_block, & + cole=cole, logger=logger) + if (hist_have_error(errors=logger)) then + call logger%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname=subname) + exit + else + call hist_add_message(subname, VERBOSE, & + "Accumulated data for", msgstr2=trim(field%diag_name()), & + msgstr3=", Buffer type, "//trim(buff_ptr%buffer_type()), & + logger=logger) + buff_ptr => buff_ptr%next + end if + else + exit + end if + end do + + end subroutine hist_field_accumulate_1dreal32 + + !####################################################################### + + subroutine hist_field_accumulate_1dreal64(field, data, cols_or_block, & + cole, logger) + use hist_msg_handler, only: hist_log_messages, hist_have_error, ERROR + use hist_field, only: hist_field_info_t + use hist_buffer, only: hist_buffer_t + + ! Dummy arguments + class(hist_field_info_t), target, intent(inout) :: field + real(REAL64), intent(in) :: data(:) + integer, intent(in) :: cols_or_block + integer, optional, intent(in) :: cole + type(hist_log_messages), optional, intent(inout) :: logger + ! Local variables + class(hist_buffer_t), pointer :: buff_ptr + character(len=:), allocatable :: buff_typestr + character(len=*), parameter :: subname = 'hist_field_accumulate_1dreal64' + + buff_ptr => field%buffers + do + if (associated(buff_ptr) .and. & + (.not. hist_have_error(errors=logger))) then + call hist_buffer_accumulate(buff_ptr, data, cols_or_block, & + cole=cole, logger=logger) + if (hist_have_error(errors=logger)) then + call logger%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname=subname) + exit + else + buff_ptr => buff_ptr%next + end if + else + exit + end if + end do + + end subroutine hist_field_accumulate_1dreal64 + + !####################################################################### + subroutine hist_buffer_accumulate_1dreal32(buffer, field, cols_or_block, & cole, logger) use hist_msg_handler, only: hist_log_messages, hist_add_error + use hist_buffer, only: hist_buffer_t ! Dummy arguments class(hist_buffer_t), target, intent(inout) :: buffer @@ -258,6 +348,7 @@ end subroutine hist_buffer_accumulate_1dreal32 subroutine hist_buffer_accumulate_1dreal64(buffer, field, cols_or_block, & cole, logger) use hist_msg_handler, only: hist_log_messages, hist_add_error + use hist_buffer, only: hist_buffer_t ! Dummy arguments class(hist_buffer_t), target, intent(inout) :: buffer @@ -293,6 +384,7 @@ end subroutine hist_buffer_accumulate_1dreal64 subroutine hist_buffer_norm_value_1dreal32(buffer, norm_val, default_val, & logger) use hist_msg_handler, only: hist_log_messages, hist_add_error + use hist_buffer, only: hist_buffer_t ! Dummy arguments class(hist_buffer_t), target, intent(inout) :: buffer @@ -335,6 +427,7 @@ end subroutine hist_buffer_norm_value_1dreal32 subroutine hist_buffer_norm_value_1dreal64(buffer, norm_val, default_val, & logger) use hist_msg_handler, only: hist_log_messages, hist_add_error + use hist_buffer, only: hist_buffer_t ! Dummy arguments class(hist_buffer_t), target, intent(inout) :: buffer diff --git a/src/hist_field.F90 b/src/hist_field.F90 index 5b64838..05a7c4d 100644 --- a/src/hist_field.F90 +++ b/src/hist_field.F90 @@ -23,11 +23,11 @@ module hist_field class(hist_buffer_t), pointer :: buffers => NULL() contains procedure :: key => hist_field_info_get_key - procedure :: diag_name => field_get_diag_name - procedure :: standard_name => field_get_standard_name - procedure :: long_name => field_get_long_name - procedure :: units => field_get_units - procedure :: type => field_get_type + procedure :: diag_name => get_diag_name + procedure :: standard_name => get_standard_name + procedure :: long_name => get_long_name + procedure :: units => get_units + procedure :: type => get_type final :: finalize_field end type hist_field_info_t @@ -82,48 +82,48 @@ end subroutine hist_get_field !####################################################################### - function field_get_diag_name(this) result(info) + function get_diag_name(this) result(info) class(hist_field_info_t), intent(in) :: this character(len=:), allocatable :: info info = this%diag_file_name - end function field_get_diag_name + end function get_diag_name !####################################################################### - function field_get_standard_name(this) result(info) + function get_standard_name(this) result(info) class(hist_field_info_t), intent(in) :: this character(len=:), allocatable :: info info = this%field_standard_name - end function field_get_standard_name + end function get_standard_name !####################################################################### - function field_get_long_name(this) result(info) + function get_long_name(this) result(info) class(hist_field_info_t), intent(in) :: this character(len=:), allocatable :: info info = this%field_long_name - end function field_get_long_name + end function get_long_name !####################################################################### - function field_get_units(this) result(info) + function get_units(this) result(info) class(hist_field_info_t), intent(in) :: this character(len=:), allocatable :: info info = this%field_units - end function field_get_units + end function get_units !####################################################################### - function field_get_type(this) result(info) + function get_type(this) result(info) class(hist_field_info_t), intent(in) :: this character(len=:), allocatable :: info info = this%field_type - end function field_get_type + end function get_type !####################################################################### diff --git a/test/test_hist_buffer.F90 b/test/test_hist_buffer.F90 index ebc120a..67bbe1d 100644 --- a/test/test_hist_buffer.F90 +++ b/test/test_hist_buffer.F90 @@ -5,7 +5,7 @@ program test_hist_buffer use hist_buffer, only: hist_buffer_t use hist_field, only: hist_field_info_t, hist_get_field use hist_api, only: hist_new_field, hist_new_buffer - use hist_api, only: hist_buffer_accumulate, hist_buffer_norm_value + use hist_api, only: hist_field_accumulate, hist_buffer_norm_value implicit none @@ -26,11 +26,11 @@ program test_hist_buffer 'm s-1', 'real', errors=errors) my_fields%next => hist_new_field('T', 'temperature', 'Temperature', & 'K', 'real', errors=errors) - fld_ptr => my_fields%next call hist_new_buffer(my_fields, (/ num_cols /), REAL32, 1, 'lst', 1, & buffer, errors=errors) call hist_new_buffer(my_fields, (/ num_cols /), REAL32, 1, 'avg', 1, & buffer, errors=errors) + fld_ptr => my_fields%next call hist_new_buffer(fld_ptr, (/ num_cols /), REAL64, 1, 'lst', 1, & buffer, errors=errors) ! Put some data into the buffers @@ -40,27 +40,17 @@ program test_hist_buffer end do ! Do some accumulation do index = 1, 2 - buff_ptr => my_fields%buffers - do - if (associated(buff_ptr) .and. (errors%num_errors() == 0)) then - call hist_buffer_accumulate(buff_ptr, field32, 1, logger=errors) + if (errors%num_errors() == 0) then + call hist_field_accumulate(my_fields, field32, 1, logger=errors) + if (errors%num_errors() > 0) then + call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 2, & + subname='my_fields32 accumulate') + else + call hist_field_accumulate(fld_ptr, field64, 1, logger=errors) if (errors%num_errors() > 0) then - call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & - subname='my_fields accumulate') - exit - else - buff_ptr => buff_ptr%next + call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 2, & + subname='my_fields64 accumulate') end if - else - exit - end if - end do - buff_ptr => fld_ptr%buffers - if (associated(buff_ptr) .and. (errors%num_errors() == 0)) then - call hist_buffer_accumulate(buff_ptr, field64, 1, logger=errors) - if (errors%num_errors() > 0) then - call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & - subname='my_fields accumulate') end if end if field32(:) = field32(:) * 2.0_real32 @@ -112,7 +102,7 @@ program test_hist_buffer buff_ptr => fld_ptr%buffers call hist_buffer_norm_value(buff_ptr, field64, logger=errors) if (errors%num_errors() > 0) then - call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + call errors%add_stack_frame(ERROR, __FILE__, __LINE__ - 2, & subname='my_fields accumulate') else ! Check answers @@ -122,7 +112,7 @@ program test_hist_buffer if (field64(index) /= test_val64) then write(errmsg, '(a,i0,2(a,f8.3))') 'field(', index, & ') = ', field64(index), ' /= ', test_val64 - call hist_add_error('hist_buff_1dreal32_lst_t check', & + call hist_add_error('hist_buff_1dreal64_lst_t check', & trim(errmsg), errors=errors) end if end do From c6c725ebdd137f5206d240dd1dd01e853af5b9e4 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Wed, 30 Sep 2020 20:05:11 -0600 Subject: [PATCH 14/16] Improve clean target --- test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Makefile b/test/Makefile index f77cc20..49ec777 100644 --- a/test/Makefile +++ b/test/Makefile @@ -52,5 +52,5 @@ test_hist_buffer: test_hist_buffer.F90 $(HISTOBJS) hist_hashable.o hist_msg_han # CLEAN clean: - @rm -f *.o *.mod + @rm -f *.o *.mod hist_test.log @rm -f test_hash_table test_hist_buffer From eeca896013aef1fcf8c43e4c0175d2cd4c034e03 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Thu, 1 Oct 2020 11:30:51 -0600 Subject: [PATCH 15/16] Allow NULL pointer for in accumulate calls --- src/hist_api.F90 | 83 +++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/hist_api.F90 b/src/hist_api.F90 index 139ab39..fe6a03f 100644 --- a/src/hist_api.F90 +++ b/src/hist_api.F90 @@ -234,7 +234,7 @@ subroutine hist_field_accumulate_1dreal32(field, data, cols_or_block, & use hist_buffer, only: hist_buffer_t ! Dummy arguments - class(hist_field_info_t), intent(inout) :: field + class(hist_field_info_t), pointer, intent(inout) :: field real(REAL32), intent(in) :: data(:) integer, intent(in) :: cols_or_block integer, optional, intent(in) :: cole @@ -244,27 +244,30 @@ subroutine hist_field_accumulate_1dreal32(field, data, cols_or_block, & character(len=:), allocatable :: buff_typestr character(len=*), parameter :: subname = 'hist_field_accumulate_1dreal32' - buff_ptr => field%buffers - do - if (associated(buff_ptr) .and. & - (.not. hist_have_error(errors=logger))) then - call hist_buffer_accumulate(buff_ptr, data, cols_or_block, & - cole=cole, logger=logger) - if (hist_have_error(errors=logger)) then - call logger%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & - subname=subname) - exit + if (associated(field)) then + buff_ptr => field%buffers + do + if (associated(buff_ptr) .and. & + (.not. hist_have_error(errors=logger))) then + call hist_buffer_accumulate(buff_ptr, data, cols_or_block, & + cole=cole, logger=logger) + if (hist_have_error(errors=logger)) then + call logger%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname=subname) + exit + else + call hist_add_message(subname, VERBOSE, & + "Accumulated data for", & + msgstr2=trim(field%diag_name())//", Buffer type, ", & + msgstr3=trim(buff_ptr%buffer_type()), & + logger=logger) + buff_ptr => buff_ptr%next + end if else - call hist_add_message(subname, VERBOSE, & - "Accumulated data for", msgstr2=trim(field%diag_name()), & - msgstr3=", Buffer type, "//trim(buff_ptr%buffer_type()), & - logger=logger) - buff_ptr => buff_ptr%next + exit end if - else - exit - end if - end do + end do + end if ! No else, it is legit to pass in a null pointer end subroutine hist_field_accumulate_1dreal32 @@ -273,11 +276,12 @@ end subroutine hist_field_accumulate_1dreal32 subroutine hist_field_accumulate_1dreal64(field, data, cols_or_block, & cole, logger) use hist_msg_handler, only: hist_log_messages, hist_have_error, ERROR + use hist_msg_handler, only: hist_add_message, VERBOSE use hist_field, only: hist_field_info_t use hist_buffer, only: hist_buffer_t ! Dummy arguments - class(hist_field_info_t), target, intent(inout) :: field + class(hist_field_info_t), pointer, intent(inout) :: field real(REAL64), intent(in) :: data(:) integer, intent(in) :: cols_or_block integer, optional, intent(in) :: cole @@ -287,23 +291,30 @@ subroutine hist_field_accumulate_1dreal64(field, data, cols_or_block, & character(len=:), allocatable :: buff_typestr character(len=*), parameter :: subname = 'hist_field_accumulate_1dreal64' - buff_ptr => field%buffers - do - if (associated(buff_ptr) .and. & - (.not. hist_have_error(errors=logger))) then - call hist_buffer_accumulate(buff_ptr, data, cols_or_block, & - cole=cole, logger=logger) - if (hist_have_error(errors=logger)) then - call logger%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & - subname=subname) - exit + if (associated(field)) then + buff_ptr => field%buffers + do + if (associated(buff_ptr) .and. & + (.not. hist_have_error(errors=logger))) then + call hist_buffer_accumulate(buff_ptr, data, cols_or_block, & + cole=cole, logger=logger) + if (hist_have_error(errors=logger)) then + call logger%add_stack_frame(ERROR, __FILE__, __LINE__ - 3, & + subname=subname) + exit + else + call hist_add_message(subname, VERBOSE, & + "Accumulated data for", & + msgstr2=trim(field%diag_name())//", Buffer type, ", & + msgstr3=trim(buff_ptr%buffer_type()), & + logger=logger) + buff_ptr => buff_ptr%next + end if else - buff_ptr => buff_ptr%next + exit end if - else - exit - end if - end do + end do + end if ! No else, it is legit to pass in a null pointer end subroutine hist_field_accumulate_1dreal64 From 5595cbbc4cadaaf4dabdd16c779def9b0877d1f9 Mon Sep 17 00:00:00 2001 From: Steve Goldhaber Date: Sat, 3 Oct 2020 19:52:56 -0600 Subject: [PATCH 16/16] Combine min and max with last and add new tests --- src/hist_api.F90 | 58 ++++---- src/hist_buffer.F90 | 240 ++++++++++++++++++++++------------ src/util/hist_msg_handler.F90 | 25 +++- test/test_hist_buffer.F90 | 29 +++- 4 files changed, 232 insertions(+), 120 deletions(-) diff --git a/src/hist_api.F90 b/src/hist_api.F90 index fe6a03f..df23dab 100644 --- a/src/hist_api.F90 +++ b/src/hist_api.F90 @@ -1,8 +1,8 @@ module hist_api use ISO_FORTRAN_ENV, only: REAL64, REAL32, INT32, INT64 - use hist_buffer, only: hist_buff_1dreal32_lst_t - use hist_buffer, only: hist_buff_1dreal64_lst_t + use hist_buffer, only: hist_buff_1dreal32_inst_t + use hist_buffer, only: hist_buff_1dreal64_inst_t implicit none private @@ -77,6 +77,8 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & use hist_msg_handler, only: hist_add_alloc_error, ERROR use hist_hashable, only: hist_hashable_t use hist_buffer, only: hist_buffer_t, buffer_factory + use hist_buffer, only: hist_accum_lst, hist_accum_min, hist_accum_max + use hist_buffer, only: hist_accum_avg, hist_accum_var use hist_field, only: hist_field_info_t ! Dummy arguments @@ -94,6 +96,7 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & ! Local variables integer :: rank integer :: line_loc + integer :: accum_val character(len=8) :: kind_string character(len=3) :: accum_string character(len=16) :: bufftype_string @@ -171,14 +174,19 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & select case(trim(accum_type)) case ('I', 'i', 'lst') accum_string = 'lst' + accum_val = hist_accum_lst case ('A', 'a', 'avg') accum_string = 'avg' + accum_val = hist_accum_avg case ('M', 'm', 'min') accum_string = 'min' + accum_val = hist_accum_min case ('X', 'x', 'max') accum_string = 'max' + accum_val = hist_accum_max case ('S', 's', 'var') accum_string = 'var' + accum_val = hist_accum_var case default call hist_add_error(subname, & "Unknown accumulation operator type, '", & @@ -204,7 +212,7 @@ subroutine hist_new_buffer(field, buff_shape, buff_kind, horiz_axis_ind, & if (associated(buffer)) then field_base => field call buffer%initialize(field_base, output_vol, horiz_axis_ind, & - buff_shape, block_sizes, block_ind, logger=errors) + accum_val, buff_shape, block_sizes, block_ind, logger=errors) ! Add this buffer to its field (field should be there if buffer is) if (associated(field%buffers)) then buff_ptr => field%buffers @@ -332,16 +340,16 @@ subroutine hist_buffer_accumulate_1dreal32(buffer, field, cols_or_block, & integer, optional, intent(in) :: cole type(hist_log_messages), optional, intent(inout) :: logger ! Local variables - class(hist_buff_1dreal32_lst_t), pointer :: buff32 - class(hist_buff_1dreal64_lst_t), pointer :: buff64 - character(len=:), allocatable :: buff_typestr + class(hist_buff_1dreal32_inst_t), pointer :: buff32 + class(hist_buff_1dreal64_inst_t), pointer :: buff64 + character(len=:), allocatable :: buff_typestr character(len=*), parameter :: subname = 'hist_buffer_accumulate_1dreal32' select type(buffer) - class is (hist_buff_1dreal32_lst_t) + class is (hist_buff_1dreal32_inst_t) buff32 => buffer call buff32%accumulate(field, cols_or_block, cole, logger) - class is (hist_buff_1dreal64_lst_t) + class is (hist_buff_1dreal64_inst_t) ! Do we want to accumulate 32bit data into 64bit buffers? buff64 => buffer call buff64%accumulate(real(field, REAL64), cols_or_block, cole, & @@ -368,18 +376,18 @@ subroutine hist_buffer_accumulate_1dreal64(buffer, field, cols_or_block, & integer, optional, intent(in) :: cole type(hist_log_messages), optional, intent(inout) :: logger ! Local variables - type(hist_buff_1dreal32_lst_t), pointer :: buff32 - type(hist_buff_1dreal64_lst_t), pointer :: buff64 - character(len=:), allocatable :: buff_typestr + type(hist_buff_1dreal32_inst_t), pointer :: buff32 + type(hist_buff_1dreal64_inst_t), pointer :: buff64 + character(len=:), allocatable :: buff_typestr character(len=*), parameter :: subname = 'hist_buffer_accumulate_1dreal64' select type(buffer) - class is (hist_buff_1dreal32_lst_t) + class is (hist_buff_1dreal32_inst_t) ! Squeeze 64 bit data into 32 bit buffers buff32 => buffer call buff32%accumulate(real(field, REAL32), cols_or_block, cole, & logger) - class is (hist_buff_1dreal64_lst_t) + class is (hist_buff_1dreal64_inst_t) buff64 => buffer call buff64%accumulate(field, cols_or_block, cole, logger) class default @@ -403,18 +411,18 @@ subroutine hist_buffer_norm_value_1dreal32(buffer, norm_val, default_val, & real(REAL32), optional, intent(in) :: default_val type(hist_log_messages), optional, intent(inout) :: logger ! Local variables - class(hist_buff_1dreal32_lst_t), pointer :: buff32 - class(hist_buff_1dreal64_lst_t), pointer :: buff64 - real(REAL64), allocatable :: norm_val64(:) - character(len=:), allocatable :: buff_typestr + class(hist_buff_1dreal32_inst_t), pointer :: buff32 + class(hist_buff_1dreal64_inst_t), pointer :: buff64 + real(REAL64), allocatable :: norm_val64(:) + character(len=:), allocatable :: buff_typestr character(len=*), parameter :: subname = 'hist_buffer_norm_value_1dreal32' select type(buffer) - class is (hist_buff_1dreal32_lst_t) + class is (hist_buff_1dreal32_inst_t) buff32 => buffer call buff32%norm_value(norm_val, default_val=default_val, & logger=logger) - class is (hist_buff_1dreal64_lst_t) + class is (hist_buff_1dreal64_inst_t) ! Truncate 64bit buffer into 32bit output buff64 => buffer allocate(norm_val64(size(norm_val, 1))) @@ -446,14 +454,14 @@ subroutine hist_buffer_norm_value_1dreal64(buffer, norm_val, default_val, & real(REAL64), optional, intent(in) :: default_val type(hist_log_messages), optional, intent(inout) :: logger ! Local variables - type(hist_buff_1dreal32_lst_t), pointer :: buff32 - type(hist_buff_1dreal64_lst_t), pointer :: buff64 - real(REAL32), allocatable :: norm_val32(:) - character(len=:), allocatable :: buff_typestr + type(hist_buff_1dreal32_inst_t), pointer :: buff32 + type(hist_buff_1dreal64_inst_t), pointer :: buff64 + real(REAL32), allocatable :: norm_val32(:) + character(len=:), allocatable :: buff_typestr character(len=*), parameter :: subname = 'hist_buffer_norm_value_1dreal64' select type(buffer) - class is (hist_buff_1dreal32_lst_t) + class is (hist_buff_1dreal32_inst_t) ! Do we want to read out 32bit buffers into 64bit data? buff32 => buffer allocate(norm_val32(size(norm_val, 1))) @@ -464,7 +472,7 @@ subroutine hist_buffer_norm_value_1dreal64(buffer, norm_val, default_val, & call buffer%norm_value(norm_val32, logger=logger) end if norm_val(:) = REAL(norm_val32(:), REAL64) - class is (hist_buff_1dreal64_lst_t) + class is (hist_buff_1dreal64_inst_t) buff64 => buffer call buffer%norm_value(norm_val, default_val=default_val, & logger=logger) diff --git a/src/hist_buffer.F90 b/src/hist_buffer.F90 index b8eb086..d301ee4 100644 --- a/src/hist_buffer.F90 +++ b/src/hist_buffer.F90 @@ -8,6 +8,24 @@ module hist_buffer ! Public interfaces public :: buffer_factory + ! Accumulation types -- the integers and array positions below must match + integer, parameter, public :: hist_accum_lst = 1 ! last sample + integer, parameter, public :: hist_accum_min = 2 ! minimum sample + integer, parameter, public :: hist_accum_max = 3 ! maximum sample + integer, parameter, public :: hist_accum_avg = 4 ! sample average + integer, parameter, public :: hist_accum_var = 5 ! sample standard deviation + + integer, parameter :: as_len = 36 + character(len=as_len), parameter, public :: accum_strings(5) = (/ & + 'last sampled value ', & + 'minimum of sampled values ', & + 'maximum of sampled values ', & + 'average of sampled values ', & + 'standard deviation of sampled values' /) + + character(len=3), parameter, public :: accum_abbrev(5) = & + (/ 'lst', 'min', 'max', 'avg', 'var' /) + ! Time sampling flag indices !!XXgoldyXX: Todo: decide on sampling types @@ -17,11 +35,11 @@ module hist_buffer integer, private :: vol = -1 ! For host output integer, private :: horiz_axis_ind = 0 integer, private :: rank = 0 + integer, private :: accum_type = 0 integer, allocatable, private :: field_shape(:) integer, allocatable, private :: num_samples(:) integer, allocatable, private :: block_begs(:) integer, allocatable, private :: block_ends(:) - character(len=:), allocatable, private :: accum_str character(len=:), allocatable, private :: buff_type class(hist_buffer_t), pointer :: next => NULL() contains @@ -37,30 +55,30 @@ module hist_buffer procedure(hist_buff_init), deferred :: initialize end type hist_buffer_t - type, public, extends(hist_buffer_t) :: hist_buff_1dreal32_lst_t + type, public, extends(hist_buffer_t) :: hist_buff_1dreal32_inst_t real(REAL32), pointer :: data(:) => NULL() CONTAINS - procedure :: clear => buff_1dreal32_lst_clear - procedure :: accumulate => buff_1dreal32_lst_accum - procedure :: norm_value => buff_1dreal32_lst_value - procedure :: initialize => init_buff_1dreal32 - end type hist_buff_1dreal32_lst_t + procedure :: clear => buff_1dreal32_inst_clear + procedure :: accumulate => buff_1dreal32_inst_accum + procedure :: norm_value => buff_1dreal32_inst_value + procedure :: initialize => init_buff_inst_1dreal32 + end type hist_buff_1dreal32_inst_t - type, public, extends(hist_buff_1dreal32_lst_t) :: hist_buff_1dreal32_avg_t + type, public, extends(hist_buff_1dreal32_inst_t) :: hist_buff_1dreal32_avg_t CONTAINS procedure :: accumulate => buff_1dreal32_avg_accum procedure :: norm_value => buff_1dreal32_avg_value procedure :: initialize => init_buff_avg_1dreal32 end type hist_buff_1dreal32_avg_t - type, public, extends(hist_buffer_t) :: hist_buff_1dreal64_lst_t + type, public, extends(hist_buffer_t) :: hist_buff_1dreal64_inst_t real(REAL64), pointer :: data(:) => NULL() CONTAINS - procedure :: clear => buff_1dreal64_lst_clear - procedure :: accumulate => buff_1dreal64_lst_accum - procedure :: norm_value => buff_1dreal64_lst_value - procedure :: initialize => init_buff_1dreal64 - end type hist_buff_1dreal64_lst_t + procedure :: clear => buff_1dreal64_inst_clear + procedure :: accumulate => buff_1dreal64_inst_accum + procedure :: norm_value => buff_1dreal64_inst_value + procedure :: initialize => init_buff_inst_1dreal64 + end type hist_buff_1dreal64_inst_t ! Abstract interfaces for hist_buffer_t class abstract interface @@ -74,7 +92,7 @@ end subroutine hist_buff_sub_log abstract interface subroutine hist_buff_init(this, field_in, volume_in, horiz_axis_in, & - shape_in, block_sizes_in, block_ind_in, logger) + accum_type_in, shape_in, block_sizes_in, block_ind_in, logger) use hist_msg_handler, only: hist_log_messages import :: hist_buffer_t import :: hist_hashable_t @@ -82,6 +100,7 @@ subroutine hist_buff_init(this, field_in, volume_in, horiz_axis_in, & class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in + integer, intent(in) :: accum_type_in integer, intent(in) :: shape_in(:) integer, optional, intent(in) :: block_sizes_in(:) integer, optional, intent(in) :: block_ind_in @@ -173,8 +192,8 @@ end subroutine hist_buff_clear !####################################################################### - subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & - block_sizes_in, block_ind_in, logger) + subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, & + accum_type_in, shape_in, block_sizes_in, block_ind_in, logger) use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error use hist_msg_handler, only: hist_add_error @@ -183,6 +202,7 @@ subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in + integer, intent(in) :: accum_type_in integer, intent(in) :: shape_in(:) integer, optional, intent(in) :: block_sizes_in(:) integer, optional, intent(in) :: block_ind_in @@ -202,6 +222,7 @@ subroutine init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & this%field_info => field_in this%vol = volume_in this%horiz_axis_ind = horiz_axis_in + this%accum_type = accum_type_in allocate(this%field_shape(size(shape_in, 1)), stat=astat) if (astat == 0) then this%field_shape(:) = shape_in(:) @@ -222,10 +243,11 @@ end subroutine init_buffer !####################################################################### function accum_string(this) result(ac_str) - class(hist_buffer_t), intent(in) :: this - character(len=:), allocatable :: ac_str + class(hist_buffer_t), intent(in) :: this + character(len=as_len), allocatable :: ac_str + + ac_str = accum_strings(this%accum_type) - ac_str = this%accum_str end function accum_string !####################################################################### @@ -244,15 +266,15 @@ end function buffer_type !####################################################################### - subroutine buff_1dreal32_lst_clear(this, logger) + subroutine buff_1dreal32_inst_clear(this, logger) use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error ! Dummy arguments - class(hist_buff_1dreal32_lst_t), intent(inout) :: this + class(hist_buff_1dreal32_inst_t), intent(inout) :: this type(hist_log_messages), optional, intent(inout) :: logger ! Local variables integer :: aerr - character(len=*), parameter :: subname = 'buff_1dreal32_lst_clear' + character(len=*), parameter :: subname = 'buff_1dreal32_inst_clear' call hist_buff_clear(this, logger) if (.not. associated(this%data)) then @@ -264,45 +286,48 @@ subroutine buff_1dreal32_lst_clear(this, logger) end if this%data = 0.0_REAL32 - end subroutine buff_1dreal32_lst_clear + end subroutine buff_1dreal32_inst_clear !####################################################################### - subroutine init_buff_1dreal32(this, field_in, volume_in, horiz_axis_in, & - shape_in, block_sizes_in, block_ind_in, logger) + subroutine init_buff_inst_1dreal32(this, field_in, volume_in, & + horiz_axis_in, accum_type_in, shape_in, block_sizes_in, block_ind_in, & + logger) use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error ! Dummy arguments - class(hist_buff_1dreal32_lst_t), intent(inout) :: this + class(hist_buff_1dreal32_inst_t), intent(inout) :: this class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in + integer, intent(in) :: accum_type_in integer, intent(in) :: shape_in(:) integer, optional, intent(in) :: block_sizes_in(:) integer, optional, intent(in) :: block_ind_in type(hist_log_messages), optional, intent(inout) :: logger - call init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & - block_sizes_in, block_ind_in, logger=logger) + call init_buffer(this, field_in, volume_in, horiz_axis_in, & + accum_type_in, shape_in, block_sizes_in, block_ind_in, logger=logger) call this%clear(logger=logger) - this%accum_str = 'last sampled value' - this%buff_type = 'hist_buff_1dreal32_lst_t' + this%buff_type = 'buff_1dreal32_'//accum_abbrev(accum_type_in) - end subroutine init_buff_1dreal32 + end subroutine init_buff_inst_1dreal32 !####################################################################### - subroutine buff_1dreal32_lst_accum(this, field, cols_or_block, cole, logger) + subroutine buff_1dreal32_inst_accum(this, field, cols_or_block, cole, logger) use hist_msg_handler, only: hist_log_messages ! Dummy arguments - class(hist_buff_1dreal32_lst_t), intent(inout) :: this + class(hist_buff_1dreal32_inst_t), intent(inout) :: this real(REAL32), intent(in) :: field(:) integer, intent(in) :: cols_or_block integer, optional, intent(in) :: cole type(hist_log_messages), optional, intent(inout) :: logger ! Local variables - integer :: col_beg_use - integer :: col_end_use + integer :: col_beg_use + integer :: col_end_use + integer :: ind1 + real(REAL32) :: fld_val if (this%has_blocks()) then ! For a blocked field, is a block index @@ -319,18 +344,41 @@ subroutine buff_1dreal32_lst_accum(this, field, cols_or_block, cole, logger) end if end if - this%data(col_beg_use:col_end_use) = field(:) - this%num_samples(col_beg_use:col_end_use) = 1 + select case (this%accum_type) + case (hist_accum_lst) + this%data(col_beg_use:col_end_use) = field(:) + this%num_samples(col_beg_use:col_end_use) = 1 + case (hist_accum_min) + do ind1 = col_beg_use, col_end_use + fld_val = field(ind1 - col_beg_use + 1) + if (this%num_samples(ind1) == 0) then + this%data(ind1) = fld_val + else if (fld_val < this%data(ind1)) then + this%data(ind1) = fld_val + end if ! No else, we already have the minimum value for this col + this%num_samples(ind1) = 1 + end do + case (hist_accum_max) + do ind1 = col_beg_use, col_end_use + fld_val = field(ind1 - col_beg_use + 1) + if (this%num_samples(ind1) == 0) then + this%data(ind1) = fld_val + else if (fld_val > this%data(ind1)) then + this%data(ind1) = fld_val + end if ! No else, we already have the maximum value for this col + this%num_samples(ind1) = 1 + end do + end select - end subroutine buff_1dreal32_lst_accum + end subroutine buff_1dreal32_inst_accum !####################################################################### - subroutine buff_1dreal32_lst_value(this, norm_val, default_val, logger) + subroutine buff_1dreal32_inst_value(this, norm_val, default_val, logger) use hist_msg_handler, only: hist_log_messages ! Dummy arguments - class(hist_buff_1dreal32_lst_t), intent(inout) :: this + class(hist_buff_1dreal32_inst_t), intent(inout) :: this real(REAL32), intent(inout) :: norm_val(:) real(REAL32), optional, intent(in) :: default_val type(hist_log_messages), optional, intent(inout) :: logger @@ -347,12 +395,13 @@ subroutine buff_1dreal32_lst_value(this, norm_val, default_val, logger) end if end do - end subroutine buff_1dreal32_lst_value + end subroutine buff_1dreal32_inst_value !####################################################################### subroutine init_buff_avg_1dreal32(this, field_in, volume_in, & - horiz_axis_in, shape_in, block_sizes_in, block_ind_in, logger) + horiz_axis_in, accum_type_in, shape_in, block_sizes_in, block_ind_in, & + logger) use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error ! Dummy arguments @@ -360,16 +409,16 @@ subroutine init_buff_avg_1dreal32(this, field_in, volume_in, & class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in + integer, intent(in) :: accum_type_in integer, intent(in) :: shape_in(:) integer, optional, intent(in) :: block_sizes_in(:) integer, optional, intent(in) :: block_ind_in type(hist_log_messages), optional, intent(inout) :: logger - call init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & - block_sizes_in, block_ind_in, logger=logger) + call init_buffer(this, field_in, volume_in, horiz_axis_in, & + accum_type_in, shape_in, block_sizes_in, block_ind_in, logger=logger) call this%clear(logger=logger) - this%accum_str = 'average of sampled values' - this%buff_type = 'hist_buff_1dreal32_avg_t' + this%buff_type = 'buff_1dreal32_'//accum_abbrev(accum_type_in) end subroutine init_buff_avg_1dreal32 @@ -437,15 +486,15 @@ end subroutine buff_1dreal32_avg_value !####################################################################### - subroutine buff_1dreal64_lst_clear(this, logger) + subroutine buff_1dreal64_inst_clear(this, logger) use hist_msg_handler, only: hist_log_messages, hist_add_alloc_error ! Dummy arguments - class(hist_buff_1dreal64_lst_t), intent(inout) :: this + class(hist_buff_1dreal64_inst_t), intent(inout) :: this type(hist_log_messages), optional, intent(inout) :: logger ! Local variables integer :: aerr - character(len=*), parameter :: subname = 'buff_1dreal64_lst_clear' + character(len=*), parameter :: subname = 'buff_1dreal64_inst_clear' call hist_buff_clear(this, logger) if (.not. associated(this%data)) then @@ -457,44 +506,47 @@ subroutine buff_1dreal64_lst_clear(this, logger) end if this%data = 0.0_REAL64 - end subroutine buff_1dreal64_lst_clear + end subroutine buff_1dreal64_inst_clear !####################################################################### - subroutine init_buff_1dreal64(this, field_in, volume_in, horiz_axis_in, & - shape_in, block_sizes_in, block_ind_in, logger) + subroutine init_buff_inst_1dreal64(this, field_in, volume_in, & + horiz_axis_in, accum_type_in, shape_in, block_sizes_in, block_ind_in, & + logger) use hist_msg_handler, only: hist_log_messages - class(hist_buff_1dreal64_lst_t), intent(inout) :: this + class(hist_buff_1dreal64_inst_t), intent(inout) :: this class(hist_hashable_t), pointer :: field_in integer, intent(in) :: volume_in integer, intent(in) :: horiz_axis_in + integer, intent(in) :: accum_type_in integer, intent(in) :: shape_in(:) integer, optional, intent(in) :: block_sizes_in(:) integer, optional, intent(in) :: block_ind_in type(hist_log_messages), optional, intent(inout) :: logger - call init_buffer(this, field_in, volume_in, horiz_axis_in, shape_in, & - block_sizes_in, block_ind_in, logger=logger) + call init_buffer(this, field_in, volume_in, horiz_axis_in, & + accum_type_in, shape_in, block_sizes_in, block_ind_in, logger=logger) call this%clear(logger=logger) - this%accum_str = 'last sampled value' - this%buff_type = 'hist_buff_1dreal64_lst_t' + this%buff_type = 'buff_1dreal64_'//accum_abbrev(accum_type_in) - end subroutine init_buff_1dreal64 + end subroutine init_buff_inst_1dreal64 !####################################################################### - subroutine buff_1dreal64_lst_accum(this, field, cols_or_block, cole, logger) + subroutine buff_1dreal64_inst_accum(this, field, cols_or_block, cole, logger) use hist_msg_handler, only: hist_log_messages ! Dummy arguments - class(hist_buff_1dreal64_lst_t), intent(inout) :: this + class(hist_buff_1dreal64_inst_t), intent(inout) :: this real(REAL64), intent(in) :: field(:) integer, intent(in) :: cols_or_block integer, optional, intent(in) :: cole type(hist_log_messages), optional, intent(inout) :: logger ! Local variables - integer :: col_beg_use - integer :: col_end_use + integer :: col_beg_use + integer :: col_end_use + integer :: ind1 + real(REAL64) :: fld_val if (this%has_blocks()) then ! For a blocked field, is a block index @@ -511,17 +563,41 @@ subroutine buff_1dreal64_lst_accum(this, field, cols_or_block, cole, logger) end if end if - this%data(col_beg_use:col_end_use) = field(:) - this%num_samples(col_beg_use:col_end_use) = 1 - end subroutine buff_1dreal64_lst_accum + select case (this%accum_type) + case (hist_accum_lst) + this%data(col_beg_use:col_end_use) = field(:) + this%num_samples(col_beg_use:col_end_use) = 1 + case (hist_accum_min) + do ind1 = col_beg_use, col_end_use + fld_val = field(ind1 - col_beg_use + 1) + if (this%num_samples(ind1) == 0) then + this%data(ind1) = fld_val + else if (fld_val < this%data(ind1)) then + this%data(ind1) = fld_val + end if ! No else, we already have the minimum value for this col + this%num_samples(ind1) = 1 + end do + case (hist_accum_max) + do ind1 = col_beg_use, col_end_use + fld_val = field(ind1 - col_beg_use + 1) + if (this%num_samples(ind1) == 0) then + this%data(ind1) = fld_val + else if (fld_val > this%data(ind1)) then + this%data(ind1) = fld_val + end if ! No else, we already have the maximum value for this col + this%num_samples(ind1) = 1 + end do + end select + + end subroutine buff_1dreal64_inst_accum !####################################################################### - subroutine buff_1dreal64_lst_value(this, norm_val, default_val, logger) + subroutine buff_1dreal64_inst_value(this, norm_val, default_val, logger) use hist_msg_handler, only: hist_log_messages, ERROR, VERBOSE ! Dummy arguments - class(hist_buff_1dreal64_lst_t), intent(inout) :: this + class(hist_buff_1dreal64_inst_t), intent(inout) :: this real(REAL64), intent(inout) :: norm_val(:) real(REAL64), optional, intent(in) :: default_val type(hist_log_messages), optional, intent(inout) :: logger @@ -540,7 +616,7 @@ subroutine buff_1dreal64_lst_value(this, norm_val, default_val, logger) norm_val(:) = this%data(:) - end subroutine buff_1dreal64_lst_value + end subroutine buff_1dreal64_inst_value !####################################################################### @@ -567,31 +643,31 @@ function buffer_factory(buffer_type, logger) result(newbuf) type(hist_log_messages), optional, intent(inout) :: logger ! Local variables - character(len=*), parameter :: subname = 'buffer_factory' - ! For buffer allocation - integer :: aerr - type(hist_buff_1dreal32_lst_t), pointer :: real32_1_lst => NULL() - type(hist_buff_1dreal32_avg_t), pointer :: real32_1_avg => NULL() - type(hist_buff_1dreal64_lst_t), pointer :: real64_1_lst => NULL() + character(len=*), parameter :: subname = 'buffer_factory' + ! For buffer allocation + integer :: aerr + type(hist_buff_1dreal32_inst_t), pointer :: real32_1_inst => NULL() + type(hist_buff_1dreal32_avg_t), pointer :: real32_1_avg => NULL() + type(hist_buff_1dreal64_inst_t), pointer :: real64_1_inst => NULL() nullify(newbuf) ! Create new buffer select case (trim(buffer_type)) - case ('real32_1_lst') - allocate(real32_1_lst, stat=aerr) + case ('real32_1_lst', 'real32_1_min', 'real32_1_max') + allocate(real32_1_inst, stat=aerr) if (aerr == 0) then - newbuf => real32_1_lst + newbuf => real32_1_inst end if - case ('real32_1_avg') + case ('real32_1_avg', 'real32_1_var') allocate(real32_1_avg, stat=aerr) if (aerr == 0) then newbuf => real32_1_avg end if - case ('real64_1_lst') - allocate(real64_1_lst, stat=aerr) + case ('real64_1_lst', 'real64_1_min', 'real64_1_max') + allocate(real64_1_inst, stat=aerr) if (aerr == 0) then - newbuf => real64_1_lst + newbuf => real64_1_inst end if case default call hist_add_error(subname, & diff --git a/src/util/hist_msg_handler.F90 b/src/util/hist_msg_handler.F90 index 6f229e7..a32743d 100644 --- a/src/util/hist_msg_handler.F90 +++ b/src/util/hist_msg_handler.F90 @@ -228,9 +228,16 @@ subroutine hist_msgs_new_message(this, msg_level, msgstr1, & ! First, calculate the message length msg_strs(:) = '' int_strs(:) = '' - msg_len = len_trim(MSG_HEAD(msg_lvl)) + len_trim(msgstr1) + msg_len = len_trim(msgstr1) + if (msg_lvl == ERROR) then + msg_len = msg_len + len_trim(MSG_HEAD(msg_lvl)) + end if if (present(subname)) then - write(msg_strs(1), '(3a)') " in ", trim(subname), ": " + if (msg_lvl == ERROR) then + write(msg_strs(1), '(3a)') " in ", trim(subname), ": " + else + write(msg_strs(1), '(3a)') "In ", trim(subname), ": " + end if else write(msg_strs(1), '(3a)') ": " end if @@ -257,10 +264,16 @@ subroutine hist_msgs_new_message(this, msg_level, msgstr1, & allocate(character(len=msg_len) :: new_log_entry%log_message, & stat=aerr) if (aerr == 0) then - write(new_log_entry%log_message, '(8a)') & - trim(MSG_HEAD(msg_lvl)), trim(msg_strs(1)), ' ', & - trim(msgstr1), trim(int_strs(1)), trim(msg_strs(2)), & - trim(int_strs(2)), trim(msg_strs(3)) + if (msg_lvl == ERROR) then + write(new_log_entry%log_message, '(8a)') & + trim(MSG_HEAD(msg_lvl)), trim(msg_strs(1)), ' ', & + trim(msgstr1), trim(int_strs(1)), trim(msg_strs(2)), & + trim(int_strs(2)), trim(msg_strs(3)) + else + write(new_log_entry%log_message, '(7a)') trim(msg_strs(1)), & + ' ', trim(msgstr1), trim(int_strs(1)), & + trim(msg_strs(2)), trim(int_strs(2)), trim(msg_strs(3)) + end if else ! Hail Mary attempt new_log_entry%log_message = 'Message allocation error' diff --git a/test/test_hist_buffer.F90 b/test/test_hist_buffer.F90 index 67bbe1d..584ca75 100644 --- a/test/test_hist_buffer.F90 +++ b/test/test_hist_buffer.F90 @@ -28,6 +28,10 @@ program test_hist_buffer 'K', 'real', errors=errors) call hist_new_buffer(my_fields, (/ num_cols /), REAL32, 1, 'lst', 1, & buffer, errors=errors) + call hist_new_buffer(my_fields, (/ num_cols /), REAL32, 1, 'min', 1, & + buffer, errors=errors) + call hist_new_buffer(my_fields, (/ num_cols /), REAL32, 1, 'max', 1, & + buffer, errors=errors) call hist_new_buffer(my_fields, (/ num_cols /), REAL32, 1, 'avg', 1, & buffer, errors=errors) fld_ptr => my_fields%next @@ -68,30 +72,41 @@ program test_hist_buffer exit else ! Check answers - if (buff_ptr%buffer_type() == 'hist_buff_1dreal32_lst_t') then + select case(buff_ptr%buffer_type()) + case ('buff_1dreal32_lst', 'buff_1dreal32_max') do index = 1, num_cols test_val32 = 4.0_real32 * real(index, real32) if (field32(index) /= test_val32) then write(errmsg, '(a,i0,2(a,f8.3))') 'field(', index, & ') = ', field32(index), ' /= ', test_val32 - call hist_add_error('hist_buff_1dreal32_lst_t check', & + call hist_add_error(buff_ptr%buffer_type()//' check', & + trim(errmsg), errors=errors) + end if + end do + case ('buff_1dreal32_min') + do index = 1, num_cols + test_val32 = 2.0_real32 * real(index, real32) + if (field32(index) /= test_val32) then + write(errmsg, '(a,i0,2(a,f8.3))') 'field(', index, & + ') = ', field32(index), ' /= ', test_val32 + call hist_add_error(buff_ptr%buffer_type()//' check', & trim(errmsg), errors=errors) end if end do - else if (buff_ptr%buffer_type() == 'hist_buff_1dreal32_avg_t') then + case ('buff_1dreal32_avg') do index = 1, num_cols test_val32 = 3.0_real32 * real(index, real32) if (field32(index) /= test_val32) then write(errmsg, '(a,i0,2(a,f8.2))') 'field(', index, & ') = ', field32(index), ' /= ', test_val32 - call hist_add_error('hist_buff_1dreal32_avg_t check', & + call hist_add_error(buff_ptr%buffer_type()//' check', & trim(errmsg), errors=errors) end if end do - else + case default call hist_add_error('answer check, unknown buffer type', & buff_ptr%buffer_type(), errors=errors) - end if + end select buff_ptr => buff_ptr%next end if else @@ -106,7 +121,7 @@ program test_hist_buffer subname='my_fields accumulate') else ! Check answers - if (buff_ptr%buffer_type() == 'hist_buff_1dreal64_lst_t') then + if (buff_ptr%buffer_type() == 'buff_1dreal64_lst') then do index = 1, num_cols test_val64 = 6.0_real64 * real(index, real64) - 3.0_real64 if (field64(index) /= test_val64) then