Example #1
0
def placer_grid_cell_refs(component_factory,
                          cols=1,
                          rows=1,
                          dx=10.0,
                          dy=10.0,
                          x0=0,
                          y0=0,
                          **settings):
    if callable(component_factory):
        settings_list = get_settings_list(**settings)
        component_list = [component_factory(**s) for s in settings_list]
    else:
        component_list = component_factory

    indices = [(i, j) for j in range(cols) for i in range(rows)]

    if rows * cols < len(component_list):
        raise ValueError(
            "Shape ({}, {}): Not enough emplacements ({}) for all these components ({})."
            .format(rows, cols, len(indices), len(component_list)))
    components = []
    for component, (i, j) in zip(component_list, indices):
        c_ref = component.ref(position=(x0 + j * dx, y0 + i * dy))
        components += [c_ref]

    return components
Example #2
0
def load_placer_with_does(filepath, defaults={"do_permutation": True}):
    """ load placer settings

    Args:
        filepath: a yaml file containing the does and placer information

    Returns:
        a dictionnary of DOEs with:
        {
            doe_name1: [(component_factory_name, parameters), ...]
            doe_name2: [(component_factory_name, parameters), ...]
            ...
        }

    """
    does = {}
    data = OmegaConf.load(filepath)

    placer_info = data.pop("placer")
    component_placement = data.pop("placement")
    gds_files = {}
    if "gds" in data.keys():
        gds_files.update(data.pop("gds"))

    for doe_chunk in data.values():
        doe_name = doe_chunk.pop("doe_name")
        component_type = doe_chunk.pop("component")

        if doe_name not in does:
            does[doe_name] = {"settings": [], "component_type": component_type}

        do_permutation = defaults["do_permutation"]
        if "do_permutation" in doe_chunk:
            do_permutation = doe_chunk.pop("do_permutation")

        doe = does[doe_name]
        # All the remaining parameters are component parameters
        if "settings" in doe_chunk:
            settings = doe_chunk.pop("settings")
        else:
            settings = {}
        doe["list_settings"] += get_settings_list(do_permutation, **settings)

        # check that the doe is valid (only one type of component)
        assert (
            component_type == doe["component_type"]
        ), "There can be only one component type per doe. Got {} while expecting {}".format(
            component_type, doe["component_type"]
        )

    return does, placer_info, component_placement, gds_files
Example #3
0
def get_markdown_table(do_permutations=True, **kwargs):
    """ returns the markdown table for a parameter sweep
    """
    list_settings = get_settings_list(do_permutations=do_permutations,
                                      **kwargs)
    # Convert table fields to strings
    head = [[str(field) for field in fields.keys()]
            for fields in list_settings][0]
    list_data_str = [[str(field) for field in fields.values()]
                     for fields in list_settings]
    N = len(head)

    field_sizes = [0] * N

    # compute the max number of character in each field
    for header, fields in zip(head, list_data_str):
        field_sizes = [
            max(field_sizes[i], len(fields[i]), len(head[i])) for i in range(N)
        ]

    field_sizes = [n + 2 for n in field_sizes]

    # Line formatting from fields
    def fmt_line(fields):
        fmt_fields = [
            " " + fields[i] + " " * (field_sizes[i] - len(fields[i]) - 1)
            for i in range(N)
        ]
        return "|".join(fmt_fields)

    def table_head_sep():
        return "|".join(["-" * n for n in field_sizes])

    t = []
    t.append(fmt_line(head))
    t.append(table_head_sep())
    for fields in list_data_str[0:]:
        t.append(fmt_line(fields))

    return t
Example #4
0
def write_doe(
    component_type,
    doe_name=None,
    do_permutations=True,
    list_settings=None,
    doe_settings=None,
    path=CONFIG["build_directory"],
    doe_metadata_path=CONFIG["doe_directory"],
    functions=None,
    name2function=name2function,
    component_factory=component_factory,
    **kwargs,
):
    """ writes each device GDS, together with metadata for each device:
    Returns a list of gdspaths

    - .gds geometry for each component
    - .json metadata for each component
    - .ports if any for each component
    - report.md for DOE
    - doe_name.json with DOE metadata

    pp.write_component_doe("mmi1x2", width_mmi=[5, 10], length_mmi=9)

    Args:
        component_type: component_name_or_function
        doe_name: autoname by default
        do_permutations: builds all permutations between the varying parameters
        list_settings: you can pass a list of settings or the variations in the kwargs
        doe_settings: shared settings for a DOE
        path: to store build artifacts
        functions: list of function names to apply to DOE
        name2function: function names to functions dict
        **kwargs: Doe default settings or variations
    """
    if hasattr(component_type, "__call__"):
        component_type = component_type.__name__

    doe_name = doe_name or component_type
    functions = functions or []
    list_settings = list_settings or get_settings_list(
        do_permutations=do_permutations, **kwargs)

    assert isinstance(component_type,
                      str), "{} not recognized".format(component_type)

    path.mkdir(parents=True, exist_ok=True)

    doe_gds_paths = []
    cell_names = []
    cell_settings = []

    for settings in list_settings:
        # print(settings)
        component_name = get_component_name(component_type, **settings)
        component_function = component_factory[component_type]
        component = component_function(name=component_name, **settings)
        if "test" in kwargs:
            component.test_protocol = kwargs.get("test")
        if "analysis" in kwargs:
            component.data_analysis_protocol = kwargs.get("analysis")
        for f in functions:
            component = name2function[f](component)

        cell_names.append(component.name)
        cell_settings.append(settings)
        gdspath = path / (component.name + ".gds")
        doe_gds_paths += [gdspath]
        write_component(component, gdspath)
    """ write DOE metadata (report + JSON) """
    write_doe_metadata(
        doe_name=doe_name,
        cell_names=cell_names,
        list_settings=list_settings,
        doe_settings=doe_settings,
        cell_settings=kwargs,
        doe_metadata_path=doe_metadata_path,
    )

    return doe_gds_paths
