コード例 #1
0
ファイル: geometry_3d.py プロジェクト: dimapu/klayout_pyxs
    def size_l2l(self, layers, dx, dy=0, dz=0, mode=2, rh=True, mc=True):
        """ Change mask size in each layer by dx and dy.

        Size in z-direction remains unchanged.

        Parameters
        ----------
        layers : list of MaterialLayer
        dx : int
            size increase in x-direction in [dbu]
        dy : int (optional)
            size increase in y-direction in [dbu]
        dz : int (optional)
            size increase in z-direction in [dbu]
        mode : int
        rh : boolean (optional)
        mc : boolean (optional)

        Returns
        -------
        res : layers : list of MaterialLayer
        """
        res = []
        for l in layers:
            sized_polys = self.size_p2p(l.mask.data, dx, dy, mode, rh, mc)
            res.append(
                MaterialLayer(LayoutData(sized_polys, l.mask._xs),
                              l.bottom - dz, l.thickness + 2 * dz))

        # Join overlapping layers
        info('    res before normalize = {}'.format(res))
        res = self.normalize(res)
        info('    res after normalize = {}'.format(res))

        return res
コード例 #2
0
ファイル: geometry_3d.py プロジェクト: dimapu/klayout_pyxs
    def merge_layers_same_z(self, layers):
        """
        Parameters
        ----------
        layers : list of MaterialLayer

        Returns
        -------
        res : list of MaterialLayer
        """
        n_layers = len(layers)
        res_merged = []

        for i, li in enumerate(layers):
            merged = MaterialLayer(li.mask, li.bottom, li.thickness)
            for j in range(i + 1, n_layers):
                lj = layers[j]
                if merged.is_z_same(lj):

                    # perform OR operation on the LayoutData
                    merged = MaterialLayer(merged.mask.or_(lj.mask),
                                           merged.bottom, merged.thickness)
                    info('    Merged layers b = {}, t = {}'.format(
                        merged.bottom, merged.top))
            res_merged.append(merged)
        return res_merged
コード例 #3
0
    def height(self, x):
        """ Configures the height of the processing window

        """
        self._height = int_floor(x / self._dbu + 0.5)
        info('XSG._height set to {}'.format(self._height))
        self._update_basic_regions()
コード例 #4
0
ファイル: geometry_3d.py プロジェクト: dimapu/klayout_pyxs
    def merge_layers_same_mask(self, layers):
        """
        Parameters
        ----------
        layers : list of MaterialLayer

        Returns
        -------
        res : list of MaterialLayer
        """
        _check_layer_list_sorted(layers)

        res_merged = []

        while layers:
            la = layers.pop(0)
            ib = 0
            while layers and (ib < len(layers)):
                lb = layers[ib]
                if la.top == lb.bottom:
                    if la.mask.data == lb.mask.data:
                        la = MaterialLayer(la.mask, la.bottom,
                                           lb.top - la.bottom)
                        info('    Merged layers ({},{}) and ({}, {})'.format(
                            la.bottom, la.top, lb.bottom, lb.top))
                        layers.pop(ib)
                elif la.top < lb.bottom:
                    # all following lb will be higher
                    res_merged.append(la)
                    break  # go to the next la
                ib += 1
            else:
                res_merged.append(la)
        return res_merged
コード例 #5
0
    def set_depth(self, x):
        """Set the depth of the processing window
        or the wafer thickness for backside processing (see below)

        """
        self._depth = int_floor(x / self._dbu + 0.5)
        info('XSG._depth set to {}'.format(self._depth))
        self._update_basic_regions()
コード例 #6
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def all(self):
        """ A pseudo-mask, covering the whole wafer

        Return
        ------
        res : MaterialData3D
        """
        res = self._mask_to_seed_material(
            LayoutData([Polygon(self._box_dbu)], self))
        info('    result: {}'.format(res))
        return res
コード例 #7
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def set_below(self, x):
        """ Configures the lower height of the processing window for backside processing

        Parameters
        ----------
        x : float
            depth below the wafer in [um]

        """
        self._below = int_floor(x / self._dbu + 0.5)
        info('XSG._below set to {}'.format(self._below))
        self._update_basic_regions()
コード例 #8
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def set_height(self, x):
        """ Configures the height of the processing window

        Parameters
        ----------
        x : float
            height in [um]

        """
        self._height = int_floor(x / self._dbu + 0.5)
        info('XSG._height set to {}'.format(self._height))
        self._update_basic_regions()
コード例 #9
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def set_depth(self, x):
        """ Configures the depth of the processing window
        or the wafer thickness for backside processing (see `below`)

        Parameters
        ----------
        x : float
            depth of the wafer in [um]
        """
        self._depth = int_floor(x / self._dbu + 0.5)
        info('XSG._depth set to {}'.format(self._depth))
        self._update_basic_regions()
