Coverage for klayout_pex/fastercap/pex25d_exporter.py: 96%
53 statements
« prev ^ index » next coverage.py v7.17.2, created at 2026-11-06 13:14 +0000
« prev ^ index » next coverage.py v7.17.2, created at 2026-11-06 13:14 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024-2026 Martin Jan Köhler and Harald Pretl
4# Johannes Kepler University, Institute for Integrated Circuits.
5#
6# This file is part of KPEX
7# (see https://github.com/iic-jku/klayout-pex).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21# SPDX-License-Identifier: GPL-3.0-or-later
22# --------------------------------------------------------------------------------
23#
25"""Protocol implementations for PEX25D FasterCap and FastCap2 exports.
27Both solvers use the same input format. Exporter instances hold no per-scene
28state; each export builds its own model. Geometry dependencies load on export.
29The STL exporter builds its solids with :func:`build_fastercap_model` for now.
30"""
32from __future__ import annotations
34from dataclasses import dataclass, fields
35import os
36from typing import TYPE_CHECKING, Any, ClassVar, List, Mapping, Optional
38from ..plugin_api.v1 import ExportError, ExporterUnavailable, PEX25DSceneExporter
40if TYPE_CHECKING:
41 from klayout_pex_protobuf.kpex.pex25d.pex25d_scene_pb2 import PEX25DScene
43 from .fastercap_model_generator import FasterCapModelGenerator
46@dataclass
47class FasterCapModelOptions:
48 """Geometry and meshing settings of the FasterCap model."""
50 delaunay_amax: float = 0.0
51 """Maximum triangle area; 0 leaves it to the mesher."""
53 delaunay_b: float = 1.0
54 """Minimum mesh angle as b = 2·sin(angle); 1.0 is 30 degrees."""
56 field_margin_um: float = 8.0
57 """
58 How far the laterally unbounded materials — the simple bands, the films that
59 cover the field, the background, the ground plane — are drawn beyond the
60 geometry. Ignored when the scene carries a DOMAIN_BOX, which says it
61 outright.
62 """
64 geometry_check: bool = False
65 """Run the generator's own geometry validation before writing."""
67 def __post_init__(self):
68 for field in fields(self):
69 value = getattr(self, field.name)
70 if field.type == 'bool' and not isinstance(value, bool):
71 raise TypeError(f"'{field.name}' must be a bool")
72 if field.type == 'float' and (isinstance(value, bool)
73 or not isinstance(value, (int, float))):
74 raise TypeError(f"'{field.name}' must be an int or float (not bool)")
77@dataclass
78class FasterCapExporterOptions(FasterCapModelOptions):
79 """FasterCap and FastCap2 settings: the model's, and STL alongside the solver input."""
81 write_stl: bool = False
82 """Also dump the generated solids as STL, for looking at."""
85def build_fastercap_model(scene: PEX25DScene,
86 settings: FasterCapModelOptions,
87 exporter_name: str) -> FasterCapModelGenerator:
88 """Build the FasterCap model of ``scene``; KLayout loads here."""
89 try:
90 from .pex25d_model_builder import PEX25DFasterCapModelBuilder
91 except ImportError as exc:
92 raise ExporterUnavailable(
93 f"The {exporter_name} exporter needs KLayout and its geometry dependencies. "
94 f"Original error: {exc}") from exc
96 generator = PEX25DFasterCapModelBuilder(scene, settings).build()
97 if settings.geometry_check:
98 generator.check()
99 return generator
102class FasterCapSceneExporter(PEX25DSceneExporter):
103 """Write a FasterCap solver deck from a resolved PEX25D scene."""
105 name: ClassVar[str] = 'fastercap'
106 default_prefix: ClassVar[str] = 'FasterCap_Input_'
108 def export(self,
109 scene: PEX25DScene,
110 *,
111 output_dir_path: str,
112 prefix: str = '',
113 options: Optional[Mapping[str, Any]] = None) -> List[str]:
114 """Build and write solver inputs, returning the primary list file first."""
115 try:
116 settings = FasterCapExporterOptions(**(options or {}))
117 except TypeError as exc:
118 raise ExportError(f"Invalid options for '{self.name}': {exc}") from exc
120 generator = build_fastercap_model(scene, settings, self.name)
122 os.makedirs(output_dir_path, exist_ok=True)
123 effective_prefix = prefix or self.default_prefix
124 written = generator.write_fastcap(output_dir_path=output_dir_path,
125 prefix=effective_prefix)
126 if settings.write_stl:
127 written.extend(generator.dump_stl(output_dir_path=output_dir_path,
128 prefix=effective_prefix))
129 return written
132class FastCap2SceneExporter(FasterCapSceneExporter):
133 """Write the shared solver input format with FastCap2 defaults."""
135 name: ClassVar[str] = 'fastcap2'
136 default_prefix: ClassVar[str] = 'FastCap_Input_'