block_preconditioner.cc
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2025 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
27
28namespace oomph
29{
30 /// Static boolean to allow block_matrix_test(...) to be run.
31 /// Defaults to false.
32 template<typename MATRIX>
34
35
36 //============================================================================
37 /// Determine the size of the matrix blocks and setup the
38 /// lookup schemes relating the global degrees of freedom with
39 /// their "blocks" and their indices (row/column numbers) in those
40 /// blocks.
41 /// The distributions of the preconditioner and the blocks are
42 /// automatically specified (and assumed to be uniform) at this
43 /// stage.
44 /// This method should be used if any block contains more than one
45 /// type of DOF. The argument vector dof_to_block_map should be of length
46 /// ndof. Each element should contain an integer indicating the block number
47 /// corresponding to that type of DOF.
48 //============================================================================
49 template<typename MATRIX>
52 {
53#ifdef PARANOID
54 // Subsidiary preconditioners don't really need the meshes
55 if (this->is_master_block_preconditioner())
56 {
57 std::ostringstream err_msg;
58 unsigned n = nmesh();
59 if (n == 0)
60 {
61 err_msg << "No meshes have been set for this block preconditioner!\n"
62 << "Set one with set_nmesh(...), set_mesh(...)" << std::endl;
63 throw OomphLibError(
65 for (unsigned m = 0; m < n; m++)
66 {
67 if (Mesh_pt[m] == 0)
68 {
69 err_msg << "The mesh pointer to mesh " << m << " is null!\n"
70 << "Set a non-null one with set_mesh(...)" << std::endl;
71 throw OomphLibError(
73 }
74 }
75 }
76 }
77#endif
78
79 // Create a copy of the vector input so that we can modify it below
81
82 if (is_subsidiary_block_preconditioner())
83 {
84#ifdef PARANOID
85 // Get the size of the Doftype_in_master_preconditioner_coarse.
87 Doftype_in_master_preconditioner_coarse.size();
88
89 // Check that the Doftype_in_master_preconditioner_coarse vector is not
90 // empty. This must be set (via the function
91 // turn_into_subsidiary_block_preconditioner) if this is a
92 // subsidiary block preconditioner.
94 {
95 std::ostringstream err_msg;
96 err_msg << "The mapping from the dof types of the master "
97 << "block preconditioner \n"
98 << "to the subsidiary block preconditioner is empty.\n"
99 << "Doftype_in_master_preconditioner_coarse.size() == 0 \n"
100 << "has turn_into_subsidiary_block_preconditioner(...)\n"
101 << "been called with the correct parameters?\n"
102 << std::endl;
103 throw OomphLibError(
105 }
106
107
108 // PARANOID checks for Doftype_coarsen_map_coarse
109 // This is also set in the function
110 // turn_into_subsidiary_block_preconditioner(...).
111 //
112 // The Doftype_coarsen_map_coarse vector must satisfy two conditions
113 // for it to be valid.
114 //
115 // 1) The dof type numbers in the dof_coarsen_map vector must be
116 // unique. For example, it does not make sense to have the vector
117 // [[0,1][1,2]] because the first inner vector says
118 // "treat dof types 0 and 1 as dof type 0" and the second inner vector
119 // says "treat dof type 1 and 2 as dof type 1", but dof type 1 is
120 // already being treated as dof type 0.
121 //
122 // 2) Every SUBSIDIARY dof type must be mapped to a dof type in the
123 // Doftype_coarsen_map_coarse vector.
124 // For example, if there are 5 dof types (passed down from the master
125 // block preconditioner), and this block subsidiary block
126 // preconditioner only deals with 3 dof types, then all 5 dof types
127 // must be mapped to a dof type in the subsidiary preconditioner. For
128 // example if the dof_map is [1,2,3,4,5], then the subsidiary block
129 // preconditioner knows that 5 dof types have been passed down. But if
130 // it only works with three dof types, we MUST have three inner vectors
131 // in the doftype_coarsen_map vector (which corresponds to dof types 0,
132 // 1 and 2), the union of the dof types in the three inner vectors must
133 // contain dof types 0, 1, 2, 3 and 4 exactly once. It cannot contain,
134 // say, 0, 1, 5, 7, 9, even though it passes the uniqueness check. We
135 // ensure this by two conditions:
136 //
137 // 2.1) The Doftype_coarsen_map_coarse vector must contain the same
138 // number of dof types as the dof_map vector.
139 // In other words, recall that Doftype_coarsen_map_coarse is a
140 // 2D vector, this must contain the same number of vectors as
141 // there are elements in the dof_to_block_map_in vector.
142 //
143 // 2.2) The maximum element in the doftype_coarsen_map_coarse vector
144 // is the length of the dof_map vector minus 1.
145
146 // A set is deal for checking the above three conditions, we shall insert
147 // all the elements in the doftype_coarsen_map_coarse vector into this
148 // set.
149 std::set<unsigned> doftype_map_set;
150
151 // Condition (1): Check for uniqueness by inserting all the values of
152 // Doftype_coarsen_map_coarse into a set.
154 Doftype_coarsen_map_coarse.size();
155
156 // Loop through the outer vector of Doftype_coarsen_map_coarse
157 // then loop through the inner vectors and attempt to insert each
158 // element of Doftype_coarsen_map_coarse into doftype_map_set.
159 //
160 // The inner for loop will throw an error if we cannot insert the
161 // element, this means that it is already inserted and thus not unique.
162 for (unsigned i = 0; i < para_doftype_coarsen_map_coarse_size; i++)
163 {
164 // Loop through the inner vector
166 Doftype_coarsen_map_coarse[i].size();
167 for (unsigned j = 0; j < para_doftype_coarsen_map_coarse_i_size; j++)
168 {
169 // Attempt to insert all the values of the inner vector into a set.
170 std::pair<std::set<unsigned>::iterator, bool> doftype_map_ret =
171 doftype_map_set.insert(Doftype_coarsen_map_coarse[i][j]);
172
173 if (!doftype_map_ret.second)
174 {
175 std::ostringstream err_msg;
176 err_msg << "Error: the doftype number "
177 << Doftype_coarsen_map_coarse[i][j]
178 << " is already inserted." << std::endl;
179 throw OomphLibError(
181 }
182 }
183 }
184
185 // Condition (2.1): Check that the doftype_map_set describes as many
186 // values as doftype_in_master_preconditioner_coarse. I.e. if dof_map
187 // contains 5 dof types, then the doftype_coarsen_map_coarse vector must
188 // also contain 5 dof types.
191 {
192 std::ostringstream err_msg;
193 err_msg << "The size of doftype_in_master_preconditioner_coarse "
194 << "must be the same as the total\n"
195 << "number of values in the doftype_coarsen_map_coarse vector."
196 << std::endl;
197 throw OomphLibError(
199 }
200
201 // Condition (2.2): Check that the maximum element in the
202 // doftype_coarsen_map_coarse vector is the length of the
203 // doftype_in_master_preconditioner_coarse minus 1.
207 *doftype_map_set.rbegin())
208 {
209 std::ostringstream err_msg;
210 err_msg << "The maximum dof type number in the "
211 << "doftype_coarsen_map vector must be "
213 << std::endl;
214 throw OomphLibError(
216 }
217#endif
218
219 // Set the mapping from the master preconditioner DOF types to the
220 // subsidiary preconditioner DOF types.
221 //
222 // IMPORTANT: Since DOF types may be coarsened in the master block
223 // preconditioner, this may no longer reflect the actual underlying dof
224 // types. We must get the actual underlying dof types for the
225 // block_setup(...) function to work properly so all the look up schemes
226 // for this (subsidiary) block preconditioner is correct and works
227 // properly, this is for backwards compatibility purposes and to make sure
228 // Richard Muddle's still works at this (subsidiary) level, although it
229 // may not be used.
230 //
231 // If we do not want to make it backwards compatible, we may as well
232 // kill the block_setup(...) for subsidiary block preconditioners -
233 // but other thing may break. Do it at your own risk (take time to
234 // fully understand the whole block preconditioning framework code).
235
236 // Create the corresponding Doftype_in_master_preconditioner_fine and
237 // Doftype_coarsen_map_fine vectors.
238
239 // First resize the vectors.
240 Doftype_in_master_preconditioner_fine.resize(0);
241 Doftype_coarsen_map_fine.resize(0);
242
243 // The Doftype_in_master_preconditioner_fine vector is easy. We know that
244 // the Doftype_coarsen_map_fine in the master preconditioner must be
245 // constructed already. So we simply loop through the values in
246 // doftype_in_master_preconditioner_coarse, then get the most fine grain
247 // dof types from the master preconditioner's Doftype_coarsen_map_fine
248 // vector.
249 //
250 // For example, if the master preconditioner has the vector:
251 // Doftype_coarsen_map_fine = [0,1,2,3][4,5,6,7][8,9,10,11][12,13][14,15]
252 //
253 // and passes the two vectors
254 // doftype_in_master_preconditioner_coarse = [1,2,3]
255 // doftype_coarsen_map_coarse = [[0][1,2]]
256 //
257 // Then we want
258 // Doftype_in_master_preconditioner_fine = [4,5,6,7,8,9,10,11,12,13]
259 //
260 // We achieve this by looking up the corresponding fine dof types in the
261 // masters' Doftype_coarsen_map_fine vector which corresponds to the
262 // values in Doftype_in_master_preconditioner_coarse.
263 //
264 // That is, the values in Doftype_in_master_preconditioner_coarse gives us
265 // the index of sub vector we want in the master's
266 // Doftype_coarsen_map_fine vector.
267
268#ifdef PARANOID
269 // Check that the master block preconditioner's Doftype_coarsen_map_fine
270 // is set up. Under the current implementation, this would always be set
271 // up properly, but we check it just in case!
272 if (master_block_preconditioner_pt()->doftype_coarsen_map_fine().size() ==
273 0)
274 {
275 std::ostringstream err_msg;
276 err_msg << "The master block preconditioner's "
277 << "Doftype_coarsen_map_fine is not\n"
278 << "set up properly.\n"
279 << "\n"
280 << "This vector is constructed in the function "
281 << "block_setup(...).\n"
282 << std::endl;
283 throw OomphLibError(
285 }
286#endif
287
289 Doftype_in_master_preconditioner_coarse.size();
291 i++)
292 {
293 // The index of the sub vector we want.
294 unsigned subvec_index = Doftype_in_master_preconditioner_coarse[i];
295
296 // Get the corresponding most fine grain sub vector from the master
297 // block preconditioner
299 Master_block_preconditioner_pt->get_fine_grain_dof_types_in(
301
302 Doftype_in_master_preconditioner_fine.insert(
303 Doftype_in_master_preconditioner_fine.end(),
304 tmp_master_dof_subvec.begin(),
306 }
307
308 // The Doftype_coarsen_map_fine vector is a bit more tricky.
309 // The Doftype_coarsen_map_coarse vector describes which coarse dof types
310 // of THIS preconditioner are grouped together. We have to translate this
311 // into the most fine grain dof types.
312 //
313 // For example, if
314 // Doftype_coarsen_map_coarse = [[0][1,2]]
315 // Doftype_in_master_preconditioner_coarse = [1,2,3]
316 //
317 // and the MASTER preconditioner has:
318 // Doftype_coarsen_map_fine= [[0,1,2,3][4,5,6,7][8,9,10,11][12,13][14,15]]
319 //
320 // Then [[0][1,2]] tell us that the most fine grain DOF types 1 of the
321 // master preconditioner most be grouped together, and the most fine
322 // grained dof types 2 and 3 of the master preconditioner must be grouped
323 // together.
324 //
325 // This gives the vector [[4,5,6,7] [8,9,10,11,12,13]], translating this
326 // into the local DOF types of this preconditioner we have
327 // Doftype_coarsen_map_fine = [[0,1,2,3][4,5,6,7,8,9]]. This corresponds
328 // with the Doftype_in_master_preconditioner_fine vector we created above:
329 // Doftype_in_master_preconditioner_fine = [4,5,6,7,8,9,10,11,12,13]
330 //
331 // Together, the master block preconditioner says to THIS subsidiary block
332 // preconditioner "work on my DOF types [4,5,6,7,8,9,10,11,12,13], but
333 // group your DOF type [0,1,2,3] together as DOF type 0 and [4,5,6,7,8,9]
334 // together together as DOF type 1".
335 //
336 // Think of it like this: For each DOF type in Doftype_coarsen_map_coarse
337 // we look at how many values this corresponds to in the master
338 // preconditioner. In this case, Doftype_coarsen_map_coarse:
339 //
340 // 1 - corresponds to fine DOF types 0,1,2,3 in this preconditioner,
341 // and 4,5,6,7 in the master preconditioner;
342 //
343 // 2 - corresponds to fine DOF types 4,5,6,7 in this preconditioner,
344 // and 8,9,10,11 in the master preconditioner;
345 //
346 // 3 - corresponds to fine DOF types 8,9 in this preconditioner,
347 // and 12,13 in the master preconditioner.
348 //
349 // Thus Doftype_coarsen_map_fine = [[0,1,2,3][4,5,6,7,8,9]]
350 //
351 ////////////////////////////////////////////////////////////////////////
352 //
353 // How to do this: First we create a 2D vector which has the corresponds
354 // to the fine dof types in the master preconditioner but starting from
355 // 0. For example, take the above example (repeated below):
356 // Passed to this prec by the master prec:
357 // Doftype_coarsen_map_coarse = [[0][1,2]]
358 // Doftype_in_master_preconditioner_coarse = [1,2,3]
359 //
360 // and the MASTER preconditioner has:
361 // Doftype_coarsen_map_fine= [[0,1,2,3][4,5,6,7][8,9,10,11][12,13][14,15]]
362 //
363 // Step 1:
364 // Then, the temp 2D vector we want to create is:
365 // master_fine_doftype_translated = [[0 1 2 3], [4,5,6,7], [8,9]]
366 // This comes from using Doftype_in_master_preconditioner_coarse
367 // then get the number of fine dof types in the master.
368 //
369 // Step 2:
370 // Then:
371 // Loop through the vector Doftype_coarsen_map_coarse,
372 // Loop over the inner vectors in Doftype_coarsen_map_coarse
373 // Each element in the inner vector corresponds to a vector in
374 // master_fine_doftype_translated. We push in the vectors of
375 // master_fine_doftype_translated intp Doftype_coarsen_map_fine
376 //
377
379 unsigned dof_type_index = 0;
381 i++)
382 {
383 // How many fine DOF types are in the master's
384 // Doftype_in_master_preconditioner_coarse[i]?
385 unsigned coarse_dof = Doftype_in_master_preconditioner_coarse[i];
386
387 unsigned n_master_fine_doftypes =
388 Master_block_preconditioner_pt->nfine_grain_dof_types_in(coarse_dof);
389
391 for (unsigned j = 0; j < n_master_fine_doftypes; j++)
392 {
393 tmp_sub_vec.push_back(dof_type_index);
395 }
397 }
398
399
400 // master_fine_doftype_translated now contains vectors with values are
401 // from 0, 1, 2, ..,
402 //
403 // Now read out the values of master_fine_doftype_translated and place
404 // them in order according to Doftype_coarsen_map_coarse.
406 Doftype_coarsen_map_coarse.size();
407 for (unsigned i = 0; i < doftype_coarsen_map_coarse_size; i++)
408 {
411 Doftype_coarsen_map_coarse[i].size();
412 for (unsigned j = 0; j < doftype_coarsen_map_coarse_i_size; j++)
413 {
414 unsigned subvec_i = Doftype_coarsen_map_coarse[i][j];
415
416 tmp_vec.insert(tmp_vec.end(),
419 }
420
421 Doftype_coarsen_map_fine.push_back(tmp_vec);
422 }
423
424 // Get the number of block types (and DOF types) in this preconditioner
425 // from the length of the dof_map vector.
426 Internal_ndof_types = Doftype_in_master_preconditioner_fine.size();
427
428 // Nblock_types is later updated in block_setup(...)
429 Internal_nblock_types = Internal_ndof_types;
430
431 // Compute number of rows in this (sub) preconditioner using data from
432 // the master.
433 Nrow = 0;
434 for (unsigned b = 0; b < Internal_ndof_types; b++)
435 {
436 Nrow += this->internal_dof_block_dimension(b);
437 }
438
439#ifdef PARANOID
440 if (Nrow == 0)
441 {
442 std::ostringstream error_message;
443 error_message
444 << "Nrow=0 in subsidiary preconditioner. This seems fishy and\n"
445 << "suggests that block_setup() was not called for the \n"
446 << "master block preconditioner yet.";
447 throw OomphLibWarning(error_message.str(),
450 }
451#endif
452 }
453
454 // If this is a master block preconditioner, then set the
455 // Doftype_coarsen_map_fine and Doftype_coarsen_map_coarse to the
456 // identity. Recall that the Doftype_coarsen_map_fine maps the dof types
457 // that this preconditioner requires with the most fine grain dof types (the
458 // internal dof types) and the Doftype_coarsen_map_coarse maps the dof
459 // types that this preconditioner requires with the dof types which this
460 // preconditioner is given from a master preconditioner (these dof types may
461 // or may not be coarsened). In the case of the master preconditioner, these
462 // are the same (since dof types are not coarsened), furthermore the
463 // identity mapping is provided to say that dof type 0 maps to dof type 0,
464 // dof type 1 maps to dof type 1,
465 // dof type 2 maps to dof type 2,
466 // etc...
467 //
468 // If this is not a master block preconditioner, then the vectors
469 // Doftype_coarsen_map_fine and Doftype_coarsen_map_coarse is handled
470 // by the turn_into_subsidiary_block_preconditioner(...) function.
471 if (is_master_block_preconditioner())
472 {
473 // How many dof types does this preconditioner work with?
475
476 // Note: at the master level, the n_external_dof_types should be the same
477 // as the internal_ndof_types(), since the dof_to_block_map MUST describe
478 // the mapping between every dof type (not yet coarsened - so it is the
479 // same number as the internal dof types) to the block types. But we
480 // distinguish them for clarity. We also check that this is the case.
481#ifdef PARANOID
482 unsigned n_internal_dof_types = internal_ndof_types();
483
485 {
486 std::ostringstream err_msg;
487 err_msg
488 << "The internal ndof types and the length of the dof_to_block_map\n"
489 << "vector is not the same. Since this is the master block "
490 << "preconditioner,\n"
491 << "you must describe which block each DOF type belongs to,\n"
492 << "no more, no less."
493 << "internal_ndof_types = " << n_internal_dof_types << "\n"
494 << "dof_to_block_map.size() = " << n_external_dof_types << "\n";
495 throw OomphLibWarning(
497 }
498#endif
499
500 // Clear and reserve space.
501 Doftype_coarsen_map_fine.clear();
502 Doftype_coarsen_map_coarse.clear();
503 Doftype_coarsen_map_fine.reserve(n_external_dof_types);
504 Doftype_coarsen_map_coarse.reserve(n_external_dof_types);
505
506 // Now push back the identity mapping.
507 for (unsigned i = 0; i < n_external_dof_types; i++)
508 {
509 // Create a vector and push it in.
511 Doftype_coarsen_map_fine.push_back(tmp_vec);
512 Doftype_coarsen_map_coarse.push_back(tmp_vec);
513 }
514 }
515 else
516 // Else this is a subsidiary block preconditioner.
517 {
518 // Both the Doftype_coarsen_map_fine and Doftype_coarsen_map_coarse
519 // vectors must be already be handled by the
520 // turn_into_subsidiary_block_preconditioner(...) function. We check this.
521#ifdef PARANOID
522 if ((Doftype_coarsen_map_fine.size() == 0) ||
523 (Doftype_coarsen_map_coarse.size() == 0))
524 {
525 std::ostringstream err_msg;
526 err_msg << "Either the Doftype_coarsen_map_fine or the \n"
527 << "Doftype_coarsen_map_coarse vectors is of size 0.\n"
528 << "Did you remember to call the function "
529 << "turn_into_subsidiary_block_preconditioner(...)?";
530 throw OomphLibWarning(
532 }
533#endif
534 }
535
536
537 // Now we create the vector Block_to_dof_map_coarse.
538 // Recall that the vector describe which dof types are in which block with
539 // the relationship:
540 //
541 // Block_to_dof_map_coarse[block_number] = Vector[dof types];
542 //
543 // Note that this is not the internal (underlying) dof type.
544 // Nor is this in relation to the parent block preconditioner's dof type.
545 // The number of elements in it is the same as dof_to_block_map vector.
546 //
547 // Since the dof type coarsening feature is added later, we encapsulate this
548 // bit of the code so it does not affect things below.
549 {
550 // Check that the dof_to_block map "makes sense" for the
551 // Doftype_coarsen_map_coarse.
552 // The Doftype_coarsen_map_coarse describes which doftypes should be
553 // considered as a single doftype in THIS preconditioner.
554 //
555 // For example, if this preconditioner is the LSC block preconditioner
556 // applied to a 3D problem, it expects 4 doftypes:
557 // 3 velocity, [u, v, w] and 1 pressure [p],
558 // giving us the dof type ordering
559 // [u v w p].
560 //
561 // The LSC preconditioner groups the velocity and pressure doftypes
562 // separately, thus the dof_to_block_map will be:
563 // [0 0 0 1]
564 //
565 // Then the Doftype_coarsen_map_coarse MUST have length 4, to identify
566 // which of the OTHER (possibly coarsened) dof types should be grouped
567 // together to be considered as a single dof types of THIS preconditioner.
568 //
569 // For example, if the preconditioner above this one has the dof type
570 // ordering:
571 // 0 1 2 3 4 5 6 7 8 9
572 // ub vb wb up vp wp ut vt wt p
573 // Then we want to tell THIS preconditioner which dof types belongs to
574 // u, v, w and p, by providing the optional argument
575 // Doftype_coarsen_map_coarse to the
576 // turn_into_subsidiary_block_preconditioner(...) function
577 // [[0 3 6] [1 4 7] [2 5 8] [9]]
578 //
579 // So, it is important that the length of dof_to_block_map is the same as
580 // the length of Doftype_coarsen_map_coarse. We check this.
582
583#ifdef PARANOID
584 if (dof_to_block_map_size != Doftype_coarsen_map_coarse.size())
585 {
586 std::ostringstream err_msg;
587 err_msg
588 << "The size of dof_to_block_map and Doftype_coarsen_map_coarse is "
589 "not "
590 << "the same.\n"
591 << "dof_to_block_map.size() = " << dof_to_block_map_size << "\n"
592 << "Doftype_coarsen_map_coarse.size() = "
593 << Doftype_coarsen_map_coarse.size() << ".\n"
594 << "One of the two list is incorrect, please look at the comments\n"
595 << "in the source code for more details.";
596 throw OomphLibWarning(
598 }
599#endif
600
601 // Create the Block_to_dof_map_coarse from
602 // the dof_to_block_map and Doftype_coarsen_map_coarse.
603
604 // find the maximum block number
605 unsigned max_block_number =
606 *std::max_element(dof_to_block_map.begin(), dof_to_block_map.end());
607
608 // Now we do the following:
609 // Lets say the Doftype_coarsen_map_coarse is:
610 // [0 3 6]
611 // [1 4 7]
612 // [2 5 8]
613 // [9]
614 //
615 // (this is the same as the above example)
616 //
617 // and the dof_to_block_map is [0 0 0 1].
618 //
619 // Then we need to form the Block_to_dof_map_coarse:
620 // [0 3 6 1 4 7 2 5 8]
621 // [9]
622
623 // Clear anything in the Block_to_dof_map_coarse
624 Block_to_dof_map_coarse.clear();
625
626 const unsigned tmp_nblock = max_block_number + 1;
627
628 Block_to_dof_map_coarse.resize(tmp_nblock);
629
630 for (unsigned i = 0; i < dof_to_block_map_size; i++)
631 {
632 Block_to_dof_map_coarse[dof_to_block_map[i]].push_back(i);
633 }
635 Block_to_dof_map_fine.clear();
636 Block_to_dof_map_fine.resize(tmp_nblock);
637 for (unsigned block_i = 0; block_i < tmp_nblock; block_i++)
638 {
639 // get the dof types in this block.
640 const unsigned ndof_in_block = Block_to_dof_map_coarse[block_i].size();
641 for (unsigned dof_i = 0; dof_i < ndof_in_block; dof_i++)
642 {
643 const unsigned coarsened_dof_i =
644 Block_to_dof_map_coarse[block_i][dof_i];
645
646 // Insert the most fine grain dofs which this dof_i corresponds to
647 // into block_i
649 Doftype_coarsen_map_fine[coarsened_dof_i];
650
651 Block_to_dof_map_fine[block_i].insert(
652 Block_to_dof_map_fine[block_i].end(),
653 dof_i_dofs.begin(),
654 dof_i_dofs.end());
655 }
656 }
657
658 // Now set the dof_to_block_map to the identify.
659 // NOTE: We are now using the internal n dof types. This is because the
660 // dof type coarsening feature was built on top of the existing block
661 // preconditioning framework which does not handle coarsening of dof
662 // types. Hence, under the hood, it still works with the most fine grain
663 // dof types and does not do any coarsening.
664
665 // Locally cache the internal ndof types (using access function because
666 // the Internal_ndof_type variable may not be set up yet if this is a
667 // master preconditioner).
668 unsigned tmp_internal_ndof_types = internal_ndof_types();
669
671
672 for (unsigned i = 0; i < tmp_internal_ndof_types; i++)
673 {
675 }
676 } // end of Block_to_dof_map_coarse encapsulation
677
678#ifdef PARANOID
679
680 // Check that the meshes are ok. This only needs to be done in the master
681 // because subsidiary preconditioners don't do anything with the meshes
682 // here.
683 if (is_master_block_preconditioner())
684 {
685 // This is declared as local_nmesh because there are other variables
686 // called nmesh floating about... but this will not exist if PARANOID is
687 // switched on.
688 unsigned local_nmesh = nmesh();
689
690 // Check that some mesh pointers have been assigned.
691 if (local_nmesh == 0)
692 {
693 std::ostringstream error_msg;
694 error_msg << "Cannot setup blocks because no meshes have been set.";
695 throw OomphLibError(
697 }
698
699 // Each mesh must contain elements with the same number of dof.
700 // A stricter check is to ensure that the mesh contains only one type of
701 // elements. This is relaxed in same cases.
702 for (unsigned mesh_i = 0; mesh_i < local_nmesh; mesh_i++)
703 {
704 // The number of elements in the current mesh.
705 unsigned n_element = mesh_pt(mesh_i)->nelement();
706
707 // When the bulk mesh is distributed, there may not be any elements
708 // in the surface mesh(es).
709 if (n_element > 0)
710 {
711 // The string of the first element in the current mesh.
712 std::string first_element_string =
713 typeid(*(mesh_pt(mesh_i)->element_pt(0))).name();
714
715 // If there are multiple element types in the current mesh,
716 // we can at least make sure that they contain the same types of DOFs.
717 if (bool(Allow_multiple_element_type_in_mesh[mesh_i]))
718 {
719 // The ndof types of the first element.
720 unsigned first_element_ndof_type =
721 mesh_pt(mesh_i)->element_pt(0)->ndof_types();
722
723 // Loop through the meshes and compare the number of types of DOFs.
724 for (unsigned el_i = 1; el_i < n_element; el_i++)
725 {
726 // The ndof type of the current element.
728 mesh_pt(mesh_i)->element_pt(el_i)->ndof_types();
729
730 // The string of the current element.
731 std::string current_element_string =
732 typeid(*(mesh_pt(mesh_i)->element_pt(el_i))).name();
733
734 // Compare against the first element.
736 {
737 std::ostringstream error_message;
738 error_message
739 << "Elements in the same mesh MUST have the same number of "
740 "types "
741 << "of DOFs.\n"
742 << "The element in mesh " << mesh_i << ", at position "
743 << el_i << " is: \n"
744 << current_element_string << ", \n"
745 << "with ndof types: " << current_element_ndof_type << ".\n"
746 << "The first element in the same mesh is: \n"
747 << first_element_string << ", \n"
748 << "with ndof types: " << first_element_ndof_type << ".\n";
749 throw OomphLibError(error_message.str(),
752 }
753 }
754 }
755 else
756 // There should be only one type of elements in the current mesh.
757 // Check that this is the case!
758 {
759 // Loop through the elements in the current mesh.
760 for (unsigned el_i = 1; el_i < n_element; el_i++)
761 {
762 // The string of the current element.
763 std::string current_element_string =
764 typeid(*(mesh_pt(mesh_i)->element_pt(el_i))).name();
765
766 // Compare against the first element.
768 {
769 std::ostringstream error_message;
770 error_message
771 << "By default, a mesh containing block preconditionable "
772 << "elements must contain only one type of element.\n"
773 << "The element in mesh " << mesh_i << ", at position "
774 << el_i << " is: \n"
775 << current_element_string << "\n"
776 << "The first element in the same mesh is: \n"
777 << first_element_string << "\n"
778 << "If this is correct, consider calling the set_mesh(...) "
779 "with\n"
780 << "the optional argument set true to allow multiple "
781 "element\n"
782 << "types in the same mesh.\n"
783 << "Note: A minimal requirement is that the elements in the "
784 "same\n"
785 << "mesh MUST have the same number of DOF types.";
786 throw OomphLibError(error_message.str(),
789 }
790 }
791 }
792 }
793 }
794 }
795
796#endif
797 // clear the memory
798 this->clear_block_preconditioner_base();
799
800 // get my_rank and nproc
801#ifdef OOMPH_HAS_MPI
802 unsigned my_rank = comm_pt()->my_rank();
803 unsigned nproc = comm_pt()->nproc();
804#endif
805
806
807 /////////////////////////////////////////////////////////////////////////////
808 // start of master block preconditioner only operations
809 /////////////////////////////////////////////////////////////////////////////
810#ifdef OOMPH_HAS_MPI
811 unsigned* nreq_sparse = new unsigned[nproc]();
812 unsigned* nreq_sparse_for_proc = new unsigned[nproc]();
813 unsigned** index_in_dof_block_sparse_send = new unsigned*[nproc]();
814 unsigned** dof_number_sparse_send = new unsigned*[nproc]();
817#endif
818
819 // If this preconditioner is the master preconditioner then we need
820 // to assemble the vectors : Dof_number
821 // Index_in_dof_block
822 if (is_master_block_preconditioner())
823 {
824 // Get the number of dof types in each mesh.
825 Ndof_types_in_mesh.assign(nmesh(), 0);
826 for (unsigned i = 0; i < nmesh(); i++)
827 {
828 Ndof_types_in_mesh[i] = mesh_pt(i)->ndof_types();
829 }
830 // Setup the distribution of this preconditioner, assumed to be the same
831 // as the matrix if the matrix is distributable.
832 if (dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt()))
833 {
834 this->build_distribution(
835 dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt())
836 ->distribution_pt());
837 }
838 else
839 {
840 LinearAlgebraDistribution dist(comm_pt(), matrix_pt()->nrow(), false);
841 this->build_distribution(dist);
842 }
843 Nrow = matrix_pt()->nrow();
844
845 // Boolean to indicate whether the matrix is actually distributed,
846 // ie distributed and on more than one processor.
847 bool matrix_distributed =
848 (this->distribution_pt()->distributed() &&
849 this->distribution_pt()->communicator_pt()->nproc() > 1);
850
851
852 // Matrix must be a CR matrix.
853 CRDoubleMatrix* cr_matrix_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt());
854
855 if (cr_matrix_pt == 0)
856 {
857 std::ostringstream error_message;
858 error_message << "Block setup for distributed matrices only works "
859 << "for CRDoubleMatrices";
860 throw OomphLibError(error_message.str(),
863 }
864
865
866 // Get distribution.
867 unsigned first_row = this->distribution_pt()->first_row();
868 unsigned nrow_local = this->distribution_pt()->nrow_local();
869 unsigned last_row = first_row + nrow_local - 1;
870
871#ifdef OOMPH_HAS_MPI
872 // storage for the rows required by each processor in the dense
873 // block lookup storage scheme
874 // dense_required_rows(p,0) is the minimum global index required by proc p
875 // ...(p,1) is the maximum global index required by proc p
876 DenseMatrix<unsigned> dense_required_rows(nproc, 2);
877 for (unsigned p = 0; p < nproc; p++)
878 {
879 dense_required_rows(p, 0) = this->distribution_pt()->first_row(p);
880 dense_required_rows(p, 1) = this->distribution_pt()->first_row(p) +
881 this->distribution_pt()->nrow_local(p) - 1;
882 }
883
884 // determine the global rows That are not in the range first_row to
885 // first_row+nrow_local for which we should store the
886 // Dof_index and Index_in_dof_block for
887 // then send the lists to other processors
888 std::set<unsigned> sparse_global_rows_for_block_lookup;
890 {
891 unsigned nnz = cr_matrix_pt->nnz();
892 int* column_index = cr_matrix_pt->column_index();
893 for (unsigned i = 0; i < nnz; i++)
894 {
895 unsigned ci = column_index[i];
897 {
899 }
900 }
901 }
902
904
905 Global_index_sparse.resize(0);
906 std::copy(sparse_global_rows_for_block_lookup.begin(),
908 std::back_inserter(Global_index_sparse));
909
910 Index_in_dof_block_sparse.resize(nsparse);
911 Dof_number_sparse.resize(nsparse);
913
916 {
919
920 int zero = 0;
921 for (unsigned p = 0; p < nproc; p++)
922 {
923 // determine the global eqn numbers required by this processor
924 // that can be classified by processor p
925 int begin = 0;
926 for (int i = 0; i < nsparse; ++i)
927 {
928 if (Global_index_sparse[i] < dense_required_rows(p, 0))
929 {
930 ++begin;
931 }
932 else
933 {
934 if (Global_index_sparse[i] <= dense_required_rows(p, 1))
935 {
936 ++nreq_sparse[p];
937 }
938 else
939 {
940 break;
941 }
942 }
943 }
944
945 // if this processor has rows to be classified by proc p
946 if (nreq_sparse[p] > 0)
947 {
948 // send the number of global eqn numbers
951 1,
953 p,
954 31,
955 comm_pt()->mpi_comm(),
956 &req1);
957 send_requests_sparse.push_back(req1);
958
959 // send the global eqn numbers
961 MPI_Isend(&Global_index_sparse[begin],
962 nreq_sparse[p],
964 p,
965 32,
966 comm_pt()->mpi_comm(),
967 &req2);
968 send_requests_sparse.push_back(req2);
969
970 // post the recvs for the data that will be returned
971
972 // the datatypes, displacements, lengths for the two datatypes
975 int lengths[2];
976
977 // index in dof block
980 MPI_Get_address(&Index_in_dof_block_sparse[begin],
981 &displacements[0]);
983 lengths[0] = 1;
984
985 // dof number
988 MPI_Get_address(&Dof_number_sparse[begin], &displacements[1]);
990 lengths[1] = 1;
991
992 // build the final type
997 MPI_Type_free(&types[0]);
998 MPI_Type_free(&types[1]);
999
1000 // and recv
1002 MPI_Irecv(
1003 nreq_sparse, 1, recv_type, p, 33, comm_pt()->mpi_comm(), &req);
1004 recv_requests_sparse.push_back(req);
1006 }
1007
1008 // if no communication required, confirm this
1009 if (nreq_sparse[p] == 0)
1010 {
1012 MPI_Isend(&zero, 1, MPI_INT, p, 31, comm_pt()->mpi_comm(), &req1);
1013 send_requests_sparse.push_back(req1);
1014 }
1015
1016 //
1019 1,
1021 p,
1022 31,
1023 comm_pt()->mpi_comm(),
1024 &req);
1025 recv_requests_sparse_nreq.push_back(req);
1026 }
1027 }
1028#endif
1029
1030 // resize the storage
1031 Dof_number_dense.resize(nrow_local);
1032 Index_in_dof_block_dense.resize(nrow_local);
1033
1034 // zero the number of dof types
1035 Internal_ndof_types = 0;
1036
1037#ifdef PARANOID
1038 // Vector to keep track of previously assigned block numbers
1039 // to check consistency between multiple assignments.
1042#endif
1043
1044 // determine whether the problem is distribution
1045 bool problem_distributed = false;
1046
1047 // the problem method distributed() is only accessible with MPI
1048#ifdef OOMPH_HAS_MPI
1049 problem_distributed = any_mesh_distributed();
1050#endif
1051
1052 // if the problem is not distributed
1054 {
1055 // Offset for the block type in the overall system.
1056 // Different meshes contain different block-preconditionable
1057 // elements -- their blocks are added one after the other.
1058 unsigned dof_offset = 0;
1059
1060 // Loop over all meshes.
1061 for (unsigned m = 0; m < nmesh(); m++)
1062 {
1063 // Number of elements in this mesh.
1064 unsigned n_element = mesh_pt(m)->nelement();
1065
1066 // Find the number of block types that the elements in this mesh
1067 // are in charge of.
1068 unsigned ndof_in_element = ndof_types_in_mesh(m);
1069 Internal_ndof_types += ndof_in_element;
1070
1071 for (unsigned e = 0; e < n_element; e++)
1072 {
1073 // List containing pairs of global equation number and
1074 // dof number for each global dof in an element.
1075 std::list<std::pair<unsigned long, unsigned>> dof_lookup_list;
1076
1077 // Get list of blocks associated with the element's global unknowns.
1078 mesh_pt(m)->element_pt(e)->get_dof_numbers_for_unknowns(
1080
1081 // Loop over all entries in the list
1082 // and store the block number.
1083 typedef std::list<std::pair<unsigned long, unsigned>>::iterator IT;
1084 for (IT it = dof_lookup_list.begin(); it != dof_lookup_list.end();
1085 it++)
1086 {
1087 unsigned long global_dof = it->first;
1088 if (global_dof >= unsigned(first_row) &&
1090 {
1091 unsigned dof_number = (it->second) + dof_offset;
1092 Dof_number_dense[global_dof - first_row] = dof_number;
1093
1094#ifdef PARANOID
1095 // Check consistency of block numbers if assigned multiple times
1097 0)
1098 {
1100 dof_number;
1101 }
1102#endif
1103 }
1104 }
1105 }
1106
1107 // About to do the next mesh which contains block preconditionable
1108 // elements of a different type; all the dofs that these elements are
1109 // "in charge of" differ from the ones considered so far.
1110 // Bump up the block counter to make sure we're not overwriting
1111 // anything here
1113 }
1114
1115#ifdef PARANOID
1116 // check that every global equation number has been allocated a dof type
1117 for (unsigned i = 0; i < nrow_local; i++)
1118 {
1120 {
1121 std::ostringstream error_message;
1122 error_message << "Not all degrees of freedom have had DOF type "
1123 << "numbers allocated. Dof number " << i
1124 << " is unallocated.";
1125 throw OomphLibError(error_message.str(),
1128 }
1129 }
1130#endif
1131 }
1132 // else the problem is distributed
1133 else
1134 {
1135#ifdef OOMPH_HAS_MPI
1136 // Offset for the block type in the overall system.
1137 // Different meshes contain different block-preconditionable
1138 // elements -- their blocks are added one after the other...
1139 unsigned dof_offset = 0;
1140
1141 // the set of global degrees of freedom and their corresponding dof
1142 // number on this processor
1143 std::map<unsigned long, unsigned> my_dof_map;
1144
1145 // Loop over all meshes
1146 for (unsigned m = 0; m < nmesh(); m++)
1147 {
1148 // Number of elements in this mesh
1149 unsigned n_element = this->mesh_pt(m)->nelement();
1150
1151 // Find the number of block types that the elements in this mesh
1152 // are in charge of
1153 unsigned ndof_in_element = ndof_types_in_mesh(m);
1154 Internal_ndof_types += ndof_in_element;
1155
1156 // Loop over all elements
1157 for (unsigned e = 0; e < n_element; e++)
1158 {
1159 // if the element is not a halo element
1160 if (!this->mesh_pt(m)->element_pt(e)->is_halo())
1161 {
1162 // List containing pairs of global equation number and
1163 // dof number for each global dof in an element
1164 std::list<std::pair<unsigned long, unsigned>> dof_lookup_list;
1165
1166 // Get list of blocks associated with the element's global
1167 // unknowns
1168 this->mesh_pt(m)->element_pt(e)->get_dof_numbers_for_unknowns(
1170
1171 // update the block numbers and put it in the map.
1172 typedef std::list<std::pair<unsigned long, unsigned>>::iterator
1173 IT;
1174 for (IT it = dof_lookup_list.begin(); it != dof_lookup_list.end();
1175 it++)
1176 {
1177 it->second = (it->second) + dof_offset;
1178 my_dof_map[it->first] = it->second;
1179 }
1180 }
1181 }
1182
1183 // About to do the next mesh which contains block preconditionable
1184 // elements of a different type; all the dofs that these elements are
1185 // "in charge of" differ from the ones considered so far.
1186 // Bump up the block counter to make sure we're not overwriting
1187 // anything here
1189 }
1190
1191 // next copy the map of my dofs to two vectors to send
1192 unsigned my_ndof = my_dof_map.size();
1193 unsigned long* my_global_dofs = new unsigned long[my_ndof];
1194 unsigned* my_dof_numbers = new unsigned[my_ndof];
1195 typedef std::map<unsigned long, unsigned>::iterator IT;
1196 unsigned pt = 0;
1197 for (IT it = my_dof_map.begin(); it != my_dof_map.end(); it++)
1198 {
1199 my_global_dofs[pt] = it->first;
1200 my_dof_numbers[pt] = it->second;
1201 pt++;
1202 }
1203
1204 // and then clear the map
1205 my_dof_map.clear();
1206
1207 // count up how many DOFs need to be sent to each processor
1208 int* first_dof_to_send = new int[nproc];
1209 int* ndof_to_send = new int[nproc];
1210 unsigned ptr = 0;
1211 for (unsigned p = 0; p < nproc; p++)
1212 {
1213 first_dof_to_send[p] = 0;
1214 ndof_to_send[p] = 0;
1215 while (ptr < my_ndof &&
1217 {
1218 ptr++;
1219 }
1221 while (ptr < my_ndof &&
1223 {
1224 ndof_to_send[p]++;
1225 ptr++;
1226 }
1227 }
1228
1229 // next communicate to each processor how many dofs it expects to recv
1230 int* ndof_to_recv = new int[nproc];
1232 1,
1233 MPI_INT,
1235 1,
1236 MPI_INT,
1237 comm_pt()->mpi_comm());
1238
1239 // the base displacements for the sends
1242
1243#ifdef PARANOID
1244 // storage for paranoid check to ensure that every row as been
1245 // imported
1246 std::vector<bool> dof_recv(nrow_local, false);
1247#endif
1248
1249 // next send and recv
1254 Vector<unsigned> proc;
1255 for (unsigned p = 0; p < nproc; p++)
1256 {
1257 if (p != my_rank)
1258 {
1259 // send
1260 if (ndof_to_send[p] > 0)
1261 {
1262 // the datatypes, displacements, lengths for the two datatypes
1265 int lengths[2];
1266
1267 // my global dofs
1272 &displacements[0]);
1274 lengths[0] = 1;
1275
1276 // my dof numbers
1280 &displacements[1]);
1282 lengths[1] = 1;
1283
1284 // build the final type
1289 MPI_Type_free(&types[0]);
1290 MPI_Type_free(&types[1]);
1291
1292 // and send
1295 1,
1296 send_type,
1297 p,
1298 2,
1299 comm_pt()->mpi_comm(),
1300 &req);
1301 send_requests.push_back(req);
1303 }
1304
1305 // and recv
1306 if (ndof_to_recv[p] > 0)
1307 {
1308 // resize the storage
1309 global_dofs_recv[p] = new unsigned long[ndof_to_recv[p]];
1310 dof_numbers_recv[p] = new unsigned[ndof_to_recv[p]];
1311 proc.push_back(p);
1312
1313 // the datatypes, displacements, lengths for the two datatypes
1316 int lengths[2];
1317
1318 // my global dofs
1324 lengths[0] = 1;
1325
1326 // my dof numbers
1331 lengths[1] = 1;
1332
1333 // build the final type
1338 MPI_Type_free(&types[0]);
1339 MPI_Type_free(&types[1]);
1340
1341 // and recv
1344 1,
1345 recv_type,
1346 p,
1347 2,
1348 comm_pt()->mpi_comm(),
1349 &req);
1350 recv_requests.push_back(req);
1352 }
1353 }
1354 // send to self
1355 else
1356 {
1357 unsigned u = first_dof_to_send[p] + ndof_to_recv[p];
1358 for (unsigned i = first_dof_to_send[p]; i < u; i++)
1359 {
1360#ifdef PARANOID
1361 // indicate that this dof has ben recv
1362 dof_recv[my_global_dofs[i] - first_row] = true;
1363#endif
1364 Dof_number_dense[my_global_dofs[i] - first_row] =
1366 }
1367 }
1368 }
1369
1370 // recv and store the data
1371 unsigned c_recv = recv_requests.size();
1372 while (c_recv > 0)
1373 {
1374 // wait for any communication to finish
1375 int req_number;
1378 recv_requests.erase(recv_requests.begin() + req_number);
1379 c_recv--;
1380
1381 // determine the source processor
1382 unsigned p = proc[req_number];
1383 proc.erase(proc.begin() + req_number);
1384
1385 // import the data
1386 for (int i = 0; i < ndof_to_recv[p]; i++)
1387 {
1388#ifdef PARANOID
1389 // indicate that this dof has ben recv
1390 dof_recv[global_dofs_recv[p][i] - first_row] = true;
1391#endif
1392 Dof_number_dense[global_dofs_recv[p][i] - first_row] =
1394 }
1395
1396 // delete the data
1397 delete[] global_dofs_recv[p];
1398 delete[] dof_numbers_recv[p];
1399 }
1400
1401 // finally wait for the send requests to complete as we are leaving
1402 // an MPI block of code
1403 unsigned csr = send_requests.size();
1404 if (csr)
1405 {
1407 }
1408
1409 // clean up
1410 delete[] ndof_to_send;
1411 delete[] first_dof_to_send;
1412 delete[] ndof_to_recv;
1413 delete[] my_global_dofs;
1414 delete[] my_dof_numbers;
1415#ifdef PARANOID
1416 unsigned all_recv = true;
1417 for (unsigned i = 0; i < nrow_local; i++)
1418 {
1419 if (!dof_recv[i])
1420 {
1421 all_recv = false;
1422 }
1423 }
1424 if (!all_recv)
1425 {
1426 std::ostringstream error_message;
1427 error_message << "Not all the DOF numbers required were received";
1428 throw OomphLibError(error_message.str(),
1431 }
1432#endif
1433#else
1434 std::ostringstream error_message;
1435 error_message
1436 << "The problem appears to be distributed, MPI is required";
1437 throw OomphLibError(error_message.str(),
1440#endif
1441 }
1442#ifdef OOMPH_HAS_MPI
1446 {
1447 // wait for number of sparse rows each processor requires
1448 // post recvs for that data
1449 if (recv_requests_sparse_nreq.size() > 0)
1450 {
1454 }
1455 for (unsigned p = 0; p < nproc; ++p)
1456 {
1457 if (nreq_sparse_for_proc[p] > 0)
1458 {
1460 sparse_rows_for_proc[p] = new unsigned[nreq_sparse_for_proc[p]];
1464 p,
1465 32,
1466 comm_pt()->mpi_comm(),
1467 &req);
1469 }
1470 }
1471 }
1472#endif
1473
1474
1475 // for every global degree of freedom required by this processor we now
1476 // have the corresponding dof number
1477
1478 // clear the Ndof_in_dof_block storage
1479 Dof_dimension.assign(Internal_ndof_types, 0);
1480
1481 // first consider a non distributed matrix
1482 if (!matrix_distributed)
1483 {
1484 // set the Index_in_dof_block
1485 unsigned nrow = this->distribution_pt()->nrow();
1486 Index_in_dof_block_dense.resize(nrow);
1487 Index_in_dof_block_dense.initialise(0);
1488 for (unsigned i = 0; i < nrow; i++)
1489 {
1490 Index_in_dof_block_dense[i] = Dof_dimension[Dof_number_dense[i]];
1491 Dof_dimension[Dof_number_dense[i]]++;
1492 }
1493 }
1494
1495 // next a distributed matrix
1496 else
1497 {
1498#ifdef OOMPH_HAS_MPI
1499
1500
1501 // first compute how many instances of each dof are on this
1502 // processor
1503 unsigned* my_nrows_in_dof_block = new unsigned[Internal_ndof_types];
1504 for (unsigned i = 0; i < Internal_ndof_types; i++)
1505 {
1507 }
1508 for (unsigned i = 0; i < nrow_local; i++)
1509 {
1510 my_nrows_in_dof_block[Dof_number_dense[i]]++;
1511 }
1512
1513 // next share the data
1514 unsigned* nrow_in_dof_block_recv =
1515 new unsigned[Internal_ndof_types * nproc];
1517 Internal_ndof_types,
1520 Internal_ndof_types,
1522 comm_pt()->mpi_comm());
1523 delete[] my_nrows_in_dof_block;
1524
1525 // compute my first dof index and Nrows_in_dof_block
1526 Vector<unsigned> my_first_dof_index(Internal_ndof_types, 0);
1527 for (unsigned i = 0; i < Internal_ndof_types; i++)
1528 {
1529 for (unsigned p = 0; p < my_rank; p++)
1530 {
1532 nrow_in_dof_block_recv[p * Internal_ndof_types + i];
1533 }
1534 Dof_dimension[i] = my_first_dof_index[i];
1535 for (unsigned p = my_rank; p < nproc; p++)
1536 {
1537 Dof_dimension[i] +=
1538 nrow_in_dof_block_recv[p * Internal_ndof_types + i];
1539 }
1540 }
1541 delete[] nrow_in_dof_block_recv;
1542
1543 // next compute Index in dof block
1544 Index_in_dof_block_dense.resize(nrow_local);
1545 Index_in_dof_block_dense.initialise(0);
1546 Vector<unsigned> dof_counter(Internal_ndof_types, 0);
1547 for (unsigned i = 0; i < nrow_local; i++)
1548 {
1549 Index_in_dof_block_dense[i] =
1550 my_first_dof_index[Dof_number_dense[i]] +
1551 dof_counter[Dof_number_dense[i]];
1552 dof_counter[Dof_number_dense[i]]++;
1553 }
1554
1555 // the base displacements for the sends
1561 }
1564 unsigned first_row = this->distribution_pt()->first_row();
1565 for (unsigned p = 0; p < nproc; ++p)
1566 {
1567 if (nreq_sparse_for_proc[p] > 0)
1568 {
1569 // construct the data
1571 new unsigned[nreq_sparse_for_proc[p]];
1573 for (unsigned i = 0; i < nreq_sparse_for_proc[p]; ++i)
1574 {
1575 unsigned r = sparse_rows_for_proc[p][i];
1576 r -= first_row;
1578 Index_in_dof_block_dense[r];
1579 dof_number_sparse_send[p][i] = Dof_number_dense[r];
1580 }
1581 delete[] sparse_rows_for_proc[p];
1582
1583 // send the data
1584 // the datatypes, displacements, lengths for the two datatypes
1587 int lengths[2];
1588
1589 // index in dof block
1594 &displacements[0]);
1596 lengths[0] = 1;
1597
1598 // dof number
1604 lengths[1] = 1;
1605
1606 // build the final type
1611 MPI_Type_free(&types[0]);
1613
1614 // and recv
1617 1,
1618 send_type,
1619 p,
1620 33,
1621 comm_pt()->mpi_comm(),
1623 send_requests_sparse.push_back(req);
1625 }
1626 else
1627 {
1630 }
1631 }
1632#endif
1633 }
1634 }
1635
1636 /////////////////////////////////////////////////////////////////////////////
1637 // end of master block preconditioner only operations
1638 /////////////////////////////////////////////////////////////////////////////
1639
1640 // compute the number of rows in each block
1641
1642#ifdef PARANOID
1643 // check the vector is the correct length
1644 if (dof_to_block_map.size() != Internal_ndof_types)
1645 {
1646 std::ostringstream error_message;
1647 error_message << "The dof_to_block_map vector (size="
1649 << ") must be of size Internal_ndof_types="
1650 << Internal_ndof_types;
1652 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1653 }
1654#endif
1655
1656 // find the maximum block number RAYAY use std::max_element
1657 unsigned max_block_number = 0;
1658 for (unsigned i = 0; i < Internal_ndof_types; i++)
1659 {
1661 {
1663 }
1664 }
1665
1666 // resize the storage the the block to dof map
1667 Block_number_to_dof_number_lookup.clear();
1668 Block_number_to_dof_number_lookup.resize(max_block_number + 1);
1669 Ndof_in_block.clear();
1670 Ndof_in_block.resize(max_block_number + 1);
1671
1672 // resize storage
1673 Dof_number_to_block_number_lookup.resize(Internal_ndof_types);
1674
1675 // build the storage for the two maps (block to dof) and (dof to block)
1676 for (unsigned i = 0; i < Internal_ndof_types; i++)
1677 {
1678 Dof_number_to_block_number_lookup[i] = dof_to_block_map[i];
1679 Block_number_to_dof_number_lookup[dof_to_block_map[i]].push_back(i);
1680 Ndof_in_block[dof_to_block_map[i]]++;
1681 }
1682
1683#ifdef PARANOID
1684 // paranoid check that every block number has at least one DOF associated
1685 // with it
1686 for (unsigned i = 0; i < max_block_number + 1; i++)
1687 {
1688 if (Block_number_to_dof_number_lookup[i].size() == 0)
1689 {
1690 std::ostringstream error_message;
1691 error_message << "block number " << i
1692 << " does not have any DOFs associated with it";
1693 throw OomphLibWarning(error_message.str(),
1696 }
1697 }
1698#endif
1699
1700 // Update the number of blocks types.
1701 Internal_nblock_types = max_block_number + 1;
1702
1703 // Distributed or not, depends on if we have more than one processor.
1704 bool distributed = this->master_distribution_pt()->distributed();
1705
1706 // Create the new block distributions.
1707 Internal_block_distribution_pt.resize(Internal_nblock_types);
1708 for (unsigned i = 0; i < Internal_nblock_types; i++)
1709 {
1710 unsigned block_dim = 0;
1711 for (unsigned j = 0; j < Ndof_in_block[i]; j++)
1712 {
1713 block_dim +=
1714 internal_dof_block_dimension(Block_number_to_dof_number_lookup[i][j]);
1715 }
1716 Internal_block_distribution_pt[i] =
1717 new LinearAlgebraDistribution(comm_pt(), block_dim, distributed);
1718 }
1719
1720 // Work out the distribution of the dof-level blocks.
1721 // Since several dof types may be coarsened into a single dof type.
1722 // We get the dof-level block distributions from the parent preconditioner.
1723
1724 // How many dof types are there?
1725 if (is_subsidiary_block_preconditioner())
1726 {
1727 // Delete any pre-existing distributions.
1728 const unsigned dof_block_distribution_size =
1729 Dof_block_distribution_pt.size();
1730 for (unsigned dof_i = 0; dof_i < dof_block_distribution_size; dof_i++)
1731 {
1732 delete Dof_block_distribution_pt[dof_i];
1733 }
1734 const unsigned ndofs = this->ndof_types();
1735 Dof_block_distribution_pt.resize(ndofs, 0);
1736
1737 // For each dof type, work out how many parent preconditioner dof types
1738 // are in it.
1739 for (unsigned dof_i = 0; dof_i < ndofs; dof_i++)
1740 {
1741 // For each external dof, we get the dofs coarsened into it (from the
1742 // parent preconditioner level, not the most fine grain level).
1743 const unsigned ncoarsened_dofs_in_dof_i =
1744 Doftype_coarsen_map_coarse[dof_i].size();
1746 0);
1748 parent_dof_i++)
1749 {
1751 master_block_preconditioner_pt()->dof_block_distribution_pt(
1752 Doftype_in_master_preconditioner_coarse
1753 [Doftype_coarsen_map_coarse[dof_i][parent_dof_i]]);
1754 }
1755
1756 Dof_block_distribution_pt[dof_i] = new LinearAlgebraDistribution;
1757
1758
1760 tmp_dist_pt, *Dof_block_distribution_pt[dof_i]);
1761 }
1762 }
1763
1764 // Create Block_distribution_pt
1765 {
1766 // Delete any existing distributions in Block_distribution_pt.
1767 // (This should already be deleted in clear_block_preconditioner_base(...)
1768 // but we are just being extra safe!).
1769 unsigned n_existing_block_dist = Block_distribution_pt.size();
1770 for (unsigned dist_i = 0; dist_i < n_existing_block_dist; dist_i++)
1771 {
1772 delete Block_distribution_pt[dist_i];
1773 }
1774
1775 Block_distribution_pt.clear();
1776
1777 // Work out the distributions of the concatenated blocks.
1778 unsigned super_block_size = Block_to_dof_map_coarse.size();
1779 Block_distribution_pt.resize(super_block_size, 0);
1780 for (unsigned super_block_i = 0; super_block_i < super_block_size;
1781 super_block_i++)
1782 {
1783 unsigned sub_block_size = Block_to_dof_map_coarse[super_block_i].size();
1785
1786 for (unsigned sub_block_i = 0; sub_block_i < sub_block_size;
1787 sub_block_i++)
1788 {
1789 tmp_dist_pt[sub_block_i] = dof_block_distribution_pt(
1790 Block_to_dof_map_coarse[super_block_i][sub_block_i]);
1791 }
1792
1793 Block_distribution_pt[super_block_i] = new LinearAlgebraDistribution;
1794
1796 tmp_dist_pt, *Block_distribution_pt[super_block_i]);
1797 }
1798
1799 } // Creating Block_distribution_pt.
1800
1801
1802 // Create the distribution of the preconditioner matrix,
1803 // if this preconditioner is a subsidiary preconditioner then it stored
1804 // at Distribution_pt;
1805 // if this preconditioner is a master preconditioner then it is stored
1806 // at Internal_preconditioner_matrix_distribution_pt.
1807 LinearAlgebraDistribution dist;
1809 Internal_block_distribution_pt, dist);
1810
1811 // Build the distribution.
1812 if (is_subsidiary_block_preconditioner())
1813 {
1814 this->build_distribution(dist);
1815 }
1816 else
1817 {
1818 Internal_preconditioner_matrix_distribution_pt =
1819 new LinearAlgebraDistribution(dist);
1820 }
1821
1822 Preconditioner_matrix_distribution_pt = new LinearAlgebraDistribution;
1824 Block_distribution_pt, *Preconditioner_matrix_distribution_pt);
1825
1826 // Clear all distributions in Auxiliary_block_distribution_pt, except for
1827 // the one which corresponds to the preconditioner matrix distribution. This
1828 // is already deleted by clear_block_preconditioner_base(...)
1829
1830 // Create the key which corresponds to
1831 // preconditioner_matrix_distribution_pt.
1832 {
1833 const unsigned nblocks = Block_distribution_pt.size();
1834 Vector<unsigned> preconditioner_matrix_key(nblocks, 0);
1835 for (unsigned i = 0; i < nblocks; i++)
1836 {
1838 }
1839
1840 // Now iterate through Auxiliary_block_distribution_pt and delete
1841 // everything except for the value which corresponds to
1842 // preconditioner_matrix_key.
1843 std::map<Vector<unsigned>, LinearAlgebraDistribution*>::iterator iter =
1844 Auxiliary_block_distribution_pt.begin();
1845 while (iter != Auxiliary_block_distribution_pt.end())
1846 {
1847 if (iter->first != preconditioner_matrix_key)
1848 {
1849 delete iter->second;
1850 iter++;
1851 }
1852 else
1853 {
1854 ++iter;
1855 }
1856 }
1857
1858 // Clear it just to be safe!
1859 Auxiliary_block_distribution_pt.clear();
1860
1861 // Insert the preconditioner matrix distribution.
1862 insert_auxiliary_block_distribution(
1863 preconditioner_matrix_key, Preconditioner_matrix_distribution_pt);
1864 } // End of Auxiliary_block_distribution_pt encapsulation.
1865
1866 // Clearing up after comm to assemble sparse lookup schemes.
1867#ifdef OOMPH_HAS_MPI
1868 if (send_requests_sparse.size() > 0)
1869 {
1873 }
1874 if (recv_requests_sparse.size() > 0)
1875 {
1879 }
1880 for (unsigned p = 0; p < nproc; p++)
1881 {
1883 delete[] dof_number_sparse_send[p];
1884 }
1886 delete[] dof_number_sparse_send;
1887 delete[] nreq_sparse;
1888 delete[] nreq_sparse_for_proc;
1889#endif
1890
1891 // Next we assemble the lookup schemes for the rows
1892 // if the matrix is not distributed then we assemble Global_index
1893 // if the matrix is distributed then Rows_to_send_..., Rows_to_recv_... etc.
1894 if (!distributed)
1895 {
1896 // Resize the storage.
1897 Global_index.resize(Internal_nblock_types);
1898 for (unsigned b = 0; b < Internal_nblock_types; b++)
1899 {
1900 Global_index[b].resize(Internal_block_distribution_pt[b]->nrow());
1901 }
1902
1903 // Compute:
1904 unsigned nrow = this->master_nrow();
1905 for (unsigned i = 0; i < nrow; i++)
1906 {
1907 // the dof type number;
1908 int dof_number = this->internal_dof_number(i);
1909 if (dof_number >= 0)
1910 {
1911 // the block number;
1912 unsigned block_number = Dof_number_to_block_number_lookup[dof_number];
1913
1914 // the index in the block.
1915 unsigned index_in_block = 0;
1916 unsigned ptr = 0;
1917 while (int(Block_number_to_dof_number_lookup[block_number][ptr]) !=
1918 dof_number)
1919 {
1920 index_in_block += internal_dof_block_dimension(
1921 Block_number_to_dof_number_lookup[block_number][ptr]);
1922 ptr++;
1923 }
1924 index_in_block += internal_index_in_dof(i);
1925 Global_index[block_number][index_in_block] = i;
1926 }
1927 }
1928 }
1929 // otherwise the matrix is distributed
1930 else
1931 {
1932#ifdef OOMPH_HAS_MPI
1933
1934 // the pointer to the master distribution
1935 const LinearAlgebraDistribution* master_distribution_pt =
1936 this->master_distribution_pt();
1937
1938 // resize the nrows... storage
1939 Nrows_to_send_for_get_block.resize(Internal_nblock_types, nproc);
1940 Nrows_to_send_for_get_block.initialise(0);
1941 Nrows_to_send_for_get_ordered.resize(nproc);
1942 Nrows_to_send_for_get_ordered.initialise(0);
1943
1944 // loop over my rows
1945 unsigned nrow_local = master_distribution_pt->nrow_local();
1946 unsigned first_row = master_distribution_pt->first_row();
1947 for (unsigned i = 0; i < nrow_local; i++)
1948 {
1949 // the block number
1950 int b = this->internal_block_number(first_row + i);
1951
1952 // check that the DOF i is associated with this preconditioner
1953 if (b >= 0)
1954 {
1955 // the block index
1956 unsigned j = this->internal_index_in_block(first_row + i);
1957
1958 // the processor this row will be sent to
1959 unsigned block_p = 0;
1960 while (!(Internal_block_distribution_pt[b]->first_row(block_p) <= j &&
1961 (Internal_block_distribution_pt[b]->first_row(block_p) +
1962 Internal_block_distribution_pt[b]->nrow_local(block_p) >
1963 j)))
1964 {
1965 block_p++;
1966 }
1967
1968 // and increment the counter
1969 Nrows_to_send_for_get_block(b, block_p)++;
1970 Nrows_to_send_for_get_ordered[block_p]++;
1971 }
1972 }
1973
1974 // resize the storage for Nrows_to_recv
1975 Nrows_to_recv_for_get_block.resize(Internal_nblock_types, nproc);
1976 Nrows_to_recv_for_get_block.initialise(0);
1977 Nrows_to_recv_for_get_ordered.resize(nproc);
1978 Nrows_to_recv_for_get_ordered.initialise(0);
1979
1980 // next we send the number of rows that will be sent by this processor
1985 Vector<unsigned> proc;
1986 for (unsigned p = 0; p < nproc; p++)
1987 {
1988 if (p != my_rank)
1989 {
1990 // send
1991 proc.push_back(p);
1992 nrows_to_send[p] = new unsigned[Internal_nblock_types];
1993 for (unsigned b = 0; b < Internal_nblock_types; b++)
1994 {
1995 nrows_to_send[p][b] = Nrows_to_send_for_get_block(b, p);
1996 }
1999 Internal_nblock_types,
2001 p,
2002 3,
2003 comm_pt()->mpi_comm(),
2004 &s_req);
2005 send_requests_nrow.push_back(s_req);
2006
2007 // recv
2008 nrows_to_recv[p] = new unsigned[Internal_nblock_types];
2011 Internal_nblock_types,
2013 p,
2014 3,
2015 comm_pt()->mpi_comm(),
2016 &r_req);
2017 recv_requests_nrow.push_back(r_req);
2018 }
2019 // send to self
2020 else
2021 {
2022 for (unsigned b = 0; b < Internal_nblock_types; b++)
2023 {
2024 Nrows_to_recv_for_get_block(b, p) =
2025 Nrows_to_send_for_get_block(b, p);
2026 }
2027 Nrows_to_recv_for_get_ordered[p] = Nrows_to_send_for_get_ordered[p];
2028 }
2029 }
2030
2031 // create some temporary storage for the global row indices that will
2032 // be received from another processor.
2033 DenseMatrix<int*> block_rows_to_send(Internal_nblock_types, nproc, 0);
2034 Vector<int*> ordered_rows_to_send(nproc, 0);
2035
2036 // resize the rows... storage
2037 Rows_to_send_for_get_block.resize(Internal_nblock_types, nproc);
2038 Rows_to_send_for_get_block.initialise(0);
2039 Rows_to_send_for_get_ordered.resize(nproc);
2040 Rows_to_send_for_get_ordered.initialise(0);
2041 Rows_to_recv_for_get_block.resize(Internal_nblock_types, nproc);
2042 Rows_to_recv_for_get_block.initialise(0);
2043
2044 // resize the storage
2045 for (unsigned p = 0; p < nproc; p++)
2046 {
2047 for (unsigned b = 0; b < Internal_nblock_types; b++)
2048 {
2049 Rows_to_send_for_get_block(b, p) =
2050 new int[Nrows_to_send_for_get_block(b, p)];
2051 if (p != my_rank)
2052 {
2053 block_rows_to_send(b, p) =
2054 new int[Nrows_to_send_for_get_block(b, p)];
2055 }
2056 else
2057 {
2058 Rows_to_recv_for_get_block(b, p) =
2059 new int[Nrows_to_send_for_get_block(b, p)];
2060 }
2061 }
2062 Rows_to_send_for_get_ordered[p] =
2063 new int[Nrows_to_send_for_get_ordered[p]];
2064 }
2065
2066
2067 // loop over my rows to allocate the nrows
2068 DenseMatrix<unsigned> ptr_block(Internal_nblock_types, nproc, 0);
2069 for (unsigned i = 0; i < nrow_local; i++)
2070 {
2071 // the block number
2072 int b = this->internal_block_number(first_row + i);
2073
2074 // check that the DOF i is associated with this preconditioner
2075 if (b >= 0)
2076 {
2077 // the block index
2078 unsigned j = this->internal_index_in_block(first_row + i);
2079
2080 // the processor this row will be sent to
2081 unsigned block_p = 0;
2082 while (!(Internal_block_distribution_pt[b]->first_row(block_p) <= j &&
2083 (Internal_block_distribution_pt[b]->first_row(block_p) +
2084 Internal_block_distribution_pt[b]->nrow_local(block_p) >
2085 j)))
2086 {
2087 block_p++;
2088 }
2089
2090 // and store the row
2091 Rows_to_send_for_get_block(b, block_p)[ptr_block(b, block_p)] = i;
2092 if (block_p != my_rank)
2093 {
2095 j - Internal_block_distribution_pt[b]->first_row(block_p);
2096 }
2097 else
2098 {
2099 Rows_to_recv_for_get_block(b, block_p)[ptr_block(b, block_p)] =
2100 j - Internal_block_distribution_pt[b]->first_row(block_p);
2101 }
2102 ptr_block(b, block_p)++;
2103 }
2104 }
2105
2106 // next block ordered
2107 for (unsigned p = 0; p < nproc; ++p)
2108 {
2109 int pt = 0;
2110 for (unsigned b = 0; b < Internal_nblock_types; ++b)
2111 {
2112 for (unsigned i = 0; i < Nrows_to_send_for_get_block(b, p); ++i)
2113 {
2114 Rows_to_send_for_get_ordered[p][pt] =
2115 Rows_to_send_for_get_block(b, p)[i];
2116 pt++;
2117 }
2118 }
2119 }
2120
2121 // next process the nrow recvs as they complete
2122
2123 // recv and store the data
2124 unsigned c = recv_requests_nrow.size();
2125 while (c > 0)
2126 {
2127 // wait for any communication to finish
2128 int req_number;
2131 c--;
2132
2133 // determine the source processor
2134 unsigned p = proc[req_number];
2135 proc.erase(proc.begin() + req_number);
2136
2137 // copy the data to its final storage
2138 Nrows_to_recv_for_get_ordered[p] = 0;
2139 for (unsigned b = 0; b < Internal_nblock_types; b++)
2140 {
2141 Nrows_to_recv_for_get_block(b, p) = nrows_to_recv[p][b];
2142 Nrows_to_recv_for_get_ordered[p] += nrows_to_recv[p][b];
2143 }
2144
2145 // and clear
2146 delete[] nrows_to_recv[p];
2147 }
2148
2149 // resize the storage for the incoming rows data
2150 Rows_to_recv_for_get_ordered.resize(nproc, 0);
2151 for (unsigned p = 0; p < nproc; p++)
2152 {
2153 if (p != my_rank)
2154 {
2155 for (unsigned b = 0; b < Internal_nblock_types; b++)
2156 {
2157 Rows_to_recv_for_get_block(b, p) =
2158 new int[Nrows_to_recv_for_get_block(b, p)];
2159 }
2160 }
2161 }
2162
2163 // compute the number of sends and recv from this processor
2164 // to each other processor
2165 Vector<unsigned> nsend_for_rows(nproc, 0);
2166 Vector<unsigned> nrecv_for_rows(nproc, 0);
2167 for (unsigned p = 0; p < nproc; p++)
2168 {
2169 if (p != my_rank)
2170 {
2171 for (unsigned b = 0; b < Internal_nblock_types; b++)
2172 {
2173 if (Nrows_to_send_for_get_block(b, p) > 0)
2174 {
2175 nsend_for_rows[p]++;
2176 }
2177 if (Nrows_to_recv_for_get_block(b, p) > 0)
2178 {
2179 nrecv_for_rows[p]++;
2180 }
2181 }
2182 }
2183 }
2184
2185 // finally post the sends and recvs
2187 MPI_Get_address(matrix_pt(), &base_displacement);
2189 for (unsigned p = 0; p < nproc; p++)
2190 {
2191 if (p != my_rank)
2192 {
2193 // send
2194 if (nsend_for_rows[p] > 0)
2195 {
2198 int send_sz[nsend_for_rows[p]];
2199 unsigned send_ptr = 0;
2200 for (unsigned b = 0; b < Internal_nblock_types; b++)
2201 {
2202 if (Nrows_to_send_for_get_block(b, p) > 0)
2203 {
2204 MPI_Type_contiguous(Nrows_to_send_for_get_block(b, p),
2205 MPI_INT,
2211 send_sz[send_ptr] = 1;
2212 send_ptr++;
2213 }
2214 }
2217 send_sz,
2219 send_types,
2222 for (unsigned i = 0; i < nsend_for_rows[p]; i++)
2223 {
2225 }
2227 MPI_Isend(matrix_pt(),
2228 1,
2230 p,
2231 4,
2232 comm_pt()->mpi_comm(),
2233 &send_req);
2234 req_rows.push_back(send_req);
2236 }
2237
2238 // recv
2239 if (nrecv_for_rows[p] > 0)
2240 {
2243 int recv_sz[nrecv_for_rows[p]];
2244 unsigned recv_ptr = 0;
2245 for (unsigned b = 0; b < Internal_nblock_types; b++)
2246 {
2247 if (Nrows_to_recv_for_get_block(b, p) > 0)
2248 {
2249 MPI_Type_contiguous(Nrows_to_recv_for_get_block(b, p),
2250 MPI_INT,
2253 MPI_Get_address(Rows_to_recv_for_get_block(b, p),
2256 recv_sz[recv_ptr] = 1;
2257 recv_ptr++;
2258 }
2259 }
2262 recv_sz,
2264 recv_types,
2267 for (unsigned i = 0; i < nrecv_for_rows[p]; i++)
2268 {
2270 }
2272 MPI_Irecv(matrix_pt(),
2273 1,
2275 p,
2276 4,
2277 comm_pt()->mpi_comm(),
2278 &recv_req);
2279 req_rows.push_back(recv_req);
2281 }
2282 }
2283 }
2284
2285 // cleaning up Waitalls
2286
2287
2288 // wait for the recv requests so we can compute
2289 // Nrows_to_recv_for_get_ordered
2290 unsigned n_req_rows = req_rows.size();
2291 if (n_req_rows)
2292 {
2294 }
2295
2296 // resize the storage
2297 Rows_to_recv_for_get_ordered.resize(nproc);
2298 Rows_to_recv_for_get_ordered.initialise(0);
2299
2300 // construct block offset
2301 Vector<int> vec_offset(Internal_nblock_types, 0);
2302 for (unsigned b = 1; b < Internal_nblock_types; ++b)
2303 {
2304 vec_offset[b] = vec_offset[b - 1] +
2305 Internal_block_distribution_pt[b - 1]->nrow_local();
2306 }
2307
2308 //
2309 for (unsigned p = 0; p < nproc; p++)
2310 {
2311 int pt = 0;
2312 Rows_to_recv_for_get_ordered[p] =
2313 new int[Nrows_to_recv_for_get_ordered[p]];
2314 for (unsigned b = 0; b < Internal_nblock_types; b++)
2315 {
2316 for (unsigned i = 0; i < Nrows_to_recv_for_get_block(b, p); i++)
2317 {
2318 Rows_to_recv_for_get_ordered[p][pt] =
2319 Rows_to_recv_for_get_block(b, p)[i] + vec_offset[b];
2320 pt++;
2321 }
2322 }
2323 }
2324
2325 // clean up
2326 for (unsigned p = 0; p < nproc; p++)
2327 {
2328 if (p != my_rank)
2329 {
2330 for (unsigned b = 0; b < Internal_nblock_types; b++)
2331 {
2332 delete[] block_rows_to_send(b, p);
2333 }
2334 if (Nrows_to_send_for_get_ordered[p] > 0)
2335 {
2336 delete[] ordered_rows_to_send[p];
2337 }
2338 }
2339 }
2340
2341 // and the send reqs
2342 unsigned n_req_send_nrow = send_requests_nrow.size();
2343 if (n_req_send_nrow)
2344 {
2346 }
2347 for (unsigned p = 0; p < nproc; p++)
2348 {
2349 delete[] nrows_to_send[p];
2350 }
2351#endif
2352 }
2353
2354 // If we asked for output of blocks to a file then do it.
2355 if (block_output_on()) output_blocks_to_files(Output_base_filename);
2356 }
2357
2358 //============================================================================
2359 //??ds
2360 /// Function to turn this preconditioner into a
2361 /// subsidiary preconditioner that operates within a bigger
2362 /// "master block preconditioner (e.g. a Navier-Stokes 2x2 block
2363 /// preconditioner dealing with the fluid sub-blocks within a
2364 /// 3x3 FSI preconditioner. Once this is done the master block
2365 /// preconditioner deals with the block setup etc.
2366 /// The vector block_map must specify the dof number in the
2367 /// master preconditioner that corresponds to a block number in this
2368 /// preconditioner. ??ds horribly misleading comment!
2369 /// The length of the vector is used to determine the number of
2370 /// blocks in this preconditioner therefore it must be correctly sized.
2371 /// This calls the other turn_into_subsidiary_block_preconditioner(...)
2372 /// function providing an empty doftype_to_doftype_map vector.
2373 //============================================================================
2374 template<typename MATRIX>
2378 {
2379 // Create the identity dof_coarsen_map
2383
2384 for (unsigned dof_i = 0;
2386 dof_i++)
2387 {
2388 // Create a vector of size 1 and value i,
2389 // then push it into the dof_coarsen_map vector.
2392 }
2393
2394 // Call the other turn_into_subsidiary_block_preconditioner function.
2395 turn_into_subsidiary_block_preconditioner(
2399 }
2400
2401
2402 //============================================================================
2403 /// Function to turn this block preconditioner into a
2404 /// subsidiary block preconditioner that operates within a bigger
2405 /// master block preconditioner (e.g. a Navier-Stokes 2x2 block
2406 /// preconditioner dealing with the fluid sub-blocks within a
2407 /// 3x3 FSI preconditioner. Once this is done the master block
2408 /// preconditioner deals with the block setup etc.
2409 ///
2410 /// The vector doftype_map must specify the dof type in the
2411 /// master preconditioner that corresponds to a dof type in this block
2412 /// preconditioner.
2413 ///
2414 /// In general, we want:
2415 /// doftype_map[doftype in subsidiary prec] = doftype in master prec.
2416 ///
2417 /// It tells this block preconditioner which dof types of the master
2418 /// block preconditioner it is working with.
2419 ///
2420 /// The length of the vector is used to determine the number of
2421 /// dof types in THIS block preconditioner therefore it must be correctly
2422 /// sized.
2423 ///
2424 /// For example, let the master block preconditioner have 5 dof types in total
2425 /// and a 1-4 dof type splitting where the block (0,0) corresponds to
2426 /// dof type 0 and the block (1,1) corresponds to dof types 1, 2, 3 and 4
2427 /// (i.e. it would have given to block_setup the vector [0,1,1,1,1]).
2428 /// Furthermore, it solves (1,1) block with subsidiary block preconditioner.
2429 /// Then the doftype_map passed to this function of the subsidiary block
2430 /// preconditioner would be [1, 2, 3, 4].
2431 ///
2432 /// Dof type coarsening (following on from the example above):
2433 /// Let the subsidiary block preconditioner (THIS block preconditioner)
2434 /// only works with two DOF types, then the master block preconditioner must
2435 /// "coarsen" the dof types by providing the optional argument
2436 /// doftype_coarsen_map vector.
2437 ///
2438 /// The doftype_coarsen_map vector (in this case) might be [[0,1], [2,3]]
2439 /// telling the subsidiary block preconditioner that the SUBSIDIARY dof types
2440 /// 0 and 1 should be treated as dof type 0 and the subsidiary dof types 2
2441 /// and 3 should be treated as subsidiary dof type 1.
2442 ///
2443 /// If no doftype_coarsen_map vector is provided, then the identity is
2444 /// used automatically (see the turn_into_subsidiary_block_preconditioner(...)
2445 /// function with only two arguments). In the above case, the identity
2446 /// doftype_coarsen_map vector for the subsidiary block preconditioner
2447 /// would be the 2D vector [[0], [1], [2], [3]] which means
2448 /// dof type 0 is treated as dof type 0,
2449 /// dof type 1 is treated as dof type 1,
2450 /// dof type 2 is treated as dof type 2, and
2451 /// dof type 3 is treated as dof type 3.
2452 //============================================================================
2453 template<typename MATRIX>
2458 {
2459 // Set the master block preconditioner pointer
2460 Master_block_preconditioner_pt = master_block_prec_pt;
2461
2462 // Set the Doftype_coarsen_map_coarse.
2463 Doftype_coarsen_map_coarse = doftype_coarsen_map_coarse;
2464
2465 Doftype_in_master_preconditioner_coarse =
2467 } // end of turn_into_subsidiary_block_preconditioner(...)
2468
2469
2470 //============================================================================
2471 /// Determine the size of the matrix blocks and setup the
2472 /// lookup schemes relating the global degrees of freedom with
2473 /// their "blocks" and their indices (row/column numbers) in those
2474 /// blocks.
2475 /// The distributions of the preconditioner and the blocks are
2476 /// automatically specified (and assumed to be uniform) at this
2477 /// stage.
2478 /// This method should be used if each DOF type corresponds to a
2479 /// unique block type.
2480 //============================================================================
2481 template<typename MATRIX>
2483 {
2484#ifdef PARANOID
2485
2486 // Subsidiary preconditioners don't really need the meshes
2487 if (this->is_master_block_preconditioner())
2488 {
2489 std::ostringstream err_msg;
2490 unsigned n = nmesh();
2491 if (n == 0)
2492 {
2493 err_msg << "No meshes have been set for this block preconditioner!\n"
2494 << "Set one with set_nmesh(...), set_mesh(...)" << std::endl;
2495 throw OomphLibError(
2497 for (unsigned m = 0; m < n; m++)
2498 {
2499 if (Mesh_pt[m] == 0)
2500 {
2501 err_msg << "The mesh pointer to mesh " << m << " is null!\n"
2502 << "Set a non-null one with set_mesh(...)" << std::endl;
2503 throw OomphLibError(
2505 }
2506 }
2507 }
2508 }
2509#endif
2510
2511 // Get the number of dof types.
2512 unsigned internal_n_dof_types = ndof_types();
2513
2514 // Build the dof to block map - assume that each type of dof corresponds
2515 // to a different type of block.
2516 Vector<unsigned> dof_to_block_lookup(internal_n_dof_types);
2517 for (unsigned i = 0; i < internal_n_dof_types; i++)
2518 {
2520 }
2521
2522 // call the block setup method
2523 this->block_setup(dof_to_block_lookup);
2524 }
2525
2526
2527 //============================================================================
2528 /// Get the block matrices required for the block preconditioner. Takes a
2529 /// pointer to a matrix of bools that indicate if a specified sub-block is
2530 /// required for the preconditioning operation. Computes the required block
2531 /// matrices, and stores pointers to them in the matrix block_matrix_pt. If an
2532 /// entry in block_matrix_pt is equal to NULL that sub-block has not been
2533 /// requested and is therefore not available.
2534 //============================================================================
2535 template<typename MATRIX>
2539 {
2540 // Cache number of block types
2541 const unsigned n_block_types = nblock_types();
2542
2543#ifdef PARANOID
2544 // If required blocks matrix pointer is not the correct size then abort.
2545 if ((required_blocks.nrow() != n_block_types) ||
2546 (required_blocks.ncol() != n_block_types))
2547 {
2548 std::ostringstream error_message;
2549 error_message << "The size of the matrix of bools required_blocks "
2550 << "(which indicates which blocks are required) is not the "
2551 << "right size, required_blocks is "
2552 << required_blocks.ncol() << " x " << required_blocks.nrow()
2553 << ", whereas it should "
2554 << "be " << n_block_types << " x " << n_block_types;
2555 throw OomphLibError(
2556 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2557 }
2558
2559 // If block matrix pointer is not the correct size then abort.
2560 if ((block_matrix_pt.nrow() != n_block_types) ||
2561 (block_matrix_pt.ncol() != n_block_types))
2562 {
2563 std::ostringstream error_message;
2564 error_message << "The size of the block matrix pt is not the "
2565 << "right size, block_matrix_pt is "
2566 << block_matrix_pt.ncol() << " x " << block_matrix_pt.nrow()
2567 << ", whereas it should "
2568 << "be " << n_block_types << " x " << n_block_types;
2569 throw OomphLibError(
2570 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2571 }
2572
2573#endif
2574
2575 // Loop over the blocks
2576 for (unsigned i = 0; i < n_block_types; i++)
2577 {
2578 for (unsigned j = 0; j < n_block_types; j++)
2579 {
2580 // If block(i,j) is required then create a matrix and fill it in.
2581 if (required_blocks(i, j))
2582 {
2583 //??ds might want to remove this use of new as well?
2584 block_matrix_pt(i, j) = new MATRIX;
2585 get_block(i, j, *block_matrix_pt(i, j));
2586 }
2587
2588 // Otherwise set pointer to null.
2589 else
2590 {
2591 block_matrix_pt(i, j) = 0;
2592 }
2593 }
2594 }
2595 }
2596
2597 //============================================================================
2598 /// Takes the naturally ordered vector and extracts the blocks
2599 /// indicated by the block number (the values) in the Vector
2600 /// block_vec_number all at once, then concatenates them without
2601 /// communication. Here, the values in block_vec_number is the block number
2602 /// in the current preconditioner.
2603 /// This is a non-const function because distributions may be created
2604 /// and stored in Auxiliary_block_distribution_pt for future use.
2605 //============================================================================
2606 template<typename MATRIX>
2609 const DoubleVector& v,
2610 DoubleVector& w)
2611 {
2612#ifdef PARANOID
2613
2614 // Check if v is built.
2615 if (!v.built())
2616 {
2617 std::ostringstream err_msg;
2618 err_msg << "The distribution of the global vector v must be setup.";
2619 throw OomphLibError(
2621 }
2622
2623 // v must have the same distribution as the upper-most master block
2624 // preconditioner, since the upper-most master block preconditioner
2625 // should have the same distribution as the matrix pointed to
2626 // by matrix_pt().
2627 if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
2628 {
2629 std::ostringstream err_msg;
2630 err_msg << "The distribution of the global vector v must match the "
2631 << " specified master_distribution_pt(). \n"
2632 << "i.e. Distribution_pt in the master preconditioner";
2633 throw OomphLibError(
2635 }
2636
2637 // Check to see if there are more blocks defined in the block_vec_number
2638 // vector than the number of block types. This is not allowed.
2639 const unsigned para_nblock_types = nblock_types();
2642 {
2643 std::ostringstream err_msg;
2644 err_msg << "You have requested " << para_block_vec_number_size
2645 << " number of blocks, (block_vec_number.size() is "
2646 << para_block_vec_number_size << ").\n"
2647 << "But there are only " << para_nblock_types
2648 << " nblock_types.\n"
2649 << "Please make sure that block_vec_number is correctly sized.\n";
2650 throw OomphLibError(
2652 }
2654 // Check if any block numbers defined in block_vec_number is equal to or
2655 // greater than the number of block types.
2656 // E.g. if there are 5 block types, we can only have block numbers:
2657 // 0, 1, 2, 3 and 4.
2658 for (unsigned i = 0; i < para_block_vec_number_size; i++)
2659 {
2662 {
2663 std::ostringstream err_msg;
2664 err_msg << "block_vec_number[" << i << "] is " << para_required_block
2665 << ".\n"
2666 << "But there are only " << para_nblock_types
2667 << " nblock_types.\n";
2668 throw OomphLibError(
2670 }
2672
2673 // Check that no block number is inserted twice.
2674 std::set<unsigned> para_set;
2675 for (unsigned b = 0; b < para_block_vec_number_size; b++)
2676 {
2677 std::pair<std::set<unsigned>::iterator, bool> para_set_ret;
2679
2680 if (!para_set_ret.second)
2681 {
2682 std::ostringstream err_msg;
2683 err_msg << "Error: the block number " << block_vec_number[b]
2684 << " appears twice.\n";
2685 throw OomphLibError(
2687 }
2688 }
2689#endif
2690
2691 // Number of blocks to get.
2692 const unsigned n_block = block_vec_number.size();
2693
2694 // Each block is made of dof types. We get the most fine grain dof types.
2695 // Most fine grain in the sense that these are the dof types that belongs
2696 // in this block before any coarsening of dof types has taken place.
2697 // The ordering of the dof types matters, this is handled properly when
2698 // creating the Block_to_dof_map_fine vector and must be respected here.
2699 // I.e. we cannot arbitrarily insert dof types (even if they are correct)
2700 // in the vector most_fine_grain_dof.
2701 Vector<unsigned> most_fine_grain_dof;
2702 for (unsigned b = 0; b < n_block; b++)
2703 {
2704 const unsigned mapped_b = block_vec_number[b];
2706 Block_to_dof_map_fine[mapped_b].begin(),
2707 Block_to_dof_map_fine[mapped_b].end());
2708 }
2709
2710 // Get all the dof level vectors in one go.
2712 internal_get_block_vectors(most_fine_grain_dof, v, dof_block_vector);
2713
2714 // Next we need to build the output DoubleVector w with the correct
2715 // distribution: the concatenation of the distributions of all the
2716 // dof-level vectors. This is the same as the concatenation of the
2717 // distributions of the blocks within this preconditioner.
2718 //
2719 // So we first check if it exists already, if not, we create it and
2720 // store it for future use. We store it because concatenation of
2721 // distributions requires communication, so concatenation of
2722 // distributions on-the-fly should be avoided.
2723 std::map<Vector<unsigned>, LinearAlgebraDistribution*>::const_iterator iter;
2724
2725 // Attempt to get an iterator pointing to the pair with the value
2726 // block_vec_number.
2727 iter = Auxiliary_block_distribution_pt.find(block_vec_number);
2728
2729 if (iter != Auxiliary_block_distribution_pt.end())
2730 // If it exists, build w with the distribution pointed to
2731 // by pair::second.
2732 {
2733 w.build(iter->second);
2734 }
2735 else
2736 // Else, we need to create the distribution and store it in
2737 // Auxiliary_block_distribution_pt.
2738 {
2740 for (unsigned b = 0; b < n_block; b++)
2741 {
2742 tmp_vec_dist_pt[b] = Block_distribution_pt[block_vec_number[b]];
2743 }
2744
2745 // Note that the distribution is created with new but not deleted here.
2746 // This is handled in the clean up functions.
2747 LinearAlgebraDistribution* tmp_dist_pt = new LinearAlgebraDistribution;
2749 *tmp_dist_pt);
2750
2751 // Store the pair of Vector<unsigned> and LinearAlgebraDistribution*
2752 insert_auxiliary_block_distribution(block_vec_number, tmp_dist_pt);
2753
2754 // Build w.
2755 w.build(tmp_dist_pt);
2756 }
2757
2758 // Now concatenate all the dof level vectors into the vector w.
2760
2761 } // get_concatenated_block_vector(...)
2762
2763 //============================================================================
2764 /// Takes concatenated block ordered vector, b, and copies its
2765 // entries to the appropriate entries in the naturally ordered vector, v.
2766 // Here the values in block_vec_number indicates which blocks the vector
2767 // b is a concatenation of. The block number are those in the current
2768 // preconditioner. If the preconditioner is a subsidiary block
2769 // preconditioner the other entries in v that are not associated with it
2770 // are left alone.
2771 //============================================================================
2772 template<typename MATRIX>
2775 const DoubleVector& w,
2776 DoubleVector& v) const
2777 {
2778#ifdef PARANOID
2779
2780 // Check if v is built.
2781 if (!v.built())
2782 {
2783 std::ostringstream err_msg;
2784 err_msg << "The distribution of the global vector v must be setup.";
2785 throw OomphLibError(
2787 }
2788
2789 // v must have the same distribution as the upper-most master block
2790 // preconditioner, since the upper-most master block preconditioner
2791 // should have the same distribution as the matrix pointed to
2792 // by matrix_pt().
2793 if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
2794 {
2795 std::ostringstream err_msg;
2796 err_msg << "The distribution of the global vector v must match the "
2797 << " specified master_distribution_pt(). \n"
2798 << "i.e. Distribution_pt in the master preconditioner";
2799 throw OomphLibError(
2801 }
2802
2803 // Check to see if there are more blocks defined in the block_vec_number
2804 // vector than the number of block types. This is not allowed.
2806 const unsigned para_n_block = nblock_types();
2808 {
2809 std::ostringstream err_msg;
2810 err_msg << "Trying to return " << para_block_vec_number_size
2811 << " block vectors.\n"
2812 << "But there are only " << para_n_block << " block types.\n";
2813 throw OomphLibError(
2815 }
2816
2817 // Check if any block numbers defined in block_vec_number is equal to or
2818 // greater than the number of block types.
2819 // E.g. if there are 5 block types, we can only have block numbers:
2820 // 0, 1, 2, 3 and 4.
2821 for (unsigned b = 0; b < para_block_vec_number_size; b++)
2822 {
2823 const unsigned para_required_block = block_vec_number[b];
2825 {
2826 std::ostringstream err_msg;
2827 err_msg << "block_vec_number[" << b << "] is " << para_required_block
2828 << ".\n"
2829 << "But there are only " << para_n_block << " block types.\n";
2830 throw OomphLibError(
2832 }
2833 }
2834
2835 // Check that no block number is inserted twice.
2836 std::set<unsigned> para_set;
2837 for (unsigned b = 0; b < para_block_vec_number_size; b++)
2838 {
2839 std::pair<std::set<unsigned>::iterator, bool> para_set_ret;
2841
2842 if (!para_set_ret.second)
2843 {
2844 std::ostringstream err_msg;
2845 err_msg << "Error: the block number " << block_vec_number[b]
2846 << " appears twice.\n";
2847 throw OomphLibError(
2849 }
2850 }
2851
2852 // Check that w is built.
2853 if (!w.built())
2854 {
2855 std::ostringstream err_msg;
2856 err_msg << "The distribution of the block vector w must be setup.";
2857 throw OomphLibError(
2859 }
2860
2861 // Check that the distributions defined by block_vec_number is correct for
2862 // the distribution from w.
2863 // Recall that w is the concatenation of the block vectors defined by
2864 // the values in block_vec_number. We check that this is the case.
2867
2868 for (unsigned b = 0; b < para_block_vec_number_size; b++)
2869 {
2870 para_vec_dist_pt[b] = Block_distribution_pt[block_vec_number[b]];
2871 }
2872
2873 LinearAlgebraDistribution para_tmp_dist;
2874
2877
2878 if (*w.distribution_pt() != para_tmp_dist)
2879 {
2880 std::ostringstream err_msg;
2881 err_msg << "The distribution of the block vector w does not match \n"
2882 << "the concatenation of the block distributions defined in \n"
2883 << "block_vec_number.\n";
2884 throw OomphLibError(
2886 }
2887#endif
2888
2889 // Number of blocks to return.
2890 const unsigned n_block = block_vec_number.size();
2891
2892 // Each block is made of dof types. We get the most fine grain dof types.
2893 // Most fine grain in the sense that these are the dof types that belongs
2894 // in this block before any coarsening of dof types has taken place.
2895 // The ordering of the dof types matters, this is handled properly when
2896 // creating the Block_to_dof_map_fine vector and must be respected here.
2897 // I.e. we cannot arbitrarily insert dof types (even if they are correct)
2898 // in the vector most_fine_grain_dof.
2899 Vector<unsigned> most_fine_grain_dof;
2900 for (unsigned b = 0; b < n_block; b++)
2901 {
2902 const unsigned mapped_b = block_vec_number[b];
2904 Block_to_dof_map_fine[mapped_b].begin(),
2905 Block_to_dof_map_fine[mapped_b].end());
2906 }
2907
2908 // The number of most fine grain dof types associated with the blocks
2909 // defined by block_vec_number.
2910 const unsigned ndof = most_fine_grain_dof.size();
2911
2912 // Build each dof level vector with the correct distribution.
2914 for (unsigned d = 0; d < ndof; d++)
2915 {
2916 dof_vector[d].build(
2917 internal_block_distribution_pt(most_fine_grain_dof[d]));
2918 }
2919
2920 // Perform the splitting of w into the most fine grain dof level vectors.
2922
2923 // Return all the dof level vectors in one go.
2924 internal_return_block_vectors(most_fine_grain_dof, dof_vector, v);
2925 } // return_concatenated_block_vector(...)
2926
2927 //============================================================================
2928 /// Takes the naturally ordered vector and rearranges it into a
2929 /// vector of sub vectors corresponding to the blocks, so s[b][i] contains
2930 /// the i-th entry in the vector associated with block b.
2931 /// Note: If the preconditioner is a subsidiary preconditioner then only the
2932 /// sub-vectors associated with the blocks of the subsidiary preconditioner
2933 /// will be included. Hence the length of v is master_nrow() whereas the
2934 /// total length of the s vectors is the sum of the lengths of the
2935 /// individual block vectors defined in block_vec_number.
2936 //============================================================================
2937 template<typename MATRIX>
2940 const DoubleVector& v,
2941 Vector<DoubleVector>& s) const
2942 {
2943#ifdef PARANOID
2944
2945 // Check if v is built.
2946 if (!v.built())
2947 {
2948 std::ostringstream err_msg;
2949 err_msg << "The distribution of the global vector v must be setup.";
2950 throw OomphLibError(
2952 }
2953
2954 // v must have the same distribution as the upper-most master block
2955 // preconditioner, since the upper-most master block preconditioner
2956 // should have the same distribution as the matrix pointed to
2957 // by matrix_pt().
2958 if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
2959 {
2960 std::ostringstream err_msg;
2961 err_msg << "The distribution of the global vector v must match the "
2962 << " specified master_distribution_pt(). \n"
2963 << "i.e. Distribution_pt in the master preconditioner";
2964 throw OomphLibError(
2966 }
2967
2968 // Check to see if there are more blocks defined in the block_vec_number
2969 // vector than the number of block types. This is not allowed.
2970 const unsigned para_nblock_types = nblock_types();
2973 {
2974 std::ostringstream err_msg;
2975 err_msg << "You have requested " << para_block_vec_number_size
2976 << " number of blocks, (block_vec_number.size() is "
2977 << para_block_vec_number_size << ").\n"
2978 << "But there are only " << para_nblock_types
2979 << " nblock_types.\n"
2980 << "Please make sure that block_vec_number is correctly sized.\n";
2981 throw OomphLibError(
2983 }
2984
2985 // Check if any block numbers defined in block_vec_number is equal to or
2986 // greater than the number of block types.
2987 // E.g. if there are 5 block types, we can only have block numbers:
2988 // 0, 1, 2, 3 and 4.
2989 for (unsigned i = 0; i < para_block_vec_number_size; i++)
2990 {
2991 const unsigned para_required_block = block_vec_number[i];
2993 {
2994 std::ostringstream err_msg;
2995 err_msg << "block_vec_number[" << i << "] is " << para_required_block
2996 << ".\n"
2997 << "But there are only " << para_nblock_types
2998 << " nblock_types.\n";
2999 throw OomphLibError(
3001 }
3002 }
3003 // Check that no block number is inserted twice.
3004 std::set<unsigned> para_set;
3005 for (unsigned b = 0; b < para_block_vec_number_size; b++)
3006 {
3007 std::pair<std::set<unsigned>::iterator, bool> para_set_ret;
3009
3010 if (!para_set_ret.second)
3011 {
3012 std::ostringstream err_msg;
3013 err_msg << "Error: the block number " << block_vec_number[b]
3014 << " appears twice.\n";
3015 throw OomphLibError(
3017 }
3018 }
3019#endif
3020
3021 // Number of blocks to get.
3022 const unsigned n_block = block_vec_number.size();
3023 s.resize(n_block);
3024
3025 // Each block is made of dof types. We get the most fine grain dof types.
3026 // Most fine grain in the sense that these are the dof types that belongs
3027 // in this block before any coarsening of dof types has taken place.
3028 // The ordering of the dof types matters, this is handled properly when
3029 // creating the Block_to_dof_map_fine vector and must be respected here.
3030 // I.e. we cannot arbitrarily insert dof types (even if they are correct)
3031 // in the vector most_fine_grain_dof.
3033 for (unsigned b = 0; b < n_block; b++)
3034 {
3035 const unsigned mapped_b = block_vec_number[b];
3036
3038 Block_to_dof_map_fine[mapped_b].begin(),
3039 Block_to_dof_map_fine[mapped_b].end());
3040 }
3041
3042 // Get all the dof level vectors in one go.
3044 internal_get_block_vectors(most_fine_grain_dof, v, dof_vector);
3045
3046 // For each block vector requested,
3047 // build the block s[b],
3048 // concatenate the corresponding dof vector
3049
3050 // Since all the dof vectors are in dof_vector,
3051 // we need to loop through this.
3052 // The offset helps us loop through this.
3053 unsigned offset = 0;
3054
3055 for (unsigned b = 0; b < n_block; b++)
3056 {
3057 // The actual block number required.
3058 const unsigned mapped_b = block_vec_number[b];
3059
3060 // How many most fine grain dofs are in this block?
3061 const unsigned n_dof = Block_to_dof_map_fine[mapped_b].size();
3062
3063 if (n_dof == 1)
3064 // No need to concatenate, just copy the DoubleVector.
3065 {
3066 s[b] = dof_vector[offset];
3067 }
3068 else
3069 // Concatenate the relevant dof vectors into s[b].
3070 {
3071 s[b].build(Block_distribution_pt[mapped_b], 0);
3073 for (unsigned vec_i = 0; vec_i < n_dof; vec_i++)
3074 {
3075 tmp_vec_pt[vec_i] = &dof_vector[offset + vec_i];
3076 }
3077
3079 s[b]);
3080 }
3081
3082 // Update the offset.
3083 offset += n_dof;
3084 }
3085 } // get_block_vectors(...)
3086
3087
3088 //============================================================================
3089 /// Takes the naturally ordered vector and rearranges it into a
3090 /// vector of sub vectors corresponding to the blocks, so s[b][i] contains
3091 /// the i-th entry in the vector associated with block b.
3092 /// Note: If the preconditioner is a subsidiary preconditioner then only the
3093 /// sub-vectors associated with the blocks of the subsidiary preconditioner
3094 /// will be included. Hence the length of v is master_nrow() whereas the
3095 /// total length of the s vectors is Nrow.
3096 /// This is simply a wrapper around the other get_block_vectors(...) function
3097 /// where the block_vec_number Vector is the identity, i.e.
3098 /// block_vec_number is [0, 1, ..., nblock_types - 1].
3099 //============================================================================
3100 template<typename MATRIX>
3102 const DoubleVector& v, Vector<DoubleVector>& s) const
3103 {
3104 // Get the number of blocks in this block preconditioner.
3105 const unsigned n_block = nblock_types();
3106
3107 // Create the identity vector.
3109 for (unsigned i = 0; i < n_block; i++)
3110 {
3111 required_block[i] = i;
3112 }
3113
3114 // Call the other function which does the work.
3115 get_block_vectors(required_block, v, s);
3116 }
3117
3118 //============================================================================
3119 /// Takes the naturally ordered vector and
3120 /// rearranges it into a vector of sub vectors corresponding to the blocks,
3121 /// so s[b][i] contains the i-th entry in the vector associated with block b.
3122 /// The block_vec_number indicates which blocks we want.
3123 /// These blocks and vectors are those corresponding to the internal blocks.
3124 /// Note: If the preconditioner is a subsidiary preconditioner then only the
3125 /// sub-vectors associated with the blocks of the subsidiary preconditioner
3126 /// will be included. Hence the length of v is master_nrow() whereas the
3127 /// total length of the s vectors is the sum of the Nrow of the sub vectors.
3128 //============================================================================
3129 template<typename MATRIX>
3132 const DoubleVector& v,
3133 Vector<DoubleVector>& s) const
3134 {
3135#ifdef PARANOID
3136 if (!v.built())
3137 {
3138 std::ostringstream error_message;
3139 error_message << "The distribution of the global vector v must be setup.";
3140 throw OomphLibError(
3141 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3142 }
3143 if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
3144 {
3145 std::ostringstream error_message;
3146 error_message << "The distribution of the global vector v must match the "
3147 << " specified master_distribution_pt(). \n"
3148 << "i.e. Distribution_pt in the master preconditioner";
3149 throw OomphLibError(
3150 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3151 }
3152#endif
3153
3154 // Number of block types
3155 // const unsigned nblock = this->internal_nblock_types();
3156 const unsigned nblock = block_vec_number.size();
3157
3158 // if + only one processor
3159 // + more than one processor but matrix_pt is not distributed
3160 // then use the serial get_block method
3161 if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
3162 !this->distribution_pt()->distributed())
3163 {
3164 // Vector of vectors for each section of residual vector
3165 s.resize(nblock);
3166
3167 // pointer to the data in v
3168 const double* v_pt = v.values_pt();
3169
3170 // setup the block vector and then insert the data
3171 for (unsigned b = 0; b < nblock; b++)
3172 {
3173 const unsigned required_block = block_vec_number[b];
3174 s[b].build(Internal_block_distribution_pt[required_block], 0.0);
3175 double* s_pt = s[b].values_pt();
3176 unsigned nrow = s[b].nrow();
3177 for (unsigned i = 0; i < nrow; i++)
3178 {
3179 s_pt[i] = v_pt[this->Global_index[required_block][i]];
3180 }
3181 }
3182 }
3183 // otherwise use mpi
3184 else
3185 {
3186#ifdef OOMPH_HAS_MPI
3187 // my rank
3188 unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
3189
3190 // the number of processors
3191 unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
3192
3193 // build the vectors
3194 s.resize(nblock);
3195 for (unsigned b = 0; b < nblock; b++)
3196 {
3197 const unsigned required_block = block_vec_number[b];
3198 s[b].build(Internal_block_distribution_pt[required_block], 0.0);
3199 }
3200
3201 // determine the maximum number of rows to be sent or recv
3202 // and determine the number of blocks each processor will send and recv
3203 // communication for
3206 unsigned max_n_send_or_recv = 0;
3207 for (unsigned p = 0; p < nproc; p++)
3208 {
3209 for (unsigned b = 0; b < nblock; b++)
3210 {
3211 const unsigned required_block = block_vec_number[b];
3212 max_n_send_or_recv = std::max(
3213 max_n_send_or_recv, Nrows_to_send_for_get_block(required_block, p));
3214 max_n_send_or_recv = std::max(
3215 max_n_send_or_recv, Nrows_to_recv_for_get_block(required_block, p));
3216 if (Nrows_to_send_for_get_block(required_block, p) > 0)
3217 {
3218 nblock_send[p]++;
3219 }
3220 if (Nrows_to_recv_for_get_block(required_block, p) > 0)
3221 {
3222 nblock_recv[p]++;
3223 }
3224 }
3225 }
3226
3227 // create a vectors of 1s the size of the nblock for the mpi indexed
3228 // data types
3229 int* block_lengths = new int[max_n_send_or_recv];
3230 for (unsigned i = 0; i < max_n_send_or_recv; i++)
3231 {
3232 block_lengths[i] = 1;
3233 }
3234
3235 // perform the sends and receives
3237 for (unsigned p = 0; p < nproc; p++)
3238 {
3239 // send and recv with other processors
3240 if (p != my_rank)
3241 {
3242 // send
3243 if (nblock_send[p] > 0)
3244 {
3245 // create the datatypes vector
3247
3248 // create the datatypes
3249 unsigned ptr = 0;
3250 for (unsigned b = 0; b < nblock; b++)
3251 {
3252 const unsigned required_block = block_vec_number[b];
3253
3254 if (Nrows_to_send_for_get_block(required_block, p) > 0)
3255 {
3256 MPI_Type_indexed(Nrows_to_send_for_get_block(required_block, p),
3258 Rows_to_send_for_get_block(required_block, p),
3259 MPI_DOUBLE,
3262 ptr++;
3263 }
3264 }
3265
3266 // compute the displacements and lengths
3268 int lengths[nblock_send[p]];
3269 for (int i = 0; i < nblock_send[p]; i++)
3270 {
3271 lengths[i] = 1;
3272 displacements[i] = 0;
3273 }
3274
3275 // build the final datatype
3278 lengths,
3281 &type_send);
3283
3284 // send
3286 MPI_Isend(const_cast<double*>(v.values_pt()),
3287 1,
3288 type_send,
3289 p,
3290 0,
3291 this->distribution_pt()->communicator_pt()->mpi_comm(),
3292 &send_req);
3294 for (int i = 0; i < nblock_send[p]; i++)
3295 {
3297 }
3298 requests.push_back(send_req);
3299 }
3300
3301 // recv
3302 if (nblock_recv[p] > 0)
3303 {
3304 // create the datatypes vector
3306
3307 // and the displacements
3309
3310 // and the lengths
3311 int lengths[nblock_recv[p]];
3312
3313 // all displacements are computed relative to s[0] values
3315 MPI_Get_address(s[0].values_pt(), &displacements_base);
3316
3317 // now build
3318 unsigned ptr = 0;
3319 for (unsigned b = 0; b < nblock; b++)
3320 {
3321 const unsigned required_block = block_vec_number[b];
3322
3323 if (Nrows_to_recv_for_get_block(required_block, p) > 0)
3324 {
3325 MPI_Type_indexed(Nrows_to_recv_for_get_block(required_block, p),
3327 Rows_to_recv_for_get_block(required_block, p),
3328 MPI_DOUBLE,
3331 MPI_Get_address(s[b].values_pt(), &displacements[ptr]);
3333 lengths[ptr] = 1;
3334 ptr++;
3335 }
3336 }
3337
3338 // build the final data type
3341 lengths,
3344 &type_recv);
3346
3347 // recv
3349 MPI_Irecv(s[0].values_pt(),
3350 1,
3351 type_recv,
3352 p,
3353 0,
3354 this->distribution_pt()->communicator_pt()->mpi_comm(),
3355 &recv_req);
3357 for (int i = 0; i < nblock_recv[p]; i++)
3358 {
3360 }
3361 requests.push_back(recv_req);
3362 }
3363 }
3364
3365 // communicate with self
3366 else
3367 {
3368 const double* v_values_pt = v.values_pt();
3369 for (unsigned b = 0; b < nblock; b++)
3370 {
3371 const unsigned required_block = block_vec_number[b];
3372
3373 double* w_values_pt = s[b].values_pt();
3374 for (unsigned i = 0;
3375 i < Nrows_to_send_for_get_block(required_block, p);
3376 i++)
3377 {
3378 w_values_pt[Rows_to_recv_for_get_block(required_block, p)[i]] =
3379 v_values_pt[Rows_to_send_for_get_block(required_block, p)[i]];
3380 }
3381 }
3382 }
3383 }
3384
3385 // and then just wait
3386 unsigned c = requests.size();
3388 if (c)
3389 {
3390 MPI_Waitall(c, &requests[0], &stat[0]);
3391 }
3392 delete[] block_lengths;
3393
3394#else
3395 // throw error
3396 std::ostringstream error_message;
3397 error_message << "The preconditioner is distributed and on more than one "
3398 << "processor. MPI is required.";
3399 throw OomphLibError(
3400 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3401#endif
3402 }
3403 }
3404
3405 //============================================================================
3406 /// A helper function, takes the naturally ordered vector and
3407 /// rearranges it into a vector of sub vectors corresponding to the blocks,
3408 /// so s[b][i] contains the i-th entry in the vector associated with block b.
3409 /// The block_vec_number indicates which blocks we want.
3410 /// These blocks and vectors are those corresponding to the internal blocks.
3411 /// Note: If the preconditioner is a subsidiary preconditioner then only the
3412 /// sub-vectors associated with the blocks of the subsidiary preconditioner
3413 /// will be included. Hence the length of v is master_nrow() whereas the
3414 /// total length of the s vectors is the sum of the Nrow of the sub vectors.
3415 /// This is simply a wrapper around the other internal_get_block_vectors(...)
3416 /// function with the identity block_vec_number vector.
3417 //============================================================================
3418 template<typename MATRIX>
3420 const DoubleVector& v, Vector<DoubleVector>& s) const
3421 {
3422 // Number of block types
3423 const unsigned nblock = this->internal_nblock_types();
3425 for (unsigned b = 0; b < nblock; b++)
3426 {
3427 block_vec_number[b] = b;
3428 }
3429
3430 internal_get_block_vectors(block_vec_number, v, s);
3431 }
3432
3433 //============================================================================
3434 /// Takes the vector of block vectors, s, and copies its entries into
3435 /// the naturally ordered vector, v. If this is a subsidiary block
3436 /// preconditioner only those entries in v that are associated with its
3437 /// blocks are affected. The block_vec_number indicates which block the
3438 /// vectors in s came from. The block number corresponds to the block
3439 /// numbers in this preconditioner.
3440 //============================================================================
3441 template<typename MATRIX>
3444 const Vector<DoubleVector>& s,
3445 DoubleVector& v) const
3446 {
3447#ifdef PARANOID
3448
3449 // Check if v is built.
3450 if (!v.built())
3451 {
3452 std::ostringstream err_msg;
3453 err_msg << "The distribution of the global vector v must be setup.";
3454 throw OomphLibError(
3456 }
3457
3458 // v must have the same distribution as the upper-most master block
3459 // preconditioner, since the upper-most master block preconditioner
3460 // should have the same distribution as the matrix pointed to
3461 // by matrix_pt().
3462 if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
3463 {
3464 std::ostringstream err_msg;
3465 err_msg << "The distribution of the global vector v must match the "
3466 << " specified master_distribution_pt(). \n"
3467 << "i.e. Distribution_pt in the master preconditioner";
3468 throw OomphLibError(
3470 }
3471
3472 // Check if the number of vectors in s is the same as the number of block
3473 // numbers described in block_vec_number.
3475 const unsigned para_s_size = s.size();
3477 {
3478 std::ostringstream err_msg;
3479 err_msg << "block_vec_number.size() is " << para_block_vec_number_size
3480 << "\n."
3481 << "s.size() is " << para_s_size << ".\n"
3482 << "But they must be the same size!\n";
3483 throw OomphLibError(
3485 }
3486
3487 // Check to see if there are more blocks defined in the block_vec_number
3488 // vector than the number of block types. This is not allowed.
3489 const unsigned para_n_block = nblock_types();
3491 {
3492 std::ostringstream err_msg;
3493 err_msg << "Trying to return " << para_block_vec_number_size
3494 << " block vectors.\n"
3495 << "But there are only " << para_n_block << " block types.\n";
3496 throw OomphLibError(
3498 }
3499
3500 // Check if any block numbers defined in block_vec_number is equal to or
3501 // greater than the number of block types.
3502 // E.g. if there are 5 block types, we can only have block numbers:
3503 // 0, 1, 2, 3 and 4.
3504 for (unsigned b = 0; b < para_block_vec_number_size; b++)
3505 {
3506 const unsigned para_required_block = block_vec_number[b];
3508 {
3509 std::ostringstream err_msg;
3510 err_msg << "block_vec_number[" << b << "] is " << para_required_block
3511 << ".\n"
3512 << "But there are only " << para_n_block << " block types.\n";
3513 throw OomphLibError(
3515 }
3516 }
3517
3518 // Check that no block number is inserted twice.
3519 std::set<unsigned> para_set;
3520 for (unsigned b = 0; b < para_block_vec_number_size; b++)
3521 {
3522 std::pair<std::set<unsigned>::iterator, bool> para_set_ret;
3524
3525 if (!para_set_ret.second)
3526 {
3527 std::ostringstream err_msg;
3528 err_msg << "Error: the block number " << block_vec_number[b]
3529 << " appears twice.\n";
3530 throw OomphLibError(
3532 }
3533 }
3534
3535 // Check to see that all the vectors in s are built
3536 // (since we are trying to return them).
3537 for (unsigned b = 0; b < para_block_vec_number_size; b++)
3538 {
3539 if (!s[b].built())
3540 {
3541 std::ostringstream err_msg;
3542 err_msg << "The distribution of the block vector s[" << b
3543 << "] must be setup.\n";
3544 throw OomphLibError(
3546 }
3547 }
3548
3549 // Since these are built, we check that the distributions are correct.
3550 // This are incorrect if the block numbers in block_vec_number and
3551 // the vectors in s does not match.
3552 for (unsigned b = 0; b < para_block_vec_number_size; b++)
3553 {
3554 if (*(s[b].distribution_pt()) !=
3555 *(Block_distribution_pt[block_vec_number[b]]))
3556 {
3557 std::ostringstream error_message;
3558 error_message
3559 << "The distribution of the block vector " << b << " must match the"
3560 << " specified distribution at "
3561 << "Block_distribution_pt[" << block_vec_number[b] << "].\n"
3562 << "The distribution of the Block_distribution_pt is determined by\n"
3563 << "the vector block_vec_number. Perhaps it is incorrect?\n";
3564 throw OomphLibError(error_message.str(),
3567 }
3568 }
3569#endif
3570
3571 // Number of blocks to get.
3572 const unsigned n_block = block_vec_number.size();
3573
3574 // Each block is made of dof types. We get the most fine grain dof types.
3575 // Most fine grain in the sense that these are the dof types that belongs
3576 // in this block before any coarsening of dof types has taken place.
3577 // The ordering of the dof types matters, this is handled properly when
3578 // creating the Block_to_dof_map_fine vector and must be respected here.
3579 // I.e. we cannot arbitrarily insert dof types (even if they are correct)
3580 // in the vector most_fine_grain_dof.
3582 for (unsigned b = 0; b < n_block; b++)
3583 {
3584 const unsigned mapped_b = block_vec_number[b];
3585
3587 Block_to_dof_map_fine[mapped_b].begin(),
3588 Block_to_dof_map_fine[mapped_b].end());
3589 }
3590
3591 // Split all the blocks into it's most fine grain dof vector.
3593
3594 unsigned offset = 0;
3595
3596 // Perform the splitting for each block.
3597 for (unsigned b = 0; b < n_block; b++)
3598 {
3599 // The actual block number.
3600 const unsigned mapped_b = block_vec_number[b];
3601
3602 // How many most fine grain dof types are associated with this block?
3603 const unsigned ndof = Block_to_dof_map_fine[mapped_b].size();
3604
3605 if (ndof == 1)
3606 // No need to split, just copy.
3607 {
3608 dof_vector[offset] = s[b];
3609 }
3610 else
3611 // Need to split s[b] into it's most fine grain dof vectors
3612 {
3613 // To store pointers to the dof vectors associated with this block.
3615
3616 for (unsigned d = 0; d < ndof; d++)
3617 {
3618 const unsigned offset_plus_d = offset + d;
3619
3620 // build the dof vector.
3622 Internal_block_distribution_pt[most_fine_grain_dof[offset_plus_d]]);
3623
3624 // Store the pointer.
3626 }
3627
3628 // Split without communication.
3631 }
3632
3633 // Update the offset!
3634 offset += ndof;
3635 }
3636
3637 // Return the block vectors all in one go.
3638 internal_return_block_vectors(most_fine_grain_dof, dof_vector, v);
3639 } // return_block_vectors(...)
3640
3641
3642 //============================================================================
3643 /// Takes the vector of block vectors, s, and copies its entries into
3644 /// the naturally ordered vector, v. If this is a subsidiary block
3645 /// preconditioner only those entries in v that are associated with its
3646 /// blocks are affected. The block_vec_number indicates which block the
3647 /// vectors in s came from. The block number corresponds to the block
3648 /// numbers in this preconditioner.
3649 /// This is simply a wrapper around the other return_block_vectors(...)
3650 /// function where the block_vec_number Vector is the identity, i.e.
3651 /// block_vec_number is [0, 1, ..., nblock_types - 1].
3652 //============================================================================
3653 template<typename MATRIX>
3655 const Vector<DoubleVector>& s, DoubleVector& v) const
3656 {
3657 // The number of block types in this preconditioner.
3658 const unsigned n_block = nblock_types();
3659
3660 // Create the identity vector.
3662 for (unsigned i = 0; i < n_block; i++)
3663 {
3664 required_block[i] = i;
3665 }
3666
3667 // Call the other return_block_vectors function which does the work.
3668 return_block_vectors(required_block, s, v);
3669 } // return_block_vectors(...)
3670
3671 //============================================================================
3672 /// Takes the naturally ordered vector and
3673 /// rearranges it into a vector of sub vectors corresponding to the blocks,
3674 /// so s[b][i] contains the i-th entry in the vector associated with block b.
3675 /// The block_vec_number indicates which blocks we want.
3676 /// These blocks and vectors are those corresponding to the internal blocks.
3677 /// Note: If the preconditioner is a subsidiary preconditioner then only the
3678 /// sub-vectors associated with the blocks of the subsidiary preconditioner
3679 /// will be included. Hence the length of v is master_nrow() whereas the
3680 /// total length of the s vectors is the sum of the Nrow of the sub vectors.
3681 //============================================================================
3682 template<typename MATRIX>
3685 const Vector<DoubleVector>& s,
3686 DoubleVector& v) const
3687 {
3688 // the number of blocks
3689 const unsigned nblock = block_vec_number.size();
3690
3691#ifdef PARANOID
3692 if (!v.built())
3693 {
3694 std::ostringstream error_message;
3695 error_message << "The distribution of the global vector v must be setup.";
3696 throw OomphLibError(
3697 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3698 }
3699 if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
3700 {
3701 std::ostringstream error_message;
3702 error_message << "The distribution of the global vector v must match the "
3703 << " specified master_distribution_pt(). \n"
3704 << "i.e. Distribution_pt in the master preconditioner";
3705 throw OomphLibError(
3706 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3707 }
3708 for (unsigned b = 0; b < nblock; b++)
3709 {
3710 if (!s[b].built())
3711 {
3712 std::ostringstream error_message;
3713 error_message << "The distribution of the block vector " << b
3714 << " must be setup.";
3715 throw OomphLibError(error_message.str(),
3718 }
3719 const unsigned required_block = block_vec_number[b];
3720 if (*(s[b].distribution_pt()) !=
3721 *(Internal_block_distribution_pt[required_block]))
3722 {
3723 std::ostringstream error_message;
3724 error_message
3725 << "The distribution of the block vector " << b << " must match the"
3726 << " specified distribution at Internal_block_distribution_pt[" << b
3727 << "]";
3728 throw OomphLibError(error_message.str(),
3731 }
3732 }
3733#endif
3734
3735 // if + only one processor
3736 // + more than one processor but matrix_pt is not distributed
3737 // then use the serial get_block method
3738 if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
3739 !this->distribution_pt()->distributed())
3740 {
3741 double* v_pt = v.values_pt();
3742 for (unsigned b = 0; b < nblock; b++)
3743 {
3744 const unsigned required_block = block_vec_number[b];
3745
3746 const double* s_pt = s[b].values_pt();
3747 unsigned nrow = this->internal_block_dimension(required_block);
3748 for (unsigned i = 0; i < nrow; i++)
3749 {
3750 v_pt[this->Global_index[required_block][i]] = s_pt[i];
3751 }
3752 }
3753 }
3754 // otherwise use mpi
3755 else
3756 {
3757#ifdef OOMPH_HAS_MPI
3758
3759 // my rank
3760 unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
3761
3762 // the number of processors
3763 unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
3764
3765 // determine the maximum number of rows to be sent or recv
3766 // and determine the number of blocks each processor will send and recv
3767 // communication for
3770 unsigned max_n_send_or_recv = 0;
3771 for (unsigned p = 0; p < nproc; p++)
3772 {
3773 for (unsigned b = 0; b < nblock; b++)
3774 {
3775 const unsigned required_block = block_vec_number[b];
3776
3777 max_n_send_or_recv = std::max(
3778 max_n_send_or_recv, Nrows_to_send_for_get_block(required_block, p));
3779 max_n_send_or_recv = std::max(
3780 max_n_send_or_recv, Nrows_to_recv_for_get_block(required_block, p));
3781 if (Nrows_to_send_for_get_block(required_block, p) > 0)
3782 {
3783 nblock_recv[p]++;
3784 }
3785 if (Nrows_to_recv_for_get_block(required_block, p) > 0)
3786 {
3787 nblock_send[p]++;
3788 }
3789 }
3790 }
3791
3792 // create a vectors of 1s the size of the nblock for the mpi indexed
3793 // data types
3794 int* block_lengths = new int[max_n_send_or_recv];
3795 for (unsigned i = 0; i < max_n_send_or_recv; i++)
3796 {
3797 block_lengths[i] = 1;
3798 }
3799
3800 // perform the sends and receives
3802 for (unsigned p = 0; p < nproc; p++)
3803 {
3804 // send and recv with other processors
3805 if (p != my_rank)
3806 {
3807 // recv
3808 if (nblock_recv[p] > 0)
3809 {
3810 // create the datatypes vector
3812
3813 // create the datatypes
3814 unsigned ptr = 0;
3815 for (unsigned b = 0; b < nblock; b++)
3816 {
3817 const unsigned required_block = block_vec_number[b];
3818
3819 if (Nrows_to_send_for_get_block(required_block, p) > 0)
3820 {
3821 MPI_Type_indexed(Nrows_to_send_for_get_block(required_block, p),
3823 Rows_to_send_for_get_block(required_block, p),
3824 MPI_DOUBLE,
3827 ptr++;
3828 }
3829 }
3830
3831 // compute the displacements and lengths
3833 int lengths[nblock_recv[p]];
3834 for (int i = 0; i < nblock_recv[p]; i++)
3835 {
3836 lengths[i] = 1;
3837 displacements[i] = 0;
3838 }
3839
3840 // build the final datatype
3843 lengths,
3846 &type_recv);
3848
3849 // recv
3851 MPI_Irecv(v.values_pt(),
3852 1,
3853 type_recv,
3854 p,
3855 0,
3856 this->distribution_pt()->communicator_pt()->mpi_comm(),
3857 &recv_req);
3859 for (int i = 0; i < nblock_recv[p]; i++)
3860 {
3862 }
3863 requests.push_back(recv_req);
3864 }
3865
3866 // send
3867 if (nblock_send[p] > 0)
3868 {
3869 // create the datatypes vector
3871
3872 // and the displacements
3874
3875 // and the lengths
3876 int lengths[nblock_send[p]];
3877
3878 // all displacements are computed relative to s[0] values
3880 MPI_Get_address(const_cast<double*>(s[0].values_pt()),
3882
3883 // now build
3884 unsigned ptr = 0;
3885 for (unsigned b = 0; b < nblock; b++)
3886 {
3887 const unsigned required_block = block_vec_number[b];
3888
3889 if (Nrows_to_recv_for_get_block(required_block, p) > 0)
3890 {
3891 MPI_Type_indexed(Nrows_to_recv_for_get_block(required_block, p),
3893 Rows_to_recv_for_get_block(required_block, p),
3894 MPI_DOUBLE,
3897 MPI_Get_address(const_cast<double*>(s[b].values_pt()),
3898 &displacements[ptr]);
3900 lengths[ptr] = 1;
3901 ptr++;
3902 }
3903 }
3904
3905 // build the final data type
3908 lengths,
3911 &type_send);
3913
3914 // send
3916 MPI_Isend(const_cast<double*>(s[0].values_pt()),
3917 1,
3918 type_send,
3919 p,
3920 0,
3921 this->distribution_pt()->communicator_pt()->mpi_comm(),
3922 &send_req);
3924 for (int i = 0; i < nblock_send[p]; i++)
3925 {
3927 }
3928 requests.push_back(send_req);
3929 }
3930 }
3931
3932 // communicate wih self
3933 else
3934 {
3935 double* v_values_pt = v.values_pt();
3936 for (unsigned b = 0; b < nblock; b++)
3937 {
3938 const unsigned required_block = block_vec_number[b];
3939
3940 const double* w_values_pt = s[b].values_pt();
3941 for (unsigned i = 0;
3942 i < Nrows_to_send_for_get_block(required_block, p);
3943 i++)
3944 {
3945 v_values_pt[Rows_to_send_for_get_block(required_block, p)[i]] =
3946 w_values_pt[Rows_to_recv_for_get_block(required_block, p)[i]];
3947 }
3948 }
3949 }
3950 }
3951
3952 // and then just wait
3953 unsigned c = requests.size();
3955 if (c)
3956 {
3957 MPI_Waitall(c, &requests[0], &stat[0]);
3958 }
3959 delete[] block_lengths;
3960
3961#else
3962 // throw error
3963 std::ostringstream error_message;
3964 error_message << "The preconditioner is distributed and on more than one "
3965 << "processor. MPI is required.";
3966 throw OomphLibError(
3967 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3968#endif
3969 }
3970 }
3971
3972 //============================================================================
3973 /// A helper function, takes the naturally ordered vector and
3974 /// rearranges it into a vector of sub vectors corresponding to the blocks,
3975 /// so s[b][i] contains the i-th entry in the vector associated with block b.
3976 /// The block_vec_number indicates which blocks we want.
3977 /// These blocks and vectors are those corresponding to the internal blocks.
3978 /// Note: If the preconditioner is a subsidiary preconditioner then only the
3979 /// sub-vectors associated with the blocks of the subsidiary preconditioner
3980 /// will be included. Hence the length of v is master_nrow() whereas the
3981 /// total length of the s vectors is the sum of the Nrow of the sub vectors.
3982 /// This is simply a wrapper around the other internal_get_block_vectors(...)
3983 /// function with the identity block_vec_number vector.
3984 //============================================================================
3985 template<typename MATRIX>
3987 const Vector<DoubleVector>& s, DoubleVector& v) const
3988 {
3989 // the number of blocks
3990 const unsigned nblock = this->internal_nblock_types();
3992 for (unsigned b = 0; b < nblock; b++)
3993 {
3994 block_vec_number[b] = b;
3995 }
3996
3997 internal_return_block_vectors(block_vec_number, s, v);
3998 }
3999
4000 //============================================================================
4001 /// A helper function, takes the naturally ordered vector, v,
4002 /// and extracts the n-th block vector, b.
4003 /// Here n is the block number in the current preconditioner.
4004 /// NOTE: The ordering of the vector b is the same as the
4005 /// ordering of the block matrix from internal_get_block(...).
4006 //============================================================================
4007 template<typename MATRIX>
4009 const unsigned& b, const DoubleVector& v, DoubleVector& w) const
4010 {
4011#ifdef PARANOID
4012 // the number of blocks
4013 const unsigned n_blocks = this->internal_nblock_types();
4014
4015 // paranoid check that block i is in this block preconditioner
4016 if (b >= n_blocks)
4017 {
4018 std::ostringstream error_message;
4019 error_message
4020 << "Requested block vector " << b
4021 << ", however this preconditioner has internal_nblock_types() "
4022 << "= " << internal_nblock_types() << std::endl;
4023 throw OomphLibError(
4024 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4025 }
4026 if (!v.built())
4027 {
4028 std::ostringstream error_message;
4029 error_message << "The distribution of the global vector v must be setup.";
4030 throw OomphLibError(
4031 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4032 }
4033 if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
4034 {
4035 std::ostringstream error_message;
4036 error_message << "The distribution of the global vector v must match the "
4037 << " specified master_distribution_pt(). \n"
4038 << "i.e. Distribution_pt in the master preconditioner";
4039 throw OomphLibError(
4040 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4041 }
4042#endif
4043
4044 // rebuild the block vector
4045 w.build(Internal_block_distribution_pt[b], 0.0);
4046
4047 // if + only one processor
4048 // + more than one processor but matrix_pt is not distributed
4049 // then use the serial get_block method
4050 if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
4051 !this->distribution_pt()->distributed())
4052 {
4053 double* w_pt = w.values_pt();
4054 const double* v_pt = v.values_pt();
4055 unsigned n_row = w.nrow();
4056 for (unsigned i = 0; i < n_row; i++)
4057 {
4058 w_pt[i] = v_pt[this->Global_index[b][i]];
4059 }
4060 }
4061 // otherwise use mpi
4062 else
4063 {
4064#ifdef OOMPH_HAS_MPI
4065
4066 // my rank
4067 unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
4068
4069 // the number of processors
4070 unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
4071
4072 // determine the maximum number of rows to be sent or recv
4073 unsigned max_n_send_or_recv = 0;
4074 for (unsigned p = 0; p < nproc; p++)
4075 {
4077 std::max(max_n_send_or_recv, Nrows_to_send_for_get_block(b, p));
4079 std::max(max_n_send_or_recv, Nrows_to_recv_for_get_block(b, p));
4080 }
4081
4082 // create a vectors of 1s (the size of the nblock for the mpi indexed
4083 // data types
4084 int* block_lengths = new int[max_n_send_or_recv];
4085 for (unsigned i = 0; i < max_n_send_or_recv; i++)
4086 {
4087 block_lengths[i] = 1;
4088 }
4089
4090 // perform the sends and receives
4092 for (unsigned p = 0; p < nproc; p++)
4093 {
4094 // send and recv with other processors
4095 if (p != my_rank)
4096 {
4097 if (Nrows_to_send_for_get_block(b, p) > 0)
4098 {
4099 // create the send datatype
4101 MPI_Type_indexed(Nrows_to_send_for_get_block(b, p),
4103 Rows_to_send_for_get_block(b, p),
4104 MPI_DOUBLE,
4105 &type_send);
4107
4108 // send
4110 MPI_Isend(const_cast<double*>(v.values_pt()),
4111 1,
4112 type_send,
4113 p,
4114 0,
4115 this->distribution_pt()->communicator_pt()->mpi_comm(),
4116 &send_req);
4118 requests.push_back(send_req);
4119 }
4120
4121 if (Nrows_to_recv_for_get_block(b, p) > 0)
4122 {
4123 // create the recv datatype
4125 MPI_Type_indexed(Nrows_to_recv_for_get_block(b, p),
4127 Rows_to_recv_for_get_block(b, p),
4128 MPI_DOUBLE,
4129 &type_recv);
4131
4132 // recv
4134 MPI_Irecv(w.values_pt(),
4135 1,
4136 type_recv,
4137 p,
4138 0,
4139 this->distribution_pt()->communicator_pt()->mpi_comm(),
4140 &recv_req);
4142 requests.push_back(recv_req);
4143 }
4144 }
4145
4146 // communicate with self
4147 else
4148 {
4149 double* w_values_pt = w.values_pt();
4150 const double* v_values_pt = v.values_pt();
4151 for (unsigned i = 0; i < Nrows_to_send_for_get_block(b, p); i++)
4152 {
4153 w_values_pt[Rows_to_recv_for_get_block(b, p)[i]] =
4154 v_values_pt[Rows_to_send_for_get_block(b, p)[i]];
4155 }
4156 }
4157 }
4158
4159 // and then just wait
4160 unsigned c = requests.size();
4162 if (c)
4163 {
4164 MPI_Waitall(c, &requests[0], &stat[0]);
4165 }
4166 delete[] block_lengths;
4167
4168#else
4169 // throw error
4170 std::ostringstream error_message;
4171 error_message << "The preconditioner is distributed and on more than one "
4172 << "processor. MPI is required.";
4173 throw OomphLibError(
4174 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4175#endif
4176 }
4177 }
4178
4179 //============================================================================
4180 /// Takes the naturally ordered vector, v and returns the n-th
4181 /// block vector, b. Here n is the block number in the current
4182 /// preconditioner.
4183 //============================================================================
4184 template<typename MATRIX>
4186 const DoubleVector& v,
4187 DoubleVector& w) const
4188 {
4189#ifdef PARANOID
4190 // the number of blocks
4191 const unsigned para_n_blocks = nblock_types();
4192
4193 // paranoid check that block i is in this block preconditioner
4194 if (b >= para_n_blocks)
4195 {
4196 std::ostringstream err_msg;
4197 err_msg << "Requested block vector " << b
4198 << ", however this preconditioner has only " << para_n_blocks
4199 << " block types"
4200 << ".\n";
4201 throw OomphLibError(
4203 }
4204
4205 if (!v.built())
4206 {
4207 std::ostringstream err_msg;
4208 err_msg << "The distribution of the global vector v must be setup.";
4209 throw OomphLibError(
4211 }
4212 if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
4213 {
4214 std::ostringstream err_msg;
4215 err_msg << "The distribution of the global vector v must match the "
4216 << " specified master_distribution_pt(). \n"
4217 << "i.e. Distribution_pt in the master preconditioner";
4218 throw OomphLibError(
4220 }
4221#endif
4222
4223 // Recall that, the relationship between the external blocks and the
4224 // external dof types, as seen by the preconditioner writer is stored in the
4225 // mapping Block_to_dof_map_coarse.
4226 //
4227 // However, each dof type could have been coarsened! The relationship
4228 // between the dof types of this preconditioner and the parent
4229 // preconditioner is stored in the mapping Doftype_coarsen_map_coarse. The
4230 // dof numbers in this map is relative to this preconditioner.
4231 //
4232 // Finally, the relationship between the dof types of this preconditioner
4233 // and the most fine grain dof types is stored in the mapping
4234 // Doftype_coarsen_map_fine. Again, the dof numbers in this map is relative
4235 // to this preconditioner.
4236 //
4237 // Furthermore, we note that concatenation of vectors without communication
4238 // is associative, but not commutative. I.e.
4239 // (V1+V2)+V3 = V1 + (V2 + V3), where + is concatenation without
4240 // communication.
4241 //
4242 // So all we need is the vectors listed in the correct order.
4243 //
4244 // We need only Block_to_dof_map_coarse to tell us which external dof types
4245 // are in this block, then Doftype_coarsen_map_fine to tell us which most
4246 // fine grain dofs to concatenate!
4247 //
4248 // All the mapping vectors are constructed to respect the ordering of
4249 // the dof types.
4250
4251 // Get the most fine grain block to dof mapping.
4252 Vector<unsigned> most_fine_grain_dof = Block_to_dof_map_fine[b];
4253
4254 // How many vectors do we need to concatenate?
4255 const unsigned n_dof_vec = most_fine_grain_dof.size();
4256
4257 if (n_dof_vec == 1)
4258 // No need to concatenate, just extract the vector.
4259 {
4260 internal_get_block_vector(most_fine_grain_dof[0], v, w);
4261 }
4262 else
4263 // Need to concatenate dof-level vectors.
4264 {
4266
4267 // Get all the dof-level vectors in one go
4268 internal_get_block_vectors(most_fine_grain_dof, v, dof_vector);
4269 // Build w with the correct distribution.
4270 w.build(Block_distribution_pt[b], 0);
4271
4272 // Concatenate the vectors.
4274
4275 dof_vector.clear();
4276 }
4277 } // get_block_vector(...)
4278
4279 //============================================================================
4280 /// Takes the n-th block ordered vector, b, and copies its entries
4281 /// to the appropriate entries in the naturally ordered vector, v.
4282 /// Here n is the block number in the current block preconditioner.
4283 /// If the preconditioner is a subsidiary block preconditioner
4284 /// the other entries in v that are not associated with it
4285 /// are left alone.
4286 ///
4287 /// This version works with the internal block types. This is legacy code
4288 /// but is kept alive, hence moved to private. Please use the
4289 /// function "return_block_vector(...)".
4290 //============================================================================
4291 template<typename MATRIX>
4293 const unsigned& b, const DoubleVector& w, DoubleVector& v) const
4294 {
4295#ifdef PARANOID
4296 // the number of blocks
4297 const unsigned n_blocks = this->internal_nblock_types();
4298
4299 // paranoid check that block i is in this block preconditioner
4300 if (b >= n_blocks)
4301 {
4302 std::ostringstream error_message;
4303 error_message
4304 << "Requested block vector " << b
4305 << ", however this preconditioner has internal_nblock_types() "
4306 << "= " << internal_nblock_types() << std::endl;
4307 throw OomphLibError(
4308 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4309 }
4310 if (!v.built())
4311 {
4312 std::ostringstream error_message;
4313 error_message << "The distribution of the global vector v must be setup.";
4314 throw OomphLibError(
4315 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4316 }
4317 if (*v.distribution_pt() != *this->master_distribution_pt())
4318 {
4319 std::ostringstream error_message;
4320 error_message << "The distribution of the global vector v must match the "
4321 << " specified master_distribution_pt(). \n"
4322 << "i.e. Distribution_pt in the master preconditioner";
4323 throw OomphLibError(
4324 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4325 }
4326 if (!w.built())
4327 {
4328 std::ostringstream error_message;
4329 error_message << "The distribution of the block vector w must be setup.";
4330 throw OomphLibError(
4331 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4332 }
4333 if (*w.distribution_pt() != *Internal_block_distribution_pt[b])
4334 {
4335 std::ostringstream error_message;
4336 error_message
4337 << "The distribution of the block vector w must match the "
4338 << " specified distribution at Internal_block_distribution_pt[b]";
4339 throw OomphLibError(
4340 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4341 }
4342#endif
4343
4344 // if + only one processor
4345 // + more than one processor but matrix_pt is not distributed
4346 // then use the serial get_block method
4347 if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
4348 !this->distribution_pt()->distributed())
4349 {
4350 // length of vector
4351 unsigned n_row = this->internal_block_dimension(b);
4352
4353 // copy back from the block vector to the naturally ordered vector
4354 double* v_pt = v.values_pt();
4355 const double* w_pt = w.values_pt();
4356 for (unsigned i = 0; i < n_row; i++)
4357 {
4358 v_pt[this->Global_index[b][i]] = w_pt[i];
4359 }
4360 }
4361 // otherwise use mpi
4362 else
4363 {
4364#ifdef OOMPH_HAS_MPI
4365
4366 // my rank
4367 unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
4368
4369 // the number of processors
4370 unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
4371
4372 // determine the maximum number of rows to be sent or recv
4373 unsigned max_n_send_or_recv = 0;
4374 for (unsigned p = 0; p < nproc; p++)
4375 {
4377 std::max(max_n_send_or_recv, Nrows_to_send_for_get_block(b, p));
4379 std::max(max_n_send_or_recv, Nrows_to_recv_for_get_block(b, p));
4380 }
4381
4382 // create a vectors of 1s (the size of the nblock for the mpi indexed
4383 // data types
4384 int* block_lengths = new int[max_n_send_or_recv];
4385 for (unsigned i = 0; i < max_n_send_or_recv; i++)
4386 {
4387 block_lengths[i] = 1;
4388 }
4389
4390 // perform the sends and receives
4392 for (unsigned p = 0; p < nproc; p++)
4393 {
4394 // send and recv with other processors
4395 if (p != my_rank)
4396 {
4397 if (Nrows_to_recv_for_get_block(b, p) > 0)
4398 {
4399 // create the send datatype
4401 MPI_Type_indexed(Nrows_to_recv_for_get_block(b, p),
4403 Rows_to_recv_for_get_block(b, p),
4404 MPI_DOUBLE,
4405 &type_send);
4407
4408 // send
4410 MPI_Isend(const_cast<double*>(w.values_pt()),
4411 1,
4412 type_send,
4413 p,
4414 0,
4415 this->distribution_pt()->communicator_pt()->mpi_comm(),
4416 &send_req);
4418 requests.push_back(send_req);
4419 }
4420
4421 if (Nrows_to_send_for_get_block(b, p) > 0)
4422 {
4423 // create the recv datatype
4425 MPI_Type_indexed(Nrows_to_send_for_get_block(b, p),
4427 Rows_to_send_for_get_block(b, p),
4428 MPI_DOUBLE,
4429 &type_recv);
4431
4432 // recv
4434 MPI_Irecv(v.values_pt(),
4435 1,
4436 type_recv,
4437 p,
4438 0,
4439 this->distribution_pt()->communicator_pt()->mpi_comm(),
4440 &recv_req);
4442 requests.push_back(recv_req);
4443 }
4444 }
4445
4446 // communicate wih self
4447 else
4448 {
4449 const double* w_values_pt = w.values_pt();
4450 double* v_values_pt = v.values_pt();
4451 for (unsigned i = 0; i < Nrows_to_send_for_get_block(b, p); i++)
4452 {
4453 v_values_pt[Rows_to_send_for_get_block(b, p)[i]] =
4454 w_values_pt[Rows_to_recv_for_get_block(b, p)[i]];
4455 }
4456 }
4457 }
4458
4459 // and then just wait
4460 unsigned c = requests.size();
4462 if (c)
4463 {
4464 MPI_Waitall(c, &requests[0], &stat[0]);
4465 }
4466 delete[] block_lengths;
4467
4468#else
4469 // throw error
4470 std::ostringstream error_message;
4471 error_message << "The preconditioner is distributed and on more than one "
4472 << "processor. MPI is required.";
4473 throw OomphLibError(
4474 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4475#endif
4476 }
4477 }
4478
4479 //============================================================================
4480 /// Takes the n-th block ordered vector, b, and copies its entries
4481 /// to the appropriate entries in the naturally ordered vector, v.
4482 /// Here n is the block number in the current block preconditioner.
4483 /// If the preconditioner is a subsidiary block preconditioner
4484 /// the other entries in v that are not associated with it
4485 /// are left alone.
4486 //============================================================================
4487 template<typename MATRIX>
4489 const DoubleVector& b,
4490 DoubleVector& v) const
4491 {
4492#ifdef PARANOID
4493 // the number of blocks
4494 const unsigned para_n_blocks = nblock_types();
4495
4496 // paranoid check that block i is in this block preconditioner
4497 if (n >= para_n_blocks)
4498 {
4499 std::ostringstream err_msg;
4500 err_msg << "Requested block vector " << b
4501 << ", however this preconditioner has " << para_n_blocks
4502 << " block types.\n";
4503 throw OomphLibError(
4505 }
4506 if (!v.built())
4507 {
4508 std::ostringstream err_msg;
4509 err_msg << "The distribution of the global vector v must be setup.";
4510 throw OomphLibError(
4512 }
4513 if (*v.distribution_pt() != *this->master_distribution_pt())
4514 {
4515 std::ostringstream err_msg;
4516 err_msg << "The distribution of the global vector v must match the "
4517 << " specified master_distribution_pt(). \n"
4518 << "i.e. Distribution_pt in the master preconditioner";
4519 throw OomphLibError(
4521 }
4522 if (!b.built())
4523 {
4524 std::ostringstream err_msg;
4525 err_msg << "The distribution of the block vector b must be setup.";
4526 throw OomphLibError(
4528 }
4529
4530#endif
4531
4532 // Get the most fine grain dof
4533 Vector<unsigned> most_fine_grain_dof = Block_to_dof_map_fine[n];
4534
4535 // How many dofs are in this block?
4536 const unsigned n_dof_vec = Block_to_dof_map_fine[n].size();
4537
4538 if (n_dof_vec == 1)
4539 // There is only one dof, no need to split.
4540 {
4541 internal_return_block_vector(most_fine_grain_dof[0], b, v);
4542 }
4543 else
4544 // Need to split the vector up before we insert them all in one go.
4545 {
4547 for (unsigned d = 0; d < n_dof_vec; d++)
4548 {
4549 dof_vector[d].build(
4550 internal_block_distribution_pt(most_fine_grain_dof[d]));
4551 }
4552
4554
4555 // return to v
4556 internal_return_block_vectors(most_fine_grain_dof, dof_vector, v);
4557 }
4558 } // return_block_vector(...)
4559
4560 //============================================================================
4561 /// Given the naturally ordered vector, v, return
4562 /// the vector rearranged in block order in w. This is a legacy function
4563 /// from the old block preconditioning framework. Kept alive in case it may
4564 /// be needed again.
4565 ///
4566 /// This uses the variables ending in "get_ordered". We no longer use this
4567 /// type of method. This function copy values from v and re-order them
4568 /// in "block order" and place them in w. Block order means that the
4569 /// values in w are the same as the concatenated block vectors.
4570 ///
4571 /// I.e. - v is naturally ordered.
4572 /// v -> s_b, v is ordered into blocks vectors
4573 /// (requires communication)
4574 /// concatenate_without_communication(s_{0,...,nblocks},w) gives w.
4575 ///
4576 /// But this function skips out the concatenation part and builds w directly
4577 /// from v.
4578 ///
4579 /// This is nice but the function is implemented in such a way that it
4580 /// always use all the (internal) blocks and concatenated with the
4581 /// identity ordering. I.e. if this preconditioner has 3 block types, then
4582 /// w will always be:
4583 /// concatenate_without_communication([s_0, s_1, s_2], w). There is easy
4584 /// way to change this.
4585 ///
4586 /// Furthermore, it does not take into account the new dof type coarsening
4587 /// feature. So this function will most likely produce the incorrect vector
4588 /// w from what the user intended. It still works, but w will be the
4589 /// concatenation of the most fine grain dof block vectors with the
4590 /// "natural" dof type ordering.
4591 ///
4592 /// This has been superseded by the function
4593 /// get_block_ordered_preconditioner_vector(...) which does the correct
4594 /// thing.
4595 //============================================================================
4596 template<typename MATRIX>
4599 DoubleVector& w) const
4600 {
4601#ifdef PARANOID
4602 if (!v.built())
4603 {
4604 std::ostringstream error_message;
4605 error_message << "The distribution of the global vector v must be setup.";
4606 throw OomphLibError(
4607 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4608 }
4609 if (*v.distribution_pt() != *this->master_distribution_pt())
4610 {
4611 std::ostringstream error_message;
4612 error_message << "The distribution of the global vector v must match the "
4613 << " specified master_distribution_pt(). \n"
4614 << "i.e. Distribution_pt in the master preconditioner";
4615 throw OomphLibError(
4616 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4617 }
4618#endif
4619
4620 // Cleared and resized w for reordered vector
4621 w.build(this->internal_preconditioner_matrix_distribution_pt(), 0.0);
4622
4623 // if + only one processor
4624 // + more than one processor but matrix_pt is not distributed
4625 // then use the serial get_block method
4626 if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
4627 !this->distribution_pt()->distributed())
4628 {
4629 // number of blocks
4630 unsigned nblock = this->Internal_nblock_types;
4631
4632 // copy to w
4633 unsigned block_offset = 0;
4634 double* w_pt = w.values_pt();
4635 const double* v_pt = v.values_pt();
4636 for (unsigned b = 0; b < nblock; b++)
4637 {
4638 unsigned block_nrow = this->internal_block_dimension(b);
4639 for (unsigned i = 0; i < block_nrow; i++)
4640 {
4641 w_pt[block_offset + i] = v_pt[this->Global_index[b][i]];
4642 }
4644 }
4645 }
4646 // otherwise use mpi
4647 else
4648 {
4649#ifdef OOMPH_HAS_MPI
4650
4651 // my rank
4652 unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
4653
4654 // the number of processors
4655 unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
4656
4657 // determine the maximum number of rows to be sent or recv
4658 unsigned max_n_send_or_recv = 0;
4659 for (unsigned p = 0; p < nproc; p++)
4660 {
4662 std::max(max_n_send_or_recv, Nrows_to_send_for_get_ordered[p]);
4664 std::max(max_n_send_or_recv, Nrows_to_recv_for_get_ordered[p]);
4665 }
4666
4667 // create a vectors of 1s (the size of the nblock for the mpi indexed
4668 // data types
4669 int* block_lengths = new int[max_n_send_or_recv];
4670 for (unsigned i = 0; i < max_n_send_or_recv; i++)
4671 {
4672 block_lengths[i] = 1;
4673 }
4674
4675 // perform the sends and receives
4677 for (unsigned p = 0; p < nproc; p++)
4678 {
4679 // send and recv with other processors
4680 if (p != my_rank)
4681 {
4682 if (Nrows_to_send_for_get_ordered[p] > 0)
4683 {
4684 // create the send datatype
4686 MPI_Type_indexed(Nrows_to_send_for_get_ordered[p],
4688 Rows_to_send_for_get_ordered[p],
4689 MPI_DOUBLE,
4690 &type_send);
4692
4693 // send
4695 MPI_Isend(const_cast<double*>(v.values_pt()),
4696 1,
4697 type_send,
4698 p,
4699 0,
4700 this->distribution_pt()->communicator_pt()->mpi_comm(),
4701 &send_req);
4703 requests.push_back(send_req);
4704 }
4705
4706 if (Nrows_to_recv_for_get_ordered[p] > 0)
4707 {
4708 // create the recv datatype
4710 MPI_Type_indexed(Nrows_to_recv_for_get_ordered[p],
4712 Rows_to_recv_for_get_ordered[p],
4713 MPI_DOUBLE,
4714 &type_recv);
4716
4717 // recv
4719 MPI_Irecv(w.values_pt(),
4720 1,
4721 type_recv,
4722 p,
4723 0,
4724 this->distribution_pt()->communicator_pt()->mpi_comm(),
4725 &recv_req);
4727 requests.push_back(recv_req);
4728 }
4729 }
4730
4731 // communicate with self
4732 else
4733 {
4734 double* w_values_pt = w.values_pt();
4735 const double* v_values_pt = v.values_pt();
4736 for (unsigned i = 0; i < Nrows_to_send_for_get_ordered[p]; i++)
4737 {
4738 w_values_pt[Rows_to_recv_for_get_ordered[p][i]] =
4739 v_values_pt[Rows_to_send_for_get_ordered[p][i]];
4740 }
4741 }
4742 }
4743
4744 // and then just wait
4745 unsigned c = requests.size();
4747 if (c)
4748 {
4749 MPI_Waitall(c, &requests[0], &stat[0]);
4750 }
4751 delete[] block_lengths;
4752
4753#else
4754 // throw error
4755 std::ostringstream error_message;
4756 error_message << "The preconditioner is distributed and on more than one "
4757 << "processor. MPI is required.";
4758 throw OomphLibError(
4759 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4760#endif
4761 }
4762 }
4763
4764 //============================================================================
4765 /// Given the naturally ordered vector, v, return
4766 /// the vector rearranged in block order in w. This function calls
4767 /// get_concatenated_block_vector(...) with the identity block mapping.
4768 ///
4769 /// This function has been re-written to work with the new dof type
4770 /// coarsening feature. The old function is kept alive in
4771 /// internal_get_block_ordered_preconditioner_vector(...) and is moved to
4772 /// the private section of the code. The differences between the two are:
4773 ///
4774 /// 1) This function extracts all the block vectors (in one go) via the
4775 /// function internal_get_block_vectors(...), and concatenates them.
4776 ///
4777 /// 2) The old function makes use of the variables ending in "get_ordered",
4778 /// thus is slightly more efficient since it does not have to concatenate
4779 /// any block vectors.
4780 ///
4781 /// 3) The old function no longer respect the new indirections if dof types
4782 /// have been coarsened.
4783 ///
4784 /// 4) This function extracts the most fine grain dof-level vectors and
4785 /// concatenates them. These dof-level vectors respect the re-ordering
4786 /// caused by the coarsening of dof types. The overhead associated with
4787 /// concatenating DoubleVectors without communication is very small.
4788 ///
4789 /// This function should be used.
4790 //============================================================================
4791 template<typename MATRIX>
4793 const DoubleVector& v, DoubleVector& w)
4794 {
4795#ifdef PARANOID
4796 if (!v.built())
4797 {
4798 std::ostringstream error_message;
4799 error_message << "The distribution of the global vector v must be setup.";
4800 throw OomphLibError(
4801 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4802 }
4803 if (*v.distribution_pt() != *this->master_distribution_pt())
4804 {
4805 std::ostringstream error_message;
4806 error_message << "The distribution of the global vector v must match the "
4807 << " specified master_distribution_pt(). \n"
4808 << "i.e. Distribution_pt in the master preconditioner";
4809 throw OomphLibError(
4810 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4811 }
4812#endif
4813
4814 // Get the number of blocks.
4815 unsigned nblocks = this->nblock_types();
4816
4817 // Fill in the identity mapping.
4819 for (unsigned b = 0; b < nblocks; b++)
4820 {
4821 block_vec_number[b] = b;
4822 }
4823
4824 // Do the work.
4825 get_concatenated_block_vector(block_vec_number, v, w);
4826 } // get_block_ordered_preconditioner_vector(...)
4827
4828 //============================================================================
4829 /// Takes the block ordered vector, w, and reorders it in the natural
4830 /// order. Reordered vector is returned in v. Note: If the preconditioner is
4831 /// a subsidiary preconditioner then only the components of the vector
4832 /// associated with the blocks of the subsidiary preconditioner will be
4833 /// included. Hence the length of v is master_nrow() whereas that of the
4834 /// vector w is of length this->nrow().
4835 ///
4836 /// This is the return function for the function
4837 /// internal_get_block_ordered_preconditioner_vector(...).
4838 /// Both internal_get_block_ordered_preconditioner_vector(...) and
4839 /// internal_return_block_ordered_preconditioner_vector(...) has been
4840 /// superseded by the functions
4841 ///
4842 /// get_block_ordered_preconditioner_vector(...) and
4843 /// return_block_ordered_preconditioner_vector(...),
4844 ///
4845 /// Thus this function is moved to the private section of the code.
4846 //============================================================================
4847 template<typename MATRIX>
4850 DoubleVector& v) const
4851 {
4852#ifdef PARANOID
4853 if (!v.built())
4854 {
4855 std::ostringstream error_message;
4856 error_message << "The distribution of the global vector v must be setup.";
4857 throw OomphLibError(
4858 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4859 }
4860 if (*v.distribution_pt() != *this->master_distribution_pt())
4861 {
4862 std::ostringstream error_message;
4863 error_message << "The distribution of the global vector v must match the "
4864 << " specified master_distribution_pt(). \n"
4865 << "i.e. Distribution_pt in the master preconditioner";
4866 throw OomphLibError(
4867 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4868 }
4869 if (!w.built())
4870 {
4871 std::ostringstream error_message;
4872 error_message << "The distribution of the block vector w must be setup.";
4873 throw OomphLibError(
4874 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4875 }
4876 if (*w.distribution_pt() !=
4877 *this->internal_preconditioner_matrix_distribution_pt())
4878 {
4879 std::ostringstream error_message;
4880 error_message << "The distribution of the block vector w must match the "
4881 << " specified distribution at Distribution_pt[b]";
4882 throw OomphLibError(
4883 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4884 }
4885#endif
4886
4887
4888 // if + only one processor
4889 // + more than one processor but matrix_pt is not distributed
4890 // then use the serial get_block method
4891 if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
4892 !this->distribution_pt()->distributed())
4893 {
4894 // number of blocks
4895 unsigned nblock = this->Internal_nblock_types;
4896
4897 // copy to w
4898 unsigned block_offset = 0;
4899 const double* w_pt = w.values_pt();
4900 double* v_pt = v.values_pt();
4901 for (unsigned b = 0; b < nblock; b++)
4902 {
4903 unsigned block_nrow = this->internal_block_dimension(b);
4904 for (unsigned i = 0; i < block_nrow; i++)
4905 {
4906 v_pt[this->Global_index[b][i]] = w_pt[block_offset + i];
4907 }
4909 }
4910 }
4911 // otherwise use mpi
4912 else
4913 {
4914#ifdef OOMPH_HAS_MPI
4915
4916 // my rank
4917 unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
4918
4919 // the number of processors
4920 unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
4921
4922 // determine the maximum number of rows to be sent or recv
4923 unsigned max_n_send_or_recv = 0;
4924 for (unsigned p = 0; p < nproc; p++)
4925 {
4927 std::max(max_n_send_or_recv, Nrows_to_send_for_get_ordered[p]);
4929 std::max(max_n_send_or_recv, Nrows_to_recv_for_get_ordered[p]);
4930 }
4931
4932 // create a vectors of 1s (the size of the nblock for the mpi indexed
4933 // data types
4934 int* block_lengths = new int[max_n_send_or_recv];
4935 for (unsigned i = 0; i < max_n_send_or_recv; i++)
4936 {
4937 block_lengths[i] = 1;
4938 }
4939
4940 // perform the sends and receives
4942 for (unsigned p = 0; p < nproc; p++)
4943 {
4944 // send and recv with other processors
4945 if (p != my_rank)
4946 {
4947 if (Nrows_to_recv_for_get_ordered[p] > 0)
4948 {
4949 // create the send datatype
4951 MPI_Type_indexed(Nrows_to_recv_for_get_ordered[p],
4953 Rows_to_recv_for_get_ordered[p],
4954 MPI_DOUBLE,
4955 &type_send);
4957
4958 // send
4960 MPI_Isend(const_cast<double*>(w.values_pt()),
4961 1,
4962 type_send,
4963 p,
4964 0,
4965 this->distribution_pt()->communicator_pt()->mpi_comm(),
4966 &send_req);
4968 requests.push_back(send_req);
4969 }
4970
4971 if (Nrows_to_send_for_get_ordered[p] > 0)
4972 {
4973 // create the recv datatype
4975 MPI_Type_indexed(Nrows_to_send_for_get_ordered[p],
4977 Rows_to_send_for_get_ordered[p],
4978 MPI_DOUBLE,
4979 &type_recv);
4981
4982 // recv
4984 MPI_Irecv(v.values_pt(),
4985 1,
4986 type_recv,
4987 p,
4988 0,
4989 this->distribution_pt()->communicator_pt()->mpi_comm(),
4990 &recv_req);
4992 requests.push_back(recv_req);
4993 }
4994 }
4995
4996 // communicate wih self
4997 else
4998 {
4999 const double* w_values_pt = w.values_pt();
5000 double* v_values_pt = v.values_pt();
5001 for (unsigned i = 0; i < Nrows_to_send_for_get_ordered[p]; i++)
5002 {
5003 v_values_pt[Rows_to_send_for_get_ordered[p][i]] =
5004 w_values_pt[Rows_to_recv_for_get_ordered[p][i]];
5005 }
5006 }
5007 }
5008
5009 // and then just wait
5010 unsigned c = requests.size();
5012 if (c)
5013 {
5014 MPI_Waitall(c, &requests[0], &stat[0]);
5015 }
5016 delete[] block_lengths;
5017
5018#else
5019 // throw error
5020 std::ostringstream error_message;
5021 error_message << "The preconditioner is distributed and on more than one "
5022 << "processor. MPI is required.";
5023 throw OomphLibError(
5024 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5025#endif
5026 } // else use mpi
5027 } // function return_block_ordered_preconditioner_vector
5028
5029
5030 //============================================================================
5031 /// Takes the block ordered vector, w, and reorders it in natural
5032 /// order. Reordered vector is returned in v. Note: If the preconditioner is
5033 /// a subsidiary preconditioner then only the components of the vector
5034 /// associated with the blocks of the subsidiary preconditioner will be
5035 /// included. Hence the length of v is master_nrow() whereas that of the
5036 /// vector w is of length this->nrow().
5037 ///
5038 /// This is the return function for the function
5039 /// get_block_ordered_preconditioner_vector(...).
5040 ///
5041 /// It calls the function return_concatenated_block_vector(...) with the
5042 /// identity block number ordering.
5043 //============================================================================
5044 template<typename MATRIX>
5046 const DoubleVector& w, DoubleVector& v) const
5047 {
5048#ifdef PARANOID
5049 if (!v.built())
5050 {
5051 std::ostringstream error_message;
5052 error_message << "The distribution of the global vector v must be setup.";
5053 throw OomphLibError(
5054 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5055 }
5056 if (*v.distribution_pt() != *this->master_distribution_pt())
5057 {
5058 std::ostringstream error_message;
5059 error_message << "The distribution of the global vector v must match the "
5060 << " specified master_distribution_pt(). \n"
5061 << "i.e. Distribution_pt in the master preconditioner";
5062 throw OomphLibError(
5063 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5064 }
5065 if (!w.built())
5066 {
5067 std::ostringstream error_message;
5068 error_message << "The distribution of the block vector w must be setup.";
5069 throw OomphLibError(
5070 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5071 }
5072 if (*w.distribution_pt() != *this->preconditioner_matrix_distribution_pt())
5073 {
5074 std::ostringstream error_message;
5075 error_message << "The distribution of the block vector w must match the "
5076 << "concatenations of distributions in "
5077 << "Block_distribution_pt.\n";
5078 throw OomphLibError(
5079 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5080 }
5081#endif
5082
5083 // Split into the block vectors.
5084 const unsigned nblocks = nblock_types();
5086 for (unsigned b = 0; b < nblocks; b++)
5087 {
5088 block_vec_number[b] = b;
5089 }
5090
5091 return_concatenated_block_vector(block_vec_number, w, v);
5092 } // function return_block_ordered_preconditioner_vector
5093
5094 //=============================================================================
5095 /// Gets block (i,j) from the matrix pointed to by
5096 /// Matrix_pt and returns it in output_block. This is associated with the
5097 /// internal blocks. Please use the other get_block(...) function.
5098 //=============================================================================
5099 template<>
5101 const unsigned& block_i,
5102 const unsigned& block_j,
5104 {
5105#ifdef PARANOID
5106 // the number of blocks
5107 const unsigned n_blocks = this->internal_nblock_types();
5108
5109 // paranoid check that block i is in this block preconditioner
5110 if (block_i >= n_blocks || block_j >= n_blocks)
5111 {
5112 std::ostringstream error_message;
5113 error_message
5114 << "Requested block (" << block_i << "," << block_j
5115 << "), however this preconditioner has internal_nblock_types() "
5116 << "= " << internal_nblock_types() << std::endl;
5117 throw OomphLibError(
5118 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5119 }
5120
5121 // Check that the matrix is the same as that of the master
5122 if (is_subsidiary_block_preconditioner())
5123 {
5124 if (master_block_preconditioner_pt()->matrix_pt() != matrix_pt())
5125 {
5126 std::string err = "Master and subs should have same matrix.";
5127 throw OomphLibError(
5129 }
5130 }
5131#endif
5132
5133 // Cast the pointer
5134 CRDoubleMatrix* cr_matrix_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt());
5135
5136 // if + only one processor
5137 // + more than one processor but matrix_pt is not distributed
5138 // then use the serial get_block method
5139 if (cr_matrix_pt->distribution_pt()->communicator_pt()->nproc() == 1 ||
5140 !cr_matrix_pt->distribution_pt()->distributed())
5141 {
5142 // pointers for the jacobian matrix is compressed row sparse format
5143 int* j_row_start;
5144 int* j_column_index;
5145 double* j_value;
5146
5147 // sets pointers to jacobian matrix
5148 j_row_start = cr_matrix_pt->row_start();
5149 j_column_index = cr_matrix_pt->column_index();
5150 j_value = cr_matrix_pt->value();
5151
5152 // get the block dimensions
5153 unsigned block_nrow = this->internal_block_dimension(block_i);
5154 unsigned block_ncol = this->internal_block_dimension(block_j);
5155
5156 // allocate temporary storage for the component vectors of block (i,j)
5157 // temp_ptr is used to point to an element in each column - required as
5158 // cannot assume that order of block's rows in jacobian and the block
5159 // matrix will be the same
5160 int* temp_row_start = new int[block_nrow + 1];
5161 for (unsigned i = 0; i <= block_nrow; i++)
5162 {
5163 temp_row_start[i] = 0;
5164 }
5166 int block_nnz = 0;
5167
5168 // get number of rows in source matrix
5169 unsigned master_nrow = this->master_nrow();
5170
5171 // determine how many non zeros there are in the block (i,j)
5172 // also determines how many non zeros are stored in each row or column -
5173 // stored in temp_ptr temporarily
5174 for (unsigned k = 0; k < master_nrow; k++)
5175 {
5176 if (internal_block_number(k) == static_cast<int>(block_i))
5177 {
5178 for (int l = j_row_start[k]; l < j_row_start[k + 1]; l++)
5179 {
5180 if (internal_block_number(j_column_index[l]) ==
5181 static_cast<int>(block_j))
5182 {
5183 block_nnz++;
5184 temp_ptr[internal_index_in_block(k) + 1]++;
5185 }
5186 }
5187 }
5188 }
5189
5190 // if the matrix is not empty
5191 int* temp_column_index = new int[block_nnz];
5192 double* temp_value = new double[block_nnz];
5193 if (block_nnz > 0)
5194 {
5195 // uses number of elements in each column of block to determine values
5196 // for the block column start (temp_row_start)
5197 temp_row_start[0] = 0;
5198 for (unsigned k = 1; k <= block_nrow; k++)
5199 {
5202 }
5203
5204 // copies the relevant elements of the jacobian to the correct entries
5205 // of the block matrix
5206 for (unsigned k = 0; k < master_nrow; k++)
5207 {
5208 if (internal_block_number(k) == static_cast<int>(block_i))
5209 {
5210 for (int l = j_row_start[k]; l < j_row_start[k + 1]; l++)
5211 {
5212 if (internal_block_number(j_column_index[l]) ==
5213 static_cast<int>(block_j))
5214 {
5215 int kk = temp_ptr[internal_index_in_block(k)]++;
5216 temp_value[kk] = j_value[l];
5218 internal_index_in_block(j_column_index[l]);
5219 }
5220 }
5221 }
5222 }
5223 }
5224
5225
5226 // Fill in the compressed row matrix ??ds Note: I kept the calls to
5227 // build as close as I could to before (had to replace new(dist) with
5228 // .build(dist) ).
5229 output_block.build(Internal_block_distribution_pt[block_i]);
5230 output_block.build_without_copy(
5232
5233#ifdef PARANOID
5234 // checks to see if block matrix has been set up correctly
5235 // block_matrix_test(matrix_pt,block_i,block_j,block_pt);
5236 if (Run_block_matrix_test)
5237 {
5238 // checks to see if block matrix has been set up correctly
5239 block_matrix_test(block_i, block_j, &output_block);
5240 }
5241#endif
5242 }
5243
5244
5245 // otherwise we are dealing with a distributed matrix
5246 else
5247 {
5248#ifdef OOMPH_HAS_MPI
5249 // number of processors
5250 unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
5251
5252 // my rank
5253 unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
5254
5255 // sets pointers to jacobian matrix
5256 int* j_row_start = cr_matrix_pt->row_start();
5257 int* j_column_index = cr_matrix_pt->column_index();
5258 double* j_value = cr_matrix_pt->value();
5259
5260 // number of non zeros in each row to be sent
5262
5263 // number of non zeros in each row to be received
5265
5266 // storage for data to be sent
5269
5270 // number of non zeros to be sent to each processor
5272
5273 // number of rows of the block matrix on this processor
5274 unsigned nrow_local =
5275 Internal_block_distribution_pt[block_i]->nrow_local();
5276
5277 // resize the nnz storage and compute nnz_send
5278 // and send and recv the nnz
5281 for (unsigned p = 0; p < nproc; p++)
5282 {
5283 int nrow_send = Nrows_to_send_for_get_block(block_i, p);
5284 int nrow_recv = Nrows_to_recv_for_get_block(block_i, p);
5285
5286 // assemble nnz recv
5287 nnz_recv[p] = new int[nrow_recv];
5288
5289 // assemble the storage to send
5290 if (nrow_send > 0 && p != my_rank)
5291 {
5292 nnz_send[p] = new int[nrow_send];
5293 }
5294
5295 // compute the number of nnzs in each row and the total number
5296 // of nnzs
5297 for (int i = 0; i < nrow_send; i++)
5298 {
5299 unsigned row = Rows_to_send_for_get_block(block_i, p)[i];
5300 int c = 0;
5301 for (int r = j_row_start[row]; r < j_row_start[row + 1]; r++)
5302 {
5303 if (internal_block_number(j_column_index[r]) == int(block_j))
5304 {
5305 c++;
5306 }
5307 }
5308 if (p != my_rank)
5309 {
5310 nnz_send[p][i] = c;
5311 }
5312 else
5313 {
5314 nnz_recv[p][i] = c;
5315 }
5316 total_nnz_send[p] += c;
5317 }
5318
5319 // send
5320 if (p != my_rank)
5321 {
5322 if (nrow_send)
5323 {
5326 nrow_send,
5327 MPI_INT,
5328 p,
5329 0,
5330 this->distribution_pt()->communicator_pt()->mpi_comm(),
5331 &req);
5332 send_req.push_back(req);
5333 }
5334
5335 // recv
5336 if (nrow_recv)
5337 {
5340 nrow_recv,
5341 MPI_INT,
5342 p,
5343 0,
5344 this->distribution_pt()->communicator_pt()->mpi_comm(),
5345 &req);
5346 recv1_req.push_back(req);
5347 }
5348 }
5349 }
5350
5351 // next assemble the values and row_start data to be sent for each
5352 // processor
5353 for (unsigned p = 0; p < nproc; p++)
5354 {
5355 int nrow_send = Nrows_to_send_for_get_block(block_i, p);
5356
5357 // assemble the storage for the values and column indices to be sent
5358 if (p != my_rank)
5359 {
5360 if (total_nnz_send[p] > 0)
5361 {
5362 values_for_proc[p] = new double[total_nnz_send[p]];
5364
5365 // copy the values and column indices to the storage
5366 unsigned ptr = 0;
5367 for (int i = 0; i < nrow_send; i++)
5368 {
5369 unsigned row = Rows_to_send_for_get_block(block_i, p)[i];
5370 for (int r = j_row_start[row]; r < j_row_start[row + 1]; r++)
5371 {
5372 if (internal_block_number(j_column_index[r]) == int(block_j))
5373 {
5376 internal_index_in_block(j_column_index[r]);
5377 ptr++;
5378 }
5379 }
5380 }
5381
5382 // create the datatypes
5388
5389 // get the start address of the vectors
5393
5394 // compute the displacements
5395 displacement[1] -= displacement[0];
5396 displacement[0] -= displacement[0];
5397
5398 // compute the block lengths
5399 int length[2];
5400 length[0] = length[1] = 1;
5401
5402 // build the struct data type
5406 MPI_Type_free(&types[0]);
5407 MPI_Type_free(&types[1]);
5408
5409 // and send
5412 1,
5413 final_type,
5414 p,
5415 1,
5416 this->distribution_pt()->communicator_pt()->mpi_comm(),
5417 &req);
5418 send_req.push_back(req);
5420 }
5421 }
5422 }
5423
5424 // wait for the recv to complete (the row_start recv which actually
5425 // contains the number of nnzs in each row)
5426 int c_recv = recv1_req.size();
5427 if (c_recv != 0)
5428 {
5430 }
5431
5432 // compute the total number of nnzs to be received
5434 int local_block_nnz = 0;
5435 for (unsigned p = 0; p < nproc; p++)
5436 {
5437 // compute the total nnzs
5438 for (unsigned i = 0; i < Nrows_to_recv_for_get_block(block_i, p); i++)
5439 {
5441 }
5443 }
5444
5445 // compute the offset for each block of nnzs (a matrix row) in the
5446 // values_recv and column_index_recv vectors
5447
5448 // fisrt determine how many blocks of rows are to be recv
5450 for (unsigned p = 0; p < nproc; p++)
5451 {
5452 if (Nrows_to_recv_for_get_block(block_i, p) > 0)
5453 {
5454 n_recv_block[p] = 1;
5455 }
5456 for (unsigned i = 1; i < Nrows_to_recv_for_get_block(block_i, p); i++)
5457 {
5458 if (Rows_to_recv_for_get_block(block_i, p)[i] !=
5459 Rows_to_recv_for_get_block(block_i, p)[i - 1] + 1)
5460 {
5461 n_recv_block[p]++;
5462 }
5463 }
5464 }
5465
5466 // next assemble row start recv
5467 int* row_start_recv = new int[nrow_local + 1];
5468 for (unsigned i = 0; i <= nrow_local; i++)
5469 {
5470 row_start_recv[i] = 0;
5471 }
5472 for (unsigned p = 0; p < nproc; p++)
5473 {
5474 for (unsigned i = 0; i < Nrows_to_recv_for_get_block(block_i, p); i++)
5475 {
5476 row_start_recv[Rows_to_recv_for_get_block(block_i, p)[i]] =
5477 nnz_recv[p][i];
5478 }
5479 }
5480 int g = row_start_recv[0];
5481 row_start_recv[0] = 0;
5482 for (unsigned i = 1; i < nrow_local; i++)
5483 {
5484 int temp_g = g;
5485 g = row_start_recv[i];
5487 }
5488 row_start_recv[nrow_local] = row_start_recv[nrow_local - 1] + g;
5489
5490 // next assemble the offset and the number of nzs in each recv block
5493 for (unsigned p = 0; p < nproc; p++)
5494 {
5495 if (Nrows_to_recv_for_get_block(block_i, p) > 0)
5496 {
5497 offset_recv_block[p] = new int[n_recv_block[p]];
5498 offset_recv_block[p][0] = 0;
5499 nnz_recv_block[p] = new int[n_recv_block[p]];
5500 for (int i = 0; i < n_recv_block[p]; i++)
5501 {
5502 nnz_recv_block[p][i] = 0;
5503 }
5504 unsigned ptr = 0;
5505 nnz_recv_block[p][ptr] += nnz_recv[p][0];
5506 offset_recv_block[p][0] =
5507 row_start_recv[Rows_to_recv_for_get_block(block_i, p)[0]];
5508 for (unsigned i = 1; i < Nrows_to_recv_for_get_block(block_i, p); i++)
5509 {
5510 if (Rows_to_recv_for_get_block(block_i, p)[i] !=
5511 Rows_to_recv_for_get_block(block_i, p)[i - 1] + 1)
5512 {
5513 ptr++;
5515 row_start_recv[Rows_to_recv_for_get_block(block_i, p)[i]];
5516 }
5517 nnz_recv_block[p][ptr] += nnz_recv[p][i];
5518 }
5519 }
5520 delete[] nnz_recv[p];
5521 }
5522
5523 // post the receives
5524 int* column_index_recv = new int[local_block_nnz];
5525 double* values_recv = new double[local_block_nnz];
5527 for (unsigned p = 0; p < nproc; p++)
5528 {
5529 if (p != my_rank)
5530 {
5531 if (total_nnz_recv_from_proc[p] != 0)
5532 {
5533 // create the datatypes
5538 MPI_DOUBLE,
5539 &types[0]);
5544 MPI_INT,
5545 &types[1]);
5547
5548 // compute the displacements
5554
5555 // compute the block lengths
5556 int length[2];
5557 length[0] = length[1] = 1;
5558
5559 // create the final datatype
5564 MPI_Type_free(&types[0]);
5565 MPI_Type_free(&types[1]);
5566
5567 // and the recv
5570 1,
5571 final_type,
5572 p,
5573 1,
5574 this->distribution_pt()->communicator_pt()->mpi_comm(),
5575 &req);
5576 recv2_req.push_back(req);
5578 }
5579 }
5580 else
5581 {
5582 // next send the values and column indices to self
5583 unsigned block_ptr = 0;
5584 unsigned counter = 0;
5585 int nrow_send = Nrows_to_send_for_get_block(block_i, my_rank);
5586 if (nrow_send > 0)
5587 {
5588 unsigned offset = offset_recv_block[my_rank][0];
5589 for (int i = 0; i < nrow_send; i++)
5590 {
5591 if (i > 0)
5592 {
5593 if (Rows_to_recv_for_get_block(block_i, p)[i] !=
5594 Rows_to_recv_for_get_block(block_i, p)[i - 1] + 1)
5595 {
5596 counter = 0;
5597 block_ptr++;
5599 }
5600 }
5601 unsigned row = Rows_to_send_for_get_block(block_i, my_rank)[i];
5602 for (int r = j_row_start[row]; r < j_row_start[row + 1]; r++)
5603 {
5604 if (internal_block_number(j_column_index[r]) == int(block_j))
5605 {
5606 values_recv[offset + counter] = j_value[r];
5607 column_index_recv[offset + counter] =
5608 internal_index_in_block(j_column_index[r]);
5609 counter++;
5610 }
5611 }
5612 }
5613 }
5614 }
5615 }
5616
5617 // wait for the recv to complete (for the column_index and the values_
5618 c_recv = recv2_req.size();
5619 if (c_recv != 0)
5620 {
5622 }
5623
5624 // Fill in the compressed row matrix
5625 output_block.build(Internal_block_distribution_pt[block_i]);
5626 output_block.build_without_copy(this->internal_block_dimension(block_j),
5631
5632 // wait for the send to complete (nnz / row_start)
5633 int c_send = send_req.size();
5634 if (c_send)
5635 {
5637 }
5638
5639 // delete temp storage used for assembling data for communication
5640 for (unsigned p = 0; p < nproc; p++)
5641 {
5642 delete[] nnz_send[p];
5643 delete[] column_index_for_proc[p];
5644 delete[] values_for_proc[p];
5645 delete[] offset_recv_block[p];
5646 delete[] nnz_recv_block[p];
5647 }
5648#else
5649 // throw error
5650 std::ostringstream error_message;
5651 error_message << "The matrix is distributed and on more than one "
5652 << "processor. MPI is required.";
5653 throw OomphLibError(
5654 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5655#endif
5656 }
5657 }
5658
5659 //=============================================================================
5660 /// Gets dof-level block (i,j).
5661 /// If Replacement_dof_block_pt(i,j) is not null, then the replacement
5662 /// block is returned via a deep copy.
5663 ///
5664 /// Otherwise if this is the uppermost block preconditioner then it calls
5665 /// internal_get_block(i,j), else if it is a subsidiary
5666 /// block preconditioner, it will call it's master block preconditioners'
5667 /// get_dof_level_block function.
5668 //=============================================================================
5669 template<>
5671 const unsigned& block_i,
5672 const unsigned& block_j,
5674 const bool& ignore_replacement_block) const
5675 {
5676#ifdef PARANOID
5677 // the number of dof types.
5678 unsigned para_ndofs = ndof_types();
5679
5680 // paranoid check that block i is in this block preconditioner
5681 if (block_i >= para_ndofs || block_j >= para_ndofs)
5682 {
5683 std::ostringstream err_msg;
5684 err_msg << "Requested dof block (" << block_i << "," << block_j
5685 << "), however this preconditioner has ndof_types() "
5686 << "= " << para_ndofs << std::endl;
5687 throw OomphLibError(
5689 }
5690#endif
5691
5693 Replacement_dof_block_pt.get(block_i, block_j);
5694
5696 {
5697 // Getting the block from parent preconditioner
5698 const unsigned ndof_in_parent_i =
5699 Doftype_coarsen_map_coarse[block_i].size();
5700 const unsigned ndof_in_parent_j =
5701 Doftype_coarsen_map_coarse[block_j].size();
5702
5703 if (ndof_in_parent_i == 1 && ndof_in_parent_j == 1)
5704 {
5705 unsigned parent_dof_i = Doftype_coarsen_map_coarse[block_i][0];
5706 unsigned parent_dof_j = Doftype_coarsen_map_coarse[block_j][0];
5707
5708 if (is_master_block_preconditioner())
5709 {
5710 internal_get_block(parent_dof_i, parent_dof_j, output_block);
5711 }
5712 else
5713 {
5714 parent_dof_i = Doftype_in_master_preconditioner_coarse[parent_dof_i];
5715 parent_dof_j = Doftype_in_master_preconditioner_coarse[parent_dof_j];
5716
5717 master_block_preconditioner_pt()->get_dof_level_block(
5719 }
5720 }
5721 else
5722 {
5725
5728
5729 for (unsigned dof_i = 0; dof_i < ndof_in_parent_i; dof_i++)
5730 {
5731 unsigned parent_dof_i = Doftype_coarsen_map_coarse[block_i][dof_i];
5732 if (is_subsidiary_block_preconditioner())
5733 {
5734 parent_dof_i =
5735 Doftype_in_master_preconditioner_coarse[parent_dof_i];
5736 }
5737
5738 for (unsigned dof_j = 0; dof_j < ndof_in_parent_j; dof_j++)
5739 {
5740 unsigned parent_dof_j = Doftype_coarsen_map_coarse[block_j][dof_j];
5741
5743
5744 new_block[dof_i][dof_j] = 1;
5745
5746 if (is_master_block_preconditioner())
5747 {
5748 internal_get_block(
5750 }
5751 else
5752 {
5753 parent_dof_j =
5754 Doftype_in_master_preconditioner_coarse[parent_dof_j];
5755
5756 master_block_preconditioner_pt()->get_dof_level_block(
5761 }
5762 }
5763 }
5764
5766
5767 for (unsigned parent_dof_i = 0; parent_dof_i < ndof_in_parent_i;
5768 parent_dof_i++)
5769 {
5770 unsigned mapped_dof_i =
5771 Doftype_coarsen_map_coarse[block_i][parent_dof_i];
5772
5773 if (is_master_block_preconditioner())
5774 {
5776 Internal_block_distribution_pt[mapped_dof_i];
5777 }
5778 else
5779 {
5780 mapped_dof_i =
5781 Doftype_in_master_preconditioner_coarse[mapped_dof_i];
5782
5784 master_block_preconditioner_pt()->dof_block_distribution_pt(
5785 mapped_dof_i);
5786 }
5787 }
5788
5790
5791 for (unsigned parent_dof_j = 0; parent_dof_j < ndof_in_parent_j;
5792 parent_dof_j++)
5793 {
5794 unsigned mapped_dof_j =
5795 Doftype_coarsen_map_coarse[block_j][parent_dof_j];
5796
5797 if (is_master_block_preconditioner())
5798 {
5800 Internal_block_distribution_pt[mapped_dof_j];
5801 }
5802 else
5803 {
5804 mapped_dof_j =
5805 Doftype_in_master_preconditioner_coarse[mapped_dof_j];
5807 master_block_preconditioner_pt()->dof_block_distribution_pt(
5808 mapped_dof_j);
5809 }
5810 }
5811
5814
5815 for (unsigned dof_i = 0; dof_i < ndof_in_parent_i; dof_i++)
5816 {
5817 for (unsigned dof_j = 0; dof_j < ndof_in_parent_j; dof_j++)
5818 {
5819 if (new_block[dof_i][dof_j])
5820 {
5821 delete tmp_blocks_pt(dof_i, dof_j);
5822 }
5823 }
5824 }
5825 }
5826 }
5827 else
5828 {
5830 }
5831 }
5832
5833 //=============================================================================
5834 /// test function to check that every element in the block matrix
5835 /// (block_i,block_j) matches the corresponding element in the original matrix
5836 //=============================================================================
5837 template<typename MATRIX>
5839 const unsigned& block_i,
5840 const unsigned& block_j,
5841 const MATRIX* block_matrix_pt) const
5842 {
5843 // boolean flag to indicate whether test is passed
5844 bool check = true;
5845
5846 // number of rows in matrix
5847 unsigned n_row = matrix_pt()->nrow();
5848
5849 // number of columns in matrix
5850 unsigned n_col = matrix_pt()->ncol();
5851
5852 // loop over rows of original matrix
5853 for (unsigned i = 0; i < n_row; i++)
5854 {
5855 // if this coefficient is associated with a block in this block
5856 // preconditioner
5857 if (static_cast<int>(block_i) == this->internal_block_number(i))
5858 {
5859 // loop over columns of original matrix
5860 for (unsigned j = 0; j < n_col; j++)
5861 {
5862 // if the coeeficient is associated with a block in this block
5863 // preconditioner
5864 if (static_cast<int>(block_j) == this->internal_block_number(j))
5865 {
5866 // check whether elements in original matrix and matrix of block
5867 // pointers match
5868 if (matrix_pt()->operator()(i, j) !=
5869 block_matrix_pt->operator()(internal_index_in_block(i),
5870 internal_index_in_block(j)))
5871 {
5872 check = false;
5873 }
5874 }
5875 }
5876 }
5877 }
5878
5879 // throw error
5880 if (!check)
5881 {
5882 std::ostringstream error_message;
5883 error_message << "The require elements have not been successfully copied"
5884 << " from the original matrix to the block matrices";
5885 throw OomphLibError(
5886 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5887 }
5888 }
5889
5890
5892
5893} // namespace oomph
e
Definition cfortran.h:571
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
void internal_return_block_vector(const unsigned &n, const DoubleVector &b, DoubleVector &v) const
Takes the n-th block ordered vector, b, and copies its entries to the appropriate entries in the natu...
void return_block_vector(const unsigned &n, const DoubleVector &b, DoubleVector &v) const
Takes the n-th block ordered vector, b, and copies its entries to the appropriate entries in the natu...
void internal_return_block_vectors(const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
A helper function, takes the vector of block vectors, s, and copies its entries into the naturally or...
void get_dof_level_block(const unsigned &i, const unsigned &j, MATRIX &output_block, const bool &ignore_replacement_block=false) const
Gets dof-level block (i,j). If Replacement_dof_block_pt(i,j) is not null, then the replacement block ...
void block_matrix_test(const unsigned &i, const unsigned &j, const MATRIX *block_matrix_pt) const
Private helper function to check that every element in the block matrix (i,j) matches the correspondi...
void internal_return_block_ordered_preconditioner_vector(const DoubleVector &w, DoubleVector &v) const
Takes the block ordered vector, w, and reorders it in the natural order. Reordered vector is returned...
void get_blocks(DenseMatrix< bool > &required_blocks, DenseMatrix< MATRIX * > &block_matrix_pt) const
Get all the block matrices required by the block preconditioner. Takes a pointer to a matrix of bools...
void return_concatenated_block_vector(const Vector< unsigned > &block_vec_number, const DoubleVector &b, DoubleVector &v) const
Takes concatenated block ordered vector, b, and copies its entries to the appropriate entries in the ...
static bool Run_block_matrix_test
Static boolean to allow block_matrix_test(...) to be run. Defaults to false.
void return_block_vectors(const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
Takes the vector of block vectors, s, and copies its entries into the naturally ordered vector,...
void get_block_vector(const unsigned &n, const DoubleVector &v, DoubleVector &b) const
Takes the naturally ordered vector, v and returns the n-th block vector, b. Here n is the block numbe...
void return_block_ordered_preconditioner_vector(const DoubleVector &w, DoubleVector &v) const
Takes the block ordered vector, w, and reorders it in natural order. Reordered vector is returned in ...
void turn_into_subsidiary_block_preconditioner(BlockPreconditioner< MATRIX > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse)
Function to turn this preconditioner into a subsidiary preconditioner that operates within a bigger "...
void internal_get_block(const unsigned &i, const unsigned &j, MATRIX &output_block) const
Gets block (i,j) from the matrix pointed to by Matrix_pt and returns it in output_block....
void internal_get_block_vectors(const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
Takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to th...
void get_block_vectors(const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
Takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to th...
void internal_get_block_vector(const unsigned &n, const DoubleVector &v, DoubleVector &b) const
A helper function, takes the naturally ordered vector, v, and extracts the n-th block vector,...
virtual void block_setup()
Determine the size of the matrix blocks and setup the lookup schemes relating the global degrees of f...
void get_concatenated_block_vector(const Vector< unsigned > &block_vec_number, const DoubleVector &v, DoubleVector &b)
Takes the naturally ordered vector and extracts the blocks indicated by the block number (the values)...
void internal_get_block_ordered_preconditioner_vector(const DoubleVector &v, DoubleVector &w) const
Given the naturally ordered vector, v, return the vector rearranged in block order in w....
void get_block_ordered_preconditioner_vector(const DoubleVector &v, DoubleVector &w)
Given the naturally ordered vector, v, return the vector rearranged in block order in w....
A class for compressed row matrices. This is a distributable object.
Definition matrices.h:888
static long Is_unclassified
Static "Magic number" used in place of the equation number to denote a value that hasn't been classif...
Definition nodes.h:192
unsigned nrow() const
access function to the number of global rows.
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
A vector in the mathematical sense, initially developed for linear algebra type applications....
void build(const DoubleVector &old_vector)
Just copys the argument DoubleVector.
double * values_pt()
access function to the underlying values
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
virtual unsigned ndof_types() const
The number of types of degrees of freedom in this element are sub-divided into.
Definition elements.h:1189
bool is_halo() const
Is this element a halo?
Definition elements.h:1150
void dof_vector(const unsigned &t, Vector< double > &dof)
Return the vector of dof values at time level t.
Definition elements.h:828
unsigned ndof() const
Return the number of equations/dofs in the element.
Definition elements.h:822
An OomphLibError object which should be thrown when an run-time error is encountered....
An OomphLibWarning object which should be created as a temporary object to issue a warning....
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition Vector.h:58
void deep_copy(const CRDoubleMatrix *const in_matrix_pt, CRDoubleMatrix &out_matrix)
Create a deep copy of the matrix pointed to by in_matrix_pt.
Definition matrices.h:3490
void concatenate_without_communication(const Vector< LinearAlgebraDistribution * > &row_distribution_pt, const Vector< LinearAlgebraDistribution * > &col_distribution_pt, const DenseMatrix< CRDoubleMatrix * > &matrix_pt, CRDoubleMatrix &result_matrix)
Concatenate CRDoubleMatrix matrices.
Definition matrices.cc:5223
void concatenate_without_communication(const Vector< DoubleVector * > &in_vector_pt, DoubleVector &out_vector)
Concatenate DoubleVectors. Takes a Vector of DoubleVectors. If the out vector is built,...
void split_without_communication(const DoubleVector &in_vector, Vector< DoubleVector * > &out_vector_pt)
Split a DoubleVector into the out DoubleVectors. Data stays on its current processor,...
void concatenate(const Vector< LinearAlgebraDistribution * > &in_distribution_pt, LinearAlgebraDistribution &out_distribution)
Takes a vector of LinearAlgebraDistribution objects and concatenates them such that the nrow_local of...
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).