.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/plot_generator_operating_window.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_plot_generator_operating_window.py: Automatic Constraint Boundary Exploration. ========================================== Demonstrates automatic exploration of the generator operating window boundary using adaptive refinement. This example shows how to use the constraint boundary exploration utilities to automatically find the operating limits of a high-voltage generator system without knowing the analytical constraint formula. .. GENERATED FROM PYTHON SOURCE LINES 14-18 Setup: Import Required Modules ------------------------------- Import OpenMDAO, the generator system, and the boundary exploration utilities. .. GENERATED FROM PYTHON SOURCE LINES 18-30 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np import openmdao.api as om from paroto.systems.generator import HighVoltageGeneratorSystem from paroto.utils.constraint_explorer import ( explore_constraint_boundary_2d, trace_constraint_boundary_2d, ) from paroto.utils.plotting import plot_constraint_boundary, plot_operating_window .. GENERATED FROM PYTHON SOURCE LINES 31-35 Define Generator Parameters ---------------------------- Set the physical parameters for the high-voltage generator system. .. GENERATED FROM PYTHON SOURCE LINES 35-47 .. code-block:: Python # Generator operating parameters Z = 50.0 # Design impedance (Ohm) t_pulse = 20e-9 # Pulse duration (s) = 20 ns P_max = 1000.0 # Maximum power (W) print("Generator Parameters:") print(f" Design Impedance (Z): {Z} Ω") print(f" Pulse Duration: {t_pulse * 1e9:.0f} ns") print(f" Maximum Power: {P_max:.0f} W") print() .. rst-class:: sphx-glr-script-out .. code-block:: none Generator Parameters: Design Impedance (Z): 50.0 Ω Pulse Duration: 20 ns Maximum Power: 1000 W .. GENERATED FROM PYTHON SOURCE LINES 48-53 Create OpenMDAO Problem with Generator System ---------------------------------------------- Set up a minimal OpenMDAO problem containing only the high-voltage generator system. This is the system we'll explore. .. GENERATED FROM PYTHON SOURCE LINES 53-79 .. code-block:: Python prob = om.Problem() # Add the high-voltage generator system prob.model.add_subsystem( "generator", HighVoltageGeneratorSystem( design_impedance=Z, pulse_duration=t_pulse, max_power=P_max, ), promotes=["*"], ) # Setup the problem prob.setup() # Set some nominal values (these will be overridden during exploration) prob.set_val("pulse_frequency", 50e3) # 50 kHz prob.set_val("hv_voltage", 10e3) # 10 kV print("OpenMDAO Problem Setup Complete") print(" Inputs: pulse_frequency, hv_voltage") print(" Outputs: operating_window_power, hv_operating_window_satisfied") print() .. rst-class:: sphx-glr-script-out .. code-block:: none OpenMDAO Problem Setup Complete Inputs: pulse_frequency, hv_voltage Outputs: operating_window_power, hv_operating_window_satisfied .. GENERATED FROM PYTHON SOURCE LINES 80-85 Coarse Exploration: Initial Grid Sampling ------------------------------------------ Perform a coarse exploration of the parameter space to identify the general shape of the operating window boundary. .. GENERATED FROM PYTHON SOURCE LINES 85-109 .. code-block:: Python print("=" * 70) print("COARSE EXPLORATION (10 × 10 grid)") print("=" * 70) # Define parameter ranges to explore freq_range = (1e3, 300e3) # 1 kHz to 300 kHz voltage_range = (1e3, 20e3) # 1 kV to 20 kV # Explore with coarse grid (no refinement) result_coarse = explore_constraint_boundary_2d( prob, param1="pulse_frequency", param2="hv_voltage", constraint_name="hv_operating_window_satisfied", param1_range=freq_range, param2_range=voltage_range, initial_resolution=(10, 10), refinement_levels=0, constraint_tolerance=0.2, ) print() .. rst-class:: sphx-glr-script-out .. code-block:: none ====================================================================== COARSE EXPLORATION (10 × 10 grid) ====================================================================== Initial sampling: 10 × 10 = 100 points Completed: 100 total evaluations Found 5 points near boundary Feasible points: 29 / 100 (29.0%) .. GENERATED FROM PYTHON SOURCE LINES 110-115 Fine Exploration: Higher Resolution Grid ----------------------------------------- Perform a finer exploration with higher grid resolution to get better boundary definition. .. GENERATED FROM PYTHON SOURCE LINES 115-134 .. code-block:: Python print("=" * 70) print("FINE EXPLORATION (30 × 30 grid)") print("=" * 70) result_fine = explore_constraint_boundary_2d( prob, param1="pulse_frequency", param2="hv_voltage", constraint_name="hv_operating_window_satisfied", param1_range=freq_range, param2_range=voltage_range, initial_resolution=(30, 30), refinement_levels=0, constraint_tolerance=0.1, ) print() .. rst-class:: sphx-glr-script-out .. code-block:: none ====================================================================== FINE EXPLORATION (30 × 30 grid) ====================================================================== Initial sampling: 30 × 30 = 900 points Completed: 900 total evaluations Found 23 points near boundary Feasible points: 222 / 900 (24.7%) .. GENERATED FROM PYTHON SOURCE LINES 135-141 Adaptive Refinement: Smart Exploration --------------------------------------- Use adaptive mesh refinement to focus evaluations near the constraint boundary. This starts with a coarse grid and automatically refines cells that contain the boundary. .. GENERATED FROM PYTHON SOURCE LINES 141-161 .. code-block:: Python print("=" * 70) print("ADAPTIVE REFINEMENT (15 × 15 grid + 2 refinement levels)") print("=" * 70) result_adaptive = explore_constraint_boundary_2d( prob, param1="pulse_frequency", param2="hv_voltage", constraint_name="hv_operating_window_satisfied", param1_range=freq_range, param2_range=voltage_range, initial_resolution=(15, 15), refinement_levels=2, refinement_factor=2, constraint_tolerance=0.1, ) print() .. rst-class:: sphx-glr-script-out .. code-block:: none ====================================================================== ADAPTIVE REFINEMENT (15 × 15 grid + 2 refinement levels) ====================================================================== Initial sampling: 15 × 15 = 225 points Starting adaptive refinement: 2 level(s) Refinement level 1... Found 26 cells to refine Added 262 new evaluation points Refinement level 2... Found 79 cells to refine Added 792 new evaluation points Completed: 1279 total evaluations Found 418 points near boundary Feasible points: 581 / 1279 (45.4%) .. GENERATED FROM PYTHON SOURCE LINES 162-167 Boundary Tracing: Efficient Curve Following -------------------------------------------- Trace the boundary curve using gradient-based continuation. This is most efficient for smooth boundaries, requiring far fewer evaluations than grid sampling. .. GENERATED FROM PYTHON SOURCE LINES 167-189 .. code-block:: Python print("=" * 70) print("BOUNDARY TRACING (gradient-based continuation)") print("=" * 70) # Start from a point on the boundary start_point = (10e3, 10000.0) # (10 kHz, 10 kV) - should be near boundary # Trace the boundary curve boundary_traced = trace_constraint_boundary_2d( prob, param1="pulse_frequency", param2="hv_voltage", constraint_name="hv_operating_window_satisfied", start_point=start_point, step_size=5000.0, # Absolute step size in parameter space max_points=50, constraint_tolerance=1e-2, ) print() .. rst-class:: sphx-glr-script-out .. code-block:: none ====================================================================== BOUNDARY TRACING (gradient-based continuation) ====================================================================== Boundary tracing: Starting from (10000.0, 10000.0) Step size: 5000.0, Max points: 50 Projecting start point onto boundary (initial error: 1.500000) Starting from: (1.224e+04, 1.426e+04) Traced 50 boundary points .. GENERATED FROM PYTHON SOURCE LINES 190-198 Visualization: Compare All Exploration Methods ----------------------------------------------- Create visualizations comparing: 1. Coarse exploration 2. Fine exploration 3. Adaptive refinement 4. Boundary tracing .. GENERATED FROM PYTHON SOURCE LINES 198-310 .. code-block:: Python # Helper function to convert units for display def convert_to_display_units(result_data): """Convert Hz to kHz and V to kV for better readability.""" display_data = result_data.copy() display_data["param1_values"] = result_data["param1_values"] / 1e3 # Hz to kHz display_data["param2_values"] = result_data["param2_values"] / 1e3 # V to kV if "boundary_points" in result_data and result_data["boundary_points"]: display_data["boundary_points"] = [ (p1 / 1e3, p2 / 1e3) for p1, p2 in result_data["boundary_points"] ] return display_data fig, axes = plt.subplots(2, 2, figsize=(14, 12)) axes = axes.flatten() # Plot 1: Coarse Exploration ax1 = axes[0] result_coarse_display = convert_to_display_units(result_coarse) plot_constraint_boundary( ax1, result_coarse_display, param1_name="Frequency (kHz)", param2_name="HV Voltage (kV)", show_grid_points=True, shade_regions=True, show_colorbar=False, ) ax1.set_title( f"Coarse Grid\n{result_coarse['num_evaluations']} evaluations", fontsize=12, fontweight="bold" ) ax1.legend(loc="best", framealpha=0.9, facecolor="white", edgecolor="gray") # Plot 2: Fine Exploration ax2 = axes[1] result_fine_display = convert_to_display_units(result_fine) plot_constraint_boundary( ax2, result_fine_display, param1_name="Frequency (kHz)", param2_name="HV Voltage (kV)", show_grid_points=True, shade_regions=True, show_colorbar=False, ) ax2.set_title( f"Fine Grid\n{result_fine['num_evaluations']} evaluations", fontsize=12, fontweight="bold" ) ax2.legend(loc="best", framealpha=0.9, facecolor="white", edgecolor="gray") # Plot 3: Adaptive Refinement ax3 = axes[2] result_adaptive_display = convert_to_display_units(result_adaptive) plot_constraint_boundary( ax3, result_adaptive_display, param1_name="Frequency (kHz)", param2_name="HV Voltage (kV)", show_grid_points=True, shade_regions=True, show_colorbar=False, ) ax3.set_title( f"Adaptive Refinement\n{result_adaptive['num_evaluations']} evaluations", fontsize=12, fontweight="bold", ) ax3.legend(loc="best", framealpha=0.9, facecolor="white", edgecolor="gray") # Plot 4: Boundary Tracing ax4 = axes[3] # Plot analytical boundary for comparison plot_operating_window( ax4, Z=Z, t_pulse=t_pulse, P_max=P_max, f_range=freq_range, V_range=voltage_range, show_annotations=False, ) # Convert to kHz and kV for display ax4.set_xlabel("Frequency (kHz)", fontsize=11) ax4.set_ylabel("HV Voltage (kV)", fontsize=11) # Update x and y tick labels xticks = ax4.get_xticks() ax4.set_xticklabels([f"{x / 1e3:.0f}" for x in xticks]) yticks = ax4.get_yticks() ax4.set_yticklabels([f"{y / 1e3:.0f}" for y in yticks]) # Overlay traced boundary ax4.plot( boundary_traced[:, 0] / 1e3, # Convert to kHz boundary_traced[:, 1] / 1e3, # Convert to kV "bo-", linewidth=2, markersize=6, label=f"Traced Boundary ({len(boundary_traced)} points)", zorder=10, ) ax4.set_title( f"Boundary Tracing\n~{len(boundary_traced) * 11} evaluations (incl. gradients)", fontsize=12, fontweight="bold", ) ax4.legend(loc="upper right", framealpha=0.9, facecolor="white", edgecolor="gray") plt.tight_layout() plt.savefig("generator_boundary_exploration.png", dpi=150, bbox_inches="tight") print("Saved plot: generator_boundary_exploration.png") .. image-sg:: /auto_examples/images/sphx_glr_plot_generator_operating_window_001.png :alt: Coarse Grid 100 evaluations, Fine Grid 900 evaluations, Adaptive Refinement 1279 evaluations, Boundary Tracing ~550 evaluations (incl. gradients) :srcset: /auto_examples/images/sphx_glr_plot_generator_operating_window_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none /home/runner/work/paroto/paroto/examples/plot_generator_operating_window.py:286: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. ax4.set_xticklabels([f"{x / 1e3:.0f}" for x in xticks]) /home/runner/work/paroto/paroto/examples/plot_generator_operating_window.py:288: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. ax4.set_yticklabels([f"{y / 1e3:.0f}" for y in yticks]) Saved plot: generator_boundary_exploration.png .. GENERATED FROM PYTHON SOURCE LINES 311-316 Overlay: Compare Explored vs Analytical Boundary ------------------------------------------------- Create a detailed comparison showing how well the exploration captures the analytical boundary. .. GENERATED FROM PYTHON SOURCE LINES 316-374 .. code-block:: Python fig, ax = plt.subplots(figsize=(10, 7)) # First plot the analytical boundary plot_operating_window( ax, Z=Z, t_pulse=t_pulse, P_max=P_max, f_range=freq_range, V_range=voltage_range, show_annotations=True, ) # Convert to kHz and kV for display ax.set_xlabel("Frequency (kHz)", fontsize=12) ax.set_ylabel("HV Voltage (kV)", fontsize=12) # Update x and y tick labels xticks = ax.get_xticks() ax.set_xticklabels([f"{x / 1e3:.0f}" for x in xticks]) yticks = ax.get_yticks() ax.set_yticklabels([f"{y / 1e3:.0f}" for y in yticks]) # Overlay the fine exploration grid points scatter = ax.scatter( result_fine["param1_values"] / 1e3, # Convert to kHz result_fine["param2_values"] / 1e3, # Convert to kV c=result_fine["constraint_values"], cmap="RdYlGn", s=30, alpha=0.7, edgecolors="black", linewidth=0.5, vmin=0, vmax=2.0, zorder=10, ) # Add colorbar cbar = plt.colorbar(scatter, ax=ax) cbar.set_label("Constraint Value\n(≥1.0 = Feasible)", rotation=270, labelpad=20) cbar.ax.axhline(1.0, color="black", linestyle="--", linewidth=1.5) ax.set_title("Constraint Boundary: Exploration vs Analytical", fontsize=14, weight="bold") # Update legend with white background and transparency legend = ax.get_legend() if legend: legend.set_frame_on(True) legend.get_frame().set_facecolor("white") legend.get_frame().set_alpha(0.9) legend.get_frame().set_edgecolor("gray") plt.tight_layout() plt.savefig("generator_boundary_comparison.png", dpi=150, bbox_inches="tight") print("Saved plot: generator_boundary_comparison.png") plt.show() .. image-sg:: /auto_examples/images/sphx_glr_plot_generator_operating_window_002.png :alt: Constraint Boundary: Exploration vs Analytical :srcset: /auto_examples/images/sphx_glr_plot_generator_operating_window_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none /home/runner/work/paroto/paroto/examples/plot_generator_operating_window.py:335: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. ax.set_xticklabels([f"{x / 1e3:.0f}" for x in xticks]) /home/runner/work/paroto/paroto/examples/plot_generator_operating_window.py:337: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. ax.set_yticklabels([f"{y / 1e3:.0f}" for y in yticks]) Saved plot: generator_boundary_comparison.png .. GENERATED FROM PYTHON SOURCE LINES 375-379 Summary and Analysis -------------------- Compare the accuracy and efficiency of different exploration strategies. .. GENERATED FROM PYTHON SOURCE LINES 379-421 .. code-block:: Python print() print("=" * 70) print("SUMMARY") print("=" * 70) print() print("Exploration Efficiency:") print(f" Coarse Grid: {result_coarse['num_evaluations']} evaluations") print(f" Fine Grid: {result_fine['num_evaluations']} evaluations") print(f" Adaptive Refinement: {result_adaptive['num_evaluations']} evaluations") print(f" Boundary Tracing: ~{len(boundary_traced) * 11} evaluations (incl. gradients)") print() print("Feasible Region Coverage:") coarse_feasible_pct = ( 100 * np.sum(result_coarse["feasible_mask"]) / result_coarse["num_evaluations"] ) fine_feasible_pct = 100 * np.sum(result_fine["feasible_mask"]) / result_fine["num_evaluations"] adaptive_feasible_pct = ( 100 * np.sum(result_adaptive["feasible_mask"]) / result_adaptive["num_evaluations"] ) print(f" Coarse Grid: {coarse_feasible_pct:.1f}% feasible") print(f" Fine Grid: {fine_feasible_pct:.1f}% feasible") print(f" Adaptive Refinement: {adaptive_feasible_pct:.1f}% feasible") print() print("Boundary Detection:") print(f" Coarse Grid: {len(result_coarse['boundary_points'])} points near boundary") print(f" Fine Grid: {len(result_fine['boundary_points'])} points near boundary") print(f" Adaptive Refinement: {len(result_adaptive['boundary_points'])} points near boundary") print(f" Boundary Tracing: {len(boundary_traced)} points along boundary") print() print("Key Insights:") print(" - Coarse grid (~100 pts): Quick overview of operating window") print(" - Fine grid (~900 pts): Better boundary definition, but expensive") print(" - Adaptive refinement: Smart compromise - refines only near boundary") print(" - Boundary tracing: Most efficient for smooth boundaries") print(" - All methods work for ANY constraint, not just analytical ones") print() print("When to Use Each Method:") print(" - Coarse/Fine Grid: First exploration, multiple constraints") print(" - Adaptive Refinement: Best overall - automatic + efficient") print(" - Boundary Tracing: Known smooth boundary, need accurate curve") print() .. rst-class:: sphx-glr-script-out .. code-block:: none ====================================================================== SUMMARY ====================================================================== Exploration Efficiency: Coarse Grid: 100 evaluations Fine Grid: 900 evaluations Adaptive Refinement: 1279 evaluations Boundary Tracing: ~550 evaluations (incl. gradients) Feasible Region Coverage: Coarse Grid: 29.0% feasible Fine Grid: 24.7% feasible Adaptive Refinement: 45.4% feasible Boundary Detection: Coarse Grid: 5 points near boundary Fine Grid: 23 points near boundary Adaptive Refinement: 418 points near boundary Boundary Tracing: 50 points along boundary Key Insights: - Coarse grid (~100 pts): Quick overview of operating window - Fine grid (~900 pts): Better boundary definition, but expensive - Adaptive refinement: Smart compromise - refines only near boundary - Boundary tracing: Most efficient for smooth boundaries - All methods work for ANY constraint, not just analytical ones When to Use Each Method: - Coarse/Fine Grid: First exploration, multiple constraints - Adaptive Refinement: Best overall - automatic + efficient - Boundary Tracing: Known smooth boundary, need accurate curve .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 2.239 seconds) .. _sphx_glr_download_auto_examples_plot_generator_operating_window.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_generator_operating_window.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_generator_operating_window.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_generator_operating_window.zip `