From 2b8dcc2fe2f51728ed5267bb0ae767cd1b2749aa Mon Sep 17 00:00:00 2001 From: apcraig Date: Mon, 25 Mar 2024 11:51:05 -0600 Subject: [PATCH] Add checks that individual history streams have unique filenames. If they do not, then streams will overwrite each other. With this change, abort in that case. Added in ice_history_shared.F90, subroutine construct_filename. The implementation tracks the latest filenames for each stream and checks versus those names. Because the file naming convention relies heavily on the current model date/time, this should be adequate (versus keeping track of all history filenames ever used). Updated the cstream string in subroutine construct_filename. It was hardwired to len=1 which probably was an error. Made it len=char_len to support longer hist_suffix character strings in filenames. Updated the ncfile variable implementation in ice_write_hist in io_binary, io_netcdf, and io_pio2. It was defined as an array of length max_nstrm, and was changed to a non-array character string. The array implementation served no purpose. Modified the set_nml.histinst to add hist_suffix values for each stream. The latest code modifications cause The current test suite to fail with "histall,histinst" because it creates multiple streams with the same filename. Setting hist_suffix for histinst fixes this (and tests hist_suffix). Clean up abort calls in ice_history_shared.F90, add space before "ERROR:". Update the documentation describing history streams. Closes #915 --- .../cicedyn/analysis/ice_history_shared.F90 | 42 ++++++++++++----- cicecore/cicedyn/general/ice_init.F90 | 2 +- .../io/io_binary/ice_history_write.F90 | 16 +++---- .../io/io_netcdf/ice_history_write.F90 | 14 +++--- .../io/io_pio2/ice_history_write.F90 | 12 ++--- .../scripts/options/set_nml.histinst | 1 + doc/source/user_guide/ug_case_settings.rst | 2 +- doc/source/user_guide/ug_implementation.rst | 45 +++++++++---------- 8 files changed, 77 insertions(+), 57 deletions(-) diff --git a/cicecore/cicedyn/analysis/ice_history_shared.F90 b/cicecore/cicedyn/analysis/ice_history_shared.F90 index 62ee3c2ba..d6fa78542 100644 --- a/cicecore/cicedyn/analysis/ice_history_shared.F90 +++ b/cicecore/cicedyn/analysis/ice_history_shared.F90 @@ -57,7 +57,7 @@ module ice_history_shared history_rearranger ! history file rearranger, box or subset for pio character (len=char_len), public :: & - hist_suffix(max_nstrm) ! appended to 'h' in filename when not 'x' + hist_suffix(max_nstrm) ! appended to history_file in filename integer (kind=int_kind), public :: & history_iotasks , & ! iotasks, root, stride defines io pes for pio @@ -757,18 +757,22 @@ subroutine construct_filename(ncfile,suffix,ns) dt use ice_restart_shared, only: lenstr - character (char_len_long), intent(inout) :: ncfile - character (len=2), intent(in) :: suffix + character (len=*), intent(inout) :: ncfile + character (len=*), intent(in) :: suffix integer (kind=int_kind), intent(in) :: ns integer (kind=int_kind) :: iyear, imonth, iday, isec - character (len=1) :: cstream + integer (kind=int_kind) :: n + character (len=char_len) :: cstream + character (len=char_len_long), save :: ncfile_last(max_nstrm) = 'UnDefineD' character(len=*), parameter :: subname = '(construct_filename)' iyear = myear imonth = mmonth iday = mday isec = int(msec - dt,int_kind) + cstream = '' + if (hist_suffix(ns) /= 'x') cstream = hist_suffix(ns) ! construct filename if (write_ic) then @@ -793,9 +797,6 @@ subroutine construct_filename(ncfile,suffix,ns) endif endif - cstream = '' - if (hist_suffix(ns) /= 'x') cstream = hist_suffix(ns) - if (hist_avg(ns)) then ! write averaged data if (histfreq(ns) == '1' .and. histfreq_n(ns) == 1) then ! timestep write(ncfile,'(a,a,i4.4,a,i2.2,a,i2.2,a,i5.5,a,a)') & @@ -831,6 +832,25 @@ subroutine construct_filename(ncfile,suffix,ns) endif + ! Check whether the filename is already in use. + ! Same filename in multiple history streams leads to files being overwritten (not good). + ! The current filename convention means we just have to check latest filename, + ! not all filenames ever generated because of use of current model date/time in filename. + + ! write(nu_diag,'(2a,i2,1x,a)') subname, 'debug ncfile= ',ns,trim(ncfile) + do n = 1,max_nstrm + ! write(nu_diag,'(2a,i2,1x,a)') subname, 'debug nfile_last= ',n,trim(ncfile_last(n)) + if (ncfile == ncfile_last(n)) then + write(nu_diag,*) subname,' history stream = ',ns + write(nu_diag,*) subname,' history filename = ',trim(ncfile) + write(nu_diag,*) subname,' filename in use for stream ',n + write(nu_diag,*) subname,' filename for stream ',trim(ncfile_last(n)) + write(nu_diag,*) subname,' Use namelist hist_suffix so history filenames are unique' + call abort_ice(subname//' ERROR: history filename already used for another history stream '//trim(ncfile)) + endif + enddo + ncfile_last(ns) = ncfile + end subroutine construct_filename !======================================================================= @@ -891,7 +911,7 @@ subroutine define_hist_field(id, vname, vunit, vcoord, vcellmeas, & if(present(mask_ice_free_points)) l_mask_ice_free_points = mask_ice_free_points if (histfreq(ns) == 'x') then - call abort_ice(subname//'ERROR: define_hist_fields has histfreq x') + call abort_ice(subname//' ERROR: define_hist_fields has histfreq x') endif if (ns == 1) id(:) = 0 @@ -901,7 +921,7 @@ subroutine define_hist_field(id, vname, vunit, vcoord, vcellmeas, & if (vhistfreq(ns1:ns1) == histfreq(ns)) then if (ns1 > 1 .and. index(vhistfreq(1:ns1-1),'x') /= 0) then - call abort_ice(subname//'ERROR: history frequency variable f_' // vname // ' can''t contain ''x'' along with active frequencies') + call abort_ice(subname//' ERROR: history frequency variable f_' // vname // ' can''t contain ''x'' along with active frequencies') endif num_avail_hist_fields_tot = num_avail_hist_fields_tot + 1 @@ -931,7 +951,7 @@ subroutine define_hist_field(id, vname, vunit, vcoord, vcellmeas, & write(nu_diag,*) subname,' num_avail_hist_fields_tot = ',num_avail_hist_fields_tot write(nu_diag,*) subname,' max_avail_hist_fields = ',max_avail_hist_fields endif - call abort_ice(subname//'ERROR: Need in computation of max_avail_hist_fields') + call abort_ice(subname//' ERROR: Need in computation of max_avail_hist_fields') endif if (num_avail_hist_fields_tot /= & @@ -947,7 +967,7 @@ subroutine define_hist_field(id, vname, vunit, vcoord, vcellmeas, & if (my_task == master_task) then write(nu_diag,*) subname,' num_avail_hist_fields_tot = ',num_avail_hist_fields_tot endif - call abort_ice(subname//'ERROR: in num_avail_hist_fields') + call abort_ice(subname//' ERROR: in num_avail_hist_fields') endif id(ns) = num_avail_hist_fields_tot diff --git a/cicecore/cicedyn/general/ice_init.F90 b/cicecore/cicedyn/general/ice_init.F90 index 24ac40db3..a7f84e46e 100644 --- a/cicecore/cicedyn/general/ice_init.F90 +++ b/cicecore/cicedyn/general/ice_init.F90 @@ -938,7 +938,7 @@ subroutine input_data call broadcast_scalar(histfreq_base(n), master_task) call broadcast_scalar(dumpfreq(n), master_task) call broadcast_scalar(dumpfreq_base(n), master_task) - call broadcast_scalar(hist_suffix(n), master_task) + call broadcast_scalar(hist_suffix(n), master_task) enddo call broadcast_array(hist_avg, master_task) call broadcast_array(histfreq_n, master_task) diff --git a/cicecore/cicedyn/infrastructure/io/io_binary/ice_history_write.F90 b/cicecore/cicedyn/infrastructure/io/io_binary/ice_history_write.F90 index b16d00f07..dae187eae 100644 --- a/cicecore/cicedyn/infrastructure/io/io_binary/ice_history_write.F90 +++ b/cicecore/cicedyn/infrastructure/io/io_binary/ice_history_write.F90 @@ -58,7 +58,7 @@ subroutine ice_write_hist(ns) integer (kind=int_kind) :: k,n,nn,nrec,nbits character (char_len) :: title - character (char_len_long) :: ncfile(max_nstrm), hdrfile + character (char_len_long) :: ncfile, hdrfile integer (kind=int_kind) :: icategory,i_aice @@ -85,26 +85,26 @@ subroutine ice_write_hist(ns) if (my_task == master_task) then - call construct_filename(ncfile(ns),'da',ns) + call construct_filename(ncfile,'da',ns) ! add local directory path name to ncfile if (write_ic) then - ncfile(ns) = trim(incond_dir)//ncfile(ns) + ncfile = trim(incond_dir)//ncfile else - ncfile(ns) = trim(history_dir)//ncfile(ns) + ncfile = trim(history_dir)//ncfile endif - hdrfile = trim(ncfile(ns))//'.hdr' + hdrfile = trim(ncfile)//'.hdr' !----------------------------------------------------------------- ! create history files !----------------------------------------------------------------- - call ice_open(nu_history, ncfile(ns), nbits) ! direct access + call ice_open(nu_history, ncfile, nbits) ! direct access open(nu_hdr,file=hdrfile,form='formatted',status='unknown') ! ascii title = 'sea ice model: CICE' write (nu_hdr, 999) 'source',title,' ' - write (nu_hdr, 999) 'file name contains model date',trim(ncfile(ns)),' ' + write (nu_hdr, 999) 'file name contains model date',trim(ncfile),' ' #ifdef CESMCOUPLED write (nu_hdr, 999) 'runid',runid,' ' #endif @@ -391,7 +391,7 @@ subroutine ice_write_hist(ns) close (nu_hdr) ! header file close (nu_history) ! data file write (nu_diag,*) ' ' - write (nu_diag,*) 'Finished writing ',trim(ncfile(ns)) + write (nu_diag,*) 'Finished writing ',trim(ncfile) endif end subroutine ice_write_hist diff --git a/cicecore/cicedyn/infrastructure/io/io_netcdf/ice_history_write.F90 b/cicecore/cicedyn/infrastructure/io/io_netcdf/ice_history_write.F90 index 396c52e37..7d29fc4cc 100644 --- a/cicecore/cicedyn/infrastructure/io/io_netcdf/ice_history_write.F90 +++ b/cicecore/cicedyn/infrastructure/io/io_netcdf/ice_history_write.F90 @@ -102,7 +102,7 @@ subroutine ice_write_hist (ns) real (kind=dbl_kind) :: ltime2 character (char_len) :: title, cal_units, cal_att character (char_len) :: time_period_freq = 'none' - character (char_len_long) :: ncfile(max_nstrm) + character (char_len_long) :: ncfile real (kind=dbl_kind) :: secday, rad_to_deg integer (kind=int_kind) :: ind,boundid, lprecision @@ -139,13 +139,13 @@ subroutine ice_write_hist (ns) if (my_task == master_task) then - call construct_filename(ncfile(ns),'nc',ns) + call construct_filename(ncfile,'nc',ns) ! add local directory path name to ncfile if (write_ic) then - ncfile(ns) = trim(incond_dir)//ncfile(ns) + ncfile = trim(incond_dir)//ncfile else - ncfile(ns) = trim(history_dir)//ncfile(ns) + ncfile = trim(history_dir)//ncfile endif ! create file @@ -161,8 +161,8 @@ subroutine ice_write_hist (ns) call abort_ice(subname//' ERROR: history_format not allowed for '//trim(history_format), & file=__FILE__, line=__LINE__) endif - status = nf90_create(ncfile(ns), iflag, ncid) - call ice_check_nc(status, subname// ' ERROR: creating history ncfile '//ncfile(ns), & + status = nf90_create(ncfile, iflag, ncid) + call ice_check_nc(status, subname// ' ERROR: creating history ncfile '//ncfile, & file=__FILE__, line=__LINE__) !----------------------------------------------------------------- @@ -1160,7 +1160,7 @@ subroutine ice_write_hist (ns) call ice_check_nc(status, subname// ' ERROR: closing netCDF history file', & file=__FILE__, line=__LINE__) write(nu_diag,*) ' ' - write(nu_diag,*) 'Finished writing ',trim(ncfile(ns)) + write(nu_diag,*) 'Finished writing ',trim(ncfile) endif #else diff --git a/cicecore/cicedyn/infrastructure/io/io_pio2/ice_history_write.F90 b/cicecore/cicedyn/infrastructure/io/io_pio2/ice_history_write.F90 index 0281f3721..b8971a872 100644 --- a/cicecore/cicedyn/infrastructure/io/io_pio2/ice_history_write.F90 +++ b/cicecore/cicedyn/infrastructure/io/io_pio2/ice_history_write.F90 @@ -93,7 +93,7 @@ subroutine ice_write_hist (ns) character (len=8) :: cdate character (len=char_len_long) :: title, cal_units, cal_att character (len=char_len) :: time_period_freq = 'none' - character (len=char_len_long) :: ncfile(max_nstrm) + character (len=char_len_long) :: ncfile integer (kind=int_kind) :: icategory,ind,i_aice,boundid, lprecision @@ -156,15 +156,15 @@ subroutine ice_write_hist (ns) file=__FILE__, line=__LINE__) if (my_task == master_task) then - call construct_filename(ncfile(ns),'nc',ns) + call construct_filename(ncfile,'nc',ns) ! add local directory path name to ncfile if (write_ic) then - ncfile(ns) = trim(incond_dir)//ncfile(ns) + ncfile = trim(incond_dir)//ncfile else - ncfile(ns) = trim(history_dir)//ncfile(ns) + ncfile = trim(history_dir)//ncfile endif - filename = ncfile(ns) + filename = ncfile end if call broadcast_scalar(filename, master_task) @@ -1252,7 +1252,7 @@ subroutine ice_write_hist (ns) call pio_closefile(File) if (my_task == master_task) then write(nu_diag,*) ' ' - write(nu_diag,*) 'Finished writing ',trim(ncfile(ns)) + write(nu_diag,*) 'Finished writing ',trim(ncfile) endif first_call = .false. diff --git a/configuration/scripts/options/set_nml.histinst b/configuration/scripts/options/set_nml.histinst index 31d566d76..34000f635 100644 --- a/configuration/scripts/options/set_nml.histinst +++ b/configuration/scripts/options/set_nml.histinst @@ -1 +1,2 @@ hist_avg = .false.,.false.,.false.,.false.,.false. +hist_suffix = '1','2','3','4','5' diff --git a/doc/source/user_guide/ug_case_settings.rst b/doc/source/user_guide/ug_case_settings.rst index b8bde525d..9f1f8a259 100644 --- a/doc/source/user_guide/ug_case_settings.rst +++ b/doc/source/user_guide/ug_case_settings.rst @@ -186,7 +186,6 @@ setup_nml "", "zero", "restart output frequency relative to year-month-day of 0000-01-01", "" "``dumpfreq_n``", "integer array", "write restart frequency with ``dumpfreq``", "1,1,1,1,1" "``dump_last``", "logical", "write restart on last time step of simulation", "``.false.``" - "``hist_avg``", "logical", "write time-averaged data", "``.true.,.true.,.true.,.true.,.true.``" "``histfreq``", "``d``", "write history every ``histfreq_n`` days", "'1','h','d','m','y'" "", "``h``", "write history every ``histfreq_n`` hours", "" "", "``m``", "write history every ``histfreq_n`` months", "" @@ -218,6 +217,7 @@ setup_nml "", "subset", "subset io rearranger option for history output", "" "``history_root``", "integer", "pe root task for history output with history_iotasks and history_stride (PIO only), -99=internal default", "-99" "``history_stride``", "integer", "pe stride for history output with history_iotasks and history_root (PIO only), -99=internal default", "-99" + "``hist_avg``", "logical", "write time-averaged data", "``.true.,.true.,.true.,.true.,.true.``" "``hist_suffix``", "character array", "appended to history_file when not x", "``x,x,x,x,x``" "``hist_time_axis``","character","history file time axis interval location: begin, middle, end","end" "``ice_ic``", "``default``", "equal to internal", "``default``" diff --git a/doc/source/user_guide/ug_implementation.rst b/doc/source/user_guide/ug_implementation.rst index c243616d2..ddddbee3d 100644 --- a/doc/source/user_guide/ug_implementation.rst +++ b/doc/source/user_guide/ug_implementation.rst @@ -1232,11 +1232,6 @@ above. In addition, ``history_format`` as well as other history namelist options control the specific file format as well as features related to IO performance, see :ref:`iooverview`. -CICE Model history output data can be written as instantaneous or average data as specified -by the ``hist_avg`` namelist array and is customizable by stream. Characters -can be added to the ``history_filename`` to distinguish the streams. This can be changed -by modifying ``hist_suffix`` to something other than "x". - The data written at the period(s) given by ``histfreq`` and ``histfreq_n`` relative to a reference date specified by ``histfreq_base``. The files are written to binary or netCDF files prepended by ``history_file`` @@ -1263,20 +1258,29 @@ collected in their own history modules (**ice\_history\_bgc.F90**, **ice\_history\_drag.F90**, **ice\_history\_mechred.F90**, **ice\_history\_pond.F90**). -The history modules allow output at different frequencies. Five output -frequencies (``1``, ``h``, ``d``, ``m``, ``y``) are available simultaneously during a run. -The same variable can be output at different frequencies (say daily and -monthly) via its namelist flag, `f\_` :math:`\left<{var}\right>`, which -is a character string corresponding to ``histfreq`` or ‘x’ for none. -(Grid variable flags are logicals, since they are written to all -files, no matter what the frequency is.) If there are no namelist flags +The history modules allow output at different frequencies, ``hist_freq``. Five output +options (``1``, ``h``, ``d``, ``m``, ``y``) are available simultaneously +during a run, and each stream must have a unique value for ``hist_freq``. In other words, ``d`` +cannot be used by two different streams. Each stream has an associated frequency +set by ``histfreq_n``. The frequency is +relative to a reference date specified by the corresponding entry in ``histfreq_base``. +Each stream can be instantaneous or time averaged +data over the frequency internal. The ``hist_avg`` namelist turns on time averaging +for each stream individually. +The same model variable can be written to multiple history streams (say daily and +monthly) via its namelist flag, `f\_` :math:`\left<{var}\right>`. The valid +values for the character string `f\_` :math:`\left<{var}\right>` is the ``hist_freq`` +values or 'x' for none. For example, ``f_aice = 'md'`` will write aice to the +monthly and daily streams. +Grid variable history output flags are logicals and written to all stream files if +turned on. If there are no namelist flags with a given ``histfreq`` value, or if an element of ``histfreq_n`` is 0, then -no file will be written at that frequency. The output period can be -discerned from the filenames or the ``hist_suffix`` can be used. Each history stream will be either instantaneous -or averaged as specified by the corresponding entry in the ``hist_avg`` namelist array, and the frequency -will be relative to a reference date specified by the corresponding entry in ``histfreq_base``. -More information about how the frequency is -computed is found in :ref:`timemanager`. +no file will be written at that frequency. The history filenames are set in +the subroutine **construct_filename** in **ice_history_shared.F90**. The stream +filename is a function of the output frequency, and the ``hist_avg`` and ``hist_suffix`` +values. In cases where two streams produce the same identical filename, the model will +abort. Use the namelist ``hist_suffix`` to make stream filenames unique. +More information about how the frequency is computed is found in :ref:`timemanager`. Also, some Earth Sytem Models require the history file time axis to be centered in the averaging interval. The flag ``hist_time_axis`` will allow the user to chose ``begin``, ``middle``, @@ -1322,11 +1326,6 @@ above, ``meltb`` is called ``meltb`` in the monthly file (for backward compatibility with the default configuration) and ``meltb_h`` in the 6-hourly file. -Using the same frequency twice in ``histfreq`` will have unexpected -consequences and currently will cause the code to abort. It is not -possible at the moment to output averages once a month and also once -every 3 months, for example. - If ``write_ic`` is set to true in **ice\_in**, a snapshot of the same set of history fields at the start of the run will be written to the history directory in **iceh\_ic.[timeID].nc(da)**. Several history variables are