class MyQComponent3(QComponent):
    """Demonstration2 - Straight segment with variable width/length"""

    default_options = Dict(width='0.5mm',
                           height='0.1mm',
                           pos_x='0mm',
                           pos_y='0mm',
                           layer='1')
    """Default drawing options"""

    # Name prefix of component + import of renderer-specific default_options
    component_metadata = Dict(
        short_name='Trace',
        _qgeometry_table_path='False',  #wirebonds
        _qgeometry_table_poly='True',
        _qgeometry_table_junction='False')  #gds imports and analysis inputs
    """Component metadata"""
    def make(self):
        """calculates the geometries of the QComponent"""
        p = self.parse_options()  # short-handle alias. Options interpreter

        rect = draw.rectangle(p.width, p.height, p.pos_x, p.pos_y)
        self.add_qgeometry('poly', {'my_polygon': rect},
                           layer=p.layer,
                           subtract=False)
        self.add_pin('in', rect.exterior.coords[:-3:-1], p.height)
Beispiel #2
0
class ShortToGround(QComponent):
    """A sample_shapes short to ground termination. Functions as a pin for auto CPW
    drawing.

    Inherits `QComponent` class.

    Default Options:
        * width: '10um' -- The width of the 'cpw' terminating to ground (this is merely for the purpose of
          generating a value to pass to the pin)

    Values (unless noted) are strings with units included, (e.g., '30um')
    """
    component_metadata = Dict(short_name='term')
    """Component metadata"""

    default_options = Dict(width='10um')
    """Default connector options"""

    TOOLTIP = """A basic short to ground termination"""

    def make(self):
        """Build the component."""
        p = self.p  # p for parsed parameters. Access to the parsed options.

        port_line = draw.LineString([(0, -p.width / 2), (0, p.width / 2)])

        # Rotates and translates the connector polygons (and temporary port_line)
        port_line = draw.rotate(port_line, p.orientation, origin=(0, 0))
        port_line = draw.translate(port_line, p.pos_x, p.pos_y)

        port_points = list(draw.shapely.geometry.shape(port_line).coords)

        #Generates the pin
        self.add_pin('short', port_points, p.width)
Beispiel #3
0
class MyQComponent(QComponent):
    """Use this class as a template for your components."""

    # Edit these to define your own tempate options for creation
    # Default drawing options
    default_options = Dict(width='500um',
                           height='300um',
                           pos_x='0um',
                           pos_y='0um',
                           rotation='0',
                           layer='1')
    """Default drawing options"""

    # Name prefix of component, if user doesn't provide name
    component_metadata = Dict(short_name='component')
    """Component metadata"""

    # DO NOT DIRECTLY INSTANTIATE USING make() Use build()
    def make(self):
        """Convert self.options into QGeometry."""

        p = self.parse_options()  # Parse the string options into numbers

        # EDIT HERE - Replace the following with your code
        # Create some raw geometry
        # Use autocompletion for the `draw.` module (use tab key)
        rect = draw.rectangle(p.width, p.height, p.pos_x, p.pos_y)
        rect = draw.rotate(rect, p.rotation)
        geom = {'my_polygon': rect}
        self.add_qgeometry('poly', geom, layer=p.layer, subtract=False)
    def test_component_get_nested_dict_item(self):
        """
        Test the functionality of get_nested_dict_item in _parsed_dynamic_attrs.py.
        """
        # Setup expected test results
        my_dict = Dict(aa=Dict(x1={'dda': '34fF'}, y1='Y', z='10um'),
                       bb=Dict(x2=5, y2='YYYsdg', z='100um'),
                       cc='100nm')

        # Generate actual result data
        _test_a = _parsed_dynamic_attrs.get_nested_dict_item(my_dict, ['cc'])
        _test_b = _parsed_dynamic_attrs.get_nested_dict_item(
            my_dict, ['aa', 'x1', 'dda'])
        _test_c = _parsed_dynamic_attrs.get_nested_dict_item(my_dict, ['aa'])
        _test_d = _parsed_dynamic_attrs.get_nested_dict_item(
            my_dict, ['empty'])

        # Test all elements of the result data against expected data
        self.assertEqual('100nm', _test_a)
        self.assertEqual('34fF', _test_b)
        self.assertEqual({
            'x1': {
                'dda': '34fF'
            },
            'y1': 'Y',
            'z': '10um'
        }, _test_c)
        self.assertEqual({}, _test_d)
Beispiel #5
0
class MyQComponent(QComponent):
    """
    This class is a template
	Use this class as a blueprint to put together for your components - have fun
    
    .. meta::
        My QComponent

    """

    # Edit these to define your own template options for creation
    # Default drawing options
    default_options = Dict(width='500um', height='300um')
    """Default drawing options"""

    # Name prefix of component, if user doesn't provide name
    component_metadata = Dict(short_name='component')
    """Component metadata"""
    def make(self):
        """Convert self.options into QGeometry."""

        p = self.parse_options()  # Parse the string options into numbers

        # EDIT HERE - Replace the following with your code
        # Create some raw geometry
        # Use autocompletion for the `draw.` module (use tab key)
        rect = draw.rectangle(p.width, p.height, p.pos_x, p.pos_y)
        rect = draw.rotate(rect, p.orientation)
        geom = {'my_polygon': rect}
        self.add_qgeometry('poly', geom, layer=p.layer, subtract=False)
Beispiel #6
0
class SmileyFace(QComponent):
    """TEST COMPONENT It is for fun only.  Can view a smiley face. Can make it
    wink or frown.

    .. image::
        SmileyFace.png

    .. meta::
        Smiley Face :)

    Default Options:
        * happy: True
        * wink: False
        * orientation: 0
    """

    component_metadata = Dict(short_name='Smile')
    """Component metadata"""

    default_options = Dict(happy=True, wink=False, orientation=0)
    """Default connector options"""

    TOOLTIP = """TEST COMPONENT It is for fun only"""

    def make(self):
        """Build the component."""
        face = draw.shapely.geometry.Point(0, 0).buffer(1)
        eye = draw.shapely.geometry.Point(0, 0).buffer(0.2)
        eye_l = draw.translate(eye, -0.4, 0.4)
        eye_r = draw.translate(eye, 0.4, 0.4)

        smile = draw.shapely.geometry.Point(0, 0).buffer(0.8)
        cut_sq = draw.shapely.geometry.box(-1, -0.3, 1, 1)
        smile = draw.subtract(smile, cut_sq)

        frown = draw.rotate(smile, 180)
        frown = draw.translate(frown, 0, 0.3)
        frown = draw.subtract(frown,
                              draw.shapely.geometry.Point(0, -0.8).buffer(0.7))

        face = draw.subtract(face, eye_l)

        if self.p.happy:
            face = draw.subtract(face, smile)
        else:
            face = draw.subtract(face, frown)

        if self.p.wink:
            face = draw.subtract(
                face,
                draw.shapely.geometry.LineString([(0.2, 0.4),
                                                  (0.6, 0.4)]).buffer(0.02))
        else:
            face = draw.subtract(face, eye_r)

        face = draw.rotate(face, self.p.orientation, origin=(0, 0))

        self.add_qgeometry('poly', {'Smiley': face})
