15a21df39Svaleriabarra# Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 25a21df39Svaleriabarra# the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 35a21df39Svaleriabarra# reserved. See files LICENSE and NOTICE for details. 45a21df39Svaleriabarra# 55a21df39Svaleriabarra# This file is part of CEED, a collection of benchmarks, miniapps, software 65a21df39Svaleriabarra# libraries and APIs for efficient high-order finite element and spectral 75a21df39Svaleriabarra# element discretizations for exascale applications. For more information and 85a21df39Svaleriabarra# source code availability see http://github.com/ceed. 95a21df39Svaleriabarra# 105a21df39Svaleriabarra# The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 115a21df39Svaleriabarra# a collaborative effort of two U.S. Department of Energy organizations (Office 125a21df39Svaleriabarra# of Science and the National Nuclear Security Administration) responsible for 135a21df39Svaleriabarra# the planning and preparation of a capable exascale ecosystem, including 145a21df39Svaleriabarra# software, applications, hardware, advanced system engineering and early 155a21df39Svaleriabarra# testbed platforms, in support of the nation's exascale computing imperative. 165a21df39Svaleriabarra 175a21df39Svaleriabarrafrom _ceed_cffi import ffi, lib 185a21df39Svaleriabarraimport tempfile 195a21df39Svaleriabarraimport numpy as np 205a21df39Svaleriabarrafrom abc import ABC 215a21df39Svaleriabarrafrom .ceed_constants import TRANSPOSE, NOTRANSPOSE 225a21df39Svaleriabarra 235a21df39Svaleriabarra# ------------------------------------------------------------------------------ 247a7b0fa3SJed Brown 257a7b0fa3SJed Brown 265a21df39Svaleriabarraclass Basis(ABC): 275a21df39Svaleriabarra """Ceed Basis: finite element basis objects.""" 285a21df39Svaleriabarra 295a21df39Svaleriabarra # Representation 305a21df39Svaleriabarra def __repr__(self): 315a21df39Svaleriabarra return "<CeedBasis instance at " + hex(id(self)) + ">" 325a21df39Svaleriabarra 335a21df39Svaleriabarra # String conversion for print() to stdout 345a21df39Svaleriabarra def __str__(self): 355a21df39Svaleriabarra """View a Basis via print().""" 365a21df39Svaleriabarra 375a21df39Svaleriabarra # libCEED call 385a21df39Svaleriabarra with tempfile.NamedTemporaryFile() as key_file: 395a21df39Svaleriabarra with open(key_file.name, 'r+') as stream_file: 405a21df39Svaleriabarra stream = ffi.cast("FILE *", stream_file) 415a21df39Svaleriabarra 42477729cfSJeremy L Thompson err_code = lib.CeedBasisView(self._pointer[0], stream) 43477729cfSJeremy L Thompson self._ceed._check_error(err_code) 445a21df39Svaleriabarra 455a21df39Svaleriabarra stream_file.seek(0) 465a21df39Svaleriabarra out_string = stream_file.read() 475a21df39Svaleriabarra 485a21df39Svaleriabarra return out_string 495a21df39Svaleriabarra 505a21df39Svaleriabarra # Apply Basis 515a21df39Svaleriabarra def apply(self, nelem, emode, u, v, tmode=NOTRANSPOSE): 525a21df39Svaleriabarra """Apply basis evaluation from nodes to quadrature points or vice versa. 535a21df39Svaleriabarra 545a21df39Svaleriabarra Args: 555a21df39Svaleriabarra nelem: the number of elements to apply the basis evaluation to; 565a21df39Svaleriabarra the backend will specify the ordering in a 575a21df39Svaleriabarra BlockedElemRestriction 585a21df39Svaleriabarra emode: basis evaluation mode 595a21df39Svaleriabarra u: input vector 605a21df39Svaleriabarra v: output vector 615a21df39Svaleriabarra **tmode: CEED_NOTRANSPOSE to evaluate from nodes to quadrature 625a21df39Svaleriabarra points, CEED_TRANSPOSE to apply the transpose, mapping 635a21df39Svaleriabarra from quadrature points to nodes; default CEED_NOTRANSPOSE""" 645a21df39Svaleriabarra 655a21df39Svaleriabarra # libCEED call 66477729cfSJeremy L Thompson err_code = lib.CeedBasisApply(self._pointer[0], nelem, tmode, emode, 675a21df39Svaleriabarra u._pointer[0], v._pointer[0]) 68477729cfSJeremy L Thompson self._ceed._check_error(err_code) 695a21df39Svaleriabarra 705a21df39Svaleriabarra # Transpose a Basis 715a21df39Svaleriabarra @property 725a21df39Svaleriabarra def T(self): 735a21df39Svaleriabarra """Transpose a Basis.""" 745a21df39Svaleriabarra 755a21df39Svaleriabarra return TransposeBasis(self) 765a21df39Svaleriabarra 775a21df39Svaleriabarra # Transpose a Basis 785a21df39Svaleriabarra @property 795a21df39Svaleriabarra def transpose(self): 805a21df39Svaleriabarra """Transpose a Basis.""" 815a21df39Svaleriabarra 825a21df39Svaleriabarra return TransposeBasis(self) 835a21df39Svaleriabarra 845a21df39Svaleriabarra # Get number of nodes 855a21df39Svaleriabarra def get_num_nodes(self): 865a21df39Svaleriabarra """Get total number of nodes (in dim dimensions) of a Basis. 875a21df39Svaleriabarra 885a21df39Svaleriabarra Returns: 895a21df39Svaleriabarra num_nodes: total number of nodes""" 905a21df39Svaleriabarra 915a21df39Svaleriabarra # Setup argument 925a21df39Svaleriabarra p_pointer = ffi.new("CeedInt *") 935a21df39Svaleriabarra 945a21df39Svaleriabarra # libCEED call 95477729cfSJeremy L Thompson err_code = lib.CeedBasisGetNumNodes(self._pointer[0], p_pointer) 96477729cfSJeremy L Thompson self._ceed._check_error(err_code) 975a21df39Svaleriabarra 985a21df39Svaleriabarra return p_pointer[0] 995a21df39Svaleriabarra 1005a21df39Svaleriabarra # Get number of quadrature points 1015a21df39Svaleriabarra def get_num_quadrature_points(self): 1025a21df39Svaleriabarra """Get total number of quadrature points (in dim dimensions) of a Basis. 1035a21df39Svaleriabarra 1045a21df39Svaleriabarra Returns: 1055a21df39Svaleriabarra num_qpts: total number of quadrature points""" 1065a21df39Svaleriabarra 1075a21df39Svaleriabarra # Setup argument 1085a21df39Svaleriabarra q_pointer = ffi.new("CeedInt *") 1095a21df39Svaleriabarra 1105a21df39Svaleriabarra # libCEED call 111477729cfSJeremy L Thompson err_code = lib.CeedBasisGetNumQuadraturePoints( 112477729cfSJeremy L Thompson self._pointer[0], q_pointer) 113477729cfSJeremy L Thompson self._ceed._check_error(err_code) 1145a21df39Svaleriabarra 1155a21df39Svaleriabarra return q_pointer[0] 1165a21df39Svaleriabarra 1175a21df39Svaleriabarra # Destructor 1185a21df39Svaleriabarra def __del__(self): 1195a21df39Svaleriabarra # libCEED call 120477729cfSJeremy L Thompson err_code = lib.CeedBasisDestroy(self._pointer) 121477729cfSJeremy L Thompson self._ceed._check_error(err_code) 1225a21df39Svaleriabarra 1235a21df39Svaleriabarra# ------------------------------------------------------------------------------ 1247a7b0fa3SJed Brown 1257a7b0fa3SJed Brown 1265a21df39Svaleriabarraclass BasisTensorH1(Basis): 1275a21df39Svaleriabarra """Ceed Tensor H1 Basis: finite element tensor-product basis objects for 1285a21df39Svaleriabarra H^1 discretizations.""" 1295a21df39Svaleriabarra 1305a21df39Svaleriabarra # Constructor 1315a21df39Svaleriabarra def __init__(self, ceed, dim, ncomp, P1d, Q1d, interp1d, grad1d, 1325a21df39Svaleriabarra qref1d, qweight1d): 1335a21df39Svaleriabarra 1345a21df39Svaleriabarra # Setup arguments 1355a21df39Svaleriabarra self._pointer = ffi.new("CeedBasis *") 1365a21df39Svaleriabarra 1375a21df39Svaleriabarra self._ceed = ceed 1385a21df39Svaleriabarra 1395a21df39Svaleriabarra interp1d_pointer = ffi.new("CeedScalar *") 1407a7b0fa3SJed Brown interp1d_pointer = ffi.cast( 1417a7b0fa3SJed Brown "CeedScalar *", 1427a7b0fa3SJed Brown interp1d.__array_interface__['data'][0]) 1435a21df39Svaleriabarra 1445a21df39Svaleriabarra grad1d_pointer = ffi.new("CeedScalar *") 1457a7b0fa3SJed Brown grad1d_pointer = ffi.cast( 1467a7b0fa3SJed Brown "CeedScalar *", 1477a7b0fa3SJed Brown grad1d.__array_interface__['data'][0]) 1485a21df39Svaleriabarra 1495a21df39Svaleriabarra qref1d_pointer = ffi.new("CeedScalar *") 1507a7b0fa3SJed Brown qref1d_pointer = ffi.cast( 1517a7b0fa3SJed Brown "CeedScalar *", 1527a7b0fa3SJed Brown qref1d.__array_interface__['data'][0]) 1535a21df39Svaleriabarra 1545a21df39Svaleriabarra qweight1d_pointer = ffi.new("CeedScalar *") 1557a7b0fa3SJed Brown qweight1d_pointer = ffi.cast( 1567a7b0fa3SJed Brown "CeedScalar *", 1577a7b0fa3SJed Brown qweight1d.__array_interface__['data'][0]) 1585a21df39Svaleriabarra 1595a21df39Svaleriabarra # libCEED call 160477729cfSJeremy L Thompson err_code = lib.CeedBasisCreateTensorH1(self._ceed._pointer[0], dim, ncomp, 161477729cfSJeremy L Thompson P1d, Q1d, interp1d_pointer, 162477729cfSJeremy L Thompson grad1d_pointer, qref1d_pointer, 1635a21df39Svaleriabarra qweight1d_pointer, self._pointer) 164477729cfSJeremy L Thompson self._ceed._check_error(err_code) 1655a21df39Svaleriabarra 166*66ce82e0Sjeremylt # Get 1D interpolation matrix 167*66ce82e0Sjeremylt def get_interp_1d(self): 168d99fa3c5SJeremy L Thompson """Return 1D interpolation matrix of a tensor product Basis. 169d99fa3c5SJeremy L Thompson 170d99fa3c5SJeremy L Thompson Returns: 171d99fa3c5SJeremy L Thompson *array: Numpy array""" 172d99fa3c5SJeremy L Thompson 173d99fa3c5SJeremy L Thompson # Compute the length of the array 174d99fa3c5SJeremy L Thompson nnodes_pointer = ffi.new("CeedInt *") 175d99fa3c5SJeremy L Thompson lib.CeedBasisGetNumNodes1D(self._pointer[0], nnodes_pointer) 176d99fa3c5SJeremy L Thompson nqpts_pointer = ffi.new("CeedInt *") 177d99fa3c5SJeremy L Thompson lib.CeedBasisGetNumQuadraturePoints1D(self._pointer[0], nqpts_pointer) 178d99fa3c5SJeremy L Thompson length = nnodes_pointer[0] * nqpts_pointer[0] 179d99fa3c5SJeremy L Thompson 180d99fa3c5SJeremy L Thompson # Setup the pointer's pointer 181d99fa3c5SJeremy L Thompson array_pointer = ffi.new("CeedScalar **") 182d99fa3c5SJeremy L Thompson 183d99fa3c5SJeremy L Thompson # libCEED call 184d99fa3c5SJeremy L Thompson lib.CeedBasisGetInterp1D(self._pointer[0], array_pointer) 185d99fa3c5SJeremy L Thompson 186d99fa3c5SJeremy L Thompson # Return array created from buffer 187d99fa3c5SJeremy L Thompson # Create buffer object from returned pointer 188d99fa3c5SJeremy L Thompson buff = ffi.buffer( 189d99fa3c5SJeremy L Thompson array_pointer[0], 190d99fa3c5SJeremy L Thompson ffi.sizeof("CeedScalar") * 191d99fa3c5SJeremy L Thompson length) 192d99fa3c5SJeremy L Thompson # return read only Numpy array 193d99fa3c5SJeremy L Thompson ret = np.frombuffer(buff, dtype="float64") 194d99fa3c5SJeremy L Thompson ret.flags['WRITEABLE'] = False 195d99fa3c5SJeremy L Thompson return ret 196d99fa3c5SJeremy L Thompson 197*66ce82e0Sjeremylt # Get 1D gradient matrix 198*66ce82e0Sjeremylt def get_grad_1d(self): 199*66ce82e0Sjeremylt """Return 1D gradient matrix of a tensor product Basis. 200*66ce82e0Sjeremylt 201*66ce82e0Sjeremylt Returns: 202*66ce82e0Sjeremylt *array: Numpy array""" 203*66ce82e0Sjeremylt 204*66ce82e0Sjeremylt # Compute the length of the array 205*66ce82e0Sjeremylt nnodes_pointer = ffi.new("CeedInt *") 206*66ce82e0Sjeremylt lib.CeedBasisGetNumNodes1D(self._pointer[0], nnodes_pointer) 207*66ce82e0Sjeremylt nqpts_pointer = ffi.new("CeedInt *") 208*66ce82e0Sjeremylt lib.CeedBasisGetNumQuadraturePoints1D(self._pointer[0], nqpts_pointer) 209*66ce82e0Sjeremylt length = nnodes_pointer[0] * nqpts_pointer[0] 210*66ce82e0Sjeremylt 211*66ce82e0Sjeremylt # Setup the pointer's pointer 212*66ce82e0Sjeremylt array_pointer = ffi.new("CeedScalar **") 213*66ce82e0Sjeremylt 214*66ce82e0Sjeremylt # libCEED call 215*66ce82e0Sjeremylt lib.CeedBasisGetGrad1D(self._pointer[0], array_pointer) 216*66ce82e0Sjeremylt 217*66ce82e0Sjeremylt # Return array created from buffer 218*66ce82e0Sjeremylt # Create buffer object from returned pointer 219*66ce82e0Sjeremylt buff = ffi.buffer( 220*66ce82e0Sjeremylt array_pointer[0], 221*66ce82e0Sjeremylt ffi.sizeof("CeedScalar") * 222*66ce82e0Sjeremylt length) 223*66ce82e0Sjeremylt # return read only Numpy array 224*66ce82e0Sjeremylt ret = np.frombuffer(buff, dtype="float64") 225*66ce82e0Sjeremylt ret.flags['WRITEABLE'] = False 226*66ce82e0Sjeremylt return ret 227*66ce82e0Sjeremylt 228*66ce82e0Sjeremylt # Get 1D quadrature weights matrix 229*66ce82e0Sjeremylt def get_q_weight_1d(self): 230*66ce82e0Sjeremylt """Return 1D quadrature weights matrix of a tensor product Basis. 231*66ce82e0Sjeremylt 232*66ce82e0Sjeremylt Returns: 233*66ce82e0Sjeremylt *array: Numpy array""" 234*66ce82e0Sjeremylt 235*66ce82e0Sjeremylt # Compute the length of the array 236*66ce82e0Sjeremylt nqpts_pointer = ffi.new("CeedInt *") 237*66ce82e0Sjeremylt lib.CeedBasisGetNumQuadraturePoints1D(self._pointer[0], nqpts_pointer) 238*66ce82e0Sjeremylt length = nqpts_pointer[0] 239*66ce82e0Sjeremylt 240*66ce82e0Sjeremylt # Setup the pointer's pointer 241*66ce82e0Sjeremylt array_pointer = ffi.new("CeedScalar **") 242*66ce82e0Sjeremylt 243*66ce82e0Sjeremylt # libCEED call 244*66ce82e0Sjeremylt lib.CeedBasisGetQWeights(self._pointer[0], array_pointer) 245*66ce82e0Sjeremylt 246*66ce82e0Sjeremylt # Return array created from buffer 247*66ce82e0Sjeremylt # Create buffer object from returned pointer 248*66ce82e0Sjeremylt buff = ffi.buffer( 249*66ce82e0Sjeremylt array_pointer[0], 250*66ce82e0Sjeremylt ffi.sizeof("CeedScalar") * 251*66ce82e0Sjeremylt length) 252*66ce82e0Sjeremylt # return read only Numpy array 253*66ce82e0Sjeremylt ret = np.frombuffer(buff, dtype="float64") 254*66ce82e0Sjeremylt ret.flags['WRITEABLE'] = False 255*66ce82e0Sjeremylt return ret 256*66ce82e0Sjeremylt 257*66ce82e0Sjeremylt # Get 1D quadrature points matrix 258*66ce82e0Sjeremylt def get_q_ref_1d(self): 259*66ce82e0Sjeremylt """Return 1D quadrature points matrix of a tensor product Basis. 260*66ce82e0Sjeremylt 261*66ce82e0Sjeremylt Returns: 262*66ce82e0Sjeremylt *array: Numpy array""" 263*66ce82e0Sjeremylt 264*66ce82e0Sjeremylt # Compute the length of the array 265*66ce82e0Sjeremylt nqpts_pointer = ffi.new("CeedInt *") 266*66ce82e0Sjeremylt lib.CeedBasisGetNumQuadraturePoints1D(self._pointer[0], nqpts_pointer) 267*66ce82e0Sjeremylt length = nqpts_pointer[0] 268*66ce82e0Sjeremylt 269*66ce82e0Sjeremylt # Setup the pointer's pointer 270*66ce82e0Sjeremylt array_pointer = ffi.new("CeedScalar **") 271*66ce82e0Sjeremylt 272*66ce82e0Sjeremylt # libCEED call 273*66ce82e0Sjeremylt lib.CeedBasisGetQRef(self._pointer[0], array_pointer) 274*66ce82e0Sjeremylt 275*66ce82e0Sjeremylt # Return array created from buffer 276*66ce82e0Sjeremylt # Create buffer object from returned pointer 277*66ce82e0Sjeremylt buff = ffi.buffer( 278*66ce82e0Sjeremylt array_pointer[0], 279*66ce82e0Sjeremylt ffi.sizeof("CeedScalar") * 280*66ce82e0Sjeremylt length) 281*66ce82e0Sjeremylt # return read only Numpy array 282*66ce82e0Sjeremylt ret = np.frombuffer(buff, dtype="float64") 283*66ce82e0Sjeremylt ret.flags['WRITEABLE'] = False 284*66ce82e0Sjeremylt return ret 285*66ce82e0Sjeremylt 286d99fa3c5SJeremy L Thompson 2875a21df39Svaleriabarra# ------------------------------------------------------------------------------ 2887a7b0fa3SJed Brown 2897a7b0fa3SJed Brown 290d99fa3c5SJeremy L Thompsonclass BasisTensorH1Lagrange(BasisTensorH1): 2915a21df39Svaleriabarra """Ceed Tensor H1 Lagrange Basis: finite element tensor-product Lagrange basis 2925a21df39Svaleriabarra objects for H^1 discretizations.""" 2935a21df39Svaleriabarra 2945a21df39Svaleriabarra # Constructor 2955a21df39Svaleriabarra def __init__(self, ceed, dim, ncomp, P, Q, qmode): 2965a21df39Svaleriabarra 2975a21df39Svaleriabarra # Setup arguments 2985a21df39Svaleriabarra self._pointer = ffi.new("CeedBasis *") 2995a21df39Svaleriabarra 3005a21df39Svaleriabarra self._ceed = ceed 3015a21df39Svaleriabarra 3025a21df39Svaleriabarra # libCEED call 303477729cfSJeremy L Thompson err_code = lib.CeedBasisCreateTensorH1Lagrange(self._ceed._pointer[0], dim, 304477729cfSJeremy L Thompson ncomp, P, Q, qmode, self._pointer) 305477729cfSJeremy L Thompson self._ceed._check_error(err_code) 3065a21df39Svaleriabarra 3075a21df39Svaleriabarra# ------------------------------------------------------------------------------ 3087a7b0fa3SJed Brown 3097a7b0fa3SJed Brown 3105a21df39Svaleriabarraclass BasisH1(Basis): 3115a21df39Svaleriabarra """Ceed H1 Basis: finite element non tensor-product basis for H^1 discretizations.""" 3125a21df39Svaleriabarra 3135a21df39Svaleriabarra # Constructor 3147a7b0fa3SJed Brown def __init__(self, ceed, topo, ncomp, nnodes, 3157a7b0fa3SJed Brown nqpts, interp, grad, qref, qweight): 3165a21df39Svaleriabarra 3175a21df39Svaleriabarra # Setup arguments 3185a21df39Svaleriabarra self._pointer = ffi.new("CeedBasis *") 3195a21df39Svaleriabarra 3205a21df39Svaleriabarra self._ceed = ceed 3215a21df39Svaleriabarra 3225a21df39Svaleriabarra interp_pointer = ffi.new("CeedScalar *") 3237a7b0fa3SJed Brown interp_pointer = ffi.cast( 3247a7b0fa3SJed Brown "CeedScalar *", 3257a7b0fa3SJed Brown interp.__array_interface__['data'][0]) 3265a21df39Svaleriabarra 3275a21df39Svaleriabarra grad_pointer = ffi.new("CeedScalar *") 3287a7b0fa3SJed Brown grad_pointer = ffi.cast( 3297a7b0fa3SJed Brown "CeedScalar *", 3307a7b0fa3SJed Brown grad.__array_interface__['data'][0]) 3315a21df39Svaleriabarra 3325a21df39Svaleriabarra qref_pointer = ffi.new("CeedScalar *") 3337a7b0fa3SJed Brown qref_pointer = ffi.cast( 3347a7b0fa3SJed Brown "CeedScalar *", 3357a7b0fa3SJed Brown qref.__array_interface__['data'][0]) 3365a21df39Svaleriabarra 3375a21df39Svaleriabarra qweight_pointer = ffi.new("CeedScalar *") 3387a7b0fa3SJed Brown qweight_pointer = ffi.cast( 3397a7b0fa3SJed Brown "CeedScalar *", 3407a7b0fa3SJed Brown qweight.__array_interface__['data'][0]) 3415a21df39Svaleriabarra 3425a21df39Svaleriabarra # libCEED call 343477729cfSJeremy L Thompson err_code = lib.CeedBasisCreateH1(self._ceed._pointer[0], topo, ncomp, 344477729cfSJeremy L Thompson nnodes, nqpts, interp_pointer, 345477729cfSJeremy L Thompson grad_pointer, qref_pointer, 3465a21df39Svaleriabarra qweight_pointer, self._pointer) 3475a21df39Svaleriabarra 3485a21df39Svaleriabarra# ------------------------------------------------------------------------------ 3497a7b0fa3SJed Brown 3507a7b0fa3SJed Brown 3515a21df39Svaleriabarraclass TransposeBasis(): 3525a21df39Svaleriabarra """Transpose Ceed Basis: transpose of finite element tensor-product basis objects.""" 3535a21df39Svaleriabarra 3545a21df39Svaleriabarra # Attributes 3555a21df39Svaleriabarra _basis = None 3565a21df39Svaleriabarra 3575a21df39Svaleriabarra # Constructor 3585a21df39Svaleriabarra def __init__(self, basis): 3595a21df39Svaleriabarra 3605a21df39Svaleriabarra # Reference basis 3615a21df39Svaleriabarra self._basis = basis 3625a21df39Svaleriabarra 3635a21df39Svaleriabarra # Representation 3645a21df39Svaleriabarra def __repr__(self): 3655a21df39Svaleriabarra return "<Transpose CeedBasis instance at " + hex(id(self)) + ">" 3665a21df39Svaleriabarra 3675a21df39Svaleriabarra # Apply Transpose Basis 3685a21df39Svaleriabarra def apply(self, nelem, emode, u, v): 3695a21df39Svaleriabarra """Apply basis evaluation from quadrature points to nodes. 3705a21df39Svaleriabarra 3715a21df39Svaleriabarra Args: 3725a21df39Svaleriabarra nelem: the number of elements to apply the basis evaluation to; 3735a21df39Svaleriabarra the backend will specify the ordering in a 3745a21df39Svaleriabarra Blocked ElemRestriction 3755a21df39Svaleriabarra **emode: basis evaluation mode 3765a21df39Svaleriabarra u: input vector 3775a21df39Svaleriabarra v: output vector""" 3785a21df39Svaleriabarra 3795a21df39Svaleriabarra # libCEED call 3805a21df39Svaleriabarra self._basis.apply(nelem, emode, u, v, tmode=TRANSPOSE) 3815a21df39Svaleriabarra 3825a21df39Svaleriabarra# ------------------------------------------------------------------------------ 383