예제 #1
0
파일: copy.py 프로젝트: simbilod/gdsfactory
def copy(D: Component,
         prefix: str = "",
         suffix: str = "_copy",
         cache: bool = True) -> Component:
    """returns a deep copy of a Component.
    based on phidl.geometry with CellArray support
    """
    D_copy = Component(name=f"{prefix}{D.name}{suffix}")
    D_copy.info = python_copy.deepcopy(D.info)
    for ref in D.references:
        if isinstance(ref, DeviceReference):
            new_ref = ComponentReference(
                ref.parent,
                origin=ref.origin,
                rotation=ref.rotation,
                magnification=ref.magnification,
                x_reflection=ref.x_reflection,
            )
            new_ref.owner = D_copy
        elif isinstance(ref, gdspy.CellArray):
            new_ref = CellArray(
                device=ref.parent,
                columns=ref.columns,
                rows=ref.rows,
                spacing=ref.spacing,
                origin=ref.origin,
                rotation=ref.rotation,
                magnification=ref.magnification,
                x_reflection=ref.x_reflection,
            )
        D_copy.add(new_ref)
        for alias_name, alias_ref in D.aliases.items():
            if alias_ref == ref:
                D_copy.aliases[alias_name] = new_ref

    for port in D.ports.values():
        D_copy.add_port(port=port)
    for poly in D.polygons:
        D_copy.add_polygon(poly)
    for label in D.labels:
        D_copy.add_label(
            text=label.text,
            position=label.position,
            layer=(label.layer, label.texttype),
        )
    if cache:
        D_copy = avoid_duplicated_cells(D_copy)
    return D_copy