Beispiel #7
0
class OpenToGround(QComponent):
    """A sample_shapes open to ground termination. Functions as a pin for auto drawing.

    Inherits `QComponent` class.

    .. image::
        OpenToGround.png

    Default Options:
        * width: '10um' -- The width of the 'cpw' terminating to ground (this is merely
          for the purpose of generating a value to pass to the pin)
        * gap: '6um' -- The gap of the 'cpw'
        * termination_gap: '6um' -- The length of dielectric from the end of the cpw center trace to the ground.
        * pos_x: '0um' -- The x position of the ground termination.
        * pos_y: '0um' -- The y position of the ground termination.
        * rotation: '0' -- The direction of the termination. 0 degrees is +x, following a
          counter-clockwise rotation (eg. 90 is +y)
        * chip: 'main' -- The chip the pin should be on.
        * layer: '1' -- Layer the pin is on. Does not have any practical impact to the short.

    Values (unless noted) are strings with units included, (e.g., '30um')
    """
    component_metadata = Dict(short_name='term', _qgeometry_table_poly='True')
    """Component metadata"""

    default_options = Dict(width='10um',
                           gap='6um',
                           termination_gap='6um',
                           pos_x='0um',
                           pos_y='0um',
                           orientation='0',
                           chip='main',
                           layer='1')
    """Default connector options"""

    TOOLTIP = """A basic open to ground termination. """

    def make(self):
        """Build the component."""
        p = self.p  # p for parsed parameters. Access to the parsed options.

        port_line = draw.LineString([(0, -p.width / 2), (0, p.width / 2)])
        open_termination = draw.box(0, -(p.width / 2 + p.gap),
                                    p.termination_gap, (p.width / 2 + p.gap))
        # Rotates and translates the connector polygons (and temporary port_line)
        polys = [open_termination, port_line]
        polys = draw.rotate(polys, p.orientation, origin=(0, 0))
        polys = draw.translate(polys, p.pos_x, p.pos_y)
        [open_termination, port_line] = polys

        # Subtracts out ground plane on the layer its on
        self.add_qgeometry('poly', {'open_to_ground': open_termination},
                           subtract=True,
                           layer=p.layer)

        # Generates the pins
        self.add_pin('open', port_line.coords, p.width)
Beispiel #8
0
class NGon(QComponent):
    """A n-gon polygon. Eg. n = 3 : triangle, n = infinity : circle

    Inherits `QComponent` class.

    Default Options:
        Convention: Values (unless noted) are strings with units included,
        (e.g., '30um')

        * n: '3' -- Number of sides of the polygon
        * radius: '30um' -- The radius of the circle given n=infinity
        * pos_x: '0um' -- The x position of the ground termination.
        * pos_y '0um' -- The y position of the ground termination.
        * rotation: '0' -- The direction of the termination. 0 degrees is +x, following a
          counter-clockwise rotation (eg. 90 is +y)
        * subtract: 'False'
        * helper: 'False'
        * chip: 'main' -- The chip the pin should be on.
        * layer: '1' -- Layer the pin is on. Does not have any practical impact to the short.
    """

    default_options = Dict(
        n='3',
        radius='30um',
        pos_x='0um',
        pos_y='0um',
        rotation='0',
        subtract='False',
        helper='False',
        chip='main',
        layer='1',
    )
    """Default drawing options"""

    def make(self):
        """
        The make function implements the logic that creates the geoemtry
        (poly, path, etc.) from the qcomponent.options dictionary of parameters,
        and the adds them to the design, using qcomponent.add_qgeometry(...),
        adding in extra needed information, such as layer, subtract, etc.
        """
        p = self.p  # p for parsed parameters. Access to the parsed options.
        n = int(p.n)
        # Create the geometry
        # Generates a list of points
        n_polygon = [(p.radius * np.cos(2 * np.pi * x / n),
                      p.radius * np.sin(2 * np.pi * x / n)) for x in range(n)]
        # Converts said list into a shapely polygon
        n_polygon = draw.Polygon(n_polygon)

        n_polygon = draw.rotate(n_polygon, p.rotation, origin=(0, 0))
        n_polygon = draw.translate(n_polygon, p.pos_x, p.pos_y)

        ##############################################
        # add qgeometry
        self.add_qgeometry('poly', {'n_polygon': n_polygon},
                           subtract=p.subtract,
                           helper=p.helper,
                           layer=p.layer,
                           chip=p.chip)
Beispiel #9
0
    def _add_route_specific_options(self, options):
        """Enriches the default_options to support different types of route styles.

        Args:
            options (dict): User options that will override the defaults

        Return:
            A modified options dictionary

        Raises:
            Exception: Unsupported route type
        """
        if self.type == "ROUTE":
            # all the defaults are fine as-is
            None
        elif self.type == "CPW":
            # add the variable to define the space between the route and the ground plane
            cpw_options = Dict(trace_gap='cpw_gap')
            if options:
                if "trace_gap" not in options:
                    # user did not pass the trace_gap, so add it
                    options.update(cpw_options)
            else:
                # user did not pass custom options, so create it to add trace_gap
                options["options"] = cpw_options
        else:
            raise Exception("Unsupported Route type: " + self.type +
                            " The only supported types are CPW and route")

        return options
class RouteStraight(QRoute):
    """
    Draw a straight Route connecting two pins.
    
    .. meta::
        Route Straight
    """

    component_metadata = Dict(short_name='cpw')
    """Component metadata"""

    TOOLTIP = """Draw a straight Route connecting two pins."""

    def make(self):
        """The make function implements the logic that creates the geometry
        (poly, path, etc.) from the qcomponent.options dictionary of
        parameters, and the adds them to the design, using
        qcomponent.add_qgeometry(...), adding in extra needed information, such
        as layer, subtract, etc."""
        # Set the CPW pins and add the points/directions to the lead-in/out arrays
        self.set_pin("start")
        self.set_pin("end")

        # Align the lead-in/out to the input options set from the user
        self.set_lead("start")
        self.set_lead("end")

        # Make points into elements
        self.make_elements(self.get_points())
class RectangleHollow(QComponent):
    """A single configurable square.

    Inherits QComponent class.

    Default Options:
        * width: '500um'
        * height: '300um'
        * pos_x: '0um'
        * pos_y: '0um'
        * rotation: '0'
        * subtract: 'False'
        * helper: 'False'
        * chip: 'main'
        * layer: '1'
        * inner: Dict
            * height: '100um'
            * offset_x: '40um'
            * offset_y: '-20um'
            * rotation: '15'
    """

    default_options = Dict(width='500um',
                           height='300um',
                           pos_x='0um',
                           pos_y='0um',
                           rotation='0',
                           subtract='False',
                           helper='False',
                           chip='main',
                           layer='1',
                           inner=dict(width='250um',
                                      height='100um',
                                      offset_x='40um',
                                      offset_y='-20um',
                                      rotation='15'))
    """Default drawing options"""
    def make(self):
        """The make function implements the logic that creates the geoemtry
        (poly, path, etc.) from the qcomponent.options dictionary of
        parameters, and the adds them to the design, using
        qcomponent.add_qgeometry(...), adding in extra needed information, such
        as layer, subtract, etc."""
        p = self.p  # p for parsed parameters. Access to the parsed options.

        # create the geometry
        rect = draw.rectangle(p.width, p.height, p.pos_x, p.pos_y)
        rec1 = draw.rectangle(p.inner.width, p.inner.height,
                              p.pos_x + p.inner.offset_x,
                              p.pos_y + p.inner.offset_y)
        rec1 = draw.rotate(rec1, p.inner.rotation)
        rect = draw.subtract(rect, rec1)
        rect = draw.rotate(rect, p.rotation)

        # add qgeometry
        self.add_qgeometry('poly', {'rect': rect},
                           subtract=p.subtract,
                           helper=p.helper,
                           layer=p.layer,
                           chip=p.chip)
