diff --git a/app.py b/app.py index 3145b7a..35c7b31 100644 --- a/app.py +++ b/app.py @@ -1,87 +1,30 @@ +from src.utils import reset_config, set_basic_info, set_sidebar_and_get_params, set_resultados from src.calculadora import calcula_tensao_critica, calcular_abaco, calcular_v_max -from src.utils import get_footer, set_chart, set_table import streamlit as st -st.set_page_config( - page_title="Calculadora", - page_icon=":coffee:", - layout="wide", - initial_sidebar_state="expanded" -) +reset_config(st) -st.markdown(get_footer(), unsafe_allow_html=True) +set_basic_info(st) -st.title('Calculadora - Tensão Crítica') - -st.markdown(""" - -Feito por: Marcelo Cardozo • [![GitHub](https://cdn.pixabay.com/photo/2022/01/30/13/33/github-6980894_1280.png)](https://github.com/marcelogcardozo) - • [![LinkedIn](https://scontent.fsdu25-1.fna.fbcdn.net/v/t39.30808-1/267422938_4880673518668256_5792791296770782057_n.png?stp=dst-png_p200x200&_nc_cat=108&ccb=1-7&_nc_sid=754033&_nc_ohc=FzJ49MszpDkAX_b3T44&_nc_ht=scontent.fsdu25-1.fna&cb_e2o_trans=t&oh=00_AfC1YK5ssU0tc3BQcFnYLlPAJFN8jotV5pf9JJc-ANb7pQ&oe=650DDC2B)](https://www.linkedin.com/in/marcelogcardozo/) - -Versão: 0.1.0 -""") - -st.markdown( - """ - - """, - unsafe_allow_html=True, -) - -st.subheader('Resultados') -st.divider() - -# Parâmetros de entrada -st.sidebar.title('Parâmetros de Entrada') - -st.sidebar.subheader('Dados do Material') -E = st.sidebar.number_input('Módulo de Elasticidade (GPa)', 0.1, None, 200.0) * 1000 -T_esc = st.sidebar.number_input('Tensão de Escoamento (MPa)', 0.1, None, 250.0) - -st.sidebar.subheader('Dados da Seção Transversal') -A = st.sidebar.number_input('Área (mm²)', 0.0, None, 0.0) -I = st.sidebar.number_input('Inércia (mm⁴)', 0.0, None, 0.0) -r = st.sidebar.number_input('Raio de Giração (mm)', 0.0, None, 0.0) -c = st.sidebar.number_input('Distância da Linha Neutra (mm)', 0.0, None, 0.0) - -st.sidebar.subheader('Dados do caso') -P = st.sidebar.number_input('Carga aplicada (kN)', 0.0, None, 0.0) -Le = st.sidebar.number_input('Comprimento efetivo do elemento (mm)', 0.1, None, 1000.0) -e = st.sidebar.number_input('Excentricidade (mm)', 0.0, None, 0.0) -FS = st.sidebar.number_input('Fator de Segurança', 0.0, None, 0.0) - -gerar_abaco_completo = st.sidebar.checkbox('Gerar ábaco completo', value=False, key='gerar_abaco') - -if gerar_abaco_completo: - - de = st.sidebar.number_input('De', 0.0, 2.0, 0.0) - ate = st.sidebar.number_input('Até', 0.1, 2.0, 1.0) - - razao = st.sidebar.number_input('Razão', 0.1, 2.0, 0.1) -else: - de = 0.0 - ate = 1.0 - razao = 0.1 +E, T_esc, A, I, r, c, P, FS, Le, k, e, gerar_abaco_completo, de, ate, razao = set_sidebar_and_get_params(st) # botao_calcular if st.sidebar.button('Calcular'): with st.spinner('Calculando...'): - - col1, col2, col3 = st.columns(3) - col4, col5 = st.columns([0.85, 0.15]) - - if (I == 0 and r == 0): - st.error('Inércia e Raio de Giração não podem ser ambos iguais a 0') + + if A == 0: + st.error('A área (A) não pode ser iguao a zero.') + st.stop() + elif (e > 0 and c == 0): + st.error('A distância do centroide à fibra mais comprimida (c) não podem ser iguais a zero quando a excentricidade (e) for maior do que zero.') + st.stop() + elif (r == 0 and I == 0): + st.error('O raio de giração (r) ou o momento de inércia (I) devem ser diferentes de zero.') + st.stop() + elif (Le == 0): + st.error('O comprimento efetivo de flambagem (Le) não pode ser igual a zero.') st.stop() - elif r == 0: - r = (I / A) ** (1/2) ecr2 = (e*c)/r**2 Ler = Le/r @@ -91,6 +34,7 @@ if (P == 0 and FS > 0): P = pcr / FS + tensao_normal_aplicada = (P * 1000) / A v_max = calcular_v_max(P, pcr, e) @@ -99,15 +43,5 @@ abaco = calcular_abaco(T_esc, ecr2, E, gerar_abaco_completo, de, ate, razao) - col1.latex('P_{cr} = '+f'{pcr:.2f} kN') - col1.latex(f'P = {P:.2f} kN') - - col2.latex('\sigma_{cr} = '+f'{tensao_normal_critica:.2f} MPa') - col2.latex(f'\sigma = {tensao_normal_aplicada:.2f} MPa') - col2.latex('V_{max} = '+f'{v_max:.2f} mm') - - col3.latex(f'FS = {FS:.2f}') - col3.latex(f'Método: {"Euler" if e == 0 else "Secante"}') + set_resultados(st, pcr, P, tensao_normal_critica, tensao_normal_aplicada, v_max, FS, e, ecr2, abaco, gerar_abaco_completo) - set_chart(col4, ecr2, abaco, gerar_abaco_completo) - set_table(col5, ecr2, abaco) diff --git a/src/utils.py b/src/utils.py index b6a305f..5ba0e9a 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,46 +1,203 @@ import plotly.graph_objects as go -def get_footer(): - style = """ - - """ - return style - -def set_chart(col_widget, ecr2_base: float, abaco: dict, gerar_abaco_completo: bool) -> None: +from math import sqrt +import numpy as np + +def reset_config(st) -> None: + + def _get_footer(): + style = """ + + """ + return style - fig = go.Figure() + st.set_page_config( + page_title="Calculadora", + page_icon=":coffee:", + layout="wide", + initial_sidebar_state="expanded", + menu_items = { + 'Get Help': 'mailto:marcelo.cardozo.cg@gmail.com?subject=Ajuda%20-%20Calculadora%20de%20Tensão%20Crítica', + 'Report a bug': 'mailto:marcelo.cardozo.cg@gmail.com?subject=BUG%20-%20Calculadora%20de%20Tensão%20Crítica', + }, + ) + + st.markdown(_get_footer(), unsafe_allow_html=True) + +def set_basic_info(st) -> None: + + st.title('Calculadora - Tensão Crítica') + + st.markdown(""" + + Feito por: Marcelo Cardozo • [![GitHub](https://cdn.pixabay.com/photo/2022/01/30/13/33/github-6980894_1280.png)](https://github.com/marcelogcardozo) + • [![LinkedIn](https://scontent.fsdu25-1.fna.fbcdn.net/v/t39.30808-1/267422938_4880673518668256_5792791296770782057_n.png?stp=dst-png_p200x200&_nc_cat=108&ccb=1-7&_nc_sid=754033&_nc_ohc=FzJ49MszpDkAX_b3T44&_nc_ht=scontent.fsdu25-1.fna&cb_e2o_trans=t&oh=00_AfC1YK5ssU0tc3BQcFnYLlPAJFN8jotV5pf9JJc-ANb7pQ&oe=650DDC2B)](https://www.linkedin.com/in/marcelogcardozo/) + + Versão: 1.0 + """) + + st.markdown( + """ + + """, + unsafe_allow_html=True, + ) + + st.subheader('Resultados') + st.divider() + +def set_sidebar_and_get_params(st) -> tuple: + + def _set_dados_do_material() -> tuple: + + st.sidebar.subheader('Dados do Material') + E = st.sidebar.number_input('Módulo de Elasticidade (E) [GPa]', 0.0, None, 200.0) * 1000 + T_esc = st.sidebar.number_input('Tensão de Escoamento ($\sigma_{y}$) [MPa]', 0.0, None, 250.0) + + return E, T_esc + + def _set_dados_secao_transversal() -> tuple: + + st.sidebar.subheader('Dados da Seção Transversal') + A = st.sidebar.number_input('Área (A) [mm²]', 0.0, None, 0.0) + col1, col2 = st.sidebar.columns([0.4, 0.6]) + select_box_I_r = col1.selectbox('Inércia ou Raio de Giração', ['I [mm⁴]', 'r [mm]'], label_visibility='collapsed') + value_I_r = col2.number_input('uma_label_qualquer', 0.0, None, 0.0, label_visibility='collapsed') - for ecr2, pontos in abaco.items(): + if select_box_I_r == 'I [mm⁴]': + I = value_I_r + try: + r = sqrt(I / A) + except ZeroDivisionError: + r = 0.0 + else: + r = value_I_r + I = A * r**2 - xs = [p.ler for p in pontos] - ys = [p.pa for p in pontos] + c = st.sidebar.number_input('Dist. da LN à Fibra de Maior Compressão (c) [mm]', 0.0, None, 0.0) - dash = 'dash' if (ecr2 == ecr2_base and gerar_abaco_completo) else None - line_trace = go.Line(x=xs, y=ys, name=f"{ecr2:.2f}", line=dict(dash=dash)) + return A, I, r, c - fig.add_trace(line_trace) + def _set_dados_do_caso() -> tuple: - fig.update_layout( - title= 'Ábaco de Euler e Secante', - xaxis_title="Le/r", - yaxis_title="P/A", - legend_title="Ecr2", - yaxis_range=[0, 300.1], - xaxis_range=[0, 200.1], - ) + st.sidebar.subheader('Dados do caso') + + col1, col2 = st.sidebar.columns([0.4, 0.6]) + + sbox_P = col1.selectbox('uma_label', ['P [kN]', 'FS'], label_visibility='collapsed') #st.sidebar.number_input('Carga aplicada (P) [kN]', 0.0, None, 0.0) + value_P = col2.number_input('uma_label2', 0.0, None, 0.0, label_visibility='collapsed') + + if sbox_P == 'P [kN]': + P = value_P + FS = 0 + else: + FS = value_P + P = 0 + + col1, col2 = st.sidebar.columns([0.4, 0.6]) + sbox_Le = col1.selectbox('Comprimento efetivo do elemento (Le)', ['Le [mm]', 'L [mm]'], label_visibility='collapsed') + value_Le = col2.number_input('uma_label_qualquer2', 0.0, None, 0.0, label_visibility='collapsed') + + if sbox_Le == 'L [mm]': + k = st.sidebar.select_slider('Coeficiente de Flambagem (k)', [str(round(i,2)) for i in np.arange(0.5, 2.05, 0.05)]) + Le = float(k) * value_Le + else: + Le = value_Le + k = 1.0 + + e = st.sidebar.number_input('Excentricidade (e) [mm]', 0.0, None, 0.0) + + return P, FS, Le, k, e + + def _set_gerar_abaco_completo_params() -> tuple: + + gerar_abaco_completo = st.sidebar.checkbox('Gerar ábaco completo', value=False, key='gerar_abaco') + + if gerar_abaco_completo: + + faixa_ecr2 = st.sidebar.slider(r'Faixa do $ec/r²$', 0.0, 2.0, (0.0, 1.0), 0.1) + de = faixa_ecr2[0] + ate = faixa_ecr2[1] + + razao = st.sidebar.number_input('Razão', 0.1, 2.0, 0.1) + else: + de = 0.0 + ate = 1.0 + razao = 0.1 + + return gerar_abaco_completo, de, ate, razao + + # Parâmetros de entrada + st.sidebar.title('Parâmetros de Entrada') + + E, T_esc = _set_dados_do_material() + A, I, r, c = _set_dados_secao_transversal() + P, FS, Le, k, e = _set_dados_do_caso() + abaco_completo, de, ate, razao = _set_gerar_abaco_completo_params() + + return E, T_esc, A, I, r, c, P, FS, Le, k, e, abaco_completo, de, ate, razao + +def set_resultados(st, pcr, P, tensao_normal_critica, tensao_normal_aplicada, v_max, FS, e, ecr2, abaco, gerar_abaco_completo) -> None: + + def set_chart(col_widget, ecr2_base: float, abaco: dict, gerar_abaco_completo: bool) -> None: + + fig = go.Figure() + + for ecr2, pontos in abaco.items(): + + xs = [p.ler for p in pontos] + ys = [p.pa for p in pontos] + + dash = 'dash' if (ecr2 == ecr2_base and gerar_abaco_completo) else None + line_trace = go.Scatter(x=xs, y=ys, name=f"{ecr2:.2f}", mode='lines', line=dict(dash=dash)) + + fig.add_trace(line_trace) + + fig.update_layout( + title= 'Ábaco de Euler e Secante', + xaxis_title="Le/r", + yaxis_title="P/A", + legend_title="Ecr2", + yaxis_range=[0, 300.1], + xaxis_range=[0, 200.1], + ) + + col_widget.plotly_chart(fig, use_container_width=True) + + def set_table(col_widget, ecr2, abaco) -> None: + + data_for_dataframe = { + 'Le/r': [round(p.ler, 2) for p in abaco[ecr2]], + 'P/A': [round(p.pa, 2) for p in abaco[ecr2]], + } + + col_widget.dataframe(data_for_dataframe) + + col1, col2, col3 = st.columns(3) + col4, col5 = st.columns([0.85, 0.15]) + + col1.latex('P_{cr} = '+f'{pcr:.2f} kN') + col1.latex(f'P = {P:.2f} kN') + + col2.latex('\sigma_{cr} = '+f'{tensao_normal_critica:.2f} MPa') + col2.latex(f'\sigma = {tensao_normal_aplicada:.2f} MPa') + col2.latex('V_{max} = '+f'{v_max:.2f} mm') - col_widget.plotly_chart(fig, use_container_width=True) + col3.latex(f'FS = {FS:.2f}') + col3.latex(f'Método: {"Euler" if e == 0 else "Secante"}') + set_chart(col4, ecr2, abaco, gerar_abaco_completo) + set_table(col5, ecr2, abaco) -def set_table(col_widget, ecr2, abaco) -> None: - data_for_dataframe = { - 'Le/r': [round(p.ler, 2) for p in abaco[ecr2]], - 'P/A': [round(p.pa, 2) for p in abaco[ecr2]], - } - col_widget.dataframe(data_for_dataframe) \ No newline at end of file