Porting from Ipkiss 3.0 to Ipkiss 3.0.1
There are a small number of changes between IPKISS 3.0 (or early betas of 3.0.1) and the release of IPKISS 3.0.1 that can cause issues due to backward incompatibility.
The set
method is more restrictive
In Ipkiss 3.0, the set
(or _set_bulk
) method on a StrongPropertyInitializer
object (which includes most Ipkiss primitives such as PCell
and View
)
would gracefully ignore keywords that did not correspond to a Property. For instance, the following code would work in Ipkiss 3.0 but will not work in Ipkiss 3.0.1:
class MyCell(StrongPropertyInitializer):
a = IntProperty()
b = NumberProperty()
>>> my_cell = MyCell(a=1, b=2)
>>> my_cell.a
1
>>> my_cell.set(a=2, b=3)
>>> my_cell.b
3
>>> my_cell.set(a=1, c=6) # works in Ipkiss 3.0, not in 3.0.1
>>> my_cell.a
1
>>> my_cell.c # c is not a property, but a regular python attribute
6
In the code above, c
is not a property, but is assigned as a regular Python attribute. This is confusing, and also error prone.
For instance, c
could be just a typo, or a confusion in the property name, and it would not introduce an error but might lead to
unpredictable behavior which is hard to discover.
In Ipkiss 3.0.1, set
will reject any keyword that does not correspond to a property. my_cell.set(a=1, c=6)
will raise an exception.
You might discover that some of your code does not work because of this. This can be
because you made a mistake in the keyword name. In that case, the tighter restriction uncovered a latent bug, and you should change or remove the keyword.
because you used
set
to add additional attributes to an Ipkiss object (e.g. some metadata that you want stored in the object). In that case, you should explicitly assign those attributes:
>>> my_cell.set(a=1)
>>> my_cell.a
1
>>> my_cell.c = 6 # explicitly assign c
>>> my_cell.c
6
Casting of Numbers in NumberProperties
For a more consistent behavior of numbers, NumberProperty
(as well as PositiveNumberProperty
, NonNegativeNumberProperty
, etc…) will
now cast all assigned numbers to float
.
The original behavior was
class MyCell(StrongPropertyInitializer):
a = IntProperty()
b = NumberProperty()
>>> my_cell = MyCell(a=1, b=2)
>>> my_cell.a
1
>>> my_cell.b
2
From now on, b
will be internally stored as a float
:
>>> my_cell = MyCell(a=1, b=2) # b is cast to float
>>> my_cell.a
1
>>> my_cell.b
2.0
This can give some problems in existing code, where an int
was assigned to a NumberProperty
, and then that value was used
in a division (resulting in an integer division). For instance, the following code would give different results in Ipkiss 3.0 and Ipkiss 3.0.1.
In Ipkiss 3.0, you would get
class MyCell(StrongPropertyInitializer):
a = IntProperty()
b = NumberProperty()
def get_c(self):
return self.a / self.b
>>> my_cell = MyCell(a=5, b=2)
>>> my_cell.get_c()
2
while in Ipkiss 3.0.1 you will get
>>> my_cell = MyCell(a=5, b=2)
>>> my_cell.get_c()
2.5
Also, in some cases you might get warnings that you did not get before, for instance when using the value to multiply a list:
>>> my_cell = MyCell(a=5, b=3)
>>> ['a'] * cell.b
['a', 'a', 'a']
The advice is to use more explicit casting:
IntProperty
where you really expect anint
NumberProperty
where you expect afloat
FloatProperty
is equivalent to NumberProperty
, but its use will be deprecated in future versions. Use NumberProperty
instead.
PortList behaves similar to TermDict
Ports (Layout) and terms (Netlist) are similar concepts. They are named object, and therefore, it is logical that
they are stored in dict()
-like containers, and that these containers behave similarly. This way, they can be accessed by name.
This is not the case in Ipkiss 3.0, but has been changed in Ipkiss 3.0.1. Currently, the original behavior is still supported, but it is recommended that you update your code to ensure future compatibility.
Consider the following PCell:
class MyCell(i3.PCell):
class Layout(i3.LayoutView):
def _generate_ports(self, prts):
# prts is a PortList
prts += i3.OpticalPort(name="p1")
prts += i3.OpticalPort(name="p2")
return prts
class Netlist(i3.NetlistView):
def _generate_terms(self, trms):
# trms is a TermDict
trms += i3.OpticalTerm(name="p1")
trms += i3.OpticalTerm(name="p2")
return trms
These port and term code in the views is nearly identical, but they return different types of objects:
>>> my_cell = MyCell(name="my_cell")
>>> layout = my_cell.Layout()
>>> layout.ports
[<OpticalPort 'p1': (0.000000, 0.000000), a=0.000000, D=<DefaultWaveguideTemplate.Layout view 'DEFAULT_WG_TEMPLATE:layout'>>,
<OpticalPort 'p2': (0.000000, 0.000000), a=0.000000, D=<DefaultWaveguideTemplate.Layout view 'DEFAULT_WG_TEMPLATE:layout'>>]
>>> netlist = my_cell.Netlist()
>>> print netlist.terms
Terms {'p1':<OpticalTerm 'p1'>,'p2':<OpticalTerm 'p2'>}
This difference becomes very clear when iterating/looping over ports and terms:
>>> for p in layout.ports: print p # p is a Port object
<OpticalPort 'p1': (0.000000, 0.000000), a=0.000000, D=<DefaultWaveguideTemplate.Layout view 'DEFAULT_WG_TEMPLATE:layout'>>
<OpticalPort 'p2': (0.000000, 0.000000), a=0.000000, D=<DefaultWaveguideTemplate.Layout view 'DEFAULT_WG_TEMPLATE:layout'>>
>>> for t in netlist.terms: print t # t is a Term name
'p1'
'p2'
This behavior is different. To ensure compatible behavior of your future code, it is recommended to update your code wherever
you use iterations over ports and terms. We have made sure that both PortList
and TermDict
support the same dict
-like
methods:
keys()
will return a python list with all the port/term namesvalues()
will return a python list with all the port/term objectsitems()
will return a python list of tuples with (name, port/term object)
>>> for p in layout.ports.keys(): print p # p is a Port name (string)
'p1'
'p2'
>>> for p in layout.ports.values(): print p # p is a Port object
<OpticalPort 'p1': (0.000000, 0.000000), a=0.000000, D=<DefaultWaveguideTemplate.Layout view 'DEFAULT_WG_TEMPLATE:layout'>>
<OpticalPort 'p2': (0.000000, 0.000000), a=0.000000, D=<DefaultWaveguideTemplate.Layout view 'DEFAULT_WG_TEMPLATE:layout'>>
>>> for p in layout.ports.items(): print p # p is a (name, object) tuple
('p1', <OpticalPort 'p1': (0.000000, 0.000000), a=0.000000, D=<DefaultWaveguideTemplate.Layout view 'DEFAULT_WG_TEMPLATE:layout'>>)
('p2', <OpticalPort 'p2': (0.000000, 0.000000), a=0.000000, D=<DefaultWaveguideTemplate.Layout view 'DEFAULT_WG_TEMPLATE:layout'>>)
These methods give the same behavior for terms.
If you use iterations over ports and terms in your code, you are advised to modify your code. A typical case is in PCells which contain child cells. Take this Ipkiss 3.0 example:
class MyParentCell(i3.PCell):
child = i3.ChildCellProperty()
class Layout(i3.LayoutView):
def _generate_instances(self, insts):
insts += i3.SRef(name="c", reference=self.child)
return insts
def _generate_ports(self, prts):
for p in self.instances["c"].in_ports:
prts += p.modified_copy(name="in_"+p.name)
return prts
class Netlist(i3.NetlistView):
def _generate_netlist(self, nl):
nl += i3.Instance(name="c", reference=self.child)
inst_terms = self.instances["c"].in_terms
for t in inst_terms:
nl += inst_terms[t].modified_copy(name="in_"+t)
return nl
In Ipkiss 3.0.1 this becomes:
class MyParentCell(i3.PCell):
child = i3.ChildCellProperty()
class Layout(i3.LayoutView):
def _generate_instances(self, insts):
insts += i3.SRef(name="c", reference=self.child)
return insts
def _generate_ports(self, prts):
for p_name, p in self.instances["c"].in_ports.items():
prts += p.modified_copy(name="in_"+p_name)
return prts
class Netlist(i3.NetlistView):
def _generate_netlist(self, nl):
nl += i3.Instance(name="c", reference=self.child)
for t_name, t in self.instances["c"].in_terms.items():
nl += t.modified_copy(name="in_"+t_name)
return trms
Layer Properties in PICAZZO
If you have used one of the PICAZZO components in Ipkiss 3.0, you will find that some properties have slightly changes. Especially layer properties.
Picazzo components that had a property layer
will now only have a property process
and purpose
. So, where you originally could use:
my_component = SomePicazzoComponent(name="my_name",
layer=TECH.PPLAYER.WG.CORE
)
You should now use
my_component = SomePicazzoComponent(name="my_name",
process=TECH.PROCESS.WG,
purpose=TECH.PURPOSE.CORE
)
In some cases, components had both a layer
property and a process
and purpose
property, creating an ambiguous situation. Therefore, the
layer
property has been removed.
The following components are affected:
Trace templates (
picazzo3.traces
):RibWaveguideTemplate
,SlotWaveguideTemplate
,DoubleSlotWaveguideTemplate
, ThinnedWaveguideTemplate,CoreCladdingShapeWaveguideTemplate
Photonic crystals (
picazzo3.phc
): Photonic crystal holes,PhCLayout
,W1HeteroCavity1Mirror
Shallow etched Multi-mode interferometers (
picazzo3.filters.mmi_rib
):RibMMIIdenticalTapered
Fiber grating couplers (
picazzo3.fibcoup
):UniformLineGrating
,GratingUniformLine
Technology
We cleaned up the confusing hierarchy of the technology source files. To avoid breaking the si_photonics
technology that is used for many
Ipkiss 2.4 designs, we created a new default_ipkiss
and silicon_photonics
technology. For any new Ipkiss3 project, you can use the
default_ipkiss
technology. If you want to work with Picazzo3 devices, you should use the silicon_photonics
technology.
from technologies import silicon_photonics
This loads all the constructs needed for Ipkiss3 and Picazzo3. The backward-compatibility entries needed to run design code based on Picazzo2
have been deliberately omitted to keep this technology folder pure. If you want to combine Picazzo2 and Picazzo3 devices within the same Ipkiss3
design, you can use the compat24
technology together with the silicon_photonics
technology:
from technologies import silicon_photonics
from technologies.compat24 import picazzo24
If you are running pure Ipkiss 2.4 scripts, you can also use the ‘old’ si_photonics
technology:
from technologies.si_photonics.picazzo import *
When creating custom technologies, we recommend basing them on silicon_photonics
or base_ipkiss
, and invoke the compat24
for managing the backward compatibility.
The new technology has a clearer folder structure:
default_waveguide.py
: The default waveguide templatetraces.py
: Default settings for waveguides and tracestransitions.py
: The auto transition databaseblocks.py
: default settings for Columns and blocksio.py
: Default adapter for columnsadmin.py
: administrative tools, such as the automatic name generationmetrics.py
: Grid, unit and discretisationrules.py
: Default design rules (line widths, spacings, …)process.py
: Processes, purposes and layersdisplay.py
: Display stylesmask.py
: Default settings for the mask fabricationgdsii.py
: GDSII import and export settings
When building a new technology, you can either copy-paste these files into your own technology, or just import them.
Explicit Evaluations of child views are no longer required
In Ipkiss 3.0, it was often needed to explicitly evaluate views of Child views, to make sure that the parameters were properly passed from the parent to the child. This is no longer needed.
For instance, in the following code from Ipkiss 3.0
my_ring = RingRect180DropFilter(name="my_ring")
cp = dict(cross_coupling1=0.1j, # The coupling from bus to ring and back
straight_coupling1=0.96, # Straight coupling
)
my_ring_cm = my_ring.CapheModel(ring_length=40.0, # we can manually specify the ring length
coupler_parameters=[cp, cp]) # 2 couplers
my_ring_cm.couplers # this line makes sure the coupler models are properly evaluated
we can now remove the explicit evaluations of the couplers
in the last line.
The origin of this issue was that in a PCell with a child cell XYZ
, the _default_XYZ
methods in the view was not always executed.
For example, consider the following Parent and Child class with a Layout view:
class SomeChildCell(i3.PCell):
class Layout(i3.LayoutView):
some_property = IntProperty(default=1)
class MyCell(i3.PCell):
XYZ = i3.ChildCellProperty()
def _default_XYZ(self):
return SomeChildCell(name=self.name + "_child")
class Layout(i3.LayoutView):
a = IntProperty(default=5)
def _default_XYZ(self):
lo = self.cell.XYZ.get_default_view(i3.LayoutView)
lo.set(some_property=self.a)
return lo
In Ipkiss 3.0, the following code would give a wrong result:
>>> my_cell = MyCell(name="my_cell")
>>> parent_layout = my_cell.Layout(a=6)
>>> child = my_cell.XYZ
>>> child_layout = child.get_default_view("Layout")
>>> child_layout.some_property
1
This is clearly wrong, as you would expect the child layout to get its parameters from the parent. In Ipkiss 3.0, this could be solved by explicitly evaluating the child view, either by calling it, or by running a visualize or GDSII export:
>>> my_cell = MyCell(name="my_cell")
>>> parent_layout = my_cell.Layout(a=6)
>>> parent_layout.XYZ # Explicitly evaluate Child View
>>> child = my_cell.XYZ
>>> child_layout = child.get_default_view("Layout")
>>> child_layout.some_property
6
or
>>> my_cell = MyCell(name="my_cell")
>>> parent_layout = my_cell.Layout(a=6)
>>> parent_layout.visualize() # Visualization will evaluate all the child views
>>> child = my_cell.XYZ
>>> child_layout = child.get_default_view("Layout")
>>> child_layout.some_property
6
In Ipkiss 3.0.1, this intermediate evaluation is no longer needed:
>>> my_cell = MyCell(name="my_cell")
>>> parent_layout = my_cell.Layout(a=6)
>>> child = my_cell.XYZ
>>> child_layout = child.get_default_view("Layout")
>>> child_layout.some_property
6
The parameter a
is now properly passed to the child cell.
Notebooks are now using IPython 3
the Notebooks have been ported to the IPython 3 engine (Do not confuse to Python 3.x - Ipkiss is still based on Python 2.7). This engine is also the one bundled with the Luceda installation. Normally, you notebooks v2 should still run, but you might need to add the following line at the start of your notebook:
%pylab inline
After saving the notebooks they will be upgraded to v3.
If you want to convert a notebook from the command line, you can use:
ipython nbconvert --to notebook {} --output {}
We have been diligent in documenting possible backward incompatibilities. If by any chance you encounter other inconsistencies, do not hesitate to use the feedback button.