Skip to content

Commit 890e4fa

Browse files
committed
merge buckboost
1 parent d32609b commit 890e4fa

File tree

2 files changed

+33
-60
lines changed

2 files changed

+33
-60
lines changed

edg/abstract_parts/AbstractPowerConverters.py

Lines changed: 25 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -575,9 +575,6 @@ def __init__(self, input_voltage: RangeLike, output_voltage: RangeLike, frequenc
575575
self.inductor_current_ripple, self.current_limits, self.efficiency,
576576
self.input_voltage_ripple, self.output_voltage_ripple)
577577

578-
# TODO, this is a hack and should be replaced by the actual peak current
579-
self.inductor_spec_peak_current = self.Parameter(FloatExpr())
580-
581578
self.actual_buck_dutycycle = self.Parameter(RangeExpr()) # possible actual duty cycle in buck mode
582579
self.actual_boost_dutycycle = self.Parameter(RangeExpr()) # possible actual duty cycle in boost mode
583580
self.actual_inductor_current_ripple = self.Parameter(RangeExpr())
@@ -596,76 +593,44 @@ def contents(self):
596593

597594
def generate(self) -> None:
598595
super().generate()
599-
input_voltage = self.get(self.input_voltage)
600-
output_voltage = self.get(self.output_voltage)
601-
frequency = self.get(self.frequency)
602-
output_current = self.get(self.output_current)
603-
inductor_current_ripple = self.get(self.inductor_current_ripple)
604-
input_voltage_ripple = self.get(self.input_voltage_ripple)
605-
output_voltage_ripple = self.get(self.output_voltage_ripple)
606-
607-
# clip each mode's duty cycle to that mode's operating range
608-
buck_dutycycle = (output_voltage / input_voltage / self.get(self.efficiency)).bound_to(Range(-float('inf'), 1))
609-
self.assign(self.actual_buck_dutycycle, buck_dutycycle)
610-
boost_dutycycle = (1 - input_voltage / output_voltage * self.get(self.efficiency)).bound_to(Range(0, float('inf')))
611-
self.assign(self.actual_boost_dutycycle, boost_dutycycle)
612-
613-
# Calculate minimum inductance based on worst case values (operating range corners producing maximum inductance)
614-
# This range must be constructed manually to not double-count the tolerance stackup of the voltages
615-
buck_inductance_min = (output_voltage.lower * (input_voltage.upper - output_voltage.lower) /
616-
(inductor_current_ripple.upper * frequency.lower * input_voltage.upper))
617-
if inductor_current_ripple.lower == 0: # basically infinite inductance
618-
buck_inductance_max = float('inf')
619-
else:
620-
buck_inductance_max = (output_voltage.lower * (input_voltage.upper - output_voltage.lower) /
621-
(inductor_current_ripple.lower * frequency.lower * input_voltage.upper))
622-
min_current = max(0, output_current.lower - inductor_current_ripple.upper / 2) # applies to both modes
623-
buck_peak_current = output_current.upper + inductor_current_ripple.upper / 2
624-
boost_inductance_min = (input_voltage.lower * (output_voltage.upper - input_voltage.lower) /
625-
(inductor_current_ripple.upper * frequency.lower * output_voltage.lower))
626-
if inductor_current_ripple.lower == 0: # basically infinite inductance
627-
boost_inductance_max = float('inf')
628-
else:
629-
boost_inductance_max = (input_voltage.lower * (output_voltage.upper - input_voltage.lower) /
630-
(inductor_current_ripple.lower * frequency.lower * output_voltage.lower))
631-
boost_peak_current = output_current.upper / (1 - boost_dutycycle.upper) + inductor_current_ripple.upper / 2
632-
inductor_spec_peak_current = max(buck_peak_current, boost_peak_current)
633-
self.assign(self.inductor_spec_peak_current, inductor_spec_peak_current)
634-
635-
# take intersection of buck and boost inductances, and hopefully they overlap
636-
inductance_min = max(buck_inductance_min, boost_inductance_min)
637-
inductance_max = min(buck_inductance_max, boost_inductance_max)
596+
buck_values = BuckConverterPowerPath.calculate_parameters(
597+
self.get(self.input_voltage), self.get(self.output_voltage),
598+
self.get(self.frequency), self.get(self.output_current),
599+
self.get(self.inductor_current_ripple), self.get(self.input_voltage_ripple), self.get(self.output_voltage_ripple),
600+
efficiency=self.get(self.efficiency), dutycycle_limit=Range(0, 1))
601+
boost_values = BoostConverterPowerPath.calculate_parameters(
602+
self.get(self.input_voltage), self.get(self.output_voltage),
603+
self.get(self.frequency), self.get(self.output_current),
604+
self.get(self.inductor_current_ripple), self.get(self.input_voltage_ripple), self.get(self.output_voltage_ripple),
605+
efficiency=self.get(self.efficiency), dutycycle_limit=Range(0, 1))
606+
self.assign(self.actual_buck_dutycycle, buck_values.effective_dutycycle)
607+
self.assign(self.actual_boost_dutycycle, boost_values.effective_dutycycle)
608+
638609
self.inductor = self.Block(Inductor(
639-
inductance=(inductance_min, inductance_max)*Henry,
640-
current=(0, inductor_spec_peak_current),
641-
frequency=frequency*Hertz
610+
inductance=buck_values.inductance.intersect(boost_values.inductance) * Henry,
611+
current=buck_values.inductor_peak_currents.hull(buck_values.inductor_peak_currents),
612+
frequency=self.frequency
642613
))
643614

