forked from xcompact3d/x3d2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathallocator.f90
207 lines (182 loc) · 6.75 KB
/
allocator.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
module m_allocator
use iso_fortran_env, only: stderr => error_unit
use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, DIR_C, none, VERT
use m_mesh, only: mesh_t
use m_field, only: field_t
implicit none
type :: allocator_t
!! An instance of type allocator_t is responsible for the
!! maintenance of a linked list of instances of equal size
!! [[m_allocator(module):field_t(type)]] objects:
!!
!! ```
!! ---- ---- ---- ---- ---- ----
!! ...-->|id=1|data|next|-->|id=0|data|next|-->null()
!! ---- ---- ---- ---- ---- ----
!! ```
!!
!! the last block's `next` pointer being non associated.
!!
!! User code can request access to a memory block by using the
!! type bound procedure
!! [[m_allocator(module):get_block(function)]]. If the list is
!! not empty, a pointer to the first block on the list is
!! returned and the block is detached from the list. If the list
!! is empty (i.e. all initially allocated blocks are currently
!! referenced to) then a new block is allocated before a pointer
!! to it is returned.
!!
!! In order to reuse memory it is important that user code
!! release blocks when they are not needed anymore. This is done
!! by calling the type bound procedure
!! [[m_allocator(module):release_block(subroutine)]]. The
!! released block is then pushed in front of the block list.
integer :: ngrid
!> The id for the next allocated block. This counter is
!> incremented each time a new block is allocated.
integer :: next_id = 0
!> The pointer to the first block on the list. Non associated if
!> the list is empty
! TODO: Rename first to head
class(mesh_t), pointer :: mesh
class(field_t), pointer :: first => null()
contains
procedure :: get_block
procedure :: release_block
procedure :: create_block
procedure :: get_block_ids
procedure :: destroy
procedure :: compute_padded_dims
end type allocator_t
interface allocator_t
module procedure allocator_init
end interface allocator_t
type :: flist_t
class(field_t), pointer :: ptr
end type flist_t
contains
function allocator_init(mesh, sz) result(allocator)
type(mesh_t), target, intent(inout) :: mesh
integer, intent(in) :: sz
type(allocator_t) :: allocator
allocator%mesh => mesh
call allocator%compute_padded_dims(sz)
allocator%ngrid = product(allocator%mesh%get_padded_dims(DIR_C))
end function allocator_init
subroutine compute_padded_dims(self, sz)
class(allocator_t), intent(inout) :: self
integer, intent(in) :: sz
integer, dimension(3) :: cdims
integer :: nx, ny, nz, nx_padded, ny_padded, nz_padded
cdims = self%mesh%get_dims(VERT)
nx = cdims(1)
ny = cdims(2)
nz = cdims(3)
! Apply padding based on sz
nx_padded = nx - 1 + mod(-(nx - 1), sz) + sz
ny_padded = ny - 1 + mod(-(ny - 1), sz) + sz
! Current reorder functions do not require a padding in z-direction.
nz_padded = nz
cdims = [nx_padded, ny_padded, nz_padded]
call self%mesh%set_sz(sz)
call self%mesh%set_padded_dims(cdims)
end subroutine
function create_block(self, next) result(ptr)
!! Allocate memory for a new block and return a pointer to a new
!! [[m_allocator(module):field_t(type)]] object.
class(allocator_t), intent(inout) :: self
type(field_t), pointer, intent(in) :: next
type(field_t), pointer :: newblock
class(field_t), pointer :: ptr
self%next_id = self%next_id + 1
allocate (newblock)
newblock = field_t(self%ngrid, next, id=self%next_id)
ptr => newblock
end function create_block
function get_block(self, dir, data_loc) result(handle)
!! Return a pointer to the first available memory block, i.e. the
!! current head of the block list. If the list is empty, allocate
!! a new block with [[m_allocator(module):create_block(function)]]
!! first.
!!
!! Example
!! ```
!! f%data => get_block()
!! ```
class(allocator_t), intent(inout) :: self
class(field_t), pointer :: handle
integer, intent(in) :: dir
integer, intent(in), optional :: data_loc
integer :: dims(3)
! If the list is empty, allocate a new block before returning a
! pointer to it.
if (.not. associated(self%first)) then
! Construct a field_t. This effectively allocates
! storage space.
self%first => self%create_block(next=self%first)
end if
handle => self%first
self%first => self%first%next ! 2nd block becomes head block
handle%next => null() ! Detach ex-head block from the block list
! Store direction info in the field type.
handle%dir = dir
if (present(data_loc)) then
handle%data_loc = data_loc
else
handle%data_loc = none
end if
! Set dims based on direction
dims = self%mesh%get_padded_dims(dir)
! Apply bounds remapping based on requested direction
call handle%set_shape(dims)
end function get_block
subroutine release_block(self, handle)
!! Release memory block pointed to by HANDLE to the block list.
!! It is pushed to the front of the block list, in other words it
!! is made the head block.
class(allocator_t), intent(inout) :: self
class(field_t), pointer :: handle
handle%next => self%first
self%first => handle
end subroutine release_block
subroutine destroy(self)
!! Go through the block list from head to tail, deallocating each
!! memory block in turn. Deallocation of a
!! [[m_allocator(module):field_t(type)]] object automatically
!! deallocates its internal allocatable
!! [[field_t(type):data(variable)]] array.
class(allocator_t), intent(inout) :: self
type(field_t), pointer :: current
do
if (.not. associated(self%first)) exit
current => self%first
self%first => self%first%next
deallocate (current)
self%next_id = self%next_id - 1
end do
end subroutine destroy
function get_block_ids(self)
!! Utility function that returns a array made of the `id` of the
!! block currently in the block list. Return the array [0] if
!! block list is empty.
! TODO: Block indices should start at 1 or return [-1] in case of
! empty block list.
class(allocator_t), intent(inout) :: self
integer, allocatable :: get_block_ids(:)
class(field_t), pointer :: current
integer :: i
current => self%first
if (.not. associated(current)) then
get_block_ids = [0]
else
i = current%id
get_block_ids = [current%id]
do
if (.not. associated(current%next)) exit
i = current%next%id
get_block_ids = [get_block_ids, current%next%id]
current => current%next
end do
end if
end function get_block_ids
end module m_allocator