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

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 * 

27 

28import os 

29import subprocess 

30 

31from ..log import ( 

32 debug, 

33 info, 

34 warning, 

35 error, 

36 rule, 

37 subproc, 

38) 

39from ..common.capacitance_matrix import CapacitanceMatrix 

40 

41 

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) 

50 

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) 

55 

56 info(f"Chdir to {work_dir}") 

57 os.chdir(work_dir) 

58 args = [ 

59 exe_path, 

60 f"-o{expansion_order}", 

61 ] 

62 

63 if partitioning_depth != 'auto': 

64 args += [ 

65 f"-d{partitioning_depth}", 

66 ] 

67 

68 args += [ 

69 f"-p{permittivity_factor}", 

70 f"-t{iterative_tolerance}", 

71 f"-l{lst_file_path}", 

72 ] 

73 

74 info(f"Calling FastCap2") 

75 subproc(f"{' '.join(args)}, output file: {log_path}") 

76 

77 rule('FastCap Output') 

78 start = time.time() 

79 

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() 

94 

95 duration = time.time() - start 

96 

97 rule() 

98 

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}") 

104 

105 

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() 

116 

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) 

124 

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 

142 

143 raise Exception(f"Could not extract capacitance matrix from FasterCap log file {log_path}")