Package: polyp
Human-Readble-Ascii to gdsII ConverterPolyp is a renderer that creates gdsII layout files from an all-ascii human-readable layout language. Geometric shapes are constructed from primitive shapes, e.g., squares, circles or text, that are translated, rotated, scaled or otherwise transformed and can be added, subtracted or intersected with each other.Polyp is definitely interesting for you if youare not afraid of using the command linehave experience with drawing gdsII filesare a fan of mark-up languagesPolyp is probably not for you if youprefer manually drawing geometryfeel uncomfortable using the command lineA minimal example is shown in the following:SYMBOL main
  LAYER 0
    rect(10).rotate(45) - text("hello world", dy=3)If this is saved in a text fileminimal.plsit can be compile with the commandpolyp minimal.plsThis creates a gdsII fileminimal.gdscontaining a single symbol with the namemainthat contains a shape in layer 0. This shape is given by a square with side length 10, rotated by 45 degrees, from which the text 'hello world' with height 3 is cut-out. For more advanced examples see the examples section of this readme.InstallationInstall polyp and its dependencies withpip install polypExecutingpolyp -hon the shell should now show the polyp quick help,polyp --versionshould show the expected version number.Basic usageTo compile a .pls polyp layout script to a .gds file, runpolyp filename, wherefilenamehas to be replaced with the path of the .pls file.Passing the-v(--view) option (polyp -v filename) compiles the layout script and opens a simple viewer afterwards.Passing the-w(--watch) option keeps checking the .pls file for updates, re-compiles in case a change in the file is detected and keeps the compiled result open in a viewer.If the-p(--pdf) option is passed to polyp, the layout script is compiled to .pdf instead of .gds. One pdf file is created for each gdsII symbol.ExamplesThis list of examples starts from a minimal .pls example and moves to more and more complex layout scripts step by step. A formal documentation of the polyp layout language is currently not available, feel free to contact me or open an issue in case this is needed.Minimal exampleA minimal layout script example looks like this:SYMBOL main
  LAYER 0
    rect(10)The capitalizedSYMBOLandLAYERcontext keywords have to be placed in the beginning of a line and mark the beginning of a symbol or layer context. Note that the indentation used in these examples is not required for the scripts to compile successfully, but improves readability.The last linerect(10)of this example creates a geometric shape, namely a 10x10 square. Since therectis placed inside theSYMBOLandLAYERcontexts, it will be placed in the gdsII symbol namedmainand in layer0in the resulting .gds file. Create a text file with the above content, name ittest.pls, compile it withpolyp -v text.pls, and see for yourself!Using different layers and symbol namesThe following example shows how geometry can be added to different layers and symbols:SYMBOL main
  LAYER 1
    rect(10)
  LAYER 2
    polygon([0,0], [0,10], [-5,5])

