5 use misc_definitions_module
9 ! Maximum umber of words to keep in memory at a time
10 ! THIS MUST BE AT LEAST AS LARGE AS THE SIZE OF THE LARGEST ARRAY TO BE STORED
11 integer, parameter :: MEMSIZE_MAX = 1E9
13 ! Name (when formatted as i9.9) of next file to be used as array storage
14 integer :: next_filenumber = 1
16 ! Time counter used by policy for evicting arrays to Fortran units
17 integer :: global_time = 0
19 ! Current memory usage of module
20 integer :: memsize = 0
22 ! Primary head and tail pointers
23 type (head_node), pointer :: head => null()
24 type (head_node), pointer :: tail => null()
26 ! Pointer for get_next_output_fieldname
27 type (head_node), pointer :: next_output_field => null()
31 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
34 ! Purpose: Initialize the storage module.
35 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
36 subroutine storage_init()
42 end subroutine storage_init
45 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
46 ! Name: reset_next_field
48 ! Purpose: Sets the next field to the first available field
49 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
50 subroutine reset_next_field()
54 next_output_field => head
56 end subroutine reset_next_field
59 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
60 ! Name: storage_put_field
62 ! Purpose: Stores an fg_input type. Upon return, IT MUST NOT BE ASSUMED that
63 ! store_me contains valid data, since all such data may have been written
65 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
66 subroutine storage_put_field(store_me)
71 type (fg_input), intent(in) :: store_me
76 character (len=64) :: fname
77 type (head_node), pointer :: name_cursor
78 type (data_node), pointer :: data_cursor
79 type (data_node), pointer :: newnode
80 type (data_node), pointer :: evictnode
82 !CWH Initialize local pointer variables
83 nullify(evictnode) !MGD initialization for evictnode should not be necessary
85 ! We'll first see if there is already a list for this fieldname
87 do while (associated(name_cursor))
88 if (primary_cmp(name_cursor%fg_data, store_me) == EQUAL) exit
89 name_cursor => name_cursor%next
92 ! If not, create a new node in the primary list
93 if (.not. associated(name_cursor)) then
95 call dup(store_me, name_cursor%fg_data)
96 nullify(name_cursor%fg_data%r_arr)
97 nullify(name_cursor%fg_data%valid_mask)
98 nullify(name_cursor%fg_data%modified_mask)
99 nullify(name_cursor%fieldlist_head)
100 nullify(name_cursor%fieldlist_tail)
101 nullify(name_cursor%prev)
102 name_cursor%next => head
103 if (.not. associated(head)) tail => name_cursor
106 if ((name_cursor%fg_data%header%time_dependent .and. .not. store_me%header%time_dependent) .or. &
107 (.not. name_cursor%fg_data%header%time_dependent .and. store_me%header%time_dependent)) then
108 call mprintf(.true.,ERROR,'Cannot combine time-independent data with '// &
109 'time-dependent data for field %s',s1=store_me%header%field)
113 ! At this point, name_cursor points to a valid head node for fieldname
114 data_cursor => name_cursor%fieldlist_head
115 do while ( associated(data_cursor) )
116 if ((secondary_cmp(store_me, data_cursor%fg_data) == LESS) .or. &
117 (secondary_cmp(store_me, data_cursor%fg_data) == EQUAL)) exit
118 data_cursor => data_cursor%next
121 if (associated(data_cursor)) then
122 if (secondary_cmp(store_me, data_cursor%fg_data) == EQUAL) then
123 if (data_cursor%filenumber > 0) then
124 ! BUG: Might need to deal with freeing up a file
125 call mprintf(.true.,WARN,'WE NEED TO FREE THE FILE ASSOCIATED WITH DATA_CURSOR')
126 call mprintf(.true.,WARN,'PLEASE REPORT THIS BUG TO THE DEVELOPER!')
128 data_cursor%fg_data%r_arr => store_me%r_arr
129 data_cursor%fg_data%valid_mask => store_me%valid_mask
130 data_cursor%fg_data%modified_mask => store_me%modified_mask
136 call dup(store_me, newnode%fg_data)
138 newnode%field_shape = shape(newnode%fg_data%r_arr)
139 memsize = memsize + size(newnode%fg_data%r_arr)
140 newnode%last_used = global_time
141 global_time = global_time + 1
142 newnode%filenumber = 0
143 call add_to_heap(newnode)
145 do while (memsize > MEMSIZE_MAX)
146 call get_min(evictnode)
147 evictnode%filenumber = next_filenumber
148 next_filenumber = next_filenumber + 1
150 inquire(unit=funit, opened=is_used)
151 if (.not. is_used) exit
153 memsize = memsize - size(evictnode%fg_data%r_arr)
154 write(fname,'(i9.9,a2,i3.3)') evictnode%filenumber,'.p',my_proc_id
155 open(funit,file=trim(fname),form='unformatted',status='unknown')
156 write(funit) evictnode%fg_data%r_arr
158 deallocate(evictnode%fg_data%r_arr)
161 ! Inserting node at the tail of list
162 if (.not. associated(data_cursor)) then
163 newnode%prev => name_cursor%fieldlist_tail
164 nullify(newnode%next)
166 ! List is actually empty
167 if (.not. associated(name_cursor%fieldlist_head)) then
168 name_cursor%fieldlist_head => newnode
169 name_cursor%fieldlist_tail => newnode
171 name_cursor%fieldlist_tail%next => newnode
172 name_cursor%fieldlist_tail => newnode
175 ! Inserting node at the head of list
176 else if ((secondary_cmp(name_cursor%fieldlist_head%fg_data, newnode%fg_data) == GREATER) .or. &
177 (secondary_cmp(name_cursor%fieldlist_head%fg_data, newnode%fg_data) == EQUAL)) then
178 nullify(newnode%prev)
179 newnode%next => name_cursor%fieldlist_head
180 name_cursor%fieldlist_head%prev => newnode
181 name_cursor%fieldlist_head => newnode
183 ! Inserting somewhere in the middle of the list
185 newnode%prev => data_cursor%prev
186 newnode%next => data_cursor
187 data_cursor%prev%next => newnode
188 data_cursor%prev => newnode
191 end subroutine storage_put_field
194 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
195 ! Name: storage_get_field
197 ! Purpose: Retrieves an fg_input type from storage; if the fg_input type whose
198 ! header matches the header of get_me does not exist, istatus = 1 upon
199 ! return; if the requested fg_input type is found, istatus = 0
200 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
201 subroutine storage_get_field(get_me, istatus)
206 type (fg_input), intent(inout) :: get_me
207 integer, intent(out) :: istatus
212 character (len=64) :: fname
213 type (head_node), pointer :: name_cursor
214 type (data_node), pointer :: data_cursor
215 type (data_node), pointer :: evictnode
217 !CWH Initialize local pointer variables
218 nullify(evictnode) !MGD initialization for evictnodeshould not be necessary
220 global_time = global_time + 1
224 ! We'll first see if there is already a list for this fieldname
226 do while (associated(name_cursor))
227 if (primary_cmp(name_cursor%fg_data, get_me) == EQUAL) exit
228 name_cursor => name_cursor%next
231 if (.not. associated(name_cursor)) return
233 ! At this point, name_cursor points to a valid head node for fieldname
234 data_cursor => name_cursor%fieldlist_head
235 do while ( associated(data_cursor) )
236 if (secondary_cmp(get_me, data_cursor%fg_data) == EQUAL) then
237 call dup(data_cursor%fg_data, get_me)
239 ! Before deciding whether we need to write an array to disk, first consider
240 ! that reading the requested array will use memory
241 if (data_cursor%filenumber > 0) then
242 memsize = memsize + data_cursor%field_shape(1)*data_cursor%field_shape(2)
245 ! If we exceed our memory limit, we need to evict
246 do while (memsize > MEMSIZE_MAX)
247 call get_min(evictnode)
248 evictnode%filenumber = next_filenumber
249 next_filenumber = next_filenumber + 1
251 inquire(unit=funit, opened=is_used)
252 if (.not. is_used) exit
254 memsize = memsize - size(evictnode%fg_data%r_arr)
255 write(fname,'(i9.9,a2,i3.3)') evictnode%filenumber,'.p',my_proc_id
256 open(funit,file=trim(fname),form='unformatted',status='unknown')
257 write(funit) evictnode%fg_data%r_arr
259 deallocate(evictnode%fg_data%r_arr)
262 ! Get requested array
263 if (data_cursor%filenumber > 0) then
264 data_cursor%last_used = global_time
265 global_time = global_time + 1
266 call add_to_heap(data_cursor)
267 write(fname,'(i9.9,a2,i3.3)') data_cursor%filenumber,'.p',my_proc_id
269 inquire(unit=funit, opened=is_used)
270 if (.not. is_used) exit
272 open(funit,file=trim(fname),form='unformatted',status='old')
273 allocate(data_cursor%fg_data%r_arr(data_cursor%field_shape(1),data_cursor%field_shape(2)))
274 read(funit) data_cursor%fg_data%r_arr
275 get_me%r_arr => data_cursor%fg_data%r_arr
276 close(funit,status='delete')
277 data_cursor%filenumber = 0
279 get_me%r_arr => data_cursor%fg_data%r_arr
281 call remove_index(data_cursor%heap_index)
282 data_cursor%last_used = global_time
283 global_time = global_time + 1
284 call add_to_heap(data_cursor)
290 data_cursor => data_cursor%next
293 end subroutine storage_get_field
296 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
297 ! Name: storage_query_field
300 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
301 subroutine storage_query_field(get_me, istatus)
306 type (fg_input), intent(inout) :: get_me
307 integer, intent(out) :: istatus
310 type (head_node), pointer :: name_cursor
311 type (data_node), pointer :: data_cursor
315 ! We'll first see if there is already a list for this fieldname
317 do while (associated(name_cursor))
318 if (primary_cmp(name_cursor%fg_data, get_me) == EQUAL) exit
319 name_cursor => name_cursor%next
322 if (.not. associated(name_cursor)) return
324 ! At this point, name_cursor points to a valid head node for fieldname
325 data_cursor => name_cursor%fieldlist_head
326 do while ( associated(data_cursor) )
327 if (secondary_cmp(get_me, data_cursor%fg_data) == EQUAL) then
328 get_me%r_arr => data_cursor%fg_data%r_arr
329 get_me%valid_mask => data_cursor%fg_data%valid_mask
330 get_me%modified_mask => data_cursor%fg_data%modified_mask
334 data_cursor => data_cursor%next
337 end subroutine storage_query_field
340 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
341 ! Name: get_next_output_fieldname
344 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
345 subroutine get_next_output_fieldname(nest_num, field_name, ndims, &
346 min_level, max_level, &
347 istagger, mem_order, dim_names, units, description, &
348 sr_x, sr_y, derived_from, &
354 integer, intent(in) :: nest_num
355 integer, intent(out) :: ndims, min_level, max_level, istagger, istatus
356 integer, intent(out) :: sr_x, sr_y
357 character (len=128), intent(out) :: field_name, mem_order, units, description, derived_from
358 character (len=128), dimension(3), intent(out) :: dim_names
360 #include "wrf_io_flags.h"
361 #include "wrf_status_codes.h"
364 type (data_node), pointer :: data_cursor
370 if (.not. associated(next_output_field)) return
376 do while (max_level == 0 .and. associated(next_output_field))
378 data_cursor => next_output_field%fieldlist_head
379 if (associated(data_cursor)) then
380 if (.not. is_mask_field(data_cursor%fg_data)) then
381 do while ( associated(data_cursor) )
383 max_level = max_level + 1
384 data_cursor => data_cursor%next
389 if (max_level == 0) next_output_field => next_output_field%next
392 if (max_level > 0 .and. associated(next_output_field)) then
394 if (max_level > 1) ndims = 3
400 if (is_time_dependent(next_output_field%fg_data)) then
402 dim_names(3)(1:32) = next_output_field%fg_data%header%vertical_coord
404 write(dim_names(3),'(a11,i4.4)') 'z-dimension', max_level
407 field_name = get_fieldname(next_output_field%fg_data)
408 istagger = get_staggering(next_output_field%fg_data)
409 if (istagger == M .or. istagger == HH .or. istagger == VV) then
410 dim_names(1) = 'west_east'
411 dim_names(2) = 'south_north'
412 else if (istagger == U) then
413 dim_names(1) = 'west_east_stag'
414 dim_names(2) = 'south_north'
415 else if (istagger == V) then
416 dim_names(1) = 'west_east'
417 dim_names(2) = 'south_north_stag'
418 else if (istagger == CORNER) then
419 dim_names(1) = 'west_east_stag'
420 dim_names(2) = 'south_north_stag'
422 dim_names(1) = 'i-dimension'
423 dim_names(2) = 'j-dimension'
425 units = get_units(next_output_field%fg_data)
426 description = get_description(next_output_field%fg_data)
427 call get_subgrid_dim_name(nest_num, field_name, dim_names(1:2), &
430 next_output_field => next_output_field%next
433 end subroutine get_next_output_fieldname
436 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
437 ! Name: get_subgrid_dim_name
440 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
441 subroutine get_subgrid_dim_name(nest_num, field_name, dimnames, &
442 sub_x, sub_y, istatus)
449 integer, intent(in) :: nest_num
450 integer, intent(out) :: sub_x, sub_y, istatus
451 character(len=128), intent(in) :: field_name
452 character(len=128), dimension(2), intent(inout) :: dimnames
457 sub_x = next_output_field%fg_data%header%sr_x
458 sub_y = next_output_field%fg_data%header%sr_y
461 dimnames(1) = trim(dimnames(1))//"_subgrid"
464 dimnames(2) = trim(dimnames(2))//"_subgrid"
469 end subroutine get_subgrid_dim_name
472 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
473 ! Name: get_next_output_field
476 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
477 subroutine get_next_output_field(field_name, r_array, &
478 start_i, end_i, start_j, end_j, min_level, max_level, istatus)
483 integer, intent(out) :: start_i, end_i, start_j, end_j, min_level, max_level, istatus
484 real, pointer, dimension(:,:,:) :: r_array
485 character (len=128), intent(out) :: field_name
487 #include "wrf_io_flags.h"
488 #include "wrf_status_codes.h"
492 type (data_node), pointer :: data_cursor
493 type (fg_input) :: temp_field
497 if (.not. associated(next_output_field)) return
502 do while (max_level == 0 .and. associated(next_output_field))
504 data_cursor => next_output_field%fieldlist_head
505 if (associated(data_cursor)) then
506 if (.not. is_mask_field(data_cursor%fg_data)) then
507 do while ( associated(data_cursor) )
509 max_level = max_level + 1
510 data_cursor => data_cursor%next
515 if (max_level == 0) next_output_field => next_output_field%next
518 if (max_level > 0 .and. associated(next_output_field)) then
521 end_i = next_output_field%fieldlist_head%field_shape(1)
523 end_j = next_output_field%fieldlist_head%field_shape(2)
525 allocate(r_array(next_output_field%fieldlist_head%field_shape(1), &
526 next_output_field%fieldlist_head%field_shape(2), &
530 data_cursor => next_output_field%fieldlist_head
531 do while ( associated(data_cursor) )
532 call dup(data_cursor%fg_data, temp_field)
533 call storage_get_field(temp_field, istatus)
534 r_array(:,:,k) = temp_field%r_arr
536 data_cursor => data_cursor%next
539 field_name = get_fieldname(next_output_field%fg_data)
541 next_output_field => next_output_field%next
544 end subroutine get_next_output_field
547 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
548 ! Name: storage_delete_field
550 ! Purpose: Deletes the stored fg_input type whose header matches delete_me
551 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
552 subroutine storage_delete_field(delete_me)
557 type (fg_input), intent(in) :: delete_me
562 character (len=64) :: fname
563 type (head_node), pointer :: name_cursor
564 type (data_node), pointer :: data_cursor
566 ! We'll first see if there is a list for this fieldname
568 do while (associated(name_cursor))
569 if (primary_cmp(name_cursor%fg_data, delete_me) == EQUAL) exit
570 name_cursor => name_cursor%next
573 if (.not. associated(name_cursor)) return
575 ! At this point, name_cursor points to a valid head node for fieldname
576 data_cursor => name_cursor%fieldlist_head
577 do while ( associated(data_cursor) )
578 if (secondary_cmp(delete_me, data_cursor%fg_data) == EQUAL) then
580 if (data_cursor%filenumber > 0) then
582 inquire(unit=funit, opened=is_used)
583 if (.not. is_used) exit
585 write(fname,'(i9.9,a2,i3.3)') data_cursor%filenumber,'.p',my_proc_id
586 open(funit,file=trim(fname),form='unformatted',status='old')
587 close(funit,status='delete')
589 call remove_index(data_cursor%heap_index)
590 memsize = memsize - size(data_cursor%fg_data%r_arr)
591 deallocate(data_cursor%fg_data%r_arr)
593 if (associated(data_cursor%fg_data%valid_mask)) call bitarray_destroy(data_cursor%fg_data%valid_mask)
594 nullify(data_cursor%fg_data%valid_mask)
595 if (associated(data_cursor%fg_data%modified_mask)) call bitarray_destroy(data_cursor%fg_data%modified_mask)
596 nullify(data_cursor%fg_data%modified_mask)
598 ! Only item in the list
599 if (.not. associated(data_cursor%next) .and. &
600 .not. associated(data_cursor%prev)) then
601 nullify(name_cursor%fieldlist_head)
602 nullify(name_cursor%fieldlist_tail)
603 deallocate(data_cursor)
604 ! DO WE REMOVE THIS HEADER NODE AT THIS POINT?
608 else if (.not. associated(data_cursor%prev)) then
609 name_cursor%fieldlist_head => data_cursor%next
610 nullify(data_cursor%next%prev)
611 deallocate(data_cursor)
615 else if (.not. associated(data_cursor%next)) then
616 name_cursor%fieldlist_tail => data_cursor%prev
617 nullify(data_cursor%prev%next)
618 deallocate(data_cursor)
623 data_cursor%prev%next => data_cursor%next
624 data_cursor%next%prev => data_cursor%prev
625 deallocate(data_cursor)
631 data_cursor => data_cursor%next
634 end subroutine storage_delete_field
637 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
638 ! Name: storage_delete_all_td
640 ! Purpose: Deletes the stored time-dependent data
641 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
642 subroutine storage_delete_all_td()
649 character (len=64) :: fname
650 type (head_node), pointer :: name_cursor
651 type (data_node), pointer :: data_cursor, next_cursor
653 ! We'll first see if there is a list for this fieldname
655 do while (associated(name_cursor))
657 data_cursor => name_cursor%fieldlist_head
658 do while ( associated(data_cursor) )
659 if ( is_time_dependent(data_cursor%fg_data) ) then
661 if (data_cursor%filenumber > 0) then
663 inquire(unit=funit, opened=is_used)
664 if (.not. is_used) exit
666 write(fname,'(i9.9,a2,i3.3)') data_cursor%filenumber,'.p',my_proc_id
667 open(funit,file=trim(fname),form='unformatted',status='old')
668 close(funit,status='delete')
670 call remove_index(data_cursor%heap_index)
671 memsize = memsize - size(data_cursor%fg_data%r_arr)
672 deallocate(data_cursor%fg_data%r_arr)
674 if (associated(data_cursor%fg_data%valid_mask)) call bitarray_destroy(data_cursor%fg_data%valid_mask)
675 nullify(data_cursor%fg_data%valid_mask)
676 if (associated(data_cursor%fg_data%modified_mask)) call bitarray_destroy(data_cursor%fg_data%modified_mask)
677 nullify(data_cursor%fg_data%modified_mask)
679 ! We should handle individual cases, that way we can deal with a list
680 ! that has both time independent and time dependent nodes in it.
682 ! Only item in the list
683 if (.not. associated(data_cursor%next) .and. &
684 .not. associated(data_cursor%prev)) then
685 next_cursor => null()
686 nullify(name_cursor%fieldlist_head)
687 nullify(name_cursor%fieldlist_tail)
688 deallocate(data_cursor)
689 ! DO WE REMOVE THIS HEADER NODE AT THIS POINT?
692 else if (.not. associated(data_cursor%prev)) then
693 name_cursor%fieldlist_head => data_cursor%next
694 next_cursor => data_cursor%next
695 nullify(data_cursor%next%prev)
696 deallocate(data_cursor)
699 else if (.not. associated(data_cursor%next)) then
700 ! THIS CASE SHOULD PROBABLY NOT OCCUR
701 name_cursor%fieldlist_tail => data_cursor%prev
702 next_cursor => null()
703 nullify(data_cursor%prev%next)
704 deallocate(data_cursor)
708 ! THIS CASE SHOULD PROBABLY NOT OCCUR
709 next_cursor => data_cursor%next
710 data_cursor%prev%next => data_cursor%next
711 data_cursor%next%prev => data_cursor%prev
712 deallocate(data_cursor)
717 data_cursor => next_cursor
720 name_cursor => name_cursor%next
723 end subroutine storage_delete_all_td
726 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
727 ! Name: storage_get_levels
729 ! Purpose: Returns a list of all levels for the field indicated in the_header.
730 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
731 subroutine storage_get_levels(the_header, list)
736 integer, pointer, dimension(:) :: list
737 type (fg_input), intent(in) :: the_header
741 type (head_node), pointer :: name_cursor
742 type (data_node), pointer :: data_cursor
744 if (associated(list)) deallocate(list)
747 ! We'll first see if there is a list for this header
749 do while (associated(name_cursor))
750 if (primary_cmp(name_cursor%fg_data, the_header) == EQUAL) exit
751 name_cursor => name_cursor%next
754 if (.not. associated(name_cursor)) return
757 ! At this point, name_cursor points to a valid head node for fieldname
758 data_cursor => name_cursor%fieldlist_head
759 do while ( associated(data_cursor) )
761 if (.not. associated(data_cursor%next)) exit
762 data_cursor => data_cursor%next
765 if (n > 0) allocate(list(n))
768 do while ( associated(data_cursor) )
769 list(n) = get_level(data_cursor%fg_data)
771 data_cursor => data_cursor%prev
774 end subroutine storage_get_levels
777 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
778 ! Name: storage_delete_all
780 ! Purpose: Deletes all data, both time-independent and time-dependent.
781 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
782 subroutine storage_delete_all()
789 character (len=64) :: fname
790 type (head_node), pointer :: name_cursor
791 type (data_node), pointer :: data_cursor
793 ! We'll first see if there is already a list for this fieldname
795 do while (associated(name_cursor))
797 if (associated(name_cursor%fieldlist_head)) then
798 data_cursor => name_cursor%fieldlist_head
799 do while ( associated(data_cursor) )
800 name_cursor%fieldlist_head => data_cursor%next
802 if (data_cursor%filenumber > 0) then
804 inquire(unit=funit, opened=is_used)
805 if (.not. is_used) exit
807 write(fname,'(i9.9,a2,i3.3)') data_cursor%filenumber,'.p',my_proc_id
808 open(funit,file=trim(fname),form='unformatted',status='old')
809 close(funit,status='delete')
811 call remove_index(data_cursor%heap_index)
812 memsize = memsize - size(data_cursor%fg_data%r_arr)
813 deallocate(data_cursor%fg_data%r_arr)
815 if (associated(data_cursor%fg_data%valid_mask)) call bitarray_destroy(data_cursor%fg_data%valid_mask)
816 nullify(data_cursor%fg_data%valid_mask)
817 if (associated(data_cursor%fg_data%modified_mask)) call bitarray_destroy(data_cursor%fg_data%modified_mask)
818 nullify(data_cursor%fg_data%modified_mask)
820 deallocate(data_cursor)
821 data_cursor => name_cursor%fieldlist_head
825 head => name_cursor%next
826 deallocate(name_cursor)
835 end subroutine storage_delete_all
838 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
839 ! Name: storage_get_all_headers
842 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
843 subroutine storage_get_all_headers(header_list)
848 type (fg_input), pointer, dimension(:) :: header_list
852 type (head_node), pointer :: name_cursor
856 ! First find out how many time-dependent headers there are
859 do while (associated(name_cursor))
860 if (associated(name_cursor%fieldlist_head)) then
861 if (.not. is_mask_field(name_cursor%fieldlist_head%fg_data)) then
862 nheaders = nheaders + 1
865 name_cursor => name_cursor%next
868 allocate(header_list(nheaders))
872 do while (associated(name_cursor))
873 if (associated(name_cursor%fieldlist_head)) then
874 if (.not. is_mask_field(name_cursor%fieldlist_head%fg_data)) then
875 nheaders = nheaders + 1
876 call dup(name_cursor%fieldlist_head%fg_data, header_list(nheaders))
879 name_cursor => name_cursor%next
882 end subroutine storage_get_all_headers
885 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
886 ! Name: storage_get_all_td_headers
889 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
890 subroutine storage_get_td_headers(header_list)
895 type (fg_input), pointer, dimension(:) :: header_list
899 type (head_node), pointer :: name_cursor
903 ! First find out how many time-dependent headers there are
906 do while (associated(name_cursor))
907 if (associated(name_cursor%fieldlist_head)) then
908 if (is_time_dependent(name_cursor%fieldlist_head%fg_data) .and. &
909 .not. is_mask_field(name_cursor%fieldlist_head%fg_data)) then
910 nheaders = nheaders + 1
913 name_cursor => name_cursor%next
916 allocate(header_list(nheaders))
920 do while (associated(name_cursor))
921 if (associated(name_cursor%fieldlist_head)) then
922 if (is_time_dependent(name_cursor%fieldlist_head%fg_data) .and. &
923 .not. is_mask_field(name_cursor%fieldlist_head%fg_data)) then
924 nheaders = nheaders + 1
925 call dup(name_cursor%fieldlist_head%fg_data, header_list(nheaders))
928 name_cursor => name_cursor%next
931 end subroutine storage_get_td_headers
934 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
935 ! Name: storage_print_fields
938 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
939 subroutine storage_print_fields()
947 integer :: i, j, k, lmax, n_fields, n_levels, max_levels, itemp
948 logical, allocatable, dimension(:,:) :: field_has_level
949 integer, allocatable, dimension(:) :: all_levels
950 integer, pointer, dimension(:) :: ilevels
951 character (len=128), allocatable, dimension(:) :: fieldname_list
952 character (len=9) :: ctemp
953 type (fg_input), pointer, dimension(:) :: header_list
955 type (list) :: all_levs
957 !CWH Initialize local pointer variables
959 nullify(header_list) !MGD initialization for header_list should not be necessary
961 call list_init(all_levs)
962 call storage_get_td_headers(header_list)
963 n_fields = size(header_list)
965 allocate(fieldname_list(n_fields))
970 fieldname_list(i) = header_list(i)%header%field
971 call storage_get_levels(header_list(i), ilevels)
973 if (.not. list_search(all_levs, ikey=ilevels(j), ivalue=itemp)) then
974 call list_insert(all_levs, ikey=ilevels(j), ivalue=ilevels(j))
977 n_levels = size(ilevels)
978 if (n_levels > max_levels) max_levels = n_levels
979 if (associated(ilevels)) deallocate(ilevels)
982 max_levels = list_length(all_levs)
984 allocate(all_levels(max_levels))
985 allocate(field_has_level(n_fields,max_levels))
987 field_has_level(:,:) = .false.
991 call storage_get_levels(header_list(i), ilevels)
992 n_levels = size(ilevels)
995 if (all_levels(k) == ilevels(j)) exit
998 all_levels(k) = ilevels(j)
1001 field_has_level(i,k) = .true.
1003 if (associated(ilevels)) deallocate(ilevels)
1006 call mprintf(.true.,DEBUG,' .',newline=.false.)
1008 write(ctemp,'(a9)') fieldname_list(i)(1:9)
1009 call right_justify(ctemp,9)
1010 call mprintf(.true.,DEBUG,ctemp,newline=.false.)
1012 call mprintf(.true.,DEBUG,' ',newline=.true.)
1014 write(ctemp,'(i9)') all_levels(j)
1015 call mprintf(.true.,DEBUG,'%s ',s1=ctemp,newline=.false.)
1017 if (field_has_level(i,j)) then
1018 call mprintf(.true.,DEBUG,' X',newline=.false.)
1020 call mprintf(.true.,DEBUG,' -',newline=.false.)
1023 call mprintf(.true.,DEBUG,' ',newline=.true.)
1026 deallocate(all_levels)
1027 deallocate(field_has_level)
1028 deallocate(fieldname_list)
1029 deallocate(header_list)
1031 call list_destroy(all_levs)
1033 end subroutine storage_print_fields
1036 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1037 ! Name: find_missing_values
1040 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1041 subroutine find_missing_values()
1047 logical :: found_missing
1048 type (head_node), pointer :: name_cursor
1049 type (data_node), pointer :: data_cursor
1051 found_missing = .false.
1054 do while (associated(name_cursor))
1056 if (associated(name_cursor%fieldlist_head)) then
1057 data_cursor => name_cursor%fieldlist_head
1058 do while ( associated(data_cursor) )
1059 if (.not. associated(data_cursor%fg_data%valid_mask)) then
1060 call mprintf(.true.,INFORM, &
1061 'Field %s does not have a valid mask and will not be checked for missing values', &
1062 s1=data_cursor%fg_data%header%field)
1064 ILOOP: do i=1,data_cursor%fg_data%header%dim1(2)-data_cursor%fg_data%header%dim1(1)+1
1065 JLOOP: do j=1,data_cursor%fg_data%header%dim2(2)-data_cursor%fg_data%header%dim2(1)+1
1066 if (.not. bitarray_test(data_cursor%fg_data%valid_mask,i,j)) then
1067 found_missing = .true.
1068 call mprintf(.true.,WARN,'Field %s has missing values at level %i at (i,j)=(%i,%i)', &
1069 s1=data_cursor%fg_data%header%field, &
1070 i1=data_cursor%fg_data%header%vertical_level, &
1071 i2=i+data_cursor%fg_data%header%dim1(1)-1, &
1072 i3=j+data_cursor%fg_data%header%dim2(1)-1)
1078 data_cursor => data_cursor%next
1082 name_cursor => name_cursor%next
1085 call mprintf(found_missing,ERROR,'Missing values encountered in interpolated fields. Stopping.')
1087 end subroutine find_missing_values
1090 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1091 ! Name: storage_print_headers
1094 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1095 subroutine storage_print_headers()
1100 type (head_node), pointer :: name_cursor
1101 type (data_node), pointer :: data_cursor
1103 call mprintf(.true.,DEBUG,'>>>> STORED FIELDS <<<<')
1104 call mprintf(.true.,DEBUG,'=======================')
1106 ! We'll first see if there is already a list for this fieldname
1108 do while (associated(name_cursor))
1109 call print_header(name_cursor%fg_data)
1111 if (associated(name_cursor%fieldlist_head)) then
1112 data_cursor => name_cursor%fieldlist_head
1113 do while ( associated(data_cursor) )
1114 call mprintf(.true.,DEBUG,' - %i', i1=get_level(data_cursor%fg_data))
1115 call mprintf(.true.,DEBUG,' ')
1116 data_cursor => data_cursor%next
1120 name_cursor => name_cursor%next
1123 end subroutine storage_print_headers
1125 end module storage_module