marray_compound.f90
Go to the documentation of this file.
1!#############################################################################
2!# #
3!# fosite - 3D hydrodynamical simulation program #
4!# module: marray_compound.f90 #
5!# #
6!# Copyright (C) 2018-2021 #
7!# Tobias Illenseer <tillense@astrophysik.uni-kiel.de> #
8!# #
9!# This program is free software; you can redistribute it and/or modify #
10!# it under the terms of the GNU General Public License as published by #
11!# the Free Software Foundation; either version 2 of the License, or (at #
12!# your option) any later version. #
13!# #
14!# This program is distributed in the hope that it will be useful, but #
15!# WITHOUT ANY WARRANTY; without even the implied warranty of #
16!# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or #
17!# NON INFRINGEMENT. See the GNU General Public License for more #
18!# details. #
19!# #
20!# You should have received a copy of the GNU General Public License #
21!# along with this program; if not, write to the Free Software #
22!# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #
23!# #
24!#############################################################################
25!----------------------------------------------------------------------------!
34!----------------------------------------------------------------------------!
37 IMPLICIT NONE
38 !--------------------------------------------------------------------------!
39 PRIVATE
41 TYPE(compound_item), POINTER :: next => null()
42 TYPE(marray_base), POINTER :: item => null()
43 INTEGER :: extent,entry_num
44 END TYPE
46 TYPE, EXTENDS(marray_base) :: marray_compound
47 PRIVATE
48 TYPE(compound_item), POINTER :: list => null()
49 INTEGER :: num_entries = 0
50 CONTAINS
51 PROCEDURE :: assignpointers
52 PROCEDURE :: assignmarray_0
53 PROCEDURE :: appendmarray
54 PROCEDURE :: lastitem
55 PROCEDURE :: firstitem
56 PROCEDURE :: nextitem
57 PROCEDURE :: getitem
58 PROCEDURE :: appenditem
59 PROCEDURE :: assignitempointers
60 PROCEDURE :: destroy
61 final :: finalize
62 END TYPE
63 INTERFACE marray_compound
64 MODULE PROCEDURE createmarray_compound
65 END INTERFACE
66 !--------------------------------------------------------------------------!
67 PUBLIC :: marray_compound
68
69CONTAINS
70
80 FUNCTION createmarray_compound(n) RESULT(new_cp)
81 IMPLICIT NONE
82 !-------------------------------------------------------------------!
83 TYPE(marray_compound) :: new_cp
84 INTEGER, OPTIONAL, INTENT(IN) :: n
85 !-------------------------------------------------------------------!
86#if DEBUG > 2
87 print *,"DEBUG INFO in marray_compound::CreateMArray_compound: creating new compound"
88#endif
89 IF (PRESENT(n)) THEN
90 ! create empty mesh array of rank 2 with first dimension set to n
91 IF (new_cp%Init(n,0)) return ! immediately return if successful
92 ELSE
93 ! default is rank 1 mesh array
94 IF (new_cp%Init(0)) return ! immediately return if successful
95 END IF
96#ifdef DEBUG
97 print *,"ERROR in marray_compound::CreateMArray: compound initialization failed"
98 stop 1
99#endif
100 END FUNCTION createmarray_compound
101
103 FUNCTION assignpointers(this) RESULT(success)
104 IMPLICIT NONE
105 !------------------------------------------------------------------------!
106 CLASS(marray_compound),INTENT(INOUT) :: this
107 LOGICAL :: success
108 !------------------------------------------------------------------------!
109#if DEBUG > 2
110 print *,"DEBUG INFO in marray_compound::AssignPointers: restoring compound pointers"
111#endif
112 ! set the multi-dim. pointers of the compound
113 success = this%marray_base%AssignPointers()
114 IF (success) success = this%AssignItemPointers()
115 END FUNCTION assignpointers
116
118 SUBROUTINE assignmarray_0(this,ma)
119 IMPLICIT NONE
120 !------------------------------------------------------------------------!
121 CLASS(marray_compound),INTENT(INOUT) :: this
122 CLASS(marray_base),INTENT(IN) :: ma
123 !------------------------------------------------------------------------!
124 TYPE(compound_item), POINTER :: p,q
125#if !defined(__GFORTRAN__) || (defined(__GFORTRAN__) && __GNUC__ >= 13)
126 TYPE(marray_base), POINTER :: new_ma
127 INTEGER :: err
128#endif
129 LOGICAL :: LHS_initialized
130 !------------------------------------------------------------------------!
131#if DEBUG > 2
132 print *,"DEBUG INFO in marray_compound::AssignMArray_0: compound assignment called"
133#endif
134 ! check if rhs is of compound class
135 SELECT TYPE(src => ma)
136 CLASS IS(marray_compound)
137 ! check if rhs is empty
138 p => src%FirstItem()
139 IF (.NOT.ASSOCIATED(p)) THEN
140#ifdef DEBUG
141#if DEBUG > 2
142 print *,"DEBUG INFO in marray_compound::AssignMArray_0: empty compound on rhs"
143#endif
144 IF (src%num_entries.GT.0.OR.SIZE(src%data1d).GT.0) THEN
145 print *, "ERROR in marray_compound::AssignMArray_0: unassigned item list on rhs but compound not empty"
146 stop 1
147 END IF
148#endif
149 this%num_entries = 0
150 NULLIFY(this%list)
151 ! do basic marray assignment
152 CALL this%marray_base%AssignMArray_0(src)
153 ELSE ! rhs is not empty
154 q => this%FirstItem()
155 ! check if lhs is an empty compound
156 lhs_initialized = ASSOCIATED(q)
157 IF (.NOT.lhs_initialized) THEN
158 ! p is associated but q isn't -> lhs of assignment should be an empty compound
159#ifdef DEBUG
160#if DEBUG > 2
161 print *,"DEBUG INFO in marray_compound::AssignMArray_0: empty compound on lhs"
162#endif
163 IF (this%num_entries.GT.0) THEN
164 print *, "ERROR in marray_compound::AssignMArray_0: empty compound on lhs expected"
165 stop 1
166 END IF
167#endif
168 ! ATTENTION: this part depends on the compiler, see comment in marray_base::AssignMArray_0
169 ! We basically have to distinguish between gfortran < 13 and other compilers (including gfortran >=13).
170 ! In case of older gfortran before version 13 there is no need to make a copy of the RHS if the LHS is
171 ! not initialized. Instead one simply assigns the pointers on the LHS to the source pointers on the RHS.
172#if !defined(__GFORTRAN__) || (defined(__GFORTRAN__) && __GNUC__ > 12)
173 ! all compilers except for gfortran < 13
174 ! make a copy of the list of items in the compound on the rhs
175 DO WHILE (ASSOCIATED(p%item))
176 ALLOCATE(new_ma,source=p%item,stat=err)
177 IF (err.NE.0) THEN
178#ifdef DEBUG
179 print *,"ERROR in marray_compound::AssignMArray_0: memory allocation failed for new_ma"
180 stop 1
181#else
182 return
183#endif
184 END IF
185 CALL this%AppendItem(new_ma)
186 p => p%next
187 IF (.NOT.ASSOCIATED(p)) EXIT
188 END DO
189#else
190 ! only gfortran < 13: assign lhs item list to rhs item list
191 this%num_entries = src%num_entries
192 this%list => src%list
193#endif
194 ! do basic marray assignment
195 CALL this%marray_base%AssignMArray_0(src)
196
197 ! assign all item pointers
198 IF (.NOT.this%AssignItemPointers()) THEN
199#ifdef DEBUG
200 print *,"ERROR in marray_compound::AssignMArray_0: assignment of item pointers failed"
201 stop 1
202#else
203 RETURN
204#endif
205 END IF
206 ELSE
207 ! p and q are both associated
208 IF (.NOT.(this.match.ma)) THEN
209#ifdef DEBUG
210 print *,"ERROR in marray_compound::AssignMArray_0: shape mismatch"
211 stop 1
212#else
213 RETURN
214#endif
215 ELSE
216 ! do basic marray assignment
217 CALL this%marray_base%AssignMArray_0(src)
218 END IF
219 END IF
220 END IF
221 CLASS DEFAULT
222#ifdef DEBUG
223 print *, "ERROR in marray_compound::AssignMArray_0: rhs must be of class marray_compound"
224 stop 1
225#else
226 RETURN
227#endif
228 END SELECT
229 END SUBROUTINE assignmarray_0
230
231
232 FUNCTION shapesmatch(this,ma) RESULT(res)
233 IMPLICIT NONE
234 !------------------------------------------------------------------------!
235 CLASS(marray_compound),INTENT(IN) :: this
236 CLASS(marray_base), INTENT(IN) :: ma
237 !------------------------------------------------------------------------!
238 TYPE(compound_item),POINTER :: p,q
239 LOGICAL :: res
240 !------------------------------------------------------------------------!
241 res = this%marray_base%ShapesMatch(ma)
242 IF (.NOT.res) THEN
243#if DEBUG > 1
244 print *,"WARNING in marray_compound::ShapesMatch: mismatch in marray_base"
245#endif
246 RETURN
247 END IF
248 SELECT TYPE(that => ma)
249 CLASS IS(marray_compound)
250 res = this%num_entries.EQ.that%num_entries
251 IF (.NOT.res) THEN
252#if DEBUG > 2
253 print *,"DEBUG INFO in marray_compound::ShapesMatch: number of entries do not match"
254#endif
255 RETURN
256 END IF
257 ! get pointers to the first items of the compound element lists
258 p => this%FirstItem()
259 q => that%FirstItem()
260 ! if both compound element lists are associated
261 DO WHILE (ASSOCIATED(p))
262 res = res.AND.ASSOCIATED(q)
263 IF (.NOT.res) RETURN ! p is associated, but q is not or extent mismatch
264 res = res.AND.(p%extent.EQ.q%extent)
265 p => p%next
266 q => q%next
267 END DO
268 ! if p isn't associated then q should not be associated as well
269 res = res.AND..NOT.ASSOCIATED(q)
270 CLASS DEFAULT
271 res = .false.
272 END SELECT
273 END FUNCTION shapesmatch
274
275 FUNCTION appendmarray(this,ma) RESULT(success)
276 IMPLICIT NONE
277 !-------------------------------------------------------------------!
278 CLASS(marray_compound) :: this
279 TYPE(marray_base), POINTER :: ma
280 LOGICAL :: success
281 !-------------------------------------------------------------------!
282 INTEGER :: m,n,i,err
283 REAL, DIMENSION(:),POINTER,CONTIGUOUS :: data1d
284 !-------------------------------------------------------------------!
285#if DEBUG > 2
286 print *,"DEBUG INFO in marray_compound::AppendMArray: appending marray to compound"
287#endif
288 success = .false.
289 ! some sanity checks
290 IF (.NOT.(ASSOCIATED(ma%data1d).OR.SIZE(ma%data1d).EQ.0)) THEN
291#ifdef DEBUG
292 print *,"ERROR in marray_compound::AppendMArray: input marray not associated of empty"
293#else
294 RETURN ! immediately return if data1d of input marray is not associated or empty
295#endif
296 END IF
297 IF (.NOT.ASSOCIATED(this%data1d)) THEN
298#ifdef DEBUG
299 print *,"ERROR in marray_compound::AppendMArray: compound uninitialized"
300#else
301 RETURN ! immediately return if data1d of compound is not associated
302#endif
303 END IF
304
305 ! check the rank of the compound (either 1 or 2)
306 SELECT CASE(this%RANK)
307 CASE(1) ! rank 1 compound
308 this%DIMS(1) = this%DIMS(1) + ma%DIMS(1)*ma%DIMS(2) ! 1st dimension (excluding mesh
309 ! dimensions) is collapsed into one index; if ma is a scalar, then this is 1;
310 ! if ma is rank 1, then this is the vector component index;
311 ! if ma is rank 2, the two indices are collapsed into one index
312 CASE(2) ! rank 2 compound
313 ! check if the 1st dimension (excluding mesh dimensions) are the same for
314 ! both compound and mesh array added to the compound
315 IF (this%DIMS(1).NE.ma%DIMS(1)) THEN
316#ifdef DEBUG
317 print *,"ERROR in marray_compound::AppendMArray: this%dims(1) != ma%dims(1)"
318#endif
319 RETURN
320 ELSE
321 ! this%DIMS(1) remains the same
322 this%DIMS(2) = this%DIMS(2) + ma%DIMS(2) ! extend the last dimension of the compound;
323 ! so far only scalars and vectors can be added to rank 2 compounds
324 END IF
325 CASE DEFAULT
326 ! this should not happen
327#ifdef DEBUG
328 print *,"ERROR in marray_compound::AppendMArray: rank of compound marrays should be either 1 or 2"
329#endif
330 RETURN
331 END SELECT
332
333 ! determine size of compound data1d array
334 m = SIZE(this%data1d(:))
335 IF (m.EQ.0) THEN
336 ! initialized but empty compound
337 DEALLOCATE(this%data1d)
338 ! append ma to an empty compound => just assign the data pointer ...
339 this%data1d(1:) => ma%data1d(1:)
340 ! ... and append ma to the list of compound items
341 CALL this%AppendItem(ma)
342 ELSE ! m > 0 -> compound has at least one component
343 ! determine size of new component
344 n = SIZE(ma%data1d(:))
345 ! allocate new contiguous data block for old+new data and for meta data
346 ALLOCATE(data1d(m+n),stat=err)
347 IF (err.NE.0) THEN
348#ifdef DEBUG
349 print *,"ERROR in marray_compound::AppendMArray: memory allocation failed"
350#else
351 RETURN ! immediatly return on failure
352#endif
353 END IF
354 ! copy the data, new data is appended to the old compound
355 DO concurrent(i=1:m)
356 data1d(i) = this%data1d(i)
357 END DO
358 DO concurrent(i=1:n)
359 data1d(m+i) = ma%data1d(i)
360 END DO
361 ! append ma to the list of items
362 CALL this%AppendItem(ma)
363 ! free old data
364 DEALLOCATE(ma%data1d,this%data1d)
365 ! set 1D compound pointer to the new data
366 this%data1d(1:) => data1d(1:)
367 END IF
368 ! go through the whole compound and restore all 1D and multi-dim. pointers
369 IF (.NOT.this%AssignPointers()) THEN
370#ifdef DEBUG
371 print *,"ERROR in marray_compound::AppendMArray: pointer assignment failed"
372#else
373 RETURN ! immediatly return on failure
374#endif
375 END IF
376#if DEBUG > 2
377 print *,"DEBUG INFO in marray_compound::AppendMArray"
378 print '(3(A,I2),I2)'," item no. ",this%num_entries, " appended to rank ",this%RANK, &
379 " compound with dims ",this%DIMS(1:2)
380 print '(A,I10,A,I2,A,2(I3))', " size ", SIZE(this%data1d), &
381 " item%rank", ma%RANK, &
382 " item%dims", ma%DIMS(:)
383 SELECT CASE(this%RANK)
384 CASE(1)
385 print '(A,4(I6))', " shape4d", shape(this%data4d)
386 CASE(2)
387 print '(A,5(I6))', " shape5d", shape(this%data5d)
388 END SELECT
389#endif
390 success = .true.
391 END FUNCTION appendmarray
392
394 FUNCTION firstitem(this) RESULT(item)
395 IMPLICIT NONE
396 !-------------------------------------------------------------------!
397 CLASS(marray_compound), INTENT(IN) :: this
398 TYPE(compound_item), POINTER :: item
399 !-------------------------------------------------------------------!
400 item => this%list
401 END FUNCTION firstitem
402
404 FUNCTION lastitem(this) RESULT(item)
405 IMPLICIT NONE
406 !-------------------------------------------------------------------!
407 CLASS(marray_compound), INTENT(IN) :: this
408 TYPE(compound_item), POINTER :: item
409 !-------------------------------------------------------------------!
410 item => this%list
411 IF (.NOT.ASSOCIATED(item)) RETURN
412 DO WHILE (ASSOCIATED(item%next))
413 item => item%next
414 END DO
415 END FUNCTION lastitem
416
418 FUNCTION nextitem(this,item) RESULT(next)
419 IMPLICIT NONE
420 !-------------------------------------------------------------------!
421 CLASS(marray_compound), INTENT(IN) :: this
422 TYPE(compound_item), POINTER :: item,next
423 !-------------------------------------------------------------------!
424 IF (ASSOCIATED(item)) THEN
425 next => item%next
426 ELSE
427 next => null()
428 END IF
429 END FUNCTION nextitem
430
432 FUNCTION getitem(this,list_entry) RESULT(ma)
433 IMPLICIT NONE
434 !-------------------------------------------------------------------!
435 CLASS(marray_compound) :: this
436 TYPE(compound_item), POINTER :: list_entry
437 TYPE(marray_base), POINTER :: ma
438 !-------------------------------------------------------------------!
439 ma => list_entry%item
440 END FUNCTION getitem
441
443 SUBROUTINE appenditem(this,ma)
444 IMPLICIT NONE
445 !-------------------------------------------------------------------!
446 CLASS(marray_compound) :: this
447 TYPE(marray_base), POINTER :: ma
448 !-------------------------------------------------------------------!
449 TYPE(compound_item), POINTER :: p
450 INTEGER :: err
451 !-------------------------------------------------------------------!
452 IF (ASSOCIATED(ma).AND.ASSOCIATED(ma%data1d).AND.SIZE(ma%data1d).GT.0) THEN
453 p => this%LastItem()
454 IF (.NOT.ASSOCIATED(p)) THEN
455 ! list is empty => create it
456#if DEBUG > 2
457 print *,"DEBUG INFO in marray_compound::AppendItem: creating new list"
458#endif
459 ALLOCATE(this%list,stat=err)
460 IF (err.NE.0) THEN
461#ifdef DEBUG
462 print *,"ERROR in marray_compound::AppendItem: memory allocation failed for new list"
463#endif
464 return
465 END IF
466 p => this%list
467 this%num_entries = 1 ! first entry in the list
468 ELSE
469 ! list exists => create a new entry at the end
470#if DEBUG > 2
471 print *,"DEBUG INFO in marray_compound::AppendItem: appending item to list of elements"
472#endif
473 ALLOCATE(p%next,stat=err)
474 IF (err.NE.0) THEN
475#ifdef DEBUG
476 print *,"ERROR in marray_compound::AppendItem: memory allocation failed for new entry"
477#endif
478 return
479 END IF
480 p => p%next
481 this%num_entries = this%num_entries + 1 ! one entry added
482 END IF
483 ! assign item pointer and store size of 1D data
484 p%item => ma
485 p%extent = SIZE(ma%data1d)
486 p%entry_num = this%num_entries
487 NULLIFY(p%next)
488 END IF
489 END SUBROUTINE appenditem
490
491 FUNCTION assignitempointers(this) RESULT(success)
492 IMPLICIT NONE
493 !-------------------------------------------------------------------!
494 CLASS(marray_compound) :: this
495 LOGICAL :: success
496 !-------------------------------------------------------------------!
497 TYPE(compound_item), POINTER :: p
498 INTEGER :: m,n
499 !------------------------------------------------------------------------!
500 ! go through the list of items and set the 1D
501 ! and multi-dim. pointers for each item
502 p => this%FirstItem()
503 m = 0
504 n = 0
505 success = .true.
506 DO WHILE (success.AND.ASSOCIATED(p))
507#if DEBUG > 2
508 print '(A,I2)'," DEBUG INFO in marray_compound::AssignItemPointers: restoring entry no. ", p%entry_num
509#endif
510 m = m + n
511 n = p%extent
512 ! point to the 1D data segment in the new array
513 p%item%data1d(1:n) => this%data1d(m+1:m+n)
514 ! restore all multi-dim. pointers
515 success = p%item%AssignPointers()
516 p => this%NextItem(p)
517 END DO
518 END FUNCTION assignitempointers
519
522 SUBROUTINE destroy(this,called_from_finalize)
523 IMPLICIT NONE
524 !-------------------------------------------------------------------!
525 CLASS(marray_compound) :: this
526 LOGICAL, OPTIONAL :: called_from_finalize
527 !-------------------------------------------------------------------!
528 TYPE(compound_item), POINTER :: p,q
529 !-------------------------------------------------------------------!
530#if DEBUG > 2
531 print *,"DEBUG INFO in marray_compound::Destroy: deallocating compound components"
532#endif
533 ! free list memory
534 p => this%FirstItem()
535 DO WHILE (ASSOCIATED(p))
536#if DEBUG > 2
537 print '(A,I2)'," DEBUG INFO in marray_compound::Destroy: deleting entry no. ", p%entry_num
538#endif
539 q => p%next
540 IF (ASSOCIATED(p%item)) THEN
541#if DEBUG > 2
542 print '(A,I2)'," DEBUG INFO in marray_compound::Destroy: deallocating item data"
543#endif
544 ! skip deallocation of the compound element data when invoking the finalizer of p%item
545 IF (ASSOCIATED(p%item%data1d)) NULLIFY(p%item%data1d)
546 DEALLOCATE(p%item)
547 END IF
548 DEALLOCATE(p)
549 p => q
550 END DO
551 this%num_entries = 0
552 NULLIFY(this%list)
553 ! only call inherited destructor if not called from Finalize
554 IF (PRESENT(called_from_finalize)) THEN
555 IF (called_from_finalize) RETURN
556 END IF
557 CALL this%marray_base%Destroy()
558 END SUBROUTINE destroy
559
561 SUBROUTINE finalize(this)
562 IMPLICIT NONE
563 !-------------------------------------------------------------------!
564 TYPE(marray_compound), INTENT(INOUT) :: this
565 !-------------------------------------------------------------------!
566#if DEBUG > 2
567 print *,"DEBUG INFO in marray_compound::Finalize: automatic finalizer called"
568#endif
569 CALL this%Destroy(.true.)
570 END SUBROUTINE finalize
571
572END MODULE marray_compound_mod
base class for mesh arrays
Definition: marray_base.f90:36
logical function assignpointers(this)
assign pointers of different shapes to the 1D data
subroutine finalize(this)
pure logical function shapesmatch(this, that)
subroutine assignmarray_0(this, ma)
assigns one mesh array to another mesh array
subroutine destroy(this, called_from_finalize)
basic destructor of mesh arrays - this is called automatically if deallocate is invoked
derived class for compound of mesh arrays
type(compound_item) function, pointer firstitem(this)
get the first item from the list of compound elements
type(compound_item) function, pointer lastitem(this)
get the last item from the list of compound elements
logical function appendmarray(this, ma)
type(compound_item) function, pointer nextitem(this, item)
get the next item from the list of compound elements
type(marray_base) function, pointer getitem(this, list_entry)
get pointer to the mesh array from a given item
logical function assignitempointers(this)
type(marray_compound) function createmarray_compound(n)
constructor for compound of mesh arrays
subroutine appenditem(this, ma)
append an item to the list of compound elements
basic mesh array class
Definition: marray_base.f90:69