コード例 #10
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def run(self):
        """ The basic generation method
        """

        if not self._setup():
            return None

        self._update_basic_regions()

        text = None
        with open(self._file_path) as file:
            text = file.read()

        if not text:
            MessageBox.critical("Error",
                                "Error reading file #{self._file_path}",
                                MessageBox.b_ok())
            return None

        # prepare variables to be visible in the script
        locals_ = dir(self)
        locals_dict = {}
        for attr in locals_:
            if attr[0] != '_':
                locals_dict.update({attr: getattr(self, attr)})

        try:
            exec(text, locals_dict)
        except Exception as e:
            # For development
            # print(e.__traceback__.)
            # print(dir(e))
            MessageBox.critical("Error", str(e), MessageBox.b_ok())
            # pass
            return None

        Application.instance().main_window().cm_lv_add_missing()  # @@@
        if self._lyp_file:
            self._target_view.load_layer_props(self._lyp_file)
        self._target_view.zoom_fit()
        self._target_layout.write(self._target_gds_file_name)

        info('    len(bulk.data) = {}'.format(len(self._bulk.data)))
        self._tech_str = '# This file was generated automatically by pyxs.\n\n'\
                         + layer_to_tech_str(255, self._bulk.data[0],
                                             'Substrate') + self._tech_str
        with open(self._target_tech_file_name, 'w') as f:
            f.write(self._tech_str)

        return None
コード例 #11
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def output(self, layer_spec, material, color=None):
        """ Outputs a material object to the output layout

        Parameters
        ----------
        layer_spec : str
            layer specification
        material : MaterialData3D
        """
        if not isinstance(material, MaterialData3D):
            raise TypeError(
                "'output' method: second parameter must be "
                "a material object (MaterialData3D). {} is given".format(
                    type(material)))

        # confine the shapes to the region of interest
        # info('    roi = {}'.format(self._roi))
        # info('    material = {}'.format(material))
        export_layers = self._lp.boolean_l2l(self._roi, material.data,
                                             LP.ModeAnd)
        info('    layers to export = {}'.format(export_layers))
        l, data_type, name = string_to_layer_info_params(layer_spec, True)
        # info('{}, {}, {}'.format(l, data_type, name))

        name = name if name else ''

        for i, layer in enumerate(export_layers):
            layer_not_empty = False
            layer_no = l + i
            if layer.thickness < MIN_EXPORT_LAYER_THICKNESS:
                continue  # next layer in the material

            ls = LayerInfo(layer_no, data_type,
                           '{} ({}-{})'.format(name, layer.bottom, layer.top))
            li = self._target_layout.insert_layer(ls)
            shapes = self._target_layout.cell(self._target_cell).shapes(li)
            for polygon in layer.mask.data:
                # info('S = {}, S_box = {}'
                #      .format(polygon.area(), polygon.bbox().area()))
                if polygon.area() > 0.001 * polygon.bbox().area():
                    layer_not_empty = True
                    shapes.insert(polygon)

            if layer_not_empty:
                self._tech_str += layer_to_tech_str(layer_no,
                                                    layer,
                                                    name=name,
                                                    color=color)
コード例 #12
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def mask(self, layer_data):
        """ Designates the layout_data object as a litho pattern (mask).

        This is the starting point for structured grow or etch operations.

        Parameters
        ----------
        layer_data : LayoutData

        Returns
        -------
        MaskData
        """
        info('    layer_data = {}'.format(layer_data))
        mask = layer_data.and_([Polygon(self._box_dbu)])
        info('    mask = {}'.format(mask))
        return self._mask_to_seed_material(mask)
コード例 #13
0
ファイル: geometry_3d.py プロジェクト: dimapu/klayout_pyxs
    def is_z_overlapping(self, other):
        """ Check two layers for overlap.

        Parameters
        ----------
        other : MaterialLayer

        Returns
        -------
        bool
        """
        info('   self.b, self.t, other.b, other.t = {} {} {} {}'.format(
            self.bottom, self.top, other.bottom, other.top))
        if (self._top <= other.bottom) or (self._bottom >= other.top):
            return False
        else:
            return True
コード例 #14
0
    def size_p2p(self, polygons, dx, dy=0, mode=2, rh=True, mc=True):
        """ Size the given polygons into polygons

        Parameters
        ----------
        polygons : list of Polygon
            The input polygons
        dx : int
            The sizing value in x direction in dbu
        dy : int (optional)
            The sizing value in y direction in dbu
        mode : int (optional)
            The sizing mode. Allowed values from 1 to 5
        rh : bool (optional)
            True, if holes should be resolved into the hull
        mc : bool (optional)
            True, if touching corners should be resolved into less connected
            contours

        Returns
        -------
        res : list of Polygon
            The output polygons

        """
        info('    polys  = {}'.format(polygons))
        info('    dx = {}, dy = {}'.format(dx, dy))
        res = super(EdgeProcessor, self).size_p2p(polygons, dx, dy, mode, rh,
                                                  mc)
        info('    EP.size_p2p().res = {}'.format(res))
        return res