예제 #2
0
def from_yaml(
    yaml_str: Union[str, pathlib.Path, IO[Any]],
    component_factory: ComponentFactoryDict = factory,
    routing_strategy: Dict[str, Callable] = routing_strategy_factories,
    cross_section_factory: Dict[str,
                                CrossSectionFactory] = cross_section_factory,
    label_instance_function: Callable = add_instance_label,
    **kwargs,
) -> Component:
    """Returns a Component defined in YAML file or string.

    Args:
        yaml: YAML IO describing Component file or string (with newlines)
          (instances, placements, routes, ports, connections, names)
        component_factory: dict of functions {factory_name: factory_function}
        routing_strategy: for links
        label_instance_function: to label each instance
        kwargs: cache, prefix, autoname ... to pass to all factories

    Returns:
        Component

    .. code::

        valid properties:
        name: Optional Component name
        vars: Optional variables
        info: Optional component info
            description: just a demo
            polarization: TE
            ...
        instances:
            name:
                component: (ComponentFactory)
                settings (Optional)
                    length: 10
                    ...
        placements:
            x: Optional[float, str]  str can be instanceName,portName
            y: Optional[float, str]
            rotation: Optional[float]
            mirror: Optional[bool, float] float is x mirror axis
            port: Optional[str] port anchor
        connections (Optional): between instances
        ports (Optional): ports to expose
        routes (Optional): bundles of routes
            routeName:
            library: optical
            links:
                instance1,port1: instance2,port2


    .. code::

        vars:
            lenght: 3

        instances:
            mmi_bot:
              component: mmi1x2
              settings:
                width_mmi: 4.5
                length_mmi: 10
            mmi_top:
              component: mmi1x2
              settings:
                width_mmi: 4.5
                length_mmi: 5

        placements:
            mmi_top:
                port: o1
                x: 0
                y: 0
            mmi_bot:
                port: o1
                x: mmi_top,o2
                y: mmi_top,o2
                dx: 30
                dy: -30
        routes:
            optical:
                library: optical
                links:
                    mmi_top,o3: mmi_bot,o1

    """
    yaml_str = (io.StringIO(yaml_str) if isinstance(yaml_str, str)
                and "\n" in yaml_str else yaml_str)

    conf = OmegaConf.load(
        yaml_str)  # nicer loader than conf = yaml.safe_load(yaml_str)
    for key in conf.keys():
        assert key in valid_top_level_keys, f"{key} not in {list(valid_top_level_keys)}"

    instances = {}
    routes = {}
    name = conf.get(
        "name",
        f"Unnamed_{hashlib.md5(json.dumps(OmegaConf.to_container(conf)).encode()).hexdigest()[:8]}",
    )
    if name in CACHE:
        return CACHE[name]
    else:
        c = Component(name)
        CACHE[name] = c
    placements_conf = conf.get("placements")
    routes_conf = conf.get("routes")
    ports_conf = conf.get("ports")
    connections_conf = conf.get("connections")
    instances_dict = conf["instances"]
    c.info = conf.get("info", omegaconf.DictConfig({}))

    for instance_name in instances_dict:
        instance_conf = instances_dict[instance_name]
        component_type = instance_conf["component"]
        assert (component_type in component_factory
                ), f"{component_type} not in {list(component_factory.keys())}"

        settings = instance_conf.get("settings", {})
        settings = OmegaConf.to_container(settings,
                                          resolve=True) if settings else {}
        settings.update(**kwargs)

        if "cross_section" in settings:
            name_or_dict = settings["cross_section"]
            if isinstance(name_or_dict, str):
                cross_section = cross_section_factory[name_or_dict]
            elif isinstance(name_or_dict, dict):
                name = name_or_dict.pop("function")
                cross_section = functools.partial(cross_section_factory[name],
                                                  **name_or_dict)
            else:
                raise ValueError(
                    f"invalid type for cross_section={name_or_dict}")
            settings["cross_section"] = cross_section

        ci = component_factory[component_type](**settings)
        ref = c << ci
        instances[instance_name] = ref

    placements_conf = dict() if placements_conf is None else placements_conf

    connections_by_transformed_inst = transform_connections_dict(
        connections_conf)
    components_to_place = set(placements_conf.keys())
    components_with_placement_conflicts = components_to_place.intersection(
        connections_by_transformed_inst.keys())
    for instance_name in components_with_placement_conflicts:
        placement_settings = placements_conf[instance_name]
        if "x" in placement_settings or "y" in placement_settings:
            warnings.warn(
                f"YAML defined: ({', '.join(components_with_placement_conflicts)}) "
                +
                "with both connection and placement. Please use one or the other.",
            )

    all_remaining_insts = list(
        set(placements_conf.keys()).union(
            set(connections_by_transformed_inst.keys())))

    while all_remaining_insts:
        place(
            placements_conf=placements_conf,
            connections_by_transformed_inst=connections_by_transformed_inst,
            instances=instances,
            encountered_insts=list(),
            all_remaining_insts=all_remaining_insts,
        )

    for instance_name in instances_dict:
        label_instance_function(component=c,
                                instance_name=instance_name,
                                reference=instances[instance_name])

    if routes_conf:
        for route_alias in routes_conf:
            route_names = []
            ports1 = []
            ports2 = []
            routes_dict = routes_conf[route_alias]
            for key in routes_dict.keys():
                if key not in valid_route_keys:
                    raise ValueError(
                        f"`{route_alias}` key=`{key}` not in {valid_route_keys}"
                    )

            settings = routes_dict.pop("settings", {})
            settings = (OmegaConf.to_container(settings, resolve=True)
                        if settings else {})
            if "cross_section" in settings:
                name_or_dict = settings["cross_section"]
                if isinstance(name_or_dict, str):
                    cross_section = cross_section_factory[name_or_dict]
                elif isinstance(name_or_dict, dict):
                    name = name_or_dict.pop("function")
                    cross_section = functools.partial(
                        cross_section_factory[name], **name_or_dict)
                else:
                    raise ValueError(
                        f"invalid type for cross_section={name_or_dict}")
                settings["cross_section"] = cross_section
            routing_strategy_name = routes_dict.pop("routing_strategy",
                                                    "get_bundle")
            if routing_strategy_name not in routing_strategy:
                raise ValueError(
                    f"function `{routing_strategy_name}` not in routing_strategy {list(routing_strategy.keys())}"
                )

            if "links" not in routes_dict:
                raise ValueError(
                    f"You need to define links for the `{route_alias}` route")
            links_dict = routes_dict["links"]

            for port_src_string, port_dst_string in links_dict.items():

                if ":" in port_src_string:
                    src, src0, src1 = [
                        s.strip() for s in port_src_string.split(":")
                    ]
                    dst, dst0, dst1 = [
                        s.strip() for s in port_dst_string.split(":")
                    ]
                    instance_src_name, port_src_name = [
                        s.strip() for s in src.split(",")
                    ]
                    instance_dst_name, port_dst_name = [
                        s.strip() for s in dst.split(",")
                    ]

                    src0 = int(src0)
                    src1 = int(src1)
                    dst0 = int(dst0)
                    dst1 = int(dst1)

                    if src1 > src0:
                        ports1names = [
                            f"{port_src_name}{i}"
                            for i in range(src0, src1 + 1, 1)
                        ]
                    else:
                        ports1names = [
                            f"{port_src_name}{i}"
                            for i in range(src0, src1 - 1, -1)
                        ]

                    if dst1 > dst0:
                        ports2names = [
                            f"{port_dst_name}{i}"
                            for i in range(dst0, dst1 + 1, 1)
                        ]
                    else:
                        ports2names = [
                            f"{port_dst_name}{i}"
                            for i in range(dst0, dst1 - 1, -1)
                        ]

                    assert len(ports1names) == len(ports2names)
                    route_names += [
                        f"{instance_src_name},{i}:{instance_dst_name},{j}"
                        for i, j in zip(ports1names, ports2names)
                    ]

                    instance_src = instances[instance_src_name]
                    instance_dst = instances[instance_dst_name]

                    for port_src_name in ports1names:
                        assert port_src_name in instance_src.ports, (
                            f"{port_src_name} not in {list(instance_src.ports.keys())}"
                            f"for {instance_src_name} ")
                        ports1.append(instance_src.ports[port_src_name])

                    for port_dst_name in ports2names:
                        assert port_dst_name in instance_dst.ports, (
                            f"{port_dst_name} not in {list(instance_dst.ports.keys())}"
                            f"for {instance_dst_name}")
                        ports2.append(instance_dst.ports[port_dst_name])

                    # print(ports1)
                    # print(ports2)
                    # print(route_names)

                else:
                    instance_src_name, port_src_name = port_src_string.split(
                        ",")
                    instance_dst_name, port_dst_name = port_dst_string.split(
                        ",")

                    instance_src_name = instance_src_name.strip()
                    instance_dst_name = instance_dst_name.strip()
                    port_src_name = port_src_name.strip()
                    port_dst_name = port_dst_name.strip()

                    assert (
                        instance_src_name in instances
                    ), f"{instance_src_name} not in {list(instances.keys())}"
                    assert (
                        instance_dst_name in instances
                    ), f"{instance_dst_name} not in {list(instances.keys())}"

                    instance_src = instances[instance_src_name]
                    instance_dst = instances[instance_dst_name]

                    assert port_src_name in instance_src.ports, (
                        f"{port_src_name} not in {list(instance_src.ports.keys())} for"
                        f" {instance_src_name} ")
                    assert port_dst_name in instance_dst.ports, (
                        f"{port_dst_name} not in {list(instance_dst.ports.keys())} for"
                        f" {instance_dst_name}")

                    ports1.append(instance_src.ports[port_src_name])
                    ports2.append(instance_dst.ports[port_dst_name])
                    route_name = f"{port_src_string}:{port_dst_string}"
                    route_names.append(route_name)

            routing_function = routing_strategy[routing_strategy_name]
            route_or_route_list = routing_function(
                ports1=ports1,
                ports2=ports2,
                **settings,
            )

            # FIXME, be more consistent
            if isinstance(route_or_route_list, list):
                for route_name, route_dict in zip(route_names,
                                                  route_or_route_list):
                    c.add(route_dict.references)
                    routes[route_name] = route_dict.length
            elif isinstance(route_or_route_list, Route):
                c.add(route_or_route_list.references)
                routes[route_name] = route_or_route_list.length
            else:
                raise ValueError(
                    f"{route_or_route_list} needs to be a Route or a list")

    if ports_conf:
        assert hasattr(ports_conf, "items"), f"{ports_conf} needs to be a dict"
        for port_name, instance_comma_port in ports_conf.items():
            instance_name, instance_port_name = instance_comma_port.split(",")
            instance_name = instance_name.strip()
            instance_port_name = instance_port_name.strip()
            assert (instance_name in instances
                    ), f"{instance_name} not in {list(instances.keys())}"
            instance = instances[instance_name]
            assert instance_port_name in instance.ports, (
                f"{instance_port_name} not in {list(instance.ports.keys())} for"
                f" {instance_name} ")
            c.add_port(port_name, port=instance.ports[instance_port_name])
    c.routes = routes
    c.instances = instances
    return c
