From 0224324997426aad1fe4fd559481d6cbeb9c92a7 Mon Sep 17 00:00:00 2001 From: Lukas Chrostowski Date: Mon, 4 Nov 2024 15:40:54 -0800 Subject: [PATCH 1/6] replace update --- klayout_dot_config/python/SiEPIC/scripts.py | 85 +++++++--- .../tests/test_replace_cell/example_bb.gds | Bin 0 -> 19030 bytes .../tests/test_replace_cell/ip_library_bb.gds | Bin 0 -> 1176 bytes .../test_replace_cell/ip_library_bb2.gds | Bin 0 -> 1176 bytes .../tests/test_replace_cell/ip_library_wb.gds | Bin 0 -> 15180 bytes .../test_scripts_replace_cell.py | 160 ++++++++++++++++++ 6 files changed, 225 insertions(+), 20 deletions(-) create mode 100644 klayout_dot_config/python/SiEPIC/tests/test_replace_cell/example_bb.gds create mode 100644 klayout_dot_config/python/SiEPIC/tests/test_replace_cell/ip_library_bb.gds create mode 100644 klayout_dot_config/python/SiEPIC/tests/test_replace_cell/ip_library_bb2.gds create mode 100644 klayout_dot_config/python/SiEPIC/tests/test_replace_cell/ip_library_wb.gds create mode 100644 klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py diff --git a/klayout_dot_config/python/SiEPIC/scripts.py b/klayout_dot_config/python/SiEPIC/scripts.py index 63609255..a30a1c0a 100644 --- a/klayout_dot_config/python/SiEPIC/scripts.py +++ b/klayout_dot_config/python/SiEPIC/scripts.py @@ -3089,22 +3089,34 @@ def layout_diff(cell1, cell2, tol = 1, verbose=True): Based on https://github.com/atait/lytest ''' - if not cell1.layout() == cell2.layout(): - raise Exception ('SiEPIC.scripts.layout_diff is only implement for cells in the same layout.') + # Get a list of the layers + layers = [] + layout1 = cell1.layout() + layout2 = cell2.layout() + if layout1 == layout2: + # cells from the same layout + for li in layout1.layer_indices(): + layers.append ( (li,li) ) + else: + # cells from different layouts + #raise Exception ('SiEPIC.scripts.layout_diff is only implement for cells in the same layout.') + for ll1 in layout1.layer_indices(): + li1 = layout1.get_info(ll1) + ll2 = layout2.find_layer(layout1.get_info(ll1)) + if ll2 is None: + raise Exception( + f"Layer {li1} in cell1 is not present in cell2." + ) + + layers.append((ll1, ll2)) # Count the differences diff_count = 0 - # Get a list of the layers - layers = [] - layout = cell1.layout() - for li in layout.layer_indices(): - layers.append ( li ) - # Do geometry checks on each layer - for li in layers: - r1 = pya.Region(cell1.begin_shapes_rec(li)) - r2 = pya.Region(cell2.begin_shapes_rec(li)) + for li1,li2 in layers: + r1 = pya.Region(cell1.begin_shapes_rec(li1)) + r2 = pya.Region(cell2.begin_shapes_rec(li2)) rxor = r1 ^ r2 @@ -3115,22 +3127,31 @@ def layout_diff(cell1, cell2, tol = 1, verbose=True): diff_count += rxor.size() if verbose: print( - f" - SiEPIC.scripts.layout_diff: {rxor.size()} differences found in {cell1.name} on layer {layout.get_info(li)}." + f" - SiEPIC.scripts.layout_diff: {rxor.size()} differences found in {cell1.name} on layer {layout1.get_info(li1)}." ) + print(r1) + print(r2) + print(rxor) return diff_count -def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_library=None, Exact = True, RequiredCharacter = '$', run_layout_diff = True, debug = False): +def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_y_library=None, cell_ref_bb = None, Exact = True, RequiredCharacter = '$', run_layout_diff = True, debug = False): ''' SiEPIC-Tools: scripts.replace_cell Search and replace: cell_x with cell_y useful for blackbox IP cell replacement + - load layout containing cell_y_name from cell_y_file or cell_y_library - replace all cell_x_name* instances with cell_y - Exact = True: the cell name must match exactly = False: the cell_y_name appears at the beginning of the cells to be replaced and RequiredCharacter appears directly after, e.g,. '$' as KLayout appends during merging (exact match is still included) + run_layout_diff = True: + perform an xor with the black box cell in the layout, versus the original (reference) black box + requires cell_ref_bb + cell_ref_bb: the black box cell, which will be compared with the cell_x + check_bbox = True: make sure the bounding box for the two cells are the same Black box True geometry Basename_BB, Basename_BB* YES: Basename @@ -3144,6 +3165,18 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr log = '' log += "- cell replacement for: %s, with cell %s (%s or %s)\n" % (cell_x_name, cell_y_name, cell_y_file, cell_y_library) + # Find the cell name from the cell_ref_bb + if not cell_x_name: + if cell_ref_bb: + cell_x_name = cell_ref_bb.name + else: + raise Exception ('missing replacement cell name') + + # Make sure we can run the layout diff check. + if run_layout_diff: + if not cell_ref_bb: + raise Exception ('missing reference black box cell, required for layout diff check') + # Find the cells that need replacement (cell_x) # find cell name exactly matching cell_x_name cells_x = [layout.cell(cell_x_name)] @@ -3165,7 +3198,7 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr if debug: print(" - none found: %s" % cell_x_name) log += " - none found: %s" % cell_x_name - return + return log, None, False if Exact: if debug: @@ -3176,6 +3209,12 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr print(" - non-exact match: %s" % ([c.name for c in cells_x]) ) log += " - non-exact match: %s" % ([c.name for c in cells_x]) + # if you don't provide the cell name, get it from the file + if not cell_y_name: + layout1 = pya.Layout() + layout1.read(cell_y_file) + cell_y_name = layout1.top_cell().name + # Load the new cell: if cell_y_file: cell_y = layout.cell(cell_y_name) @@ -3200,6 +3239,10 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr if cells_x: log += " - replacing cells: %s\n" % ([c.name for c in cells_x]) + + # Perform replacement + count = 0 + error = False for cell_x in cells_x: if debug: print(" - replace_cell: found cells to be replaced: %s" % (cell_x.name)) @@ -3224,10 +3267,10 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr # Check if the BB cells are the same, by doing an XOR operation # from . import layout_diff if run_layout_diff: - if layout_diff(inst.cell, cell_x, tol=0): - if debug: - print(" - black box cells are different: %s vs %s" % (inst.cell.name, cell_x.name)) - raise Exception (" - black box cells are different: %s vs %s" % (inst.cell.name, cell_x.name)) + if layout_diff(cell_ref_bb, cell_x, tol=0, verbose=True): + print(" - ERROR: black box cells are different: %s vs %s" % (inst.cell.name, cell_x.name)) + error = True + # raise Exception (" - black box cells are different: %s vs %s" % (inst.cell.name, cell_x.name)) break; # replace with CELL_Y if inst.is_regular_array(): @@ -3235,13 +3278,15 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr print(" - checked, and replaced %s in %s, with cell array: %s" % (cell_x.name, cc.name, cell_y.name)) ci = inst.cell_inst cc.replace(inst, pya.CellInstArray(cell_y.cell_index(),inst.trans, ci.a, ci.b, ci.na, ci.nb)) + count += 1 else: if debug: print(" - replacing %s in %s, with cell: %s" % (cell_x.name, cc.name, cell_y.name)) cc.replace(inst, pya.CellInstArray(cell_y.cell_index(),inst.trans)) + count += 1 inst = next(itr, None) - return log + return log, count, error def svg_from_cell(verbose=True): @@ -3435,7 +3480,7 @@ def instantiate_all_library_cells(topcell, terminator_cells = None, terminator_l else: print('Error in: %s' % n) p.inc() - x, y = xmax, 0 + x, y = xmax, 0 # all the fixed cells for c in li.layout().each_top_cell(): diff --git a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/example_bb.gds b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/example_bb.gds new file mode 100644 index 0000000000000000000000000000000000000000..cb67c385244caf2d75ad6eb339df6d8875860943 GIT binary patch literal 19030 zcmeI4d0bRw_rMR=%ZNK$8fHmlsY%2xXdoiH(XlbUS@k) zwp!U{>)W1{z3rP;u8ATb;0C|%z2n&UnoHaJ`y=>#@XOq}&wb80&w0){&tXsuq9~K4 zpP$lslXMeRdP+YT;`_67RSX$vmO{HoXea-ak(Uh}{nmp`N$D%kT6+5wM*1IqWN(9E zz<>dx#*UeoF?nKs&X~-xycD&oNMNwx{K9IBwaR9;42Tiw9@t5|Uxp!ybdt~r!?;ly zd3nhP?i118Q6z?U2Gw{+=euj2mchvbC%9cUXZXa7@aXvXsBnipBD^L!K1%y(pgzt| zrfB1wh3?|P$y3c5hYdVSnh>eMhjE@^09h>0$V@zUXbmH*nsJO_)gb4k`U>+GA zZk&-E6>ib~aU~ZOmQ`A&>CYGG8DYrFNlV^uD>XJInod4+P2=J`YZ@OpG--HDJWJ~RDJCH@DRy{#jJ}}S&xcj> z@GII`j_C0x84MO9dnLa%ztC(eG#0vTrhMGcX>$yS7Bv7L?x9G!7XcP%}Pvq@HFTK|_itbO{4~*}vjW_i1lK?UF_2cL6C-|q{s(&r@>?U^`<6IIR zW*Aj&ca}RW4tMe)vf-nepZ=cbD*Z${ogu@*3?n1MM;8}X7gpNBE$+zh!m@~PbGa?D z++{s0Iw~?cIx6X`+zAuMXGF$CMJGkZBt;L6kBSceBQxrn#Dy91s_Yh*%~V)MFqxQf zE|FzS#;A$;Wj3R$&{dn9ZmG0c9pR-mcjZ~Ss-C&Jc&z59TfM6oPS$J}(2I!U*Ce6( z8bokKSOZn#v5jJ-25{?-Fi)p$$)nAsjcRY-bCNbnr?35ykaa~u)|Gz`tZSIQu6KCW z_3cD`TtJwi-06-z$ePDEUYFK0KIX9Fef`R3evhAGBhPqzTJs-|Uw7Kz@oPW)TBQ07 z?!8YWH=@10Jxk<_*R;B-k6C3wyzF5P_$RL7v_F*`QtL_0RBJ_#K=CKZnyG`*MmA zz6q`H6<7(MgSBu4oClwTi{RsMF?>wZqrJTikGqNMUTSvPZy()$osS$<`W-&1cx~Si zQ+{t0>xd~tm5!M5PmL*ohJ!2c|Ef)tGGe$N{voBOdsv}}U;9cCs3>W$hbSsBx#CPw z43|N(C;_?fDN(w-BjV~UN;l%HYq2QZiMy43;U@T;C_R>lxb33!B#u{Q!ncWs=ZMm4 zght7vG^%T^k3pNW`n)KvCCsVqCqb8~x!u zqHcN%rii*Foq668^_Tf@n5bK?K#n^_twZka8%5ow$=4GiU$@SpZXbmnZ^IYSD@WA& z{^)rrL~eB2~BjUDU=W(dQXa8?aw+kf=LiLcg zajVe#GuVzDW{J}KRP53KBSp6p)a}-LfXGEZaGg{*`#kO^pVjV#$Q=2$*-X1{_*P*I4sy4!6bX zq8`;woqtS^ruou4%wV#Vm5nL1TYQeYe@N2Cm*mng!vv?zWXX3Hx(e;Xkh09;u)2$r zqavfCqGA)`q7sLObG621=3>0%%&4K^&T=%VOg?4mRPQlRI}%Ten989zGHU;^_`pm3 zG_1YyIUG-$c1pO5Gf!2e{>TKX!ecmDq~d_w`Rl6gFiSt`e2>7$8mS38$Zj*a${m%1 zkL8(%dP+l+M1~m-?ieH=zF)9k@}pMrelt?pr7yATBXsKe9(~lU53pxKiK*A=^tF<2 z{odK9{y)S@%z9A$-TlA`=fL^lu;aCu&in_}{{aWaYv5CUGj(a#9cO zs~~3epjNN=p4gAET>arx$ooAXgv%kh#<3xLszvZM$bEs^$Yo`s1oeT$&Y%=xYB4mzI`}V<88z^3VlJ^e=w(Q52>P5{M@|UZ z3crQK%vlTc*e|nVVK}jR0~{hU2YrHx$rt6rWOCwZkeobs2h0$ehdqKbJ+U6@u|GIn z)O9_$PR?4l7M@LRcodRTesaMxME#i@6WkyE0t3k*=pRJR+K>l-fji-6=lsM(Xz-P|%cf^N*P$XeQX^n5Kdn|kUu6Vb&hyc~>3;YLyq$TkgVYZGW8qxn^M}~MfPI=K zBRBIlrJ_$)i2npU3Ol1OwN4{`+vO+tI`+W+jkjSZ#y6J3Yarvg_klg&t;qj5cJ2+C zx5w4+Le@itVXzXrt$}Od0@g7LQnU41#yb4Kx_5zG4>ZDo@Hy7+P5dSRPJ@$J?|OI# zycqrfyW^i)4a7PHUjTbU5Rw54U>KU!n5{zeG;pd_F*SZi$ zjj7e9P_MZ{iBI!h;dKK06#Iro!{gv!_A&8BtF`9*3sPgv#_pk=pg(dDcV>-+-x3$t zk0JCRYT4%@4@34($Q8tshhQnBW)5NB*Hplhh(Gvka3f@VFypGQYw)!Ye-EA_;(ie_ zuR8_1t`({LH@p*$gXf93*1;~&&AL$gyRd7ZNu**KK|*iD8s3ntkl=Z{LaUEmnbQ_V4Wzb)CtAoMHxx{ zFXkMQwo1e{Nt99Ka~pL@I_Js~>Z9~6^q#06Gpa?(E`^-a?C*$@c`o%xCR|EA(;0q2 z{nG`uP$yC6WhvBCU+Pphw{pFe`jY2lektlU>d4Hysav(@Oc1rBLQ%7gih`KY2 zc{w*V^PKcCqP7e~u3O-D$oYz>tuv8(rKr0M=rIFcgkHPhkLZ~s>h37?9ty44fqHiL zBHb?P?(00~gZ=auhkjjW(&^$aCdNGuNANuA>-ILDp8z?Bq;c+R4@Dm8*tY4&`-Q0c zsPD3hMBR%WvtMVPoucHdM~)&MbXAT^@Cqqbtvy&w8lLvzm1}g?Xv>ifzcj zdAjIpceWHk|0zK#*8>wGYIR_i5*Hdt+!cWlqF?fwANvFXw-7ezw zE9l#AIi_Bt)-N$_{onK&wYkhI{+?c=_H4`Xe^swhmvr3GdW}g#kJxMU_7Am>qG;{J zcsi7x52hS=<@s<5FTH(9?c*tbL9bDhzp&M}*O_p-;(Ruvl@qv z#Ai8UCWb&_XyOlW0(<*@NGuqp#Rdy|p0lpS2|s~zMLByeB-SJkg7?99;S0nF&We^# z;a<31lo3}EV>r{M_JNU*6)fV+IFcAs#94CWE$|*<4Nat?kKi0gEKS=)?ujOrkA=j@ zQBQbsQablpiCO7I#4vgv=`X@5#ICN8oRx7t+zgkJo8sYmIG@~cI{XTvPe}~q{gSKU zSFoNK*pu9KKExiSuR|v>ll!tbtCu|p-y)Y0JG1KGy~NHSV(TCn1D}GNg|*+;WS7Bj z-~wWBHN2gi*9Q_aD_(`K5~Il_*&mSuPljJZ^vNbByKjT^CaVe{Ik}oMbaoSLhs~Z? z5B1oeO^<5}u{c}n!D#Z-ksHb(Ii(K0v+1$akz=wyCWk%-Un7SU!pF&_y&%1w`jv1Y zIpzYW_2io1cyjEOkk7p|o(O}XrvEl_F7jmY`#P-`mbHXjh5lI;ke)(XF_uDp7TUWy%WxX$HTkv%f*m& zPT2?7LJQoBAHPSAJ`N6sL-6x?klZ_b4IBsi!+cSOX}P>al%xkBxjk_$oDD-EwF~=7 z-HYE*W2t)=dTI$T+h%&MYaB|it_^!7B4-==Ce}kI&qe>XV&=I7PG{axaJ(pI3rvI0 zLhO)?ecDbCC53t0I>8r3-M0z-ros0hwa&g}=zRlRfIYDPzG;y0`|$5HZ-s9|=1ot9{oym%=Udi?PnYhw5nc)1@)~De)FdbflKd|rF4{8hRl+zWzVV~w8S2O!D$Blm>SI)gK1FpvZ@P{1s zLDQ+!G$-Jnm%-uq?N)dJ%!Q2G!M@175SN!-yw8!M-pPJ$Djs9)%ymFnAB~i19g$ zD{Y6V5dX;`Rh6J;HuIL$!mFXcJmTIJ&>!|=9aa(_ABR@R`eg+ZPuJ_~CRWxda|*B7 zHyPCCMPI^eAlEZag~RXzK8Iy#gd^Y+_z7{*QUP;d0)F&1Sroa_kjwl5e)bGx-;X5z znq8u#UJqHH5xd}N@IvU1-@ZU*XaA>=hfIu1E`lpWIh*mOyYT;ZcopP#Fs6&x7Z*e3 zO(OoAtn8cZFdu5Q>lF4?2E>1~Gmba!K|MbQh)-7c={0)q>&TDz!BYH(ebSL1JMv>k zek7iC-5j7aU7%vl&ve*r`j3s3A&?)ET|0Nyz`! zC4A4M9+^lDLA_*4p(fy5RZ>XbiJnyH%XAW+W-PT#0q4weYMBD+1m{MPeCj9{XR`dk z)L+z0)2Yj9-0)42+I^6^Vm9AUUC(^e|1Ijq|3Iy-o5r{^sP~?O)QQ_TgB4Im*Hhya zyeDeI2h6*II&%SX(8FsEMJ~=oEl%X5er&xSxo1(=&Ov7C?cK4+YJ$`xcIw(aCn76# z^h4qfTu) z4V!V!YP=QtA@5$~84WYB@nhsIJ3Iw_sHH5+MBRQm%oMfmA@rbb{{{P*UlVondU!W> zA?KTK^4JOLb~AHrW;EAd#jaZ+bzv3uv1Cx^Vsp#G$ovTNoJ?I?3YR0h898#%owcF- z631tdbG9Q9xz}QwN6{l3#-SH$U-mKY@^a>Jri%9E`Anm}qRpAUea5($NKMeA zf2Q$>_GtP1Z|O72^6o?qyncDOPF>$Fz9$l-`tQG=!vBe*He%xHg*tt$}O(5aT+n=TS#oEAQo%!-Z=@f z=lOi16g!CnKSJVwgP2o%GcjU5e4M@i9efWChs5|&ENBaWDm;^1Q2}|Djq$d5e4o!Z z$h;9{o z;XT-j99cLA+YN@(;9GD6HZ6vXtLg>2!w;HB-Gp7Vyto0iTX;r+mQT#c#qX}R9tTf@uftSP>#$qlbWyi;gY@aPJp}3JY)^yl!_Of7oO;F^ z8CSm@P8M}XHM|&o$$FMFEiOMOAPG{2A`SFX$h&q(J(8Ev!NDShx~?0+GvxT&<_TpWt|W#|3BO zEB^34NSAciGy+Q1oWYA+>D=>(Wh-*hrSoXSFyuhcn3tz zX4VN;QJeFy(-p8kJOTUHV@LdJ=gTk$UW%PJLF{1VzMY{E`!wMXMd;szzD4wDn|Qxy zB*bqm_*vug@M*{-7VO`697LZ6_JdZ}G~~lqMBUK_FNfI6j6HTlz(M#I_nG#wPn}z5y{YfzogxJ-59zJqE)O-cMHS?_UPhksmVfPrQ*}npx!LF94pcV4I<9L1D#Qr|K z3Lm-zehZUX=V*KgS*^@fhCbGfkoW0HN?8tk9AZCPUwjOE*toBhXWQ_-(gpa}On4z= z&z8pXjqOW=7}J$&_*oggUb2R{V&SuJHM|p<%HbkpMz#_+dzp6 z{MeBnJM!Z{Fh6!a@@EhLA8GWbKGDW$V97yMfk_Vh4>g`iW8Tr}KZxHVJGzUPvj4h9 KT`H`ms`7tt%nl|1 literal 0 HcmV?d00001 diff --git a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/ip_library_bb.gds b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/ip_library_bb.gds new file mode 100644 index 0000000000000000000000000000000000000000..29e4074885e61b5a6b1bbc85f1ddf0ee2903c97e GIT binary patch literal 1176 zcmbW$&ui0Q7zgktN&BwOx=k2!)gn=LkWtg5T4$9GXIs>5RIMFIVU%|GO%BOvsZ{Fv5@|GBh;$;d7 zV$XZpNgnE>AW7YysaIsF(}rFq8kD@_6F0`DUfll>OFj+Xxt+^W(7Saq$e2+;GT+MU z^QK|Sf5C}#14o=^Kty1-;KIG;|Z64`voEY$USE1onOX0ak)HLXUNx7s~*Y2BT=fT);)xfOLARci-Q7$x084X!_EA`@umoZ-aJek_y_38i-?FP5kXK{{N~N76CF+q<ucFO30vQGvO2>Af%6S_Kmj`Jjr|Itas~(K)jujG3`E6DdmZQV5%2n*np-#E*;;q~Wq;<` zKkvo|hZVpi3TdF8@rG@pVkKAdQuxm>Vt{6uKv%CqLag6W#UTN ztXtE@Y~f5Mm(Gry&1BLexx%bftQ+-aYSe64rHXplvKukc>c*-2-g;A)ICWrdg%iIG zj71&CIR*4zZOOUbao0WPR@m)nU}@o}(tGA#@?Syoi@a`#@^_@@JUvdElut6>pb1){ z2WgQWpi^`|ouu6~OGES=_ME&?)+y7U@%3qEBdpKB7(f kkj~Qwbb;Qdi}aq`hvTgAEG==pr8}4P`H0ie&J*ItcM5725C8xG literal 0 HcmV?d00001 diff --git a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/ip_library_wb.gds b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/ip_library_wb.gds new file mode 100644 index 0000000000000000000000000000000000000000..46b7ec5a2faf9964773545db3d72682fa7eccaf8 GIT binary patch literal 15180 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 literal 0 HcmV?d00001 diff --git a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py new file mode 100644 index 00000000..22b1d924 --- /dev/null +++ b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py @@ -0,0 +1,160 @@ +""" +Test for SiEPIC.scripts.replace_cell + +by Lukas Chrostowski 2024 + +""" + +def test_replace_cell(): + ''' + + ''' + + import pya + + import SiEPIC + from SiEPIC._globals import Python_Env + from SiEPIC.utils.layout import new_layout + + import os + + if Python_Env == 'Script': + # For external Python mode, when installed using pip install siepic_ebeam_pdk + import GSiP + + tech_name = 'GSiP' + + from packaging import version + if version.parse(SiEPIC.__version__) < version.parse("0.5.4"): + raise Exception("Errors", "This example requires SiEPIC-Tools version 0.5.4 or greater.") + + + from SiEPIC.scripts import replace_cell, delete_extra_topcells + + # The circuit layout that contains BB cells + path = os.path.dirname(os.path.realpath(__file__)) + file_in = os.path.join(path,'example_bb.gds') + layout = pya.Layout() + layout.read(file_in) + top_cell = layout.top_cell() + + # the individual BB reference cell + file_bb = os.path.join(path,'ip_library_bb.gds') + ly_bb = pya.Layout() + ly_bb.read(file_bb) + cell_bb = ly_bb.top_cell() + + # the individual WB cell + file_wb = os.path.join(path,'ip_library_wb.gds') + ''' + ly_wb = pya.Layout() + ly_wb.read(file_wb) + cell_wb = ly_wb.top_cell() + ''' + def check_bb_geometries(layout): + ''' + check if there are any Black Box layers in the layout + ''' + layer_bb = layout.layer(pya.LayerInfo(998,0)) # hard coded for the GSiP PDK + r1 = pya.Region(top_cell.begin_shapes_rec(layer_bb)) + diff_count = 0 + if not r1.is_empty(): + diff_count = r1.size() + print( + f" - SiEPIC.scripts.layout_diff: {r1.size()} Black Box geometry(ies) found in {top_cell.name} on layer {layout.get_info(layer_bb)}." + ) + return diff_count + + + # Check -- exact replacement (without $) + if 1: + text_out, count, error = replace_cell(layout, + cell_ref_bb = cell_bb, + cell_y_file = file_wb, + Exact = True, + run_layout_diff = False, + debug = True, + ) + print('replaced %s' %count) + assert count == 1 + assert check_bb_geometries(layout) == 1 + + file_out = os.path.join(path,'example_replaced.gds') + delete_extra_topcells(layout, top_cell.name) + layout.write(file_out) + try: + # Display the layout in KLayout, using KLayout Package "klive", which needs to be installed in the KLayout Application + if Python_Env == 'Script': + from SiEPIC.utils import klive + klive.show(file_out, technology='EBeam') + except: + pass + os.remove(file_out) + + + # Check -- non-exact replacement (with $) + if 1: + layout = pya.Layout() + layout.read(file_in) + top_cell = layout.top_cell() + text_out, count, error = replace_cell(layout, + cell_ref_bb = cell_bb, + cell_y_file = file_wb, + Exact = False, RequiredCharacter='$', + run_layout_diff = False, + debug = True, + ) + print('replaced %s' %count) + assert count == 2 + assert check_bb_geometries(layout) == 0 + + file_out = os.path.join(path,'example_replaced2.gds') + delete_extra_topcells(layout, top_cell.name) + layout.write(file_out) + try: + # Display the layout in KLayout, using KLayout Package "klive", which needs to be installed in the KLayout Application + if Python_Env == 'Script': + from SiEPIC.utils import klive + klive.show(file_out, technology='EBeam') + except: + pass + os.remove(file_out) + + # Check -- Run BB reference vs. design layout difference, non-exact replacement (with $) + if 1: + layout = pya.Layout() + layout.read(file_in) + top_cell = layout.top_cell() + text_out, count, error = replace_cell(layout, + cell_ref_bb = cell_bb, + cell_y_file = file_wb, + Exact = False, RequiredCharacter='$', + run_layout_diff = True, + debug = True, + ) + print('replaced %s' %count) + assert count == 2 + assert check_bb_geometries(layout) == 0 + assert error == False + + # Check -- Run BB reference (changed) vs. design layout difference, non-exact replacement (with $) + if 1: + layout = pya.Layout() + layout.read(file_in) + top_cell = layout.top_cell() + # the (changed) BB reference cell + file_bb = os.path.join(path,'ip_library_bb2.gds') + ly_bb = pya.Layout() + ly_bb.read(file_bb) + cell_bb = ly_bb.top_cell() + text_out, count, error = replace_cell(layout, + cell_ref_bb = cell_bb, + cell_y_file = file_wb, + Exact = False, RequiredCharacter='$', + run_layout_diff = True, + debug = True, + ) + assert error == True + +if __name__ == "__main__": + test_replace_cell() From 4b4f1059eea3080dc5bbc9f389f9a5f68d3ff4ae Mon Sep 17 00:00:00 2001 From: Lukas Chrostowski Date: Mon, 4 Nov 2024 15:52:26 -0800 Subject: [PATCH 2/6] Update test_scripts_replace_cell.py --- .../SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py index 22b1d924..b5343aa4 100644 --- a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py +++ b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py @@ -126,9 +126,9 @@ def check_bb_geometries(layout): layout.read(file_in) top_cell = layout.top_cell() text_out, count, error = replace_cell(layout, - cell_ref_bb = cell_bb, cell_y_file = file_wb, Exact = False, RequiredCharacter='$', + cell_ref_bb = cell_bb, run_layout_diff = True, debug = True, ) From 49cf9b815afa5238a0bcfcccb20856e2c208bd25 Mon Sep 17 00:00:00 2001 From: Lukas Chrostowski Date: Mon, 4 Nov 2024 19:54:15 -0800 Subject: [PATCH 3/6] new function: cells_containing_bb_layers return a list of cell names that contain black box layer polygons --- klayout_dot_config/python/SiEPIC/scripts.py | 44 ++++++++++++++++++- .../test_scripts_replace_cell.py | 25 +++-------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/klayout_dot_config/python/SiEPIC/scripts.py b/klayout_dot_config/python/SiEPIC/scripts.py index a30a1c0a..983328e2 100644 --- a/klayout_dot_config/python/SiEPIC/scripts.py +++ b/klayout_dot_config/python/SiEPIC/scripts.py @@ -3075,6 +3075,48 @@ def button(self): wdg.show() +def check_bb_geometries(topcell, BB_layerinfo=pya.LayerInfo(998,0), verbose=True): + ''' + Check if there are any Black Box layers in the layout. + Returns: + Count: number of different shapes + Args: + topcell: pya.Cell + BB_layerinfo: pya.LayerInfo + ''' + layout = topcell.layout() + layer_bb = layout.layer(BB_layerinfo) # hard coded for the GSiP PDK + r1 = pya.Region(topcell.begin_shapes_rec(layer_bb)) + diff_count = 0 + if not r1.is_empty(): + diff_count = r1.size() + if verbose: + print( + f" - SiEPIC.scripts.check_bb_geometries: {r1.size()} Black Box geometry(ies) found in {topcell.name} on layer {layout.get_info(layer_bb)}." + ) + else: + if verbose: + print('Black box replacement -- success -- no black box layers remaining.') + return diff_count + +def cells_containing_bb_layers(topcell, BB_layerinfo=pya.LayerInfo(998,0), verbose=True): + ''' + return a list of cell names that contain black box polygons + Args: + topcell: pya.Cell + BB_layerinfo: pya.LayerInfo + ''' + layout = topcell.layout() + iter1 = pya.RecursiveShapeIterator(layout, topcell, layout.layer(BB_layerinfo) ) + cells = [] + while not iter1.at_end(): + cells.append (iter1.cell().name) + if verbose: + print(" - %s" % iter1.cell().name) + iter1.next() + # return unique cell names + return sorted(list(set(cells))) + def layout_diff(cell1, cell2, tol = 1, verbose=True): ''' Check two cells to make sure they are identical, within a tolerance. @@ -3135,7 +3177,7 @@ def layout_diff(cell1, cell2, tol = 1, verbose=True): return diff_count -def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_y_library=None, cell_ref_bb = None, Exact = True, RequiredCharacter = '$', run_layout_diff = True, debug = False): +def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_y_library=None, cell_ref_bb = None, Exact = True, RequiredCharacter = '$', run_layout_diff = False, debug = False): ''' SiEPIC-Tools: scripts.replace_cell Search and replace: cell_x with cell_y diff --git a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py index b5343aa4..a29cc220 100644 --- a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py +++ b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py @@ -29,7 +29,7 @@ def test_replace_cell(): raise Exception("Errors", "This example requires SiEPIC-Tools version 0.5.4 or greater.") - from SiEPIC.scripts import replace_cell, delete_extra_topcells + from SiEPIC.scripts import replace_cell, delete_extra_topcells, cells_containing_bb_layers, check_bb_geometries # The circuit layout that contains BB cells path = os.path.dirname(os.path.realpath(__file__)) @@ -51,20 +51,6 @@ def test_replace_cell(): ly_wb.read(file_wb) cell_wb = ly_wb.top_cell() ''' - def check_bb_geometries(layout): - ''' - check if there are any Black Box layers in the layout - ''' - layer_bb = layout.layer(pya.LayerInfo(998,0)) # hard coded for the GSiP PDK - r1 = pya.Region(top_cell.begin_shapes_rec(layer_bb)) - diff_count = 0 - if not r1.is_empty(): - diff_count = r1.size() - print( - f" - SiEPIC.scripts.layout_diff: {r1.size()} Black Box geometry(ies) found in {top_cell.name} on layer {layout.get_info(layer_bb)}." - ) - return diff_count - # Check -- exact replacement (without $) if 1: @@ -77,7 +63,8 @@ def check_bb_geometries(layout): ) print('replaced %s' %count) assert count == 1 - assert check_bb_geometries(layout) == 1 + assert check_bb_geometries(top_cell) == 1 + assert cells_containing_bb_layers(top_cell) == ['ebeam_y_adiabatic_500pin$1'] file_out = os.path.join(path,'example_replaced.gds') delete_extra_topcells(layout, top_cell.name) @@ -106,7 +93,8 @@ def check_bb_geometries(layout): ) print('replaced %s' %count) assert count == 2 - assert check_bb_geometries(layout) == 0 + assert check_bb_geometries(top_cell) == 0 + assert cells_containing_bb_layers(top_cell) == [] file_out = os.path.join(path,'example_replaced2.gds') delete_extra_topcells(layout, top_cell.name) @@ -134,8 +122,9 @@ def check_bb_geometries(layout): ) print('replaced %s' %count) assert count == 2 - assert check_bb_geometries(layout) == 0 + assert check_bb_geometries(top_cell) == 0 assert error == False + assert cells_containing_bb_layers(top_cell) == [] # Check -- Run BB reference (changed) vs. design layout difference, non-exact replacement (with $) if 1: From 24286bb0f0ae92032344edc748bc44cbc343ac4e Mon Sep 17 00:00:00 2001 From: Lukas Chrostowski Date: Mon, 4 Nov 2024 20:07:13 -0800 Subject: [PATCH 4/6] unit tests: optionally display using klive --- .../test_scripts_replace_cell.py | 14 +++++-------- .../tech/GSiP/pymacros/tests/test_FaML.py | 11 +++++----- .../GSiP/pymacros/tests/test_SiEPIC_utils.py | 11 +++++----- .../tech/GSiP/pymacros/tests/test_bezier.py | 20 ++++++++++--------- .../GSiP/pymacros/tests/test_coupler_array.py | 11 +++++----- .../pymacros/tests/test_example_circuit.py | 11 +++++----- .../tests/test_instantiate_all_cells.py | 7 ++++--- 7 files changed, 44 insertions(+), 41 deletions(-) diff --git a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py index a29cc220..bd857da3 100644 --- a/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py +++ b/klayout_dot_config/python/SiEPIC/tests/test_replace_cell/test_scripts_replace_cell.py @@ -5,9 +5,9 @@ """ -def test_replace_cell(): +def test_replace_cell(show_klive=False): ''' - + show_klive: True to open the layout in KLayout using KLive ''' import pya @@ -69,13 +69,11 @@ def test_replace_cell(): file_out = os.path.join(path,'example_replaced.gds') delete_extra_topcells(layout, top_cell.name) layout.write(file_out) - try: + if show_klive: # Display the layout in KLayout, using KLayout Package "klive", which needs to be installed in the KLayout Application if Python_Env == 'Script': from SiEPIC.utils import klive klive.show(file_out, technology='EBeam') - except: - pass os.remove(file_out) @@ -99,13 +97,11 @@ def test_replace_cell(): file_out = os.path.join(path,'example_replaced2.gds') delete_extra_topcells(layout, top_cell.name) layout.write(file_out) - try: + if show_klive: # Display the layout in KLayout, using KLayout Package "klive", which needs to be installed in the KLayout Application if Python_Env == 'Script': from SiEPIC.utils import klive klive.show(file_out, technology='EBeam') - except: - pass os.remove(file_out) # Check -- Run BB reference vs. design layout difference, non-exact replacement (with $) @@ -146,4 +142,4 @@ def test_replace_cell(): assert error == True if __name__ == "__main__": - test_replace_cell() + test_replace_cell(show_klive=True) diff --git a/klayout_dot_config/tech/GSiP/pymacros/tests/test_FaML.py b/klayout_dot_config/tech/GSiP/pymacros/tests/test_FaML.py index addeaeb6..ee34005c 100644 --- a/klayout_dot_config/tech/GSiP/pymacros/tests/test_FaML.py +++ b/klayout_dot_config/tech/GSiP/pymacros/tests/test_FaML.py @@ -5,7 +5,7 @@ """ -def test_FaML_two(): +def test_FaML_two(show_klive=False): ''' --- Simple MZI, tested using Facet-Attached Micro Lenses (FaML) --- @@ -102,13 +102,14 @@ def test_FaML_two(): print('Number of errors: %s' % num_errors) # Display the layout in KLayout, using KLayout Package "klive", which needs to be installed in the KLayout Application - if Python_Env == 'Script': - from SiEPIC.utils import klive - klive.show(file_out, lyrdb_filename=file_lyrdb, technology=tech_name) + if show_klive: + if Python_Env == 'Script': + from SiEPIC.utils import klive + klive.show(file_out, lyrdb_filename=file_lyrdb, technology=tech_name) os.remove(file_out) if num_errors > 0: raise Exception ('Errors found in test_FaML_two') if __name__ == "__main__": - test_FaML_two() + test_FaML_two(show_klive=True) diff --git a/klayout_dot_config/tech/GSiP/pymacros/tests/test_SiEPIC_utils.py b/klayout_dot_config/tech/GSiP/pymacros/tests/test_SiEPIC_utils.py index 7a78c9c6..9d3a3976 100644 --- a/klayout_dot_config/tech/GSiP/pymacros/tests/test_SiEPIC_utils.py +++ b/klayout_dot_config/tech/GSiP/pymacros/tests/test_SiEPIC_utils.py @@ -82,7 +82,7 @@ def test_create_cell2(): topcell.insert(CellInstArray(cell_y.cell_index(), t)) -def test_waveguide_length(): +def test_waveguide_length(show_klive=False): import pya import SiEPIC from SiEPIC._globals import Python_Env @@ -124,9 +124,10 @@ def test_waveguide_length(): # Display in KLayout from SiEPIC._globals import Python_Env - if Python_Env == 'Script': - from SiEPIC.utils import klive - klive.show(file_out, technology=tech_name, keep_position=True) + if show_klive: + if Python_Env == 'Script': + from SiEPIC.utils import klive + klive.show(file_out, technology=tech_name, keep_position=True) os.remove(file_out) @@ -137,4 +138,4 @@ def test_waveguide_length(): if __name__ == "__main__": test_load_layout() test_create_cell2() - test_waveguide_length() \ No newline at end of file + test_waveguide_length(show_klive=True) \ No newline at end of file diff --git a/klayout_dot_config/tech/GSiP/pymacros/tests/test_bezier.py b/klayout_dot_config/tech/GSiP/pymacros/tests/test_bezier.py index e40d3477..d291823f 100644 --- a/klayout_dot_config/tech/GSiP/pymacros/tests/test_bezier.py +++ b/klayout_dot_config/tech/GSiP/pymacros/tests/test_bezier.py @@ -15,7 +15,7 @@ from pya import * -def test_bezier_bends(): +def test_bezier_bends(show_klive=False): designer_name = 'Test_Bezier_bend' top_cell_name = 'GSiP_%s' % designer_name @@ -124,15 +124,16 @@ def test_bezier_bends(): # Display in KLayout from SiEPIC._globals import Python_Env - if Python_Env == 'Script': - from SiEPIC.utils import klive - klive.show(file_out, technology=tech_name, keep_position=True) + if show_klive: + if Python_Env == 'Script': + from SiEPIC.utils import klive + klive.show(file_out, technology=tech_name, keep_position=True) os.remove(file_out) # Plot # cell.plot() # in the browser -def test_bezier_tapers(): +def test_bezier_tapers(show_klive=False): designer_name = 'Test_Bezier_tapers' top_cell_name = 'GSiP_%s' % designer_name @@ -220,9 +221,10 @@ def test_bezier_tapers(): # Display in KLayout from SiEPIC._globals import Python_Env - if Python_Env == 'Script': - from SiEPIC.utils import klive - klive.show(file_out, technology=tech_name, keep_position=True) + if show_klive: + if Python_Env == 'Script': + from SiEPIC.utils import klive + klive.show(file_out, technology=tech_name, keep_position=True) os.remove(file_out) # Plot @@ -231,5 +233,5 @@ def test_bezier_tapers(): if __name__ == "__main__": # test_bezier_bends() - test_bezier_tapers() + test_bezier_tapers(show_klive=True) diff --git a/klayout_dot_config/tech/GSiP/pymacros/tests/test_coupler_array.py b/klayout_dot_config/tech/GSiP/pymacros/tests/test_coupler_array.py index c81cd686..67632109 100644 --- a/klayout_dot_config/tech/GSiP/pymacros/tests/test_coupler_array.py +++ b/klayout_dot_config/tech/GSiP/pymacros/tests/test_coupler_array.py @@ -5,7 +5,7 @@ """ -def test_coupler_array(): +def test_coupler_array(show_klive=False): ''' --- Simple MZI, tested using Facet-Attached Micro Lenses (FaML) --- @@ -101,13 +101,14 @@ def test_coupler_array(): print('Number of errors: %s' % num_errors) # Display the layout in KLayout, using KLayout Package "klive", which needs to be installed in the KLayout Application - if Python_Env == 'Script': - from SiEPIC.utils import klive - klive.show(file_out, lyrdb_filename=file_lyrdb, technology=tech_name) + if show_klive: + if Python_Env == 'Script': + from SiEPIC.utils import klive + klive.show(file_out, lyrdb_filename=file_lyrdb, technology=tech_name) os.remove(file_out) if num_errors > 0: raise Exception ('Errors found in test_coupler_array') if __name__ == "__main__": - test_coupler_array() + test_coupler_array(show_klive=True) diff --git a/klayout_dot_config/tech/GSiP/pymacros/tests/test_example_circuit.py b/klayout_dot_config/tech/GSiP/pymacros/tests/test_example_circuit.py index 29829dee..40a7b3da 100644 --- a/klayout_dot_config/tech/GSiP/pymacros/tests/test_example_circuit.py +++ b/klayout_dot_config/tech/GSiP/pymacros/tests/test_example_circuit.py @@ -16,7 +16,7 @@ from pya import * -def example_circuit(): +def example_circuit(show_klive=False): designer_name = 'Test' top_cell_name = 'GSiP_%s' % designer_name @@ -122,9 +122,10 @@ def example_circuit(): # Display in KLayout from SiEPIC._globals import Python_Env - if Python_Env == 'Script': - from SiEPIC.utils import klive - klive.show(file_out, technology=tech_name, keep_position=True) + if show_klive: + if Python_Env == 'Script': + from SiEPIC.utils import klive + klive.show(file_out, technology=tech_name, keep_position=True) os.remove(file_out) # Plot @@ -143,5 +144,5 @@ def test_example_circuit(): assert example_circuit() == 0 if __name__ == "__main__": - example_circuit() + example_circuit(show_klive=True) diff --git a/klayout_dot_config/tech/GSiP/pymacros/tests/test_instantiate_all_cells.py b/klayout_dot_config/tech/GSiP/pymacros/tests/test_instantiate_all_cells.py index 76aaaf9a..9a731717 100644 --- a/klayout_dot_config/tech/GSiP/pymacros/tests/test_instantiate_all_cells.py +++ b/klayout_dot_config/tech/GSiP/pymacros/tests/test_instantiate_all_cells.py @@ -10,7 +10,7 @@ from pya import * -def test_all_library_cells(): +def test_all_library_cells(show_klive=False): designer_name = 'Test' top_cell_name = 'GSiP_%s' % designer_name @@ -54,7 +54,8 @@ def test_all_library_cells(): if c.is_empty() or c.bbox().area() == 0: raise Exception('Empty cell: %s' % c.name) - topcell.show() + if show_klive: + topcell.show() # Verify num_errors = layout_check(cell=topcell, verify_DFT=False, verbose=False, GUI=True) @@ -64,4 +65,4 @@ def test_all_library_cells(): if __name__ == "__main__": - test_all_library_cells() + test_all_library_cells(show_klive=True) From d4cbb1015c7db5172b41ebac221a56826bfbb6dc Mon Sep 17 00:00:00 2001 From: Lukas Chrostowski Date: Mon, 4 Nov 2024 22:10:12 -0800 Subject: [PATCH 5/6] Update scripts.py --- klayout_dot_config/python/SiEPIC/scripts.py | 25 +++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/klayout_dot_config/python/SiEPIC/scripts.py b/klayout_dot_config/python/SiEPIC/scripts.py index 983328e2..ac96614a 100644 --- a/klayout_dot_config/python/SiEPIC/scripts.py +++ b/klayout_dot_config/python/SiEPIC/scripts.py @@ -3177,7 +3177,7 @@ def layout_diff(cell1, cell2, tol = 1, verbose=True): return diff_count -def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_y_library=None, cell_ref_bb = None, Exact = True, RequiredCharacter = '$', run_layout_diff = False, debug = False): +def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_y_library=None, cell_ref_bb = None, Exact = True, OptionalSuffix='_BB', RequiredCharacter = '$', run_layout_diff = False, debug = False): ''' SiEPIC-Tools: scripts.replace_cell Search and replace: cell_x with cell_y @@ -3194,18 +3194,13 @@ def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, requires cell_ref_bb cell_ref_bb: the black box cell, which will be compared with the cell_x check_bbox = True: make sure the bounding box for the two cells are the same + OptionalSuffix = "_BB": some people add _BB to their black box cells, optionally remove it if found Black box True geometry Basename_BB, Basename_BB* YES: Basename Basename, Basename* NO: Basename_extension Basename, Basename* YES: DifferentName ''' - - import os - if debug: - print(" - cell replacement for: %s, with cell %s (%s or %s), " % (cell_x_name, cell_y_name, cell_y_file, cell_y_library)) - log = '' - log += "- cell replacement for: %s, with cell %s (%s or %s)\n" % (cell_x_name, cell_y_name, cell_y_file, cell_y_library) # Find the cell name from the cell_ref_bb if not cell_x_name: @@ -3213,6 +3208,12 @@ def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_x_name = cell_ref_bb.name else: raise Exception ('missing replacement cell name') + + import os + if debug: + print(" - cell replacement for: %s, with cell %s (%s or %s), " % (cell_x_name, cell_y_name, cell_y_file, cell_y_library)) + log = '' + log += "- cell replacement for: %s, with cell %s (%s or %s)\n" % (cell_x_name, cell_y_name, cell_y_file, cell_y_library) # Make sure we can run the layout diff check. if run_layout_diff: @@ -3221,13 +3222,19 @@ def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, # Find the cells that need replacement (cell_x) # find cell name exactly matching cell_x_name - cells_x = [layout.cell(cell_x_name)] + if layout.cell(cell_x_name): + cells_x = [layout.cell(cell_x_name)] + else: + cells_x = [] if not Exact: # replacement for all cells that: # 1) cell name exact matching cell_x_name, OR # 2) that begin with the cell name, i.e., xxx* is matched # i.e., xxx and xxx* are matched - cells_x += [cell for cell in layout.each_cell() if cell.name.find(cell_x_name+RequiredCharacter) == 0] + if OptionalSuffix: + cells_x += [cell for cell in layout.each_cell() if cell.name.removesuffix(OptionalSuffix).find(cell_x_name+RequiredCharacter) == 0] + else: + cells_x += [cell for cell in layout.each_cell() if cell.name.find(cell_x_name+RequiredCharacter) == 0] # replacement for all cells that: # 1) cell name exact matching cell_x_name, OR From 2df15a071cf571a91c974c0bb53484231045dfeb Mon Sep 17 00:00:00 2001 From: Lukas Chrostowski Date: Mon, 4 Nov 2024 22:30:19 -0800 Subject: [PATCH 6/6] remove OptionalSuffix --- klayout_dot_config/python/SiEPIC/scripts.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/klayout_dot_config/python/SiEPIC/scripts.py b/klayout_dot_config/python/SiEPIC/scripts.py index f1001a9f..32ffb8da 100644 --- a/klayout_dot_config/python/SiEPIC/scripts.py +++ b/klayout_dot_config/python/SiEPIC/scripts.py @@ -3177,7 +3177,7 @@ def layout_diff(cell1, cell2, tol = 1, verbose=True): return diff_count -def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_y_library=None, cell_ref_bb = None, Exact = True, RequiredCharacter = '$', run_layout_diff = True, debug = False): +def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_y_library=None, cell_ref_bb = None, Exact = True, RequiredCharacter = '$', run_layout_diff = False, debug = False): ''' SiEPIC-Tools: scripts.replace_cell Search and replace: cell_x with cell_y @@ -3237,10 +3237,7 @@ def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, # 1) cell name exact matching cell_x_name, OR # 2) that begin with the cell name, i.e., xxx* is matched # i.e., xxx and xxx* are matched - if OptionalSuffix: - cells_x += [cell for cell in layout.each_cell() if cell.name.removesuffix(OptionalSuffix).find(cell_x_name+RequiredCharacter) == 0] - else: - cells_x += [cell for cell in layout.each_cell() if cell.name.find(cell_x_name+RequiredCharacter) == 0] + cells_x += [cell for cell in layout.each_cell() if cell.name.find(cell_x_name+RequiredCharacter) == 0] # replacement for all cells that: # 1) cell name exact matching cell_x_name, OR