initial commit
This commit is contained in:
240
optimizer.py
Normal file
240
optimizer.py
Normal 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
|
||||
Reference in New Issue
Block a user