Merge lp:~jobh/dolfin/misc into lp:~fenics-core/dolfin/trunk

Proposed by Joachim Haga
Status: Merged
Merged at revision: 6626
Proposed branch: lp:~jobh/dolfin/misc
Merge into: lp:~fenics-core/dolfin/trunk
Diff against target: 309 lines (+165/-21)
4 files modified
demo/undocumented/sym-dirichlet-bc/python/speed-up-test.py (+8/-3)
dolfin/common/RangedIndexSet.h (+108/-0)
dolfin/fem/DirichletBC.cpp (+30/-16)
dolfin/mesh/SubDomain.cpp (+19/-2)
To merge this branch: bzr merge lp:~jobh/dolfin/misc
Reviewer Review Type Date Requested Status
Garth Wells Approve
Review via email: mp+96079@code.launchpad.net

Description of the change

This branch finishes the DirichletBC speed-up effort for topological and geometric search (and, incidentally, SubDomain::apply_markers, since that is used when initialising a DirichletBC from a sub domain). These search methods (from a sub domain) are now roughly as fast as pointwise search again.

It also fixes the problem with setting BCs on a subdomain which has been reported (it checks if dofmap is a view before calling ownership_range()).

I seem to have created some problems earlier which may be due to criss-cross merges, so I haven't merged with trunk this time; however, I have performed a test-merge which was clean so there should be no problems.

It passes all tests, and the test-merge has passed the quicktests.

