// Copyright (C) 2009--2011, 2013, 2014 Jed Brown and Ed Bueler and Constantine Khroulev and David Maxwell // // This file is part of PISM. // // PISM is free software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // PISM 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 General Public License for more // details. // // You should have received a copy of the GNU General Public License // along with PISM; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // Utility functions used by the SSAFEM code. #include "FETools.hh" #include "flowlaws.hh" #include "IceGrid.hh" #include const FEShapeQ1::ShapeFunctionSpec FEShapeQ1::shapeFunction[FEShapeQ1::Nk] = {FEShapeQ1::shape0, FEShapeQ1::shape1, FEShapeQ1::shape2, FEShapeQ1::shape3}; FEElementMap::FEElementMap(const IceGrid &g) { // Start by assuming ghost elements exist in all directions. // Elements are indexed by their lower left vertex. If there is a ghost // element on the right, its i-index will be the same as the maximum // i-index of a non-ghost vertex in the local grid. xs= g.xs-1; // Start at ghost to the left. int xf = g.xs + g.xm - 1; // End at ghost to the right. ys= g.ys-1; // Start at ghost at the bottom. int yf = g.ys + g.ym - 1; // End at ghost at the top. lxs = g.xs; int lxf = lxs + g.xm - 1; lys = g.ys; int lyf = lys + g.ym - 1; // Now correct if needed. The only way there will not be ghosts is if the // grid is not periodic and we are up against the grid boundary. if( !(g.periodicity & X_PERIODIC) ) { // Leftmost element has x-index 0. if(xs < 0){ xs = 0; } // Rightmost vertex has index g.Mx-1, so the rightmost element has index g.Mx-2 if(xf > g.Mx-2) { xf = g.Mx-2; lxf = g.Mx-2; } } if( !(g.periodicity & Y_PERIODIC) ) { // Bottom element has y-index 0. if(ys < 0){ ys = 0; } // Topmost vertex has index g.My-1, so the topmost element has index g.My-2 if(yf > g.My-2) { yf = g.My-2; lyf = g.My-2; } } // Tally up the number of elements in each direction xm = xf-xs+1; ym = yf-ys+1; lxm = lxf-lxs+1; lym = lyf-lys+1; } /*! \brief Extract local degrees of freedom for element (\a i,\a j) from global vector \a xg to local vector \a x (scalar-valued DOF version). */ void FEDOFMap::extractLocalDOFs(int i,int j, double const*const*xg,double *x) const { x[0] = xg[i][j]; x[1] = xg[i+1][j]; x[2] = xg[i+1][j+1]; x[3] = xg[i][j+1]; } /*! \brief Extract local degrees of freedom for element (\a i,\a j) from global vector \a xg to local vector \a x (vector-valued DOF version). */ void FEDOFMap::extractLocalDOFs(int i,int j, PISMVector2 const*const*xg,PISMVector2 *x) const { x[0] = xg[i][j]; x[1] = xg[i+1][j]; x[2] = xg[i+1][j+1]; x[3] = xg[i][j+1]; } //! Extract scalar degrees of freedom for the element specified previously with FEDOFMap::reset void FEDOFMap::extractLocalDOFs(double const*const*xg,double *x) const { extractLocalDOFs(m_i,m_j,xg,x); } //! Extract vector degrees of freedom for the element specified previously with FEDOFMap::reset void FEDOFMap::extractLocalDOFs(PISMVector2 const*const*xg,PISMVector2 *x) const { extractLocalDOFs(m_i,m_j,xg,x); } //! Convert a local degree of freedom index \a k to a global degree of freedom index (\a i,\a j). void FEDOFMap::localToGlobal(int k, int *i, int *j) { *i = m_i + kIOffset[k]; *j = m_j + kJOffset[k]; } /*!\brief Initialize the FEDOFMap to element (\a i, \a j) for the purposes of inserting into global residual and Jacobian arrays. */ void FEDOFMap::reset(int i, int j, const IceGrid &grid) { m_i = i; m_j = j; // The meaning of i and j for a PISM IceGrid and for a Petsc DA are swapped (the so-called // fundamental transpose. The interface between PISM and Petsc is the stencils, so all // interactions with the stencils involve a transpose. m_col[0].j = i; m_col[0].i = j; m_col[1].j = i+1; m_col[1].i = j; m_col[2].j = i+1; m_col[2].i = j+1; m_col[3].j = i; m_col[3].i = j+1; memcpy(m_row,m_col,Nk*sizeof(m_col[0])); // We do not ever sum into rows that are not owned by the local rank. for(int k=0; kget_grid()->com, "Warning: DirichletData destructing with IceModelVecs still accessed. Looks like DirichletData::finish() was not called."); CHKERRCONTINUE(ierr); } } PetscErrorCode DirichletData::init(IceModelVec2Int *indices, IceModelVec2V *values, double weight) { PetscErrorCode ierr; m_indices = indices; m_values = values; m_weight = weight; if( m_indices != NULL) { ierr = m_indices->get_array(m_pIndices); CHKERRQ(ierr); } else { m_indices = NULL; m_pIndices = NULL; } if( values != NULL) { ierr = values->get_array(reinterpret_cast(m_pValues)); CHKERRQ(ierr); } else { m_values = NULL; m_pValues = NULL; } return 0; } PetscErrorCode DirichletData::init(IceModelVec2Int *indices, IceModelVec2S *values, double weight) { PetscErrorCode ierr; m_indices = indices; m_values = values; m_weight = weight; if( m_indices != NULL) { ierr = m_indices->get_array(m_pIndices); CHKERRQ(ierr); } else { m_indices = NULL; m_pIndices = NULL; } if( values != NULL) { ierr = values->get_array(reinterpret_cast(m_pValues)); CHKERRQ(ierr); } else { m_values = NULL; m_pValues = NULL; } return 0; } PetscErrorCode DirichletData::init(IceModelVec2Int *indices ) { PetscErrorCode ierr; m_values = NULL; m_pValues = NULL; m_weight = 1; m_indices = indices; if( m_indices != NULL) { ierr = m_indices->get_array(m_pIndices); CHKERRQ(ierr); } else { m_indices = NULL; m_pIndices = NULL; } return 0; } PetscErrorCode DirichletData::finish() { PetscErrorCode ierr; if(m_indices) { ierr = m_indices->end_access(); CHKERRQ(ierr); m_indices = NULL; m_pIndices = NULL; } if(m_values) { ierr = m_values->end_access(); CHKERRQ(ierr); m_values = NULL; m_pValues = NULL; } return 0; } void DirichletData::constrain( FEDOFMap &dofmap ) { dofmap.extractLocalDOFs(m_pIndices,m_indices_e); for (int k=0; k 0.5) { // Dirichlet node // Mark any kind of Dirichlet node as not to be touched dofmap.markRowInvalid(k); dofmap.markColInvalid(k); } } } void DirichletData::update( FEDOFMap &dofmap, PISMVector2* x_e ) { #if (PISM_DEBUG==1) assert(m_values != NULL); #endif dofmap.extractLocalDOFs(m_pIndices,m_indices_e); PISMVector2 **pValues = reinterpret_cast(m_pValues); for (int k=0; k 0.5) { // Dirichlet node int i, j; dofmap.localToGlobal(k,&i,&j); x_e[k].u = pValues[i][j].u; x_e[k].v = pValues[i][j].v; } } } void DirichletData::update( FEDOFMap &dofmap, double* x_e ) { #if (PISM_DEBUG==1) assert(m_values != NULL); #endif dofmap.extractLocalDOFs(m_pIndices,m_indices_e); double **pValues = reinterpret_cast(m_pValues); for (int k=0; k 0.5) { // Dirichlet node int i, j; dofmap.localToGlobal(k,&i,&j); x_e[k] = pValues[i][j]; } } } void DirichletData::updateHomogeneous( FEDOFMap &dofmap, PISMVector2* x_e ) { dofmap.extractLocalDOFs(m_pIndices,m_indices_e); for (int k=0; k 0.5) { // Dirichlet node x_e[k].u = 0; x_e[k].v = 0; } } } void DirichletData::updateHomogeneous( FEDOFMap &dofmap, double* x_e ) { dofmap.extractLocalDOFs(m_pIndices,m_indices_e); for (int k=0; k 0.5) { // Dirichlet node x_e[k] = 0.; } } } void DirichletData::fixResidual( PISMVector2 **x, PISMVector2 **r) { #if (PISM_DEBUG==1) assert(m_values != NULL); #endif IceGrid &grid = *m_indices->get_grid(); PISMVector2 **pValues = reinterpret_cast(m_pValues); int i,j; // For each node that we own: for (i=grid.xs; ias_int(i,j) == 1) { // Enforce explicit dirichlet data. r[i][j].u = m_weight * (x[i][j].u - pValues[i][j].u); r[i][j].v = m_weight * (x[i][j].v - pValues[i][j].v); } } } } void DirichletData::fixResidualHomogeneous( PISMVector2 **r) { IceGrid &grid = *m_indices->get_grid(); int i,j; // For each node that we own: for (i=grid.xs; ias_int(i,j) == 1) { // Enforce explicit dirichlet data. r[i][j].u = 0; r[i][j].v = 0; } } } } void DirichletData::fixResidual( double **x, double **r) { #if (PISM_DEBUG==1) assert(m_values != NULL); #endif IceGrid &grid = *m_indices->get_grid(); int i,j; double **pValues = reinterpret_cast(m_pValues); // For each node that we own: for (i=grid.xs; ias_int(i,j) == 1) { // Enforce explicit dirichlet data. r[i][j] = m_weight * (x[i][j] - pValues[i][j]); } } } } void DirichletData::fixResidualHomogeneous( double **r) { IceGrid &grid = *m_indices->get_grid(); int i,j; // For each node that we own: for (i=grid.xs; ias_int(i,j) == 1) { // Enforce explicit dirichlet data. r[i][j] = 0; } } } } PetscErrorCode DirichletData::fixJacobian2V(Mat J) { int i,j; PetscErrorCode ierr; IceGrid &grid = *m_indices->get_grid(); // Until now, the rows and columns correspoinding to Dirichlet data have not been set. We now // put an identity block in for these unknowns. Note that because we have takes steps to not touching these // columns previously, the symmetry of the Jacobian matrix is preserved. const double ident[4] = {m_weight,0,0,m_weight}; for (i=grid.xs; ias_int(i,j) == 1) { MatStencil row; // Transpose shows up here! row.j = i; row.i = j; ierr = MatSetValuesBlockedStencil(J,1,&row,1,&row,ident,ADD_VALUES); CHKERRQ(ierr); } } } return 0; } PetscErrorCode DirichletData::fixJacobian2S(Mat J) { int i,j; PetscErrorCode ierr; IceGrid &grid = *m_indices->get_grid(); // Until now, the rows and columns correspoinding to Dirichlet data have not been set. We now // put an identity block in for these unknowns. Note that because we have takes steps to not touching these // columns previously, the symmetry of the Jacobian matrix is preserved. const double ident = m_weight; for (i=grid.xs; ias_int(i,j) == 1) { MatStencil row; // Transpose shows up here! row.j = i; row.i = j; ierr = MatSetValuesBlockedStencil(J,1,&row,1,&row,&ident,ADD_VALUES); CHKERRQ(ierr); } } } return 0; }