|
37 | 37 | "# c = cells.ring_single()\n", |
38 | 38 | "c = cells.coupler()\n", |
39 | 39 | "\n", |
| 40 | + "dirname = \"./sim-data-mesh-----coupler\"\n", |
| 41 | + "\n", |
40 | 42 | "c" |
41 | 43 | ] |
42 | 44 | }, |
|
106 | 108 | "metadata": {}, |
107 | 109 | "outputs": [], |
108 | 110 | "source": [ |
109 | | - "result = sim.mesh(\"./mesh-ybranch\")\n", |
| 111 | + "result = sim.mesh(dirname, refined_mesh_size=0.2, max_mesh_size=1.0)\n", |
110 | 112 | "config_path = sim.write_config()\n", |
111 | 113 | "\n", |
112 | 114 | "print(f\"Mesh file: {result.mesh_path}\")\n", |
|
119 | 121 | "id": "7", |
120 | 122 | "metadata": {}, |
121 | 123 | "source": [ |
122 | | - "### Inspect mesh statistics" |
| 124 | + "### Mesh summary" |
123 | 125 | ] |
124 | 126 | }, |
125 | 127 | { |
|
129 | 131 | "metadata": {}, |
130 | 132 | "outputs": [], |
131 | 133 | "source": [ |
132 | | - "import json\n", |
| 134 | + "import meshio\n", |
| 135 | + "import numpy as np\n", |
133 | 136 | "\n", |
134 | | - "# Mesh statistics\n", |
135 | 137 | "stats = result.mesh_stats\n", |
136 | | - "print(\"=== Mesh Stats ===\")\n", |
137 | | - "print(json.dumps(stats, indent=2))\n", |
138 | | - "\n", |
139 | | - "# Config contents\n", |
140 | | - "print(\"\\n=== mesh_config.json ===\")\n", |
141 | | - "print(config_path.read_text())" |
142 | | - ] |
143 | | - }, |
144 | | - { |
145 | | - "cell_type": "markdown", |
146 | | - "id": "9", |
147 | | - "metadata": {}, |
148 | | - "source": [ |
149 | | - "### Inspect physical groups" |
150 | | - ] |
151 | | - }, |
152 | | - { |
153 | | - "cell_type": "code", |
154 | | - "execution_count": null, |
155 | | - "id": "10", |
156 | | - "metadata": {}, |
157 | | - "outputs": [], |
158 | | - "source": [ |
159 | 138 | "groups = result.groups\n", |
| 139 | + "m = meshio.read(result.mesh_path)\n", |
| 140 | + "tag_to_name = {tag: name for name, (tag, _) in m.field_data.items()}\n", |
160 | 141 | "\n", |
161 | | - "print(\"Volume groups (materials):\")\n", |
162 | | - "for name, info in groups[\"volumes\"].items():\n", |
163 | | - " print(f\" {name}: phys_group={info['phys_group']}, tags={info['tags']}\")\n", |
| 142 | + "# --- Header ---\n", |
| 143 | + "size_kb = result.mesh_path.stat().st_size / 1024\n", |
| 144 | + "print(f\"Mesh: {result.mesh_path.name} ({size_kb:.0f} KB)\")\n", |
| 145 | + "print(f\"Nodes: {stats.get('nodes', '?'):,} Tets: {stats.get('tetrahedra', '?'):,}\")\n", |
| 146 | + "print()\n", |
164 | 147 | "\n", |
165 | | - "print(\"\\nLayer volumes:\")\n", |
166 | | - "for name, info in groups[\"layer_volumes\"].items():\n", |
167 | | - " print(\n", |
168 | | - " f\" {name}: phys_group={info['phys_group']}, material={info['material']}, tags={info['tags']}\"\n", |
169 | | - " )\n", |
| 148 | + "# --- Quality ---\n", |
| 149 | + "q = stats.get(\"quality\", {})\n", |
| 150 | + "sicn = stats.get(\"sicn\", {})\n", |
| 151 | + "edge = stats.get(\"edge_length\", {})\n", |
| 152 | + "print(\"Quality\")\n", |
| 153 | + "print(\n", |
| 154 | + " f\" Shape (gamma): min={q.get('min', '?')} mean={q.get('mean', '?')} max={q.get('max', '?')}\"\n", |
| 155 | + ")\n", |
| 156 | + "print(\n", |
| 157 | + " f\" SICN: min={sicn.get('min', '?')} mean={sicn.get('mean', '?')} invalid={sicn.get('invalid', '?')}\"\n", |
| 158 | + ")\n", |
| 159 | + "print(f\" Edge length: min={edge.get('min', '?')} max={edge.get('max', '?')}\")\n", |
170 | 160 | "\n", |
171 | | - "if groups.get(\"port_surfaces\"):\n", |
172 | | - " print(\"\\nPort surfaces:\")\n", |
173 | | - " for name, info in groups[\"port_surfaces\"].items():\n", |
| 161 | + "# Edge ratio from actual mesh\n", |
| 162 | + "for cells in m.cells:\n", |
| 163 | + " if cells.type == \"tetra\":\n", |
| 164 | + " pts = m.points[cells.data]\n", |
| 165 | + " pairs = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]\n", |
| 166 | + " all_edges = np.stack(\n", |
| 167 | + " [np.linalg.norm(pts[:, i] - pts[:, j], axis=1) for i, j in pairs]\n", |
| 168 | + " )\n", |
| 169 | + " ratios = all_edges.max(axis=0) / np.maximum(all_edges.min(axis=0), 1e-15)\n", |
| 170 | + " bad = int(np.sum(ratios > 20))\n", |
174 | 171 | " print(\n", |
175 | | - " f\" {name}: phys_group={info['phys_group']}, center={info['center']}, \"\n", |
176 | | - " f\"width={info['width']}, layer={info['layer']}, z_range={info['z_range']}\"\n", |
| 172 | + " f\" Edge ratio: mean={ratios.mean():.1f} max={ratios.max():.1f} bad(>20)={bad}\"\n", |
177 | 173 | " )\n", |
| 174 | + "print()\n", |
| 175 | + "\n", |
| 176 | + "# --- Bounding box ---\n", |
| 177 | + "bb = stats.get(\"bbox\", {})\n", |
| 178 | + "print(f\"Bounding box\")\n", |
| 179 | + "print(\n", |
| 180 | + " f\" x: [{bb.get('xmin', 0):.2f}, {bb.get('xmax', 0):.2f}] ({bb.get('xmax', 0) - bb.get('xmin', 0):.2f} um)\"\n", |
| 181 | + ")\n", |
| 182 | + "print(\n", |
| 183 | + " f\" y: [{bb.get('ymin', 0):.2f}, {bb.get('ymax', 0):.2f}] ({bb.get('ymax', 0) - bb.get('ymin', 0):.2f} um)\"\n", |
| 184 | + ")\n", |
| 185 | + "print(\n", |
| 186 | + " f\" z: [{bb.get('zmin', 0):.2f}, {bb.get('zmax', 0):.2f}] ({bb.get('zmax', 0) - bb.get('zmin', 0):.2f} um)\"\n", |
| 187 | + ")\n", |
| 188 | + "print()\n", |
178 | 189 | "\n", |
179 | | - "if groups.get(\"outer_boundary\"):\n", |
180 | | - " print(f\"\\nOuter boundary: phys_group={groups['outer_boundary']['phys_group']}\")\n", |
181 | | - "else:\n", |
182 | | - " print(\"\\nNo outer boundary (airbox not included)\")" |
| 190 | + "# --- Physical groups ---\n", |
| 191 | + "print(\"Physical groups\")\n", |
| 192 | + "for cells, phys in zip(m.cells, m.cell_data[\"gmsh:physical\"]):\n", |
| 193 | + " if cells.type != \"tetra\":\n", |
| 194 | + " continue\n", |
| 195 | + " for tag, name in sorted(tag_to_name.items(), key=lambda x: x[1]):\n", |
| 196 | + " mask = phys == tag\n", |
| 197 | + " n = int(np.sum(mask))\n", |
| 198 | + " if n == 0:\n", |
| 199 | + " continue\n", |
| 200 | + " pts = m.points[cells.data[mask].ravel()]\n", |
| 201 | + " zlo, zhi = pts[:, 2].min(), pts[:, 2].max()\n", |
| 202 | + " mat = \"\"\n", |
| 203 | + " if name in groups.get(\"layer_volumes\", {}):\n", |
| 204 | + " mat = f\" (material: {groups['layer_volumes'][name].get('material', '?')})\"\n", |
| 205 | + " print(f\" {name:16s} {n:>7,} tets z=[{zlo:.3f}, {zhi:.3f}]{mat}\")\n", |
| 206 | + "\n", |
| 207 | + "if groups.get(\"port_surfaces\"):\n", |
| 208 | + " print()\n", |
| 209 | + " print(\"Port surfaces\")\n", |
| 210 | + " for name, info in groups[\"port_surfaces\"].items():\n", |
| 211 | + " c = info[\"center\"]\n", |
| 212 | + " print(\n", |
| 213 | + " f\" {name:16s} center=({c[0]:.2f}, {c[1]:.2f}) width={info['width']:.2f} z=[{info['z_range'][0]:.3f}, {info['z_range'][1]:.3f}]\"\n", |
| 214 | + " )" |
183 | 215 | ] |
184 | 216 | }, |
185 | 217 | { |
186 | 218 | "cell_type": "markdown", |
187 | | - "id": "11", |
| 219 | + "id": "9", |
188 | 220 | "metadata": {}, |
189 | 221 | "source": [ |
190 | 222 | "### Visualize the mesh" |
|
193 | 225 | { |
194 | 226 | "cell_type": "code", |
195 | 227 | "execution_count": null, |
196 | | - "id": "12", |
| 228 | + "id": "10", |
197 | 229 | "metadata": {}, |
198 | 230 | "outputs": [], |
199 | 231 | "source": [ |
200 | 232 | "# Full mesh wireframe\n", |
201 | | - "sim.plot_mesh(interactive=False)" |
| 233 | + "sim.plot_mesh(interactive=True)" |
202 | 234 | ] |
203 | 235 | }, |
204 | 236 | { |
205 | 237 | "cell_type": "code", |
206 | 238 | "execution_count": null, |
207 | | - "id": "13", |
| 239 | + "id": "11", |
208 | 240 | "metadata": {}, |
209 | 241 | "outputs": [], |
210 | 242 | "source": [ |
|
0 commit comments