Skip to content

Commit 2ddc161

Browse files
Merge pull request #70 from upb-lea/pecst
Add prototype for capacitor optimization
2 parents f2407da + d1c631e commit 2ddc161

14 files changed

+486
-2
lines changed

.github/workflows/main.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,21 @@ jobs:
109109
echo show folder structure:
110110
ls
111111
112+
- name: install PE-CST developer version
113+
run: |
114+
pwd
115+
ls
116+
wget https://github.com/upb-lea/capacitor_selection_toolbox/archive/refs/heads/main.zip
117+
unzip main.zip
118+
rm main.zip
119+
cd capacitor_selection_toolbox-main
120+
pip install -e .
121+
cd ..
122+
echo show folder path
123+
pwd
124+
echo show folder structure:
125+
ls
126+
112127
- name: install the package
113128
run: |
114129
pip install -e .

.github/workflows/sphinx_render_docs.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,21 @@ jobs:
6969
echo show folder structure:
7070
ls
7171
72+
- name: install PE-CST developer version
73+
run: |
74+
pwd
75+
ls
76+
wget https://github.com/upb-lea/capacitor_selection_toolbox/archive/refs/heads/main.zip
77+
unzip main.zip
78+
rm main.zip
79+
cd capacitor_selection_toolbox-main
80+
pip install -e .
81+
cd ..
82+
echo show folder path
83+
pwd
84+
echo show folder structure:
85+
ls
86+
7287
- name: install the package
7388
run: |
7489
pip install -e .

dct/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
# summary
2525
from dct.summary_processing import *
2626
from dct.summary_pre_processing import *
27+
# capacitor selection classes
28+
from dct.capacitor_selection import *
29+
from dct.capacitor_optimization_dtos import *
2730
# optimization classes
2831
from dct.inductor_optimization import *
2932
from dct.transformer_optimization import *