コード例 #15
0
    def _update_basic_regions(self):

        h = self._height  # height above the wafer
        d = self._depth  # thickness of the wafer
        b = self._below  # distance below the wafer

        w = self._line_dbu.length()  # length of the ruler
        e = self._extend  # extend to the sides

        self._area = Box(-e, -(d + b), w + e, h)
        self._air = MaterialData([Polygon(Box(-e, 0, w + e, h))], self)
        self._air_below = MaterialData([Polygon(Box(-e, -(d + b), w + e, -d))],
                                       self)

        self._bulk = MaterialData([Polygon(Box(-e, -d, w + e, 0))], self)
        self._roi = Box(0, -(d + b), w, h)

        info('    XSG._area:      {}'.format(self._area))
        info('    XSG._roi:       {}'.format(self._roi))
        info('    XSG._air:       {}'.format(self._air))
        info('    XSG._bulk:      {}'.format(self._bulk))
        info('    XSG._air_below: {}'.format(self._air_below))
コード例 #16
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def _update_basic_regions(self):

        h = self._height  # height above the wafer
        d = self._depth  # thickness of the wafer
        b = self._below  # distance below the wafer

        # w = self._line_dbu.length()  # length of the ruler
        e = self._extend  # extend to the sides

        # TODO: add extend to the basic regions
        self._area = [
            MaterialLayer(LayoutData([Polygon(self._box_dbu)], self), -(b + d),
                          (b + d + h))
        ]  # Box(-e, -(d+b), w+e, h)
        self._roi = [
            MaterialLayer(LayoutData([Polygon(self._box_dbu)], self), -(b + d),
                          (b + d + h))
        ]  # Box(0, -(d + b), w, h)

        self._air = MaterialData3D(
            [MaterialLayer(LayoutData([Polygon(self._box_dbu)], self), 0, h)],
            self, 0)
        self._air_below = MaterialData3D([
            MaterialLayer(LayoutData([Polygon(self._box_dbu)], self), -(d + b),
                          b)
        ], self, 0)

        self._bulk = MaterialData3D(
            [MaterialLayer(LayoutData([Polygon(self._box_dbu)], self), -d, d)],
            self, 0)

        info('    XSG._area:      {}'.format(self._area))
        info('    XSG._roi:       {}'.format(self._roi))
        info('    XSG._air:       {}'.format(self._air))
        info('    XSG._bulk:      {}'.format(self._bulk))
        info('    XSG._air_below: {}'.format(self._air_below))
コード例 #17
0
    def all(self):
        """ A pseudo-mask, covering the whole wafer

        Return
        ------
        res : MaterialData
        """
        e = self._extend
        info('e = {}'.format(e))

        line_dbu = self._line_dbu
        info('line_dbu = {}'.format(line_dbu))

        res = self._xpoints_to_mask([[-e, 1], [line_dbu.length() + e, -1]])

        info('    all().res = {}'.format(res))
        return res
コード例 #18
0
    def __init__(self, air_polygons, mask_polygons, xs):
        """
        Parameters
        ----------
        air_polygons : list of Polygon
            list of shapes constituting air in cross-section
        mask_polygons : list of Polygon
            list of shapes constituting material in cross-section
        xs: XSectionGenerator
            passed to LayoutData.__init__()
        delta : float
            the intrinsic height (required for mask data because there
            cannot be an infinitely small mask layer (in database units)
        """
        super(MaskData, self).__init__([], xs)  # LayoutData()
        self._air_polygons = air_polygons
        self._mask_polygons = mask_polygons

        info('air_polygons = {}'.format(air_polygons))
        info('mask_polygons = {}'.format(mask_polygons))
        info('Success!')
コード例 #19
0
 def delta(self, x):
     self._delta = int_floor(x / self._dbu + 0.5)
     info('XSG._delta set to {}'.format(self._delta))
コード例 #20
0
    def _setup(self, p1, p2):
        """
        Parameters
        ----------
        p1 : Point
            first point of the ruler
        p2 : Point
            second point of the ruler

        """
        # locate the layout
        app = Application.instance()
        view = app.main_window().current_view()  # LayoutView
        if not view:
            MessageBox.critical(
                "Error",
                "No view open for creating the cross-"
                "section from",
                MessageBox.b_ok(),
            )
            return False

        cv = view.cellview(view.active_cellview_index())  # CellView
        if not cv.is_valid():
            MessageBox.critical("Error", "The selected layout is not valid",
                                MessageBox.b_ok())
            return False

        self._cv = cv  # CellView
        self._layout = cv.layout()  # Layout
        self._dbu = self._layout.dbu
        self._cell = cv.cell_index  # int

        # get the start and end points in database units and micron
        p1_dbu = Point.from_dpoint(p1 * (1.0 / self._dbu))
        p2_dbu = Point.from_dpoint(p2 * (1.0 / self._dbu))
        self._line_dbu = Edge(p1_dbu, p2_dbu)  # Edge describing the ruler

        # initialize height and depth
        self._extend = int_floor(2.0 / self._dbu + 0.5)  # 2 um in dbu
        self._delta = 10
        self._height = int_floor(2.0 / self._dbu + 0.5)  # 2 um in dbu
        self._depth = int_floor(2.0 / self._dbu + 0.5)  # 2 um in dbu
        self._below = int_floor(2.0 / self._dbu + 0.5)  # 2 um in dbu

        info('    XSG._dbu is:    {}'.format(self._dbu))
        info('    XSG._extend is: {}'.format(self._extend))
        info('    XSG._delta is:  {}'.format(self._delta))
        info('    XSG._height is: {}'.format(self._height))
        info('    XSG._depth is:  {}'.format(self._depth))
        info('    XSG._below is:  {}'.format(self._below))

        return True
