Geometry Module#

The geometry module provides classes and functions for creating various array geometries, from simple rectangular grids to complex conformal and sparse arrays.

Classes#

class phased_array.ArrayGeometry(x, y, z=None, nx=None, ny=None, nz=None, element_indices=None, tx=None, ty=None, tz=None)[source]#

Bases: object

Container for phased array element positions and orientations.

x#

Element x-positions in meters

Type:

ndarray

y#

Element y-positions in meters

Type:

ndarray

z#

Element z-positions in meters (for 3D/conformal arrays)

Type:

ndarray, optional

nx#

Element normal x-components (unit vectors)

Type:

ndarray, optional

ny#

Element normal y-components

Type:

ndarray, optional

nz#

Element normal z-components

Type:

ndarray, optional

element_indices#

Original indices before any thinning (for tracking)

Type:

ndarray, optional

tx#

Element tangent x-components (local x-axis / polarization reference for conformal vector patterns)

Type:

ndarray, optional

ty#

Element tangent y-components

Type:

ndarray, optional

tz#

Element tangent z-components

Type:

ndarray, optional

tx: ndarray | None = None#
ty: ndarray | None = None#
tz: ndarray | None = None#
property n_elements: int#

Number of elements in the array.

property is_planar: bool#

Check if array is planar (all z and normals the same).

property is_conformal: bool#

Check if array has varying element orientations.

copy()[source]#

Create a deep copy of the geometry.

class phased_array.SubarrayArchitecture(geometry, subarray_assignments, n_subarrays, subarray_centers=None, overlapped=False, subarray_elements=None, overlap_weights=None)[source]#

Bases: object

Defines subarray partitioning of a phased array.

Supports both non-overlapped and overlapped subarray architectures. In overlapped architectures, elements can belong to multiple subarrays with potentially different weights.

geometry#

Full array geometry

Type:

ArrayGeometry

subarray_assignments#

Subarray index for each element (shape: n_elements,) None for overlapped subarrays

Type:

ndarray, optional

n_subarrays#

Number of subarrays

Type:

int

subarray_centers#

Center positions of each subarray (n_subarrays, 2 or 3)

Type:

ndarray, optional

overlapped#

True if this is an overlapped subarray architecture

Type:

bool

subarray_elements#

For overlapped: list of element indices for each subarray

Type:

list of ndarray, optional

overlap_weights#

For overlapped: amplitude weights for shared elements in each subarray

Type:

list of ndarray, optional

overlapped: bool = False#
subarray_elements: List[ndarray] | None = None#
overlap_weights: List[ndarray] | None = None#
get_subarray_elements(subarray_idx)[source]#

Get element indices and mask for a specific subarray.

get_element_subarrays(element_idx)[source]#

Get indices of all subarrays that contain a given element.

For non-overlapped architectures, returns single subarray index. For overlapped, may return multiple indices.

Parameters:

element_idx (int) – Index of the element

Returns:

subarray_indices – Array of subarray indices containing this element

Return type:

ndarray

Planar Arrays#

phased_array.create_rectangular_array(Nx, Ny, dx, dy, wavelength=1.0, center=True)[source]#

Create a rectangular planar array.

Parameters:
  • Nx (int) – Number of elements in x

  • Ny (int) – Number of elements in y

  • dx (float) – Element spacing in x (in wavelengths)

  • dy (float) – Element spacing in y (in wavelengths)

  • wavelength (float) – Wavelength in meters (converts spacing to meters)

  • center (bool) – If True, center array at origin

Returns:

geometry – Array geometry with element positions

Return type:

ArrayGeometry

Examples

Create a 16x16 array with half-wavelength spacing:

>>> import phased_array as pa
>>> geom = pa.create_rectangular_array(16, 16, dx=0.5, dy=0.5)
>>> geom.n_elements
256
>>> geom.x.shape
(256,)

Create a 10x10 array at 10 GHz (3 cm wavelength):

>>> geom = pa.create_rectangular_array(10, 10, dx=0.5, dy=0.5, wavelength=0.03)
>>> geom.n_elements
100

Access element positions:

