Coverage for klayout_pex / fastcap / fastcap_runner.py: 56%
61 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-02 17:12 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-02 17:12 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024-2025 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#
24import re
25import time
26from typing import *
28import os
29import subprocess
31from ..log import (
32 debug,
33 info,
34 warning,
35 error,
36 rule,
37 subproc,
38)
39from ..common.capacitance_matrix import CapacitanceMatrix
42def run_fastcap(exe_path: str,
43 lst_file_path: str,
44 log_path: str,
45 expansion_order: float = 2,
46 partitioning_depth: str = 'auto',
47 permittivity_factor: float = 1.0,
48 iterative_tolerance: float = 0.01):
49 work_dir = os.path.dirname(lst_file_path)
51 # we have to chdir into the directory containing the lst file,
52 # so make all paths absolute, and the lst_file relative
53 log_path = os.path.abspath(log_path)
54 lst_file_path = os.path.basename(lst_file_path)
56 args = [
57 exe_path,
58 f"-o{expansion_order}",
59 ]
61 if partitioning_depth != 'auto':
62 args += [
63 f"-d{partitioning_depth}",
64 ]
66 args += [
67 f"-p{permittivity_factor}",
68 f"-t{iterative_tolerance}",
69 f"-l{lst_file_path}",
70 ]
72 info(f"Calling FastCap2 in {work_dir}")
73 subproc(f"{' '.join(args)}, output file: {log_path}")
75 rule('FastCap Output')
76 start = time.time()
78 proc = subprocess.Popen(args,
79 cwd=work_dir,
80 stdin=subprocess.DEVNULL,
81 stdout=subprocess.PIPE,
82 stderr=subprocess.STDOUT,
83 universal_newlines=True,
84 text=True)
85 with open(log_path, 'w', encoding='utf-8') as f:
86 while True:
87 line = proc.stdout.readline()
88 if not line:
89 break
90 subproc(line[:-1]) # remove newline
91 f.writelines([line])
92 proc.wait()
94 duration = time.time() - start
96 rule()
98 if proc.returncode == 0:
99 info(f"FastCap2 succeeded after {'%.4g' % duration}s")
100 else:
101 raise Exception(f"FastCap2 failed with status code {proc.returncode} after {'%.4g' % duration}s, "
102 f"see log file: {log_path}")
105# CAPACITANCE MATRIX, picofarads
106# 1 2 3 4
107# $1%GROUP2 1 7850 -7277 -2115 54.97
108# $1%GROUP2 2 -7277 3.778e+05 130.9 -3.682e+05
109# $2%GROUP3 3 -2115 130.9 6792 -5388
110# $2%GROUP3 4 54.97 -3.682e+05 -5388 3.753e+05
111def fastcap_parse_capacitance_matrix(log_path: str) -> CapacitanceMatrix:
112 with open(log_path, 'r') as f:
113 rlines = f.readlines()
114 rlines.reverse()
116 # multiple iterations possible, find the last matrix
117 for idx, line in enumerate(rlines):
118 if line.startswith('CAPACITANCE MATRIX, '):
119 section_m = re.match(r'CAPACITANCE MATRIX, (\w+)', line)
120 if not section_m:
121 raise Exception(f"Could not parse capacitor unit")
122 unit_str = section_m.group(1)
124 dimension_line = rlines[idx-1].strip()
125 dimensions = dimension_line.split() # remove whitespace
126 dim = len(dimensions)
127 conductor_names: List[str] = []
128 rows: List[List[float]] = []
129 for i in reversed(range(idx-1-dim, idx-1)):
130 line = rlines[i].strip()
131 cells = [cell.strip() for cell in line.split(' ')]
132 if cells[1] != str(i):
133 warning(f"Expected capacitor matrix row to have index {i}, but obtained {cells[1]}")
134 cells.pop(1)
135 cells = list(filter(lambda c: len(c) >= 1, cells))
136 conductor_names.append(cells[0])
137 row = [float(cell)/1e6 for cell in cells[1:]]
138 rows.append(row)
139 cm = CapacitanceMatrix(conductor_names=conductor_names, rows=rows)
140 return cm
142 raise Exception(f"Could not extract capacitance matrix from FasterCap log file {log_path}")