コード例 #21
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
 def set_delta(self, x):
     """Configures the accuracy parameter
     """
     self._delta = int_floor(x / self._dbu + 0.5)
     info('XSG._delta set to {}'.format(self._delta))
コード例 #22
0
ファイル: geometry_3d.py プロジェクト: dimapu/klayout_pyxs
    def boolean_l2l(self, la, lb, mode, rh=True, mc=True):
        """
        Parameters
        ----------
        la : list of MaterialLayer or empty list
            sorted list. layers must not overlap with each other
        lb : list of MaterialLayer or empty list
            sorted list. layers must not overlap with each other
        mode: int
        rh : bool (optional)
            resolve_holes
        mc : bool (optional)
            min_coherence

        Returns
        -------
        list of MaterialLayer or []
        """
        n_la, n_lb = len(la), len(lb)  # number of polygons in pa and pb

        info('    n_la = {}, n_lb = {}, mode = {}'.format(n_la, n_lb, mode))

        ia, ib = 0, 0
        a = la[ia] if la else None
        b = lb[ib] if lb else None
        la_res, lb_res, oa, ob = [], [], [], []
        while a and b:
            info('    a = {}'.format(a))
            info('    b = {}'.format(b))
            if a.is_lower_s(b, 'bottom'):
                info('    a bottom is lower')
                top = min(a.top, b.bottom)
                info('    top = {}'.format(top))
                if top == a.top:  # no overlap
                    info('    a top is lower than b bottom, no overlap')
                    la_res += [a]
                    ia += 1
                    a = None if ia >= len(la) else la[ia]
                    continue
                else:
                    info('    a top is higher than b bottom, overlap')
                    # use part of a from a.bottom to top
                    la_res += [MaterialLayer(a.mask, a.bottom, top - a.bottom)]
                    # overlapping candidate a is a from top to a.top
                    a = MaterialLayer(a.mask, top, a.top - top)
                    continue
            elif b.is_lower_s(a, 'bottom'):
                info('    b is lower')
                top = min(b.top, a.bottom)
                if top == b.top:  # no overlap
                    lb_res += [b]
                    ib += 1
                    b = None if ib >= len(lb) else lb[ib]
                    continue
                else:
                    # use part of b from b.bottom to top
                    lb_res += [MaterialLayer(b.mask, b.bottom, top - b.bottom)]
                    # overlapping candidate b is b from top to b.top
                    b = MaterialLayer(b.mask, top, b.top - top)
                    continue
            else:
                assert a.bottom == b.bottom, 'bottoms must be equal here'
                info('    same bottom')
                if a.is_lower_s(b, 'top') or b.is_lower_s(a, 'top'):
                    top = min(a.top, b.top)
                    if top < b.top:  # a is in the overlap, b is higher
                        info('    b is higher')
                        oa += [a]
                        ob += [MaterialLayer(b.mask, b.bottom, top - b.bottom)]
                        b = MaterialLayer(b.mask, top,
                                          b.top - top)  # remaining top
                        ia += 1
                        a = None if ia >= len(la) else la[ia]
                        continue
                    elif top < a.top:  # b is in the overlap, a is higher
                        info('    a is higher')
                        ob += [b]
                        oa += [MaterialLayer(a.mask, a.bottom, top - a.bottom)]
                        a = MaterialLayer(a.mask, top,
                                          a.top - top)  # remaining top
                        ib += 1
                        b = None if ib >= len(lb) else lb[ib]
                        continue
                else:
                    assert a.top == b.top, 'tops must be equal here'
                    info('    same top')
                    oa += [a]
                    ob += [b]
                    ia += 1
                    a = None if ia >= len(la) else la[ia]
                    ib += 1
                    b = None if ib >= len(lb) else lb[ib]
                    continue

        if a:
            la_res += [a]
        if b:
            lb_res += [b]

        # add remaining a's and b's
        while ia < len(la) - 1:
            ia += 1
            la_res += [la[ia]]

        while ib < len(lb) - 1:
            ib += 1
            lb_res += [lb[ib]]

        lo_res = []
        for a, b in zip(oa, ob):
            o_polygons = self.boolean_p2p(a.mask.data, b.mask.data, mode, rh,
                                          mc)
            if o_polygons:
                lo_res += [
                    MaterialLayer(LayoutData(o_polygons, a.mask._xs), a.bottom,
                                  a.top - a.bottom)
                ]

        info('    la_res = {}'.format(la_res))
        info('    lb_res = {}'.format(lb_res))
        info('    lo_res = {}'.format(lo_res))

        if mode == self.ModeAnd:  # either la and lb is empty, mode AND
            info('    mode AND')
            res = lo_res  # will be empty
        elif mode == self.ModeOr or mode == self.ModeXor:
            info('    mode OR/XOR')
            res = la_res + lo_res + lb_res
        elif mode == self.ModeANotB:
            info('    mode ANotB')
            res = la_res + lo_res
        elif mode == self.ModeBNotA:
            info('    mode BNotA')
            res = lo_res + lb_res
        else:
            res = []

        res = self.normalize(res)
        # res = self.merge_layers_same_z(res)
        # res = self.merge_layers_same_mask(res)
        # res.sort()
        info('    boolean_l2l().res = {}'.format(res))
        return res