644-
buck_actual_ripple = (output_voltage.lower * (input_voltage.upper - output_voltage.lower) /
645-
(self.inductor.actual_inductance * frequency.lower * input_voltage.upper))
646-
boost_actual_ripple = (input_voltage.lower * (output_voltage.upper - input_voltage.lower) /
647-
(self.inductor.actual_inductance * frequency.lower * output_voltage.lower))
615+
# TODO deduplciate w/ ripple code in buck and boost converters
616+
buck_actual_ripple = (self.output_voltage.lower() * (self.input_voltage.upper() - self.output_voltage.lower()) /
617+
(self.inductor.actual_inductance * self.frequency.lower() * self.input_voltage.upper()))
618+
boost_actual_ripple = (self.input_voltage.lower() * (self.output_voltage.upper() - self.input_voltage.lower()) /
619+
(self.inductor.actual_inductance * self.frequency.lower() * self.output_voltage.lower()))
648620
self.assign(self.actual_inductor_current_ripple, buck_actual_ripple.hull(boost_actual_ripple))
649621

650622
self.connect(self.switch_in, self.inductor.a)
651-
self.assign(self.actual_inductor_current, (min_current, inductor_spec_peak_current)) # peak currents
623+
dc_current_range = self.output_current / (1 - boost_values.effective_dutycycle.upper) # DC range at worst case boost
624+
self.assign(self.actual_inductor_current, dc_current_range + (self.actual_inductor_current_ripple.upper() / 2))
652625
self.connect(self.switch_out, self.inductor.b)
653626
self.assign(self.actual_avg_current_rating, (0, self.current_limits.intersect(self.inductor.actual_current_rating).upper() -
654627
(self.actual_inductor_current_ripple.upper() / 2)))
655628

656-
input_buck_min_cap = (output_current.upper * BuckConverterPowerPath.max_d_inverse_d(buck_dutycycle) /
657-
(frequency.lower * input_voltage_ripple))
658-
input_boost_min_cap = ((output_current.upper / (1 - boost_dutycycle.upper)) /
659-
(frequency.lower * input_voltage_ripple))
660629
self.in_cap = self.Block(DecouplingCapacitor(
661-
capacitance=Range.from_lower(max(input_buck_min_cap, input_boost_min_cap))*Farad,
630+
capacitance=buck_values.input_capacitance.intersect(boost_values.input_capacitance) * Farad,
662631
exact_capacitance=True
663632
)).connected(self.gnd, self.pwr_in)
664-
665-
# calculated with steady-state ripple
666-
output_buck_min_cap = inductor_current_ripple.upper / (8 * frequency.lower * output_voltage_ripple)
667-
output_boost_min_cap = output_current.upper * boost_dutycycle.upper / (frequency.lower * output_voltage_ripple)
668633
self.out_cap = self.Block(DecouplingCapacitor(
669-
capacitance=Range.from_lower(max(output_buck_min_cap, output_boost_min_cap))*Farad,
634+
capacitance=buck_values.output_capacitance.intersect(boost_values.output_capacitance) * Farad,
670635
exact_capacitance=True
671636
)).connected(self.gnd, self.pwr_out)

edg/core/Range.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,17 @@ def __contains__(self, item: Union['Range', float]) -> bool:
144144
else:
145145
return NotImplemented
146146

147+
def hull(self, other: 'Range') -> 'Range':
148+
return Range(min(self.lower, other.lower), max(self.upper, other.upper))
149+
147150
def intersects(self, other: 'Range') -> bool:
148151
return (self.upper >= other.lower) and (self.lower <= other.upper)
149152

153+
def intersect(self, other: 'Range') -> 'Range':
154+
# TODO make behavior more consistent w/ compiler and returning empty that props as a unit
155+
assert self.intersects(other), "Cannot intersect ranges that do not intersect"
156+
return Range(max(self.lower, other.lower), min(self.upper, other.upper))
157+
150158
def __add__(self, other: Union['Range', float]) -> 'Range':
151159
if isinstance(other, Range):
152160
return Range(self.lower + other.lower, self.upper + other.upper)

0 commit comments

Comments
 (0)