Merge lp:~jobh/dolfin/symmetric-assembler-update into lp:~fenics-core/dolfin/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
Reviewer Review Type Date Requested Status
Garth Wells Pending
Review via email: mp+128119@code.launchpad.net

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] = &macro_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] = &macro_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

Subscribers

People subscribed via source and target branches