コード例 #23
0
    def planarize(self, *args, **kwargs):
        """ Planarization
        """
        downto = None
        less = None
        to = None
        into = []

        for k, v in kwargs.items():
            if k == 'downto':
                downto = make_iterable(v)
                for i in downto:
                    if not isinstance(i, MaterialData):
                        raise TypeError("'planarize' method: 'downto' expects "
                                        "a material parameter or an array "
                                        "of such")

            elif k == 'into':
                into = make_iterable(v)
                for i in into:
                    if not isinstance(i, MaterialData):
                        raise TypeError("'planarize' method: 'into' expects "
                                        "a material parameter or an array "
                                        "of such")
            elif k == 'less':
                less = int_floor(0.5 + float(v) / self.dbu)
            elif k == 'to':
                to = int_floor(0.5 + float(v) / self.dbu)

        if not into:
            raise ValueError("'planarize' requires an 'into' argument")

        info('   downto = {}'.format(downto))
        info('   less = {}'.format(less))
        info('   to = {}'.format(to))
        info('   into = {}'.format(into))

        if downto:
            downto_data = None
            if len(downto) == 1:
                downto_data = downto[0].data
            else:
                for i in downto:
                    if len(downto_data) == 0:
                        downto_data = i.data
                    else:
                        downto_data = self._ep.boolean_p2p(
                            i.data, downto_data, EP.ModeOr)

            # determine upper bound of material
            if downto_data:
                for p in downto_data:
                    yt = p.bbox().top
                    yb = p.bbox().bottom
                    to = to or yt
                    if not self._flipped:
                        to = max([to, yt, yb])
                    else:
                        to = min([to, yt, yb])
                info('    to = {}'.format(to))

        elif into and not to:

            # determine upper bound of our material
            for i in into:
                for p in i.data:
                    yt = p.bbox().top
                    yb = p.bbox().bottom
                    to = to or yt
                    if not self._flipped:
                        to = max([to, yt, yb])
                    else:
                        to = min([to, yt, yb])

        if to is not None:
            info('    to is true')
            less = less or 0
            if self._flipped:
                removed_box = Box(
                    -self._extend,
                    -self.depth_dbu - self.below_dbu,
                    self._line_dbu.length() + self._extend,
                    to + less,
                )
            else:
                removed_box = Box(
                    -self._extend,
                    to - less,
                    self._line_dbu.length() + self._extend,
                    self.height_dbu,
                )

            rem = LayoutData([], self)
            for i in into:
                rem.add(i.and_([Polygon(removed_box)]))
                i.sub([Polygon(removed_box)])

            self.air().add(rem)
