| import numpy as np |
| import streamlit as st |
| import plotly.graph_objects as go |
| import CAL_IA |
|
|
| st.set_page_config(page_title="Maillage FEM interactif", layout="wide") |
|
|
| st.title("Visualisation interactive du maillage FEM") |
|
|
| |
| st.sidebar.header("Options") |
|
|
| |
| node_file = st.sidebar.text_input("Fichier NODE", "NODE.txt") |
| elem_file = st.sidebar.text_input("Fichier ELEMENT", "ELEMENT.txt") |
|
|
| amplification = st.sidebar.slider("Facteur d'amplification des déplacements", 0.0, 50.0, 10.0, 1.0) |
| afficher_deplacement = st.sidebar.checkbox("Afficher maillage déformé (si U disponible)", value=False) |
| afficher_numeros_noeuds = st.sidebar.checkbox("Afficher numéros de nœuds", value=True) |
|
|
| st.sidebar.write("Clique sur le bouton ci-dessous pour recharger les données.") |
| reload = st.sidebar.button("Recharger les fichiers") |
|
|
| |
| @st.cache_data |
| def charger_maillage(node_file, elem_file): |
| nodeData = np.loadtxt(node_file) |
| elementData = np.loadtxt(elem_file) |
|
|
| |
|
|
| |
| |
|
|
| |
| nodes = nodeData[:, 6:8] |
|
|
| |
| elements = elementData[:, 5:8] |
|
|
| |
| elements = elements.astype(int) |
|
|
| return nodes, elements |
|
|
| nodes, elements = charger_maillage(node_file, elem_file) |
|
|
| st.write(f"Nombre de nœuds : {nodes.shape[0]}") |
| st.write(f"Nombre d'éléments : {elements.shape[0]}") |
|
|
| |
| U = CAL_IA.U |
| if afficher_deplacement: |
| |
|
|
| |
| |
|
|
| |
| try: |
| U = np.loadtxt("U.txt") |
|
|
| |
| if U.ndim > 1: |
| U = U.flatten() |
| except OSError: |
| st.warning("Fichier U.txt introuvable ou illisible. Le maillage déformé ne sera pas affiché.") |
| U = None |
|
|
| |
| if U is not None and afficher_deplacement: |
| ne = nodes.shape[0] |
| if U.shape[0] != 2*ne: |
| st.error(f"Taille de U incohérente : attendu {2*ne}, trouvé {U.shape[0]}") |
| nodes_def = nodes.copy() |
| disp_norm = np.zeros(ne) |
| else: |
| Depl = np.zeros_like(nodes) |
| Depl[:, 0] = U[0::2] |
|
|
| |
| Depl[:, 1] = U[1::2] |
|
|
| |
| nodes_def = nodes + amplification * Depl |
| disp_norm = np.linalg.norm(Depl, axis=1) |
| else: |
| nodes_def = nodes.copy() |
| disp_norm = np.zeros(nodes.shape[0]) |
|
|
| |
| fig = go.Figure() |
|
|
| |
| fig.add_trace(go.Mesh3d( |
| x=nodes[:, 0], |
| y=nodes[:, 1], |
| z=np.zeros(nodes.shape[0]), |
| i=elements[:, 0] - 1, |
|
|
| |
| j=elements[:, 1] - 1, |
| k=elements[:, 2] - 1, |
| color='lightgray', |
| opacity=0.3, |
| name="Maillage initial", |
| showscale=False |
| )) |
|
|
| |
| if U is not None and afficher_deplacement: |
| fig.add_trace(go.Mesh3d( |
| x=nodes_def[:, 0], |
| y=nodes_def[:, 1], |
| z=np.zeros(nodes_def.shape[0]), |
| i=elements[:, 0] - 1, |
| j=elements[:, 1] - 1, |
| k=elements[:, 2] - 1, |
| intensity=disp_norm, |
|
|
| |
| colorscale='Jet', |
| opacity=0.8, |
| name="Maillage déformé", |
| showscale=True, |
| colorbar_title="||U||" |
| )) |
|
|
| |
| if afficher_numeros_noeuds: |
| fig.add_trace(go.Scatter3d( |
| x=nodes_def[:, 0], |
| y=nodes_def[:, 1], |
| z=np.zeros(nodes_def.shape[0]), |
| mode='markers+text', |
| |
| textposition='top center', |
| marker=dict(size=3, color='red'), |
| |
| name="Nœuds" |
| )) |
| else: |
| fig.add_trace(go.Scatter3d( |
| x=nodes_def[:, 0], |
| y=nodes_def[:, 1], |
| z=np.zeros(nodes_def.shape[0]), |
| mode='markers', |
| marker=dict(size=3, color='red'), |
| hovertemplate='X: %{x:.3f}<br>Y: %{y:.3f}<extra></extra>', |
| name="Nœuds" |
| )) |
|
|
| fig.update_layout( |
| scene=dict( |
| xaxis_title='X', |
| yaxis_title='Y', |
| zaxis_title='', |
| aspectmode='data' |
| ), |
| margin=dict(l=0, r=0, t=40, b=0), |
| height=700, |
| title="Maillage FEM (Plotly + Streamlit)" |
| ) |
|
|
| st.plotly_chart(fig, width='stretch') |
|
|
| st.info("Modifie tes fichiers NODE.txt / ELEMENT.txt (et U.txt si utilisé), puis clique sur 'Recharger les fichiers' dans la barre latérale pour mettre à jour le graphe.") |
|
|