Beispiel #12
0
class ShortToGround(QComponent):
    """A sample_shapes short to ground termination. Functions as a pin for auto CPW
    drawing.

    Inherits `QComponent` class.

    Default Options:
        * width: '10um' -- The width of the 'cpw' terminating to ground (this is merely for the purpose of
          generating a value to pass to the pin)
        * pos_x: '0um' -- The x position of the ground termination.
        * pos_y: '0um' -- The y position of the ground termination.
        * rotation: '0' -- The direction of the termination. 0 degrees is +x, following a
          counter-clockwise rotation (eg. 90 is +y)
        * chip: 'main' -- The chip the pin should be on.
        * layer: '1' -- Layer the pin is on. Does not have any practical impact to the short.

    Values (unless noted) are strings with units included, (e.g., '30um')
    """
    component_metadata = Dict(short_name='term')
    """Component metadata"""

    default_options = Dict(width='10um',
                           pos_x='0um',
                           pos_y='0um',
                           orientation='0',
                           chip='main',
                           layer='1')
    """Default connector options"""

    TOOLTIP = """A basic short to ground termination"""

    def make(self):
        """Build the component."""
        p = self.p  # p for parsed parameters. Access to the parsed options.

        port_line = draw.LineString([(0, -p.width / 2), (0, p.width / 2)])

        # Rotates and translates the connector polygons (and temporary port_line)
        port_line = draw.rotate(port_line, p.orientation, origin=(0, 0))
        port_line = draw.translate(port_line, p.pos_x, p.pos_y)

        port_points = list(draw.shapely.geometry.shape(port_line).coords)

        #Generates the pin
        self.add_pin('short', port_points, p.width)
    def get_chip_names(self) -> Dict:
        """ Returns a dict of unique chip names for ALL tables within QGeometry.
        In another words, for every "path" table, "poly" table ... etc, this method will search for unique
        chip names and return a dict of unique chip names from QGeometry table.

        Returns:
            Dict: dict with key of chip names and value of empty dict to hold things for renderers.
        """
        chip_names = Dict()
        for table_name in self.design.qgeometry.get_element_types():
            table = self.design.qgeometry.tables[table_name]
            names = table['chip'].unique().tolist()
            chip_names += names
        unique_list = list(set(chip_names))

        unique_dict = Dict()
        for chip in unique_list:
            unique_dict[chip] = Dict()
        return unique_dict
Beispiel #14
0
    def _init_metadata(cls) -> Dict:
        """Initialize default metadata dictionary.

        Returns:
            Dict: default metadata dictionary
        """
        now = datetime.now()  # current date and time
        return Dict(design_name='my_design',
                    notes='',
                    time_created=now.strftime("%m/%d/%Y, %H:%M:%S"))
Beispiel #15
0
class NGon(QComponent):
    """A n-gon polygon. Eg. n = 3 : triangle, n = infinity : circle

    Inherits `QComponent` class.

    .. image::
        NGon.png

    .. meta::
        N-Gon Polygon

    Default Options:
        Convention: Values (unless noted) are strings with units included,
        (e.g., '30um')

        * n: '3' -- Number of sides of the polygon
        * radius: '30um' -- The radius of the circle given n=infinity
        * subtract: 'False'
        * helper: 'False'
    """

    default_options = Dict(n='3',
                           radius='30um',
                           subtract='False',
                           helper='False')
    """Default drawing options"""

    TOOLTIP = """A n-gon polygon"""

    def make(self):
        """The make function implements the logic that creates the geoemtry
        (poly, path, etc.) from the qcomponent.options dictionary of
        parameters, and the adds them to the design, using
        qcomponent.add_qgeometry(...), adding in extra needed information, such
        as layer, subtract, etc."""
        p = self.p  # p for parsed parameters. Access to the parsed options.
        n = int(p.n)
        # Create the geometry
        # Generates a list of points
        n_polygon = [(p.radius * np.cos(2 * np.pi * x / n),
                      p.radius * np.sin(2 * np.pi * x / n)) for x in range(n)]
        # Converts said list into a shapely polygon
        n_polygon = draw.Polygon(n_polygon)

        n_polygon = draw.rotate(n_polygon, p.orientation, origin=(0, 0))
        n_polygon = draw.translate(n_polygon, p.pos_x, p.pos_y)

        ##############################################
        # add qgeometry
        self.add_qgeometry('poly', {'n_polygon': n_polygon},
                           subtract=p.subtract,
                           helper=p.helper,
                           layer=p.layer,
                           chip=p.chip)
Beispiel #16
0
class Rectangle(QComponent):
    """A single configurable square.

    Inherits QComponent class.

    The class will add default_options class Dict to QComponent class before calling make.

    .. image::
        Rectangle.png


    Default Options:
        * width: '500um'
        * height: '300um'
        * pos_x: '0um'
        * pos_y: '0um'
        * rotation: '0'
        * subtract: 'False'
        * helper: 'False'
        * chip: 'main'
        * layer: '1'
    """

    default_options = Dict(width='500um',
                           height='300um',
                           pos_x='0um',
                           pos_y='0um',
                           rotation='0',
                           subtract='False',
                           helper='False',
                           chip='main',
                           layer='1')
    """Default drawing options"""

    TOOLTIP = """A single configurable square"""

    def make(self):
        """The make function implements the logic that creates the geoemtry
        (poly, path, etc.) from the qcomponent.options dictionary of
        parameters, and the adds them to the design, using
        qcomponent.add_qgeometry(...), adding in extra needed information, such
        as layer, subtract, etc."""
        p = self.p  # p for parsed parameters. Access to the parsed options.

        # create the geometry
        rect = draw.rectangle(p.width, p.height, p.pos_x, p.pos_y)
        rect = draw.rotate(rect, p.rotation)
        ##############################################
        # add qgeometry
        self.add_qgeometry('poly', {'rectangle': rect},
                           subtract=p.subtract,
                           helper=p.helper,
                           layer=p.layer,
                           chip=p.chip)
Beispiel #17
0
    def __init__(self, *args, **kwargs):
        """Creates a new Analysis object with a setup derived from the default_setup Dict.
        """
        super().__init__(*args, **kwargs)
        self._setup, self._supported_data_labels = self._gather_from_all_children(
        )

        # first self.clear_data()
        self._variables = Dict()

        # Keep a reference to Sweeper class.
        self._sweeper = None
Beispiel #18
0
    def rename_variable(self, old_key: str, new_key: str):
        """Renames a variable in the variables dictionary. Preserves order.

        Args:
            old_key (str): Previous variable name
            new_key (str): New variable name
        """

        keys = list(self._variables.keys())
        values = list(self._variables.values())

        keys[keys.index(old_key)] = new_key
        self._variables = Dict(zip(keys, values))
    def test_qlibrary_base_get_template_options(self):
        """Test get_template_options in base.py."""
        design = designs.DesignPlanar()

        expected = Dict(pos_x='0.0um',
                        pos_y='0.0um',
                        orientation='0.0',
                        chip='main',
                        layer='1')
        self.assertEqual(QComponent.get_template_options(design), expected)

        expected.update(connection_pads={}, _default_connection_pads={})
        self.assertEqual(BaseQubit.get_template_options(design), expected)
Beispiel #20
0
class MyQComponent4(QComponent):
    """Demonstration3 - Straight segment with variable width/length"""

    default_options = Dict(width='0.5mm',
                           height='0.1mm',
                           gap='0.02mm',
                           pos_x='0mm',
                           pos_y='0mm',
                           layer='1')
    """Default drawing options"""

    # Name prefix of component + import of renderer-specific default_options
    component_metadata = Dict(
        short_name='Trace',
        _qgeometry_table_path='True',  #wirebonds
        _qgeometry_table_poly='False',
        _qgeometry_table_junction='False')  #gds
    """Component metadata"""
    def make(self):
        """calculates the geometries of the QComponent"""
        p = self.parse_options()

        line = draw.LineString([(-p.width / 2, 0), (p.width / 2, 0)])
        line = draw.translate(line, p.pos_x, p.pos_y)
        self.add_qgeometry('path', {'trace': line},
                           width=p.height,
                           layer=p.layer,
                           subtract=False)
        line2 = draw.LineString([((-p.width / 2) - 2 * p.gap, 0),
                                 ((p.width / 2) + 2 * p.gap, 0)])
        line2 = draw.translate(line2, p.pos_x, p.pos_y)
        self.add_qgeometry('path', {'cut': line2},
                           width=p.height + 2 * p.gap,
                           layer=p.layer,
                           subtract=True)
        self.add_pin('in', line.coords[::-1], p.height, input_as_norm=True)