コード例 #24
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def produce_geom(self, method, xy, z, into, through, on, taper, bias, mode,
                     buried):
        """

        method : str
        xy : float
            mask extension, lateral in [um]
        z : float
            vertical material size in [um]
        into : list of MaterialData3D
        through : list of MaterialData3D
        on : list of MaterialData3D
        taper : float
        bias : float
        mode : str
            'round|square|octagon'
        buried : float

        Returns
        -------
        layers : list of MaterialLayer
        """
        info('    method={}, xy={}, z={}, \n'
             '    into={}, through={}, on={}, \n'
             '    taper={}, bias={}, mode={}, buried={})'.format(
                 method, xy, z, into, through, on, taper, bias, mode, buried))

        prebias = bias or 0.0

        if xy < 0.0:
            xy = -xy
            prebias += xy
        '''
        if taper:
            d = z * math.tan(math.pi / 180.0 * taper)
            prebias += d - xy
            xy = d
        '''
        # determine the "into" material by joining the layers of all "into"
        # materials or taking air's layers if required.
        # Finally we get a into_layers : list of MaterialLayer
        if into:
            into_layers = []
            for i in into:
                info('    i = {}'.format(i))
                if len(into_layers) == 0:
                    into_layers = i.data
                else:
                    into_layers = self._lp.boolean_l2l(i.data, into_layers,
                                                       LP.ModeOr)
        else:
            # when deposit or grow is selected, into_layers is self._xs.air()
            into_layers = self._xs.air().data

        info('    into_layers = {}'.format(into_layers))

        # determine the "through" material by joining the layers of all
        # "through" materials
        # Finally we get a thru_layers : list of MaterialLayer
        if through:
            thru_layers = []
            for t in through:
                if len(thru_layers) == 0:
                    thru_layers = t.data
                else:
                    thru_layers = self._lp.boolean_l2l(t.data, thru_layers,
                                                       LP.ModeOr)
            info('    thru_layers = {}'.format(thru_layers))

        # determine the "on" material by joining the data of all "on" materials
        # Finally we get an on_layers : list of MaterialLayer
        if on:
            on_layers = []
            for o in on:
                if len(on_layers) == 0:
                    on_layers = o.data
                else:
                    on_layers = self._lp.boolean_l2l(o.data, on_layers,
                                                     LP.ModeOr)
            info('    on_layers = {}'.format(on_layers))

        offset = self._delta
        layers = self._layers
        info('    Seed material to be grown: {}'.format(self))
        '''
        if abs(buried or 0.0) > 1e-6:
            t = Trans(Point(
                    0, -_int_floor(buried / self._xs.dbu + 0.5)))
            d = [p.transformed(t) for p in d]
        '''

        # in the "into" case determine the interface region between
        # self and into
        if into or through or on:
            # apply an artificial sizing to create an overlap before
            if offset == 0:
                offset = self._xs.delta_dbu / 2
                layers = self._lp.size_l2l(layers, 0, dz=offset)

            if on:
                layers = self._lp.boolean_l2l(layers, on_layers, EP.ModeAnd)
            elif through:
                layers = self._lp.boolean_l2l(layers, thru_layers, EP.ModeAnd)
            else:
                layers = self._lp.boolean_l2l(layers, into_layers, EP.ModeAnd)
        info('    overlap layers = {}'.format(layers))

        pi = int_floor(prebias / self._xs.dbu + 0.5)
        info('    pi = {}'.format(pi))
        if pi < 0:
            layers = self._lp.size_l2l(layers, -pi, dy=-pi, dz=0)
        elif pi > 0:
            raise NotImplementedError('pi > 0 not implemented yet')
            # apply a positive prebias by filtering with a sized box
            dd = []
            for p in d:
                box = p.bbox()
                if box.width > 2 * pi:
                    box = Box(box.left + pi, box.bottom, box.right - pi,
                              box.top)

                    for pp in self._ep.boolean_p2p([Polygon(box)], [p],
                                                   EP.ModeAnd):
                        dd.append(pp)
            d = dd

        xyi = int_floor(xy / self._xs.dbu + 0.5)  # size change in [dbu]
        zi = int_floor(z / self._xs.dbu + 0.5) - offset  # height in [dbu]
        info('    xyi = {}, zi = {}'.format(xyi, zi))

        if taper:
            raise NotImplementedError('taper option is not supported yet')
            # d = self._ep.size_p2p(d, xyi, zi, 0)
        elif xyi <= 0:
            layers = self._lp.size_l2l(layers, 0, dy=0, dz=zi)
            # d = self._ep.size_p2p(d, 0, zi)
        elif mode == 'round':
            # same as square for now
            layers = self._lp.size_l2l(layers, xyi, dy=xyi, dz=zi)

            # raise NotImplementedError('round option is not supported yet')
            # emulate "rounding" of corners by performing soft-edged sizes
            # d = self._ep.size_p2p(d, xyi / 3, zi / 3, 1)
            # d = self._ep.size_p2p(d, xyi / 3, zi / 3, 0)
            # d = self._ep.size_p2p(d, xyi - 2 * (xyi / 3), zi - 2 * (zi / 3), 0)
        elif mode == 'square':
            layers = self._lp.size_l2l(layers, xyi, dy=xyi, dz=zi)
        elif mode == 'octagon':
            raise NotImplementedError('octagon option is not supported yet')
            # d = self._ep.size_p2p(d, xyi, zi, 1)

        if through:
            layers = self._lp.boolean_l2l(layers, thru_layers, LP.ModeANotB)

        info('    layers before and with into:'.format(layers))
        layers = self._lp.boolean_l2l(layers, into_layers, LP.ModeAnd)
        info('    layers after and with into:'.format(layers))

        info('    final layers = {}'.format(layers))
        if None:
            # remove small features
            # Hint: this is done separately in x and y direction since that is
            # more robust against snapping distortions
            layers = self._lp.size_p2p(layers, 0, self._xs.delta_dbu / 2)
            layers = self._lp.size_p2p(layers, 0, -self._xs.delta_dbu)
            layers = self._lp.size_p2p(layers, 0, self._xs.delta_dbu / 2)
            layers = self._lp.size_p2p(layers, self._xs.delta_dbu / 2, 0)
            layers = self._lp.size_p2p(layers, -self._xs.delta_dbu, 0)
            layers = self._lp.size_p2p(layers, self._xs.delta_dbu / 2, 0)

        return layers
