1# Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 2# the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 3# reserved. See files LICENSE and NOTICE for details. 4# 5# This file is part of CEED, a collection of benchmarks, miniapps, software 6# libraries and APIs for efficient high-order finite element and spectral 7# element discretizations for exascale applications. For more information and 8# source code availability see http://github.com/ceed. 9# 10# The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 11# a collaborative effort of two U.S. Department of Energy organizations (Office 12# of Science and the National Nuclear Security Administration) responsible for 13# the planning and preparation of a capable exascale ecosystem, including 14# software, applications, hardware, advanced system engineering and early 15# testbed platforms, in support of the nation's exascale computing imperative. 16 17from _ceed_cffi import ffi, lib 18import tempfile 19import numpy as np 20from abc import ABC 21from .ceed_constants import TRANSPOSE, NOTRANSPOSE 22 23# ------------------------------------------------------------------------------ 24 25 26class Basis(ABC): 27 """Ceed Basis: finite element basis objects.""" 28 29 # Attributes 30 _ceed = ffi.NULL 31 _pointer = ffi.NULL 32 33 # Representation 34 def __repr__(self): 35 return "<CeedBasis instance at " + hex(id(self)) + ">" 36 37 # String conversion for print() to stdout 38 def __str__(self): 39 """View a Basis via print().""" 40 41 # libCEED call 42 with tempfile.NamedTemporaryFile() as key_file: 43 with open(key_file.name, 'r+') as stream_file: 44 stream = ffi.cast("FILE *", stream_file) 45 46 err_code = lib.CeedBasisView(self._pointer[0], stream) 47 self._ceed._check_error(err_code) 48 49 stream_file.seek(0) 50 out_string = stream_file.read() 51 52 return out_string 53 54 # Apply Basis 55 def apply(self, nelem, emode, u, v, tmode=NOTRANSPOSE): 56 """Apply basis evaluation from nodes to quadrature points or vice versa. 57 58 Args: 59 nelem: the number of elements to apply the basis evaluation to; 60 the backend will specify the ordering in a 61 BlockedElemRestriction 62 emode: basis evaluation mode 63 u: input vector 64 v: output vector 65 **tmode: CEED_NOTRANSPOSE to evaluate from nodes to quadrature 66 points, CEED_TRANSPOSE to apply the transpose, mapping 67 from quadrature points to nodes; default CEED_NOTRANSPOSE""" 68 69 # libCEED call 70 err_code = lib.CeedBasisApply(self._pointer[0], nelem, tmode, emode, 71 u._pointer[0], v._pointer[0]) 72 self._ceed._check_error(err_code) 73 74 # Transpose a Basis 75 @property 76 def T(self): 77 """Transpose a Basis.""" 78 79 return TransposeBasis(self) 80 81 # Transpose a Basis 82 @property 83 def transpose(self): 84 """Transpose a Basis.""" 85 86 return TransposeBasis(self) 87 88 # Get number of nodes 89 def get_num_nodes(self): 90 """Get total number of nodes (in dim dimensions) of a Basis. 91 92 Returns: 93 num_nodes: total number of nodes""" 94 95 # Setup argument 96 p_pointer = ffi.new("CeedInt *") 97 98 # libCEED call 99 err_code = lib.CeedBasisGetNumNodes(self._pointer[0], p_pointer) 100 self._ceed._check_error(err_code) 101 102 return p_pointer[0] 103 104 # Get number of quadrature points 105 def get_num_quadrature_points(self): 106 """Get total number of quadrature points (in dim dimensions) of a Basis. 107 108 Returns: 109 num_qpts: total number of quadrature points""" 110 111 # Setup argument 112 q_pointer = ffi.new("CeedInt *") 113 114 # libCEED call 115 err_code = lib.CeedBasisGetNumQuadraturePoints( 116 self._pointer[0], q_pointer) 117 self._ceed._check_error(err_code) 118 119 return q_pointer[0] 120 121 # Gauss quadrature 122 @staticmethod 123 def gauss_quadrature(q): 124 """Construct a Gauss-Legendre quadrature. 125 126 Args: 127 Q: number of quadrature points (integrates polynomials of 128 degree 2*Q-1 exactly) 129 130 Returns: 131 (qref1d, qweight1d): array of length Q to hold the abscissa on [-1, 1] 132 and array of length Q to hold the weights""" 133 134 # Setup arguments 135 qref1d = np.empty(q, dtype="float64") 136 qweight1d = np.empty(q, dtype="float64") 137 138 qref1d_pointer = ffi.new("CeedScalar *") 139 qref1d_pointer = ffi.cast( 140 "CeedScalar *", 141 qref1d.__array_interface__['data'][0]) 142 143 qweight1d_pointer = ffi.new("CeedScalar *") 144 qweight1d_pointer = ffi.cast( 145 "CeedScalar *", 146 qweight1d.__array_interface__['data'][0]) 147 148 # libCEED call 149 err_code = lib.CeedGaussQuadrature(q, qref1d_pointer, qweight1d_pointer) 150 self._ceed._check_error(err_code) 151 152 return qref1d, qweight1d 153 154 # Lobatto quadrature 155 @staticmethod 156 def lobatto_quadrature(q): 157 """Construct a Gauss-Legendre-Lobatto quadrature. 158 159 Args: 160 q: number of quadrature points (integrates polynomials of 161 degree 2*Q-3 exactly) 162 163 Returns: 164 (qref1d, qweight1d): array of length Q to hold the abscissa on [-1, 1] 165 and array of length Q to hold the weights""" 166 167 # Setup arguments 168 qref1d = np.empty(q, dtype="float64") 169 qref1d_pointer = ffi.new("CeedScalar *") 170 qref1d_pointer = ffi.cast( 171 "CeedScalar *", 172 qref1d.__array_interface__['data'][0]) 173 174 qweight1d = np.empty(q, dtype="float64") 175 qweight1d_pointer = ffi.new("CeedScalar *") 176 qweight1d_pointer = ffi.cast( 177 "CeedScalar *", 178 qweight1d.__array_interface__['data'][0]) 179 180 # libCEED call 181 err_code = lib.CeedLobattoQuadrature( 182 q, qref1d_pointer, qweight1d_pointer) 183 self._ceed._check_error(err_code) 184 185 return qref1d, qweight1d 186 187 # QR factorization 188 @staticmethod 189 def qr_factorization(ceed, mat, tau, m, n): 190 """Return QR Factorization of a matrix. 191 192 Args: 193 ceed: Ceed context currently in use 194 *mat: Numpy array holding the row-major matrix to be factorized in place 195 *tau: Numpy array to hold the vector of lengt m of scaling factors 196 m: number of rows 197 n: numbef of columns""" 198 199 # Setup arguments 200 mat_pointer = ffi.new("CeedScalar *") 201 mat_pointer = ffi.cast( 202 "CeedScalar *", 203 mat.__array_interface__['data'][0]) 204 205 tau_pointer = ffi.new("CeedScalar *") 206 tau_pointer = ffi.cast( 207 "CeedScalar *", 208 tau.__array_interface__['data'][0]) 209 210 # libCEED call 211 lib.CeedQRFactorization( 212 ceed._pointer[0], mat_pointer, tau_pointer, m, n) 213 214 return mat, tau 215 216 # Symmetric Schur decomposition 217 @staticmethod 218 def symmetric_schur_decomposition(ceed, mat, n): 219 """Return symmetric Schur decomposition of a symmetric matrix 220 via symmetric QR factorization. 221 222 Args: 223 ceed: Ceed context currently in use 224 *mat: Numpy array holding the row-major matrix to be factorized in place 225 n: number of rows/columns 226 227 Returns: 228 lbda: Numpy array of length n holding eigenvalues""" 229 230 # Setup arguments 231 mat_pointer = ffi.new("CeedScalar *") 232 mat_pointer = ffi.cast( 233 "CeedScalar *", 234 mat.__array_interface__['data'][0]) 235 236 lbda = np.empty(n, dtype="float64") 237 l_pointer = ffi.new("CeedScalar *") 238 l_pointer = ffi.cast( 239 "CeedScalar *", 240 lbda.__array_interface__['data'][0]) 241 242 # libCEED call 243 lib.CeedSymmetricSchurDecomposition( 244 ceed._pointer[0], mat_pointer, l_pointer, n) 245 246 return lbda 247 248 # Simultaneous Diagonalization 249 @staticmethod 250 def simultaneous_diagonalization(ceed, matA, matB, n): 251 """Return Simultaneous Diagonalization of two matrices. 252 253 Args: 254 ceed: Ceed context currently in use 255 *matA: Numpy array holding the row-major matrix to be factorized with 256 eigenvalues 257 *matB: Numpy array holding the row-major matrix to be factorized to identity 258 n: number of rows/columns 259 260 Returns: 261 (x, lbda): Numpy array holding the row-major orthogonal matrix and 262 Numpy array holding the vector of length n of generalized 263 eigenvalues""" 264 265 # Setup arguments 266 matA_pointer = ffi.new("CeedScalar *") 267 matA_pointer = ffi.cast( 268 "CeedScalar *", 269 matA.__array_interface__['data'][0]) 270 271 matB_pointer = ffi.new("CeedScalar *") 272 matB_pointer = ffi.cast( 273 "CeedScalar *", 274 matB.__array_interface__['data'][0]) 275 276 lbda = np.empty(n, dtype="float64") 277 l_pointer = ffi.new("CeedScalar *") 278 l_pointer = ffi.cast( 279 "CeedScalar *", 280 lbda.__array_interface__['data'][0]) 281 282 x = np.empty(n * n, dtype="float64") 283 x_pointer = ffi.new("CeedScalar *") 284 x_pointer = ffi.cast("CeedScalar *", x.__array_interface__['data'][0]) 285 286 # libCEED call 287 lib.CeedSimultaneousDiagonalization(ceed._pointer[0], matA_pointer, matB_pointer, 288 x_pointer, l_pointer, n) 289 290 return x, lbda 291 292 # Destructor 293 def __del__(self): 294 # libCEED call 295 err_code = lib.CeedBasisDestroy(self._pointer) 296 self._ceed._check_error(err_code) 297 298# ------------------------------------------------------------------------------ 299 300 301class BasisTensorH1(Basis): 302 """Ceed Tensor H1 Basis: finite element tensor-product basis objects for 303 H^1 discretizations.""" 304 305 # Constructor 306 def __init__(self, ceed, dim, ncomp, P1d, Q1d, interp1d, grad1d, 307 qref1d, qweight1d): 308 309 # Setup arguments 310 self._pointer = ffi.new("CeedBasis *") 311 312 self._ceed = ceed 313 314 interp1d_pointer = ffi.new("CeedScalar *") 315 interp1d_pointer = ffi.cast( 316 "CeedScalar *", 317 interp1d.__array_interface__['data'][0]) 318 319 grad1d_pointer = ffi.new("CeedScalar *") 320 grad1d_pointer = ffi.cast( 321 "CeedScalar *", 322 grad1d.__array_interface__['data'][0]) 323 324 qref1d_pointer = ffi.new("CeedScalar *") 325 qref1d_pointer = ffi.cast( 326 "CeedScalar *", 327 qref1d.__array_interface__['data'][0]) 328 329 qweight1d_pointer = ffi.new("CeedScalar *") 330 qweight1d_pointer = ffi.cast( 331 "CeedScalar *", 332 qweight1d.__array_interface__['data'][0]) 333 334 # libCEED call 335 err_code = lib.CeedBasisCreateTensorH1(self._ceed._pointer[0], dim, ncomp, 336 P1d, Q1d, interp1d_pointer, 337 grad1d_pointer, qref1d_pointer, 338 qweight1d_pointer, self._pointer) 339 self._ceed._check_error(err_code) 340 341# ------------------------------------------------------------------------------ 342 343 344class BasisTensorH1Lagrange(Basis): 345 """Ceed Tensor H1 Lagrange Basis: finite element tensor-product Lagrange basis 346 objects for H^1 discretizations.""" 347 348 # Constructor 349 def __init__(self, ceed, dim, ncomp, P, Q, qmode): 350 351 # Setup arguments 352 self._pointer = ffi.new("CeedBasis *") 353 354 self._ceed = ceed 355 356 # libCEED call 357 err_code = lib.CeedBasisCreateTensorH1Lagrange(self._ceed._pointer[0], dim, 358 ncomp, P, Q, qmode, self._pointer) 359 self._ceed._check_error(err_code) 360 361# ------------------------------------------------------------------------------ 362 363 364class BasisH1(Basis): 365 """Ceed H1 Basis: finite element non tensor-product basis for H^1 discretizations.""" 366 367 # Constructor 368 def __init__(self, ceed, topo, ncomp, nnodes, 369 nqpts, interp, grad, qref, qweight): 370 371 # Setup arguments 372 self._pointer = ffi.new("CeedBasis *") 373 374 self._ceed = ceed 375 376 interp_pointer = ffi.new("CeedScalar *") 377 interp_pointer = ffi.cast( 378 "CeedScalar *", 379 interp.__array_interface__['data'][0]) 380 381 grad_pointer = ffi.new("CeedScalar *") 382 grad_pointer = ffi.cast( 383 "CeedScalar *", 384 grad.__array_interface__['data'][0]) 385 386 qref_pointer = ffi.new("CeedScalar *") 387 qref_pointer = ffi.cast( 388 "CeedScalar *", 389 qref.__array_interface__['data'][0]) 390 391 qweight_pointer = ffi.new("CeedScalar *") 392 qweight_pointer = ffi.cast( 393 "CeedScalar *", 394 qweight.__array_interface__['data'][0]) 395 396 # libCEED call 397 err_code = lib.CeedBasisCreateH1(self._ceed._pointer[0], topo, ncomp, 398 nnodes, nqpts, interp_pointer, 399 grad_pointer, qref_pointer, 400 qweight_pointer, self._pointer) 401 402# ------------------------------------------------------------------------------ 403 404 405class TransposeBasis(): 406 """Transpose Ceed Basis: transpose of finite element tensor-product basis objects.""" 407 408 # Attributes 409 _basis = None 410 411 # Constructor 412 def __init__(self, basis): 413 414 # Reference basis 415 self._basis = basis 416 417 # Representation 418 def __repr__(self): 419 return "<Transpose CeedBasis instance at " + hex(id(self)) + ">" 420 421 # Apply Transpose Basis 422 def apply(self, nelem, emode, u, v): 423 """Apply basis evaluation from quadrature points to nodes. 424 425 Args: 426 nelem: the number of elements to apply the basis evaluation to; 427 the backend will specify the ordering in a 428 Blocked ElemRestriction 429 **emode: basis evaluation mode 430 u: input vector 431 v: output vector""" 432 433 # libCEED call 434 self._basis.apply(nelem, emode, u, v, tmode=TRANSPOSE) 435 436# ------------------------------------------------------------------------------ 437