diff --git a/.gitmodules b/.gitmodules index ae27d43f1..35855cd4b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,9 +5,9 @@ fxrequired = AlwaysRequired fxDONOTUSEurl = https://github.com/NCAR/ccpp-framework [submodule "history"] - path = src/history/buffers - url = https://github.com/ESMCI/history_output - fxtag = history01_00 + path = src/history/buffers + url = https://github.com/ESMCI/history_output + fxtag = history01_01 fxrequired = AlwaysRequired fxDONOTUSEurl = https://github.com/ESMCI/history_output [submodule "mpas"] @@ -24,8 +24,8 @@ fxrequired = AlwaysRequired fxDONOTUSEurl = https://github.com/ESCOMP/atmospheric_physics [submodule "rrtmgp-data"] - path = src/physics/utils/rrtmgp-data - url = https://github.com/earth-system-radiation/rrtmgp-data.git + path = src/physics/utils/rrtmgp-data + url = https://github.com/earth-system-radiation/rrtmgp-data.git fxrequired = AlwaysRequired fxtag = v1.8 fxDONOTUSEurl = https://github.com/earth-system-radiation/rrtmgp-data.git @@ -102,8 +102,8 @@ fxrequired = ToplevelRequired fxDONOTUSEurl = https://github.com/ESCOMP/CESM_share [submodule "tools/CUPiD"] - path = tools/CUPiD - url = https://github.com/NCAR/CUPiD.git + path = tools/CUPiD + url = https://github.com/NCAR/CUPiD.git fxtag = v0.4.0 fxrequired = ToplevelRequired fxDONOTUSEurl = https://github.com/NCAR/CUPiD.git diff --git a/cime_config/atm_in_paramgen.py b/cime_config/atm_in_paramgen.py index 5fb1aea1d..6682ac622 100644 --- a/cime_config/atm_in_paramgen.py +++ b/cime_config/atm_in_paramgen.py @@ -1410,7 +1410,7 @@ def append_user_nl_file(self, user_nl_file): #End if (array indices) #Extract value string: - val_str = ' '.join(line_ss[1:]) # the rest is the value string + val_str = ' '.join(line_ss[1:]).strip() # the rest is the value string #Check if value string ends in array continuation: if is_array: diff --git a/cime_config/hist_config.py b/cime_config/hist_config.py index 1be9ac485..ce5ea4a7d 100644 --- a/cime_config/hist_config.py +++ b/cime_config/hist_config.py @@ -345,23 +345,34 @@ def __init__(self, volume, list_type, list_desc): self.__field_names = [] self.__max_namelen = 0 - def _add_item(self, item, pobj, logger): + def _add_item(self, item, pobj, full_field_list, logger): """Add field name, to this object and return True if this field name was added. is a single item to be added. is the ParseObject source of . + is the list of all fields for this volume """ if not _is_string(item)[0]: errmsg = f"Bad diagnostic name, '{item}'" pobj.add_syntax_err(errmsg) return False # end if - if item in self.__field_names: - # Field is a duplicate - ctx = context_string(pobj) - logger.warning(f"Field, '{item}' already in {self.desc} fields for hist volume, {self.volume}{ctx}") - return False - # end if + for field_list in full_field_list: + if item in field_list.__field_names: + if self == field_list: + # Field is a duplicate, same flag (so this is just a warning) + ctx = context_string(pobj) + logger.warning(f"Field, '{item}' already in {self.desc} fields for hist volume, {self.volume}{ctx}") + return False + else: + # Field is a duplicate, different flag (this is an error) + ctx = context_string(pobj) + emsg = f"Field, '{item}' already in {field_list.desc} fields for hist volume, {self.volume}. Cannot also add to {self.desc} fields for the same volume{ctx}" + pobj.add_syntax_err(emsg) + return False + # end if + # end if + # end for self.__field_names.append(item) self.__max_namelen = max(len(item), self.__max_namelen) if logger.getEffectiveLevel() <= logging.DEBUG: @@ -370,18 +381,19 @@ def _add_item(self, item, pobj, logger): # end if return True - def add_fields(self, items, pobj, logger): + def add_fields(self, items, pobj, full_field_list, logger): """Add to this object and return True if all items were added. can be a single item or a list. is the ParseObject source of + is the list of all fields for this volume """ if isinstance(items, list): do_add = True for item in items: - do_add &= self._add_item(item, pobj, logger) + do_add &= self._add_item(item, pobj, full_field_list, logger) # end for else: - do_add = self._add_item(items, pobj, logger) + do_add = self._add_item(items, pobj, full_field_list, logger) # end if return do_add @@ -409,7 +421,6 @@ def num_fields(self): """Return the number of fields in this HistFieldList object.""" return len(self.__field_names) - def output_nl_fieldlist(self, outfile, field_varname): """Output the field name of this HistFieldList object as a namelist variable that is an array of strings. @@ -633,7 +644,7 @@ def add_inst_fields(self, fields, pobj, logger): HistoryVolConfig object. Return True if it was okay to add to list of last fields. """ - add_ok = self.__inst_fields.add_fields(fields, pobj, logger) + add_ok = self.__inst_fields.add_fields(fields, pobj, self.__all_fields, logger) return add_ok def add_avg_fields(self, fields, pobj, logger): @@ -641,28 +652,28 @@ def add_avg_fields(self, fields, pobj, logger): object. Return True if it was okay to add to list of avg fields. """ - add_ok = self.__avg_fields.add_fields(fields, pobj, logger) + add_ok = self.__avg_fields.add_fields(fields, pobj, self.__all_fields, logger) return add_ok def add_min_fields(self, fields, pobj, logger): """Add one or more min_fields to this HistoryVolConfig object. Return True if it was okay to add to list of min fields. """ - add_ok = self.__min_fields.add_fields(fields, pobj, logger) + add_ok = self.__min_fields.add_fields(fields, pobj, self.__all_fields, logger) return add_ok def add_max_fields(self, fields, pobj, logger): """Add one or more max_fields to this HistoryVolConfig object. Return True if it was okay to add to list of max fields. """ - add_ok = self.__max_fields.add_fields(fields, pobj, logger) + add_ok = self.__max_fields.add_fields(fields, pobj, self.__all_fields, logger) return add_ok def add_var_fields(self, fields, pobj, logger): """Add one or more var_fields to this HistoryVolConfig object. Return True if it was okay to add to list of var fields. """ - add_ok = self.__var_fields.add_fields(fields, pobj, logger) + add_ok = self.__var_fields.add_fields(fields, pobj, self.__all_fields, logger) return add_ok def remove_fields(self, fields, pobj, logger): @@ -685,6 +696,32 @@ def remove_fields(self, fields, pobj, logger): # end if return all_removed + def convert_accumulated_fields(self, pobj, logger): + """Move all accumulated fields to instantaneous field list""" + if self.__var_fields.num_fields() > 0: + self.__inst_fields.add_fields(self.__var_fields.field_names, pobj, [self.__inst_fields], logger) + var_field_list = list(self.__var_fields.field_names) + self.__var_fields.remove_fields(var_field_list, pobj, logger) + # end if + + if self.__avg_fields.num_fields() > 0: + self.__inst_fields.add_fields(self.__avg_fields.field_names, pobj, [self.__inst_fields], logger) + avg_field_list = list(self.__avg_fields.field_names) + self.__avg_fields.remove_fields(avg_field_list, pobj, logger) + # end if + + if self.__min_fields.num_fields() > 0: + self.__inst_fields.add_fields(self.__min_fields.field_names, pobj, [self.__inst_fields], logger) + min_field_list = list(self.__min_fields.field_names) + self.__min_fields.remove_fields(min_field_list.field_names, pobj, logger) + # end if + + if self.__max_fields.num_fields() > 0: + self.__inst_fields.add_fields(self.__max_fields.field_names, pobj, [self.__inst_fields], logger) + max_field_list = list(self.__max_fields.field_names) + self.__max_fields.remove_fields(max_field_list, pobj, logger) + # end if + @property def volume(self): """Return the volume for this HistoryVolConfig object""" @@ -794,6 +831,11 @@ def set_output_frequency(self, ofreq, pobj, logger): ctx = context_string(pobj) logger.debug(f"Setting output_frequency to '{ofreq}'{ctx}") # end if + # Convert accumulated fields to instantaneous fields if we're set + # to output every timestep + if self.__output_freq == (1, 'nsteps'): + self.convert_accumulated_fields(pobj, logger) + # end if return True # end if emsg = f"Attempt to set unrecognized output_frequency, '{ofreq}'" diff --git a/cime_config/testdefs/testlist_cam.xml b/cime_config/testdefs/testlist_cam.xml index 250ca93d0..5b1f8c55d 100644 --- a/cime_config/testdefs/testlist_cam.xml +++ b/cime_config/testdefs/testlist_cam.xml @@ -177,6 +177,18 @@ + + + + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/cam/outfrq_kessler_mpas_derecho_history/shell_commands b/cime_config/testdefs/testmods_dirs/cam/outfrq_kessler_mpas_derecho_history/shell_commands new file mode 100644 index 000000000..fd81526e2 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/cam/outfrq_kessler_mpas_derecho_history/shell_commands @@ -0,0 +1 @@ + ./xmlchange CAM_LINKED_LIBS="-lmpas" diff --git a/cime_config/testdefs/testmods_dirs/cam/outfrq_kessler_mpas_derecho_history/user_nl_cam b/cime_config/testdefs/testmods_dirs/cam/outfrq_kessler_mpas_derecho_history/user_nl_cam new file mode 100644 index 000000000..a0260f636 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/cam/outfrq_kessler_mpas_derecho_history/user_nl_cam @@ -0,0 +1,8 @@ +debug_output=0 +hist_add_inst_fields;h3: Q,T,U,V,PS +hist_add_avg_fields;h3: PSDRY +hist_add_max_fields;h3: PHIS +hist_add_min_fields;h3: PDEL, PDELDRY +hist_add_var_fields;h3: ZM, PRECT +hist_output_frequency;h3: 2*nsteps +hist_write_nstep0;h3: .true. diff --git a/src/history/buffers b/src/history/buffers index f59357ea6..dc000fe06 160000 --- a/src/history/buffers +++ b/src/history/buffers @@ -1 +1 @@ -Subproject commit f59357ea6d96cfe949ba5b08995a48dff4b1b987 +Subproject commit dc000fe064e10e60baa9263fbc60eccd1bc84309 diff --git a/src/history/cam_hist_file.F90 b/src/history/cam_hist_file.F90 index 15dc06439..afad4316c 100644 --- a/src/history/cam_hist_file.F90 +++ b/src/history/cam_hist_file.F90 @@ -126,6 +126,7 @@ module cam_hist_file procedure :: write_field => config_write_field procedure :: close_files => config_close_files procedure :: clear_buffers => config_clear_buffers + procedure :: reset_samples => config_reset_samples end type hist_file_t private :: count_array ! Number of non-blank strings in array @@ -762,7 +763,6 @@ subroutine config_set_up_fields(this, possible_field_list) write(errmsg,'(3a)') 'ERROR Field : ',trim(this%field_names(idx)),' not available' call endrun(subname//errmsg, file=__FILE__, line=__LINE__) end select - !call field_ptr%dimensions(dimensions) dimensions = field_ptr%dimensions() field_shape = field_ptr%shape() beg_dim = field_ptr%beg_dims() @@ -770,19 +770,18 @@ subroutine config_set_up_fields(this, possible_field_list) field_info => hist_new_field(this%field_names(idx), & field_ptr%standard_name(), field_ptr%long_name(), & field_ptr%units(), field_ptr%type(), field_ptr%decomp(), & - dimensions, this%accumulate_types(idx), field_ptr%num_levels(), & - field_shape, beg_dims=beg_dim, end_dims=end_dim, & - mixing_ratio=field_ptr%mixing_ratio(), & + dimensions, this%accumulate_types(idx), & + field_ptr%num_levels(), field_shape, & + field_ptr%fill_value(), beg_dims=beg_dim, & + end_dims=end_dim, mixing_ratio=field_ptr%mixing_ratio(), & flag_xyfill=field_ptr%flag_xyfill(), & sampling_seq=field_ptr%sampling_sequence()) call hist_new_buffer(field_info, field_shape, & - this%rl_kind, 1, this%accumulate_types(idx), 1, errors=errors) - if (masterproc) then + 1, this%accumulate_types(idx), 1, errors=errors) + if (masterproc .and. errors%num_errors() > 0) then call errors%output(iulog) + call endrun(subname//' error(s) during buffer creation') end if - ! peverwhee - TODO: create additional buffer(s) for other accum types -! call hist_new_buffer(field_info, field_shape, & -! this%rl_kind, 1, this%accumulate_types(idx), 1) ! Add to field list array and hash table this%field_list(idx) = field_info call this%field_list_hash_table%add_hash_key(field_info) @@ -914,7 +913,6 @@ subroutine config_define_file(this, restart, logname, host, model_doi_url) use cam_history_support, only: write_hist_coord_attrs use cam_history_support, only: write_hist_coord_vars use cam_history_support, only: max_chars - use cam_history_support, only: fillvalue use shr_kind_mod, only: r4 => shr_kind_r4 use time_manager, only: get_ref_date, timemgr_get_calendar_cf use time_manager, only: get_step_size @@ -1293,14 +1291,14 @@ subroutine config_define_file(this, restart, logname, host, model_doi_url) ! netCDF-aware tools (ncview, ncdump, etc.) recognise fill ! values. The attribute type must match the variable type. if (ncreal == pio_double) then - ierr = pio_put_att(this%hist_files(split_file_index), varid, '_FillValue', fillvalue) + ierr = pio_put_att(this%hist_files(split_file_index), varid, '_FillValue', this%field_list(field_index)%fill_value()) call cam_pio_handle_error(ierr, subname//'cannot define _FillValue for '//trim(fname_tmp)) - ierr = pio_put_att(this%hist_files(split_file_index), varid, 'missing_value', fillvalue) + ierr = pio_put_att(this%hist_files(split_file_index), varid, 'missing_value', this%field_list(field_index)%fill_value()) call cam_pio_handle_error(ierr, subname//'cannot define missing_value for '//trim(fname_tmp)) else - ierr = pio_put_att(this%hist_files(split_file_index), varid, '_FillValue', real(fillvalue, r4)) + ierr = pio_put_att(this%hist_files(split_file_index), varid, '_FillValue', real(this%field_list(field_index)%fill_value(), r4)) call cam_pio_handle_error(ierr, subname//'cannot define _FillValue for '//trim(fname_tmp)) - ierr = pio_put_att(this%hist_files(split_file_index), varid, 'missing_value', real(fillvalue, r4)) + ierr = pio_put_att(this%hist_files(split_file_index), varid, 'missing_value', real(this%field_list(field_index)%fill_value(), r4)) call cam_pio_handle_error(ierr, subname//'cannot define missing_value for '//trim(fname_tmp)) end if end if @@ -1515,12 +1513,14 @@ end subroutine config_write_time_dependent_variables subroutine config_write_field(this, field, split_file_index, restart, & sample_index, field_index, field_precision) use pio, only: PIO_OFFSET_KIND, pio_setframe - use hist_buffer, only: hist_buffer_t - use hist_api, only: hist_buffer_norm_value + use hist_api, only: hist_field_norm_value use cam_grid_support, only: cam_grid_write_dist_array use cam_abortutils, only: check_allocate, endrun use hist_field, only: hist_field_info_t use shr_kind_mod, only: r4 => shr_kind_r4 + use hist_msg_handler, only: hist_log_messages + use cam_logfile, only: iulog + use cam_abortutils, only: endrun ! Dummy arguments class(hist_file_t), intent(inout) :: this type(hist_field_info_t), intent(inout) :: field @@ -1542,9 +1542,8 @@ subroutine config_write_field(this, field, split_file_index, restart, & type(var_desc_t) :: varid integer :: field_decomp integer :: idx - real(r8), allocatable :: field_data_r8(:,:) - real(r4), allocatable :: field_data_r4(:,:) - class(hist_buffer_t), pointer :: buff_ptr + real(r8), allocatable :: field_data(:,:) + type(hist_log_messages) :: errors character(len=CL) :: errmsg character(len=*), parameter :: subname = 'config_write_field: ' @@ -1556,19 +1555,19 @@ subroutine config_write_field(this, field, split_file_index, restart, & frank = size(field_shape) if (frank == 1) then if (trim(field_precision) == 'REAL32') then - allocate(field_data_r4(end_dims(1) - beg_dims(1) + 1, 1), stat=ierr, errmsg=errmsg) - call check_allocate(ierr, subname, 'field_data_r4', file=__FILE__, line=__LINE__-1, errmsg=errmsg) + allocate(field_data(end_dims(1) - beg_dims(1) + 1, 1), stat=ierr, errmsg=errmsg) + call check_allocate(ierr, subname, 'field_data', file=__FILE__, line=__LINE__-1, errmsg=errmsg) else - allocate(field_data_r8(end_dims(1) - beg_dims(1) + 1, 1), stat=ierr, errmsg=errmsg) - call check_allocate(ierr, subname, 'field_data_r8', file=__FILE__, line=__LINE__-1, errmsg=errmsg) + allocate(field_data(end_dims(1) - beg_dims(1) + 1, 1), stat=ierr, errmsg=errmsg) + call check_allocate(ierr, subname, 'field_data', file=__FILE__, line=__LINE__-1, errmsg=errmsg) end if else if (trim(field_precision) == 'REAL32') then - allocate(field_data_r4(end_dims(1) - beg_dims(1) + 1, field_shape(2)), stat=ierr, errmsg=errmsg) - call check_allocate(ierr, subname, 'field_data_r4', file=__FILE__, line=__LINE__-1, errmsg=errmsg) + allocate(field_data(end_dims(1) - beg_dims(1) + 1, field_shape(2)), stat=ierr, errmsg=errmsg) + call check_allocate(ierr, subname, 'field_data', file=__FILE__, line=__LINE__-1, errmsg=errmsg) else - allocate(field_data_r8(end_dims(1) - beg_dims(1) + 1, field_shape(2)), stat=ierr, errmsg=errmsg) - call check_allocate(ierr, subname, 'field_data_r8', file=__FILE__, line=__LINE__-1, errmsg=errmsg) + allocate(field_data(end_dims(1) - beg_dims(1) + 1, field_shape(2)), stat=ierr, errmsg=errmsg) + call check_allocate(ierr, subname, 'field_data', file=__FILE__, line=__LINE__-1, errmsg=errmsg) end if end if ! Shape of array @@ -1586,30 +1585,40 @@ subroutine config_write_field(this, field, split_file_index, restart, & do patch_idx = 1, num_patches varid = this%file_varids(field_index, patch_idx) call pio_setframe(this%hist_files(split_file_index), varid, int(sample_index,kind=PIO_OFFSET_KIND)) - buff_ptr => field%buffers if (frank == 1) then + call hist_field_norm_value(field, field_data(:,1), logger=errors) + if (errors%num_errors() > 0) then + call errors%output(iulog) + write(errmsg, *) subname, 'ERROR writing field "', trim(field%diag_name()), '"' + call endrun(errmsg) + end if if (trim(field_precision) == 'REAL32') then - call hist_buffer_norm_value(buff_ptr, field_data_r4(:,1)) call cam_grid_write_dist_array(this%hist_files(split_file_index), field_decomp, (/dim_sizes(1)/), & - field_shape, field_data_r4(:,1), varid) + field_shape, real(field_data(:,1), r4), varid) else - call hist_buffer_norm_value(buff_ptr, field_data_r8(:,1)) call cam_grid_write_dist_array(this%hist_files(split_file_index), field_decomp, (/dim_sizes(1)/), & - field_shape, field_data_r8(:,1), varid) + field_shape, field_data(:,1), varid) end if else + call hist_field_norm_value(field, field_data, logger=errors) + if (errors%num_errors() > 0) then + call errors%output(iulog) + write(errmsg, *) subname, 'ERROR writing field "', trim(field%diag_name()), '"' + call endrun(errmsg) + end if if (trim(field_precision) == 'REAL32') then - call hist_buffer_norm_value(buff_ptr, field_data_r4) call cam_grid_write_dist_array(this%hist_files(split_file_index), field_decomp, dim_sizes(1:frank), & - field_shape, field_data_r4, varid) + field_shape, real(field_data, r4), varid) else - call hist_buffer_norm_value(buff_ptr, field_data_r8) call cam_grid_write_dist_array(this%hist_files(split_file_index), field_decomp, dim_sizes(1:frank), & - field_shape, field_data_r8, varid) + field_shape, field_data, varid) end if end if end do + ! Clear the buffers after writing + call field%clear_buffers(logger=errors) + end subroutine config_write_field ! ======================================================================== @@ -1659,7 +1668,6 @@ subroutine config_clear_buffers(this) integer :: field_idx type(hist_log_messages) :: errors - do field_idx = 1, size(this%field_list) call this%field_list(field_idx)%clear_buffers(logger=errors) if (masterproc) then @@ -1671,6 +1679,16 @@ end subroutine config_clear_buffers ! ======================================================================== + subroutine config_reset_samples(this) + + class(hist_file_t), intent(inout) :: this + + this%num_samples = 0 + + end subroutine config_reset_samples + +! ======================================================================== + pure function count_array(arr_in) result(arr_count) ! Dummy arguments character(len=*), intent(in) :: arr_in(:) @@ -1818,26 +1836,18 @@ subroutine read_namelist_entry(unitn, hfile_config, hist_inst_fields, & masterprocid, mpicom, ierr) end if if (num_fields_avg > 0) then - call endrun(subname//"ERROR, average fields not yet implemented", & - file=__FILE__, line=__LINE__) call MPI_Bcast(hist_avg_fields(:), max_fldlen*num_fields_avg, MPI_CHARACTER, & masterprocid, mpicom, ierr) end if if (num_fields_min > 0) then - call endrun(subname//"ERROR, minimum fields not yet implemented", & - file=__FILE__, line=__LINE__) call MPI_Bcast(hist_min_fields(:), max_fldlen*num_fields_min, MPI_CHARACTER, & masterprocid, mpicom, ierr) end if if (num_fields_max > 0) then - call endrun(subname//"ERROR, maximum fields not yet implemented", & - file=__FILE__, line=__LINE__) call MPI_Bcast(hist_max_fields(:), max_fldlen*num_fields_max, MPI_CHARACTER, & masterprocid, mpicom, ierr) end if if (num_fields_var > 0) then - call endrun(subname//"ERROR, standard deviation fields not yet implemented", & - file=__FILE__, line=__LINE__) call MPI_Bcast(hist_var_fields(:), max_fldlen*num_fields_var, MPI_CHARACTER, & masterprocid, mpicom, ierr) end if diff --git a/src/history/cam_history.F90 b/src/history/cam_history.F90 index d96d9c466..a023b5574 100644 --- a/src/history/cam_history.F90 +++ b/src/history/cam_history.F90 @@ -179,6 +179,8 @@ subroutine history_write_files() write_history = .true. else write_history = .false. + ! Zero out the history buffers so nothing gets accumulated from nstep0 + call hist_configs(file_idx)%clear_buffers() end if end if if (.not. write_history) then @@ -211,6 +213,10 @@ subroutine history_write_files() call hist_configs(file_idx)%define_file(restart, logname, host, model_doi_url) end if call hist_configs(file_idx)%write_time_dependent_variables(restart) + if (nstep == 0) then + ! Reset samples if nstep0 was written + call hist_configs(file_idx)%reset_samples() + end if end do end subroutine history_write_files @@ -376,9 +382,10 @@ end subroutine set_up_field_list_hash_table !=========================================================================== subroutine history_add_field_1d(diagnostic_name, standard_name, vdim_name, & - avgflag, units, gridname, flag_xyfill, mixing_ratio) + avgflag, units, gridname, flag_xyfill, fill_value, mixing_ratio) use cam_history_support, only: get_hist_coord_index, max_chars, horiz_only use cam_abortutils, only: endrun, check_allocate + use shr_kind_mod, only: r8 => shr_kind_r8 !----------------------------------------------------------------------- ! ! Purpose: Add a field to the master field list @@ -399,6 +406,7 @@ subroutine history_add_field_1d(diagnostic_name, standard_name, vdim_name, & character(len=*), intent(in) :: units ! units of fname (max_chars) character(len=*), optional, intent(in) :: gridname logical, optional, intent(in) :: flag_xyfill + real(r8), optional, intent(in) :: fill_value character(len=*), optional, intent(in) :: mixing_ratio ! @@ -424,14 +432,14 @@ subroutine history_add_field_1d(diagnostic_name, standard_name, vdim_name, & dimnames(1) = trim(vdim_name) end if call history_add_field(diagnostic_name, standard_name, dimnames, avgflag, units, & - gridname=gridname, flag_xyfill=flag_xyfill, mixing_ratio=mixing_ratio) + gridname=gridname, flag_xyfill=flag_xyfill, fill_value=fill_value, mixing_ratio=mixing_ratio) end subroutine history_add_field_1d !=========================================================================== subroutine history_add_field_nd(diagnostic_name, standard_name, dimnames, avgflag, & - units, gridname, flag_xyfill, mixing_ratio) + units, gridname, flag_xyfill, fill_value, mixing_ratio) !----------------------------------------------------------------------- ! ! Purpose: Add a field to the master field list - generic; called from @@ -447,7 +455,7 @@ subroutine history_add_field_nd(diagnostic_name, standard_name, dimnames, avgfla use cam_grid_support, only: cam_grid_dimensions use cam_grid_support, only: cam_grid_id, cam_grid_is_zonal use cam_grid_support, only: cam_grid_get_array_bounds - use cam_history_support, only: lookup_hist_coord_indices + use cam_history_support, only: lookup_hist_coord_indices, fillvalue use cam_history_support, only: hist_coord_find_levels, hist_coords use cam_history_support, only: max_fldlen=>max_fieldname_len, max_chars, fieldname_len use cam_hist_file, only: strip_suffix @@ -455,6 +463,7 @@ subroutine history_add_field_nd(diagnostic_name, standard_name, dimnames, avgfla use cam_abortutils, only: endrun, check_allocate use spmd_utils, only: masterproc use string_utils, only: stringify + use shr_kind_mod, only: r8 => shr_kind_r8 character(len=*), intent(in) :: diagnostic_name character(len=*), intent(in) :: standard_name @@ -463,6 +472,7 @@ subroutine history_add_field_nd(diagnostic_name, standard_name, dimnames, avgfla character(len=*), intent(in) :: units ! units of fname (max_chars) character(len=*), optional, intent(in) :: gridname logical, optional, intent(in) :: flag_xyfill + real(r8), optional, intent(in) :: fill_value character(len=*), optional, intent(in) :: mixing_ratio ! Local variables @@ -482,6 +492,7 @@ subroutine history_add_field_nd(diagnostic_name, standard_name, dimnames, avgfla character(len=max_fldlen) :: cell_methods character(len=3) :: mixing_ratio_loc character(len=*), parameter :: subname = 'history_add_field_nd: ' + real(r8) :: fill_value_local if (size(hist_configs) > 0) then if (hist_configs(1)%file_is_setup()) then @@ -539,6 +550,12 @@ subroutine history_add_field_nd(diagnostic_name, standard_name, dimnames, avgfla call endrun('history_add_field_nd: '//trim(errmsg)) end if + if (present(fill_value)) then + fill_value_local = fill_value + else + fill_value_local = fillvalue + end if + ! Indicate if some field pre-processing occurred (e.g., zonal mean) if (cam_grid_is_zonal(grid_decomp)) then call cam_grid_get_coord_names(grid_decomp, coord_name, errmsg) @@ -593,7 +610,8 @@ subroutine history_add_field_nd(diagnostic_name, standard_name, dimnames, avgfla else field_ptr%next => hist_new_field(diagnostic_name, & standard_name, standard_name, units, 'real', grid_decomp, & - mdim_indices, avgflag, num_levels, field_shape, mixing_ratio=mixing_ratio_loc, & + mdim_indices, avgflag, num_levels, field_shape, fill_value_local, & + mixing_ratio=mixing_ratio_loc, & dim_bounds=dimbounds, mdim_sizes=mdim_sizes, cell_methods=cell_methods, & flag_xyfill=flag_xyfill) exit @@ -602,7 +620,8 @@ subroutine history_add_field_nd(diagnostic_name, standard_name, dimnames, avgfla else possible_field_list_head => hist_new_field(diagnostic_name, & standard_name, standard_name, units, 'real', grid_decomp, & - mdim_indices, avgflag, num_levels, field_shape, mixing_ratio=mixing_ratio_loc, & + mdim_indices, avgflag, num_levels, field_shape, fill_value_local, & + mixing_ratio=mixing_ratio_loc, & dim_bounds=dimbounds, mdim_sizes=mdim_sizes, cell_methods=cell_methods, & flag_xyfill=flag_xyfill) end if @@ -652,6 +671,10 @@ subroutine history_out_field_1d(diagnostic_name, field_values) call hist_field_accumulate(field_info, field_values, 1, logger=logger) if (masterproc) then call logger%output(iulog) + if (logger%num_errors() > 0) then + write(errmsg, *) subname, 'Error during hist_field_accumulate for "', trim(diagnostic_name), '"' + call endrun(errmsg) + end if end if end do @@ -699,6 +722,10 @@ subroutine history_out_field_2d(diagnostic_name, field_values) call hist_field_accumulate(field_info, field_values, 1, logger=logger) if (masterproc) then call logger%output(iulog) + if (logger%num_errors() > 0) then + write(errmsg, *) subname, 'Error during hist_field_accumulate for "', trim(diagnostic_name), '"' + call endrun(errmsg) + end if end if end do @@ -743,6 +770,10 @@ subroutine history_out_field_3d(diagnostic_name, field_values) !call hist_field_accumulate(field_info, real(field_values, REAL64), 1) !if (masterproc) then ! call logger%output(iulog) + ! if (logger%num_errors() > 0) then + ! write(errmsg, *) subname, 'Error during hist_field_accumulate for "', trim(diagnostic_name), '"' + ! call endrun(errmsg) + ! end if !end if end do @@ -812,7 +843,7 @@ subroutine history_wrap_up(restart_write, last_timestep) full = .false. num_samples = hist_configs(file_idx)%get_num_samples() max_frames = hist_configs(file_idx)%max_frame() - if (mod(num_samples, max_frames) == 0 .and. num_samples > 0) then + if (nstep == 0 .or. (mod(num_samples, max_frames) == 0 .and. num_samples > 0)) then full = .true. end if if ((full .or. (last_timestep .and. num_samples >= 1)) .and. & @@ -841,7 +872,6 @@ subroutine history_wrap_up(restart_write, last_timestep) write(iulog,9004) end if end if - call hist_configs(file_idx)%clear_buffers() end do 9003 format(' Output at NSTEP = ',i10,/, & ' Number of time samples on this file = ',i10,/, & diff --git a/test/unit/python/sample_files/hist_config_files/atm_in_flat b/test/unit/python/sample_files/hist_config_files/atm_in_flat index 86efcd4bb..bd5cb4296 100644 --- a/test/unit/python/sample_files/hist_config_files/atm_in_flat +++ b/test/unit/python/sample_files/hist_config_files/atm_in_flat @@ -1,6 +1,6 @@ &hist_config_arrays_nl - hist_num_inst_fields = 3 + hist_num_inst_fields = 4 hist_num_avg_fields = 3 hist_num_min_fields = 3 hist_num_max_fields = 2 @@ -23,9 +23,9 @@ &hist_file_config_nl hist_volume = 'h3' - hist_inst_fields = 'T', 'U', 'V' + hist_inst_fields = 'T ', 'U ', 'V ', 'PS' hist_max_frames = 24 - hist_output_frequency = '2*nsteps' + hist_output_frequency = '1*nsteps' hist_precision = 'REAL64' hist_file_type = 'history' hist_filename_spec = '%c.cam.%u%f.%y-%m-%d-%s.nc' diff --git a/test/unit/python/sample_files/hist_config_files/user_nl_cam_flat b/test/unit/python/sample_files/hist_config_files/user_nl_cam_flat index a03e0c603..4d208ecc8 100644 --- a/test/unit/python/sample_files/hist_config_files/user_nl_cam_flat +++ b/test/unit/python/sample_files/hist_config_files/user_nl_cam_flat @@ -13,7 +13,9 @@ use_topo_file = .false. ! More history configuration hist_add_inst_fields;h3: T, U, V -hist_output_frequency;h3: 2*nsteps +! This avg field will be converted to inst because output freq = 1*nsteps +hist_add_avg_fields;h3: PS +hist_output_frequency;h3: 1*nsteps hist_precision;h3: REAL64 hist_max_frames;h3: 24 diff --git a/test/unit/python/test_hist_config.py b/test/unit/python/test_hist_config.py index 57fad85f7..7a1b0b42f 100644 --- a/test/unit/python/test_hist_config.py +++ b/test/unit/python/test_hist_config.py @@ -117,7 +117,7 @@ def test_flat_user_nl_cam(self): self._test_config(hconfig, 'h1', 'REAL32', 30, (14, 'hours'), 'history', '.false.', '%c.cam.%u%f.%y-%m-%d-%s.nc', '%c.cam.r%u.%y-%m-%d-%s.nc') self.assertTrue('h3' in hist_configs, msg="'h3' not in hist_configs") hconfig = hist_configs['h3'] - self._test_config(hconfig, 'h3', 'REAL64', 24, (2, 'nsteps'), 'history', '.false.', '%c.cam.%u%f.%y-%m-%d-%s.nc', '%c.cam.r%u.%y-%m-%d-%s.nc') + self._test_config(hconfig, 'h3', 'REAL64', 24, (1, 'nsteps'), 'history', '.false.', '%c.cam.%u%f.%y-%m-%d-%s.nc', '%c.cam.r%u.%y-%m-%d-%s.nc') _LOGGER.setLevel(logging.DEBUG) # Write out the namelist file with open(out_source, 'w', encoding='utf-8') as nl_file: @@ -188,13 +188,14 @@ def test_bad_user_nl_cam(self): # Read in file: file_lines = old_file.readlines() # Edit to add bad lines - file_lines[8] = "" + file_lines[8] = "hist_add_inst_fields;h3: T\n" file_lines[9] = "hist_remove_fields;h0:\n" file_lines[10] = "hist_output_frequency;h0: 1+nmonths\n" file_lines[11] = "hist_precision;h0: REAL34\n" file_lines[13] = "hist_add_inst_fields;h3: T&U&V\n" file_lines[16] = "hist_max_frames;h3: -24\n" file_lines[17] = "hist_write_nstep0;h3: treu\n" + file_lines[18] = "hist_add_avg_fields;h3: T\n" # end with # Create a new modified version of the file with the bad entries @@ -215,7 +216,8 @@ def test_bad_user_nl_cam(self): "Found invalid identifiers", "T&U&V, at", "Attempt to set max frames to '-24', must be a positive integer, at", - "hist_write_nstep0 must be one of .false., .true., f, false, t, true, at"] + "hist_write_nstep0 must be one of .false., .true., f, false, t, true, at", + "Field, 'T' already in instantaneous fields for hist volume, h3. Cannot also add to average fields for the same volume, at"] # Check error messages are as expected for index, errmsg in enumerate(exception_split):