def render_element_junction(self, qgeom: pd.Series): """ Render a Josephson junction consisting of 1. A rectangle of length pad_gap and width inductor_width. Defines lumped element RLC boundary condition. 2. A line that is later used to calculate the voltage in post-processing analysis. Args: qgeom (pd.Series): GeoSeries of element properties. """ ansys_options = dict(transparency=0.0) qc_name = 'Lj_' + str(qgeom['component']) qc_elt = get_clean_name(qgeom['name']) qc_shapely = qgeom.geometry qc_chip_z = parse_units(self.design.get_chip_z(qgeom.chip)) qc_width = parse_units(qgeom.width) name = f'{qc_name}{QAnsysRenderer.NAME_DELIM}{qc_elt}' endpoints = parse_units(list(qc_shapely.coords)) endpoints_3d = to_vec3D(endpoints, qc_chip_z) x0, y0, z0 = endpoints_3d[0] x1, y1, z0 = endpoints_3d[1] if abs(y1 - y0) > abs(x1 - x0): # Junction runs vertically up/down x_min, x_max = x0 - qc_width / 2, x0 + qc_width / 2 y_min, y_max = min(y0, y1), max(y0, y1) else: # Junction runs horizontally left/right x_min, x_max = min(x0, x1), max(x0, x1) y_min, y_max = y0 - qc_width / 2, y0 + qc_width / 2 # Draw rectangle self.logger.debug(f'Drawing a rectangle: {name}') poly_ansys = self.modeler.draw_rect_corner([x_min, y_min, qc_chip_z], x_max - x_min, y_max - y_min, qc_chip_z, **ansys_options) axis = 'x' if abs(x1 - x0) > abs(y1 - y0) else 'y' self.modeler.rename_obj(poly_ansys, 'JJ_rect_' + name) self.assign_mesh.append('JJ_rect_' + name) # Draw line poly_jj = self.modeler.draw_polyline( [endpoints_3d[0], endpoints_3d[1]], closed=False, **dict(color=(128, 0, 128))) poly_jj = poly_jj.rename('JJ_' + name + '_') poly_jj.show_direction = True
def create_ports(self, port_list: list): """ Add ports and their respective impedances in Ohms to designated pins in port_list. Port_list is formatted as [(qcomp_0, pin_0, impedance_0), (qcomp_1, pin_1, impedance_1), ...]. Args: port_list (list): List of tuples of pins to be rendered as ports. """ for qcomp, pin, impedance in port_list: port_name = f'Port_{qcomp}_{pin}' pdict = self.design.components[qcomp].pins[pin] midpt, gap_size, norm_vec, width = pdict['middle'], pdict['gap'], \ pdict['normal'], pdict['width'] width = parse_units(width) endpoints = parse_units([midpt, midpt + gap_size * norm_vec]) endpoints_3d = to_vec3D(endpoints, 0) # Set z height to 0 x0, y0 = endpoints_3d[0][:2] x1, y1 = endpoints_3d[1][:2] if abs(y1 - y0) > abs(x1 - x0): # Junction runs vertically up/down x_min, x_max = x0 - width / 2, x0 + width / 2 y_min, y_max = min(y0, y1), max(y0, y1) else: # Junction runs horizontally left/right x_min, x_max = min(x0, x1), max(x0, x1) y_min, y_max = y0 - width / 2, y0 + width / 2 # Draw rectangle self.logger.debug(f'Drawing a rectangle: {port_name}') poly_ansys = self.modeler.draw_rect_corner( [x_min, y_min, 0], x_max - x_min, y_max - y_min, 0, **dict(transparency=0.0)) axis = 'x' if abs(x1 - x0) > abs(y1 - y0) else 'y' poly_ansys.make_lumped_port(axis, z0=str(impedance) + 'ohm', name=f'LumpPort_{qcomp}_{pin}') self.modeler.rename_obj(poly_ansys, port_name) # Draw line lump_line = self.modeler.draw_polyline( [endpoints_3d[0], endpoints_3d[1]], closed=False, **dict(color=(128, 0, 128))) lump_line = lump_line.rename(f'voltage_line_{port_name}') lump_line.show_direction = True
def render_element_path(self, qgeom: pd.Series): """ Render a path-type element. Args: qgeom (pd.Series): GeoSeries of element properties. """ ansys_options = dict(transparency=0.0) qc_name = self.design._components[qgeom['component']].name qc_elt = get_clean_name(qgeom['name']) qc_shapely = qgeom.geometry # shapely geom qc_chip_z = parse_units(self.design.get_chip_z(qgeom.chip)) name = f'{qc_elt}{QAnsysRenderer.NAME_DELIM}{qc_name}' qc_width = parse_units(qgeom.width) points = parse_units(list(qc_shapely.coords)) points_3d = to_vec3D(points, qc_chip_z) try: poly_ansys = self.modeler.draw_polyline(points_3d, closed=False, **ansys_options) except AttributeError: if self.modeler is None: self.logger.error( 'No modeler was found. Are you connected to an active Ansys Design?' ) raise poly_ansys = poly_ansys.rename(name) qc_fillet = round(qgeom.fillet, 7) if qc_fillet > 0: qc_fillet = parse_units(qc_fillet) idxs_to_fillet = good_fillet_idxs( points, qc_fillet, precision=self.design._template_options.PRECISION, isclosed=False) if idxs_to_fillet: self.modeler._fillet(qc_fillet, idxs_to_fillet, poly_ansys) if qc_width: x0, y0 = points[0] x1, y1 = points[1] vlen = math.sqrt((x1 - x0)**2 + (y1 - y0)**2) p0 = np.array([ x0, y0, 0 ]) + qc_width / (2 * vlen) * np.array([y0 - y1, x1 - x0, 0]) p1 = np.array([ x0, y0, 0 ]) + qc_width / (2 * vlen) * np.array([y1 - y0, x0 - x1, 0]) shortline = self.modeler.draw_polyline([p0, p1], closed=False) # sweepline import pythoncom try: self.modeler._sweep_along_path(shortline, poly_ansys) except pythoncom.com_error as error: print("com_error: ", error) hr, msg, exc, arg = error.args if msg == "Exception occurred." and hr == -2147352567: self.logger.error( "We cannot find a writable design. \n Either you are trying to use a Ansys " "design that is not empty, in which case please clear it manually or with the " "renderer method clean_active_design(). \n Or you accidentally deleted " "the design in Ansys, in which case please create a new one." ) raise error if qgeom.chip not in self.chip_subtract_dict: self.chip_subtract_dict[qgeom.chip] = set() if qgeom['subtract']: self.chip_subtract_dict[qgeom.chip].add(name) elif qgeom['width'] and (not qgeom['helper']): self.assign_perfE.append(name)
def render_element_poly(self, qgeom: pd.Series): """ Render a closed polygon. Args: qgeom (pd.Series): GeoSeries of element properties. """ ansys_options = dict(transparency=0.0) qc_name = self.design._components[qgeom['component']].name qc_elt = get_clean_name(qgeom['name']) qc_shapely = qgeom.geometry # shapely geom qc_chip_z = parse_units(self.design.get_chip_z(qgeom.chip)) qc_fillet = round(qgeom.fillet, 7) name = f'{qc_elt}{QAnsysRenderer.NAME_DELIM}{qc_name}' points = parse_units(list( qc_shapely.exterior.coords)) # list of 2d point tuples points_3d = to_vec3D(points, qc_chip_z) if is_rectangle(qc_shapely): # Draw as rectangle self.logger.debug(f'Drawing a rectangle: {name}') x_min, y_min, x_max, y_max = qc_shapely.bounds poly_ansys = self.modeler.draw_rect_corner( *parse_units([[x_min, y_min, qc_chip_z], x_max - x_min, y_max - y_min, qc_chip_z]), **ansys_options) self.modeler.rename_obj(poly_ansys, name) else: # Draw general closed poly poly_ansys = self.modeler.draw_polyline(points_3d[:-1], closed=True, **ansys_options) # rename: handle bug if the name of the cut already exits and is used to make a cut poly_ansys = poly_ansys.rename(name) qc_fillet = round(qgeom.fillet, 7) if qc_fillet > 0: qc_fillet = parse_units(qc_fillet) idxs_to_fillet = good_fillet_idxs( points, qc_fillet, precision=self.design._template_options.PRECISION, isclosed=True) if idxs_to_fillet: self.modeler._fillet(qc_fillet, idxs_to_fillet, poly_ansys) # Subtract interior shapes, if any if len(qc_shapely.interiors) > 0: for i, x in enumerate(qc_shapely.interiors): interior_points_3d = to_vec3D(parse_units(list(x.coords)), qc_chip_z) inner_shape = self.modeler.draw_polyline( interior_points_3d[:-1], closed=True) self.modeler.subtract(name, [inner_shape]) # Input chip info into self.chip_subtract_dict if qgeom.chip not in self.chip_subtract_dict: self.chip_subtract_dict[qgeom.chip] = set() if qgeom['subtract']: self.chip_subtract_dict[qgeom.chip].add(name) # Potentially add to list of elements to metallize elif not qgeom['helper']: self.assign_perfE.append(name)
def render_element_junction(self, qgeom: pd.Series): """ Render a Josephson junction depending on the solution type. If in HFSS eigenmode, junctions are rendered as inductors consisting of 1. A rectangle of length pad_gap and width inductor_width. Defines lumped element RLC boundary condition. 2. A line that is later used to calculate the voltage in post-processing analysis. If in HFSS driven modal, junctions can be inductors, lumped ports, both inductors and lumped ports, or omitted altogether. Ports are characterized by an impedance value given in the list jj_to_port when render_design() is called. Args: qgeom (pd.Series): GeoSeries of element properties. """ qcomp = self.design._components[qgeom['component']].name qc_elt = get_clean_name(qgeom['name']) if (qcomp, qc_elt) not in self.jj_to_ignore: qc_shapely = qgeom.geometry qc_chip_z = parse_units(self.design.get_chip_z(qgeom.chip)) qc_width = parse_units(qgeom.width) endpoints = parse_units(list(qc_shapely.coords)) endpoints_3d = to_vec3D(endpoints, qc_chip_z) x0, y0, z0 = endpoints_3d[0] x1, y1, z0 = endpoints_3d[1] if abs(y1 - y0) > abs(x1 - x0): # Junction runs vertically up/down axis = 'y' x_min, x_max = x0 - qc_width / 2, x0 + qc_width / 2 y_min, y_max = min(y0, y1), max(y0, y1) else: # Junction runs horizontally left/right axis = 'x' x_min, x_max = min(x0, x1), max(x0, x1) y_min, y_max = y0 - qc_width / 2, y0 + qc_width / 2 if (qcomp, qc_elt) in self.jj_lumped_ports: if self.jj_lumped_ports[(qcomp, qc_elt)][1]: # Draw both port and inductor side by side with small gap in between gap = parse_units(self.hfss_options['port_inductor_gap']) x_mid, y_mid = (x_min + x_max) / 2, (y_min + y_max) / 2 if axis == 'x': y_mid_hi = y_mid + gap / 2 y_mid_lo = y_mid - gap / 2 self.render_junction_port(qgeom, x_min, x_max, y_mid_hi, y_max, qc_chip_z, axis) self.render_junction_inductor(qgeom, x_min, x_max, y_min, y_mid_lo, qc_chip_z, axis) elif axis == 'y': x_mid_lo = x_mid - gap / 2 x_mid_hi = x_mid + gap / 2 self.render_junction_port(qgeom, x_mid_hi, x_max, y_min, y_max, qc_chip_z, axis) self.render_junction_inductor(qgeom, x_min, x_mid_lo, y_min, y_max, qc_chip_z, axis) else: # Only draw port self.render_junction_port(qgeom, x_min, x_max, y_min, y_max, qc_chip_z, axis) else: # Only draw inductor self.render_junction_inductor(qgeom, x_min, x_max, y_min, y_max, qc_chip_z, axis)