PISM, A Parallel Ice Sheet Model stable 0.4.1779

src/base/stressbalance/SSAFEM.cc

Go to the documentation of this file.
00001 // Copyright (C) 2009--2011 Jed Brown and Ed Bueler and Constantine Khroulev and David Maxwell
00002 //
00003 // This file is part of PISM.
00004 //
00005 // PISM is free software; you can redistribute it and/or modify it under the
00006 // terms of the GNU General Public License as published by the Free Software
00007 // Foundation; either version 2 of the License, or (at your option) any later
00008 // version.
00009 //
00010 // PISM is distributed in the hope that it will be useful, but WITHOUT ANY
00011 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00012 // FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
00013 // details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with PISM; if not, write to the Free Software
00017 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018 
00019 #include "SSAFEM.hh"
00020 #include "FETools.hh"
00021 #include "Mask.hh"
00022 
00023 SSA *SSAFEMFactory(IceGrid &g, IceBasalResistancePlasticLaw &b, 
00024                 IceFlowLaw &i, EnthalpyConverter &ec, 
00025                 const NCConfigVariable &c)
00026 {
00027   return new SSAFEM(g,b,i,ec,c);
00028 }
00029 
00031 PetscErrorCode SSAFEM::allocate_fem() {
00032   PetscErrorCode ierr;
00033 
00034   dirichletScale = 1.0;
00035   ocean_rho = config.get("sea_water_density");
00036   earth_grav = config.get("standard_gravity");
00037   m_beta_ice_free_bedrock = config.get("beta_ice_free_bedrock");
00038 
00039   ierr = DACreateGlobalVector(SSADA, &r);CHKERRQ(ierr);
00040   ierr = DAGetMatrix(SSADA, "baij", &J); CHKERRQ(ierr);
00041 
00042   ierr = SNESCreate(grid.com,&snes);CHKERRQ(ierr);
00043 
00044   // Default of maximum 200 iterations; possibly overridded by commandline
00045   PetscInt snes_max_it = 200;
00046   ierr = SNESSetTolerances(snes,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,
00047                            snes_max_it,PETSC_DEFAULT);
00048   // ierr = SNESSetOptionsPrefix(snes,((PetscObject)this)->prefix);CHKERRQ(ierr);
00049 
00050   ierr = SNESSetFromOptions(snes);CHKERRQ(ierr);
00051 
00052   // Allocate feStore, which contains coefficient data at the quadrature points of all the elements.  
00053   // There are nElement elements, and FEQuadrature::Nq quadrature points.
00054   PetscInt nElements = element_index.element_count();
00055   feStore = new FEStoreNode[FEQuadrature::Nq*nElements];
00056 
00057   // hardav IceModelVec2S is not used (so far).
00058   const PetscScalar power = 1.0 / ice.exponent();
00059   char unitstr[TEMPORARY_STRING_LENGTH];
00060   snprintf(unitstr, sizeof(unitstr), "Pa s%f", power);
00061   ierr = hardav.create(grid, "hardav", true); CHKERRQ(ierr);
00062   ierr = hardav.set_attrs("internal", "vertically-averaged ice hardness", unitstr, ""); CHKERRQ(ierr);
00063 
00064   return 0;
00065 }
00066 
00068 PetscErrorCode SSAFEM::deallocate_fem() {
00069   PetscErrorCode ierr;
00070 
00071   ierr = SNESDestroy(snes);CHKERRQ(ierr);
00072   delete feStore;
00073   ierr = VecDestroy(r); CHKERRQ(ierr);
00074   ierr = MatDestroy(J); CHKERRQ(ierr);
00075 
00076   return 0;
00077 }
00078 
00079 // Initialize the solver, called once by the client before use.
00080 PetscErrorCode SSAFEM::init(PISMVars &vars) {
00081   PetscErrorCode ierr;
00082 
00083   ierr = SSA::init(vars); CHKERRQ(ierr);
00084   ierr = verbPrintf(2,grid.com,
00085            "  [using the SNES-based finite element method implementation]\n");
00086            CHKERRQ(ierr);
00087 
00088   ierr = setFromOptions(); CHKERRQ(ierr);
00089 
00090   // On restart, SSA::init() reads the SSA velocity from a PISM output file
00091   // into IceModelVec2V "velocity". We use that field as an initial guess.
00092   // If we are not restarting from a PISM file, "velocity" is identically zero,
00093   // and the call below clears SSAX.
00094 
00095   ierr = velocity.copy_to(SSAX); CHKERRQ(ierr);
00096 
00097   return 0;
00098 }
00099 
00101 
00102 PetscErrorCode SSAFEM::setFromOptions()
00103 {
00104   PetscErrorCode ierr;
00105 
00106   PetscFunctionBegin;
00107   ierr = PetscOptionsHead("SSA FEM options");CHKERRQ(ierr);
00108   dirichletScale = 1.0e9;
00109   ierr = PetscOptionsReal("-ssa_fe_dirichlet_scale",
00110                           "Enforce Dirichlet conditions with this additional scaling",
00111                           "",
00112                           dirichletScale,
00113                           &dirichletScale,NULL);CHKERRQ(ierr);
00114   ierr = PetscOptionsTail();CHKERRQ(ierr);
00115   PetscFunctionReturn(0);
00116 }
00117 
00119 PetscErrorCode SSAFEM::solve()
00120 {
00121   PetscErrorCode ierr;
00122   PetscViewer    viewer;
00123   char           filename[PETSC_MAX_PATH_LEN];
00124   PetscTruth     flg;
00125   
00126   m_epsilon_ssa = config.get("epsilon_ssafd");
00127 
00128   ierr = PetscOptionsGetString(NULL, "-ssa_view", filename,
00129                                PETSC_MAX_PATH_LEN, &flg); CHKERRQ(ierr);
00130   if (flg) {
00131     ierr = PetscViewerASCIIOpen(grid.com,filename,&viewer);
00132              CHKERRQ(ierr);
00133     ierr = PetscViewerASCIIPrintf(viewer,"SNES before SSASolve_FE\n");
00134              CHKERRQ(ierr);
00135     ierr = SNESView(snes,viewer);CHKERRQ(ierr);
00136     ierr = PetscViewerASCIIPrintf(viewer,"solution vector before SSASolve_FE\n");
00137              CHKERRQ(ierr);
00138     ierr = VecView(SSAX,viewer);CHKERRQ(ierr);
00139     ierr = PetscViewerDestroy(viewer);CHKERRQ(ierr);
00140   }
00141 
00142   // Set the SNES callbacks to call into our compute_local_function and compute_local_jacobian
00143   // methods via SSAFEFunction and SSAFEJ
00144   ierr = DASetLocalFunction(SSADA,(DALocalFunction1)SSAFEFunction);CHKERRQ(ierr);
00145   ierr = DASetLocalJacobian(SSADA,(DALocalFunction1)SSAFEJacobian);CHKERRQ(ierr);
00146   callback_data.da = SSADA;  callback_data.ssa = this;
00147   ierr = SNESSetFunction(snes, r,    SNESDAFormFunction,   &callback_data);CHKERRQ(ierr);
00148   ierr = SNESSetJacobian(snes, J, J, SNESDAComputeJacobian,&callback_data);CHKERRQ(ierr);
00149 
00150   stdout_ssa = "";
00151   if (getVerbosityLevel() >= 2) 
00152     stdout_ssa = "  SSA: ";
00153   
00154   // Set up the system to solve (store coefficient data at the quadrature points):
00155   ierr = setup(); CHKERRQ(ierr);
00156 
00157   // Solve:
00158   ierr = SNESSolve(snes,NULL,SSAX);CHKERRQ(ierr);
00159 
00160   // See if it worked.
00161   SNESConvergedReason reason;
00162   ierr = SNESGetConvergedReason( snes, &reason); CHKERRQ(ierr);
00163   if(reason < 0)
00164   {
00165     SETERRQ1(1, 
00166       "SSAFEM solve failed to converge (SNES reason %s)\n\n", SNESConvergedReasons[reason]);
00167   }
00168   else if(getVerbosityLevel() > 2)
00169   {
00170     stdout_ssa += "SSAFEM converged (SNES reason ";
00171     stdout_ssa += SNESConvergedReasons[reason];
00172     stdout_ssa += ")\n";
00173   }
00174 
00175   // Extract the solution back from SSAX to velocity and communicate.
00176   ierr = velocity.copy_from(SSAX); CHKERRQ(ierr);
00177   ierr = velocity.beginGhostComm(); CHKERRQ(ierr);
00178   ierr = velocity.endGhostComm(); CHKERRQ(ierr);
00179 
00180   ierr = PetscOptionsHasName(NULL,"-ssa_view_solution",&flg);CHKERRQ(ierr);
00181   if (flg) {
00182     ierr = PetscViewerASCIIOpen(grid.com,filename,&viewer);CHKERRQ(ierr);
00183     ierr = PetscViewerASCIIPrintf(viewer,"solution vector after SSASolve\n");
00184              CHKERRQ(ierr);
00185     ierr = VecView(SSAX,viewer);CHKERRQ(ierr);
00186     ierr = PetscViewerDestroy(viewer);CHKERRQ(ierr);
00187   }
00188   return 0;
00189 }
00190 
00192 /* This method is should be called after SSAFEM::init and whenever
00193 any geometry or temperature related coefficients have changed. The method
00194 stores the values of the coefficients at the quadrature points of each
00195 element so that these interpolated values do not need to be computed
00196 during each outer iteration of the nonlinear solve.*/
00197 PetscErrorCode SSAFEM::setup()
00198 {
00199   PetscReal      **h,
00200                  **H,
00201                  **topg,
00202                  **tauc_array,
00203                   *Enth_e[4],
00204                   *Enth_q[4];
00205   PetscInt         i,j,k,q,p,
00206                    Mz = grid.Mz;
00207   PetscErrorCode   ierr;
00208 
00209   for(q=0;q<FEQuadrature::Nq;q++)
00210   {
00211     Enth_q[q] = new PetscReal[Mz];
00212   }
00213   
00214   GeometryCalculator gc(sea_level, ice, config);
00215   
00216   ierr = enthalpy->begin_access();CHKERRQ(ierr);
00217   ierr = surface->get_array(h);CHKERRQ(ierr);
00218   ierr = thickness->get_array(H);CHKERRQ(ierr);
00219   ierr = bed->get_array(topg);CHKERRQ(ierr);
00220   ierr = tauc->get_array(tauc_array);CHKERRQ(ierr);
00221 
00222   PetscInt xs = element_index.xs, xm = element_index.xm,
00223            ys = element_index.ys, ym = element_index.ym;  
00224   for (i=xs; i<xs+xm; i++) {
00225     for (j=ys;j<ys+ym; j++) {
00226       
00227       // Extract coefficient values at the quadrature points.
00228       PetscReal hq[FEQuadrature::Nq],hxq[FEQuadrature::Nq],hyq[FEQuadrature::Nq];
00229       quadrature.computeTrialFunctionValues(i,j,dofmap,h,hq,hxq,hyq);
00230 
00231       PetscReal Hq[FEQuadrature::Nq], bq[FEQuadrature::Nq], taucq[FEQuadrature::Nq];
00232       quadrature.computeTrialFunctionValues(i,j,dofmap,H,Hq);
00233       quadrature.computeTrialFunctionValues(i,j,dofmap,topg,bq);
00234       quadrature.computeTrialFunctionValues(i,j,dofmap,tauc_array,taucq);
00235 
00236       const PetscInt ij = element_index.flatten(i,j);
00237       FEStoreNode *feS = &feStore[4*ij];
00238       for (q=0; q<4; q++) {
00239         feS[q].H  = Hq[q];
00240         feS[q].b  = bq[q];
00241         feS[q].tauc = taucq[q];
00242         feS[q].hx = hxq[q];
00243         feS[q].hy = hyq[q];
00244 
00245         feS[q].mask = gc.mask(feS[q].b, feS[q].H);
00246       }
00247 
00248       // In the following, we obtain the averaged hardness value from enthalpy by 
00249       // interpolating enthalpy in each column over a quadrature point and then 
00250       // taking the average over the column.  A faster approach would be to take 
00251       // the column average over each element nodes and then interpolate to the 
00252       // quadrature points. Does this make a difference?
00253 
00254       // Obtain the values of enthalpy at each vertical level at each of the vertices
00255       // of the current element.
00256       ierr = enthalpy->getInternalColumn(i,j,&Enth_e[0]);
00257       ierr = enthalpy->getInternalColumn(i+1,j,&Enth_e[1]);CHKERRQ(ierr);
00258       ierr = enthalpy->getInternalColumn(i+1,j+1,&Enth_e[2]);CHKERRQ(ierr);
00259       ierr = enthalpy->getInternalColumn(i,j+1,&Enth_e[3]);CHKERRQ(ierr);
00260 
00261       // We now want to interpolate to the quadrature points at each of the 
00262       // vertical levels.  It would be nice to use quadrature::computeTestFunctionValues,
00263       // but the way we have just obtained the values at the element vertices
00264       // using getInternalColumn doesn't make this straightforward.  So we compute the values
00265       // by hand.
00266       const FEFunctionGerm (*test)[FEQuadrature::Nk] = quadrature.testFunctionValues();
00267       for (k=0; k<Mz; k++) { 
00268         Enth_q[0][k] = Enth_q[1][k] = Enth_q[2][k] = Enth_q[3][k] = 0;
00269         for (q=0; q<FEQuadrature::Nq; q++) {
00270           for (p=0; p<FEQuadrature::Nk; p++) {
00271             Enth_q[q][k] += test[q][p].val * Enth_e[p][k];
00272           }
00273         }
00274       }
00275 
00276       // Now, for each column over a quadrature point, find the averagedHardness.
00277       for (q=0; q<FEQuadrature::Nq; q++) {
00278         // Evaluate column integrals in flow law at every quadrature point's column
00279         feS[q].B = ice.averagedHardness_from_enth(feS[q].H, grid.kBelowHeight(feS[q].H),
00280                                                   grid.zlevels.data(), Enth_q[q]);
00281       }
00282     }
00283   }
00284   ierr = surface->end_access();CHKERRQ(ierr);
00285   ierr = thickness->end_access();CHKERRQ(ierr);
00286   ierr = bed->end_access();CHKERRQ(ierr);
00287   ierr = tauc->end_access();CHKERRQ(ierr);
00288   ierr = enthalpy->end_access();CHKERRQ(ierr);
00289 
00290   for(q=0;q<4;q++)
00291   {
00292     delete [] Enth_q[q];
00293   }
00294 
00295   return 0;
00296 }
00297 
00300 
00306 inline PetscErrorCode SSAFEM::PointwiseNuHAndBeta(const FEStoreNode *feS,
00307                                                   const PISMVector2 *u,const PetscReal Du[],
00308                                                   PetscReal *nuH, PetscReal *dNuH,
00309                                                   PetscReal *beta, PetscReal *dbeta)
00310 {
00311 
00312   Mask M;
00313   
00314   if (feS->H < strength_extension->get_min_thickness()) {
00315     *nuH = strength_extension->get_notional_strength();
00316     if (dNuH) *dNuH = 0;
00317   } else {
00318     ice.effectiveViscosity_with_derivative(feS->B, Du, nuH, dNuH);
00319     *nuH  *= feS->H;
00320     *nuH += m_epsilon_ssa;
00321     if (dNuH) *dNuH *= feS->H;
00322   }
00323   *nuH  *=  2;
00324   if (dNuH) *dNuH *= 2;
00325   
00326   if( M.grounded_ice(feS->mask) )
00327   {
00328     basal.dragWithDerivative(feS->tauc,u->u,u->v,beta,dbeta);
00329   } else {
00330     *beta = 0;
00331     if( M.ice_free_land(feS->mask) )
00332     {
00333       *beta = m_beta_ice_free_bedrock;
00334     }
00335     if(dbeta) *dbeta = 0;
00336   }
00337   return 0;
00338 }
00339 
00342 
00349 void SSAFEM::FixDirichletValues( PetscReal local_bc_mask[],PISMVector2 **BC_vel,
00350                                 PISMVector2 x[], FEDOFMap &my_dofmap)
00351 {
00352   for (PetscInt k=0; k<4; k++) {
00353     if (PismIntMask(local_bc_mask[k]) == 1) { // Dirichlet node
00354       PetscInt ii, jj;
00355       my_dofmap.localToGlobal(k,&ii,&jj);
00356       x[k].u = BC_vel[ii][jj].u;
00357       x[k].v = BC_vel[ii][jj].v;
00358       // Mark any kind of Dirichlet node as not to be touched
00359       my_dofmap.markRowInvalid(k);
00360       my_dofmap.markColInvalid(k);      
00361     }
00362   }
00363 }
00364 
00366 
00368 PetscErrorCode SSAFEM::compute_local_function(DALocalInfo *info, const PISMVector2 **xg, PISMVector2 **yg)
00369 {
00370   PetscInt         i,j,k,q;
00371   PetscReal        **bc_mask;
00372   PISMVector2        **BC_vel;
00373   PetscErrorCode   ierr;
00374 
00375   (void) info; // Avoid compiler warning.
00376 
00377   // Zero out the portion of the function we are responsible for computing.
00378   for (i=grid.xs; i<grid.xs+grid.xm; i++) {
00379     for (j=grid.ys; j<grid.ys+grid.ym; j++) {
00380       yg[i][j].u = yg[i][j].v = 0;
00381     }
00382   }
00383   
00384   // Start access of Dirichlet data, if present.
00385   if (bc_locations && vel_bc) {
00386     ierr = bc_locations->get_array(bc_mask);CHKERRQ(ierr);
00387     ierr = vel_bc->get_array(BC_vel); CHKERRQ(ierr);
00388   }
00389 
00390   // Jacobian times weights for quadrature.
00391   PetscScalar JxW[FEQuadrature::Nq];
00392   quadrature.getWeightedJacobian(JxW);
00393 
00394   // Storage for the current solution at quadrature points.
00395   PISMVector2 u[FEQuadrature::Nq];
00396   PetscScalar Du[FEQuadrature::Nq][3];
00397 
00398   // An Nq by Nk array of test function values.
00399   const FEFunctionGerm (*test)[FEQuadrature::Nk] = quadrature.testFunctionValues();
00400 
00401   // Flags for each vertex in an element that determine if explicit Dirichlet data has
00402   // been set.
00403   PetscReal local_bc_mask[FEQuadrature::Nk];
00404 
00405   // Iterate over the elements.
00406   PetscInt xs = element_index.xs, xm = element_index.xm,
00407            ys = element_index.ys, ym = element_index.ym;
00408   for (i=xs; i<xs+xm; i++) {
00409     for (j=ys; j<ys+ym; j++) {
00410       // Storage for element-local solution and residuals.
00411       PISMVector2     x[4],y[4]; 
00412       // Index into coefficient storage in feStore
00413       const PetscInt ij = element_index.flatten(i,j);
00414 
00415       // Initialize the map from global to local degrees of freedom for this element.
00416       dofmap.reset(i,j,grid);
00417 
00418       // Obtain the value of the solution at the adjacent nodes to the element.
00419       dofmap.extractLocalDOFs(i,j,xg,x);
00420 
00421       // These values now need to be adjusted if some nodes in the element have
00422       // Dirichlet data.
00423       if(bc_locations && vel_bc) {
00424         dofmap.extractLocalDOFs(i,j,bc_mask,local_bc_mask);
00425         FixDirichletValues(local_bc_mask,BC_vel,x,dofmap);
00426       }
00427 
00428       // Zero out the element-local residual in prep for updating it.
00429       for(k=0;k<FEQuadrature::Nk;k++){ 
00430         y[k].u = 0; y[k].v = 0;
00431       }
00432 
00433       // Compute the solution values and symmetric gradient at the quadrature points.
00434       quadrature.computeTrialFunctionValues(x,u,Du);
00435 
00436       for (q=0; q<FEQuadrature::Nq; q++) {     // loop over quadrature points on this element.
00437 
00438         // Symmetric gradient at the quadrature point.
00439         PetscScalar *Duq = Du[q];
00440 
00441         // Coefficients and weights for this quadrature point.
00442         const FEStoreNode *feS = &feStore[ij*4+q];
00443         const PetscReal    jw  = JxW[q];
00444         PetscReal nuH, beta;
00445         ierr = PointwiseNuHAndBeta(feS,u+q,Duq,&nuH,NULL,&beta,NULL);CHKERRQ(ierr);
00446 
00447         // The next few lines compute the actual residual for the element.
00448         PISMVector2 f;
00449         f.u = beta*u[q].u + ice.rho*earth_grav*feS->H*feS->hx;
00450         f.v = beta*u[q].v + ice.rho*earth_grav*feS->H*feS->hy;
00451 
00452         for(k=0; k<4;k++) {  // loop over the test functions.
00453           const FEFunctionGerm &testqk = test[q][k];
00454           y[k].u += jw*(nuH*(testqk.dx*(2*Duq[0]+Duq[1]) + testqk.dy*Duq[2]) + testqk.val*f.u);
00455           y[k].v += jw*(nuH*(testqk.dy*(2*Duq[1]+Duq[0]) + testqk.dx*Duq[2]) + testqk.val*f.v);
00456         }
00457       } // q
00458 
00459       dofmap.addLocalResidualBlock(y,yg);
00460     } // j-loop
00461   } // i-loop
00462 
00463   // Until now we have not touched rows in the residual corresponding to Dirichlet data.
00464   // We fix this now.
00465   if (bc_locations && vel_bc) {
00466     // Enforce Dirichlet conditions strongly
00467     for (i=grid.xs; i<grid.xs+grid.xm; i++) {
00468       for (j=grid.ys; j<grid.ys+grid.ym; j++) {
00469         if (bc_locations->as_int(i,j) == 1) {
00470           // Enforce explicit dirichlet data.
00471           yg[i][j].u = dirichletScale * (xg[i][j].u - BC_vel[i][j].u);
00472           yg[i][j].v = dirichletScale * (xg[i][j].v - BC_vel[i][j].v);
00473         }
00474       }
00475     }
00476     ierr = bc_locations->end_access();CHKERRQ(ierr);
00477     ierr = vel_bc->end_access(); CHKERRQ(ierr);
00478   }
00479 
00480   PetscTruth monitorFunction;
00481   ierr = PetscOptionsHasName(NULL,"-ssa_monitor_function",&monitorFunction);CHKERRQ(ierr);
00482   if (monitorFunction) {
00483     ierr = PetscPrintf(grid.com,"SSA Solution and Function values (pointwise residuals)\n");CHKERRQ(ierr);
00484     for (i=grid.xs; i<grid.xs+grid.xm; i++) {
00485       for (j=grid.ys; j<grid.ys+grid.ym; j++) {
00486         ierr = PetscSynchronizedPrintf(grid.com,
00487                                        "[%2d,%2d] u=(%12.10e,%12.10e)  f=(%12.4e,%12.4e)\n",
00488                                        i,j,xg[i][j].u,xg[i][j].v,yg[i][j].u,yg[i][j].v);CHKERRQ(ierr);
00489       }
00490     }
00491     ierr = PetscSynchronizedFlush(grid.com);CHKERRQ(ierr);
00492   }
00493 
00494   return 0;
00495 }
00496 
00497 
00498 
00500 
00503 PetscErrorCode SSAFEM::compute_local_jacobian(DALocalInfo *info, const PISMVector2 **xg, Mat Jac )
00504 {
00505 
00506   PetscReal      **bc_mask;
00507   PISMVector2    **BC_vel;
00508   PetscInt         i,j;
00509   PetscErrorCode   ierr;
00510 
00511   // Avoid compiler warning.
00512   (void) info;
00513 
00514   // Zero out the Jacobian in preparation for updating it.
00515   ierr = MatZeroEntries(Jac);CHKERRQ(ierr);
00516 
00517   // Start access to Dirichlet data if present.
00518   if (bc_locations && vel_bc) {
00519     ierr = bc_locations->get_array(bc_mask);CHKERRQ(ierr);
00520     ierr = vel_bc->get_array(BC_vel); CHKERRQ(ierr); 
00521   }
00522 
00523   // Jacobian times weights for quadrature.
00524   PetscScalar JxW[FEQuadrature::Nq];
00525   quadrature.getWeightedJacobian(JxW);
00526 
00527   // Storage for the current solution at quadrature points.
00528   PISMVector2 w[FEQuadrature::Nq];
00529   PetscScalar Dw[FEQuadrature::Nq][3];
00530 
00531   // Values of the finite element test functions at the quadrature points.
00532   // This is an Nq by Nk array of function germs (Nq=#of quad pts, Nk=#of test functions).
00533   const FEFunctionGerm (*test)[FEQuadrature::Nk] = quadrature.testFunctionValues();
00534 
00535   // Flags for each vertex in an element that determine if explicit Dirichlet data has
00536   // been set.
00537   PetscReal local_bc_mask[FEQuadrature::Nk];
00538 
00539   // Loop through all the elements.
00540   PetscInt xs = element_index.xs, xm = element_index.xm,
00541            ys = element_index.ys, ym = element_index.ym;
00542   for (i=xs; i<xs+xm; i++) {
00543     for (j=ys; j<ys+ym; j++) {
00544       // Values of the solution at the nodes of the current element.
00545       PISMVector2    x[FEQuadrature::Nk];
00546 
00547       // Element-local Jacobian matrix (there are FEQuadrature::Nk vector valued degrees 
00548       // of freedom per elment, for a total of (2*FEQuadrature::Nk)*(2*FEQuadrature::Nk) = 16
00549       // entries in the local Jacobian.
00550       PetscReal      K[(2*FEQuadrature::Nk)*(2*FEQuadrature::Nk)];
00551 
00552       // Index into the coefficient storage array.
00553       const PetscInt ij = element_index.flatten(i,j);
00554       
00555       // Initialize the map from global to local degrees of freedom for this element.
00556       dofmap.reset(i,j,grid);
00557 
00558       // Obtain the value of the solution at the adjacent nodes to the element.
00559       dofmap.extractLocalDOFs(i,j,xg,x);
00560 
00561       // These values now need to be adjusted if some nodes in the element have
00562       // Dirichlet data.
00563       if(bc_locations && vel_bc) {
00564         dofmap.extractLocalDOFs(i,j,bc_mask,local_bc_mask);
00565         FixDirichletValues(local_bc_mask,BC_vel,x,dofmap);
00566       }
00567 
00568       // Compute the values of the solution at the quadrature points.
00569       quadrature.computeTrialFunctionValues(x,w,Dw);
00570 
00571       // Build the element-local Jacobian.
00572       ierr = PetscMemzero(K,sizeof(K));CHKERRQ(ierr);
00573       for (PetscInt q=0; q<FEQuadrature::Nq; q++) {
00574 
00575         // Shorthand for values and derivatives of the solution at the single quadrature point.
00576         PISMVector2 &wq = w[q];
00577         PetscReal *Dwq = Dw[q];
00578 
00579         // Coefficients evaluated at the single quadrature point.
00580         const FEStoreNode *feS = &feStore[ij*4+q];
00581         const PetscReal    jw  = JxW[q];
00582         PetscReal nuH,dNuH,beta,dbeta;
00583         ierr = PointwiseNuHAndBeta(feS,&wq,Dwq,&nuH,&dNuH,&beta,&dbeta);CHKERRQ(ierr);
00584 
00585         for (PetscInt k=0; k<4; k++) {   // Test functions
00586           for (PetscInt l=0; l<4; l++) { // Trial functions
00587 
00588             // FIXME (DAM 2/28/11) The following computations could be a little better documented.
00589             const FEFunctionGerm &test_qk=test[q][k];
00590             const FEFunctionGerm &test_ql=test[q][l];
00591 
00592             const PetscReal ht = test_qk.val,h = test_ql.val,
00593                   dxt = test_qk.dx, dyt = test_qk.dy,
00594                   dx = test_ql.dx, dy = test_ql.dy,
00595 
00596             // Cross terms appearing with beta'
00597             bvx = ht*wq.u,bvy = ht*wq.v,bux = wq.u*h,buy = wq.v*h,
00598             // Cross terms appearing with nuH'
00599             cvx = dxt*(2*Dwq[0]+Dwq[1]) + dyt*Dwq[2],
00600             cvy = dyt*(2*Dwq[1]+Dwq[0]) + dxt*Dwq[2],
00601             cux = (2*Dwq[0]+Dwq[1])*dx + Dwq[2]*dy,
00602             cuy = (2*Dwq[1]+Dwq[0])*dy + Dwq[2]*dx;
00603             
00604             if(nuH==0)
00605             {
00606               verbPrintf(1,grid.com,"nuh=0 i %d j %d q %d k %d\n",i,j,q,k);
00607             }
00608             // u-u coupling
00609             K[k*16+l*2]     += jw*(beta*ht*h + dbeta*bvx*bux + nuH*(2*dxt*dx + dyt*0.5*dy) + dNuH*cvx*cux);
00610             // u-v coupling
00611             K[k*16+l*2+1]   += jw*(dbeta*bvx*buy + nuH*(0.5*dyt*dx + dxt*dy) + dNuH*cvx*cuy);
00612             // v-u coupling
00613             K[k*16+8+l*2]   += jw*(dbeta*bvy*bux + nuH*(0.5*dxt*dy + dyt*dx) + dNuH*cvy*cux);
00614             // v-v coupling
00615             K[k*16+8+l*2+1] += jw*(beta*ht*h + dbeta*bvy*buy + nuH*(2*dyt*dy + dxt*0.5*dx) + dNuH*cvy*cuy);
00616           } // l
00617         } // k
00618       } // q
00619       ierr = dofmap.addLocalJacobianBlock(K,Jac);
00620     } // j
00621   } // i
00622   
00623   
00624   // Until now, the rows and columns correspoinding to Dirichlet data have not been set.  We now 
00625   // put an identity block in for these unknowns.  Note that because we have takes steps to not touching these 
00626   // columns previously, the symmetry of the Jacobian matrix is preserved.
00627   if (bc_locations && vel_bc) {
00628     for (i=grid.xs; i<grid.xs+grid.xm; i++) {
00629       for (j=grid.ys; j<grid.ys+grid.ym; j++) {
00630         if (bc_locations->as_int(i,j) == 1) {
00631           const PetscReal ident[4] = {dirichletScale,0,0,dirichletScale};
00632           MatStencil row;
00633           // FIXME: Transpose shows up here!
00634           row.j = i; row.i = j;
00635           ierr = MatSetValuesBlockedStencil(Jac,1,&row,1,&row,ident,ADD_VALUES);CHKERRQ(ierr);
00636         }
00637       }
00638     }
00639   }
00640 
00641   if(bc_locations) {
00642     ierr = bc_locations->end_access();CHKERRQ(ierr);
00643   }
00644   if(vel_bc) {
00645     ierr = vel_bc->end_access(); CHKERRQ(ierr);
00646   }
00647   
00648   ierr = MatAssemblyBegin(Jac,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
00649   ierr = MatAssemblyEnd(Jac,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
00650 
00651   PetscTruth monitor_jacobian;
00652   ierr = PetscOptionsHasName(NULL,"-ssa_monitor_jacobian",&monitor_jacobian);CHKERRQ(ierr);
00653   if (monitor_jacobian) {
00654     PetscViewer    viewer;
00655     
00656     char           file_name[PETSC_MAX_PATH_LEN];
00657     PetscInt iter;
00658     ierr = SNESGetIterationNumber(snes,&iter);
00659     snprintf(file_name,  PETSC_MAX_PATH_LEN, "PISM_SSAFEM_J%d.m",iter);
00660 
00661       ierr = verbPrintf(2, grid.com, 
00662                  "writing Matlab-readable file for SSAFEM system A xsoln = rhs to file `%s' ...\n",
00663                  file_name); CHKERRQ(ierr);
00664       ierr = PetscViewerCreate(grid.com, &viewer);CHKERRQ(ierr);
00665       ierr = PetscViewerSetType(viewer, PETSC_VIEWER_ASCII);CHKERRQ(ierr);
00666       ierr = PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);CHKERRQ(ierr);
00667       ierr = PetscViewerFileSetName(viewer, file_name);CHKERRQ(ierr);
00668       
00669       ierr = PetscObjectSetName((PetscObject) Jac,"A"); CHKERRQ(ierr);
00670       ierr = MatView(Jac, viewer);CHKERRQ(ierr);
00671   }
00672 
00673   ierr = MatSetOption(Jac,MAT_NEW_NONZERO_LOCATION_ERR,PETSC_TRUE);CHKERRQ(ierr);
00674 
00675 
00676   PetscFunctionReturn(0);
00677 }
00678 
00680 PetscErrorCode SSAFEFunction(DALocalInfo *info,
00681                              const PISMVector2 **xg, PISMVector2 **yg,
00682                              SSAFEM_SNESCallbackData *fe)
00683 {
00684   return fe->ssa->compute_local_function(info,xg,yg);
00685 }
00686 
00687 
00688 PetscErrorCode SSAFEJacobian(DALocalInfo *info,
00689                              const PISMVector2 **xg, Mat J,
00690                              SSAFEM_SNESCallbackData *fe)
00691 {
00692   return fe->ssa->compute_local_jacobian(info,xg,J);
00693 }
00694 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines