Coverage for klayout_pex/fastcap/fastcap_runner.py: 54%
63 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-17 17:24 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-17 17:24 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024 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/martinjankoehler/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 info(f"Chdir to {work_dir}")
57 os.chdir(work_dir)
58 args = [
59 exe_path,
60 f"-o{expansion_order}",
61 ]
63 if partitioning_depth != 'auto':
64 args += [
65 f"-d{partitioning_depth}",
66 ]
68 args += [
69 f"-p{permittivity_factor}",
70 f"-t{iterative_tolerance}",
71 f"-l{lst_file_path}",
72 ]
74 info(f"Calling FastCap2")
75 subproc(f"{' '.join(args)}, output file: {log_path}")
77 rule('FastCap Output')
78 start = time.time()
80 proc = subprocess.Popen(args,
81 stdin=subprocess.DEVNULL,
82 stdout=subprocess.PIPE,
83 stderr=subprocess.STDOUT,
84 universal_newlines=True,
85 text=True)
86 with open(log_path, 'w') as f:
87 while True:
88 line = proc.stdout.readline()
89 if not line:
90 break
91 subproc(line[:-1]) # remove newline
92 f.writelines([line])
93 proc.wait()
95 duration = time.time() - start
97 rule()
99 if proc.returncode == 0:
100 info(f"FastCap2 succeeded after {'%.4g' % duration}s")
101 else:
102 raise Exception(f"FastCap2 failed with status code {proc.returncode} after {'%.4g' % duration}s, "
103 f"see log file: {log_path}")
106# CAPACITANCE MATRIX, picofarads
107# 1 2 3 4
108# $1%GROUP2 1 7850 -7277 -2115 54.97
109# $1%GROUP2 2 -7277 3.778e+05 130.9 -3.682e+05
110# $2%GROUP3 3 -2115 130.9 6792 -5388
111# $2%GROUP3 4 54.97 -3.682e+05 -5388 3.753e+05
112def fastcap_parse_capacitance_matrix(log_path: str) -> CapacitanceMatrix:
113 with open(log_path, 'r') as f:
114 rlines = f.readlines()
115 rlines.reverse()
117 # multiple iterations possible, find the last matrix
118 for idx, line in enumerate(rlines):
119 if line.startswith('CAPACITANCE MATRIX, '):
120 section_m = re.match(r'CAPACITANCE MATRIX, (\w+)', line)
121 if not section_m:
122 raise Exception(f"Could not parse capacitor unit")
123 unit_str = section_m.group(1)
125 dimension_line = rlines[idx-1].strip()
126 dimensions = dimension_line.split() # remove whitespace
127 dim = len(dimensions)
128 conductor_names: List[str] = []
129 rows: List[List[float]] = []
130 for i in reversed(range(idx-1-dim, idx-1)):
131 line = rlines[i].strip()
132 cells = [cell.strip() for cell in line.split(' ')]
133 if cells[1] != str(i):
134 warning(f"Expected capacitor matrix row to have index {i}, but obtained {cells[1]}")
135 cells.pop(1)
136 cells = list(filter(lambda c: len(c) >= 1, cells))
137 conductor_names.append(cells[0])
138 row = [float(cell)/1e6 for cell in cells[1:]]
139 rows.append(row)
140 cm = CapacitanceMatrix(conductor_names=conductor_names, rows=rows)
141 return cm
143 raise Exception(f"Could not extract capacitance matrix from FasterCap log file {log_path}")