// ATC header files #include "ATC_Error.h" #include "FE_Mesh.h" #include "FE_Element.h" #include "FE_Interpolate.h" #include "LinearSolver.h" #include "PolynomialSolver.h" #include "Utility.h" // Other headers #include using ATC_Utility::dbl_geq; using ATC_Utility::det3; using std::vector; namespace ATC { static const int localCoordinatesMaxIterations_ = 40; static const double localCoordinatesTolerance = 1.e-09; // ============================================================= // class FE_Element // ============================================================= FE_Element::FE_Element(const int nSD, int numFaces, int numNodes, int numFaceNodes, int numNodes1d) : nSD_(nSD), numFaces_(numFaces), numNodes_(numNodes), numFaceNodes_(numFaceNodes), numNodes1d_(numNodes1d), tolerance_(localCoordinatesTolerance), projectionGuess_(COORDINATE_ALIGNED) { feInterpolate_ = NULL; } FE_Element::~FE_Element() { if (feInterpolate_) delete feInterpolate_; } int FE_Element::num_ips() const { return feInterpolate_->num_ips(); } int FE_Element::num_face_ips() const { return feInterpolate_->num_face_ips(); } void FE_Element::face_coordinates(const DENS_MAT &eltCoords, const int faceID, DENS_MAT & faceCoords) const { faceCoords.reset(nSD_, numFaceNodes_); for (int inode=0; inode < numFaceNodes_; inode++) { int id = localFaceConn_(faceID,inode); for (int isd=0; isd &mapping) const { for (int iSD=0; iSD((localCoords_(iSD,inode)+1)/2* (numNodes1d_-1)); } } DENS_VEC FE_Element::local_coords_1d() const { DENS_VEC localCoords1d(numNodes1d_); for (int inode1d=0; inode1dinitial_local_coordinates(eltCoords,x,xiGuess); // clip out-of-range guesses if (fabs(xiGuess(0)) > 1.0) xiGuess(0) = 0.; if (fabs(xiGuess(1)) > 1.0) xiGuess(1) = 0.; if (fabs(xiGuess(2)) > 1.0) xiGuess(2) = 0.; // iteratively solve the equation by calculating the global // position of the guess and bringing the difference between it // and the actual global position of x to zero // // uses Newton's method DENS_VEC N(numNodes_); DENS_MAT dNdr(nSD_,numNodes_); DENS_VEC xGuess(nSD_); DENS_VEC xDiff(nSD_); DENS_MAT eltCoordsT = transpose(eltCoords); int count = 0; bool converged = false; while (count < localCoordinatesMaxIterations_) { feInterpolate_->compute_N_dNdr(xiGuess,N,dNdr); xGuess = N*eltCoordsT; xDiff = xGuess-x; // determine if the guess is close enough. // if it is, take it and run // if not, use Newton's method to update the guess if (!dbl_geq(abs(xDiff(0)),tolerance_) && !dbl_geq(abs(xDiff(1)),tolerance_) && !dbl_geq(abs(xDiff(2)),tolerance_)) { converged = true; xi = xiGuess; break; } else { xiGuess = xiGuess - transpose(inv(dNdr*eltCoordsT))*xDiff; } ++count; } return converged; } // ------------------------------------------------------------- // guess for initial local coordinates // ------------------------------------------------------------- void FE_Element::initial_local_coordinates(const DENS_MAT &eltCoords, const DENS_VEC &x, DENS_VEC &xi) const { xi.reset(nSD_); if (projectionGuess_ == COORDINATE_ALIGNED) { double min=0; double max=0; for (int i=0; i ts; ts.reserve(nSD_); tangents(eltCoords,xi0,ts); DENS_VEC & t1 = ts[0]; DENS_VEC & t2 = ts[1]; DENS_VEC & t3 = ts[2]; double J = det3(t1.ptr(),t2.ptr(),t3.ptr()); double J1 = det3(dx.ptr(),t2.ptr(),t3.ptr()); double J2 = det3(t1.ptr(),dx.ptr(),t3.ptr()); double J3 = det3(t1.ptr(),t2.ptr(),dx.ptr()); xi(0) = J1/J; xi(1) = J2/J; xi(2) = J3/J; } else if (projectionGuess_ == TWOD_ANALYTIC) { // assume x-y planar and HEX8 double x0 = x(0), y0 = x(1); double X[4] = {eltCoords(0,0),eltCoords(0,1),eltCoords(0,2),eltCoords(0,3)}; double Y[4] = {eltCoords(1,0),eltCoords(1,1),eltCoords(1,2),eltCoords(1,3)}; double c[3]={0,0,0}; c[0] = y0*X[0] - y0*X[1] - y0*X[2] + y0*X[3] - x0*Y[0] + (X[1]*Y[0])*0.5 + (X[2]*Y[0])*0.5 + x0*Y[1] - (X[0]*Y[1])*0.5 - (X[3]*Y[1])*0.5 + x0*Y[2] - (X[0]*Y[2])*0.5 - (X[3]*Y[2])*0.5 - x0*Y[3] + (X[1]*Y[3])*0.5 + (X[2]*Y[3])*0.5; c[1] = -(y0*X[0]) + y0*X[1] - y0*X[2] + y0*X[3] + x0*Y[0] - X[1]*Y[0] - x0*Y[1] + X[0]*Y[1] + x0*Y[2] - X[3]*Y[2] - x0*Y[3] + X[2]*Y[3]; c[1] = (X[1]*Y[0])*0.5 - (X[2]*Y[0])*0.5 - (X[0]*Y[1])*0.5 + (X[3]*Y[1])*0.5 + (X[0]*Y[2])*0.5 - (X[3]*Y[2])*0.5 - (X[1]*Y[3])*0.5 + (X[2]*Y[3])*0.5; double xi2[2]={0,0}; int nroots = solve_quadratic(c,xi2); if (nroots == 0) throw ATC_Error("no real roots in 2D analytic projection guess"); double xi1[2]={0,0}; xi1[0] = (4*x0 - X[0] + xi2[0]*X[0] - X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0] - X[0] - xi2[0]*X[0])/(-X[0] + xi2[0]*X[0] + X[0] - xi2[0]*X[0] + X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0]); xi1[1] = (4*x0 - X[0] + xi2[0]*X[0] - X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0] - X[0] - xi2[0]*X[0])/(-X[0] + xi2[0]*X[0] + X[0] - xi2[0]*X[0] + X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0]); // choose which one gives back x xi(0) = xi1[0]; xi(1) = xi2[0]; xi(2) = 0; } } bool FE_Element::range_check(const DENS_MAT &eltCoords, const DENS_VEC &x) const { double min=0; double max=0; for (int i=0; iface_normal(faceCoords, 0, normal); faceToPoint = x - column(faceCoords, 0); dot = normal.dot(faceToPoint); if (dbl_geq(dot,0)) { inside = false; break; } } return inside; } // ------------------------------------------------------------- // returns the minimum and maximum values of an element in the // specified dimension // ------------------------------------------------------------- void FE_Element::bounds_in_dim(const DENS_MAT &eltCoords, const int dim, double &min, double &max) const { int it; // iterate over all nodes min = eltCoords(dim,0); it = 1; while (it < numNodes_) { if (dbl_geq(min,eltCoords(dim,it))) { if (dbl_geq(eltCoords(dim,it),min)) { ++it; } else { // set min to this node's coord in the specified dim, if it's // smaller than the value previously stored min = eltCoords(dim,it); } } else { ++it; } } max = eltCoords(dim,0); it = 1; while (it < numNodes_) { if (dbl_geq(max,eltCoords(dim,it))) { ++it; } else { // same, except max/larger max = eltCoords(dim,it); } } } // ------------------------------------------------------------- // shape_function calls should stay generic at all costs // ------------------------------------------------------------- void FE_Element::shape_function(const VECTOR &xi, DENS_VEC &N) const { feInterpolate_->shape_function(xi, N); } void FE_Element::shape_function(const DENS_MAT eltCoords, const VECTOR &x, DENS_VEC &N) { DENS_VEC xi; local_coordinates(eltCoords, x, xi); feInterpolate_->shape_function(xi, N); } void FE_Element::shape_function(const DENS_MAT eltCoords, const VECTOR &x, DENS_VEC &N, DENS_MAT &dNdx) { DENS_VEC xi; local_coordinates(eltCoords, x, xi); feInterpolate_->shape_function(eltCoords, xi, N, dNdx); } void FE_Element::shape_function_derivatives(const DENS_MAT eltCoords, const VECTOR &x, DENS_MAT &dNdx) { DENS_VEC xi; local_coordinates(eltCoords, x, xi); feInterpolate_->shape_function_derivatives(eltCoords, xi, dNdx); } void FE_Element::shape_function(const DENS_MAT eltCoords, DENS_MAT &N, vector &dN, DIAG_MAT &weights) { feInterpolate_->shape_function(eltCoords, N, dN, weights); } void FE_Element::face_shape_function(const DENS_MAT &eltCoords, const int faceID, DENS_MAT &N, DENS_MAT &n, DIAG_MAT &weights) { DENS_MAT faceCoords; face_coordinates(eltCoords, faceID, faceCoords); feInterpolate_->face_shape_function(eltCoords, faceCoords, faceID, N, n, weights); } void FE_Element::face_shape_function(const DENS_MAT &eltCoords, const int faceID, DENS_MAT &N, vector &dN, vector &Nn, DIAG_MAT &weights) { DENS_MAT faceCoords; face_coordinates(eltCoords, faceID, faceCoords); feInterpolate_->face_shape_function(eltCoords, faceCoords, faceID, N, dN, Nn, weights); } double FE_Element::face_normal(const DENS_MAT &eltCoords, const int faceID, int ip, DENS_VEC &normal) { DENS_MAT faceCoords; face_coordinates(eltCoords, faceID, faceCoords); double J = feInterpolate_->face_normal(faceCoords, ip, normal); return J; } void FE_Element::tangents(const DENS_MAT &eltCoords, const DENS_VEC & localCoords, vector &tangents, const bool normalize) const { feInterpolate_->tangents(eltCoords,localCoords,tangents,normalize); } // ============================================================= // class FE_ElementHex // ============================================================= FE_ElementHex::FE_ElementHex(int numNodes, int numFaceNodes, int numNodes1d) : FE_Element(3, // number of spatial dimensions 6, // number of faces numNodes, numFaceNodes, numNodes1d) { // 3 --- 2 // /| /| // / 0 --/ 1 y // 7 --- 6 / | // | |/ | // 4 --- 5 -----x // / // / // z // Basic properties of element: vol_ = 8.0; faceArea_ = 4.0; // Order-specific information: if (numNodes != 8 && numNodes != 20 && numNodes != 27) { throw ATC_Error("Unrecognized interpolation order specified " "for element class: \n" " element only knows how to construct lin " "and quad elments."); } localCoords_.resize(nSD_,numNodes_); localFaceConn_ = Array2D(numFaces_,numFaceNodes_); // Matrix of local nodal coordinates localCoords_(0,0) = -1; localCoords_(0,4) = -1; localCoords_(1,0) = -1; localCoords_(1,4) = -1; localCoords_(2,0) = -1; localCoords_(2,4) = 1; // localCoords_(0,1) = 1; localCoords_(0,5) = 1; localCoords_(1,1) = -1; localCoords_(1,5) = -1; localCoords_(2,1) = -1; localCoords_(2,5) = 1; // localCoords_(0,2) = 1; localCoords_(0,6) = 1; localCoords_(1,2) = 1; localCoords_(1,6) = 1; localCoords_(2,2) = -1; localCoords_(2,6) = 1; // localCoords_(0,3) = -1; localCoords_(0,7) = -1; localCoords_(1,3) = 1; localCoords_(1,7) = 1; localCoords_(2,3) = -1; localCoords_(2,7) = 1; if (numNodes >= 20) { // only for quads localCoords_(0,8) = 0; localCoords_(0,14) = 1; localCoords_(1,8) = -1; localCoords_(1,14) = 1; localCoords_(2,8) = -1; localCoords_(2,14) = 0; // localCoords_(0,9) = 1; localCoords_(0,15) = -1; localCoords_(1,9) = 0; localCoords_(1,15) = 1; localCoords_(2,9) = -1; localCoords_(2,15) = 0; // localCoords_(0,10) = 0; localCoords_(0,16) = 0; localCoords_(1,10) = 1; localCoords_(1,16) = -1; localCoords_(2,10) = -1; localCoords_(2,16) = 1; // localCoords_(0,11) = -1; localCoords_(0,17) = 1; localCoords_(1,11) = 0; localCoords_(1,17) = 0; localCoords_(2,11) = -1; localCoords_(2,17) = 1; // localCoords_(0,12) = -1; localCoords_(0,18) = 0; localCoords_(1,12) = -1; localCoords_(1,18) = 1; localCoords_(2,12) = 0; localCoords_(2,18) = 1; // localCoords_(0,13) = 1; localCoords_(0,19) = -1; localCoords_(1,13) = -1; localCoords_(1,19) = 0; localCoords_(2,13) = 0; localCoords_(2,19) = 1; if (numNodes >= 27) { // only for quads localCoords_(0,20) = 0; localCoords_(0,24) = 1; localCoords_(1,20) = 0; localCoords_(1,24) = 0; localCoords_(2,20) = 0; localCoords_(2,24) = 0; // localCoords_(0,21) = 0; localCoords_(0,25) = 0; localCoords_(1,21) = 0; localCoords_(1,25) = -1; localCoords_(2,21) = -1; localCoords_(2,25) = 0; // localCoords_(0,22) = 0; localCoords_(0,26) = 0; localCoords_(1,22) = 0; localCoords_(1,26) = 1; localCoords_(2,22) = 1; localCoords_(2,26) = 0; // localCoords_(0,23) = -1; localCoords_(1,23) = 0; localCoords_(2,23) = 0; } } // Matrix of local face connectivity // -x // +x localFaceConn_(0,0) = 0; localFaceConn_(1,0) = 1; localFaceConn_(0,1) = 4; localFaceConn_(1,1) = 2; localFaceConn_(0,2) = 7; localFaceConn_(1,2) = 6; localFaceConn_(0,3) = 3; localFaceConn_(1,3) = 5; if (numNodes >= 20) { localFaceConn_(0,4) = 12; localFaceConn_(1,4) = 9; localFaceConn_(0,5) = 19; localFaceConn_(1,5) = 14; localFaceConn_(0,6) = 15; localFaceConn_(1,6) = 17; localFaceConn_(0,7) = 11; localFaceConn_(1,7) = 13; if (numNodes >= 27) { localFaceConn_(0,8) = 23; localFaceConn_(1,8) = 24; } } // -y // +y localFaceConn_(2,0) = 0; localFaceConn_(3,0) = 3; localFaceConn_(2,1) = 1; localFaceConn_(3,1) = 7; localFaceConn_(2,2) = 5; localFaceConn_(3,2) = 6; localFaceConn_(2,3) = 4; localFaceConn_(3,3) = 2; if (numNodes >= 20) { localFaceConn_(2,4) = 8; localFaceConn_(3,4) = 15; localFaceConn_(2,5) = 13; localFaceConn_(3,5) = 18; localFaceConn_(2,6) = 16; localFaceConn_(3,6) = 14; localFaceConn_(2,7) = 12; localFaceConn_(3,7) = 10; if (numNodes >= 27) { localFaceConn_(2,8) = 25; localFaceConn_(3,8) = 26; } } // -z // +z localFaceConn_(4,0) = 0; localFaceConn_(5,0) = 4; localFaceConn_(4,1) = 3; localFaceConn_(5,1) = 5; localFaceConn_(4,2) = 2; localFaceConn_(5,2) = 6; localFaceConn_(4,3) = 1; localFaceConn_(5,3) = 7; if (numNodes >= 20) { localFaceConn_(4,4) = 8; localFaceConn_(5,4) = 16; localFaceConn_(4,5) = 11; localFaceConn_(5,5) = 17; localFaceConn_(4,6) = 10; localFaceConn_(5,6) = 18; localFaceConn_(4,7) = 9; localFaceConn_(5,7) = 19; if (numNodes >= 27) { localFaceConn_(4,8) = 21; localFaceConn_(5,8) = 22; } } if (numNodes == 8) { feInterpolate_ = new FE_InterpolateCartLin(this); } else if (numNodes == 20) { feInterpolate_ = new FE_InterpolateCartSerendipity(this); } else if (numNodes == 27) { feInterpolate_ = new FE_InterpolateCartLagrange(this); } // determine alignment and skewness to see which guess we should use } FE_ElementHex::~FE_ElementHex() { // Handled by base class } void FE_ElementHex::set_quadrature(FeIntQuadrature type) { feInterpolate_->set_quadrature(HEXA,type); } bool FE_ElementHex::contains_point(const DENS_MAT &eltCoords, const DENS_VEC &x) const { if (! range_check(eltCoords,x) ) return false; DENS_VEC xi; bool converged = local_coordinates(eltCoords,x,xi); if (!converged) return false; for (int i=0; i r // 0 1 // // (This is as dictated by the EXODUSII standard.) // // The face opposite point 1 has r = 0, // 2 has s = 0, // 3 has t = 0, // 0 has u = 0. // Basic properties of element: vol_ = 1.0/6.0; // local volume faceArea_ = 1.0/2.0; // Order-specific information: if (numNodes != 4 && numNodes != 10) { throw ATC_Error("Unrecognized interpolation order specified " "for element class: \n" " element only knows how to construct lin " "and quad elments."); } localCoords_.resize(nSD_+1, numNodes_); localFaceConn_ = Array2D(numFaces_,numFaceNodes_); // Matrix of local nodal coordinates // // Remember, there's actually another coordinate too (u), coming // out from the third non-normal face. But we can deal with it on // the fly; these coordinates are barycentric, such that // r + s + t + u = 1 always. As such we only need to deal with r, // s, and t and the computations become easy. // // The first three axes correspond to x, y, and z (essentially), // for the canonical element. // Everyone gets these nodes... localCoords_(0,0) = 0; localCoords_(0,2) = 0; localCoords_(1,0) = 0; localCoords_(1,2) = 1; localCoords_(2,0) = 0; localCoords_(2,2) = 0; localCoords_(3,0) = 1; localCoords_(3,2) = 0; // localCoords_(0,1) = 1; localCoords_(0,3) = 0; localCoords_(1,1) = 0; localCoords_(1,3) = 0; localCoords_(2,1) = 0; localCoords_(2,3) = 1; localCoords_(3,1) = 0; localCoords_(3,3) = 0; if (numNodes >= 10) { // ...quads get even more! localCoords_(0,4) = 0.5; localCoords_(0,5) = 0.5; localCoords_(1,4) = 0.0; localCoords_(1,5) = 0.5; localCoords_(2,4) = 0.0; localCoords_(2,5) = 0.0; localCoords_(3,4) = 0.5; localCoords_(3,5) = 0.0; // localCoords_(0,6) = 0.0; localCoords_(0,7) = 0.0; localCoords_(1,6) = 0.5; localCoords_(1,7) = 0.0; localCoords_(2,6) = 0.0; localCoords_(2,7) = 0.5; localCoords_(3,6) = 0.5; localCoords_(3,7) = 0.5; // localCoords_(0,8) = 0.5; localCoords_(0,9) = 0.0; localCoords_(1,8) = 0.0; localCoords_(1,9) = 0.5; localCoords_(2,8) = 0.5; localCoords_(2,9) = 0.5; localCoords_(3,8) = 0.0; localCoords_(3,9) = 0.0; } // Matrix of local face connectivity: // ...opposite point 0, ...opposite point 2, localFaceConn_(0,0) = 1; localFaceConn_(2,0) = 0; localFaceConn_(0,1) = 2; localFaceConn_(2,1) = 1; localFaceConn_(0,2) = 3; localFaceConn_(2,2) = 3; // ...opposite point 1, ...opposite point 3. localFaceConn_(1,0) = 2; localFaceConn_(3,0) = 0; localFaceConn_(1,1) = 0; localFaceConn_(3,1) = 2; localFaceConn_(1,2) = 3; localFaceConn_(3,2) = 1; feInterpolate_ = new FE_InterpolateSimpLin(this); } FE_ElementTet::~FE_ElementTet() { // Handled by base class } void FE_ElementTet::set_quadrature(FeIntQuadrature type) { feInterpolate_->set_quadrature(TETRA,type); } bool FE_ElementTet::local_coordinates(const DENS_MAT &eltCoords, const DENS_VEC &x, DENS_VEC &xi) const { DENS_MAT T(nSD_, numNodes_-1); DENS_VEC r(nSD_); for (int iSD=0; iSD