dct/capacitor_optimization_dtos.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Data transfer objects (DTOs) for the capacitor optimization."""
2+
3+
# python libraries
4+
import dataclasses
5+
6+
# 3rd party libraries
7+
import pecst
8+
# own libraries
9+
from dct.server_ctl_dtos import ProgressData
10+
11+
@dataclasses.dataclass
12+
class CapacitorOptimizationDto:
13+
"""DTO for the inductor optimization."""
14+
15+
circuit_filtered_point_filename: str
16+
progress_data: ProgressData
17+
capacitor_optimization_dto: pecst.CapacitorRequirements

dct/capacitor_selection.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
"""Capacitor selection."""
2+
3+
# python libraries
4+
import logging
5+
import os
6+
from multiprocessing import Pool, cpu_count
7+
import copy
8+
import pickle
9+
10+
# 3rd party libraries
11+
import numpy as np
12+
import pandas as pd
13+
import tqdm
14+
15+
# own libraries
16+
import pecst
17+
from dct.capacitor_optimization_dtos import CapacitorOptimizationDto
18+
from dct.toml_checker import TomlCapacitorSelection, Debug
19+
from dct.datasets import HandleDabDto
20+
from dct.datasets_dtos import StudyData, FilterData
21+
from dct.server_ctl_dtos import ProgressData, ProgressStatus
22+
from dct.functions_waveforms import full_current_waveform_from_currents, full_angle_waveform_from_angles
23+
from dct.datasets_dtos import CapacitorResults
24+
25+
logger = logging.getLogger(__name__)
26+
27+
class CapacitorSelection:
28+
"""Select suitable capacitors."""
29+
30+
c_df: pd.DataFrame
31+
_optimization_config_list: list[CapacitorOptimizationDto]
32+
33+
def __init__(self):
34+
self._optimization_config_list = []
35+
36+
@staticmethod
37+
def verify_optimization_parameter(toml_capacitor: TomlCapacitorSelection) -> tuple[bool, str]:
38+
"""
39+
Verify the parameters from toml file for the capacitor optimization.
40+
41+
Dummy method so far.
42+
43+
:param toml_capacitor: capacitor toml file to check
44+
:type toml_capacitor: TomlCapacitorSelection
45+
:return: is_consistent, issue_report
46+
:rtype: tuple[bool, str]
47+
"""
48+
if toml_capacitor:
49+
pass
50+
return True, ""
51+
52+
def initialize_capacitor_selection(self, toml_capacitor: TomlCapacitorSelection, study_data: StudyData, circuit_filter_data: FilterData) -> None:
53+
"""
54+
Initialize the capacitor selection.
55+
56+
:param toml_capacitor: capacitor data
57+
:type toml_capacitor: TomlCapacitorSelection
58+
:param study_data: capacitor study data
59+
:type study_data: StudyData
60+
:param circuit_filter_data: filtered circuit data
61+
:type circuit_filter_data: FilterData
62+
"""
63+
pecst.download_esr_csv_files()
64+
65+
# Create the io_config_list for all trials
66+
for circuit_trial_file in circuit_filter_data.filtered_list_files:
67+
circuit_filepath = os.path.join(circuit_filter_data.filtered_list_pathname, f"{circuit_trial_file}.pkl")
68+
# Check filename
69+
if os.path.isfile(circuit_filepath):
70+
# Read results from circuit optimization
71+
circuit_dto = HandleDabDto.load_from_file(circuit_filepath)
72+
73+
optimization_directory = os.path.join(study_data.optimization_directory, circuit_trial_file, study_data.study_name)
74+
75+
# figure out worst case working point for the capacitor per circuit design
76+
sorted_max_rms_angles, i_c1_max_rms_current_waveform = HandleDabDto.get_max_rms_waveform_capacitor(circuit_dto, plot=False)
77+
time = sorted_max_rms_angles / (2 * np.pi * circuit_dto.input_config.fs)
78+
v_max = np.max(circuit_dto.input_config.mesh_v1)
79+
80+
# generate capacitor requirements from circuit simulation data
81+
capacitor_requirements_dto = pecst.CapacitorRequirements(
82+
maximum_peak_to_peak_voltage_ripple=toml_capacitor.maximum_peak_to_peak_voltage_ripple,
83+
current_waveform_for_op_max_current=np.array([time, i_c1_max_rms_current_waveform]),
84+
v_dc_for_op_max_voltage=v_max,
85+
temperature_ambient=toml_capacitor.temperature_ambient,
86+
voltage_safety_margin_percentage=toml_capacitor.voltage_safety_margin_percentage,
87+
capacitor_type_list=[pecst.CapacitorType.FilmCapacitor],
88+
maximum_number_series_capacitors=toml_capacitor.maximum_number_series_capacitors,
89+
capacitor_tolerance_percent=pecst.CapacitanceTolerance.TenPercent,
90+
lifetime_h=toml_capacitor.lifetime_h,
91+
results_directory=optimization_directory
92+
)
93+
94+
# Initialize the statistical data
95+
stat_data_init: ProgressData = ProgressData(run_time=0, number_of_filtered_points=0,
96+
progress_status=ProgressStatus.Idle)
97+
98+
capacitor_dto = CapacitorOptimizationDto(
99+
circuit_filtered_point_filename=circuit_trial_file,
100+
progress_data=copy.deepcopy(stat_data_init),
101+
capacitor_optimization_dto=capacitor_requirements_dto)
102+
103+
self._optimization_config_list.append(capacitor_dto)
104+
else:
105+
logger.info(f"Wrong path or file {circuit_filepath} does not exists!")
106+
107+
@staticmethod
108+
def _start_optimization(circuit_filtered_point_file: str, act_cst_config: pecst.CapacitorRequirements, filter_data: FilterData,
109+
debug: Debug) -> int:
110+
# capacitor requirements
111+
_, c_db_df_list = pecst.select_capacitors(act_cst_config)
112+
113+
c_db_df = pd.concat(c_db_df_list)
114+
115+
if not os.path.exists(act_cst_config.results_directory):
116+
os.makedirs(act_cst_config.results_directory)
117+
c_db_df.to_csv(f"{act_cst_config.results_directory}/results.csv")
118+
119+
df_filtered = pecst.filter_df(c_db_df)
120+
if debug.general.is_debug:
121+
# reduce dataset to the given number from the debug configuration
122+
df_filtered = df_filtered.iloc[:debug.capacitor_1.number_working_point_max]
123+
# save Pareto front designs (reduced in case of active debugging)
124+
df_filtered.to_csv(f"{act_cst_config.results_directory}/results_filtered.csv")
125+
126+
# Load configuration
127+
circuit_dto = HandleDabDto.load_from_file(os.path.join(filter_data.filtered_list_pathname, f"{circuit_filtered_point_file}.pkl"))
128+
129+
# sweep through all current waveforms
130+
i_l1_sorted = np.transpose(circuit_dto.calc_currents.i_l_1_sorted, (1, 2, 3, 0))
131+
angles_rad_sorted = np.transpose(circuit_dto.calc_currents.angles_rad_sorted, (1, 2, 3, 0))
132+
133+
all_operation_point_ordering_codes_list = df_filtered["ordering code"].to_numpy()
134+
all_operation_point_volume_list = df_filtered["volume_total"].to_numpy()
135+
all_operation_point_area_list = df_filtered["area_total"].to_numpy()
136+
all_operation_point_n_series_list = df_filtered["in_series_needed"].to_numpy()
137+
all_operation_point_n_parallel_list = df_filtered["in_parallel_needed"].to_numpy()
138+
139+
# Overtake the filtered operation points
140+
number_of_filtered_points = len(all_operation_point_ordering_codes_list)
141+
142+
logger.info(f"Full-operating point simulation list: {all_operation_point_ordering_codes_list}")
143+
144+
# simulate all operating points
145+
for count, ordering_code in enumerate(tqdm.tqdm(all_operation_point_ordering_codes_list)):
146+
147+
volume_total = all_operation_point_volume_list[count]
148+
area_total = all_operation_point_area_list[count]
149+
df_geometry_re_simulation_number = df_filtered[df_filtered["ordering code"] == ordering_code]
150+
n_series = all_operation_point_n_series_list[count]
151+
n_parallel = all_operation_point_n_parallel_list[count]
152+
153+
logger.debug(f"ordering_code: \n"
154+
f" {df_geometry_re_simulation_number.head()}")
155+
156+
loss_total_array = np.full_like(circuit_dto.calc_modulation.phi, np.nan)
157+
158+
new_circuit_dto_directory = os.path.join(act_cst_config.results_directory, "01_circuit_dtos_incl_capacitor_1_loss")
159+
if not os.path.exists(new_circuit_dto_directory):
160+
os.makedirs(new_circuit_dto_directory)
161+
162+
if os.path.exists(os.path.join(new_circuit_dto_directory, f"{ordering_code}.pkl")):
163+
logger.info(f"Re-simulation of {circuit_dto.name} already exists. Skip.")
164+
else:
165+
for vec_vvp in np.ndindex(circuit_dto.calc_modulation.phi.shape):
166+
time, unique_indices = np.unique(full_angle_waveform_from_angles(
167+
angles_rad_sorted[vec_vvp]) / 2 / np.pi / circuit_dto.input_config.fs, return_index=True)
168+
current = full_current_waveform_from_currents(i_l1_sorted[vec_vvp])[unique_indices]
169+
170+
current_waveform = np.array([time, current])
171+
logger.debug(f"{current_waveform=}")
172+
logger.debug("All operating point simulation of:")
173+
logger.debug(f" * Circuit study: {filter_data.circuit_study_name}")
174+
logger.debug(f" * Circuit trial: {circuit_filtered_point_file}")
175+
logger.debug(f" * Inductor re-simulation trial: {ordering_code}")
176+
177+
[frequency_list, current_amplitude_list, _] = pecst.fft(current_waveform, plot='no', mode='time', title='fft input current')
178+
179+
loss_per_capacitor = pecst.power_loss_film_capacitor(ordering_code, frequency_list, current_amplitude_list,
180+
number_parallel_capacitors=n_parallel)
181+
182+
loss_total_array[vec_vvp] = loss_per_capacitor * n_series * n_parallel
183+
184+
capacitor_losses = CapacitorResults(
185+
p_combined_losses=loss_total_array,
186+
volume=volume_total,
187+
pcb_area=area_total,
188+
circuit_trial_file=circuit_filtered_point_file,
189+
capacitor_order_number=df_geometry_re_simulation_number,
190+
)
191+
192+
pickle_file = os.path.join(new_circuit_dto_directory, f"{ordering_code}.pkl")
193+
with open(pickle_file, 'wb') as output:
194+
pickle.dump(capacitor_losses, output, pickle.HIGHEST_PROTOCOL)
195+
196+
# returns the number of filtered results
197+
return number_of_filtered_points
198+
199+
def optimization_handler(self, filter_data: FilterData, debug: Debug) -> None:
200+
"""
201+
Control the multi simulation processes.
202+
203+
:param filter_data: Information about the filtered designs
204+
:type filter_data: dct.FilterData
205+
:param debug: True to use debug mode which stops earlier
206+
:type debug: bool
207+
"""
208+
number_cpus = cpu_count()
209+
210+
with Pool(processes=number_cpus) as pool:
211+
parameters = []
212+
for count, act_optimization_configuration in enumerate(self._optimization_config_list):
213+
if debug.general.is_debug:
214+
# in debug mode, stop when number of configuration parameters has reached the same as parallel cores are used
215+
if count == number_cpus:
216+
break
217+
218+
parameters.append((
219+
act_optimization_configuration.circuit_filtered_point_filename,
220+
act_optimization_configuration.capacitor_optimization_dto,
221+
filter_data,
222+
debug
223+
))
224+
225+
pool.starmap(func=CapacitorSelection._start_optimization, iterable=parameters)

dct/datasets.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,10 @@ def init_config(name: str, mesh_v1: np.ndarray, mesh_v2: np.ndarray, mesh_p: np.
117117
gecko_additional_params=gecko_additional_params,
118118
gecko_results=None,
119119
gecko_waveforms=None,
120+
capacitor_1_results=None,
121+
capacitor_2_results=None,
120122
inductor_results=None,
121-
stacked_transformer_results=None
123+
stacked_transformer_results=None,
122124
)
123125
return dab_dto
124126

@@ -405,6 +407,44 @@ def get_max_peak_waveform_inductor(dab_dto: d_dtos.CircuitDabDTO, plot: bool = F
405407

406408
return sorted_max_angles, i_l1_max_current_waveform
407409

410+
@staticmethod
411+
def get_max_rms_waveform_capacitor(dab_dto: d_dtos.CircuitDabDTO, plot: bool = False) -> tuple[np.ndarray, np.ndarray]:
412+
"""
413+
Get the capacitor waveform with the maximum RMS current out of the three-dimensional simulation array (v_1, v_2, P).
414+
415+
:param dab_dto: DAB data transfer object (DTO)
416+
:type dab_dto: d_dtos.CircuitDabDTO
417+
:param plot: True to plot the results, mostly for understanding and debugging
418+
:type plot: bool
419+
:return: sorted_max_rms_angles, i_hf1_max_rms_current_waveform, All as a numpy array.
420+
:rtype: List[np.ndarray]
421+
"""
422+
i_c1_sorted = np.transpose(dab_dto.calc_currents.i_hf_1_sorted, (1, 2, 3, 0))
423+
i_c1_rms = dab_dto.calc_currents.i_hf_1_rms
424+
angles_rad_sorted = np.transpose(dab_dto.calc_currents.angles_rad_sorted, (1, 2, 3, 0))
425+
426+
max_index = (0, 0, 0)
427+
for vec_vvp in np.ndindex(dab_dto.calc_modulation.phi.shape):
428+
max_index = vec_vvp if np.max(i_c1_rms[vec_vvp]) > np.max(i_c1_rms[max_index]) else max_index # type: ignore
429+
if plot:
430+
plt.plot(d_waveforms.full_angle_waveform_from_angles(angles_rad_sorted[vec_vvp]),
431+
d_waveforms.full_current_waveform_from_currents(i_c1_sorted[vec_vvp]), color='gray')
432+
433+
i_c1_max_rms_current_waveform = i_c1_sorted[max_index]
434+
435+
sorted_max_rms_angles, unique_indices = np.unique(d_waveforms.full_angle_waveform_from_angles(angles_rad_sorted[max_index]), return_index=True)
436+
i_c1_max_rms_current_waveform = d_waveforms.full_current_waveform_from_currents(i_c1_max_rms_current_waveform)[unique_indices]
437+
438+
if plot:
439+
plt.plot(sorted_max_rms_angles, i_c1_max_rms_current_waveform, color='red', label='peak current full waveform')
440+
plt.grid()
441+
plt.xlabel('Angle in rad')
442+
plt.ylabel('Current in A')
443+
plt.legend()
444+
plt.show()
445+
446+
return sorted_max_rms_angles, i_c1_max_rms_current_waveform
447+
408448
@staticmethod
409449
def export_transformer_target_parameters_dto(dab_dto: d_dtos.CircuitDabDTO) -> d_dtos.TransformerTargetParameters:
410450
"""