コード例 #25
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def _setup(self):

        # locate the layout
        app = Application.instance()
        view = app.main_window().current_view()  # LayoutView
        if not view:
            MessageBox.critical(
                "Error", "No view open for creating the cross "
                "section from", MessageBox.b_ok())
            return False

        # locate the (single) ruler
        rulers = []
        n_rulers = 0
        for a in view.each_annotation():
            # Use only rulers with "plain line" style
            # print(a.style)
            # print(Annotation.StyleLine)
            # if a.style == Annotation.StyleLine:
            rulers.append(a)
            n_rulers += 1

        # if n_rulers == 0 or n_rulers >= 2:
        #     MessageBox.info("No rulers",
        #                         "Number of rulers is not equal to one. "
        #                         "Will be exporting the whole layout",
        #                         pya.MessageBox.b_ok())

        # if n_rulers == 1:
        #     MessageBox.info(
        #             "Box export", "One ruler is present for the cross "
        #             "section line. Will be exporting only shapes in the box",
        #             pya.MessageBox.b_ok())

        cv = view.cellview(view.active_cellview_index())  # CellView
        if not cv.is_valid():
            MessageBox.critical("Error", "The selected layout is not valid",
                                MessageBox.b_ok())
            return False

        self._cv = cv  # CellView
        self._layout = cv.layout()  # Layout
        self._dbu = self._layout.dbu
        self._cell = cv.cell_index  # int

        if n_rulers == 1:
            # get the start and end points in database units and micron
            p1_dbu = Point.from_dpoint(rulers[0].p1 * (1.0 / self._dbu))
            p2_dbu = Point.from_dpoint(rulers[0].p2 * (1.0 / self._dbu))
            self._box_dbu = Box(p1_dbu, p2_dbu)  # box describing the ruler
        else:
            # TODO: choose current cell, not top cell
            top_cell = self._layout.top_cell()
            p1_dbu = (top_cell.bbox().p1 * (1.0 / self._dbu)).dup()
            p1_dbu = top_cell.bbox().p1.dup()
            p2_dbu = (top_cell.bbox().p2 * (1.0 / self._dbu)).dup()
            p2_dbu = top_cell.bbox().p2.dup()
            self._box_dbu = Box(p1_dbu, p2_dbu)  # box describing the top cell

        info('XSG._box_dbu to be used is: {}'.format(self._box_dbu))

        # create a new layout for the output
        cv = app.main_window().create_layout(1)
        cell = cv.layout().add_cell("XSECTION")
        self._target_view = app.main_window().current_view()
        self._target_view.select_cell(cell, 0)
        self._target_layout = cv.layout()
        self._target_layout.dbu = self._dbu
        self._target_cell = cell

        # initialize height and depth
        self._extend = int_floor(2.0 / self._dbu + 0.5)  # 2 um in dbu
        self._delta = 10
        self._height = int_floor(2.0 / self._dbu + 0.5)  # 2 um in dbu
        self._depth = int_floor(2.0 / self._dbu + 0.5)  # 2 um in dbu
        self._below = int_floor(2.0 / self._dbu + 0.5)  # 2 um in dbu

        info('    XSG._dbu is:    {}'.format(self._dbu))
        info('    XSG._extend is: {}'.format(self._extend))
        info('    XSG._delta is:  {}'.format(self._delta))
        info('    XSG._height is: {}'.format(self._height))
        info('    XSG._depth is:  {}'.format(self._depth))
        info('    XSG._below is:  {}'.format(self._below))

        return True
コード例 #26
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
    def _mask_to_seed_material(self, mask):
        """ Convert mask to a seed material for growth / etch operations.

        Parameters
        ----------
        mask: LayoutData
            top view of the region to be grown / etched

        Return
        ------
        seed : MaterialData3D
            Thin seed material to be used in geometry generation.
        """
        info('    mask = {}'.format(mask))

        mask_material = [
            MaterialLayer(mask, -(self._depth + self._below),
                          self._depth + self._below + self._height)
        ]
        info('    mask material = {}'.format(mask))

        air = self._air.data
        info('    air =        {}'.format(air))

        air_sized = self._lp.size_l2l(air, 0, 0, self._delta)
        info('    air sized =  {}'.format(air_sized))

        # extended air minus air
        air_border = self._lp.boolean_l2l(air_sized, air, LP.ModeANotB)
        info('    air_border = {}'.format(air_border))

        # overlap of air border and mask layer
        seed_layers = self._lp.boolean_l2l(air_border, mask_material,
                                           EP.ModeAnd)

        info('    seed_layers= {}'.format(seed_layers))

        seed = MaterialData3D(seed_layers, self, self._delta)

        return seed