SYMBOL sub
  LAYER 1
    rect(5, 10)The linerect(10)again creates a 10x10 square in symbol main, layer 1. Thepolygon(...)built-in creates a polygon using the passed list of coordinates. Coordinates are specified as a comma separated pair of numbers enclosed in square brackets[...]in polyp layout scripts. Thepolygon(...)line is placed afterLAYER 2, hence the resulting polygon is placed in layer 2 of the gds file.The lineSYMBOL subthen switches the active symbol frommaintosub, hence all following geometry is placed in the symbolsub. In this case, the last linerect(5, 10)creates a rectangle of 5 units width and 10 units height.Creating textThe built-intext(...)creates geometry defined by a string of characters. The named parameterdxordymust be present to specify the resulting text's height or width. For exampletext("hello world", dy=10)creates a geometry reading "hello world" with a height of 10 units.The text is placed with the shape's center of mass placed at [0,0] by default. Optionally, an anchor position can be specified by passing a named parametern,e,s,w,ne,se,sw, ornw, which places the text such that the respective side or corner of the bounding box is placed at the given position. For example the linetext("hello world", se=[0,0])places the lower left corner of the text geometry on the point [0,0].Other ways to callrect(...)We already saw that calling rect with a single parameter creates a square with the respective side length. It is also possible to pass two numeric parameters, e.g.rect(5, 10), to create a rectangle with the given width and height. In addition, an anchor position can be specified by passing a named argumentn,e,s,w,ne,se,sw, ornw, analogous totext(...).Geometric sum, difference and intersect operationsIn polyp layout scripts, geometric objects can be easily added, subtracted or intersected with each other using the arithmetic operators+,-and*, respectively:SYMBOL main
  LAYER 1
    rect(10) + polygon([0,0], [0,10], [-5,5])
  LAYER 2
    rect(10) - polygon([0,0], [0,10], [-5,5])
  LAYER 3
    rect(10) * polygon([0,0], [0,10], [-5,5])In this example, the sum of the square and the polygon is placed in layer 1, the difference is placed in layer 2, and the intersection of both is placed in layer 3.Translate, rotate, mirror, grow, round, arrayEvery geometric shape supports a number of transformations that can be invoked by appending a.and the respective transformation:TranslateTo shift a shape to another position, use.translate(...):Relative shift:The code linerect(10).translate(20, 30)creates a square with side length 10 and shifts it 20 units in x-direction and 30 units in y-directionAbsolute shift:The code linerect(10).translate(s=[10,20])creates a square with side length 10 and shifts it such that the southern end of the shape is moved to the coordinate[10, 20]. Here, south refers to the lower middle of the shapes bounding box. All possible anchors aren,e,s,w,ne,se,swandnw, referring to the respective corner of side of the shapes bounding box.Translate supports the optional boolean arguementcopy, which if set to true, causes the untranslated shape to be kept in the result, e.g.rect(10).translate(0, 20, copy=True).RotateTo rotate a square by 30 degrees, userect(10).rotate(30). Optionally, the center of rotation can be specified as a second parameter:rect(10).rotate(30, [0, 10]). By default, the center of rotation is given by the center of mass of the rotated geometry.Rotate supports the optional boolean argumentcopy, which if set to true, causes the unrotated shape to be kept in the result, e.g.rect(10).rotate(120, copy=True).ScaleTo scale a shape, use.scale(s1, s2, center), wheres1is the scale factor in x-direction,s2is the scale factor in y-direction andcenteris the reference point of the scaling. Ifcenteris omitted, the center of mass of the shape is used. Ifs2is omitted, both directions will be scaled bys1.Scale supports the optional boolean argumentcopy, which if set to true, causes the unscaled shape to be kept in the result, e.g.rect(10).scale(3, center=[0,0], copy=True).MirrorTo mirror a geometry at a given point or line, use the.mirror(...). The following example shows three different ways of calling the method:SYMBOL main
  LAYER 1
    polygon([0,0], [0,10], [10,0]).mirror([-5, 5], copy=True)
  LAYER 2
    polygon([0,0], [0,10], [10,0]).mirror(x=-5, copy=True)
  LAYER 3
    polygon([0,0], [0,10], [10,0]).mirror([5,10], [10,0], copy=True)Mirror at a given point:Passing a single coordinate to the mirror method performs a point-reflection of the shape at the given coordinate.Mirror at the x or y-axis:Passing a named parameter x or y, e.g.,x=-5,y=10, mirrors with respect to the implied line.Mirror at a freely defined line:Passing two coordinates to mirror causes the shape to be mirrored at the line that connects these to points.Mirror supports the optional boolean arguementcopy, which if set to true, causes the unmirrored shape to be kept in the result, e.g.rect(10).mirror([20, 5], copy=True).Grow/shrinkTo grow or shrink a given geometry, use.grow(...). Grow expects a single numeric parameter that specifies by how many units to grow the respective shape in all directions. If this number is negative, the shape is shrunk instead.Round cornersTo round all corners a given geometry, use.round(...). Round expects a single numeric parameter that specifies the maximum radius of the corners after rounding.Creating arrays of shapesTo place a shape many times in as a 1D or 2D array, use.array(...). Array expects two numeric parameters that specify how often the shape should be multiplied in x and in y-direction. Two more optional parameters can be used to specify column and row spacing. The default spacing is zero, implying that the shape is repeated in x- or y-direction with a period equal to its width or height, respectively.This example code places an array of rotated squares with and without specified column and row spacing:SYMBOL main
  LAYER 1
    rect(10).rotate(45).array(5, 5)
  LAYER 2
    rect(10).rotate(45).array(5, 5, 10, 20)Calculating height, width, bounding box and center of massThe functionsheight(...)andwidth(...)return the height and width of the shape that is passed as an argument. For exampleheight(rect(10))returns 10. The functionbb(...)returns the bounding box of a shape. The functioncenter(...)returns a shapes center of mass.MathThe trigonometric functions and there inverse functions are available ascos,sin,tan,asin,acos,atan. Angles are given in units of degrees. The functionatan2is a variant ofatanthat uses two arguments and is able to handle the full circle. See the numpy documentation ofatan2for further details.The functionabs(...)returns the absolute value of the passed number,sqrt(...)returns the square root. The functionsmin,maxandmeanreturn the minimum, maximum or arithmetical mean of the passed list of numbers.Type conversionsUseint(...)to convert a number to integer. Usechar(...)to convert an integer number to the respective letter of the alphabet.Defining (parametric) shapesDrawing large geometries using only primitive shapes results in large expressions that get increasingly confusing and difficult to read. To make expressions more readable, polyp allows to define named shapes with the keywordSHAPE:SHAPE diamond()
  rect(10).rotate(45)

