File size: 14,349 Bytes
dad5dde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
import gradio as gr
import numpy as np
import matplotlib.pyplot as plt
import versDV as dv
import deviatoire as dev
from math import pi
import pandas as pd
import io
import base64

# CSS personnalisé avec les couleurs de l'école
css = """
/* Palette de couleurs Centrale Lyon (version rouge) */
:root {
    --primary-red: #D52B1E;
    --secondary-red: #B22222;
    --accent-red: #8B0000;
    --light-gray: #F5F5F5;
    --dark-gray: #333333;
}

.gradio-container {
    font-family: 'Arial', sans-serif;
}

.main-header {
    background: linear-gradient(90deg, var(--primary-red) 0%, var(--secondary-red) 100%);
    padding: 1.5rem;
    border-radius: 0 0 10px 10px;
    color: white;
    text-align: center;
    margin-bottom: 2rem;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.centrale-title {
    font-family: 'Georgia', serif;
    font-weight: 700;
    margin-bottom: 0.5rem;
}

.centrale-subtitle {
    font-family: 'Arial', sans-serif;
    font-size: 1.2rem;
    opacity: 0.9;
}

.centrale-card {
    border-left: 4px solid var(--accent-red);
    padding: 1rem;
    background-color: var(--light-gray);
    border-radius: 0 8px 8px 0;
    margin: 1rem 0;
}

.footer {
    background-color: var(--dark-gray);
    color: white;
    padding: 1rem;
    text-align: center;
    margin-top: 2rem;
    border-radius: 8px 8px 0 0;
    font-size: 0.9rem;
}
"""

def create_header():
    """Crée le header HTML avec le style Centrale Lyon"""
    return f"""
    <div class="main-header">
        <h1 class="centrale-title">ÉCOLE CENTRALE LYON</h1>
        <h2 class="centrale-subtitle">Analyse de Fatigue - Critère de Dang Van</h2>
    </div>
    """

def create_info_panel():
    """Crée le panneau d'informations"""
    return """
    <div class="centrale-card">
        <h3>📝 À propos du critère de Dang Van</h3>
        <p>Le critère de Dang Van est un critère multiaxial de fatigue à haute durée de vie. 
        Il permet de prendre en compte l'effet de la pression hydrostatique sur l'endurance 
        en fatigue des matériaux métalliques. Cette application visualise le domaine de 
        sécurité défini par ce critère pour différents types de chargements.</p>
        
        <h4>👥 Équipe</h4>
        <p><strong>Étudiants :</strong> Kevin TONGUE, Paul LORTHIOIR</p>
        <p><strong>Enseignants :</strong> Éric FEULVACH, Françoise FAUVIN</p>
        <p><strong>UE :</strong> Projet de recherche et innovation</p>
        <p><strong>Thème :</strong> Analyse en fatigue de structures industrielles soumises à des chargements complexes</p>
        <p><strong>Date :</strong> 2026</p>
    </div>
    """