>>> geom = pa.create_rectangular_array(4, 4, dx=0.5, dy=0.5)
>>> geom.is_planar
True
phased_array.create_triangular_array(Nx, Ny, dx, dy=None, wavelength=1.0, center=True)[source]#

Create an offset triangular (hexagonal) array.

Rows are offset by half the x-spacing for optimal packing.

Parameters:
  • Nx (int) – Approximate number of elements in x

  • Ny (int) – Number of rows in y

  • dx (float) – Element spacing in x (in wavelengths)

  • dy (float, optional) – Row spacing in y. Default: dx * sqrt(3)/2 for equilateral

  • wavelength (float) – Wavelength in meters

  • center (bool) – If True, center array at origin

Returns:

geometry

Return type:

ArrayGeometry

Examples

Create a triangular grid array (hexagonal packing):

>>> import phased_array as pa
>>> geom = pa.create_triangular_array(10, 10, dx=0.5)
>>> geom.n_elements  # Slightly fewer than 10x10 due to offset rows
95
>>> geom.is_planar
True

Triangular grids provide ~13% better packing efficiency:

>>> rect = pa.create_rectangular_array(10, 10, dx=0.5, dy=0.5)
>>> tri = pa.create_triangular_array(10, 10, dx=0.5)
>>> rect.n_elements > tri.n_elements  # Rect has more for same Nx, Ny
True
phased_array.create_elliptical_array(a, b, dx, dy=None, grid_type='rectangular', wavelength=1.0)[source]#

Create an array within an elliptical boundary.

Parameters:
  • a (float) – Ellipse semi-axis in x (in wavelengths)

  • b (float) – Ellipse semi-axis in y (in wavelengths)

  • dx (float) – Element spacing in x (in wavelengths)

  • dy (float, optional) – Element spacing in y (default: same as dx)

  • grid_type (str) – ‘rectangular’ or ‘triangular’

  • wavelength (float) – Wavelength in meters

Returns:

geometry

Return type:

ArrayGeometry

Circular and Ring Arrays#

phased_array.create_circular_array(n_elements, radius, wavelength=1.0, start_angle=0.0)[source]#

Create a circular (ring) array in the xy-plane.

Parameters:
  • n_elements (int) – Number of elements

  • radius (float) – Ring radius in wavelengths

  • wavelength (float) – Wavelength in meters

  • start_angle (float) – Starting angle in radians

Returns:

geometry

Return type:

ArrayGeometry

phased_array.create_concentric_rings_array(n_rings, elements_per_ring, ring_spacing, wavelength=1.0, include_center=True)[source]#

Create a concentric rings array.

Parameters:
  • n_rings (int) – Number of rings

  • elements_per_ring (int or list) – Elements per ring (int applies to all, list for custom)

  • ring_spacing (float) – Spacing between rings in wavelengths

  • wavelength (float) – Wavelength in meters

  • include_center (bool) – Include a center element

Returns:

geometry

Return type:

ArrayGeometry

Conformal Arrays#

phased_array.create_cylindrical_array(n_azimuth, n_vertical, radius, height, wavelength=1.0)[source]#

Create a cylindrical conformal array.

Elements are distributed on a cylinder surface with normals pointing radially outward.

Parameters:
  • n_azimuth (int) – Number of elements around circumference

  • n_vertical (int) – Number of vertical rings

  • radius (float) – Cylinder radius in wavelengths

  • height (float) – Cylinder height in wavelengths

  • wavelength (float) – Wavelength in meters

Returns:

geometry

Return type:

ArrayGeometry

phased_array.create_spherical_array(n_theta, n_phi, radius, theta_min=0.0, theta_max=1.5707963267948966, wavelength=1.0)[source]#

Create a spherical conformal array.

Parameters:
  • n_theta (int) – Number of elements in theta (elevation)

  • n_phi (int) – Number of elements in phi (azimuth)

  • radius (float) – Sphere radius in wavelengths

  • theta_min (float) – Minimum theta in radians (0 = north pole)

  • theta_max (float) – Maximum theta in radians

  • wavelength (float) – Wavelength in meters