Example #5
0
def write_doe(
    component_type,
    doe_name=None,
    do_permutations=True,
    add_io_function=None,
    functions=None,
    list_settings=None,
    description=None,
    analysis=None,
    test=None,
    flag_write_component=True,
    **kwargs,
):
    """ writes each device GDS, together with metadata for each device:
    Returns a list of gdspaths

    - .gds geometry for each component
    - .json metadata for each component
    - .ports if any for each component
    - report.md for DOE
    - doe_name.json with DOE metadata

    pp.write_component_doe("mmi1x2", width_mmi=[5, 10], length_mmi=9)

    Args:
        component_type: component_name_or_function
        doe_name: autoname by default
        do_permutations: builds all permutations between the varying parameters
        add_io_function: add_io_optical
        list_settings: you can pass a list of settings or the variations in the kwargs
        descrption: description
        analysis: data analysis protocol
        test: test protocol
        **kwargs: Doe default settings or variations
    """
    if hasattr(component_type, "__call__"):
        component_type = component_type.__name__

    if doe_name is None:
        doe_name = component_type

    if functions is None:
        functions = []

    assert isinstance(component_type,
                      str), "{} not recognized".format(component_type)

    path = CONFIG["build_directory"] / "devices"

    if list_settings is None:
        list_settings = get_settings_list(do_permutations=do_permutations,
                                          **kwargs)

    doe_gds_paths = []
    cell_names = []
    cell_settings = []
    for settings in list_settings:
        # print(settings)
        component_name = get_component_name(component_type, **settings)
        component_factory = component_type2factory[component_type]
        component = component_factory(name=component_name, **settings)
        component.function_name = component_factory.__name__
        if test:
            component.test_protocol = test
        if analysis:
            component.data_analysis_protocol = analysis
        if add_io_function:
            component = add_io_function(component)
        for f in functions:
            component = name2function[f](component)

        cell_names.append(component.name)
        cell_settings.append(settings)
        gdspath = path / (component.name + ".gds")
        doe_gds_paths += [gdspath]
        if flag_write_component:
            write_component(component, gdspath)
    """ JSON  """
    doe_path = CONFIG["build_directory"] / "devices" / doe_name
    json_path = f"{doe_path}.json"
    d = {}
    d["type"] = "doe"
    d["name"] = doe_name
    d["cells"] = cell_names
    d["settings"] = cell_settings
    d["description"] = description
    d["analysis"] = analysis
    d["test"] = test
    with open(json_path, "w+") as fw:
        fw.write(json.dumps(d, indent=2))
    """ Markdown report """
    report_path = f"{doe_path}.md"
    with open(report_path, "w+") as fw:

        def w(line=""):
            fw.write(line + "\n")

        w("# {}".format(doe_name))
        w("- Number of devices: {}".format(len(list_settings)))
        w("- Settings")

        if len(kwargs) > 0:
            w(json.dumps(kwargs))

        # w(json.dumps(list_settings))
        if not list_settings:
            return

        w()
        EOL = ""

        # Convert table fields to strings
        head = [[str(field) for field in fields.keys()]
                for fields in list_settings][0]
        list_data_str = [[str(field) for field in fields.values()]
                         for fields in list_settings]
        N = len(head)

        field_sizes = [0] * N

        # compute the max number of character in each field
        for header, fields in zip(head, list_data_str):
            field_sizes = [
                max(field_sizes[i], len(fields[i]), len(head[i]))
                for i in range(N)
            ]

        field_sizes = [n + 2 for n in field_sizes]

        # Line formatting from fields
        def fmt_line(fields):
            fmt_fields = [
                " " + fields[i] + " " * (field_sizes[i] - len(fields[i]) - 1)
                for i in range(N)
            ]
            return "|".join(fmt_fields) + EOL

        def table_head_sep():
            return "|".join(["-" * n for n in field_sizes]) + EOL

        w(fmt_line(head))
        w(table_head_sep())
        for fields in list_data_str[0:]:
            w(fmt_line(fields))

        w()
        w("- Cells:")
        for cell_name in cell_names:
            w(cell_name)
        w()

    return doe_gds_paths