Beispiel #21
0
    def __init__(self, design: 'QDesign' = None, renderer_name: str = None):
        """Initialize the Lumped Oscillator Model analysis.

        Args:
            design (QDesign): Pointer to the main qiskit-metal design.
                Used to access the QRenderer. Defaults to None.
            renderer_name (str, optional): Which renderer to use. Valid entries: 'q3d'.
                Defaults to None.
        """
        # QAnalysis are expected to either run simulation or use pre-saved sim outputs
        # we use a Dict() to store the sim outputs previously saved. Its key names need
        # to match those found in the correspondent simulation class.
        self.sim = Dict() if renderer_name is None else LumpedElementsSim(
            design, renderer_name)
        super().__init__()
Beispiel #22
0
    def clear_data(self, data_name: Union[str, list] = None):
        """Clear data. Can optionally specify one or more labels to delete those labels and data.

        Args:
            data_name (Union[str, list], optional): Can list specific labels to clean.
                Defaults to None.
        """
        if data_name is None:
            self._variables = Dict()
        else:
            if isinstance(data_name, str):
                self._variables[data_name] = None
            else:
                for name in data_name:
                    if name in self._variables[name]:
                        del self._variables[name]
Beispiel #23
0
    def run_sim(  # pylint: disable=arguments-differ
            self,
            name: str = None,
            components: Union[list, None] = None,
            open_terminations: Union[list, None] = None,
            box_plus_buffer: bool = True) -> Tuple[str, str]:
        """Executes the capacitance matrix extraction.
        First it makes sure the tool is running. Then it does the necessary to render the design.
        Finally it runs the setup defined in this class. So you need to modify the setup ahead.
        You can modify the setup by using the methods defined in the QAnalysis super-class.
        After this method concludes you can inspect the output using this class properties.

        Args:
            name (str): reference name for the components selection. If None,
                it will use the design.name. Defaults to None.
            components (Union[list, None], optional): List of components to render.
                Defaults to None.
            open_terminations (Union[list, None], optional):
                List of tuples of pins that are open. Defaults to None.
            box_plus_buffer (bool, optional): Either calculate a bounding box based on the location
                of rendered geometries or use chip size from design class. Defaults to True.

        Returns:
            (str, str): Name of the design and name of the setup.
        """
        # save input variables to run(). This line must be the first in the method
        if components is not None:
            argm = dict(locals())
            del argm['self']
            self.save_run_args(**argm)
        # wipe data from the previous run (if any)
        self.clear_data()

        if not self.renderer_initialized:
            self._initialize_renderer()

        renderer_design_name = self._render(name=name,
                                            solution_type='capacitive',
                                            selection=components,
                                            open_pins=open_terminations,
                                            box_plus_buffer=box_plus_buffer,
                                            vars_to_initialize=Dict())

        self._analyze()
        return renderer_design_name, self.sim_setup_name
Beispiel #24
0
    def populate_all_sweep(self, all_sweep: Dict, item: str, option_name: str):
        """Populate the Dict passed in all_sweep from QAnalysis.  

        Args:
            all_sweep (Dict): Reference to hold each item which corresponds
              to option_name.
            item (str): The value of each item that we want to sweep
              in option_name.
            option_name (str): The option of QComponent that we want to sweep.
        """
        sweep_values = Dict()
        sweep_values['option_name'] = option_name
        sweep_values['variables'] = self.parent._variables

        if hasattr(self.parent, 'sim'):
            sweep_values['sim_variables'] = self.parent.sim._variables

        all_sweep[item] = sweep_values
Beispiel #25
0
class MyQComponent2(QComponent):
    """Demonstration2 - Straight segment with variable width/length"""

    # Your knobs to modify the cell behavior
    default_options = Dict(width='0.5mm',
                           height='0.1mm',
                           pos_x='0mm',
                           pos_y='0mm',
                           layer='1')
    """Default drawing options"""
    def make(self):
        """calculates the geometries of the QComponent"""
        p = self.parse_options(
        )  # short-handle alias for the options interpreter

        rect = draw.rectangle(p.width, p.height, p.pos_x, p.pos_y)
        self.add_qgeometry('poly', {'my_polygon': rect},
                           layer=p.layer,
                           subtract=False)
        self.add_pin('in', rect.exterior.coords[:-3:-1], p.height)
Beispiel #26
0
    def _gather_from_all_children(cls):
        """From the QAnalysis core class, traverse the child classes to
        gather the default_setup and data_labels for each child class.

        For default_setup: If the same key is found twice, the youngest child will be picked.

        Returns:
            (dict, set): dict = setup, set = supported_data_labels
        """
        setup_from_children = Dict()
        labels_from_children = set()
        parents = inspect.getmro(cls)

        # len-2: base.py is not expected to have default_setup and data_labels.
        for child in parents[len(parents) - 2::-1]:
            # The template default options are in the attribute `default_setup`.
            if hasattr(child, 'default_setup'):
                setup_from_children.update(deepcopy(child.default_setup))
            # The data_labels are in the attribute `data_labels`.
            if hasattr(child, 'data_labels'):
                labels_from_children.update(child.data_labels)

        return setup_from_children, labels_from_children
class CapNInterdigital(QComponent):
    """Generates a two pin (+) structure comprised of a north CPW transmission
    line, and a south transmission line, coupled together via a finger
    capacitor. Such a structure can be used, as an example, for generating CPW
    resonators. (0,0) represents the center position of the component. Setting
    finger length to 0 gives a simple gap capacitor. The width of the gap
    capacitor is found via.

    (cap_width * finger_count +  * cap_gap * (finger_count-1)).

    Inherits QComponent class.

    ::

                  (0,0)     N
                    +       ^
                    |       |
                    |
                    |
               --|-----|--
              |  |  |  |  |
              |-----|-----|
                    |
                    |
                    |
                    |
                    +

    .. image::
        CapNInterdigital.png

    Options:
        * north_width: '10um' -- The width of the 'north' portion of the CPW transmission line
        * north_gap: '6um' -- The dielectric gap of the 'north' portion of the CPW transmission line
        * south_width: '10um' -- The width of the 'south' portion of the CPW transmission line
        * south_gap: '6um' -- The dielectric gap of the 'south' portion of the CPW transmission line
            (also for the capacitor gap to ground)
        * cap_width: '10um' -- The width of the finger capacitor metal (and islands)
        * cap_gap: '6um' -- The width of dielectric for the capacitive coupling/fingers
        * cap_gap_ground: '6um' -- Width of the dielectric between the capacitor and ground
        * finger_length: '20um' -- The depth of the finger islands of the capacitor
        * finger_count: '5' -- Number of fingers in the capacitor
        * cap_distance: '50um' -- Distance of the north point of the capacitor from the north pin
    """
    component_metadata = Dict(short_name='cpw',
                              _qgeometry_table_poly='True',
                              _qgeometry_table_path='True')
    """Component metadata"""

    #Currently setting the primary CPW length based on the coupling_length
    #May want it to be it's own value that the user can control?
    default_options = Dict(north_width='10um',
                           north_gap='6um',
                           south_width='10um',
                           south_gap='6um',
                           cap_width='10um',
                           cap_gap='6um',
                           cap_gap_ground='6um',
                           finger_length='20um',
                           finger_count='5',
                           cap_distance='50um')
    """Default connector options"""

    TOOLTIP = """Generates a two pin (+) structure
     comprised of a north CPW transmission line, 
     and a south transmission line, coupled 
     together via a finger capacitor."""

    def make(self):
        """Build the component."""
        p = self.p
        N = int(p.finger_count)

        #Finger Capacitor
        cap_box = draw.rectangle(N * p.cap_width + (N - 1) * p.cap_gap,
                                 p.cap_gap + 2 * p.cap_width + p.finger_length,
                                 0, 0)
        make_cut_list = []
        make_cut_list.append([0, (p.finger_length) / 2])
        make_cut_list.append([(p.cap_width) + (p.cap_gap / 2),
                              (p.finger_length) / 2])
        flip = -1

        for i in range(1, N):
            make_cut_list.append([
                i * (p.cap_width) + (2 * i - 1) * (p.cap_gap / 2),
                flip * (p.finger_length) / 2
            ])
            make_cut_list.append([
                (i + 1) * (p.cap_width) + (2 * i + 1) * (p.cap_gap / 2),
                flip * (p.finger_length) / 2
            ])
            flip = flip * -1

        cap_cut = draw.LineString(make_cut_list).buffer(p.cap_gap / 2,
                                                        cap_style=2,
                                                        join_style=2)
        cap_cut = draw.translate(cap_cut,
                                 -(N * p.cap_width + (N - 1) * p.cap_gap) / 2,
                                 0)

        cap_body = draw.subtract(cap_box, cap_cut)
        cap_body = draw.translate(
            cap_body, 0, -p.cap_distance -
            (p.cap_gap + 2 * p.cap_width + p.finger_length) / 2)

        cap_etch = draw.rectangle(
            N * p.cap_width + (N - 1) * p.cap_gap + 2 * p.cap_gap_ground,
            p.cap_gap + 2 * p.cap_width + p.finger_length +
            2 * p.cap_gap_ground, 0, -p.cap_distance -
            (p.cap_gap + 2 * p.cap_width + p.finger_length) / 2)

        #CPW
        north_cpw = draw.LineString([[0, 0], [0, -p.cap_distance]])

        south_cpw = draw.LineString(
            [[
                0, -p.cap_distance -
                (p.cap_gap + 2 * p.cap_width + p.finger_length)
            ],
             [
                 0, -2 * p.cap_distance -
                 (p.cap_gap + 2 * p.cap_width + p.finger_length)
             ]])

        #Rotate and Translate
        c_items = [north_cpw, south_cpw, cap_body, cap_etch]
        c_items = draw.rotate(c_items, p.orientation, origin=(0, 0))
        c_items = draw.translate(c_items, p.pos_x, p.pos_y)
        [north_cpw, south_cpw, cap_body, cap_etch] = c_items

        #Add to qgeometry tables
        self.add_qgeometry('path', {'north_cpw': north_cpw},
                           width=p.north_width,
                           layer=p.layer)
        self.add_qgeometry('path', {'north_cpw_sub': north_cpw},
                           width=p.north_width + 2 * p.north_gap,
                           layer=p.layer,
                           subtract=True)

        self.add_qgeometry('path', {'south_cpw': south_cpw},
                           width=p.south_width,
                           layer=p.layer)
        self.add_qgeometry('path', {'south_cpw_sub': south_cpw},
                           width=p.south_width + 2 * p.south_gap,
                           layer=p.layer,
                           subtract=True)

        self.add_qgeometry('poly', {'cap_body': cap_body}, layer=p.layer)
        self.add_qgeometry('poly', {'cap_etch': cap_etch},
                           layer=p.layer,
                           subtract=True)

        #Add pins
        north_pin_list = north_cpw.coords
        south_pin_list = south_cpw.coords

        self.add_pin('north_end',
                     points=np.array(north_pin_list[::-1]),
                     width=p.north_width,
                     input_as_norm=True)
        self.add_pin('south_end',
                     points=np.array(south_pin_list),
                     width=p.south_width,
                     input_as_norm=True)
