blob: d2cd4f2046843bca31b98bb260465be5c9afbf36 [file] [log] [blame]
#ifndef _Hex8_hpp_
#define _Hex8_hpp_
//@HEADER
// ************************************************************************
//
// MiniFE: Simple Finite Element Assembly and Solve
// Copyright (2006-2013) Sandia Corporation
//
// Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
// license for use of this work by or on behalf of the U.S. Government.
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
// ************************************************************************
//@HEADER
#ifndef KERNEL_PREFIX
#define KERNEL_PREFIX
#endif
#include <gauss_pts.hpp>
#include <matrix_algebra_3x3.hpp>
#include <Hex8_enums.hpp>
namespace miniFE {
namespace Hex8 {
template<typename Scalar>
KERNEL_PREFIX void shape_fns(const Scalar* x, Scalar* values_at_nodes)
{
//assumptions: values_at_nodes has length numNodesPerElem
// x has length 3 (hard-coded spatialDim)
const Scalar u = 1.0 - x[0];
const Scalar v = 1.0 - x[1];
const Scalar w = 1.0 - x[2];
const Scalar up1 = 1.0 + x[0];
const Scalar vp1 = 1.0 + x[1];
const Scalar wp1 = 1.0 + x[2];
values_at_nodes[0] = 0.125 * u * v * w;//(1-x)*(1-y)*(1-z)
values_at_nodes[1] = 0.125 * up1 * v * w;//(1+x)*(1-y)*(1-z)
values_at_nodes[2] = 0.125 * up1 * vp1 * w;//(1+x)*(1+y)*(1-z)
values_at_nodes[3] = 0.125 * u * vp1 * w;//(1-x)*(1+y)*(1-z)
values_at_nodes[4] = 0.125 * u * v * wp1;//(1-x)*(1-y)*(1+z)
values_at_nodes[5] = 0.125 * up1 * v * wp1;//(1+x)*(1-y)*(1+z)
values_at_nodes[6] = 0.125 * up1 * vp1 * wp1;//(1+x)*(1+y)*(1+z)
values_at_nodes[7] = 0.125 * u * vp1 * wp1;//(1-x)*(1+y)*(1+z)
}
template<typename Scalar>
KERNEL_PREFIX void gradients(const Scalar* x, Scalar* values_per_fn)
{
//assumptions values_per_fn has length 24 (numNodesPerElem*spatialDim)
// spatialDim == 3
const Scalar u = 1.0 - x[0];
const Scalar v = 1.0 - x[1];
const Scalar w = 1.0 - x[2];
const Scalar up1 = 1.0 + x[0];
const Scalar vp1 = 1.0 + x[1];
const Scalar wp1 = 1.0 + x[2];
//fn 0
values_per_fn[0] = -0.125 * v * w;
values_per_fn[1] = -0.125 * u * w;
values_per_fn[2] = -0.125 * u * v;
//fn 1
values_per_fn[3] = 0.125 * v * w;
values_per_fn[4] = -0.125 * up1 * w;
values_per_fn[5] = -0.125 * up1 * v;
//fn 2
values_per_fn[6] = 0.125 * vp1 * w;
values_per_fn[7] = 0.125 * up1 * w;
values_per_fn[8] = -0.125 * up1 * vp1;
//fn 3
values_per_fn[9] = -0.125 * vp1 * w;
values_per_fn[10] = 0.125 * u * w;
values_per_fn[11] = -0.125 * u * vp1;
//fn 4
values_per_fn[12] = -0.125 * v * wp1;
values_per_fn[13] = -0.125 * u * wp1;
values_per_fn[14] = 0.125 * u * v;
//fn 5
values_per_fn[15] = 0.125 * v * wp1;
values_per_fn[16] = -0.125 * up1 * wp1;
values_per_fn[17] = 0.125 * up1 * v;
//fn 6
values_per_fn[18] = 0.125 * vp1 * wp1;
values_per_fn[19] = 0.125 * up1 * wp1;
values_per_fn[20] = 0.125 * up1 * vp1;
//fn 7
values_per_fn[21] = -0.125 * vp1 * wp1;
values_per_fn[22] = 0.125 * u * wp1;
values_per_fn[23] = 0.125 * u * vp1;
}
template<typename Scalar>
KERNEL_PREFIX void gradients_and_detJ(const Scalar* elemNodeCoords,
const Scalar* grad_vals,
Scalar& detJ)
{
/**
pt is the point at which the jacobian is to be computed.
*/
//assumptions on the lengths of input arguments:
//elemNodeCoords has length numNodesPerElem*spatialDim,
//grad_vals has length numNodesPerElem*spatialDim
const Scalar zero = 0;
Scalar J00 = zero;
Scalar J01 = zero;
Scalar J02 = zero;
Scalar J10 = zero;
Scalar J11 = zero;
Scalar J12 = zero;
Scalar J20 = zero;
Scalar J21 = zero;
Scalar J22 = zero;
size_t i_X_spatialDim = 0;
for(size_t i=0; i<numNodesPerElem; ++i) {
// size_t offset = 0;
// for(size_t gd=0; gd<spatialDim; ++gd) {
//
// Scalar gval = grad_vals[i_X_spatialDim+gd];
//
// for(size_t jd=0; jd<spatialDim; ++jd) {
// J[offset++] += gval*elemNodeCoords[i_X_spatialDim+jd];
// }
// }
//for optimization, unroll the above double-loop over spatialDim:
//(hard-coded assumption that spatialDim == 3)
J00 += grad_vals[i_X_spatialDim+0]*elemNodeCoords[i_X_spatialDim+0];
J01 += grad_vals[i_X_spatialDim+0]*elemNodeCoords[i_X_spatialDim+1];
J02 += grad_vals[i_X_spatialDim+0]*elemNodeCoords[i_X_spatialDim+2];
J10 += grad_vals[i_X_spatialDim+1]*elemNodeCoords[i_X_spatialDim+0];
J11 += grad_vals[i_X_spatialDim+1]*elemNodeCoords[i_X_spatialDim+1];
J12 += grad_vals[i_X_spatialDim+1]*elemNodeCoords[i_X_spatialDim+2];
J20 += grad_vals[i_X_spatialDim+2]*elemNodeCoords[i_X_spatialDim+0];
J21 += grad_vals[i_X_spatialDim+2]*elemNodeCoords[i_X_spatialDim+1];
J22 += grad_vals[i_X_spatialDim+2]*elemNodeCoords[i_X_spatialDim+2];
i_X_spatialDim += spatialDim;
}
Scalar term0 = J22*J11 - J21*J12;
Scalar term1 = J22*J01 - J21*J02;
Scalar term2 = J12*J01 - J11*J02;
detJ = J00*term0 - J10*term1 + J20*term2;
}
template<typename Scalar>
KERNEL_PREFIX void gradients_and_invJ_and_detJ(const Scalar* elemNodeCoords,
const Scalar* grad_vals,
Scalar* invJ,
Scalar& detJ)
{
/**
pt is the point at which the jacobian is to be computed.
*/
//assumptions on the lengths of input arguments:
//pt has length spatialDim,
//elemNodeCoords has length numNodesPerElem*spatialDim,
//grad_vals has length numNodesPerElem*spatialDim, and
//J has length spatialDim*spatialDim
const Scalar zero = 0;
//
//First we compute the jacobian J:
//
Scalar J00 = zero;
Scalar J01 = zero;
Scalar J02 = zero;
Scalar J10 = zero;
Scalar J11 = zero;
Scalar J12 = zero;
Scalar J20 = zero;
Scalar J21 = zero;
Scalar J22 = zero;
size_t i_X_spatialDim = 0;
for(size_t i=0; i<numNodesPerElem; ++i) {
// size_t offset = 0;
// for(size_t gd=0; gd<spatialDim; ++gd) {
//
// Scalar gval = grad_vals[i_X_spatialDim+gd];
//
// for(size_t jd=0; jd<spatialDim; ++jd) {
// J[offset++] += gval*elemNodeCoords[i_X_spatialDim+jd];
// }
// }
//for optimization, unroll the above double-loop over spatialDim:
//(a hard-coded assumption that spatialDim == 3)
J00 += grad_vals[i_X_spatialDim+0]*elemNodeCoords[i_X_spatialDim+0];
J01 += grad_vals[i_X_spatialDim+0]*elemNodeCoords[i_X_spatialDim+1];
J02 += grad_vals[i_X_spatialDim+0]*elemNodeCoords[i_X_spatialDim+2];
J10 += grad_vals[i_X_spatialDim+1]*elemNodeCoords[i_X_spatialDim+0];
J11 += grad_vals[i_X_spatialDim+1]*elemNodeCoords[i_X_spatialDim+1];
J12 += grad_vals[i_X_spatialDim+1]*elemNodeCoords[i_X_spatialDim+2];
J20 += grad_vals[i_X_spatialDim+2]*elemNodeCoords[i_X_spatialDim+0];
J21 += grad_vals[i_X_spatialDim+2]*elemNodeCoords[i_X_spatialDim+1];
J22 += grad_vals[i_X_spatialDim+2]*elemNodeCoords[i_X_spatialDim+2];
i_X_spatialDim += spatialDim;
}
Scalar term0 = J22*J11 - J21*J12;
Scalar term1 = J22*J01 - J21*J02;
Scalar term2 = J12*J01 - J11*J02;
detJ = J00*term0 - J10*term1 + J20*term2;
Scalar inv_detJ = 1.0/detJ;
invJ[0] = term0*inv_detJ;
invJ[1] = -term1*inv_detJ;
invJ[2] = term2*inv_detJ;
invJ[3] = -(J22*J10 - J20*J12)*inv_detJ;
invJ[4] = (J22*J00 - J20*J02)*inv_detJ;
invJ[5] = -(J12*J00 - J10*J02)*inv_detJ;
invJ[6] = (J21*J10 - J20*J11)*inv_detJ;
invJ[7] = -(J21*J00 - J20*J01)*inv_detJ;
invJ[8] = (J11*J00 - J10*J01)*inv_detJ;
}
template<typename Scalar>
KERNEL_PREFIX void diffusionMatrix_symm(const Scalar* elemNodeCoords,
const Scalar* grad_vals,
Scalar* elem_mat)
{
int len = (numNodesPerElem * (numNodesPerElem+1))/2;
const Scalar zero = 0;
miniFE::fill(elem_mat, elem_mat+len, zero);
Scalar gpts[numGaussPointsPerDim];
Scalar gwts[numGaussPointsPerDim];
gauss_pts(numGaussPointsPerDim, gpts, gwts);
const Scalar k = 1.0;
Scalar detJ = 0.0;
Scalar dpsidx[numNodesPerElem], dpsidy[numNodesPerElem], dpsidz[numNodesPerElem];
Scalar invJ[spatialDim*spatialDim];
//The following nested loop implements equations 3.4.5 and 3.4.7 on page 88
//of Reddy & Gartling, "The Finite Element Method in Heat Transfer and Fluid
//Dynamics", 2nd edition,
//to compute the element diffusion matrix for the steady conduction equation.
Scalar pt[spatialDim];
#ifdef MINIFE_DEBUG
Scalar volume = zero;
#endif
size_t gv_offset = 0;
for(size_t ig=0; ig<numGaussPointsPerDim; ++ig) {
Scalar wi = gwts[ig];
for(size_t jg=0; jg<numGaussPointsPerDim; ++jg) {
Scalar wi_wj = wi*gwts[jg];
for(size_t kg=0; kg<numGaussPointsPerDim; ++kg) {
Scalar wi_wj_wk = wi_wj*gwts[kg];
const Scalar* grad_vals_ptr = &grad_vals[gv_offset];
gv_offset += numNodesPerElem*spatialDim;
gradients_and_invJ_and_detJ(elemNodeCoords, grad_vals_ptr, invJ, detJ);
#ifdef MINIFE_DEBUG
volume += detJ;
#endif
Scalar k_detJ_wi_wj_wk = k*detJ*wi_wj_wk;
const Scalar* gv = grad_vals_ptr;
for(int i=0; i<numNodesPerElem; ++i) {
Scalar gv0 = gv[0], gv1 = gv[1], gv2 = gv[2];
dpsidx[i] = gv0 * invJ[0] +
gv1 * invJ[1] +
gv2 * invJ[2];
dpsidy[i] = gv0 * invJ[3] +
gv1 * invJ[4] +
gv2 * invJ[5];
dpsidz[i] = gv0 * invJ[6] +
gv1 * invJ[7] +
gv2 * invJ[8];
gv += spatialDim;
}
int offset = 0;
for(int m=0; m<numNodesPerElem; ++m) {
const Scalar dpsidx_m = dpsidx[m];
const Scalar dpsidy_m = dpsidy[m];
const Scalar dpsidz_m = dpsidz[m];
elem_mat[offset++] += k_detJ_wi_wj_wk *
((dpsidx_m*dpsidx_m) +
(dpsidy_m*dpsidy_m) +
(dpsidz_m*dpsidz_m));
for(int n=m+1; n<numNodesPerElem; ++n) {
elem_mat[offset++] += k_detJ_wi_wj_wk *
((dpsidx_m * dpsidx[n]) +
(dpsidy_m * dpsidy[n]) +
(dpsidz_m * dpsidz[n]));
}
}
}//for kg
}//for jg
}//for ig
//int offset = 0;
//std::cout.precision(16);
//for(int m=0; m<numNodesPerElem; ++m) {
// for(int n=m; n<numNodesPerElem; ++n) {
//std::cout<<"elem_mat["<<offset<<"] = "<<elem_mat[offset]<<";"<<std::endl;
// ++offset;
// }
//}
#ifdef MINIFE_DEBUG
// std::cout << "element volume: " << volume << std::endl;
// if (std::abs(volume - 1) > 1.e-7) {
// std::cout << "element volume is "<<volume<<", expected 1.0."<<std::endl;
// }
#endif
}
template<typename Scalar>
KERNEL_PREFIX void sourceVector(const Scalar* elemNodeCoords,
const Scalar* grad_vals,
Scalar* elem_vec)
{
int len = numNodesPerElem;
const Scalar zero = 0;
miniFE::fill(elem_vec, elem_vec+len, zero);
Scalar gpts[numGaussPointsPerDim];
Scalar gwts[numGaussPointsPerDim];
Scalar psi[numNodesPerElem];
gauss_pts(numGaussPointsPerDim, gpts, gwts);
Scalar Q = 1.0;
Scalar pt[spatialDim];
size_t gv_offset = 0;
for(size_t ig=0; ig<numGaussPointsPerDim; ++ig) {
pt[0] = gpts[ig];
Scalar wi = gwts[ig];
for(size_t jg=0; jg<numGaussPointsPerDim; ++jg) {
pt[1] = gpts[jg];
Scalar wj = gwts[jg];
for(size_t kg=0; kg<numGaussPointsPerDim; ++kg) {
pt[2] = gpts[kg];
Scalar wk = gwts[kg];
shape_fns(pt, psi);
const Scalar* grad_vals_ptr = &grad_vals[gv_offset];
gv_offset += numNodesPerElem*spatialDim;
Scalar detJ;
gradients_and_detJ(elemNodeCoords, grad_vals_ptr, detJ);
Scalar term = Q*detJ*wi*wj*wk;
for(int i=0; i<numNodesPerElem; ++i) {
elem_vec[i] += psi[i]*term;
}
}
}
}
}
}//namespace Hex8
}//namespace miniFE
#endif