Example #6
0
def generate_does(
    filepath,
    component_filter=default_component_filter,
    component_factory=component_factory,
    doe_root_path=CONFIG["cache_doe_directory"],
    doe_metadata_path=CONFIG["doe_directory"],
    n_cores=4,
    logger=logging,
    regenerate_report_if_doe_exists=False,
    precision=1e-9,
):
    """ Generates a DOEs of components specified in a yaml file
    allows for each DOE to have its own x and y spacing (more flexible than method1)
    similar to write_doe
    """

    doe_root_path.mkdir(parents=True, exist_ok=True)
    doe_metadata_path.mkdir(parents=True, exist_ok=True)

    dicts, mask_settings = load_does(filepath)
    does, templates_by_type = separate_does_from_templates(dicts)

    dict_templates = (templates_by_type["template"]
                      if "template" in templates_by_type else {})

    default_use_cached_does = (mask_settings["cache"]
                               if "cache" in mask_settings else False)

    list_args = []
    for doe_name, doe in does.items():
        doe["name"] = doe_name
        component = doe["component"]

        if component not in component_factory:
            raise ValueError(f"{component} not in {component_factory.keys()}")

        if "template" in doe:
            """
            The keyword template is used to enrich the dictionnary from the template
            """
            templates = doe["template"]
            if not isinstance(templates, list):
                templates = [templates]
            for template in templates:
                try:
                    doe = update_dicts_recurse(doe, dict_templates[template])
                except Exception:
                    print(template, "does not exist")
                    raise

        do_permutation = doe.pop("do_permutation")
        settings = doe["settings"]
        doe["list_settings"] = get_settings_list(do_permutation, **settings)

        list_args += [doe]

    does_running = []
    start_times = {}
    finish_times = {}
    doe_name_to_process = {}
    while list_args:
        while len(does_running) < n_cores:
            if not list_args:
                break
            doe = list_args.pop()
            doe_name = doe["name"]
            """
            Only launch a build process if we do not use the cache
            Or if the DOE is not built
            """

            list_settings = doe["list_settings"]

            use_cached_does = (default_use_cached_does
                               if "cache" not in doe else doe["cache"])

            _doe_exists = False

            if "doe_template" in doe:
                """
                In that case, the DOE is not built: this DOE points to another existing component
                """
                _doe_exists = True
                logger.info("Using template - {}".format(doe_name))
                save_doe_use_template(doe)

            elif use_cached_does:
                _doe_exists = doe_exists(doe_name, list_settings)
                if _doe_exists:
                    logger.info("Cached - {}".format(doe_name))
                    if regenerate_report_if_doe_exists:
                        component_names = load_doe_component_names(doe_name)

                        write_doe_metadata(
                            doe_name=doe["name"],
                            cell_names=component_names,
                            list_settings=doe["list_settings"],
                            doe_metadata_path=doe_metadata_path,
                        )

            if not _doe_exists:
                start_times[doe_name] = time.time()
                p = Process(
                    target=write_doe,
                    args=(doe, component_factory),
                    kwargs={
                        "component_filter": component_filter,
                        "doe_root_path": doe_root_path,
                        "doe_metadata_path": doe_metadata_path,
                        "regenerate_report_if_doe_exists":
                        regenerate_report_if_doe_exists,
                        "precision": precision,
                        "logger": logger,
                    },
                )
                doe_name_to_process[doe_name] = p
                does_running += [doe_name]
                try:
                    p.start()
                except Exception:
                    print("Issue starting process for {}".format(doe_name))
                    print(type(component_factory))
                    raise

        to_rm = []
        for i, doe_name in enumerate(does_running):
            _p = doe_name_to_process[doe_name]
            if not _p.is_alive():
                to_rm += [i]
                finish_times[doe_name] = time.time()
                dt = finish_times[doe_name] - start_times[doe_name]
                line = "Done - {} ({:.1f}s)".format(doe_name, dt)
                logger.info(line)

        for i in to_rm[::-1]:
            does_running.pop(i)

        time.sleep(0.001)

    while does_running:
        to_rm = []
        for i, _doe_name in enumerate(does_running):
            _p = doe_name_to_process[_doe_name]
            if not _p.is_alive():
                to_rm += [i]
        for i in to_rm[::-1]:
            does_running.pop(i)

        time.sleep(0.05)