Beispiel #28
0
class TransmonPocket6(BaseQubit):
    """Transmon pocket with 6 connection pads.

    Inherits `BaseQubit` class

    Description:
        Create a standard pocket transmon qubit for a ground plane.
        Can have variable number of connection pads, up to 6.

    Options:
        Convention: Values (unless noted) are strings with units included,
        (e.g., '30um')

    Pocket:
        * pos_x / pos_y   - where the center of the pocket should be located on chip
          (where the 'junction' is)
        * pad_gap         - the distance between the two charge islands, which is also the
          resulting 'length' of the pseudo junction
        * inductor_width  - width of the pseudo junction between the two charge islands
          (if in doubt, make the same as pad_gap). Really just for simulating
          in HFSS / other EM software
        * pad_width       - the width (x-axis) of the charge island pads
        * pad_height      - the size (y-axis) of the charge island pads
        * pocket_width    - size of the pocket (cut out in ground) along x-axis
        * pocket_height   - size of the pocket (cut out in ground) along y-axis
        * orientation     - degree of qubit rotation

    Connector lines:
        * pad_gap        - space between the connector pad and the charge island it is
          nearest to
        * pad_width      - width (x-axis) of the connector pad
        * pad_height     - height (y-axis) of the connector pad
        * pad_cpw_shift  - shift the connector pad cpw line by this much away from qubit
        * pad_cpw_extent - how long should the pad be - edge that is parallel to pocket
        * cpw_width      - center trace width of the CPW line
        * cpw_gap        - dielectric gap width of the CPW line
        * cpw_extend     - depth the connector line extends into ground (past the pocket edge)
        * pocket_extent  - How deep into the pocket should we penetrate with the cpw connector
          (into the ground plane)
        * pocket_rise    - How far up or down relative to the center of the transmon should we
          elevate the cpw connection point on the ground plane
        * loc_W / H      - which 'quadrant' of the pocket the connector is set to, +/- 1 (check
          if diagram is correct)


    Sketch:
        Below is a sketch of the qubit
        ::

                 -1             0
                _________________________________
            -1  |______ ____    _|_    __________|          Y
                |      |____|  |___|   |____|    |          ^
                |        __________________      |          |
                |       |     island       |     |          |----->  X
                |       |__________________|     |
                |                 |              |
                |  pocket         x              |
                |        _________|________      |
                |       |                  |     |
                |       |__________________|     |
                |        ______                  |
                |_______|______|                 |
                |________________________________|   +1
                                            +1

    .. image::
        QComponent_Qubit_Transmon_Pocket.png
    """

    #_img = 'transmon_pocket1.png'

    # Default drawing options
    default_options = Dict(
        chip='main',
        pos_x='0um',
        pos_y='0um',
        pad_gap='30um',
        inductor_width='20um',
        pad_width='455um',
        pad_height='90um',
        pocket_width='650um',
        pocket_height='650um',
        # 90 has dipole aligned along the +X axis,
        # while 0 has dipole aligned along the +Y axis
        orientation='0',
        _default_connection_pads=Dict(
            pad_gap='15um',
            pad_width='125um',
            pad_height='30um',
            pad_cpw_shift='0um',
            pad_cpw_extent='25um',
            cpw_width='10um',
            cpw_gap='6um',
            # : cpw_extend: how far into the ground to extend the CPW line from the coupling pads
            cpw_extend='100um',
            pocket_extent='5um',
            pocket_rise='0um',
            loc_W='+1',  # width location  only +-1 or 0
        ))
    """Default drawing options"""

    component_metadata = Dict(short_name='Pocket',
                              _qgeometry_table_path='True',
                              _qgeometry_table_poly='True',
                              _qgeometry_table_junction='True')
    """Component metadata"""

    def make(self):
        """Define the way the options are turned into QGeometry.

        The make function implements the logic that creates the geoemtry
        (poly, path, etc.) from the qcomponent.options dictionary of
        parameters, and the adds them to the design, using
        qcomponent.add_qgeometry(...), adding in extra needed
        information, such as layer, subtract, etc.
        """
        self.make_pocket()
        self.make_connection_pads()

    def make_pocket(self):
        """Makes standard transmon in a pocket."""

        # self.p allows us to directly access parsed values (string -> numbers) form the user option
        p = self.p

        # since we will reuse these options, parse them once and define them as variables
        pad_width = p.pad_width
        pad_height = p.pad_height
        pad_gap = p.pad_gap

        # make the pads as rectangles (shapely polygons)
        pad = draw.rectangle(pad_width, pad_height)
        pad_top = draw.translate(pad, 0, +(pad_height + pad_gap) / 2.)
        pad_bot = draw.translate(pad, 0, -(pad_height + pad_gap) / 2.)

        rect_jj = draw.LineString([(0, -pad_gap / 2), (0, +pad_gap / 2)])
        # the draw.rectangle representing the josephson junction
        # rect_jj = draw.rectangle(p.inductor_width, pad_gap)

        rect_pk = draw.rectangle(p.pocket_width, p.pocket_height)

        # Rotate and translate all qgeometry as needed.
        # Done with utility functions in Metal 'draw_utility' for easy rotation/translation
        # NOTE: Should modify so rotate/translate accepts qgeometry, would allow for
        # smoother implementation.
        polys = [rect_jj, pad_top, pad_bot, rect_pk]
        polys = draw.rotate(polys, p.orientation, origin=(0, 0))
        polys = draw.translate(polys, p.pos_x, p.pos_y)
        [rect_jj, pad_top, pad_bot, rect_pk] = polys

        # Use the geometry to create Metal qgeometry
        self.add_qgeometry('poly', dict(pad_top=pad_top, pad_bot=pad_bot))
        self.add_qgeometry('poly', dict(rect_pk=rect_pk), subtract=True)
        # self.add_qgeometry('poly', dict(
        #     rect_jj=rect_jj), helper=True)
        self.add_qgeometry('junction',
                           dict(rect_jj=rect_jj),
                           width=p.inductor_width)

    def make_connection_pads(self):
        """Makes standard transmon in a pocket."""
        for name in self.options.connection_pads:
            self.make_connection_pad(name)

    def make_connection_pad(self, name: str):
        """Makes n individual connector.

        Args:
            name (str) : Name of the connector
        """

        # self.p allows us to directly access parsed values (string -> numbers) form the user option
        p = self.p
        pc = self.p.connection_pads[name]  # parser on connector options

        # define commonly used variables once
        cpw_width = pc.cpw_width
        cpw_extend = pc.cpw_extend
        pad_width = pc.pad_width
        pad_height = pc.pad_height
        pad_cpw_shift = pc.pad_cpw_shift
        pocket_rise = pc.pocket_rise
        pocket_extent = pc.pocket_extent

        loc_W = float(pc.loc_W)
        loc_W, loc_H = float(pc.loc_W), float(pc.loc_H)
        if float(loc_W) not in [-1., +1., 0] or float(loc_H) not in [-1., +1.]:
            self.logger.info(
                'Warning: Did you mean to define a transmon qubit with loc_W and'
                ' loc_H that are not +1, -1, or 0? Are you sure you want to do this?'
            )

        # Define the geometry
        # Connector pad

        if float(loc_W) != 0:
            connector_pad = draw.rectangle(pad_width, pad_height,
                                           -pad_width / 2, pad_height / 2)
            # Connector CPW wire
            connector_wire_path = draw.wkt.loads(f"""LINESTRING (\
                0 {pad_cpw_shift+cpw_width/2}, \
                {pc.pad_cpw_extent}                           {pad_cpw_shift+cpw_width/2}, \
                {(p.pocket_width-p.pad_width)/2-pocket_extent} {pad_cpw_shift+cpw_width/2+pocket_rise}, \
                {(p.pocket_width-p.pad_width)/2+cpw_extend}    {pad_cpw_shift+cpw_width/2+pocket_rise}\
                                            )""")
        else:
            connector_pad = draw.rectangle(pad_width, pad_height, 0,
                                           pad_height / 2)
            connector_wire_path = draw.LineString(
                [[0, pad_height],
                 [
                     0,
                     (p.pocket_width / 2 - p.pad_height - p.pad_gap / 2 -
                      pc.pad_gap) + cpw_extend
                 ]])

        # Position the connector, rotate and translate
        objects = [connector_pad, connector_wire_path]

        if loc_W == 0:
            loc_Woff = 1
        else:
            loc_Woff = loc_W

        objects = draw.scale(objects, loc_Woff, loc_H, origin=(0, 0))
        objects = draw.translate(
            objects,
            loc_W * (p.pad_width) / 2.,
            loc_H * (p.pad_height + p.pad_gap / 2 + pc.pad_gap))
        objects = draw.rotate_position(objects, p.orientation,
                                       [p.pos_x, p.pos_y])
        [connector_pad, connector_wire_path] = objects

        self.add_qgeometry('poly', {f'{name}_connector_pad': connector_pad})
        self.add_qgeometry('path', {f'{name}_wire': connector_wire_path},
                           width=cpw_width)
        self.add_qgeometry('path', {f'{name}_wire_sub': connector_wire_path},
                           width=cpw_width + 2 * pc.cpw_gap,
                           subtract=True)

        ############################################################

        # add pins
        points = np.array(connector_wire_path.coords)
        self.add_pin(name,
                     points=points[-2:],
                     width=cpw_width,
                     input_as_norm=True)
