|
PISM, A Parallel Ice Sheet Model stable 0.4.1779
|
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
1.7.3