コード例 #27
0
    def _xpoints_to_mask(self, iv):
        """ Convert crossing points to a mask

        Parameters
        ----------
        iv : list of lists or list of tuple
            each list / tuple represents two coordinates.

        Return
        ------
        res : MaterialData
            Top ot the surface for deposition
        """
        info('    iv = {})'.format(iv))
        s = 0
        last_s = 0
        p1 = 0
        p2 = 0

        mask_polygons = []
        for i in iv:
            z = i[0]  # first coordinate
            s += i[1]  # increase second coordinate

            if last_s <= 0 < s:  # s increased and became > 0
                p1 = z
            elif last_s > 0 >= s:  # s decreased and became < 0
                p2 = z
                poly = Polygon(
                    Box(p1, -self._depth - self._below, p2, self._height))
                info('        Appending poly {}'.format(poly))
                mask_polygons.append(poly)
            last_s = s

        info('    mask_polys = {}'.format(mask_polygons))
        '''
        air = self._air.data
        info('    air =        {}'.format(air))

        # Sizing is needed only in vertical direction, it seems
        # air_sized = self._ep.size_p2p(air, self._delta, self._delta)
        air_sized = self._ep.size_p2p(air, self._delta, self._delta)
        info('    air_sized =  {}'.format(air_sized))

        # extended air minus air
        air_border = self._ep.boolean_p2p(air_sized, air, EP.ModeANotB)
        info('    air_border = {}'.format(air_border))

        # overlap of air border and mask polygons
        mask_data = self._ep.boolean_p2p(
                air_border, mask_polygons,
                EP.ModeAnd)
        info('    mask_data  = {}'.format(mask_data))

        # info('____Creating MD from {}'.format([str(p) for p in mask_data]))
        return MaterialData(mask_data, self)
        '''
        info('Before MaskData creation')
        res = MaskData(self._air.data, mask_polygons, self)
        info('res = {}'.format(res))
        return res
コード例 #28
0
ファイル: pyxs3D_lib.py プロジェクト: dimapu/klayout_pyxs
from klayout_pyxs import Action
from klayout_pyxs import FileDialog

from klayout_pyxs import Box
from klayout_pyxs import LayerInfo
from klayout_pyxs import Point
from klayout_pyxs import Polygon
from klayout_pyxs import Trans

from klayout_pyxs.utils import print_info, int_floor, make_iterable, info
from klayout_pyxs.geometry_2d import EP, LayoutData, parse_grow_etch_args
from klayout_pyxs.layer_parameters import string_to_layer_info_params
from klayout_pyxs.layer_parameters import string_to_layer_info
from klayout_pyxs.geometry_3d import MaterialLayer, LP, lp, layer_to_tech_str

info('Module pyxs3D_lib.py reloaded')
MIN_EXPORT_LAYER_THICKNESS = 5


class MaterialData3D(object):
    """ Class to operate 3D materials.

    3D material is described by its top view (mask), vertical
    position (elevation), and thickness.

    """
    def __init__(self, layers, xs, delta):
        """
        Parameters
        ----------
        layers : list of geometry_3d.MaterialLayer
コード例 #29
0
 def grow(self, *args, **kwargs):
     """ Same as deposit()
     """
     all = self.all()
     info(all)
     return all.grow(*args, **kwargs)
コード例 #30
0
ファイル: geometry_3d.py プロジェクト: dimapu/klayout_pyxs
    def split_overlapping_z(self, layers):
        """
        Parameters
        ----------
        layers : list of MaterialLayer
            a list of non-sorted and / or overlapping layers

        Returns
        -------
        res : list of MaterialLayer
            a sorted list of non-overlapping layers
        """
        info('    layers = {}'.format(layers))
        _check_layer_list_sorted(layers)

        res = []

        while layers:
            la = layers.pop(0)  # first element is the lowest in z
            info('    la = {}'.format(la))
            if not layers:  # la was the only element
                res += [la]
                continue

            lb = layers.pop(0)  # take next element
            info('    lb = {}'.format(lb))
            if la.is_lower(lb, levela='top', levelb='bottom'):
                # a is lower or touching
                # layers is sorted, la will not overlap with other lb
                res += [la]
                layers.insert(0, lb)
                info(
                    '    la top < lb btm, la is moved to result, lb is returned'
                )
            elif la.bottom == lb.bottom:
                info('    la btm == lb btm')
                if la.top < lb.top:
                    lb_split = lb.split_by_layer(la)
                    o = MaterialLayer(la.mask.or_(lb_split[0].mask), la.bottom,
                                      la.thickness)
                    layers = [o] + layers

                    # top part of b is inserted to layers, ensuring sorted order
                    i = 0
                    while i < len(layers):
                        if lb_split[1].bottom < layers[i].bottom:
                            layers.insert(i, lb_split[1])
                            break
                        elif lb_split[1].bottom == layers[i].bottom:
                            if lb_split[1].top <= layers[i].top:
                                layers.insert(i, lb_split[1])
                                break
                        i += 1
                    else:
                        layers.append(lb_split[1])
                    info('    la top < lb top, o calculated, added to layers')
                else:
                    # la is the same height as lb
                    # perform OR operation on the LayoutData
                    o = MaterialLayer(la.mask.or_(lb.mask), la.bottom,
                                      la.thickness)
                    layers.insert(0, o)
                    info('    la top == lb top, o calculated, added to layers')
            else:
                info(
                    '    la btm < lb btm, lower part of la is result, rest added to layers'
                )
                # lb bottom splits a somewhere (maybe lb top too)
                la_split = la.split_by_layer(lb)

                # bottom sublayer is not overlapping with lb and others
                res.append(la_split[0])
                layers = la_split[1:] + [lb] + layers

        info('    res = {}'.format(res))
        return res