Beispiel #29
0
class RouteAnchors(QRoute):
    """Creates and connects a series of anchors through which the Route passes.

    QRoute Default Options:
        * pin_inputs: Dict
            * start_pin: Dict -- Component and pin string pair. Define which pin to start from
                * component: '' -- Name of component to start from, which has a pin
                * pin: '' -- Name of pin used for pin_start
            * end_pin=Dict -- Component and pin string pair. Define which pin to start from
                * component: '' -- Name of component to end on, which has a pin
                * pin: '' -- Name of pin used for pin_end
        * fillet: '0'
        * lead: Dict
            * start_straight: '0mm' -- Lead-in, defined as the straight segment extension from start_pin.  Defaults to 0.1um.
            * end_straight: '0mm' -- Lead-out, defined as the straight segment extension from end_pin.  Defaults to 0.1um.
            * start_jogged_extension: '' -- Lead-in, jogged extension of lead-in. Described as list of tuples
            * end_jogged_extension: '' -- Lead-out, jogged extension of lead-out. Described as list of tuples
        * total_length: '7mm'
        * trace_width: 'cpw_width' -- Defines the width of the line.  Defaults to 'cpw_width'.

    Default Options:
        * anchors: OrderedDict -- Intermediate anchors only; doesn't include endpoints
        * advanced: Dict
            * avoid_collision: 'false' -- true/false, defines if the route needs to avoid collisions.  Defaults to 'false'.
    """

    component_metadata = Dict(short_name='cpw')
    """Component metadata"""

    default_options = Dict(
        anchors=OrderedDict(
        ),  # Intermediate anchors only; doesn't include endpoints
        # Example: {1: np.array([x1, y1]), 2: np.array([x2, y2])}
        # startpin -> startpin + leadin -> anchors -> endpin + leadout -> endpin
        advanced=Dict(avoid_collision='false'))
    """Default options"""

    TOOLTIP = """Creates and connects a series of anchors through which the Route passes."""

    from shapely.ops import cascaded_union
    from matplotlib import pyplot as plt
    import geopandas as gpd

    from shapely.geometry import CAP_STYLE, JOIN_STYLE

    def unobstructed_close_up(self, segment: list,
                              component_name: str) -> bool:
        """Checks whether the given component's perimeter intersects or
        overlaps a given segment.

        Args:
            segment (list): 2 vertices, in the form [np.array([x0, y0]), np.array([x1, y1])]
            component_name (str): Alphanumeric component name

        Returns:
            bool: True is no obstacles
        """
        # transform path to polygons
        paths_converted = []
        paths = self.design.components[component_name].qgeometry_table('path')
        for _, row in paths.iterrows():
            paths_converted.append(row['geometry'].buffer(
                row['width'] / 2, cap_style=CAP_STYLE.flat))
        # merge all the polygons
        polygons = self.design.components[component_name].qgeometry_list(
            'poly')
        boundary = gpd.GeoSeries(cascaded_union(polygons + paths_converted))
        boundary_coords = list(boundary.geometry.exterior[0].coords)
        if any(
                intersecting(segment[0], segment[1], boundary_coords[i],
                             boundary_coords[i + 1])
                for i in range(len(boundary_coords) - 1)):
            # At least 1 intersection with the actual component contour; do not proceed!
            return False
        # All clear, no intersections
        return True

    def unobstructed(self, segment: list) -> bool:
        """Check that no component's bounding box in self.design intersects or
        overlaps a given segment.

        Args:
            segment (list): 2 vertices, in the form [np.array([x0, y0]), np.array([x1, y1])]

        Returns:
            bool: True is no obstacles
        """

        # assumes rectangular bounding boxes
        for component in self.design.components:
            if component == self.name:
                continue
            xmin, ymin, xmax, ymax = self.design.components[
                component].qgeometry_bounds()
            # p, q, r, s are corner coordinates of each bounding box
            p, q, r, s = [
                np.array([xmin, ymin]),
                np.array([xmin, ymax]),
                np.array([xmax, ymin]),
                np.array([xmax, ymax])
            ]
            if any(
                    intersecting(segment[0], segment[1], k, l)
                    for k, l in [(p, q), (p, r), (r, s), (q, s)]):
                # At least 1 intersection with the component bounding box. Check the actual contour.
                if not self.unobstructed_close_up(segment, component):
                    # At least 1 intersection with the actual component contour; do not proceed!
                    return False
        # All clear, no intersections
        return True

    def connect_simple(self, start_pt: QRoutePoint,
                       end_pt: QRoutePoint) -> np.ndarray:
        """Try connecting start and end with single or 2-segment/S-shaped CPWs
        if possible.

        Args:
            start_pt (QRoutePoint): QRoutePoint of the start
            end_pt (QRoutePoint): QRoutePoint of the end

        Returns:
            List of vertices of a CPW going from start to end

        Raises:
            QiskitMetalDesignError: If the connect_simple() has failed.
        """
        avoid_collision = self.parse_options().advanced.avoid_collision

        start_direction = start_pt.direction
        start = start_pt.position
        end_direction = end_pt.direction
        end = end_pt.position

        # end_direction originates strictly from endpoint + leadout (NOT intermediate stopping anchors)
        self.assign_direction_to_anchor(start_pt, end_pt)
        stop_direction = end_pt.direction

        if (start[0] == end[0]) or (start[1] == end[1]):
            # Matching x or y coordinates -> check if endpoints can be connected with a single segment
            if mao.dot(start_direction, end - start) >= 0:
                # Start direction and end - start for CPW must not be anti-aligned
                if (end_direction is None) or (mao.dot(end - start,
                                                       end_direction) <= 0):
                    # If leadout + end has been reached, the single segment CPW must not be aligned with its direction
                    return np.empty((0, 2), float)
        else:
            # If the endpoints don't share a common x or y value:
            # designate them as 2 corners of an axis aligned rectangle
            # and check if both start and end directions are aligned with
            # the displacement vectors between start/end and
            # either of the 2 remaining corners ("perfect alignment").
            corner1 = np.array([start[0],
                                end[1]])  # x coordinate matches with start
            corner2 = np.array([end[0],
                                start[1]])  # x coordinate matches with end
            if avoid_collision:
                # Check for collisions at the outset to avoid repeat work
                startc1end = bool(
                    self.unobstructed([start, corner1])
                    and self.unobstructed([corner1, end]))
                startc2end = bool(
                    self.unobstructed([start, corner2])
                    and self.unobstructed([corner2, end]))
            else:
                startc1end = startc2end = True
            if (mao.dot(start_direction, corner1 - start) > 0) and startc1end:
                # corner1 is "in front of" the start_pt
                if (end_direction is None) or (mao.dot(end_direction,
                                                       corner1 - end) >= 0):
                    # corner1 is also "in front of" the end_pt
                    return np.expand_dims(corner1, axis=0)
            elif (mao.dot(start_direction, corner2 - start) >
                  0) and startc2end:
                # corner2 is "in front of" the start_pt
                if (end_direction is None) or (mao.dot(end_direction,
                                                       corner2 - end) >= 0):
                    # corner2 is also "in front of" the end_pt
                    return np.expand_dims(corner2, axis=0)
            # In notation below, corners 3 and 4 correspond to
            # the ends of the segment bisecting the longer rectangle formed by start and end
            # while the segment formed by corners 5 and 6 bisect the shorter rectangle
            if stop_direction[
                    0]:  # "Wide" rectangle -> vertical middle segment is more natural
                corner3 = np.array([(start[0] + end[0]) / 2, start[1]])
                corner4 = np.array([(start[0] + end[0]) / 2, end[1]])
                corner5 = np.array([start[0], (start[1] + end[1]) / 2])
                corner6 = np.array([end[0], (start[1] + end[1]) / 2])
            else:  # "Tall" rectangle -> horizontal middle segment is more natural
                corner3 = np.array([start[0], (start[1] + end[1]) / 2])
                corner4 = np.array([end[0], (start[1] + end[1]) / 2])
                corner5 = np.array([(start[0] + end[0]) / 2, start[1]])
                corner6 = np.array([(start[0] + end[0]) / 2, end[1]])
            if avoid_collision:
                startc3c4end = bool(
                    self.unobstructed([start, corner3])
                    and self.unobstructed([corner3, corner4])
                    and self.unobstructed([corner4, end]))
                startc5c6end = bool(
                    self.unobstructed([start, corner5])
                    and self.unobstructed([corner5, corner6])
                    and self.unobstructed([corner6, end]))
            else:
                startc3c4end = startc5c6end = True
            if (mao.dot(start_direction, stop_direction) < 0) and (mao.dot(
                    start_direction, corner3 - start) > 0) and startc3c4end:
                if (end_direction is None) or (mao.dot(end_direction,
                                                       corner4 - end) > 0):
                    # Perfectly aligned S-shaped CPW
                    return np.vstack((corner3, corner4))
            # Relax constraints and check if imperfect 2-segment or S-segment works,
            # where "imperfect" means 1 or more dot products of directions
            # between successive segments is 0; otherwise return an empty list
            if (mao.dot(start_direction, corner1 - start) >= 0) and startc1end:
                if (end_direction is None) or (mao.dot(end_direction,
                                                       corner1 - end) >= 0):
                    return np.expand_dims(corner1, axis=0)
            if (mao.dot(start_direction, corner2 - start) >= 0) and startc2end:
                if (end_direction is None) or (mao.dot(end_direction,
                                                       corner2 - end) >= 0):
                    return np.expand_dims(corner2, axis=0)
            if (mao.dot(start_direction, corner3 - start) >=
                    0) and startc3c4end:
                if (end_direction is None) or (mao.dot(end_direction,
                                                       corner4 - end) >= 0):
                    return np.vstack((corner3, corner4))
            if (mao.dot(start_direction, corner5 - start) >=
                    0) and startc5c6end:
                if (end_direction is None) or (mao.dot(end_direction,
                                                       corner6 - end) >= 0):
                    return np.vstack((corner5, corner6))
        raise QiskitMetalDesignError(
            "connect_simple() has failed. This might be due to one of two reasons. "
            f"1. Either one of the start point {start} or the end point {end} "
            "provided are inside the bounding box of another QComponent. "
            "Please move the point, or setup a \"lead\" to exit the QComponent area. "
            "2. none of the 4 routing possibilities of this algorithm "
            "(^|_, ^^|, __|, _|^) can complete. Please use Pathfinder instead")

    def free_manhattan_length_anchors(self):
        """Computes the free-flight manhattan distance between start_pt and
        end_pt passing through all of the given anchor points.

        Returns:
            float: Total length connecting all points in order
        """
        anchors = self.parse_options().anchors
        reference = [self.head.get_tip().position]
        reference.extend(list(anchors.values()))
        reference.append(self.tail.get_tip().position)

        length = 0
        for i in range(1, len(reference)):
            length += abs(reference[i][0] -
                          reference[i - 1][0]) + abs(reference[i][1] -
                                                     reference[i - 1][1])
        return length

    def trim_pts(self):
        """Crops the sequence of points to concatenate.

        For example, if a segment between two anchors has no points,
        then the segment is eliminated (only anchor points will do).
        Modified directly the self.intermediate_pts, thus nothing is
        returned.
        """
        if isinstance(self.intermediate_pts, Mapping):
            keys_to_delete = set()
            for key, value in self.intermediate_pts.items():
                if value is None:
                    keys_to_delete.add(key)
                try:
                    # value is a list
                    if not value:
                        keys_to_delete.add(key)
                except ValueError:
                    # value is a numpy
                    if not value.size:
                        keys_to_delete.add(key)
            for key in keys_to_delete:
                del self.intermediate_pts[key]

    def make(self):
        """Generates path from start pin to end pin."""
        p = self.parse_options()
        anchors = p.anchors

        # Set the CPW pins and add the points/directions to the lead-in/out arrays
        self.set_pin("start")
        self.set_pin("end")

        # Align the lead-in/out to the input options set from the user
        start_point = self.set_lead("start")
        end_point = self.set_lead("end")

        self.intermediate_pts = OrderedDict()
        for arc_num, coord in anchors.items():
            arc_pts = self.connect_simple(self.get_tip(), QRoutePoint(coord))
            if arc_pts is None:
                self.intermediate_pts[arc_num] = [coord]
            else:
                self.intermediate_pts[arc_num] = np.concatenate(
                    [arc_pts, [coord]], axis=0)
        arc_pts = self.connect_simple(self.get_tip(), end_point)
        if arc_pts is not None:
            self.intermediate_pts[len(anchors)] = np.array(arc_pts)

        # concatenate all points, transforming the dictionary into a single numpy array
        self.trim_pts()
        self.intermediate_pts = np.concatenate(list(
            self.intermediate_pts.values()),
                                               axis=0)

        # Make points into elements
        self.make_elements(self.get_points())