Returns:

geometry

Return type:

ArrayGeometry

phased_array.array_factor_conformal(theta, phi, geometry, weights, k, element_pattern_func=None, **element_kwargs)[source]#

Compute array factor for conformal arrays with element orientation.

Each element’s contribution is weighted by its projected pattern in the observation direction.

Parameters:
  • theta (ndarray) – Observation theta angles in radians

  • phi (ndarray) – Observation phi angles in radians

  • geometry (ArrayGeometry) – Array geometry with element normals

  • weights (ndarray) – Complex element weights

  • k (float) – Wavenumber

  • element_pattern_func (callable, optional) – Element pattern function

  • **element_kwargs – Arguments for element pattern function

Returns:

AF – Array factor (same shape as theta)

Return type:

ndarray

Sparse/Thinned Arrays#

phased_array.thin_array_random(geometry, thinning_factor, seed=None)[source]#

Randomly thin an array by removing elements.

Parameters:
  • geometry (ArrayGeometry) – Original array geometry

  • thinning_factor (float) – Fraction of elements to keep (0 to 1)

  • seed (int, optional) – Random seed for reproducibility

Returns:

geometry – Thinned array geometry

Return type:

ArrayGeometry

phased_array.thin_array_density_tapered(geometry, density_func, seed=None)[source]#

Thin array with position-dependent density (statistical thinning).

Parameters:
  • geometry (ArrayGeometry) – Original array geometry

  • density_func (callable) – Function(x, y) -> probability of keeping each element (0 to 1)

  • seed (int, optional) – Random seed

Returns:

geometry – Thinned geometry

Return type:

ArrayGeometry

phased_array.thin_array_genetic_algorithm(geometry, n_target, objective_func, population_size=50, n_generations=100, mutation_rate=0.1, seed=None)[source]#

Optimize element selection using a genetic algorithm.

Parameters:
  • geometry (ArrayGeometry) – Original full array geometry

  • n_target (int) – Target number of elements to keep

  • objective_func (callable) – Function(ArrayGeometry) -> float to minimize

  • population_size (int) – GA population size

  • n_generations (int) – Number of generations

  • mutation_rate (float) – Probability of mutation per gene

  • seed (int, optional) – Random seed

Returns:

geometry – Optimized thinned geometry

Return type:

ArrayGeometry

Subarrays#

phased_array.create_rectangular_subarrays(Nx_total, Ny_total, Nx_sub, Ny_sub, dx, dy, wavelength=1.0)[source]#

Create rectangular subarrays from a rectangular array.

Parameters:
  • Nx_total (int) – Total elements in x

  • Ny_total (int) – Total elements in y

  • Nx_sub (int) – Elements per subarray in x

  • Ny_sub (int) – Elements per subarray in y

  • dx (float) – Element spacing in wavelengths

  • dy (float) – Element spacing in wavelengths

  • wavelength (float) – Wavelength in meters

Returns:

architecture

Return type:

SubarrayArchitecture

phased_array.compute_subarray_weights(architecture, k, theta0_deg, phi0_deg, intra_subarray_weights=None)[source]#

Compute element weights for subarray-level beamforming.

Phase is quantized to subarray centers (one phase shifter per subarray).

Parameters:
  • architecture (SubarrayArchitecture) – Subarray architecture

  • k (float) – Wavenumber

  • theta0_deg (float) – Steering direction in degrees

  • phi0_deg (float) – Steering direction in degrees

  • intra_subarray_weights (ndarray, optional) – Amplitude taper applied within each subarray

Returns:

weights – Complex weights for all elements

Return type:

ndarray

Overlapped Subarrays#

Overlapped subarray architectures share elements between adjacent subarrays, improving pattern characteristics and reducing grating lobes while maintaining independent subarray beam control.

phased_array.create_overlapped_subarrays(Nx_total, Ny_total, Nx_sub, Ny_sub, overlap_x, overlap_y, dx, dy, wavelength=1.0, taper_overlap=True)[source]#

Create overlapped subarrays from a rectangular array.

Overlapped subarrays share elements between adjacent subarrays, which can improve pattern characteristics and reduce grating lobes while maintaining independent subarray beam control.

