|
|
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 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 = QWidget() |
|
|
self.setCentralWidget(central_widget) |
|
|
main_layout = QVBoxLayout(central_widget) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
self.tabs = QTabWidget() |
|
|
main_layout.addWidget(self.tabs) |
|
|
|
|
|
|
|
|
self.create_analysis_tab() |
|
|
self.create_case1_tab() |
|
|
self.create_case2_tab() |
|
|
self.create_numerical_examples_tab() |
|
|
|
|
|
|
|
|
self.statusBar().showMessage('Ready') |
|
|
|
|
|
def create_analysis_tab(self): |
|
|
"""Tab for running the complete analysis""" |
|
|
tab = QWidget() |
|
|
layout = QVBoxLayout(tab) |
|
|
|
|
|
|
|
|
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_btn = QPushButton('Run Complete Analysis') |
|
|
run_btn.clicked.connect(self.run_complete_analysis) |
|
|
layout.addWidget(run_btn) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
form_group = QGroupBox("Vector Field Parameters") |
|
|
form_layout = QFormLayout(form_group) |
|
|
|
|
|
|
|
|
self.case1_component = QComboBox() |
|
|
self.case1_component.addItems(['x', 'y']) |
|
|
form_layout.addRow("Constant Component:", self.case1_component) |
|
|
|
|
|
|
|
|
self.case1_constant = QLineEdit() |
|
|
self.case1_constant.setText('2') |
|
|
form_layout.addRow("Constant Value:", self.case1_constant) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
form_group = QGroupBox("Vector Field Parameters") |
|
|
form_layout = QFormLayout(form_group) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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() |
|
|
|
|
|
|
|
|
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: |
|
|
|
|
|
component = self.case1_component.currentText() |
|
|
constant = float(self.case1_constant.text()) |
|
|
function_str = self.case1_function.text() |
|
|
|
|
|
|
|
|
analyzer = ConstantComponentField(component) |
|
|
|
|
|
|
|
|
x, y = sp.symbols('x y') |
|
|
if component == 'x': |
|
|
Vx = constant |
|
|
Vy = sp.sympify(function_str) |
|
|
else: |
|
|
Vx = sp.sympify(function_str) |
|
|
Vy = constant |
|
|
|
|
|
|
|
|
phi = analyzer.find_potential_for_specific_field(Vx, Vy) |
|
|
|
|
|
|
|
|
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" |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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" |
|
|
|
|
|
|
|
|
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: |
|
|
|
|
|
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()) |
|
|
|
|
|
|
|
|
analyzer = LinearComponentField() |
|
|
x, y = sp.symbols('x y') |
|
|
|
|
|
|
|
|
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" |
|
|
|
|
|
|
|
|
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: |
|
|
|
|
|
phi = analyzer.find_potential_for_specific_field(Vx, Vy) |
|
|
output += f"Potential Function: φ(x,y) = {sp.simplify(phi)}\n\n" |
|
|
|
|
|
|
|
|
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" |
|
|
|
|
|
|
|
|
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" |
|
|
|
|
|
|
|
|
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" |
|
|
|
|
|
|
|
|
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: |
|
|
|
|
|
a1 = float(self.case2_a1.text()) |
|
|
b1 = float(self.case2_b1.text()) |
|
|
a2 = float(self.case2_a2.text()) |
|
|
|
|
|
|
|
|
condition_satisfied = abs(a2 - b1) < 1e-10 |
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
app.setStyle('Fusion') |
|
|
|
|
|
|
|
|
window = GradientFieldApp() |
|
|
window.show() |
|
|
|
|
|
|
|
|
sys.exit(app.exec_()) |
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
main() |