class CPWTFingerCap(QComponent):
    """Generates a three pin (+) structure comprised of a primary two pin CPW
    transmission line,     and a secondary one pin neighboring CPW transmission
    line that is capacitively coupled to the primary. Such a structure can be
    used, as an example, for generating CPW resonator hangars off of a
    transmission line. (0,0) represents the center position of the component.
    Setting finger length to 0 gives a simple gap capacitor.

    Inherits QComponent class.

    ::

                  (0,0)
        +--------------------------+
                    |
               --|-----|--
              |  |  |  |  |
              |-----|-----|
                    |
                    |
                    |
                    |
                    +

    Options:
        * prime_width: '10um' -- The width of the trace of the two pin CPW transmission line
        * prime_gap: '6um' -- The dielectric gap of the two pin CPW transmission line
        * second_width: '10um' -- The width of the trace of the one pin CPW transmission line
        * second_gap: '6um' -- The dielectric gap of the one pin CPW transmission line (also for the capacitor)
        * cap_gap: '6um' -- The width of dielectric for the capacitive coupling (default same as second_gap)
        * cap_width: '10um' -- The width of the finger capacitor (default same as second)
        * finger_length: '20um' -- The depth of the charge islands of the capacitor
        * finger_count: '5' -- Number of fingers in the capacitor
        * cap_distance: '50um' -- Distance of capacitor from center transmission line
        * pos_x/_y: '0um' -- The x/y position of the center of the primary transmission line.
        * rotation: '0' -- The direction of the primary transmission line. 0 degrees is +x, following a
          counter-clockwise rotation (eg. 90 is +y)
        * chip: 'main' -- The chip the capacitor should be on.
        * layer: '1' -- Layer the capacitor is on.
    """
    component_metadata = Dict(short_name='cpw',
                              _qgeometry_table_poly='True',
                              _qgeometry_table_path='True')
    """Component metadata"""

    #Currently setting the primary CPW length based on the coupling_length
    #May want it to be it's own value that the user can control?
    default_options = Dict(prime_width='10um',
                           prime_gap='6um',
                           second_width='10um',
                           second_gap='6um',
                           cap_gap='6um',
                           cap_width='10um',
                           finger_length='20um',
                           finger_count='5',
                           cap_distance='50um',
                           pos_x='0um',
                           pos_y='0um',
                           orientation='0',
                           chip='main',
                           layer='1')
    """Default connector options"""
    def make(self):
        """Build the component."""
        p = self.p
        N = int(p.finger_count)
        prime_cpw_length = p.cap_width * 2 * N

        #Primary CPW
        prime_cpw = draw.LineString([[-prime_cpw_length / 2, 0],
                                     [prime_cpw_length / 2, 0]])

        #Finger Capacitor
        cap_box = draw.rectangle(N * p.cap_width + (N - 1) * p.cap_gap,
                                 p.cap_gap + 2 * p.cap_width + p.finger_length,
                                 0, 0)
        make_cut_list = []
        make_cut_list.append([0, (p.finger_length) / 2])
        make_cut_list.append([(p.cap_width) + (p.cap_gap / 2),
                              (p.finger_length) / 2])
        flip = -1

        for i in range(1, N):
            make_cut_list.append([
                i * (p.cap_width) + (2 * i - 1) * (p.cap_gap / 2),
                flip * (p.finger_length) / 2
            ])
            make_cut_list.append([
                (i + 1) * (p.cap_width) + (2 * i + 1) * (p.cap_gap / 2),
                flip * (p.finger_length) / 2
            ])
            flip = flip * -1

        cap_cut = draw.LineString(make_cut_list).buffer(p.cap_gap / 2,
                                                        cap_style=2,
                                                        join_style=2)
        cap_cut = draw.translate(cap_cut,
                                 -(N * p.cap_width + (N - 1) * p.cap_gap) / 2,
                                 0)

        cap_body = draw.subtract(cap_box, cap_cut)
        cap_body = draw.translate(
            cap_body, 0, -p.cap_distance -
            (p.cap_gap + 2 * p.cap_width + p.finger_length) / 2)

        cap_etch = draw.rectangle(
            N * p.cap_width + (N - 1) * p.cap_gap + 2 * p.second_gap,
            p.cap_gap + 2 * p.cap_width + p.finger_length + 2 * p.second_gap,
            0, -p.cap_distance -
            (p.cap_gap + 2 * p.cap_width + p.finger_length) / 2)

        #Secondary CPW
        second_cpw_top = draw.LineString([[0, -p.prime_width / 2],
                                          [0, -p.cap_distance]])

        second_cpw_bottom = draw.LineString(
            [[
                0, -p.cap_distance -
                (p.cap_gap + 2 * p.cap_width + p.finger_length)
            ],
             [
                 0, -2 * p.cap_distance -
                 (p.cap_gap + 2 * p.cap_width + p.finger_length)
             ]])

        #Rotate and Translate
        c_items = [
            prime_cpw, second_cpw_top, second_cpw_bottom, cap_body, cap_etch
        ]
        c_items = draw.rotate(c_items, p.orientation, origin=(0, 0))
        c_items = draw.translate(c_items, p.pos_x, p.pos_y)
        [prime_cpw, second_cpw_top, second_cpw_bottom, cap_body,
         cap_etch] = c_items

        #Add to qgeometry tables
        self.add_qgeometry('path', {'prime_cpw': prime_cpw},
                           width=p.prime_width,
                           layer=p.layer)
        self.add_qgeometry('path', {'prime_cpw_sub': prime_cpw},
                           width=p.prime_width + 2 * p.prime_gap,
                           subtract=True,
                           layer=p.layer)
        self.add_qgeometry('path', {
            'second_cpw_top': second_cpw_top,
            'second_cpw_bottom': second_cpw_bottom
        },
                           width=p.second_width,
                           layer=p.layer)
        self.add_qgeometry('path', {
            'second_cpw_top_sub': second_cpw_top,
            'second_cpw_bottom_sub': second_cpw_bottom
        },
                           width=p.second_width + 2 * p.second_gap,
                           subtract=True,
                           layer=p.layer)

        self.add_qgeometry('poly', {'cap_body': cap_body}, layer=p.layer)
        self.add_qgeometry('poly', {'cap_etch': cap_etch},
                           layer=p.layer,
                           subtract=True)

        #Add pins
        prime_pin_list = prime_cpw.coords
        second_pin_list = second_cpw_bottom.coords

        self.add_pin('prime_start',
                     points=np.array(prime_pin_list[::-1]),
                     width=p.prime_width,
                     input_as_norm=True)
        self.add_pin('prime_end',
                     points=np.array(prime_pin_list),
                     width=p.prime_width,
                     input_as_norm=True)
        self.add_pin('second_end',
                     points=np.array(second_pin_list),
                     width=p.second_width,
                     input_as_norm=True)