tFETools.cc - pism - [fork] customized build of PISM, the parallel ice sheet model (tillflux branch)
HTML git clone git://src.adamsgaard.dk/pism
DIR Log
DIR Files
DIR Refs
DIR LICENSE
---
tFETools.cc (25284B)
---
1 // Copyright (C) 2009--2011, 2013, 2014, 2015, 2016, 2017, 2018 Jed Brown and Ed Bueler and Constantine Khroulev and David Maxwell
2 //
3 // This file is part of PISM.
4 //
5 // PISM is free software; you can redistribute it and/or modify it under the
6 // terms of the GNU General Public License as published by the Free Software
7 // Foundation; either version 3 of the License, or (at your option) any later
8 // version.
9 //
10 // PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with PISM; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 // Utility functions used by the SSAFEM code.
20
21 #include <cassert> // assert
22 #include <cstring> // memset
23 #include <cstdlib> // malloc, free
24
25 #include "FETools.hh"
26 #include "pism/util/IceGrid.hh"
27 #include "pism/util/iceModelVec.hh"
28 #include "pism/util/pism_utilities.hh"
29 #include "pism/util/Logger.hh"
30
31 #include "pism/util/error_handling.hh"
32
33 namespace pism {
34
35 //! FEM (Finite Element Method) utilities
36 namespace fem {
37
38 namespace q0 {
39 /*!
40 * Piecewise-constant shape functions.
41 */
42 Germ chi(unsigned int k, const QuadPoint &pt) {
43 assert(k < q0::n_chi);
44
45 Germ result;
46
47 if ((k == 0 and pt.xi <= 0.0 and pt.eta <= 0.0) or
48 (k == 1 and pt.xi > 0.0 and pt.eta <= 0.0) or
49 (k == 2 and pt.xi > 0.0 and pt.eta > 0.0) or
50 (k == 3 and pt.xi <= 0.0 and pt.eta > 0.0)) {
51 result.val = 1.0;
52 } else {
53 result.val = 0.0;
54 }
55
56 result.dx = 0.0;
57 result.dy = 0.0;
58
59 return result;
60 }
61 } // end of namespace q0
62
63 namespace q1 {
64
65 //! Q1 basis functions on the reference element with nodes (-1,-1), (1,-1), (1,1), (-1,1).
66 Germ chi(unsigned int k, const QuadPoint &pt) {
67 assert(k < q1::n_chi);
68
69 const double xi[] = {-1.0, 1.0, 1.0, -1.0};
70 const double eta[] = {-1.0, -1.0, 1.0, 1.0};
71
72 Germ result;
73
74 result.val = 0.25 * (1.0 + xi[k] * pt.xi) * (1.0 + eta[k] * pt.eta);
75 result.dx = 0.25 * xi[k] * (1.0 + eta[k] * pt.eta);
76 result.dy = 0.25 * eta[k] * (1.0 + xi[k] * pt.xi);
77
78 return result;
79 }
80
81
82 const Vector2* outward_normals() {
83 static const Vector2 n[] = {Vector2( 0.0, -1.0), // south
84 Vector2( 1.0, 0.0), // east
85 Vector2( 0.0, 1.0), // north
86 Vector2(-1.0, 0.0)}; // west
87 return n;
88 }
89
90 } // end of namespace q1
91
92 namespace p1 {
93
94 //! P1 basis functions on the reference element with nodes (0,0), (1,0), (0,1).
95 Germ chi(unsigned int k, const QuadPoint &pt) {
96 assert(k < q1::n_chi);
97 Germ result;
98
99 switch (k) {
100 case 0:
101 result.val = 1.0 - pt.xi - pt.eta;
102 result.dx = -1.0;
103 result.dy = -1.0;
104 break;
105 case 1:
106 result.val = pt.xi;
107 result.dx = 1.0;
108 result.dy = 0.0;
109 break;
110 case 2:
111 result.val = pt.eta;
112 result.dx = 0.0;
113 result.dy = 1.0;
114 break;
115 default: // the fourth (dummy) basis function
116 result.val = 0.0;
117 result.dx = 0.0;
118 result.dy = 0.0;
119 }
120 return result;
121 }
122 } // end of namespace p1
123
124
125 ElementIterator::ElementIterator(const IceGrid &g) {
126 // Start by assuming ghost elements exist in all directions.
127 // Elements are indexed by their lower left vertex. If there is a ghost
128 // element on the right, its i-index will be the same as the maximum
129 // i-index of a non-ghost vertex in the local grid.
130 xs = g.xs() - 1; // Start at ghost to the left.
131 int xf = g.xs() + g.xm() - 1; // End at ghost to the right.
132 ys = g.ys() - 1; // Start at ghost at the bottom.
133 int yf = g.ys() + g.ym() - 1; // End at ghost at the top.
134
135 lxs = g.xs();
136 int lxf = lxs + g.xm() - 1;
137 lys = g.ys();
138 int lyf = lys + g.ym() - 1;
139
140 // Now correct if needed. The only way there will not be ghosts is if the
141 // grid is not periodic and we are up against the grid boundary.
142
143 if (!(g.periodicity() & X_PERIODIC)) {
144 // Leftmost element has x-index 0.
145 if (xs < 0) {
146 xs = 0;
147 }
148 // Rightmost vertex has index g.Mx-1, so the rightmost element has index g.Mx-2
149 if (xf > (int)g.Mx() - 2) {
150 xf = g.Mx() - 2;
151 lxf = g.Mx() - 2;
152 }
153 }
154
155 if (!(g.periodicity() & Y_PERIODIC)) {
156 // Bottom element has y-index 0.
157 if (ys < 0) {
158 ys = 0;
159 }
160 // Topmost vertex has index g.My - 1, so the topmost element has index g.My - 2
161 if (yf > (int)g.My() - 2) {
162 yf = g.My() - 2;
163 lyf = g.My() - 2;
164 }
165 }
166
167 // Tally up the number of elements in each direction
168 xm = xf - xs + 1;
169 ym = yf - ys + 1;
170 lxm = lxf - lxs + 1;
171 lym = lyf - lys + 1;
172 }
173
174 ElementMap::ElementMap(const IceGrid &grid)
175 : m_grid(grid) {
176 reset(0, 0);
177 }
178
179 ElementMap::~ElementMap() {
180 // empty
181 }
182
183 void ElementMap::nodal_values(const IceModelVec2Int &x_global, int *result) const {
184 for (unsigned int k = 0; k < q1::n_chi; ++k) {
185 const int
186 ii = m_i + m_i_offset[k],
187 jj = m_j + m_j_offset[k];
188 result[k] = x_global.as_int(ii, jj);
189 }
190 }
191
192 /*!@brief Initialize the ElementMap to element (`i`, `j`) for the purposes of inserting into
193 global residual and Jacobian arrays. */
194 void ElementMap::reset(int i, int j) {
195 m_i = i;
196 m_j = j;
197
198 for (unsigned int k = 0; k < fem::q1::n_chi; ++k) {
199 m_col[k].i = i + m_i_offset[k];
200 m_col[k].j = j + m_j_offset[k];
201 m_col[k].k = 0;
202
203 m_row[k].i = m_col[k].i;
204 m_row[k].j = m_col[k].j;
205 m_row[k].k = m_col[k].k;
206 }
207
208 // We do not ever sum into rows that are not owned by the local rank.
209 for (unsigned int k = 0; k < fem::q1::n_chi; k++) {
210 int pism_i = m_row[k].i, pism_j = m_row[k].j;
211 if (pism_i < m_grid.xs() || m_grid.xs() + m_grid.xm() - 1 < pism_i ||
212 pism_j < m_grid.ys() || m_grid.ys() + m_grid.ym() - 1 < pism_j) {
213 mark_row_invalid(k);
214 }
215 }
216 }
217
218 /*!@brief Mark that the row corresponding to local degree of freedom `k` should not be updated
219 when inserting into the global residual or Jacobian arrays. */
220 void ElementMap::mark_row_invalid(int k) {
221 m_row[k].i = m_row[k].j = m_invalid_dof;
222 // We are solving a 2D system, so MatStencil::k is not used. Here we
223 // use it to mark invalid rows.
224 m_row[k].k = 1;
225 }
226
227 /*!@brief Mark that the column corresponding to local degree of freedom `k` should not be updated
228 when inserting into the global Jacobian arrays. */
229 void ElementMap::mark_col_invalid(int k) {
230 m_col[k].i = m_col[k].j = m_invalid_dof;
231 // We are solving a 2D system, so MatStencil::k is not used. Here we
232 // use it to mark invalid columns.
233 m_col[k].k = 1;
234 }
235
236 //! Add the contributions of an element-local Jacobian to the global Jacobian vector.
237 /*! The element-local Jacobian should be given as a row-major array of
238 * Nk*Nk values in the scalar case or (2Nk)*(2Nk) values in the
239 * vector valued case.
240 *
241 * Note that MatSetValuesBlockedStencil ignores negative indexes, so
242 * values in K corresponding to locations marked using
243 * mark_row_invalid() and mark_col_invalid() are ignored. (Just as they
244 * should be.)
245 */
246 void ElementMap::add_contribution(const double *K, Mat J) const {
247 PetscErrorCode ierr = MatSetValuesBlockedStencil(J,
248 fem::q1::n_chi, m_row,
249 fem::q1::n_chi, m_col,
250 K, ADD_VALUES);
251 PISM_CHK(ierr, "MatSetValuesBlockedStencil");
252 }
253
254 const int ElementMap::m_i_offset[4] = {0, 1, 1, 0};
255 const int ElementMap::m_j_offset[4] = {0, 0, 1, 1};
256
257 Quadrature::Quadrature(unsigned int N)
258 : m_Nq(N) {
259
260 m_W = (double*) malloc(m_Nq * sizeof(double));
261 if (m_W == NULL) {
262 throw std::runtime_error("Failed to allocate a Quadrature instance");
263 }
264
265 m_germs = (Germs*) malloc(m_Nq * q1::n_chi * sizeof(Germ));
266 if (m_germs == NULL) {
267 free(m_W);
268 throw std::runtime_error("Failed to allocate a Quadrature instance");
269 }
270 }
271
272 Germ Quadrature::test_function_values(unsigned int q, unsigned int k) const {
273 return m_germs[q][k];
274 }
275
276 double Quadrature::weights(unsigned int q) const {
277 return m_W[q];
278 }
279
280 UniformQxQuadrature::UniformQxQuadrature(unsigned int size, double dx, double dy, double scaling)
281 : Quadrature(size) {
282 // We use uniform Cartesian coordinates, so the Jacobian is constant and diagonal on every
283 // element.
284 //
285 // Note that the reference element is [-1,1]^2, hence the extra factor of 1/2.
286 m_J[0][0] = 0.5 * dx / scaling;
287 m_J[0][1] = 0.0;
288 m_J[1][0] = 0.0;
289 m_J[1][1] = 0.5 * dy / scaling;
290 }
291
292 Quadrature::~Quadrature() {
293 free(m_W);
294 m_W = NULL;
295
296 free(m_germs);
297 m_germs = NULL;
298 }
299
300 //! Build quadrature points and weights for a tensor product quadrature based on a 1D quadrature
301 //! rule. Uses the same 1D quadrature in both directions.
302 /**
303 @param[in] n 1D quadrature size (the resulting quadrature has size n*n)
304 @param[in] points1 1D quadrature points
305 @param[in] weights1 1D quadrature weights
306 @param[out] points resulting 2D quadrature points
307 @param[out] weights resulting 2D quadrature weights
308 */
309 static void tensor_product_quadrature(unsigned int n,
310 const double *points1,
311 const double *weights1,
312 QuadPoint *points,
313 double *weights) {
314 unsigned int q = 0;
315 for (unsigned int j = 0; j < n; ++j) {
316 for (unsigned int i = 0; i < n; ++i) {
317 points[q].xi = points1[i];
318 points[q].eta = points1[j];
319
320 weights[q] = weights1[i] * weights1[j];
321
322 ++q;
323 }
324 }
325 }
326
327 //! Determinant of a square matrix of size 2.
328 static double determinant(const double J[2][2]) {
329 return J[0][0] * J[1][1] - J[1][0] * J[0][1];
330 }
331
332 //! Compute the inverse of a two by two matrix.
333 static void invert(const double A[2][2], double A_inv[2][2]) {
334 const double det_A = determinant(A);
335
336 assert(det_A != 0.0);
337
338 A_inv[0][0] = A[1][1] / det_A;
339 A_inv[0][1] = -A[0][1] / det_A;
340 A_inv[1][0] = -A[1][0] / det_A;
341 A_inv[1][1] = A[0][0] / det_A;
342 }
343
344 //! Compute derivatives with respect to x,y using J^{-1} and derivatives with respect to xi, eta.
345 static Germ apply_jacobian_inverse(const double J_inv[2][2], const Germ &f) {
346 Germ result;
347 result.val = f.val;
348 result.dx = f.dx * J_inv[0][0] + f.dy * J_inv[0][1];
349 result.dy = f.dx * J_inv[1][0] + f.dy * J_inv[1][1];
350 return result;
351 }
352
353 //! Two-by-two Gaussian quadrature on a rectangle.
354 Q1Quadrature4::Q1Quadrature4(double dx, double dy, double L)
355 : UniformQxQuadrature(m_size, dx, dy, L) {
356
357 // coordinates and weights of the 2-point 1D Gaussian quadrature
358 const double
359 A = 1.0 / sqrt(3.0),
360 points2[2] = {-A, A},
361 weights2[2] = {1.0, 1.0};
362
363 QuadPoint points[m_size];
364 double W[m_size];
365
366 tensor_product_quadrature(2, points2, weights2, points, W);
367
368 initialize(q1::chi, q1::n_chi, points, W);
369 }
370
371 Q1Quadrature9::Q1Quadrature9(double dx, double dy, double L)
372 : UniformQxQuadrature(m_size, dx, dy, L) {
373 // The quadrature points on the reference square.
374
375 const double
376 A = 0.0,
377 B = sqrt(0.6),
378 points3[3] = {-B, A, B};
379
380 const double
381 w1 = 5.0 / 9.0,
382 w2 = 8.0 / 9.0,
383 weights3[3] = {w1, w2, w1};
384
385 QuadPoint points[m_size];
386 double W[m_size];
387
388 tensor_product_quadrature(3, points3, weights3, points, W);
389
390 initialize(q1::chi, q1::n_chi, points, W);
391 }
392
393 Q1Quadrature16::Q1Quadrature16(double dx, double dy, double L)
394 : UniformQxQuadrature(m_size, dx, dy, L) {
395 // The quadrature points on the reference square.
396 const double
397 A = sqrt(3.0 / 7.0 - (2.0 / 7.0) * sqrt(6.0 / 5.0)), // smaller magnitude
398 B = sqrt(3.0 / 7.0 + (2.0 / 7.0) * sqrt(6.0 / 5.0)), // larger magnitude
399 points4[4] = {-B, -A, A, B};
400
401 // The weights w_i for Gaussian quadrature on the reference element with these quadrature points
402 const double
403 w1 = (18.0 + sqrt(30.0)) / 36.0, // larger
404 w2 = (18.0 - sqrt(30.0)) / 36.0, // smaller
405 weights4[4] = {w2, w1, w1, w2};
406
407 QuadPoint points[m_size];
408 double W[m_size];
409
410 tensor_product_quadrature(4, points4, weights4, points, W);
411
412 initialize(q1::chi, q1::n_chi, points, W);
413 }
414
415
416 //! @brief 1e4-point (100x100) uniform (*not* Gaussian) quadrature for integrating discontinuous
417 //! functions.
418 Q0Quadrature1e4::Q0Quadrature1e4(double dx, double dy, double L)
419 : UniformQxQuadrature(m_size, dx, dy, L) {
420 assert(m_size_1d * m_size_1d == m_size);
421
422 double xi[m_size_1d], w[m_size_1d];
423 const double dxi = 2.0 / m_size_1d;
424 for (unsigned int k = 0; k < m_size_1d; ++k) {
425 xi[k] = -1.0 + dxi*(k + 0.5);
426 w[k] = 2.0 / m_size_1d;
427 }
428
429 QuadPoint points[m_size];
430 double W[m_size];
431
432 tensor_product_quadrature(m_size_1d, xi, w, points, W);
433
434 initialize(q0::chi, q0::n_chi, points, W);
435 }
436
437 //! @brief 1e4-point (100x100) uniform (*not* Gaussian) quadrature for integrating discontinuous
438 //! functions.
439 Q1Quadrature1e4::Q1Quadrature1e4(double dx, double dy, double L)
440 : UniformQxQuadrature(m_size, dx, dy, L) {
441 assert(m_size_1d * m_size_1d == m_size);
442
443 double xi[m_size_1d], w[m_size_1d];
444 const double dxi = 2.0 / m_size_1d;
445 for (unsigned int k = 0; k < m_size_1d; ++k) {
446 xi[k] = -1.0 + dxi*(k + 0.5);
447 w[k] = 2.0 / m_size_1d;
448 }
449
450 QuadPoint points[m_size];
451 double W[m_size];
452
453 tensor_product_quadrature(m_size_1d, xi, w, points, W);
454
455 initialize(q1::chi, q1::n_chi, points, W);
456 }
457
458 //! Initialize shape function values and weights of a 2D quadrature.
459 /** Assumes that the Jacobian does not depend on coordinates of the current quadrature point.
460 */
461 void Quadrature::initialize(ShapeFunction2 f,
462 unsigned int n_chi,
463 const QuadPoint *points,
464 const double *W) {
465
466 double J_inv[2][2];
467 invert(m_J, J_inv);
468
469 for (unsigned int q = 0; q < m_Nq; q++) {
470 for (unsigned int k = 0; k < n_chi; k++) {
471 m_germs[q][k] = apply_jacobian_inverse(J_inv, f(k, points[q]));
472 }
473 }
474
475 const double J_det = determinant(m_J);
476 for (unsigned int q = 0; q < m_Nq; q++) {
477 m_W[q] = J_det * W[q];
478 }
479 }
480
481 /** Create a quadrature on a P1 element aligned with coordinate axes and embedded in a Q1 element.
482
483 There are four possible P1 elements in a Q1 element. The argument `N` specifies which one,
484 numbering them by the node at the right angle in the "reference" element (0,0) -- (1,0) --
485 (0,1).
486 */
487 P1Quadrature::P1Quadrature(unsigned int size, unsigned int N,
488 double dx, double dy, double L)
489 : Quadrature(size) {
490
491 // Compute the Jacobian. The nodes of the selected triangle are
492 // numbered, the unused node is marked with an "X". In all triangles
493 // nodes are numbered in the counter-clockwise direction.
494 switch (N) {
495 case 0:
496 /*
497 2------X
498 | |
499 | |
500 0------1
501 */
502 m_J[0][0] = dx / L;
503 m_J[0][1] = 0.0;
504 m_J[1][0] = 0.0;
505 m_J[1][1] = dy / L;
506 break;
507 case 1:
508 /*
509 X------1
510 | |
511 | |
512 2------0
513 */
514 m_J[0][0] = 0.0;
515 m_J[0][1] = dy / L;
516 m_J[1][0] = -dx / L;
517 m_J[1][1] = 0.0;
518 break;
519 case 2:
520 /*
521 1------0
522 | |
523 | |
524 X------2
525 */
526 m_J[0][0] = -dx / L;
527 m_J[0][1] = 0.0;
528 m_J[1][0] = 0.0;
529 m_J[1][1] = -dy / L;
530 break;
531 case 3:
532 /*
533 0------2
534 | |
535 | |
536 1------X
537 */
538 m_J[0][0] = 0.0;
539 m_J[0][1] = -dy / L;
540 m_J[1][0] = dx / L;
541 m_J[1][1] = 0.0;
542 break;
543 }
544 }
545
546 //! Permute shape functions stored in `f` *in place*.
547 static void permute(const unsigned int p[q1::n_chi], Germ f[q1::n_chi]) {
548 Germ tmp[q1::n_chi];
549
550 // store permuted values to avoid overwriting f too soon
551 for (unsigned int k = 0; k < q1::n_chi; ++k) {
552 tmp[k] = f[p[k]];
553 }
554
555 // copy back into f
556 for (unsigned int k = 0; k < q1::n_chi; ++k) {
557 f[k] = tmp[k];
558 }
559 }
560
561 P1Quadrature3::P1Quadrature3(unsigned int N,
562 double dx, double dy, double L)
563 : P1Quadrature(m_size, N, dx, dy, L) {
564
565 const double
566 one_over_six = 1.0 / 6.0,
567 two_over_three = 2.0 / 3.0;
568
569 const QuadPoint points[3] = {{two_over_three, one_over_six},
570 {one_over_six, two_over_three},
571 {one_over_six, one_over_six}};
572
573 const double W[3] = {one_over_six, one_over_six, one_over_six};
574
575 // Note that we use q1::n_chi here.
576 initialize(p1::chi, q1::n_chi, points, W);
577
578 // Permute shape function values according to N, the index of this triangle in the Q1 element.
579 const unsigned int
580 X = 3, // index of the dummy shape function
581 p[4][4] = {{0, 1, X, 2},
582 {2, 0, 1, X},
583 {X, 2, 0, 1},
584 {1, X, 2, 0}};
585 for (unsigned int q = 0; q < m_size; ++q) {
586 permute(p[N], m_germs[q]);
587 }
588 }
589
590 DirichletData::DirichletData()
591 : m_indices(NULL), m_weight(1.0) {
592 for (unsigned int k = 0; k < q1::n_chi; ++k) {
593 m_indices_e[k] = 0;
594 }
595 }
596
597 DirichletData::~DirichletData() {
598 finish(NULL);
599 }
600
601 void DirichletData::init(const IceModelVec2Int *indices,
602 const IceModelVec *values,
603 double weight) {
604 m_weight = weight;
605
606 if (indices != NULL) {
607 indices->begin_access();
608 m_indices = indices;
609 }
610
611 if (values != NULL) {
612 values->begin_access();
613 }
614 }
615
616 void DirichletData::finish(const IceModelVec *values) {
617 if (m_indices != NULL) {
618 MPI_Comm com = m_indices->grid()->ctx()->com();
619 try {
620 m_indices->end_access();
621 m_indices = NULL;
622 } catch (...) {
623 handle_fatal_errors(com);
624 }
625 }
626
627 if (values != NULL) {
628 MPI_Comm com = values->grid()->ctx()->com();
629 try {
630 values->end_access();
631 } catch (...) {
632 handle_fatal_errors(com);
633 }
634 }
635 }
636
637 //! @brief Constrain `element`, i.e. ensure that quadratures do not contribute to Dirichlet nodes by marking corresponding rows and columns as "invalid".
638 void DirichletData::constrain(ElementMap &element) {
639 element.nodal_values(*m_indices, m_indices_e);
640 for (unsigned int k = 0; k < q1::n_chi; k++) {
641 if (m_indices_e[k] > 0.5) { // Dirichlet node
642 // Mark any kind of Dirichlet node as not to be touched
643 element.mark_row_invalid(k);
644 element.mark_col_invalid(k);
645 }
646 }
647 }
648
649 // Scalar version
650
651 DirichletData_Scalar::DirichletData_Scalar(const IceModelVec2Int *indices,
652 const IceModelVec2S *values,
653 double weight)
654 : m_values(values) {
655 init(indices, m_values, weight);
656 }
657
658 void DirichletData_Scalar::enforce(const ElementMap &element, double* x_nodal) {
659 assert(m_values != NULL);
660
661 element.nodal_values(*m_indices, m_indices_e);
662 for (unsigned int k = 0; k < q1::n_chi; k++) {
663 if (m_indices_e[k] > 0.5) { // Dirichlet node
664 int i = 0, j = 0;
665 element.local_to_global(k, i, j);
666 x_nodal[k] = (*m_values)(i, j);
667 }
668 }
669 }
670
671 void DirichletData_Scalar::enforce_homogeneous(const ElementMap &element, double* x_nodal) {
672 element.nodal_values(*m_indices, m_indices_e);
673 for (unsigned int k = 0; k < q1::n_chi; k++) {
674 if (m_indices_e[k] > 0.5) { // Dirichlet node
675 x_nodal[k] = 0.;
676 }
677 }
678 }
679
680 void DirichletData_Scalar::fix_residual(double const *const *const x_global, double **r_global) {
681 assert(m_values != NULL);
682
683 const IceGrid &grid = *m_indices->grid();
684
685 // For each node that we own:
686 for (Points p(grid); p; p.next()) {
687 const int i = p.i(), j = p.j();
688
689 if ((*m_indices)(i, j) > 0.5) {
690 // Enforce explicit dirichlet data.
691 r_global[j][i] = m_weight * (x_global[j][i] - (*m_values)(i,j));
692 }
693 }
694 }
695
696 void DirichletData_Scalar::fix_residual_homogeneous(double **r_global) {
697 const IceGrid &grid = *m_indices->grid();
698
699 // For each node that we own:
700 for (Points p(grid); p; p.next()) {
701 const int i = p.i(), j = p.j();
702
703 if ((*m_indices)(i, j) > 0.5) {
704 // Enforce explicit dirichlet data.
705 r_global[j][i] = 0.0;
706 }
707 }
708 }
709
710 void DirichletData_Scalar::fix_jacobian(Mat J) {
711 const IceGrid &grid = *m_indices->grid();
712
713 // Until now, the rows and columns correspoinding to Dirichlet data
714 // have not been set. We now put an identity block in for these
715 // unknowns. Note that because we have takes steps to not touching
716 // these columns previously, the symmetry of the Jacobian matrix is
717 // preserved.
718
719 const double identity = m_weight;
720 ParallelSection loop(grid.com);
721 try {
722 for (Points p(grid); p; p.next()) {
723 const int i = p.i(), j = p.j();
724
725 if ((*m_indices)(i, j) > 0.5) {
726 MatStencil row;
727 row.j = j;
728 row.i = i;
729 PetscErrorCode ierr = MatSetValuesBlockedStencil(J, 1, &row, 1, &row, &identity,
730 ADD_VALUES);
731 PISM_CHK(ierr, "MatSetValuesBlockedStencil"); // this may throw
732 }
733 }
734 } catch (...) {
735 loop.failed();
736 }
737 loop.check();
738 }
739
740 DirichletData_Scalar::~DirichletData_Scalar() {
741 finish(m_values);
742 m_values = NULL;
743 }
744
745 // Vector version
746
747 DirichletData_Vector::DirichletData_Vector(const IceModelVec2Int *indices,
748 const IceModelVec2V *values,
749 double weight)
750 : m_values(values) {
751 init(indices, m_values, weight);
752 }
753
754 void DirichletData_Vector::enforce(const ElementMap &element, Vector2* x_nodal) {
755 assert(m_values != NULL);
756
757 element.nodal_values(*m_indices, m_indices_e);
758 for (unsigned int k = 0; k < q1::n_chi; k++) {
759 if (m_indices_e[k] > 0.5) { // Dirichlet node
760 int i = 0, j = 0;
761 element.local_to_global(k, i, j);
762 x_nodal[k] = (*m_values)(i, j);
763 }
764 }
765 }
766
767 void DirichletData_Vector::enforce_homogeneous(const ElementMap &element, Vector2* x_nodal) {
768 element.nodal_values(*m_indices, m_indices_e);
769 for (unsigned int k = 0; k < q1::n_chi; k++) {
770 if (m_indices_e[k] > 0.5) { // Dirichlet node
771 x_nodal[k].u = 0.0;
772 x_nodal[k].v = 0.0;
773 }
774 }
775 }
776
777 void DirichletData_Vector::fix_residual(Vector2 const *const *const x_global, Vector2 **r_global) {
778 assert(m_values != NULL);
779
780 const IceGrid &grid = *m_indices->grid();
781
782 // For each node that we own:
783 for (Points p(grid); p; p.next()) {
784 const int i = p.i(), j = p.j();
785
786 if ((*m_indices)(i, j) > 0.5) {
787 // Enforce explicit dirichlet data.
788 r_global[j][i] = m_weight * (x_global[j][i] - (*m_values)(i, j));
789 }
790 }
791 }
792
793 void DirichletData_Vector::fix_residual_homogeneous(Vector2 **r_global) {
794 const IceGrid &grid = *m_indices->grid();
795
796 // For each node that we own:
797 for (Points p(grid); p; p.next()) {
798 const int i = p.i(), j = p.j();
799
800 if ((*m_indices)(i, j) > 0.5) {
801 // Enforce explicit dirichlet data.
802 r_global[j][i].u = 0.0;
803 r_global[j][i].v = 0.0;
804 }
805 }
806 }
807
808 void DirichletData_Vector::fix_jacobian(Mat J) {
809 const IceGrid &grid = *m_indices->grid();
810
811 // Until now, the rows and columns correspoinding to Dirichlet data
812 // have not been set. We now put an identity block in for these
813 // unknowns. Note that because we have takes steps to not touching
814 // these columns previously, the symmetry of the Jacobian matrix is
815 // preserved.
816
817 const double identity[4] = {m_weight, 0,
818 0, m_weight};
819 ParallelSection loop(grid.com);
820 try {
821 for (Points p(grid); p; p.next()) {
822 const int i = p.i(), j = p.j();
823
824 if ((*m_indices)(i, j) > 0.5) {
825 MatStencil row;
826 row.j = j;
827 row.i = i;
828 PetscErrorCode ierr = MatSetValuesBlockedStencil(J, 1, &row, 1, &row, identity,
829 ADD_VALUES);
830 PISM_CHK(ierr, "MatSetValuesBlockedStencil"); // this may throw
831 }
832 }
833 } catch (...) {
834 loop.failed();
835 }
836 loop.check();
837 }
838
839 DirichletData_Vector::~DirichletData_Vector() {
840 finish(m_values);
841 m_values = NULL;
842 }
843
844 BoundaryQuadrature2::BoundaryQuadrature2(double dx, double dy, double L) {
845
846 const double J[2][2] = {{0.5 * dx / L, 0.0},
847 {0.0, 0.5 * dy / L}};
848
849 // The inverse of the Jacobian.
850 double J_inv[2][2];
851 invert(J, J_inv);
852
853 // Note that all quadrature weights are 1.0 (and so they are implicitly included below).
854 //
855 // bottom
856 m_W[0] = J[0][0];
857 // right
858 m_W[1] = J[1][1];
859 // top
860 m_W[2] = J[0][0];
861 // left
862 m_W[3] = J[1][1];
863
864 const double C = 1.0 / sqrt(3);
865 const QuadPoint points[q1::n_sides][m_Nq] = {
866 {{ -C, -1.0}, { C, -1.0}}, // South
867 {{ 1.0, -C}, { 1.0, C}}, // East
868 {{ -C, 1.0}, { C, 1.0}}, // North
869 {{-1.0, -C}, {-1.0, C}} // West
870 };
871
872 memset(m_germs, 0, q1::n_sides*m_Nq*q1::n_chi*sizeof(Germ));
873
874 for (unsigned int side = 0; side < q1::n_sides; ++side) {
875 for (unsigned int q = 0; q < m_Nq; ++q) {
876 for (unsigned int k = 0; k < q1::n_chi; ++k) {
877 Germ phi = q1::chi(k, points[side][q]);
878
879 m_germs[side][q][k] = apply_jacobian_inverse(J_inv, phi);
880 }
881 }
882 }
883 }
884
885 } // end of namespace fem
886 } // end of namespace pism