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