Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
matrix.f90
Go to the documentation of this file.
1! Copyright (c) 2024, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
34module matrix
36 use num_types, only : rp, xp
41 use, intrinsic :: iso_c_binding
42 implicit none
43 private
44
45 type, public :: matrix_t
46 real(kind=rp), allocatable :: x(:,:)
47 character(len=NEKO_VARNAME_LEN) :: name = ""
48 type(c_ptr) :: x_d = c_null_ptr
49 integer, private :: nrows = 0
50 integer, private :: ncols = 0
51 integer, private :: n = 0
52 contains
54 procedure, pass(m) :: init => matrix_init
56 procedure, pass(m) :: free => matrix_free
58 procedure, pass(m) :: size => matrix_size
60 procedure, pass(m) :: copy_from => matrix_copy_from
62 procedure, pass(m) :: get_nrows => matrix_nrows
64 procedure, pass(m) :: get_ncols => matrix_ncols
66 procedure, pass(m) :: get_dims => matrix_dims
68 procedure, pass(m) :: matrix_assign_matrix
70 procedure, pass(m) :: matrix_assign_scalar
72 procedure, pass(m) :: inverse => matrix_bcknd_inverse
73 procedure, pass(m) :: inverse_on_host => cpu_matrix_inverse
74
75 generic :: assignment(=) => matrix_assign_matrix, &
77
79 procedure, pass(m), private :: alloc => matrix_allocate
80 end type matrix_t
81
82 type, public :: matrix_ptr_t
83 type(matrix_t), pointer :: ptr
84 end type matrix_ptr_t
85
86contains
87
91 subroutine matrix_init(m, nrows, ncols, name)
92 class(matrix_t), intent(inout) :: m
93 integer, intent(in) :: nrows
94 integer, intent(in) :: ncols
95 character(len=*), intent(in), optional :: name
96
97 ! m%alloc zeroes the device side (and synchronizes) before any
98 ! host-side touch: under zero-copy the device then faults the
99 ! pages (device first touch), which gives contiguous physical
100 ! mappings and thus better GPU TLB utilisation; rewriting the
101 ! zeros on the host afterwards is benign
102 call m%alloc(nrows, ncols)
103 m%x = 0.0_rp
104
105 if (present(name)) then
106 m%name = name
107 end if
108
109
110 end subroutine matrix_init
111
115 subroutine matrix_allocate(m, nrows, ncols)
116 class(matrix_t), intent(inout) :: m
117 integer, intent(in) :: nrows
118 integer, intent(in) :: ncols
119
120 call m%free()
121
122 allocate(m%x(nrows, ncols))
123 m%nrows = nrows
124 m%ncols = ncols
125 m%n = nrows*ncols
126
127 if (neko_bcknd_device .eq. 1) then
128 call device_map(m%x, m%x_d, m%n)
129 call device_cfill(m%x_d, 0.0_rp, m%n)
130 call device_sync()
131 end if
132
133 end subroutine matrix_allocate
134
136 subroutine matrix_free(m)
137 class(matrix_t), intent(inout) :: m
138
139 if (allocated(m%x)) then
140 if (neko_bcknd_device .eq. 1) then
141 call device_unmap(m%x, m%x_d)
142 end if
143 deallocate(m%x)
144 end if
145
146 m%nrows = 0
147 m%ncols = 0
148 m%n = 0
149 m%name = ""
150
151 end subroutine matrix_free
152
154 pure function matrix_size(m) result(s)
155 class(matrix_t), intent(in) :: m
156 integer :: s
157 s = m%n
158 end function matrix_size
159
160
165 subroutine matrix_copy_from(m, memdir, sync)
166 class(matrix_t), intent(inout) :: m
167 integer, intent(in) :: memdir
168 logical, intent(in) :: sync
169
170 if (neko_bcknd_device .eq. 1) then
171 call device_memcpy(m%x, m%x_d, m%n, memdir, sync)
172 end if
173
174 end subroutine matrix_copy_from
175
177 pure function matrix_nrows(m) result(nr)
178 class(matrix_t), intent(in) :: m
179 integer :: nr
180 nr = m%nrows
181 end function matrix_nrows
182
184 pure function matrix_ncols(m) result(nc)
185 class(matrix_t), intent(in) :: m
186 integer :: nc
187 nc = m%ncols
188 end function matrix_ncols
189
191 pure function matrix_dims(m) result(dims)
192 class(matrix_t), intent(in) :: m
193 integer :: dims(2)
194 dims = [m%nrows, m%ncols]
195 end function matrix_dims
196
198 subroutine matrix_assign_matrix(m, w)
199 class(matrix_t), intent(inout) :: m
200 type(matrix_t), intent(in) :: w
201
202 if (allocated(m%x)) then
203 call m%free()
204 end if
205
206 if (.not. allocated(m%x)) then
207
208 m%nrows = w%nrows
209 m%ncols = w%ncols
210 m%n = w%n
211 allocate(m%x(m%nrows, m%ncols))
212
213 if (neko_bcknd_device .eq. 1) then
214 call device_map(m%x, m%x_d, m%n)
215 end if
216
217 end if
218
219 if (neko_bcknd_device .eq. 1) then
220 call device_copy(m%x_d, w%x_d, m%n)
221 else
222 m%x = w%x
223 end if
224
225 m%name = w%name
226
227 end subroutine matrix_assign_matrix
228
230 subroutine matrix_assign_scalar(m, s)
231 class(matrix_t), intent(inout) :: m
232 real(kind=rp), intent(in) :: s
233
234 if (.not. allocated(m%x)) then
235 call neko_error('matrix not allocated')
236 end if
237
238 if (neko_bcknd_device .eq. 1) then
239 call device_cfill(m%x_d, s, m%n)
240 else
241 m%x = s
242 end if
243
244 end subroutine matrix_assign_scalar
245
246 subroutine matrix_bcknd_inverse(m, bcknd)
247 class(matrix_t), intent(inout) :: m
248 integer, optional :: bcknd
249
250 if (neko_bcknd_device .eq. 1 .and. &
251 bcknd .eq. neko_bcknd_device) then
252 call neko_error("matrix_bcknd_inverse not &
253 &implemented on accelarators.")
254 else
255 call cpu_matrix_inverse(m)
256 end if
257
258 end subroutine matrix_bcknd_inverse
259
260 subroutine cpu_matrix_inverse(m)
261 ! Gauss-Jordan matrix inversion with full pivoting
262 ! Num. Rec. p. 30, 2nd Ed., Fortran
263 ! m%x is an sqaure matrix
264 ! rmult is m work array of length nrows = ncols
265 class(matrix_t), intent(inout) :: m
266 integer :: indr(m%nrows), indc(m%ncols), ipiv(m%ncols)
267 real(kind=xp) :: rmult(m%nrows), amx, tmp, piv, eps
268 integer :: i, j, k, ir, jc
269
270 if (.not. (m%ncols .eq. m%nrows)) then
271 call neko_error("Fatal error: trying to invert m matrix that is not &
272 &square")
273 end if
274
275 eps = 1e-9_rp
276 ipiv = 0
277
278 do k = 1, m%nrows
279 amx = 0.0_rp
280 do i = 1, m%nrows ! Pivot search
281 if (ipiv(i) .ne. 1) then
282 do j = 1, m%nrows
283 if (ipiv(j) .eq. 0) then
284 if (abs(m%x(i, j)) .ge. amx) then
285 amx = abs(m%x(i, j))
286 ir = i
287 jc = j
288 end if
289 else if (ipiv(j) .gt. 1) then
290 return
291 end if
292 end do
293 end if
294 end do
295 ipiv(jc) = ipiv(jc) + 1
296
297 ! Swap rows
298 if (ir .ne. jc) then
299 do j = 1, m%ncols
300 tmp = m%x(ir, j)
301 m%x(ir, j) = m%x(jc, j)
302 m%x(jc, j) = tmp
303 end do
304 end if
305 indr(k) = ir
306 indc(k) = jc
307
308 if (abs(m%x(jc, jc)) .lt. eps) then
309 call neko_error("matrix_inverse error: small Gauss Jordan Piv")
310 end if
311 piv = 1.0_xp/m%x(jc, jc)
312 m%x(jc, jc) = 1.0_xp
313 do j = 1, m%ncols
314 m%x(jc, j) = m%x(jc, j)*piv
315 end do
316
317 do j = 1, m%ncols
318 tmp = m%x(jc, j)
319 m%x(jc, j) = m%x(1 , j)
320 m%x(1 , j) = tmp
321 end do
322 do i = 2, m%nrows
323 rmult(i) = m%x(i, jc)
324 m%x(i, jc) = 0.0_rp
325 end do
326
327 do j = 1, m%ncols
328 do i = 2, m%nrows
329 m%x(i, j) = m%x(i, j) - rmult(i)*m%x(1, j)
330 end do
331 end do
332
333 do j = 1, m%ncols
334 tmp = m%x(jc, j)
335 m%x(jc, j) = m%x(1 , j)
336 m%x(1 , j) = tmp
337 end do
338 end do
339
340 ! Unscramble matrix
341 do j = m%nrows, 1, -1
342 if (indr(j) .ne. indc(j)) then
343 do i = 1, m%nrows
344 tmp = m%x(i, indr(j))
345 m%x(i, indr(j)) = m%x(i, indc(j))
346 m%x(i, indc(j)) = tmp
347 end do
348 end if
349 end do
350
351 return
352 end subroutine cpu_matrix_inverse
353
354end module matrix
Map a Fortran array to a device (allocate and associate)
Definition device.F90:83
Copy data between host and device (or device and device)
Definition device.F90:72
Synchronize a device or stream.
Definition device.F90:119
Unmap a Fortran array from a device (deassociate and free)
Definition device.F90:89
subroutine, public device_copy(a_d, b_d, n, strm)
Copy a vector .
subroutine, public device_cfill(a_d, c, n, strm)
Set all elements to a constant c .
Device abstraction, common interface for various accelerators.
Definition device.F90:34
Defines a matrix.
Definition matrix.f90:34
subroutine matrix_free(m)
Deallocate a matrix.
Definition matrix.f90:137
subroutine matrix_bcknd_inverse(m, bcknd)
Definition matrix.f90:247
pure integer function matrix_ncols(m)
Returns the number of columns in the matrix.
Definition matrix.f90:185
subroutine matrix_assign_matrix(m, w)
Assignment .
Definition matrix.f90:199
pure integer function, dimension(2) matrix_dims(m)
Returns the dimensions of the matrix.
Definition matrix.f90:192
subroutine cpu_matrix_inverse(m)
Definition matrix.f90:261
pure integer function matrix_nrows(m)
Returns the number of rows in the matrix.
Definition matrix.f90:178
subroutine matrix_init(m, nrows, ncols, name)
Initialise a matrix of size nrows*ncols.
Definition matrix.f90:92
pure integer function matrix_size(m)
Returns the number of entries in the matrix.
Definition matrix.f90:155
subroutine matrix_copy_from(m, memdir, sync)
Easy way to copy between host and device.
Definition matrix.f90:166
subroutine matrix_assign_scalar(m, s)
Assignment .
Definition matrix.f90:231
subroutine matrix_allocate(m, nrows, ncols)
Allocate a matrix of size nrows*ncols.
Definition matrix.f90:116
Build configurations.
integer, parameter neko_bcknd_device
integer, parameter, public xp
Definition num_types.f90:16
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Utilities.
Definition utils.f90:35
integer, parameter, public neko_varname_len
Definition utils.f90:43