Merge lp:~jobh/dolfin/symmetric-assembler-update into lp:~fenics-core/dolfin/trunk
- symmetric-assembler-update
- Merge into trunk
Proposed by
Joachim Haga
Status: | Merged |
---|---|
Approved by: | Garth Wells |
Approved revision: | 6958 |
Merged at revision: | 6959 |
Proposed branch: | lp:~jobh/dolfin/symmetric-assembler-update |
Merge into: | lp:~fenics-core/dolfin/trunk |
Diff against target: |
929 lines (+212/-502) 4 files modified
dolfin/fem/Assembler.cpp (+18/-5) dolfin/fem/Assembler.h (+10/-1) dolfin/fem/SymmetricAssembler.cpp (+135/-471) dolfin/fem/SymmetricAssembler.h (+49/-25) |
To merge this branch: | bzr merge lp:~jobh/dolfin/symmetric-assembler-update |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Garth Wells | Pending | ||
Review via email:
|
Commit message
Description of the change
Update and re-enable the symmetric assembler.
To address concerns about code duplication, a small change is made in the standard Assembler to allow SymmetricAssembler to inherit from and reuse much of it. The symmetric assembler now does not do any integration itself, it only modifies the finished local tensor just before adding it to the global tensor.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'dolfin/fem/Assembler.cpp' |
2 | --- dolfin/fem/Assembler.cpp 2012-09-10 14:50:55 +0000 |
3 | +++ dolfin/fem/Assembler.cpp 2012-10-04 21:38:25 +0000 |
4 | @@ -18,9 +18,10 @@ |
5 | // Modified by Garth N. Wells 2007-2009 |
6 | // Modified by Ola Skavhaug 2007-2009 |
7 | // Modified by Kent-Andre Mardal 2008 |
8 | +// Modified by Joachim B Haga 2012 |
9 | // |
10 | // First added: 2007-01-17 |
11 | -// Last changed: 2011-09-29 |
12 | +// Last changed: 2012-10-04 |
13 | |
14 | #include <boost/scoped_ptr.hpp> |
15 | |
16 | @@ -236,7 +237,7 @@ |
17 | if (values && ufc.form.rank() == 0) |
18 | (*values)[cell->index()] = ufc.A[0]; |
19 | else |
20 | - A.add(&ufc.A[0], dofs); |
21 | + add_to_global_tensor(A, ufc.A, dofs); |
22 | |
23 | p++; |
24 | } |
25 | @@ -322,7 +323,7 @@ |
26 | integral->tabulate_tensor(&ufc.A[0], ufc.w(), ufc.cell, local_facet); |
27 | |
28 | // Add entries to global tensor |
29 | - A.add(&ufc.A[0], dofs); |
30 | + add_to_global_tensor(A, ufc.A, dofs); |
31 | |
32 | p++; |
33 | } |
34 | @@ -353,8 +354,13 @@ |
35 | for (uint i = 0; i < form_rank; ++i) |
36 | dofmaps.push_back(a.function_space(i)->dofmap().get()); |
37 | |
38 | - // Vector to hold dofs for cells |
39 | + // Vector to hold dofs for cells, and a vector holding pointers to same |
40 | std::vector<std::vector<uint> > macro_dofs(form_rank); |
41 | + std::vector<const std::vector<uint>* > macro_dof_ptrs(form_rank); |
42 | + for (uint i = 0; i < form_rank; i++) |
43 | + { |
44 | + macro_dof_ptrs[i] = ¯o_dofs[i]; |
45 | + } |
46 | |
47 | // Interior facet integral |
48 | dolfin_assert(!ufc.interior_facet_integrals.empty()); |
49 | @@ -439,9 +445,16 @@ |
50 | local_facet0, local_facet1); |
51 | |
52 | // Add entries to global tensor |
53 | - A.add(&ufc.macro_A[0], macro_dofs); |
54 | + add_to_global_tensor(A, ufc.macro_A, macro_dof_ptrs); |
55 | |
56 | p++; |
57 | } |
58 | } |
59 | //----------------------------------------------------------------------------- |
60 | +void Assembler::add_to_global_tensor(GenericTensor& A, |
61 | + std::vector<double>& cell_tensor, |
62 | + std::vector<const std::vector<uint>* >& dofs) |
63 | +{ |
64 | + A.add(&cell_tensor[0], dofs); |
65 | +} |
66 | +//----------------------------------------------------------------------------- |
67 | |
68 | === modified file 'dolfin/fem/Assembler.h' |
69 | --- dolfin/fem/Assembler.h 2012-09-09 10:53:56 +0000 |
70 | +++ dolfin/fem/Assembler.h 2012-10-04 21:38:25 +0000 |
71 | @@ -17,9 +17,10 @@ |
72 | // |
73 | // Modified by Garth N. Wells, 2007-2008. |
74 | // Modified by Ola Skavhaug, 2008. |
75 | +// Modified by Joachim B Haga, 2012. |
76 | // |
77 | // First added: 2007-01-17 |
78 | -// Last changed: 2011-09-29 |
79 | +// Last changed: 2012-10-04 |
80 | |
81 | #ifndef __ASSEMBLER_H |
82 | #define __ASSEMBLER_H |
83 | @@ -131,6 +132,14 @@ |
84 | const MeshFunction<uint>* domains, |
85 | std::vector<double>* values); |
86 | |
87 | + protected: |
88 | + |
89 | + /// Add cell tensor to global tensor. Hook to allow the SymmetricAssembler |
90 | + /// to split the cell tensor into symmetric/antisymmetric parts. |
91 | + virtual void add_to_global_tensor(GenericTensor& A, |
92 | + std::vector<double>& cell_tensor, |
93 | + std::vector<const std::vector<uint>* >& dofs); |
94 | + |
95 | }; |
96 | |
97 | } |
98 | |
99 | === modified file 'dolfin/fem/SymmetricAssembler.cpp' |
100 | --- dolfin/fem/SymmetricAssembler.cpp 2012-09-10 14:50:55 +0000 |
101 | +++ dolfin/fem/SymmetricAssembler.cpp 2012-10-04 21:38:25 +0000 |
102 | @@ -1,4 +1,3 @@ |
103 | -// Copyright (C) 2007-2011 Anders Logg |
104 | // Copyright (C) 2012 Joachim B. Haga |
105 | // |
106 | // This file is part of DOLFIN. |
107 | @@ -16,108 +15,86 @@ |
108 | // You should have received a copy of the GNU Lesser General Public License |
109 | // along with DOLFIN. If not, see <http://www.gnu.org/licenses/>. |
110 | // |
111 | -// First added: 2012-02-01 (modified from Assembler.cpp by jobh@simula.no) |
112 | -// Last changed: 2012-03-03 |
113 | - |
114 | -#include <boost/scoped_ptr.hpp> |
115 | - |
116 | -#include <dolfin/log/dolfin_log.h> |
117 | -#include <dolfin/common/Timer.h> |
118 | +// First added: 2012-02-01 |
119 | +// Last changed: 2012-10-04 |
120 | + |
121 | #include <dolfin/common/utils.h> |
122 | -#include <dolfin/parameter/GlobalParameters.h> |
123 | +#include <dolfin/fem/DirichletBC.h> |
124 | #include <dolfin/la/GenericMatrix.h> |
125 | -#include <dolfin/mesh/Mesh.h> |
126 | -#include <dolfin/mesh/Cell.h> |
127 | -#include <dolfin/mesh/Facet.h> |
128 | -#include <dolfin/mesh/MeshData.h> |
129 | -#include <dolfin/mesh/MeshFunction.h> |
130 | #include <dolfin/mesh/SubDomain.h> |
131 | -#include <dolfin/function/GenericFunction.h> |
132 | -#include <dolfin/function/FunctionSpace.h> |
133 | -#include "GenericDofMap.h" |
134 | +#include <dolfin/parameter/GlobalParameters.h> |
135 | #include "Form.h" |
136 | #include "UFC.h" |
137 | -#include "FiniteElement.h" |
138 | -#include "AssemblerBase.h" |
139 | #include "SymmetricAssembler.h" |
140 | |
141 | using namespace dolfin; |
142 | |
143 | -/// The private implementation class. It holds all relevant parameters for a |
144 | -/// single assemble, the implementation, and some scratch variables. Its |
145 | -/// lifetime is never longer than the assemble itself, so it's safe to keep |
146 | -/// references to parameters. |
147 | -class SymmetricAssembler::PImpl |
148 | +class SymmetricAssembler::Private |
149 | { |
150 | -public: |
151 | - |
152 | - // User-provided parameters |
153 | - GenericMatrix& A; |
154 | - GenericMatrix& A_asymm; |
155 | - const Form& a; |
156 | - const std::vector<const DirichletBC*> row_bcs; |
157 | - const std::vector<const DirichletBC*> col_bcs; |
158 | - const MeshFunction<uint>* cell_domains; |
159 | - const MeshFunction<uint>* exterior_facet_domains; |
160 | - const MeshFunction<uint>* interior_facet_domains; |
161 | - bool reset_sparsity, add_values, finalize_tensor, keep_diagonal; |
162 | - |
163 | - PImpl(GenericMatrix& _A, GenericMatrix& _A_asymm, |
164 | - const Form& _a, |
165 | - const std::vector<const DirichletBC*> _row_bcs, |
166 | - const std::vector<const DirichletBC*> _col_bcs, |
167 | - const MeshFunction<uint>* _cell_domains, |
168 | - const MeshFunction<uint>* _exterior_facet_domains, |
169 | - const MeshFunction<uint>* _interior_facet_domains, |
170 | - bool _reset_sparsity, bool _add_values, bool _finalize_tensor, |
171 | - bool _keep_diagonal) |
172 | - : A(_A), A_asymm(_A_asymm), a(_a), |
173 | - row_bcs(_row_bcs), col_bcs(_col_bcs), |
174 | - cell_domains(_cell_domains), |
175 | - exterior_facet_domains(_exterior_facet_domains), |
176 | - interior_facet_domains(_interior_facet_domains), |
177 | - reset_sparsity(_reset_sparsity), |
178 | - add_values(_add_values), |
179 | - finalize_tensor(_finalize_tensor), |
180 | - keep_diagonal(_keep_diagonal), |
181 | - mesh(_a.mesh()), ufc(_a), ufc_asymm(_a) |
182 | - { |
183 | - } |
184 | - |
185 | - void assemble(); |
186 | - |
187 | -private: |
188 | - |
189 | - void assemble_cells(); |
190 | - void assemble_exterior_facets(); |
191 | - void assemble_interior_facets(); |
192 | - |
193 | - // Adjust the columns of the local element tensor so that it becomes |
194 | - // symmetric once BCs have been applied to the rows. Returns true if any |
195 | - // columns have been moved to _asymm. |
196 | - bool make_bc_symmetric(std::vector<double>& elm_A, std::vector<double>& elm_A_asymm, |
197 | - const std::vector<const std::vector<uint>*> dofs); |
198 | - |
199 | - // These are derived from the variables above: |
200 | - const Mesh& mesh; // = Mesh(a) |
201 | - UFC ufc; // = UFC(a) |
202 | - UFC ufc_asymm; // = UFC(a), used for scratch local tensors |
203 | + public: |
204 | + |
205 | + // Parameters that need to be accessible from add_to_global_tensor. These |
206 | + // are stored (by the assemble() method) as instance variables, since the |
207 | + // add_to_global_tensor interface is fixed. |
208 | + |
209 | + Private(GenericMatrix& _B) : B(_B) {} |
210 | + |
211 | + GenericMatrix &B; // The non-symmetric-part coefficient matrix |
212 | + |
213 | bool matching_bcs; // true if row_bcs==col_bcs |
214 | DirichletBC::Map row_bc_values; // derived from row_bcs |
215 | DirichletBC::Map col_bc_values; // derived from col_bcs, but empty if matching_bcs |
216 | |
217 | - // These are used to keep track of which diagonals have been set: |
218 | + // These are used to keep track of which diagonals (BCs) have been set: |
219 | std::pair<uint,uint> processor_dof_range; |
220 | std::set<uint> inserted_diagonals; |
221 | |
222 | // Scratch variables |
223 | + std::vector<double> local_B; |
224 | std::vector<bool> local_row_is_bc; |
225 | |
226 | }; |
227 | - |
228 | -//----------------------------------------------------------------------------- |
229 | -void SymmetricAssembler::assemble(GenericMatrix& A, |
230 | - GenericMatrix& A_asymm, |
231 | +//----------------------------------------------------------------------------- |
232 | +void SymmetricAssembler::assemble(GenericMatrix& A, |
233 | + GenericMatrix& B, |
234 | + const Form& a, |
235 | + const std::vector<const DirichletBC*> row_bcs, |
236 | + const std::vector<const DirichletBC*> col_bcs, |
237 | + const SubDomain &sub_domain) |
238 | +{ |
239 | + // |
240 | + // Convert the SubDomain to meshfunctions, suitable for the real assemble |
241 | + // method. Copied from Assembler, except for the final assemble(...) call. |
242 | + // |
243 | + |
244 | + dolfin_assert(a.ufc_form()); |
245 | + |
246 | + // Extract mesh |
247 | + const Mesh& mesh = a.mesh(); |
248 | + |
249 | + // Extract cell domains |
250 | + boost::scoped_ptr<MeshFunction<uint> > cell_domains; |
251 | + if (a.ufc_form()->num_cell_domains() > 0) |
252 | + { |
253 | + cell_domains.reset(new MeshFunction<uint>(mesh, mesh.topology().dim(), 1)); |
254 | + sub_domain.mark(*cell_domains, 0); |
255 | + } |
256 | + |
257 | + // Extract facet domains |
258 | + boost::scoped_ptr<MeshFunction<uint> > facet_domains; |
259 | + if (a.ufc_form()->num_exterior_facet_domains() > 0 || |
260 | + a.ufc_form()->num_interior_facet_domains() > 0) |
261 | + { |
262 | + facet_domains.reset(new MeshFunction<uint>(mesh, mesh.topology().dim() - 1, 1)); |
263 | + sub_domain.mark(*facet_domains, 0); |
264 | + } |
265 | + |
266 | + // Assemble |
267 | + assemble(A, B, a, row_bcs, col_bcs, cell_domains.get(), facet_domains.get(), facet_domains.get()); |
268 | +} |
269 | +//----------------------------------------------------------------------------- |
270 | +void SymmetricAssembler::assemble(GenericMatrix& A, |
271 | + GenericMatrix& B, |
272 | const Form& a, |
273 | const std::vector<const DirichletBC*> row_bcs, |
274 | const std::vector<const DirichletBC*> col_bcs, |
275 | @@ -125,433 +102,113 @@ |
276 | const MeshFunction<uint>* exterior_facet_domains, |
277 | const MeshFunction<uint>* interior_facet_domains) |
278 | { |
279 | - PImpl pImpl(A, A_asymm, a, row_bcs, col_bcs, |
280 | - cell_domains, exterior_facet_domains, interior_facet_domains, |
281 | - reset_sparsity, add_values, finalize_tensor, keep_diagonal); |
282 | - pImpl.assemble(); |
283 | -} |
284 | -//----------------------------------------------------------------------------- |
285 | -void SymmetricAssembler::PImpl::assemble() |
286 | -{ |
287 | - // All assembler functions above end up calling this function, which |
288 | - // in turn calls the assembler functions below to assemble over |
289 | - // cells, exterior and interior facets. |
290 | - // |
291 | - // Important notes: |
292 | - // |
293 | - // 1. Note the importance of treating empty mesh functions as null |
294 | - // pointers for the PyDOLFIN interface. |
295 | - // |
296 | - // 2. Note that subdomains given as input to this function override |
297 | - // subdomains attached to forms, which in turn override subdomains |
298 | - // stored as part of the mesh. |
299 | - |
300 | - // If the bcs match (which is the usual case), we are assembling a normal |
301 | - // square matrix which contains the diagonal (and the dofmaps should match, |
302 | - // too). |
303 | - matching_bcs = (row_bcs == col_bcs); |
304 | + dolfin_assert(a.rank() == 2); |
305 | + |
306 | + #ifdef HAS_OPENMP |
307 | + const uint num_threads = parameters["num_threads"]; |
308 | + if (num_threads > 0) |
309 | + { |
310 | + dolfin_error("SymmetricAssembler.cpp", "assemble", |
311 | + "OpenMP symmetric assemble not supported"); |
312 | + } |
313 | + #endif |
314 | + |
315 | + // Store parameters that the standard assembler don't use |
316 | + impl = new Private(B); |
317 | + |
318 | + impl->matching_bcs = (row_bcs == col_bcs); |
319 | |
320 | // Get Dirichlet dofs rows and values for local mesh |
321 | for (uint i = 0; i < row_bcs.size(); ++i) |
322 | { |
323 | - row_bcs[i]->get_boundary_values(row_bc_values); |
324 | + row_bcs[i]->get_boundary_values(impl->row_bc_values); |
325 | if (MPI::num_processes() > 1 && row_bcs[i]->method() != "pointwise") |
326 | - row_bcs[i]->gather(row_bc_values); |
327 | + row_bcs[i]->gather(impl->row_bc_values); |
328 | } |
329 | - if (!matching_bcs) |
330 | + if (!impl->matching_bcs) |
331 | { |
332 | // Get Dirichlet dofs columns and values for local mesh |
333 | for (uint i = 0; i < col_bcs.size(); ++i) |
334 | { |
335 | - col_bcs[i]->get_boundary_values(col_bc_values); |
336 | + col_bcs[i]->get_boundary_values(impl->col_bc_values); |
337 | if (MPI::num_processes() > 1 && col_bcs[i]->method() != "pointwise") |
338 | - col_bcs[i]->gather(col_bc_values); |
339 | + col_bcs[i]->gather(impl->col_bc_values); |
340 | } |
341 | } |
342 | |
343 | - dolfin_assert(a.rank() == 2); |
344 | - |
345 | - // Get cell domains |
346 | - if (!cell_domains || cell_domains->size() == 0) |
347 | - { |
348 | - cell_domains = a.cell_domains_shared_ptr().get(); |
349 | - if (!cell_domains) |
350 | - cell_domains = a.mesh().domains().cell_domains(a.mesh()).get(); |
351 | - } |
352 | - |
353 | - // Get exterior facet domains |
354 | - if (!exterior_facet_domains || exterior_facet_domains->size() == 0) |
355 | - { |
356 | - exterior_facet_domains = a.exterior_facet_domains_shared_ptr().get(); |
357 | - if (!exterior_facet_domains) |
358 | - exterior_facet_domains = a.mesh().domains().facet_domains(a.mesh()).get(); |
359 | - } |
360 | - |
361 | - // Get interior facet domains |
362 | - if (!interior_facet_domains || interior_facet_domains->size() == 0) |
363 | - { |
364 | - interior_facet_domains = a.interior_facet_domains_shared_ptr().get(); |
365 | - if (!interior_facet_domains) |
366 | - interior_facet_domains = a.mesh().domains().facet_domains(a.mesh()).get(); |
367 | - } |
368 | - |
369 | - // Check form |
370 | - AssemblerBase::check(a); |
371 | - |
372 | - // Update off-process coefficients |
373 | - const std::vector<boost::shared_ptr<const GenericFunction> > |
374 | - coefficients = a.coefficients(); |
375 | - for (uint i = 0; i < coefficients.size(); ++i) |
376 | - coefficients[i]->update(); |
377 | - |
378 | - // Initialize global tensors |
379 | - error("SymmetricAssembler is presently broken. It will be removed soon."); |
380 | - //const std::vector<std::pair<std::pair<uint, uint>, std::pair<uint, uint> > > periodic_master_slave_dofs; |
381 | - //init_global_tensor(A, a, periodic_master_slave_dofs); |
382 | - //init_global_tensor(A_asymm, a, periodic_master_slave_dofs); |
383 | + // Initialize the global nonsymmetric tensor (the symmetric one is handled by Assembler) |
384 | + const std::vector<std::pair<std::pair<uint, uint>, std::pair<uint, uint> > > periodic_master_slave_dofs; |
385 | + init_global_tensor(impl->B, a, periodic_master_slave_dofs); |
386 | |
387 | // Get dofs that are local to this processor |
388 | - processor_dof_range = A.local_range(0); |
389 | - |
390 | - // Assemble over cells |
391 | - assemble_cells(); |
392 | - |
393 | - // Assemble over exterior facets |
394 | - assemble_exterior_facets(); |
395 | - |
396 | - // Assemble over interior facets |
397 | - assemble_interior_facets(); |
398 | - |
399 | - // Finalize assembly of global tensor |
400 | + impl->processor_dof_range = impl->B.local_range(0); |
401 | + |
402 | + // Call the standard assembler (which in turn calls add_to_global_tensor) |
403 | + Assembler::assemble(A, a, cell_domains, exterior_facet_domains, interior_facet_domains); |
404 | + |
405 | + // Finalize assembly of global nonsymmetric tensor (the symmetric one is |
406 | + // finalized by Assembler) |
407 | if (finalize_tensor) |
408 | { |
409 | - A.apply("add"); |
410 | - A_asymm.apply("add"); |
411 | - } |
412 | -} |
413 | -//----------------------------------------------------------------------------- |
414 | -void SymmetricAssembler::PImpl::assemble_cells() |
415 | -{ |
416 | - // Skip assembly if there are no cell integrals |
417 | - if (ufc.form.num_cell_domains() == 0) |
418 | - return; |
419 | - |
420 | - // Set timer |
421 | - Timer timer("Assemble cells"); |
422 | - |
423 | - // Form rank |
424 | - const uint form_rank = ufc.form.rank(); |
425 | - |
426 | - // Collect pointers to dof maps |
427 | - std::vector<const GenericDofMap*> dofmaps; |
428 | - for (uint i = 0; i < form_rank; ++i) |
429 | - dofmaps.push_back(a.function_space(i)->dofmap().get()); |
430 | - |
431 | - // Vector to hold dof map for a cell |
432 | - std::vector<const std::vector<uint>* > dofs(form_rank); |
433 | - |
434 | - // Cell integral |
435 | - dolfin_assert(ufc.cell_integrals.size() > 0); |
436 | - ufc::cell_integral* integral = ufc.cell_integrals[0].get(); |
437 | - |
438 | - // Assemble over cells |
439 | - Progress p(AssemblerBase::progress_message(A.rank(), "cells"), mesh.num_cells()); |
440 | - for (CellIterator cell(mesh); !cell.end(); ++cell) |
441 | - { |
442 | - // Get integral for sub domain (if any) |
443 | - if (cell_domains && cell_domains->size() > 0) |
444 | - { |
445 | - const uint domain = (*cell_domains)[*cell]; |
446 | - if (domain < ufc.form.num_cell_domains()) |
447 | - integral = ufc.cell_integrals[domain].get(); |
448 | - else |
449 | - continue; |
450 | - } |
451 | - |
452 | - // Skip integral if zero |
453 | - if (!integral) |
454 | - continue; |
455 | - |
456 | - // Update to current cell |
457 | - ufc.update(*cell); |
458 | - |
459 | - // Get local-to-global dof maps for cell |
460 | - for (uint i = 0; i < form_rank; ++i) |
461 | - dofs[i] = &(dofmaps[i]->cell_dofs(cell->index())); |
462 | - |
463 | - // Tabulate cell tensor |
464 | - integral->tabulate_tensor(&ufc.A[0], ufc.w(), ufc.cell); |
465 | - |
466 | - // Apply boundary conditions |
467 | - const bool asymm_changed = make_bc_symmetric(ufc.A, ufc_asymm.A, dofs); |
468 | - |
469 | - // Add entries to global tensor. |
470 | - A.add(&ufc.A[0], dofs); |
471 | - if (asymm_changed) |
472 | - A_asymm.add(&ufc_asymm.A[0], dofs); |
473 | - |
474 | - p++; |
475 | - } |
476 | -} |
477 | -//----------------------------------------------------------------------------- |
478 | -void SymmetricAssembler::PImpl::assemble_exterior_facets() |
479 | -{ |
480 | - // Skip assembly if there are no exterior facet integrals |
481 | - if (ufc.form.num_exterior_facet_domains() == 0) |
482 | - return; |
483 | - Timer timer("Assemble exterior facets"); |
484 | - |
485 | - // Extract mesh |
486 | - const Mesh& mesh = a.mesh(); |
487 | - |
488 | - // Form rank |
489 | - const uint form_rank = ufc.form.rank(); |
490 | - |
491 | - // Collect pointers to dof maps |
492 | - std::vector<const GenericDofMap*> dofmaps; |
493 | - for (uint i = 0; i < form_rank; ++i) |
494 | - dofmaps.push_back(a.function_space(i)->dofmap().get()); |
495 | - |
496 | - // Vector to hold dof map for a cell |
497 | - std::vector<const std::vector<uint>* > dofs(form_rank); |
498 | - |
499 | - // Exterior facet integral |
500 | - dolfin_assert(ufc.exterior_facet_integrals.size() > 0); |
501 | - const ufc::exterior_facet_integral* |
502 | - integral = ufc.exterior_facet_integrals[0].get(); |
503 | - |
504 | - // Compute facets and facet - cell connectivity if not already computed |
505 | - const uint D = mesh.topology().dim(); |
506 | - mesh.init(D - 1); |
507 | - mesh.init(D - 1, D); |
508 | - dolfin_assert(mesh.ordered()); |
509 | - |
510 | - // Assemble over exterior facets (the cells of the boundary) |
511 | - Progress p(AssemblerBase::progress_message(A.rank(), "exterior facets"), |
512 | - mesh.num_facets()); |
513 | - for (FacetIterator facet(mesh); !facet.end(); ++facet) |
514 | - { |
515 | - // Only consider exterior facets |
516 | - if (!facet->exterior()) |
517 | - { |
518 | - p++; |
519 | - continue; |
520 | - } |
521 | - |
522 | - // Get integral for sub domain (if any) |
523 | - if (exterior_facet_domains && exterior_facet_domains->size() > 0) |
524 | - { |
525 | - const uint domain = (*exterior_facet_domains)[*facet]; |
526 | - if (domain < ufc.form.num_exterior_facet_domains()) |
527 | - integral = ufc.exterior_facet_integrals[domain].get(); |
528 | - else |
529 | - continue; |
530 | - } |
531 | - |
532 | - // Skip integral if zero |
533 | - if (!integral) |
534 | - continue; |
535 | - |
536 | - // Get mesh cell to which mesh facet belongs (pick first, there is only one) |
537 | - dolfin_assert(facet->num_entities(D) == 1); |
538 | - Cell mesh_cell(mesh, facet->entities(D)[0]); |
539 | - |
540 | - // Get local index of facet with respect to the cell |
541 | - const uint local_facet = mesh_cell.index(*facet); |
542 | - |
543 | - // Update to current cell |
544 | - ufc.update(mesh_cell, local_facet); |
545 | - |
546 | - // Get local-to-global dof maps for cell |
547 | - for (uint i = 0; i < form_rank; ++i) |
548 | - dofs[i] = &(dofmaps[i]->cell_dofs(mesh_cell.index())); |
549 | - |
550 | - // Tabulate exterior facet tensor |
551 | - integral->tabulate_tensor(&ufc.A[0], ufc.w(), ufc.cell, local_facet); |
552 | - |
553 | - // Apply boundary conditions |
554 | - const bool asymm_changed = make_bc_symmetric(ufc.A, ufc_asymm.A, dofs); |
555 | - |
556 | - // Add entries to global tensor |
557 | - A.add(&ufc.A[0], dofs); |
558 | - if (asymm_changed) |
559 | - A_asymm.add(&ufc_asymm.A[0], dofs); |
560 | - |
561 | - p++; |
562 | - } |
563 | -} |
564 | -//----------------------------------------------------------------------------- |
565 | -void SymmetricAssembler::PImpl::assemble_interior_facets() |
566 | -{ |
567 | - // Skip assembly if there are no interior facet integrals |
568 | - if (ufc.form.num_interior_facet_domains() == 0) |
569 | - return; |
570 | - |
571 | - not_working_in_parallel("Assembly over interior facets"); |
572 | - |
573 | - Timer timer("Assemble interior facets"); |
574 | - |
575 | - // Extract mesh and coefficients |
576 | - const Mesh& mesh = a.mesh(); |
577 | - |
578 | - // Form rank |
579 | - const uint form_rank = ufc.form.rank(); |
580 | - |
581 | - // Collect pointers to dof maps |
582 | - std::vector<const GenericDofMap*> dofmaps; |
583 | - for (uint i = 0; i < form_rank; ++i) |
584 | - dofmaps.push_back(a.function_space(i)->dofmap().get()); |
585 | - |
586 | - // Vector to hold dofs for cells |
587 | - std::vector<std::vector<uint> > macro_dofs(form_rank); |
588 | - std::vector<const std::vector<uint>*> macro_dof_ptrs(form_rank); |
589 | - for (uint i = 0; i < form_rank; i++) |
590 | - macro_dof_ptrs[i] = ¯o_dofs[i]; |
591 | - |
592 | - // Interior facet integral |
593 | - dolfin_assert(ufc.interior_facet_integrals.size() > 0); |
594 | - const ufc::interior_facet_integral* |
595 | - integral = ufc.interior_facet_integrals[0].get(); |
596 | - |
597 | - // Compute facets and facet - cell connectivity if not already computed |
598 | - const uint D = mesh.topology().dim(); |
599 | - mesh.init(D - 1); |
600 | - mesh.init(D - 1, D); |
601 | - dolfin_assert(mesh.ordered()); |
602 | - |
603 | - // Get interior facet directions (if any) |
604 | - boost::shared_ptr<MeshFunction<unsigned int> > |
605 | - facet_orientation = mesh.data().mesh_function("facet_orientation"); |
606 | - if (facet_orientation && facet_orientation->dim() != D - 1) |
607 | - { |
608 | - dolfin_error("Assembler.cpp", |
609 | - "assemble form over interior facets", |
610 | - "Expecting facet orientation to be defined on facets (not dimension %d)", |
611 | - facet_orientation->dim()); |
612 | - } |
613 | - |
614 | - // Assemble over interior facets (the facets of the mesh) |
615 | - Progress p(AssemblerBase::progress_message(A.rank(), "interior facets"), |
616 | - mesh.num_facets()); |
617 | - for (FacetIterator facet(mesh); !facet.end(); ++facet) |
618 | - { |
619 | - // Only consider interior facets |
620 | - if (facet->exterior()) |
621 | - { |
622 | - p++; |
623 | - continue; |
624 | - } |
625 | - |
626 | - // Get integral for sub domain (if any) |
627 | - if (interior_facet_domains && interior_facet_domains->size() > 0) |
628 | - { |
629 | - const uint domain = (*interior_facet_domains)[*facet]; |
630 | - if (domain < ufc.form.num_interior_facet_domains()) |
631 | - integral = ufc.interior_facet_integrals[domain].get(); |
632 | - else |
633 | - continue; |
634 | - } |
635 | - |
636 | - // Skip integral if zero |
637 | - if (!integral) |
638 | - continue; |
639 | - |
640 | - // Get cells incident with facet |
641 | - std::pair<const Cell, const Cell> |
642 | - cells = facet->adjacent_cells(facet_orientation.get()); |
643 | - const Cell& cell0 = cells.first; |
644 | - const Cell& cell1 = cells.second; |
645 | - |
646 | - // Get local index of facet with respect to each cell |
647 | - uint local_facet0 = cell0.index(*facet); |
648 | - uint local_facet1 = cell1.index(*facet); |
649 | - |
650 | - // Update to current pair of cells |
651 | - ufc.update(cell0, local_facet0, cell1, local_facet1); |
652 | - |
653 | - // Tabulate dofs for each dimension on macro element |
654 | - for (uint i = 0; i < form_rank; ++i) |
655 | - { |
656 | - // Get dofs for each cell |
657 | - const std::vector<uint>& cell_dofs0 = dofmaps[i]->cell_dofs(cell0.index()); |
658 | - const std::vector<uint>& cell_dofs1 = dofmaps[i]->cell_dofs(cell1.index()); |
659 | - |
660 | - // Create space in macro dof vector |
661 | - macro_dofs[i].resize(cell_dofs0.size() + cell_dofs1.size()); |
662 | - |
663 | - // Copy cell dofs into macro dof vector |
664 | - std::copy(cell_dofs0.begin(), cell_dofs0.end(), |
665 | - macro_dofs[i].begin()); |
666 | - std::copy(cell_dofs1.begin(), cell_dofs1.end(), |
667 | - macro_dofs[i].begin() + cell_dofs0.size()); |
668 | - } |
669 | - |
670 | - // Tabulate exterior interior facet tensor on macro element |
671 | - integral->tabulate_tensor(&ufc.macro_A[0], ufc.macro_w(), |
672 | - ufc.cell0, ufc.cell1, |
673 | - local_facet0, local_facet1); |
674 | - |
675 | - // Apply boundary conditions |
676 | - const bool asymm_changed = make_bc_symmetric(ufc.macro_A, |
677 | - ufc_asymm.macro_A, macro_dof_ptrs); |
678 | - |
679 | - // Add entries to global tensor |
680 | - A.add(&ufc.macro_A[0], macro_dofs); |
681 | - if (asymm_changed) |
682 | - A_asymm.add(&ufc_asymm.macro_A[0], macro_dofs); |
683 | - |
684 | - p++; |
685 | - } |
686 | -} |
687 | -//----------------------------------------------------------------------------- |
688 | -bool SymmetricAssembler::PImpl::make_bc_symmetric(std::vector<double>& local_A, |
689 | - std::vector<double>& local_A_asymm, |
690 | - const std::vector<const std::vector<uint>*> dofs) |
691 | -{ |
692 | + impl->B.apply("add"); |
693 | + } |
694 | + |
695 | + // Delete the private instance holding additional parameters for |
696 | + // add_to_global_tensor |
697 | + delete impl; |
698 | +} |
699 | +//----------------------------------------------------------------------------- |
700 | +void SymmetricAssembler::add_to_global_tensor(GenericTensor &A, |
701 | + std::vector<double>& local_A, |
702 | + std::vector<const std::vector<uint>* >& dofs) |
703 | +{ |
704 | + // Apply boundary conditions, and move affected columns of the local element |
705 | + // tensor, to restore symmetry. |
706 | + |
707 | // Get local dimensions |
708 | const uint num_local_rows = dofs[0]->size(); |
709 | const uint num_local_cols = dofs[1]->size(); |
710 | |
711 | - // Return value, true if columns have been moved to _asymm |
712 | - bool columns_moved = false; |
713 | + // Return value, true if columns have been moved from local_A to local_B |
714 | + bool local_B_is_set = false; |
715 | |
716 | // Convenience aliases |
717 | const std::vector<uint>& row_dofs = *dofs[0]; |
718 | const std::vector<uint>& col_dofs = *dofs[1]; |
719 | |
720 | - if (matching_bcs && row_dofs!=col_dofs) |
721 | + if (impl->matching_bcs && row_dofs != col_dofs) |
722 | dolfin_error("SymmetricAssembler.cpp", |
723 | "make_bc_symmetric", |
724 | "Same BCs are used for rows and columns, but dofmaps don't match"); |
725 | |
726 | // Store the local boundary conditions, to avoid multiple searches in the |
727 | // (common) case of matching_bcs |
728 | - local_row_is_bc.resize(num_local_rows); |
729 | + impl->local_row_is_bc.resize(num_local_rows); |
730 | for (uint row = 0; row < num_local_rows; ++row) |
731 | { |
732 | - DirichletBC::Map::const_iterator bc_item = row_bc_values.find(row_dofs[row]); |
733 | - local_row_is_bc[row] = (bc_item != row_bc_values.end()); |
734 | + DirichletBC::Map::const_iterator bc_item = impl->row_bc_values.find(row_dofs[row]); |
735 | + impl->local_row_is_bc[row] = (bc_item != impl->row_bc_values.end()); |
736 | } |
737 | |
738 | // Clear matrix rows belonging to BCs. These modifications destroy symmetry. |
739 | for (uint row = 0; row < num_local_rows; ++row) |
740 | { |
741 | // Do nothing if row is not affected by BCs |
742 | - if (!local_row_is_bc[row]) |
743 | + if (!impl->local_row_is_bc[row]) |
744 | continue; |
745 | |
746 | - // Zero out the row |
747 | + // Zero the row in local_A |
748 | zerofill(&local_A[row*num_local_cols], num_local_cols); |
749 | |
750 | - // Set the diagonal if we're in a diagonal block |
751 | - if (matching_bcs) |
752 | + // Set the diagonal, if we're in a diagonal block... |
753 | + if (impl->matching_bcs) |
754 | { |
755 | // ...but only set it on the owning processor |
756 | const uint dof = row_dofs[row]; |
757 | - if (dof >= processor_dof_range.first && dof < processor_dof_range.second) |
758 | + if (dof >= impl->processor_dof_range.first && dof < impl->processor_dof_range.second) |
759 | { |
760 | // ...and only once. |
761 | - const bool already_inserted = !inserted_diagonals.insert(dof).second; |
762 | + const bool already_inserted = !impl->inserted_diagonals.insert(dof).second; |
763 | if (!already_inserted) |
764 | local_A[row + row*num_local_cols] = 1.0; |
765 | } |
766 | @@ -564,36 +221,43 @@ |
767 | for (uint col = 0; col < num_local_cols; ++col) |
768 | { |
769 | // Do nothing if column is not affected by BCs |
770 | - if (matching_bcs) { |
771 | - if (!local_row_is_bc[col]) |
772 | + if (impl->matching_bcs) { |
773 | + if (!impl->local_row_is_bc[col]) |
774 | continue; |
775 | } |
776 | else |
777 | { |
778 | - DirichletBC::Map::const_iterator bc_item = col_bc_values.find(col_dofs[col]); |
779 | - if (bc_item == col_bc_values.end()) |
780 | + DirichletBC::Map::const_iterator bc_item = impl->col_bc_values.find(col_dofs[col]); |
781 | + if (bc_item == impl->col_bc_values.end()) |
782 | continue; |
783 | } |
784 | |
785 | - // Zero the asymmetric part before use |
786 | - if (!columns_moved) |
787 | + // Resize and zero the local asymmetric part before use. The resize is a |
788 | + // no-op most of the time. |
789 | + if (!local_B_is_set) |
790 | { |
791 | - zerofill(local_A_asymm); |
792 | - columns_moved = true; |
793 | + impl->local_B.resize(local_A.size()); |
794 | + zerofill(impl->local_B); |
795 | + local_B_is_set = true; |
796 | } |
797 | |
798 | - // Move the column to A_asymm, zero it in A |
799 | + // Move the column to B, zero it in A |
800 | for (uint row = 0; row < num_local_rows; ++row) |
801 | { |
802 | - if (!local_row_is_bc[row]) |
803 | + if (!impl->local_row_is_bc[row]) |
804 | { |
805 | const uint entry = col + row*num_local_cols; |
806 | - local_A_asymm[entry] = local_A[entry]; |
807 | + impl->local_B[entry] = local_A[entry]; |
808 | local_A[entry] = 0.0; |
809 | } |
810 | } |
811 | } |
812 | |
813 | - return columns_moved; |
814 | + // Add entries to global tensor. |
815 | + A.add(&local_A[0], dofs); |
816 | + if (local_B_is_set) |
817 | + { |
818 | + impl->B.add(&impl->local_B[0], dofs); |
819 | + } |
820 | } |
821 | //----------------------------------------------------------------------------- |
822 | |
823 | === modified file 'dolfin/fem/SymmetricAssembler.h' |
824 | --- dolfin/fem/SymmetricAssembler.h 2012-09-08 09:49:23 +0000 |
825 | +++ dolfin/fem/SymmetricAssembler.h 2012-10-04 21:38:25 +0000 |
826 | @@ -15,23 +15,21 @@ |
827 | // You should have received a copy of the GNU Lesser General Public License |
828 | // along with DOLFIN. If not, see <http://www.gnu.org/licenses/>. |
829 | // |
830 | -// First added: 2012-01-26 (jobh@simula.no) |
831 | -// Last changed: 2012-03-03 |
832 | +// First added: 2012-01-26 |
833 | +// Last changed: 2012-10-04 |
834 | |
835 | #ifndef __SYMMETRIC_ASSEMBLER_H |
836 | #define __SYMMETRIC_ASSEMBLER_H |
837 | |
838 | -#include <map> |
839 | -#include <vector> |
840 | -#include <boost/scoped_ptr.hpp> |
841 | -#include <dolfin/common/types.h> |
842 | -#include "Form.h" |
843 | -#include "DirichletBC.h" |
844 | -#include "AssemblerBase.h" |
845 | +#include "Assembler.h" |
846 | |
847 | namespace dolfin |
848 | { |
849 | |
850 | + // Forward declarations |
851 | + class GenericMatrix; |
852 | + class DirichletBC; |
853 | + |
854 | /// This class provides implements an assembler for systems |
855 | /// of the form Ax = b. Its assembly algorithms are similar to SystemAssember's, |
856 | /// but it saves the matrix modifications into a separate tensor so that it |
857 | @@ -55,31 +53,57 @@ |
858 | /// A_n.mult(b, b_mod); |
859 | /// b -= b_mod; |
860 | |
861 | - class SymmetricAssembler : public AssemblerBase |
862 | + class SymmetricAssembler : public Assembler |
863 | { |
864 | public: |
865 | |
866 | /// Constructor |
867 | SymmetricAssembler() {} |
868 | |
869 | - /// Assemble A and apply Dirichlet boundary conditions. Returns two |
870 | - /// matrices, where the second contains the symmetric modifications |
871 | - /// suitable for modifying RHS vectors. |
872 | - /// |
873 | - /// Note: row_bcs and col_bcs will normally be the same, but are different |
874 | - /// for e.g. off-diagonal block matrices in a mixed PDE. |
875 | - void assemble(GenericMatrix &A, |
876 | - GenericMatrix &A_nonsymm, |
877 | - const Form &a, |
878 | - const std::vector<const DirichletBC*> row_bcs, |
879 | - const std::vector<const DirichletBC*> col_bcs, |
880 | - const MeshFunction<uint> *cell_domains=NULL, |
881 | - const MeshFunction<uint> *exterior_facet_domains=NULL, |
882 | - const MeshFunction<uint> *interior_facet_domains=NULL); |
883 | + /// Assemble a and apply Dirichlet boundary conditions. Returns two |
884 | + /// matrices, where the second contains the symmetric modifications |
885 | + /// suitable for modifying RHS vectors. |
886 | + /// |
887 | + /// Note: row_bcs and col_bcs will normally be the same, but are different |
888 | + /// for e.g. off-diagonal block matrices in a mixed PDE. |
889 | + void assemble(GenericMatrix& A, |
890 | + GenericMatrix& B, |
891 | + const Form& a, |
892 | + const std::vector<const DirichletBC*> row_bcs, |
893 | + const std::vector<const DirichletBC*> col_bcs, |
894 | + const MeshFunction<uint>* cell_domains=NULL, |
895 | + const MeshFunction<uint>* exterior_facet_domains=NULL, |
896 | + const MeshFunction<uint>* interior_facet_domains=NULL); |
897 | + |
898 | + /// Assemble a and apply Dirichlet boundary conditions. Returns two |
899 | + /// matrices, where the second contains the symmetric modifications |
900 | + /// suitable for modifying RHS vectors. |
901 | + /// |
902 | + /// Note: row_bcs and col_bcs will normally be the same, but are different |
903 | + /// for e.g. off-diagonal block matrices in a mixed PDE. |
904 | + void assemble(GenericMatrix& A, |
905 | + GenericMatrix& B, |
906 | + const Form& a, |
907 | + const std::vector<const DirichletBC*> row_bcs, |
908 | + const std::vector<const DirichletBC*> col_bcs, |
909 | + const SubDomain& sub_domain); |
910 | + |
911 | + protected: |
912 | + |
913 | + /// Add cell tensor to global tensor. Hook to allow the SymmetricAssembler |
914 | + /// to split the cell tensor into symmetric/antisymmetric parts. |
915 | + virtual void add_to_global_tensor(GenericTensor& A, |
916 | + std::vector<double>& local_A, |
917 | + std::vector<const std::vector<uint>* >& dofs); |
918 | |
919 | private: |
920 | |
921 | - class PImpl; |
922 | + // Parameters and methods used in add_to_global_tensor. The parameters |
923 | + // are stored (by the assemble() method) in this instance, since the |
924 | + // add_to_global_tensor interface is fixed. |
925 | + |
926 | + class Private; |
927 | + Private *impl; |
928 | |
929 | }; |
930 |