Array Geometries#
This guide covers the various array geometries supported by the library, from simple rectangular grids to complex conformal arrays.
Planar Arrays#
Rectangular Grid#
The most common array configuration. Elements are arranged on a regular rectangular grid with specified spacing.
import phased_array as pa
# 16x16 array with half-wavelength spacing
geom = pa.create_rectangular_array(
Nx=16, Ny=16,
dx=0.5, dy=0.5 # spacing in wavelengths
)
print(f"Elements: {geom.n_elements}") # 256
# Access element positions
print(f"X range: {geom.x.min():.2f} to {geom.x.max():.2f}")
print(f"Y range: {geom.y.min():.2f} to {geom.y.max():.2f}")
Key parameters:
Nx, Ny: Number of elements in each dimensiondx, dy: Element spacing in wavelengthswavelength: Physical wavelength (converts spacing to meters)center: If True (default), center array at origin
Triangular Grid#
Offset triangular (hexagonal) lattice provides ~13% better packing efficiency than rectangular grids while maintaining similar grating lobe performance.
geom = pa.create_triangular_array(
Nx=16, Ny=16,
dx=0.5 # dy is automatically set for equilateral spacing
)
print(f"Elements: {geom.n_elements}") # ~240 (fewer than 16x16)
The row offset is dx/2, and default dy = dx * sqrt(3)/2 for equilateral
triangular cells.
Elliptical Boundary#
Arrays within elliptical (or circular) boundaries are common in radar systems.
# Circular array (a = b)
geom_circle = pa.create_elliptical_array(
a=4.0, b=4.0, # semi-axes in wavelengths
dx=0.5,
grid_type='rectangular'
)
# Elliptical with triangular grid
geom_ellipse = pa.create_elliptical_array(
a=5.0, b=3.0,
dx=0.5,
grid_type='triangular'
)
Ring and Circular Arrays#
Circular Array (Single Ring)#
Elements arranged in a single ring, useful for direction finding.
geom = pa.create_circular_array(
n_elements=16,
radius=2.0, # in wavelengths
start_angle=0.0 # radians
)
Concentric Rings#
Multiple concentric rings, often used for low-sidelobe designs.
geom = pa.create_concentric_rings_array(
n_rings=4,
elements_per_ring=[8, 12, 16, 20], # increasing with radius
ring_spacing=0.5,
include_center=True
)
Conformal Arrays#
Conformal arrays conform to curved surfaces. The key difference from planar arrays is that elements have different orientations (normal vectors).
Cylindrical Array#
Elements on a cylindrical surface, normals pointing radially outward.
geom = pa.create_cylindrical_array(
n_azimuth=32, # around circumference
n_vertical=8, # vertical rings
radius=5.0, # wavelengths
height=4.0 # wavelengths
)
print(f"Is conformal: {geom.is_conformal}") # True
Spherical Array#
Elements on a spherical cap, useful for hemispherical coverage.
geom = pa.create_spherical_array(
n_theta=8,
n_phi=32,
radius=5.0,
theta_min=0.0, # start at pole
theta_max=np.pi/2 # hemisphere
)
Computing Patterns for Conformal Arrays#
Conformal arrays require special pattern computation that accounts for element orientations:
import numpy as np
# Create cylindrical array
geom = pa.create_cylindrical_array(16, 4, radius=3.0, height=2.0)
k = pa.wavelength_to_k(1.0)
# Steering weights
weights = pa.steering_vector(k, geom.x, geom.y, theta0_deg=0, phi0_deg=0, z=geom.z)
# Use conformal array factor (accounts for element normals)
theta = np.linspace(0, np.pi/2, 91)
phi = np.linspace(0, 2*np.pi, 181)
theta_grid, phi_grid = np.meshgrid(theta, phi, indexing='ij')
AF = pa.array_factor_conformal(
theta_grid, phi_grid, geom, weights, k,
element_pattern_func=pa.element_pattern
)
Sparse/Thinned Arrays#
Sparse arrays remove elements from a full grid to reduce cost while maintaining the same aperture size. The tradeoff is higher sidelobes.
Random Thinning#
Simple random element removal:
# Start with full array
full_geom = pa.create_rectangular_array(32, 32, dx=0.5, dy=0.5)
# Keep 50% of elements randomly
sparse_geom = pa.thin_array_random(
full_geom,
thinning_factor=0.5,
seed=42 # for reproducibility
)
print(f"Full: {full_geom.n_elements}, Sparse: {sparse_geom.n_elements}")
Density Taper Thinning#
Remove elements with probability based on position (more elements at center):
def taylor_density(x, y):
# Higher probability near center
r = np.sqrt(x**2 + y**2)
r_max = np.sqrt(x.max()**2 + y.max()**2)
return 1.0 - 0.7 * (r / r_max)**2
sparse_geom = pa.thin_array_density_tapered(
full_geom,
density_func=taylor_density,
seed=42
)
Genetic Algorithm Optimization#
Optimize element selection to minimize an objective (e.g., peak sidelobe):
def sidelobe_objective(geom):
# Compute peak sidelobe level
k = pa.wavelength_to_k(1.0)
weights = np.ones(geom.n_elements)
theta, phi, pattern_dB = pa.compute_full_pattern(
geom.x, geom.y, weights, k, n_theta=91, n_phi=1
)
# Return peak sidelobe (excluding main beam)
main_beam_idx = np.argmax(pattern_dB[:, 0])
sidelobes = np.concatenate([
pattern_dB[:main_beam_idx-5, 0],
pattern_dB[main_beam_idx+5:, 0]
])
return np.max(sidelobes)
optimized_geom = pa.thin_array_genetic_algorithm(
full_geom,
n_target=500, # keep 500 elements
objective_func=sidelobe_objective,
population_size=30,
n_generations=50,
seed=42
)
Subarray Architectures#
Subarrays group elements that share a common phase shifter, reducing hardware cost at the expense of beam quality.
# Create 64x64 array divided into 8x8 subarrays
architecture = pa.create_rectangular_subarrays(
Nx_total=64, Ny_total=64,
Nx_sub=8, Ny_sub=8, # 8x8 elements per subarray
dx=0.5, dy=0.5
)
print(f"Total elements: {architecture.geometry.n_elements}") # 4096
print(f"Number of subarrays: {architecture.n_subarrays}") # 64
# Compute subarray-level steering
k = pa.wavelength_to_k(1.0)
weights = pa.compute_subarray_weights(
architecture, k,
theta0_deg=20, phi0_deg=0
)
ArrayGeometry Class#
All geometry functions return an ArrayGeometry dataclass:
from dataclasses import dataclass
@dataclass
class ArrayGeometry:
x: np.ndarray # X positions (wavelengths or meters)
y: np.ndarray # Y positions
z: np.ndarray # Z positions (for 3D arrays)
nx: np.ndarray # Normal vector X components
ny: np.ndarray # Normal vector Y components
nz: np.ndarray # Normal vector Z components
element_indices: np.ndarray # Original indices
# Properties
geom.n_elements # Number of elements
geom.is_planar # True if all z values are equal
geom.is_conformal # True if element normals vary
Visualizing Array Geometries#
# 2D plot (matplotlib)
pa.plot_array_geometry(geom.x, geom.y)
# 3D interactive plot (plotly)
fig = pa.plot_array_geometry_3d_plotly(geom)
fig.show()