def _example_ii(): from gdshelpers.parts.splitter import MMI from gdshelpers.geometry import geometric_union mmi1 = MMI((0, 0), 0, 1, 42, 7.7, 2, 2) mmi2 = MMI((0, 10), 0, 1, 42, 7.7, 2, 2) mmi3 = MMI((0, -10), 0, 1, 42, 7.7, 2, 2) mmi4 = MMI((0, -20), 0, 1, 42, 7.7, 2, 2) mmi5 = MMI((0, -30), 0, 1, 42, 7.7, 2, 2) shapely_objects = [] s3d1 = Shapely3d( geometric_union([ mmi1.get_shapely_object(), mmi2.get_shapely_object(), mmi3.get_shapely_object() ]), 0.0, 0.7, (255, 0, 0)) s3d2 = Shapely3d( geometric_union([mmi4.get_shapely_object(), mmi5.get_shapely_object()]), 0.0, 0.7, (0, 255, 0)) shapely_objects.append(s3d1) shapely_objects.append(s3d2) render_image_and_save_as_blend(shapely_objects, "mmi2")
def _example(): from gdshelpers.geometry.chip import Cell from gdshelpers.parts.waveguide import Waveguide from gdshelpers.parts.resonator import RingResonator # ==== create some sample structures (straight line with ring resonator) wg = Waveguide(origin=(0, 0), angle=np.deg2rad(-90), width=1) wg.add_straight_segment(length=5) wg.add_bend(np.pi / 2, 5) wg2 = Waveguide.make_at_port(wg.current_port) wg2.add_straight_segment(15) reso = RingResonator.make_at_port(port=wg2.current_port, gap=0.2, radius=5) wg2.add_straight_segment(length=15) coupler2 = GratingCoupler.make_traditional_coupler_from_database_at_port( wg2.current_port, db_id='si220', wavelength=1550) underetching_parts = geometric_union([wg2, reso, coupler2]) structure = geometric_union([underetching_parts, wg]) # create the holes with a radius of 0.5 microns, a distance of 2 microns to the structure borders and # a distance of 2 microns between the holes holes = create_holes_for_under_etching(underetch_parts=underetching_parts, complete_structure=structure, hole_radius=0.5, hole_distance=2, hole_spacing=3, hole_length=3) # create a cell with the structures in layer 1 and the holes in layer 2 cell = Cell('CELL') cell.add_to_layer(1, structure) cell.add_to_layer(2, holes) # Show the cell cell.show()
def make_electrodes(self): bottom_left = self.nanowire_port.origin + np.array( [0.5 * self.electrodes_gap, self.nw_length + 25.]) top_left = bottom_left + (0, self.electrodes_height) electrode_width = self.electrodes_pitch - self.electrodes_gap top_right = top_left + (electrode_width, 0) bottom_right = (top_right[0], bottom_left[1]) bottom_middle = np.array((bottom_right[0] - 0.5 * electrode_width, self.wing_pad_bottom_left[1])) pad = Polygon([ self.wing_pad_bottom_left, self.wing_pad_top_left, self.wing_pad_top_right, bottom_left, top_left, top_right, bottom_right, bottom_middle ]) pad = geometric_union([pad]) pad = shapely.affinity.translate(pad, yoff=0.5 * self.nw_width) pad_l = shapely.affinity.scale(pad, xfact=-1.0, yfact=1.0, zfact=1.0, origin=[0, 0, 0]) pad = geometric_union([pad, pad_l]) pad = shapely.affinity.rotate(pad, self._origin_port.angle - 0.5 * np.pi, origin=[0, 0], use_radians=True) self.pad = shapely.affinity.translate(pad, xoff=self._origin_port.origin[0], yoff=self._origin_port.origin[1])
def create_holes_for_under_etching(underetch_parts, complete_structure, hole_radius, hole_distance, hole_spacing, hole_length=0, cap_style='round'): """ Creates holes around given parts which can be used for underetching processes :param underetch_parts: List of gdshelpers parts around which the holes shall be placed :param complete_structure: geometric union of the complete structure, needed to avoid collisions between underetching holes and other structures, e.g. waveguides :param hole_radius: Radius of the holes in microns :param hole_distance: Distance between the holes edges from the the structures in microns :param hole_spacing: Distance between the holes in microns :param hole_length: Length of the holes (if 0 creates circles, else rectangle like) :param cap_style: CAP_STYLE of the holes (i.e. 'round' or 'square', see Shapely Docs) :return: Geometric union of the created holes """ cap_style = { 'round': CAP_STYLE.round, 'square': CAP_STYLE.square }[cap_style] union = geometric_union(underetch_parts) no_hole_zone = complete_structure.buffer(0.9 * (hole_distance + hole_radius), resolution=32, cap_style=3) poly = union.buffer(hole_distance + hole_radius, resolution=32, cap_style=CAP_STYLE.square) base_polygon = shapely_adapter.shapely_collection_to_basic_objs(poly) holes = [] for obj in base_polygon: for interior in [obj.exterior] + list(obj.interiors): dist = 0 while dist < interior.length: if hole_length == 0: hole = interior.interpolate(distance=dist) dist += hole_spacing + 2 * hole_radius else: positions = [ interior.interpolate(distance=d) for d in np.linspace( dist - hole_length / 2 + hole_radius, dist + hole_length / 2 - hole_radius, 10) ] dist += hole_spacing + hole_length hole = LineString(positions) if not no_hole_zone.contains(hole): holes.append(hole.buffer(hole_radius, cap_style=cap_style)) return geometric_union(holes)
def convert_to_positive_resist(parts, buffer_radius, outer_resolution=None, clearance_features=None, exclude=None): """ Convert a list of parts and shapely objects to a positive resist design by adding a buffer around the actual design. :param parts: List of parts and shapely objects. :param buffer_radius: Buffer radius :param outer_resolution: Outer buffer circumference resolution. Defaults to one 20th of the buffer radius. :param clearance_features: List of additional features to include in the generated structure. Can be useful for providing clearance areas around couplers or other features. :param exclude: List of features to subtract from the generated structure. Can be used for interconnects between structures from different cells, such that the end of a waveguide remains "open". :return: Converted Shapely geometry. :rtype: shapely.base.BaseGeometry """ outer_resolution = buffer_radius / 20. if outer_resolution is None else outer_resolution assert buffer_radius > 0, 'The buffer radius must be positive.' assert outer_resolution >= 0, 'Resolution must be positive or zero.' parts = (parts, ) if not isinstance(parts, (tuple, list)) else parts # First merge all parts into one big shapely object union = geometric_union(parts) # Sometimes those polygons do not touch correctly and have micro gaps due to # floating point precision. We work around this by inflating the object a tiny bit. fixed_union = union.buffer(np.finfo(np.float32).eps, resolution=0) # Generate the outer polygon and simplify if required outer_poly = union.buffer(buffer_radius) if outer_resolution: outer_poly = outer_poly.simplify(outer_resolution) # Add clearance features (before subtracting the actual parts) if clearance_features is not None: clearance = (clearance_features, ) if not isinstance( clearance_features, (tuple, list)) else clearance_features outer_poly = geometric_union((outer_poly, *clearance)) # Substract the original parts from outer poly inverted = outer_poly.difference(fixed_union) # Exclude other exclusion features from the generated structure if exclude is not None: exclude = (exclude, ) if not isinstance(exclude, (tuple, list)) else exclude exclude = geometric_union(exclude) inverted = inverted.difference(exclude) return inverted
def make_waveguide(self): wg = Waveguide.make_at_port(self._origin_port) wg.add_straight_segment(self.nw_length + 0.5 * self.nw_width + self.wing_height) if self.waveguide_tapering: wg.add_straight_segment(2. * self._origin_port.width, final_width=0.01) self.waveguide_port = wg.current_port wg = geometric_union([wg]) nw = self.nw.difference(wg) buffer = nw.buffer(.2) self.wg = geometric_union([wg, buffer])
def get_shapely_object(self): size_half = self.size / 2. p1 = shapely.geometry.Polygon( [(-size_half, size_half + self.frame_width), (size_half, size_half + self.frame_width), (0, size_half)]) p2 = shapely.geometry.Polygon([(size_half, size_half), (size_half, size_half + self.frame_width), (size_half + self.frame_width, size_half)]) one_side = geometric_union([p1, p2]) marker = geometric_union([shapely.affinity.rotate(one_side, phi, (0, 0)) for phi in (0, 90, 180, 270)]) return shapely.affinity.translate(marker, self.origin[0], self.origin[1])
def _make_electrodes(self): phi = np.linspace(math.pi, 2 * math.pi, self.points) circle_points = np.array( [self.radius * np.cos(phi), self.radius * np.sin(phi)]).T first_part_points = [ circle_points[0], (-self.radius, self.el_l_fine), (self.radius, self.el_l_fine), circle_points[-1] ] taper_points = [ first_part_points[1], (-self.final_width / 2, self.el_l_fine + self.el_l_taper), (self.final_width / 2, self.el_l_fine + self.el_l_taper), first_part_points[-2] ] last_part_points = [ taper_points[1], (-self.final_width / 2, self.el_l_fine + self.el_l_taper + self.el_l_straight), (self.final_width / 2, self.el_l_fine + self.el_l_taper + self.el_l_straight), taper_points[-2] ] cirlce_polygon = shapely.geometry.Polygon(circle_points) first_part_polygon = shapely.geometry.Polygon(first_part_points) taper_polygon = shapely.geometry.Polygon(taper_points) last_part_polygon = shapely.geometry.Polygon(last_part_points) polygon = geometric_union([ cirlce_polygon, taper_polygon, first_part_polygon, last_part_polygon ]) upper_electrode = shapely.affinity.translate(polygon, self.el_shift_x, self.el_shift_y) lower_electrode = shapely.affinity.scale(upper_electrode, xfact=-1, yfact=-1, origin=(0, 0)) electrode = geometric_union([lower_electrode, upper_electrode]) electrode = shapely.affinity.rotate(electrode, self._cnt_port.angle, use_radians=True) electrode = shapely.affinity.translate(electrode, self._cnt_port.origin[0], self._cnt_port.origin[1]) self.electrodes = electrode
def get_reduced_layer(self, layer: int): """ Returns a single shapely object containing the structures on a certain layer from this cell and all added cells. :param layer: the layer whose structures will be returned :return: a single shapely-geometry """ def translate_and_rotate(geometry, offset, angle, columns, rows, spacing): if not geometry: return geometry if not spacing: return translate( rotate(geometry, angle if angle else 0, use_radians=True, origin=(0, 0)), *offset) return translate( geometric_union( translate( rotate(geometry, angle if angle else 0, use_radians=True, origin=(0, 0)), spacing[0] * c, spacing[1] * r) for c in range(columns) for r in range(rows)), *offset) return geometric_union( (self.layer_dict[layer] if layer in self.layer_dict else []) + [ translate_and_rotate(cell['cell'].get_reduced_layer( layer), cell['origin'], cell['angle'], cell['columns'], cell['rows'], cell['spacing']) for cell in self.cells ])
def surround_with_holes(geometry, hole_spacing, hole_radius, padding, max_distance): """ Surrounds the given geometry with holes, which are arranged in a square lattice around the structure. This can be used for generating vortex traps like presented in https://doi.org/10.1103/PhysRevApplied.11.064053 :param geometry: The geometry around which the holes are generated :param hole_spacing: Spacing between the holes :param hole_radius: Radius of the holes :param padding: Padding around the geometry :param max_distance: Maximum distance of a hole from the geometry :return: Shapely object, which describes the holes """ geometry = geometric_union(geometry if isinstance(geometry, ( tuple, list)) else (geometry, )) buffer_around_waveguide = geometry.buffer(max_distance) area_for_holes = prep( buffer_around_waveguide.difference( geometry.buffer(hole_radius + padding))) area = buffer_around_waveguide.bounds points = (Point(x, y) for x in np.arange(area[0], area[2], hole_spacing) for y in np.arange(area[1], area[3], hole_spacing)) return MultiPolygon([ point.buffer(hole_radius) for point in points if area_for_holes.contains(point) ])
def get_shapely_object(self): if not self._in_wgs or not self._out_wgs: self._calculate(do_in_wgs=True, do_out_wgs=True) markers = [DLWMarker(pos) for pos in self.marker_positions] return geometric_union(self._out_wgs + self._in_wgs + markers)
def _example_iii(): from gdshelpers.parts.waveguide import Waveguide from gdshelpers.parts.coupler import GratingCoupler from gdshelpers.geometry import geometric_union import numpy as np coupler1 = GratingCoupler.make_traditional_coupler((250 / 2, 0), 1.3, np.deg2rad(40), 1.13, 0.85, 20, taper_length=16, ap_max_ff=0.985, n_ap_gratings=10) wave_guide = Waveguide.make_at_port(coupler1.port) wave_guide.add_straight_segment(20) wave_guide.add_bend(0.5 * np.pi, 40) wave_guide.add_straight_segment(250 - 2 * 40) wave_guide.add_bend(0.5 * np.pi, 40) wave_guide.add_straight_segment(20) coupler2 = GratingCoupler.make_traditional_coupler( (wave_guide.current_port.origin[0], wave_guide.current_port.origin[1]), 1.3, np.deg2rad(40), 1.13, 0.85, 20, taper_length=16, ap_max_ff=0.985, n_ap_gratings=10) shapely_objects = [ Shapely3d( geometric_union([ coupler1.get_shapely_object(), wave_guide.get_shapely_object(), coupler2.get_shapely_object() ]), 0.0, 0.7, (255, 255, 255)) ] render_image_and_save_as_blend(shapely_objects, "test_device_under_right", camera_position_y='under', camera_position_x='right') render_image_and_save_as_blend(shapely_objects, "test_device_above_left", camera_position_y='above', camera_position_x='left') render_image_and_save_as_blend(shapely_objects, "test_device_under_left", camera_position_y='under', camera_position_x='left') render_image_and_save_as_blend(shapely_objects, "test_device_above_right", camera_position_y='above', camera_position_x='right')
def get_shapely_object(self): splitter1 = Splitter(self.origin, self.angle, self.splitter_length, self.width, self.splitter_separation) upper_wg = Waveguide.make_at_port(splitter1.left_branch_port) upper_wg.add_bend(np.deg2rad(90), self.bend_radius) upper_wg.add_straight_segment(self.upper_vertical_length) upper_wg.add_bend(np.deg2rad(-90), self.bend_radius) upper_wg.add_straight_segment(self.horizontal_length) upper_wg.add_bend(np.deg2rad(-90), self.bend_radius) upper_wg.add_straight_segment(self.upper_vertical_length) upper_wg.add_bend(np.deg2rad(90), self.bend_radius) lower_wg = Waveguide.make_at_port(splitter1.right_branch_port) lower_wg.add_bend(np.deg2rad(-90), self.bend_radius) lower_wg.add_straight_segment(self.lower_vertical_length) lower_wg.add_bend(np.deg2rad(90), self.bend_radius) lower_wg.add_straight_segment(self.horizontal_length) lower_wg.add_bend(np.deg2rad(90), self.bend_radius) lower_wg.add_straight_segment(self.lower_vertical_length) lower_wg.add_bend(np.deg2rad(-90), self.bend_radius) splitter2 = Splitter.make_at_right_branch_port(upper_wg.current_port, self.splitter_length, self.splitter_separation) return geometric_union([splitter1, splitter2, upper_wg, lower_wg])
def convert_to_positive_resist(parts, buffer_radius, outer_resolution=None): """ Convert a list of parts and shapely objects to a positive resist design by adding a buffer around the actual design. :param parts: List of parts and shapely objects. :param buffer_radius: Buffer radius :param outer_resolution: Outer buffer circumference resolution. Defaults to one 20th of the buffer radius. :return: Converted Shapely geometry. :rtype: shapely.base.BaseGeometry """ outer_resolution = buffer_radius / 20. if outer_resolution is None else outer_resolution assert buffer_radius > 0, 'The buffer radius must be positive.' assert outer_resolution >= 0, 'Resolution must be positive or zero.' parts = (parts,) if not isinstance(parts, (tuple, list)) else parts # First merge all parts into one big shapely object union = geometric_union(parts) # Sometimes those polygons do not touch correctly and have micro gaps due to # floating point precision. We work around this by inflating the object a tiny bit. fixed_union = union.buffer(np.finfo(np.float32).eps, resolution=0) # Generate the outer polygon and simplify if required outer_poly = union.buffer(buffer_radius) if outer_resolution: outer_poly = outer_poly.simplify(outer_resolution) # Substract the original parts from outer poly inverted = outer_poly.difference(fixed_union) return inverted
def get_shapely_object(self): splitter1 = MMI(origin=self.origin, angle=self.angle, wg_width=self.width, length=self.splitter_length, width=self.splitter_width, num_inputs=1, num_outputs=2) print(splitter1.output_ports) upper_wg = Waveguide.make_at_port(splitter1.left_branch_port) upper_wg.add_bend(np.deg2rad(90), self.bend_radius) upper_wg.add_straight_segment(self.upper_vertical_length) upper_wg.add_bend(np.deg2rad(-90), self.bend_radius) upper_wg.add_straight_segment(self.horizontal_length) upper_wg.add_bend(np.deg2rad(-90), self.bend_radius) upper_wg.add_straight_segment(self.upper_vertical_length) upper_wg.add_bend(np.deg2rad(90), self.bend_radius) lower_wg = Waveguide.make_at_port(splitter1.right_branch_port) lower_wg.add_bend(np.deg2rad(-90), self.bend_radius) lower_wg.add_straight_segment(self.lower_vertical_length) lower_wg.add_bend(np.deg2rad(90), self.bend_radius) lower_wg.add_straight_segment(self.horizontal_length) lower_wg.add_bend(np.deg2rad(90), self.bend_radius) lower_wg.add_straight_segment(self.lower_vertical_length) lower_wg.add_bend(np.deg2rad(-90), self.bend_radius) splitter2 = MMI(origin=[self.origin[0] + self.dev_width, self.origin[1]], angle=self.angle + np.pi, wg_width=self.width, length=self.splitter_length, width=self.splitter_width, num_inputs=1, num_outputs=2) print(splitter2.input_ports) return geometric_union([splitter1, splitter2, upper_wg, lower_wg])
def get_patches(self, origin=(0, 0), angle_sum=0, angle=0, layers=None): from descartes import PolygonPatch def rotate_pos(pos, rotation_angle): if rotation_angle is None: return pos c, s = np.cos(rotation_angle), np.sin(rotation_angle) result = np.array([[c, -s], [s, c]]).dot(pos) return result own_patches = [] for layer, geometry in self.layer_dict.items(): if layers is not None and layer not in layers: continue geometry = geometric_union(geometry) if geometry.is_empty: continue geometry = translate(rotate(geometry, angle_sum, use_radians=True, origin=(0, 0)), *origin) own_patches.append( PolygonPatch(geometry, color=['red', 'green', 'blue', 'teal', 'pink'][(layer - 1) % 5], linewidth=0)) sub_cells_patches = [p for cell_dict in self.cells for p in cell_dict['cell'].get_patches( np.array(origin) + rotate_pos(cell_dict['origin'], angle), angle_sum=angle_sum + (cell_dict['angle'] or 0), angle=cell_dict['angle'], layers=layers)] return own_patches + sub_cells_patches
def _example_mmi(): import gdsCAD.core from gdshelpers.geometry import convert_to_gdscad mmi = MMI((80, 0), 0, 1, 20, 10, 2, 2) cell = gdsCAD.core.Cell('Splitter') cell.add(convert_to_gdscad(geometric_union([mmi]))) cell.show()
def get_reduced_layer(self, layer): def translate_and_rotate(geometry, offset, angle): if not geometry: return geometry return translate(rotate(geometry, angle if angle else 0, use_radians=True, origin=(0, 0)), *offset) return geometric_union( (self.layer_dict[layer] if layer in self.layer_dict else []) + [translate_and_rotate(cell['cell'].get_reduced_layer(layer), cell['origin'], cell['angle']) for cell in self.cells])
def get_shapely_object(self): shapely_object = geometric_union([ self._gate(), self._choke_channel(), self._choke_left(), self._choke_right(), self._channel() ]) rotated_object = rotate(shapely_object, self._angle, (0, 0), True) return translate(rotated_object, self._origin[0], self._origin[1], 0)
def _cnt_example(): import gdsCAD.core from gdshelpers.geometry import convert_to_gdscad from gdshelpers.parts.waveguide import Waveguide # photonics start_port = Port((0, 0), 0, 1.1) wg = Waveguide.make_at_port(start_port) wg.add_straight_segment(20) cnt_port = wg.current_port cnt = CNT.make_at_port(cnt_port, gap=0.4, l_taper=100, w_taper=0.1) wg2 = Waveguide.make_at_port(cnt.out_port) wg2.add_bend(np.pi / 4, 100) cnt2 = CNT.make_at_port(wg2.current_port, gap=0.15) union = geometric_union([wg, cnt, wg2, cnt2]) # electrodes el1_l = Waveguide.make_at_port(cnt.left_electrode_port) el1_l.add_straight_segment(100, 100) el1_r = Waveguide.make_at_port(cnt.right_electrode_port) # el1_r.add_straight_segment(100) port = cnt2.left_electrode_port port.width = 20 el2_l = Waveguide.make_at_port(port) el2_l.add_straight_segment(30) el = geometric_union( [cnt.electrodes, cnt2.electrodes, el1_l, el1_r, el2_l]) cell = gdsCAD.core.Cell('test') cell.add(convert_to_gdscad(union)) cell.add(convert_to_gdscad(el, layer=2)) layout = gdsCAD.core.Layout() layout.add(cell) layout.show() layout.save('CNT_Device_Test.gds')
def _example(): from gdshelpers.geometry.chip import Cell from gdshelpers.geometry import geometric_union # Generate a coupler, which ought to be identical to # coupler_sn330_1550_bf_ff_ap( 0:0 1550 0.7 22 0.96 10 22 1 "active") # also known as # coupler_bf_ap_mff( 0:0 1 40.0 1.13 0.7 22 0.96 10 22 200 "active") coupler = GratingCoupler.make_traditional_coupler([150, 0], 1, np.deg2rad(40), 1.13, 0.7, 22, 0.96, 10, 22, angle=-np.pi) coupler2 = GratingCoupler.make_traditional_coupler_from_database([0, 0], 1, 'sn330', 1550) print(coupler.get_description_str()) whole_layout = (coupler, coupler2) layout = Cell('LIBRARY') cell = Cell('TOP') cell.add_to_layer(1, *whole_layout) cell.add_to_layer(StandardLayers.parnamelayer1, coupler.get_description_text()) cell.add_to_layer(StandardLayers.parnamelayer1, coupler2.get_description_text()) cell.add_to_layer(1, coupler.get_description_text()) cell.add_to_layer(1, coupler2.get_description_text()) cell.add_to_layer(1, coupler2.get_description_text(side='left')) layout.add_cell(cell) # We could also easily generate a dark field layout out of this def make_dark_field(obj, buffer_size=1.5): return obj.buffer(buffer_size).difference(obj) cell_df = Cell('TOP_DF') cell_df.add_to_layer(2, make_dark_field(geometric_union(whole_layout))) layout.add_cell(cell_df) layout.save('coupler.gds') cell.show()
def get_fractured_layer_dict(self, max_points=4000, max_line_points=4000): from gdshelpers.geometry.shapely_adapter import shapely_collection_to_basic_objs, fracture_intelligently fractured_layer_dict = {} for layer, geometries in self.layer_dict.items(): fractured_geometries = [] for geometry in geometries: geometry = geometry.get_shapely_object() if hasattr(geometry, 'get_shapely_object') else geometry if type(geometry) in [list, tuple]: geometry = geometric_union(geometry) geometry = shapely_collection_to_basic_objs(geometry) geometry = itertools.chain( *[fracture_intelligently(geo, max_points, max_line_points) for geo in geometry if not geo.is_empty]) fractured_geometries.append(geometry) fractured_layer_dict[layer] = itertools.chain(*fractured_geometries) return fractured_layer_dict
def create_holes_for_under_etching(underetch_parts, complete_structure, hole_radius, hole_distance, hole_spacing): """ Creates holes around given parts which can be used for underetching processes :param underetch_parts: List of gdshelpers parts around which the holes shall be placed :param complete_structure: geometric union of the complete structure, needed to avoid collisions between underetching holes and other structures, e.g. waveguides :param hole_radius: Radius of the holes in microns :param hole_distance: Distance of the holes center from the the structures in microns :param hole_spacing: Distance between the holes in microns :return: Geometric union of the created holes """ union = geometric_union(underetch_parts) no_hole_zone = complete_structure.buffer(0.9 * hole_distance, resolution=32, cap_style=3) poly = union.buffer(hole_distance, resolution=32, cap_style=3) base_polygon = shapely_adapter.shapely_collection_to_basic_objs(poly) holes = [] for obj in base_polygon: ext = obj.exterior for dist in np.arange(0, ext.length, hole_spacing): pos = ext.interpolate(distance=dist) if not no_hole_zone.contains(pos): holes.append(pos.buffer(hole_radius)) for interior in obj.interiors: for dist in np.arange(0, interior.length, hole_spacing): pos = interior.interpolate(distance=dist) if not no_hole_zone.contains(pos): holes.append(pos.buffer(hole_radius)) return geometric_union(holes)
def _example(): import gdsCAD.core from gdshelpers.geometry import convert_to_gdscad dc = DirectionalCoupler((0, 0), np.pi / 4, 1, 10, 1, 10) dc2 = DirectionalCoupler.make_at_port(dc.right_ports[1], 5, 2, 10, which=0) wg = Waveguide.make_at_port(dc.left_ports[1]) wg.add_straight_segment(12) wg2 = Waveguide.make_at_port(dc2.right_ports[0]) wg2.add_straight_segment(12) mmi = MMI((80, 0), 0, 1, 20, 10, 2, 1) mmi2 = MMI.make_at_port(dc2.right_ports[1], 10, 10, 2, 2, 'i1') cell = gdsCAD.core.Cell('Splitter') cell.add(convert_to_gdscad(geometric_union((dc, dc2, wg, wg2, mmi, mmi2)))) cell.show()
def get_shapely_object(self): wg = Waveguide.make_at_port(self._origin_port) opposite_wg = Waveguide.make_at_port( self.opposite_side_port_in.inverted_direction) if self.straight_feeding: wg.add_straight_segment(self.radius) if self.draw_opposite_side_wg: opposite_wg.add_straight_segment(self.radius) ring_port = wg.current_port.parallel_offset(self._offset) ring_port.width = self.res_wg_width ring = Waveguide.make_at_port(ring_port) if self.race_length: wg.add_straight_segment(self.race_length) if self.draw_opposite_side_wg: opposite_wg.add_straight_segment(self.race_length) if self.straight_feeding: wg.add_straight_segment(self.radius) if self.draw_opposite_side_wg: opposite_wg.add_straight_segment(self.radius) # Build the ring bend_angle = math.copysign(0.5 * np.pi, self.gap) if self.race_length: ring.add_straight_segment(self.race_length) ring.add_bend(bend_angle, self.radius, n_points=self.points) if self.vertical_race_length: ring.add_straight_segment(self.vertical_race_length) ring.add_bend(bend_angle, self.radius, n_points=self.points) if self.race_length: ring.add_straight_segment(self.race_length) ring.add_bend(bend_angle, self.radius, n_points=self.points) if self.vertical_race_length: ring.add_straight_segment(self.vertical_race_length) ring.add_bend(bend_angle, self.radius, n_points=self.points) return geometric_union([ring, wg, opposite_wg])
def translate_and_rotate(geometry, offset, angle, columns, rows, spacing): if not geometry: return geometry if not spacing: return translate( rotate(geometry, angle if angle else 0, use_radians=True, origin=(0, 0)), *offset) return translate( geometric_union( translate( rotate(geometry, angle if angle else 0, use_radians=True, origin=(0, 0)), spacing[0] * c, spacing[1] * r) for c in range(columns) for r in range(rows)), *offset)
def get_shapely_object(self): if not self.wg_in or not self.wg_out: self._generate() return geometric_union([self.wg_in, self.wg_out])
def get_shapely_object(self): return geometric_union(self.waveguide)
def _generate(self): if self._sep < self._wr: raise ValueError( 'The separation gap must be larger than the branch width.') if self._implement_cadence_bug: v1 = Splitter._connect_two_points( [self._wl / 2, 0], [self._sep / 2 + self._wr / 2, self._total_length], self._n_points) if self._wl < 2 * self._wr: v2 = (v1 - [self._wr, 0])[::-1, :] else: # NOTE: In this obscure case, the generated splitter looks different than the cadence version. # But probably it is better, since the paths are all smooth and curvy instead of just painting a # triangle. v2 = Splitter._connect_two_points( [-self._wr / 2, 0], [self._sep / 2 - self._wr / 2, self._total_length], self._n_points)[::-1, :] v = np.vstack((v1, v2)) polygon1 = shapely.geometry.Polygon(v) polygon1 = polygon1.difference( shapely.geometry.box(-self._sep / 2, 0, 0, self._total_length)) polygon2 = shapely.affinity.scale(polygon1, xfact=-1, origin=[0, 0, 0]) polygon = polygon1.union(polygon2) polygon = shapely.affinity.rotate(polygon, -np.pi / 2 + self._angle, origin=[0, 0], use_radians=True) polygon = shapely.affinity.translate(polygon, self._origin[0], self._origin[1]) # Keep track of the ports port_points = shapely.geometry.MultiPoint([ (0, 0), (-self._sep / 2, self._total_length), (+self._sep / 2, self._total_length) ]) port_points = shapely.affinity.rotate(port_points, -np.pi / 2 + self._angle, origin=[0, 0], use_radians=True) port_points = shapely.affinity.translate(port_points, self._origin[0], self._origin[1]) self._polygon = polygon self._ports['root'] = Port(port_points[0].coords[0], self._angle + np.pi, width=self._wl) self._ports['left_branch'] = Port(port_points[1].coords[0], self._angle, width=self._wr) self._ports['right_branch'] = Port(port_points[2].coords[0], self._angle, width=self._wr) else: # Simpler version which also cares for a constant wave guide width alpha = np.arctan(4.0 * self._total_length * self._sep / (4.0 * self._total_length**2.0 - self._sep**2.0)) radius = 1.0 / 8.0 * (4.0 * self._total_length**2.0 + self._sep**2.0) / self._sep root_port = Port(self._origin, self._angle, self._wl) half_final_width = (self._wl + self._wr) / 2. upper_wg = Waveguide.make_at_port(root_port) upper_wg.add_bend(alpha, radius, final_width=half_final_width) upper_wg.add_bend(-alpha, radius, final_width=self._wr) lower_wg = Waveguide.make_at_port(root_port) lower_wg.add_bend(-alpha, radius, final_width=half_final_width) lower_wg.add_bend(alpha, radius, final_width=self._wr) self._polygon = geometric_union([upper_wg, lower_wg]) self._ports['root'] = root_port.inverted_direction self._ports['left_branch'] = upper_wg.current_port self._ports['right_branch'] = lower_wg.current_port
def get_shapely_object(self): return geometric_union(self._wgs)