Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

832 box plus buffer vs chips for Multiplanar design. #833

Merged
merged 14 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion qiskit_metal/renderers/renderer_base/renderer_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,10 @@ def get_unique_component_ids(
highlight_qcomponents (Union[list, None], optional): Components to render. Defaults to None.

Returns:
Tuple[list, int]: Empty or partial list of components in QDesign.
Tuple[list, int]: Tuple: Empty or partial list of components in QDesign.
int: 0 subset selected
1 every component selected
2 invalid
"""
highlight_qcomponents = highlight_qcomponents if highlight_qcomponents else []
unique_qcomponents = set(highlight_qcomponents)
Expand Down
2 changes: 2 additions & 0 deletions qiskit_metal/toolbox_metal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from .parsing import is_numeric_possible
from .parsing import parse_units
from .layer_stack_handler import LayerStackHandler
from .bounds_for_path_and_poly_tables import BoundsForPathAndPolyTables

from .. import config
if config.is_building_docs():
Expand All @@ -62,3 +63,4 @@
from . import parsing
from . import math_and_overrides
from . import layer_stack_handler
from . import bounds_for_path_and_poly_tables
281 changes: 281 additions & 0 deletions qiskit_metal/toolbox_metal/bounds_for_path_and_poly_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
# -*- coding: utf-8 -*-
from typing import List, Tuple, Union
from copy import deepcopy
from qiskit_metal.toolbox_python.utility_functions import determine_larger_box

import pandas as pd


class BoundsForPathAndPolyTables():
"""Create class which can be used by multiple renderers. In particular, this class
assumes a LayerStack is being used within QDesign.
"""

def __init__(self, design: 'MultiPlanar'):
self.design = design
self.chip_names_matched = None # bool
self.valid_chip_names = None # set of valid chip names from layer_stack

def get_bounds_of_path_and_poly_tables(
self, box_plus_buffer: bool, qcomp_ids: List, case: int, x_buff: float,
y_buff: float
) -> tuple[tuple[float, float, float, float], pd.DataFrame, bool, Union[
None, set]]:
"""If box_plus_buffer is True, returns a tuple containing minx, miny, maxx, maxy values for the
bounds of what was selected to be rendered.
Else, use the chip size for Tuple.

Also, return the Tuple so can be used later within a renderer.

Args:
box_plus_buffer (bool): Use the box of selected components plus a buffer size OR
the chip size based on QComponents selected.
qcomp_ids (List): Empty or partial list of components in QDesign.
From QRenderer.get_unique_component_ids
case (int): Return code used from QRenderer.get_unique_component_ids()
x_buff (float): If box_plus_buffer, need the buffer value in x coordinate.
y_buff (float): If box_plus_buffer, need the buffer value in y coordinate.

Returns:
tuple[tuple[float, float, float, float], pd.DataFrame, bool, Union[None,set]]:
tuple: minx, miny, maxx, maxy values based
on either total chip size or box_plus_buffer.
In xy plane, the box_for_xy_bounds will be limited
by the chip_bounds_xy if box_for_xy_bounds is larger
than chip_bounds_xy.
pd.DataFrame: The path and poly dataframes concatenated for qcomp_ids
bool: If there is a key in design with same chip_name as in layer_stack
Union[None,set]: Either None if the names don't match,
or the set of chip_names that can be used.
"""
box_for_xy_bounds = None
self.chip_names_matched = None
self.valid_chip_names = None

path_dataframe = self.design.qgeometry.tables['path']
poly_dataframe = self.design.qgeometry.tables['poly']

# NOTE:get_box_for_xy_bounds populates self.chip_names_matched and self.valid_chip_names
chip_minx, chip_miny, chip_maxx, chip_maxy = self.get_box_for_xy_bounds(
)

chip_bounds_xy = (chip_minx, chip_miny, chip_maxx, chip_maxy)

if box_plus_buffer:
# Based on component selection, determine the bounds for box_plus_buffer.
frames = None
path_and_poly_with_valid_comps = None

if case == 2: # One or more components not in QDesign.
self.design.logger.warning("One or more components not found.")
elif case == 1: # Render all components
frames = [path_dataframe, poly_dataframe]
else: # Strict subset rendered.
mask_path = path_dataframe['component'].isin(qcomp_ids)
mask_poly = poly_dataframe['component'].isin(qcomp_ids)
subset_path_df = path_dataframe[mask_path]
subset_poly_df = poly_dataframe[mask_poly]
frames = [subset_path_df, subset_poly_df]

#Concat the frames and then determine the total bounds of all the geometries.
# maybe, change name to package_cavity
path_and_poly_with_valid_comps = pd.concat(frames,
ignore_index=True)
minx, miny, maxx, maxy = list(
path_and_poly_with_valid_comps['geometry'].total_bounds)
# minx, miny, maxx, maxy = list(
# pd.concat(frames, ignore_index=True)['geometry'].total_bounds)
# # Add the buffer, using options for renderer.
# x_buff = parse_entry(self._options["x_buffer_width_mm"])
# y_buff = parse_entry(self._options["y_buffer_width_mm"])

minx -= x_buff
miny -= y_buff
maxx += x_buff
maxy += y_buff
box_for_xy_bounds = (minx, miny, maxx, maxy)

safe_xy_bounds = self.ensure_component_box_smaller_than_chip_box_(
box_for_xy_bounds, chip_bounds_xy)

return safe_xy_bounds, path_and_poly_with_valid_comps, self.chip_names_matched, self.valid_chip_names
else: # Incorporate all the chip sizes.

frames = [path_dataframe, poly_dataframe]
path_and_poly_with_valid_comps = pd.concat(frames,
ignore_index=True)
return chip_bounds_xy, path_and_poly_with_valid_comps, self.chip_names_matched, self.valid_chip_names

@classmethod
def ensure_component_box_smaller_than_chip_box_(
cls, box_for_xy_bounds: Tuple, chip_bounds_xy: Tuple) -> Tuple:
"""If the box_plus_buffer is larger than the aggregate chip bounds from DesignPlanar,
use the chip bounds as the cutoff.

Args:
box_for_xy_bounds (Tuple): In xy plane, the bounding box for the components to render.
Box from QGeometry tables.
chip_bounds_xy (Tuple): In xy plane, the bounding box for aggregate chip size.
Box from MultiPlanar chip size.

Returns:
Tuple: In xy plane, the box_for_xy_bounds will be limited by the chip_bounds_xy
if box_for_xy_bounds is larger than chip_bounds_xy.
If chip_bounds_xy is None (bad input), no checking
will happen and box_for_xy_bounds will be returned.
"""

chip_minx, chip_miny, chip_maxx, chip_maxy = chip_bounds_xy
if chip_minx is None or chip_miny is None or chip_maxx is None or chip_maxy is None:
input_xy_box = deepcopy(box_for_xy_bounds)
return input_xy_box

box_minx, box_miny, box_maxx, box_maxy = box_for_xy_bounds
safe_xy_box = list()
# Keep the order of appends in this way. It should match (minx, miny, maxx, maxy)

#yapf: disable
safe_xy_box.append(chip_minx) if box_minx < chip_minx else safe_xy_box.append(box_minx)
safe_xy_box.append(chip_miny) if box_miny < chip_miny else safe_xy_box.append(box_miny)
safe_xy_box.append(chip_maxx) if box_maxx > chip_maxx else safe_xy_box.append(box_maxx)
safe_xy_box.append(chip_maxy) if box_maxy > chip_maxy else safe_xy_box.append(box_maxy)
#yapf: enable

return tuple(safe_xy_box)

def get_box_for_xy_bounds(
self
) -> Union[None, Union[Tuple[float, float, float, float], None]]:
"""Assuming the chip size is used from Multiplanar design, and list of chip_names
comes from layer_stack that will be used to determine the box size for simulation.

Returns:
Union[None, Union[Tuple[float, float, float, float], None]]:
None if not able to get the chip information
Tuple holds the box to use for simulation [minx, miny, maxx, maxy]
"""

self.chip_names_matched, self.valid_chip_names = self.are_all_chipnames_in_design(
)

minx, miny, maxx, maxy = None, None, None, None
if self.chip_names_matched:
# Using the chip size from Multiplanar design and z_coord from layer_stack, get the box
# for chip_name in self.chip_names_matched:
for chip_name in self.valid_chip_names:
if self.design.chips[chip_name]['size']:
chip_box, return_code = self.get_x_y_for_chip(chip_name)

if return_code == 0: # All was found and good.
minx, miny, maxx, maxy = determine_larger_box(
minx, miny, maxx, maxy, chip_box)

else:
self.chip_size_not_in_chipname_within_design(chip_name)

return minx, miny, maxx, maxy

def are_all_chipnames_in_design(self) -> Tuple[bool, Union[set, None]]:
"""Using chip names in layer_stack information,
then check if the information is in MultiPlanar design.

Returns:
Tuple[bool, Union[set, None]]: bool if there is a key in design with same chip_name as in layer_stack
Union has either None if the names don't match,
or the set of chip_names that can be used.
"""

chip_set_from_design = set(self.design.chips.keys())
chip_set_from_layer_stack = self.design.ls.get_unique_chip_names()
if not chip_set_from_layer_stack.issubset(chip_set_from_design):
self.chip_names_not_in_design(chip_set_from_layer_stack,
chip_set_from_design)
return False, None

return True, chip_set_from_layer_stack

def get_x_y_for_chip(self, chip_name: str) -> Tuple[tuple, int]:
"""If the chip_name is in self.chips, along with entry for size
information then return a tuple=(minx, miny, maxx, maxy). Used for
subtraction while exporting design.

Args:
chip_name (str): Name of chip that you want the size of.

Returns:
Tuple[tuple, int]:
tuple: The exact placement on rectangle coordinate (minx, miny, maxx, maxy).
int: 0=all is good
1=chip_name not in self._chips
2=size information missing or no good
"""
x_y_location = tuple()

if chip_name in self.design.chips:
if 'size' in self.design.chips[chip_name]:

size = self.design.parse_value(
self.design.chips[chip_name]['size'])
if 'center_x' in size \
and 'center_y' in size \
and 'size_x' in size \
and 'size_y' in size:
if type(size.center_x) in [int, float] and \
type(size.center_y) in [int, float] and \
type(size.size_x) in [int, float] and \
type(size.size_y) in [int, float]:
x_y_location = (
size['center_x'] - (size['size_x'] / 2.0),
size['center_y'] - (size['size_y'] / 2.0),
size['center_x'] + (size['size_x'] / 2.0),
size['center_y'] + (size['size_y'] / 2.0))
return x_y_location, 0

self.design.logger.warning(
f'Size information within self.design.chips[{chip_name}][\"size\"]'
f' is NOT an int or float.')
return x_y_location, 2

self.design.logger.warning(
'center_x or center_y or size_x or size_y '
f' NOT in self.design.chips[{chip_name}][\"size\"]')
return x_y_location, 2

self.design.logger.warning(
f'Information for size in NOT in self.design.chips[{chip_name}]'
' dict. Return "None" in tuple.')
return x_y_location, 2

self.design.logger.warning(
f'Chip name "{chip_name}" is not in self.design.chips dict. Return "None" in tuple.'
)
return x_y_location, 1


######### Warnings and Errors##################################################

def chip_names_not_in_design(self, layer_stack_names: set,
design_names: set):
"""
Tell user to check the chip name and data in design.

Args:
layer_stack_names (set): Chip names from layer_stack.
design_names (set): Chip names from design.
"""
self.design.logger.warning(
f'\nThe chip_names from layer_stack are not in design. '
f'\n The names in layer_stack:{layer_stack_names}.'
f'\n The names in design:{design_names}.')

def chip_size_not_in_chipname_within_design(self, chip_name: str):
"""
Tell user to check the chip size data within design.

Args:
chip_name (str): Chip names from layer_stack.
"""
self.design.logger.error(
f'\nThe chip_name:{chip_name} within design. '
f'\n Update your QDesign or subclass to see confirm the size information is provided.'
)
4 changes: 1 addition & 3 deletions qiskit_metal/toolbox_metal/layer_stack_handler.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# -*- coding: utf-8 -*-

from textwrap import fill
from xmlrpc.client import Boolean
import pandas as pd
from typing import List, Tuple
from typing import Union
from addict import Dict
import os
import logging

from .parsing import parse_units, TRUE_STR, FALSE_STR
from .parsing import TRUE_STR, FALSE_STR


class LayerStackHandler():
Expand Down