Hello,
I am trying to create complex PCells in Python, hence I would like to build up a PCell using other PCells (including ones provided in the Basic library).
I tried to combine some of the code examples, as below. It doesn't work. I end up with a main cell, with a sub cell, but the name shows up as "SiEPIC_EBeam_PCells.Basic.DONUT(r=0.000000..0.000000)", and the nested PCell is empty.
Is there a way to fix the above? Or perhaps it is possible to call only the function that draws the structure, i.e., "produce_impl"?
Thank you
Lukas
import pya
import math
"""
This Python file implements a library called "SiEPIC_EBeam_PCells" with PCells:
- Circle: draws a circle. It demonstrates the basic implementation techniques for a PCell
and how to use the "guiding shape" feature to implement a handle for the circle
radius.
NOTE: after changing the code, the macro needs to be rerun to install the new
implementation. The macro is also set to "auto run" to install the PCell
when KLayout is run.
Original version from KLayout examples
modified by Lukas Chrostowski 2015/11/05
"""
class DoubleBus_RingResonator(pya.PCellDeclarationHelper):
"""
The PCell declaration for the DoubleBus_RingResonator
"""
def __init__(self):
# Important: initialize the super class
super(DoubleBus_RingResonator, self).__init__()
# declare the parameters
self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0))
self.param("s", self.TypeShape, "", default = pya.DPoint(0, 0))
self.param("r", self.TypeDouble, "Radius", default = 10)
self.param("w", self.TypeDouble, "Waveguide Width", default = 0.5)
self.param("g", self.TypeDouble, "Gap", default = 0.5)
self.param("n", self.TypeInt, "Number of points", default = 500)
# this hidden parameter is used to determine whether the radius has changed
# or the "s" handle has been moved
self.param("ru", self.TypeDouble, "Radius", default = 0.0, hidden = True)
self.param("rd", self.TypeDouble, "Double radius", readonly = True)
self.lib = pya.Library.library_by_name("Basic")
if self.lib == None:
raise Exception("Unknown lib 'Basic'")
self.pcell_decl = self.lib.layout().pcell_declaration("DONUT");
if self.pcell_decl == None:
raise Exception("Unknown PCell 'DONUT'")
# don't work for some reason...
# self.layer_index = self.layout.layer(pya.LayerInfo(1,0))
def display_text_impl(self):
# Provide a descriptive text for the cell
return "DoubleBus_RingResonator(R=" + ('%.3f' % self.r) + ",g=" + ('%.3f' % self.g) + ")"
def coerce_parameters_impl(self):
# We employ coerce_parameters_impl to decide whether the handle or the
# numeric parameter has changed (by comparing against the effective
# radius ru) and set ru to the effective radius. We also update the
# numerical value or the shape, depending on which on has not changed.
rs = None
if isinstance(self.s, pya.DPoint):
# compute distance in micron
rs = self.s.distance(pya.DPoint(0, 0))
if rs != None and abs(self.r-self.ru) < 1e-6:
self.ru = rs
self.r = rs
else:
self.ru = self.r
self.s = pya.DPoint(-self.r, 0)
self.rd = 2*self.r
# n must be larger or equal than 4
if self.n <= 4:
self.n = 4
def produce_impl(self):
# This is the main part of the implementation: create the layout
# Use Library: Basic, DONUT PCell
lib = pya.Library.library_by_name("Basic")
print lib.name()
if lib == None:
raise Exception("Unknown lib 'Basic'")
# locate the declaration
pcell_decl = lib.layout().pcell_declaration("DONUT");
if pcell_decl == None:
raise Exception("Unknown PCell 'DONUT'")
# to find the parameter variables, copy this into the Console:
#for p in pcell_decl.get_parameters():
# print p.name
# create the PCell variant
radius1=self.r-self.w/2
print radius1
param = {
# "layer": pya.LayerInfo(1, 0),
"layer": self.l,
"radius1": radius1,
"radius2": self.r+self.w/2,
"npoints": self.n
}
# Debug with hard coded parameters
param = {
"layer": pya.LayerInfo(1, 0),
"radius1": 10,
"radius2": 11,
"npoints": 40
}
pv = []
for p in pcell_decl.get_parameters():
if p.name in param:
pv.append(param[p.name])
else:
pv.append(p.default)
cell_index = self.layout.add_pcell_variant(lib, pcell_decl.id(), pv)
# place the PCell
t = pya.Trans(pya.Trans.R0, 0, 0)
self.cell.insert(pya.CellInstArray(cell_index, t))
# "fake PCell code" - http://klayout.de/forum/comments.php?DiscussionID=547
text_cell = self.layout.create_cell("1T")
# self.pcell_decl.produce(self.layout, [ self.layer_index ], pv, text_cell)
self.pcell_decl.produce(self.layout, [ self.l_layer ], pv, text_cell)
# Debugging - make a circle...
# fetch the parameters
ru_dbu = self.ru / self.layout.dbu
# compute the circle
pts = []
da = math.pi * 2 / self.n
for i in range(0, self.n):
pts.append(pya.Point.from_dpoint(pya.DPoint(ru_dbu * math.cos(i * da), ru_dbu * math.sin(i * da))))
# create the shape
self.cell.shapes(self.l_layer).insert(pya.Polygon(pts))
class SiEPIC_EBeam_PCells(pya.Library):
"""
The library where we will put the PCell into
"""
def __init__(self):
# Set the description
self.description = "SiEPIC_EBeam_PCells"
# Create the PCell declarations
self.layout().register_pcell("Circle", Circle())
self.layout().register_pcell("DoubleBus_RingResonator", DoubleBus_RingResonator())
# That would be the place to put in more PCells ...
# Register us with the name "SiEPIC_EBeam_PCells".
# If a library with that name already existed, it will be replaced then.
self.register("SiEPIC_EBeam_PCells")
# Instantiate and register the library
SiEPIC_EBeam_PCells()
Comments
Hi Lukas,
Creating cells within PCells is possible, but I don't encourage it because this way the management of cells gets pretty messy. PCells can reference static cells from a library and PCells can instantiate other PCells (preferably from the same library). But it's not advisable to have PCells creating normal cells in their layout instantiation code. The PCell layout generation code is called quite often and each time a new, untracked cell will be generated, leading to an inflation of cells.
There is some more material about instantiating PCells within other PCells here: http://klayout.de/forum/comments.php?DiscussionID=701
In general, it's cleaner and safer to actually embed the code which generates a PCell rather than instantiating a PCell.
Let me explain that using an example:
I'm assuming you have two PCells, A and B and PCell B likes to embed A. Assume both are in the same library then your Python code looks roughly like this:
If you organize the code differently and delegate the layout production you can avoid the instantiation:
This will basically create a flat version of the same B. If you need the ability to transform layout to emulate the placement of a cell, you could do so by adding a transformation parameter to the "produce_A" function and set this parameter to "unity transformation" when generating A and a different transformation when producing the A layout for B.
I hope this is not too confusing. The basic message is: it's easier to use the actual layout generation code than to place a PCell. Modularizing ones code helps when implementing this scheme.
A special issue arises, if you want to utilize functionality which comes from the BASIC library where the production code is not readily available. The basic functionality can be recoded with little effort:
For more functionality like the TEXT PCell feature, the "fake PCell" trick comes into play. Since a PCell's generation code is accessible through the declaration object, you can explicitly call it if you provide a target cell and the necessary parameters. It's valid to supply a temporary cell or even a temporary layout object for that purpose, provided you delete it later.
Best regards,
Matthias
# text_cell = self.layout.create_cell("1T")
text_cell = self.cell
self.textlayer_index = self.layout.layer(pya.LayerInfo(10,0))
self.textpcell_decl.produce(self.layout, [ self.textlayer_index ], pv, text_cell)
t = pya.Trans(pya.Trans.R0, 0, 0)
self.cell.insert(pya.CellInstArray(text_cell.cell_index(), t))
However, I get an error:
Caught the following exception:
Internal error: /Users/sekigawakazunari/SVNWork/klayout-0.24.2/src/dbLayout.cc:1262 topological_sort () was not true in Library.register (Class exceptions.RuntimeError)
Press 'Ok' to continue and 'Cancel' to stop in the debugger
I ended up creating the following function, and calling it from a PCell:
Hi Lukas,
The "Internal error: ... topological_sort () is not true" occurs if the layout hierarchy is recursive (cells instantiating itself or a parent of itself).
This is what basically happens, when you created the instance of "text_cell" inside itself in the first code.
The second code is a good and valid solution since it uses a temporary cell. Maybe it makes sense to delete the cell after the "flatten" call. I'm not sure if that happens automatically. It will still work without that, but the temporary cells will accumulate over time.
Best regards,
Matthias
1) In the above example, the code isn't quite right. It should be cell.flatten(True). It flattens everything under cell, namely the fake text_cell. But...
I notice that all the flatten functions you have flatten all the contents of the cell, but not the cell itself. Say I have a top cell containing cells fakeA and realB, and I want to flatten fakeA. fakeA.flatten() flattens the contents of fakeA, but leaves the cell fakeA in the top cell. Similarly, layout.flatten(fakeA...) does the same.
The GUI, you can select a cell, and flatten that particular one. But not in the script?
2) Earlier today, I had nested PCells working very well, but the more I spend time on it, I'm seeing more and more crashing. I was thinking flattening individual cells might help debug this.
I can nest several different PCells within one another, each calling it's sub-PCell using layout.create_cell. I can build nice layouts with this.
What I like about the nested PCells is that if I have sub-cells that are have the same parameters, KLayout doesn't make duplicate copies. And when I change the top-level parameters of one cell, the lower cells either separate out into different sub-cells if the parameters are different, or they disappear leaving only one copy. It's neat watching the Cells listing in flat mode to see cells coming and going as I change parameters.
But problems come up when I re-run the library self.register() function. In some cases, the crashing is pretty repeatable.
As you suggest, the fake cell approach seems more stable, but isn't quite as elegant to look at in the cell listing. Do you have a short-cut for this perhaps? instead of the 10 lines, I like the layout.create_cell ( pcellName, Library, parameters)
thank you
Lukas
Hi Lukas,
regarding 1.)
Instance#flatten
should provide the same functionality than the GUI - it will replace the instance by the shapes it represents. But the cell itself still remains.Instance#flatten
is available with version 0.24.Regarding the crashes - there is some improvement in that area in 0.24.3. Maybe that helps preventing the crashes. But in general re-registering a library is a very complex topic. KLayout has to remove all references and replace them by references to a new library. I'm quite sure I have not considered all potential implications of that. If possible I'd like to prevent crashes and if there is a situation which is reproducible I'm interested in debugging that problem. Maybe you can paste a stack trace if you happen to get one here.
But under normal operation, self.register should only be called once. I understand you are getting these crashes only while developing a PCell, do you?
Best regards,
Matthias
Sorry to butt in, but it's possible lukasc is talking about selecting a child instance in the layout and choosing Edit > Selection > Flatten cell > All hierarchy levels. This flattens the entire instance. lukasc, is that correct?
The behavior Matthias is describing comes from, for example, right-clicking on the cell name in the cell hierarchy tree and choosing Flatten Cell.
I think the problem is that lukasc calls "cell" what is technically "instance".
David
Hi Matthias,
Re - crashing. Yes, they occur during self.register, while developing PCells. The following crash occurred after I copied and pasted a PCell in the code, renamed it, (didn't add a new register_pcell line) and ran the script.
Also, I've played with both approaches:
class TestStruct_DoubleBus_Ring(pya.PCellDeclarationHelper):
class TestStruct_DoubleBus_Ring2(pya.PCellDeclarationHelper):
where class DoubleBus_Ring(pya.PCellDeclarationHelper) is the sub-PCell.
They both seem to work just fine in the GUI. As you point out, it's the re-register that crashes it.
So I will keep exploring the nested PCell approach...
I push OK, and it crashes again:
and again, OK:
and again OK, the following line just changes; otherwise the same info:
Hi Lukas,
thanks for the stack traces. I'll have a look at that. I hope I'm able to reproduce the issue.
Best regards,
Matthias
Hi Matthias,
I notice that perhaps the functionality of nested PCells has changed. I am using the following method of instantiating a PCell and creating a cell:
Previously, I was enjoying having a PCell call another PCell, and seeing the entire cell structure in the GDS hierarchy. I was using this for my netlist extraction. Namely, I scan the hierarchy for primitive components for which I have compact models; and assume that the component is a cell in the hierarchy. Now, it seems that the create_cell function flattens everything below it? Is there way to keep the cells and hierarchy?
By the way, regarding the crashes above, they still occur if I have a layout open, make changes to the PCell script file, and run the file. To avoid the crashing, I need to close the layout, run the PCell definition script, then open the layout again.
Thank you
Lukas
Hi Lukas,
I'm not aware of having changed something there in 0.24.5 (compared to 0.24.4). Flattening should not happen. But if you are using the code above (from 8th of November) there is an explicit flatten call.
Regarding the crashes: I'm aware that changing PCell code while the layout is open is a critical operation, specifically if nested PCells are involved. I'm not sure whether I have received some sample code from you yet - maybe given real code would help me to reproduce it. Or are you referring to the first version of the script above?
Thanks,
Matthias
is there any update/solution to the crash caused by nested PCells when the layout is open?
I still have this issue.
Thanks,
Antoine
Hi Antoine,
that issue is really old (almost exactly 2 years) :-)
Does this still happen with 0.25.3 (the latest version)?
If it does, please provide a testcase for the issue (code preferred).
Regards,
Matthias