To post a comment you must log in.
Revision history for this message
Garth Wells (garth-wells) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'demo/undocumented/sym-dirichlet-bc/python/speed-up-test.py'
2--- demo/undocumented/sym-dirichlet-bc/python/speed-up-test.py 2011-10-10 00:06:19 +0000
3+++ demo/undocumented/sym-dirichlet-bc/python/speed-up-test.py 2012-03-06 09:08:20 +0000
4@@ -22,8 +22,8 @@
5 # First added: 2008-08-13
6 # Last changed: 2008-08-13
7
8+from dolfin import *
9 import time
10-from dolfin import *
11
12
13 # Sub domain for Dirichlet boundary condition
14@@ -31,14 +31,14 @@
15 def inside(self, x, on_boundary):
16 return bool(on_boundary and x[0] < DOLFIN_EPS)
17
18-mesh = UnitSquare(500,500)
19+mesh = UnitCube(32,32,32)
20 V = FunctionSpace(mesh, "CG", 1)
21
22 # Define variational problem
23 v = TestFunction(V)
24 u = TrialFunction(V)
25
26-f = Function(V, "500.0*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)")
27+f = Expression("500.0*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)")
28
29
30 a = dot(grad(v), grad(u))*dx
31@@ -71,5 +71,10 @@
32 t1 = time.time()
33 print "time for new assembly ", t1-t0, " using ", backend
34
35+ t0 = time.time()
36+ A, Aa = symmetric_assemble(a, bcs=bc)
37+ b = assemble(L, bcs=bc)
38+ t1 = time.time()
39+ print "time for symm assembly ", t1-t0, " using ", backend
40
41 #summary()
42
43=== added file 'dolfin/common/RangedIndexSet.h'
44--- dolfin/common/RangedIndexSet.h 1970-01-01 00:00:00 +0000
45+++ dolfin/common/RangedIndexSet.h 2012-03-06 09:08:20 +0000
46@@ -0,0 +1,108 @@
47+// Copyright (C) 2012 Joachim B Haga
48+//
49+// This file is part of DOLFIN.
50+//
51+// DOLFIN is free software: you can redistribute it and/or modify
52+// it under the terms of the GNU Lesser General Public License as published by
53+// the Free Software Foundation, either version 3 of the License, or
54+// (at your option) any later version.
55+//
56+// DOLFIN is distributed in the hope that it will be useful,
57+// but WITHOUT ANY WARRANTY; without even the implied warranty of
58+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59+// GNU Lesser General Public License for more details.
60+//
61+// You should have received a copy of the GNU Lesser General Public License
62+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
63+//
64+// First added: 2012-03-02
65+// Last changed: 2012-03-02
66+
67+#ifndef __RANGED_INDEX_SET_H
68+#define __RANGED_INDEX_SET_H
69+
70+#include <vector>
71+#include <dolfin/log/log.h>
72+#include "types.h"
73+
74+namespace dolfin
75+{
76+
77+ /// This class provides an special-purpose data structure for testing if a given
78+ /// index within a range is set. It is very fast.
79+ ///
80+ /// The memory requirements are one bit per item in range, since it uses a
81+ /// (packed) std::vector<bool> for storage.
82+
83+ class RangedIndexSet
84+ {
85+
86+ public:
87+
88+ /// Create a ranged set with range given as a (lower, upper) pair.
89+ RangedIndexSet(std::pair<uint, uint> range)
90+ : _range(range), _is_set(range.second - range.first)
91+ {
92+ clear();
93+ }
94+
95+ /// Create a ranged set with 0 as lower range
96+ RangedIndexSet(uint upper_range)
97+ : _range(std::pair<uint, uint>(0, upper_range)), _is_set(upper_range)
98+ {
99+ clear();
100+ }
101+
102+ /// Return true if a given index is within range, i.e., if it can be stored in the set.
103+ bool in_range(uint i) const
104+ {
105+ return (i >= _range.first && i < _range.second);
106+ }
107+
108+ /// Check is the set contains the given index.
109+ bool has_index(uint i) const
110+ {
111+ dolfin_assert(in_range(i));
112+ return _is_set[i - _range.first];
113+ }
114+
115+ /// Insert a given index into the set. Returns true if the index was
116+ /// inserted (i.e., the index was not already in the set).
117+ bool insert(uint i)
118+ {
119+ dolfin_assert(in_range(i));
120+ std::vector<bool>::reference entry = _is_set[i - _range.first];
121+ if (entry)
122+ {
123+ return false;
124+ }
125+ else
126+ {
127+ entry = true;
128+ return true;
129+ }
130+ }
131+
132+ /// Erase an index from the set.
133+ void erase(uint i)
134+ {
135+ dolfin_assert(in_range(i));
136+ _is_set[i - _range.first] = false;
137+ }
138+
139+ /// Erase all indices from the set.
140+ void clear()
141+ {
142+ std::fill(_is_set.begin(), _is_set.end(), false);
143+ }
144+
145+ private:
146+
147+ const std::pair<uint, uint> _range;
148+ std::vector<bool> _is_set;
149+
150+ };
151+
152+}
153+
154+#endif
155
156=== modified file 'dolfin/fem/DirichletBC.cpp'
157--- dolfin/fem/DirichletBC.cpp 2012-03-02 15:40:22 +0000
158+++ dolfin/fem/DirichletBC.cpp 2012-03-06 09:08:20 +0000
159@@ -32,6 +32,7 @@
160 #include <dolfin/common/constants.h>
161 #include <dolfin/common/Array.h>
162 #include <dolfin/common/NoDeleter.h>
163+#include <dolfin/common/RangedIndexSet.h>
164 #include <dolfin/function/GenericFunction.h>
165 #include <dolfin/function/FunctionSpace.h>
166 #include <dolfin/function/Constant.h>
167@@ -818,6 +819,11 @@
168 log(TRACE, "Computing facets, needed for geometric application of boundary conditions.");
169 mesh.init(mesh.topology().dim() - 1);
170
171+ // Speed up the computations by only visiting (most) dofs once
172+ RangedIndexSet already_visited(dofmap.is_view()
173+ ? std::pair<uint, uint>(0,0)
174+ : dofmap.ownership_range());
175+
176 // Iterate over facets
177 Progress p("Computing Dirichlet boundary values, geometric search", facets.size());
178 for (uint f = 0; f < facets.size(); ++f)
179@@ -841,17 +847,28 @@
180 {
181 ufc_cell.update(*c, facet_number);
182
183+ bool tabulated = false;
184 bool interpolated = false;
185
186- // Tabulate coordinates of dofs on cell
187- dofmap.tabulate_coordinates(data.coordinates, ufc_cell);
188-
189 // Tabulate dofs on cell
190 const std::vector<uint>& cell_dofs = dofmap.cell_dofs(c->index());
191
192 // Loop over all dofs on cell
193- for (uint i = 0; i < dofmap.cell_dimension(c->index()); ++i)
194+ for (uint i = 0; i < cell_dofs.size(); ++i)
195 {
196+ const uint global_dof = cell_dofs[i];
197+
198+ // Skip already checked dofs
199+ if (already_visited.in_range(global_dof) && !already_visited.insert(global_dof))
200+ continue;
201+
202+ if (!tabulated)
203+ {
204+ // Tabulate coordinates of dofs on cell
205+ dofmap.tabulate_coordinates(data.coordinates, ufc_cell);
206+ tabulated = true;
207+ }
208+
209 // Check if the coordinates are on current facet and thus on boundary
210 if (!on_facet(&(data.coordinates[i][0]), facet))
211 continue;
212@@ -860,10 +877,10 @@
213 {
214 // Restrict coefficient to cell
215 g->restrict(&data.w[0], *_function_space->element(), cell, ufc_cell);
216+ interpolated = true;
217 }
218
219 // Set boundary value
220- const uint global_dof = cell_dofs[i];
221 const double value = data.w[i];
222 boundary_values[global_dof] = value;
223 }
224@@ -896,10 +913,10 @@
225 // Create UFC cell object
226 UFCCell ufc_cell(mesh);
227
228- // Speeder-upper
229- std::pair<uint,uint> local_range = dofmap.ownership_range();
230- std::vector<bool> already_visited(local_range.second - local_range.first);
231- std::fill(already_visited.begin(), already_visited.end(), false);
232+ // Speed up the computations by only visiting (most) dofs once
233+ RangedIndexSet already_visited(dofmap.is_view()
234+ ? std::pair<uint, uint>(0,0)
235+ : dofmap.ownership_range());
236
237 // Iterate over cells
238 Progress p("Computing Dirichlet boundary values, pointwise search", mesh.num_cells());
239@@ -921,13 +938,10 @@
240 for (uint i = 0; i < dofmap.cell_dimension(cell->index()); ++i)
241 {
242 const uint global_dof = cell_dofs[i];
243- if (global_dof >= local_range.first && global_dof < local_range.second)
244- {
245- const uint dof_index = global_dof - local_range.first;
246- if (already_visited[dof_index])
247- continue;
248- already_visited[dof_index] = true;
249- }
250+
251+ // Skip already checked dofs
252+ if (already_visited.in_range(global_dof) && !already_visited.insert(global_dof))
253+ continue;
254
255 // Check if the coordinates are part of the sub domain (calls user-defined 'inside' function)
256 Array<double> x(gdim, &data.coordinates[i][0]);
257
258=== modified file 'dolfin/mesh/SubDomain.cpp'
259--- dolfin/mesh/SubDomain.cpp 2012-03-01 12:01:06 +0000
260+++ dolfin/mesh/SubDomain.cpp 2012-03-06 09:08:20 +0000
261@@ -21,6 +21,7 @@
262 // Last changed: 2011-08-31
263
264 #include <dolfin/common/Array.h>
265+#include <dolfin/common/RangedIndexSet.h>
266 #include <dolfin/log/log.h>
267 #include "Mesh.h"
268 #include "MeshData.h"
269@@ -164,6 +165,13 @@
270 // Array for vertex coordinate
271 Array<double> x;
272
273+ // Speed up the computation by only checking each vertex once (or twice if it
274+ // is on the boundary for some but not all facets).
275+ RangedIndexSet boundary_visited(mesh.num_vertices());
276+ RangedIndexSet interior_visited(mesh.num_vertices());
277+ std::vector<bool> boundary_inside(mesh.num_vertices());
278+ std::vector<bool> interior_inside(mesh.num_vertices());
279+
280 // Compute sub domain markers
281 Progress p("Computing sub domain markers", mesh.num_entities(dim));
282 for (MeshEntityIterator entity(mesh, dim); !entity.end(); ++entity)
283@@ -175,6 +183,10 @@
284 (exterior.empty() || exterior[*entity]);
285 }
286
287+ // Select the visited-cache to use for this facet (or entity)
288+ RangedIndexSet& is_visited = (on_boundary ? boundary_visited : interior_visited);
289+ std::vector<bool>& is_inside = (on_boundary ? boundary_inside : interior_inside);
290+
291 // Start by assuming all points are inside
292 bool all_points_inside = true;
293
294@@ -183,8 +195,13 @@
295 {
296 for (VertexIterator vertex(*entity); !vertex.end(); ++vertex)
297 {
298- x.update(_geometric_dimension, const_cast<double*>(vertex->x()));
299- if (!inside(x, on_boundary))
300+ if (is_visited.insert(vertex->index()))
301+ {
302+ x.update(_geometric_dimension, const_cast<double*>(vertex->x()));
303+ is_inside[vertex->index()] = inside(x, on_boundary);
304+ }
305+
306+ if (!is_inside[vertex->index()])
307 {
308 all_points_inside = false;
309 break;

Subscribers

People subscribed via source and target branches