SYMBOL main
  LAYER 1
    diamond()
  LAYER 2
    diamond().grow(-2).round(1)
  LAYER 3
    diamond().translate(5, 5)
  LAYER 3
    diamond() - text('A', dy=5)Like theSYMBOLandLAYERkeywords,SHAPEmust be upper case and placed at the beginning of a line.SHAPEis followed by the desired symbol name and a pair of round brackets. The following lines specify the desired geometry. In this example, the shape name isdiamondand the geometry is given by a square rotated by 45 degrees. Later, in the layers of the symbolmain, this shape is created using the statementdiamond(). As shown, all available methods to manipulate geometry can also be applied todiamond().Important:it is not possible to use gdsII references forSHAPEs in polyp.SHAPEs are always copied as often as they are used in the geometry. It is also not possible to use multiple layers in aSHAPE. If you want reference the same piece of geometry multiple times in your script to save memory, or if you want to define multi layered geometries, you are looking for the gdsII symbol and symbol references, which are described later in this tutorial.It is often desirable to define shapes that use free parameters. In polyp, this can be done by specifying an argument list in the brackets of theSHAPEdefinition:SHAPE label(string, height)
  rect(width(text(string, dy=height))+3, height+1)
    - text(string, dy=height)

SYMBOL main
  LAYER 1
    label("such a nice label", 10)In this example, the parametric shapelabelexpects two parameters. The resulting geometry consists of a rectangle with the width calculated from the width of a text instance:width(text(string, dy=height))+3. Another text instance is then subtracted from this rectangle.To create this parametric shape, it is required to pass the parameters to thelabel(...)call, as shown in symbol main. With this definition labels can then be conveniently created and in case the label structure should be changed later, it is possible to do so by only editing a few lines of the script.Defining symbol referencesSymbols and symbol references are an important feature of the gdsII standard.  Symbols are created by placing symbol sections to the pls script. References are created using theref(...)function:SYMBOL sym1
  LAYER 1
    rect(10)

SYMBOL sym2
  LAYER 1
    text('foobar', dy=10)

SYMBOL main
  ref(sym1).array(2, 2, 5, 5)
  ref(sym2).translate(0,10).rotate(10)

  LAYER 2
    rect(5)In this example, two symbolssym1andsym2are defined, each containing a simple geometric shape on layer 1. Then the symbolmainis defined, which contains references to both shapes. Obviously, the referenced geometry cannot be altered except for translation, rotation or array formation. Adding, subtracting, growing or rounding corners are not available for references.Because referenced symbols already contain layer definitions, it is not necessary to place the references inLAYERsections. Regular shapes can be added to the symbol in addition to references usingLAYERsections.Defining and referencing parametric symbolsUsing free parameters in the definition of symbols can be quite useful. In analogy toSHAPEs, this can be done by adding an argument list after the symbol name:SYMBOL sym{}{:.0f}(a, b)
  LAYER 1
    rect(a).translate(-a-1, 0)
  LAYER 2
    rect(b).translate(a+1, 0)

