initial commit

This commit is contained in:
Brent Perteet
2025-12-05 16:08:21 -06:00
commit 5f3beeda8d
6 changed files with 746 additions and 0 deletions

240
optimizer.py Normal file
View File

@@ -0,0 +1,240 @@
from typing import Tuple, Optional, Dict, List
from dataclasses import dataclass
from model import TransformerModel
import itertools
@dataclass
class OptimizationResult:
"""Result of transformer optimization."""
# Optimal configuration
primary_tap: int
secondary_tap: int
Vp_rms: float
efficiency: float
# Operating point details
Np_eff: int
Ns_eff: int
turns_ratio: float
B_peak_T: float
Vs_rms: float
Ip_rms: float
Is_rms: float
P_out_W: float
P_in_W: float
P_cu_W: float
P_cu_primary_W: float
P_cu_secondary_W: float
P_core_W: float
# Constraint satisfaction
power_error_percent: float
voltage_used: float
flux_density_T: float
class TransformerOptimizer:
"""
Optimizer for finding best transformer configuration.
Searches across primary taps, secondary taps, and input voltages to:
- Deliver desired power to the load
- Maximize efficiency
- Stay within flux density and voltage constraints
"""
def __init__(self, model: TransformerModel):
self.model = model
def optimize(
self,
load_ohms: float,
target_power_W: float,
freq_hz: float,
Vp_min: float = 5.0,
Vp_max: float = 50.0,
Vp_step: float = 0.5,
B_max_T: float = 0.3,
Vs_max: float = float('inf'),
core_loss_W: float = 0.0,
power_tolerance_percent: float = 2.0,
fallback_max_power: bool = True,
) -> Optional[OptimizationResult]:
"""
Find optimal transformer configuration for given constraints.
Arguments:
- load_ohms: Resistive load (ohms)
- target_power_W: Desired output power (W)
- freq_hz: Operating frequency (Hz)
- Vp_min, Vp_max, Vp_step: Input voltage search range
- B_max_T: Maximum allowed flux density (Tesla)
- Vs_max: Maximum allowed secondary voltage (V)
- core_loss_W: Core loss at this operating point
- power_tolerance_percent: Acceptable power delivery error (%)
- fallback_max_power: If True and target cannot be met, find max power with best efficiency
Returns:
- OptimizationResult with best configuration, or None if no valid solution
"""
if load_ohms <= 0:
raise ValueError("Load must be > 0")
if target_power_W <= 0:
raise ValueError("Target power must be > 0")
if Vp_min >= Vp_max:
raise ValueError("Vp_min must be < Vp_max")
# Generate all possible tap numbers
primary_taps = list(range(1, len(self.model.primary_taps)))
secondary_taps = list(range(1, len(self.model.secondary_taps)))
if not primary_taps or not secondary_taps:
raise ValueError("Need at least 2 taps on each winding")
best_result = None
best_efficiency = -1.0
# Fallback tracking: find max achievable power under target
fallback_result = None
fallback_max_power_achieved = -1.0
fallback_best_efficiency = -1.0
# Search over all tap combinations
for p_tap, s_tap in itertools.product(primary_taps, secondary_taps):
# For each tap combination, find the voltage that delivers target power
result = self._optimize_voltage_for_tap(
primary_tap=p_tap,
secondary_tap=s_tap,
load_ohms=load_ohms,
target_power_W=target_power_W,
freq_hz=freq_hz,
Vp_min=Vp_min,
Vp_max=Vp_max,
Vp_step=Vp_step,
B_max_T=B_max_T,
Vs_max=Vs_max,
core_loss_W=core_loss_W,
power_tolerance_percent=power_tolerance_percent,
fallback_max_power=fallback_max_power,
)
if result is not None:
power_error = abs(result.P_out_W - target_power_W) / target_power_W * 100
# Check if this meets the target power tolerance
if power_error <= power_tolerance_percent:
if result.efficiency > best_efficiency:
best_efficiency = result.efficiency
best_result = result
# Track fallback: maximize power below target, then maximize efficiency
elif fallback_max_power and result.P_out_W < target_power_W:
if (result.P_out_W > fallback_max_power_achieved or
(result.P_out_W == fallback_max_power_achieved and result.efficiency > fallback_best_efficiency)):
fallback_max_power_achieved = result.P_out_W
fallback_best_efficiency = result.efficiency
fallback_result = result
# Return best result if found, otherwise fallback
return best_result if best_result is not None else fallback_result
def _optimize_voltage_for_tap(
self,
primary_tap: int,
secondary_tap: int,
load_ohms: float,
target_power_W: float,
freq_hz: float,
Vp_min: float,
Vp_max: float,
Vp_step: float,
B_max_T: float,
Vs_max: float,
core_loss_W: float,
power_tolerance_percent: float,
fallback_max_power: bool,
) -> Optional[OptimizationResult]:
"""
For a given tap configuration, find the best voltage.
In normal mode: returns highest efficiency that meets target power ± tolerance
In fallback mode: returns highest efficiency at max achievable power below target
Returns OptimizationResult if valid solution found, else None.
"""
best_within_tolerance = None
best_efficiency_within_tolerance = -1.0
# For fallback: track max power achievable and best efficiency at that power
max_power_below_target = -1.0
best_at_max_power = None
best_efficiency_at_max_power = -1.0
# Generate voltage sweep
import numpy as np
voltages = np.arange(Vp_min, Vp_max + Vp_step/2, Vp_step)
for Vp_rms in voltages:
try:
sim = self.model.simulate(
primary_tap=primary_tap,
secondary_tap=secondary_tap,
Vp_rms=float(Vp_rms),
freq_hz=freq_hz,
load_ohms=load_ohms,
core_loss_W=core_loss_W,
)
except (ValueError, ZeroDivisionError):
continue
# Check constraints
if sim["B_peak_T"] > B_max_T:
continue
if sim["Vs_rms"] > Vs_max:
continue
power_error = abs(sim["P_out_W"] - target_power_W) / target_power_W * 100
result = OptimizationResult(
primary_tap=primary_tap,
secondary_tap=secondary_tap,
Vp_rms=float(Vp_rms),
efficiency=sim["efficiency"],
Np_eff=sim["Np_eff"],
Ns_eff=sim["Ns_eff"],
turns_ratio=sim["turns_ratio"],
B_peak_T=sim["B_peak_T"],
Vs_rms=sim["Vs_rms"],
Ip_rms=sim["Ip_rms"],
Is_rms=sim["Is_rms"],
P_out_W=sim["P_out_W"],
P_in_W=sim["P_in_W"],
P_cu_W=sim["P_cu_W"],
P_cu_primary_W=sim["P_cu_primary_W"],
P_cu_secondary_W=sim["P_cu_secondary_W"],
P_core_W=sim["P_core_W"],
power_error_percent=power_error,
voltage_used=float(Vp_rms),
flux_density_T=sim["B_peak_T"],
)
# Track solutions within tolerance
if power_error <= power_tolerance_percent:
if sim["efficiency"] > best_efficiency_within_tolerance:
best_efficiency_within_tolerance = sim["efficiency"]
best_within_tolerance = result
# Track fallback: max power below target with best efficiency
if fallback_max_power and sim["P_out_W"] < target_power_W:
if sim["P_out_W"] > max_power_below_target:
max_power_below_target = sim["P_out_W"]
best_efficiency_at_max_power = sim["efficiency"]
best_at_max_power = result
elif sim["P_out_W"] == max_power_below_target and sim["efficiency"] > best_efficiency_at_max_power:
best_efficiency_at_max_power = sim["efficiency"]
best_at_max_power = result
# Return best within tolerance if found, otherwise fallback
return best_within_tolerance if best_within_tolerance is not None else best_at_max_power