palace-toolkit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ include tools/build_palace.py
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: palace-toolkit
3
+ Version: 0.1.0
4
+ Summary: PALACE electromagnetic simulation course materials
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: gmsh
7
+ Requires-Dist: numpy
8
+ Requires-Dist: scipy
9
+ Requires-Dist: meshio
10
+ Requires-Dist: mfem
11
+ Requires-Dist: pyvista
12
+ Requires-Dist: enlighten
13
+ Requires-Dist: pint
14
+ Provides-Extra: plot
15
+ Requires-Dist: pandas; extra == "plot"
16
+ Requires-Dist: matplotlib; extra == "plot"
17
+ Provides-Extra: palace-cpu
18
+ Requires-Dist: palacetoolkit-palace-cpu>=0.1.0; extra == "palace-cpu"
19
+ Provides-Extra: docs
20
+ Requires-Dist: mkdocs; extra == "docs"
21
+ Requires-Dist: mkdocs-material; extra == "docs"
22
+ Requires-Dist: mkdocs-jupyter; extra == "docs"
23
+ Requires-Dist: pyvista[jupyter]; extra == "docs"
24
+ Requires-Dist: nbconvert; extra == "docs"
25
+ Requires-Dist: ipykernel; extra == "docs"
26
+ Requires-Dist: papermill; extra == "docs"
27
+ Requires-Dist: pytest; extra == "docs"
28
+ Requires-Dist: nb-clean; extra == "docs"
29
+ Requires-Dist: pre-commit; extra == "docs"
@@ -0,0 +1,176 @@
1
+ # PalaceToolkit
2
+
3
+ A Python toolkit for open-source electromagnetic simulation with
4
+ [Palace](https://awslabs.github.io/palace/) and [Gmsh](https://gmsh.info/).
5
+ PalaceToolkit provides a declarative pipeline that takes you from geometry
6
+ definition to post-processed S-parameters and far-field plots — no commercial
7
+ licence required.
8
+
9
+ ## Features
10
+
11
+ | Module | Description |
12
+ |--------|-------------|
13
+ | `palace.mesh` | Priority-based boolean pipeline for multi-material Gmsh models with automatic size-field grading. |
14
+ | `palace.simulation` | Run Palace via Apptainer/MPI and extract S-parameters and impedance. |
15
+ | `palace.verify_topology` | Validate that a 3D tetrahedral mesh is topologically consistent for Palace/MFEM. |
16
+ | `palace.analytic` | Closed-form transmission-line formulas (CPW impedance, effective index, …). |
17
+ | `palace.s_plot` | Quick matplotlib plots of Palace S-parameter CSV files. |
18
+ | `palace.view_mesh` | Interactive PyVista viewer with per-group colouring. |
19
+ | `palace.viz` | Headless-safe visualisation helpers that export standalone HTML for docs and notebooks. |
20
+
21
+ ## Installation
22
+
23
+ ### Prerequisites
24
+
25
+ * Python ≥ 3.8
26
+ * [Gmsh](https://gmsh.info/) (the `gmsh` Python package is pulled automatically)
27
+
28
+ ### Install the package
29
+
30
+ ```bash
31
+ # Clone the repository
32
+ git clone https://github.com/EpsilonForge/PalaceToolkit.git
33
+ cd PalaceToolkit
34
+
35
+ # Create a virtual environment and install in editable mode
36
+ python -m venv .venv
37
+ source .venv/bin/activate
38
+
39
+ # Local clone install (recommended for now)
40
+ ./tools/install_local_editable.sh
41
+
42
+ # Or equivalent manual commands:
43
+ pip install https://github.com/EpsilonForge/PalaceToolkit/releases/download/palace-cpu-vX.Y.Z/<wheel-file>.whl
44
+ pip install -e ".[plot,docs]"
45
+ ```
46
+
47
+ Default runtime is binary-first when `palacetoolkit-palace-cpu` is installed.
48
+
49
+ By default, `./tools/install_local_editable.sh` fetches the latest
50
+ GA-built binary wheel from GitHub Releases.
51
+
52
+ To force local binary package mode from the checkout:
53
+
54
+ ```bash
55
+ PALACETOOLKIT_BINARY_SOURCE=local ./tools/install_local_editable.sh
56
+ ```
57
+
58
+ ### Compatibility Policy
59
+
60
+ - Stable releases of `palace-toolkit` are validated against a matching stable release of `palacetoolkit-palace-cpu`.
61
+ - The default local clone path (`./tools/install_local_editable.sh`) installs both packages from the same repository checkout and is the reference development workflow.
62
+ - Nightly Palace builds are supported for power users through opt-in source builds and are treated as best-effort (no stability guarantee across commits).
63
+ - If API/runtime behavior differs between stable and nightly Palace, `palace-toolkit` stable behavior is defined by the stable `palacetoolkit-palace-cpu` line.
64
+
65
+ See `docs/getting-started/compatibility-policy.md` for the full policy and release cadence.
66
+
67
+ ### Release Tags and CI Publishing
68
+
69
+ - `palace-cpu-vX.Y.Z` triggers binary build/publish workflow for `palacetoolkit-palace-cpu`.
70
+ - `vX.Y.Z` triggers main package build/publish workflow for `palace-toolkit`.
71
+ - Both workflows also support manual dispatch from GitHub Actions.
72
+
73
+ ### Optional: Power-user source build (nightly/custom)
74
+
75
+ Source builds are opt-in and disabled by default.
76
+ Use this only if you need custom flags (CUDA/HIP/MAGMA/etc.) or nightly Palace.
77
+
78
+ ```bash
79
+ PALACETOOLKIT_BUILD_PALACE=1 \
80
+ PALACETOOLKIT_CLONE_NIGHTLY=1 \
81
+ PALACETOOLKIT_PALACE_WITH_CUDA=0 \
82
+ PALACETOOLKIT_PALACE_WITH_HIP=0 \
83
+ PALACETOOLKIT_PALACE_WITH_MAGMA=0 \
84
+ pip install -e .
85
+ ```
86
+
87
+ Source builds are cached at:
88
+
89
+ `~/.cache/palacetoolkit/palace/<source-key>-<platform>-<options-hash>/build/bin/palace`
90
+
91
+ On subsequent installs, the cached build is reused automatically. Useful controls:
92
+
93
+ ```bash
94
+ # Force rebuild even when cache exists
95
+ PALACETOOLKIT_FORCE_PALACE_REBUILD=1 pip install -e .
96
+
97
+ # Use a local Palace source tree instead of cloning nightly
98
+ PALACETOOLKIT_PALACE_SOURCE=/path/to/palace PALACETOOLKIT_BUILD_PALACE=1 pip install -e .
99
+
100
+ # Override parallel build jobs
101
+ PALACETOOLKIT_PALACE_JOBS=8 pip install -e .
102
+
103
+ # Extra custom CMake args
104
+ PALACETOOLKIT_PALACE_EXTRA_CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release" pip install -e .
105
+ ```
106
+
107
+ The core install pulls `gmsh`, `numpy`, `meshio`, `pyvista`,
108
+ `enlighten`, and `pint`. The optional dependency groups are:
109
+
110
+ | Group | Extra packages |
111
+ |-------|---------------|
112
+ | `plot` | `pandas`, `matplotlib` |
113
+ | `docs` | `mkdocs`, `mkdocs-material`, `pyvista[jupyter]`, `nbconvert`, `ipykernel`, `papermill`, `nb-clean`, `pre-commit` |
114
+
115
+ ## Quick start
116
+
117
+ Below is a minimal example that creates a coaxial geometry, meshes it, runs
118
+ Palace, and plots the S-parameters:
119
+
120
+ ```python
121
+ import palacetoolkit as ptk
122
+ from ptk.mesh import Entity, run_meshing_pipeline
123
+ from ptk.simulation import run_palace
124
+ from ptk.s_plot import plot_s_params
125
+
126
+ # 1. Define entities with names, priorities and materials
127
+ inner = Entity(name="inner_conductor", ...)
128
+ dielectric = Entity(name="dielectric", ...)
129
+ outer = Entity(name="outer_conductor", ...)
130
+
131
+ # 2. Run the boolean pipeline — cuts, fragments, and meshes
132
+ run_meshing_pipeline([inner, dielectric, outer], output="coax.msh")
133
+
134
+ # 3. Run Palace with a JSON config
135
+ run_palace("coax.json", np=4)
136
+
137
+ # 4. Plot results
138
+ plot_s_params("postpro/coax")
139
+ ```
140
+
141
+ See `docs/examples/` notebooks for worked examples covering waveguides,
142
+ dipole antennas, horn antennas, and planar microwave circuits.
143
+
144
+ ## Building the docs
145
+
146
+ The documentation site is built with
147
+ [MkDocs Material](https://squidfunk.github.io/mkdocs-material/).
148
+ A [justfile](https://github.com/casey/just) automates the full pipeline.
149
+
150
+ ```bash
151
+ # Install docs dependencies (if not already)
152
+ pip install -e ".[docs]"
153
+
154
+ # Register the virtualenv as a Jupyter kernel
155
+ just ipykernel
156
+
157
+ # Full build: execute notebooks → build site
158
+ just docs-full
159
+
160
+ # Run documentation doctests (executes notebooks and fails on errors)
161
+ just doctest
162
+
163
+ # Or run each step individually:
164
+ just nbrun # execute docs example notebooks with papermill
165
+ just nbdocs # no-op (MkDocs renders .ipynb directly)
166
+ just docs # build the MkDocs static site
167
+
168
+ # Serve locally for development
169
+ just serve # starts a dev server on http://localhost:8080
170
+ ```
171
+
172
+ ### Other useful recipes
173
+
174
+ | Recipe | Description |
175
+ |--------|-------------|
176
+ | `just nbclean` | Strip cell outputs from docs example notebooks for clean commits. |
@@ -0,0 +1,48 @@
1
+ [project]
2
+ name = "palace-toolkit"
3
+ version = "0.1.0"
4
+ description = "PALACE electromagnetic simulation course materials"
5
+ requires-python = ">=3.8"
6
+ dependencies = [
7
+ "gmsh",
8
+ "numpy",
9
+ "scipy",
10
+ "meshio",
11
+ "mfem",
12
+ "pyvista",
13
+ "enlighten",
14
+ "pint",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ plot = ["pandas", "matplotlib"]
19
+ palace-cpu = ["palacetoolkit-palace-cpu>=0.1.0"]
20
+ docs = [
21
+ "mkdocs",
22
+ "mkdocs-material",
23
+ "mkdocs-jupyter",
24
+ "pyvista[jupyter]",
25
+ "nbconvert",
26
+ "ipykernel",
27
+ "papermill",
28
+ "pytest",
29
+ "nb-clean",
30
+ "pre-commit",
31
+ ]
32
+
33
+ [tool.pytest.ini_options]
34
+ testpaths = ["tests"]
35
+ markers = [
36
+ "docs: execute documentation notebooks",
37
+ ]
38
+
39
+ [tool.setuptools]
40
+ package-dir = {"" = "src"}
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["src"]
44
+ include = ["palacetoolkit", "palacetoolkit.*"]
45
+
46
+ [build-system]
47
+ requires = ["setuptools>=45", "wheel"]
48
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ from setuptools import setup
7
+ from setuptools.command.build_py import build_py as _build_py
8
+
9
+
10
+ def _wants_palace_build() -> bool:
11
+ raw = os.environ.get("PALACETOOLKIT_BUILD_PALACE", "0").strip().lower()
12
+ return raw not in {"0", "false", "no", "off"}
13
+
14
+
15
+ def _run_palace_build() -> None:
16
+ tools_dir = Path(__file__).parent / "tools"
17
+ script = tools_dir / "build_palace.py"
18
+ ns: dict[str, object] = {}
19
+ with script.open("r", encoding="utf-8") as f:
20
+ code = compile(f.read(), str(script), "exec")
21
+ exec(code, ns)
22
+ ns["ensure_palace_cached_build"]()
23
+
24
+
25
+ class build_py(_build_py):
26
+ def run(self) -> None:
27
+ if _wants_palace_build():
28
+ self.announce("PalaceToolkit: opt-in Palace source build requested", level=3)
29
+ _run_palace_build()
30
+ else:
31
+ self.announce("PalaceToolkit: skipping Palace source build (default)", level=3)
32
+ super().run()
33
+
34
+
35
+ cmdclass: dict[str, type] = {"build_py": build_py}
36
+
37
+ try:
38
+ from setuptools.command.editable_wheel import editable_wheel as _editable_wheel
39
+
40
+ class editable_wheel(_editable_wheel):
41
+ def run(self) -> None:
42
+ if _wants_palace_build():
43
+ self.announce("PalaceToolkit: opt-in Palace source build requested", level=3)
44
+ _run_palace_build()
45
+ else:
46
+ self.announce("PalaceToolkit: skipping Palace source build (default)", level=3)
47
+ super().run()
48
+
49
+ cmdclass["editable_wheel"] = editable_wheel
50
+ except Exception:
51
+ # Older setuptools versions may not expose editable_wheel.
52
+ pass
53
+
54
+ setup(cmdclass=cmdclass)
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: palace-toolkit
3
+ Version: 0.1.0
4
+ Summary: PALACE electromagnetic simulation course materials
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: gmsh
7
+ Requires-Dist: numpy
8
+ Requires-Dist: scipy
9
+ Requires-Dist: meshio
10
+ Requires-Dist: mfem
11
+ Requires-Dist: pyvista
12
+ Requires-Dist: enlighten
13
+ Requires-Dist: pint
14
+ Provides-Extra: plot
15
+ Requires-Dist: pandas; extra == "plot"
16
+ Requires-Dist: matplotlib; extra == "plot"
17
+ Provides-Extra: palace-cpu
18
+ Requires-Dist: palacetoolkit-palace-cpu>=0.1.0; extra == "palace-cpu"
19
+ Provides-Extra: docs
20
+ Requires-Dist: mkdocs; extra == "docs"
21
+ Requires-Dist: mkdocs-material; extra == "docs"
22
+ Requires-Dist: mkdocs-jupyter; extra == "docs"
23
+ Requires-Dist: pyvista[jupyter]; extra == "docs"
24
+ Requires-Dist: nbconvert; extra == "docs"
25
+ Requires-Dist: ipykernel; extra == "docs"
26
+ Requires-Dist: papermill; extra == "docs"
27
+ Requires-Dist: pytest; extra == "docs"
28
+ Requires-Dist: nb-clean; extra == "docs"
29
+ Requires-Dist: pre-commit; extra == "docs"
@@ -0,0 +1,23 @@
1
+ MANIFEST.in
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ src/palace_toolkit.egg-info/PKG-INFO
6
+ src/palace_toolkit.egg-info/SOURCES.txt
7
+ src/palace_toolkit.egg-info/dependency_links.txt
8
+ src/palace_toolkit.egg-info/requires.txt
9
+ src/palace_toolkit.egg-info/top_level.txt
10
+ src/palacetoolkit/__init__.py
11
+ src/palacetoolkit/analytic.py
12
+ src/palacetoolkit/geometry.py
13
+ src/palacetoolkit/mesh.py
14
+ src/palacetoolkit/mode_solver.py
15
+ src/palacetoolkit/palace_runtime.py
16
+ src/palacetoolkit/plot_farfield.py
17
+ src/palacetoolkit/s_plot.py
18
+ src/palacetoolkit/simulation.py
19
+ src/palacetoolkit/utils.py
20
+ src/palacetoolkit/verify_topology.py
21
+ src/palacetoolkit/viz.py
22
+ tests/test_docs_notebooks.py
23
+ tools/build_palace.py
@@ -0,0 +1,27 @@
1
+ gmsh
2
+ numpy
3
+ scipy
4
+ meshio
5
+ mfem
6
+ pyvista
7
+ enlighten
8
+ pint
9
+
10
+ [docs]
11
+ mkdocs
12
+ mkdocs-material
13
+ mkdocs-jupyter
14
+ pyvista[jupyter]
15
+ nbconvert
16
+ ipykernel
17
+ papermill
18
+ pytest
19
+ nb-clean
20
+ pre-commit
21
+
22
+ [palace-cpu]
23
+ palacetoolkit-palace-cpu>=0.1.0
24
+
25
+ [plot]
26
+ pandas
27
+ matplotlib
@@ -0,0 +1 @@
1
+ palacetoolkit
@@ -0,0 +1,31 @@
1
+ """PalaceToolkit — utilities for Palace electromagnetic simulations.
2
+
3
+ Import as::
4
+
5
+ from palacetoolkit.analytic import cpw_impedance
6
+ from palacetoolkit.mesh import Entity, run_meshing_pipeline
7
+ from palacetoolkit.simulation import run_palace, generate_palace_config
8
+ from palacetoolkit.viz import view_mesh
9
+ from palacetoolkit.verify_topology import analyse_mesh
10
+ """
11
+
12
+ from palacetoolkit.analytic import * # noqa: F401,F403
13
+ from palacetoolkit.mesh import * # noqa: F401,F403
14
+ from palacetoolkit.simulation import * # noqa: F401,F403
15
+ from palacetoolkit.verify_topology import * # noqa: F401,F403
16
+
17
+ # Optional submodules — silently skip when extra deps are missing
18
+ try:
19
+ from palacetoolkit.s_plot import * # noqa: F401,F403
20
+ except ImportError:
21
+ pass
22
+
23
+ try:
24
+ from palacetoolkit.viz import * # noqa: F401,F403
25
+ except ImportError:
26
+ pass
27
+
28
+ try:
29
+ from palacetoolkit.utils import * # noqa: F401,F403
30
+ except ImportError:
31
+ pass
@@ -0,0 +1,60 @@
1
+ """Analytic closed-form expressions for transmission line parameters."""
2
+
3
+ import numpy as np
4
+
5
+ def _K_over_Kp(k: float, kp: float) -> float:
6
+ """Approximate ratio K(k)/K(k') using the Hilberg formula."""
7
+ def _s(x: float) -> float:
8
+ return np.log(
9
+ 2 * (np.sqrt(1 + x) + (4 * x) ** 0.25)
10
+ / (np.sqrt(1 + x) - (4 * x) ** 0.25)
11
+ )
12
+
13
+ if k >= 1.0 / np.sqrt(2):
14
+ return _s(k) / (2 * np.pi)
15
+ else:
16
+ return 2 * np.pi / _s(kp)
17
+
18
+ def cpw_impedance(w: float, s: float, h: float, eps_r: float) -> float:
19
+ """Compute the characteristic impedance of a CPW line.
20
+
21
+ Uses the conformal mapping approach with elliptic integral ratios K(k)/K(k').
22
+
23
+ Args:
24
+ w: Centre conductor width.
25
+ s: Gap between centre conductor and ground.
26
+ h: Substrate thickness.
27
+ eps_r: Relative permittivity of the substrate.
28
+
29
+ Returns:
30
+ Z0: Characteristic impedance in Ohms.
31
+ """
32
+ k = w / (w + 2 * s)
33
+ k1 = np.sinh(np.pi * w / (4 * h)) / np.sinh(np.pi * (w + 2 * s) / (4 * h))
34
+
35
+ kp = np.sqrt(1 - k ** 2)
36
+ k1p = np.sqrt(1 - k1 ** 2)
37
+
38
+ kok = _K_over_Kp(k, kp)
39
+ k1ok = _K_over_Kp(k1, k1p)
40
+
41
+ eps_eff = 1 + ((eps_r - 1) / 2) * k1ok / kok
42
+ Z0 = 30 * np.pi / (kok * np.sqrt(eps_eff))
43
+ return Z0
44
+
45
+
46
+ def cpw_effective_index(w: float, s: float, h: float, eps_r: float) -> float:
47
+ """Return the effective refractive index of a CPW line.
48
+
49
+ Uses the same conformal-mapping model as :func:`cpw_impedance`.
50
+ """
51
+ k = w / (w + 2 * s)
52
+ k1 = np.sinh(np.pi * w / (4 * h)) / np.sinh(np.pi * (w + 2 * s) / (4 * h))
53
+
54
+ kp = np.sqrt(1 - k ** 2)
55
+ k1p = np.sqrt(1 - k1 ** 2)
56
+ kok = _K_over_Kp(k, kp)
57
+ k1ok = _K_over_Kp(k1, k1p)
58
+
59
+ eps_eff = 1 + ((eps_r - 1) / 2) * k1ok / kok
60
+ return np.sqrt(eps_eff)
@@ -0,0 +1,48 @@
1
+ import gmsh
2
+
3
+ def extract_tag(obj):
4
+ """
5
+ Extract the Gmsh tag from an object.
6
+
7
+ If object contains only one tag, return it as an integer, otherwise, preserve its
8
+ container.
9
+
10
+ Most gmsh functions return list of tuples like [(2, 5), (2, 8), (2, 10), ...], where the
11
+ first number is dimensionality and the second is the integer tag associated to that object.
12
+
13
+ Examples
14
+ --------
15
+ >>> extract_tag((3, 6))
16
+ 6
17
+
18
+ >>> entities = [(2, 5), (2, 8), (2, 10)]
19
+ >>> [extract_tag(e) for e in entities]
20
+ [5, 8, 10]
21
+ """
22
+
23
+ if isinstance(obj, list) and len(obj) == 1:
24
+ return extract_tag(obj[0])
25
+ elif isinstance(obj, tuple):
26
+ return obj[1]
27
+ else:
28
+ raise ValueError("Expected a tuple or single-element list")
29
+
30
+
31
+ # Convenience functions to extract extrema of the bounding box of an entity
32
+ def xmin(dimtag):
33
+ return gmsh.model.occ.getBoundingBox(dimtag[0], dimtag[1])[0]
34
+
35
+ def ymin(dimtag):
36
+ return gmsh.model.occ.getBoundingBox(dimtag[0], dimtag[1])[1]
37
+
38
+ def zmin(dimtag):
39
+ return gmsh.model.occ.getBoundingBox(dimtag[0], dimtag[1])[2]
40
+
41
+ def xmax(dimtag):
42
+ return gmsh.model.occ.getBoundingBox(dimtag[0], dimtag[1])[3]
43
+
44
+ def ymax(dimtag):
45
+ return gmsh.model.occ.getBoundingBox(dimtag[0], dimtag[1])[4]
46
+
47
+ def zmax(dimtag):
48
+ return gmsh.model.occ.getBoundingBox(dimtag[0], dimtag[1])[5]