SYMBOL main
  ref(sym, 10,  5)
  ref(sym,  5,  5)
  ref(sym,  5, 10)Here, a parametric symbol with the namesym{}{:.0f}with two parametersaandbis defined (why there are curly braces in the name is explained below). The symbol contains translated rectangles with sizes specified by the two parameters on layer 1 and layer 2.This parametric symbol is referenced with three different choices ofaandbin the symbolmain. The parameters are passed to theref()function in addition to the symbol name.Because the gdsII file format does not support parametric symbols, polyp will create as many gdsII symbols, as parameter choices exist in the layout script. To do so, polyp needs to be capable of creating a unique symbol name from a given parameter set. Therefore, the names of parametric symbols should contain as many{}placeholders as parameters exist. A placeholder is given by a pair of curly braces{}, optionally containing formatting options. In this example, the symbol namesym{}{:.0f}contains the placeholder{}for the parametersaand the placeholder{:.0f}for the parameterb. See the documentation ofpython's format languagefor more details on placeholder structure.ImportsAny pls script can also serve as a library that can be references from other pls scripts: TheIMPORTkeyword followed by the path to another pls script allows to import shapes and symbols from the external file. Consider this example for a layout split across two different files lib.pls and main.pls:lib.pls:SHAPE externalShape(a, b)
  rect(a).rotate(b)main.pls:IMPORT lib.pls

SYMBOL main
  LAYER 0
    lib.externalShape(10, 60)TheIMPORTin main.pls loads all definitions from lib.pls which then can be referenced bylub.name(...), where name is the name defined in lib.pls. To prevent naming collisions, the symbols from a library can also be made accessible under a custom name using theASkeyword:IMPORT lib.pls AS sub
...
sub.externalShape(...)Named layersTheLAYERcontext also accepts names, not only numbers:...
LAYER named_layer
  text('foobar', dy=10)
...Because gdsII files do not support named layers, polyp will automatically assign a layer number and create a symbol called "legend", in which the layer name to layer number correspondence is written. To force a specific gdsII layer number and give the layer a name at the same time, the simply pass both number and name to theLAYERcontext:...
LAYER 10 named_layer
  text('foobar', dy=10)
...Parameter sweepsIt is often desired to design a geometry with a free parameter and to place copies of this geometry with the free parameter being swept through a given range. Parametric geometry is possible with parametricSHAPEs orSYMBOLs, as shown before. To sweep paramters, the.call(...)method is used:SHAPE shiftedCross(size, linewidth, xOffset, yOffset)
  (rect(size, linewidth)
  + rect(linewidth, size)).translate(xOffset, yOffset)

SYMBOL main
  LAYER param_sweeps
    shiftedCross.call(start=(5,.5,36,-10), step=(0,0,6,6), stop=(0,0,55,5))This example defines a parametric shapeshiftedCrosswith four free parameters. The lineshiftedCross.call(...)then places instances of this shape with the four parameters varied as given by thestart,stepandstoparguments. The routine starts by choosing all parameters according to thestart-list, namelysize=5,linewidth=.5,xOffset=36andyOffset=-10. Each parameter is then incremented as specified by the respective number in thestep-list until it reaches the value given in thestop-list. Astepvalue of zero implies that the respective parameter is not swept at all. The parametric shapeshiftedCrossis created for all possible combinations of parameters.GlobalsIn larger layouts it is helpful to maintain a central list of global parameters, e.g. linewidths, minimum paddings or spacings etc. This is done using theGLOBALScontext in polyp layout scripts:GLOBALS
  linewidth = 15
  width = 100

SYMBOL main
  LAYER 0
    rect(linewidth, width)The definitions from theGLOBALSsection can be used anywhere in the layout script.Objects und unpackingObjects can be created to group globally stored parameters:GLOBALS
  optsA = {
    linewidth = 15
    width = 100
  }
  optsB = {
    linewidth = 15
    width = 100
  }

SHAPE wire(linewidth, width)
  rect(linewidth, width)


SYMBOL main
  LAYER 0
    wire(*optsA)
  LAYER 1
    wire(*optsB)The unary*operator applied to objects unpacks the object into a parameter list. This way it is not necessary to write down all argument names anymore, which is helpful in layouts with many free parameters.QR codesTheqrcodenative creates a qr code geometry from a given string. The optional named parametersdxanddyspecify the size of the resulting code in x and y directions. With therobustparameter (1...4, higher=more robust, default 2) the redundancy in the generated code can be adjusted. Theresparameter controls the number of "pixels" used in the qrcode. By default, theresis automatically chosen according to the input string.