예제 #3
0
def import_gds(
    gdspath: Union[str, Path],
    cellname: Optional[str] = None,
    flatten: bool = False,
    snap_to_grid_nm: Optional[int] = None,
    name: Optional[str] = None,
    decorator: Optional[Callable] = None,
    gdsdir: Optional[Union[str, Path]] = None,
    **kwargs,
) -> Component:
    """Returns a Componenent from a GDS file.

    Adapted from phidl/geometry.py

    if any cell names are found on the component CACHE we append a $ with a
    number to the name

    Args:
        gdspath: path of GDS file.
        cellname: cell of the name to import (None) imports top cell.
        flatten: if True returns flattened (no hierarchy)
        snap_to_grid_nm: snap to different nm grid (does not snap if False)
        name: Optional name. Over-rides the default imported name.
        decorator: function to apply over the imported gds.
        gdsdir: optional GDS directory.
        kwargs: settings for the imported component (polarization, wavelength ...).
    """
    gdspath = Path(gdsdir) / Path(gdspath) if gdsdir else Path(gdspath)
    if not gdspath.exists():
        raise FileNotFoundError(f"No file {gdspath!r} found")

    metadata_filepath = gdspath.with_suffix(".yml")

    gdsii_lib = gdspy.GdsLibrary()
    gdsii_lib.read_gds(str(gdspath))
    top_level_cells = gdsii_lib.top_level()
    cellnames = [c.name for c in top_level_cells]

    if cellname is not None:
        if cellname not in gdsii_lib.cells:
            raise ValueError(
                f"cell {cellname} is not in file {gdspath} with cells {cellnames}"
            )
        topcell = gdsii_lib.cells[cellname]
    elif cellname is None and len(top_level_cells) == 1:
        topcell = top_level_cells[0]
    elif cellname is None and len(top_level_cells) > 1:
        raise ValueError(
            f"import_gds() There are multiple top-level cells in {gdspath!r}, "
            f"you must specify `cellname` to select of one of them among {cellnames}"
        )

    if name:
        if name in CACHE or name in CACHE_IMPORTED_CELLS:
            raise ValueError(
                f"name = {name!r} already on cache. "
                "Please, choose a different name or set name = None. ")
        else:
            topcell.name = name

    if flatten:
        component = Component(name=name or cellname or cellnames[0])
        polygons = topcell.get_polygons(by_spec=True)

        for layer_in_gds, polys in polygons.items():
            component.add_polygon(polys, layer=layer_in_gds)

        component = avoid_duplicated_cells(component)

    else:
        D_list = []
        cell_to_device = {}
        for c in gdsii_lib.cells.values():
            D = Component(name=c.name)
            D.polygons = c.polygons
            D.references = c.references
            D.name = c.name
            for label in c.labels:
                rotation = label.rotation
                if rotation is None:
                    rotation = 0
                label_ref = D.add_label(
                    text=label.text,
                    position=np.asfarray(label.position),
                    magnification=label.magnification,
                    rotation=rotation * 180 / np.pi,
                    layer=(label.layer, label.texttype),
                )
                label_ref.anchor = label.anchor

            D = avoid_duplicated_cells(D)
            D.unlock()

            cell_to_device.update({c: D})
            D_list += [D]

        for D in D_list:
            # First convert each reference so it points to the right Device
            converted_references = []
            for e in D.references:
                ref_device = cell_to_device[e.ref_cell]
                if isinstance(e, gdspy.CellReference):
                    dr = DeviceReference(
                        device=ref_device,
                        origin=e.origin,
                        rotation=e.rotation,
                        magnification=e.magnification,
                        x_reflection=e.x_reflection,
                    )
                    dr.owner = D
                    converted_references.append(dr)
                elif isinstance(e, gdspy.CellArray):
                    dr = CellArray(
                        device=ref_device,
                        columns=e.columns,
                        rows=e.rows,
                        spacing=e.spacing,
                        origin=e.origin,
                        rotation=e.rotation,
                        magnification=e.magnification,
                        x_reflection=e.x_reflection,
                    )
                    dr.owner = D
                    converted_references.append(dr)
            D.references = converted_references

            # Next convert each Polygon
            # temp_polygons = list(D.polygons)
            # D.polygons = []
            # for p in temp_polygons:
            #     D.add_polygon(p)

            # Next convert each Polygon
            temp_polygons = list(D.polygons)
            D.polygons = []
            for p in temp_polygons:
                if snap_to_grid_nm:
                    points_on_grid = snap_to_grid(p.polygons[0],
                                                  nm=snap_to_grid_nm)
                    p = gdspy.Polygon(points_on_grid,
                                      layer=p.layers[0],
                                      datatype=p.datatypes[0])
                D.add_polygon(p)
        component = cell_to_device[topcell]
        cast(Component, component)

    name = name or component.name
    component.name = name

    if metadata_filepath.exists():
        logger.info(f"Read YAML metadata from {metadata_filepath}")
        metadata = OmegaConf.load(metadata_filepath)

        for port_name, port in metadata.ports.items():
            if port_name not in component.ports:
                component.add_port(
                    name=port_name,
                    midpoint=port.midpoint,
                    width=port.width,
                    orientation=port.orientation,
                    layer=port.layer,
                    port_type=port.port_type,
                )

        component.info = metadata.info

    component.info.update(**kwargs)
    component.name = name
    component.info.name = name

    if decorator:
        component_new = decorator(component)
        component = component_new or component
    if flatten:
        component.flatten()
    component.lock()
    return component