def calculate_dang_van(sigma1, omega, fin, pasTemps, point_size, show_grid, theme):
    """
    Fonction principale de calcul pour le critère de Dang Van
    """
    try:
        print(f"Début calcul avec sigma1={sigma1}, omega={omega}, fin={fin}, pasTemps={pasTemps}")
        
        # Configuration du style selon le thème choisi
        try:
            if theme == "Moderne":
                plt.style.use('seaborn-v0_8-darkgrid')
            elif theme == "Scientifique":
                plt.style.use('seaborn-v0_8-paper')
            else:
                plt.style.use('default')
        except Exception as style_error:
            print(f"Erreur style: {style_error}")
            plt.style.use('default')
        
        # Calcul des points pour chargement uniaxial
        print("Calcul des points uniaxiaux...")
        points_uniaxial = dv.nuage(sigma1, omega, pasTemps, fin)
        print(f"Points uniaxiaux calculés: {points_uniaxial.shape}")
        
        # Calcul des points pour torsion
        print("Calcul des points torsion...")
        points_torsion = dv.nuageOrt(sigma1, omega, pasTemps, fin)
        print(f"Points torsion calculés: {points_torsion.shape}")
        
        # Préparation de la figure
        fig, ax = plt.subplots(figsize=(10, 6))
        
        # Tracé des points
        scatter1 = ax.scatter(
            points_uniaxial[:, 0],
            points_uniaxial[:, 1],
            s=point_size,
            alpha=0.7,
            label='Traction-Compression',
            edgecolors='white',
            linewidth=1
        )

        scatter2 = ax.scatter(
            points_torsion[:, 0],
            points_torsion[:, 1],
            s=point_size,
            alpha=0.7,
            label='Torsion',
            edgecolors='white',
            linewidth=1
        )
        
        # Configuration des axes et titres
        ax.set_xlabel("Pression hydrostatique (MPa)", fontsize=12, fontweight='bold')
        ax.set_ylabel("Amplitude de cisaillement max (MPa)", fontsize=12, fontweight='bold')
        ax.set_title("Diagramme de Dang Van - École Centrale Lyon", 
                    fontsize=14, fontweight='bold', pad=20)
        
        if show_grid:
            ax.grid(True, linestyle='--', alpha=0.3)
        
        ax.legend(loc='best', frameon=True, fancybox=True, shadow=True)
        
        # Ajustement des limites
        xlim_min = min(points_uniaxial[:, 0].min(), points_torsion[:, 0].min()) - 10
        xlim_max = max(points_uniaxial[:, 0].max(), points_torsion[:, 0].max()) + 10
        ylim_max = max(points_uniaxial[:, 1].max(), points_torsion[:, 1].max()) + 10
        ax.set_xlim(xlim_min, xlim_max)
        ax.set_ylim(0, ylim_max)
        
        # Sauvegarde de la figure pour affichage HTML
        buf = io.BytesIO()
        fig.savefig(buf, format='png', dpi=150, bbox_inches='tight')  # Réduit DPI pour performance
        buf.seek(0)
        plot_data = base64.b64encode(buf.read()).decode()
        plot_html = f'<img src="data:image/png;base64,{plot_data}" style="max-width: 100%; height: auto;">'
        plt.close(fig)
        
        print("Graphique généré")
        
        # Calcul des statistiques
        stats_uniaxial = len(points_uniaxial)
        stats_torsion = len(points_torsion)
        mean_hydro = float(np.mean(points_uniaxial[:, 0]))
        max_shear = float(max(points_uniaxial[:, 1].max(), points_torsion[:, 1].max()))
        
        # Création des DataFrames pour l'export
        try:
            df_uniaxial = pd.DataFrame(points_uniaxial, columns=['Pression_hydrostatique', 'Cisaillement_max'])
            df_torsion = pd.DataFrame(points_torsion, columns=['Pression_hydrostatique', 'Cisaillement_max'])
            print("DataFrames créés")
        except Exception as df_error:
            print(f"Erreur DataFrame: {df_error}")
            # Créer des DataFrames vides en cas d'erreur
            df_uniaxial = pd.DataFrame(columns=['Pression_hydrostatique', 'Cisaillement_max'])
            df_torsion = pd.DataFrame(columns=['Pression_hydrostatique', 'Cisaillement_max'])
        
        # Analyse du critère
        try:
            alpha_est = 0.5
            beta_est = float(points_uniaxial[:, 1].max() + alpha_est * points_uniaxial[:, 0].mean())
        except Exception as analysis_error:
            print(f"Erreur analyse: {analysis_error}")
            alpha_est = 0.5
            beta_est = 0.0
        
        analysis_text = f"""
        ### Analyse du critère de Dang Van
        
        Le critère de Dang Van s'exprime sous la forme :
        
        τ_a,max + α × p_h ≤ β
        
        où :
        - τ_a,max est l'amplitude maximale de cisaillement
        - p_h est la pression hydrostatique
        - α et β sont des constantes matériau
        
        **Paramètres estimés :**
        - α ≈ {alpha_est:.3f}
        - β ≈ {beta_est:.1f} MPa
        """
        
        stats_text = f"""
        ### 📊 Résultats statistiques
        
        **Points uniaxiaux :** {stats_uniaxial} (σ₁={sigma1}MPa)
        **Points torsion :** {stats_torsion} (ω={omega:.2f} rad/s)
        **Pression hydro. moyenne :** {mean_hydro:.1f} MPa (Uniaxial)
        **Cisaillement max :** {max_shear:.1f} MPa
        """
        
        # Générer les tableaux HTML
        try:
            uniaxial_html = df_uniaxial.head(20).to_html(classes='table table-striped')
            torsion_html = df_torsion.head(20).to_html(classes='table table-striped')
        except Exception as table_error:
            print(f"Erreur tableaux HTML: {table_error}")
            uniaxial_html = "<p>Erreur dans le tableau</p>"
            torsion_html = "<p>Erreur dans le tableau</p>"
        
        # Générer les CSV
        try:
            uniaxial_csv = df_uniaxial.to_csv(index=False)
            torsion_csv = df_torsion.to_csv(index=False)
            plot_image = buf.getvalue()
        except Exception as csv_error:
            print(f"Erreur CSV: {csv_error}")
            uniaxial_csv = "Pression_hydrostatique,Cisaillement_max\n"
            torsion_csv = "Pression_hydrostatique,Cisaillement_max\n"
            plot_image = None
        
        print("Calcul terminé avec succès")
        
        # Retourner les résultats dans le bon format pour Gradio
        return (
            plot_html,  # HTML pour l'affichage du graphique
            stats_text,  # Texte pour les statistiques
            analysis_text,  # Texte pour l'analyse
            uniaxial_html,  # HTML tableau uniaxial
            torsion_html,  # HTML tableau torsion
            uniaxial_csv,  # CSV uniaxial pour téléchargement
            torsion_csv,  # CSV torsion pour téléchargement
            plot_image  # Image pour téléchargement
        )
        
    except Exception as e:
        print(f"ERREUR COMPLÈTE: {e}")
        import traceback
        traceback.print_exc()
        
        error_msg = f"Erreur lors du calcul : {str(e)}"
        # Retourner une structure vide mais valide en cas d'erreur
        return (
            f"<p style='color: red;'>{error_msg}</p>",
            f"❌ {error_msg}",
            f"❌ {error_msg}",
            f"<p style='color: red;'>{error_msg}</p>",
            f"<p style='color: red;'>{error_msg}</p>",
            "",
            "",
            None
        )

