Setting properties for PCells

Hi

I am slightly lost in user properties. I understand, that Shape, Cell and CellInstance can all have their properties. I would like to pass some information about a PCell via the properties, but if I use self.cell.set_property(name, value) in produce_impl of a PCellDeclarationHelper, the property does not appear for either PCell variant (one in the Cell view on the left) nor for cell instance (one in the design). How could I set properties for the instances of PCell in the PCell definition code?

Comments

  • Hi,

    you cannot. A PCell shall deliver layout and not modify the external attributes of a cell (such as name, placement, etc.). The user properties are among the external attributes. Instances cannot be accessed from within the PCell because KLayout will optimize the PCell generation such that different instances with the same parameter set will share the same PCellVariant.

    You can use hidden PCell parameters to keep information you don't what to show up in the user interface, if that is the intention should have.

    Regards,

    Matthias

  • edited February 2019

    My specific goal was to export Sonnet project files and use properties to define port locations. Then, a script could export all cells irrespective if they are PCell variants or manually created.

    I think I can use the properties of the shapes in the Cell for now.

    Initially I thought, that instances would inherit user properties from a PCell variant. But it seems that even PCell variant does not get the properties of the produced PCell.

  • Hello,

    Yes, shape properties are possible. You could also use texts to label the ports, if that is an option.

    Instance properties cannot be used because the PCell is the target of the instance, not the instance itself. Multiple instances can share the same PCell variant (variant in terms of PCell parameters). Hence there is no connection back from the PCell variant to the instance.

    If you really, really want to have an instance with properties you can create a cell inside the PCell and produce in instance of this cell inside ... but I'd only do this is absolutely necessary.

    Regards,

    Matthias

  • Thank you for the reply (and the great software)!

    Is instances inheriting properties of the cell variants a bad idea? Obliviously many instances would share the inherited properties. And cell variants could get properties during the production.

    I already use text to annotate the ports for users. For Sonnet files I need to tell the index of the polygon edge to be used as a port and finding the edge under a text position seems a bit inefficient. For this purpose I now use the property of the shape.

  • Shape properties are fine. Instances have their own properties and it's not common to have instances inherit cell properties. Both are different concepts and used for very different purposes, so I'd not mix that.

    Matthias

  • edited March 2019

    I keep pumping into the same issue and from reading the Q&A regarding the Circle PCell parameters I think other people have reached the same point. I define a lot of features in PCellDeclarationHelper. It generates geometry, but some of the intermediate results are useful for other object and I want to access them.

    First, why do I keep using PCells: I want to make use of API handling copies of cells with same parameters - this is a very nice feature. Also, then a user can turn the top cell into static and change parameters of the children.

    Second, why do I define the geometry and other calculations inside PCellDeclarationHelper? I want to initiate my object via a single interface. If the geometry was defined in another object, I would initiate the geometry object to extract intermediate results and then initialize the library PCell with the same arguments. I want to avoid doing it twice and keep using PCells. Thus, the geometry is defined behind PCell.

    Useful intermediate results from PCells: First, there are geometric locations, which other constructs need to relate to. These I can "leak out" from PCellDeclarationHelper inside coordinates of the Text objects on an extra layer. This is reasonable (although it would be very nice to see some mark at the exact location of Text). However, there are other useful quantities as well. For example, an index for a special polygon edge or a length of my waveguide with rounded edges. How to "leak" them out from PCellDeclarationHelper? I tried two approaches.

    Readonly pcell_parameter - it seems, that they get updated only after a call to coerce_parameters_impl and this only happens after event on the GUI. If I create an instance in the code, the coerce_parameters is never evoked and non of the parameter values is updated even if I set the value in produce_impl. Is that true? Is it possible to work around this? I also tried layout.update().

    Property of Cell objects. Inside PCellDeclarationHelper we have access to some self.cell, to which we add shapes. But if I set self.cell.set_property, these properties are not passed to the Cell object given by layout.create_cell, and also it is not passed to the Instance returned by topcell.insert. This relates to the discussion we had above.

    How to pass out internal variables of PCellDeclarationHelper?

    To check my claims I modified the Circle example by adding the following lines to the end of produce_impl:

        self.cell.set_property("test", "test")
        self.ru = self.r
        print("Prop Inside:", self.cell.property("test"))
        print("Parm Inside:", self.ru)
    

    And in a macro I had following lines:

    params = {"r":105}
    lib = layout.create_cell("Circle", "QCircuit", params)
    inst = cell.insert(pya.DCellInstArray(lib.cell_index(), pya.DTrans(pya.DVector(0, -1200))))
    print("Init only")
    print(layout.properties(lib.prop_id))
    print(layout.properties(inst.prop_id))
    print("Prop Outside inst:",inst.property("test"))
    print("Prop Outside lib:",lib.property("test"))
    print("Param Outside inst:", inst.pcell_parameters_by_name())
    print("Param Outside lib:", lib.pcell_parameters_by_name())
    
    inst.change_pcell_parameters( {"r":101})
    print("Inst param reset")
    print(layout.properties(lib.prop_id))
    print(layout.properties(inst.prop_id))
    print("Prop Outside inst:",inst.property("test"))
    print("Prop Outside lib:",lib.property("test"))
    print("Param Outside inst:", inst.pcell_parameters_by_name())
    print("Param Outside lib:", lib.pcell_parameters_by_name())
    
    layout.update()
    print("Update")
    print(layout.properties(lib.prop_id))
    print(layout.properties(inst.prop_id))
    print("Prop Outside inst:",inst.property("test"))
    print("Prop Outside lib:",lib.property("test"))
    print("Param Outside inst:", inst.pcell_parameters_by_name())
    print("Param Outside lib:", lib.pcell_parameters_by_name())
    

    And here is the output I got:

    Prop Inside: test
    Parm Inside: 109
    Init only
    []
    []
    Prop Outside inst: None
    Prop Outside lib: None
    Param Outside inst: {'l': 1/0, 'rd': None, 'r': 109, 'n': 64, 's': 0,0, 'ru': 0.0}
    Param Outside lib: {'l': 1/0, 'rd': None, 'r': 109, 'n': 64, 's': 0,0, 'ru': 0.0}
    Joonistab
    Prop Inside: test
    Parm Inside: 110
    Inst param reset
    []
    []
    Prop Outside inst: None
    Prop Outside lib: None
    Param Outside inst: {'l': 1/0, 'rd': None, 'r': 110, 'n': 64, 's': 0,0, 'ru': 0.0}
    Param Outside lib: {'l': 1/0, 'rd': None, 'r': 109, 'n': 64, 's': 0,0, 'ru': 0.0}
    Update
    []
    []
    Prop Outside inst: None
    Prop Outside lib: None
    Param Outside inst: {'l': 1/0, 'rd': None, 'r': 110, 'n': 64, 's': 0,0, 'ru': 0.0}
    Param Outside lib: {'l': 1/0, 'rd': None, 'r': 109, 'n': 64, 's': 0,0, 'ru': 0.0}
    

    If I open the PCell parameters window in the GUI and only click apply, I get extra output

    Prop Inside: test
    Parm Inside: 109,0
    

    and if I run single a print command of the macro again, I get

     Param Outside inst: {'l': 'Optical lit. 1' (1/0), 'rd': 218.0, 'r': 109.0, 'n': 64, 's': -109,0, 'ru': 109.0}
    

    as expected.

    I would have been happy, if the output before touching GUI would have included

    ["test":"test"]
    Prop Outside lib: "test"
    Param Outside inst: {'l': 1/0, 'rd': None, 'r': 110, 'n': 64, 's': 0,0, 'ru': 110}
    

    or something similar.

    I know, that I could have set 'ru' in params, but my goal was to calculate something inside PCellDeclarationHelper.

    I hope you see my point and can suggest a workaround. Perhaps, if I understood it all correctly and you consider adding features, I suggest that PCell parameter update was invoked by some additional events via API and the self.cell parameters form PCellDeclarationHelper would be passed to Cell objects in the layout.

    KLayout version 0.25.6, Windows 64 bit build obtained from Lukas via the package manger.

  • edited March 2019

    Hi,

    thanks for the lengthy description ... this discussions isn't new, but here are my points:

    1.) doing something from inside the PCell to the objects that use the PCell simply isn't possible. You shouldn't underestimate the complexity of the whole system. The PCell might live in a library which is shared by multiple layouts etc. So then what is the cell or instance that is the PCell in your layout? The cell you see is a pointer (a proxy) to another cell inside a library which itself is a proxy to a PCell variant. The cell you get when you create PCell geometry is the PCell variant proxy. It's cached for performance and two links away from the actual layout. So setting properties on the target layout's cell or even instance simply isn't possible.

    2.) The only storage KLayout gives you are the parameters. Only these are persisted in GDS (how this is done is a discussion on it's own). So using hidden parameters is the only way to store additional attributes.

    It's true that "coerce_parameters"/"coerce_parameters_impl" is only called on manual changes. That's because the user cannot change readonly or hidden parameters. So "coerce_parameters_impl" is there to compute these values.

    When changing parameters programmatically, you don't have this limitation. So you're expected to supply the PCell implementation with the right and consistent parameters. You could even supply a parameter set that goes beyond what is possible in the UI+coerce_parameters. If you really want to have coerce_parameters, you can explicitly call "coerce_parameters" (note: without "_impl"):

    inst = ... # A PCell Instance. 
    # This assumes the PCell is in a library. If not, change "cell().library().layout()" to "cell().layout()":
    inst.change_pcell_parameters(inst.pcell_declaration().coerce_parameters(inst.cell().library().layout(), inst.pcell_parameters()))
    

    There is a strong restriction when a PCell's state can be changed: the functions by which the PCell is supposed to deliver information (update geometry, derive description etc.) are operated in strict readonly mode and cannot modify anything on the PCell. To understand that in detail, you can study the PCellDeclarationHelper implementation which is a thin layer atop of PCellDeclaration. This object lacks some of the convenience of the PCellDeclarationHelper but reveals the strict, low-level API.

    Regards,

    Matthias

  • edited March 2019

    Thank you for a detailed reply! I had a look at pcell_declaration_helper.lymand learned about what you described. However, I think I still did not make my goal clear. Sorry for insisting.

    doing something from inside the PCell to the objects that use the PCell simply isn't possible

    I never intend to change instance from PCell declaration, I only want to read PCell Cell properties. lib object in my code is the library Cell, a PCell variant stored in the library layout, right? Why does lib.property("test") or lib.library().layout().properties(lib.library().layout().cell(lib.library_cell_index()).prop_id) not show properties I have set in produce? Somehow produce has added shapes to the Cell, but not the cell parameters.

    So you're expected to supply the PCell implementation with the right and consistent parameters.

    This is clear, but I would use some parameters as inputs and some readonly parameters as outputs. From the documentation alone it was not clear that only coerce_parameters can update parameters. The workaround you suggest works. Now I have to calculate geometry both in coerce_parameters and produce though.

  • Good that it works now :-)

    The answer to your question

    Why does lib.property("test") or lib.library().layout().properties(lib.library().layout().cell(lib.library_cell_index()).prop_id) not show properties I have set in produce? Somehow produce has added shapes to the Cell, but not the cell parameters.

    is simply that "produce" is only intended to produce shapes and instances. By "intended" I mean I have designed it for this purpose and this is the contract between KLayout's core and the PCell implementation. So trying to do something else is working against my design and this contract.

    The cell object this method receives is just a container, not necessarily a real cell. Take caching: in this case, "produce" isn't even called. KLayout will take the data from the cell which acts as the data container for the cache. Think of "cell" as "multi-layered container for shapes plus instances", not as something that lives in your target layout. Properties simply aren't part of this concept. Note also that there is no warranty that "produce" is called if KLayout decides that no update is required. The basis KLayout makes this decision is the parameter set alone. The assumption is that if there is no parameter change, not update is required.

    Matthias

  • edited June 2019

    I keep trying to redesign my architecture, but I keep pumping into this constraint.

    Invoking coerce_parameters does not solve my problem. First, it would require rerunning the geometry calculations. Secondly, as it does not have access to the Cell object, unlike produce, I cannot produce instances during the calculation. When generating and arranging subcells I produce computationally expensive information and I want to pass it out (like the length of a complex waveguide recursively made of other waveguides). As far as I know I cannot pass anything out from produce except the child instances and shapes. Do I really have to serialize my extra information into text in the shapes?

    Just to illustrate how hard I try to work around this, here is something I tried to get working for 2 days. I tried setting a class instance variable in produce and copied it to a PCell parameter in coerce. It works if the PCell parameters are unique, as then the PCell is produced just before I call coerce (which triggers produce again). However, this works only if produce is called always before coerce as other PCell variants produced meanwhile could leave other values of the instance variable.

    I think it comes down to philosophy on what can be "produced". I understand, that shapes have to be cached very efficiently, but attaching some metadata to raw geometry really has its use cases in my humble opinion. From reading the documentation I got the wrong impression that "parameters" are exactly for that - metadata for shapes and cells.

  • You can use hidden PCell parameters to keep information you don't what to show up in the user interface, if that is the intention should have.

    Does not solve the problem, if I cannot set the hidden parameters in produce. Parameters only seem to be intended as "inputs" for generating the Cell and not "outputs" as metadata.

    The problem seems to be, that the Porperties are stored in with a layout and do not move together with the geometry. And Parameters are only intended as input.

    create a cell inside the PCell and produce in instance of this cell inside ... but I'd only do this is absolutely necessary.

    At this point this seems like the only option: to use a shape or subcell to carry metadata of the parent cell produced in PCell produce. I imagine creating a special "metadata" PCell type, such that I could easily find it if I want to access the metadata :)

  • This discussion is getting pretty philosophical. I'd like to have a sample on which we can discuss this further.

    As a matter of fact, you cannot access the layout objects and it will stay like this. A program architecture is based on certain interfaces and contracts, and I'm not going to support such a substantial modification of these interfaces. Of course, you can dig into the C++ code if you want to do it yourself. But I think you will just discover why my position is that way.

    Basically the roles of the functions are these and this is the contract and the warranties:

    • "coerce_parameters" reads and writes parameters. Typically it reads "given" ones and writes "hidden ones". "coerce_parameters" is called at least once (not specified when) if a parameter value gets changed.
    • "produce" reads parameters and writes layout shapes. It must not and cannot modify parameters.

    So if you want to cache computationally expensive results, you can only use "coerce_parameters". You can use more hidden parameters as shadow parameters to keep track of actual parameter changes to minimize the execution frequency of the computation (if shadow != real value update shadow with real value and trigger computation). The results of any computation can be stored in forms compatible with the parameter types in hidden parameters and read by the "produce" function. Parameter values can be strings, doubles etc.

    The number of (hidden) parameters is essentially unlimited, string lengths are limited by the way they are serialized into GDS attributes to about 10k bytes for binary data and about 32k for ASCII.

    I you find that this approach is not what you are looking for, I'd strongly suggest to consider a "generator" approach which is non-interactive, like providing a menu function that generates or modifies a cell with a certain layout structure. You'll have all freedom you ask for then. That's what you would do with complex arrangements such a memory blocks. No one would seriously consider implementing a SRAM generator inside a PCell for example.

    Matthias

  • I don't want to push more but just for record for other people reading this.

    PCell hidden parameters would not be suitable solutions, since the expensive calculation produces subcells, shapes and metadata. While I could limit the metadata format to fit into concept of hidden cells I doubt I should store shapes and subcells in hidden parameters until I later call produce. (Also, after creating instances in code I would need to manually call coerce_parameters as discussed before.)

    Downside of the generator class is that in order to avoid regenerating the geometry I would need to cache the generated elements. This is exactly what PCells do and I would duplicate this functionality. I will consider this when restructuring the project.

    My workaround at this point is to have a geometric representation of my metadata which is kept in a extra layer and then it is cashed together with the shapes. It is quite natural to store the endpoints of the waveguide as points. For easy extraction of the waveguide length I generated an extra Line object following the waveguide and it is easy to get the line length in contrast polygons of the curved waveguide.

  • Thanks for the discuss here, the code from Matthias works for me. smile:

    inst = cell.insert() # A PCell Instance. 
    # This assumes the PCell is in a library. If not, change "cell().library().layout()" to "cell().layout()":
    inst.change_pcell_parameters(inst.pcell_declaration().coerce_parameters(inst.cell().library().layout(), inst.pcell_parameters()))
    

    A little problem here, because of the parameter changed, it create a new Pcell variant and the original Pcell variant become unused and appears as a new top cell.

  • @wishalpha Thanks for sharing the code.

    Usually KLayout does a cleanup of such unused cells itself. If it doesn't, you can call

    layout.cleanup([])
    

    to clean up. The argument is an empty list - if there are precious orphan PCell variants you want to keep you can list their (proxy) cell indexes there.

    Matthias

  • Hi @Matthias

    Regarding cleanup(), I find that these orphan PCell variants show up in my layout when I load the GDS file. Namely I save a file with a single top cell, and after opening, there are new orphan top cells.

    The specific case is a PCell that creates fixed subcells, where the names are fixed but there are many different instances of the PCell with different parameters. The fixed names get a $1, $2, appended in the subcell, which is fine, except when they are re-evaluated at loading, then I end up with tons of orphans.

    I need to run the cleanup function manually after opening the layout. I wrote a script to trigger this to happen automatically after a GDS file is loaded, however, then the layout is marked as edited [+].

    I tried adding cleanup() inside the PCells, but that didn't help.

    Is there a proper way of having PCells contain subcells and hierarchy?

    thank you
    Lukas

  • edited April 2022

    Hi Lukas,

    I did not consider need for cleanup after re-evaluation (specifically after reading), but in your scenario this makes sense.

    I created a ticket: https://github.com/KLayout/klayout/issues/1059.

    Matthias

Sign In or Register to comment.