From ce35245b98857721408f89e808e642c9d1be2a92 Mon Sep 17 00:00:00 2001 From: mdmaas Date: Fri, 29 May 2026 17:34:28 -0300 Subject: [PATCH 1/4] fix(palace): remove implicit stack capping and align PDK stack defaults --- nbs/_palace_electrostatic.ipynb | 318 +++++++++++++++++++++++++---- nbs/_palace_electrostatic.py | 156 +++++++++++--- nbs/palace_inductor.ipynb | 243 +++------------------- nbs/palace_inductor.py | 12 +- src/gsim/common/stack/__init__.py | 4 + src/gsim/common/stack/extractor.py | 176 +++++++++++++--- src/gsim/palace/base.py | 5 +- src/gsim/palace/mesh/geometry.py | 47 +++-- src/gsim/palace/models/mesh.py | 2 +- tests/palace/test_mesh.py | 51 ++++- tests/palace/test_sim_classes.py | 90 ++++++++ tests/palace/test_stack.py | 110 ++++++++++ 12 files changed, 896 insertions(+), 318 deletions(-) diff --git a/nbs/_palace_electrostatic.ipynb b/nbs/_palace_electrostatic.ipynb index 8f824570..c656d983 100644 --- a/nbs/_palace_electrostatic.ipynb +++ b/nbs/_palace_electrostatic.ipynb @@ -28,10 +28,43 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "0b8b2971", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ports: [('MINUS', (-5.86, 0.0)), ('PLUS', (4.5, 0.0))]\n" + ] + }, + { + "ename": "PermissionError", + "evalue": "[Errno 13] Permission denied: '/tmp/gdsfactory/layer_properties.lyp'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mPermissionError\u001b[39m Traceback (most recent call last)", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/gsim/.venv/lib/python3.12/site-packages/kfactory/kcell.py:890\u001b[39m, in \u001b[36mProtoTKCell._ipython_display_\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 885\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_ipython_display_\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 886\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Display a cell in a Jupyter Cell.\u001b[39;00m\n\u001b[32m 887\u001b[39m \n\u001b[32m 888\u001b[39m \u001b[33;03m Usage: Pass the kcell variable as an argument in the cell at the end\u001b[39;00m\n\u001b[32m 889\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m890\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/gsim/.venv/lib/python3.12/site-packages/gdsfactory/component.py:1199\u001b[39m, in \u001b[36mComponent.plot\u001b[39m\u001b[34m(self, lyrdb, display_type, show_labels, show_ruler, pixel_buffer_options, return_fig)\u001b[39m\n\u001b[32m 1197\u001b[39m lyp_path = pathlib.Path(layer_views)\n\u001b[32m 1198\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1199\u001b[39m \u001b[43mlayer_views\u001b[49m\u001b[43m.\u001b[49m\u001b[43mto_lyp\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilepath\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlyp_path\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1201\u001b[39m layout_view = lay.LayoutView()\n\u001b[32m 1202\u001b[39m cell_view_index = layout_view.create_layout(\u001b[38;5;28;01mTrue\u001b[39;00m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/gsim/.venv/lib/python3.12/site-packages/gdsfactory/technology/layer_views.py:1065\u001b[39m, in \u001b[36mLayerViews.to_lyp\u001b[39m\u001b[34m(self, filepath, overwrite)\u001b[39m\n\u001b[32m 1062\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m ls \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m.custom_line_styles.values():\n\u001b[32m 1063\u001b[39m root.append(ls.to_klayout_xml())\n\u001b[32m-> \u001b[39m\u001b[32m1065\u001b[39m \u001b[43mfilepath\u001b[49m\u001b[43m.\u001b[49m\u001b[43mwrite_bytes\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmake_pretty_xml\u001b[49m\u001b[43m(\u001b[49m\u001b[43mroot\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1066\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m filepath\n", + "\u001b[36mFile \u001b[39m\u001b[32m/usr/lib/python3.12/pathlib.py:1038\u001b[39m, in \u001b[36mPath.write_bytes\u001b[39m\u001b[34m(self, data)\u001b[39m\n\u001b[32m 1036\u001b[39m \u001b[38;5;66;03m# type-check for the buffer interface before truncating the file\u001b[39;00m\n\u001b[32m 1037\u001b[39m view = \u001b[38;5;28mmemoryview\u001b[39m(data)\n\u001b[32m-> \u001b[39m\u001b[32m1038\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mopen\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmode\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mwb\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[32m 1039\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m f.write(view)\n", + "\u001b[36mFile \u001b[39m\u001b[32m/usr/lib/python3.12/pathlib.py:1015\u001b[39m, in \u001b[36mPath.open\u001b[39m\u001b[34m(self, mode, buffering, encoding, errors, newline)\u001b[39m\n\u001b[32m 1013\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m mode:\n\u001b[32m 1014\u001b[39m encoding = io.text_encoding(encoding)\n\u001b[32m-> \u001b[39m\u001b[32m1015\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mio\u001b[49m\u001b[43m.\u001b[49m\u001b[43mopen\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbuffering\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mencoding\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merrors\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnewline\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[31mPermissionError\u001b[39m: [Errno 13] Permission denied: '/tmp/gdsfactory/layer_properties.lyp'" + ] + }, + { + "data": { + "text/plain": [ + "Component(name=cmim_W10_L10_LMMetal5drawing_LMMIMdrawing_LVVmimdrawing_98194961$2$1, ports=['MINUS', 'PLUS'], pins=[], instances=['rectangle_gdsfactorypcomponentspshapesprectangle_S11p92_59dd5848_0_0', 'rectangle_gdsfactorypcomponentspshapesprectangle_S10p72_f76ae31b_0_0', 'rectangle_gdsfactorypcomponentspshapesprectangle_S10_10_ef94ac30_0_0', 'via_array_VTVmim_C10_R10_VS0p42_VS0p94_VE0p42_LCContdra_14498da7_-4440_-4440', 'rectangle_gdsfactorypcomponentspshapesprectangle_S11p92_316b73f6_0_0', 'rectangle_gdsfactorypcomponentspshapesprectangle_S11p92_2d7918b4_0_0', 'rectangle_gdsfactorypcomponentspshapesprectangle_S11p92_ff69e054_0_0', 'rectangle_gdsfactorypcomponentspshapesprectangle_S11p92_81cf13d4_0_0', 'rectangle_gdsfactorypcomponentspshapesprectangle_S11p92_3e735432_0_0', 'rectangle_gdsfactorypcomponentspshapesprectangle_S1_2_L_313130ad_-5460_0', 'rectangle_gdsfactorypcomponentspshapesprectangle_S1_2_L_21edbbcb_4500_0'], locked=False, kcl=DEFAULT)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "from ihp import PDK, cells\n", "\n", @@ -44,7 +77,10 @@ "# -> 10x10 Vmim vias (0.42 um, pitch 0.94 um) -> TopMetal1 (top plate, PLUS)\n", "c = cells.cmim(width=cap_width, length=cap_length).copy()\n", "print(\"Ports:\", [(p.name, tuple(p.center)) for p in c.ports])\n", - "c.plot()" + "\n", + "cc = c.copy()\n", + "cc.draw_ports()\n", + "cc" ] }, { @@ -57,10 +93,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "115539f6", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Validation: PASSED\n" + ] + } + ], "source": [ "from gsim.palace import ElectrostaticSim\n", "\n", @@ -68,7 +112,8 @@ "\n", "sim.set_output_dir(\"./palace-sim-electrostatic\")\n", "sim.set_geometry(c)\n", - "sim.set_stack(air_above=100.0, substrate_thickness=2.0)\n", + "sim.set_stack(substrate_thickness=2.0)\n", + "sim.set_airbox(margin_x=5, margin_y=5, z_above=5, z_below=5)\n", "\n", "# Metal5 = bottom plate (MINUS), TopMetal1 = top plate (PLUS)\n", "sim.add_terminal(\"T1\", layer=\"metal5\")\n", @@ -89,25 +134,87 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "4fae1dec", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "Mesh Summary\n", + "========================================\n", + "Dimensions: 31.9 x 31.9 x 20.2 µm\n", + "Nodes: 26,689\n", + "Elements: 203,560\n", + "Tetrahedra: 150,804\n", + "Edge length: 0.06 - 15.96 µm\n", + "Quality: 0.610 (min: 0.002)\n", + "SICN: 0.669 (all valid)\n", + "----------------------------------------\n", + "Volumes (3):\n", + " - vmim [1]\n", + " - sio2 [2]\n", + " - air [3]\n", + "Surfaces (7):\n", + " - metal5_xy [4]\n", + " - metal5_z [5]\n", + " - topmetal1_xy [6]\n", + " - topmetal1_z [7]\n", + " - sio2__vmim [8]\n", + " - air__sio2 [9]\n", + " - air__None [10]\n", + "----------------------------------------\n", + "Mesh: palace-sim-electrostatic/palace.msh" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Vmim vias are 0.42 um with 0.52 um gaps; MIM dielectric is 0.19 um thick\n", - "sim.mesh(preset=\"fine\", margin=20, refined_mesh_size=0.1, merge_via_distance=0)" + "sim.mesh(preset=\"fine\", refined_mesh_size=0.1, merge_via_distance=0)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "id": "8b3073de", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "13cd220370864fe1818fbc373211b82e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Widget(value='