|
17 | 17 | " - How to extract geometrical information from a `Grid`.\n", |
18 | 18 | " - How periodicity is handled in Gridap, and the difference between nodes and vertices.\n", |
19 | 19 | " - How to create a periodic model from scratch, use the example of a Mobius strip.\n", |
| 20 | + " - How to create and manipulate `FaceLabeling` objects, which are used to handle boundary conditions.\n", |
20 | 21 | "\n", |
21 | 22 | "## Required Packages" |
22 | 23 | ], |
|
43 | 44 | "4. Geometric Mappings\n", |
44 | 45 | "5. High-order Grids\n", |
45 | 46 | "6. Periodicity in Gridap\n", |
| 47 | + "7. FaceLabelings\n", |
46 | 48 | "\n", |
47 | 49 | "## 1. Utility Functions\n", |
48 | 50 | "We begin by defining helper functions that will be essential throughout this tutorial.\n", |
|
53 | 55 | { |
54 | 56 | "cell_type": "markdown", |
55 | 57 | "source": [ |
56 | | - "Convert a CartesianDiscreteModel to an UnstructuredDiscreteModel for more generic handling" |
| 58 | + "Convert a `CartesianDiscreteModel` to an `UnstructuredDiscreteModel` for more generic handling." |
57 | 59 | ], |
58 | 60 | "metadata": {} |
59 | 61 | }, |
|
71 | 73 | { |
72 | 74 | "cell_type": "markdown", |
73 | 75 | "source": [ |
74 | | - "Visualization function to plot nodes with their IDs\n", |
75 | | - "Input: node_coords - Array of node coordinates\n", |
76 | | - " node_ids - Array of corresponding node IDs" |
| 76 | + "Visualization function to plot nodes with their IDs. Input:\n", |
| 77 | + "- node_coords: Array of node coordinates.\n", |
| 78 | + "- node_ids: Array of corresponding node IDs." |
77 | 79 | ], |
78 | 80 | "metadata": {} |
79 | 81 | }, |
|
96 | 98 | { |
97 | 99 | "cell_type": "markdown", |
98 | 100 | "source": [ |
99 | | - "Overloaded method to plot node numbering directly from a model\n", |
100 | | - "This function extracts the necessary information from the model and calls the base plotting function" |
| 101 | + "Overloaded method to plot node numbering directly from a model.\n", |
| 102 | + "This function extracts the necessary information from the model and calls the base plotting function." |
101 | 103 | ], |
102 | 104 | "metadata": {} |
103 | 105 | }, |
|
148 | 150 | "\n", |
149 | 151 | "### Key Concept: Nodes vs. Vertices\n", |
150 | 152 | "\n", |
151 | | - "One of the most important distinctions in Gridap is between nodes and vertices:\n", |
| 153 | + "A very important distinction in Gridap is between nodes and vertices:\n", |
152 | 154 | "\n", |
153 | 155 | " - **Vertices** (Topological entities):\n", |
154 | 156 | " * 0-dimensional entities in the `GridTopology`\n", |
155 | 157 | " * Define the connectivity of the mesh\n", |
156 | 158 | " * Used for neighbor queries and mesh traversal\n", |
157 | 159 | " * Number of vertices depends only on topology\n", |
158 | 160 | "\n", |
159 | | - " - **Nodes** (Geometric entities):\n", |
| 161 | + " - **Nodes** (Geometrical entities):\n", |
160 | 162 | " * Control points stored in the `Grid`\n", |
161 | 163 | " * Define the geometry of elements\n", |
162 | 164 | " * Used for interpolation and mapping\n", |
|
387 | 389 | "\n", |
388 | 390 | "There are two ways to get the coordinates of nodes for each cell:\n", |
389 | 391 | "\n", |
390 | | - "1. Using standard Julia mapping:" |
| 392 | + "A) Using standard Julia mapping:" |
391 | 393 | ], |
392 | 394 | "metadata": {} |
393 | 395 | }, |
|
403 | 405 | { |
404 | 406 | "cell_type": "markdown", |
405 | 407 | "source": [ |
406 | | - "2. Using Gridap's lazy evaluation system (more efficient for large meshes):" |
| 408 | + "B) Using Gridap's lazy evaluation system (more efficient for large meshes):" |
407 | 409 | ], |
408 | 410 | "metadata": {} |
409 | 411 | }, |
|
538 | 540 | "our half-cylinder looks faceted. This is because we're still using linear elements\n", |
539 | 541 | "(straight edges) to approximate the curved geometry.\n", |
540 | 542 | "\n", |
541 | | - "### Solution: High-order Elements\n", |
| 543 | + "### Example: High-order Elements\n", |
542 | 544 | "\n", |
543 | 545 | "To accurately represent curved geometries, we need high-order elements:" |
544 | 546 | ], |
|
1059 | 1061 | ], |
1060 | 1062 | "metadata": {} |
1061 | 1063 | }, |
| 1064 | + { |
| 1065 | + "cell_type": "markdown", |
| 1066 | + "source": [ |
| 1067 | + "## 7. FaceLabelings and boundary conditions\n", |
| 1068 | + "\n", |
| 1069 | + "The `FaceLabeling` component of a `DiscreteModel` is the way Gridap handles boundary conditions.\n", |
| 1070 | + "The basic idea is that, similar to Gmsh, we classify the d-faces (cells, faces, edges, nodes) of the mesh\n", |
| 1071 | + "into different entities (physical groups in Gmsh terminology) which in turn have one or more\n", |
| 1072 | + "tags/labels associated with them.\n", |
| 1073 | + "We can then query the `FaceLabeling` for the tags associated with a given d-face,\n", |
| 1074 | + "or the d-faces associated with a given tag.\n", |
| 1075 | + "\n", |
| 1076 | + "We will now explore ways to create and manipulate `Facelabeling` objects.\n", |
| 1077 | + "\n", |
| 1078 | + "### Creating FaceLabelings\n", |
| 1079 | + "\n", |
| 1080 | + "The simplest way to create a blank `FaceLabeling` is to use your `GridTopology`:" |
| 1081 | + ], |
| 1082 | + "metadata": {} |
| 1083 | + }, |
| 1084 | + { |
| 1085 | + "outputs": [], |
| 1086 | + "cell_type": "code", |
| 1087 | + "source": [ |
| 1088 | + "model = cartesian_model((0,1,0,1),(3,3))\n", |
| 1089 | + "topo = get_grid_topology(model)\n", |
| 1090 | + "\n", |
| 1091 | + "labels = FaceLabeling(topo)" |
| 1092 | + ], |
| 1093 | + "metadata": {}, |
| 1094 | + "execution_count": null |
| 1095 | + }, |
| 1096 | + { |
| 1097 | + "cell_type": "markdown", |
| 1098 | + "source": [ |
| 1099 | + "The above `FaceLabeling` is by default created with 2 entities and 2 tags, associated to\n", |
| 1100 | + "interior and boundary d-faces respectively. The boundary facets are chosen as the ones\n", |
| 1101 | + "with a single neighboring cell.\n", |
| 1102 | + "\n", |
| 1103 | + "We can extract the low-level information from the `FaceLabeling` object:" |
| 1104 | + ], |
| 1105 | + "metadata": {} |
| 1106 | + }, |
| 1107 | + { |
| 1108 | + "outputs": [], |
| 1109 | + "cell_type": "code", |
| 1110 | + "source": [ |
| 1111 | + "tag_names = get_tag_name(labels) # Each name is a string\n", |
| 1112 | + "tag_entities = get_tag_entities(labels) # For each tag, a vector of entities\n", |
| 1113 | + "cell_to_entity = get_face_entity(labels,2) # For each cell, its associated entity\n", |
| 1114 | + "edge_to_entity = get_face_entity(labels,1) # For each edge, its associated entity\n", |
| 1115 | + "node_to_entity = get_face_entity(labels,0) # For each node, its associated entity" |
| 1116 | + ], |
| 1117 | + "metadata": {}, |
| 1118 | + "execution_count": null |
| 1119 | + }, |
| 1120 | + { |
| 1121 | + "cell_type": "markdown", |
| 1122 | + "source": [ |
| 1123 | + "It is usually more convenient to visualise it in Paraview by exporting to vtk:" |
| 1124 | + ], |
| 1125 | + "metadata": {} |
| 1126 | + }, |
| 1127 | + { |
| 1128 | + "outputs": [], |
| 1129 | + "cell_type": "code", |
| 1130 | + "source": [ |
| 1131 | + "writevtk(model,\"labels_basic\",labels=labels)" |
| 1132 | + ], |
| 1133 | + "metadata": {}, |
| 1134 | + "execution_count": null |
| 1135 | + }, |
| 1136 | + { |
| 1137 | + "cell_type": "markdown", |
| 1138 | + "source": [ |
| 1139 | + "Another useful way to create a `FaceLabeling` is by providing a coloring for the mesh cells,\n", |
| 1140 | + "where each color corresponds to a different tag.\n", |
| 1141 | + "The d-faces of the mesh will have all the tags associated to the cells that share them." |
| 1142 | + ], |
| 1143 | + "metadata": {} |
| 1144 | + }, |
| 1145 | + { |
| 1146 | + "outputs": [], |
| 1147 | + "cell_type": "code", |
| 1148 | + "source": [ |
| 1149 | + "cell_to_tag = [1,1,1,2,2,3,2,2,3]\n", |
| 1150 | + "tag_to_name = [\"A\",\"B\",\"C\"]\n", |
| 1151 | + "labels_cw = Geometry.face_labeling_from_cell_tags(topo,cell_to_tag,tag_to_name)\n", |
| 1152 | + "writevtk(model,\"labels_cellwise\",labels=labels_cw)" |
| 1153 | + ], |
| 1154 | + "metadata": {}, |
| 1155 | + "execution_count": null |
| 1156 | + }, |
| 1157 | + { |
| 1158 | + "cell_type": "markdown", |
| 1159 | + "source": [ |
| 1160 | + "We can also create a `FaceLabeling` from a vertex filter. The resulting `FaceLabeling` will have\n", |
| 1161 | + "only one tag, gathering the d-faces whose vertices ALL fullfill `filter(x) == true`." |
| 1162 | + ], |
| 1163 | + "metadata": {} |
| 1164 | + }, |
| 1165 | + { |
| 1166 | + "outputs": [], |
| 1167 | + "cell_type": "code", |
| 1168 | + "source": [ |
| 1169 | + "vfilter(x) = abs(x[1]- 1.0) < 1.e-5\n", |
| 1170 | + "labels_vf = Geometry.face_labeling_from_vertex_filter(topo, \"top\", vfilter)\n", |
| 1171 | + "writevtk(model,\"labels_filter\",labels=labels_vf)" |
| 1172 | + ], |
| 1173 | + "metadata": {}, |
| 1174 | + "execution_count": null |
| 1175 | + }, |
| 1176 | + { |
| 1177 | + "cell_type": "markdown", |
| 1178 | + "source": [ |
| 1179 | + "`FaceLabeling` objects can also be merged together. The resulting `FaceLabeling` will have\n", |
| 1180 | + "the union of the tags and entities of the original ones.\n", |
| 1181 | + "Note that this modifies the first `FaceLabeling` in place." |
| 1182 | + ], |
| 1183 | + "metadata": {} |
| 1184 | + }, |
| 1185 | + { |
| 1186 | + "outputs": [], |
| 1187 | + "cell_type": "code", |
| 1188 | + "source": [ |
| 1189 | + "labels = merge!(labels, labels_cw, labels_vf)\n", |
| 1190 | + "writevtk(model,\"labels_merged\",labels=labels)" |
| 1191 | + ], |
| 1192 | + "metadata": {}, |
| 1193 | + "execution_count": null |
| 1194 | + }, |
| 1195 | + { |
| 1196 | + "cell_type": "markdown", |
| 1197 | + "source": [ |
| 1198 | + "### Creating new tags from existing ones\n", |
| 1199 | + "\n", |
| 1200 | + "Tags in a `FaceLabeling` support all the usual set operation, i.e union, intersection,\n", |
| 1201 | + "difference and complementary." |
| 1202 | + ], |
| 1203 | + "metadata": {} |
| 1204 | + }, |
| 1205 | + { |
| 1206 | + "outputs": [], |
| 1207 | + "cell_type": "code", |
| 1208 | + "source": [ |
| 1209 | + "cell_to_tag = [1,1,1,2,2,3,2,2,3]\n", |
| 1210 | + "tag_to_name = [\"A\",\"B\",\"C\"]\n", |
| 1211 | + "labels = Geometry.face_labeling_from_cell_tags(topo,cell_to_tag,tag_to_name)" |
| 1212 | + ], |
| 1213 | + "metadata": {}, |
| 1214 | + "execution_count": null |
| 1215 | + }, |
| 1216 | + { |
| 1217 | + "cell_type": "markdown", |
| 1218 | + "source": [ |
| 1219 | + "Union: Takes as input a list of tags and creates a new tag that is the union of all of them." |
| 1220 | + ], |
| 1221 | + "metadata": {} |
| 1222 | + }, |
| 1223 | + { |
| 1224 | + "outputs": [], |
| 1225 | + "cell_type": "code", |
| 1226 | + "source": [ |
| 1227 | + "Geometry.add_tag_from_tags!(labels,\"A∪B\",[\"A\",\"B\"])" |
| 1228 | + ], |
| 1229 | + "metadata": {}, |
| 1230 | + "execution_count": null |
| 1231 | + }, |
| 1232 | + { |
| 1233 | + "cell_type": "markdown", |
| 1234 | + "source": [ |
| 1235 | + "Intersection: Takes as input a list of tags and creates a new tag that is the intersection of all of them." |
| 1236 | + ], |
| 1237 | + "metadata": {} |
| 1238 | + }, |
| 1239 | + { |
| 1240 | + "outputs": [], |
| 1241 | + "cell_type": "code", |
| 1242 | + "source": [ |
| 1243 | + "Geometry.add_tag_from_tags_intersection!(labels,\"A∩B\",[\"A\",\"B\"])" |
| 1244 | + ], |
| 1245 | + "metadata": {}, |
| 1246 | + "execution_count": null |
| 1247 | + }, |
| 1248 | + { |
| 1249 | + "cell_type": "markdown", |
| 1250 | + "source": [ |
| 1251 | + "Complementary: Takes as input a list of tags and creates a new tag that is the complementary of the union." |
| 1252 | + ], |
| 1253 | + "metadata": {} |
| 1254 | + }, |
| 1255 | + { |
| 1256 | + "outputs": [], |
| 1257 | + "cell_type": "code", |
| 1258 | + "source": [ |
| 1259 | + "Geometry.add_tag_from_tags_complementary!(labels,\"!A\",[\"A\"])" |
| 1260 | + ], |
| 1261 | + "metadata": {}, |
| 1262 | + "execution_count": null |
| 1263 | + }, |
| 1264 | + { |
| 1265 | + "cell_type": "markdown", |
| 1266 | + "source": [ |
| 1267 | + "Set difference: Takes as input two lists of tags (tags_include - tags_exclude)\n", |
| 1268 | + "and creates a new tag that contains all the d-faces that are in the first list but not in the second." |
| 1269 | + ], |
| 1270 | + "metadata": {} |
| 1271 | + }, |
| 1272 | + { |
| 1273 | + "outputs": [], |
| 1274 | + "cell_type": "code", |
| 1275 | + "source": [ |
| 1276 | + "Geometry.add_tag_from_tags_setdiff!(labels,\"A-B\",[\"A\"],[\"B\"]) # set difference\n", |
| 1277 | + "\n", |
| 1278 | + "writevtk(model,\"labels_setops\",labels=labels)" |
| 1279 | + ], |
| 1280 | + "metadata": {}, |
| 1281 | + "execution_count": null |
| 1282 | + }, |
| 1283 | + { |
| 1284 | + "cell_type": "markdown", |
| 1285 | + "source": [ |
| 1286 | + "### FaceLabeling queries\n", |
| 1287 | + "\n", |
| 1288 | + "The most common way of query information from a `FaceLabeling` is to query a face mask for\n", |
| 1289 | + "a given tag and face dimension. If multiple tags are provided, the union of the tags is returned." |
| 1290 | + ], |
| 1291 | + "metadata": {} |
| 1292 | + }, |
| 1293 | + { |
| 1294 | + "outputs": [], |
| 1295 | + "cell_type": "code", |
| 1296 | + "source": [ |
| 1297 | + "face_dim = 1\n", |
| 1298 | + "mask = get_face_mask(labels,[\"A\",\"C\"],face_dim) # Boolean mask\n", |
| 1299 | + "ids = findall(mask) # Edge IDs" |
| 1300 | + ], |
| 1301 | + "metadata": {}, |
| 1302 | + "execution_count": null |
| 1303 | + }, |
1062 | 1304 | { |
1063 | 1305 | "cell_type": "markdown", |
1064 | 1306 | "source": [ |
|
0 commit comments