Parameters:
  • Nx_total (int) – Total elements in x

  • Ny_total (int) – Total elements in y

  • Nx_sub (int) – Elements per subarray in x

  • Ny_sub (int) – Elements per subarray in y

  • overlap_x (int) – Number of overlapping elements in x between adjacent subarrays

  • overlap_y (int) – Number of overlapping elements in y between adjacent subarrays

  • dx (float) – Element spacing in wavelengths

  • dy (float) – Element spacing in wavelengths

  • wavelength (float) – Wavelength in meters

  • taper_overlap (bool) – If True, apply amplitude taper to overlapping elements

Returns:

architecture – Overlapped subarray architecture

Return type:

SubarrayArchitecture

Examples

Create 50% overlapped 4x4 subarrays:

>>> import phased_array as pa
>>> arch = pa.create_overlapped_subarrays(
...     Nx_total=16, Ny_total=16,
...     Nx_sub=4, Ny_sub=4,
...     overlap_x=2, overlap_y=2,
...     dx=0.5, dy=0.5
... )
>>> arch.overlapped
True
>>> arch.n_subarrays > 0
True

Notes

Subarray stride (non-overlapping portion):

stride_x = Nx_sub - overlap_x stride_y = Ny_sub - overlap_y

Elements in the overlap region receive contributions from multiple subarrays, typically with tapered weights.

phased_array.overlapped_subarray_weights(architecture, k, theta0_deg, phi0_deg, subarray_weights=None)[source]#

Compute element weights for overlapped subarray beamforming.

Parameters:
  • architecture (SubarrayArchitecture) – Overlapped subarray architecture

  • k (float) – Wavenumber

  • theta0_deg (float) – Steering direction in degrees

  • phi0_deg (float) – Steering direction in degrees

  • subarray_weights (ndarray, optional) – Complex weights for each subarray (n_subarrays,). If None, uses uniform amplitude with steering phase.

Returns:

weights – Complex weights for all elements

Return type:

ndarray

Examples

>>> import numpy as np
>>> import phased_array as pa
>>> arch = pa.create_overlapped_subarrays(
...     Nx_total=16, Ny_total=16,
...     Nx_sub=4, Ny_sub=4,
...     overlap_x=2, overlap_y=2,
...     dx=0.5, dy=0.5
... )
>>> k = pa.wavelength_to_k(1.0)
>>> weights = pa.overlapped_subarray_weights(arch, k, 15, 0)
>>> weights.shape
(256,)

Notes

For overlapped architectures, each element’s weight is the sum of contributions from all subarrays it belongs to, weighted by the overlap weight for that subarray.

phased_array.compute_overlapped_pattern(architecture, k, theta0_deg, phi0_deg, theta_range=(0, 1.5707963267948966), n_points=361, phi_cut_deg=0.0)[source]#

Compute radiation pattern for overlapped subarray architecture.

Parameters:
  • architecture (SubarrayArchitecture) – Overlapped subarray architecture

  • k (float) – Wavenumber

  • theta0_deg (float) – Steering direction in degrees

  • phi0_deg (float) – Steering direction in degrees

  • theta_range (tuple) – (min, max) theta range in radians

  • n_points (int) – Number of pattern points

  • phi_cut_deg (float) – Phi angle for the pattern cut

Returns:

  • theta_deg (ndarray) – Theta angles in degrees

  • pattern_dB (ndarray) – Normalized pattern in dB

Return type:

Tuple[ndarray, ndarray]

Examples

>>> import numpy as np
>>> import phased_array as pa
>>> arch = pa.create_overlapped_subarrays(
...     Nx_total=16, Ny_total=16,
...     Nx_sub=4, Ny_sub=4,
...     overlap_x=2, overlap_y=2,
...     dx=0.5, dy=0.5
... )
>>> k = pa.wavelength_to_k(1.0)
>>> theta_deg, pattern_dB = pa.compute_overlapped_pattern(
...     arch, k, theta0_deg=0, phi0_deg=0
... )
>>> len(theta_deg) == 361
True