import sys import io from contextlib import redirect_stdout from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QTextEdit, QPushButton, QGroupBox, QLabel, QComboBox, QSpinBox, QDoubleSpinBox, QLineEdit, QFormLayout, QSplitter, QMessageBox) from PyQt5.QtCore import Qt from PyQt5.QtGui import QFont, QTextCursor # Import the original script components import sympy as sp from constructing_gradient_fields_case_1_and_2_optimized_final import ( GradientFieldFactory, NumericalExamples, ConstantComponentField, LinearComponentField ) class GradientFieldApp(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle('Gradient Field Analyzer') self.setGeometry(100, 100, 1200, 800) # Central widget and main layout central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # Title title_label = QLabel('Gradient Field Analyzer') title_label.setAlignment(Qt.AlignCenter) title_font = QFont() title_font.setPointSize(16) title_font.setBold(True) title_label.setFont(title_font) main_layout.addWidget(title_label) # Create tab widget self.tabs = QTabWidget() main_layout.addWidget(self.tabs) # Create tabs self.create_analysis_tab() self.create_case1_tab() self.create_case2_tab() self.create_numerical_examples_tab() # Status bar self.statusBar().showMessage('Ready') def create_analysis_tab(self): """Tab for running the complete analysis""" tab = QWidget() layout = QVBoxLayout(tab) # Description desc_label = QLabel( "Run the complete gradient field analysis from the case study. " "This will analyze both Case 1 (one constant component) and " "Case 2 (both linear components)." ) desc_label.setWordWrap(True) layout.addWidget(desc_label) # Run analysis button run_btn = QPushButton('Run Complete Analysis') run_btn.clicked.connect(self.run_complete_analysis) layout.addWidget(run_btn) # Output area self.analysis_output = QTextEdit() self.analysis_output.setReadOnly(True) layout.addWidget(self.analysis_output) self.tabs.addTab(tab, "Complete Analysis") def create_case1_tab(self): """Tab for Case 1: One constant component""" tab = QWidget() layout = QVBoxLayout(tab) # Description desc_label = QLabel( "Case 1: One component of the vector field is constant. " "The potential function can be found by direct integration." ) desc_label.setWordWrap(True) layout.addWidget(desc_label) # Input form form_group = QGroupBox("Vector Field Parameters") form_layout = QFormLayout(form_group) # Component selection self.case1_component = QComboBox() self.case1_component.addItems(['x', 'y']) form_layout.addRow("Constant Component:", self.case1_component) # Constant value self.case1_constant = QLineEdit() self.case1_constant.setText('2') form_layout.addRow("Constant Value:", self.case1_constant) # Function for non-constant component self.case1_function = QLineEdit() self.case1_function.setText('y**2') form_layout.addRow("Function (for non-constant component):", self.case1_function) layout.addWidget(form_group) # Buttons btn_layout = QHBoxLayout() analyze_btn = QPushButton('Analyze Case 1') analyze_btn.clicked.connect(self.analyze_case1) btn_layout.addWidget(analyze_btn) symbolic_btn = QPushButton('Show Symbolic Analysis') symbolic_btn.clicked.connect(self.show_symbolic_case1) btn_layout.addWidget(symbolic_btn) layout.addLayout(btn_layout) # Output area self.case1_output = QTextEdit() self.case1_output.setReadOnly(True) layout.addWidget(self.case1_output) self.tabs.addTab(tab, "Case 1") def create_case2_tab(self): """Tab for Case 2: Both linear components""" tab = QWidget() layout = QVBoxLayout(tab) # Description desc_label = QLabel( "Case 2: Both components of the vector field are linear functions. " "The field is a gradient field if and only if a₂ = b₁." ) desc_label.setWordWrap(True) layout.addWidget(desc_label) # Input form form_group = QGroupBox("Vector Field Parameters") form_layout = QFormLayout(form_group) # Coefficients for Vx = a1*x + b1*y + c1 self.case2_a1 = QLineEdit() self.case2_a1.setText('2') form_layout.addRow("a₁ (coefficient of x in Vx):", self.case2_a1) self.case2_b1 = QLineEdit() self.case2_b1.setText('3') form_layout.addRow("b₁ (coefficient of y in Vx):", self.case2_b1) self.case2_c1 = QLineEdit() self.case2_c1.setText('1') form_layout.addRow("c₁ (constant in Vx):", self.case2_c1) # Coefficients for Vy = a2*x + b2*y + c2 self.case2_a2 = QLineEdit() self.case2_a2.setText('3') form_layout.addRow("a₂ (coefficient of x in Vy):", self.case2_a2) self.case2_b2 = QLineEdit() self.case2_b2.setText('4') form_layout.addRow("b₂ (coefficient of y in Vy):", self.case2_b2) self.case2_c2 = QLineEdit() self.case2_c2.setText('2') form_layout.addRow("c₂ (constant in Vy):", self.case2_c2) layout.addWidget(form_group) # Buttons btn_layout = QHBoxLayout() analyze_btn = QPushButton('Analyze Case 2') analyze_btn.clicked.connect(self.analyze_case2) btn_layout.addWidget(analyze_btn) symbolic_btn = QPushButton('Show Symbolic Analysis') symbolic_btn.clicked.connect(self.show_symbolic_case2) btn_layout.addWidget(symbolic_btn) check_btn = QPushButton('Check Gradient Condition') check_btn.clicked.connect(self.check_gradient_condition) btn_layout.addWidget(check_btn) layout.addLayout(btn_layout) # Output area self.case2_output = QTextEdit() self.case2_output.setReadOnly(True) layout.addWidget(self.case2_output) self.tabs.addTab(tab, "Case 2") def create_numerical_examples_tab(self): """Tab for running numerical examples""" tab = QWidget() layout = QVBoxLayout(tab) # Description desc_label = QLabel( "Run numerical examples to verify the gradient field analysis. " "These examples demonstrate both valid gradient fields and non-gradient fields." ) desc_label.setWordWrap(True) layout.addWidget(desc_label) # Buttons btn_layout = QHBoxLayout() case1_examples_btn = QPushButton('Run Case 1 Examples') case1_examples_btn.clicked.connect(self.run_case1_examples) btn_layout.addWidget(case1_examples_btn) case2_examples_btn = QPushButton('Run Case 2 Examples') case2_examples_btn.clicked.connect(self.run_case2_examples) btn_layout.addWidget(case2_examples_btn) all_examples_btn = QPushButton('Run All Examples') all_examples_btn.clicked.connect(self.run_all_examples) btn_layout.addWidget(all_examples_btn) layout.addLayout(btn_layout) # Output area self.examples_output = QTextEdit() self.examples_output.setReadOnly(True) layout.addWidget(self.examples_output) self.tabs.addTab(tab, "Numerical Examples") def run_complete_analysis(self): """Run the complete analysis from the original script""" self.analysis_output.clear() # Capture stdout to display in the text area output = io.StringIO() with redirect_stdout(output): try: analyzer1, analyzer2, analyzer3 = GradientFieldFactory.analyze_all_cases() self.statusBar().showMessage('Complete analysis finished successfully') except Exception as e: print(f"Error during analysis: {e}") self.statusBar().showMessage(f'Error: {e}') self.analysis_output.setPlainText(output.getvalue()) def analyze_case1(self): """Analyze a specific Case 1 example""" self.case1_output.clear() try: # Get parameters from UI component = self.case1_component.currentText() constant = float(self.case1_constant.text()) function_str = self.case1_function.text() # Create analyzer analyzer = ConstantComponentField(component) # Create vector field x, y = sp.symbols('x y') if component == 'x': Vx = constant Vy = sp.sympify(function_str) else: Vx = sp.sympify(function_str) Vy = constant # Find potential phi = analyzer.find_potential_for_specific_field(Vx, Vy) # Display results output = f"Case 1 Analysis:\n" output += f"Vector Field: F(x,y) = [{Vx}, {Vy}]\n" output += f"Potential Function: φ(x,y) = {sp.simplify(phi)}\n\n" # Verify is_valid, diff_x, diff_y = analyzer.verify_potential(phi, Vx, Vy) output += f"Verification: ∇φ = F? {is_valid}\n" if not is_valid: output += f" ∂φ/∂x - Vx = {diff_x}\n" output += f" ∂φ/∂y - Vy = {diff_y}\n" self.case1_output.setPlainText(output) self.statusBar().showMessage('Case 1 analysis completed') except Exception as e: self.case1_output.setPlainText(f"Error: {e}") self.statusBar().showMessage(f'Error in Case 1 analysis: {e}') def show_symbolic_case1(self): """Show symbolic analysis for Case 1""" self.case1_output.clear() try: component = self.case1_component.currentText() analyzer = ConstantComponentField(component) # Get symbolic results phi = analyzer.find_potential() Vx, Vy = analyzer.get_vector_field() output = f"Symbolic Analysis for Case 1:\n" output += f"Vector Field: F(x,y) = [{Vx}, {Vy}]\n" output += f"Potential Function: φ(x,y) = {phi}\n\n" # Show specific cases output += "Specific Examples:\n" examples = analyzer.get_specific_cases() for case_name, (Vx_ex, Vy_ex, phi_ex) in examples.items(): output += f"{case_name}:\n" output += f" F(x,y) = [{Vx_ex}, {Vy_ex}]\n" output += f" φ(x,y) = {phi_ex}\n\n" self.case1_output.setPlainText(output) self.statusBar().showMessage('Symbolic Case 1 analysis completed') except Exception as e: self.case1_output.setPlainText(f"Error: {e}") self.statusBar().showMessage(f'Error in symbolic Case 1 analysis: {e}') def analyze_case2(self): """Analyze a specific Case 2 example""" self.case2_output.clear() try: # Get parameters from UI a1 = float(self.case2_a1.text()) b1 = float(self.case2_b1.text()) c1 = float(self.case2_c1.text()) a2 = float(self.case2_a2.text()) b2 = float(self.case2_b2.text()) c2 = float(self.case2_c2.text()) # Create analyzer analyzer = LinearComponentField() x, y = sp.symbols('x y') # Create vector field Vx = a1*x + b1*y + c1 Vy = a2*x + b2*y + c2 output = f"Case 2 Analysis:\n" output += f"Vector Field: F(x,y) = [{Vx}, {Vy}]\n\n" # Check gradient condition is_gradient = analyzer.check_gradient_condition_for_specific(Vx, Vy) output += f"Gradient Field Condition: ∂P/∂y = ∂Q/∂x\n" output += f" ∂P/∂y = {sp.diff(Vx, y)}\n" output += f" ∂Q/∂x = {sp.diff(Vy, x)}\n" output += f" Condition satisfied? {is_gradient}\n\n" if is_gradient: # Find potential phi = analyzer.find_potential_for_specific_field(Vx, Vy) output += f"Potential Function: φ(x,y) = {sp.simplify(phi)}\n\n" # Verify is_valid, diff_x, diff_y = analyzer.verify_potential(phi, Vx, Vy) output += f"Verification: ∇φ = F? {is_valid}\n" if not is_valid: output += f" ∂φ/∂x - Vx = {diff_x}\n" output += f" ∂φ/∂y - Vy = {diff_y}\n" else: output += "This field is not a gradient field. No potential function exists.\n" self.case2_output.setPlainText(output) self.statusBar().showMessage('Case 2 analysis completed') except Exception as e: self.case2_output.setPlainText(f"Error: {e}") self.statusBar().showMessage(f'Error in Case 2 analysis: {e}') def show_symbolic_case2(self): """Show symbolic analysis for Case 2""" self.case2_output.clear() try: analyzer = LinearComponentField() output = "Symbolic Analysis for Case 2:\n\n" # Show gradient condition condition = analyzer.get_gradient_condition() output += f"Gradient Field Condition: {condition} = 0\n" output += "This means: a₂ = b₁ for the general linear field\n\n" # Show specific gradient cases output += "Specific Gradient Cases:\n" examples = analyzer.get_specific_gradient_cases() for case_name, (Vx, Vy, phi) in examples.items(): output += f"{case_name}:\n" output += f" F(x,y) = [{Vx}, {Vy}]\n" output += f" φ(x,y) = {phi}\n\n" # Show case study forms output += "Exact Forms from Case Study:\n" output_stream = io.StringIO() with redirect_stdout(output_stream): analyzer.demonstrate_case_study_forms() output += output_stream.getvalue() self.case2_output.setPlainText(output) self.statusBar().showMessage('Symbolic Case 2 analysis completed') except Exception as e: self.case2_output.setPlainText(f"Error: {e}") self.statusBar().showMessage(f'Error in symbolic Case 2 analysis: {e}') def check_gradient_condition(self): """Check only the gradient condition for Case 2""" try: # Get parameters from UI a1 = float(self.case2_a1.text()) b1 = float(self.case2_b1.text()) a2 = float(self.case2_a2.text()) # Check condition condition_satisfied = abs(a2 - b1) < 1e-10 # Account for floating point errors if condition_satisfied: message = f"✓ Gradient condition satisfied: a₂ ({a2}) = b₁ ({b1})" QMessageBox.information(self, "Gradient Condition", message) else: message = f"✗ Gradient condition not satisfied: a₂ ({a2}) ≠ b₁ ({b1})" QMessageBox.warning(self, "Gradient Condition", message) except Exception as e: QMessageBox.critical(self, "Error", f"Error checking gradient condition: {e}") def run_case1_examples(self): """Run Case 1 numerical examples""" self.examples_output.clear() output = io.StringIO() with redirect_stdout(output): try: numerical_examples = NumericalExamples() numerical_examples.run_case1_examples() self.statusBar().showMessage('Case 1 examples completed') except Exception as e: print(f"Error running Case 1 examples: {e}") self.statusBar().showMessage(f'Error: {e}') self.examples_output.setPlainText(output.getvalue()) def run_case2_examples(self): """Run Case 2 numerical examples""" self.examples_output.clear() output = io.StringIO() with redirect_stdout(output): try: numerical_examples = NumericalExamples() numerical_examples.run_case2_examples() self.statusBar().showMessage('Case 2 examples completed') except Exception as e: print(f"Error running Case 2 examples: {e}") self.statusBar().showMessage(f'Error: {e}') self.examples_output.setPlainText(output.getvalue()) def run_all_examples(self): """Run all numerical examples""" self.examples_output.clear() output = io.StringIO() with redirect_stdout(output): try: numerical_examples = NumericalExamples() print("=" * 60) print("NUMERICAL EXAMPLES VERIFICATION") print("=" * 60) numerical_examples.run_case1_examples() numerical_examples.run_case2_examples() self.statusBar().showMessage('All examples completed') except Exception as e: print(f"Error running examples: {e}") self.statusBar().showMessage(f'Error: {e}') self.examples_output.setPlainText(output.getvalue()) def main(): app = QApplication(sys.argv) # Set application style app.setStyle('Fusion') # Create and show the main window window = GradientFieldApp() window.show() # Start the event loop sys.exit(app.exec_()) if __name__ == '__main__': main()