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, is_subgrid_var, 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 logical, intent(in) :: is_subgrid_var
358 character (len=128), intent(out) :: field_name, mem_order, units, description, derived_from
359 character (len=128), dimension(3), intent(out) :: dim_names
361 #include "wrf_io_flags.h"
362 #include "wrf_status_codes.h"
365 type (data_node), pointer :: data_cursor
371 if (.not. associated(next_output_field)) return
377 do while (max_level == 0 .and. associated(next_output_field))
379 data_cursor => next_output_field%fieldlist_head
380 if (associated(data_cursor)) then
381 if (.not. is_mask_field(data_cursor%fg_data)) then
382 do while ( associated(data_cursor) )
384 max_level = max_level + 1
385 data_cursor => data_cursor%next
390 if (max_level == 0) next_output_field => next_output_field%next
393 if (max_level > 0 .and. associated(next_output_field)) then
395 if (max_level > 1) ndims = 3
401 if (is_time_dependent(next_output_field%fg_data)) then
403 dim_names(3)(1:32) = next_output_field%fg_data%header%vertical_coord
405 write(dim_names(3),'(a11,i4.4)') 'z-dimension', max_level
408 field_name = get_fieldname(next_output_field%fg_data)
409 istagger = get_staggering(next_output_field%fg_data)
410 if (istagger == M .or. istagger == HH .or. istagger == VV) then
411 dim_names(1) = 'west_east'
412 dim_names(2) = 'south_north'
413 else if (istagger == U) then
414 dim_names(1) = 'west_east_stag'
415 dim_names(2) = 'south_north'
416 else if (istagger == V) then
417 dim_names(1) = 'west_east'
418 dim_names(2) = 'south_north_stag'
419 else if (istagger == CORNER) then
420 dim_names(1) = 'west_east_stag'
421 dim_names(2) = 'south_north_stag'
423 dim_names(1) = 'i-dimension'
424 dim_names(2) = 'j-dimension'
426 units = get_units(next_output_field%fg_data)
427 description = get_description(next_output_field%fg_data)
428 call get_subgrid_dim_name(nest_num, field_name, dim_names(1:2), &
431 next_output_field => next_output_field%next
434 end subroutine get_next_output_fieldname
437 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
438 ! Name: get_subgrid_dim_name
441 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
442 subroutine get_subgrid_dim_name(nest_num, field_name, dimnames, &
443 sub_x, sub_y, istatus)
450 integer, intent(in) :: nest_num
451 integer, intent(out) :: sub_x, sub_y, istatus
452 character(len=128), intent(in) :: field_name
453 character(len=128), dimension(2), intent(inout) :: dimnames
458 sub_x = next_output_field%fg_data%header%sr_x
459 sub_y = next_output_field%fg_data%header%sr_y
462 dimnames(1) = trim(dimnames(1))//"_subgrid"
465 dimnames(2) = trim(dimnames(2))//"_subgrid"
470 end subroutine get_subgrid_dim_name
473 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
474 ! Name: get_next_output_field
477 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
478 subroutine get_next_output_field(field_name, r_array, &
479 start_i, end_i, start_j, end_j, min_level, max_level, istatus)
484 integer, intent(out) :: start_i, end_i, start_j, end_j, min_level, max_level, istatus
485 real, pointer, dimension(:,:,:) :: r_array
486 character (len=128), intent(out) :: field_name
488 #include "wrf_io_flags.h"
489 #include "wrf_status_codes.h"
493 type (data_node), pointer :: data_cursor
494 type (fg_input) :: temp_field
498 if (.not. associated(next_output_field)) return
503 do while (max_level == 0 .and. associated(next_output_field))
505 data_cursor => next_output_field%fieldlist_head
506 if (associated(data_cursor)) then
507 if (.not. is_mask_field(data_cursor%fg_data)) then
508 do while ( associated(data_cursor) )
510 max_level = max_level + 1
511 data_cursor => data_cursor%next
516 if (max_level == 0) next_output_field => next_output_field%next
519 if (max_level > 0 .and. associated(next_output_field)) then
522 end_i = next_output_field%fieldlist_head%field_shape(1)
524 end_j = next_output_field%fieldlist_head%field_shape(2)
526 allocate(r_array(next_output_field%fieldlist_head%field_shape(1), &
527 next_output_field%fieldlist_head%field_shape(2), &
531 data_cursor => next_output_field%fieldlist_head
532 do while ( associated(data_cursor) )
533 call dup(data_cursor%fg_data, temp_field)
534 call storage_get_field(temp_field, istatus)
535 r_array(:,:,k) = temp_field%r_arr
537 data_cursor => data_cursor%next
540 field_name = get_fieldname(next_output_field%fg_data)
542 next_output_field => next_output_field%next
545 end subroutine get_next_output_field
548 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
549 ! Name: storage_delete_field
551 ! Purpose: Deletes the stored fg_input type whose header matches delete_me
552 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
553 subroutine storage_delete_field(delete_me)
558 type (fg_input), intent(in) :: delete_me
563 character (len=64) :: fname
564 type (head_node), pointer :: name_cursor
565 type (data_node), pointer :: data_cursor
567 ! We'll first see if there is a list for this fieldname
569 do while (associated(name_cursor))
570 if (primary_cmp(name_cursor%fg_data, delete_me) == EQUAL) exit
571 name_cursor => name_cursor%next
574 if (.not. associated(name_cursor)) return
576 ! At this point, name_cursor points to a valid head node for fieldname
577 data_cursor => name_cursor%fieldlist_head
578 do while ( associated(data_cursor) )
579 if (secondary_cmp(delete_me, data_cursor%fg_data) == EQUAL) then
581 if (data_cursor%filenumber > 0) then
583 inquire(unit=funit, opened=is_used)
584 if (.not. is_used) exit
586 write(fname,'(i9.9,a2,i3.3)') data_cursor%filenumber,'.p',my_proc_id
587 open(funit,file=trim(fname),form='unformatted',status='old')
588 close(funit,status='delete')
590 call remove_index(data_cursor%heap_index)
591 memsize = memsize - size(data_cursor%fg_data%r_arr)
592 deallocate(data_cursor%fg_data%r_arr)
594 if (associated(data_cursor%fg_data%valid_mask)) call bitarray_destroy(data_cursor%fg_data%valid_mask)
595 nullify(data_cursor%fg_data%valid_mask)
596 if (associated(data_cursor%fg_data%modified_mask)) call bitarray_destroy(data_cursor%fg_data%modified_mask)
597 nullify(data_cursor%fg_data%modified_mask)
599 ! Only item in the list
600 if (.not. associated(data_cursor%next) .and. &
601 .not. associated(data_cursor%prev)) then
602 nullify(name_cursor%fieldlist_head)
603 nullify(name_cursor%fieldlist_tail)
604 deallocate(data_cursor)
605 ! DO WE REMOVE THIS HEADER NODE AT THIS POINT?
609 else if (.not. associated(data_cursor%prev)) then
610 name_cursor%fieldlist_head => data_cursor%next
611 nullify(data_cursor%next%prev)
612 deallocate(data_cursor)
616 else if (.not. associated(data_cursor%next)) then
617 name_cursor%fieldlist_tail => data_cursor%prev
618 nullify(data_cursor%prev%next)
619 deallocate(data_cursor)
624 data_cursor%prev%next => data_cursor%next
625 data_cursor%next%prev => data_cursor%prev
626 deallocate(data_cursor)
632 data_cursor => data_cursor%next
635 end subroutine storage_delete_field
638 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
639 ! Name: storage_delete_all_td
641 ! Purpose: Deletes the stored time-dependent data
642 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
643 subroutine storage_delete_all_td()
650 character (len=64) :: fname
651 type (head_node), pointer :: name_cursor
652 type (data_node), pointer :: data_cursor, next_cursor
654 ! We'll first see if there is a list for this fieldname
656 do while (associated(name_cursor))
658 data_cursor => name_cursor%fieldlist_head
659 do while ( associated(data_cursor) )
660 if ( is_time_dependent(data_cursor%fg_data) ) then
662 if (data_cursor%filenumber > 0) then
664 inquire(unit=funit, opened=is_used)
665 if (.not. is_used) exit
667 write(fname,'(i9.9,a2,i3.3)') data_cursor%filenumber,'.p',my_proc_id
668 open(funit,file=trim(fname),form='unformatted',status='old')
669 close(funit,status='delete')
671 call remove_index(data_cursor%heap_index)
672 memsize = memsize - size(data_cursor%fg_data%r_arr)
673 deallocate(data_cursor%fg_data%r_arr)
675 if (associated(data_cursor%fg_data%valid_mask)) call bitarray_destroy(data_cursor%fg_data%valid_mask)
676 nullify(data_cursor%fg_data%valid_mask)
677 if (associated(data_cursor%fg_data%modified_mask)) call bitarray_destroy(data_cursor%fg_data%modified_mask)
678 nullify(data_cursor%fg_data%modified_mask)
680 ! We should handle individual cases, that way we can deal with a list
681 ! that has both time independent and time dependent nodes in it.
683 ! Only item in the list
684 if (.not. associated(data_cursor%next) .and. &
685 .not. associated(data_cursor%prev)) then
686 next_cursor => null()
687 nullify(name_cursor%fieldlist_head)
688 nullify(name_cursor%fieldlist_tail)
689 deallocate(data_cursor)
690 ! DO WE REMOVE THIS HEADER NODE AT THIS POINT?
693 else if (.not. associated(data_cursor%prev)) then
694 name_cursor%fieldlist_head => data_cursor%next
695 next_cursor => data_cursor%next
696 nullify(data_cursor%next%prev)
697 deallocate(data_cursor)
700 else if (.not. associated(data_cursor%next)) then
701 ! THIS CASE SHOULD PROBABLY NOT OCCUR
702 name_cursor%fieldlist_tail => data_cursor%prev
703 next_cursor => null()
704 nullify(data_cursor%prev%next)
705 deallocate(data_cursor)
709 ! THIS CASE SHOULD PROBABLY NOT OCCUR
710 next_cursor => data_cursor%next
711 data_cursor%prev%next => data_cursor%next
712 data_cursor%next%prev => data_cursor%prev
713 deallocate(data_cursor)
718 data_cursor => next_cursor
721 name_cursor => name_cursor%next
724 end subroutine storage_delete_all_td
727 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
728 ! Name: storage_get_levels
730 ! Purpose: Returns a list of all levels for the field indicated in the_header.
731 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
732 subroutine storage_get_levels(the_header, list)
737 integer, pointer, dimension(:) :: list
738 type (fg_input), intent(in) :: the_header
742 type (head_node), pointer :: name_cursor
743 type (data_node), pointer :: data_cursor
745 if (associated(list)) deallocate(list)
748 ! We'll first see if there is a list for this header
750 do while (associated(name_cursor))
751 if (primary_cmp(name_cursor%fg_data, the_header) == EQUAL) exit
752 name_cursor => name_cursor%next
755 if (.not. associated(name_cursor)) return
758 ! At this point, name_cursor points to a valid head node for fieldname
759 data_cursor => name_cursor%fieldlist_head
760 do while ( associated(data_cursor) )
762 if (.not. associated(data_cursor%next)) exit
763 data_cursor => data_cursor%next
766 if (n > 0) allocate(list(n))
769 do while ( associated(data_cursor) )
770 list(n) = get_level(data_cursor%fg_data)
772 data_cursor => data_cursor%prev
775 end subroutine storage_get_levels
778 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
779 ! Name: storage_delete_all
781 ! Purpose: Deletes all data, both time-independent and time-dependent.
782 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
783 subroutine storage_delete_all()
790 character (len=64) :: fname
791 type (head_node), pointer :: name_cursor
792 type (data_node), pointer :: data_cursor
794 ! We'll first see if there is already a list for this fieldname
796 do while (associated(name_cursor))
798 if (associated(name_cursor%fieldlist_head)) then
799 data_cursor => name_cursor%fieldlist_head
800 do while ( associated(data_cursor) )
801 name_cursor%fieldlist_head => data_cursor%next
803 if (data_cursor%filenumber > 0) then
805 inquire(unit=funit, opened=is_used)
806 if (.not. is_used) exit
808 write(fname,'(i9.9,a2,i3.3)') data_cursor%filenumber,'.p',my_proc_id
809 open(funit,file=trim(fname),form='unformatted',status='old')
810 close(funit,status='delete')
812 call remove_index(data_cursor%heap_index)
813 memsize = memsize - size(data_cursor%fg_data%r_arr)
814 deallocate(data_cursor%fg_data%r_arr)
816 if (associated(data_cursor%fg_data%valid_mask)) call bitarray_destroy(data_cursor%fg_data%valid_mask)
817 nullify(data_cursor%fg_data%valid_mask)
818 if (associated(data_cursor%fg_data%modified_mask)) call bitarray_destroy(data_cursor%fg_data%modified_mask)
819 nullify(data_cursor%fg_data%modified_mask)
821 deallocate(data_cursor)
822 data_cursor => name_cursor%fieldlist_head
826 head => name_cursor%next
827 deallocate(name_cursor)
836 end subroutine storage_delete_all
839 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
840 ! Name: storage_get_all_headers
843 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
844 subroutine storage_get_all_headers(header_list)
849 type (fg_input), pointer, dimension(:) :: header_list
853 type (head_node), pointer :: name_cursor
857 ! First find out how many time-dependent headers there are
860 do while (associated(name_cursor))
861 if (associated(name_cursor%fieldlist_head)) then
862 if (.not. is_mask_field(name_cursor%fieldlist_head%fg_data)) then
863 nheaders = nheaders + 1
866 name_cursor => name_cursor%next
869 allocate(header_list(nheaders))
873 do while (associated(name_cursor))
874 if (associated(name_cursor%fieldlist_head)) then
875 if (.not. is_mask_field(name_cursor%fieldlist_head%fg_data)) then
876 nheaders = nheaders + 1
877 call dup(name_cursor%fieldlist_head%fg_data, header_list(nheaders))
880 name_cursor => name_cursor%next
883 end subroutine storage_get_all_headers
886 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
887 ! Name: storage_get_all_td_headers
890 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
891 subroutine storage_get_td_headers(header_list)
896 type (fg_input), pointer, dimension(:) :: header_list
900 type (head_node), pointer :: name_cursor
904 ! First find out how many time-dependent headers there are
907 do while (associated(name_cursor))
908 if (associated(name_cursor%fieldlist_head)) then
909 if (is_time_dependent(name_cursor%fieldlist_head%fg_data) .and. &
910 .not. is_mask_field(name_cursor%fieldlist_head%fg_data)) then
911 nheaders = nheaders + 1
914 name_cursor => name_cursor%next
917 allocate(header_list(nheaders))
921 do while (associated(name_cursor))
922 if (associated(name_cursor%fieldlist_head)) then
923 if (is_time_dependent(name_cursor%fieldlist_head%fg_data) .and. &
924 .not. is_mask_field(name_cursor%fieldlist_head%fg_data)) then
925 nheaders = nheaders + 1
926 call dup(name_cursor%fieldlist_head%fg_data, header_list(nheaders))
929 name_cursor => name_cursor%next
932 end subroutine storage_get_td_headers
935 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
936 ! Name: storage_print_fields
939 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
940 subroutine storage_print_fields()
948 integer :: i, j, k, lmax, n_fields, n_levels, max_levels, itemp
949 logical, allocatable, dimension(:,:) :: field_has_level
950 integer, allocatable, dimension(:) :: all_levels
951 integer, pointer, dimension(:) :: ilevels
952 character (len=128), allocatable, dimension(:) :: fieldname_list
953 character (len=9) :: ctemp
954 type (fg_input), pointer, dimension(:) :: header_list
956 type (list) :: all_levs
958 !CWH Initialize local pointer variables
960 nullify(header_list) !MGD initialization for header_list should not be necessary
962 call list_init(all_levs)
963 call storage_get_td_headers(header_list)
964 n_fields = size(header_list)
966 allocate(fieldname_list(n_fields))
971 fieldname_list(i) = header_list(i)%header%field
972 call storage_get_levels(header_list(i), ilevels)
974 if (.not. list_search(all_levs, ikey=ilevels(j), ivalue=itemp)) then
975 call list_insert(all_levs, ikey=ilevels(j), ivalue=ilevels(j))
978 n_levels = size(ilevels)
979 if (n_levels > max_levels) max_levels = n_levels
980 if (associated(ilevels)) deallocate(ilevels)
983 max_levels = list_length(all_levs)
985 allocate(all_levels(max_levels))
986 allocate(field_has_level(n_fields,max_levels))
988 field_has_level(:,:) = .false.
992 call storage_get_levels(header_list(i), ilevels)
993 n_levels = size(ilevels)
996 if (all_levels(k) == ilevels(j)) exit
999 all_levels(k) = ilevels(j)
1002 field_has_level(i,k) = .true.
1004 if (associated(ilevels)) deallocate(ilevels)
1007 call mprintf(.true.,DEBUG,' .',newline=.false.)
1009 write(ctemp,'(a9)') fieldname_list(i)(1:9)
1010 call right_justify(ctemp,9)
1011 call mprintf(.true.,DEBUG,ctemp,newline=.false.)
1013 call mprintf(.true.,DEBUG,' ',newline=.true.)
1015 write(ctemp,'(i9)') all_levels(j)
1016 call mprintf(.true.,DEBUG,'%s ',s1=ctemp,newline=.false.)
1018 if (field_has_level(i,j)) then
1019 call mprintf(.true.,DEBUG,' X',newline=.false.)
1021 call mprintf(.true.,DEBUG,' -',newline=.false.)
1024 call mprintf(.true.,DEBUG,' ',newline=.true.)
1027 deallocate(all_levels)
1028 deallocate(field_has_level)
1029 deallocate(fieldname_list)
1030 deallocate(header_list)
1032 call list_destroy(all_levs)
1034 end subroutine storage_print_fields
1037 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1038 ! Name: find_missing_values
1041 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1042 subroutine find_missing_values()
1048 logical :: found_missing
1049 type (head_node), pointer :: name_cursor
1050 type (data_node), pointer :: data_cursor
1052 found_missing = .false.
1055 do while (associated(name_cursor))
1057 if (associated(name_cursor%fieldlist_head)) then
1058 data_cursor => name_cursor%fieldlist_head
1059 do while ( associated(data_cursor) )
1060 if (.not. associated(data_cursor%fg_data%valid_mask)) then
1061 call mprintf(.true.,INFORM, &
1062 'Field %s does not have a valid mask and will not be checked for missing values', &
1063 s1=data_cursor%fg_data%header%field)
1065 ILOOP: do i=1,data_cursor%fg_data%header%dim1(2)-data_cursor%fg_data%header%dim1(1)+1
1066 JLOOP: do j=1,data_cursor%fg_data%header%dim2(2)-data_cursor%fg_data%header%dim2(1)+1
1067 if (.not. bitarray_test(data_cursor%fg_data%valid_mask,i,j)) then
1068 found_missing = .true.
1069 call mprintf(.true.,WARN,'Field %s has missing values at level %i at (i,j)=(%i,%i)', &
1070 s1=data_cursor%fg_data%header%field, &
1071 i1=data_cursor%fg_data%header%vertical_level, &
1072 i2=i+data_cursor%fg_data%header%dim1(1)-1, &
1073 i3=j+data_cursor%fg_data%header%dim2(1)-1)
1079 data_cursor => data_cursor%next
1083 name_cursor => name_cursor%next
1086 call mprintf(found_missing,ERROR,'Missing values encountered in interpolated fields. Stopping.')
1088 end subroutine find_missing_values
1091 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1092 ! Name: storage_print_headers
1095 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1096 subroutine storage_print_headers()
1101 type (head_node), pointer :: name_cursor
1102 type (data_node), pointer :: data_cursor
1104 call mprintf(.true.,DEBUG,'>>>> STORED FIELDS <<<<')
1105 call mprintf(.true.,DEBUG,'=======================')
1107 ! We'll first see if there is already a list for this fieldname
1109 do while (associated(name_cursor))
1110 call print_header(name_cursor%fg_data)
1112 if (associated(name_cursor%fieldlist_head)) then
1113 data_cursor => name_cursor%fieldlist_head
1114 do while ( associated(data_cursor) )
1115 call mprintf(.true.,DEBUG,' - %i', i1=get_level(data_cursor%fg_data))
1116 call mprintf(.true.,DEBUG,' ')
1117 data_cursor => data_cursor%next
1121 name_cursor => name_cursor%next
1124 end subroutine storage_print_headers
1126 end module storage_module