"""
Utility functions for geometry calculations and runtime operations in quantum circuit designs.
This module provides helper functions for common geometric calculations needed in
quantum circuit design and simulation, such as finding junction positions, calculating
midpoints, normalizing vectors, and performing rotations. It also includes utilities
for string parsing of values with units and process management.
"""
import os
from typing import List, Tuple
import time
import numpy as np
from typing import Optional
[docs]
def close_ansys() -> None:
"""
Terminate all running Ansys HFSS processes using Windows task management.
Note:
This function only works on Windows operating systems.
"""
os.system("taskkill /f /im ansysedt.exe")
[docs]
def get_junction_position(design, qcomponent) -> Tuple[str, str]:
"""
Calculate the position of a Josephson junction in a quantum component.
Extracts the geometric coordinates of a Josephson junction from a quantum
component's geometry tables. This is particularly useful for determining
where flux lines should be placed in relation to the junction.
Args:
design: The Qiskit Metal design containing the component.
qcomponent: The quantum component containing the junction.
Returns:
A tuple of (x, y) coordinates as strings with "mm" units,
suitable for use in QComponent options.
Note:
Supports only components containing a single junction.
Example:
>>> x_pos, y_pos = get_junction_position(design, transmon)
>>> print(x_pos, y_pos)
'2.5mm' '3.7mm'
"""
junction_table = design.qgeometry.tables["junction"]
rect_jj_junction = junction_table.loc[
junction_table["component"] == qcomponent.id, "geometry"
]
assert len(rect_jj_junction) == 1, "Only supports a single junction per component"
coords = list(rect_jj_junction.iloc[0].coords)
x, y = coords[1]
return f"{x}mm", f"{y}mm"
[docs]
def get_middle_point(
point1: Tuple[float, float], point2: Tuple[float, float]
) -> Tuple[float, float]:
"""
Calculate the midpoint between two points in a 2D plane.
Args:
point1: The (x, y) coordinates of the first point.
point2: The (x, y) coordinates of the second point.
Returns:
The (x, y) coordinates of the midpoint.
Example:
>>> get_middle_point((0, 0), (10, 20))
(5.0, 10.0)
"""
x1, y1 = point1
x2, y2 = point2
return (x1 + x2) / 2, (y1 + y2) / 2
[docs]
def get_normalized_vector(
point1: Tuple[float, float], point2: Tuple[float, float]
) -> Tuple[float, float]:
"""
Calculate the normalized unit vector pointing from point1 to point2.
Computes a vector of length 1 that points in the direction from
the first point to the second point in a 2D plane.
Args:
point1: The (x, y) coordinates of the origin point.
point2: The (x, y) coordinates of the target point.
Returns:
A tuple (dx, dy) representing the normalized vector components.
Example:
>>> get_normalized_vector((0, 0), (3, 4))
(0.6, 0.8)
"""
x1, y1 = point1
x2, y2 = point2
dx = x2 - x1
dy = y2 - y1
length = np.sqrt(dx**2 + dy**2)
return dx / length, dy / length
[docs]
def rotate_point(
point: np.ndarray, rotation_center: np.ndarray, angle_rad: float
) -> np.ndarray:
"""
Rotate a point counterclockwise around a center point by a specified angle.
Applies a 2D rotation transformation to a point around a specified center
using standard rotation matrix operations.
Args:
point: A numpy array [x, y] representing the point to rotate.
rotation_center: A numpy array [x, y] representing the center of rotation.
angle_rad: The angle of rotation in radians (positive for counterclockwise).
Returns:
A numpy array with the coordinates of the rotated point.
Example:
>>> import numpy as np
>>> rotate_point(np.array([1, 0]), np.array([0, 0]), np.pi/2)
array([0., 1.])
"""
# Translate the point so that the rotation center is at the origin
point_translated = point - rotation_center
# Perform the rotation
rotation_matrix = np.array(
[
[np.cos(angle_rad), -np.sin(angle_rad)],
[np.sin(angle_rad), np.cos(angle_rad)],
]
)
rotated_point_translated = np.dot(rotation_matrix, point_translated)
# Translate back
rotated_point = rotated_point_translated + rotation_center
return rotated_point
[docs]
def get_value_and_unit(val_unit: str) -> Tuple[float, str]:
"""
Extract numerical value and unit from a string representation.
Parses a string containing a number followed by an optional unit
(e.g., "10.5mm") and separates it into the numerical value and unit string.
Args:
val_unit: A string representing a value with optional unit (e.g., "10.5mm").
Returns:
A tuple (value, unit) where value is a float and unit is a string.
If no unit is present, the unit string will be empty..
Example:
>>> get_value_and_unit("10.5mm")
(10.5, 'mm')
>>> get_value_and_unit("42")
(42.0, '')
"""
try:
if str.isalpha(val_unit[-1]):
idx = 1
while idx < len(val_unit) and str.isalpha(val_unit[-idx - 1]):
idx += 1
unit = val_unit[-idx:]
val = float(val_unit.replace(unit, ""))
else:
val = float(val_unit)
unit = ""
return val, unit
except Exception as exc:
raise ValueError(f"Could not parse value and unit from {val_unit}") from exc
[docs]
def sum_expression(vals: List[str]) -> str:
"""
Calculate the sum of values with consistent units.
Takes a list of strings representing values with units (e.g., ["10mm", "5mm"]),
extracts the numerical values while preserving the unit, computes their sum,
and returns the result with the same unit.
Args:
vals: A list of strings representing values with the same unit.
Returns:
A string representing the sum with the preserved unit.
Raises:
AssertionError: If the units of the provided values are not the same.
Example:
>>> sum_expression(["10mm", "5mm", "2.5mm"])
'17.5mm'
>>> sum_expression(["1.2GHz", "0.8GHz"])
'2.0GHz'
"""
sum_val = 0.0
_, unit_0 = get_value_and_unit(vals[0])
for val_str in vals:
val, unit = get_value_and_unit(val_str)
assert unit_0 == unit, f"Units must be the same: {unit_0} != {unit}"
sum_val += val
sum_unit = unit
return f"{sum_val}{sum_unit}"
[docs]
def get_save_path(out_folder: str, chip_name: str, time_format: str = "%Y%m%d-%H%M%S"):
"""Create a path to save simulation results by appending the start time of the simulation to the identifier name."""
return out_folder + chip_name + "_" + time.strftime(time_format)