def place_rects(rect_name, rect_length, rect_width, rect_angles, rect_x_coords, rect_y_coords): try: rect_width[0] except TypeError: rect_width = num.array([rect_width]) with nd.Cell(rect_name) as rect_array: for u in num.arange(rect_y_coords.size): for v in num.arange(rect_angles.size): # (c, s) = (num.cos(rect_angles[v]*num.pi/180), # num.sin(rect_angles[v]*num.pi/180)) # rotation = num.array(((c, -s), (s, c))) rect_pts = num.array([[0, 0, rect_width[u], rect_width[u]], [0, rect_length, rect_length, 0]]) rect_pts = num.transpose( num.dot(rotation_mat(rect_angles[v]), rect_pts)) rect_pts = geom.transform(points=rect_pts, move=(rect_x_coords[v], rect_y_coords[u], 0)) nd.Polygon(points=rect_pts, layer='layer3').put(0) message = 'Angle = ' + num.array2string(rect_angles[0]) + \ ' to ' + num.array2string(rect_angles[-1]) + ', width = ' if rect_width.size == 1: message += rect_width else: message += num.array2string(rect_width[u]) nd.text(text=message, height=20, layer='layer3', align='rb') \ .put(int(rect_x_coords[-2] + 250 ), int(rect_y_coords[u]) + 100) rect_array.put(0) return rect_array
def RF_pad(self, pin): """Standard RF pad for pixapp layout. The current pin will be set at rc of the pad. Args: pin (Node): pin to connect standard RF pad to. Returns: Cell: cell with RF pad and name of the pad """ cfg.cp = pin pnpos = pin.name.find('_rf') padname = pin.name[pnpos + 1:pnpos + 6] with nd.Cell('RF-pad-' + padname) as C: pad = sp.gsg_pad(length_pad=90, width_pad_sig=70, width_pad_gnd=70, gap2=80 + 30).put('rc') pad.raise_pins() C.default_pins('rc', 'c1') txt = '{}\n{}'.format(padname[0:2], padname[2:]) nd.text(txt, height=30, align='ct', layer=self.textlayer).\ put(pad.pin['c1'].move(-7,0,-90)) return C
def bezier(a=0, b=0, c=0, p0_x=0, p0_y=0, length=2000, width=5000, orientation=-1): """Create a Bezier waveguide based on parameters a, b and c.""" with nd.Cell(name='Bezier') as bez: t = np.arange(0, 1, 0.001) width = width * orientation p1_x = p0_x + length p1_y = p0_y p2_x = p1_x p2_y = p1_y - width p3_x = p0_x p3_y = p0_y - width x = p0_x * (1 - t)**3 + 3 * p1_x * t * ( 1 - t)**2 + 3 * p2_x * t * t * (1 - t) + p3_x * t**3 y = p0_y * (1 - t)**3 + 3 * p1_y * t * ( 1 - t)**2 + 3 * p2_y * t * t * (1 - t) + p3_y * t**3 #l = list(product(x, y)) #mylist = [[0,1], [0,2], [0,2], [0,5]]; x1 = np.array([x]) y1 = np.array([y]) x2 = x1.T y2 = y1.T bezier_curve = np.hstack((x2, y2)) #bezier_curve = [(0, 0), (3, 3), (500, 500),(500, 900)] # Bezier waveguide outline #nd.Polygon(layer=1,points=bezier_curve ).put(0) nd.Polyline(layer=0, points=bezier_curve, width=50).put(0) #add pins to the input and output, pointing outwards of the bezier guide nd.Pin('a0').put(0, 0, 180) #if bezier starts in (0, 0, 0) # nd.Pin('b0').put(xout, yout, aout) nd.strt(length=100, width=100).put(p0_x, p0_y) nd.strt(length=100, width=100).put(p1_x, p1_y) nd.strt(length=100, width=100).put(p2_x, p2_y) nd.strt(length=100, width=100).put(p3_x, p3_y) width_x = (p1_x + p2_x) / 2 width_y = (p1_y + p2_y) / 2 nd.text('width', height=250, align='cc', layer=2).put(width_x, width_y) length_x = (p0_x + p1_x) / 2 length_y = (p0_y + p1_y) / 2 nd.text('length', height=250, align='cc', layer=2).put(length_x, length_y) return bez
def place_cross(cross_name, x, y, cross_w, cross_l): try: cross_w[0] except TypeError: cross_w = num.array([cross_w]) with nd.Cell(name=cross_name) as xs: for u in num.arange(num.size(cross_w)): box_pts = num.array([[0, 0, cross_l, cross_l], [cross_w[u], 0, 0, cross_w[u]]]) xs_box_h = geom.transform( points=num.transpose(box_pts), move=(+x, y + (cross_l - cross_w[u]) / 2 + u * (cross_l + 50), 0)) xs_box_v = geom.transform(points=num.transpose( num.dot(rotation_mat(90), box_pts)), move=(+x + (cross_l + cross_w[u]) / 2, +y + u * (cross_l + 50), 0)) xs_box_30 = geom.transform( points=num.transpose(num.dot(rotation_mat(30), box_pts)), move=(+x + (cross_l - cross_l * num.cos(num.pi / 6) + cross_w[u] * num.sin(num.pi / 6)) / 2, +y + (cross_l - cross_l * num.sin(num.pi / 6) - cross_w[u] * num.cos(num.pi / 6)) / 2 + u * (cross_l + 50), 0)) xs_box_60 = geom.transform( points=num.transpose(num.dot(rotation_mat(60), box_pts)), move=(+x + (cross_l - cross_l * num.cos(num.pi / 3) + cross_w[u] * num.sin(num.pi / 3)) / 2, +y + (cross_l - cross_l * num.sin(num.pi / 3) - cross_w[u] * num.cos(num.pi / 3)) / 2 + u * (cross_l + 50), 0)) xs_box_120 = geom.transform(xs_box_60, flipy=True, move=(0, +2 * y + cross_l + 2 * u * (cross_l + 50), 0)) xs_box_150 = geom.transform(xs_box_30, flipx=True, move=(+2 * x + cross_l, 0, 0)) nd.Polygon(points=xs_box_h, layer='layer3').put(0) nd.Polygon(points=xs_box_v, layer='layer3').put(0) nd.Polygon(points=xs_box_30, layer='layer3').put(0) nd.Polygon(points=xs_box_60, layer='layer3').put(0) nd.Polygon(points=xs_box_120, layer='layer3').put(0) nd.Polygon(points=xs_box_150, layer='layer3').put(0) message_txt = 'Height = ' + str(cross_l) + ', Width = ' + str( cross_w[u]) nd.text(text=message_txt, height=20, layer='layer3') \ .put(int(x - cross_l / 2 - 150), int(y + u * (cross_l + 50))) xs.put(0) return
def compass(size=200, layer=1): """Show compass image to identify the sample direction. Args: size (double): size (wxh) of the bounding box. layer (int | str | tuple): layer (or list of layers) to draw in. Returns: cell containing the compass. """ s = size/182 quart = [(0,0), (s,0), (s,s), (16*s,16*s), (s/2,70*s), (-s/2,70*s), (-16*s,16*s), (-15*s,15*s), (-s,65*s), (0,65*s)] p = 75 * s h = 20 * s with nd.Cell("compass_"+nd.md5(layer)) as C: for lay in nd.make_iter(layer): nd.Polygon(layer=lay, points=quart).put(0,0,0) nd.Polygon(layer=lay, points=quart).put(0,0,90) nd.Polygon(layer=lay, points=quart).put(0,0,180) nd.Polygon(layer=lay, points=quart).put(0,0,270) nd.text('E', layer=lay, height=h, align='lc').put(p, 0) nd.text('N', layer=lay, height=h, align='cb').put(0, 71*s) nd.text('W', layer=lay, height=h, align='rc').put(-p, 0) nd.text('S', layer=lay, height=h, align='ct').put(0, -p) return C
def cell(length=length, height=height, cleave=cleave, pitch=pitch): """Create a cell boundary. Returns: Cell """ with nd.Cell(hashme=True) as C: pdk.parameters('hashme').put(0) #TODO: No foundry specific stuff here: remove. for lay, grow, acc in nd.layeriter(xs): frame = geom.frame(sizew=cleave, sizel=length, sizeh=height, grow=grow) nd.Polygon(layer=lay, points=frame).put(0) #Placing IOs amount_ios = round((height - cleave - pitch) / pitch) #IOs positions lay = 'AnnotationIO' for no in range(0, amount_ios): pinID = 'ioL{:03d}'.format(no) angle = 0 p = nd.Pin(name=pinID).put(-cleave, pitch + no * pitch, angle) p = nd.Pin(name='ioL' + str(no)).put(-cleave, pitch + no * pitch, angle) pdk.arrow.put(p) nd.text(pinID, layer=lay, height=0.15, align='rc').put(p.move(-0.1)) for no in range(0, amount_ios): mo = amount_ios + no pinID = 'ioR{:03d}'.format(no) angle = 0 p = nd.Pin(name=pinID).put(length, pitch + no * pitch, 180 + angle) p = nd.Pin(name='ioR' + str(no)).put(length, pitch + no * pitch, 180 + angle) pdk.arrow.put(p) nd.text(pinID, layer=lay, height=0.15, align='lc').\ put(p.move(-0.1, 0, 180)) C.groupname = groupname return C
def _put_arrow(self, loc, name, i): self.arrow.put(loc) t = nd.text(name.format(i), layer=annotationlayer, height=textheight, align='rc') t.put(loc.move(textmove))
def cellname(cellname=None, length=0, width=None, align='lc'): """Create the cellname as a text cell. Args: cellname (str): name of the cell length (float): length available for the BB name in um width (float): align (str): text alignment (see nazca.text, default = 'lc') Returns: Cell: text with cellname """ cell = cfg.cells[-1] if cellname is None: cellname = cell.cell_paramsname if width is not None: maxheight = min(width * 0.8, cfg.cellname_max_height) else: maxheight = cfg.cellname_max_height length = length * cfg.cellname_scaling texth = min(maxheight, abs(length) / nd.linelength(cellname, 1)) txt = nd.text(cellname, texth, layer='bbox_name', align=align) return txt
def place_blks(block_name, block_height, block_width, block_angles, block_x_coords, block_y_coords): with nd.Cell(block_name) as blk_arr: for u in num.arange(block_width.size): for v in num.arange(block_angles.size): block_pts = geom.parallelogram(length=block_width[u], height=block_height, angle=block_angles[v], shift=(block_x_coords[v], block_y_coords[u], 0)) nd.Polygon(points=block_pts, layer='layer3').put(0) message = 'Angle = ' + num.array2string(block_angles[0]) + \ ' to ' + num.array2string(block_angles[-1]) + \ ', Width = ' + num.array2string(block_width[0] * num.sin(num.pi/180 *block_angles[0])) nd.text(text=message, height=20, layer='layer3') \ .put(int(block_x_coords[-1] + 0.5 * block_x_coords[0]), int(block_y_coords[u])) blk_arr.put(0) return blk_arr
def DC_pad(self, pin): """Standard DC pad for pixapp layout. The current pin will be set at rc of the pad. Args: pin (Node): pin to connect standard DC pad to. Returns: Cell: cell with DC pad and name of the pad """ cfg.cp = pin pnpos = pin.name.find('_dc') padname = pin.name[pnpos + 1:pnpos + 7] with nd.Cell('DC-pad-' + padname, instantiate=False) as C: pad = self.DCpadcell.put('rc') pad.raise_pins() C.default_pins('rc', 'c0') txt = '{}\n{}'.format(padname[0:3], padname[3:]) nd.text(txt, height=30, align='cc', layer=self.textlayer).\ put(pad.pin['c0'].move(0,0,-90)) return C
def whereami(text='here', size=100, pin=None): """Show current pointer position as arrow in the layout. Args: text (str): annotation text size (float): size of the annotation Returns: None """ layer = 500 nd.cp.push() if pin is None: pin = nd.cp.here() points = [(0, 0), (-0.5, 0.5), (-0.4, 0.25), (-1, 0.3), (-1, -0.3), (-0.4, -0.25), (-0.5, -0.5)] points = [(x * size, y * size) for x, y in points] with Cell('I am'.format(wherecnt)) as crisis: nd.Polygon(layer=layer, points=points).put(0) nd.text(text + ' ', height=size / 4, align='rc', layer=layer).put(0) crisis.put(pin) nd.cp.pop()
def cell(self): """Create a Cell with DC, RF, Optical fiber postitions. Returns: Cell: package cell """ with nd.Cell(name=self.name) as C: for arr in self.pads: count = arr['count'] #Placing PAD IOs IOcountmax = round(\ (self.die_length - 2*arr['edge_sep_side']) / arr['pitch']) if count is not None and count < IOcountmax: IOcount = count else: IOcount = IOcountmax pin = arr['edge'] + arr['type'] if arr['edge'] == 'left': x0, y0, a0 = 0, 0, 90 elif arr['edge'] == 'top': x0, y0, a0 = 0, self.die_height, 0 elif arr['edge'] == 'right': x0, y0, a0 = self.die_length, self.die_height, -90 elif arr['edge'] == 'bottom': x0, y0, a0 = self.die_length, 0, 180 else: print("Edge not recognized in Package2: '{}'".format( arr['edge'])) #PAD positions for n in range(0, IOcount): pinID = pin_naming[pin].format(n) if arr['center']: if arr['edge'] in ['top', 'bottom']: dist_edge = self.die_length -\ (IOcount-1)*arr['pitch'] - 2*arr['edge_sep_side'] elif arr['edge'] in ['left', 'right']: dist_edge = self.die_height -\ (IOcount-1)*arr['pitch'] - 2*arr['edge_sep_side'] else: dist_edge = 0 #place metal io pins start = nd.Pin().put(x0, y0, a0) p = nd.Pin(pinID).put( start.move( n * arr['pitch'] + arr['edge_sep_side'] + 0.5 * dist_edge, -arr['edge_sep_front'], -90)) self.arrow.put(p) text = nd.text(pinID, layer=annotationlayer, height=textheight, align='rc') text.put(p.move(textmove)) #Fiber positions dist_edgey = self.die_height - self.cleave - self.fiberarea faa_len = self.cleave + 50 if self.show_fiberarea: farea = geom.box(width=self.fiberarea, length=faa_len) poly = nd.Polygon(layer=fiberIOlayer, points=farea) poly.put(-faa_len - 0.5 * self.cleave, 0.5 * dist_edgey + 0.5 * self.fiberarea, 0) else: print( 'Set show_fiberarea == True to see the fiber allowed area for this package.' ) return C
def cell(self): """Create a Cell with DC, RF, Optical fiber postitions. Returns: Cell: package cell """ with nd.Cell(name=self.name) as C: #Placing PAD IOs IOcountmax = round( (self.die_length - 2 * self.DCside) / self.DCpitch) if self.DCcount is not None and self.DCcount < IOcountmax: IOcount = self.DCcount else: IOcount = IOcountmax #DC PAD positions for n in range(0, IOcount): pinIDT = DCtopname.format(n) pinIDB = DCbotname.format(n) if self.DCcenter: dist_edgex = 0.5 * (self.die_length - (IOcount - 1) * self.DCpitch) else: dist_edgex = self.DCside #Top DC ports p = nd.Pin(pinIDT).put( dist_edgex + n * self.DCpitch - 0.5 * self.cleave, self.die_height - self.DCedge - 0.5 * self.cleave, -90) self.arrow.put(p) text = nd.text(pinIDT, layer=annotationlayer, height=textheight, align='rc') text.put(p.move(textmove)) #Bottom DC ports p = nd.Pin(pinIDB).put( dist_edgex + n * self.DCpitch - 0.5 * self.cleave, self.DCedge - 0.5 * self.cleave, 90) self.arrow.put(p) text = nd.text(pinIDB, layer=annotationlayer, height=textheight, align='rc') text.put(p.move(textmove)) #DC PAD positions double row if self.double_row_DC: for ndr in range(0, IOcount - 1): n = ndr + IOcountmax pinIDT = DCtopname.format(n) pinIDB = DCbotname.format(n) if self.DCcenter == True: dist_edgex = self.die_length - self.cleave -\ (IOcount-1)*self.DCpitch-2*self.DCside else: dist_edgex = 0 if self.DCx_doublerow == None: self.DCx_doublerow = 0.5 * self.DCpitch if self.DCy_doublerow == None: self.DCy_doublerow = 1.0 * self.DCpitch #Top DC ports p = nd.Pin(pinIDT).put( ndr * self.DCpitch + self.DCside + 0.5 * dist_edgex + self.DCx_doublerow, self.die_height - self.cleave - self.DCedge - self.DCy_doublerow, -90) self.arrow.put(p) text = nd.text(pinIDT, layer=annotationlayer, height=textheight, align='rc') text.put(p.move(textmove)) #Bottom DC ports p = nd.Pin(pinIDB).put( ndr * self.DCpitch + self.DCside + 0.5 * dist_edgex + self.DCx_doublerow, self.DCedge + self.DCy_doublerow, 90) self.arrow.put(p) text = nd.text(pinIDB, layer=annotationlayer, height=textheight, align='rc') text.put(p.move(textmove)) #RF PAD positions IOcountmax = round( (self.die_height - 2 * self.RFside) / self.RFpitch) if self.RFcount is not None and self.RFcount < IOcountmax: IOcount = self.RFcount else: IOcount = IOcountmax for n in range(0, IOcount): piRFn = RFname.format(n) if self.RFcenter: dist_edgey = self.die_height-self.cleave -\ (IOcount-1)*self.RFpitch-2*self.RFside else: dist_edgey = 0 p = nd.Pin(piRFn).put( self.die_length - self.cleave - self.RFedge, n * self.RFpitch + self.RFside + 0.5 * dist_edgey, 180) self.arrow.put(p) text = nd.text(piRFn, layer=annotationlayer, height=textheight, align='rc') text.put(p.move(textmove)) #RF PAD positions double row if self.double_row_RF: for ndr in range(0, IOcount - 1): n = ndr + IOcountmax piRFn = RFname.format(n) if self.RFcenter == True: dist_edgey = self.die_height-self.cleave -\ (IOcount-1)*self.RFpitch-2*self.RFside else: dist_edgey = 0 if self.RFx_doublerow == None: self.RFx_doublerow = 1.0 * self.RFpitch if self.RFy_doublerow == None: self.RFy_doublerow = 0.5 * self.RFpitch p = nd.Pin(piRFn).put(\ self.die_length-self.cleave-self.RFedge-self.RFx_doublerow, ndr*self.RFpitch+self.RFside+0.5*dist_edgey+self.RFy_doublerow, 180) self.arrow.put(p) text = nd.text(piRFn, layer=annotationlayer, height=textheight, align='rc') text.put(p.move(textmove)) #Fiber positions dist_edgey = self.die_height - self.cleave - self.fiberarea faa_len = self.cleave + 50 if self.show_fiberarea: farea = geom.box(width=self.fiberarea, length=faa_len) poly = nd.Polygon(layer=fiberIOlayer, points=farea) poly.put(-faa_len - 0.5 * self.cleave, 0.5 * dist_edgey + 0.5 * self.fiberarea, 0) else: print( 'Set show_fiberarea == True to see the fiber allowed area for this package.' ) return C
def cell(self): """Create a Project Cell with IO postition pins. Returns: Cell: cell containing design area and cleave boundaries """ with nd.Cell(name=self.name) as C: # put the dice/cleave border frame = geom.frame(sizew=self.cleave, sizel=self.length, sizeh=self.height) nd.Polygon(layer='DiceArea', points=frame).put(0) nd.Pin('left', show=False).put(-0.5 * (self.cleave), -0.5 * self.cleave + 0.5 * self.height, 0) tdk.put_boundingbox('left', self.length, self.height) # put the pins IOcountmax = round( (self.height - self.cleave - self.pitch) / self.pitch) pin_indent = 0 ioy0 = 50 for no in range(0, IOcountmax): pinIDL = 'ioL{:03d}'.format(no) pinIDR = 'ioR{:03d}'.format(no) if no % 2 is 0: angle = 7 else: angle = 0 #IOs left p = nd.Pin(name=pinIDL, xs='Shallow', type=angle).put(-self.cleave + pin_indent, ioy0 + no * self.pitch, angle) self.arrow.put(p) nd.text(pinIDL, layer=package_pin_text, height=0.15, align='rc').put(p.move(-0.1)) #IOs right p = nd.Pin(name=pinIDR, xs='Shallow', type=angle).put(-pin_indent + self.length, ioy0 + no * self.pitch, 180 + angle) self.arrow.put(p) nd.text(pinIDR, layer=package_pin_text, height=0.15, align='rc').put(p.move(-0.575, 0, 180)) # Adding coating options = { 'AR': 'Coating_AR', 'HR': 'Coating_HR', 'NO': 'Coating_NO', 'DC': 'Coating_DC' } coating_on_chip = 100 box_coatno = [(0, 0), (self.cleave / 2, 0), (self.cleave / 2, self.height), (0, self.height)] box_coatother = [(0, 0), (coating_on_chip + 0.5 * self.cleave, 0), (coating_on_chip + 0.5 * self.cleave, self.height), (0, self.height)] pinL = nd.Pin().put(-0.5 * self.cleave, -0.5 * self.cleave) pinR = nd.Pin().put(self.length - 0.5 * self.cleave, self.height - 0.5 * self.cleave, 180) coatings = {'L': (self.coatingL, pinL), 'R': (self.coatingR, pinR)} for coat, pin in coatings.values(): if coat in options.keys(): layer = options[coat] else: print( 'Available coating options: AR | HR | NO | DC. Default is NO.' ) if coat is 'NO': box = box_coatno else: box = box_coatother nd.Polygon(layer=layer, points=box).put(pin) return C return cell
def cell(cleave=cleave, length=length, height=height, coatingW=coatingW, coatingE=coatingE): """Create a Cell_Boundary cell. Returns: Cell """ with nd.Cell(hashme=True) as C: pdk.put_boundingbox('org', length, height, outline=False, \ move=(-0.5*cleave, 0.5*height-0.5*cleave)) pdk.parameters('hashme').put(0, 'cc') #TODO: No foundry specific stuff here: remove. outline = [(0, 0), (length - cleave, 0), (length - cleave, height - cleave), (0, height - cleave)] nd.Polygon(layer='Polyimide1Base', points=outline).put(0) for lay, grow, acc in nd.layeriter(xs): frame = geom.frame(sizew=cleave, sizel=length, sizeh=height, grow=grow) nd.Polygon(layer=lay, points=frame).put(0) ##adding coating to nd.Cell boundary: west and east options = { 'AR': 'Coating_AR', 'HR': 'Coating_HR', 'NO': 'Coating_NO', 'DC': 'Coating_DC' } coating_on_chip = nd.get_parameter('cell_coating_on_chip') box_coatno = [(0, 0), (cleave / 2, 0), (cleave / 2, height), (0, height)] box_coatother = [(0, 0), (coating_on_chip + 0.5 * cleave, 0), (coating_on_chip + 0.5 * cleave, height), (0, height)] pinW = nd.Pin().put(-0.5 * cleave, -0.5 * cleave) pinE = nd.Pin().put(length - 0.5 * cleave, height - 0.5 * cleave, 180) coatings = {'E': (coatingE, pinE), 'W': (coatingW, pinW)} for coat, pin in coatings.values(): if coat in options.keys(): layer = options[coat] else: print( 'Available coating options: AR | HR | NO | DC. Default is NO.' ) if coat is 'NO': box = box_coatno else: box = box_coatother nd.Polygon(layer=layer, points=box).put(pin) #Placing IOs pitch = 12.5 amount_ios = round((height - cleave - pitch) / pitch) #IOs positions lay = 'bb_pin' for no in range(0, amount_ios): pinID = 'io{:03d}'.format(no) if no % 2 is 0: angle = 7 else: angle = 0 p = nd.Pin(name=pinID).put(-cleave / 2, 12.5 + no * pitch, angle) p = nd.Pin(name='io' + str(no)).put(-cleave / 2, 12.5 + no * pitch, angle) pdk.make_pincell().put(p) nd.text(pinID, layer=lay, height=0.15, align='rc').put(p.move(-0.1)) for no in range(0, amount_ios): mo = amount_ios + no pinID = 'io{:03d}'.format(mo) if mo % 2 is 0: angle = 7 else: angle = 0 p = nd.Pin(name=pinID).put(length - cleave / 2, 2 * 12.5 + no * pitch, 180 + angle) pdk.make_pincell().put(p) nd.text(pinID, layer=lay, height=0.15, align='lc').\ put(p.move(-0.1, 0, 180)) C.groupname = groupname return C
pitch = 50 wafer = 25.4 * 4 * 1000 w = 2.3 "wg width" pos_y = 0 nd.bend(angle=360, radius=25.4 * 1000 * 2, width=w).put(25.4 * 1000 * 2, -25.4 * 1000 * 2) # dose 600mj/cm2 n = 0 while n < 10: nd.strt(length=25.4 * 2 * 1000, width=w).put(25.4 * 1000, 25.4 * 1000 + n * 50) n = n + 1 nd.text('600', height=2000).put() # dose 800mj/cm2 n = 0 while n < 10: nd.strt(length=25.4 * 2 * 1000, width=w).put(25.4 * 1000, n * 50 - 10 * 1000) n = n + 1 nd.text('XL', height=5000).put(48000, 20000) # dose 1000mj/cm2 n = 0 while n < 10: nd.strt(length=25.4 * 2 * 1000, width=w).put(25.4 * 1000,