Source code for qiskit_metal.toolbox_metal.bounds_for_path_and_poly_tables

# -*- coding: utf-8 -*-
from re import sub
from typing import List, Tuple, Union
from copy import deepcopy

import pandas as pd


[docs] def determine_larger_box(minx: Union[None, float], miny: Union[None, float], maxx: Union[None, float], maxy: Union[None, float], chip_box: tuple) -> Tuple[float, float, float, float]: """Return box which includes the two boxes. Args: minx (Union[None, float]): Minimum of x coordinate miny (Union[None, float]): Minimum of y coordinate maxx (Union[None, float]): Maximum of x coordinate maxy (Union[None, float]): Maximum of y coordinate chip_box (tuple): Second box in following format: minx, miny, maxx, maxy Returns: Tuple[float, float, float, float]: The size: minx, miny, maxx, maxy of box which includes both boxes. """ large_minx, large_miny, large_maxx, large_maxy = None, None, None, None if minx and miny and maxx and maxy: chip_minx, chip_miny, chip_maxx, chip_maxy = chip_box large_minx = min(minx, chip_minx) large_miny = min(miny, chip_miny) large_maxx = max(maxx, chip_maxx) large_maxy = max(maxy, chip_maxy) else: # First time getting chip size, so just use chip_box large_minx, large_miny, large_maxx, large_maxy = chip_box return large_minx, large_miny, large_maxx, large_maxy
[docs] 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
[docs] 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, 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, 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 pd.DataFrame: The path, poly, junction 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'] junction_dataframe = self.design.qgeometry.tables['junction'] # 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 frames_with_jj = 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] frames_with_jj = [ path_dataframe, poly_dataframe, junction_dataframe ] else: # Strict subset rendered. mask_path = path_dataframe['component'].isin(qcomp_ids) mask_poly = poly_dataframe['component'].isin(qcomp_ids) mask_junction = junction_dataframe['component'].isin(qcomp_ids) subset_path_df = path_dataframe[mask_path] subset_poly_df = poly_dataframe[mask_poly] subset_junction_df = junction_dataframe[mask_junction] frames = [subset_path_df, subset_poly_df] frames_with_jj = [ subset_path_df, subset_poly_df, subset_junction_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) path_poly_and_junction_valid_comps = pd.concat(frames_with_jj, 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, path_poly_and_junction_valid_comps, self.chip_names_matched, self.valid_chip_names else: # Incorporate all the chip sizes. frames = [path_dataframe, poly_dataframe] frames_with_jj = [ path_dataframe, poly_dataframe, junction_dataframe ] path_and_poly_with_valid_comps = pd.concat(frames, ignore_index=True) path_poly_and_junction_valid_comps = pd.concat(frames_with_jj, ignore_index=True) return chip_bounds_xy, path_and_poly_with_valid_comps, path_poly_and_junction_valid_comps, self.chip_names_matched, self.valid_chip_names
[docs] @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)
[docs] 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
[docs] 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
[docs] 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##################################################
[docs] 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}.')
[docs] 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.' )