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]]: """Return bounds and concatenated geometry tables for selected components. Args: box_plus_buffer: If True, use selected components' bounding box plus the supplied buffer; otherwise use the chip size. qcomp_ids: Component ids to include (may be empty to include all). case: Return code from ``QRenderer.get_unique_component_ids``. x_buff: Buffer to add in x when ``box_plus_buffer`` is True. y_buff: Buffer to add in y when ``box_plus_buffer`` is True. Returns: tuple: Bounding box (minx, miny, maxx, maxy). pd.DataFrame: Path and poly tables concatenated for the selection. pd.DataFrame: Path, poly, and junction tables concatenated. bool: True if chip names match between design and layer stack. set | None: Valid chip names if they match, otherwise None. """ 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.' )