# Interface Gradio
with gr.Blocks(title="Critère de Dang Van - École Centrale Lyon") as demo:
    gr.HTML(create_header())
    
    with gr.Row():
        with gr.Column(scale=1):
            gr.Markdown("### 🔧 Paramètres d'étude")
            
            sigma1 = gr.Slider(
                minimum=10,
                maximum=200,
                value=100,
                step=5,
                label="Amplitude σ₁ (MPa)",
                info="Amplitude de contrainte en traction-compression"
            )
            
            omega = gr.Slider(
                minimum=0.1,
                maximum=10.0,
                value=2*pi,
                step=0.1,
                label="ω (rad/s)",
                info="Fréquence angulaire du chargement"
            )
            
            fin = gr.Slider(
                minimum=0.1,
                maximum=2.0,
                value=1.0,
                step=0.1,
                label="Temps final"
            )
            
            pasTemps = gr.Slider(
                minimum=0.001,
                maximum=0.1,
                value=0.1,
                step=0.001,
                label="Pas de temps"
            )
            
            gr.Markdown("### 📊 Options d'affichage")
            point_size = gr.Slider(10, 100, 30, label="Taille des points")
            show_grid = gr.Checkbox(True, label="Afficher la grille")
            theme = gr.Dropdown(
                ["Classique", "Moderne", "Scientifique"],
                value="Classique",
                label="Thème du graphique"
            )
            
            calculate_btn = gr.Button(
                "🚀 Lancer le calcul et la visualisation",
                variant="primary",
                size="lg"
            )
            
        with gr.Column(scale=2):
            gr.Markdown("### Objectif de l'étude")
            gr.HTML("""
            <div class="centrale-card">
                <p>Cette application permet d'analyser le comportement en fatigue des matériaux selon le critère de Dang Van. 
                Le critère permet de prédire l'apparition de fissures de fatigue en considérant simultanément la pression 
                hydrostatique et l'amplitude de cisaillement.</p>
            </div>
            """)
            
            plot_output = gr.HTML()
            
            with gr.Tabs():
                with gr.TabItem("📈 Statistiques"):
                    stats_output = gr.Markdown()
                
                with gr.TabItem("📄 Analyse"):
                    analysis_output = gr.Markdown()
                
                with gr.TabItem("💾 Export des données"):
                    with gr.Row():
                        with gr.Column():
                            gr.Markdown("**Données Traction-Compression**")
                            uniaxial_table = gr.HTML()
                        with gr.Column():
                            gr.Markdown("**Données Torsion**")
                            torsion_table = gr.HTML()
                    
                    with gr.Row():
                        uniaxial_csv = gr.File(label="CSV Traction-Compression")
                        torsion_csv = gr.File(label="CSV Torsion")
                        plot_download = gr.File(label="Graphique PNG")
    
    # Informations
    gr.HTML(create_info_panel())
    
    # Footer
    gr.HTML("""
    <div class="footer">
        <p><strong>École Centrale Lyon</strong> | Mécanique des Matériaux | UE: Fatigue et Fissuration</p>
        <p style="font-size: 0.8rem; opacity: 0.8;">
            Rapport technique - © 2026 - Tous droits réservés
        </p>
    </div>
    """)
    
    # Gestion des événements
    calculate_btn.click(
        fn=calculate_dang_van,
        inputs=[sigma1, omega, fin, pasTemps, point_size, show_grid, theme],
        outputs=[
            plot_output,
            stats_output,
            analysis_output,
            uniaxial_table,
            torsion_table,
            uniaxial_csv,
            torsion_csv,
            plot_download
        ]
    )

# Lancement de l'application
if __name__ == "__main__":
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        css=css
    )