.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/VedEf_optim/3_analysis_dashboard.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_VedEf_optim_3_analysis_dashboard.py: Interactive Dash Dashboard for VedEf Operating Window Exploration. This dashboard provides real-time interactive exploration of the 5D parameter space with constraint visualization and model connectivity graph. **Usage**:: python dashboard_operating_window.py Then open browser to http://127.0.0.1:8050/ **Features**: - Left Panel: 5 parameter sliders with real-time model evaluation - Top Center: Interactive Cytoscape graph of model connectivity - Bottom Center: Temporal plots on node click - Right Panel: Operating window slices with current point overlay .. GENERATED FROM PYTHON SOURCE LINES 19-438 .. code-block:: Python import importlib.util from pathlib import Path import dash_cytoscape as cyto import numpy as np import plotly.graph_objs as go from dash import Dash, Input, Output, dcc, html from paroto.viz import get_cytoscape_stylesheet, model_graph_to_cytoscape # Import setup_vedef_problem from the numbered file spec = importlib.util.spec_from_file_location( "problem_module", Path(__file__).parent / "1_setup_vedef_problem.py" ) problem_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(problem_module) setup_vedef_problem = problem_module.setup_vedef_problem # Initialize Dash app app = Dash(__name__) app.title = "VedEf Operating Window Explorer" # Define parameter ranges param_ranges = { "V": (10e3, 60e3, "V", "kV", 1e-3), # (min, max, unit, display_unit, scale) "e": (0.005, 0.025, "m", "mm", 1e3), "d_t": (0.015, 0.035, "m", "mm", 1e3), "f": (10e3, 200e3, "Hz", "kHz", 1e-3), "E_p": (1e-3, 30e-3, "J", "mJ", 1e3), } # Create Cytoscape elements parameters = ["V", "e", "d_t", "f", "E_p"] models = [ "Initial Temperature", "Breakdown Voltage", "Arc Coverage", "Arc Density", "HV Generator", "Flow Model", ] constraints = [ "Breakdown", "HV Window", "Coverage", "Density", "Power", ] cyto_elements = model_graph_to_cytoscape(parameters, models, constraints) # App layout app.layout = html.Div( [ html.H1( "VedEf Operating Window Explorer", style={"textAlign": "center", "marginBottom": 20} ), html.Div( [ # Left Panel: Parameter Controls html.Div( [ html.H3("Parameters", style={"textAlign": "center"}), html.Hr(), # V slider html.Label("High Voltage V (kV)", style={"fontWeight": "bold"}), dcc.Slider( id="slider-V", min=param_ranges["V"][0], max=param_ranges["V"][1], value=20e3, marks={int(v): f"{v / 1e3:.0f}" for v in np.linspace(10e3, 60e3, 6)}, tooltip={"placement": "bottom", "always_visible": True}, ), html.Br(), # e slider html.Label("Gap Distance e (mm)", style={"fontWeight": "bold"}), dcc.Slider( id="slider-e", min=param_ranges["e"][0], max=param_ranges["e"][1], value=0.01, marks={v: f"{v * 1e3:.0f}" for v in np.linspace(0.005, 0.025, 5)}, tooltip={"placement": "bottom", "always_visible": True}, ), html.Br(), # d_t slider html.Label("Torch Diameter d_t (mm)", style={"fontWeight": "bold"}), dcc.Slider( id="slider-d_t", min=param_ranges["d_t"][0], max=param_ranges["d_t"][1], value=0.02, marks={v: f"{v * 1e3:.0f}" for v in np.linspace(0.015, 0.035, 5)}, tooltip={"placement": "bottom", "always_visible": True}, ), html.Br(), # f slider html.Label("Pulse Frequency f (kHz)", style={"fontWeight": "bold"}), dcc.Slider( id="slider-f", min=param_ranges["f"][0], max=param_ranges["f"][1], value=50e3, marks={int(v): f"{v / 1e3:.0f}" for v in np.logspace(4, 5.3, 6)}, tooltip={"placement": "bottom", "always_visible": True}, ), html.Br(), # E_p slider html.Label("Energy per Pulse E_p (mJ)", style={"fontWeight": "bold"}), dcc.Slider( id="slider-E_p", min=param_ranges["E_p"][0], max=param_ranges["E_p"][1], value=10e-3, marks={v: f"{v * 1e3:.0f}" for v in np.linspace(1e-3, 30e-3, 6)}, tooltip={"placement": "bottom", "always_visible": True}, ), html.Br(), html.Hr(), html.H4("Constraint Status", style={"textAlign": "center"}), html.Div(id="constraint-status", style={"fontSize": 12}), ], style={ "width": "20%", "display": "inline-block", "verticalAlign": "top", "padding": 20, "backgroundColor": "#f8f9fa", }, ), # Center Panel: Model Graph and Plots html.Div( [ # Cytoscape Graph html.H3("Model Connectivity", style={"textAlign": "center"}), cyto.Cytoscape( id="cytoscape-graph", elements=cyto_elements, style={"width": "100%", "height": "400px", "border": "1px solid #ddd"}, stylesheet=get_cytoscape_stylesheet(), layout={"name": "breadthfirst", "directed": True, "spacingFactor": 1.5}, ), html.Hr(), # Temporal Plot html.H3("Temporal Behavior", style={"textAlign": "center"}), html.Div( id="selected-node-info", style={"textAlign": "center", "fontSize": 14} ), dcc.Graph(id="temporal-plot", style={"height": "400px"}), ], style={ "width": "50%", "display": "inline-block", "verticalAlign": "top", "padding": 20, }, ), # Right Panel: Operating Window Slice html.Div( [ html.H3("Operating Window", style={"textAlign": "center"}), html.Label("Select 2D Slice:", style={"fontWeight": "bold"}), dcc.Dropdown( id="slice-selector", options=[ {"label": "V vs e", "value": "V-e"}, {"label": "V vs f", "value": "V-f"}, {"label": "f vs E_p", "value": "f-E_p"}, {"label": "e vs d_t", "value": "e-d_t"}, ], value="V-e", ), dcc.Graph(id="operating-window-slice", style={"height": "500px"}), html.Div( id="feasibility-indicator", style={ "textAlign": "center", "fontSize": 16, "fontWeight": "bold", "marginTop": 10, }, ), ], style={ "width": "28%", "display": "inline-block", "verticalAlign": "top", "padding": 20, }, ), ] ), # Hidden div to store current evaluation results dcc.Store(id="current-results"), ] ) @app.callback( [ Output("current-results", "data"), Output("constraint-status", "children"), Output("feasibility-indicator", "children"), ], [ Input("slider-V", "value"), Input("slider-e", "value"), Input("slider-d_t", "value"), Input("slider-f", "value"), Input("slider-E_p", "value"), ], ) def evaluate_model(V, e, d_t, f, E_p): """Evaluate OpenMDAO model at current parameter values.""" try: # Create and setup problem prob = setup_vedef_problem(target_power=25000.0, max_energy_density=1e9, as_subsystem=False) # Set parameters (using ARC002 naming convention) prob.set_val("G_UMAX_OUT", V, units="V") prob.set_val("G_e", e, units="m") prob.set_val("TP_D_OUT", d_t, units="m") prob.set_val("G_F", f, units="Hz") prob.set_val("G_Ep", E_p, units="J") # Set fixed parameters prob.set_val("pulse_duration", 1e-6, units="s") prob.set_val("TP_QM", 160.0 / 86400.0, units="kg/s") prob.set_val("ThermalDiameterModel.gas_density", 0.717, units="kg/m**3") prob.set_val("ThermalDiameterModel.gas_heat_capacity", 2200.0, units="J/(kg*K)") prob.set_val("ThermalDiameterModel.thermal_conductivity", 0.08, units="W/(m*K)") prob.set_val("InitialTemperatureModel.gas_density", 0.717, units="kg/m**3") prob.set_val("BreakdownModel.gas_properties_pressure", 101325.0, units="Pa") # Run model prob.run_model() # Extract results results = { "T_0": float(prob.get_val("T_0", units="K")[0]), "T_max": float(prob.get_val("T_max", units="K")[0]), "breakdown_margin": float(prob.get_val("breakdown_margin", units="V")[0]), "coverage_margin": float(prob.get_val("coverage_margin")[0]), "density_margin": float(prob.get_val("density_margin", units="J/m**3")[0]), "power_error": float(prob.get_val("PowerConstraint.power_error")[0]), "G_PW_OUT": float(prob.get_val("G_PW_OUT", units="W")[0]), } # Check constraints breakdown_ok = results["breakdown_margin"] > 0 coverage_ok = results["coverage_margin"] > 0 density_ok = results["density_margin"] > 0 power_ok = results["power_error"] < 0.1 all_ok = breakdown_ok and coverage_ok and density_ok and power_ok # Create constraint status display status_lines = [ html.Div( [ html.Span( "✓ " if breakdown_ok else "✗ ", style={"color": "green" if breakdown_ok else "red"}, ), f"Breakdown: {results['breakdown_margin'] / 1e3:.1f} kV", ] ), html.Div( [ html.Span( "✓ " if coverage_ok else "✗ ", style={"color": "green" if coverage_ok else "red"}, ), f"Coverage: {results['coverage_margin']:.3f}", ] ), html.Div( [ html.Span( "✓ " if density_ok else "✗ ", style={"color": "green" if density_ok else "red"}, ), f"Density: {results['density_margin']:.2e} J/m³", ] ), html.Div( [ html.Span( "✓ " if power_ok else "✗ ", style={"color": "green" if power_ok else "red"} ), f"Power: {results['G_PW_OUT'] / 1e3:.1f} kW " f"(err: {results['power_error'] * 100:.1f}%)", ] ), ] # Feasibility indicator if all_ok: feas_indicator = html.Div("✓ FEASIBLE", style={"color": "green"}) else: feas_indicator = html.Div("✗ INFEASIBLE", style={"color": "red"}) return results, status_lines, feas_indicator except Exception as e: return ( {}, html.Div(f"Error: {str(e)}", style={"color": "red"}), html.Div("ERROR", style={"color": "red"}), ) @app.callback( [Output("temporal-plot", "figure"), Output("selected-node-info", "children")], [Input("cytoscape-graph", "tapNodeData"), Input("current-results", "data")], ) def update_temporal_plot(node_data, results): """Update temporal plot based on selected node.""" if not node_data or not results: return go.Figure(), "Click a model node to see temporal behavior" node_label = node_data.get("label", "") if "Initial Temperature" in node_label: # Plot T(t) over time t_off = 1.0 / 50e3 # Approximate off-time t = np.linspace(0, t_off * 5, 200) T_amb = 300.0 delta_T = results["T_max"] - T_amb tau = 20e-6 # Approximate relaxation time T = T_amb + delta_T * np.exp(-t / tau) fig = go.Figure() fig.add_trace(go.Scatter(x=t * 1e6, y=T, mode="lines", name="Temperature")) fig.update_layout( title="Temperature Evolution Over Time", xaxis_title="Time (μs)", yaxis_title="Temperature (K)", hovermode="x unified", ) info = f"Showing: {node_label} temporal behavior" else: # Default placeholder fig = go.Figure() fig.add_annotation( text="Temporal plot for this model
coming soon", xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False, font=dict(size=16), ) info = f"Selected: {node_label}" return fig, info @app.callback( Output("operating-window-slice", "figure"), [ Input("slice-selector", "value"), Input("slider-V", "value"), Input("slider-e", "value"), Input("slider-d_t", "value"), Input("slider-f", "value"), Input("slider-E_p", "value"), ], ) def update_operating_window(slice_type, V, e, d_t, f, E_p): """Update operating window slice plot.""" # Parse slice type param_x, param_y = slice_type.split("-") # Current point current_vals = {"V": V, "e": e, "d_t": d_t, "f": f, "E_p": E_p} fig = go.Figure() # Add current point marker fig.add_trace( go.Scatter( x=[current_vals[param_x]], y=[current_vals[param_y]], mode="markers", marker=dict(size=15, color="red", symbol="star"), name="Current Point", ) ) # Set axis labels x_label = f"{param_x} ({param_ranges[param_x][3]})" if param_x in param_ranges else param_x y_label = f"{param_y} ({param_ranges[param_y][3]})" if param_y in param_ranges else param_y fig.update_layout( title=f"Operating Window: {param_x} vs {param_y}", xaxis_title=x_label, yaxis_title=y_label, hovermode="closest", showlegend=True, ) return fig if __name__ == "__main__": print("\n" + "=" * 70) print(" VedEf Operating Window Dashboard") print("=" * 70) print("\nStarting Dash server...") print("Open browser to: http://127.0.0.1:8050/") print("\nPress Ctrl+C to stop") print("=" * 70 + "\n") app.run_server(debug=True, host="127.0.0.1", port=8050) .. _sphx_glr_download_auto_examples_VedEf_optim_3_analysis_dashboard.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: 3_analysis_dashboard.ipynb <3_analysis_dashboard.ipynb>` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: 3_analysis_dashboard.py <3_analysis_dashboard.py>` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: 3_analysis_dashboard.zip <3_analysis_dashboard.zip>`