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
def layer(self, layer_spec): """ Fetches an input layer from the original layout. Parameters ---------- layer_spec : str Returns ------- ld : LayerData """ ld = LayoutData([], self) # empty # collect shapes from the corresponding layer into ld._polygons ld.load(self._layout, self._cell, self._line_dbu.bbox().enlarge( Point(self._extend, self._extend)), layer_spec) return ld
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
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))
def inverted(self): """ Calculate inversion of the material. Total region is determined by self._xs.background(). Returns ------- res : MaterialData3D """ return MaterialData3D( self._lp.boolean_l2l(self._layers, [ MaterialLayer( LayoutData([Polygon(self._xs.background())], self._xs), -(self._xs.depth_dbu + self._xs.below_dbu), self._xs.depth_dbu + self._xs.below_dbu + self._xs.height_dbu) ], EP.ModeXor), self._xs, self._delta)
def planarize(self, into=[], downto=[], less=None, to=None, **kwargs): """Planarization """ if not into: raise ValueError("'planarize' requires an 'into' argument") into = make_iterable(into) for i in into: # should be MaterialData @@@ if not isinstance(i, MaterialData3D): raise TypeError("'planarize' method: 'into' expects " "a material parameter or an array " "of such") downto = make_iterable(downto) for i in downto: # should be MaterialData @@@ if not isinstance(i, MaterialData3D): raise TypeError("'planarize' method: 'downto' expects " "a material parameter or an array " "of such") if less is not None: less = int_floor(0.5 + float(less) / self.dbu) if to is not None: to = int_floor(0.5 + float(to) / self.dbu) if downto: downto_data = [] for d in downto: if len(downto_data) == 0: downto_data = d.data else: downto_data = self._lp.boolean_p2p(d.data, downto_data, LP.ModeOr) # determine upper bound of material if downto_data: raise NotImplementedError('downto not implemented yet') 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]) elif into and not to: raise NotImplementedError('into and not to not implemented yet') # 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: less = less or 0 if self._flipped: removed_box = MaterialLayer( LayoutData([ Polygon( self._box_dbu.enlarged( Point(self._extend, self._extend))) ], self), -(self.depth_dbu + self.below_dbu), (to + less) + self.depth_dbu + self.below_dbu) else: removed_box = MaterialLayer( LayoutData([ Polygon( self._box_dbu.enlarged( Point(self._extend, self._extend))) ], self), to - less, self.height_dbu - (to - less)) rem = MaterialData3D([], self, self._delta) for i in into: rem.add(i.and_([removed_box])) i.sub([removed_box]) self.air().add(rem) self.air().close_gaps()
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
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)