dct/datasets_dtos.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,23 @@ def __init__(self, **kwargs):
171171
if k in names:
172172
setattr(self, k, v)
173173

174+
175+
@dataclasses.dataclass(init=False)
176+
class CapacitorResults:
177+
"""DTO contains the inductor losses."""
178+
179+
loss_total_array: np.ndarray
180+
volume_total: float
181+
area_total: float
182+
circuit_trial_file: str
183+
capacitor_order_number: int
184+
185+
def __init__(self, **kwargs):
186+
names = set([f.name for f in dataclasses.fields(self)])
187+
for k, v in kwargs.items():
188+
if k in names:
189+
setattr(self, k, v)
190+
174191
@dataclasses.dataclass(init=False)
175192
class InductorResults:
176193
"""DTO contains the inductor losses."""
@@ -292,6 +309,8 @@ class CircuitDabDTO:
292309
gecko_additional_params: GeckoAdditionalParameters
293310
gecko_results: GeckoResults | None
294311
gecko_waveforms: GeckoWaveforms | None
312+
capacitor_1_results: CapacitorResults | None
313+
capacitor_2_results: CapacitorResults | None
295314
inductor_results: InductorResults | None
296315
stacked_transformer_results: StackedTransformerResults | None
297316

0 commit comments

Comments
 (0)