V3FIT
compression.f
Go to the documentation of this file.
1 !*******************************************************************************
4 !
5 ! Note separating the Doxygen comment block here so detailed decription is
6 ! found in the Module not the file.
7 !
12 !*******************************************************************************
13 
14  MODULE compression
15  USE stel_kinds
16  USE mpi_inc
17 
18  IMPLICIT NONE
19 
20 !*******************************************************************************
21 ! reconstruction module parameters
22 !*******************************************************************************
24  REAL (rprec), PARAMETER :: compression_max_percentage = 100.0
25 
27  CHARACTER (len=*), PARAMETER :: &
28  & compression_data_buffer_post = 'data_buffer'
30  CHARACTER (len=*), PARAMETER :: &
31  & compression_u_buffer_post = 'u_buffer'
33  CHARACTER (len=*), PARAMETER :: &
34  & compression_wvt_buffer_post = 'wvt_buffer'
36  CHARACTER (len=*), PARAMETER :: &
37  & compression_data_dim_post = 'data_dim'
38 
39 !*******************************************************************************
40 ! DERIVED-TYPE DECLARATIONS
41 ! 1) compression base class
42 !
43 !*******************************************************************************
44 !-------------------------------------------------------------------------------
46 !-------------------------------------------------------------------------------
49  REAL (rprec), DIMENSION(:,:), POINTER :: data_buffer => null()
50 
52  REAL (rprec), DIMENSION(:,:), POINTER :: u_buffer => null()
54  REAL (rprec), DIMENSION(:,:), POINTER :: wvt_buffer => null()
55 
58  INTEGER, DIMENSION(:), POINTER :: data_dim => null()
59  END TYPE
60 
61 !-------------------------------------------------------------------------------
65 !-------------------------------------------------------------------------------
69  TYPE (compression_class), POINTER :: p => null()
70  END TYPE
71 
72 !*******************************************************************************
73 ! INTERFACE BLOCKS
74 !*******************************************************************************
75 !-------------------------------------------------------------------------------
78 !-------------------------------------------------------------------------------
80  MODULE PROCEDURE compression_construct_new, &
82  END INTERFACE
83 
84  CONTAINS
85 !*******************************************************************************
86 ! CONSTRUCTION SUBROUTINES
87 !*******************************************************************************
88 !-------------------------------------------------------------------------------
99 !-------------------------------------------------------------------------------
100  FUNCTION compression_construct_new(data_in, svd_cut_off)
101  USE v3_utilities
102 
103  IMPLICIT NONE
104 
105 ! Declare Arguments
107  REAL (rprec), DIMENSION(:,:), INTENT(in) :: data_in
108  REAL (rprec), INTENT(in) :: svd_cut_off
109 
110 ! local arguments
111  INTEGER :: m, n, i
112  REAL (rprec), DIMENSION(:), ALLOCATABLE :: w_svd
113  REAL (rprec), DIMENSION(:), ALLOCATABLE :: svd_work
114  INTEGER :: work_size
115  INTEGER :: status
116  REAL (rprec) :: e_zero
117  REAL (rprec) :: e_r
118 
119 ! Start of executable code
120  ALLOCATE(compression_construct_new)
121 
122 ! Special case if all the data is zero, there is no need to retain any singular
123 ! values. This will be detected in the uncompress by having the data_dim array
124 ! allocated.
125  IF (all(data_in .eq. 0.0)) THEN
126  ALLOCATE(compression_construct_new%data_dim(2))
127  compression_construct_new%data_dim = shape(data_in)
128  RETURN
129  END IF
130 
131  m = SIZE(data_in, 1)
132  n = SIZE(data_in, 2)
133  ALLOCATE(compression_construct_new%data_buffer(m,n))
134  compression_construct_new%data_buffer = data_in
135 
136  IF (svd_cut_off .eq. 0.0) THEN
137  RETURN
138  END IF
139 
140  ALLOCATE(compression_construct_new%wvt_buffer(n,n))
141  ALLOCATE(compression_construct_new%u_buffer(m,m))
142  ALLOCATE(svd_work(1))
143  ALLOCATE(w_svd(min(m,n)))
144 
145  compression_construct_new%wvt_buffer = 0.0
146  compression_construct_new%u_buffer = 0.0
147  w_svd = 0.0
148  svd_work = 0.0
149 
150 ! Find the optimal work size.
151  CALL dgesvd('All', 'All', m, n, &
152  & compression_construct_new%data_buffer, m, &
153  & w_svd, compression_construct_new%u_buffer, m, &
154  & compression_construct_new%wvt_buffer, n, svd_work, -1, &
155  & status)
156  work_size = int(svd_work(1))
157  DEALLOCATE(svd_work)
158  ALLOCATE(svd_work(work_size))
159  svd_work = 0.0
160 
161 ! Factor the matrix to M = U * W * V^T
162  CALL dgesvd('All', 'All', m, n, &
163  & compression_construct_new%data_buffer, m, &
164  & w_svd, compression_construct_new%u_buffer, m, &
165  & compression_construct_new%wvt_buffer, n, svd_work, &
166  & work_size, status)
167  CALL assert_eq(0, status, 'dgesvd problem when compressing ' // &
168  & 'buffer')
169 
170  DEALLOCATE(svd_work)
171 
172 ! Determine the number of singular values to keep. If the number of singular
173 ! values is greater than half, do not compress the buffer. First start by
174 ! setting any negative values to zero.
175  WHERE (w_svd .lt. 0.0)
176  w_svd = 0.0
177  END WHERE
178 
179 ! Find the minimum number of singular values to keep based on
180 ! 1 - n(r) = svd_cut_off where n(r) is equation (7) in del-Castillo-Negrete
181 ! et. al. doi:10.1016/j.jcp.2006.07.022
182  e_zero = dot_product(w_svd, w_svd)
183 
184  e_r = 0.0
185 ! We only need to check up to one more than half the number of singular values.
186 ! Anymore than that will use more data storage. Store the number of singular
187 ! values retained in the work_size.
188  DO i = 1, SIZE(w_svd)/2 + 1
189  e_r = e_r + w_svd(i)*w_svd(i)
190  work_size = i
191  IF ((1.0 - e_r/e_zero) .le. svd_cut_off) THEN
192  EXIT
193  END IF
194  END DO
195 
196 ! Check the compression percentage. The compression is defined as the
197 ! percentage of the orginal data size. The compression percentage is defined as
198 !
199 ! compressed_size/uncompressed_size*100.0 (1)
200 !
201 ! where
202 !
203 ! compressed_size = r + r*m + r*n (2)
204 !
205 ! and
206 !
207 ! uncompressed_size = n*m (3)
208 !
209 ! n and m are the dimensions of the orginal matrix and r is the number of
210 ! singular values retained.
211 !
212 ! However you can gain even more storage space. The compressed data is '
213 ! uncompressed reconstructing the matrix from U.W.V^T. By precomputing W.V^T
214 ! and storing that buffer instead instead of the W and V^T buffers separately,
215 ! the an array the size of the number of signular values is eliminiated. This
216 ! also has the added benifit of speed when uncompressing the data. The new
217 ! compressed size becomes
218 !
219 ! compressed_size = r*m + r*n (4)
220 !
221 ! Barrow the e_ variables to compute th compression
222 ! percentage. Use e_r for the compressed size and e_zero of the uncompressed
223 ! size.
224 
225  e_r = work_size*m + work_size*n
226  e_zero = n*m
227 
228  IF (100.0*e_r/e_zero .ge. compression_max_percentage) THEN
229 ! Use the uncompressed buffers.
230  compression_construct_new%data_buffer = data_in
231 
232  DEALLOCATE(compression_construct_new%u_buffer)
233  compression_construct_new%u_buffer => null()
234 
235  DEALLOCATE(compression_construct_new%wvt_buffer)
236  compression_construct_new%wvt_buffer => null()
237  ELSE
238 ! Use the compressed buffers. Barrow the data_buffer as temp buffer for
239 ! resizing the storage buffers.
240  DEALLOCATE(compression_construct_new%data_buffer)
241 
242 ! Store the U buffer. The reduced U buffer now only needs m x r storage space.
243  ALLOCATE(compression_construct_new%data_buffer(m,work_size))
244  compression_construct_new%data_buffer = &
245  & compression_construct_new%u_buffer(:,1:work_size)
246  DEALLOCATE(compression_construct_new%u_buffer)
247  ALLOCATE(compression_construct_new%u_buffer(m,work_size))
248  compression_construct_new%u_buffer = &
249  & compression_construct_new%data_buffer
250  DEALLOCATE(compression_construct_new%data_buffer)
251 
252 ! Store the VT buffer. The reduced VT buffer now only needs r x n storage
253 ! space.
254  ALLOCATE(compression_construct_new%data_buffer(work_size,n))
255  compression_construct_new%data_buffer = &
256  & compression_construct_new%wvt_buffer(1:work_size,:)
257  DEALLOCATE(compression_construct_new%wvt_buffer)
258  ALLOCATE(compression_construct_new%wvt_buffer(work_size,n))
259 
260 ! Multiply W.VT and store in the WVT buffer.
261  DO i = 1, n
262  compression_construct_new%wvt_buffer(:,i) = &
263  & w_svd(1:work_size) * &
264  & compression_construct_new%data_buffer(:,i)
265  END DO
266 
267  DEALLOCATE(compression_construct_new%data_buffer)
268  compression_construct_new%data_buffer => null()
269  END IF
270 
271  DEALLOCATE(w_svd)
272 
273  END FUNCTION
274 
275 !-------------------------------------------------------------------------------
285 !-------------------------------------------------------------------------------
286  FUNCTION compression_construct_netcdf(ncid, name)
287  USE ezcdf
288 
289  IMPLICIT NONE
290 
291 ! Declare Arguments
293  INTEGER, INTENT(in) :: ncid
294  CHARACTER (len=*), INTENT(in) :: name
295 
296 ! local variables
297  INTEGER, DIMENSION(2) :: dim_lengths
298  INTEGER :: error
299 
300 ! Start of executable code
302 
303  dim_lengths = -1
304 
305  CALL cdf_inquire(ncid, trim(name) // compression_data_buffer_post, &
306  & dim_lengths)
307  IF (all(dim_lengths .gt. 0)) THEN
308  ALLOCATE( &
309  & compression_construct_netcdf%data_buffer(dim_lengths(1), &
310  & dim_lengths(2)))
311  CALL cdf_read(ncid, &
312  & trim(name) // compression_data_buffer_post, &
313  & compression_construct_netcdf%data_buffer)
314  RETURN
315  END IF
316 
317  CALL cdf_inquire(ncid, trim(name) // compression_u_buffer_post, &
318  & dim_lengths)
319  IF (all(dim_lengths .gt. 0)) THEN
320  ALLOCATE( &
321  & compression_construct_netcdf%u_buffer(dim_lengths(1), &
322  & dim_lengths(2)))
323  CALL cdf_read(ncid, trim(name) // compression_u_buffer_post, &
324  & compression_construct_netcdf%u_buffer)
325 
326  CALL cdf_inquire(ncid, &
327  & trim(name) // compression_wvt_buffer_post, &
328  & dim_lengths)
329  ALLOCATE( &
330  & compression_construct_netcdf%wvt_buffer(dim_lengths(1), &
331  & dim_lengths(2)))
332  CALL cdf_read(ncid, trim(name) // compression_wvt_buffer_post, &
333  & compression_construct_netcdf%wvt_buffer)
334  RETURN
335  END IF
336 
337  CALL cdf_inquire(ncid, trim(name) // compression_data_dim_post, &
338  & dim_lengths(1:1))
339  ALLOCATE( compression_construct_netcdf%data_dim(dim_lengths(1)))
340  CALL cdf_read(ncid, trim(name) // compression_data_dim_post, &
341  & compression_construct_netcdf%data_dim)
342 
343  END FUNCTION
344 
345 !*******************************************************************************
346 ! DESTRUCTION SUBROUTINES
347 !*******************************************************************************
348 !-------------------------------------------------------------------------------
354 !-------------------------------------------------------------------------------
355  SUBROUTINE compression_destruct(this)
356 
357  IMPLICIT NONE
358 
359 ! Declare Arguments
360  TYPE (compression_class), POINTER :: this
361 
362 ! Start of executable code
363  IF (ASSOCIATED(this%data_buffer)) THEN
364  DEALLOCATE(this%data_buffer)
365  this%data_buffer => null()
366  END IF
367 
368  IF (ASSOCIATED(this%u_buffer)) THEN
369  DEALLOCATE(this%u_buffer)
370  this%u_buffer => null()
371  END IF
372 
373  IF (ASSOCIATED(this%wvt_buffer)) THEN
374  DEALLOCATE(this%wvt_buffer)
375  this%wvt_buffer => null()
376  END IF
377 
378  DEALLOCATE(this)
379 
380  END SUBROUTINE
381 
382 !*******************************************************************************
383 ! GETTER SUBROUTINES
384 !*******************************************************************************
385 !-------------------------------------------------------------------------------
393 !-------------------------------------------------------------------------------
394  FUNCTION compression_get_dimension1(this)
395 
396  IMPLICIT NONE
397 
398 ! Declare Aruments
399  INTEGER :: compression_get_dimension1
400  TYPE (compression_class), POINTER :: this
401 
402 ! Start of executable code
403  IF (ASSOCIATED(this%data_buffer)) THEN
404  compression_get_dimension1 = SIZE(this%data_buffer, 1)
405  ELSE IF (ASSOCIATED(this%u_buffer)) THEN
406  compression_get_dimension1 = SIZE(this%u_buffer, 1)
407  ELSE IF (ASSOCIATED(this%data_dim)) THEN
408  compression_get_dimension1 = this%data_dim(1)
409  ELSE
411  END IF
412 
413  END FUNCTION
414 
415 !-------------------------------------------------------------------------------
423 !-------------------------------------------------------------------------------
424  FUNCTION compression_get_dimension2(this)
425 
426  IMPLICIT NONE
427 
428 ! Declare Aruments
429  INTEGER :: compression_get_dimension2
430  TYPE (compression_class), POINTER :: this
431 
432 ! Start of executable code
433  IF (ASSOCIATED(this%data_buffer)) THEN
434  compression_get_dimension2 = SIZE(this%data_buffer, 2)
435  ELSE IF (ASSOCIATED(this%u_buffer)) THEN
436  compression_get_dimension2 = SIZE(this%wvt_buffer, 2)
437  ELSE IF (ASSOCIATED(this%data_dim)) THEN
438  compression_get_dimension2 = this%data_dim(2)
439  ELSE
441  END IF
442 
443  END FUNCTION
444 
445 !*******************************************************************************
446 ! UTILITY SUBROUTINES
447 !*******************************************************************************
448 !-------------------------------------------------------------------------------
457 !-------------------------------------------------------------------------------
458  SUBROUTINE compression_decompress(this)
459 
460  IMPLICIT NONE
461 
462 ! Declare Arguments
463  TYPE (compression_class), POINTER :: this
464 
465 ! Start of executable code
466  IF (ASSOCIATED(this%data_buffer)) THEN
467 ! Data is stored uncompressed. No need to do anything.
468  RETURN
469  END IF
470 
471  IF (ASSOCIATED(this%data_dim)) THEN
472 ! Data is all zeros. Allocate the data_buffer and set to zero.
473  ALLOCATE(this%data_buffer(this%data_dim(1),this%data_dim(2)))
474  this%data_buffer = 0.0
475  RETURN
476  END IF
477 
478 ! If control has reached this point, the data is compressed. Uncompress it by
479 ! U.WVT.
480  ALLOCATE(this%data_buffer(SIZE(this%u_buffer, 1), &
481  & SIZE(this%wvt_buffer, 2)))
482 
483  this%data_buffer = matmul(this%u_buffer, this%wvt_buffer)
484 
485  END SUBROUTINE
486 
487 !-------------------------------------------------------------------------------
494 !-------------------------------------------------------------------------------
495  SUBROUTINE compression_cleanup(this)
496 
497  IMPLICIT NONE
498 
499 ! Declare Arguments
500  TYPE (compression_class), POINTER :: this
501 
502 ! Start of executable code
503  IF (ASSOCIATED(this%data_dim) .or. ASSOCIATED(this%u_buffer)) THEN
504  DEALLOCATE(this%data_buffer)
505  this%data_buffer => null()
506  END IF
507 
508  END SUBROUTINE
509 
510 !*******************************************************************************
511 ! NETCDF SUBROUTINES
512 !*******************************************************************************
513 !-------------------------------------------------------------------------------
522 !-------------------------------------------------------------------------------
523  SUBROUTINE compression_define(this, ncid, name)
524  USE ezcdf
525 
526  IMPLICIT NONE
527 
528 ! Declare Arguments
529  TYPE (compression_class), INTENT(in) :: this
530  INTEGER, INTENT(in) :: ncid
531  CHARACTER (len=*), INTENT(in) :: name
532 
533 ! Start of executable code
534  IF (ASSOCIATED(this%data_buffer)) THEN
535  CALL cdf_define(ncid, &
536  & trim(name) // compression_data_buffer_post, &
537  & this%data_buffer)
538  ELSE IF (ASSOCIATED(this%u_buffer)) THEN
539  CALL cdf_define(ncid, trim(name) // compression_u_buffer_post, &
540  & this%u_buffer)
541  CALL cdf_define(ncid, &
542  & trim(name) // compression_wvt_buffer_post, &
543  & this%wvt_buffer)
544  ELSE
545  CALL cdf_define(ncid, trim(name) // compression_data_dim_post, &
546  & this%data_dim)
547  END IF
548 
549  END SUBROUTINE
550 
551 !-------------------------------------------------------------------------------
560 !-------------------------------------------------------------------------------
561  SUBROUTINE compression_write(this, ncid, name)
562  USE ezcdf
563 
564  IMPLICIT NONE
565 
566 ! Declare Arguments
567  TYPE (compression_class), INTENT(in) :: this
568  INTEGER, INTENT(in) :: ncid
569  CHARACTER (len=*), INTENT(in) :: name
570 
571 ! Start of executable code
572  IF (ASSOCIATED(this%data_buffer)) THEN
573 
574  CALL cdf_write(ncid, &
575  & trim(name) // compression_data_buffer_post, &
576  & this%data_buffer)
577 
578  ELSE IF (ASSOCIATED(this%u_buffer)) THEN
579 
580  CALL cdf_write(ncid, trim(name) // compression_u_buffer_post, &
581  & this%u_buffer)
582  CALL cdf_write(ncid, trim(name) // compression_wvt_buffer_post, &
583  & this%wvt_buffer)
584 
585  ELSE
586 
587  CALL cdf_write(ncid, trim(name) // compression_data_dim_post, &
588  & this%data_dim)
589 
590  END IF
591 
592  END SUBROUTINE
593 
594  END MODULE
compression::compression_construct_netcdf
type(compression_class) function, pointer compression_construct_netcdf(ncid, name)
Construct a compression_class object by reading from an netcdf. file.
Definition: compression.f:287
compression::compression_get_dimension2
integer function compression_get_dimension2(this)
Get the jth dimension length.
Definition: compression.f:425
compression::compression_cleanup
subroutine compression_cleanup(this)
Cleanup decompressed data.
Definition: compression.f:496
compression::compression_construct_new
type(compression_class) function, pointer compression_construct_new(data_in, svd_cut_off)
Construct a compression_class object.
Definition: compression.f:101
compression::compression_data_dim_post
character(len= *), parameter compression_data_dim_post
Postfix for the data buffer.
Definition: compression.f:36
v3_utilities::assert_eq
Definition: v3_utilities.f:62
compression::compression_write
subroutine compression_write(this, ncid, name)
Cleanup decompressed data.
Definition: compression.f:562
compression::compression_u_buffer_post
character(len= *), parameter compression_u_buffer_post
Postfix for the data buffer.
Definition: compression.f:30
mpi_inc
Umbrella module avoid multiple inlcudes of the mpif.h header.
Definition: mpi_inc.f:11
compression::compression_wvt_buffer_post
character(len= *), parameter compression_wvt_buffer_post
Postfix for the data buffer.
Definition: compression.f:33
compression::compression_define
subroutine compression_define(this, ncid, name)
Define variables for the compressed data.
Definition: compression.f:524
compression::compression_data_buffer_post
character(len= *), parameter compression_data_buffer_post
Postfix for the data buffer.
Definition: compression.f:27
compression::compression_decompress
subroutine compression_decompress(this)
Decompress the data.
Definition: compression.f:459
compression::compression_pointer
Pointer to a compression object. Used for creating arrays of compression pointers....
Definition: compression.f:66
compression::compression_max_percentage
real(rprec), parameter compression_max_percentage
Maximum compressed size before uncompressed buffers are stored.
Definition: compression.f:24
compression
Defines the base class of the type compression_class. This class contains the code and buffers to hol...
Definition: compression.f:14
compression::compression_get_dimension1
integer function compression_get_dimension1(this)
Get the ith dimension length.
Definition: compression.f:395
compression::compression_class
Base class containing buffers for compressed and uncompressed data.
Definition: compression.f:47
compression::compression_construct
Interface for the construction of compression_class types using compression_construct_new or compress...
Definition: compression.f:79
compression::compression_destruct
subroutine compression_destruct(this)
Deconstruct a compression_class object.
Definition: compression.f:356