phased-array-systems 0.1.2__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/PKG-INFO +1 -1
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/examples/01_comms_single_case.py +1 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/examples/02_comms_doe_trade.py +5 -3
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/notebooks/tutorial_phased_array_trade_study.ipynb +2 -24
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/__about__.py +1 -1
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/architecture/config.py +115 -1
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_architecture.py +104 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/.github/workflows/ci.yml +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/.github/workflows/publish.yml +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/.gitignore +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/CLAUDE.md +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/LICENSE +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/README.md +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/examples/configs/comms_basic.yaml +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/examples/configs/comms_doe.yaml +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/pyproject.toml +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/ruff.toml +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/architecture/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/cli.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/constants.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/evaluate.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/io/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/io/config_loader.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/io/exporters.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/io/schema.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/antenna/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/antenna/adapter.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/antenna/metrics.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/base.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/comms/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/comms/link_budget.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/comms/propagation.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/swapc/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/swapc/cost.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/swapc/power.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/requirements/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/requirements/core.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/scenarios/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/scenarios/base.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/scenarios/comms.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/scenarios/radar.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/trades/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/trades/design_space.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/trades/doe.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/trades/pareto.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/trades/runner.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/types.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/utils/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/utils/hashing.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/viz/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/viz/plots.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/__init__.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_antenna_adapter.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_comms_link_budget.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_doe.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_evaluate.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_pareto.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_requirements.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_runner.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_swapc.py +0 -0
- {phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/tests/test_viz.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: phased-array-systems
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Phased array antenna system design, optimization, and performance visualization
|
|
5
5
|
Project-URL: Homepage, https://github.com/jman4162/phased-array-systems
|
|
6
6
|
Project-URL: Documentation, https://github.com/jman4162/phased-array-systems#readme
|
|
@@ -66,6 +66,7 @@ def main():
|
|
|
66
66
|
|
|
67
67
|
print(f"\nArchitecture: {architecture.name}")
|
|
68
68
|
print(f" Array: {array_config.nx}x{array_config.ny} = {architecture.n_elements} elements")
|
|
69
|
+
print(f" Sub-arrays: {array_config.n_subarrays_x}x{array_config.n_subarrays_y} = {array_config.n_subarrays} total")
|
|
69
70
|
print(f" TX Power/Element: {rf_config.tx_power_w_per_elem} W")
|
|
70
71
|
print(f" Total TX Power: {architecture.n_elements * rf_config.tx_power_w_per_elem} W")
|
|
71
72
|
|
|
@@ -96,9 +96,11 @@ def main():
|
|
|
96
96
|
# =========================================================================
|
|
97
97
|
design_space = (
|
|
98
98
|
DesignSpace(name="Comms Array Design Space")
|
|
99
|
-
# Array dimensions
|
|
100
|
-
.add_variable("array.nx", type="
|
|
101
|
-
.add_variable("array.ny", type="
|
|
99
|
+
# Array dimensions (must be powers of 2 for sub-array constraints)
|
|
100
|
+
.add_variable("array.nx", type="categorical", values=[4, 8, 16])
|
|
101
|
+
.add_variable("array.ny", type="categorical", values=[4, 8, 16])
|
|
102
|
+
# Disable sub-array constraint for DOE flexibility (or use power-of-2 values)
|
|
103
|
+
.add_variable("array.enforce_subarray_constraint", type="categorical", values=[True])
|
|
102
104
|
# Fixed array parameters
|
|
103
105
|
.add_variable("array.geometry", type="categorical", values=["rectangular"])
|
|
104
106
|
.add_variable("array.dx_lambda", type="float", low=0.5, high=0.5)
|
|
@@ -356,29 +356,7 @@
|
|
|
356
356
|
"execution_count": null,
|
|
357
357
|
"metadata": {},
|
|
358
358
|
"outputs": [],
|
|
359
|
-
"source": [
|
|
360
|
-
"# Define the design space to explore\n",
|
|
361
|
-
"design_space = (\n",
|
|
362
|
-
" DesignSpace(name=\"Comms Array Trade Space\")\n",
|
|
363
|
-
" # Variable parameters\n",
|
|
364
|
-
" .add_variable(\"array.nx\", type=\"int\", low=4, high=16)\n",
|
|
365
|
-
" .add_variable(\"array.ny\", type=\"int\", low=4, high=16)\n",
|
|
366
|
-
" .add_variable(\"rf.tx_power_w_per_elem\", type=\"float\", low=0.5, high=3.0)\n",
|
|
367
|
-
" .add_variable(\"cost.cost_per_elem_usd\", type=\"float\", low=75.0, high=150.0)\n",
|
|
368
|
-
" # Fixed parameters\n",
|
|
369
|
-
" .add_variable(\"array.geometry\", type=\"categorical\", values=[\"rectangular\"])\n",
|
|
370
|
-
" .add_variable(\"array.dx_lambda\", type=\"float\", low=0.5, high=0.5)\n",
|
|
371
|
-
" .add_variable(\"array.dy_lambda\", type=\"float\", low=0.5, high=0.5)\n",
|
|
372
|
-
" .add_variable(\"array.scan_limit_deg\", type=\"float\", low=60.0, high=60.0)\n",
|
|
373
|
-
" .add_variable(\"rf.pa_efficiency\", type=\"float\", low=0.3, high=0.3)\n",
|
|
374
|
-
" .add_variable(\"rf.noise_figure_db\", type=\"float\", low=3.0, high=3.0)\n",
|
|
375
|
-
" .add_variable(\"rf.n_tx_beams\", type=\"int\", low=1, high=1)\n",
|
|
376
|
-
" .add_variable(\"rf.feed_loss_db\", type=\"float\", low=1.0, high=1.0)\n",
|
|
377
|
-
" .add_variable(\"rf.system_loss_db\", type=\"float\", low=0.0, high=0.0)\n",
|
|
378
|
-
")\n",
|
|
379
|
-
"\n",
|
|
380
|
-
"print(f\"Design space: {design_space.n_dims} variables\")"
|
|
381
|
-
]
|
|
359
|
+
"source": "# Define the design space to explore\ndesign_space = (\n DesignSpace(name=\"Comms Array Trade Space\")\n # Variable parameters (array dimensions must be powers of 2 for RF constraints)\n .add_variable(\"array.nx\", type=\"categorical\", values=[4, 8, 16])\n .add_variable(\"array.ny\", type=\"categorical\", values=[4, 8, 16])\n .add_variable(\"rf.tx_power_w_per_elem\", type=\"float\", low=0.5, high=3.0)\n .add_variable(\"cost.cost_per_elem_usd\", type=\"float\", low=75.0, high=150.0)\n # Fixed parameters\n .add_variable(\"array.geometry\", type=\"categorical\", values=[\"rectangular\"])\n .add_variable(\"array.dx_lambda\", type=\"float\", low=0.5, high=0.5)\n .add_variable(\"array.dy_lambda\", type=\"float\", low=0.5, high=0.5)\n .add_variable(\"array.scan_limit_deg\", type=\"float\", low=60.0, high=60.0)\n .add_variable(\"array.enforce_subarray_constraint\", type=\"categorical\", values=[True])\n .add_variable(\"rf.pa_efficiency\", type=\"float\", low=0.3, high=0.3)\n .add_variable(\"rf.noise_figure_db\", type=\"float\", low=3.0, high=3.0)\n .add_variable(\"rf.n_tx_beams\", type=\"int\", low=1, high=1)\n .add_variable(\"rf.feed_loss_db\", type=\"float\", low=1.0, high=1.0)\n .add_variable(\"rf.system_loss_db\", type=\"float\", low=0.0, high=0.0)\n)\n\nprint(f\"Design space: {design_space.n_dims} variables\")"
|
|
382
360
|
},
|
|
383
361
|
{
|
|
384
362
|
"cell_type": "code",
|
|
@@ -685,4 +663,4 @@
|
|
|
685
663
|
},
|
|
686
664
|
"nbformat": 4,
|
|
687
665
|
"nbformat_minor": 4
|
|
688
|
-
}
|
|
666
|
+
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
"""Architecture configuration models using Pydantic."""
|
|
2
2
|
|
|
3
|
+
import math
|
|
3
4
|
from typing import Literal
|
|
4
5
|
|
|
5
|
-
from pydantic import BaseModel, Field, field_validator
|
|
6
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
7
|
+
|
|
8
|
+
# Valid power-of-two values for sub-array dimensions
|
|
9
|
+
VALID_POWERS_OF_TWO = [2**i for i in range(1, 10)] # 2, 4, 8, 16, 32, 64, 128, 256, 512
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_power_of_two(n: int) -> bool:
|
|
13
|
+
"""Check if n is a power of two (and >= 2)."""
|
|
14
|
+
return n >= 2 and (n & (n - 1)) == 0
|
|
6
15
|
|
|
7
16
|
|
|
8
17
|
class ArrayConfig(BaseModel):
|
|
@@ -15,6 +24,9 @@ class ArrayConfig(BaseModel):
|
|
|
15
24
|
dx_lambda: Element spacing in x-direction (wavelengths)
|
|
16
25
|
dy_lambda: Element spacing in y-direction (wavelengths)
|
|
17
26
|
scan_limit_deg: Maximum scan angle from boresight (degrees)
|
|
27
|
+
max_subarray_nx: Maximum elements per sub-array in x (must be power of 2)
|
|
28
|
+
max_subarray_ny: Maximum elements per sub-array in y (must be power of 2)
|
|
29
|
+
enforce_subarray_constraint: Whether to enforce power-of-two sub-array constraint
|
|
18
30
|
"""
|
|
19
31
|
|
|
20
32
|
geometry: Literal["rectangular", "circular", "triangular"] = "rectangular"
|
|
@@ -26,11 +38,113 @@ class ArrayConfig(BaseModel):
|
|
|
26
38
|
default=60.0, ge=0, le=90, description="Maximum scan angle (degrees)"
|
|
27
39
|
)
|
|
28
40
|
|
|
41
|
+
# Sub-array constraints for practical RF component design (power-of-two)
|
|
42
|
+
max_subarray_nx: int = Field(
|
|
43
|
+
default=8, ge=2, description="Max elements per sub-array in x (must be power of 2)"
|
|
44
|
+
)
|
|
45
|
+
max_subarray_ny: int = Field(
|
|
46
|
+
default=8, ge=2, description="Max elements per sub-array in y (must be power of 2)"
|
|
47
|
+
)
|
|
48
|
+
enforce_subarray_constraint: bool = Field(
|
|
49
|
+
default=True, description="Enforce power-of-two sub-array constraint"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
@field_validator("max_subarray_nx", "max_subarray_ny")
|
|
53
|
+
@classmethod
|
|
54
|
+
def validate_power_of_two(cls, v: int) -> int:
|
|
55
|
+
"""Validate that max sub-array dimensions are powers of two."""
|
|
56
|
+
if not is_power_of_two(v):
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"max_subarray value {v} must be a power of 2 "
|
|
59
|
+
f"(valid values: {VALID_POWERS_OF_TWO})"
|
|
60
|
+
)
|
|
61
|
+
return v
|
|
62
|
+
|
|
63
|
+
@model_validator(mode="after")
|
|
64
|
+
def validate_subarray_constraints(self) -> "ArrayConfig":
|
|
65
|
+
"""Validate that array dimensions result in power-of-two sub-arrays."""
|
|
66
|
+
if not self.enforce_subarray_constraint:
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
if self.geometry != "rectangular":
|
|
70
|
+
# Sub-array constraints only apply to rectangular arrays for now
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
# Check x-direction
|
|
74
|
+
if self.nx > self.max_subarray_nx:
|
|
75
|
+
# Must be divisible by max_subarray_nx
|
|
76
|
+
if self.nx % self.max_subarray_nx != 0:
|
|
77
|
+
raise ValueError(
|
|
78
|
+
f"nx={self.nx} must be divisible by max_subarray_nx={self.max_subarray_nx} "
|
|
79
|
+
f"for arrays larger than max sub-array size. "
|
|
80
|
+
f"Set enforce_subarray_constraint=False to disable."
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
# Small array: nx itself must be power of two
|
|
84
|
+
if not is_power_of_two(self.nx):
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"nx={self.nx} must be a power of 2 (2, 4, 8, 16, ...) when "
|
|
87
|
+
f"enforce_subarray_constraint=True. Valid values: {VALID_POWERS_OF_TWO}"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Check y-direction
|
|
91
|
+
if self.ny > self.max_subarray_ny:
|
|
92
|
+
# Must be divisible by max_subarray_ny
|
|
93
|
+
if self.ny % self.max_subarray_ny != 0:
|
|
94
|
+
raise ValueError(
|
|
95
|
+
f"ny={self.ny} must be divisible by max_subarray_ny={self.max_subarray_ny} "
|
|
96
|
+
f"for arrays larger than max sub-array size. "
|
|
97
|
+
f"Set enforce_subarray_constraint=False to disable."
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
# Small array: ny itself must be power of two
|
|
101
|
+
if not is_power_of_two(self.ny):
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"ny={self.ny} must be a power of 2 (2, 4, 8, 16, ...) when "
|
|
104
|
+
f"enforce_subarray_constraint=True. Valid values: {VALID_POWERS_OF_TWO}"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return self
|
|
108
|
+
|
|
29
109
|
@property
|
|
30
110
|
def n_elements(self) -> int:
|
|
31
111
|
"""Total number of elements in the array."""
|
|
32
112
|
return self.nx * self.ny
|
|
33
113
|
|
|
114
|
+
@property
|
|
115
|
+
def subarray_nx(self) -> int:
|
|
116
|
+
"""Actual elements per sub-array in x-direction."""
|
|
117
|
+
if self.nx <= self.max_subarray_nx:
|
|
118
|
+
return self.nx
|
|
119
|
+
return self.max_subarray_nx
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def subarray_ny(self) -> int:
|
|
123
|
+
"""Actual elements per sub-array in y-direction."""
|
|
124
|
+
if self.ny <= self.max_subarray_ny:
|
|
125
|
+
return self.ny
|
|
126
|
+
return self.max_subarray_ny
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def n_subarrays_x(self) -> int:
|
|
130
|
+
"""Number of sub-arrays in x-direction."""
|
|
131
|
+
return math.ceil(self.nx / self.max_subarray_nx)
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def n_subarrays_y(self) -> int:
|
|
135
|
+
"""Number of sub-arrays in y-direction."""
|
|
136
|
+
return math.ceil(self.ny / self.max_subarray_ny)
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def n_subarrays(self) -> int:
|
|
140
|
+
"""Total number of sub-arrays."""
|
|
141
|
+
return self.n_subarrays_x * self.n_subarrays_y
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def elements_per_subarray(self) -> int:
|
|
145
|
+
"""Number of elements per sub-array."""
|
|
146
|
+
return self.subarray_nx * self.subarray_ny
|
|
147
|
+
|
|
34
148
|
|
|
35
149
|
class RFChainConfig(BaseModel):
|
|
36
150
|
"""Configuration for the RF chain.
|
|
@@ -38,6 +38,110 @@ class TestArrayConfig:
|
|
|
38
38
|
ArrayConfig(nx=8, ny=8, scan_limit_deg=95.0)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
class TestSubarrayConstraints:
|
|
42
|
+
"""Tests for sub-array constraints (power-of-two)."""
|
|
43
|
+
|
|
44
|
+
def test_default_subarray_values(self):
|
|
45
|
+
"""Test default sub-array constraint values."""
|
|
46
|
+
config = ArrayConfig(nx=8, ny=8)
|
|
47
|
+
assert config.max_subarray_nx == 8
|
|
48
|
+
assert config.max_subarray_ny == 8
|
|
49
|
+
assert config.enforce_subarray_constraint is True
|
|
50
|
+
|
|
51
|
+
def test_subarray_properties_small_array(self):
|
|
52
|
+
"""Test sub-array properties for array smaller than max."""
|
|
53
|
+
config = ArrayConfig(nx=4, ny=4)
|
|
54
|
+
assert config.subarray_nx == 4
|
|
55
|
+
assert config.subarray_ny == 4
|
|
56
|
+
assert config.n_subarrays_x == 1
|
|
57
|
+
assert config.n_subarrays_y == 1
|
|
58
|
+
assert config.n_subarrays == 1
|
|
59
|
+
assert config.elements_per_subarray == 16
|
|
60
|
+
|
|
61
|
+
def test_subarray_properties_large_array(self):
|
|
62
|
+
"""Test sub-array properties for array larger than max."""
|
|
63
|
+
config = ArrayConfig(nx=16, ny=32, max_subarray_nx=8, max_subarray_ny=8)
|
|
64
|
+
assert config.subarray_nx == 8
|
|
65
|
+
assert config.subarray_ny == 8
|
|
66
|
+
assert config.n_subarrays_x == 2
|
|
67
|
+
assert config.n_subarrays_y == 4
|
|
68
|
+
assert config.n_subarrays == 8
|
|
69
|
+
assert config.elements_per_subarray == 64
|
|
70
|
+
|
|
71
|
+
def test_subarray_constraint_enforced_valid(self):
|
|
72
|
+
"""Test valid array dimensions with constraint enforced."""
|
|
73
|
+
# 16 is divisible by 8 and both are powers of 2
|
|
74
|
+
config = ArrayConfig(nx=16, ny=16, max_subarray_nx=8, max_subarray_ny=8)
|
|
75
|
+
assert config.n_subarrays == 4
|
|
76
|
+
|
|
77
|
+
def test_subarray_constraint_invalid_not_power_of_two_small(self):
|
|
78
|
+
"""Test that non-power-of-two small array raises error."""
|
|
79
|
+
# nx=6 is not a power of 2
|
|
80
|
+
with pytest.raises(ValidationError, match="nx=6 must be a power of 2"):
|
|
81
|
+
ArrayConfig(nx=6, ny=8, max_subarray_nx=8, max_subarray_ny=8)
|
|
82
|
+
|
|
83
|
+
def test_subarray_constraint_invalid_not_divisible(self):
|
|
84
|
+
"""Test that non-divisible large array raises error."""
|
|
85
|
+
# nx=12 is larger than 8 but not divisible by 8
|
|
86
|
+
with pytest.raises(ValidationError, match="nx=12 must be divisible by"):
|
|
87
|
+
ArrayConfig(nx=12, ny=8, max_subarray_nx=8, max_subarray_ny=8)
|
|
88
|
+
|
|
89
|
+
def test_max_subarray_must_be_power_of_two(self):
|
|
90
|
+
"""Test that max_subarray values must be powers of two."""
|
|
91
|
+
with pytest.raises(ValidationError, match="must be a power of 2"):
|
|
92
|
+
ArrayConfig(nx=8, ny=8, max_subarray_nx=6, max_subarray_ny=8)
|
|
93
|
+
|
|
94
|
+
def test_subarray_constraint_disabled(self):
|
|
95
|
+
"""Test that constraint can be disabled."""
|
|
96
|
+
# This would fail with constraint enabled (10, 12 not powers of 2)
|
|
97
|
+
config = ArrayConfig(
|
|
98
|
+
nx=10, ny=12,
|
|
99
|
+
max_subarray_nx=8, max_subarray_ny=8,
|
|
100
|
+
enforce_subarray_constraint=False
|
|
101
|
+
)
|
|
102
|
+
assert config.nx == 10
|
|
103
|
+
assert config.ny == 12
|
|
104
|
+
assert config.n_subarrays_x == 2 # ceil(10/8)
|
|
105
|
+
assert config.n_subarrays_y == 2 # ceil(12/8)
|
|
106
|
+
|
|
107
|
+
def test_custom_subarray_size_power_of_two(self):
|
|
108
|
+
"""Test custom sub-array sizes (must be powers of two)."""
|
|
109
|
+
config = ArrayConfig(
|
|
110
|
+
nx=16, ny=16,
|
|
111
|
+
max_subarray_nx=4, max_subarray_ny=4
|
|
112
|
+
)
|
|
113
|
+
assert config.n_subarrays_x == 4
|
|
114
|
+
assert config.n_subarrays_y == 4
|
|
115
|
+
assert config.n_subarrays == 16
|
|
116
|
+
|
|
117
|
+
def test_non_rectangular_skips_constraint(self):
|
|
118
|
+
"""Test that non-rectangular geometries skip constraint validation."""
|
|
119
|
+
# This would fail for rectangular with constraint enforced
|
|
120
|
+
config = ArrayConfig(
|
|
121
|
+
nx=10, ny=12,
|
|
122
|
+
geometry="circular",
|
|
123
|
+
max_subarray_nx=8, max_subarray_ny=8,
|
|
124
|
+
enforce_subarray_constraint=True
|
|
125
|
+
)
|
|
126
|
+
assert config.nx == 10
|
|
127
|
+
assert config.ny == 12
|
|
128
|
+
|
|
129
|
+
def test_valid_power_of_two_sizes(self):
|
|
130
|
+
"""Test various valid power-of-two array sizes."""
|
|
131
|
+
for size in [2, 4, 8, 16, 32]:
|
|
132
|
+
config = ArrayConfig(nx=size, ny=size)
|
|
133
|
+
assert config.nx == size
|
|
134
|
+
assert config.ny == size
|
|
135
|
+
|
|
136
|
+
def test_large_array_multiple_subarrays(self):
|
|
137
|
+
"""Test large array with multiple sub-arrays."""
|
|
138
|
+
config = ArrayConfig(nx=64, ny=32, max_subarray_nx=16, max_subarray_ny=8)
|
|
139
|
+
assert config.n_subarrays_x == 4 # 64/16
|
|
140
|
+
assert config.n_subarrays_y == 4 # 32/8
|
|
141
|
+
assert config.n_subarrays == 16
|
|
142
|
+
assert config.elements_per_subarray == 128 # 16*8
|
|
143
|
+
|
|
144
|
+
|
|
41
145
|
class TestRFChainConfig:
|
|
42
146
|
"""Tests for RFChainConfig."""
|
|
43
147
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/constants.py
RENAMED
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/evaluate.py
RENAMED
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/io/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/io/exporters.py
RENAMED
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/io/schema.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/models/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/scenarios/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/trades/doe.py
RENAMED
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/trades/pareto.py
RENAMED
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/trades/runner.py
RENAMED
|
File without changes
|
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/utils/__init__.py
RENAMED
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/utils/hashing.py
RENAMED
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/viz/__init__.py
RENAMED
|
File without changes
|
{phased_array_systems-0.1.2 → phased_array_systems-0.2.0}/src/phased_array_systems/viz/plots.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|