MONC
writer_federator.F90
Go to the documentation of this file.
1 
34  use grids_mod, only : z_index, y_index, x_index
35  use mpi, only : mpi_int, mpi_max
37  implicit none
38 
39 #ifndef TEST_MODE
40  private
41 #endif
42 
43  type(writer_type), volatile, dimension(:), allocatable :: writer_entries
46 
48  logical, volatile :: currently_writing
49 
53 contains
54 
57  subroutine initialise_writer_federator(io_configuration, diagnostic_generation_frequency, continuation_run)
58  type(io_configuration_type), intent(inout) :: io_configuration
59  type(hashmap_type), intent(inout) :: diagnostic_generation_frequency
60  logical, intent(in) :: continuation_run
61 
62  integer :: i, j, number_contents, current_field_index
63  type(hashset_type) :: writer_field_names, duplicate_field_names
64 
68 
69  currently_writing=.false.
70 
74 
75  allocate(writer_entries(io_configuration%number_of_writers))
76  do i=1, io_configuration%number_of_writers
77  current_field_index=0
78  number_contents=io_configuration%file_writers(i)%number_of_contents
79  allocate(writer_entries(i)%contents(get_total_number_writer_fields(io_configuration, i)))
80  writer_entries(i)%filename=io_configuration%file_writers(i)%file_name
81  writer_entries(i)%title=io_configuration%file_writers(i)%title
82  writer_entries(i)%write_on_terminate=io_configuration%file_writers(i)%write_on_terminate
83  writer_entries(i)%include_in_io_state_write=io_configuration%file_writers(i)%include_in_io_state_write
84  call check_thread_status(forthread_mutex_init(writer_entries(i)%trigger_and_write_mutex, -1))
85  call check_thread_status(forthread_mutex_init(writer_entries(i)%num_fields_to_write_mutex, -1))
86  call check_thread_status(forthread_mutex_init(writer_entries(i)%pending_writes_mutex, -1))
87  writer_entries(i)%write_on_model_time=io_configuration%file_writers(i)%write_on_model_time
88  if (writer_entries(i)%write_on_model_time) then
89  writer_entries(i)%write_timestep_frequency=0
90  writer_entries(i)%write_time_frequency=io_configuration%file_writers(i)%write_time_frequency
91  else
92  writer_entries(i)%write_time_frequency=0
93  writer_entries(i)%write_timestep_frequency=io_configuration%file_writers(i)%write_timestep_frequency
94  end if
95  writer_entries(i)%previous_write_time=0
96  writer_entries(i)%defined_write_time=io_configuration%file_writers(i)%write_time_frequency
97  writer_entries(i)%latest_pending_write_time=0
98  writer_entries(i)%latest_pending_write_timestep=0
99  writer_entries(i)%contains_io_status_dump=.false.
100  do j=1, number_contents
101  if (io_configuration%file_writers(i)%contents(j)%facet_type == group_type) then
102  current_field_index=add_group_of_fields_to_writer_entry(io_configuration, i, j, current_field_index, &
103  writer_field_names, duplicate_field_names, diagnostic_generation_frequency)
104  else if (io_configuration%file_writers(i)%contents(j)%facet_type == field_type) then
105  current_field_index=current_field_index+add_field_to_writer_entry(io_configuration, &
106  i, j, current_field_index, io_configuration%file_writers(i)%contents(j)%facet_name, "", writer_field_names, &
107  duplicate_field_names, diagnostic_generation_frequency)
108  else if (io_configuration%file_writers(i)%contents(j)%facet_type == io_state_type) then
109  writer_entries(i)%contains_io_status_dump=.true.
110  end if
111  end do
112  if (.not. c_is_empty(duplicate_field_names)) call handle_duplicate_field_names(writer_entries(i), duplicate_field_names)
113  call c_free(writer_field_names)
114  call c_free(duplicate_field_names)
115  end do
116  if (continuation_run) then
118  end if
119  end subroutine initialise_writer_federator
120 
129  end subroutine finalise_writer_federator
130 
131  subroutine inform_writer_federator_time_point(io_configuration, source, data_id, data_dump)
132  type(io_configuration_type), intent(inout) :: io_configuration
133  integer, intent(in) :: source, data_id
134  character, dimension(:), allocatable, intent(in) :: data_dump
135 
136  real(kind=default_precision) :: time
137  integer :: timestep
138  character(len=STRING_LENGTH) :: timestep_key
139 
140  if (is_field_present(io_configuration, source, data_id, "time") .and. &
141  is_field_present(io_configuration, source, data_id, "timestep")) then
142  time=get_scalar_real_from_monc(io_configuration, source, data_id, data_dump, "time")
143  timestep=get_scalar_integer_from_monc(io_configuration, source, data_id, data_dump, "timestep")
144 
145  timestep_key=conv_to_string(timestep)
146 
148  if (.not. c_contains(time_points, timestep_key)) then
151  if (.not. c_contains(time_points, timestep_key)) then
152  call c_put_real(time_points, timestep_key, time)
153  end if
154  end if
156  end if
158 
161  subroutine inform_writer_federator_fields_present(io_configuration, field_names, diag_field_names_and_roots)
162  type(io_configuration_type), intent(inout) :: io_configuration
163  type(hashset_type), intent(inout), optional :: field_names
164  type(hashmap_type), intent(inout), optional :: diag_field_names_and_roots
165 
166  type(iterator_type) :: iterator
167  character(len=STRING_LENGTH) :: specific_name
168  integer :: i, number_q_fields, expected_io
169  logical :: field_found, expected_here, diagnostics_mode
170 
172  expected_io=-1
173  do while (c_has_next(iterator))
174  specific_name=c_next_string(iterator)
175  if (present(field_names)) then
176  field_found=c_contains(field_names, specific_name)
177  diagnostics_mode=.false.
178  else if (present(diag_field_names_and_roots)) then
179  field_found=c_contains(diag_field_names_and_roots, specific_name)
180  if (field_found) expected_io=c_get_integer(diag_field_names_and_roots, specific_name)
181  diagnostics_mode=.true.
182  else
183  field_found=.false.
184  end if
185  if (field_found) then
186  expected_here=expected_io == -1 .or. expected_io == io_configuration%my_io_rank
187  call enable_specific_field_by_name(specific_name, diagnostics_mode, expected_here)
188  end if
189  end do
190  iterator=c_get_iterator(q_field_names)
191  do while (c_has_next(iterator))
192  specific_name=c_next_string(iterator)
193  if (present(field_names)) then
194  field_found=c_contains(field_names, specific_name)
195  diagnostics_mode=.false.
196  else if (present(diag_field_names_and_roots)) then
197  field_found=c_contains(diag_field_names_and_roots, specific_name)
198  if (field_found) expected_io=c_get_integer(diag_field_names_and_roots, specific_name)
199  diagnostics_mode=.true.
200  else
201  field_found=.false.
202  end if
203  if (field_found) then
204  expected_here=expected_io == -1 .or. expected_io == io_configuration%my_io_rank
205  number_q_fields=c_get_integer(io_configuration%dimension_sizing, "qfields")
206  do i=1, number_q_fields
207  if (c_size(io_configuration%q_field_names) .ge. i) then
208  call enable_specific_field_by_name(trim(specific_name)//"_"//trim(c_get_string(io_configuration%q_field_names, i)), &
209  diagnostics_mode, expected_here)
210  else
211  call enable_specific_field_by_name(trim(specific_name)//"_udef"//trim(conv_to_string(i)), &
212  diagnostics_mode, expected_here)
213  end if
214  end do
215  end if
216  end do
218 
222  logical function is_field_used_by_writer_federator(field_name, field_namespace)
223  character(len=*), intent(in) :: field_name, field_namespace
224 
225  integer :: writer_index, contents_index
226 
227  writer_index=1
228  contents_index=1
229  if (c_contains(used_field_names, field_name)) then
230  is_field_used_by_writer_federator=get_next_applicable_writer_entry(field_name, field_namespace, writer_index, contents_index)
231  else
233  end if
235 
239  logical function is_field_split_on_q(field_name)
240  character(len=*), intent(in) :: field_name
241 
243  end function is_field_split_on_q
244 
247  subroutine enable_specific_field_by_name(field_name, diagnostics_mode, expected_here)
248  character(len=*), intent(in) :: field_name
249  logical, intent(in) :: diagnostics_mode
250  logical, intent(in), optional :: expected_here
251 
252  logical :: continue_search
253  integer :: writer_index, contents_index
254 
255  continue_search=.true.
256  writer_index=1
257  contents_index=0
258  do while (continue_search)
259  contents_index=contents_index+1
260  continue_search=get_next_applicable_writer_entry(field_name, writer_index_point=writer_index, &
261  contents_index_point=contents_index)
262  if (continue_search) then
263  if ((writer_entries(writer_index)%contents(contents_index)%diagnostic_field .and. diagnostics_mode) .or. &
264  (writer_entries(writer_index)%contents(contents_index)%prognostic_field .and. .not. diagnostics_mode)) then
265  writer_entries(writer_index)%contents(contents_index)%enabled=.true.
266  if (present(expected_here)) then
267  writer_entries(writer_index)%contents(contents_index)%expected_here=expected_here
268  end if
269  end if
270  end if
271  end do
272  end subroutine enable_specific_field_by_name
273 
277  subroutine provide_q_field_names_to_writer_federator(q_provided_field_names)
278  type(list_type), intent(inout) :: q_provided_field_names
279 
280  type(iterator_type) :: iterator, q_field_iterator
281  logical :: continue_search
282  integer :: writer_index, contents_index, i
283  character(len=STRING_LENGTH) :: search_field, field_name, specific_name
284 
285  iterator=c_get_iterator(q_field_names)
286  do while (c_has_next(iterator))
287  specific_name=c_next_string(iterator)
288  q_field_iterator=c_get_iterator(q_provided_field_names)
289  i=1
290  do while (c_has_next(q_field_iterator))
291  search_field=trim(specific_name)//"_udef"//trim(conv_to_string(i))
292  field_name=trim(specific_name)//"_"//trim(c_next_string(q_field_iterator))
293  continue_search=.true.
294  writer_index=1
295  contents_index=0
296  do while (continue_search)
297  contents_index=contents_index+1
298  continue_search=get_next_applicable_writer_entry(search_field, writer_index_point=writer_index, &
299  contents_index_point=contents_index)
300  if (continue_search) then
301  writer_entries(writer_index)%contents(contents_index)%field_name=field_name
302  end if
303  end do
304  i=i+1
305  call c_add_string(used_field_names, field_name)
306  call c_remove(used_field_names, search_field)
307  end do
308  end do
310 
311  subroutine provide_ordered_field_to_writer_federator(io_configuration, field_name, field_namespace, field_values, &
312  timestep, time, source)
313  type(io_configuration_type), intent(inout) :: io_configuration
314  character(len=*), intent(in) :: field_name, field_namespace
315  integer, intent(in) :: timestep, source
316  type(data_values_type), target :: field_values
317  real(kind=default_precision), intent(in) :: time
318 
319  integer :: writer_index, contents_index
320  logical :: continue_search
321  type(data_values_type), pointer :: result_values
322  type(hashmap_type) :: typed_result_values
323  class(*), pointer :: generic
324 
325  if (field_values%data_type == double_data_type) then
326  call provide_ordered_field_to_writer_federator_real_values(io_configuration, field_name, field_namespace, &
327  field_values%values, timestep, time, source)
328  else if (field_values%data_type == string_data_type) then
329  continue_search=.true.
330  writer_index=1
331  contents_index=0
332  allocate(result_values, source=field_values)
333  generic=>result_values
334  if (c_contains(used_field_names, field_name)) then
335  do while (continue_search)
336  contents_index=contents_index+1
337  continue_search=get_next_applicable_writer_entry(field_name, field_namespace, writer_index, contents_index)
338  if (continue_search) then
339  if (.not. writer_entries(writer_index)%contents(contents_index)%enabled) then
340  call log_log(log_warn, "Received data for previously un-enabled field '"//&
341  writer_entries(writer_index)%contents(contents_index)%field_name//"'")
342  end if
343  writer_entries(writer_index)%contents(contents_index)%enabled=.true.
344  writer_entries(writer_index)%contents(contents_index)%latest_timestep_values=timestep
345  if (log_get_logging_level() .ge. log_debug) then
346  call log_log(log_debug, "[WRITE FED VALUE STORE] Storing value for field "//trim(field_name)//" ts="//&
347  trim(conv_to_string(timestep))// " t="//trim(conv_to_string(time)))
348  end if
349  call check_thread_status(forthread_mutex_lock(writer_entries(writer_index)%contents(contents_index)%values_mutex))
350  call c_put_generic(writer_entries(writer_index)%contents(contents_index)%values_to_write, conv_to_string(time), &
351  generic, .false.)
352  call check_thread_status(forthread_mutex_unlock(writer_entries(writer_index)%contents(contents_index)%values_mutex))
353  if (writer_entries(writer_index)%contents(contents_index)%pending_to_write) then
354  call determine_if_outstanding_field_can_be_written(io_configuration, writer_entries(writer_index), &
355  writer_entries(writer_index)%contents(contents_index))
356  end if
357  end if
358  end do
359  end if
360  end if
362 
371  subroutine provide_ordered_field_to_writer_federator_real_values(io_configuration, field_name, field_namespace, field_values, &
372  timestep, time, source)
373  type(io_configuration_type), intent(inout) :: io_configuration
374  character(len=*), intent(in) :: field_name, field_namespace
375  integer, intent(in) :: timestep, source
376  real(kind=default_precision), dimension(:), intent(in) :: field_values
377  real(kind=default_precision), intent(in) :: time
378 
379  type(iterator_type) :: iterator
380  integer :: individual_size, index
381 
382  if (c_contains(used_field_names, field_name)) then
383  call provide_ordered_single_field_to_writer_federator(io_configuration, field_name, field_namespace, field_values, &
384  timestep, time, source)
385  else if (c_contains(q_field_names, field_name)) then
386  if (c_contains(q_field_splits, field_name)) then
387  individual_size=c_get_integer(q_field_splits, field_name)
388  else if (source .gt. -1) then
389  individual_size=get_size_of_collective_q(io_configuration, field_name, source)
390  else
391  call log_log(log_warn, "Can not find Q split field in Q field names or collective field names with source, ignoring")
392  return
393  end if
394  iterator=c_get_iterator(io_configuration%q_field_names)
395  index=1
396  do while (c_has_next(iterator))
397  call provide_ordered_single_field_to_writer_federator(io_configuration, &
398  trim(field_name)//"_"//trim(c_next_string(iterator)), field_namespace, field_values(index:index+individual_size-1), &
399  timestep, time, source)
400  index=index+individual_size
401  end do
402  end if
404 
410  integer function get_size_of_collective_q(io_configuration, field_name, source)
411  type(io_configuration_type), intent(inout) :: io_configuration
412  character(len=*), intent(in) :: field_name
413  integer, intent(in) :: source
414 
415  class(*), pointer :: generic
416  integer :: i, monc_index
417 
419  monc_index=get_monc_location(io_configuration, source)
420  generic=>c_get_generic(collective_q_field_dims, field_name)
421  select type(generic)
423  do i=1, size(generic%dimensions)
425  get_size_of_collective_q*io_configuration%registered_moncs(monc_index)%local_dim_sizes(generic%dimensions(i))
426  end do
427  end select
428  end function get_size_of_collective_q
429 
437  subroutine provide_ordered_single_field_to_writer_federator(io_configuration, field_name, field_namespace, field_values, &
438  timestep, time, source)
439  type(io_configuration_type), intent(inout) :: io_configuration
440  character(len=*), intent(in) :: field_name, field_namespace
441  integer, intent(in) :: timestep, source
442  real(kind=default_precision), dimension(:), intent(in) :: field_values
443  real(kind=default_precision), intent(in) :: time
444 
445  integer :: writer_index, contents_index
446  logical :: continue_search
447  type(data_values_type), pointer :: result_values
448  type(hashmap_type) :: typed_result_values
449  class(*), pointer :: generic
450 
451  continue_search=.true.
452  writer_index=1
453  contents_index=0
454  if (c_contains(used_field_names, field_name)) then
455  do while (continue_search)
456  contents_index=contents_index+1
457  continue_search=get_next_applicable_writer_entry(field_name, field_namespace, writer_index, contents_index)
458  if (continue_search) then
459  if (.not. writer_entries(writer_index)%contents(contents_index)%enabled) then
460  call log_log(log_warn, "Received data for previously un-enabled field '"//&
461  writer_entries(writer_index)%contents(contents_index)%field_name//"'")
462  end if
463  writer_entries(writer_index)%contents(contents_index)%enabled=.true.
464  if (.not. c_contains(typed_result_values, conv_to_string(&
465  writer_entries(writer_index)%contents(contents_index)%time_manipulation_type))) then
466  allocate(result_values)
467  if (writer_entries(writer_index)%contents(contents_index)%collective_write .and. source .gt. -1) then
468  result_values=writer_entries(writer_index)%contents(contents_index)%time_manipulation(field_values, &
469  writer_entries(writer_index)%contents(contents_index)%output_frequency, &
470  trim(field_name)//"#"//conv_to_string(source), timestep, time)
471  else
472  result_values=writer_entries(writer_index)%contents(contents_index)%time_manipulation(field_values, &
473  writer_entries(writer_index)%contents(contents_index)%output_frequency, &
474  field_name, timestep, time)
475  end if
476  generic=>result_values
477  call c_put_generic(typed_result_values, conv_to_string(&
478  writer_entries(writer_index)%contents(contents_index)%time_manipulation_type), generic, .false.)
479  else
480  result_values=>get_data_value_by_field_name(typed_result_values, conv_to_string(&
481  writer_entries(writer_index)%contents(contents_index)%time_manipulation_type))
482  end if
483  if (allocated(result_values%values)) then
484  writer_entries(writer_index)%contents(contents_index)%latest_timestep_values=timestep
485  if (log_get_logging_level() .ge. log_debug) then
486  call log_log(log_debug, "[WRITE FED VALUE STORE] Storing value for field "//trim(field_name)//" ts="//&
487  trim(conv_to_string(timestep))// " t="//trim(conv_to_string(time)))
488  end if
489  call check_thread_status(forthread_mutex_lock(writer_entries(writer_index)%contents(contents_index)%values_mutex))
490  if (writer_entries(writer_index)%contents(contents_index)%collective_write .and. source .gt. -1) then
491  call write_collective_write_value(result_values, writer_index, contents_index, source, conv_to_string(time))
492  else
493  call c_put_generic(writer_entries(writer_index)%contents(contents_index)%values_to_write, conv_to_string(time), &
494  generic, .false.)
495  end if
496  call check_thread_status(forthread_mutex_unlock(writer_entries(writer_index)%contents(contents_index)%values_mutex))
497  if (writer_entries(writer_index)%contents(contents_index)%pending_to_write) then
498  call determine_if_outstanding_field_can_be_written(io_configuration, writer_entries(writer_index), &
499  writer_entries(writer_index)%contents(contents_index))
500  end if
501  end if
502  end if
503  end do
504  end if
505  call c_free(typed_result_values)
507 
515  subroutine write_collective_write_value(result_values, writer_index, contents_index, source, lookup_key)
516  integer, intent(in) :: writer_index, contents_index, source
517  type(data_values_type), pointer :: result_values
518  character(len=*), intent(in) :: lookup_key
519 
520  class(*), pointer :: generic
521  type(write_field_collective_values_type), pointer :: stored_monc_values
522 
523  if (c_contains(writer_entries(writer_index)%contents(contents_index)%values_to_write, lookup_key)) then
524  generic=>c_get_generic(writer_entries(writer_index)%contents(contents_index)%values_to_write, lookup_key)
525  select type(generic)
527  stored_monc_values=>generic
528  end select
529  else
530  allocate(stored_monc_values)
531  generic=>stored_monc_values
532  call c_put_generic(writer_entries(writer_index)%contents(contents_index)%values_to_write, lookup_key, generic, .false.)
533  end if
534  generic=>result_values
535  call c_put_generic(stored_monc_values%monc_values, conv_to_string(source), generic, .false.)
536  end subroutine write_collective_write_value
537 
541  subroutine determine_if_outstanding_field_can_be_written(io_configuration, writer_entry, specific_field)
542  type(io_configuration_type), intent(inout) :: io_configuration
543  type(writer_type), intent(inout) :: writer_entry
544  type(writer_field_type), intent(inout) :: specific_field
545 
546  logical :: field_write_success, do_close_num_fields
547 
548  if (specific_field%pending_to_write) then
549  call determine_if_field_can_be_written(io_configuration, writer_entry, specific_field, writer_entry%write_timestep, &
550  writer_entry%previous_write_timestep, writer_entry%write_time, writer_entry%previous_write_time, field_write_success)
551  if (field_write_success) then
552  if (log_get_logging_level() .ge. log_debug) then
553  call log_log(log_debug, "Flushed outstanding field ts="//conv_to_string(writer_entry%write_timestep)//&
554  " write time="//conv_to_string(writer_entry%write_time))
555  end if
556  call check_thread_status(forthread_mutex_lock(writer_entry%num_fields_to_write_mutex))
557  writer_entry%num_fields_to_write=writer_entry%num_fields_to_write-1
558  do_close_num_fields=writer_entry%num_fields_to_write == 0
559  call check_thread_status(forthread_mutex_unlock(writer_entry%num_fields_to_write_mutex))
560  if (do_close_num_fields) then
561  call close_diagnostics_file(io_configuration, writer_entry, writer_entry%write_timestep, writer_entry%write_time)
562  end if
563  end if
564  end if
566 
573  subroutine determine_if_field_can_be_written(io_configuration, writer_entry, specific_field, &
574  timestep, previous_write_timestep, write_time, previous_write_time, field_written)
575  type(io_configuration_type), intent(inout) :: io_configuration
576  type(writer_type), intent(inout) :: writer_entry
577  type(writer_field_type), intent(inout) :: specific_field
578  integer, intent(in) :: timestep, previous_write_timestep
579  real, intent(in) :: write_time, previous_write_time
580  logical, intent(out), optional :: field_written
581 
582  real :: value_to_test, largest_value_found
583  integer :: num_matching
584  logical :: entry_beyond_this_write
585  type(iterator_type) :: iterator
586  type(mapentry_type) :: map_entry
587  type(write_field_collective_values_type), pointer :: multi_monc_entries
588  class(*), pointer :: generic
589 
590  num_matching=0
591  largest_value_found=0.0
592  entry_beyond_this_write=.false.
593  call check_thread_status(forthread_mutex_lock(specific_field%values_mutex))
594  if (.not. c_is_empty(specific_field%values_to_write)) then
595  iterator=c_get_iterator(specific_field%values_to_write)
596  do while (c_has_next(iterator))
597  map_entry=c_next_mapentry(iterator)
598  value_to_test=conv_to_real(map_entry%key)
599  if (specific_field%collective_write) then
600  generic=>c_get_generic(map_entry)
601  select type(generic)
603  multi_monc_entries=>generic
604  end select
605  if (c_size(multi_monc_entries%monc_values) .ne. io_configuration%number_of_moncs) cycle
606  end if
607  if (value_to_test .gt. write_time) entry_beyond_this_write=.true.
608  if (value_to_test .le. write_time .and. value_to_test .gt. previous_write_time) then
609  num_matching=num_matching+1
610  if (largest_value_found .lt. value_to_test) largest_value_found=value_to_test
611  end if
612  end do
613  end if
614 
615  if (num_matching .gt. 0 .and. (specific_field%ready_to_write(largest_value_found, specific_field%output_frequency, write_time, &
616  specific_field%latest_timestep_values, timestep) .or. entry_beyond_this_write)) then
617  if (.not. specific_field%collective_write .or. .not. specific_field%collective_contiguous_optimisation) then
618  if (specific_field%issue_write) then
619  call write_variable(io_configuration, specific_field, writer_entry%filename, timestep, write_time)
620  end if
621  specific_field%previous_write_time=writer_entry%write_time
622  end if
623  specific_field%pending_to_write=.false.
624  if (present(field_written)) field_written=.true.
625  else
626  if (log_get_logging_level() .ge. log_debug) then
627  call log_log(log_debug, "Setting outstanding field ts="//conv_to_string(writer_entry%write_timestep)//&
628  " write time="//conv_to_string(writer_entry%write_time)//" prev="//conv_to_string(previous_write_time)//&
629  " largest entry="//conv_to_string(largest_value_found)//" num matching="//conv_to_string(num_matching))
630  end if
631  specific_field%pending_to_write=.true.
632  if (present(field_written)) field_written=.false.
633  end if
634  call check_thread_status(forthread_mutex_unlock(specific_field%values_mutex))
635  end subroutine determine_if_field_can_be_written
636 
642  subroutine check_writer_for_trigger(io_configuration, source, data_id, data_dump)
643  type(io_configuration_type), intent(inout) :: io_configuration
644  integer, intent(in) :: source, data_id
645  character, dimension(:), allocatable, intent(in) :: data_dump
646 
647  integer :: i, timestep
648  real(kind=default_precision) :: time
649  logical :: terminated
650 
651  if (is_field_present(io_configuration, source, data_id, "timestep") .and. &
652  is_field_present(io_configuration, source, data_id, "time")) then
653  timestep=get_scalar_integer_from_monc(io_configuration, source, data_id, data_dump, "timestep")
654  time=get_scalar_real_from_monc(io_configuration, source, data_id, data_dump, "time")
655 
656  if (is_field_present(io_configuration, source, data_id, "terminated")) then
657  terminated=get_scalar_logical_from_monc(io_configuration, source, data_id, data_dump, "terminated")
658  else
659  terminated=.false.
660  end if
661  do i=1, size(writer_entries)
662  call check_writer_trigger(io_configuration, i, timestep, real(time, kind=4), terminated)
663  end do
664  end if
665  end subroutine check_writer_for_trigger
666 
674  subroutine check_writer_trigger(io_configuration, writer_entry_index, timestep, time, terminated)
675  type(io_configuration_type), intent(inout) :: io_configuration
676  integer, intent(in) :: writer_entry_index, timestep
677  real, intent(in) :: time
678  logical, intent(in) :: terminated
679 
680  real :: time_difference
681  integer :: i
682  logical :: issue_write, issue_terminated_write
683 
684  call check_thread_status(forthread_mutex_lock(writer_entries(writer_entry_index)%trigger_and_write_mutex))
685  issue_terminated_write=writer_entries(writer_entry_index)%write_on_terminate .and. terminated
686  if (writer_entries(writer_entry_index)%write_on_model_time) then
687  time_difference=time-writer_entries(writer_entry_index)%latest_pending_write_time
688  issue_write=time_difference .ge. writer_entries(writer_entry_index)%write_time_frequency
689  else
690  if (writer_entries(writer_entry_index)%write_timestep_frequency .gt. 0) then
691  issue_write=writer_entries(writer_entry_index)%latest_pending_write_timestep .ne. timestep .and. &
692  mod(timestep, writer_entries(writer_entry_index)%write_timestep_frequency) == 0
693  else
694  issue_write=.false.
695  end if
696  issue_terminated_write=issue_terminated_write .and. &
697  writer_entries(writer_entry_index)%latest_pending_write_timestep .ne. timestep
698  end if
699 
700  if (issue_write .or. issue_terminated_write) then
701  writer_entries(writer_entry_index)%latest_pending_write_time=time
702  writer_entries(writer_entry_index)%latest_pending_write_timestep=timestep
703 
705 
706  if (currently_writing) then
708  call check_thread_status(forthread_mutex_unlock(writer_entries(writer_entry_index)%trigger_and_write_mutex))
709  call register_pending_file_write(writer_entry_index, timestep, time, &
710  writer_entries(writer_entry_index)%write_on_terminate .and. terminated)
711  else
712  currently_writing=.true.
714  call check_thread_status(forthread_mutex_unlock(writer_entries(writer_entry_index)%trigger_and_write_mutex))
715  call issue_actual_write(io_configuration, writer_entries(writer_entry_index), timestep, time, &
716  writer_entries(writer_entry_index)%write_on_terminate .and. terminated)
717  end if
718  else
719  call check_thread_status(forthread_mutex_unlock(writer_entries(writer_entry_index)%trigger_and_write_mutex))
720  end if
721  end subroutine check_writer_trigger
722 
728  subroutine issue_actual_write(io_configuration, writer_entry, timestep, time, terminated_write)
729  type(io_configuration_type), intent(inout) :: io_configuration
730  type(writer_type), intent(inout) :: writer_entry
731  integer, intent(in) :: timestep
732  real, intent(in) :: time
733  logical, intent(in) :: terminated_write
734 
735  integer :: i, j, total_outstanding, num_written, total_flds
736  logical :: field_written
737  type(map_type) :: applicable_time_points
738 
740  do i=1, size(writer_entry%contents)
741  if (writer_entry%contents(i)%enabled .and. writer_entry%contents(i)%collective_write) then
742  if (.not. writer_entry%contents(i)%collective_initialised) then
743  call determine_collective_type_and_optimise_if_possible(io_configuration, writer_entry%contents(i))
744  end if
745  end if
746  end do
748 
749  writer_entry%write_time=time
750  writer_entry%write_timestep=timestep
751  applicable_time_points=extract_applicable_time_points(writer_entry%previous_write_time, time)
752  call define_netcdf_file(io_configuration, writer_entry, timestep, time, applicable_time_points, terminated_write)
753  call c_free(applicable_time_points)
754  total_outstanding=0
755  total_flds=0
756  num_written=0
757  call check_thread_status(forthread_mutex_lock(writer_entry%num_fields_to_write_mutex))
758  do j=1, size(writer_entry%contents)
759  if (writer_entry%contents(j)%enabled .and. writer_entry%contents(j)%expected_here) then
760  total_flds=total_flds+1
761  call determine_if_field_can_be_written(io_configuration, writer_entry, writer_entry%contents(j), timestep, &
762  writer_entry%previous_write_timestep, time, writer_entry%contents(j)%previous_write_time, field_written)
763  if (.not. field_written) then
764  total_outstanding=total_outstanding+1
765  else
766  num_written=num_written+1
767  end if
768  end if
769  end do
770  writer_entry%num_fields_to_write=total_outstanding
771  call check_thread_status(forthread_mutex_unlock(writer_entry%num_fields_to_write_mutex))
772  if (log_get_logging_level() .ge. log_debug) then
773  call log_log(log_debug, "Started write for NetCDF file, timestep= "//trim(conv_to_string(timestep))&
774  //" total="//trim(conv_to_string(total_flds))//" written="//trim(conv_to_string(num_written))//&
775  " outstanding="//trim(conv_to_string(total_outstanding)))
776  end if
777  if (total_outstanding == 0) then
778  call close_diagnostics_file(io_configuration, writer_entry, timestep, time)
779  end if
780  end subroutine issue_actual_write
781 
784  subroutine clean_time_points()
785  real :: time_entry
786  type(iterator_type) :: iterator
787  type(mapentry_type) :: map_entry
788  type(list_type) :: removed_entries
789  integer :: i
790  logical :: remove_timepoint
791 
793  iterator=c_get_iterator(time_points)
794  do while (c_has_next(iterator))
795  map_entry=c_next_mapentry(iterator)
796  time_entry=real(c_get_real(map_entry))
797  remove_timepoint=.true.
798  do i=1, size(writer_entries)
799  if (writer_entries(i)%previous_write_time .le. time_entry) then
800  remove_timepoint=.false.
801  if (writer_entries(i)%write_on_terminate) then
802  !print *, "Ignore CTP ", time_entry, writer_entries(i)%previous_write_time
803  end if
804  exit
805  end if
806  end do
807  if (remove_timepoint) call c_add_string(removed_entries, map_entry%key)
808  end do
809  iterator=c_get_iterator(removed_entries)
810  do while (c_has_next(iterator))
811  call c_remove(time_points, c_next_string(iterator))
812  end do
814  call c_free(removed_entries)
815  end subroutine clean_time_points
816 
817 
822  type(map_type) function extract_applicable_time_points(start_time, end_time)
823  real, intent(in) :: start_time, end_time
824 
825  real :: time_entry
826  type(iterator_type) :: iterator
827  type(mapentry_type) :: map_entry
828 
830  iterator=c_get_iterator(time_points)
831  do while (c_has_next(iterator))
832  map_entry=c_next_mapentry(iterator)
833  time_entry=real(c_get_real(map_entry))
834  if (time_entry .gt. start_time .and. time_entry .le. end_time) then
836  end if
837  end do
840  end function extract_applicable_time_points
841 
846  type(map_type) function sort_applicable_time_points(unsorted_timepoints)
847  type(map_type), intent(inout) :: unsorted_timepoints
848 
849  integer :: i, entries, specific_ts, smallest_ts
850  character(len=STRING_LENGTH) :: smallest_key
851  real(kind=default_precision) :: rvalue
852  type(iterator_type) :: iterator
853  type(mapentry_type) :: map_entry
854 
855  entries=c_size(unsorted_timepoints)
856  do i=1, entries
857  smallest_key=""
858  iterator=c_get_iterator(unsorted_timepoints)
859  do while (c_has_next(iterator))
860  map_entry=c_next_mapentry(iterator)
861  specific_ts=conv_to_integer(map_entry%key)
862  if (len_trim(smallest_key) == 0 .or. smallest_ts .gt. specific_ts) then
863  smallest_ts=specific_ts
864  smallest_key=map_entry%key
865  rvalue=c_get_real(map_entry)
866  end if
867  end do
868  call c_put_real(sort_applicable_time_points, smallest_key, rvalue)
869  call c_remove(unsorted_timepoints, smallest_key)
870  end do
871  call c_free(unsorted_timepoints)
872  end function sort_applicable_time_points
873 
880  subroutine close_diagnostics_file(io_configuration, writer_entry, timestep, time)
881  type(io_configuration_type), intent(inout) :: io_configuration
882  type(writer_type), intent(inout) :: writer_entry
883  integer, intent(in) :: timestep
884  real, intent(in) :: time
885 
886  if (log_get_logging_level() .ge. log_debug) then
887  call log_log(log_debug, "Issue close for NetCDF file at timestep "//trim(conv_to_string(timestep)))
888  end if
889  call perform_global_callback(io_configuration, writer_entry%filename, timestep, handle_close_diagnostics_globalcallback)
890  end subroutine close_diagnostics_file
891 
899  subroutine handle_close_diagnostics_globalcallback(io_configuration, values, field_name, timestep)
900  type(io_configuration_type), intent(inout) :: io_configuration
901  real(DEFAULT_PRECISION), dimension(:) :: values
902  character(len=STRING_LENGTH) :: field_name
903  integer :: timestep
904 
905  type(writer_type), pointer :: writer_entry
906  integer :: i
907  logical :: terminated, done_chain_run
908 
909  writer_entry=>get_writer_entry_from_netcdf(field_name, timestep, terminated)
910 
911  do i=1, size(writer_entry%contents)
912  if (writer_entry%contents(i)%enabled .and. writer_entry%contents(i)%collective_write .and. &
913  writer_entry%contents(i)%collective_contiguous_optimisation) then
914  call check_thread_status(forthread_mutex_lock(writer_entry%contents(i)%values_mutex))
915  call write_variable(io_configuration, writer_entry%contents(i), writer_entry%filename, timestep, writer_entry%write_time)
916  writer_entry%contents(i)%previous_write_time=writer_entry%write_time
917  call check_thread_status(forthread_mutex_unlock(writer_entry%contents(i)%values_mutex))
918  end if
919  end do
920 
921  writer_entry%previous_write_time=writer_entry%write_time
922  writer_entry%previous_write_timestep=writer_entry%write_timestep
923  writer_entry%defined_write_time=writer_entry%defined_write_time+writer_entry%write_time_frequency
924 
925  call clean_time_points()
926 
927  if (writer_entry%contains_io_status_dump) then
928  if (.not. terminated) then
929  do while (.not. is_io_server_state_writer_ready(timestep))
930  end do
931  end if
932  call check_thread_status(forthread_rwlock_rdlock(time_points_rwlock))
933  call store_io_server_state(io_configuration, writer_entries, time_points, writer_entry, timestep)
934  call check_thread_status(forthread_rwlock_unlock(time_points_rwlock))
935  end if
936 
937  call close_netcdf_file(io_configuration, field_name, timestep)
938 
939  done_chain_run=.false.
940  do i=1, size(writer_entries)
941  if (writer_entries(i)%filename .ne. writer_entry%filename) then
942  done_chain_run=check_for_and_issue_chain_write(io_configuration, writer_entries(i))
943  if (done_chain_run) exit
944  end if
945  end do
946  if (.not. done_chain_run) done_chain_run=check_for_and_issue_chain_write(io_configuration, writer_entry)
947 
948  if (.not. done_chain_run) then
949  call check_thread_status(forthread_mutex_lock(currently_writing_mutex))
950  currently_writing=.false.
951  call check_thread_status(forthread_mutex_unlock(currently_writing_mutex))
952  if (log_get_logging_level() .ge. log_debug) then
953  call log_log(log_debug, "No more pending entries to chain to at ts= "//trim(conv_to_string(timestep)))
954  end if
955  end if
957 
962  logical function check_for_and_issue_chain_write(io_configuration, writer_entry)
963  type(io_configuration_type), intent(inout) :: io_configuration
964  type(writer_type), intent(inout) :: writer_entry
965 
966  class(*), pointer :: generic
967 
968  call check_thread_status(forthread_mutex_lock(writer_entry%pending_writes_mutex))
969  if (.not. c_is_empty(writer_entry%pending_writes)) then
971  generic=>c_pop_generic(writer_entry%pending_writes)
972  call check_thread_status(forthread_mutex_unlock(writer_entry%pending_writes_mutex))
973  select type(generic)
974  type is (pending_write_type)
975  if (log_get_logging_level() .ge. log_debug) then
976  call log_log(log_debug, "Chain to next pending entry ts= "//trim(conv_to_string(generic%timestep)))
977  end if
978  call issue_actual_write(io_configuration, writer_entry, generic%timestep, &
979  generic%write_time, generic%terminated_write)
980  deallocate(generic)
981  end select
982  else
984  call check_thread_status(forthread_mutex_unlock(writer_entry%pending_writes_mutex))
985  end if
987 
992  subroutine register_pending_file_write(writer_entry_index, timestep, time, terminated_write)
993  integer, intent(in) :: writer_entry_index, timestep
994  real, intent(in) :: time
995  logical, intent(in) :: terminated_write
996 
997  type(pending_write_type), pointer :: pending_write
998  class(*), pointer :: generic
999 
1000  allocate(pending_write)
1001  pending_write%write_time=time
1002  pending_write%timestep=timestep
1003  pending_write%terminated_write=terminated_write
1004 
1005  generic=>pending_write
1006  call check_thread_status(forthread_mutex_lock(writer_entries(writer_entry_index)%pending_writes_mutex))
1007  call c_push_generic(writer_entries(writer_entry_index)%pending_writes, generic, .false.)
1008  call check_thread_status(forthread_mutex_unlock(writer_entries(writer_entry_index)%pending_writes_mutex))
1009  end subroutine register_pending_file_write
1010 
1017  logical function get_next_applicable_writer_entry(field_name, field_namespace, writer_index_point, contents_index_point)
1018  character(len=*), intent(in) :: field_name
1019  character(len=*), intent(in), optional :: field_namespace
1020  integer, intent(inout) :: writer_index_point, contents_index_point
1021 
1022  integer :: i, j
1023 
1024  if (writer_index_point .le. size(writer_entries)) then
1025  do i=writer_index_point, size(writer_entries)
1026  if (contents_index_point .le. size(writer_entries(i)%contents)) then
1027  do j=contents_index_point, size(writer_entries(i)%contents)
1028  if (writer_entries(i)%contents(j)%field_name==field_name) then
1029  if (present(field_namespace)) then
1030  if (writer_entries(i)%contents(j)%field_namespace .ne. field_namespace) cycle
1031  end if
1032  writer_index_point=i
1033  contents_index_point=j
1035  return
1036  end if
1037  end do
1038  end if
1039  contents_index_point=1
1040  end do
1041  end if
1043  end function get_next_applicable_writer_entry
1044 
1050  integer function get_total_number_writer_fields(io_configuration, writer_entry_index)
1051  type(io_configuration_type), intent(inout) :: io_configuration
1052  integer, intent(in) :: writer_entry_index
1053 
1054  integer :: i, number_contents, group_index, number_q_fields
1055 
1057  number_q_fields=c_get_integer(io_configuration%dimension_sizing, "qfields")
1058 
1059  number_contents=io_configuration%file_writers(writer_entry_index)%number_of_contents
1060  do i=1, number_contents
1061  if (io_configuration%file_writers(writer_entry_index)%contents(i)%facet_type == group_type) then
1062  group_index=get_index_of_group(io_configuration, &
1063  io_configuration%file_writers(writer_entry_index)%contents(i)%facet_name)
1064  if (group_index == 0) call log_log(log_error, "Can not find group '"//trim(&
1065  io_configuration%file_writers(writer_entry_index)%contents(i)%facet_name)//"'")
1067  get_group_number_of_fields(io_configuration, io_configuration%groups(group_index)%members, number_q_fields, &
1068  io_configuration%groups(group_index)%namespace)
1069  else if (io_configuration%file_writers(writer_entry_index)%contents(i)%facet_type == field_type) then
1070  ! NSE
1072  io_configuration%file_writers(writer_entry_index)%contents(i)%facet_name, "", number_q_fields)
1073  end if
1074  end do
1075  end function get_total_number_writer_fields
1076 
1082  integer function get_group_number_of_fields(io_configuration, group_members, num_q_fields, namespace)
1083  type(io_configuration_type), intent(inout) :: io_configuration
1084  type(list_type) :: group_members
1085  integer, intent(in) :: num_q_fields
1086  character(len=STRING_LENGTH), intent(in) :: namespace
1087 
1088  type(iterator_type) :: iterator
1089  character(len=STRING_LENGTH) :: field_name
1090 
1092  iterator=c_get_iterator(group_members)
1093  do while (c_has_next(iterator))
1094  field_name=c_next_string(iterator)
1095  get_group_number_of_fields=get_group_number_of_fields+get_field_number_of_fields(io_configuration, field_name, namespace, &
1096  num_q_fields)
1097  end do
1098  end function get_group_number_of_fields
1099 
1106  integer function get_field_number_of_fields(io_configuration, field_name, field_namespace, num_q_fields)
1107  type(io_configuration_type), intent(inout) :: io_configuration
1108  character(len=STRING_LENGTH), intent(in) :: field_name, field_namespace
1109  integer, intent(in) :: num_q_fields
1110 
1111  type(io_configuration_field_type) :: prognostic_field_configuration
1112  type(io_configuration_data_definition_type) :: prognostic_containing_data_defn
1113  type(io_configuration_diagnostic_field_type) :: diagnostic_field_configuration
1114 
1115  if (get_diagnostic_field_configuration(io_configuration, field_name, field_namespace, diagnostic_field_configuration)) then
1116  if (diagnostic_field_configuration%field_type == array_field_type) then
1117  if (diagnostic_field_configuration%dim_size_defns(diagnostic_field_configuration%dimensions) .eq. "qfields") then
1118  get_field_number_of_fields=num_q_fields
1119  return
1120  end if
1121  end if
1123  else if (get_prognostic_field_configuration(io_configuration, field_name, field_namespace, &
1124  prognostic_field_configuration, prognostic_containing_data_defn)) then
1125  if (prognostic_field_configuration%field_type == array_field_type) then
1126  if (prognostic_field_configuration%dim_size_defns(prognostic_field_configuration%dimensions) .eq. "qfields") then
1127  get_field_number_of_fields=num_q_fields
1128  return
1129  end if
1130  end if
1132  end if
1133  end function get_field_number_of_fields
1134 
1142  integer function add_group_of_fields_to_writer_entry(io_configuration, writer_entry_index, facet_index, current_field_index, &
1143  writer_field_names, duplicate_field_names, diagnostic_generation_frequency)
1144  type(io_configuration_type), intent(inout) :: io_configuration
1145  integer, intent(in) :: writer_entry_index, facet_index, current_field_index
1146  type(hashset_type), intent(inout) :: writer_field_names, duplicate_field_names
1147  type(hashmap_type), intent(inout) :: diagnostic_generation_frequency
1148 
1149  integer :: group_index
1150  character(len=STRING_LENGTH) :: field_name
1151  type(iterator_type) :: iterator
1152 
1153  add_group_of_fields_to_writer_entry=current_field_index
1154  group_index=get_index_of_group(io_configuration, &
1155  io_configuration%file_writers(writer_entry_index)%contents(facet_index)%facet_name)
1156  if (group_index == 0) then
1157  call log_log(log_error, "Can not find group '"//&
1158  trim(io_configuration%file_writers(writer_entry_index)%contents(facet_index)%facet_name)//"' in the configuration")
1159  end if
1160  iterator=c_get_iterator(io_configuration%groups(group_index)%members)
1161  do while (c_has_next(iterator))
1162  field_name=c_next_string(iterator)
1164  writer_entry_index, facet_index, add_group_of_fields_to_writer_entry, field_name, &
1165  io_configuration%groups(group_index)%namespace, writer_field_names, duplicate_field_names, &
1166  diagnostic_generation_frequency)
1167  end do
1169 
1181  integer function add_field_to_writer_entry(io_configuration, writer_entry_index, io_config_facet_index, &
1182  my_facet_index, field_name, field_namespace, writer_field_names, duplicate_field_names, diagnostic_generation_frequency)
1183  type(io_configuration_type), intent(inout) :: io_configuration
1184  integer, intent(in) :: writer_entry_index, io_config_facet_index, my_facet_index
1185  character(len=*), intent(in) :: field_name, field_namespace
1186  type(hashset_type), intent(inout) :: writer_field_names, duplicate_field_names
1187  type(hashmap_type), intent(inout) :: diagnostic_generation_frequency
1188 
1189  integer :: i, number_q_fields, tot_size
1190  type(io_configuration_field_type) :: prognostic_field_configuration
1191  type(io_configuration_data_definition_type) :: prognostic_containing_data_defn
1192  type(io_configuration_diagnostic_field_type) :: diagnostic_field_configuration
1193  type(collective_q_field_representation_type), pointer :: collective_q_field
1194  class(*), pointer :: generic
1195 
1196  if (get_diagnostic_field_configuration(io_configuration, field_name, field_namespace, diagnostic_field_configuration)) then
1197  if (diagnostic_field_configuration%field_type == array_field_type) then
1198  if (diagnostic_field_configuration%dim_size_defns(diagnostic_field_configuration%dimensions) .eq. "qfields") then
1199  number_q_fields=c_get_integer(io_configuration%dimension_sizing, "qfields")
1200  do i=1, number_q_fields
1201  call add_specific_field_to_writer_entry(io_configuration, writer_entry_index, io_config_facet_index, &
1202  my_facet_index+i, trim(field_name)//"_udef"//trim(conv_to_string(i)), field_namespace, writer_field_names, &
1203  duplicate_field_names, c_get_integer(diagnostic_generation_frequency, field_name), diagnostic_field_configuration)
1204  end do
1205  tot_size=1
1206  do i=1, writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%dimensions
1207  tot_size=tot_size*writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%actual_dim_size(i)
1208  end do
1209  call c_put_integer(q_field_splits, field_name, tot_size)
1210  add_field_to_writer_entry=number_q_fields
1211  return
1212  end if
1213  end if
1214  call add_specific_field_to_writer_entry(io_configuration, writer_entry_index, io_config_facet_index, &
1215  my_facet_index+1, field_name, field_namespace, writer_field_names, duplicate_field_names, &
1216  c_get_integer(diagnostic_generation_frequency, field_name), diagnostic_field_configuration)
1218  else if (get_prognostic_field_configuration(io_configuration, field_name, field_namespace, &
1219  prognostic_field_configuration, prognostic_containing_data_defn)) then
1220  if (prognostic_field_configuration%field_type == array_field_type) then
1221  if (prognostic_field_configuration%dim_size_defns(prognostic_field_configuration%dimensions) .eq. "qfields") then
1222  number_q_fields=c_get_integer(io_configuration%dimension_sizing, "qfields")
1223  do i=1, number_q_fields
1224  call add_specific_field_to_writer_entry(io_configuration, writer_entry_index, io_config_facet_index, &
1225  my_facet_index+i, trim(field_name)//"_udef"//trim(conv_to_string(i)), field_namespace, writer_field_names, &
1226  duplicate_field_names, prognostic_containing_data_defn%frequency, &
1227  prognostic_field_configuration=prognostic_field_configuration)
1228  end do
1229  if (prognostic_field_configuration%collective) then
1230  allocate(collective_q_field)
1231  allocate(collective_q_field%dimensions(&
1232  writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%dimensions))
1233  do i=1, writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%dimensions
1234  if (trim(writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%dim_size_defns(i)) == "z") then
1235  collective_q_field%dimensions(i)=1
1236  else if (trim(writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%dim_size_defns(i)) &
1237  == "y") then
1238  collective_q_field%dimensions(i)=2
1239  else if (trim(writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%dim_size_defns(i)) &
1240  == "x") then
1241  collective_q_field%dimensions(i)=3
1242  end if
1243  end do
1244  generic=>collective_q_field
1245  call c_put_generic(collective_q_field_dims, field_name, generic, .false.)
1246  else
1247  tot_size=1
1248  do i=1, writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%dimensions
1249  tot_size=tot_size*writer_entries(writer_entry_index)%contents(my_facet_index+number_q_fields)%actual_dim_size(i)
1250  end do
1251  call c_put_integer(q_field_splits, field_name, tot_size)
1252  end if
1253  call c_add_string(q_field_names, field_name)
1254  add_field_to_writer_entry=number_q_fields
1255  return
1256  end if
1257  end if
1258  call add_specific_field_to_writer_entry(io_configuration, writer_entry_index, io_config_facet_index, &
1259  my_facet_index+1, field_name, field_namespace, writer_field_names, duplicate_field_names, &
1260  prognostic_containing_data_defn%frequency, prognostic_field_configuration=prognostic_field_configuration)
1262  else
1263  call log_log(log_error, "Field '"//trim(field_name)//&
1264  "' configured for file write but can not find this as a prognostic or diagnostic definition")
1265  end if
1266  end function add_field_to_writer_entry
1267 
1279  subroutine add_specific_field_to_writer_entry(io_configuration, writer_entry_index, io_config_facet_index, &
1280  my_facet_index, field_name, field_namespace, writer_field_names, duplicate_field_names, timestep_frequency, &
1281  diagnostic_field_configuration, prognostic_field_configuration)
1282  type(io_configuration_type), intent(inout) :: io_configuration
1283  integer, intent(in) :: writer_entry_index, io_config_facet_index, my_facet_index, timestep_frequency
1284  character(len=*), intent(in) :: field_name, field_namespace
1285  type(hashset_type), intent(inout) :: writer_field_names, duplicate_field_names
1286  type(io_configuration_diagnostic_field_type), intent(inout), optional :: diagnostic_field_configuration
1287  type(io_configuration_field_type), intent(inout), optional :: prognostic_field_configuration
1288 
1289  integer :: i
1290 
1291  writer_entries(writer_entry_index)%contents(my_facet_index)%field_name=field_name
1292  writer_entries(writer_entry_index)%contents(my_facet_index)%field_namespace=field_namespace
1293 
1294  call c_add_string(used_field_names, field_name)
1295 
1296  if (.not. c_contains(writer_field_names, field_name)) then
1297  call c_add_string(writer_field_names, writer_entries(writer_entry_index)%contents(my_facet_index)%field_name)
1298  else
1299  call c_add_string(duplicate_field_names, writer_entries(writer_entry_index)%contents(my_facet_index)%field_name)
1300  end if
1301 
1302  if (io_configuration%file_writers(writer_entry_index)%contents(io_config_facet_index)%time_manipulation_type == &
1303  instantaneous_type) then
1304  writer_entries(writer_entry_index)%contents(my_facet_index)%time_manipulation=>perform_instantaneous_time_manipulation
1305  writer_entries(writer_entry_index)%contents(my_facet_index)%ready_to_write=>is_instantaneous_time_manipulation_ready_to_write
1306  else if (io_configuration%file_writers(writer_entry_index)%contents(io_config_facet_index)%time_manipulation_type == &
1307  time_averaged_type) then
1308  writer_entries(writer_entry_index)%contents(my_facet_index)%time_manipulation=>perform_timeaveraged_time_manipulation
1309  writer_entries(writer_entry_index)%contents(my_facet_index)%ready_to_write=>is_time_averaged_time_manipulation_ready_to_write
1310  else if (io_configuration%file_writers(writer_entry_index)%contents(io_config_facet_index)%time_manipulation_type == &
1311  none_type) then
1312  writer_entries(writer_entry_index)%contents(my_facet_index)%time_manipulation=>perform_none_time_manipulation
1313  writer_entries(writer_entry_index)%contents(my_facet_index)%ready_to_write=>is_none_time_manipulation_ready_to_write
1314  end if
1315  writer_entries(writer_entry_index)%contents(my_facet_index)%time_manipulation_type=&
1316  io_configuration%file_writers(writer_entry_index)%contents(io_config_facet_index)%time_manipulation_type
1317  writer_entries(writer_entry_index)%contents(my_facet_index)%output_frequency=&
1318  io_configuration%file_writers(writer_entry_index)%contents(io_config_facet_index)%output_time_frequency
1319  writer_entries(writer_entry_index)%contents(my_facet_index)%previous_write_time=0.0
1320  writer_entries(writer_entry_index)%contents(my_facet_index)%previous_tracked_write_point=0.0
1321  writer_entries(writer_entry_index)%contents(my_facet_index)%duplicate_field_name=.false.
1322  writer_entries(writer_entry_index)%contents(my_facet_index)%pending_to_write=.false.
1323  writer_entries(writer_entry_index)%contents(my_facet_index)%enabled=.false.
1324  writer_entries(writer_entry_index)%contents(my_facet_index)%expected_here=.true.
1325  writer_entries(writer_entry_index)%contents(my_facet_index)%prognostic_field=.false.
1326  writer_entries(writer_entry_index)%contents(my_facet_index)%diagnostic_field=.false.
1327 
1328  if (present(diagnostic_field_configuration)) then
1329  writer_entries(writer_entry_index)%contents(my_facet_index)%timestep_frequency=timestep_frequency
1330  writer_entries(writer_entry_index)%contents(my_facet_index)%dimensions=diagnostic_field_configuration%dimensions
1331  writer_entries(writer_entry_index)%contents(my_facet_index)%data_type=diagnostic_field_configuration%data_type
1332  writer_entries(writer_entry_index)%contents(my_facet_index)%field_type=diagnostic_field_configuration%field_type
1333  writer_entries(writer_entry_index)%contents(my_facet_index)%dim_size_defns=diagnostic_field_configuration%dim_size_defns
1334  writer_entries(writer_entry_index)%contents(my_facet_index)%units=diagnostic_field_configuration%units
1335  writer_entries(writer_entry_index)%contents(my_facet_index)%collective_write=diagnostic_field_configuration%collective
1336  writer_entries(writer_entry_index)%contents(my_facet_index)%collective_initialised=.false.
1337  writer_entries(writer_entry_index)%contents(my_facet_index)%issue_write=.true.
1338  writer_entries(writer_entry_index)%contents(my_facet_index)%diagnostic_field=.true.
1339  else if (present(prognostic_field_configuration)) then
1340  writer_entries(writer_entry_index)%contents(my_facet_index)%timestep_frequency=timestep_frequency
1341  writer_entries(writer_entry_index)%contents(my_facet_index)%data_type=prognostic_field_configuration%data_type
1342  writer_entries(writer_entry_index)%contents(my_facet_index)%field_type=prognostic_field_configuration%field_type
1343  writer_entries(writer_entry_index)%contents(my_facet_index)%units=prognostic_field_configuration%units
1344  writer_entries(writer_entry_index)%contents(my_facet_index)%dimensions=prognostic_field_configuration%dimensions
1345  writer_entries(writer_entry_index)%contents(my_facet_index)%collective_write=prognostic_field_configuration%collective
1346  writer_entries(writer_entry_index)%contents(my_facet_index)%collective_initialised=.false.
1347  writer_entries(writer_entry_index)%contents(my_facet_index)%prognostic_field=.true.
1348  if (.not. prognostic_field_configuration%collective) then
1349  writer_entries(writer_entry_index)%contents(my_facet_index)%issue_write=io_configuration%my_io_rank==0
1350  else
1351  writer_entries(writer_entry_index)%contents(my_facet_index)%issue_write=.true.
1352  end if
1353  if (prognostic_field_configuration%field_type == array_field_type .or. &
1354  prognostic_field_configuration%field_type == map_field_type) then
1355  if (prognostic_field_configuration%dimensions .gt. 0) then
1356  writer_entries(writer_entry_index)%contents(my_facet_index)%dimensions=prognostic_field_configuration%dimensions
1357  writer_entries(writer_entry_index)%contents(my_facet_index)%dim_size_defns=&
1358  prognostic_field_configuration%dim_size_defns
1359  else
1360  call log_log(log_error, "The writing prognostic field '"//trim(field_name)//"' configuration must have dimensions")
1361  end if
1362  end if
1363  else
1364  call log_log(log_error, "A diagnostic or prognostic configuration for the field '"//trim(field_name)//"' was not found")
1365  end if
1366  if (writer_entries(writer_entry_index)%contents(my_facet_index)%dimensions .gt. 0) then
1367  if (writer_entries(writer_entry_index)%contents(my_facet_index)%dim_size_defns(&
1368  writer_entries(writer_entry_index)%contents(my_facet_index)%dimensions) .eq. "qfields") then
1369  writer_entries(writer_entry_index)%contents(my_facet_index)%dimensions=&
1370  writer_entries(writer_entry_index)%contents(my_facet_index)%dimensions-1
1371  end if
1372  do i=1, writer_entries(writer_entry_index)%contents(my_facet_index)%dimensions
1373  writer_entries(writer_entry_index)%contents(my_facet_index)%actual_dim_size(i)=c_get_integer(&
1374  io_configuration%dimension_sizing, writer_entries(writer_entry_index)%contents(my_facet_index)%dim_size_defns(i))
1375  end do
1376  end if
1377  call check_thread_status(forthread_mutex_init(writer_entries(writer_entry_index)%contents(my_facet_index)%values_mutex, -1))
1378  end subroutine add_specific_field_to_writer_entry
1379 
1384  subroutine handle_duplicate_field_names(writer_entry, duplicate_field_names)
1385  type(writer_type), intent(inout) :: writer_entry
1386  type(hashset_type), intent(inout) :: duplicate_field_names
1387 
1388  integer :: i
1389 
1390  do i=1, size(writer_entry%contents)
1391  if (c_contains(duplicate_field_names, writer_entry%contents(i)%field_name)) then
1392  writer_entry%contents(i)%duplicate_field_name=.true.
1393  end if
1394  end do
1395  end subroutine handle_duplicate_field_names
1396 
1402  integer function get_index_of_group(io_configuration, group_name)
1403  type(io_configuration_type), intent(inout) :: io_configuration
1404  character(len=*), intent(in) :: group_name
1405 
1406  integer :: i, entries
1407 
1408  entries=io_configuration%number_of_groups
1409  do i=1, entries
1410  if (io_configuration%groups(i)%name == group_name) then
1412  return
1413  end if
1414  end do
1416  end function get_index_of_group
1417 
1423  subroutine determine_collective_type_and_optimise_if_possible(io_configuration, field_to_write_information)
1424  type(io_configuration_type), intent(inout) :: io_configuration
1425  type(writer_field_type), intent(inout) :: field_to_write_information
1426 
1427  if (field_to_write_information%dimensions .eq. 3 .and. &
1428  get_dimension_identifier(field_to_write_information%dim_size_defns(1)) == z_index .and. &
1429  get_dimension_identifier(field_to_write_information%dim_size_defns(2)) == y_index .and. &
1430  get_dimension_identifier(field_to_write_information%dim_size_defns(3)) == x_index) then
1431  field_to_write_information%collective_contiguous_optimisation=.true.
1432  call initialise_contiguous_data_regions(io_configuration, field_to_write_information)
1433  else
1434  field_to_write_information%collective_contiguous_optimisation=.false.
1435  end if
1436  field_to_write_information%collective_initialised=.true.
1438 
1444  subroutine initialise_contiguous_data_regions(io_configuration, field_to_write_information)
1445  type(io_configuration_type), intent(inout) :: io_configuration
1446  type(writer_field_type), intent(inout) :: field_to_write_information
1447 
1448  integer :: start(field_to_write_information%dimensions, io_configuration%number_of_moncs), &
1449  count(field_to_write_information%dimensions, io_configuration%number_of_moncs), &
1450  common_starters(io_configuration%number_of_moncs), num_common, num_current_contents, active_dim, other_dim, &
1451  j, k, i, dim_identifier, number_distinct_writes, start_blocks(io_configuration%number_of_moncs), ierr, &
1452  count_blocks(io_configuration%number_of_moncs), current_contents(io_configuration%number_of_moncs), &
1453  monc_write_start_offset_per_dim(field_to_write_information%dimensions,io_configuration%number_of_moncs)
1454  logical :: processed(io_configuration%number_of_moncs)
1455 
1456  type(write_field_collective_descriptor_type), pointer :: collective_descriptor
1457  type(write_field_collective_monc_info_type), pointer :: specific_monc_collective
1458  class(*), pointer :: generic
1459 
1460  processed=.false.
1461  number_distinct_writes=0
1462  do j=1, io_configuration%number_of_moncs
1463  do k=1, field_to_write_information%dimensions
1464  dim_identifier=get_dimension_identifier(field_to_write_information%dim_size_defns(k))
1465  start(k, j)=io_configuration%registered_moncs(j)%local_dim_starts(dim_identifier)
1466  count(k, j)=io_configuration%registered_moncs(j)%local_dim_sizes(dim_identifier)
1467  end do
1468  end do
1469 
1470  do j=1, io_configuration%number_of_moncs
1471  if (.not. processed(j)) then
1472  call get_common_starts(y_index, start(y_index, j), start, common_starters, num_common)
1473  if (num_common == 0) then
1474  call get_common_starts(x_index, start(x_index, j), start, common_starters, num_common)
1475  if (num_common .gt. 0) then
1476  active_dim=x_index
1477  other_dim=y_index
1478  end if
1479  else
1480  active_dim=y_index
1481  other_dim=x_index
1482  end if
1483  number_distinct_writes=number_distinct_writes+1
1484  allocate(collective_descriptor)
1485  allocate(collective_descriptor%absolute_start(field_to_write_information%dimensions), &
1486  collective_descriptor%count(field_to_write_information%dimensions))
1487  start_blocks(number_distinct_writes)=start(other_dim, j)
1488  count_blocks(number_distinct_writes)=count(other_dim, j)
1489  num_current_contents=1
1490  current_contents(num_current_contents)=j
1491  processed(j)=.true.
1492  if (num_common .gt. 0) then
1493  do k=1, num_common
1494  do i=1, num_common
1495  if (.not. processed(common_starters(i)) .and. count(active_dim, j) == count(active_dim, i)) then
1496  if (start(other_dim, common_starters(i)) .lt. start_blocks(number_distinct_writes) .and. &
1497  start(other_dim, common_starters(i)) + count(other_dim, common_starters(i)) &
1498  == start_blocks(number_distinct_writes)) then
1499  start_blocks(number_distinct_writes)=start(other_dim, common_starters(i))
1500  count_blocks(number_distinct_writes)=count_blocks(number_distinct_writes)+count(other_dim, common_starters(i))
1501  processed(common_starters(i))=.true.
1502  num_current_contents=num_current_contents+1
1503  current_contents(num_current_contents)=common_starters(i)
1504  else if (start(other_dim, common_starters(i)) .gt. start_blocks(number_distinct_writes) .and. &
1505  start_blocks(number_distinct_writes) + count_blocks(number_distinct_writes) &
1506  == start(other_dim, common_starters(i))) then
1507  count_blocks(number_distinct_writes)=count_blocks(number_distinct_writes)+count(other_dim, common_starters(i))
1508  processed(common_starters(i))=.true.
1509  num_current_contents=num_current_contents+1
1510  current_contents(num_current_contents)=common_starters(i)
1511  end if
1512  end if
1513  end do
1514  end do
1515  end if
1516  collective_descriptor%absolute_start=start(:,j)
1517  collective_descriptor%count=count(:,j)
1518  collective_descriptor%absolute_start(other_dim)=start_blocks(number_distinct_writes)
1519  collective_descriptor%count(other_dim)=count_blocks(number_distinct_writes)
1520  collective_descriptor%split_dim=other_dim
1521  if (num_current_contents .gt. 0) then
1522  do k=1, num_current_contents
1523  allocate(specific_monc_collective)
1524  specific_monc_collective%relative_dimension_start=(start(other_dim,current_contents(k))-&
1525  start_blocks(number_distinct_writes)) + 1
1526  specific_monc_collective%counts=count(:, current_contents(k))
1527  specific_monc_collective%monc_location=current_contents(k)
1528  specific_monc_collective%monc_source=io_configuration%registered_moncs(current_contents(k))%source_id
1529  generic=>specific_monc_collective
1530  call c_add_generic(collective_descriptor%specific_monc_info, generic, .false.)
1531  end do
1532  end if
1533  generic=>collective_descriptor
1534  call c_add_generic(field_to_write_information%collective_descriptors, generic, .false.)
1535  end if
1536  end do
1537  call lock_mpi()
1538  call mpi_iallreduce(number_distinct_writes, field_to_write_information%max_num_collective_writes, 1, mpi_int, mpi_max, &
1539  io_configuration%io_communicator, field_to_write_information%max_num_collective_writes_request_handle, ierr)
1540  call unlock_mpi()
1541  end subroutine initialise_contiguous_data_regions
1542 
1549  subroutine get_common_starts(dim, val, vals, common_starters, num_common)
1550  integer, intent(in) :: dim, val
1551  integer, dimension(:,:), intent(in) :: vals
1552  integer, dimension(:), intent(out) :: common_starters
1553  integer, intent(out) :: num_common
1554 
1555  integer :: i
1556 
1557  num_common=0
1558  do i=1, size(vals, 2)
1559  if (vals(dim, i) == val) then
1560  num_common=num_common+1
1561  common_starters(num_common)=i
1562  end if
1563  end do
1564  end subroutine get_common_starts
1565 
1570  integer function get_dimension_identifier(dim_name, is_auto_dimension)
1571  character(len=*), intent(in) :: dim_name
1572  logical, intent(out), optional :: is_auto_dimension
1573 
1574  integer :: dash_idx
1575  logical :: is_modified_size
1576 
1577  dash_idx=index(dim_name, "_")
1578  dash_idx=dash_idx-1
1579  is_modified_size=dash_idx .ne. -1
1580  if (.not. is_modified_size) dash_idx=len_trim(dim_name)
1581 
1582  if (dim_name(:dash_idx) .eq. "z" .or. dim_name(:dash_idx) .eq. "zn") then
1583  get_dimension_identifier=z_index
1584  else if (dim_name(:dash_idx) .eq. "y") then
1585  get_dimension_identifier=y_index
1586  else if (dim_name(:dash_idx) .eq. "x") then
1587  get_dimension_identifier=x_index
1588  else
1590  end if
1591 
1592  if (present(is_auto_dimension)) is_auto_dimension=is_modified_size
1593  end function get_dimension_identifier
1594 end module writer_federator_mod
logging_mod::log_error
integer, parameter, public log_error
Only log ERROR messages.
Definition: logging.F90:11
global_callback_inter_io_mod
Global callback inter IO, which registers the callback with identifiers and then the procedure is act...
Definition: global-callback.F90:3
conversions_mod
Conversion between common inbuilt FORTRAN data types.
Definition: conversions.F90:5
collections_mod::map_type
Map data structure that holds string (length 20 maximum) key value pairs.
Definition: collections.F90:86
collections_mod::c_is_empty
Returns whether a collection is empty.
Definition: collections.F90:437
collections_mod::c_add_generic
Adds a generic element to the end of the list.
Definition: collections.F90:204
timeaveraged_time_manipulation_mod::init_time_averaged_manipulation
subroutine, public init_time_averaged_manipulation()
Initialises the reduction action.
Definition: timeaveraged_manipulation.F90:40
io_server_state_reader_mod
Reads the IO server state that was stored in a NetCDF checkpoint file.
Definition: io_state_reader.F90:2
netcdf_filetype_writer_mod::close_netcdf_file
subroutine, public close_netcdf_file(io_configuration, field_name, timestep)
Call back for the inter IO reduction which actually does the NetCDF file closing which is a collectiv...
Definition: netcdf_filetype.F90:194
forthread_mod::forthread_mutex_lock
integer function forthread_mutex_lock(mutex_id)
Definition: forthread.F90:284
collections_mod::c_key_at
Retrieves the key currently being held at a specific index in the map or "" if the index > map elemen...
Definition: collections.F90:457
logging_mod::log_warn
integer, parameter, public log_warn
Log WARNING and ERROR messages.
Definition: logging.F90:12
writer_federator_mod::get_field_number_of_fields
integer function get_field_number_of_fields(io_configuration, field_name, field_namespace, num_q_fields)
Retrieves the number of fields that make up this field, if it is a Q field then it will be split into...
Definition: writer_federator.F90:1107
io_server_client_mod::string_data_type
integer, parameter, public string_data_type
Definition: ioclient.F90:40
data_utils_mod::is_field_present
logical function, public is_field_present(io_configuration, source, data_id, key)
Definition: datautils.F90:146
instantaneous_time_manipulation_mod::init_instantaneous_manipulation
subroutine, public init_instantaneous_manipulation()
Initialises the instantaneous time manipulation.
Definition: instantaneous_manipulation.F90:29
collections_mod::c_real_at
Retrieves the double precision real value held at the specific map index or null if index > map eleme...
Definition: collections.F90:497
io_server_state_writer_mod
The IO server state module which will write out the current state of the IO server to a NetCDF file.
Definition: io_state_writer.F90:2
writer_federator_mod::writer_entries
type(writer_type), dimension(:), allocatable, volatile writer_entries
Definition: writer_federator.F90:43
collections_mod::c_put_generic
Puts a generic key-value pair into the map.
Definition: collections.F90:305
mpi_communication_mod
Abstraction layer around MPI, this issues and marshals the lower level communication details.
Definition: mpicommunication.F90:2
conversions_mod::conv_to_integer
Converts data types to integers.
Definition: conversions.F90:49
writer_federator_mod::provide_ordered_single_field_to_writer_federator
subroutine provide_ordered_single_field_to_writer_federator(io_configuration, field_name, field_namespace, field_values, timestep, time, source)
Provides a single ordered field, i.e. Q fields have been split by this point.
Definition: writer_federator.F90:439
data_utils_mod
Contains functionality for managing and extracting data from the raw data dumps that the IO server re...
Definition: datautils.F90:3
writer_federator_mod
This federates over the writing of diagnostic and prognostic data to the file system....
Definition: writer_federator.F90:3
global_callback_inter_io_mod::perform_global_callback
subroutine, public perform_global_callback(io_configuration, field_name, timestep, completion_procedure)
Performs a global callback.
Definition: global-callback.F90:39
forthread_mod::forthread_mutex_init
integer function forthread_mutex_init(mutex_id, attr_id)
Definition: forthread.F90:274
timeaveraged_time_manipulation_mod::finalise_time_averaged_manipulation
subroutine, public finalise_time_averaged_manipulation()
Finalises the reduction action, waiting for all outstanding requests and then freeing data.
Definition: timeaveraged_manipulation.F90:46
collections_mod
Collection data structures.
Definition: collections.F90:7
writer_types_mod::writer_field_type
Definition: writer_types.F90:63
writer_federator_mod::get_group_number_of_fields
integer function get_group_number_of_fields(io_configuration, group_members, num_q_fields, namespace)
Retrieves the number of fields within a group of fields.
Definition: writer_federator.F90:1083
collections_mod::c_get_string
Gets a specific string element out of the list, stack, queue or map with the corresponding key.
Definition: collections.F90:388
grids_mod::x_index
integer, parameter, public x_index
Definition: grids.F90:14
netcdf_filetype_writer_mod::write_variable
subroutine, public write_variable(io_configuration, field_to_write_information, filename, timestep, time)
Writes the contents of a variable to the NetCDF file. This also removes the written entries from the ...
Definition: netcdf_filetype.F90:238
grids_mod::y_index
integer, parameter, public y_index
Definition: grids.F90:14
collections_mod::c_has_next
Definition: collections.F90:586
timeaveraged_time_manipulation_mod
Performs time averaged, time manipulation and only returns a value if the output frequency determines...
Definition: timeaveraged_manipulation.F90:2
writer_federator_mod::get_next_applicable_writer_entry
logical function get_next_applicable_writer_entry(field_name, field_namespace, writer_index_point, contents_index_point)
Retrieves the index of the next writer which uses a specific field. If none is found then returns fal...
Definition: writer_federator.F90:1018
collections_mod::hashmap_type
A hashmap structure, the same as a map but uses hashing for greatly improved performance when storing...
Definition: collections.F90:94
io_server_client_mod::map_field_type
integer, parameter, public map_field_type
Field data type identifiers.
Definition: ioclient.F90:38
writer_types_mod
Writer types which are shared across writing functionality. Also includes serialisation functionality...
Definition: writer_types.F90:2
collections_mod::c_size
Returns the number of elements in the collection.
Definition: collections.F90:428
forthread_mod
Definition: forthread.F90:1
io_server_state_reader_mod::reactivate_writer_federator_state
subroutine, public reactivate_writer_federator_state(io_configuration, writer_entries, time_points)
Reactivates the writer federator and everything beneath it (i.e. just not the writer field manager....
Definition: io_state_reader.F90:106
writer_federator_mod::register_pending_file_write
subroutine register_pending_file_write(writer_entry_index, timestep, time, terminated_write)
Registers a pending file write which will be actioned later on.
Definition: writer_federator.F90:993
writer_federator_mod::is_field_split_on_q
logical function, public is_field_split_on_q(field_name)
Determines whether a field is split on Q or not.
Definition: writer_federator.F90:240
writer_federator_mod::time_points
type(hashmap_type), volatile time_points
Definition: writer_federator.F90:45
configuration_parser_mod::instantaneous_type
integer, parameter, public instantaneous_type
Definition: configurationparser.F90:28
writer_federator_mod::used_field_names
type(hashset_type), volatile used_field_names
Definition: writer_federator.F90:44
io_server_client_mod::array_field_type
integer, parameter, public array_field_type
Definition: ioclient.F90:38
logging_mod::log_log
subroutine, public log_log(level, message, str)
Logs a message at the specified level. If the level is above the current level then the message is ig...
Definition: logging.F90:75
forthread_mod::forthread_rwlock_destroy
integer function forthread_rwlock_destroy(rwlock_id)
Definition: forthread.F90:495
writer_federator_mod::add_group_of_fields_to_writer_entry
integer function add_group_of_fields_to_writer_entry(io_configuration, writer_entry_index, facet_index, current_field_index, writer_field_names, duplicate_field_names, diagnostic_generation_frequency)
Adds a group of fields to a writer entry, groups are expanded out into individual fields,...
Definition: writer_federator.F90:1144
mpi_communication_mod::lock_mpi
subroutine, public lock_mpi()
If we are explicitly managing MPI thread safety (SERIALIZED mode) then locks MPI.
Definition: mpicommunication.F90:58
logging_mod::log_get_logging_level
integer function, public log_get_logging_level()
Retrieves the current logging level.
Definition: logging.F90:122
forthread_mod::forthread_mutex_destroy
integer function forthread_mutex_destroy(mutex_id)
Definition: forthread.F90:265
writer_federator_mod::issue_actual_write
subroutine, public issue_actual_write(io_configuration, writer_entry, timestep, time, terminated_write)
Issues the actual file creation, write of available fields and closure if all completed.
Definition: writer_federator.F90:729
writer_federator_mod::currently_writing_mutex
integer, volatile currently_writing_mutex
Definition: writer_federator.F90:47
logging_mod::log_debug
integer, parameter, public log_debug
Log DEBUG, INFO, WARNING and ERROR messages.
Definition: logging.F90:14
writer_federator_mod::add_specific_field_to_writer_entry
subroutine add_specific_field_to_writer_entry(io_configuration, writer_entry_index, io_config_facet_index, my_facet_index, field_name, field_namespace, writer_field_names, duplicate_field_names, timestep_frequency, diagnostic_field_configuration, prognostic_field_configuration)
Adds a specific field and its information to a writer entry.
Definition: writer_federator.F90:1282
configuration_parser_mod::io_configuration_data_definition_type
Configuration of a specific data definition.
Definition: configurationparser.F90:58
configuration_parser_mod::none_type
integer, parameter, public none_type
Definition: configurationparser.F90:28
io_server_client_mod
This defines some constants and procedures that are useful to the IO server and clients that call it....
Definition: ioclient.F90:3
collections_mod::mapentry_type
Definition: collections.F90:46
data_utils_mod::get_scalar_integer_from_monc
integer function, public get_scalar_integer_from_monc(io_configuration, source, data_id, data_dump, key)
Retrieves a single integer element (scalar) from the data dump.
Definition: datautils.F90:372
none_time_manipulation_mod
Performs no time manipulation and returns the value, basically a no-op.
Definition: none_manipulation.F90:2
writer_federator_mod::write_collective_write_value
subroutine write_collective_write_value(result_values, writer_index, contents_index, source, lookup_key)
Writes the collective values, this is held differently to independent values which are written direct...
Definition: writer_federator.F90:516
writer_types_mod::writer_type
Definition: writer_types.F90:78
writer_federator_mod::sort_applicable_time_points
type(map_type) function sort_applicable_time_points(unsorted_timepoints)
Sorts the time points based upon their timestep, smallest to largest. Note that this is a bubble sort...
Definition: writer_federator.F90:847
writer_federator_mod::q_field_splits
type(hashmap_type), volatile q_field_splits
Definition: writer_federator.F90:45
writer_federator_mod::get_size_of_collective_q
integer function get_size_of_collective_q(io_configuration, field_name, source)
Retrieves the data size for each Q entry of a collective Q field for the specific source MONC that ha...
Definition: writer_federator.F90:411
collections_mod::c_get_generic
Gets a specific generic element out of the list, stack, queue or map with the corresponding key.
Definition: collections.F90:367
configuration_parser_mod::field_type
integer, parameter, public field_type
Definition: configurationparser.F90:28
netcdf_filetype_writer_mod::define_netcdf_file
subroutine, public define_netcdf_file(io_configuration, file_writer_information, timestep, time, time_points, termination_write)
Defines a NetCDF file - which creates it, defines all dimensions and variables. This must be called b...
Definition: netcdf_filetype.F90:70
io_server_client_mod::double_data_type
integer, parameter, public double_data_type
Definition: ioclient.F90:40
collections_mod::c_contains
Determines whether or not a map contains a specific key.
Definition: collections.F90:447
writer_federator_mod::inform_writer_federator_time_point
subroutine, public inform_writer_federator_time_point(io_configuration, source, data_id, data_dump)
Definition: writer_federator.F90:132
writer_types_mod::write_field_collective_values_type
Definition: writer_types.F90:46
writer_federator_mod::initialise_writer_federator
subroutine, public initialise_writer_federator(io_configuration, diagnostic_generation_frequency, continuation_run)
Initialises the write federator and configures it based on the user configuration....
Definition: writer_federator.F90:58
io_server_state_writer_mod::is_io_server_state_writer_ready
logical function, public is_io_server_state_writer_ready(timestep)
Determines whether the IO server state writer is ready (i.e. state is at a specific level for the tim...
Definition: io_state_writer.F90:123
writer_federator_mod::collective_contiguous_initialisation_mutex
integer, volatile collective_contiguous_initialisation_mutex
Definition: writer_federator.F90:47
threadpool_mod
This is a thread pool and the single management "main" thread will spawn out free threads in the pool...
Definition: threadpool.F90:5
netcdf_filetype_writer_mod::store_io_server_state
subroutine, public store_io_server_state(io_configuration, writer_entries, time_points, file_writer_information, timestep)
Stores the IO server state in the NetCDF file.
Definition: netcdf_filetype.F90:152
writer_federator_mod::handle_duplicate_field_names
subroutine handle_duplicate_field_names(writer_entry, duplicate_field_names)
Marks duplicate field names in a writer entry as duplicates so that the NetCDF layer can then deal wi...
Definition: writer_federator.F90:1385
writer_federator_mod::is_field_used_by_writer_federator
logical function, public is_field_used_by_writer_federator(field_name, field_namespace)
Determines whether a field is used by the writer federator or not.
Definition: writer_federator.F90:223
writer_types_mod::write_field_collective_descriptor_type
Definition: writer_types.F90:51
threadpool_mod::check_thread_status
subroutine, public check_thread_status(ierr)
Checks the error status of any thread operation and reports an error if it failed.
Definition: threadpool.F90:229
writer_federator_mod::get_total_number_writer_fields
integer function get_total_number_writer_fields(io_configuration, writer_entry_index)
Determines the total number of fields that make up a writer entry, this is all the fields of the grou...
Definition: writer_federator.F90:1051
writer_federator_mod::enable_specific_field_by_name
subroutine enable_specific_field_by_name(field_name, diagnostics_mode, expected_here)
Enables a specific field by its name, this will locate all the fields with this name and enable them.
Definition: writer_federator.F90:248
writer_federator_mod::provide_ordered_field_to_writer_federator_real_values
subroutine provide_ordered_field_to_writer_federator_real_values(io_configuration, field_name, field_namespace, field_values, timestep, time, source)
Provides fields (either diagnostics or prognostics) to the write federator which will action these as...
Definition: writer_federator.F90:373
writer_federator_mod::check_writer_trigger
subroutine check_writer_trigger(io_configuration, writer_entry_index, timestep, time, terminated)
Checks a writer trigger and issues a file creation along with field write if the conditions (time or ...
Definition: writer_federator.F90:675
writer_federator_mod::currently_writing
logical, volatile currently_writing
Definition: writer_federator.F90:48
grids_mod::z_index
integer, parameter, public z_index
Grid index parameters.
Definition: grids.F90:14
configuration_parser_mod::io_state_type
integer, parameter, public io_state_type
Definition: configurationparser.F90:28
conversions_mod::conv_to_string
Converts data types to strings.
Definition: conversions.F90:38
writer_federator_mod::inform_writer_federator_fields_present
subroutine, public inform_writer_federator_fields_present(io_configuration, field_names, diag_field_names_and_roots)
Informs the writer federator that specific fields are present and should be reflected in the diagnost...
Definition: writer_federator.F90:162
configuration_parser_mod::io_configuration_diagnostic_field_type
Definition: configurationparser.F90:77
writer_federator_mod::determine_collective_type_and_optimise_if_possible
subroutine determine_collective_type_and_optimise_if_possible(io_configuration, field_to_write_information)
Determines whether it can optimise a specific collective field. If the field fits into certain limite...
Definition: writer_federator.F90:1424
collections_mod::queue_type
Queue (FIFO) data structure.
Definition: collections.F90:70
collections_mod::iterator_type
Definition: collections.F90:51
writer_federator_mod::time_points_rwlock
integer, volatile time_points_rwlock
Definition: writer_federator.F90:47
netcdf_filetype_writer_mod::finalise_netcdf_filetype
subroutine, public finalise_netcdf_filetype()
Finalises the NetCDF writing functionality.
Definition: netcdf_filetype.F90:59
writer_federator_mod::close_diagnostics_file
subroutine close_diagnostics_file(io_configuration, writer_entry, timestep, time)
Closes the diagnostics file, this is done via a global callback to issue the closes synchronously (co...
Definition: writer_federator.F90:881
configuration_parser_mod::time_averaged_type
integer, parameter, public time_averaged_type
Definition: configurationparser.F90:28
writer_federator_mod::provide_q_field_names_to_writer_federator
subroutine, public provide_q_field_names_to_writer_federator(q_provided_field_names)
Provides the Q field names to the write federator, this is required as on initialisation we don't kno...
Definition: writer_federator.F90:278
forthread_mod::forthread_rwlock_init
integer function forthread_rwlock_init(rwlock_id, attr_id)
Definition: forthread.F90:504
netcdf_filetype_writer_mod
The NetCDF file type writer which performs actual writing of NetCDF files to the parallel filesystem....
Definition: netcdf_filetype.F90:3
writer_federator_mod::q_field_names
type(hashset_type), volatile q_field_names
Definition: writer_federator.F90:44
configuration_parser_mod::io_configuration_type
Overall IO configuration.
Definition: configurationparser.F90:104
collections_mod::c_get_real
Gets a specific double precision real element out of the list, stack, queue or map with the correspon...
Definition: collections.F90:399
collections_mod::c_put_integer
Puts an integer key-value pair into the map.
Definition: collections.F90:318
collections_mod::c_pop_generic
Pops a generic element off the stack or queue.
Definition: collections.F90:158
writer_federator_mod::handle_close_diagnostics_globalcallback
subroutine handle_close_diagnostics_globalcallback(io_configuration, values, field_name, timestep)
Call back for the inter IO reduction which actually does the NetCDF file closing which is a collectiv...
Definition: writer_federator.F90:900
configuration_parser_mod::get_diagnostic_field_configuration
logical function, public get_diagnostic_field_configuration(io_configuration, field_name, field_namespace, diagnostic_config)
Retrieves the diagnostics field configuration corresponding to a specific field name and returns whet...
Definition: configurationparser.F90:1246
collections_mod::c_free
Frees up all the allocatable, heap, memory associated with a list, stack, queue or map.
Definition: collections.F90:577
writer_federator_mod::finalise_writer_federator
subroutine, public finalise_writer_federator()
Finalises the write federator and the manipulations.
Definition: writer_federator.F90:123
timeaveraged_time_manipulation_mod::perform_timeaveraged_time_manipulation
type(data_values_type) function, public perform_timeaveraged_time_manipulation(instant_values, output_frequency, field_name, timestep, time)
Performs the time averaged manipulation and only returns values if these are to be stored (i....
Definition: timeaveraged_manipulation.F90:66
none_time_manipulation_mod::is_none_time_manipulation_ready_to_write
logical function, public is_none_time_manipulation_ready_to_write(latest_time, output_frequency, write_time, latest_timestep, write_timestep)
Definition: none_manipulation.F90:20
netcdf_filetype_writer_mod::get_writer_entry_from_netcdf
type(writer_type) function, pointer, public get_writer_entry_from_netcdf(field_name, timestep, terminated)
Looks up and retrieves the writer entry that corresponds to this NetCDF file state.
Definition: netcdf_filetype.F90:176
writer_federator_mod::get_common_starts
subroutine get_common_starts(dim, val, vals, common_starters, num_common)
Retrieves the number of common starting points that match a specific input value.
Definition: writer_federator.F90:1550
writer_types_mod::collective_q_field_representation_type
Definition: writer_types.F90:89
writer_federator_mod::clean_time_points
subroutine clean_time_points()
Cleans out old timepoints which are no longer going to be of any relavence to the file writing....
Definition: writer_federator.F90:785
configuration_parser_mod::get_data_value_by_field_name
Definition: configurationparser.F90:30
collections_mod::c_put_real
Puts a double precision real key-value pair into the map.
Definition: collections.F90:344
netcdf_filetype_writer_mod::initialise_netcdf_filetype
subroutine, public initialise_netcdf_filetype()
Initialises the NetCDF writing functionality.
Definition: netcdf_filetype.F90:53
writer_federator_mod::get_dimension_identifier
integer function get_dimension_identifier(dim_name, is_auto_dimension)
Translates a dimension name to its numeric corresponding identifier.
Definition: writer_federator.F90:1571
writer_types_mod::write_field_collective_monc_info_type
Definition: writer_types.F90:58
configuration_parser_mod::group_type
integer, parameter, public group_type
Definition: configurationparser.F90:28
logging_mod::log_is_master
logical function, public log_is_master()
Determines whether the process is the master logging process. This might be preferable rather than ca...
Definition: logging.F90:66
logging_mod
Logging utility.
Definition: logging.F90:2
data_utils_mod::get_scalar_logical_from_monc
logical function, public get_scalar_logical_from_monc(io_configuration, source, data_id, data_dump, key)
Retrieves a single logical element (scalar) from the data dump.
Definition: datautils.F90:328
datadefn_mod
Contains common definitions for the data and datatypes used by MONC.
Definition: datadefn.F90:2
writer_federator_mod::determine_if_field_can_be_written
subroutine determine_if_field_can_be_written(io_configuration, writer_entry, specific_field, timestep, previous_write_timestep, write_time, previous_write_time, field_written)
Determines if a file can be written to its overarching write representation. If so then a write is is...
Definition: writer_federator.F90:575
configuration_parser_mod::data_values_type
Definition: configurationparser.F90:34
datadefn_mod::string_length
integer, parameter, public string_length
Default length of strings.
Definition: datadefn.F90:10
forthread_mod::forthread_mutex_unlock
integer function forthread_mutex_unlock(mutex_id)
Definition: forthread.F90:302
logging_mod::log_master_log
subroutine, public log_master_log(level, message)
Will log just from the master process.
Definition: logging.F90:47
writer_federator_mod::determine_if_outstanding_field_can_be_written
subroutine determine_if_outstanding_field_can_be_written(io_configuration, writer_entry, specific_field)
For a specific field wil determine and handle any outstanding fields writes until an outstanding writ...
Definition: writer_federator.F90:542
collections_mod::c_get_iterator
Definition: collections.F90:581
writer_federator_mod::get_index_of_group
integer function get_index_of_group(io_configuration, group_name)
Searches the IO server configuration for a group with a specific name and returns the index to that g...
Definition: writer_federator.F90:1403
collections_mod::list_type
List data structure which implements a doubly linked list. This list will preserve its order.
Definition: collections.F90:60
writer_federator_mod::extract_applicable_time_points
type(map_type) function extract_applicable_time_points(start_time, end_time)
Extracts the applicable time points from the overall map that lie within a specific range.
Definition: writer_federator.F90:823
writer_federator_mod::check_writer_for_trigger
subroutine, public check_writer_for_trigger(io_configuration, source, data_id, data_dump)
Checks all writer entries for any trigger fires and issues the underlying file storage.
Definition: writer_federator.F90:643
writer_federator_mod::check_for_and_issue_chain_write
logical function check_for_and_issue_chain_write(io_configuration, writer_entry)
Will check whether there are any pending writes and if so will issue a chain write for this.
Definition: writer_federator.F90:963
none_time_manipulation_mod::perform_none_time_manipulation
type(data_values_type) function, public perform_none_time_manipulation(instant_values, output_frequency, field_name, timestep, time)
Performs no time manipulation and returns data.
Definition: none_manipulation.F90:35
configuration_parser_mod::get_monc_location
integer function, public get_monc_location(io_configuration, source)
A helper function to get the location of a MONC's configuration in the IO data structure.
Definition: configurationparser.F90:1234
writer_federator_mod::initialise_contiguous_data_regions
subroutine initialise_contiguous_data_regions(io_configuration, field_to_write_information)
Will initialise the collective data regions that form contiguous blocks within the data....
Definition: writer_federator.F90:1445
writer_federator_mod::provide_ordered_field_to_writer_federator
subroutine, public provide_ordered_field_to_writer_federator(io_configuration, field_name, field_namespace, field_values, timestep, time, source)
Definition: writer_federator.F90:313
data_utils_mod::get_scalar_real_from_monc
real(kind=double_precision) function, public get_scalar_real_from_monc(io_configuration, source, data_id, data_dump, key)
Retreives a scalar real with a corresponding key from the raw data dump.
Definition: datautils.F90:416
mpi_communication_mod::unlock_mpi
subroutine, public unlock_mpi()
If we are explicitly managing MPI thread safety (SERIALIZED mode) then unlocks MPI.
Definition: mpicommunication.F90:63
collections_mod::c_remove
Removes a specific element from the list or map.
Definition: collections.F90:419
collections_mod::c_next_string
Definition: collections.F90:594
forthread_mod::forthread_rwlock_wrlock
integer function forthread_rwlock_wrlock(lock_id)
Definition: forthread.F90:532
instantaneous_time_manipulation_mod::finalise_instantaneous_manipulation
subroutine, public finalise_instantaneous_manipulation()
Finalises the instantaneous time manipulation.
Definition: instantaneous_manipulation.F90:34
conversions_mod::conv_to_real
Converts data types to real.
Definition: conversions.F90:60
collections_mod::c_next_mapentry
Definition: collections.F90:606
collections_mod::c_get_integer
Gets a specific integer element out of the list, stack, queue or map with the corresponding key.
Definition: collections.F90:378
forthread_mod::forthread_rwlock_rdlock
integer function forthread_rwlock_rdlock(lock_id)
Definition: forthread.F90:514
grids_mod
Functionality to support the different types of grid and abstraction between global grids and local o...
Definition: grids.F90:5
instantaneous_time_manipulation_mod::perform_instantaneous_time_manipulation
type(data_values_type) function, public perform_instantaneous_time_manipulation(instant_values, output_frequency, field_name, timestep, time)
Performs the instantaneous time manipulation and returns data only if this is to be written to the st...
Definition: instantaneous_manipulation.F90:56
configuration_parser_mod::io_configuration_field_type
Configuration associated with the representation of a specific data field.
Definition: configurationparser.F90:51
collections_mod::c_add_string
Adds a string to the end of the list.
Definition: collections.F90:222
writer_types_mod::pending_write_type
Definition: writer_types.F90:39
collections_mod::c_push_generic
Pushes a generic element onto the stack or queue.
Definition: collections.F90:113
configuration_parser_mod::get_prognostic_field_configuration
logical function, public get_prognostic_field_configuration(io_configuration, field_name, field_namespace, prognostic_config, prognostic_containing_data_defn)
Retrieves the prognostic field configuration corresponding to a specific field name and returns wheth...
Definition: configurationparser.F90:1270
configuration_parser_mod
Parses the XML configuration file to produce the io configuration description which contains the data...
Definition: configurationparser.F90:3
timeaveraged_time_manipulation_mod::is_time_averaged_time_manipulation_ready_to_write
logical function, public is_time_averaged_time_manipulation_ready_to_write(latest_time, output_frequency, write_time, latest_timestep, write_timestep)
Definition: timeaveraged_manipulation.F90:51
instantaneous_time_manipulation_mod::is_instantaneous_time_manipulation_ready_to_write
logical function, public is_instantaneous_time_manipulation_ready_to_write(latest_time, output_frequency, write_time, latest_timestep, write_timestep)
Definition: instantaneous_manipulation.F90:39
forthread_mod::forthread_rwlock_unlock
integer function forthread_rwlock_unlock(lock_id)
Definition: forthread.F90:550
writer_federator_mod::collective_q_field_dims
type(hashmap_type), volatile collective_q_field_dims
Definition: writer_federator.F90:45
collections_mod::hashset_type
Hashset structure which will store unique strings. The hashing aspect means that lookup is very fast ...
Definition: collections.F90:102
writer_federator_mod::add_field_to_writer_entry
integer function add_field_to_writer_entry(io_configuration, writer_entry_index, io_config_facet_index, my_facet_index, field_name, field_namespace, writer_field_names, duplicate_field_names, diagnostic_generation_frequency)
Adds a field to the writer entry, this will split the Q fields. However at initialisation we don't kn...
Definition: writer_federator.F90:1183
instantaneous_time_manipulation_mod
Performs instantaneous time manipulation and only returns a value if the output frequency determines ...
Definition: instantaneous_manipulation.F90:2
datadefn_mod::default_precision
integer, parameter, public default_precision
MPI communication type which we use for the prognostic and calculation data.
Definition: datadefn.F90:17
conversions_mod::conv_single_real_to_double
real(kind=double_precision) function, public conv_single_real_to_double(input_real)
Converts from a single to double precision real. This applies some rounding to a certain number of de...
Definition: conversions.F90:114