File size: 5,383 Bytes
8c1652d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

import gradio as gr
import datetime
import re
import joblib

# Load the trained pipeline
model = joblib.load("gradient_boosting_model.pkl")

ERROR_LABELS = {
    "UnderConstrained": "1) Under-constrained / rigid-body",
    "MeshQuality": "2) Bad mesh / distortion",
    "ContactFailure": "3) Contact issues",
    "MaterialError": "4) Material / units issues",
    "other_or_unknown": "Other / unknown"
}

GLOSSARY = {
    "rigid body motion": "Movement of an entire structure without deformation, often caused by missing constraints.",
    "jacobian": "A matrix used to calculate element distortion during deformation; bad values mean mesh issues.",
    "newton-raphson": "A nonlinear solution method sensitive to poor contact setup or load step configuration.",
    "initial penetration": "When contact surfaces start interpenetrated, often due to bad geometry or setup.",
    "divergence": "The solver cannot find a converging solution, often caused by too-large load steps or unstable models.",
    "singular matrix": "Solver can't invert the stiffness matrix, often caused by unconstrained parts.",
    "degrees of freedom": "Independent movements allowed for a part (translation/rotation in X, Y, Z directions)."
}

EXPLANATIONS = {
    "UnderConstrained": (
        "Your model appears under-constrained. The solver detected rigid-body motion or insufficient constraints.",
        "- Check if all bodies have proper supports.\n- Ensure key DOFs are restrained.\n- Avoid soft supports that only partially constrain motion.\n"),
    "MeshQuality": (
        "Your mesh likely has poor quality or distortion. This causes inaccurate results or solver failure.",
        "- Refine the mesh near stress concentrations.\n- Improve skewed or stretched elements.\n- Use proper element types for geometry.\n"),
    "ContactFailure": (
        "Contact elements may be missing or misconfigured. The solver can't resolve interactions between parts.",
        "- Check that all contacts are correctly defined.\n- Consider using bonded contact instead of frictionless.\n- Inspect contact surfaces and face orientation.\n"),
    "MaterialError": (
        "Material data or units are inconsistent. Properties like Young’s modulus or density may be off by scale.",
        "- Verify if Young’s modulus is in MPa or Pa.\n- Check that material density makes sense (e.g. kg/m³).\n- Look at plastic strain curve units if applicable.\n"),
    "other_or_unknown": (
        "I couldn't confidently match this log to a known error category.",
        "- Try checking boundary conditions, contact, and mesh. Rerun with more detailed solver output enabled.\n")
}

def classify_and_explain(log_text):
    if not log_text.strip():
        return ("No log detected", "Paste an ANSYS solver output to analyze.", log_text, "", None)

    pred = model.predict([log_text])[0]
    label = ERROR_LABELS.get(pred, "Other / unknown")
    explanation, troubleshooting = EXPLANATIONS.get(pred, EXPLANATIONS["other_or_unknown"])

    highlighted = re.sub(
        r"(?i)\b(" + "|".join(re.escape(k) for k in GLOSSARY) + r")\b",
        lambda m: f"**{m.group(1)}** 🛈",
        log_text,
    )

    found_terms = [term for term in GLOSSARY if term in log_text.lower()]
    glossary_text = "\n\n".join(f"**{term.title()}**: {GLOSSARY[term]}" for term in found_terms)

    return label, f"### Explanation\n{explanation}\n\n### Troubleshooting\n{troubleshooting}", highlighted, glossary_text, pred

def export_report(log_text, category, explanation):
    if not log_text or not category:
        return None, "⚠️ Please run analysis before exporting."

    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"/tmp/ansys_debug_report_{timestamp}.txt"

    try:
        with open(filename, "w", encoding="utf-8") as f:
            f.write("ANSYS Solver Debug Report\n")
            f.write("=" * 30 + "\n")
            f.write(f"Category: {category}\n\n")
            f.write("Explanation:\n")
            f.write(explanation + "\n\n")
            f.write("Original Log:\n")
            f.write(log_text + "\n")
        return filename, "✅ Report exported successfully."
    except Exception as e:
        return None, f"❌ Export failed: {str(e)}"

with gr.Blocks(title="ANSYS Structural Solver Debugger — ML Edition") as demo:
    gr.Markdown("# 🧠 ANSYS Structural Solver Debugger — ML Edition")

    with gr.Row():
        with gr.Column(scale=2):
            log_input = gr.Textbox(label="Paste ANSYS Solver Output", lines=20)
            analyze_btn = gr.Button("🔍 Analyze Log")
            export_btn = gr.Button("📁 Export Report")
            export_status = gr.Textbox(label="Export Status", interactive=False)
            report_file = gr.File(label="Download Report")

        with gr.Column(scale=3):
            category_out = gr.Textbox(label="Detected Error Type", interactive=False)
            explanation_out = gr.Markdown()
            highlighted_out = gr.Markdown(label="🔍 Highlighted Log")
            glossary_box = gr.Markdown(label="📘 Glossary Definitions")

    analyze_btn.click(classify_and_explain, inputs=[log_input], outputs=[category_out, explanation_out, highlighted_out, glossary_box])
    export_btn.click(export_report, inputs=[log_input, category_out, explanation_out], outputs=[report_file, export_status])

demo.launch()