1 /*--------------------------------------------------------------------------*/
2 /* ALBERTA: an Adaptive multi Level finite element toolbox using */
3 /* Bisectioning refinement and Error control by Residual */
4 /* Techniques for scientific Applications */
5 /* */
6 /* www.alberta-fem.de */
7 /* */
8 /*--------------------------------------------------------------------------*/
9 /* */
10 /* file: periodic.c */
11 /* */
12 /* description: support routines for periodic meshes: compute the orbit */
13 /* of macro element vertices under the action of the group */
14 /* of wall transformations. In 3d, we need also the orbits */
15 /* of the macro element edges. */
16 /* */
17 /*--------------------------------------------------------------------------*/
18 /* */
19 /* This file's authors: Claus-Justus Heine */
20 /* Abteilung f�r Angewandte Mathematik */
21 /* Universit�t Freiburg */
22 /* Hermann-Herder-Stra�e 10 */
23 /* 79104 Freiburg, Germany */
24 /* */
25 /* (c) by C.-J. Heine (2006). */
26 /* */
27 /*--------------------------------------------------------------------------*/
28
29 #include "alberta_intern.h"
30 #include "alberta.h"
31
32 /* Compute the orbit of a vertex (of the macro triangulatin) under the
33 * action of the group of wall transformations.
34 *
35 * @param[in] dim The dimension of the mesh.
36 *
37 * @param[in] wall_trafos The wall transformations as permutation group acting
38 * on the vertex indices of the macro triangulation.
39 *
40 * @param[in] nwt Number of Wall Transformations.
41 *
42 * @param[in] v The vertex to compute the orbit of.
43 *
44 * @param[out] orbit The orbit of e under the action of wall_trafos.
45 *
46 * @param[in] nv Number of Vertices.
47 *
48 * @return The length of the orbit.
49 *
50 */
_AI_wall_trafo_vertex_orbit(int dim,int (* wall_trafos)[N_VERTICES (DIM_MAX-1)][2],int nwt,int v,int * orbit,int nv)51 int _AI_wall_trafo_vertex_orbit(int dim,
52 int (*wall_trafos)[N_VERTICES(DIM_MAX-1)][2],
53 int nwt,
54 int v, int *orbit, int nv)
55 {
56 char in_orbit[nv]; /* flag vectors, true if v is contained in the orbit */
57 int i, j, k, l, new_v;
58 int orb_len;
59
60 /* initialisation: orbit is empty */
61 for (i = 0; i < nv; i++) {
62 in_orbit[i] = false;
63 }
64 orb_len = 0;
65
66 /* stuff v itself into the orbit */
67 in_orbit[v] = true;
68 orbit[orb_len++] = v;
69
70 for (i = 0; i < orb_len; i++) {
71 v = orbit[i];
72
73 for (j = 0; j < nwt; j++) {
74 for (k = 0; k < N_VERTICES(dim-1); k++) {
75 /* compute the image of v w.r.t. the wall trafo and its
76 * inverse. We would not nead the inverse, as the group of
77 * wall-trafos is finite, but this should speed up things.
78 */
79 for (l = 0; l < 2; l++) {
80 if (v == wall_trafos[j][k][l]) {
81 new_v = wall_trafos[j][k][1-l];
82 if (!in_orbit[new_v]) {
83 orbit[orb_len++] = new_v;
84 in_orbit[new_v] = true;
85 }
86 /* go to the next wall transformation, there can only be
87 * one match per transformation
88 */
89 goto next_wt;
90 }
91 }
92 }
93 next_wt:
94 continue;
95 }
96 }
97 return orb_len;
98 }
99
100 /**Compute the number of orbits under the action of the group of wall
101 * transformations on the vertices of the macro triangulation and map
102 * each vertex to its orbit number. The map is returned in
103 * "orbit_map". If orbit_map[e] == -1 then e is a fix-point.
104 *
105 * @param[in] wall_trafos The wall transformations as permutation group acting
106 * on the global vertex indices of the macro triangulation.
107 *
108 * @param[n] nwt Number of Wall Transformations.
109 *
110 * @param[out] orbit_map The mapping of vertex numbers to orbit
111 * numbers. Fix-points have the virtual orbit number -1. @a orbit_map
112 * may be NULL.
113 *
114 * @param[in,out] nv On entry "*nv" holds the number of vertices, on
115 * exit "*nv" holds the size of the set of orbits (i.e. the number of
116 * vertices after identifying periodic walls).
117 *
118 * @return The number of non-trivial orbits, i.e. without counting
119 * fix-points.
120 *
121 */
_AI_wall_trafo_vertex_orbits(int dim,int (* wall_trafos)[N_VERTICES (DIM_MAX-1)][2],int nwt,int * orbit_map,int * nvp)122 int _AI_wall_trafo_vertex_orbits(int dim,
123 int (*wall_trafos)[N_VERTICES(DIM_MAX-1)][2],
124 int nwt,
125 int *orbit_map, int *nvp)
126 {
127 int v, i, nv = *nvp;
128 int orb_len, orbit[nv];
129 int n_orbits, nv_in_orbits;
130 int orbit_map_space[nv];
131
132 if (!orbit_map) {
133 orbit_map = orbit_map_space;
134 }
135 for (v = 0; v < nv; v++) {
136 orbit_map[v] = -1;
137 }
138 n_orbits = 0;
139 nv_in_orbits = 0;
140 *nvp = 0;
141 for (v = 0; v < nv && nv_in_orbits < nv; v++) {
142 if (orbit_map[v] < 0) {
143 orb_len =
144 _AI_wall_trafo_vertex_orbit(dim, wall_trafos, nwt, v, orbit, nv);
145 nv_in_orbits += orb_len;
146 ++(*nvp);
147 if (orb_len > 1) {
148 for (i = 0; i < orb_len; i++) {
149 orbit_map[orbit[i]] = n_orbits;
150 }
151 n_orbits++;
152 }
153 }
154 }
155 return n_orbits;
156 }
157
158 #if DIM_MAX == 3
159 /* We assign each unordered pair (i,j) with i < j a unique number
160 * according to the lexicographical ordering (0,1), (0,2), ...,
161 * (1,2),...
162 *
163 * The formula is just the difference of two arithmetic sums, taking
164 * into account that we start the numbering at 0.
165 *
166 * (i,j) with i < j or j < i, nv is the number of vertices
167 */
168 #define _PAIR_NR(i, j, nv) (((j)-(i)-1)+(i)*(2*(nv)-(i)-1)/2)
169 #define PAIR_NR(i, j, nv) ((i) < (j) ? _PAIR_NR(i, j, nv) : _PAIR_NR(j, i, nv))
170
171 /* Compute the orbit of an edge (of the macro triangulatin) under the
172 * action of the group of wall transformations. Edges are represented
173 * as (unordered) pairs of vertex numbers.
174 *
175 * @param[in] wall_trafos The wall transformations as permutation group acting
176 * on the vertex indices of the macro triangulation.
177 *
178 * @param[in] nwt Number of Wall Transformations.
179 *
180 * @param[in] e The edge to compute the orbit of.
181 *
182 * @param[out] orbit The orbit of e under the action of wall_trafos.
183 *
184 * @param[in] edges The edges of the macro triangulation as unordered pairs of
185 * of vertex numbers.
186 *
187 * @param[in] ne Number of Edges.
188 *
189 * @return The length of the orbit.
190 *
191 */
_AI_wall_trafo_edge_orbit(int (* wall_trafos)[N_VERTICES_2D][2],int nwt,int e,int * orbit,int (* edges)[2],int ne)192 int _AI_wall_trafo_edge_orbit(int (*wall_trafos)[N_VERTICES_2D][2],
193 int nwt,
194 int e, int *orbit,
195 int (*edges)[2], int ne)
196 {
197 const int edge_ind[N_EDGES_2D][2] = { { 1, 2 }, { 2, 0 }, {0, 1} };
198 int i, j, nv;
199 int edge_wall_trafos[nwt][N_EDGES_2D][2];
200
201 nv = 0;
202 for (i = 0; i < ne; i++) {
203 nv = MAX(edges[i][0], MAX(edges[i][1], nv));
204 }
205 ++nv;
206
207 {
208 int np = nv*(nv-1)/2;
209 int pair2edge[np];
210
211 /* first build up the mappings between memory_3d.c edge numbering
212 * and the numbering of edges interpreted as unordered pair
213 */
214 for (i = 0; i < np; i++) {
215 pair2edge[i] = -1;
216 }
217 for (i = 0; i < ne; i++) {
218 pair2edge[PAIR_NR(edges[i][0], edges[i][1], nv)] = i;
219 }
220
221 /* Now build a representation of the wall-transformations action
222 * as permutations on the edge indices. Each wall transformation
223 * is the product of three (N_EDGES_2D) disjoint transpositions.
224 */
225 for (i = 0; i < nwt; i++) {
226 for (j = 0; j < N_EDGES_2D; j++) {
227 int frompair, topair;
228
229 frompair = PAIR_NR(wall_trafos[i][edge_ind[j][0]][0],
230 wall_trafos[i][edge_ind[j][1]][0], nv);
231 topair = PAIR_NR(wall_trafos[i][edge_ind[j][0]][1],
232 wall_trafos[i][edge_ind[j][1]][1], nv);
233 DEBUG_TEST_EXIT(pair2edge[frompair] >= 0 && pair2edge[topair] >= 0,
234 "Wall transformations do not seem to operate on "
235 "the set of edges of the triangulation.\n");
236 edge_wall_trafos[i][j][0] = pair2edge[frompair];
237 edge_wall_trafos[i][j][1] = pair2edge[topair];
238 }
239 }
240 }
241
242 /******************************* BIG FAT NOTE ****************************/
243 /* */
244 /* AT THIS POINT WE EXPLOIT THE FACT THAT N_EDGES_2D == N_VERTICES_2D, */
245 /* THIS MEANS WE CAN USE _AI_wall_trafo_vertex_orbit(): edge_wall_trafos */
246 /* ACT ON THE SET OF UNSORTED TRIPLETS OF INTEGERS, THIS IS JUST WHAT */
247 /* wall_trafos DOES, FROM THE GROUP THEORITIC POINT OF VIEW THERE IS NO */
248 /* DIFFERENCE. */
249 /* */
250 /*************************************************************************/
251
252 return _AI_wall_trafo_vertex_orbit(DIM_MAX /* 3 */,
253 edge_wall_trafos, nwt,
254 e, orbit, ne);
255 }
256
257 /**Compute the number of orbits under the action of the group of wall
258 * transformations and map each (macro triangulation) edge to its
259 * proper orbit. The map is returned in "orbit_map". Negative entries
260 * in "orbit_map" mean that the orbit has only one element, i.e. if
261 * orbit_map[e] < 0 then e is a fix-point.
262 *
263 * @param[in] wall_trafos The wall transformations as permutation group acting
264 * on the vertex indices of the macro triangulation.
265 *
266 * @param[n] nwt Number of Wall Transformations.
267 *
268 * @param[out] orbit_map The mapping of orbit numbers to edge
269 * numbers. Fix-points have the virtual orbit number -1.
270 *
271 * @param[in] edges The edges of the macro triangulation as unordered pairs of
272 * of vertex numbers.
273 *
274 * @param[in] ne Number of Edges.
275 *
276 * @return The number of non-trivial orbits, i.e. without counting
277 * fix-points.
278 *
279 */
_AI_wall_trafo_edge_orbits(int (* wall_trafos)[N_VERTICES (DIM_MAX-1)][2],int nwt,int * orbit_map,int (* edges)[2],int ne)280 int _AI_wall_trafo_edge_orbits(int (*wall_trafos)[N_VERTICES(DIM_MAX-1)][2],
281 int nwt,
282 int *orbit_map,
283 int (*edges)[2], int ne)
284 {
285 char in_orbit[ne]; /* flag vectors, true if e is contained in any orbit */
286 int e, i;
287 int orb_len, orbit[ne];
288 int n_orbits, ne_in_orbits;
289
290 for (e = 0; e < ne; e++) {
291 in_orbit[e] = false;
292 }
293 n_orbits = 0;
294 ne_in_orbits = 0;
295 for (e = 0; e < ne && ne_in_orbits < ne; e++) {
296 if (in_orbit[e] == false) {
297 orb_len = _AI_wall_trafo_edge_orbit(wall_trafos, nwt,
298 e, orbit, edges, ne);
299 ne_in_orbits += orb_len;
300 if (orb_len > 1) {
301 for (i = 0; i < orb_len; i++) {
302 in_orbit[orbit[i]] = true;
303 orbit_map[orbit[i]] = n_orbits;
304 }
305 n_orbits++;
306 } else {
307 orbit_map[e] = -1;
308 }
309 }
310 }
311 return n_orbits;
312 }
313 #endif
314
315 /* Given a mesh compute the generators for the group of wall
316 * transformations for the macro-level. Vertices are numbered
317 * according to the address of the coordinate pointers; we assume that
318 * the coordinates are stored contiguously in memory (as is assumed in
319 * the memory_Xd.c files).
320 *
321 * Return value is the number of wall-transformations.
322 */
323 int
_AI_compute_macro_wall_trafos(MESH * mesh,int (** wall_trafos_ptr)[N_VERTICES (DIM_MAX-1)][2])324 _AI_compute_macro_wall_trafos(MESH *mesh,
325 int (**wall_trafos_ptr)[N_VERTICES(DIM_MAX-1)][2])
326 {
327 int dim = mesh->dim;
328 REAL_D *coords = ((MESH_MEM_INFO *)mesh->mem_info)->coords;
329 MACRO_EL *mel, *mel_n;
330 int i, j, k;
331 int el_wall_trafos[mesh->n_macro_el][N_WALLS(dim)];
332 int (*wall_trafos)[N_VERTICES(DIM_MAX-1)][2] = NULL;
333 int nwt = 0;
334
335 if (!mesh->is_periodic) {
336 *wall_trafos_ptr = NULL;
337 return 0;
338 }
339
340 memset(el_wall_trafos, 0, sizeof(el_wall_trafos));
341 for (i = 0; i < mesh->n_macro_el; i++) {
342 mel = mesh->macro_els + i;
343 for (j = 0; j < N_WALLS(dim); j++) {
344 if (el_wall_trafos[i][j] != 0 || mel->neigh_vertices[j][0] == -1) {
345 continue;
346 }
347 /* Need to create a new wall transformation */
348 if (nwt % 100 == 0) {
349 wall_trafos =(int (*)[N_VERTICES(DIM_MAX-1)][2])
350 MEM_REALLOC(wall_trafos,
351 nwt * sizeof(*wall_trafos),
352 (nwt+100) * sizeof(*wall_trafos), char);
353 }
354 mel_n = mel->neigh[j];
355 for (k = 0; k < N_VERTICES(dim-1); k++) {
356 wall_trafos[nwt][k][0] =
357 (int)((REAL_D *)mel->coord[(j+k+1) % N_VERTICES(dim)] - coords);
358 wall_trafos[nwt][k][1] =
359 (int)((REAL_D *)mel_n->coord[mel->neigh_vertices[j][k]] - coords);
360 }
361 el_wall_trafos[mel->index][j] = nwt+1;
362 el_wall_trafos[mel_n->index][mel->opp_vertex[j]] = -(nwt+1);
363 nwt++;
364 }
365 }
366 wall_trafos = (int (*)[N_VERTICES(DIM_MAX-1)][2])
367 MEM_REALLOC(wall_trafos,
368 (nwt + 100 - 1) / 100 * 100 * sizeof(*wall_trafos),
369 nwt * sizeof(*wall_trafos),
370 char);
371 *wall_trafos_ptr = wall_trafos;
372 return nwt;
373 }
374