From a8f8841ce479693eb9014716be82e0126ef1d70c Mon Sep 17 00:00:00 2001 From: EduardoAMXango <167939544+EduardoAMXango@users.noreply.github.com> Date: Tue, 14 May 2024 21:09:32 -0300 Subject: [PATCH 01/25] Add files via upload --- MatrizdeRigidez_&_Massa.py | 75 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 MatrizdeRigidez_&_Massa.py diff --git a/MatrizdeRigidez_&_Massa.py b/MatrizdeRigidez_&_Massa.py new file mode 100644 index 0000000..7c16a6b --- /dev/null +++ b/MatrizdeRigidez_&_Massa.py @@ -0,0 +1,75 @@ +import numpy as np +import matplotlib.pyplot as plt +import math + +#Aço 1020 +#densidade: 7,87 g/cm3, 7870 kg/m3 ou 0,284 lb/in3 +#Modulo da elasticidade: 207 GPa = 207*10^9Pa, 30*10^6psi, razão de poisson 0,30 + +class Barra(): # para 2 graus de liberdade + + def __init__(self,area,elasticidade=1,comprimento=1,angulo=0,densidade=207,inercia=1): + #Declaração dos atributos + + self.area= area + self.elasticidade = elasticidade + self.comprimento = comprimento + self.densidade = densidade + self.angulo = math.radians(angulo) + self.inercia = inercia + + #Variveis para facilitar o calculo das matrizes + self.cos_teta= math.cos(self.angulo) + self.sen_teta= math.sin(self.angulo) + self.c= (self.elasticidade*self.area)/(self.comprimento) + self.m= (1/420)*self.densidade*self.area*self.comprimento + + def matriz_rigidez_local_barra(self): # Matriz de rigidez elementar da barra + K=self.c*np.array(([1,0,-1,0],[0,0,0,0],[-1,0,1,0],[0,0,0,0])) + return K + + def matriz_rotacao(self): # Matriz de rotação + T = np.array([[self.cos_teta, self.sen_teta, 0, 0], + [-self.sen_teta, self.cos_teta, 0, 0], + [0, 0, self.cos_teta, self.sen_teta], + [0, 0, -self.sen_teta, self.cos_teta]]) + return T + + def matriz_rigidez_global_barra(self): # Matriz de rigidez global do elemento de barra 2D, Equação retirada do documento CILAMCE 2016 + K_e = self.c*np.array([[self.cos_teta**2, self.sen_teta*self.cos_teta, -self.cos_teta**2, -self.sen_teta * self.cos_teta], + [0, self.cos_teta**2, -self.sen_teta*self.cos_teta, -self.sen_teta**2], + [0, 0, self.cos_teta**2, self.sen_teta * self.cos_teta], + [0, 0, 0, self.sen_teta**2]]) + K_e[np.tril_indices(4, k=-1)] = K_e.T[np.tril_indices(4, k=-1)] # Preenchendo a parte inferior esquerda da matriz (simétrica) + return K_e + + def matriz_rigidez_global_barra_livro(self): # Matriz de rigidez global do elemento de barra 2D, Equação retirada do livro que virgilio forneceu (The finite element method) + K_e_livro = ((2*self.elasticidade*self.inercia)/self.comprimento**3)*np.array([[6, -3*self.comprimento, -6, -3*self.comprimento], + [-3*self.comprimento, 2*self.comprimento**2, 3*self.comprimento, self.comprimento**2], + [-6, 3*self.comprimento, 6, 3*self.comprimento], + [-3*self.comprimento, self.comprimento**2, 3*self.comprimento, self.comprimento**2]]) + return K_e_livro + + def matriz_massa_elemento(self): # matriz de massa do elemento/ equação encontrada na pagina 311 do livro (The finite element method) + M_e =self.m *np.array([[156, 22 * self.comprimento, 54, -13 * self.comprimento], + [22 * self.comprimento, 4 * self.comprimento**2, 13 * self.comprimento, -3 * self.comprimento**2], + [54, 13 * self.comprimento, 156, 22 * self.comprimento], + [-13 * self.comprimento, -3 * self.comprimento**2, 22 * self.comprimento, 4 * self.comprimento**2]]) + return M_e + +#elemento de barra=Barra(Area,Elasticidade,Comprimento,angulo,densidade,inercia) + +elemento1=Barra(0.008,207,2,60,78,1) + +matriz_massa=elemento1.matriz_massa_elemento() +print(elemento1.matriz_rigidez_global_barra()) +print(elemento1.matriz_massa_elemento()) + + +# Plotando o gráfico da matriz de massa +plt.imshow(matriz_massa, cmap='viridis', interpolation='nearest') +plt.title('Matriz de Massa do Elemento') +plt.colorbar(label='Valores da Matriz') +plt.xlabel('Coluna') +plt.ylabel('Linha') +plt.show() \ No newline at end of file From adb8b45525b523365251a76583e383acfee46a90 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Tue, 11 Jun 2024 10:07:47 -0300 Subject: [PATCH 02/25] =?UTF-8?q?Create=20For=C3=A7as=20Aerodinamicas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Código base para calculo das forças aerodinâmicas --- "For\303\247as Aerodinamicas.py" | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 "For\303\247as Aerodinamicas.py" diff --git "a/For\303\247as Aerodinamicas.py" "b/For\303\247as Aerodinamicas.py" new file mode 100644 index 0000000..48892eb --- /dev/null +++ "b/For\303\247as Aerodinamicas.py" @@ -0,0 +1,70 @@ +import numpy as np +import matplotlib.pyplot as plt +import math + +class forcas_aerodinamicas(): + def __init__(self,area_frontal,area_superior,densidade_ar,coeficient_drag,coeficient_lift,length,ni): + self.af= area_frontal + self.asup= area_superior + self.rho= densidade_ar + self.cd= coeficient_drag + self.cl= coeficient_lift + self.x= length + self.ni=ni + + def aerodynamic_forces(self,velocidade): # Função para o cálculo de forças aerodinamicas + pressao_dinamica=0.5*self.rho*(velocidade**2) + drag= self.cd * pressao_dinamica * self.af + lift=self.cl * pressao_dinamica * self.asup + return drag,lift + + def numero_Reynolds(self,velocidade): # Função para encontrar o numero de reynolds + return (self.x*velocidade)/self.ni + + +#Parâmetros físicos do ar: +p_atm= 101325 #pressâo atmosférica (N/m^2) +rho= 1.184 #densidade (kg/m^3) +mi= 1.849*10**-5 #Viscosidade dinâmica (kg/m*s) +ni= (mi/rho) #Viscosidade Cinematica (m²/s) +#parâmetros do carro +length= 2 #Comprimento do carro (m^2) +af= 1.5 #Área Frontal do carro (m^2) +a_sup= 2 #Área de Superfície do carro (m^2) +cd = 0.75 #Coeficiente de arrasto por pressão do carro +cl= -0.3 #Coeficiente de lift do carro + +carro=forcas_aerodinamicas(af,a_sup,rho,cd,cl,length,ni) + +# Velocidades de 0 a 60 m/s +velocidades = np.linspace(0.1,60,50) +#calculo das forças +drags=[] +lifts=[] +for v in velocidades: + drag,lift= carro.aerodynamic_forces(v) + drags.append(drag) + lifts.append(lift) + +# Plotagem do gráfico 1 +plt.figure(figsize=(10, 6)) +plt.subplot(1,2,1) +plt.plot(velocidades, drags, label='Drag') +# Configurações do gráfico +plt.title('Gráfico de Arrasto em função da Velocidade') +plt.xlabel('Velocidade (m/s)') +plt.ylabel('Força (N)') +plt.legend() +plt.grid(True) + +# Plotagem do gráfico 2 +plt.subplot(1,2,2) +plt.plot(velocidades, lifts, label='Downforce', linestyle='--') +# Configurações do gráfico2 +plt.title('Gráfico de Downforce em função da Velocidade') +plt.xlabel('Velocidade (m/s)') +plt.ylabel('Força (N)') +plt.legend() +plt.grid(True) +plt.show() + From 0050bfe3bb918843ae92c105cec40b3237017181 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Tue, 11 Jun 2024 20:37:30 -0300 Subject: [PATCH 03/25] =?UTF-8?q?Atualiza=C3=A7=C3=A3o=20do=20software=20d?= =?UTF-8?q?e=20aero?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adicionado as equações para o calculo de arrasto e lift para asas --- "For\303\247as Aerodinamicas.py" | 95 +++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 21 deletions(-) diff --git "a/For\303\247as Aerodinamicas.py" "b/For\303\247as Aerodinamicas.py" index 48892eb..fb24f7b 100644 --- "a/For\303\247as Aerodinamicas.py" +++ "b/For\303\247as Aerodinamicas.py" @@ -3,25 +3,48 @@ import math class forcas_aerodinamicas(): - def __init__(self,area_frontal,area_superior,densidade_ar,coeficient_drag,coeficient_lift,length,ni): - self.af= area_frontal - self.asup= area_superior - self.rho= densidade_ar - self.cd= coeficient_drag + def __init__(self,frontal_area,planform_area,air_density,coeficient_drag_p,coeficient_drag_f,coeficient_lift,length,ni,atm_pressure,attack_angle,span,chord): + self.af= frontal_area + self.asup= planform_area + self.rho= air_density + self.cd_p= coeficient_drag_p + self.cd_f= coeficient_drag_f self.cl= coeficient_lift self.x= length - self.ni=ni + self.ni= ni + self.a1= a1 + self.a2= a2 + self.p_atm= atm_pressure + self.alpha= attack_angle + self.a_wing=span*chord + self.AR= (span**2)/self.a_wing + - def aerodynamic_forces(self,velocidade): # Função para o cálculo de forças aerodinamicas + def aerodynamic_forces(self,velocidade): # Função para o cálculo de forças aerodinâmicas pressao_dinamica=0.5*self.rho*(velocidade**2) - drag= self.cd * pressao_dinamica * self.af + drag= (self.cd_p * pressao_dinamica * self.af) + (self.cd_f* pressao_dinamica * self.asup) lift=self.cl * pressao_dinamica * self.asup return drag,lift - def numero_Reynolds(self,velocidade): # Função para encontrar o numero de reynolds +# def numero_Reynolds(self,velocidade): # Função para encontrar o numero de reynolds return (self.x*velocidade)/self.ni - - + +# def bernoulli(self,velocidade): + v2=velocidade*self.a1/self.a2 # Equação de conservação para encontrar a velocidade embaixo do carro + p2=self.p_atm +0.5*rho*velocidade**2 - 0.5*v2**2 # Equação de bernoulli para encontrar a pressão embaixo do carro + deltap=self.p_atm-p2 # Diferença de pressão entre a parte de baixo e a parte de cima do carro + return deltap + + def aerodynamics_forces_wing(self,velocidade): #Função para calcular as forças aerodinamicas geradas pela asa + pressao_dinamica=0.5*self.rho*(velocidade**2) + cl_alpha=(2*math.pi)/(1+(2/self.AR)) + cl_wing=cl_alpha*self.alpha #Coeficiente de lift da asa + cd_induced=(1/(math.pi*self.AR))*cl_wing**2 #Coeficiente de arrasto induzido + cd_wing=cd_induced+0.05 #Coeficiente de arrasto total da asa= Cd=Cd_induzido+Cd_forma + drag_wing= (cd_wing * pressao_dinamica * self.a_wing) #Calculo da força de arrasto + lift_wing= (cl_wing * pressao_dinamica * self.a_wing) + return drag_wing,lift_wing + #Parâmetros físicos do ar: p_atm= 101325 #pressâo atmosférica (N/m^2) rho= 1.184 #densidade (kg/m^3) @@ -31,40 +54,70 @@ def numero_Reynolds(self,velocidade): # Função para encontrar o numero length= 2 #Comprimento do carro (m^2) af= 1.5 #Área Frontal do carro (m^2) a_sup= 2 #Área de Superfície do carro (m^2) -cd = 0.75 #Coeficiente de arrasto por pressão do carro +cd_p = 0.45 #Coeficiente de arrasto por pressão do carro +cd_f = 0.05 #Coeficiente de arrasto por atrito do carro cl= -0.3 #Coeficiente de lift do carro +a1= 0.25 #Área de entrada do ar embaixo do carro (m^2) +a2= 0.20 #Área embaixo do carro (m^2) +#parâmetros da asa +chord=0.25 #Comprimento da asa (m) +span=1 #Largura da asa (m) +thickness=0.05 #Expessura máxima da asa (m) +alpha=math.radians(3.75) #Ângulo de incidencia do vento com a asa (radianos) -carro=forcas_aerodinamicas(af,a_sup,rho,cd,cl,length,ni) +carro=forcas_aerodinamicas(af,a_sup,rho,cd_p,cd_f,cl,length,ni,p_atm,alpha,span,chord) #Definição do objeto # Velocidades de 0 a 60 m/s velocidades = np.linspace(0.1,60,50) #calculo das forças drags=[] lifts=[] +drags_w=[] +lifts_w=[] for v in velocidades: drag,lift= carro.aerodynamic_forces(v) + drag_w,lift_w= carro.aerodynamics_forces_wing(v) drags.append(drag) lifts.append(lift) + drags_w.append(drag_w) + lifts_w.append(lift_w) # Plotagem do gráfico 1 -plt.figure(figsize=(10, 6)) -plt.subplot(1,2,1) +plt.figure(figsize=(12, 6)) +plt.subplot(2,2,1) plt.plot(velocidades, drags, label='Drag') # Configurações do gráfico -plt.title('Gráfico de Arrasto em função da Velocidade') +plt.title('Gráfico de Arrasto do carro em função da Velocidade') plt.xlabel('Velocidade (m/s)') plt.ylabel('Força (N)') plt.legend() plt.grid(True) - # Plotagem do gráfico 2 -plt.subplot(1,2,2) +plt.subplot(2,2,2) plt.plot(velocidades, lifts, label='Downforce', linestyle='--') # Configurações do gráfico2 -plt.title('Gráfico de Downforce em função da Velocidade') +plt.title('Gráfico de Downforce do carro em função da Velocidade') plt.xlabel('Velocidade (m/s)') plt.ylabel('Força (N)') plt.legend() plt.grid(True) -plt.show() - +# Plotagem do gráfico 3 +plt.subplot(2,2,3) +plt.plot(velocidades, drags_w, label='Drag') +# Configurações do gráfico +plt.title('Gráfico de Arrasto da Asa em função da Velocidade') +plt.xlabel('Velocidade (m/s)') +plt.ylabel('Força (N)') +plt.legend() +plt.grid(True) +# Plotagem do gráfico 4 +plt.subplot(2,2,4) +plt.plot(velocidades, lifts_w, label='Lift', linestyle='--') +# Configurações do gráfico +plt.title('Gráfico de Lift da Asa em função da Velocidade') +plt.xlabel('Velocidade (m/s)') +plt.ylabel('Força (N)') +plt.legend() +plt.grid(True) +plt.tight_layout() +plt.show() \ No newline at end of file From 81b8f69010f61ab4d8283d922193f54e4e77495c Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Mon, 24 Jun 2024 15:56:42 -0300 Subject: [PATCH 04/25] =?UTF-8?q?Update=20For=C3=A7as=20Aerodinamicas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "For\303\247as Aerodinamicas.py" | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git "a/For\303\247as Aerodinamicas.py" "b/For\303\247as Aerodinamicas.py" index fb24f7b..75fe922 100644 --- "a/For\303\247as Aerodinamicas.py" +++ "b/For\303\247as Aerodinamicas.py" @@ -12,8 +12,8 @@ def __init__(self,frontal_area,planform_area,air_density,coeficient_drag_p,coefi self.cl= coeficient_lift self.x= length self.ni= ni - self.a1= a1 - self.a2= a2 + # self.a1= a1 + # self.a2= a2 self.p_atm= atm_pressure self.alpha= attack_angle self.a_wing=span*chord @@ -26,22 +26,23 @@ def aerodynamic_forces(self,velocidade): # Função para lift=self.cl * pressao_dinamica * self.asup return drag,lift -# def numero_Reynolds(self,velocidade): # Função para encontrar o numero de reynolds +# def numero_Reynolds(self,velocidade): # Função para encontrar o numero de reynolds return (self.x*velocidade)/self.ni # def bernoulli(self,velocidade): v2=velocidade*self.a1/self.a2 # Equação de conservação para encontrar a velocidade embaixo do carro p2=self.p_atm +0.5*rho*velocidade**2 - 0.5*v2**2 # Equação de bernoulli para encontrar a pressão embaixo do carro - deltap=self.p_atm-p2 # Diferença de pressão entre a parte de baixo e a parte de cima do carro - return deltap + deltap=p2-self.p_atm # Diferença de pressão entre a parte de baixo e a parte de cima do carro + downforce=deltap*self.asup + return downforce - def aerodynamics_forces_wing(self,velocidade): #Função para calcular as forças aerodinamicas geradas pela asa + def aerodynamics_forces_wing(self,velocidade): #Função para calcular as forças aerodinamicas geradas pela asa pressao_dinamica=0.5*self.rho*(velocidade**2) cl_alpha=(2*math.pi)/(1+(2/self.AR)) - cl_wing=cl_alpha*self.alpha #Coeficiente de lift da asa - cd_induced=(1/(math.pi*self.AR))*cl_wing**2 #Coeficiente de arrasto induzido - cd_wing=cd_induced+0.05 #Coeficiente de arrasto total da asa= Cd=Cd_induzido+Cd_forma - drag_wing= (cd_wing * pressao_dinamica * self.a_wing) #Calculo da força de arrasto + cl_wing=cl_alpha*self.alpha #Coeficiente de lift da asa + cd_induced=(1/(math.pi*self.AR))*(cl_wing**2) #Coeficiente de arrasto induzido + cd_wing=cd_induced+0.05 #Coeficiente de arrasto total da asa= Cd=Cd_induzido+Cd_forma + drag_wing= (cd_wing * pressao_dinamica * self.a_wing) #Calculo da força de arrasto lift_wing= (cl_wing * pressao_dinamica * self.a_wing) return drag_wing,lift_wing @@ -63,7 +64,7 @@ def aerodynamics_forces_wing(self,velocidade): #Função chord=0.25 #Comprimento da asa (m) span=1 #Largura da asa (m) thickness=0.05 #Expessura máxima da asa (m) -alpha=math.radians(3.75) #Ângulo de incidencia do vento com a asa (radianos) +alpha=math.radians(-3.75) #Ângulo de incidencia do vento com a asa (radianos) carro=forcas_aerodinamicas(af,a_sup,rho,cd_p,cd_f,cl,length,ni,p_atm,alpha,span,chord) #Definição do objeto From 0186b71c3f3b335d74fef5a88d34ee9277ebafad Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Mon, 24 Jun 2024 15:57:57 -0300 Subject: [PATCH 05/25] Create Arrefecimento.py Inicio do desenvolvimento do software para arrefecimento dos freios --- Arrefecimento.py | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Arrefecimento.py diff --git a/Arrefecimento.py b/Arrefecimento.py new file mode 100644 index 0000000..8492fdd --- /dev/null +++ b/Arrefecimento.py @@ -0,0 +1,74 @@ +import numpy as np +import matplotlib.pyplot as plt +import math + +class Arrefecimento(): + def __init__(self,area_resfriamento,area_entrada,densidade_ar,coeficiente_transferencia,calor_especifico_ar,temp_ar,temp_objeto,velocidade_ar,viscosidade_ar,condutividade_ar): + self.a_res= area_resfriamento + self.a_ent= area_entrada + self.rho= densidade_ar + self.h= coeficiente_transferencia + self.c= calor_especifico_ar + self.tf=temp_ar + self.to=temp_objeto + self.ni=viscosidade_ar + self.k_ar=condutividade_ar + self.Vazao= velocidade_ar*area_entrada*densidade_ar + + def convecção(self): #Equação base para convecção + Qconvecção= self.h*self.a_res*(self.to-self.tf) + return Qconvecção + + def calorimetria(self,velocidade): #Equação de calorimetria + calor=self.c*(self.to-self.tf)*(self.rho*self.a_ent*velocidade) + return calor + + def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleração,velocidade_carro,rho_f,volume_f,c_f,t_parada,t_res,n_frenagem): + rey_rotacional= (omega_inicial*(diametro_freio**2))/self.ni + if rey_rotacional > 10**6: + h_freio=0.4*(self.k_ar/diametro_freio)*(rey_rotacional**0.8) #Coeficiente de convecção do freio para fluxo turbulento + else: + h_freio=0.7*(self.k_ar/diametro_freio)*(rey_rotacional**0.55) #Coeficiente de convecção do freio para laminar + calor=h_freio*self.a_res*(self.to-self.tf) #Calor dissipado por convecção + P_bav=massa_carro*aceleração*(velocidade_carro/2) # Potencia media após uma frenagem + deltaT= (P_bav*t_parada)/(rho_f*c_f*volume_f) + + # Calcular os denominadores comuns + denominator_common = (h_freio*self.a_res*t_res)/(rho_f*c_f*volume_f) + + # Calcular o numerador e o denominador separadamente + numerator = (1 - math.exp(-n_frenagem * denominator_common)) * deltaT + denominator = 1 - math.exp(-denominator_common) + # Calcular a diferença de temperatura + temperature_change = (numerator / denominator) + self.tf + + return temperature_change + + +#Parâmetros físicos do ar: +rho= 1.184 #densidade(kg/m^3) +c_ar= 1007 #Calor especifico (J/(kg*K)) +mi= 1.849*10**-5 #Viscosidade dinâmica (kg/m*s) +k_ar=0.02551 #Condutividade termica do ar (W/m*K) +ni= (mi/rho) #Viscosidade Cinematica (m²/s) +#parâmetros para a troca termica +temp_ar=25 #temperatura do ar (°C) +a_ent=0.004 #Área de admissão do vento para arrefecimento (m^2) +a_res = 1 #Área de resfriamento da bateria (m^2) +h= 300 #Coeficiente de transferência de calor (W/m2*K) +v=5 +#Parametros para arrefecimento dos freios, considerando que o material do freio é o aço 1020 +omega_inicial=50 +diametro_freio=0.15 +a_freio= math.pi*(diametro_freio/2)**2 +temp_freio=100 +volume_freio=a_freio*0.004 +rho_freio=7900 #densidade(kg/m^3) +c_freio=420 #Calor especifico (J/(kg*K)) +k_freio=169200 #Condutividade termica do ar (W/m*K) + + +freio_1=Arrefecimento(a_freio,a_ent,rho,h,c_ar,temp_ar,temp_freio,v,ni,k_ar) +temp_final=freio_1.arrefecimento_freio(omega_inicial,diametro_freio,230,1,15,rho_freio,volume_freio,c_freio,1,15,2) +print(f"{temp_final:0.2f} °C") +print(f"{freio_1.calorimetria(v)}") From 9e41cc8eba580cca196f82c8c0d111fc9186590a Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Tue, 25 Jun 2024 21:21:20 -0300 Subject: [PATCH 06/25] Update Arrefecimento.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atualização do código de arrefecimento para freios. Adicionado o gráfico da variação de temperatura do freio após n frenagens --- Arrefecimento.py | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/Arrefecimento.py b/Arrefecimento.py index 8492fdd..386b010 100644 --- a/Arrefecimento.py +++ b/Arrefecimento.py @@ -15,14 +15,6 @@ def __init__(self,area_resfriamento,area_entrada,densidade_ar,coeficiente_transf self.k_ar=condutividade_ar self.Vazao= velocidade_ar*area_entrada*densidade_ar - def convecção(self): #Equação base para convecção - Qconvecção= self.h*self.a_res*(self.to-self.tf) - return Qconvecção - - def calorimetria(self,velocidade): #Equação de calorimetria - calor=self.c*(self.to-self.tf)*(self.rho*self.a_ent*velocidade) - return calor - def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleração,velocidade_carro,rho_f,volume_f,c_f,t_parada,t_res,n_frenagem): rey_rotacional= (omega_inicial*(diametro_freio**2))/self.ni if rey_rotacional > 10**6: @@ -31,7 +23,7 @@ def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleraç h_freio=0.7*(self.k_ar/diametro_freio)*(rey_rotacional**0.55) #Coeficiente de convecção do freio para laminar calor=h_freio*self.a_res*(self.to-self.tf) #Calor dissipado por convecção P_bav=massa_carro*aceleração*(velocidade_carro/2) # Potencia media após uma frenagem - deltaT= (P_bav*t_parada)/(rho_f*c_f*volume_f) + deltaT= (P_bav*t_parada)/(rho_f*c_f*volume_f) #Calculo da diferença de temperatura após a frenagem # Calcular os denominadores comuns denominator_common = (h_freio*self.a_res*t_res)/(rho_f*c_f*volume_f) @@ -40,10 +32,9 @@ def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleraç numerator = (1 - math.exp(-n_frenagem * denominator_common)) * deltaT denominator = 1 - math.exp(-denominator_common) # Calcular a diferença de temperatura - temperature_change = (numerator / denominator) + self.tf - - return temperature_change + temperature_change = (numerator / denominator) + self.tf #Calculo da temperatura do freio após n frenagens e tendo resfriamento a ar + return temperature_change #Parâmetros físicos do ar: rho= 1.184 #densidade(kg/m^3) @@ -52,13 +43,19 @@ def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleraç k_ar=0.02551 #Condutividade termica do ar (W/m*K) ni= (mi/rho) #Viscosidade Cinematica (m²/s) #parâmetros para a troca termica -temp_ar=25 #temperatura do ar (°C) +temp_ar=30 #temperatura do ar (°C) a_ent=0.004 #Área de admissão do vento para arrefecimento (m^2) a_res = 1 #Área de resfriamento da bateria (m^2) h= 300 #Coeficiente de transferência de calor (W/m2*K) -v=5 +#Parametros do carro +v=8.88 +massa_c=230 +a=1 +t_parada=4.5 +t_res=90 + #Parametros para arrefecimento dos freios, considerando que o material do freio é o aço 1020 -omega_inicial=50 +omega_inicial=30 diametro_freio=0.15 a_freio= math.pi*(diametro_freio/2)**2 temp_freio=100 @@ -67,8 +64,15 @@ def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleraç c_freio=420 #Calor especifico (J/(kg*K)) k_freio=169200 #Condutividade termica do ar (W/m*K) - freio_1=Arrefecimento(a_freio,a_ent,rho,h,c_ar,temp_ar,temp_freio,v,ni,k_ar) -temp_final=freio_1.arrefecimento_freio(omega_inicial,diametro_freio,230,1,15,rho_freio,volume_freio,c_freio,1,15,2) -print(f"{temp_final:0.2f} °C") -print(f"{freio_1.calorimetria(v)}") +# Calcular a temperatura para diferentes números de frenagens +n_frenagens = np.arange(1, 100) +temperaturas = [freio_1.arrefecimento_freio(omega_inicial, diametro_freio, massa_c, a, v, rho_freio, volume_freio, c_freio, t_parada, t_res, n) for n in n_frenagens] + +# Plotar o gráfico +plt.plot(n_frenagens, temperaturas) +plt.xlabel('Número de frenagens') +plt.ylabel('Temperatura (°C)') +plt.title('Temperatura em função do número de frenagens') +plt.grid(True) +plt.show() \ No newline at end of file From 8d2113fe3f744f76c8c09fbdcbd701d5080bf648 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Fri, 28 Jun 2024 17:27:21 -0300 Subject: [PATCH 07/25] =?UTF-8?q?Adicionado=20as=20equa=C3=A7=C3=B5es=20pa?= =?UTF-8?q?ra=20arrefecimento=20do=20sistema=20de=20pwt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adicionado as equações para encontrar o coeficiente de convecção para o sistema de pwt , e a equação para arrefecimento por convecção --- Arrefecimento.py | 93 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/Arrefecimento.py b/Arrefecimento.py index 386b010..7107386 100644 --- a/Arrefecimento.py +++ b/Arrefecimento.py @@ -3,26 +3,43 @@ import math class Arrefecimento(): - def __init__(self,area_resfriamento,area_entrada,densidade_ar,coeficiente_transferencia,calor_especifico_ar,temp_ar,temp_objeto,velocidade_ar,viscosidade_ar,condutividade_ar): + def __init__(self,area_resfriamento,area_entrada,densidade_ar,calor_especifico_ar,temp_ar,temp_objeto,velocidade,viscosidade_cinematica_ar,condutividade_ar,viscosidade_dinamica_ar): self.a_res= area_resfriamento self.a_ent= area_entrada self.rho= densidade_ar - self.h= coeficiente_transferencia self.c= calor_especifico_ar + self.v=velocidade self.tf=temp_ar self.to=temp_objeto - self.ni=viscosidade_ar + self.mi=viscosidade_dinamica_ar + self.ni=viscosidade_cinematica_ar self.k_ar=condutividade_ar - self.Vazao= velocidade_ar*area_entrada*densidade_ar + self.Vazao= velocidade*area_entrada*densidade_ar - def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleração,velocidade_carro,rho_f,volume_f,c_f,t_parada,t_res,n_frenagem): + def arrefecimento_pwt(self,comprimento,massa,condutividade,temp_desejada): + Pr=(self.mi*self.c)/self.k_ar # Calculo do numero de Prandlt + Rey=comprimento*self.v/self.ni # Calculo do numero de Reynolds + + if Rey < 200000: + Nul= (0.664 * (Rey**0.5) * (Pr**(1/3))) + else: + X=200000*self.ni/self.v #Comprimento da placa onde o fluxo transiciona para turbulento + Rey_X =X*self.v/self.ni # Calculo do numero de Reynolds para a distância X + A= 0.037*(Rey_X**0.8) - 0.664*(Rey_X**0.5) + Nul= (0.037 * ((Rey**0.8) - A) * (Pr**(1/3))) + h_pwt= Nul * self.k_ar/comprimento + qconv = h_pwt*self.a_res*(self.to - self.tf) + quantidade_de_calor= massa*condutividade*(temp_desejada-self.to) + return qconv,quantidade_de_calor + + def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleração,rho_f,volume_f,c_f,t_parada,t_res,n_frenagem): rey_rotacional= (omega_inicial*(diametro_freio**2))/self.ni if rey_rotacional > 10**6: h_freio=0.4*(self.k_ar/diametro_freio)*(rey_rotacional**0.8) #Coeficiente de convecção do freio para fluxo turbulento else: h_freio=0.7*(self.k_ar/diametro_freio)*(rey_rotacional**0.55) #Coeficiente de convecção do freio para laminar calor=h_freio*self.a_res*(self.to-self.tf) #Calor dissipado por convecção - P_bav=massa_carro*aceleração*(velocidade_carro/2) # Potencia media após uma frenagem + P_bav=massa_carro*aceleração*(self.v/2) #Potencia media após uma frenagem deltaT= (P_bav*t_parada)/(rho_f*c_f*volume_f) #Calculo da diferença de temperatura após a frenagem # Calcular os denominadores comuns @@ -42,32 +59,56 @@ def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleraç mi= 1.849*10**-5 #Viscosidade dinâmica (kg/m*s) k_ar=0.02551 #Condutividade termica do ar (W/m*K) ni= (mi/rho) #Viscosidade Cinematica (m²/s) -#parâmetros para a troca termica -temp_ar=30 #temperatura do ar (°C) -a_ent=0.004 #Área de admissão do vento para arrefecimento (m^2) -a_res = 1 #Área de resfriamento da bateria (m^2) -h= 300 #Coeficiente de transferência de calor (W/m2*K) +temp_ar=25 #temperatura do ar (°C) + #Parametros do carro -v=8.88 -massa_c=230 -a=1 -t_parada=4.5 -t_res=90 +a_ent=0.004 #Área de admissão do vento para arrefecimento (m^2) +v=8.88 #Velocidade do carro (m/s) +massa_c=230 #massa do carro (kg) +a=1 #Aceleração horizontal do carro (m/s^2) +t_parada=4 #Tempo que leva até o carro parar após uma frenagem (s) +t_res=90 #Tempo de resfriamento entre as frenagem (s) +#Parametros de PWT + #Motor +massa_motor=70 #Massa do motor (kg) +temp_motor=100 #Temperatura do motor (°C) +comprimento_motor=0.6 #Comprimento do motor (m) +largura_motor=0.3 #Largura do motor (m) +a_res_motor= largura_motor*comprimento_motor #Área de resfriamento do motor (m^2) +c_motor=420 #Calor especifico (J/(kg*K)) + #Bateria +massa_bateria=50 #Massa do Pack de baterias (kg) +temp_bateria=100 #Temperatura do pack de baterias(°C) +comprimento_bateria=0.5 #Comprimento do pack de baterias (m) +largura_bateria=0.5 #largura do pack de baterias (m) +a_res_bateria= largura_bateria*comprimento_bateria #Área de resfriamento do motor (m^2) +c_bateria=420 #Calor especifico (J/(kg*K)) #Parametros para arrefecimento dos freios, considerando que o material do freio é o aço 1020 -omega_inicial=30 -diametro_freio=0.15 -a_freio= math.pi*(diametro_freio/2)**2 -temp_freio=100 -volume_freio=a_freio*0.004 -rho_freio=7900 #densidade(kg/m^3) -c_freio=420 #Calor especifico (J/(kg*K)) -k_freio=169200 #Condutividade termica do ar (W/m*K) +omega_inicial=50 #Velocidade angular inicial das rodas (rad/s) +diametro_freio=0.17 #Diametro do disco de freio (m) +a_freio= math.pi*(diametro_freio/2)**2 #Área do disco de freio (m^2) +temp_freio=100 #Temperatura do freio (°C) +volume_freio=a_freio*0.008 #área do disco de freio * expessura dele +rho_freio=7900 #densidade(kg/m^3) +c_freio=420 #Calor especifico (J/(kg*K)) +k_freio=169200 #Condutividade termica do ar (W/m*K) + +motor=Arrefecimento(a_res_motor,a_ent,rho,c_ar,temp_ar,temp_motor,v,ni,k_ar,mi) #Definição do objeto para motor +bateria=Arrefecimento(a_res_bateria,a_ent,rho,c_ar,temp_ar,temp_bateria,v,ni,k_ar,mi) #Definição do objeto para bateria +freio_1=Arrefecimento(a_freio,a_ent,rho,c_ar,temp_ar,temp_freio,v,ni,k_ar,mi) #Definição do objeto para freio + +#Arrefecimento para PWT +calor_abs_motor,calor_necessario_motor=motor.arrefecimento_pwt(comprimento_motor,massa_motor,c_motor,80) +print(f"O arrefecimento do motor absorbe {calor_abs_motor:0.2f} J/s") +print(f"O motor precisa perder {calor_necessario_motor:0.2f} J para descer até a temperatura ideal") -freio_1=Arrefecimento(a_freio,a_ent,rho,h,c_ar,temp_ar,temp_freio,v,ni,k_ar) +calor_abs_bateria,calor_necessario_bateria=bateria.arrefecimento_pwt(comprimento_bateria,massa_bateria,c_bateria,80) +print(f"O arrefecimento da bateria absorbe {calor_abs_bateria:0.2f} J/s") +print(f"A bateria precisa perder {calor_necessario_bateria:0.2f} J para descer até a temperatura ideal") # Calcular a temperatura para diferentes números de frenagens n_frenagens = np.arange(1, 100) -temperaturas = [freio_1.arrefecimento_freio(omega_inicial, diametro_freio, massa_c, a, v, rho_freio, volume_freio, c_freio, t_parada, t_res, n) for n in n_frenagens] +temperaturas = [freio_1.arrefecimento_freio(omega_inicial, diametro_freio, massa_c, a,rho_freio, volume_freio, c_freio, t_parada, t_res, n) for n in n_frenagens] # Plotar o gráfico plt.plot(n_frenagens, temperaturas) From d0cff8410507c5fa3a64959f3439343cad3fa859 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Fri, 12 Jul 2024 22:44:20 -0300 Subject: [PATCH 08/25] =?UTF-8?q?Adicionado=20=20gr=C3=A1fico=20de=20resfr?= =?UTF-8?q?iamento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adicionado o calculo do tempo para resfriar os objetos até uma temperatura desejada, juntamente com um gráfico mostrando o comportamento da temperatura. OBS: Ainda é necessário refatorar o código --- Arrefecimento.py | 231 +++++++++++++++++++++++++++++------------------ 1 file changed, 143 insertions(+), 88 deletions(-) diff --git a/Arrefecimento.py b/Arrefecimento.py index 7107386..c69d0a8 100644 --- a/Arrefecimento.py +++ b/Arrefecimento.py @@ -3,9 +3,8 @@ import math class Arrefecimento(): - def __init__(self,area_resfriamento,area_entrada,densidade_ar,calor_especifico_ar,temp_ar,temp_objeto,velocidade,viscosidade_cinematica_ar,condutividade_ar,viscosidade_dinamica_ar): + def __init__(self,area_resfriamento,densidade_ar,calor_especifico_ar,temp_ar,temp_objeto,velocidade,viscosidade_cinematica_ar,condutividade_ar,viscosidade_dinamica_ar,massa,temp_desejada,calor_especifico_objeto): self.a_res= area_resfriamento - self.a_ent= area_entrada self.rho= densidade_ar self.c= calor_especifico_ar self.v=velocidade @@ -14,106 +13,162 @@ def __init__(self,area_resfriamento,area_entrada,densidade_ar,calor_especifico_a self.mi=viscosidade_dinamica_ar self.ni=viscosidade_cinematica_ar self.k_ar=condutividade_ar - self.Vazao= velocidade*area_entrada*densidade_ar - - def arrefecimento_pwt(self,comprimento,massa,condutividade,temp_desejada): - Pr=(self.mi*self.c)/self.k_ar # Calculo do numero de Prandlt - Rey=comprimento*self.v/self.ni # Calculo do numero de Reynolds + self.m=massa + self.temp_desejada= temp_desejada + self.c_objeto=calor_especifico_objeto + def arrefecimento_pwt(self,comprimento): + Pr=(self.mi*self.c)/self.k_ar # Cálculo do número de Prandlt + Rey=comprimento*self.v/self.ni # Cálculo do número de Reynolds - if Rey < 200000: + if Rey < 200000: #Fluxo laminar Nul= (0.664 * (Rey**0.5) * (Pr**(1/3))) - else: - X=200000*self.ni/self.v #Comprimento da placa onde o fluxo transiciona para turbulento - Rey_X =X*self.v/self.ni # Calculo do numero de Reynolds para a distância X + else: #Fluxo Turbulento + X=200000*self.ni/self.v # Comprimento da placa onde o fluxo transiciona para turbulento + Rey_X= X*self.v/self.ni # Cálculo do número de Reynolds para a distância X A= 0.037*(Rey_X**0.8) - 0.664*(Rey_X**0.5) - Nul= (0.037 * ((Rey**0.8) - A) * (Pr**(1/3))) - h_pwt= Nul * self.k_ar/comprimento - qconv = h_pwt*self.a_res*(self.to - self.tf) - quantidade_de_calor= massa*condutividade*(temp_desejada-self.to) - return qconv,quantidade_de_calor + Nul= (0.037 * ((Rey**0.8) - A) * (Pr**(1/3))) # Cálculo do número de Nusselt + h_pwt= Nul * self.k_ar/comprimento # Cálculo do coeficiente de convecção + flag = 0 + flag_tempo = 0 + temp_atual = self.to + quantidade_de_calor = self.m * self.c_objeto * (self.temp_desejada - temp_atual) # Cálculo da quantidade de calor que o objeto precisa perder para alcançar a temperatura ideal + temp_grafico = [] + tempo_grafico = [] + while flag == 0: + qconv = h_pwt * self.a_res * (temp_atual - self.tf) # Calor absorvido por convecção + temp_final = (-qconv / (self.m * self.c_objeto)) + temp_atual + temp_atual = temp_final + temp_grafico.append(temp_atual) + tempo_grafico.append(flag_tempo) + flag_tempo += 1 + if temp_final <= self.temp_desejada: + flag = 1 + return quantidade_de_calor, flag_tempo, temp_grafico, tempo_grafico - def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleração,rho_f,volume_f,c_f,t_parada,t_res,n_frenagem): + def arrefecimento_freio(self,omega_inicial,diametro_freio,massa_carro,aceleração,rho_f,volume_f,t_parada,t_res,n_frenagem): rey_rotacional= (omega_inicial*(diametro_freio**2))/self.ni if rey_rotacional > 10**6: - h_freio=0.4*(self.k_ar/diametro_freio)*(rey_rotacional**0.8) #Coeficiente de convecção do freio para fluxo turbulento + h_freio=0.4*(self.k_ar/diametro_freio)*(rey_rotacional**0.8) # Coeficiente de convecção do freio para fluxo turbulento else: - h_freio=0.7*(self.k_ar/diametro_freio)*(rey_rotacional**0.55) #Coeficiente de convecção do freio para laminar - calor=h_freio*self.a_res*(self.to-self.tf) #Calor dissipado por convecção - P_bav=massa_carro*aceleração*(self.v/2) #Potencia media após uma frenagem - deltaT= (P_bav*t_parada)/(rho_f*c_f*volume_f) #Calculo da diferença de temperatura após a frenagem - + h_freio=0.7*(self.k_ar/diametro_freio)*(rey_rotacional**0.55) # Coeficiente de convecção do freio para laminar + P_bav=massa_carro*aceleração*(self.v/2) # Potência média após uma frenagem + deltaT= (P_bav*t_parada)/(rho_f*self.c_objeto*volume_f) # Cálculo da diferença de temperatura após a frenagem # Calcular os denominadores comuns - denominator_common = (h_freio*self.a_res*t_res)/(rho_f*c_f*volume_f) - - # Calcular o numerador e o denominador separadamente - numerator = (1 - math.exp(-n_frenagem * denominator_common)) * deltaT - denominator = 1 - math.exp(-denominator_common) - # Calcular a diferença de temperatura - temperature_change = (numerator / denominator) + self.tf #Calculo da temperatura do freio após n frenagens e tendo resfriamento a ar + denominator_common = (h_freio*self.a_res*t_res)/(rho_f*self.c_objeto*volume_f) + Temp_frenagem=[] + for n in range (n_frenagem): + # Calcular o numerador e o denominador separadamente + numerator = (1 - math.exp(-n * denominator_common)) * deltaT + denominator = 1 - math.exp(-denominator_common) + # Calcular a diferença de temperatura + temperature_change = (numerator / denominator) + self.tf # Cálculo da temperatura do freio após n frenagens e tendo resfriamento a ar + Temp_frenagem.append(temperature_change) + flag2 = 0 + flag_tempo2 = 0 + temp_atual_f = Temp_frenagem.pop() + quantidade_de_calor_f = self.m * self.c_objeto * (self.temp_desejada - temp_atual_f) # Cálculo da quantidade de calor que o objeto precisa perder para alcançar a temperatura ideal + temp_grafico_f = [] + tempo_grafico_f = [] + while flag2 == 0: + qconv_f = h_freio * self.a_res * (temp_atual_f - self.tf) # Calor absorvido por convecção + temp_final_f = (-qconv_f / (self.m * self.c_objeto)) + temp_atual_f + temp_atual_f = temp_final_f + temp_grafico_f.append(temp_atual_f) + tempo_grafico_f.append(flag_tempo2) + flag_tempo2 += 1 + if temp_final_f <= self.temp_desejada: + flag2 = 1 + return Temp_frenagem,quantidade_de_calor_f,flag_tempo2,temp_grafico_f,tempo_grafico_f +# Parâmetros físicos do ar: +rho = 1.184 # Densidade(kg/m^3) +c_ar = 1007 # Calor especifico (J/(kg*K)) +mi = 1.849*10**-5 # Viscosidade dinâmica (kg/m*s) +k_ar = 0.02551 # Condutividade termica do ar (W/m*K) +ni = (mi/rho) # Viscosidade Cinematica (m²/s) +temp_ar = 25 # Temperatura do ar (°C) - return temperature_change +# Parametros do carro +a_ent = 0.004 # Área de admissão do vento para arrefecimento (m^2) +v = 10 # Velocidade do carro (m/s) +massa_c = 230 # Massa do carro (kg) +a = 1 # Aceleração horizontal do carro (m/s^2) +t_parada = 4 # Tempo que leva até o carro parar após uma frenagem (s) +t_res = 90 # Tempo de resfriamento entre as frenagem (s) -#Parâmetros físicos do ar: -rho= 1.184 #densidade(kg/m^3) -c_ar= 1007 #Calor especifico (J/(kg*K)) -mi= 1.849*10**-5 #Viscosidade dinâmica (kg/m*s) -k_ar=0.02551 #Condutividade termica do ar (W/m*K) -ni= (mi/rho) #Viscosidade Cinematica (m²/s) -temp_ar=25 #temperatura do ar (°C) +# Parametros de PWT + # Motor +massa_motor = 70 # Massa do motor (kg) +temp_motor = 100 # Temperatura do motor (°C) +comprimento_motor = 0.6 # Comprimento do motor (m) +largura_motor = 0.3 # Largura do motor (m) +a_res_motor = largura_motor*comprimento_motor # Área de resfriamento do motor (m^2) +c_motor = 420 # Calor especifico (J/(kg*K)) + # Bateria +massa_bateria = 50 # Massa do Pack de baterias (kg) +temp_bateria = 100 # Temperatura do pack de baterias(°C) +comprimento_bateria = 0.5 # Comprimento do pack de baterias (m) +largura_bateria = 0.5 # largura do pack de baterias (m) +a_res_bateria = largura_bateria*comprimento_bateria # Área de resfriamento do motor (m^2) +c_bateria = 420 # Calor especifico (J/(kg*K)) +temp_ideal=40 +# Parametros para arrefecimento dos freios, considerando que o material do freio é o aço 1020 +omega_inicial = 50 # Velocidade angular inicial das rodas (rad/s) +diametro_freio = 0.17 # Diametro do disco de freio (m) +a_freio = math.pi*(diametro_freio/2)**2 # Área do disco de freio (m^2) +volume_freio = a_freio*0.008 # Área do disco de freio * expessura dele +rho_freio = 7900 # Densidade(kg/m^3) +c_freio = 420 # Calor especifico (J/(kg*K)) +k_freio = 169200 # Condutividade termica do freio (W/m*K) +massa_freio=volume_freio*rho_freio +temp_ideal_f=60 +n_frenagens = 100 +motor = Arrefecimento(a_res_motor,rho,c_ar,temp_ar,temp_motor,v,ni,k_ar,mi,massa_motor,temp_ideal,c_motor) # Definição do objeto para motor +bateria = Arrefecimento(a_res_bateria,rho,c_ar,temp_ar,temp_bateria,v,ni,k_ar,mi,massa_bateria,temp_ideal,c_bateria) # Definição do objeto para bateria +freio_1 = Arrefecimento(a_freio,rho,c_ar,temp_ar,1,v,ni,k_ar,mi,massa_freio,temp_ideal_f,c_freio) # Definição do objeto para freio -#Parametros do carro -a_ent=0.004 #Área de admissão do vento para arrefecimento (m^2) -v=8.88 #Velocidade do carro (m/s) -massa_c=230 #massa do carro (kg) -a=1 #Aceleração horizontal do carro (m/s^2) -t_parada=4 #Tempo que leva até o carro parar após uma frenagem (s) -t_res=90 #Tempo de resfriamento entre as frenagem (s) -#Parametros de PWT - #Motor -massa_motor=70 #Massa do motor (kg) -temp_motor=100 #Temperatura do motor (°C) -comprimento_motor=0.6 #Comprimento do motor (m) -largura_motor=0.3 #Largura do motor (m) -a_res_motor= largura_motor*comprimento_motor #Área de resfriamento do motor (m^2) -c_motor=420 #Calor especifico (J/(kg*K)) - #Bateria -massa_bateria=50 #Massa do Pack de baterias (kg) -temp_bateria=100 #Temperatura do pack de baterias(°C) -comprimento_bateria=0.5 #Comprimento do pack de baterias (m) -largura_bateria=0.5 #largura do pack de baterias (m) -a_res_bateria= largura_bateria*comprimento_bateria #Área de resfriamento do motor (m^2) -c_bateria=420 #Calor especifico (J/(kg*K)) +# Arrefecimento para PWT +calor_necessario_motor, tempo_resfriamento_motor, temperatura_motor, tempo_motor = motor.arrefecimento_pwt(comprimento_motor) +print(f"O motor precisa perder {calor_necessario_motor:0.2f} J para esfriar até a temperatura ideal") +print(f"O motor leva {(tempo_resfriamento_motor)/60:0.2f} minutos esfriar até a temperatura ideal\n") -#Parametros para arrefecimento dos freios, considerando que o material do freio é o aço 1020 -omega_inicial=50 #Velocidade angular inicial das rodas (rad/s) -diametro_freio=0.17 #Diametro do disco de freio (m) -a_freio= math.pi*(diametro_freio/2)**2 #Área do disco de freio (m^2) -temp_freio=100 #Temperatura do freio (°C) -volume_freio=a_freio*0.008 #área do disco de freio * expessura dele -rho_freio=7900 #densidade(kg/m^3) -c_freio=420 #Calor especifico (J/(kg*K)) -k_freio=169200 #Condutividade termica do ar (W/m*K) - -motor=Arrefecimento(a_res_motor,a_ent,rho,c_ar,temp_ar,temp_motor,v,ni,k_ar,mi) #Definição do objeto para motor -bateria=Arrefecimento(a_res_bateria,a_ent,rho,c_ar,temp_ar,temp_bateria,v,ni,k_ar,mi) #Definição do objeto para bateria -freio_1=Arrefecimento(a_freio,a_ent,rho,c_ar,temp_ar,temp_freio,v,ni,k_ar,mi) #Definição do objeto para freio - -#Arrefecimento para PWT -calor_abs_motor,calor_necessario_motor=motor.arrefecimento_pwt(comprimento_motor,massa_motor,c_motor,80) -print(f"O arrefecimento do motor absorbe {calor_abs_motor:0.2f} J/s") -print(f"O motor precisa perder {calor_necessario_motor:0.2f} J para descer até a temperatura ideal") - -calor_abs_bateria,calor_necessario_bateria=bateria.arrefecimento_pwt(comprimento_bateria,massa_bateria,c_bateria,80) -print(f"O arrefecimento da bateria absorbe {calor_abs_bateria:0.2f} J/s") -print(f"A bateria precisa perder {calor_necessario_bateria:0.2f} J para descer até a temperatura ideal") +calor_necessario_bateria, tempo_resfriamento_bateria, temperatura_bateria, tempo_bateria=bateria.arrefecimento_pwt(comprimento_bateria) +print(f"A bateria precisa perder {calor_necessario_bateria:0.2f} J para esfriar até a temperatura ideal") +print(f"O motor leva {(tempo_resfriamento_bateria/60):0.2f} minutos esfriar até a temperatura ideal\n") # Calcular a temperatura para diferentes números de frenagens -n_frenagens = np.arange(1, 100) -temperaturas = [freio_1.arrefecimento_freio(omega_inicial, diametro_freio, massa_c, a,rho_freio, volume_freio, c_freio, t_parada, t_res, n) for n in n_frenagens] +temperaturas,calor_necessario_freio,tempo_resfriamento_freio,temperatura_freio,tempo_freio= freio_1.arrefecimento_freio(omega_inicial, diametro_freio, massa_c, a,rho_freio, volume_freio, t_parada, t_res, n_frenagens) +print(f"O freio precisa perder {calor_necessario_freio:0.2f} J para esfriar até a temperatura ideal") +print(f"O freio leva {(tempo_resfriamento_freio)/60:0.2f} minutos esfriar até a temperatura ideal\n") -# Plotar o gráfico -plt.plot(n_frenagens, temperaturas) +# Plotar o gráfico de freio +plt.figure(figsize=(12, 6)) +plt.subplot(1,2,1) +plt.plot(temperaturas) plt.xlabel('Número de frenagens') plt.ylabel('Temperatura (°C)') -plt.title('Temperatura em função do número de frenagens') +plt.title('Temperatura do freio em função do número de frenagens') +plt.grid(True) +# Plotar o gráfico do resfriamento do freio +plt.subplot(1,2,2) +plt.plot(tempo_freio, temperatura_freio) +plt.xlabel('Tempo(s)') +plt.ylabel('Temperatura (°C)') +plt.title('Temperatura do freio em função do Tempo') +plt.grid(True) +plt.show() + +# Plotar o gráfico das Temperaturas de PWT +plt.figure(figsize=(12, 6)) +plt.subplot(1,2,1) +plt.plot(tempo_motor, temperatura_motor) +plt.title('Gráfico de Temperatura do motor em função do tempo') +plt.xlabel('Tempo(s)') +plt.ylabel('Temperatura (°C)') +plt.grid(True) + +plt.subplot(1,2,2) +plt.plot(tempo_bateria, temperatura_bateria) +plt.title('Gráfico de Temperatura da bateria em função do tempo') +plt.xlabel('Tempo(s)') +plt.ylabel('Temperatura (°C)') plt.grid(True) -plt.show() \ No newline at end of file +plt.show() From 8933586a026679825fcf72a0c22dffc5c74ee273 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Sun, 14 Jul 2024 20:16:24 -0300 Subject: [PATCH 09/25] =?UTF-8?q?Jun=C3=A7=C3=A3o=20do=20codigo=20de=20mat?= =?UTF-8?q?riz=20de=20Rigidez=20global=20com=20o=20modelo=20simplificado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Junção de todas as defs para matriz de rigidez em um única função cuja saída é a matriz global. As funções de matriz de localização e de conectividade foram colocadas dentro da classe Estrutura. O código utilizado como base foi o de Alexandre. Além disso foi adicionado o modelo simplificado criado por Patrícia --- ...mplificado com Matriz de Rigidez Global.py | 130 ++++++++++++++++++ "For\303\247as Aerodinamicas.py" | 37 ++--- 2 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 Chassi Simplificado com Matriz de Rigidez Global.py diff --git a/Chassi Simplificado com Matriz de Rigidez Global.py b/Chassi Simplificado com Matriz de Rigidez Global.py new file mode 100644 index 0000000..859fd1b --- /dev/null +++ b/Chassi Simplificado com Matriz de Rigidez Global.py @@ -0,0 +1,130 @@ +import numpy as np +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D + +class Estrutura: + def __init__(self, E, I, nodes, elements): + self.E = E + self.I = I + self.nodes = nodes + self.elements = elements + self.num_dofs_per_node = 6 + self.num_nodes = len(nodes) + self.num_dofs = self.num_nodes * self.num_dofs_per_node + self.K_global = np.zeros((self.num_dofs, self.num_dofs)) + + def connect_matrix(self): + # Número de conexões + num_connect = len(self.elements) + + # Gerando a matriz de conectividade: (Para cada linha, teremos: [índice a coneção, 1º nó, 2º nó]) + CM = np.zeros((num_connect, 3), dtype=int) + + # Preenchendo a matriz: + for i, (no1, no2) in enumerate(self.elements, start=0): + CM[i][0] = i + 1 + CM[i][1] = no1 + CM[i][2] = no2 + + print("\n Conexão 1º Nó 2º Nó") + print(CM) + + def node_loc_matrix(self, node_tags, node_coord): + # Número de Nós + num_nodes = len(node_tags) + + # Gerando uma matrix número de nos x 4: (Para cada linha, teremos: [índice do nó, x, y, z]) + node_loc_matrix = np.zeros((num_nodes, 4), dtype=float) # Na primeira coluna, teremos os índices, nas seguintes, x y e z. + + # Preenchendo a matriz de zeros + for i, (x, y, z) in enumerate(node_coord, start=0): + node_loc_matrix[i][0] = node_tags[i] # Número do nó na primeira coluna + node_loc_matrix[i][1] = x # Coordenada x na segunda coluna + node_loc_matrix[i][2] = y # Coordenada y na terceira coluna + node_loc_matrix[i][3] = z # Coordenada z na quarta coluna + + print("\n Nó x y z") + print(node_loc_matrix) + + def matriz_rigidez_global(self): + for element in self.elements: + node1, node2 = element + x1, y1, z1 = self.nodes[node1] + x2, y2, z2 = self.nodes[node2] + L_e = np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) + K_e = np.zeros((12, 12)) + coef = (2 * self.E * self.I / L_e**3) + K_e[:4, :4] = coef * np.array([ + [6, -3 * L_e, -6, -3 * L_e], + [3 * L_e, 2 * L_e**2, -3 * L_e, L_e**2], + [-6, -3 * L_e, 6, 3 * L_e], + [-3 * L_e, L_e**2, 3 * L_e, 2 * L_e**2]]) + K_e[6:10, 6:10] = K_e[:4, :4] + dofs_node1 = [node1 * self.num_dofs_per_node + i for i in range(self.num_dofs_per_node)] + dofs_node2 = [node2 * self.num_dofs_per_node + i for i in range(self.num_dofs_per_node)] + dofs = dofs_node1 + dofs_node2 + for i in range(len(dofs)): + for j in range(len(dofs)): + self.K_global[dofs[i], dofs[j]] += K_e[i, j] + return self.K_global + +# Exemplo de uso +E = 210e9 # Módulo de elasticidade em Pa +I = 8.33e-6 # Momento de inércia em m^4 + +# Coordenadas dos nós (x, y, z) +nodes = [ + (0, 0, 0), + (0, 375, 0), + (0, 700, 0), + (1500, 375, 0), + (1500, 0, 0), + (1500, 700, 0) +] + +# Conectividade dos elementos (índices dos nós) +elements = [ + (0, 1), # Elemento entre os nós 0 e 1 + (1, 2), # Elemento entre os nós 1 e 2 + (4, 3), # Elemento entre os nós 4 e 3 + (3, 5), # Elemento entre os nós 3 e 5 + (1, 3) # Elemento entre os nós 1 e 3 +] + +# Criar a estrutura e montar a matriz de rigidez global +estrutura = Estrutura(E, I, nodes, elements) +K_global = estrutura.matriz_rigidez_global() +# Gerar as matrizes de localização dos nós e de conectividade +node_tags = list(range(len(nodes))) +estrutura.node_loc_matrix(node_tags, nodes) +estrutura.connect_matrix() + +print("\n Matriz de Rigidez Global") +print(K_global) + +# Convertendo a matriz de conectividade para um array numpy +connectivity_array = np.array(elements) + +# Plotando o gráfico 3D +fig = plt.figure(figsize=(10, 8)) +ax = fig.add_subplot(111, projection='3d') + +# Adicionando os pontos +for i, (x, y, z) in enumerate(nodes): + ax.scatter(x, y, z, color='b', s=100) + ax.text(x, y, z, f' {i}', color='black', fontsize=12) + +# Adicionando as linhas de ligação entre os nós +for node1, node2 in elements: + x = [nodes[node1][0], nodes[node2][0]] + y = [nodes[node1][1], nodes[node2][1]] + z = [nodes[node1][2], nodes[node2][2]] + ax.plot(x, y, z, marker='o') + +# Configurações adicionais +ax.set_xlabel('X') +ax.set_ylabel('Y') +ax.set_zlabel('Z') +ax.set_title('Estrutura 3D') + +plt.show() \ No newline at end of file diff --git "a/For\303\247as Aerodinamicas.py" "b/For\303\247as Aerodinamicas.py" index 75fe922..0d570f4 100644 --- "a/For\303\247as Aerodinamicas.py" +++ "b/For\303\247as Aerodinamicas.py" @@ -44,27 +44,27 @@ def aerodynamics_forces_wing(self,velocidade): #Função pa cd_wing=cd_induced+0.05 #Coeficiente de arrasto total da asa= Cd=Cd_induzido+Cd_forma drag_wing= (cd_wing * pressao_dinamica * self.a_wing) #Calculo da força de arrasto lift_wing= (cl_wing * pressao_dinamica * self.a_wing) - return drag_wing,lift_wing + return drag_wing,lift_wing,cl_wing,cd_wing #Parâmetros físicos do ar: -p_atm= 101325 #pressâo atmosférica (N/m^2) -rho= 1.184 #densidade (kg/m^3) -mi= 1.849*10**-5 #Viscosidade dinâmica (kg/m*s) -ni= (mi/rho) #Viscosidade Cinematica (m²/s) +p_atm= 101325 #pressâo atmosférica (N/m^2) +rho= 1.184 #densidade (kg/m^3) +mi= 1.849*10**-5 #Viscosidade dinâmica (kg/m*s) +ni= (mi/rho) #Viscosidade Cinematica (m²/s) #parâmetros do carro -length= 2 #Comprimento do carro (m^2) -af= 1.5 #Área Frontal do carro (m^2) -a_sup= 2 #Área de Superfície do carro (m^2) -cd_p = 0.45 #Coeficiente de arrasto por pressão do carro -cd_f = 0.05 #Coeficiente de arrasto por atrito do carro -cl= -0.3 #Coeficiente de lift do carro -a1= 0.25 #Área de entrada do ar embaixo do carro (m^2) -a2= 0.20 #Área embaixo do carro (m^2) +length= 2 #Comprimento do carro (m^2) +af= 1.5 #Área Frontal do carro (m^2) +a_sup= 2 #Área de Superfície do carro (m^2) +cd_p = 0.45 #Coeficiente de arrasto por pressão do carro +cd_f = 0.05 #Coeficiente de arrasto por atrito do carro +cl= -0.3 #Coeficiente de lift do carro +a1= 0.25 #Área de entrada do ar embaixo do carro (m^2) +a2= 0.20 #Área embaixo do carro (m^2) #parâmetros da asa -chord=0.25 #Comprimento da asa (m) -span=1 #Largura da asa (m) +chord=0.50 #Comprimento da asa (m) +span=1.5 #Largura da asa (m) thickness=0.05 #Expessura máxima da asa (m) -alpha=math.radians(-3.75) #Ângulo de incidencia do vento com a asa (radianos) +alpha=math.radians(-3.75) #Ângulo de incidencia do vento com a asa (radianos) carro=forcas_aerodinamicas(af,a_sup,rho,cd_p,cd_f,cl,length,ni,p_atm,alpha,span,chord) #Definição do objeto @@ -77,12 +77,13 @@ def aerodynamics_forces_wing(self,velocidade): #Função pa lifts_w=[] for v in velocidades: drag,lift= carro.aerodynamic_forces(v) - drag_w,lift_w= carro.aerodynamics_forces_wing(v) + drag_w,lift_w,cl_w,cd_w= carro.aerodynamics_forces_wing(v) drags.append(drag) lifts.append(lift) drags_w.append(drag_w) lifts_w.append(lift_w) - +print(cd_w) +print(cl_w) # Plotagem do gráfico 1 plt.figure(figsize=(12, 6)) plt.subplot(2,2,1) From ca5f571ea62da017dcacb5bd46a0147809524aff Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Wed, 17 Jul 2024 22:32:18 -0300 Subject: [PATCH 10/25] Create Elemento De Barra e Viga + Shape Function.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Junção do codigo de Antonio com as defs para elemento de barra e viga, e a def de shape fun. Está gerando gráficos --- Elemento De Barra e Viga + Shape Function.py | 388 +++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 Elemento De Barra e Viga + Shape Function.py diff --git a/Elemento De Barra e Viga + Shape Function.py b/Elemento De Barra e Viga + Shape Function.py new file mode 100644 index 0000000..5caefde --- /dev/null +++ b/Elemento De Barra e Viga + Shape Function.py @@ -0,0 +1,388 @@ +import numpy as np +from scipy.linalg import eigh +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D +from scipy.sparse import lil_matrix +from scipy.sparse.linalg import spsolve + +class Estrutura: + def __init__(self, elements, nodes, m, Id, Ip): + self.elements = elements #Matriz de elementos conectados + self.num_elements = len(elements) #Número de elementos + self.nodes = nodes #Matriz de nós com suas posiççoes + self.num_nodes = len(nodes) #Número total de nós + self.massa = m #Massa do carro (Kg) + self.momento_inercia_direcao = Id #Momento de inércia em relação à direção (kg.m^2) + self.momento_inercia_plano = Ip #Momento de inércia em relação ao plano (kg.m^2) + self.num_dofs_per_node = 6 #Graus de liberdade por nó + self.num_dofs = self.num_nodes * self.num_dofs_per_node #Total de Graus de liberdade (gdls) + self.K_global_barra = np.zeros((self.num_elements+1, self.num_elements+1)) #Matriz de rigidez global para barra + self.M_global_barra = np.zeros((self.num_elements+1, self.num_elements+1)) #Matriz de massa global para barra + self.K_global_viga = np.zeros((2*self.num_elements+2, 2*self.num_elements+2)) #Matriz de rigidez global para viga + self.M_global_viga = np.zeros((2*self.num_elements+2, 2*self.num_elements+2)) #Matriz de massa global para viga + self.num_modes = 20 #Número de modos de vibração a serem retornados + + def node_loc_matrix(self, node_tags, node_coord): + # Número de Nós + num_nodes = len(node_tags) + + # Gerando uma matrix número de nos x 4: (Para cada linha, teremos: [índice do nó, x, y, z]) + node_loc_matrix = np.zeros((num_nodes, 4), dtype=float) + + # Preenchendo a matriz de zeros + for i, (x, y, z) in enumerate(node_coord, start=0): + node_loc_matrix[i][0] = node_tags[i] + 1 # Número do nó na primeira coluna + node_loc_matrix[i][1] = x # Coordenada x na segunda coluna + node_loc_matrix[i][2] = y # Coordenada y na terceira coluna + node_loc_matrix[i][3] = z # Coordenada z na quarta coluna + + print("\n Nó x y z") + print(node_loc_matrix) + + def connect_matrix(self): + # Número de conexões + num_connect = len(self.elements) + + # Gerando a matriz de conectividade: (Para cada linha, teremos: [índice a coneção, 1º nó, 2º nó]) + CM = np.zeros((num_connect, 3), dtype=int) + + # Preenchendo a matriz: + for i, (no1, no2) in enumerate(self.elements, start=0): + CM[i][0] = i + 1 + CM[i][1] = no1 + CM[i][2] = no2 + + print("\n Conexão 1º Nó 2º Nó") + print(CM) + + def calcular_comprimento(self, element): + node1, node2 = element + x1, y1, z1 = self.nodes[node1] + x2, y2, z2 = self.nodes[node2] + return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) + + def elemento_barra_viga(self,elemento): + E = 210e9 # Pa + I = 1.6667e-5 # m^4 + rho = 7850 + A= 0.0125 + L_e = self.calcular_comprimento(elemento) + k_e_viga = (2 * E * I / L_e ** 3) * np.array([[6, -3 * L_e, -6, -3 * L_e], + [3 * L_e, 2 * L_e ** 2, -3 * L_e, L_e ** 2], + [-6, -3 * L_e, 6, 3 * L_e], + [-3 * L_e, L_e ** 2, 3 * L_e, 2 * L_e ** 2]]) + + m_e_viga = (rho * A* L_e / 420) * np.array([[156, 22 * L_e, 54, -13 * L_e], + [22 * L_e, 4 * L_e**2, 13 * L_e, -3 * L_e**2], + [54, 13 * L_e, 156, -22 * L_e], + [-13 * L_e, -3 * L_e**2, -22 * L_e, 4 * L_e**2]]) + k_e_barra = (E * A / L_e) * np.array([[1, -1], + [-1, 1]]) + + m_e_barra = (rho * A * L_e / 6) * np.array([[2, 1], + [1, 2]]) + return k_e_viga , m_e_viga,k_e_barra,m_e_barra + + def matrizes_globais_barra(self): + #Calculando as matrizes de rigidez e massa de cada elemento + for element in self.elements: + node1, node2 = element + x1, y1, z1 = self.nodes[node1] + x2, y2, z2 = self.nodes[node2] + k_e_viga , m_e_viga,k_e_barra,m_e_barra=self.elemento_barra_viga(element) + #Montando as matrizes globais + dofs = [node1, node1+1, node2, node2+1] + for i in range(2): + for j in range(2): + self.K_global_barra[dofs[i], dofs[j]] += k_e_barra[i, j] + self.M_global_barra[dofs[i], dofs[j]] += m_e_barra[i, j] + + #Aplicando engastes nas extremidades + self.K_global_barra[0, 0] = 10**10 + self.K_global_barra[1, 1] = 10**10 + self.K_global_barra[-1, -1] = 10**10 + self.K_global_barra[-2, -2] = 10**10 + + return self.K_global_barra, self.M_global_barra + + def matrizes_globais_viga(self): + #Calculando as matrizes de rigidez e massa de cada elemento + for element in self.elements: + node1, node2 = element + x1, y1, z1 = self.nodes[node1] + x2, y2, z2 = self.nodes[node2] + k_e_viga , m_e_viga,k_e_barra,m_e_barra=self.elemento_barra_viga(element) + + #Montando as matrizes globais + dofs = [2*node1, 2*node1+1, 2*node2, 2*node2+1] + for i in range(4): + for j in range(4): + self.K_global_viga[dofs[i], dofs[j]] += k_e_viga[i, j] + self.M_global_viga[dofs[i], dofs[j]] += m_e_viga[i, j] + + #Aplicando engastes nas extremidades + self.K_global_viga[0, 0] = 10**10 + self.K_global_viga[-1, -1] = 10**10 + + return self.K_global_viga, self.M_global_viga + + def shape_fun(self, F1,F2,F3): + E = 210e9 # Pa + I = 1.6667e-5 # m^4 + G = 81.2e9 # Pa + A= 0.0125 + J = 1e-6 # m^4 (momento polar de inércia) + torsao, deformacao, flexao = [], [], [] + for element in self.elements: + L_e = self.calcular_comprimento(element) + # Equação de torção + torsao_val = (F1 * L_e) / (G * J) + torsao.append(torsao_val) + # Equação diferencial para deformação axial + deformacao_val = (F2 / (A * E)) * L_e + deformacao.append(deformacao_val) + # Equação de Euler-Bernoulli para flexão + x = np.linspace(0, L_e, len(F3)) + v_val = np.zeros_like(F3) + for i in range(1, len(F3)): + v_val[i] = (F3[i] - F3[i-1]) / (E * I) * (L_e ** 3 / 12) + flexao.append(v_val) + + return np.array(torsao), np.array(deformacao), np.array(flexao) + + def modal_analysis_viga(self): + #Análise modal por resolução do problema de autovalor e autovetor + unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global_viga, self.M_global_viga) + + #Frequências naturais (raiz quadrada dos autovalores) + unsorted_frequencies = np.sqrt(unsorted_eigenvalues) / (2*np.pi) #Divisão por 2*pi para converter para hertz + + + #Tratando os dados (tomando apenas as 20 primeiras frequências naturais) + sorted_indices = np.argsort(unsorted_frequencies) #Ordena as frequências em ordem crescente + top_indices = sorted_indices[:self.num_modes] #Seleciona os índices dos primeiros n modos + + eigenvalues = np.array(unsorted_eigenvalues)[top_indices] #Filtra os primeiros n autovalores + eigenvectors = np.array(unsorted_eigenvectors)[:, top_indices] #Filtra os primeiros n autovetores + frequencies = np.array(unsorted_frequencies)[top_indices] #Filtra as primeiras n frequências + + return eigenvalues, eigenvectors, frequencies + + def modal_analysis_barra(self): + #Análise modal por resolução do problema de autovalor e autovetor + unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global_barra, self.M_global_barra) + + #Frequências naturais (raiz quadrada dos autovalores) + unsorted_frequencies = np.sqrt(unsorted_eigenvalues) / (2*np.pi) #Divisão por 2*pi para converter para hertz + + + #Tratando os dados (tomando apenas as 20 primeiras frequências naturais) + sorted_indices = np.argsort(unsorted_frequencies) #Ordena as frequências em ordem crescente + top_indices = sorted_indices[:self.num_modes] #Seleciona os índices dos primeiros n modos + + eigenvalues = np.array(unsorted_eigenvalues)[top_indices] #Filtra os primeiros n autovalores + eigenvectors = np.array(unsorted_eigenvectors)[:, top_indices] #Filtra os primeiros n autovetores + frequencies = np.array(unsorted_frequencies)[top_indices] #Filtra as primeiras n frequências + + return eigenvalues, eigenvectors, frequencies + +#Coordenadas dos nós (x, y, z) +nodes = [ + (0, 0, 0), + (0, 0.375, 0), + (0, 0.700, 0), + (1.500, 0.375, 0), + (1.500, 0, 0), + (1.500, 0.700, 0) +] + +#Conectividade dos elementos (índices dos nós) +elements = [ + (0, 1), # Elemento entre os nós 0 e 1 + (1, 2), # Elemento entre os nós 1 e 2 + (4, 3), # Elemento entre os nós 4 e 3 + (3, 5), # Elemento entre os nós 3 e 5 + (1, 3) # Elemento entre os nós 1 e 3 +] + +#Criar a estrutura e montar as matrizes de rigidez e massa globais +#Dados: n = len(nodes), +# m = 1500 kg, +# rho = 7850 kg/m^3 +# A = 0.225 m^2 +# E = 210e9 # Módulo de elasticidade em Pa +# I = 8.33e-6 # Momento de inércia em m^4 +# Ip = Id = 8.33e-6 kg.m^2 +F1 = np.array([1000, 2000, 3000, 4000, 5000]) +F2 = np.array([1000, 2000, 3000, 4000, 5000]) +T = np.array([1000, 2000, 3000, 4000, 5000]) +estrutura = Estrutura(elements, nodes, 1500, 8.33e-6, 8.33e-6) +K_global_viga, M_global_viga = estrutura.matrizes_globais_viga() +K_global_barra, M_global_barra = estrutura.matrizes_globais_barra() + +#Gerar as matrizes de localização dos nós e de conectividade +node_tags = list(range(len(nodes))) +estrutura.node_loc_matrix(node_tags, nodes) +estrutura.connect_matrix() + +#Gerar autovalores, autovetores e frequências naturais +autovalores_viga, autovetores_viga, frequencias_viga = estrutura.modal_analysis_viga() +autovalores_barra, autovetores_barra, frequencias_barra = estrutura.modal_analysis_barra() + +torsao, deformacao_axial, flexao = estrutura.shape_fun(F1,F2,T) +# Plotando os resultados das deformações +fig, axs = plt.subplots(3, 1, figsize=(12, 18)) + + +# Plot da Torção +axs[0].plot(torsao, 'o-', label=[f'Elemento {x}' for x in range(5)]) +axs[0].set_title('Deformação por Torção') +axs[0].set_xlabel('Elemento') +axs[0].set_ylabel('Torção (rad)') +axs[0].legend() + +# Plot da Deformação Axial +axs[1].plot(deformacao_axial, 's-', label=[f'Elemento {x}' for x in range(5)]) +axs[1].set_title('Deformação Axial') +axs[1].set_xlabel('Elemento') +axs[1].set_ylabel('Deformação (m)') +axs[1].legend() + +# Plot da Flexão +for i, v_val in enumerate(flexao): + axs[2].plot(v_val, label=f'Elemento {i}') +axs[2].set_title('Deformação por Flexão') +axs[2].set_xlabel('Posição ao longo do elemento') +axs[2].set_ylabel('Flexão(m)') +axs[2].legend() + +plt.tight_layout() +plt.show() + + +print("\n Matriz de Rigidez Global para a estrutura de vigas") +print(K_global_viga) + +print("\n Matriz de Massa Global para a estrutura de vigas") +print(M_global_viga) + +print("\n Matriz de Rigidez Global para a estrutura de barras") +print(K_global_barra) + +print("\n Matriz de Massa Global para a estrutura de barras") +print(M_global_barra) + +# Convertendo a matriz de conectividade para um array numpy +connectivity_array = np.array(elements) + +# Plotando o gráfico 3D da estrutura +fig = plt.figure(figsize=(10, 8)) +ax = fig.add_subplot(111, projection='3d') + +# Adicionando os pontos +for i, (x, y, z) in enumerate(nodes): + ax.scatter(x, y, z, color='b', s=100) + ax.text(x, y, z, f' {i}', color='black', fontsize=12) + +# Adicionando as linhas de ligação entre os nós +for node1, node2 in elements: + x = [nodes[node1][0], nodes[node2][0]] + y = [nodes[node1][1], nodes[node2][1]] + z = [nodes[node1][2], nodes[node2][2]] + ax.plot(x, y, z, marker='o') + +# Configurações adicionais +ax.set_xlabel('X') +ax.set_ylabel('Y') +ax.set_zlabel('Z') +ax.set_title('Estrutura 3D') + +plt.show() + + +#Exibindo as frequências naturais e modos de vibração da estrutura +print("\n Frequências Naturais (ω) da estrutura montada por vigas:") +print(frequencias_viga) + +print("\n Frequências Naturais (ω) da estrutura montada por barras:") +print(frequencias_barra) + +#Plotagem dos modos de vibração para a estrutura de vigas +for mode_idx in range(len(autovalores_viga)): + fig = plt.figure(figsize=(8, 6)) + ax = fig.add_subplot(111, projection='3d') + ax.set_title(f'Modo {mode_idx + 1} Viga') + + for i, (x, y, z) in enumerate(nodes): + ax.scatter(x, y, z, color='b', s=100) + ax.text(x, y, z, f' {i}', color='black', fontsize=8) + + for node1, node2 in elements: + x = [nodes[node1][0], nodes[node2][0]] + y = [nodes[node1][1], nodes[node2][1]] + z = [nodes[node1][2], nodes[node2][2]] + ax.plot(x, y, z, 'b--') + + mode_shape = autovetores_viga[:, mode_idx] + displacements = np.zeros((len(nodes), 3)) + + for j, (x, y, z) in enumerate(nodes): + if j > 0 and j < len(nodes) - 1: + displacements[j, 0] = mode_shape[2 * j] + displacements[j, 1] = mode_shape[2 * j + 1] + displacements[j, 2] = 0 + + deformed_nodes = np.array(nodes) + 0.1 * displacements + + for node1, node2 in elements: + x = [deformed_nodes[node1][0], deformed_nodes[node2][0]] + y = [deformed_nodes[node1][1], deformed_nodes[node2][1]] + z = [deformed_nodes[node1][2], deformed_nodes[node2][2]] + ax.plot(x, y, z, 'r-') + + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + + plt.tight_layout() + plt.show() + +#Plotagem dos modos de vibração para a estrutura de barras +for mode_idx in range(len(autovalores_barra)): + fig = plt.figure(figsize=(8, 6)) + ax = fig.add_subplot(111, projection='3d') + ax.set_title(f'Modo {mode_idx + 1} Barra') + + for i, (x, y, z) in enumerate(nodes): + ax.scatter(x, y, z, color='b', s=100) + ax.text(x, y, z, f' {i}', color='black', fontsize=8) + + for node1, node2 in elements: + x = [nodes[node1][0], nodes[node2][0]] + y = [nodes[node1][1], nodes[node2][1]] + z = [nodes[node1][2], nodes[node2][2]] + ax.plot(x, y, z, 'b--') + + mode_shape = autovetores_barra[:, mode_idx] + displacements = np.zeros((len(nodes), 3)) + + for j, (x, y, z) in enumerate(nodes): + if j > 0 and j < len(nodes) - 1: + displacements[j, 0] = mode_shape[j] + displacements[j, 1] = mode_shape[j + 1] + displacements[j, 2] = 0 + + deformed_nodes = np.array(nodes) + 0.1 * displacements + + for node1, node2 in elements: + x = [deformed_nodes[node1][0], deformed_nodes[node2][0]] + y = [deformed_nodes[node1][1], deformed_nodes[node2][1]] + z = [deformed_nodes[node1][2], deformed_nodes[node2][2]] + ax.plot(x, y, z, 'r-') + + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + + plt.tight_layout() + plt.show() From 6bd78c49be609cbfe5f32e5d2544ebf766eb9454 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Mon, 12 Aug 2024 22:15:03 -0300 Subject: [PATCH 11/25] =?UTF-8?q?Create=20Atualiza=C3=A7=C3=A3o=20da=20Def?= =?UTF-8?q?=20das=20Matrizes=20Globais.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modificações na def de matrizes globais: -Modificado a adição de engaste -Adicionado ao codigo a biblioteca Pandas para exportar as matrizes globais para um arquivo csv --- ...7\303\243o da Def das Matrizes Globais.py" | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 "Atualiza\303\247\303\243o da Def das Matrizes Globais.py" diff --git "a/Atualiza\303\247\303\243o da Def das Matrizes Globais.py" "b/Atualiza\303\247\303\243o da Def das Matrizes Globais.py" new file mode 100644 index 0000000..cdae76e --- /dev/null +++ "b/Atualiza\303\247\303\243o da Def das Matrizes Globais.py" @@ -0,0 +1,337 @@ +import numpy as np +from scipy.linalg import eigh +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D +from scipy.sparse import lil_matrix +from scipy.sparse.linalg import spsolve +import pandas as pd +import sys + +np.set_printoptions(threshold=sys.maxsize) +np.set_printoptions(linewidth=200, suppress=True) + +class Estrutura: + def __init__(self, elements, nodes, m, Id, Ip): + self.elements = elements #Matriz de elementos conectados + self.num_elements = len(elements) #Número de elementos + self.nodes = nodes #Matriz de nós com suas posiççoes + self.num_nodes = len(nodes) #Número total de nós + self.massa = m #Massa do carro (Kg) + self.momento_inercia_direcao = Id #Momento de inércia em relação à direção (kg.m^2) + self.momento_inercia_plano = Ip #Momento de inércia em relação ao plano (kg.m^2) + self.num_dofs_per_node = 6 #Graus de liberdade por nó + self.num_dofs = self.num_nodes * self.num_dofs_per_node #Total de Graus de liberdade (gdls) + self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Tamanho adequado para a matriz global de Rigidez + self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Tamanho adequado para a matriz global de Massa + self.num_modes = 12 #Número de modos de vibração a serem retornados + + + def node_loc_matrix(self, node_tags, node_coord): + # Gerando uma matriz número de nos x 4: (Para cada linha, teremos: [índice do nó, x, y, z]) + node_loc_matrix = np.zeros((self.num_nodes, 4), dtype=float) + + # Preenchendo a matriz de zeros + for i, (x, y, z) in enumerate(node_coord, start=0): + node_loc_matrix[i][0] = node_tags[i] + 1 # Número do nó na primeira coluna + node_loc_matrix[i][1] = x # Coordenada x na segunda coluna + node_loc_matrix[i][2] = y # Coordenada y na terceira coluna + node_loc_matrix[i][3] = z # Coordenada z na quarta coluna + + print("\n Nó x y z") + print(node_loc_matrix) + + + def connect_matrix(self): + # Número de conexões + num_connect = len(self.elements) + + # Gerando a matriz de conectividade: (Para cada linha, teremos: [índice a coneção, 1º nó, 2º nó]) + CM = np.zeros((num_connect, 3), dtype=int) + + # Preenchendo a matriz: + for i, (no1, no2) in enumerate(self.elements, start=0): + CM[i][0] = i + 1 + CM[i][1] = no1 +1 + CM[i][2] = no2 +1 + + print("\n Conexão 1º Nó 2º Nó") + print(CM) + + + def calcular_comprimento(self, element): + node1, node2 = element + x1, y1, z1 = self.nodes[node1] + x2, y2, z2 = self.nodes[node2] + return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) + + + def element(self,elemento): + # Variáveis e constantes físicas do modelo + E = 210e9 #Modulo de Young (Pa) + I = 1.6667e-5 #Momento de inercia (m^4) + G = 81.2e9 #Modulo de Cisalhamento(Pa) + A= 0.0125 #Área da seção do elemento (m^2) + J = I/2 #Momento polar de inércia (m^4) + kappa=0.9 #Fator de correção para cisalhamento + L_e = self.calcular_comprimento(elemento) + Phi = (12 * E * I) / (kappa * G * A * L_e**2) + rho = 7850 # kg/m^3 + #Preparação das constantes para matrizes + c1 = E * A / L_e + c2 = G * J / L_e + c3 = E * I / L_e**3 #Euler-Bernoulli + c4 = (E*I)/(L_e**3 * (1+Phi)) #Timoshenko + t1=(4+Phi) + t2=(2-Phi) + d1 = rho*A*L_e + d2 = (I*L_e)/6 + d3 = (rho*A*L_e)/420 + + # Matriz de Rigidez Elementar (Euler-Bernoulli) + # Para converter para timoshenko basta trocar c3 por c4,onde tem (4 * L_e**2 * c3) substitui por (t1* L_e**2 * c4) e onde tiver (2 * L_e**2 * c3) por (t2* L_e**2 * c3) ) + k_e= np.array([ + [12 * c4, 0, 0, 6 * L_e * c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4, 0, 0], + [0, c1, 0, 0, 0, 0, 0, -c1, 0, 0, 0, 0], + [0, 0, 12 * c4, 0, 0, 6 * L_e* c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4], + [6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2* L_e**2 * c4, 0, 0], + [0, 0, 0, 0, c2, 0, 0, 0, 0, 0, -c2, 0], + [0, 0, 6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2* L_e**2 * c4], + [-12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4, 0, 0], + [0, -c1, 0, 0, 0, 0, 0, c1, 0, 0, 0, 0], + [0, 0, -12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4], + [6 * L_e * c4, 0, 0, t2* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0], + [0, 0, 0, 0, -c2, 0, 0, 0, 0, 0, c2, 0], + [0, 0, 6 * L_e * c4, 0, 0, t2* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4] + ]) + + # Matriz de Massa Elementar + m_e= np.array([ + [156 * d3, 0, 0, 22 * L_e * d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3, 0, 0], + [0,2*d1, 0, 0, 0, 0, 0, d1, 0, 0, 0, 0], + [0, 0, 156 * d3, 0, 0, 22 * L_e* d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3], + [22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0], + [0, 0, 0, 0, 2*d2, 0, 0, 0, 0, 0, d2, 0], + [0, 0, 22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3], + [54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156* d3, 0, 0, -22 * L_e * d3, 0, 0], + [0, d1, 0, 0, 0, 0, 0, 2*d1, 0, 0, 0, 0], + [0, 0, 54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156 * d3, 0, 0, -22 * L_e * d3], + [-13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0], + [0, 0, 0, 0, d2, 0, 0, 0, 0, 0, 2*d2, 0], + [0, 0, -13 * L_e * d3, 0, 0,-3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3] + ]) + return k_e,m_e + + def aplicar_engastes(self, nodes, dofs): + for node in nodes: #Laço para selecionar cada nó que será engastado + for dof in dofs: #Laço para selecionar quais graus de liberdade serão fixados + index = node * self.num_dofs_per_node + dof #Identificação da entrada da matriz que precisa ser restringida pelo engaste + self.K_global[index, index] = 10**10 # Um valor suficientemente grande para simular um engaste + + def matrizes_global(self): + #Calculando as matrizes de rigidez e massa de cada elemento + for element in self.elements: + node1,node2=element + k_e,m_e= self.element(element) + #Montando as matrizes globais + # x1 y1 z1 rx1 ry1 rz1 x2 y2 z2 rx2 ry2 rz2 + dofs = [6*node1, 6*node1+1, 6*node1+2, 6*node1+3, 6*node1+4, 6*node1+5, 6*node2, 6*node2+1, 6*node2+2, 6*node2+3, 6*node2+4, 6*node2+5] + for i in range(len(dofs)): + for j in range(len(dofs)): + self.K_global[dofs[i], dofs[j]] += k_e[i, j] + self.M_global[dofs[i], dofs[j]] += m_e[i, j] + + self.aplicar_engastes([0, 2, 4, 5], [0, 1, 2]) + + # Convert the array to a DataFrame + df_1 = pd.DataFrame(self.K_global) + df_2 = pd.DataFrame(self.M_global) + # Save the DataFrame to a CSV file + df_1.to_csv('Matriz_Global_Rigidez.csv', index=True, header=True) + df_2.to_csv('Matriz_Global_Massa.csv', index=True, header=True) + return self.K_global,self.M_global + + def shape_fun(self, F_flexao,F_axial,F_torcao): + E = 210e9 #Modulo de Young (Pa) + I = 1.6667e-5 #Momento de inercia (m^4) + G = 81.2e9 #Modulo de Cisalhamento(Pa) + A= 0.0125 #Área da seção do elemento (m^2) + J = I/2 #Momento polar de inércia (m^4) + torcao, deformacao, flexao = [], [], [] + for element in self.elements: + L_e = self.calcular_comprimento(element) + # Equação de torsão + torcao_val = (F_torcao * L_e) / (G * J) #Fonte[1] + torcao.append(torcao_val) + # Equação para deformação axial + deformacao_val = (F_axial* L_e / (A * E)) #Fonte[2] + deformacao.append(deformacao_val) + # Equação para flexão + flexao_val = (F_flexao*L_e**3)/(3 * E * I) #Fonte[3] + flexao.append(flexao_val) + return np.array(torcao), np.array(deformacao), np.array(flexao) + + def modal_analysis(self): + # Análise modal por resolução do problema de autovalor e autovetor + unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) + + # Frequências naturais (raiz quadrada dos autovalores) + unsorted_frequencies = np.sqrt(unsorted_eigenvalues) / (2 * np.pi) # Divisão por 2*pi para converter para hertz + + # Tratando os dados (tomando apenas as 20 primeiras frequências naturais) + sorted_indices = np.argsort(unsorted_frequencies) # Ordena as frequências em ordem crescente + top_indices = sorted_indices[:self.num_modes] # Seleciona os índices dos primeiros n modos + + eigenvalues = np.array(unsorted_eigenvalues)[top_indices] # Filtra os primeiros n autovalores + eigenvectors = np.array(unsorted_eigenvectors)[:, top_indices] # Filtra os primeiros n autovetores + frequencies = np.array(unsorted_frequencies)[top_indices] # Filtra as primeiras n frequências + + return eigenvalues, eigenvectors, frequencies + +#Coordenadas dos nós (x, y, z) +nodes = [ + (0, 0, 0), + (0, 0.375, 0), + (0, 0.700, 0), + (1.500, 0.375, 0), + (1.500, 0, 0), + (1.500, 0.700, 0) +] + +#Conectividade dos elementos (índices dos nós) +elements = [ + (0, 1), # Elemento entre os nós 0 e 1 + (1, 2), # Elemento entre os nós 1 e 2 + (4, 3), # Elemento entre os nós 4 e 3 + (3, 5), # Elemento entre os nós 3 e 5 + (1, 3) # Elemento entre os nós 1 e 3 +] + +#Criar a estrutura e montar as matrizes de rigidez e massa globais +#Dados: n = len(nodes), +# m = 1500 kg, +# rho = 7850 kg/m^3 +# A = 0.225 m^2 +# E = 210e9 # Módulo de elasticidade em Pa +# I = 8.33e-6 # Momento de inércia em m^4 +# Ip = Id = 8.33e-6 kg.m^2 +F_flexao = np.array([1000, 2000, 3000, 4000, 5000]) +F_axial = np.array([1000, 2000, 3000, 4000, 5000]) +F_torcao = np.array([1000, 2000, 3000, 4000, 5000]) +estrutura = Estrutura(elements, nodes, 1500, 8.33e-6, 8.33e-6) +K_global, M_global = estrutura.matrizes_global() +K_e, M_e = estrutura.element(elements[0]) +#Gerar as matrizes de localização dos nós e de conectividade +node_tags = list(range(len(nodes))) +estrutura.node_loc_matrix(node_tags, nodes) +estrutura.connect_matrix() + +print("\n Matriz de Rigidez Global") +print(K_global) + +print("\n Matriz de Massa Global") +print(M_global) + +#Gerar autovalores, autovetores e frequências naturais +autovalores, autovetores, frequencias = estrutura.modal_analysis() + +torcao, deformacao_axial, flexao = estrutura.shape_fun(F_flexao,F_axial,F_torcao) +# Plotando os resultados das deformações +fig, axs = plt.subplots(3, 1, figsize=(12, 18)) + + +# Plot da Torção +axs[0].plot(torcao, 'o-', label=[f'Força {F}N' for F in F_torcao]) +axs[0].set_title('Deformação por Torção de cada Elemento') +axs[0].set_xlabel('Elemento') +axs[0].set_ylabel('Torção (rad)') +axs[0].legend() + +# Plot da Deformação Axial +axs[1].plot(deformacao_axial, 's-', label=[f'Força {F}N' for F in F_axial]) +axs[1].set_title('Deformação Axial de cada Elemento') +axs[1].set_xlabel('Elemento') +axs[1].set_ylabel('Deformação (m)') +axs[1].legend() + +# Plot da Flexão +axs[2].plot(flexao,'o-', label=[f'Força {F}N' for F in F_flexao]) +axs[2].set_title('Deformação por Flexão de cada Elemento') +axs[2].set_xlabel('Posição ao longo do elemento') +axs[2].set_ylabel('Flexão(m)') +axs[2].legend() + +plt.tight_layout() +plt.show() + +# Convertendo a matriz de conectividade para um array numpy +connectivity_array = np.array(elements) + +# Plotando o gráfico 3D da estrutura +fig = plt.figure(figsize=(10, 8)) +ax = fig.add_subplot(111, projection='3d') + +# Adicionando os pontos +for i, (x, y, z) in enumerate(nodes): + ax.scatter(x, y, z, color='b', s=100) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=12) + +# Adicionando as linhas de ligação entre os nós +for node1, node2 in elements: + x = [nodes[node1][0], nodes[node2][0]] + y = [nodes[node1][1], nodes[node2][1]] + z = [nodes[node1][2], nodes[node2][2]] + ax.plot(x, y, z, marker='o') + +# Configurações adicionais +ax.set_xlabel('X') +ax.set_ylabel('Y') +ax.set_zlabel('Z') +ax.set_title('Estrutura 3D') + +plt.show() + +#Exibindo as frequências naturais e modos de vibração da estrutura +print("\n Frequências Naturais (ω) da estrutura montada por vigas:") +print(frequencias) + +#Plotagem dos modos de vibração para a estrutura de vigas +for mode_idx in range(len(autovalores)): + fig = plt.figure(figsize=(8, 6)) + ax = fig.add_subplot(111, projection='3d') + ax.set_title(f'Modo {mode_idx + 1} Viga') + + for i, (x, y, z) in enumerate(nodes): + ax.scatter(x, y, z, color='b', s=100) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + for node1, node2 in elements: + x = [nodes[node1][0], nodes[node2][0]] + y = [nodes[node1][1], nodes[node2][1]] + z = [nodes[node1][2], nodes[node2][2]] + ax.plot(x, y, z, 'b--') + + mode_shape = autovetores[:, mode_idx] + displacements = np.zeros((len(nodes), 3)) + + for j, (x, y, z) in enumerate(nodes): + if j > 0 and j < len(nodes) - 1: + displacements[j, 0] = mode_shape[2 * j] + displacements[j, 1] = mode_shape[2 * j + 1] + displacements[j, 2] = 0 + + deformed_nodes = np.array(nodes) + displacements + + for node1, node2 in elements: + x = [deformed_nodes[node1][0], deformed_nodes[node2][0]] + y = [deformed_nodes[node1][1], deformed_nodes[node2][1]] + z = [deformed_nodes[node1][2], deformed_nodes[node2][2]] + ax.plot(x, y, z, 'r-') + + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + + plt.tight_layout() + plt.show() + +estrutura.Mesh() \ No newline at end of file From 72e5225c0b5f889148b394c78d90985a0c99a587 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Tue, 22 Oct 2024 21:09:26 -0300 Subject: [PATCH 12/25] Create Arrefecimento_atualizado.py Codigo Novo para o arrefecimento de motor e bateria --- Arrefecimento_atualizado.py | 116 ++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 Arrefecimento_atualizado.py diff --git a/Arrefecimento_atualizado.py b/Arrefecimento_atualizado.py new file mode 100644 index 0000000..0a2ae42 --- /dev/null +++ b/Arrefecimento_atualizado.py @@ -0,0 +1,116 @@ +import numpy as np +import matplotlib.pyplot as plt +import math + +# Dicionário contendo as propriedades dos materiais +materiais = { + 'aluminio': {'densidade': 2600, 'calor_especifico': 950}, # Densidade em kg/m^3, Calor específico em J/(kg·K) + 'cobre': {'densidade': 8960, 'calor_especifico': 385}, + 'aço': {'densidade': 7850, 'calor_especifico': 500}, + 'titânio': {'densidade': 4500, 'calor_especifico': 522}, + 'latão': {'densidade': 8530, 'calor_especifico': 380}, # Latão adicionado + 'polietileno': {'densidade': 950, 'calor_especifico': 1900} # Polietileno adicionado +} + +class Arrefecimento: + def __init__(self, velocidade, area, comprimento, calor_gerado,tipo_geometria='placa'): + self.rho = 1.184 # Massa específica do ar (kg/m^3) + self.c = 1007 # Calor específico do ar (J/(kg*K)) + self.v = velocidade + self.mi = 1.849e-5 # Viscosidade dinâmica do ar (kg/m*s) + self.ni = self.mi / self.rho # Viscosidade cinemática do ar (m²/s) + self.k_ar = 0.02551 # Condutividade térmica do ar (W/m*K) + self.tf = 25 # Temperatura do ar (°C) + self.Pr = self.mi * self.c / self.k_ar # Número de Prandtl + self.a_res = area # Área de resfriamento (m²) + self.L_c = comprimento # Comprimento característico (m) + self.calor_gerado = calor_gerado # Calor gerado pelo objeto (W) + self.tipo_geometria = tipo_geometria # Geometria do objeto ('placa' ou 'cilindro') + + def reynolds(self): + return self.rho * self.v * self.L_c / self.mi + + def calc_Nu(self): + Rey = self.reynolds() + + if self.tipo_geometria == 'placa': + if Rey < 200000: # Fluxo laminar + Nu = 0.332 * Rey**0.5 * self.Pr**(1/3) + else: # Fluxo Turbulento + X = 200000 * self.ni / self.v # Ponto de transição para turbulento + Rey_X = X * self.v / self.ni + A = 0.037 * Rey_X**0.8 - 0.664 * Rey_X**0.5 + Nu = 0.037 * (Rey**0.8 - A) * self.Pr**(1/3) + elif self.tipo_geometria == 'cilindro': + Nu = 0.3 + (0.62 * Rey**0.5 * self.Pr**(1/3)) / (1 + (0.4/self.Pr)**(2/3))**0.25 + else: + raise ValueError("Tipo de geometria não suportada. Escolha 'placa' ou 'cilindro'.") + + return Nu + + def calor_conveccao(self, temp_sup): + nu = self.calc_Nu() + h = nu * self.k_ar / self.L_c + return h * self.a_res * (temp_sup - self.tf) + + def troca_termica(self, tempo_simulacao, temp_objeto, c_objeto, m_objeto): + temp_atual = temp_objeto + temperaturas = [] + tempos = [] + for i in range(tempo_simulacao): + q_conveccao = self.calor_conveccao(temp_atual) + q_total = self.calor_gerado - q_conveccao + temp_atual = (q_total / (c_objeto * m_objeto)) + temp_atual + tempos.append(i) + temperaturas.append(temp_atual) + return tempos, temperaturas + + def plotar_grafico(self, temp_objeto, c_objeto, m_objeto, tempo_simulacao,objeto): + tempos, temperaturas = self.troca_termica(tempo_simulacao, temp_objeto, c_objeto, m_objeto) + plt.plot(tempos, temperaturas) + plt.title(f'Variação da Temperatura ao Longo do Tempo ({objeto})') + plt.xlabel('Tempo (s)') + plt.ylabel('Temperatura (°C)') + plt.grid(True) + plt.show() + +# Função para calcular o volume da camada externa de um cilindro +def volume_camada_cilindro(raio_externo, espessura, comprimento): + raio_interno = raio_externo - espessura + return math.pi * comprimento * (raio_externo**2 - raio_interno**2) + +# Parâmetros de simulação +velocidade = 10 +calor_gerado = 100000 / 3600 # Potência convertida para J/s +temp_objeto = 100 # Temperatura inicial do objeto (°C) +tempo_simulacao = 6000 # Tempo total da simulação (s) +material = 'aluminio' # Escolha o material ('aluminio', 'cobre', 'aço', 'titânio', 'latão', 'polietileno') +rho = materiais[material]['densidade'] +calor_especifico = materiais[material]['calor_especifico'] + +# Dimensões da placa +L = 0.60 # Comprimento (m) +W = 0.22 # Largura (m) +espessura_placa = 0.003 # Espessura (m) +area_placa = 5 * (L * W) + 0.3 # Área total de troca térmica (m²) +volume_placa = 6 * L * W * espessura_placa # Volume da placa (m³) +massa_placa = rho * volume_placa # Massa da placa (kg) + +# Simulação para a placa +placa = Arrefecimento(velocidade, area_placa, L, calor_gerado, tipo_geometria='placa') +placa.plotar_grafico(temp_objeto, calor_especifico, massa_placa, tempo_simulacao,"bateria") + +# Dimensões do motor +comprimento_motor = 0.355 # Comprimento do motor (m) +diametro_motor = 0.20 # Diâmetro do motor (m) +raio_externo_motor = diametro_motor / 2 # Raio externo do motor (m) +espessura_motor = 0.003 # Considerando apenas os 3 mm mais externos + +# Área e volume da camada externa do motor +area_motor = math.pi * diametro_motor * comprimento_motor # Área externa do cilindro (m²) +volume_motor = volume_camada_cilindro(raio_externo_motor, espessura_motor, comprimento_motor) +massa_motor = volume_motor * rho # Massa do motor considerando 3 mm externos + +# Simulação para o motor +motor = Arrefecimento(velocidade, area_motor, diametro_motor, calor_gerado, tipo_geometria='cilindro') +motor.plotar_grafico(temp_objeto, calor_especifico, massa_motor, tempo_simulacao,"motor") \ No newline at end of file From acc5f216ee986f570f902d45363fd7ed2ae9b552 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Sun, 3 Nov 2024 21:28:57 -0300 Subject: [PATCH 13/25] =?UTF-8?q?Create=20Chassi=20Com=20Implementa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Planilhas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementação das tabelas contendo informações dos nós, conectividade e parâmetros físicos de cada barra. Montagem das matrizes e plots da analise modal funcionando. --- ...plementa\303\247\303\243o de Planilhas.py" | 401 ++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 "Chassi Com Implementa\303\247\303\243o de Planilhas.py" diff --git "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" new file mode 100644 index 0000000..11b5afa --- /dev/null +++ "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" @@ -0,0 +1,401 @@ +import numpy as np +from scipy.linalg import eigh +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D +from scipy.sparse import lil_matrix +from scipy.sparse.linalg import spsolve +import pandas as pd +import sys +import os +np.set_printoptions(threshold=sys.maxsize) +np.set_printoptions(linewidth=200, suppress=True) + +class Estrutura: + def __init__(self, elements,element_properties, nodes, m, Id, Ip): + self.elements = elements #Matriz de elementos conectados + self.num_elements = len(elements) #Número de elementos + self.element_properties = element_properties #Matriz de propriedades dos elementos + self.nodes = nodes #Matriz de nós com suas posições + self.coordinates = np.array(nodes[['x', 'y', 'z']]) + self.connections = np.array(elements[['Node a', 'Node b']]) + self.num_nodes = len(nodes) #Número total de nós + self.massa = m #Massa do carro (Kg) + self.momento_inercia_direcao = Id #Momento de inércia em relação à direção (kg.m^2) + self.momento_inercia_plano = Ip #Momento de inércia em relação ao plano (kg.m^2) + self.num_dofs_per_node = 6 #Graus de liberdade por nó + self.num_dofs = self.num_nodes * self.num_dofs_per_node #Total de Graus de liberdade (gdls) + self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) #Tamanho adequado para a matriz global + self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) + self.num_modes = 12 #Número de modos de vibração a serem retornados + + def node_loc_matrix(self): + print(self.nodes) + + def connect_matrix(self): + print(self.elements) + + def calcular_comprimento(self, index): #Função auxiliar para cálculo de comprimento dos elementos + + node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) + x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] + x2, y2, z2 = self.nodes['x'][node2], self.nodes['y'][node2], self.nodes['z'][node2] + return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) + + def element(self, index): + #Tomando as propriedades de cada elemento + E = self.element_properties['E'][index] + A = self.element_properties['A'][index] + I = self.element_properties['I'][index] + J = self.element_properties['J'][index] + G = self.element_properties['G'][index] + + kappa=0.9 #Fator de correção para cisalhamento + + L_e = self.calcular_comprimento(index) + Phi = (12 * E * I) / (kappa * G * A * L_e**2) + rho = 7850 # kg/m^3 + c1 = E * A / L_e + c2 = G * J / L_e + c3 = E * I / L_e**3 #Euler-Bernoulli + c4 = (E*I)/(L_e**3*(1+Phi)) #Timoshenko + t1 = (4+Phi) + t2 = (2-Phi) + d1 = rho*A*L_e + d2 = (I*L_e)/6 + d3 = (rho*A*L_e)/420 + + # Matriz de Rigidez Elementar (Euler-Bernoulli) + # Para converter para timoshenko basta trocar c3 por c4,onde tem (4 * L_e**2 * c3) substitui por (t1* L_e**2 * c4) e onde tiver (2 * L_e**2 * c3) por (t2* L_e**2 * c4)) + k_e= np.array([ + [12 * c4, 0, 0, 6 * L_e * c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4, 0, 0], + [0, c1, 0, 0, 0, 0, 0, -c1, 0, 0, 0, 0], + [0, 0, 12 * c4, 0, 0, 6 * L_e* c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4], + [6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0], + [0, 0, 0, 0, c2, 0, 0, 0, 0, 0, -c2, 0], + [0, 0, 6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2 * L_e**2 * c4], + [-12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4, 0, 0], + [0, -c1, 0, 0, 0, 0, 0, c1, 0, 0, 0, 0], + [0, 0, -12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4], + [6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0], + [0, 0, 0, 0, -c2, 0, 0, 0, 0, 0, c2, 0], + [0, 0, 6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4] + ]) + + # Matriz de Massa Elementar + m_e= np.array([ + [156 * d3, 0, 0, 22 * L_e * d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3, 0, 0], + [0,2*d1, 0, 0, 0, 0, 0, d1, 0, 0, 0, 0], + [0, 0, 156 * d3, 0, 0, 22 * L_e* d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3], + [22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0], + [0, 0, 0, 0, 2*d2, 0, 0, 0, 0, 0, d2, 0], + [0, 0, 22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3], + [54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156* d3, 0, 0, -22 * L_e * d3, 0, 0], + [0, d1, 0, 0, 0, 0, 0, 2*d1, 0, 0, 0, 0], + [0, 0, 54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156 * d3, 0, 0, -22 * L_e * d3], + [-13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0], + [0, 0, 0, 0, d2, 0, 0, 0, 0, 0, 2*d2, 0], + [0, 0, -13 * L_e * d3, 0, 0,-3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3] + ]) + return k_e,m_e + + def aplicar_engastes(self, nodes, dofs): + for node in nodes: #Laço para selecionar cada nó que será engastado + for dof in dofs: #Laço para selecionar quais graus de liberdade serão fixados + index = node * self.num_dofs_per_node + dof #Identificação da entrada da matriz que precisa ser restringida pelo engaste + self.K_global[index, index] = 10**10 # Um valor suficientemente grande para simular um engaste + + def matrizes_global(self): + #Calculando as matrizes de rigidez e massa de cada elemento + for index in range(self.num_elements): + + k_e, m_e= self.element(index) + node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) + # DOFs associados ao elemento + dofs = [6 * node1, 6 * node1 + 1, 6 * node1 + 2, 6 * node1 + 3, 6 * node1 + 4, 6 * node1 + 5, + 6 * node2, 6 * node2 + 1, 6 * node2 + 2, 6 * node2 + 3, 6 * node2 + 4, 6 * node2 + 5] + + # Atualizando as matrizes globais + self.K_global[np.ix_(dofs, dofs)] += k_e + self.M_global[np.ix_(dofs, dofs)] += m_e + + #self.aplicar_engastes([0, 2, 4, 5], [0, 1, 2, 3, 4, 5]) #Por enquanto não estaremos considerando engastes + pd.DataFrame(self.K_global).to_csv('Matriz_Global_Rigidez.csv', index=True, header=True) + pd.DataFrame(self.M_global).to_csv('Matriz_Global_Massa.csv', index=True, header=True) + + return self.K_global,self.M_global + + def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): + KF_total = 0 + KT_total = 0 + KF_elements = [] + KT_elements = [] + torcao, deformacao, flexao1, flexao2, flexao3 = [], [], [], [], [] + for index in range(len(self.elements)): + E = self.element_properties['E'][index] + A = self.element_properties['A'][index] + I = self.element_properties['I'][index] + J = self.element_properties['J'][index] + G = self.element_properties['G'][index] + L_e = self.calcular_comprimento(index) + # Equação de torsão + torcao_val = (F_torcao * L_e) / (G * J) #Fonte[1] + torcao.append(torcao_val) + # Equação para deformação axial + deformacao_val = (F_axial* L_e / (A * E)) #Fonte[2] + deformacao.append(deformacao_val) + # Equação para flexão + flexao_val1 = (F_flexao1*L_e**3)/(48 * E * I) #Fonte[3.1] (carga pontual no meio do elemento biapoiado) + flexao_val2 = (5*F_flexao2*L_e**4)/(384 * E * I) #Fonte[3.2] (carga distribuída ao longo de todo o elemento biapoiado) + flexao_val3 = flexao_val1 + flexao_val2 #Fonte[3.3] (tentativa de carregamento misto) + flexao1.append(flexao_val1) + flexao2.append(flexao_val2) + flexao3.append(flexao_val3) + + # Rigidez flexional + KF = E * I / L_e + # Rigidez torsional + KT = G * J / L_e + + KF_total += KF + KT_total += KT + + KF_elements.append(KF) + KT_elements.append(KT) + + return (np.array(torcao), np.array(deformacao), np.array(flexao1), + np.array(flexao2), np.array(flexao3), KF_total, KT_total, KF_elements, KT_elements) + + def modal_analysis(self): + # Análise modal por resolução do problema de autovalor e autovetor + unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) + + # Frequências naturais (raiz quadrada dos autovalores) + unsorted_frequencies = np.sqrt(unsorted_eigenvalues) / (2 * np.pi) # Divisão por 2*pi para converter para hertz + + # Tratando os dados (tomando apenas as 20 primeiras frequências naturais) + sorted_indices = np.argsort(unsorted_frequencies) # Ordena as frequências em ordem crescente + top_indices = sorted_indices[:self.num_modes] # Seleciona os índices dos primeiros n modos + + eigenvalues = np.array(unsorted_eigenvalues)[top_indices] # Filtra os primeiros n autovalores + eigenvectors = np.array(unsorted_eigenvectors)[:, top_indices] # Filtra os primeiros n autovetores + frequencies = np.array(unsorted_frequencies)[top_indices] # Filtra as primeiras n frequências + + return eigenvalues, eigenvectors, frequencies + + def Mesh(self): + + filename = input("Insira o nome do arquivo: ") + ".geo" + diretorio = input("Insira o diretorio onde o arquivo .geo deve ser salvo: ") + + if not os.path.exists(diretorio): + os.makedirs(diretorio) + + filepath = os.path.join(diretorio, filename) + + with open(filepath, 'w') as geo_file: + for i, (x, y, z) in enumerate(self.nodes): + geo_file.write(f'Point({i + 1}) = {{{x}, {y}, {z}, 1.0}};\n') + + for i, (start, end) in enumerate(self.elements): + geo_file.write(f'Line({i + 1}) = {{{start + 1}, {end + 1}}};\n') + + if len(self.elements) > 2: + line_loop_indices = ', '.join(str(i + 1) for i in range(len(elements))) + geo_file.write(f'Line Loop(1) = {{{line_loop_indices}}};\n') + geo_file.write('Plane Surface(1) = {1};\n') + + geo_file.write('Mesh.Algorithm = 6;\n') + geo_file.write('Mesh.ElementOrder = 1;\n') + geo_file.write('Mesh.Format = 1;\n') + + print(f'O arquivo foi salvo em: {filepath}, basta abrir o GMSH, e abrir o arquivo') + + def structure_plot(self): + # Plotando o gráfico 3D da estrutura + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Adicionando os pontos (nós) + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + # Adicionando as linhas de ligação entre os nós + for node1, node2 in self.connections: + x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] + y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] + z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] + ax.plot(x_coords, y_coords, z_coords, 'r-', marker='o') + + # Configurações adicionais do gráfico + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.set_title('Estrutura 3D do Chassi') + + plt.show() + + def modal_analysis_plot(self): + autovalores, autovetores, _ = self.modal_analysis() + #Criando plot para os modos de vibração separadamente + for mode_idx in range(len(autovalores)): + fig = plt.figure(figsize=(8, 6)) + ax = fig.add_subplot(111, projection='3d') + ax.set_title(f'Modo {mode_idx + 1} Viga') + + #Colocando a legenda dos nós no gráfico + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=100) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + #Colocando os nós no gráfico + for node1, node2 in self.connections: + x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] + y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] + z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] + ax.plot(x_coords, y_coords, z_coords, 'b--') + + #Inicializando os modos de vibração e vetor de deslocamentos + mode_shape = autovetores[:, mode_idx] + displacements = np.zeros((len(self.coordinates), 3)) + + #Aplicando os deslocamentos na estrutura + for j, (x, y, z) in enumerate(self.coordinates): + if j > 0 and j < len(self.coordinates) - 1: + displacements[j, 0] = mode_shape[2 * j] + displacements[j, 1] = mode_shape[2 * j + 1] + displacements[j, 2] = 0 + + deformed_nodes = np.array(self.coordinates) + displacements + + #Plotando a estrutura deformada + for node1, node2 in self.connections: + x = [deformed_nodes[node1-1][0], deformed_nodes[node2-1][0]] + y = [deformed_nodes[node1-1][1], deformed_nodes[node2-1][1]] + z = [deformed_nodes[node1-1][2], deformed_nodes[node2-1][2]] + ax.plot(x, y, z, 'r-') + + #Configurações adicionais + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + plt.xlim([-0.5,2]) + plt.ylim([-0.5,1]) + ax.set_zlim({0,1}) + plt.tight_layout() + plt.show() + + + def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): + torcao,deformacao_axial,flexao1,flexao2,flexao3,KF_total,KT_total,KF_elements,KT_elements= self.shape_fun(F_flexao1, F_flexao2, F_axial, F_torcao) + # Configuração dos subplots + fig, axs = plt.subplots(6, 1, figsize=(12, 22)) + + # Plot da Torção + axs[0].plot(torcao, 'o-', label=[f'Força {F}N' for F in F_torcao]) + axs[0].set_title('Deformação por Torção de cada Elemento') + axs[0].set_xlabel('Elemento') + axs[0].set_ylabel('Torção (rad)') + axs[0].legend() + + # Plot da Deformação Axial + axs[1].plot(deformacao_axial, 's-', label=[f'Força {F}N' for F in F_axial]) + axs[1].set_title('Deformação Axial de cada Elemento') + axs[1].set_xlabel('Elemento') + axs[1].set_ylabel('Deformação (m)') + axs[1].legend() + + # Plot da Flexão por Carga Pontual + axs[2].plot(flexao1, 'o-', label=[f'Força {F}N' for F in F_flexao1]) + axs[2].set_title('Deformação por Carga Pontual de cada Elemento') + axs[2].set_xlabel('Elemento') + axs[2].set_ylabel('Deflexão (m)') + axs[2].legend() + + # Plot da Flexão por Carga Distribuída + axs[3].plot(flexao2, 'o-', label=[f'Força {F}N' for F in F_flexao2]) + axs[3].set_title('Deformação por Carga Distribuída de cada Elemento') + axs[3].set_xlabel('Elemento') + axs[3].set_ylabel('Deflexão (m)') + axs[3].legend() + + # Plot da Flexão Mista + axs[4].plot(flexao3, 'o-', label='Carregamento misto') + axs[4].set_title('Deformação por Flexão Mista de cada Elemento') + axs[4].set_xlabel('Elemento') + axs[4].set_ylabel('Deflexão (m)') + axs[4].legend() + + # Plot da Rigidez Flexional e Torsional por Elemento + axs[5].plot(KF_elements, 'o-', label='Rigidez Flexional (KF)') + axs[5].plot(KT_elements, 's-', label='Rigidez Torsional (KT)') + axs[5].set_title('Rigidez Flexional e Torsional de cada Elemento') + axs[5].set_xlabel('Elemento') + axs[5].set_ylabel('Rigidez (N/m)') + axs[5].legend() + + # Mostrando os totais no título geral + plt.suptitle(f'KF Total: {KF_total:.2e} N/m, KT Total: {KT_total:.2e} N/m', fontsize=16) + + # Ajustes de layout + plt.tight_layout(rect=[0, 0, 1, 0.96]) + plt.show() + + +nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" +elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Elements.csv" + +# Carregar o arquivo para inspecionar seu conteúdo +nodes = pd.read_csv(nodes_file_path) +element_data = pd.read_csv(elements_file_path) + +# Selecionando as colunas de conectividades e propriedades dos elementos + +elements = element_data[['Element ID','Node a', 'Node b']] +element_properties = element_data[['Element ID','A', 'I', 'J', 'E', 'G']] + +#Criar a estrutura e montar as matrizes de rigidez e massa globais +#Dados: n = len(nodes), +# m = 1500 kg, +# rho = 7850 kg/m^3 +# A = 0.225 m^2 +# E = 210e9 # Módulo de elasticidade em Pa +# I = 8.33e-6 # Momento de inércia em m^4 +# Ip = Id = 8.33e-6 kg.m^2 + +#Criar a estrutura e montar as matrizes de rigidez e massa globais, atribuir forças +F_flexao1 = np.array([1000, 2000, 3000, 4000, 5000]) +F_flexao2 = np.array([1000, 1000, 1000, 1000, 1000]) +F_axial = np.array([1000, 2000, 3000, 4000, 5000]) +F_torcao = np.array([1000, 2000, 3000, 4000, 5000]) + +#Inicializando a Estrutura +estrutura = Estrutura(elements, element_properties, nodes, 1500, 8.33e-6, 8.33e-6) + +#Gerar as matrizes de localização dos nós e de conectividade +estrutura.node_loc_matrix() +estrutura.connect_matrix() + +K_global, M_global = estrutura.matrizes_global() + +#print("\\n Matriz de Rigidez Global") +#print(K_global) + +#print("\\n Matriz de Massa Global") +#print(M_global) + +#Plotando os resultados das deformações +estrutura.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) + +# Plotando o gráfico 3D da estrutura +estrutura.structure_plot() + +#Gerar autovalores, autovetores e frequências naturais +autovalores, autovetores, frequencias = estrutura.modal_analysis() + +#Exibindo as frequências naturais e modos de vibração da estrutura +print("\\n Frequências Naturais (ω) da estrutura:") +print(frequencias) + +#Plotagem dos modos de vibração para a estrutura de vigas +estrutura.modal_analysis_plot() \ No newline at end of file From 2284f73f3c6c37a63a761c146c2106d6581c4352 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Sat, 16 Nov 2024 17:37:35 -0300 Subject: [PATCH 14/25] Update Arrefecimento_atualizado.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementação da funçãode geração de aletas para melhora do arrefecimento desenvolvido por Cayque. Geometria das aletas consideradas: Triangulares Retangulares --- Arrefecimento_atualizado.py | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Arrefecimento_atualizado.py b/Arrefecimento_atualizado.py index 0a2ae42..190a23a 100644 --- a/Arrefecimento_atualizado.py +++ b/Arrefecimento_atualizado.py @@ -8,24 +8,24 @@ 'cobre': {'densidade': 8960, 'calor_especifico': 385}, 'aço': {'densidade': 7850, 'calor_especifico': 500}, 'titânio': {'densidade': 4500, 'calor_especifico': 522}, - 'latão': {'densidade': 8530, 'calor_especifico': 380}, # Latão adicionado - 'polietileno': {'densidade': 950, 'calor_especifico': 1900} # Polietileno adicionado + 'latão': {'densidade': 8530, 'calor_especifico': 380}, + 'polietileno': {'densidade': 950, 'calor_especifico': 1900} } class Arrefecimento: def __init__(self, velocidade, area, comprimento, calor_gerado,tipo_geometria='placa'): - self.rho = 1.184 # Massa específica do ar (kg/m^3) - self.c = 1007 # Calor específico do ar (J/(kg*K)) - self.v = velocidade - self.mi = 1.849e-5 # Viscosidade dinâmica do ar (kg/m*s) - self.ni = self.mi / self.rho # Viscosidade cinemática do ar (m²/s) - self.k_ar = 0.02551 # Condutividade térmica do ar (W/m*K) - self.tf = 25 # Temperatura do ar (°C) - self.Pr = self.mi * self.c / self.k_ar # Número de Prandtl - self.a_res = area # Área de resfriamento (m²) - self.L_c = comprimento # Comprimento característico (m) - self.calor_gerado = calor_gerado # Calor gerado pelo objeto (W) - self.tipo_geometria = tipo_geometria # Geometria do objeto ('placa' ou 'cilindro') + self.rho = 1.184 # Massa específica do ar (kg/m^3) + self.c = 1007 # Calor específico do ar (J/(kg*K)) + self.v = velocidade # Velocidade do ar (m/s) + self.mi = 1.849e-5 # Viscosidade dinâmica do ar (kg/m*s) + self.ni = self.mi / self.rho # Viscosidade cinemática do ar (m²/s) + self.k_ar = 0.02551 # Condutividade térmica do ar (W/m*K) + self.tf = 25 # Temperatura do ar (°C) + self.Pr = self.mi * self.c / self.k_ar # Número de Prandtl + self.a_res = area # Área de resfriamento (m²) + self.L_c = comprimento # Comprimento característico (m) + self.calor_gerado = calor_gerado # Calor gerado pelo objeto (W) + self.tipo_geometria = tipo_geometria # Geometria do objeto ('placa' ou 'cilindro') def reynolds(self): return self.rho * self.v * self.L_c / self.mi @@ -80,9 +80,9 @@ def volume_camada_cilindro(raio_externo, espessura, comprimento): return math.pi * comprimento * (raio_externo**2 - raio_interno**2) # Parâmetros de simulação -velocidade = 10 -calor_gerado = 100000 / 3600 # Potência convertida para J/s -temp_objeto = 100 # Temperatura inicial do objeto (°C) +velocidade = 5 +calor_gerado = 10000 / 3600 # Potência convertida para J/s +temp_objeto = 25 # Temperatura inicial do objeto (°C) tempo_simulacao = 6000 # Tempo total da simulação (s) material = 'aluminio' # Escolha o material ('aluminio', 'cobre', 'aço', 'titânio', 'latão', 'polietileno') rho = materiais[material]['densidade'] @@ -91,20 +91,20 @@ def volume_camada_cilindro(raio_externo, espessura, comprimento): # Dimensões da placa L = 0.60 # Comprimento (m) W = 0.22 # Largura (m) -espessura_placa = 0.003 # Espessura (m) -area_placa = 5 * (L * W) + 0.3 # Área total de troca térmica (m²) -volume_placa = 6 * L * W * espessura_placa # Volume da placa (m³) -massa_placa = rho * volume_placa # Massa da placa (kg) +espessura_caixa = 0.003 # Espessura (m) +area_caixa = 5 * (L * W) + 0.3 # Área total de troca térmica (m²) +volume_caixa = 6 * L * W * espessura_caixa # Volume da placa (m³) +massa_caixa = rho * volume_caixa # Massa da placa (kg) # Simulação para a placa -placa = Arrefecimento(velocidade, area_placa, L, calor_gerado, tipo_geometria='placa') -placa.plotar_grafico(temp_objeto, calor_especifico, massa_placa, tempo_simulacao,"bateria") +placa = Arrefecimento(velocidade, area_caixa, L, calor_gerado, tipo_geometria='placa') +placa.plotar_grafico(temp_objeto, calor_especifico, massa_caixa, tempo_simulacao,"bateria") # Dimensões do motor comprimento_motor = 0.355 # Comprimento do motor (m) diametro_motor = 0.20 # Diâmetro do motor (m) raio_externo_motor = diametro_motor / 2 # Raio externo do motor (m) -espessura_motor = 0.003 # Considerando apenas os 3 mm mais externos +espessura_motor = 0.003 # Considerando apenas os 5 mm mais externos # Área e volume da camada externa do motor area_motor = math.pi * diametro_motor * comprimento_motor # Área externa do cilindro (m²) From d8d5d8f376affed114c02b85552bbdb42d6829be Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Sat, 16 Nov 2024 17:39:47 -0300 Subject: [PATCH 15/25] Revert "Update Arrefecimento_atualizado.py" This reverts commit 2284f73f3c6c37a63a761c146c2106d6581c4352. --- Arrefecimento_atualizado.py | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Arrefecimento_atualizado.py b/Arrefecimento_atualizado.py index 190a23a..0a2ae42 100644 --- a/Arrefecimento_atualizado.py +++ b/Arrefecimento_atualizado.py @@ -8,24 +8,24 @@ 'cobre': {'densidade': 8960, 'calor_especifico': 385}, 'aço': {'densidade': 7850, 'calor_especifico': 500}, 'titânio': {'densidade': 4500, 'calor_especifico': 522}, - 'latão': {'densidade': 8530, 'calor_especifico': 380}, - 'polietileno': {'densidade': 950, 'calor_especifico': 1900} + 'latão': {'densidade': 8530, 'calor_especifico': 380}, # Latão adicionado + 'polietileno': {'densidade': 950, 'calor_especifico': 1900} # Polietileno adicionado } class Arrefecimento: def __init__(self, velocidade, area, comprimento, calor_gerado,tipo_geometria='placa'): - self.rho = 1.184 # Massa específica do ar (kg/m^3) - self.c = 1007 # Calor específico do ar (J/(kg*K)) - self.v = velocidade # Velocidade do ar (m/s) - self.mi = 1.849e-5 # Viscosidade dinâmica do ar (kg/m*s) - self.ni = self.mi / self.rho # Viscosidade cinemática do ar (m²/s) - self.k_ar = 0.02551 # Condutividade térmica do ar (W/m*K) - self.tf = 25 # Temperatura do ar (°C) - self.Pr = self.mi * self.c / self.k_ar # Número de Prandtl - self.a_res = area # Área de resfriamento (m²) - self.L_c = comprimento # Comprimento característico (m) - self.calor_gerado = calor_gerado # Calor gerado pelo objeto (W) - self.tipo_geometria = tipo_geometria # Geometria do objeto ('placa' ou 'cilindro') + self.rho = 1.184 # Massa específica do ar (kg/m^3) + self.c = 1007 # Calor específico do ar (J/(kg*K)) + self.v = velocidade + self.mi = 1.849e-5 # Viscosidade dinâmica do ar (kg/m*s) + self.ni = self.mi / self.rho # Viscosidade cinemática do ar (m²/s) + self.k_ar = 0.02551 # Condutividade térmica do ar (W/m*K) + self.tf = 25 # Temperatura do ar (°C) + self.Pr = self.mi * self.c / self.k_ar # Número de Prandtl + self.a_res = area # Área de resfriamento (m²) + self.L_c = comprimento # Comprimento característico (m) + self.calor_gerado = calor_gerado # Calor gerado pelo objeto (W) + self.tipo_geometria = tipo_geometria # Geometria do objeto ('placa' ou 'cilindro') def reynolds(self): return self.rho * self.v * self.L_c / self.mi @@ -80,9 +80,9 @@ def volume_camada_cilindro(raio_externo, espessura, comprimento): return math.pi * comprimento * (raio_externo**2 - raio_interno**2) # Parâmetros de simulação -velocidade = 5 -calor_gerado = 10000 / 3600 # Potência convertida para J/s -temp_objeto = 25 # Temperatura inicial do objeto (°C) +velocidade = 10 +calor_gerado = 100000 / 3600 # Potência convertida para J/s +temp_objeto = 100 # Temperatura inicial do objeto (°C) tempo_simulacao = 6000 # Tempo total da simulação (s) material = 'aluminio' # Escolha o material ('aluminio', 'cobre', 'aço', 'titânio', 'latão', 'polietileno') rho = materiais[material]['densidade'] @@ -91,20 +91,20 @@ def volume_camada_cilindro(raio_externo, espessura, comprimento): # Dimensões da placa L = 0.60 # Comprimento (m) W = 0.22 # Largura (m) -espessura_caixa = 0.003 # Espessura (m) -area_caixa = 5 * (L * W) + 0.3 # Área total de troca térmica (m²) -volume_caixa = 6 * L * W * espessura_caixa # Volume da placa (m³) -massa_caixa = rho * volume_caixa # Massa da placa (kg) +espessura_placa = 0.003 # Espessura (m) +area_placa = 5 * (L * W) + 0.3 # Área total de troca térmica (m²) +volume_placa = 6 * L * W * espessura_placa # Volume da placa (m³) +massa_placa = rho * volume_placa # Massa da placa (kg) # Simulação para a placa -placa = Arrefecimento(velocidade, area_caixa, L, calor_gerado, tipo_geometria='placa') -placa.plotar_grafico(temp_objeto, calor_especifico, massa_caixa, tempo_simulacao,"bateria") +placa = Arrefecimento(velocidade, area_placa, L, calor_gerado, tipo_geometria='placa') +placa.plotar_grafico(temp_objeto, calor_especifico, massa_placa, tempo_simulacao,"bateria") # Dimensões do motor comprimento_motor = 0.355 # Comprimento do motor (m) diametro_motor = 0.20 # Diâmetro do motor (m) raio_externo_motor = diametro_motor / 2 # Raio externo do motor (m) -espessura_motor = 0.003 # Considerando apenas os 5 mm mais externos +espessura_motor = 0.003 # Considerando apenas os 3 mm mais externos # Área e volume da camada externa do motor area_motor = math.pi * diametro_motor * comprimento_motor # Área externa do cilindro (m²) From 112b95c67534e98499096a3d287160d049ea0d64 Mon Sep 17 00:00:00 2001 From: EduardoAMXango <167939544+EduardoAMXango@users.noreply.github.com> Date: Sat, 16 Nov 2024 17:44:47 -0300 Subject: [PATCH 16/25] Update Arrefecimento_atualizado.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementação da função que adiciona aletas desenvolvida por Cayque. Geometria das aletas consideradas: Retangulares Triangulares --- Arrefecimento_atualizado.py | 163 ++++++++++++++++++++++-------------- 1 file changed, 98 insertions(+), 65 deletions(-) diff --git a/Arrefecimento_atualizado.py b/Arrefecimento_atualizado.py index 0a2ae42..b2a2e38 100644 --- a/Arrefecimento_atualizado.py +++ b/Arrefecimento_atualizado.py @@ -4,74 +4,109 @@ # Dicionário contendo as propriedades dos materiais materiais = { - 'aluminio': {'densidade': 2600, 'calor_especifico': 950}, # Densidade em kg/m^3, Calor específico em J/(kg·K) + 'aluminio': {'densidade': 2600, 'calor_especifico': 950}, 'cobre': {'densidade': 8960, 'calor_especifico': 385}, 'aço': {'densidade': 7850, 'calor_especifico': 500}, 'titânio': {'densidade': 4500, 'calor_especifico': 522}, - 'latão': {'densidade': 8530, 'calor_especifico': 380}, # Latão adicionado - 'polietileno': {'densidade': 950, 'calor_especifico': 1900} # Polietileno adicionado + 'latão': {'densidade': 8530, 'calor_especifico': 380}, + 'polietileno': {'densidade': 950, 'calor_especifico': 1900} } class Arrefecimento: - def __init__(self, velocidade, area, comprimento, calor_gerado,tipo_geometria='placa'): - self.rho = 1.184 # Massa específica do ar (kg/m^3) - self.c = 1007 # Calor específico do ar (J/(kg*K)) + def __init__(self, velocidade, area, comprimento, calor_gerado, tipo_geometria='placa'): + self.rho = 1.184 + self.c = 1007 self.v = velocidade - self.mi = 1.849e-5 # Viscosidade dinâmica do ar (kg/m*s) - self.ni = self.mi / self.rho # Viscosidade cinemática do ar (m²/s) - self.k_ar = 0.02551 # Condutividade térmica do ar (W/m*K) - self.tf = 25 # Temperatura do ar (°C) - self.Pr = self.mi * self.c / self.k_ar # Número de Prandtl - self.a_res = area # Área de resfriamento (m²) - self.L_c = comprimento # Comprimento característico (m) - self.calor_gerado = calor_gerado # Calor gerado pelo objeto (W) - self.tipo_geometria = tipo_geometria # Geometria do objeto ('placa' ou 'cilindro') + self.mi = 1.849e-5 + self.ni = self.mi / self.rho + self.k_ar = 0.02551 + self.tf = 25 + self.Pr = self.mi * self.c / self.k_ar + self.a_res = area + self.L_c = comprimento #Comprimento caracteristico + self.calor_gerado = calor_gerado + self.tipo_geometria = tipo_geometria + self.tipos_aletas = ['retangular', 'triangular'] + self.alturas_aletas = [0.005, 0.01] # Altura de aletas variadas (menores que 0.01m) + self.espacos_entre_aletas = [0.01, 0.03] # Distâncias entre as aletas + self.espessura_aleta = 0.003 + + def geometria_aleta(self, tipo, altura_aleta, comprimento_aleta, espessura_aleta, rho): + if tipo == 'retangular': + area = 2 * altura_aleta * comprimento_aleta # Área da seção transversal + volume = altura_aleta * comprimento_aleta * espessura_aleta + elif tipo == 'triangular': + area = 2 * math.sqrt(altura_aleta**2 + (espessura_aleta/2)**2) * comprimento_aleta + volume = (espessura_aleta * altura_aleta / 2) * comprimento_aleta + else: + raise ValueError("Tipo de geometria não suportado. Use 'retangular' ou 'triangular'.") + return area, volume * rho + + def numero_aletas(self, espaco): + if self.tipo_geometria == 'placa': + n1 = math.floor(self.L_c / (espaco + self.espessura_aleta)) #Calculo do numero de aletas em uma face da caixa de baterias + num_aletas = 4 * n1 #Calculo para o numero total das aletas, considerando 4 faces + elif self.tipo_geometria == 'cilindro': + num_aletas = math.floor((self.L_c * math.pi) / (espaco + self.espessura_aleta)) #Calculo para o numero de aletas no motor + else: + raise ValueError("Tipo de geometria não suportada.") + return num_aletas def reynolds(self): return self.rho * self.v * self.L_c / self.mi - + def calc_Nu(self): Rey = self.reynolds() - if self.tipo_geometria == 'placa': - if Rey < 200000: # Fluxo laminar + if Rey < 200000: Nu = 0.332 * Rey**0.5 * self.Pr**(1/3) - else: # Fluxo Turbulento - X = 200000 * self.ni / self.v # Ponto de transição para turbulento + else: + X = 200000 * self.ni / self.v Rey_X = X * self.v / self.ni A = 0.037 * Rey_X**0.8 - 0.664 * Rey_X**0.5 Nu = 0.037 * (Rey**0.8 - A) * self.Pr**(1/3) elif self.tipo_geometria == 'cilindro': Nu = 0.3 + (0.62 * Rey**0.5 * self.Pr**(1/3)) / (1 + (0.4/self.Pr)**(2/3))**0.25 else: - raise ValueError("Tipo de geometria não suportada. Escolha 'placa' ou 'cilindro'.") - + raise ValueError("Tipo de geometria não suportada.") return Nu - - def calor_conveccao(self, temp_sup): - nu = self.calc_Nu() - h = nu * self.k_ar / self.L_c - return h * self.a_res * (temp_sup - self.tf) - - def troca_termica(self, tempo_simulacao, temp_objeto, c_objeto, m_objeto): - temp_atual = temp_objeto - temperaturas = [] - tempos = [] - for i in range(tempo_simulacao): - q_conveccao = self.calor_conveccao(temp_atual) - q_total = self.calor_gerado - q_conveccao - temp_atual = (q_total / (c_objeto * m_objeto)) + temp_atual - tempos.append(i) - temperaturas.append(temp_atual) + + def arrefecimento(self, tempo, a_res, temp_inicial, c_objeto, m_objeto): + h = (self.calc_Nu() * self.k_ar) / self.L_c + T_final = self.tf + (self.calor_gerado / (h * a_res)) + tau = (m_objeto * c_objeto) / (h * a_res) + return T_final + (temp_inicial - T_final) * np.exp(-tempo / tau) + + def troca_termica(self, tempo_simulacao, temp_inicial, a_res, c_objeto, m_objeto): + tempos = np.linspace(0, tempo_simulacao, tempo_simulacao) + temperaturas = self.arrefecimento(tempos, a_res, temp_inicial, c_objeto, m_objeto) return tempos, temperaturas - def plotar_grafico(self, temp_objeto, c_objeto, m_objeto, tempo_simulacao,objeto): - tempos, temperaturas = self.troca_termica(tempo_simulacao, temp_objeto, c_objeto, m_objeto) - plt.plot(tempos, temperaturas) + def plotar_grafico(self, temp_objeto, a_res, c_objeto, m_objeto, tempo_simulacao, legenda): + tempos, temperaturas = self.troca_termica(tempo_simulacao, temp_objeto, a_res, c_objeto, m_objeto) + plt.plot(tempos, temperaturas, label=legenda) + + def plotar_graficos_aletas(self, temp_inicial, c_objeto, m_objeto, tempo_simulacao, objeto): + plt.figure(figsize=(12, 8)) + # Plot do objeto sem aletas + self.plotar_grafico(temp_inicial, self.a_res, c_objeto, m_objeto, tempo_simulacao, 'Sem aletas') + # Plots com diferentes configurações de aletas + for tipo in self.tipos_aletas: + for altura in self.alturas_aletas: + for espaco in self.espacos_entre_aletas: + area_aleta, massa_aleta = self.geometria_aleta(tipo, altura, self.L_c, self.espessura_aleta, rho) + num_aletas = self.numero_aletas(espaco) + area_com_aletada = self.a_res + num_aletas * area_aleta + massa_com_aletas = m_objeto + num_aletas * massa_aleta + legenda = f'Aleta {tipo}, Altura: {altura*1000:.1f}mm, Espaço: {espaco*1000:.1f}mm' + self.plotar_grafico(temp_inicial, area_com_aletada, c_objeto, massa_com_aletas, tempo_simulacao, legenda) + plt.title(f'Variação da Temperatura ao Longo do Tempo ({objeto})') plt.xlabel('Tempo (s)') plt.ylabel('Temperatura (°C)') + plt.legend(loc='upper left', bbox_to_anchor=(1.05, 1)) plt.grid(True) + plt.tight_layout() plt.show() # Função para calcular o volume da camada externa de um cilindro @@ -81,36 +116,34 @@ def volume_camada_cilindro(raio_externo, espessura, comprimento): # Parâmetros de simulação velocidade = 10 -calor_gerado = 100000 / 3600 # Potência convertida para J/s -temp_objeto = 100 # Temperatura inicial do objeto (°C) -tempo_simulacao = 6000 # Tempo total da simulação (s) -material = 'aluminio' # Escolha o material ('aluminio', 'cobre', 'aço', 'titânio', 'latão', 'polietileno') +calor_gerado = 100000 / 3600 # Potência convertida para J/s +temp_objeto = 60 # Temperatura inicial do objeto (°C) +tempo_simulacao = 2500 # Tempo total da simulação (s) +material = 'aluminio' rho = materiais[material]['densidade'] calor_especifico = materiais[material]['calor_especifico'] -# Dimensões da placa -L = 0.60 # Comprimento (m) -W = 0.22 # Largura (m) -espessura_placa = 0.003 # Espessura (m) -area_placa = 5 * (L * W) + 0.3 # Área total de troca térmica (m²) -volume_placa = 6 * L * W * espessura_placa # Volume da placa (m³) -massa_placa = rho * volume_placa # Massa da placa (kg) +# Dimensões da placa e do motor +L = 0.60 +W = 0.22 +espessura_caixa = 0.003 +area_caixa = 5 * (L * W) +volume_caixa = 6 * L * W * espessura_caixa +massa_caixa = rho * volume_caixa # Simulação para a placa -placa = Arrefecimento(velocidade, area_placa, L, calor_gerado, tipo_geometria='placa') -placa.plotar_grafico(temp_objeto, calor_especifico, massa_placa, tempo_simulacao,"bateria") - -# Dimensões do motor -comprimento_motor = 0.355 # Comprimento do motor (m) -diametro_motor = 0.20 # Diâmetro do motor (m) -raio_externo_motor = diametro_motor / 2 # Raio externo do motor (m) -espessura_motor = 0.003 # Considerando apenas os 3 mm mais externos +bateria = Arrefecimento(velocidade, area_caixa, L, calor_gerado, 'placa') +bateria.plotar_graficos_aletas(temp_objeto, calor_especifico, massa_caixa, tempo_simulacao, "bateria") -# Área e volume da camada externa do motor -area_motor = math.pi * diametro_motor * comprimento_motor # Área externa do cilindro (m²) +comprimento_motor = 0.355 +diametro_motor = 0.20 +raio_externo_motor = diametro_motor / 2 +espessura_motor = 0.003 +area_motor = math.pi * diametro_motor * comprimento_motor volume_motor = volume_camada_cilindro(raio_externo_motor, espessura_motor, comprimento_motor) -massa_motor = volume_motor * rho # Massa do motor considerando 3 mm externos +massa_motor = volume_motor * rho # Simulação para o motor -motor = Arrefecimento(velocidade, area_motor, diametro_motor, calor_gerado, tipo_geometria='cilindro') -motor.plotar_grafico(temp_objeto, calor_especifico, massa_motor, tempo_simulacao,"motor") \ No newline at end of file +motor = Arrefecimento(velocidade, area_motor, comprimento_motor, calor_gerado, 'cilindro') +motor.plotar_graficos_aletas(temp_objeto, calor_especifico, massa_motor, tempo_simulacao, "motor") + From 18a9dbd5a5cfe0bbe916ae888dcc37f6118a787a Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Wed, 27 Nov 2024 18:08:10 -0300 Subject: [PATCH 17/25] =?UTF-8?q?Update=20Chassi=20Com=20Implementa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Planilhas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adição das novas defs e plots desenvolvidas por Vergílio no código. --- ...plementa\303\247\303\243o de Planilhas.py" | 282 +++++++++++++++--- 1 file changed, 236 insertions(+), 46 deletions(-) diff --git "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" index 11b5afa..50a49c5 100644 --- "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" +++ "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" @@ -122,6 +122,23 @@ def matrizes_global(self): pd.DataFrame(self.K_global).to_csv('Matriz_Global_Rigidez.csv', index=True, header=True) pd.DataFrame(self.M_global).to_csv('Matriz_Global_Massa.csv', index=True, header=True) + plt.figure(figsize=(6, 6)) + plt.spy(self.K_global, markersize=10) # Adjust markersize for visibility + plt.title("Spy Plot of the Kg") + plt.xlabel("Columns") + plt.ylabel("Rows") + plt.grid(True, which="both", linestyle="--", linewidth=0.5) + plt.show() + + plt.figure(figsize=(6, 6)) + plt.spy(self.M_global, markersize=10) # Adjust markersize for visibility + plt.title("Spy Plot of the Mg") + plt.xlabel("Columns") + plt.ylabel("Rows") + plt.grid(True, which="both", linestyle="--", linewidth=0.5) + plt.show() + + return self.K_global,self.M_global def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): @@ -182,6 +199,113 @@ def modal_analysis(self): return eigenvalues, eigenvectors, frequencies + def static_analysis(self,K_global, F_global, fixed_dofs): + """ + Perform static analysis by solving Ku = F with boundary conditions. + + Parameters: + K_global (ndarray): Global stiffness matrix (N x N). + F_global (ndarray): Global force vector (N). + fixed_dofs (list): List of DOF indices to be fixed. + + Returns: + displacements (ndarray): Displacement vector (N). + """ + # Total number of DOFs + n_dofs = K_global.shape[0] + + # Create a mask for free DOFs (DOFs not constrained) + free_dofs = np.array([i for i in range(n_dofs) if i not in fixed_dofs]) + + # Reduce the stiffness matrix and force vector + K_reduced = K_global[np.ix_(free_dofs, free_dofs)] + F_reduced = F_global[free_dofs] + + # Solve for displacements at free DOFs + u_reduced = np.linalg.solve(K_reduced, F_reduced) + + # Construct full displacement vector + displacements = np.zeros(n_dofs) + displacements[free_dofs] = u_reduced + + return displacements + + def compute_strain(self, B_matrices,F_global, fixed_dofs): + """ + Compute strains for all elements. + + Parameters: + displacements (ndarray): Displacement vector for all nodes. + B_matrices (list of ndarray): Strain-displacement matrices for each element. + + Returns: + strains (list of ndarray): Strain tensors for all elements. + """ + #### AQUI A MATRIZ B PRECISA SER CALCULADA DIRETO DAS FUNÇÕES DE FORMA + #### NÃO É TRIVIAL E VAI LEVAR TEMPO, MAS O RESTO DAS CONTAS ESTÃO OK + displacements = self.static_analysis(F_global, fixed_dofs) + strains = [] + for B in B_matrices: + strain = np.dot(B, displacements) # B-matrix times displacement vector + strains.append(strain) + return strains + + def compute_stress(self,B_matrices,F_global, fixed_dofs, E, nu): + """ + Compute stresses for all elements using Hooke's law. + + Parameters: + strains (list of ndarray): Strain tensors for all elements. + E (float): Young's modulus. + nu (float): Poisson's ratio. + + Returns: + stresses (list of ndarray): Stress tensors for all elements. + """ + strains = self.compute_strain(B_matrices,F_global, fixed_dofs) + # Construct constitutive matrix (isotropic 3D elasticity) + lambda_ = (E * nu) / ((1 + nu) * (1 - 2 * nu)) + G = E / (2 * (1 + nu)) + C = np.array([ + [lambda_ + 2*G , lambda_ , lambda_ , 0, 0, 0], + [lambda_ , lambda_ + 2*G , lambda_ , 0, 0, 0], + [lambda_ , lambda_ , lambda_ + 2*G , 0, 0, 0], + [ 0, 0, 0, G, 0, 0], + [ 0, 0, 0, 0, G, 0], + [ 0, 0, 0, 0, 0, G] + ]) + + stresses = [] + for strain in strains: + stress = np.dot(C, strain) # Hooke's law: C times strain + stresses.append(stress) + return stresses + + def compute_von_mises(self,B_matrices,F_global, fixed_dofs, E, nu): + """ + Compute von Mises stress for all elements. + + Parameters: + stresses (list of ndarray): Stress tensors for all elements. + + Returns: + von_mises_stresses (list of float): Von Mises stress for each element. + """ + stresses = self.compute_stress(B_matrices,F_global, fixed_dofs, E, nu) + von_mises_stresses = [] + for stress in stresses: + sigma_xx, sigma_yy, sigma_zz, tau_xy, tau_yz, tau_zx = stress + von_mises = np.sqrt( + 0.5 * ( + (sigma_xx - sigma_yy)**2 + + (sigma_yy - sigma_zz)**2 + + (sigma_zz - sigma_xx)**2 + + 6 * (tau_xy**2 + tau_yz**2 + tau_zx**2) + ) + ) + von_mises_stresses.append(von_mises) + return von_mises_stresses + def Mesh(self): filename = input("Insira o nome do arquivo: ") + ".geo" @@ -233,58 +357,106 @@ def structure_plot(self): ax.set_zlabel('Z') ax.set_title('Estrutura 3D do Chassi') - plt.show() - - def modal_analysis_plot(self): - autovalores, autovetores, _ = self.modal_analysis() - #Criando plot para os modos de vibração separadamente - for mode_idx in range(len(autovalores)): - fig = plt.figure(figsize=(8, 6)) - ax = fig.add_subplot(111, projection='3d') - ax.set_title(f'Modo {mode_idx + 1} Viga') + plt.show() + + def plot_colored_wireframe(self,scalar_values, colormap='jet'): + """ + Plots a 3D wireframe of the structure with color mapping based on scalar values. + + Parameters: + nodes (array): Array of node coordinates (N x 3). + elements (list): List of tuples defining connections between nodes. + scalar_values (array): 1D array of scalar values (e.g., strain) at each node. + colormap (str): Colormap name for visualization. + """ + # Normalize scalar values to [0, 1] for colormap + norm = plt.Normalize(vmin=np.min(scalar_values), vmax=np.max(scalar_values)) + cmap = plt.get_cmap(colormap) + + # Create the plot + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Adicionando os pontos (nós) + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + # Adicionando as linhas de ligação entre os nós + for node1, node2 in self.connections: + x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] + y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] + z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] + ax.plot(x_coords, y_coords, z_coords, 'r-', marker='o') + # Get the scalar value for the midpoint of the element + scalar_midpoint = (scalar_values[node1-1] + scalar_values[node2-1]) / 2 + + # Map scalar value to color + color = cmap(norm(scalar_midpoint)) + + # Plot the line segment with the corresponding color + ax.plot(x_coords, y_coords, z_coords, color=color, linewidth=2) - #Colocando a legenda dos nós no gráfico - for i, (x, y, z) in enumerate(self.coordinates): - ax.scatter(x, y, z, color='b', s=100) - ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + # Add a colorbar + mappable = plt.cm.ScalarMappable(cmap=cmap, norm=norm) + mappable.set_array(scalar_values) + cbar = plt.colorbar(mappable, ax=ax, orientation='vertical', shrink=0.8, pad=0.1) + cbar.set_label("Strain (or other variable)", fontsize=12) - #Colocando os nós no gráfico - for node1, node2 in self.connections: - x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] - y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] - z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] - ax.plot(x_coords, y_coords, z_coords, 'b--') + # Set axis labels and title + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.set_title('Wireframe with Scalar Color Mapping') + plt.tight_layout() + plt.show() + + def modal_analysis_plot(self): + autovalores, autovetores, _ = self.modal_analysis() - #Inicializando os modos de vibração e vetor de deslocamentos + for mode_idx in range(len(autovalores)): mode_shape = autovetores[:, mode_idx] - displacements = np.zeros((len(self.coordinates), 3)) + displacements = np.zeros((len(self.coordinates), 3)) # Assuming we want to visualize x, y, z displacements only - #Aplicando os deslocamentos na estrutura + # Loop through nodes to extract the translations for j, (x, y, z) in enumerate(self.coordinates): - if j > 0 and j < len(self.coordinates) - 1: - displacements[j, 0] = mode_shape[2 * j] - displacements[j, 1] = mode_shape[2 * j + 1] - displacements[j, 2] = 0 - - deformed_nodes = np.array(self.coordinates) + displacements + # 6 DOFs per node: [u_x, u_y, u_z, theta_x, theta_y, theta_z] + dof_start = 6 * j # Start index of DOFs for node j + displacements[j, 0] = mode_shape[dof_start] # u_x + displacements[j, 1] = mode_shape[dof_start + 1] # u_y + displacements[j, 2] = mode_shape[dof_start + 2] # u_z + + # Scale displacements for plots + scale_factor = 1 # Adjust as needed + deformed_nodes = np.array(self.coordinates) + displacements * scale_factor + + # Plot deformed + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') - #Plotando a estrutura deformada - for node1, node2 in self.connections: + # Plot deformed structure + for i, (node1, node2) in enumerate(self.connections): x = [deformed_nodes[node1-1][0], deformed_nodes[node2-1][0]] y = [deformed_nodes[node1-1][1], deformed_nodes[node2-1][1]] z = [deformed_nodes[node1-1][2], deformed_nodes[node2-1][2]] - ax.plot(x, y, z, 'r-') + ax.plot(x, y, z, 'r-', label="Deformed" if i == 0 else "") # Add label only once + + # Plot original structure + for i, (node1, node2) in enumerate(self.connections): + x = [self.coordinates[node1-1][0], self.coordinates[node2-1][0]] + y = [self.coordinates[node1-1][1], self.coordinates[node2-1][1]] + z = [self.coordinates[node1-1][2], self.coordinates[node2-1][2]] + ax.plot(x, y, z, 'k--', label="Original" if i == 0 else "") # Add label only once - #Configurações adicionais + # Add labels and title ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') - plt.xlim([-0.5,2]) - plt.ylim([-0.5,1]) - ax.set_zlim({0,1}) + ax.set_title(f'Forma modal nº: {mode_idx + 1}') + ax.legend() # Ensure the legend is displayed + ax.set_zlim([-0.5,2]) plt.tight_layout() plt.show() - def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): torcao,deformacao_axial,flexao1,flexao2,flexao3,KF_total,KT_total,KF_elements,KT_elements= self.shape_fun(F_flexao1, F_flexao2, F_axial, F_torcao) @@ -376,20 +548,14 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): estrutura.node_loc_matrix() estrutura.connect_matrix() -K_global, M_global = estrutura.matrizes_global() - -#print("\\n Matriz de Rigidez Global") -#print(K_global) +# Plotando o gráfico 3D da estrutura +estrutura.structure_plot() -#print("\\n Matriz de Massa Global") -#print(M_global) +K_global, M_global = estrutura.matrizes_global() #Plotando os resultados das deformações estrutura.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) -# Plotando o gráfico 3D da estrutura -estrutura.structure_plot() - #Gerar autovalores, autovetores e frequências naturais autovalores, autovetores, frequencias = estrutura.modal_analysis() @@ -398,4 +564,28 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): print(frequencias) #Plotagem dos modos de vibração para a estrutura de vigas -estrutura.modal_analysis_plot() \ No newline at end of file +estrutura.modal_analysis_plot() + +F_global = np.zeros(K_global.size) # Force vector +F_global[2+5*6] = 100 +F_global[2+5*9] = -50 +fixed_dofs = [0, 1, 2, 3, 4, 5] + +# Perform analysis +displacements = estrutura.static_analysis(K_global,F_global, fixed_dofs) +print("Displacement Vector:", displacements) + +estrutura.plot_colored_wireframe(displacements) +print(nodes.size) +print(displacements.size) +print(F_torcao.size) +print(F_flexao1.size) + +""" +Estrutura.plot_colored_wireframe(nodes, elements, torcao/(np.max(np.max(torcao)))) +Estrutura.plot_colored_wireframe(nodes, elements, deformacao_axial) +Estrutura.plot_colored_wireframe(nodes, elements, flexao1) +Estrutura.plot_colored_wireframe(nodes, elements, flexao2) +Estrutura.plot_colored_wireframe(nodes, elements, flexao3) + +""" \ No newline at end of file From 1d468848f6d7963f681166315a721c20fd4ad1d0 Mon Sep 17 00:00:00 2001 From: EduardoAMXango <167939544+EduardoAMXango@users.noreply.github.com> Date: Thu, 5 Dec 2024 19:01:43 -0300 Subject: [PATCH 18/25] =?UTF-8?q?Update=20Chassi=20Com=20Implementa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Planilhas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adição das descrições de cada def (necessário melhorar) --- ...plementa\303\247\303\243o de Planilhas.py" | 172 +++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" index 50a49c5..e021064 100644 --- "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" +++ "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" @@ -34,7 +34,20 @@ def node_loc_matrix(self): def connect_matrix(self): print(self.elements) - def calcular_comprimento(self, index): #Função auxiliar para cálculo de comprimento dos elementos + def calcular_comprimento(self, index): + """ + Calculate the length of a finite element. + Args: + index (int): The index of the element whose length is to be calculated. + Returns: + float: The length of the element. + Process: + 1. Extracts the indices of the nodes (`Node a` and `Node b`) that define the element. + - Node indices in the `elements` dictionary are converted to zero-based indexing. + 2. Retrieves the 3D coordinates (`x`, `y`, `z`) of the nodes from the `nodes` dictionary. + 3. Computes the Euclidean distance between the two nodes: + \( L = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2} \) + """ node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] @@ -42,6 +55,22 @@ def calcular_comprimento(self, index): #Função return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) def element(self, index): + """ + Calculate the elemental stiffness and mass matrices for a given finite element. + + Args: + index (int): Index of the element for which the stiffness and mass matrices are to be computed. + + Returns: + tuple: A tuple containing: + - `k_e` (numpy.ndarray): The 12x12 stiffness matrix for the element, considering either + Euler-Bernoulli or Timoshenko beam theory. + - `m_e` (numpy.ndarray): The 12x12 mass matrix for the element. + Notes: + - Euler-Bernoulli and Timoshenko beam theories differ in how they handle shear deformation: + - Replace `c4` with `c3` in the stiffness matrix to use Euler-Bernoulli theory. + - Mass matrix assumes consistent mass distribution along the element. + """ #Tomando as propriedades de cada elemento E = self.element_properties['E'][index] A = self.element_properties['A'][index] @@ -99,12 +128,52 @@ def element(self, index): return k_e,m_e def aplicar_engastes(self, nodes, dofs): + """ + Apply constraints (fixed supports) to specific degrees of freedom (DOFs) in the global stiffness matrix. + + This method modifies the global stiffness matrix (`K_global`) to simulate fixed supports (encastrés) + by applying large stiffness values to the specified DOFs of the given nodes. This effectively + constrains the structure's motion in those directions. + + Args: + nodes (list of int): Indices of the nodes where constraints are to be applied. + Each node index corresponds to its position in the global node list. + dofs (list of int): Degrees of freedom to be constrained for each node. + """ for node in nodes: #Laço para selecionar cada nó que será engastado for dof in dofs: #Laço para selecionar quais graus de liberdade serão fixados index = node * self.num_dofs_per_node + dof #Identificação da entrada da matriz que precisa ser restringida pelo engaste self.K_global[index, index] = 10**10 # Um valor suficientemente grande para simular um engaste def matrizes_global(self): + """ + Compute and assemble the global stiffness and mass matrices for the entire structure. + + This function calculates the stiffness (`K_global`) and mass (`M_global`) matrices of each element + and assembles them into the global matrices by appropriately mapping the degrees of freedom (DOFs). + + Steps: + 1. Iterates over all elements in the structure. + 2. For each element, retrieves the stiffness and mass matrices using the `element` method. + 3. Maps the local element matrices to the corresponding global DOFs. + 4. Updates the global matrices with contributions from each element. + 5. Saves the assembled matrices as CSV files for further use. + 6. Visualizes the sparsity patterns of the global matrices using `spy` plots. + + Returns: + tuple: `(K_global, M_global)` + - `K_global` (numpy.ndarray): Global stiffness matrix of the structure. + - `M_global` (numpy.ndarray): Global mass matrix of the structure. + + Files exported: + - `Matriz_Global_Rigidez.csv`: Global stiffness matrix. + - `Matriz_Global_Massa.csv`: Global mass matrix. + + Visualization: + - Two spy plots are generated: + 1. Sparsity pattern of the global stiffness matrix. + 2. Sparsity pattern of the global mass matrix. + """ #Calculando as matrizes de rigidez e massa de cada elemento for index in range(self.num_elements): @@ -142,6 +211,27 @@ def matrizes_global(self): return self.K_global,self.M_global def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): + """ + Calculates deformation and stiffness values for axial, torsional, and flexural forces for all elements in the structure. + + Parameters: + F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). + F_flexao2 (float): Force applied for distributed flexural deformation. + F_axial (float): Axial force applied to the elements. + F_torcao (float): Torsional force applied to the elements. + + Returns: + tuple: + - torcao (array): Torsional deformation for each element. + - deformacao (array): Axial deformation for each element. + - flexao1 (array): Flexural deformation due to point load for each element. + - flexao2 (array): Flexural deformation due to distributed load for each element. + - flexao3 (array): Combined flexural deformation for each element. + - KF_total (float): Total flexural stiffness of the structure. + - KT_total (float): Total torsional stiffness of the structure. + - KF_elements (list): Flexural stiffness for each element. + - KT_elements (list): Torsional stiffness for each element. + """ KF_total = 0 KT_total = 0 KF_elements = [] @@ -183,6 +273,24 @@ def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): np.array(flexao2), np.array(flexao3), KF_total, KT_total, KF_elements, KT_elements) def modal_analysis(self): + """ + Performs modal analysis of the structure by solving the eigenvalue problem. + + This function computes the natural frequencies and mode shapes of the structure + using the global stiffness (`K_global`) and mass (`M_global`) matrices. + + Returns: + tuple: + - eigenvalues (array): Array of the selected eigenvalues corresponding to the natural frequencies. + - eigenvectors (array): Array of the selected eigenvectors representing mode shapes. + - frequencies (array): Array of the selected natural frequencies in Hertz. + + Parameters: + None (uses class attributes): + - K_global (array): Global stiffness matrix of the structure. + - M_global (array): Global mass matrix of the structure. + - num_modes (int): Number of modes to retain in the analysis. + """ # Análise modal por resolução do problema de autovalor e autovetor unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) @@ -335,6 +443,14 @@ def Mesh(self): print(f'O arquivo foi salvo em: {filepath}, basta abrir o GMSH, e abrir o arquivo') def structure_plot(self): + """ + Plots a 3D wireframe of the structure. + + Parameters: + coordinates (array): Array of node coordinates (N x 3). + connections (list): List of tuples defining connections between nodes. + """ + # Plotando o gráfico 3D da estrutura fig = plt.figure(figsize=(10, 8)) ax = fig.add_subplot(111, projection='3d') @@ -412,6 +528,21 @@ def plot_colored_wireframe(self,scalar_values, colormap='jet'): plt.show() def modal_analysis_plot(self): + """ + Plots the modal shapes of the structure in 3D, showing the deformed and original configurations. + + This function performs the following: + - Computes eigenvalues and eigenvectors using the modal analysis method. + - Extracts the translational displacements (x, y, z) for each node from the modal shapes. + - Scales and visualizes the deformed structure for each mode, overlaying it on the original structure. + - Annotates the nodes of both the original and deformed structures. + + Parameters: + None (uses class attributes): + - coordinates (array): Array of node coordinates (N x 3). + - connections (list): List of tuples defining element connections between nodes. + - modal_analysis (method): Computes eigenvalues and eigenvectors. + """ autovalores, autovetores, _ = self.modal_analysis() for mode_idx in range(len(autovalores)): @@ -441,6 +572,16 @@ def modal_analysis_plot(self): z = [deformed_nodes[node1-1][2], deformed_nodes[node2-1][2]] ax.plot(x, y, z, 'r-', label="Deformed" if i == 0 else "") # Add label only once + #Colocando a legenda dos nós no gráfico + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + #Colocando a legenda dos nós após a deformação no gráfico + for i, (x, y, z) in enumerate(deformed_nodes): + ax.scatter(x, y, z, color='r', s=25) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + # Plot original structure for i, (node1, node2) in enumerate(self.connections): x = [self.coordinates[node1-1][0], self.coordinates[node2-1][0]] @@ -459,7 +600,32 @@ def modal_analysis_plot(self): plt.show() def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): + """ + Generates plots of deformations and stiffness values for each element based on the given forces. + + This function calls `shape_fun` to calculate torsional, axial, and flexural deformations, as well as stiffness values. + It then plots these results across subplots to visualize the behavior of each element under the applied forces. + + Parameters: + F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). + F_flexao2 (float): Force applied for distributed flexural deformation. + F_axial (float): Axial force applied to the elements. + F_torcao (float): Torsional force applied to the elements. + + Plots: + 1. Torsional deformation for each element. + 2. Axial deformation for each element. + 3. Flexural deformation due to point load for each element. + 4. Flexural deformation due to distributed load for each element. + 5. Combined flexural deformation for each element. + 6. Flexural and torsional stiffness for each element. + + Notes: + - Total flexural stiffness (KF_total) and torsional stiffness (KT_total) are displayed in the overall plot title. + - The function uses subplots to organize the visuals, and the layout is adjusted for clarity. + """ torcao,deformacao_axial,flexao1,flexao2,flexao3,KF_total,KT_total,KF_elements,KT_elements= self.shape_fun(F_flexao1, F_flexao2, F_axial, F_torcao) + # Configuração dos subplots fig, axs = plt.subplots(6, 1, figsize=(12, 22)) @@ -512,6 +678,8 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): # Ajustes de layout plt.tight_layout(rect=[0, 0, 1, 0.96]) plt.show() + print(f'KF Total: {KF_total:.2e} N/m \nKT Total: {KT_total:.2e} N/m') + nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" @@ -588,4 +756,4 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): Estrutura.plot_colored_wireframe(nodes, elements, flexao2) Estrutura.plot_colored_wireframe(nodes, elements, flexao3) -""" \ No newline at end of file +""" From f1f9131f1b0142b147dc46fa17d4d478904343f7 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Fri, 6 Dec 2024 13:42:46 -0300 Subject: [PATCH 19/25] =?UTF-8?q?Update=20Chassi=20Com=20Implementa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Planilhas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adição do calculo da massa do carro a partir da geometria . --- ...plementa\303\247\303\243o de Planilhas.py" | 327 ++++++++++++++---- 1 file changed, 257 insertions(+), 70 deletions(-) diff --git "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" index 50a49c5..dfa818c 100644 --- "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" +++ "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" @@ -11,22 +11,22 @@ np.set_printoptions(linewidth=200, suppress=True) class Estrutura: - def __init__(self, elements,element_properties, nodes, m, Id, Ip): - self.elements = elements #Matriz de elementos conectados - self.num_elements = len(elements) #Número de elementos - self.element_properties = element_properties #Matriz de propriedades dos elementos - self.nodes = nodes #Matriz de nós com suas posições - self.coordinates = np.array(nodes[['x', 'y', 'z']]) - self.connections = np.array(elements[['Node a', 'Node b']]) - self.num_nodes = len(nodes) #Número total de nós - self.massa = m #Massa do carro (Kg) - self.momento_inercia_direcao = Id #Momento de inércia em relação à direção (kg.m^2) - self.momento_inercia_plano = Ip #Momento de inércia em relação ao plano (kg.m^2) - self.num_dofs_per_node = 6 #Graus de liberdade por nó - self.num_dofs = self.num_nodes * self.num_dofs_per_node #Total de Graus de liberdade (gdls) - self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) #Tamanho adequado para a matriz global - self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) - self.num_modes = 12 #Número de modos de vibração a serem retornados + def __init__(self, elements,element_properties, nodes, Id, Ip): + self.elements = elements # Matrix of connected elements + self.num_elements = len(elements) # Number of elements + self.element_properties = element_properties # Matrix of element properties + self.nodes = nodes # Matrix of nodes with their positions + self.coordinates = np.array(nodes[['x', 'y', 'z']]) # Coordinates of the nodes + self.connections = np.array(elements[['Node a', 'Node b']]) # Connections between nodes (elements) + self.num_nodes = len(nodes) # Total number of nodes + self.car_mass = 0 # Mass of the vehicle (kg) + self.moment_of_inertia_direction = Id # Moment of inertia in a specific direction (kg.m^2) + self.moment_of_inertia_plane = Ip # Moment of inertia in the plane (kg.m^2) + self.num_dofs_per_node = 6 # Degrees of freedom per node + self.num_dofs = self.num_nodes * self.num_dofs_per_node # Total number of degrees of freedom + self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global stiffness matrix + self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global mass matrix + self.num_modes = 12 # Number of vibration modes to return def node_loc_matrix(self): print(self.nodes) @@ -34,14 +34,55 @@ def node_loc_matrix(self): def connect_matrix(self): print(self.elements) - def calcular_comprimento(self, index): #Função auxiliar para cálculo de comprimento dos elementos + def lenght_calculator(self, index): + """ + Calculate the length of a finite element. + Args: + index (int): The index of the element whose length is to be calculated. + Returns: + float: The length of the element. + Process: + 1. Extracts the indices of the nodes (`Node a` and `Node b`) that define the element. + - Node indices in the `elements` dictionary are converted to zero-based indexing. + 2. Retrieves the 3D coordinates (`x`, `y`, `z`) of the nodes from the `nodes` dictionary. + 3. Computes the Euclidean distance between the two nodes: + \( L = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2} \) + """ node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] x2, y2, z2 = self.nodes['x'][node2], self.nodes['y'][node2], self.nodes['z'][node2] return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) + def mass(self): + for index in range(self.num_elements): + L_e = self.lenght_calculator(index) + X = self.element_properties['X'][index] + A = self.element_properties['A'][index] + rho = 7870 # kg/m^3 + raio_externo = np.sqrt(A/np.pi) + raio_interno = raio_externo - X + volume = np.pi*L_e* (raio_externo**2 - raio_interno**2) + self.car_mass+= volume*rho + print(f"{self.car_mass.round(2) } kg") + def element(self, index): + """ + Calculate the elemental stiffness and mass matrices for a given finite element. + + Args: + index (int): Index of the element for which the stiffness and mass matrices are to be computed. + + Returns: + tuple: A tuple containing: + - `k_e` (numpy.ndarray): The 12x12 stiffness matrix for the element, considering either + Euler-Bernoulli or Timoshenko beam theory. + - `m_e` (numpy.ndarray): The 12x12 mass matrix for the element. + Notes: + - Euler-Bernoulli and Timoshenko beam theories differ in how they handle shear deformation: + - Replace `c4` with `c3` in the stiffness matrix to use Euler-Bernoulli theory. + - Mass matrix assumes consistent mass distribution along the element. + """ #Tomando as propriedades de cada elemento E = self.element_properties['E'][index] A = self.element_properties['A'][index] @@ -51,7 +92,7 @@ def element(self, index): kappa=0.9 #Fator de correção para cisalhamento - L_e = self.calcular_comprimento(index) + L_e = self.lenght_calculator(index) Phi = (12 * E * I) / (kappa * G * A * L_e**2) rho = 7850 # kg/m^3 c1 = E * A / L_e @@ -99,12 +140,52 @@ def element(self, index): return k_e,m_e def aplicar_engastes(self, nodes, dofs): + """ + Apply constraints (fixed supports) to specific degrees of freedom (DOFs) in the global stiffness matrix. + + This method modifies the global stiffness matrix (`K_global`) to simulate fixed supports (encastrés) + by applying large stiffness values to the specified DOFs of the given nodes. This effectively + constrains the structure's motion in those directions. + + Args: + nodes (list of int): Indices of the nodes where constraints are to be applied. + Each node index corresponds to its position in the global node list. + dofs (list of int): Degrees of freedom to be constrained for each node. + """ for node in nodes: #Laço para selecionar cada nó que será engastado for dof in dofs: #Laço para selecionar quais graus de liberdade serão fixados index = node * self.num_dofs_per_node + dof #Identificação da entrada da matriz que precisa ser restringida pelo engaste self.K_global[index, index] = 10**10 # Um valor suficientemente grande para simular um engaste def matrizes_global(self): + """ + Compute and assemble the global stiffness and mass matrices for the entire structure. + + This function calculates the stiffness (`K_global`) and mass (`M_global`) matrices of each element + and assembles them into the global matrices by appropriately mapping the degrees of freedom (DOFs). + + Steps: + 1. Iterates over all elements in the structure. + 2. For each element, retrieves the stiffness and mass matrices using the `element` method. + 3. Maps the local element matrices to the corresponding global DOFs. + 4. Updates the global matrices with contributions from each element. + 5. Saves the assembled matrices as CSV files for further use. + 6. Visualizes the sparsity patterns of the global matrices using `spy` plots. + + Returns: + tuple: `(K_global, M_global)` + - `K_global` (numpy.ndarray): Global stiffness matrix of the structure. + - `M_global` (numpy.ndarray): Global mass matrix of the structure. + + Files exported: + - `Matriz_Global_Rigidez.csv`: Global stiffness matrix. + - `Matriz_Global_Massa.csv`: Global mass matrix. + + Visualization: + - Two spy plots are generated: + 1. Sparsity pattern of the global stiffness matrix. + 2. Sparsity pattern of the global mass matrix. + """ #Calculando as matrizes de rigidez e massa de cada elemento for index in range(self.num_elements): @@ -142,6 +223,27 @@ def matrizes_global(self): return self.K_global,self.M_global def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): + """ + Calculates deformation and stiffness values for axial, torsional, and flexural forces for all elements in the structure. + + Parameters: + F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). + F_flexao2 (float): Force applied for distributed flexural deformation. + F_axial (float): Axial force applied to the elements. + F_torcao (float): Torsional force applied to the elements. + + Returns: + tuple: + - torcao (array): Torsional deformation for each element. + - deformacao (array): Axial deformation for each element. + - flexao1 (array): Flexural deformation due to point load for each element. + - flexao2 (array): Flexural deformation due to distributed load for each element. + - flexao3 (array): Combined flexural deformation for each element. + - KF_total (float): Total flexural stiffness of the structure. + - KT_total (float): Total torsional stiffness of the structure. + - KF_elements (list): Flexural stiffness for each element. + - KT_elements (list): Torsional stiffness for each element. + """ KF_total = 0 KT_total = 0 KF_elements = [] @@ -153,7 +255,7 @@ def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): I = self.element_properties['I'][index] J = self.element_properties['J'][index] G = self.element_properties['G'][index] - L_e = self.calcular_comprimento(index) + L_e = self.lenght_calculator(index) # Equação de torsão torcao_val = (F_torcao * L_e) / (G * J) #Fonte[1] torcao.append(torcao_val) @@ -183,6 +285,24 @@ def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): np.array(flexao2), np.array(flexao3), KF_total, KT_total, KF_elements, KT_elements) def modal_analysis(self): + """ + Performs modal analysis of the structure by solving the eigenvalue problem. + + This function computes the natural frequencies and mode shapes of the structure + using the global stiffness (`K_global`) and mass (`M_global`) matrices. + + Returns: + tuple: + - eigenvalues (array): Array of the selected eigenvalues corresponding to the natural frequencies. + - eigenvectors (array): Array of the selected eigenvectors representing mode shapes. + - frequencies (array): Array of the selected natural frequencies in Hertz. + + Parameters: + None (uses class attributes): + - K_global (array): Global stiffness matrix of the structure. + - M_global (array): Global mass matrix of the structure. + - num_modes (int): Number of modes to retain in the analysis. + """ # Análise modal por resolução do problema de autovalor e autovetor unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) @@ -335,6 +455,14 @@ def Mesh(self): print(f'O arquivo foi salvo em: {filepath}, basta abrir o GMSH, e abrir o arquivo') def structure_plot(self): + """ + Plots a 3D wireframe of the structure. + + Parameters: + coordinates (array): Array of node coordinates (N x 3). + connections (list): List of tuples defining connections between nodes. + """ + # Plotando o gráfico 3D da estrutura fig = plt.figure(figsize=(10, 8)) ax = fig.add_subplot(111, projection='3d') @@ -355,6 +483,7 @@ def structure_plot(self): ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') + ax.set_ylim([-0.5,1.5]) ax.set_title('Estrutura 3D do Chassi') plt.show() @@ -408,10 +537,26 @@ def plot_colored_wireframe(self,scalar_values, colormap='jet'): ax.set_ylabel('Y') ax.set_zlabel('Z') ax.set_title('Wireframe with Scalar Color Mapping') + ax.set_ylim([-0.5,1.5]) plt.tight_layout() plt.show() def modal_analysis_plot(self): + """ + Plots the modal shapes of the structure in 3D, showing the deformed and original configurations. + + This function performs the following: + - Computes eigenvalues and eigenvectors using the modal analysis method. + - Extracts the translational displacements (x, y, z) for each node from the modal shapes. + - Scales and visualizes the deformed structure for each mode, overlaying it on the original structure. + - Annotates the nodes of both the original and deformed structures. + + Parameters: + None (uses class attributes): + - coordinates (array): Array of node coordinates (N x 3). + - connections (list): List of tuples defining element connections between nodes. + - modal_analysis (method): Computes eigenvalues and eigenvectors. + """ autovalores, autovetores, _ = self.modal_analysis() for mode_idx in range(len(autovalores)): @@ -441,6 +586,16 @@ def modal_analysis_plot(self): z = [deformed_nodes[node1-1][2], deformed_nodes[node2-1][2]] ax.plot(x, y, z, 'r-', label="Deformed" if i == 0 else "") # Add label only once + #Colocando a legenda dos nós no gráfico + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + #Colocando a legenda dos nós após a deformação no gráfico + #for i, (x, y, z) in enumerate(deformed_nodes): + # ax.scatter(x, y, z, color='r', s=25) + # ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + # Plot original structure for i, (node1, node2) in enumerate(self.connections): x = [self.coordinates[node1-1][0], self.coordinates[node2-1][0]] @@ -455,11 +610,37 @@ def modal_analysis_plot(self): ax.set_title(f'Forma modal nº: {mode_idx + 1}') ax.legend() # Ensure the legend is displayed ax.set_zlim([-0.5,2]) + ax.set_ylim([-0.5,1.5]) plt.tight_layout() plt.show() def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): + """ + Generates plots of deformations and stiffness values for each element based on the given forces. + + This function calls `shape_fun` to calculate torsional, axial, and flexural deformations, as well as stiffness values. + It then plots these results across subplots to visualize the behavior of each element under the applied forces. + + Parameters: + F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). + F_flexao2 (float): Force applied for distributed flexural deformation. + F_axial (float): Axial force applied to the elements. + F_torcao (float): Torsional force applied to the elements. + + Plots: + 1. Torsional deformation for each element. + 2. Axial deformation for each element. + 3. Flexural deformation due to point load for each element. + 4. Flexural deformation due to distributed load for each element. + 5. Combined flexural deformation for each element. + 6. Flexural and torsional stiffness for each element. + + Notes: + - Total flexural stiffness (KF_total) and torsional stiffness (KT_total) are displayed in the overall plot title. + - The function uses subplots to organize the visuals, and the layout is adjusted for clarity. + """ torcao,deformacao_axial,flexao1,flexao2,flexao3,KF_total,KT_total,KF_elements,KT_elements= self.shape_fun(F_flexao1, F_flexao2, F_axial, F_torcao) + # Configuração dos subplots fig, axs = plt.subplots(6, 1, figsize=(12, 22)) @@ -512,10 +693,56 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): # Ajustes de layout plt.tight_layout(rect=[0, 0, 1, 0.96]) plt.show() + print(f'KF Total: {KF_total:.2e} N/m \nKT Total: {KT_total:.2e} N/m') - -nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" -elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Elements.csv" + def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): + """ + Executes the complete structural analysis workflow, including mass matrix generation, + node and connectivity matrices, deformation visualization, modal analysis, and static analysis. + + Parameters: + F_flexao1 (array): Forces due to bending in one direction, applied to the structure. + F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. + F_axial (array): Axial forces applied to the structure. + F_torcao (array): Torsional forces applied to the structure. + """ + self.mass() + #Gerar as matrizes de localização dos nós e de conectividade + self.node_loc_matrix() + self.connect_matrix() + # Plotando o gráfico 3D da estrutura + self.structure_plot() + self.matrizes_global() + + #Plotando os resultados das deformações + self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) + + #Gerar autovalores, autovetores e frequências naturais + autovalores, autovetores, frequencias = self.modal_analysis() + self.modal_analysis_plot() + #Exibindo as frequências naturais e modos de vibração da estrutura + print("\\n Frequências Naturais (ω) da estrutura:") + print(frequencias) + F_global = np.zeros(self.K_global.size) # Force vector + F_global[2+5*6] = 100 + F_global[2+5*9] = -50 + fixed_dofs = [0, 1, 2, 3, 4, 5] + + # Perform analysis + displacements = estrutura.static_analysis(self.K_global,F_global, fixed_dofs) + print("Displacement Vector:", displacements) + + estrutura.plot_colored_wireframe(displacements) + print(self.num_nodes) + print(displacements.size) + print(F_torcao.size) + print(F_flexao1.size) + +#nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" +#elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Elements.csv" + +nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nos_final.csv" +elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Conectividade_final.csv" # Carregar o arquivo para inspecionar seu conteúdo nodes = pd.read_csv(nodes_file_path) @@ -524,64 +751,24 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): # Selecionando as colunas de conectividades e propriedades dos elementos elements = element_data[['Element ID','Node a', 'Node b']] -element_properties = element_data[['Element ID','A', 'I', 'J', 'E', 'G']] - -#Criar a estrutura e montar as matrizes de rigidez e massa globais -#Dados: n = len(nodes), -# m = 1500 kg, -# rho = 7850 kg/m^3 -# A = 0.225 m^2 -# E = 210e9 # Módulo de elasticidade em Pa -# I = 8.33e-6 # Momento de inércia em m^4 -# Ip = Id = 8.33e-6 kg.m^2 - -#Criar a estrutura e montar as matrizes de rigidez e massa globais, atribuir forças +element_properties = element_data[['Element ID','A', 'I', 'J', 'E', 'G','X']] + + + +#Atribuir forças F_flexao1 = np.array([1000, 2000, 3000, 4000, 5000]) F_flexao2 = np.array([1000, 1000, 1000, 1000, 1000]) F_axial = np.array([1000, 2000, 3000, 4000, 5000]) F_torcao = np.array([1000, 2000, 3000, 4000, 5000]) #Inicializando a Estrutura -estrutura = Estrutura(elements, element_properties, nodes, 1500, 8.33e-6, 8.33e-6) - -#Gerar as matrizes de localização dos nós e de conectividade -estrutura.node_loc_matrix() -estrutura.connect_matrix() - -# Plotando o gráfico 3D da estrutura -estrutura.structure_plot() - -K_global, M_global = estrutura.matrizes_global() +estrutura = Estrutura(elements, element_properties, nodes, 8.33e-6, 8.33e-6) +estrutura.run(F_flexao1, F_flexao2, F_axial, F_torcao) -#Plotando os resultados das deformações -estrutura.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) -#Gerar autovalores, autovetores e frequências naturais -autovalores, autovetores, frequencias = estrutura.modal_analysis() - -#Exibindo as frequências naturais e modos de vibração da estrutura -print("\\n Frequências Naturais (ω) da estrutura:") -print(frequencias) - -#Plotagem dos modos de vibração para a estrutura de vigas -estrutura.modal_analysis_plot() - -F_global = np.zeros(K_global.size) # Force vector -F_global[2+5*6] = 100 -F_global[2+5*9] = -50 -fixed_dofs = [0, 1, 2, 3, 4, 5] - -# Perform analysis -displacements = estrutura.static_analysis(K_global,F_global, fixed_dofs) -print("Displacement Vector:", displacements) - -estrutura.plot_colored_wireframe(displacements) -print(nodes.size) -print(displacements.size) -print(F_torcao.size) -print(F_flexao1.size) """ + Estrutura.plot_colored_wireframe(nodes, elements, torcao/(np.max(np.max(torcao)))) Estrutura.plot_colored_wireframe(nodes, elements, deformacao_axial) Estrutura.plot_colored_wireframe(nodes, elements, flexao1) From 7a9aed403e76c296166f80d94e363c7ee540e253 Mon Sep 17 00:00:00 2001 From: EduardoAMXango <167939544+EduardoAMXango@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:45:42 -0300 Subject: [PATCH 20/25] =?UTF-8?q?Update=20Chassi=20Com=20Implementa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Planilhas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adição do calculo da massa total do carro e adição de uma def de execução do código --- ...plementa\303\247\303\243o de Planilhas.py" | 167 ++++++++++-------- 1 file changed, 93 insertions(+), 74 deletions(-) diff --git "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" index e021064..0d7a98c 100644 --- "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" +++ "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" @@ -11,22 +11,22 @@ np.set_printoptions(linewidth=200, suppress=True) class Estrutura: - def __init__(self, elements,element_properties, nodes, m, Id, Ip): - self.elements = elements #Matriz de elementos conectados - self.num_elements = len(elements) #Número de elementos - self.element_properties = element_properties #Matriz de propriedades dos elementos - self.nodes = nodes #Matriz de nós com suas posições - self.coordinates = np.array(nodes[['x', 'y', 'z']]) - self.connections = np.array(elements[['Node a', 'Node b']]) - self.num_nodes = len(nodes) #Número total de nós - self.massa = m #Massa do carro (Kg) - self.momento_inercia_direcao = Id #Momento de inércia em relação à direção (kg.m^2) - self.momento_inercia_plano = Ip #Momento de inércia em relação ao plano (kg.m^2) - self.num_dofs_per_node = 6 #Graus de liberdade por nó - self.num_dofs = self.num_nodes * self.num_dofs_per_node #Total de Graus de liberdade (gdls) - self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) #Tamanho adequado para a matriz global - self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) - self.num_modes = 12 #Número de modos de vibração a serem retornados + def __init__(self, elements,element_properties, nodes, Id, Ip): + self.elements = elements # Matrix of connected elements + self.num_elements = len(elements) # Number of elements + self.element_properties = element_properties # Matrix of element properties + self.nodes = nodes # Matrix of nodes with their positions + self.coordinates = np.array(nodes[['x', 'y', 'z']]) # Coordinates of the nodes + self.connections = np.array(elements[['Node a', 'Node b']]) # Connections between nodes (elements) + self.num_nodes = len(nodes) # Total number of nodes + self.car_mass = 0 # Mass of the vehicle (kg) + self.moment_of_inertia_direction = Id # Moment of inertia in a specific direction (kg.m^2) + self.moment_of_inertia_plane = Ip # Moment of inertia in the plane (kg.m^2) + self.num_dofs_per_node = 6 # Degrees of freedom per node + self.num_dofs = self.num_nodes * self.num_dofs_per_node # Total number of degrees of freedom + self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global stiffness matrix + self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global mass matrix + self.num_modes = 12 # Number of vibration modes to return def node_loc_matrix(self): print(self.nodes) @@ -34,7 +34,7 @@ def node_loc_matrix(self): def connect_matrix(self): print(self.elements) - def calcular_comprimento(self, index): + def lenght_calculator(self, index): """ Calculate the length of a finite element. Args: @@ -54,6 +54,18 @@ def calcular_comprimento(self, index): x2, y2, z2 = self.nodes['x'][node2], self.nodes['y'][node2], self.nodes['z'][node2] return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) + def mass(self): + for index in range(self.num_elements): + L_e = self.lenght_calculator(index) + X = self.element_properties['X'][index] + A = self.element_properties['A'][index] + rho = 7870 # kg/m^3 + raio_externo = np.sqrt(A/np.pi) + raio_interno = raio_externo - X + volume = np.pi*L_e* (raio_externo**2 - raio_interno**2) + self.car_mass+= volume*rho + print(f"{self.car_mass.round(2) } kg") + def element(self, index): """ Calculate the elemental stiffness and mass matrices for a given finite element. @@ -80,7 +92,7 @@ def element(self, index): kappa=0.9 #Fator de correção para cisalhamento - L_e = self.calcular_comprimento(index) + L_e = self.lenght_calculator(index) Phi = (12 * E * I) / (kappa * G * A * L_e**2) rho = 7850 # kg/m^3 c1 = E * A / L_e @@ -243,7 +255,7 @@ def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): I = self.element_properties['I'][index] J = self.element_properties['J'][index] G = self.element_properties['G'][index] - L_e = self.calcular_comprimento(index) + L_e = self.lenght_calculator(index) # Equação de torsão torcao_val = (F_torcao * L_e) / (G * J) #Fonte[1] torcao.append(torcao_val) @@ -471,6 +483,7 @@ def structure_plot(self): ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') + ax.set_ylim([-0.5,1.5]) ax.set_title('Estrutura 3D do Chassi') plt.show() @@ -524,6 +537,7 @@ def plot_colored_wireframe(self,scalar_values, colormap='jet'): ax.set_ylabel('Y') ax.set_zlabel('Z') ax.set_title('Wireframe with Scalar Color Mapping') + ax.set_ylim([-0.5,1.5]) plt.tight_layout() plt.show() @@ -578,9 +592,9 @@ def modal_analysis_plot(self): ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) #Colocando a legenda dos nós após a deformação no gráfico - for i, (x, y, z) in enumerate(deformed_nodes): - ax.scatter(x, y, z, color='r', s=25) - ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + #for i, (x, y, z) in enumerate(deformed_nodes): + # ax.scatter(x, y, z, color='r', s=25) + # ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) # Plot original structure for i, (node1, node2) in enumerate(self.connections): @@ -596,6 +610,7 @@ def modal_analysis_plot(self): ax.set_title(f'Forma modal nº: {mode_idx + 1}') ax.legend() # Ensure the legend is displayed ax.set_zlim([-0.5,2]) + ax.set_ylim([-0.5,1.5]) plt.tight_layout() plt.show() @@ -680,10 +695,54 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): plt.show() print(f'KF Total: {KF_total:.2e} N/m \nKT Total: {KT_total:.2e} N/m') - - -nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" -elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Elements.csv" + def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): + """ + Executes the complete structural analysis workflow, including mass matrix generation, + node and connectivity matrices, deformation visualization, modal analysis, and static analysis. + + Parameters: + F_flexao1 (array): Forces due to bending in one direction, applied to the structure. + F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. + F_axial (array): Axial forces applied to the structure. + F_torcao (array): Torsional forces applied to the structure. + """ + self.mass() + #Gerar as matrizes de localização dos nós e de conectividade + self.node_loc_matrix() + self.connect_matrix() + # Plotando o gráfico 3D da estrutura + self.structure_plot() + self.matrizes_global() + + #Plotando os resultados das deformações + self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) + + #Gerar autovalores, autovetores e frequências naturais + autovalores, autovetores, frequencias = self.modal_analysis() + self.modal_analysis_plot() + #Exibindo as frequências naturais e modos de vibração da estrutura + print("\\n Frequências Naturais (ω) da estrutura:") + print(frequencias) + F_global = np.zeros(self.K_global.size) # Force vector + F_global[2+5*6] = 100 + F_global[2+5*9] = -50 + fixed_dofs = [0, 1, 2, 3, 4, 5] + + # Perform analysis + displacements = estrutura.static_analysis(self.K_global,F_global, fixed_dofs) + print("Displacement Vector:", displacements) + + estrutura.plot_colored_wireframe(displacements) + print(self.num_nodes) + print(displacements.size) + print(F_torcao.size) + print(F_flexao1.size) + +#nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" +#elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Elements.csv" + +nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nos_final.csv" +elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Conectividade_final.csv" # Carregar o arquivo para inspecionar seu conteúdo nodes = pd.read_csv(nodes_file_path) @@ -692,64 +751,24 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): # Selecionando as colunas de conectividades e propriedades dos elementos elements = element_data[['Element ID','Node a', 'Node b']] -element_properties = element_data[['Element ID','A', 'I', 'J', 'E', 'G']] - -#Criar a estrutura e montar as matrizes de rigidez e massa globais -#Dados: n = len(nodes), -# m = 1500 kg, -# rho = 7850 kg/m^3 -# A = 0.225 m^2 -# E = 210e9 # Módulo de elasticidade em Pa -# I = 8.33e-6 # Momento de inércia em m^4 -# Ip = Id = 8.33e-6 kg.m^2 - -#Criar a estrutura e montar as matrizes de rigidez e massa globais, atribuir forças +element_properties = element_data[['Element ID','A', 'I', 'J', 'E', 'G','X']] + + + +#Atribuir forças F_flexao1 = np.array([1000, 2000, 3000, 4000, 5000]) F_flexao2 = np.array([1000, 1000, 1000, 1000, 1000]) F_axial = np.array([1000, 2000, 3000, 4000, 5000]) F_torcao = np.array([1000, 2000, 3000, 4000, 5000]) #Inicializando a Estrutura -estrutura = Estrutura(elements, element_properties, nodes, 1500, 8.33e-6, 8.33e-6) - -#Gerar as matrizes de localização dos nós e de conectividade -estrutura.node_loc_matrix() -estrutura.connect_matrix() - -# Plotando o gráfico 3D da estrutura -estrutura.structure_plot() - -K_global, M_global = estrutura.matrizes_global() - -#Plotando os resultados das deformações -estrutura.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) +estrutura = Estrutura(elements, element_properties, nodes, 8.33e-6, 8.33e-6) +estrutura.run(F_flexao1, F_flexao2, F_axial, F_torcao) -#Gerar autovalores, autovetores e frequências naturais -autovalores, autovetores, frequencias = estrutura.modal_analysis() -#Exibindo as frequências naturais e modos de vibração da estrutura -print("\\n Frequências Naturais (ω) da estrutura:") -print(frequencias) - -#Plotagem dos modos de vibração para a estrutura de vigas -estrutura.modal_analysis_plot() - -F_global = np.zeros(K_global.size) # Force vector -F_global[2+5*6] = 100 -F_global[2+5*9] = -50 -fixed_dofs = [0, 1, 2, 3, 4, 5] - -# Perform analysis -displacements = estrutura.static_analysis(K_global,F_global, fixed_dofs) -print("Displacement Vector:", displacements) - -estrutura.plot_colored_wireframe(displacements) -print(nodes.size) -print(displacements.size) -print(F_torcao.size) -print(F_flexao1.size) """ + Estrutura.plot_colored_wireframe(nodes, elements, torcao/(np.max(np.max(torcao)))) Estrutura.plot_colored_wireframe(nodes, elements, deformacao_axial) Estrutura.plot_colored_wireframe(nodes, elements, flexao1) From 1925650a15a39e141e4e969168b7604ae5de21a3 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Tue, 10 Dec 2024 19:55:03 -0300 Subject: [PATCH 21/25] =?UTF-8?q?Update=20Chassi=20Com=20Implementa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Planilhas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...plementa\303\247\303\243o de Planilhas.py" | 121 +++++++++--------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" index dfa818c..3ad3fdc 100644 --- "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" +++ "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" @@ -12,6 +12,16 @@ class Estrutura: def __init__(self, elements,element_properties, nodes, Id, Ip): + """ + Initializes the structure with elements, nodes, and physical properties. + Inputs: + - elements: connectivity matrix between nodes (tuples of node indices). + - nodes: node coordinates (Nx3 array, where N is the number of nodes). + - m: total mass of the system (float). + - Id: directional moment of inertia (float). + - Ip: planar moment of inertia (float). + Outputs: None. + """ self.elements = elements # Matrix of connected elements self.num_elements = len(elements) # Number of elements self.element_properties = element_properties # Matrix of element properties @@ -29,25 +39,31 @@ def __init__(self, elements,element_properties, nodes, Id, Ip): self.num_modes = 12 # Number of vibration modes to return def node_loc_matrix(self): + """ + Creates a matrix with node locations for visualization. + Inputs: + - node_tags: list of node identifiers. + - node_coord: matrix of node coordinates. + Outputs: None. + """ print(self.nodes) def connect_matrix(self): + """ + Prints the connectivity matrix of elements. + Inputs: None (uses class attributes). + Outputs: None. + """ print(self.elements) def lenght_calculator(self, index): """ - Calculate the length of a finite element. - Args: - index (int): The index of the element whose length is to be calculated. - Returns: - float: The length of the element. - Process: - 1. Extracts the indices of the nodes (`Node a` and `Node b`) that define the element. - - Node indices in the `elements` dictionary are converted to zero-based indexing. - 2. Retrieves the 3D coordinates (`x`, `y`, `z`) of the nodes from the `nodes` dictionary. - 3. Computes the Euclidean distance between the two nodes: - \( L = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2} \) - """ + Calculates the length of an element based on node coordinates. + Inputs: + - element: tuple (start node index, end node index). + Outputs: + - element length (float). + """ node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] @@ -55,6 +71,18 @@ def lenght_calculator(self, index): return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) def mass(self): + """ + Calculate the mass of the entire structure. + + Parameters: + None (uses class attributes): + - K_global (array): Global stiffness matrix of the structure. + - M_global (array): Global mass matrix of the structure. + - num_modes (int): Number of modes to retain in the analysis. + + Returns + self.car_mass (float): Mass of the entire structure + """ for index in range(self.num_elements): L_e = self.lenght_calculator(index) X = self.element_properties['X'][index] @@ -68,20 +96,12 @@ def mass(self): def element(self, index): """ - Calculate the elemental stiffness and mass matrices for a given finite element. - - Args: - index (int): Index of the element for which the stiffness and mass matrices are to be computed. - - Returns: - tuple: A tuple containing: - - `k_e` (numpy.ndarray): The 12x12 stiffness matrix for the element, considering either - Euler-Bernoulli or Timoshenko beam theory. - - `m_e` (numpy.ndarray): The 12x12 mass matrix for the element. - Notes: - - Euler-Bernoulli and Timoshenko beam theories differ in how they handle shear deformation: - - Replace `c4` with `c3` in the stiffness matrix to use Euler-Bernoulli theory. - - Mass matrix assumes consistent mass distribution along the element. + Computes the element stiffness and mass matrices. + Inputs: + - element: tuple (start node index, end node index). + Outputs: + - k_e: element stiffness matrix. + - m_e: element mass matrix. """ #Tomando as propriedades de cada elemento E = self.element_properties['E'][index] @@ -141,16 +161,11 @@ def element(self, index): def aplicar_engastes(self, nodes, dofs): """ - Apply constraints (fixed supports) to specific degrees of freedom (DOFs) in the global stiffness matrix. - - This method modifies the global stiffness matrix (`K_global`) to simulate fixed supports (encastrés) - by applying large stiffness values to the specified DOFs of the given nodes. This effectively - constrains the structure's motion in those directions. - - Args: - nodes (list of int): Indices of the nodes where constraints are to be applied. - Each node index corresponds to its position in the global node list. - dofs (list of int): Degrees of freedom to be constrained for each node. + Applies constraints (fixed DOFs) on specific nodes. + Inputs: + - nodes: list of node indices to be constrained. + - dofs: list of degrees of freedom to be fixed. + Outputs: None. """ for node in nodes: #Laço para selecionar cada nó que será engastado for dof in dofs: #Laço para selecionar quais graus de liberdade serão fixados @@ -159,23 +174,11 @@ def aplicar_engastes(self, nodes, dofs): def matrizes_global(self): """ - Compute and assemble the global stiffness and mass matrices for the entire structure. - - This function calculates the stiffness (`K_global`) and mass (`M_global`) matrices of each element - and assembles them into the global matrices by appropriately mapping the degrees of freedom (DOFs). - - Steps: - 1. Iterates over all elements in the structure. - 2. For each element, retrieves the stiffness and mass matrices using the `element` method. - 3. Maps the local element matrices to the corresponding global DOFs. - 4. Updates the global matrices with contributions from each element. - 5. Saves the assembled matrices as CSV files for further use. - 6. Visualizes the sparsity patterns of the global matrices using `spy` plots. - - Returns: - tuple: `(K_global, M_global)` - - `K_global` (numpy.ndarray): Global stiffness matrix of the structure. - - `M_global` (numpy.ndarray): Global mass matrix of the structure. + Assembles the global stiffness and mass matrices. + Inputs: None (uses class attributes). + Outputs: + - K_global: global stiffness matrix. + - M_global: global mass matrix. Files exported: - `Matriz_Global_Rigidez.csv`: Global stiffness matrix. @@ -483,7 +486,9 @@ def structure_plot(self): ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') - ax.set_ylim([-0.5,1.5]) + ax.set_xlim([-0.5,2.0]) + ax.set_ylim([-0.5,2.0]) + ax.set_zlim([-0.5,1.5]) ax.set_title('Estrutura 3D do Chassi') plt.show() @@ -715,17 +720,17 @@ def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): self.matrizes_global() #Plotando os resultados das deformações - self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) + #self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) #Gerar autovalores, autovetores e frequências naturais autovalores, autovetores, frequencias = self.modal_analysis() - self.modal_analysis_plot() + #self.modal_analysis_plot() #Exibindo as frequências naturais e modos de vibração da estrutura print("\\n Frequências Naturais (ω) da estrutura:") print(frequencias) F_global = np.zeros(self.K_global.size) # Force vector - F_global[2+5*6] = 100 - F_global[2+5*9] = -50 + F_global[2+5*6] = 1000 + F_global[2+5*9] = 0 fixed_dofs = [0, 1, 2, 3, 4, 5] # Perform analysis From 0187fab40b3c36f523ee8ab497cc2c3b67616c50 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Fri, 13 Dec 2024 21:04:31 -0300 Subject: [PATCH 22/25] =?UTF-8?q?Update=20Chassi=20Com=20Implementa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Planilhas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...plementa\303\247\303\243o de Planilhas.py" | 112 ++++++++---------- 1 file changed, 48 insertions(+), 64 deletions(-) diff --git "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" index 3ad3fdc..a1d39eb 100644 --- "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" +++ "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" @@ -22,6 +22,7 @@ def __init__(self, elements,element_properties, nodes, Id, Ip): - Ip: planar moment of inertia (float). Outputs: None. """ + self.elements = elements # Matrix of connected elements self.num_elements = len(elements) # Number of elements self.element_properties = element_properties # Matrix of element properties @@ -50,7 +51,7 @@ def node_loc_matrix(self): def connect_matrix(self): """ - Prints the connectivity matrix of elements. + Generates and prints the connectivity matrix of elements. Inputs: None (uses class attributes). Outputs: None. """ @@ -58,12 +59,12 @@ def connect_matrix(self): def lenght_calculator(self, index): """ - Calculates the length of an element based on node coordinates. + Creates a matrix with node locations for visualization. Inputs: - - element: tuple (start node index, end node index). - Outputs: - - element length (float). - """ + - node_tags: list of node identifiers. + - node_coord: matrix of node coordinates. + Outputs: None. + """ node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] @@ -80,8 +81,8 @@ def mass(self): - M_global (array): Global mass matrix of the structure. - num_modes (int): Number of modes to retain in the analysis. - Returns - self.car_mass (float): Mass of the entire structure + outputs: + - self.car_mass (float): Mass of the entire structure """ for index in range(self.num_elements): L_e = self.lenght_calculator(index) @@ -179,15 +180,6 @@ def matrizes_global(self): Outputs: - K_global: global stiffness matrix. - M_global: global mass matrix. - - Files exported: - - `Matriz_Global_Rigidez.csv`: Global stiffness matrix. - - `Matriz_Global_Massa.csv`: Global mass matrix. - - Visualization: - - Two spy plots are generated: - 1. Sparsity pattern of the global stiffness matrix. - 2. Sparsity pattern of the global mass matrix. """ #Calculando as matrizes de rigidez e massa de cada elemento for index in range(self.num_elements): @@ -227,26 +219,15 @@ def matrizes_global(self): def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): """ - Calculates deformation and stiffness values for axial, torsional, and flexural forces for all elements in the structure. - - Parameters: - F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). - F_flexao2 (float): Force applied for distributed flexural deformation. - F_axial (float): Axial force applied to the elements. - F_torcao (float): Torsional force applied to the elements. - - Returns: - tuple: - - torcao (array): Torsional deformation for each element. - - deformacao (array): Axial deformation for each element. - - flexao1 (array): Flexural deformation due to point load for each element. - - flexao2 (array): Flexural deformation due to distributed load for each element. - - flexao3 (array): Combined flexural deformation for each element. - - KF_total (float): Total flexural stiffness of the structure. - - KT_total (float): Total torsional stiffness of the structure. - - KF_elements (list): Flexural stiffness for each element. - - KT_elements (list): Torsional stiffness for each element. - """ + Calculates deformations and stiffness of elements under loads. + Inputs: + - F_flex1: array of point bending forces. + - F_flex2: array of distributed bending forces. + - F_axial: array of axial forces. + - F_torsion: array of torsion forces. + Outputs: + - Arrays of torsion, deformations, and stiffness (bending and torsional). + """ KF_total = 0 KT_total = 0 KF_elements = [] @@ -289,23 +270,13 @@ def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): def modal_analysis(self): """ - Performs modal analysis of the structure by solving the eigenvalue problem. - - This function computes the natural frequencies and mode shapes of the structure - using the global stiffness (`K_global`) and mass (`M_global`) matrices. - - Returns: - tuple: - - eigenvalues (array): Array of the selected eigenvalues corresponding to the natural frequencies. - - eigenvectors (array): Array of the selected eigenvectors representing mode shapes. - - frequencies (array): Array of the selected natural frequencies in Hertz. - - Parameters: - None (uses class attributes): - - K_global (array): Global stiffness matrix of the structure. - - M_global (array): Global mass matrix of the structure. - - num_modes (int): Number of modes to retain in the analysis. - """ + Performs modal analysis to compute natural frequencies and mode shapes. + Inputs: None. + Outputs: + - eigenvalues: eigenvalues (squared natural frequencies). + - eigenvectors: eigenvectors (mode shapes). + - frequencies: natural frequencies (Hz). + """ # Análise modal por resolução do problema de autovalor e autovetor unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) @@ -333,6 +304,14 @@ def static_analysis(self,K_global, F_global, fixed_dofs): Returns: displacements (ndarray): Displacement vector (N). + + Resolve uma análise estática para deslocamentos em DOFs livres. + Entradas: + - K_global: matriz de rigidez global. + - F_global: vetor de forças globais. + - fixed_dofs: índices de graus de liberdade fixos. + Saídas: + - displacements: vetor de deslocamentos nos DOFs. """ # Total number of DOFs n_dofs = K_global.shape[0] @@ -430,6 +409,11 @@ def compute_von_mises(self,B_matrices,F_global, fixed_dofs, E, nu): return von_mises_stresses def Mesh(self): + """ + Generates a `.geo` file for the structure mesh in GMSH. + Inputs: None (uses class attributes and user-provided file name). + Outputs: None. + """ filename = input("Insira o nome do arquivo: ") + ".geo" diretorio = input("Insira o diretorio onde o arquivo .geo deve ser salvo: ") @@ -702,14 +686,14 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): """ - Executes the complete structural analysis workflow, including mass matrix generation, - node and connectivity matrices, deformation visualization, modal analysis, and static analysis. - - Parameters: - F_flexao1 (array): Forces due to bending in one direction, applied to the structure. - F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. - F_axial (array): Axial forces applied to the structure. - F_torcao (array): Torsional forces applied to the structure. + Executes the complete structural analysis workflow, including mass matrix generation, + node and connectivity matrices, deformation visualization, modal analysis, and static analysis. + + Parameters: + F_flexao1 (array): Forces due to bending in one direction, applied to the structure. + F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. + F_axial (array): Axial forces applied to the structure. + F_torcao (array): Torsional forces applied to the structure. """ self.mass() #Gerar as matrizes de localização dos nós e de conectividade @@ -720,11 +704,11 @@ def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): self.matrizes_global() #Plotando os resultados das deformações - #self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) + self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) #Gerar autovalores, autovetores e frequências naturais autovalores, autovetores, frequencias = self.modal_analysis() - #self.modal_analysis_plot() + self.modal_analysis_plot() #Exibindo as frequências naturais e modos de vibração da estrutura print("\\n Frequências Naturais (ω) da estrutura:") print(frequencias) From f360fc46927281fd20b545200d04643a7880dbf5 Mon Sep 17 00:00:00 2001 From: EduardoAMXango <167939544+EduardoAMXango@users.noreply.github.com> Date: Fri, 21 Feb 2025 19:30:03 -0300 Subject: [PATCH 23/25] =?UTF-8?q?Update=20Chassi=20Com=20Implementa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20Planilhas.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...plementa\303\247\303\243o de Planilhas.py" | 108 ++++++++++-------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" index a1d39eb..9ba4846 100644 --- "a/Chassi Com Implementa\303\247\303\243o de Planilhas.py" +++ "b/Chassi Com Implementa\303\247\303\243o de Planilhas.py" @@ -22,7 +22,6 @@ def __init__(self, elements,element_properties, nodes, Id, Ip): - Ip: planar moment of inertia (float). Outputs: None. """ - self.elements = elements # Matrix of connected elements self.num_elements = len(elements) # Number of elements self.element_properties = element_properties # Matrix of element properties @@ -51,7 +50,7 @@ def node_loc_matrix(self): def connect_matrix(self): """ - Generates and prints the connectivity matrix of elements. + Prints the connectivity matrix of elements. Inputs: None (uses class attributes). Outputs: None. """ @@ -59,12 +58,12 @@ def connect_matrix(self): def lenght_calculator(self, index): """ - Creates a matrix with node locations for visualization. + Calculates the length of an element based on node coordinates. Inputs: - - node_tags: list of node identifiers. - - node_coord: matrix of node coordinates. - Outputs: None. - """ + - element: tuple (start node index, end node index). + Outputs: + - element length (float). + """ node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] @@ -180,6 +179,15 @@ def matrizes_global(self): Outputs: - K_global: global stiffness matrix. - M_global: global mass matrix. + + Files exported: + - `Matriz_Global_Rigidez.csv`: Global stiffness matrix. + - `Matriz_Global_Massa.csv`: Global mass matrix. + + Visualization: + - Two spy plots are generated: + 1. Sparsity pattern of the global stiffness matrix. + 2. Sparsity pattern of the global mass matrix. """ #Calculando as matrizes de rigidez e massa de cada elemento for index in range(self.num_elements): @@ -194,7 +202,7 @@ def matrizes_global(self): self.K_global[np.ix_(dofs, dofs)] += k_e self.M_global[np.ix_(dofs, dofs)] += m_e - #self.aplicar_engastes([0, 2, 4, 5], [0, 1, 2, 3, 4, 5]) #Por enquanto não estaremos considerando engastes + #self.aplicar_engastes([0, 2, 4, 5], [0, 1, 2, 3, 4, 5]) #Por enquanto não estaremos considerando engastes pd.DataFrame(self.K_global).to_csv('Matriz_Global_Rigidez.csv', index=True, header=True) pd.DataFrame(self.M_global).to_csv('Matriz_Global_Massa.csv', index=True, header=True) @@ -219,15 +227,26 @@ def matrizes_global(self): def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): """ - Calculates deformations and stiffness of elements under loads. - Inputs: - - F_flex1: array of point bending forces. - - F_flex2: array of distributed bending forces. - - F_axial: array of axial forces. - - F_torsion: array of torsion forces. - Outputs: - - Arrays of torsion, deformations, and stiffness (bending and torsional). - """ + Calculates deformation and stiffness values for axial, torsional, and flexural forces for all elements in the structure. + + Parameters: + F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). + F_flexao2 (float): Force applied for distributed flexural deformation. + F_axial (float): Axial force applied to the elements. + F_torcao (float): Torsional force applied to the elements. + + Returns: + tuple: + - torcao (array): Torsional deformation for each element. + - deformacao (array): Axial deformation for each element. + - flexao1 (array): Flexural deformation due to point load for each element. + - flexao2 (array): Flexural deformation due to distributed load for each element. + - flexao3 (array): Combined flexural deformation for each element. + - KF_total (float): Total flexural stiffness of the structure. + - KT_total (float): Total torsional stiffness of the structure. + - KF_elements (list): Flexural stiffness for each element. + - KT_elements (list): Torsional stiffness for each element. + """ KF_total = 0 KT_total = 0 KF_elements = [] @@ -270,13 +289,23 @@ def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): def modal_analysis(self): """ - Performs modal analysis to compute natural frequencies and mode shapes. - Inputs: None. - Outputs: - - eigenvalues: eigenvalues (squared natural frequencies). - - eigenvectors: eigenvectors (mode shapes). - - frequencies: natural frequencies (Hz). - """ + Performs modal analysis of the structure by solving the eigenvalue problem. + + This function computes the natural frequencies and mode shapes of the structure + using the global stiffness (`K_global`) and mass (`M_global`) matrices. + + Returns: + tuple: + - eigenvalues (array): Array of the selected eigenvalues corresponding to the natural frequencies. + - eigenvectors (array): Array of the selected eigenvectors representing mode shapes. + - frequencies (array): Array of the selected natural frequencies in Hertz. + + Parameters: + None (uses class attributes): + - K_global (array): Global stiffness matrix of the structure. + - M_global (array): Global mass matrix of the structure. + - num_modes (int): Number of modes to retain in the analysis. + """ # Análise modal por resolução do problema de autovalor e autovetor unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) @@ -304,14 +333,6 @@ def static_analysis(self,K_global, F_global, fixed_dofs): Returns: displacements (ndarray): Displacement vector (N). - - Resolve uma análise estática para deslocamentos em DOFs livres. - Entradas: - - K_global: matriz de rigidez global. - - F_global: vetor de forças globais. - - fixed_dofs: índices de graus de liberdade fixos. - Saídas: - - displacements: vetor de deslocamentos nos DOFs. """ # Total number of DOFs n_dofs = K_global.shape[0] @@ -409,11 +430,6 @@ def compute_von_mises(self,B_matrices,F_global, fixed_dofs, E, nu): return von_mises_stresses def Mesh(self): - """ - Generates a `.geo` file for the structure mesh in GMSH. - Inputs: None (uses class attributes and user-provided file name). - Outputs: None. - """ filename = input("Insira o nome do arquivo: ") + ".geo" diretorio = input("Insira o diretorio onde o arquivo .geo deve ser salvo: ") @@ -686,14 +702,14 @@ def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): """ - Executes the complete structural analysis workflow, including mass matrix generation, - node and connectivity matrices, deformation visualization, modal analysis, and static analysis. - - Parameters: - F_flexao1 (array): Forces due to bending in one direction, applied to the structure. - F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. - F_axial (array): Axial forces applied to the structure. - F_torcao (array): Torsional forces applied to the structure. + Executes the complete structural analysis workflow, including mass matrix generation, + node and connectivity matrices, deformation visualization, modal analysis, and static analysis. + + Parameters: + F_flexao1 (array): Forces due to bending in one direction, applied to the structure. + F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. + F_axial (array): Axial forces applied to the structure. + F_torcao (array): Torsional forces applied to the structure. """ self.mass() #Gerar as matrizes de localização dos nós e de conectividade @@ -764,4 +780,4 @@ def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): Estrutura.plot_colored_wireframe(nodes, elements, flexao2) Estrutura.plot_colored_wireframe(nodes, elements, flexao3) -""" \ No newline at end of file +""" From 342b9e166c1ededdb8dd106ce1f36d4333654cb0 Mon Sep 17 00:00:00 2001 From: EduardoAM Date: Fri, 21 Feb 2025 19:30:51 -0300 Subject: [PATCH 24/25] Create Codigo_Estrutura_Suposta_Matriz_B.py --- Codigo_Estrutura_Suposta_Matriz_B.py | 809 +++++++++++++++++++++++++++ 1 file changed, 809 insertions(+) create mode 100644 Codigo_Estrutura_Suposta_Matriz_B.py diff --git a/Codigo_Estrutura_Suposta_Matriz_B.py b/Codigo_Estrutura_Suposta_Matriz_B.py new file mode 100644 index 0000000..119e174 --- /dev/null +++ b/Codigo_Estrutura_Suposta_Matriz_B.py @@ -0,0 +1,809 @@ +import numpy as np +from scipy.linalg import eigh +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D +from scipy.sparse import lil_matrix +from scipy.sparse.linalg import spsolve +import pandas as pd +import sys +import os +np.set_printoptions(threshold=sys.maxsize) +np.set_printoptions(linewidth=200, suppress=True) + +class Estrutura: + def __init__(self, elements,element_properties, nodes, Id, Ip): + """ + Initializes the structure with elements, nodes, and physical properties. + Inputs: + - elements: connectivity matrix between nodes (tuples of node indices). + - nodes: node coordinates (Nx3 array, where N is the number of nodes). + - m: total mass of the system (float). + - Id: directional moment of inertia (float). + - Ip: planar moment of inertia (float). + Outputs: None. + """ + + self.elements = elements # Matrix of connected elements + self.num_elements = len(elements) # Number of elements + self.element_properties = element_properties # Matrix of element properties + self.nodes = nodes # Matrix of nodes with their positions + self.coordinates = np.array(nodes[['x', 'y', 'z']]) # Coordinates of the nodes + self.connections = np.array(elements[['Node a', 'Node b']]) # Connections between nodes (elements) + self.num_nodes = len(nodes) # Total number of nodes + self.car_mass = 0 # Mass of the vehicle (kg) + self.moment_of_inertia_direction = Id # Moment of inertia in a specific direction (kg.m^2) + self.moment_of_inertia_plane = Ip # Moment of inertia in the plane (kg.m^2) + self.num_dofs_per_node = 6 # Degrees of freedom per node + self.num_dofs = self.num_nodes * self.num_dofs_per_node # Total number of degrees of freedom + self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global stiffness matrix + self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global mass matrix + self.num_modes = 1 # Number of vibration modes to return + + def node_loc_matrix(self): + """ + Creates a matrix with node locations for visualization. + Inputs: + - node_tags: list of node identifiers. + - node_coord: matrix of node coordinates. + Outputs: None. + """ + print(self.nodes) + + def connect_matrix(self): + """ + Generates and prints the connectivity matrix of elements. + Inputs: None (uses class attributes). + Outputs: None. + """ + print(self.elements) + + def calcular_comprimento(self, index): + """ + Creates a matrix with node locations for visualization. + Inputs: + - node_tags: list of node identifiers. + - node_coord: matrix of node coordinates. + Outputs: None. + """ + + node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) + x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] + x2, y2, z2 = self.nodes['x'][node2], self.nodes['y'][node2], self.nodes['z'][node2] + return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) + + def mass(self): + """ + Calculate the mass of the entire structure. + + Parameters: + None (uses class attributes): + - K_global (array): Global stiffness matrix of the structure. + - M_global (array): Global mass matrix of the structure. + - num_modes (int): Number of modes to retain in the analysis. + + outputs: + - self.car_mass (float): Mass of the entire structure + """ + for index in range(self.num_elements): + L_e = self.calcular_comprimento(index) + X = self.element_properties['X'][index] + A = self.element_properties['A'][index] + rho = 7870 # kg/m^3 + raio_externo = np.sqrt(A/np.pi) + raio_interno = raio_externo - X + volume = np.pi*L_e* (raio_externo**2 - raio_interno**2) + self.car_mass+= volume*rho + print(f"{self.car_mass.round(2) } kg") + + def element(self, index): + """ + Computes the element stiffness and mass matrices. + Inputs: + - element: tuple (start node index, end node index). + Outputs: + - k_e: element stiffness matrix. + - m_e: element mass matrix. + """ + #Tomando as propriedades de cada elemento + E = self.element_properties['E'][index] + A = self.element_properties['A'][index] + I = self.element_properties['I'][index] + J = self.element_properties['J'][index] + G = self.element_properties['G'][index] + + kappa=0.9 #Fator de correção para cisalhamento + + L_e = self.calcular_comprimento(index) + Phi = (12 * E * I) / (kappa * G * A * L_e**2) + rho = 7850 # kg/m^3 + c1 = E * A / L_e + c2 = G * J / L_e + c3 = E * I / L_e**3 #Euler-Bernoulli + c4 = (E*I)/(L_e**3*(1+Phi)) #Timoshenko + t1 = (4+Phi) + t2 = (2-Phi) + d1 = rho*A*L_e + d2 = (I*L_e)/6 + d3 = (rho*A*L_e)/420 + + # Matriz de Rigidez Elementar (Euler-Bernoulli) + # Para converter para timoshenko basta trocar c3 por c4,onde tem (4 * L_e**2 * c3) substitui por (t1* L_e**2 * c4) e onde tiver (2 * L_e**2 * c3) por (t2* L_e**2 * c4)) + k_e= np.array([ + [12 * c4, 0, 0, 6 * L_e * c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4, 0, 0], + [0, c1, 0, 0, 0, 0, 0, -c1, 0, 0, 0, 0], + [0, 0, 12 * c4, 0, 0, 6 * L_e* c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4], + [6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0], + [0, 0, 0, 0, c2, 0, 0, 0, 0, 0, -c2, 0], + [0, 0, 6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2 * L_e**2 * c4], + [-12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4, 0, 0], + [0, -c1, 0, 0, 0, 0, 0, c1, 0, 0, 0, 0], + [0, 0, -12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4], + [6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0], + [0, 0, 0, 0, -c2, 0, 0, 0, 0, 0, c2, 0], + [0, 0, 6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4] + ]) + + # Matriz de Massa Elementar + m_e= np.array([ + [156 * d3, 0, 0, 22 * L_e * d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3, 0, 0], + [0,2*d1, 0, 0, 0, 0, 0, d1, 0, 0, 0, 0], + [0, 0, 156 * d3, 0, 0, 22 * L_e* d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3], + [22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0], + [0, 0, 0, 0, 2*d2, 0, 0, 0, 0, 0, d2, 0], + [0, 0, 22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3], + [54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156* d3, 0, 0, -22 * L_e * d3, 0, 0], + [0, d1, 0, 0, 0, 0, 0, 2*d1, 0, 0, 0, 0], + [0, 0, 54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156 * d3, 0, 0, -22 * L_e * d3], + [-13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0], + [0, 0, 0, 0, d2, 0, 0, 0, 0, 0, 2*d2, 0], + [0, 0, -13 * L_e * d3, 0, 0,-3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3] + ]) + return k_e,m_e + + def B_matrix(self,index): + """ + Calculates the B-matrix for a 3D Timoshenko beam element. + Inputs: + - element: tuple (start node index, end node index). + Outputs: + - B: B-matrix for the element. + """ + L_e = self.calcular_comprimento(index) # Comprimento do elemento + x = L_e / 2 # Ponto médio do elemento (ou outro ponto de interesse) + + # Derivadas das funções de forma para deslocamentos axiais + dN1_dx = -1 / L_e + dN2_dx = 1 / L_e + + # Derivadas das funções de forma para deslocamentos transversais e rotações + dN3_dx = (-6 * x / L_e**2) + (6 * x**2 / L_e**3) + dN5_dx = (6 * x / L_e**2) - (6 * x**2 / L_e**3) + + # Montagem da B-matriz para Timoshenko + B = np.array([ + [dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0, 0, 0, 0, 0], # Extensão axial + [0, -dN3_dx, 0, 0, 0, 0, 0, -dN5_dx, 0, 0, 0, 0], # Curvatura em y + [0, 0, -dN3_dx, 0, 0, 0, 0, 0, -dN5_dx, 0, 0, 0], # Curvatura em z + [0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0, 0], # Torção + [0, 0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0], # Cisalhamento em y + [0, 0, 0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx], # Cisalhamento em z + [0, dN3_dx, 0, 0, 0, 0, 0, dN5_dx, 0, 0, 0, 0], # Deformação por cisalhamento em xy + [0, 0, dN3_dx, 0, 0, 0, 0, 0, dN5_dx, 0, 0, 0] # Deformação por cisalhamento em xz + ]) + + return B + + def aplicar_engastes(self, nodes, dofs): + """ + Applies constraints (fixed DOFs) on specific nodes. + Inputs: + - nodes: list of node indices to be constrained. + - dofs: list of degrees of freedom to be fixed. + Outputs: None. + """ + for node in nodes: #Laço para selecionar cada nó que será engastado + for dof in dofs: #Laço para selecionar quais graus de liberdade serão fixados + index = node * self.num_dofs_per_node + dof #Identificação da entrada da matriz que precisa ser restringida pelo engaste + self.K_global[index, index] = 10**10 # Um valor suficientemente grande para simular um engaste + + def matrizes_global(self): + """ + Assembles the global stiffness and mass matrices. + Inputs: None (uses class attributes). + Outputs: + - K_global: global stiffness matrix. + - M_global: global mass matrix. + """ + #Calculando as matrizes de rigidez e massa de cada elemento + for index in range(self.num_elements): + + k_e, m_e= self.element(index) + node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) + # DOFs associados ao elemento + dofs = [6 * node1, 6 * node1 + 1, 6 * node1 + 2, 6 * node1 + 3, 6 * node1 + 4, 6 * node1 + 5, + 6 * node2, 6 * node2 + 1, 6 * node2 + 2, 6 * node2 + 3, 6 * node2 + 4, 6 * node2 + 5] + + # Atualizando as matrizes globais + self.K_global[np.ix_(dofs, dofs)] += k_e + self.M_global[np.ix_(dofs, dofs)] += m_e + + #self.aplicar_engastes([0, 2, 4, 5], [0, 1, 2, 3, 4, 5]) #Por enquanto não estaremos considerando engastes + pd.DataFrame(self.K_global).to_csv('Matriz_Global_Rigidez.csv', index=True, header=True) + pd.DataFrame(self.M_global).to_csv('Matriz_Global_Massa.csv', index=True, header=True) + + plt.figure(figsize=(6, 6)) + plt.spy(self.K_global, markersize=10) # Adjust markersize for visibility + plt.title("Spy Plot of the Kg") + plt.xlabel("Columns") + plt.ylabel("Rows") + plt.grid(True, which="both", linestyle="--", linewidth=0.5) + plt.show() + + plt.figure(figsize=(6, 6)) + plt.spy(self.M_global, markersize=10) # Adjust markersize for visibility + plt.title("Spy Plot of the Mg") + plt.xlabel("Columns") + plt.ylabel("Rows") + plt.grid(True, which="both", linestyle="--", linewidth=0.5) + plt.show() + + + return self.K_global,self.M_global + + def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): + """ + Calculates deformations and stiffness of elements under loads. + Inputs: + - F_flex1: array of point bending forces. + - F_flex2: array of distributed bending forces. + - F_axial: array of axial forces. + - F_torsion: array of torsion forces. + Outputs: + - Arrays of torsion, deformations, and stiffness (bending and torsional). + """ + KF_total = 0 + KT_total = 0 + KF_elements = [] + KT_elements = [] + torcao, deformacao, flexao1, flexao2, flexao3 = [], [], [], [], [] + for index in range(len(self.elements)): + E = self.element_properties['E'][index] + A = self.element_properties['A'][index] + I = self.element_properties['I'][index] + J = self.element_properties['J'][index] + G = self.element_properties['G'][index] + L_e = self.calcular_comprimento(index) + # Equação de torsão + torcao_val = (F_torcao * L_e) / (G * J) #Fonte[1] + torcao.append(torcao_val) + # Equação para deformação axial + deformacao_val = (F_axial* L_e / (A * E)) #Fonte[2] + deformacao.append(deformacao_val) + # Equação para flexão + flexao_val1 = (F_flexao1*L_e**3)/(48 * E * I) #Fonte[3.1] (carga pontual no meio do elemento biapoiado) + flexao_val2 = (5*F_flexao2*L_e**4)/(384 * E * I) #Fonte[3.2] (carga distribuída ao longo de todo o elemento biapoiado) + flexao_val3 = flexao_val1 + flexao_val2 #Fonte[3.3] (tentativa de carregamento misto) + flexao1.append(flexao_val1) + flexao2.append(flexao_val2) + flexao3.append(flexao_val3) + + # Rigidez flexional + KF = E * I / L_e + # Rigidez torsional + KT = G * J / L_e + + KF_total += KF + KT_total += KT + + KF_elements.append(KF) + KT_elements.append(KT) + + return (np.array(torcao), np.array(deformacao), np.array(flexao1), + np.array(flexao2), np.array(flexao3), KF_total, KT_total, KF_elements, KT_elements) + + def modal_analysis(self): + """ + Performs modal analysis to compute natural frequencies and mode shapes. + Inputs: None. + Outputs: + - eigenvalues: eigenvalues (squared natural frequencies). + - eigenvectors: eigenvectors (mode shapes). + - frequencies: natural frequencies (Hz). + """ + # Análise modal por resolução do problema de autovalor e autovetor + unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) + + # Frequências naturais (raiz quadrada dos autovalores) + unsorted_frequencies = np.sqrt(unsorted_eigenvalues) / (2 * np.pi) # Divisão por 2*pi para converter para hertz + + # Tratando os dados (tomando apenas as 20 primeiras frequências naturais) + sorted_indices = np.argsort(unsorted_frequencies) # Ordena as frequências em ordem crescente + top_indices = sorted_indices[:self.num_modes] # Seleciona os índices dos primeiros n modos + + eigenvalues = np.array(unsorted_eigenvalues)[top_indices] # Filtra os primeiros n autovalores + eigenvectors = np.array(unsorted_eigenvectors)[:, top_indices] # Filtra os primeiros n autovetores + frequencies = np.array(unsorted_frequencies)[top_indices] # Filtra as primeiras n frequências + + return eigenvalues, eigenvectors, frequencies + + def static_analysis(self,K_global, F_global, fixed_dofs): + """ + Perform static analysis by solving Ku = F with boundary conditions. + + Parameters: + K_global (ndarray): Global stiffness matrix (N x N). + F_global (ndarray): Global force vector (N). + fixed_dofs (list): List of DOF indices to be fixed. + + Returns: + displacements (ndarray): Displacement vector (N). + + Resolve uma análise estática para deslocamentos em DOFs livres. + Entradas: + - K_global: matriz de rigidez global. + - F_global: vetor de forças globais. + - fixed_dofs: índices de graus de liberdade fixos. + Saídas: + - displacements: vetor de deslocamentos nos DOFs. + """ + # Total number of DOFs + n_dofs = K_global.shape[0] + + # Create a mask for free DOFs (DOFs not constrained) + free_dofs = np.array([i for i in range(n_dofs) if i not in fixed_dofs]) + + # Reduce the stiffness matrix and force vector + K_reduced = K_global[np.ix_(free_dofs, free_dofs)] + F_reduced = F_global[free_dofs] + + # Solve for displacements at free DOFs + # USE "linalg.lstsq" FOR NEAR SINGULAR MATRICES (ALL OF THEM) + u_reduced = np.linalg.lstsq(K_reduced, F_reduced, rcond=None)[0] # Get only the solution vector + + # Construct full displacement vector + displacements = np.zeros(n_dofs) + + displacements[free_dofs] = u_reduced + + return displacements + + def compute_strain(self, displacements): + """ + Compute strains for all elements. + + Parameters: + displacements (ndarray): Displacement vector for all nodes. + B_matrices (list of ndarray): Strain-displacement matrices for each element. + + Returns: + strains (list of ndarray): Strain tensors for all elements. + """ + strains = [] + for index in range(self.num_elements): + B = self.B_matrix(index) # Certifique-se de que o nome da função está correto + node1, node2 = int(self.elements["Node a"][index] - 1), int(self.elements["Node b"][index] - 1) + + # DOFs associados ao elemento + dofs = [6 * node1, 6 * node1 + 1, 6 * node1 + 2, 6 * node1 + 3, 6 * node1 + 4, 6 * node1 + 5, + 6 * node2, 6 * node2 + 1, 6 * node2 + 2, 6 * node2 + 3, 6 * node2 + 4, 6 * node2 + 5] + + u_reduced = displacements[dofs] # Seleciona os deslocamentos associados aos DOFs + + strain = np.dot(B, u_reduced) # B-matrix vezes o vetor de deslocamento + + strains.append(strain) + + return np.array(strains) + + def compute_stress(self,strains, E, nu): + """ + Compute stresses for all elements using Hooke's law. + + Parameters: + strains (list of ndarray): Strain tensors for all elements. + E (float): Young's modulus. + nu (float): Poisson's ratio. + + Returns: + stresses (list of ndarray): Stress tensors for all elements. + """ + # Construct constitutive matrix (isotropic 3D elasticity) + lambda_ = (E * nu) / ((1 + nu) * (1 - 2 * nu)) + G = E / (2 * (1 + nu)) + C = np.array([ + [lambda_ + 2*G , lambda_ , lambda_ , 0, 0, 0], + [lambda_ , lambda_ + 2*G , lambda_ , 0, 0, 0], + [lambda_ , lambda_ , lambda_ + 2*G , 0, 0, 0], + [ 0, 0, 0, G, 0, 0], + [ 0, 0, 0, 0, G, 0], + [ 0, 0, 0, 0, 0, G] + ]) + + stresses = [] + for strain in strains: + stress = np.dot(C, strain) # Hooke's law: C times strain + stresses.append(stress) + return stresses + + def compute_von_mises(self,B_matrices,F_global, fixed_dofs, E, nu): + """ + Compute von Mises stress for all elements. + + Parameters: + stresses (list of ndarray): Stress tensors for all elements. + + Returns: + von_mises_stresses (list of float): Von Mises stress for each element. + """ + stresses = self.compute_stress(B_matrices,F_global, fixed_dofs, E, nu) + von_mises_stresses = [] + for stress in stresses: + sigma_xx, sigma_yy, sigma_zz, tau_xy, tau_yz, tau_zx = stress + von_mises = np.sqrt( + 0.5 * ( + (sigma_xx - sigma_yy)**2 + + (sigma_yy - sigma_zz)**2 + + (sigma_zz - sigma_xx)**2 + + 6 * (tau_xy**2 + tau_yz**2 + tau_zx**2) + ) + ) + von_mises_stresses.append(von_mises) + return von_mises_stresses + + def Mesh(self): + """ + Generates a `.geo` file for the structure mesh in GMSH. + Inputs: None (uses class attributes and user-provided file name). + Outputs: None. + """ + + filename = input("Insira o nome do arquivo: ") + ".geo" + diretorio = input("Insira o diretorio onde o arquivo .geo deve ser salvo: ") + + if not os.path.exists(diretorio): + os.makedirs(diretorio) + + filepath = os.path.join(diretorio, filename) + + with open(filepath, 'w') as geo_file: + for i, (x, y, z) in enumerate(self.nodes): + geo_file.write(f'Point({i + 1}) = {{{x}, {y}, {z}, 1.0}};\n') + + for i, (start, end) in enumerate(self.elements): + geo_file.write(f'Line({i + 1}) = {{{start + 1}, {end + 1}}};\n') + + if len(self.elements) > 2: + line_loop_indices = ', '.join(str(i + 1) for i in range(len(elements))) + geo_file.write(f'Line Loop(1) = {{{line_loop_indices}}};\n') + geo_file.write('Plane Surface(1) = {1};\n') + + geo_file.write('Mesh.Algorithm = 6;\n') + geo_file.write('Mesh.ElementOrder = 1;\n') + geo_file.write('Mesh.Format = 1;\n') + + print(f'O arquivo foi salvo em: {filepath}, basta abrir o GMSH, e abrir o arquivo') + + def structure_plot(self): + """ + Plots a 3D wireframe of the structure. + + Parameters: + coordinates (array): Array of node coordinates (N x 3). + connections (list): List of tuples defining connections between nodes. + """ + + # Plotando o gráfico 3D da estrutura + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Adicionando os pontos (nós) + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + # Adicionando as linhas de ligação entre os nós + for node1, node2 in self.connections: + x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] + y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] + z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] + ax.plot(x_coords, y_coords, z_coords, 'r-', marker='o') + + # Configurações adicionais do gráfico + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.set_xlim([-0.5,2.0]) + ax.set_ylim([-0.5,2.0]) + ax.set_zlim([-0.5,1.5]) + ax.set_title('Estrutura 3D do Chassi') + + plt.show() + + def plot_colored_wireframe(self,scalar_values, colormap='jet'): + """ + Plots a 3D wireframe of the structure with color mapping based on scalar values. + + Parameters: + nodes (array): Array of node coordinates (N x 3). + elements (list): List of tuples defining connections between nodes. + scalar_values (array): 1D array of scalar values (e.g., strain) at each node. + colormap (str): Colormap name for visualization. + """ + # Normalize scalar values to [0, 1] for colormap + norm = plt.Normalize(vmin=np.min(scalar_values), vmax=np.max(scalar_values)) + cmap = plt.get_cmap(colormap) + + # Create the plot + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Adicionando os pontos (nós) + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + # Adicionando as linhas de ligação entre os nós + for node1, node2 in self.connections: + x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] + y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] + z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] + ax.plot(x_coords, y_coords, z_coords, 'r-', marker='o') + # Get the scalar value for the midpoint of the element + scalar_midpoint = (scalar_values[node1-1] + scalar_values[node2-1]) / 2 + + # Map scalar value to color + color = cmap(norm(scalar_midpoint)) + + # Plot the line segment with the corresponding color + ax.plot(x_coords, y_coords, z_coords, color=color, linewidth=2) + + # Add a colorbar + mappable = plt.cm.ScalarMappable(cmap=cmap, norm=norm) + mappable.set_array(scalar_values) + cbar = plt.colorbar(mappable, ax=ax, orientation='vertical', shrink=0.8, pad=0.1) + cbar.set_label("Strain (or other variable)", fontsize=12) + + # Set axis labels and title + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.set_title('Wireframe with Scalar Color Mapping') + ax.set_ylim([-0.5,1.5]) + plt.tight_layout() + plt.show() + + def modal_analysis_plot(self): + """ + Plots the modal shapes of the structure in 3D, showing the deformed and original configurations. + + This function performs the following: + - Computes eigenvalues and eigenvectors using the modal analysis method. + - Extracts the translational displacements (x, y, z) for each node from the modal shapes. + - Scales and visualizes the deformed structure for each mode, overlaying it on the original structure. + - Annotates the nodes of both the original and deformed structures. + + Parameters: + None (uses class attributes): + - coordinates (array): Array of node coordinates (N x 3). + - connections (list): List of tuples defining element connections between nodes. + - modal_analysis (method): Computes eigenvalues and eigenvectors. + """ + autovalores, autovetores, _ = self.modal_analysis() + + for mode_idx in range(len(autovalores)): + mode_shape = autovetores[:, mode_idx] + displacements = np.zeros((len(self.coordinates), 3)) # Assuming we want to visualize x, y, z displacements only + + # Loop through nodes to extract the translations + for j, (x, y, z) in enumerate(self.coordinates): + # 6 DOFs per node: [u_x, u_y, u_z, theta_x, theta_y, theta_z] + dof_start = 6 * j # Start index of DOFs for node j + displacements[j, 0] = mode_shape[dof_start] # u_x + displacements[j, 1] = mode_shape[dof_start + 1] # u_y + displacements[j, 2] = mode_shape[dof_start + 2] # u_z + + # Scale displacements for plots + scale_factor = 1 # Adjust as needed + deformed_nodes = np.array(self.coordinates) + displacements * scale_factor + + # Plot deformed + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Plot deformed structure + for i, (node1, node2) in enumerate(self.connections): + x = [deformed_nodes[node1-1][0], deformed_nodes[node2-1][0]] + y = [deformed_nodes[node1-1][1], deformed_nodes[node2-1][1]] + z = [deformed_nodes[node1-1][2], deformed_nodes[node2-1][2]] + ax.plot(x, y, z, 'r-', label="Deformed" if i == 0 else "") # Add label only once + + #Colocando a legenda dos nós no gráfico + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + #Colocando a legenda dos nós após a deformação no gráfico + #for i, (x, y, z) in enumerate(deformed_nodes): + # ax.scatter(x, y, z, color='r', s=25) + # ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + # Plot original structure + for i, (node1, node2) in enumerate(self.connections): + x = [self.coordinates[node1-1][0], self.coordinates[node2-1][0]] + y = [self.coordinates[node1-1][1], self.coordinates[node2-1][1]] + z = [self.coordinates[node1-1][2], self.coordinates[node2-1][2]] + ax.plot(x, y, z, 'k--', label="Original" if i == 0 else "") # Add label only once + + # Add labels and title + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.set_title(f'Forma modal nº: {mode_idx + 1}') + ax.legend() # Ensure the legend is displayed + ax.set_zlim([-0.5,2]) + ax.set_ylim([-0.5,1.5]) + plt.tight_layout() + plt.show() + + def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): + """ + Generates plots of deformations and stiffness values for each element based on the given forces. + + This function calls `shape_fun` to calculate torsional, axial, and flexural deformations, as well as stiffness values. + It then plots these results across subplots to visualize the behavior of each element under the applied forces. + + Parameters: + F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). + F_flexao2 (float): Force applied for distributed flexural deformation. + F_axial (float): Axial force applied to the elements. + F_torcao (float): Torsional force applied to the elements. + + Plots: + 1. Torsional deformation for each element. + 2. Axial deformation for each element. + 3. Flexural deformation due to point load for each element. + 4. Flexural deformation due to distributed load for each element. + 5. Combined flexural deformation for each element. + 6. Flexural and torsional stiffness for each element. + + Notes: + - Total flexural stiffness (KF_total) and torsional stiffness (KT_total) are displayed in the overall plot title. + - The function uses subplots to organize the visuals, and the layout is adjusted for clarity. + """ + torcao,deformacao_axial,flexao1,flexao2,flexao3,KF_total,KT_total,KF_elements,KT_elements= self.shape_fun(F_flexao1, F_flexao2, F_axial, F_torcao) + + # Configuração dos subplots + fig, axs = plt.subplots(6, 1, figsize=(12, 22)) + + # Plot da Torção + axs[0].plot(torcao, 'o-', label=[f'Força {F}N' for F in F_torcao]) + axs[0].set_title('Deformação por Torção de cada Elemento') + axs[0].set_xlabel('Elemento') + axs[0].set_ylabel('Torção (rad)') + axs[0].legend() + + # Plot da Deformação Axial + axs[1].plot(deformacao_axial, 's-', label=[f'Força {F}N' for F in F_axial]) + axs[1].set_title('Deformação Axial de cada Elemento') + axs[1].set_xlabel('Elemento') + axs[1].set_ylabel('Deformação (m)') + axs[1].legend() + + # Plot da Flexão por Carga Pontual + axs[2].plot(flexao1, 'o-', label=[f'Força {F}N' for F in F_flexao1]) + axs[2].set_title('Deformação por Carga Pontual de cada Elemento') + axs[2].set_xlabel('Elemento') + axs[2].set_ylabel('Deflexão (m)') + axs[2].legend() + + # Plot da Flexão por Carga Distribuída + axs[3].plot(flexao2, 'o-', label=[f'Força {F}N' for F in F_flexao2]) + axs[3].set_title('Deformação por Carga Distribuída de cada Elemento') + axs[3].set_xlabel('Elemento') + axs[3].set_ylabel('Deflexão (m)') + axs[3].legend() + + # Plot da Flexão Mista + axs[4].plot(flexao3, 'o-', label='Carregamento misto') + axs[4].set_title('Deformação por Flexão Mista de cada Elemento') + axs[4].set_xlabel('Elemento') + axs[4].set_ylabel('Deflexão (m)') + axs[4].legend() + + # Plot da Rigidez Flexional e Torsional por Elemento + axs[5].plot(KF_elements, 'o-', label='Rigidez Flexional (KF)') + axs[5].plot(KT_elements, 's-', label='Rigidez Torsional (KT)') + axs[5].set_title('Rigidez Flexional e Torsional de cada Elemento') + axs[5].set_xlabel('Elemento') + axs[5].set_ylabel('Rigidez (N/m)') + axs[5].legend() + + # Mostrando os totais no título geral + plt.suptitle(f'KF Total: {KF_total:.2e} N/m, KT Total: {KT_total:.2e} N/m', fontsize=16) + + # Ajustes de layout + plt.tight_layout(rect=[0, 0, 1, 0.96]) + plt.show() + print(f'KF Total: {KF_total:.2e} N/m \nKT Total: {KT_total:.2e} N/m') + + def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): + """ + Executes the complete structural analysis workflow, including mass matrix generation, + node and connectivity matrices, deformation visualization, modal analysis, and static analysis. + + Parameters: + F_flexao1 (array): Forces due to bending in one direction, applied to the structure. + F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. + F_axial (array): Axial forces applied to the structure. + F_torcao (array): Torsional forces applied to the structure. + """ + self.mass() + #Gerar as matrizes de localização dos nós e de conectividade + self.node_loc_matrix() + self.connect_matrix() + # Plotando o gráfico 3D da estrutura + self.structure_plot() + self.matrizes_global() + + #Plotando os resultados das deformações + self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) + + #Gerar autovalores, autovetores e frequências naturais + autovalores, autovetores, frequencias = self.modal_analysis() + self.modal_analysis_plot() + #Exibindo as frequências naturais e modos de vibração da estrutura + print("\\n Frequências Naturais (ω) da estrutura:") + print(frequencias) + F_global = np.zeros(self.K_global.size) # Force vector + F_global[2+5*6] = 100 + F_global[2+5*9] = -50 + fixed_dofs = [0, 1, 2, 3, 4, 5] + + # Perform analysis + displacements = estrutura.static_analysis(self.K_global,F_global, fixed_dofs) + print("Displacement Vector:", displacements) + estrutura.plot_colored_wireframe(displacements) + strain=self.compute_strain(displacements) + print("Strain:",strain) + #print(strain.size) + stress = self.compute_stress(strain,210e9,0.8) + print(stress) + +#nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" +#elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Elements.csv" + +nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nos_final.csv" +elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Conectividade_final.csv" + +# Carregar o arquivo para inspecionar seu conteúdo +nodes = pd.read_csv(nodes_file_path) +element_data = pd.read_csv(elements_file_path) + +# Selecionando as colunas de conectividades e propriedades dos elementos + +elements = element_data[['Element ID','Node a', 'Node b']] +element_properties = element_data[['Element ID','A', 'I', 'J', 'E', 'G','X']] + + + +#Atribuir forças +F_flexao1 = np.array([1000, 2000, 3000, 4000, 5000]) +F_flexao2 = np.array([1000, 1000, 1000, 1000, 1000]) +F_axial = np.array([1000, 2000, 3000, 4000, 5000]) +F_torcao = np.array([1000, 2000, 3000, 4000, 5000]) + +#Inicializando a Estrutura +estrutura = Estrutura(elements, element_properties, nodes, 8.33e-6, 8.33e-6) +estrutura.run(F_flexao1, F_flexao2, F_axial, F_torcao) + + + +""" + +Estrutura.plot_colored_wireframe(nodes, elements, torcao/(np.max(np.max(torcao)))) +Estrutura.plot_colored_wireframe(nodes, elements, deformacao_axial) +Estrutura.plot_colored_wireframe(nodes, elements, flexao1) +Estrutura.plot_colored_wireframe(nodes, elements, flexao2) +Estrutura.plot_colored_wireframe(nodes, elements, flexao3) + +""" \ No newline at end of file From bac2b182e1f1bcabd64615b63c75b89109e33f5e Mon Sep 17 00:00:00 2001 From: EduardoAMXango <167939544+EduardoAMXango@users.noreply.github.com> Date: Fri, 21 Feb 2025 19:34:24 -0300 Subject: [PATCH 25/25] Add files via upload --- Codigo_Estrutura_Suposta_Matriz_B.py | 1616 +++++++++++++------------- 1 file changed, 808 insertions(+), 808 deletions(-) diff --git a/Codigo_Estrutura_Suposta_Matriz_B.py b/Codigo_Estrutura_Suposta_Matriz_B.py index 119e174..3ae8546 100644 --- a/Codigo_Estrutura_Suposta_Matriz_B.py +++ b/Codigo_Estrutura_Suposta_Matriz_B.py @@ -1,809 +1,809 @@ -import numpy as np -from scipy.linalg import eigh -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d import Axes3D -from scipy.sparse import lil_matrix -from scipy.sparse.linalg import spsolve -import pandas as pd -import sys -import os -np.set_printoptions(threshold=sys.maxsize) -np.set_printoptions(linewidth=200, suppress=True) - -class Estrutura: - def __init__(self, elements,element_properties, nodes, Id, Ip): - """ - Initializes the structure with elements, nodes, and physical properties. - Inputs: - - elements: connectivity matrix between nodes (tuples of node indices). - - nodes: node coordinates (Nx3 array, where N is the number of nodes). - - m: total mass of the system (float). - - Id: directional moment of inertia (float). - - Ip: planar moment of inertia (float). - Outputs: None. - """ - - self.elements = elements # Matrix of connected elements - self.num_elements = len(elements) # Number of elements - self.element_properties = element_properties # Matrix of element properties - self.nodes = nodes # Matrix of nodes with their positions - self.coordinates = np.array(nodes[['x', 'y', 'z']]) # Coordinates of the nodes - self.connections = np.array(elements[['Node a', 'Node b']]) # Connections between nodes (elements) - self.num_nodes = len(nodes) # Total number of nodes - self.car_mass = 0 # Mass of the vehicle (kg) - self.moment_of_inertia_direction = Id # Moment of inertia in a specific direction (kg.m^2) - self.moment_of_inertia_plane = Ip # Moment of inertia in the plane (kg.m^2) - self.num_dofs_per_node = 6 # Degrees of freedom per node - self.num_dofs = self.num_nodes * self.num_dofs_per_node # Total number of degrees of freedom - self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global stiffness matrix - self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global mass matrix - self.num_modes = 1 # Number of vibration modes to return - - def node_loc_matrix(self): - """ - Creates a matrix with node locations for visualization. - Inputs: - - node_tags: list of node identifiers. - - node_coord: matrix of node coordinates. - Outputs: None. - """ - print(self.nodes) - - def connect_matrix(self): - """ - Generates and prints the connectivity matrix of elements. - Inputs: None (uses class attributes). - Outputs: None. - """ - print(self.elements) - - def calcular_comprimento(self, index): - """ - Creates a matrix with node locations for visualization. - Inputs: - - node_tags: list of node identifiers. - - node_coord: matrix of node coordinates. - Outputs: None. - """ - - node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) - x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] - x2, y2, z2 = self.nodes['x'][node2], self.nodes['y'][node2], self.nodes['z'][node2] - return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) - - def mass(self): - """ - Calculate the mass of the entire structure. - - Parameters: - None (uses class attributes): - - K_global (array): Global stiffness matrix of the structure. - - M_global (array): Global mass matrix of the structure. - - num_modes (int): Number of modes to retain in the analysis. - - outputs: - - self.car_mass (float): Mass of the entire structure - """ - for index in range(self.num_elements): - L_e = self.calcular_comprimento(index) - X = self.element_properties['X'][index] - A = self.element_properties['A'][index] - rho = 7870 # kg/m^3 - raio_externo = np.sqrt(A/np.pi) - raio_interno = raio_externo - X - volume = np.pi*L_e* (raio_externo**2 - raio_interno**2) - self.car_mass+= volume*rho - print(f"{self.car_mass.round(2) } kg") - - def element(self, index): - """ - Computes the element stiffness and mass matrices. - Inputs: - - element: tuple (start node index, end node index). - Outputs: - - k_e: element stiffness matrix. - - m_e: element mass matrix. - """ - #Tomando as propriedades de cada elemento - E = self.element_properties['E'][index] - A = self.element_properties['A'][index] - I = self.element_properties['I'][index] - J = self.element_properties['J'][index] - G = self.element_properties['G'][index] - - kappa=0.9 #Fator de correção para cisalhamento - - L_e = self.calcular_comprimento(index) - Phi = (12 * E * I) / (kappa * G * A * L_e**2) - rho = 7850 # kg/m^3 - c1 = E * A / L_e - c2 = G * J / L_e - c3 = E * I / L_e**3 #Euler-Bernoulli - c4 = (E*I)/(L_e**3*(1+Phi)) #Timoshenko - t1 = (4+Phi) - t2 = (2-Phi) - d1 = rho*A*L_e - d2 = (I*L_e)/6 - d3 = (rho*A*L_e)/420 - - # Matriz de Rigidez Elementar (Euler-Bernoulli) - # Para converter para timoshenko basta trocar c3 por c4,onde tem (4 * L_e**2 * c3) substitui por (t1* L_e**2 * c4) e onde tiver (2 * L_e**2 * c3) por (t2* L_e**2 * c4)) - k_e= np.array([ - [12 * c4, 0, 0, 6 * L_e * c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4, 0, 0], - [0, c1, 0, 0, 0, 0, 0, -c1, 0, 0, 0, 0], - [0, 0, 12 * c4, 0, 0, 6 * L_e* c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4], - [6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0], - [0, 0, 0, 0, c2, 0, 0, 0, 0, 0, -c2, 0], - [0, 0, 6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2 * L_e**2 * c4], - [-12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4, 0, 0], - [0, -c1, 0, 0, 0, 0, 0, c1, 0, 0, 0, 0], - [0, 0, -12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4], - [6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0], - [0, 0, 0, 0, -c2, 0, 0, 0, 0, 0, c2, 0], - [0, 0, 6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4] - ]) - - # Matriz de Massa Elementar - m_e= np.array([ - [156 * d3, 0, 0, 22 * L_e * d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3, 0, 0], - [0,2*d1, 0, 0, 0, 0, 0, d1, 0, 0, 0, 0], - [0, 0, 156 * d3, 0, 0, 22 * L_e* d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3], - [22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0], - [0, 0, 0, 0, 2*d2, 0, 0, 0, 0, 0, d2, 0], - [0, 0, 22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3], - [54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156* d3, 0, 0, -22 * L_e * d3, 0, 0], - [0, d1, 0, 0, 0, 0, 0, 2*d1, 0, 0, 0, 0], - [0, 0, 54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156 * d3, 0, 0, -22 * L_e * d3], - [-13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0], - [0, 0, 0, 0, d2, 0, 0, 0, 0, 0, 2*d2, 0], - [0, 0, -13 * L_e * d3, 0, 0,-3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3] - ]) - return k_e,m_e - - def B_matrix(self,index): - """ - Calculates the B-matrix for a 3D Timoshenko beam element. - Inputs: - - element: tuple (start node index, end node index). - Outputs: - - B: B-matrix for the element. - """ - L_e = self.calcular_comprimento(index) # Comprimento do elemento - x = L_e / 2 # Ponto médio do elemento (ou outro ponto de interesse) - - # Derivadas das funções de forma para deslocamentos axiais - dN1_dx = -1 / L_e - dN2_dx = 1 / L_e - - # Derivadas das funções de forma para deslocamentos transversais e rotações - dN3_dx = (-6 * x / L_e**2) + (6 * x**2 / L_e**3) - dN5_dx = (6 * x / L_e**2) - (6 * x**2 / L_e**3) - - # Montagem da B-matriz para Timoshenko - B = np.array([ - [dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0, 0, 0, 0, 0], # Extensão axial - [0, -dN3_dx, 0, 0, 0, 0, 0, -dN5_dx, 0, 0, 0, 0], # Curvatura em y - [0, 0, -dN3_dx, 0, 0, 0, 0, 0, -dN5_dx, 0, 0, 0], # Curvatura em z - [0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0, 0], # Torção - [0, 0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0], # Cisalhamento em y - [0, 0, 0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx], # Cisalhamento em z - [0, dN3_dx, 0, 0, 0, 0, 0, dN5_dx, 0, 0, 0, 0], # Deformação por cisalhamento em xy - [0, 0, dN3_dx, 0, 0, 0, 0, 0, dN5_dx, 0, 0, 0] # Deformação por cisalhamento em xz - ]) - - return B - - def aplicar_engastes(self, nodes, dofs): - """ - Applies constraints (fixed DOFs) on specific nodes. - Inputs: - - nodes: list of node indices to be constrained. - - dofs: list of degrees of freedom to be fixed. - Outputs: None. - """ - for node in nodes: #Laço para selecionar cada nó que será engastado - for dof in dofs: #Laço para selecionar quais graus de liberdade serão fixados - index = node * self.num_dofs_per_node + dof #Identificação da entrada da matriz que precisa ser restringida pelo engaste - self.K_global[index, index] = 10**10 # Um valor suficientemente grande para simular um engaste - - def matrizes_global(self): - """ - Assembles the global stiffness and mass matrices. - Inputs: None (uses class attributes). - Outputs: - - K_global: global stiffness matrix. - - M_global: global mass matrix. - """ - #Calculando as matrizes de rigidez e massa de cada elemento - for index in range(self.num_elements): - - k_e, m_e= self.element(index) - node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) - # DOFs associados ao elemento - dofs = [6 * node1, 6 * node1 + 1, 6 * node1 + 2, 6 * node1 + 3, 6 * node1 + 4, 6 * node1 + 5, - 6 * node2, 6 * node2 + 1, 6 * node2 + 2, 6 * node2 + 3, 6 * node2 + 4, 6 * node2 + 5] - - # Atualizando as matrizes globais - self.K_global[np.ix_(dofs, dofs)] += k_e - self.M_global[np.ix_(dofs, dofs)] += m_e - - #self.aplicar_engastes([0, 2, 4, 5], [0, 1, 2, 3, 4, 5]) #Por enquanto não estaremos considerando engastes - pd.DataFrame(self.K_global).to_csv('Matriz_Global_Rigidez.csv', index=True, header=True) - pd.DataFrame(self.M_global).to_csv('Matriz_Global_Massa.csv', index=True, header=True) - - plt.figure(figsize=(6, 6)) - plt.spy(self.K_global, markersize=10) # Adjust markersize for visibility - plt.title("Spy Plot of the Kg") - plt.xlabel("Columns") - plt.ylabel("Rows") - plt.grid(True, which="both", linestyle="--", linewidth=0.5) - plt.show() - - plt.figure(figsize=(6, 6)) - plt.spy(self.M_global, markersize=10) # Adjust markersize for visibility - plt.title("Spy Plot of the Mg") - plt.xlabel("Columns") - plt.ylabel("Rows") - plt.grid(True, which="both", linestyle="--", linewidth=0.5) - plt.show() - - - return self.K_global,self.M_global - - def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): - """ - Calculates deformations and stiffness of elements under loads. - Inputs: - - F_flex1: array of point bending forces. - - F_flex2: array of distributed bending forces. - - F_axial: array of axial forces. - - F_torsion: array of torsion forces. - Outputs: - - Arrays of torsion, deformations, and stiffness (bending and torsional). - """ - KF_total = 0 - KT_total = 0 - KF_elements = [] - KT_elements = [] - torcao, deformacao, flexao1, flexao2, flexao3 = [], [], [], [], [] - for index in range(len(self.elements)): - E = self.element_properties['E'][index] - A = self.element_properties['A'][index] - I = self.element_properties['I'][index] - J = self.element_properties['J'][index] - G = self.element_properties['G'][index] - L_e = self.calcular_comprimento(index) - # Equação de torsão - torcao_val = (F_torcao * L_e) / (G * J) #Fonte[1] - torcao.append(torcao_val) - # Equação para deformação axial - deformacao_val = (F_axial* L_e / (A * E)) #Fonte[2] - deformacao.append(deformacao_val) - # Equação para flexão - flexao_val1 = (F_flexao1*L_e**3)/(48 * E * I) #Fonte[3.1] (carga pontual no meio do elemento biapoiado) - flexao_val2 = (5*F_flexao2*L_e**4)/(384 * E * I) #Fonte[3.2] (carga distribuída ao longo de todo o elemento biapoiado) - flexao_val3 = flexao_val1 + flexao_val2 #Fonte[3.3] (tentativa de carregamento misto) - flexao1.append(flexao_val1) - flexao2.append(flexao_val2) - flexao3.append(flexao_val3) - - # Rigidez flexional - KF = E * I / L_e - # Rigidez torsional - KT = G * J / L_e - - KF_total += KF - KT_total += KT - - KF_elements.append(KF) - KT_elements.append(KT) - - return (np.array(torcao), np.array(deformacao), np.array(flexao1), - np.array(flexao2), np.array(flexao3), KF_total, KT_total, KF_elements, KT_elements) - - def modal_analysis(self): - """ - Performs modal analysis to compute natural frequencies and mode shapes. - Inputs: None. - Outputs: - - eigenvalues: eigenvalues (squared natural frequencies). - - eigenvectors: eigenvectors (mode shapes). - - frequencies: natural frequencies (Hz). - """ - # Análise modal por resolução do problema de autovalor e autovetor - unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) - - # Frequências naturais (raiz quadrada dos autovalores) - unsorted_frequencies = np.sqrt(unsorted_eigenvalues) / (2 * np.pi) # Divisão por 2*pi para converter para hertz - - # Tratando os dados (tomando apenas as 20 primeiras frequências naturais) - sorted_indices = np.argsort(unsorted_frequencies) # Ordena as frequências em ordem crescente - top_indices = sorted_indices[:self.num_modes] # Seleciona os índices dos primeiros n modos - - eigenvalues = np.array(unsorted_eigenvalues)[top_indices] # Filtra os primeiros n autovalores - eigenvectors = np.array(unsorted_eigenvectors)[:, top_indices] # Filtra os primeiros n autovetores - frequencies = np.array(unsorted_frequencies)[top_indices] # Filtra as primeiras n frequências - - return eigenvalues, eigenvectors, frequencies - - def static_analysis(self,K_global, F_global, fixed_dofs): - """ - Perform static analysis by solving Ku = F with boundary conditions. - - Parameters: - K_global (ndarray): Global stiffness matrix (N x N). - F_global (ndarray): Global force vector (N). - fixed_dofs (list): List of DOF indices to be fixed. - - Returns: - displacements (ndarray): Displacement vector (N). - - Resolve uma análise estática para deslocamentos em DOFs livres. - Entradas: - - K_global: matriz de rigidez global. - - F_global: vetor de forças globais. - - fixed_dofs: índices de graus de liberdade fixos. - Saídas: - - displacements: vetor de deslocamentos nos DOFs. - """ - # Total number of DOFs - n_dofs = K_global.shape[0] - - # Create a mask for free DOFs (DOFs not constrained) - free_dofs = np.array([i for i in range(n_dofs) if i not in fixed_dofs]) - - # Reduce the stiffness matrix and force vector - K_reduced = K_global[np.ix_(free_dofs, free_dofs)] - F_reduced = F_global[free_dofs] - - # Solve for displacements at free DOFs - # USE "linalg.lstsq" FOR NEAR SINGULAR MATRICES (ALL OF THEM) - u_reduced = np.linalg.lstsq(K_reduced, F_reduced, rcond=None)[0] # Get only the solution vector - - # Construct full displacement vector - displacements = np.zeros(n_dofs) - - displacements[free_dofs] = u_reduced - - return displacements - - def compute_strain(self, displacements): - """ - Compute strains for all elements. - - Parameters: - displacements (ndarray): Displacement vector for all nodes. - B_matrices (list of ndarray): Strain-displacement matrices for each element. - - Returns: - strains (list of ndarray): Strain tensors for all elements. - """ - strains = [] - for index in range(self.num_elements): - B = self.B_matrix(index) # Certifique-se de que o nome da função está correto - node1, node2 = int(self.elements["Node a"][index] - 1), int(self.elements["Node b"][index] - 1) - - # DOFs associados ao elemento - dofs = [6 * node1, 6 * node1 + 1, 6 * node1 + 2, 6 * node1 + 3, 6 * node1 + 4, 6 * node1 + 5, - 6 * node2, 6 * node2 + 1, 6 * node2 + 2, 6 * node2 + 3, 6 * node2 + 4, 6 * node2 + 5] - - u_reduced = displacements[dofs] # Seleciona os deslocamentos associados aos DOFs - - strain = np.dot(B, u_reduced) # B-matrix vezes o vetor de deslocamento - - strains.append(strain) - - return np.array(strains) - - def compute_stress(self,strains, E, nu): - """ - Compute stresses for all elements using Hooke's law. - - Parameters: - strains (list of ndarray): Strain tensors for all elements. - E (float): Young's modulus. - nu (float): Poisson's ratio. - - Returns: - stresses (list of ndarray): Stress tensors for all elements. - """ - # Construct constitutive matrix (isotropic 3D elasticity) - lambda_ = (E * nu) / ((1 + nu) * (1 - 2 * nu)) - G = E / (2 * (1 + nu)) - C = np.array([ - [lambda_ + 2*G , lambda_ , lambda_ , 0, 0, 0], - [lambda_ , lambda_ + 2*G , lambda_ , 0, 0, 0], - [lambda_ , lambda_ , lambda_ + 2*G , 0, 0, 0], - [ 0, 0, 0, G, 0, 0], - [ 0, 0, 0, 0, G, 0], - [ 0, 0, 0, 0, 0, G] - ]) - - stresses = [] - for strain in strains: - stress = np.dot(C, strain) # Hooke's law: C times strain - stresses.append(stress) - return stresses - - def compute_von_mises(self,B_matrices,F_global, fixed_dofs, E, nu): - """ - Compute von Mises stress for all elements. - - Parameters: - stresses (list of ndarray): Stress tensors for all elements. - - Returns: - von_mises_stresses (list of float): Von Mises stress for each element. - """ - stresses = self.compute_stress(B_matrices,F_global, fixed_dofs, E, nu) - von_mises_stresses = [] - for stress in stresses: - sigma_xx, sigma_yy, sigma_zz, tau_xy, tau_yz, tau_zx = stress - von_mises = np.sqrt( - 0.5 * ( - (sigma_xx - sigma_yy)**2 + - (sigma_yy - sigma_zz)**2 + - (sigma_zz - sigma_xx)**2 + - 6 * (tau_xy**2 + tau_yz**2 + tau_zx**2) - ) - ) - von_mises_stresses.append(von_mises) - return von_mises_stresses - - def Mesh(self): - """ - Generates a `.geo` file for the structure mesh in GMSH. - Inputs: None (uses class attributes and user-provided file name). - Outputs: None. - """ - - filename = input("Insira o nome do arquivo: ") + ".geo" - diretorio = input("Insira o diretorio onde o arquivo .geo deve ser salvo: ") - - if not os.path.exists(diretorio): - os.makedirs(diretorio) - - filepath = os.path.join(diretorio, filename) - - with open(filepath, 'w') as geo_file: - for i, (x, y, z) in enumerate(self.nodes): - geo_file.write(f'Point({i + 1}) = {{{x}, {y}, {z}, 1.0}};\n') - - for i, (start, end) in enumerate(self.elements): - geo_file.write(f'Line({i + 1}) = {{{start + 1}, {end + 1}}};\n') - - if len(self.elements) > 2: - line_loop_indices = ', '.join(str(i + 1) for i in range(len(elements))) - geo_file.write(f'Line Loop(1) = {{{line_loop_indices}}};\n') - geo_file.write('Plane Surface(1) = {1};\n') - - geo_file.write('Mesh.Algorithm = 6;\n') - geo_file.write('Mesh.ElementOrder = 1;\n') - geo_file.write('Mesh.Format = 1;\n') - - print(f'O arquivo foi salvo em: {filepath}, basta abrir o GMSH, e abrir o arquivo') - - def structure_plot(self): - """ - Plots a 3D wireframe of the structure. - - Parameters: - coordinates (array): Array of node coordinates (N x 3). - connections (list): List of tuples defining connections between nodes. - """ - - # Plotando o gráfico 3D da estrutura - fig = plt.figure(figsize=(10, 8)) - ax = fig.add_subplot(111, projection='3d') - - # Adicionando os pontos (nós) - for i, (x, y, z) in enumerate(self.coordinates): - ax.scatter(x, y, z, color='b', s=50) - ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) - - # Adicionando as linhas de ligação entre os nós - for node1, node2 in self.connections: - x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] - y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] - z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] - ax.plot(x_coords, y_coords, z_coords, 'r-', marker='o') - - # Configurações adicionais do gráfico - ax.set_xlabel('X') - ax.set_ylabel('Y') - ax.set_zlabel('Z') - ax.set_xlim([-0.5,2.0]) - ax.set_ylim([-0.5,2.0]) - ax.set_zlim([-0.5,1.5]) - ax.set_title('Estrutura 3D do Chassi') - - plt.show() - - def plot_colored_wireframe(self,scalar_values, colormap='jet'): - """ - Plots a 3D wireframe of the structure with color mapping based on scalar values. - - Parameters: - nodes (array): Array of node coordinates (N x 3). - elements (list): List of tuples defining connections between nodes. - scalar_values (array): 1D array of scalar values (e.g., strain) at each node. - colormap (str): Colormap name for visualization. - """ - # Normalize scalar values to [0, 1] for colormap - norm = plt.Normalize(vmin=np.min(scalar_values), vmax=np.max(scalar_values)) - cmap = plt.get_cmap(colormap) - - # Create the plot - fig = plt.figure(figsize=(10, 8)) - ax = fig.add_subplot(111, projection='3d') - - # Adicionando os pontos (nós) - for i, (x, y, z) in enumerate(self.coordinates): - ax.scatter(x, y, z, color='b', s=50) - ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) - - # Adicionando as linhas de ligação entre os nós - for node1, node2 in self.connections: - x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] - y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] - z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] - ax.plot(x_coords, y_coords, z_coords, 'r-', marker='o') - # Get the scalar value for the midpoint of the element - scalar_midpoint = (scalar_values[node1-1] + scalar_values[node2-1]) / 2 - - # Map scalar value to color - color = cmap(norm(scalar_midpoint)) - - # Plot the line segment with the corresponding color - ax.plot(x_coords, y_coords, z_coords, color=color, linewidth=2) - - # Add a colorbar - mappable = plt.cm.ScalarMappable(cmap=cmap, norm=norm) - mappable.set_array(scalar_values) - cbar = plt.colorbar(mappable, ax=ax, orientation='vertical', shrink=0.8, pad=0.1) - cbar.set_label("Strain (or other variable)", fontsize=12) - - # Set axis labels and title - ax.set_xlabel('X') - ax.set_ylabel('Y') - ax.set_zlabel('Z') - ax.set_title('Wireframe with Scalar Color Mapping') - ax.set_ylim([-0.5,1.5]) - plt.tight_layout() - plt.show() - - def modal_analysis_plot(self): - """ - Plots the modal shapes of the structure in 3D, showing the deformed and original configurations. - - This function performs the following: - - Computes eigenvalues and eigenvectors using the modal analysis method. - - Extracts the translational displacements (x, y, z) for each node from the modal shapes. - - Scales and visualizes the deformed structure for each mode, overlaying it on the original structure. - - Annotates the nodes of both the original and deformed structures. - - Parameters: - None (uses class attributes): - - coordinates (array): Array of node coordinates (N x 3). - - connections (list): List of tuples defining element connections between nodes. - - modal_analysis (method): Computes eigenvalues and eigenvectors. - """ - autovalores, autovetores, _ = self.modal_analysis() - - for mode_idx in range(len(autovalores)): - mode_shape = autovetores[:, mode_idx] - displacements = np.zeros((len(self.coordinates), 3)) # Assuming we want to visualize x, y, z displacements only - - # Loop through nodes to extract the translations - for j, (x, y, z) in enumerate(self.coordinates): - # 6 DOFs per node: [u_x, u_y, u_z, theta_x, theta_y, theta_z] - dof_start = 6 * j # Start index of DOFs for node j - displacements[j, 0] = mode_shape[dof_start] # u_x - displacements[j, 1] = mode_shape[dof_start + 1] # u_y - displacements[j, 2] = mode_shape[dof_start + 2] # u_z - - # Scale displacements for plots - scale_factor = 1 # Adjust as needed - deformed_nodes = np.array(self.coordinates) + displacements * scale_factor - - # Plot deformed - fig = plt.figure(figsize=(10, 8)) - ax = fig.add_subplot(111, projection='3d') - - # Plot deformed structure - for i, (node1, node2) in enumerate(self.connections): - x = [deformed_nodes[node1-1][0], deformed_nodes[node2-1][0]] - y = [deformed_nodes[node1-1][1], deformed_nodes[node2-1][1]] - z = [deformed_nodes[node1-1][2], deformed_nodes[node2-1][2]] - ax.plot(x, y, z, 'r-', label="Deformed" if i == 0 else "") # Add label only once - - #Colocando a legenda dos nós no gráfico - for i, (x, y, z) in enumerate(self.coordinates): - ax.scatter(x, y, z, color='b', s=50) - ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) - - #Colocando a legenda dos nós após a deformação no gráfico - #for i, (x, y, z) in enumerate(deformed_nodes): - # ax.scatter(x, y, z, color='r', s=25) - # ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) - - # Plot original structure - for i, (node1, node2) in enumerate(self.connections): - x = [self.coordinates[node1-1][0], self.coordinates[node2-1][0]] - y = [self.coordinates[node1-1][1], self.coordinates[node2-1][1]] - z = [self.coordinates[node1-1][2], self.coordinates[node2-1][2]] - ax.plot(x, y, z, 'k--', label="Original" if i == 0 else "") # Add label only once - - # Add labels and title - ax.set_xlabel('X') - ax.set_ylabel('Y') - ax.set_zlabel('Z') - ax.set_title(f'Forma modal nº: {mode_idx + 1}') - ax.legend() # Ensure the legend is displayed - ax.set_zlim([-0.5,2]) - ax.set_ylim([-0.5,1.5]) - plt.tight_layout() - plt.show() - - def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): - """ - Generates plots of deformations and stiffness values for each element based on the given forces. - - This function calls `shape_fun` to calculate torsional, axial, and flexural deformations, as well as stiffness values. - It then plots these results across subplots to visualize the behavior of each element under the applied forces. - - Parameters: - F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). - F_flexao2 (float): Force applied for distributed flexural deformation. - F_axial (float): Axial force applied to the elements. - F_torcao (float): Torsional force applied to the elements. - - Plots: - 1. Torsional deformation for each element. - 2. Axial deformation for each element. - 3. Flexural deformation due to point load for each element. - 4. Flexural deformation due to distributed load for each element. - 5. Combined flexural deformation for each element. - 6. Flexural and torsional stiffness for each element. - - Notes: - - Total flexural stiffness (KF_total) and torsional stiffness (KT_total) are displayed in the overall plot title. - - The function uses subplots to organize the visuals, and the layout is adjusted for clarity. - """ - torcao,deformacao_axial,flexao1,flexao2,flexao3,KF_total,KT_total,KF_elements,KT_elements= self.shape_fun(F_flexao1, F_flexao2, F_axial, F_torcao) - - # Configuração dos subplots - fig, axs = plt.subplots(6, 1, figsize=(12, 22)) - - # Plot da Torção - axs[0].plot(torcao, 'o-', label=[f'Força {F}N' for F in F_torcao]) - axs[0].set_title('Deformação por Torção de cada Elemento') - axs[0].set_xlabel('Elemento') - axs[0].set_ylabel('Torção (rad)') - axs[0].legend() - - # Plot da Deformação Axial - axs[1].plot(deformacao_axial, 's-', label=[f'Força {F}N' for F in F_axial]) - axs[1].set_title('Deformação Axial de cada Elemento') - axs[1].set_xlabel('Elemento') - axs[1].set_ylabel('Deformação (m)') - axs[1].legend() - - # Plot da Flexão por Carga Pontual - axs[2].plot(flexao1, 'o-', label=[f'Força {F}N' for F in F_flexao1]) - axs[2].set_title('Deformação por Carga Pontual de cada Elemento') - axs[2].set_xlabel('Elemento') - axs[2].set_ylabel('Deflexão (m)') - axs[2].legend() - - # Plot da Flexão por Carga Distribuída - axs[3].plot(flexao2, 'o-', label=[f'Força {F}N' for F in F_flexao2]) - axs[3].set_title('Deformação por Carga Distribuída de cada Elemento') - axs[3].set_xlabel('Elemento') - axs[3].set_ylabel('Deflexão (m)') - axs[3].legend() - - # Plot da Flexão Mista - axs[4].plot(flexao3, 'o-', label='Carregamento misto') - axs[4].set_title('Deformação por Flexão Mista de cada Elemento') - axs[4].set_xlabel('Elemento') - axs[4].set_ylabel('Deflexão (m)') - axs[4].legend() - - # Plot da Rigidez Flexional e Torsional por Elemento - axs[5].plot(KF_elements, 'o-', label='Rigidez Flexional (KF)') - axs[5].plot(KT_elements, 's-', label='Rigidez Torsional (KT)') - axs[5].set_title('Rigidez Flexional e Torsional de cada Elemento') - axs[5].set_xlabel('Elemento') - axs[5].set_ylabel('Rigidez (N/m)') - axs[5].legend() - - # Mostrando os totais no título geral - plt.suptitle(f'KF Total: {KF_total:.2e} N/m, KT Total: {KT_total:.2e} N/m', fontsize=16) - - # Ajustes de layout - plt.tight_layout(rect=[0, 0, 1, 0.96]) - plt.show() - print(f'KF Total: {KF_total:.2e} N/m \nKT Total: {KT_total:.2e} N/m') - - def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): - """ - Executes the complete structural analysis workflow, including mass matrix generation, - node and connectivity matrices, deformation visualization, modal analysis, and static analysis. - - Parameters: - F_flexao1 (array): Forces due to bending in one direction, applied to the structure. - F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. - F_axial (array): Axial forces applied to the structure. - F_torcao (array): Torsional forces applied to the structure. - """ - self.mass() - #Gerar as matrizes de localização dos nós e de conectividade - self.node_loc_matrix() - self.connect_matrix() - # Plotando o gráfico 3D da estrutura - self.structure_plot() - self.matrizes_global() - - #Plotando os resultados das deformações - self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) - - #Gerar autovalores, autovetores e frequências naturais - autovalores, autovetores, frequencias = self.modal_analysis() - self.modal_analysis_plot() - #Exibindo as frequências naturais e modos de vibração da estrutura - print("\\n Frequências Naturais (ω) da estrutura:") - print(frequencias) - F_global = np.zeros(self.K_global.size) # Force vector - F_global[2+5*6] = 100 - F_global[2+5*9] = -50 - fixed_dofs = [0, 1, 2, 3, 4, 5] - - # Perform analysis - displacements = estrutura.static_analysis(self.K_global,F_global, fixed_dofs) - print("Displacement Vector:", displacements) - estrutura.plot_colored_wireframe(displacements) - strain=self.compute_strain(displacements) - print("Strain:",strain) - #print(strain.size) - stress = self.compute_stress(strain,210e9,0.8) - print(stress) - -#nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" -#elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Elements.csv" - -nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nos_final.csv" -elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Conectividade_final.csv" - -# Carregar o arquivo para inspecionar seu conteúdo -nodes = pd.read_csv(nodes_file_path) -element_data = pd.read_csv(elements_file_path) - -# Selecionando as colunas de conectividades e propriedades dos elementos - -elements = element_data[['Element ID','Node a', 'Node b']] -element_properties = element_data[['Element ID','A', 'I', 'J', 'E', 'G','X']] - - - -#Atribuir forças -F_flexao1 = np.array([1000, 2000, 3000, 4000, 5000]) -F_flexao2 = np.array([1000, 1000, 1000, 1000, 1000]) -F_axial = np.array([1000, 2000, 3000, 4000, 5000]) -F_torcao = np.array([1000, 2000, 3000, 4000, 5000]) - -#Inicializando a Estrutura -estrutura = Estrutura(elements, element_properties, nodes, 8.33e-6, 8.33e-6) -estrutura.run(F_flexao1, F_flexao2, F_axial, F_torcao) - - - -""" - -Estrutura.plot_colored_wireframe(nodes, elements, torcao/(np.max(np.max(torcao)))) -Estrutura.plot_colored_wireframe(nodes, elements, deformacao_axial) -Estrutura.plot_colored_wireframe(nodes, elements, flexao1) -Estrutura.plot_colored_wireframe(nodes, elements, flexao2) -Estrutura.plot_colored_wireframe(nodes, elements, flexao3) - +import numpy as np +from scipy.linalg import eigh +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D +from scipy.sparse import lil_matrix +from scipy.sparse.linalg import spsolve +import pandas as pd +import sys +import os +np.set_printoptions(threshold=sys.maxsize) +np.set_printoptions(linewidth=200, suppress=True) + +class Estrutura: + def __init__(self, elements,element_properties, nodes, Id, Ip): + """ + Initializes the structure with elements, nodes, and physical properties. + Inputs: + - elements: connectivity matrix between nodes (tuples of node indices). + - nodes: node coordinates (Nx3 array, where N is the number of nodes). + - m: total mass of the system (float). + - Id: directional moment of inertia (float). + - Ip: planar moment of inertia (float). + Outputs: None. + """ + + self.elements = elements # Matrix of connected elements + self.num_elements = len(elements) # Number of elements + self.element_properties = element_properties # Matrix of element properties + self.nodes = nodes # Matrix of nodes with their positions + self.coordinates = np.array(nodes[['x', 'y', 'z']]) # Coordinates of the nodes + self.connections = np.array(elements[['Node a', 'Node b']]) # Connections between nodes (elements) + self.num_nodes = len(nodes) # Total number of nodes + self.car_mass = 0 # Mass of the vehicle (kg) + self.moment_of_inertia_direction = Id # Moment of inertia in a specific direction (kg.m^2) + self.moment_of_inertia_plane = Ip # Moment of inertia in the plane (kg.m^2) + self.num_dofs_per_node = 6 # Degrees of freedom per node + self.num_dofs = self.num_nodes * self.num_dofs_per_node # Total number of degrees of freedom + self.K_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global stiffness matrix + self.M_global = np.zeros((len(nodes) * 6, len(nodes) * 6)) # Global mass matrix + self.num_modes = 1 # Number of vibration modes to return + + def node_loc_matrix(self): + """ + Creates a matrix with node locations for visualization. + Inputs: + - node_tags: list of node identifiers. + - node_coord: matrix of node coordinates. + Outputs: None. + """ + print(self.nodes) + + def connect_matrix(self): + """ + Generates and prints the connectivity matrix of elements. + Inputs: None (uses class attributes). + Outputs: None. + """ + print(self.elements) + + def calcular_comprimento(self, index): + """ + Creates a matrix with node locations for visualization. + Inputs: + - node_tags: list of node identifiers. + - node_coord: matrix of node coordinates. + Outputs: None. + """ + + node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) + x1, y1, z1 = self.nodes['x'][node1], self.nodes['y'][node1], self.nodes['z'][node1] + x2, y2, z2 = self.nodes['x'][node2], self.nodes['y'][node2], self.nodes['z'][node2] + return np.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) + + def mass(self): + """ + Calculate the mass of the entire structure. + + Parameters: + None (uses class attributes): + - K_global (array): Global stiffness matrix of the structure. + - M_global (array): Global mass matrix of the structure. + - num_modes (int): Number of modes to retain in the analysis. + + outputs: + - self.car_mass (float): Mass of the entire structure + """ + for index in range(self.num_elements): + L_e = self.calcular_comprimento(index) + X = self.element_properties['X'][index] + A = self.element_properties['A'][index] + rho = 7870 # kg/m^3 + raio_externo = np.sqrt(A/np.pi) + raio_interno = raio_externo - X + volume = np.pi*L_e* (raio_externo**2 - raio_interno**2) + self.car_mass+= volume*rho + print(f"{self.car_mass.round(2) } kg") + + def element(self, index): + """ + Computes the element stiffness and mass matrices. + Inputs: + - element: tuple (start node index, end node index). + Outputs: + - k_e: element stiffness matrix. + - m_e: element mass matrix. + """ + #Tomando as propriedades de cada elemento + E = self.element_properties['E'][index] + A = self.element_properties['A'][index] + I = self.element_properties['I'][index] + J = self.element_properties['J'][index] + G = self.element_properties['G'][index] + + kappa=0.9 #Fator de correção para cisalhamento + + L_e = self.calcular_comprimento(index) + Phi = (12 * E * I) / (kappa * G * A * L_e**2) + rho = 7850 # kg/m^3 + c1 = E * A / L_e + c2 = G * J / L_e + c3 = E * I / L_e**3 #Euler-Bernoulli + c4 = (E*I)/(L_e**3*(1+Phi)) #Timoshenko + t1 = (4+Phi) + t2 = (2-Phi) + d1 = rho*A*L_e + d2 = (I*L_e)/6 + d3 = (rho*A*L_e)/420 + + # Matriz de Rigidez Elementar (Euler-Bernoulli) + # Para converter para timoshenko basta trocar c3 por c4,onde tem (4 * L_e**2 * c3) substitui por (t1* L_e**2 * c4) e onde tiver (2 * L_e**2 * c3) por (t2* L_e**2 * c4)) + k_e= np.array([ + [12 * c4, 0, 0, 6 * L_e * c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4, 0, 0], + [0, c1, 0, 0, 0, 0, 0, -c1, 0, 0, 0, 0], + [0, 0, 12 * c4, 0, 0, 6 * L_e* c4, 0, 0, -12 * c4, 0, 0, 6 * L_e * c4], + [6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0], + [0, 0, 0, 0, c2, 0, 0, 0, 0, 0, -c2, 0], + [0, 0, 6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t2 * L_e**2 * c4], + [-12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4, 0, 0], + [0, -c1, 0, 0, 0, 0, 0, c1, 0, 0, 0, 0], + [0, 0, -12 * c4, 0, 0, -6 * L_e * c4, 0, 0, 12 * c4, 0, 0, -6 * L_e * c4], + [6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4, 0, 0], + [0, 0, 0, 0, -c2, 0, 0, 0, 0, 0, c2, 0], + [0, 0, 6 * L_e * c4, 0, 0, t2 * L_e**2 * c4, 0, 0, -6 * L_e * c4, 0, 0, t1* L_e**2 * c4] + ]) + + # Matriz de Massa Elementar + m_e= np.array([ + [156 * d3, 0, 0, 22 * L_e * d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3, 0, 0], + [0,2*d1, 0, 0, 0, 0, 0, d1, 0, 0, 0, 0], + [0, 0, 156 * d3, 0, 0, 22 * L_e* d3, 0, 0, 54 * d3, 0, 0, -13 * L_e * d3], + [22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0], + [0, 0, 0, 0, 2*d2, 0, 0, 0, 0, 0, d2, 0], + [0, 0, 22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0, 13 * L_e * d3, 0, 0, -3 * L_e**2 * d3], + [54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156* d3, 0, 0, -22 * L_e * d3, 0, 0], + [0, d1, 0, 0, 0, 0, 0, 2*d1, 0, 0, 0, 0], + [0, 0, 54 * d3, 0, 0, 13 * L_e * d3, 0, 0, 156 * d3, 0, 0, -22 * L_e * d3], + [-13 * L_e * d3, 0, 0, -3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3, 0, 0], + [0, 0, 0, 0, d2, 0, 0, 0, 0, 0, 2*d2, 0], + [0, 0, -13 * L_e * d3, 0, 0,-3 * L_e**2 * d3, 0, 0, -22 * L_e * d3, 0, 0, 4 * L_e**2 * d3] + ]) + return k_e,m_e + + def B_matrix(self,index): + """ + Calculates the B-matrix for a 3D Timoshenko beam element. + Inputs: + - element: tuple (start node index, end node index). + Outputs: + - B: B-matrix for the element. + """ + L_e = self.calcular_comprimento(index) # Comprimento do elemento + x = L_e / 2 # Ponto médio do elemento (ou outro ponto de interesse) + + # Derivadas das funções de forma para deslocamentos axiais + dN1_dx = -1 / L_e + dN2_dx = 1 / L_e + + # Derivadas das funções de forma para deslocamentos transversais e rotações + dN3_dx = (-6 * x / L_e**2) + (6 * x**2 / L_e**3) + dN5_dx = (6 * x / L_e**2) - (6 * x**2 / L_e**3) + + # Montagem da B-matriz para Timoshenko + B = np.array([ + [dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0, 0, 0, 0, 0], # Extensão axial + [0, -dN3_dx, 0, 0, 0, 0, 0, -dN5_dx, 0, 0, 0, 0], # Curvatura em y + [0, 0, -dN3_dx, 0, 0, 0, 0, 0, -dN5_dx, 0, 0, 0], # Curvatura em z + [0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0, 0], # Torção + [0, 0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx, 0], # Cisalhamento em y + [0, 0, 0, 0, 0, dN1_dx, 0, 0, 0, 0, 0, dN2_dx], # Cisalhamento em z + [0, dN3_dx, 0, 0, 0, 0, 0, dN5_dx, 0, 0, 0, 0], # Deformação por cisalhamento em xy + [0, 0, dN3_dx, 0, 0, 0, 0, 0, dN5_dx, 0, 0, 0] # Deformação por cisalhamento em xz + ]) + + return B + + def aplicar_engastes(self, nodes, dofs): + """ + Applies constraints (fixed DOFs) on specific nodes. + Inputs: + - nodes: list of node indices to be constrained. + - dofs: list of degrees of freedom to be fixed. + Outputs: None. + """ + for node in nodes: #Laço para selecionar cada nó que será engastado + for dof in dofs: #Laço para selecionar quais graus de liberdade serão fixados + index = node * self.num_dofs_per_node + dof #Identificação da entrada da matriz que precisa ser restringida pelo engaste + self.K_global[index, index] = 10**10 # Um valor suficientemente grande para simular um engaste + + def matrizes_global(self): + """ + Assembles the global stiffness and mass matrices. + Inputs: None (uses class attributes). + Outputs: + - K_global: global stiffness matrix. + - M_global: global mass matrix. + """ + #Calculando as matrizes de rigidez e massa de cada elemento + for index in range(self.num_elements): + + k_e, m_e= self.element(index) + node1 , node2 = int(self.elements["Node a"][index]-1) , int(self.elements["Node b"][index]-1) + # DOFs associados ao elemento + dofs = [6 * node1, 6 * node1 + 1, 6 * node1 + 2, 6 * node1 + 3, 6 * node1 + 4, 6 * node1 + 5, + 6 * node2, 6 * node2 + 1, 6 * node2 + 2, 6 * node2 + 3, 6 * node2 + 4, 6 * node2 + 5] + + # Atualizando as matrizes globais + self.K_global[np.ix_(dofs, dofs)] += k_e + self.M_global[np.ix_(dofs, dofs)] += m_e + + #self.aplicar_engastes([0, 2, 4, 5], [0, 1, 2, 3, 4, 5]) #Por enquanto não estaremos considerando engastes + pd.DataFrame(self.K_global).to_csv('Matriz_Global_Rigidez.csv', index=True, header=True) + pd.DataFrame(self.M_global).to_csv('Matriz_Global_Massa.csv', index=True, header=True) + + plt.figure(figsize=(6, 6)) + plt.spy(self.K_global, markersize=10) # Adjust markersize for visibility + plt.title("Spy Plot of the Kg") + plt.xlabel("Columns") + plt.ylabel("Rows") + plt.grid(True, which="both", linestyle="--", linewidth=0.5) + plt.show() + + plt.figure(figsize=(6, 6)) + plt.spy(self.M_global, markersize=10) # Adjust markersize for visibility + plt.title("Spy Plot of the Mg") + plt.xlabel("Columns") + plt.ylabel("Rows") + plt.grid(True, which="both", linestyle="--", linewidth=0.5) + plt.show() + + + return self.K_global,self.M_global + + def shape_fun(self, F_flexao1, F_flexao2, F_axial,F_torcao): + """ + Calculates deformations and stiffness of elements under loads. + Inputs: + - F_flex1: array of point bending forces. + - F_flex2: array of distributed bending forces. + - F_axial: array of axial forces. + - F_torsion: array of torsion forces. + Outputs: + - Arrays of torsion, deformations, and stiffness (bending and torsional). + """ + KF_total = 0 + KT_total = 0 + KF_elements = [] + KT_elements = [] + torcao, deformacao, flexao1, flexao2, flexao3 = [], [], [], [], [] + for index in range(len(self.elements)): + E = self.element_properties['E'][index] + A = self.element_properties['A'][index] + I = self.element_properties['I'][index] + J = self.element_properties['J'][index] + G = self.element_properties['G'][index] + L_e = self.calcular_comprimento(index) + # Equação de torsão + torcao_val = (F_torcao * L_e) / (G * J) #Fonte[1] + torcao.append(torcao_val) + # Equação para deformação axial + deformacao_val = (F_axial* L_e / (A * E)) #Fonte[2] + deformacao.append(deformacao_val) + # Equação para flexão + flexao_val1 = (F_flexao1*L_e**3)/(48 * E * I) #Fonte[3.1] (carga pontual no meio do elemento biapoiado) + flexao_val2 = (5*F_flexao2*L_e**4)/(384 * E * I) #Fonte[3.2] (carga distribuída ao longo de todo o elemento biapoiado) + flexao_val3 = flexao_val1 + flexao_val2 #Fonte[3.3] (tentativa de carregamento misto) + flexao1.append(flexao_val1) + flexao2.append(flexao_val2) + flexao3.append(flexao_val3) + + # Rigidez flexional + KF = E * I / L_e + # Rigidez torsional + KT = G * J / L_e + + KF_total += KF + KT_total += KT + + KF_elements.append(KF) + KT_elements.append(KT) + + return (np.array(torcao), np.array(deformacao), np.array(flexao1), + np.array(flexao2), np.array(flexao3), KF_total, KT_total, KF_elements, KT_elements) + + def modal_analysis(self): + """ + Performs modal analysis to compute natural frequencies and mode shapes. + Inputs: None. + Outputs: + - eigenvalues: eigenvalues (squared natural frequencies). + - eigenvectors: eigenvectors (mode shapes). + - frequencies: natural frequencies (Hz). + """ + # Análise modal por resolução do problema de autovalor e autovetor + unsorted_eigenvalues, unsorted_eigenvectors = eigh(self.K_global, self.M_global) + + # Frequências naturais (raiz quadrada dos autovalores) + unsorted_frequencies = np.sqrt(unsorted_eigenvalues) / (2 * np.pi) # Divisão por 2*pi para converter para hertz + + # Tratando os dados (tomando apenas as 20 primeiras frequências naturais) + sorted_indices = np.argsort(unsorted_frequencies) # Ordena as frequências em ordem crescente + top_indices = sorted_indices[:self.num_modes] # Seleciona os índices dos primeiros n modos + + eigenvalues = np.array(unsorted_eigenvalues)[top_indices] # Filtra os primeiros n autovalores + eigenvectors = np.array(unsorted_eigenvectors)[:, top_indices] # Filtra os primeiros n autovetores + frequencies = np.array(unsorted_frequencies)[top_indices] # Filtra as primeiras n frequências + + return eigenvalues, eigenvectors, frequencies + + def static_analysis(self,K_global, F_global, fixed_dofs): + """ + Perform static analysis by solving Ku = F with boundary conditions. + + Parameters: + K_global (ndarray): Global stiffness matrix (N x N). + F_global (ndarray): Global force vector (N). + fixed_dofs (list): List of DOF indices to be fixed. + + Returns: + displacements (ndarray): Displacement vector (N). + + Resolve uma análise estática para deslocamentos em DOFs livres. + Entradas: + - K_global: matriz de rigidez global. + - F_global: vetor de forças globais. + - fixed_dofs: índices de graus de liberdade fixos. + Saídas: + - displacements: vetor de deslocamentos nos DOFs. + """ + # Total number of DOFs + n_dofs = K_global.shape[0] + + # Create a mask for free DOFs (DOFs not constrained) + free_dofs = np.array([i for i in range(n_dofs) if i not in fixed_dofs]) + + # Reduce the stiffness matrix and force vector + K_reduced = K_global[np.ix_(free_dofs, free_dofs)] + F_reduced = F_global[free_dofs] + + # Solve for displacements at free DOFs + # USE "linalg.lstsq" FOR NEAR SINGULAR MATRICES (ALL OF THEM) + u_reduced = np.linalg.lstsq(K_reduced, F_reduced, rcond=None)[0] # Get only the solution vector + + # Construct full displacement vector + displacements = np.zeros(n_dofs) + + displacements[free_dofs] = u_reduced + + return displacements + + def compute_strain(self, displacements): + """ + Compute strains for all elements. + + Parameters: + displacements (ndarray): Displacement vector for all nodes. + B_matrices (list of ndarray): Strain-displacement matrices for each element. + + Returns: + strains (list of ndarray): Strain tensors for all elements. + """ + strains = [] + for index in range(self.num_elements): + B = self.B_matrix(index) # Certifique-se de que o nome da função está correto + node1, node2 = int(self.elements["Node a"][index] - 1), int(self.elements["Node b"][index] - 1) + + # DOFs associados ao elemento + dofs = [6 * node1, 6 * node1 + 1, 6 * node1 + 2, 6 * node1 + 3, 6 * node1 + 4, 6 * node1 + 5, + 6 * node2, 6 * node2 + 1, 6 * node2 + 2, 6 * node2 + 3, 6 * node2 + 4, 6 * node2 + 5] + + u_reduced = displacements[dofs] # Seleciona os deslocamentos associados aos DOFs + + strain = np.dot(B, u_reduced) # B-matrix vezes o vetor de deslocamento + + strains.append(strain) + + return np.array(strains) + + def compute_stress(self,strains, E, nu): + """ + Compute stresses for all elements using Hooke's law. + + Parameters: + strains (list of ndarray): Strain tensors for all elements. + E (float): Young's modulus. + nu (float): Poisson's ratio. + + Returns: + stresses (list of ndarray): Stress tensors for all elements. + """ + # Construct constitutive matrix (isotropic 3D elasticity) + lambda_ = (E * nu) / ((1 + nu) * (1 - 2 * nu)) + G = E / (2 * (1 + nu)) + C = np.array([ + [lambda_ + 2*G , lambda_ , lambda_ , 0, 0, 0], + [lambda_ , lambda_ + 2*G , lambda_ , 0, 0, 0], + [lambda_ , lambda_ , lambda_ + 2*G , 0, 0, 0], + [ 0, 0, 0, G, 0, 0], + [ 0, 0, 0, 0, G, 0], + [ 0, 0, 0, 0, 0, G] + ]) + + stresses = [] + for strain in strains: + stress = np.dot(C, strain) # Hooke's law: C times strain + stresses.append(stress) + return stresses + + def compute_von_mises(self,B_matrices,F_global, fixed_dofs, E, nu): + """ + Compute von Mises stress for all elements. + + Parameters: + stresses (list of ndarray): Stress tensors for all elements. + + Returns: + von_mises_stresses (list of float): Von Mises stress for each element. + """ + stresses = self.compute_stress(B_matrices,F_global, fixed_dofs, E, nu) + von_mises_stresses = [] + for stress in stresses: + sigma_xx, sigma_yy, sigma_zz, tau_xy, tau_yz, tau_zx = stress + von_mises = np.sqrt( + 0.5 * ( + (sigma_xx - sigma_yy)**2 + + (sigma_yy - sigma_zz)**2 + + (sigma_zz - sigma_xx)**2 + + 6 * (tau_xy**2 + tau_yz**2 + tau_zx**2) + ) + ) + von_mises_stresses.append(von_mises) + return von_mises_stresses + + def Mesh(self): + """ + Generates a `.geo` file for the structure mesh in GMSH. + Inputs: None (uses class attributes and user-provided file name). + Outputs: None. + """ + + filename = input("Insira o nome do arquivo: ") + ".geo" + diretorio = input("Insira o diretorio onde o arquivo .geo deve ser salvo: ") + + if not os.path.exists(diretorio): + os.makedirs(diretorio) + + filepath = os.path.join(diretorio, filename) + + with open(filepath, 'w') as geo_file: + for i, (x, y, z) in enumerate(self.nodes): + geo_file.write(f'Point({i + 1}) = {{{x}, {y}, {z}, 1.0}};\n') + + for i, (start, end) in enumerate(self.elements): + geo_file.write(f'Line({i + 1}) = {{{start + 1}, {end + 1}}};\n') + + if len(self.elements) > 2: + line_loop_indices = ', '.join(str(i + 1) for i in range(len(elements))) + geo_file.write(f'Line Loop(1) = {{{line_loop_indices}}};\n') + geo_file.write('Plane Surface(1) = {1};\n') + + geo_file.write('Mesh.Algorithm = 6;\n') + geo_file.write('Mesh.ElementOrder = 1;\n') + geo_file.write('Mesh.Format = 1;\n') + + print(f'O arquivo foi salvo em: {filepath}, basta abrir o GMSH, e abrir o arquivo') + + def structure_plot(self): + """ + Plots a 3D wireframe of the structure. + + Parameters: + coordinates (array): Array of node coordinates (N x 3). + connections (list): List of tuples defining connections between nodes. + """ + + # Plotando o gráfico 3D da estrutura + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Adicionando os pontos (nós) + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + # Adicionando as linhas de ligação entre os nós + for node1, node2 in self.connections: + x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] + y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] + z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] + ax.plot(x_coords, y_coords, z_coords, 'r-', marker='o') + + # Configurações adicionais do gráfico + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.set_xlim([-0.5,2.0]) + ax.set_ylim([-0.5,2.0]) + ax.set_zlim([-0.5,1.5]) + ax.set_title('Estrutura 3D do Chassi') + + plt.show() + + def plot_colored_wireframe(self,scalar_values, colormap='jet'): + """ + Plots a 3D wireframe of the structure with color mapping based on scalar values. + + Parameters: + nodes (array): Array of node coordinates (N x 3). + elements (list): List of tuples defining connections between nodes. + scalar_values (array): 1D array of scalar values (e.g., strain) at each node. + colormap (str): Colormap name for visualization. + """ + # Normalize scalar values to [0, 1] for colormap + norm = plt.Normalize(vmin=np.min(scalar_values), vmax=np.max(scalar_values)) + cmap = plt.get_cmap(colormap) + + # Create the plot + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Adicionando os pontos (nós) + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + # Adicionando as linhas de ligação entre os nós + for node1, node2 in self.connections: + x_coords = [self.coordinates[node1 - 1][0], self.coordinates[node2 - 1][0]] + y_coords = [self.coordinates[node1 - 1][1], self.coordinates[node2 - 1][1]] + z_coords = [self.coordinates[node1 - 1][2], self.coordinates[node2 - 1][2]] + ax.plot(x_coords, y_coords, z_coords, 'r-', marker='o') + # Get the scalar value for the midpoint of the element + scalar_midpoint = (scalar_values[node1-1] + scalar_values[node2-1]) / 2 + + # Map scalar value to color + color = cmap(norm(scalar_midpoint)) + + # Plot the line segment with the corresponding color + ax.plot(x_coords, y_coords, z_coords, color=color, linewidth=2) + + # Add a colorbar + mappable = plt.cm.ScalarMappable(cmap=cmap, norm=norm) + mappable.set_array(scalar_values) + cbar = plt.colorbar(mappable, ax=ax, orientation='vertical', shrink=0.8, pad=0.1) + cbar.set_label("Strain (or other variable)", fontsize=12) + + # Set axis labels and title + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.set_title('Wireframe with Scalar Color Mapping') + ax.set_ylim([-0.5,1.5]) + plt.tight_layout() + plt.show() + + def modal_analysis_plot(self): + """ + Plots the modal shapes of the structure in 3D, showing the deformed and original configurations. + + This function performs the following: + - Computes eigenvalues and eigenvectors using the modal analysis method. + - Extracts the translational displacements (x, y, z) for each node from the modal shapes. + - Scales and visualizes the deformed structure for each mode, overlaying it on the original structure. + - Annotates the nodes of both the original and deformed structures. + + Parameters: + None (uses class attributes): + - coordinates (array): Array of node coordinates (N x 3). + - connections (list): List of tuples defining element connections between nodes. + - modal_analysis (method): Computes eigenvalues and eigenvectors. + """ + autovalores, autovetores, _ = self.modal_analysis() + + for mode_idx in range(len(autovalores)): + mode_shape = autovetores[:, mode_idx] + displacements = np.zeros((len(self.coordinates), 3)) # Assuming we want to visualize x, y, z displacements only + + # Loop through nodes to extract the translations + for j, (x, y, z) in enumerate(self.coordinates): + # 6 DOFs per node: [u_x, u_y, u_z, theta_x, theta_y, theta_z] + dof_start = 6 * j # Start index of DOFs for node j + displacements[j, 0] = mode_shape[dof_start] # u_x + displacements[j, 1] = mode_shape[dof_start + 1] # u_y + displacements[j, 2] = mode_shape[dof_start + 2] # u_z + + # Scale displacements for plots + scale_factor = 1 # Adjust as needed + deformed_nodes = np.array(self.coordinates) + displacements * scale_factor + + # Plot deformed + fig = plt.figure(figsize=(10, 8)) + ax = fig.add_subplot(111, projection='3d') + + # Plot deformed structure + for i, (node1, node2) in enumerate(self.connections): + x = [deformed_nodes[node1-1][0], deformed_nodes[node2-1][0]] + y = [deformed_nodes[node1-1][1], deformed_nodes[node2-1][1]] + z = [deformed_nodes[node1-1][2], deformed_nodes[node2-1][2]] + ax.plot(x, y, z, 'r-', label="Deformed" if i == 0 else "") # Add label only once + + #Colocando a legenda dos nós no gráfico + for i, (x, y, z) in enumerate(self.coordinates): + ax.scatter(x, y, z, color='b', s=50) + ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + #Colocando a legenda dos nós após a deformação no gráfico + #for i, (x, y, z) in enumerate(deformed_nodes): + # ax.scatter(x, y, z, color='r', s=25) + # ax.text(x, y, z, f' {i+1}', color='black', fontsize=8) + + # Plot original structure + for i, (node1, node2) in enumerate(self.connections): + x = [self.coordinates[node1-1][0], self.coordinates[node2-1][0]] + y = [self.coordinates[node1-1][1], self.coordinates[node2-1][1]] + z = [self.coordinates[node1-1][2], self.coordinates[node2-1][2]] + ax.plot(x, y, z, 'k--', label="Original" if i == 0 else "") # Add label only once + + # Add labels and title + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_zlabel('Z') + ax.set_title(f'Forma modal nº: {mode_idx + 1}') + ax.legend() # Ensure the legend is displayed + ax.set_zlim([-0.5,2]) + ax.set_ylim([-0.5,1.5]) + plt.tight_layout() + plt.show() + + def shape_fun_plot(self, F_flexao1, F_flexao2, F_axial, F_torcao): + """ + Generates plots of deformations and stiffness values for each element based on the given forces. + + This function calls `shape_fun` to calculate torsional, axial, and flexural deformations, as well as stiffness values. + It then plots these results across subplots to visualize the behavior of each element under the applied forces. + + Parameters: + F_flexao1 (float): Force applied for flexural deformation (point load at mid-span). + F_flexao2 (float): Force applied for distributed flexural deformation. + F_axial (float): Axial force applied to the elements. + F_torcao (float): Torsional force applied to the elements. + + Plots: + 1. Torsional deformation for each element. + 2. Axial deformation for each element. + 3. Flexural deformation due to point load for each element. + 4. Flexural deformation due to distributed load for each element. + 5. Combined flexural deformation for each element. + 6. Flexural and torsional stiffness for each element. + + Notes: + - Total flexural stiffness (KF_total) and torsional stiffness (KT_total) are displayed in the overall plot title. + - The function uses subplots to organize the visuals, and the layout is adjusted for clarity. + """ + torcao,deformacao_axial,flexao1,flexao2,flexao3,KF_total,KT_total,KF_elements,KT_elements= self.shape_fun(F_flexao1, F_flexao2, F_axial, F_torcao) + + # Configuração dos subplots + fig, axs = plt.subplots(6, 1, figsize=(12, 22)) + + # Plot da Torção + axs[0].plot(torcao, 'o-', label=[f'Força {F}N' for F in F_torcao]) + axs[0].set_title('Deformação por Torção de cada Elemento') + axs[0].set_xlabel('Elemento') + axs[0].set_ylabel('Torção (rad)') + axs[0].legend() + + # Plot da Deformação Axial + axs[1].plot(deformacao_axial, 's-', label=[f'Força {F}N' for F in F_axial]) + axs[1].set_title('Deformação Axial de cada Elemento') + axs[1].set_xlabel('Elemento') + axs[1].set_ylabel('Deformação (m)') + axs[1].legend() + + # Plot da Flexão por Carga Pontual + axs[2].plot(flexao1, 'o-', label=[f'Força {F}N' for F in F_flexao1]) + axs[2].set_title('Deformação por Carga Pontual de cada Elemento') + axs[2].set_xlabel('Elemento') + axs[2].set_ylabel('Deflexão (m)') + axs[2].legend() + + # Plot da Flexão por Carga Distribuída + axs[3].plot(flexao2, 'o-', label=[f'Força {F}N' for F in F_flexao2]) + axs[3].set_title('Deformação por Carga Distribuída de cada Elemento') + axs[3].set_xlabel('Elemento') + axs[3].set_ylabel('Deflexão (m)') + axs[3].legend() + + # Plot da Flexão Mista + axs[4].plot(flexao3, 'o-', label='Carregamento misto') + axs[4].set_title('Deformação por Flexão Mista de cada Elemento') + axs[4].set_xlabel('Elemento') + axs[4].set_ylabel('Deflexão (m)') + axs[4].legend() + + # Plot da Rigidez Flexional e Torsional por Elemento + axs[5].plot(KF_elements, 'o-', label='Rigidez Flexional (KF)') + axs[5].plot(KT_elements, 's-', label='Rigidez Torsional (KT)') + axs[5].set_title('Rigidez Flexional e Torsional de cada Elemento') + axs[5].set_xlabel('Elemento') + axs[5].set_ylabel('Rigidez (N/m)') + axs[5].legend() + + # Mostrando os totais no título geral + plt.suptitle(f'KF Total: {KF_total:.2e} N/m, KT Total: {KT_total:.2e} N/m', fontsize=16) + + # Ajustes de layout + plt.tight_layout(rect=[0, 0, 1, 0.96]) + plt.show() + print(f'KF Total: {KF_total:.2e} N/m \nKT Total: {KT_total:.2e} N/m') + + def run(self,F_flexao1, F_flexao2, F_axial,F_torcao): + """ + Executes the complete structural analysis workflow, including mass matrix generation, + node and connectivity matrices, deformation visualization, modal analysis, and static analysis. + + Parameters: + F_flexao1 (array): Forces due to bending in one direction, applied to the structure. + F_flexao2 (array): Forces due to bending in a perpendicular direction, applied to the structure. + F_axial (array): Axial forces applied to the structure. + F_torcao (array): Torsional forces applied to the structure. + """ + self.mass() + #Gerar as matrizes de localização dos nós e de conectividade + self.node_loc_matrix() + self.connect_matrix() + # Plotando o gráfico 3D da estrutura + self.structure_plot() + self.matrizes_global() + + #Plotando os resultados das deformações + self.shape_fun_plot(F_flexao1, F_flexao2, F_axial,F_torcao) + + #Gerar autovalores, autovetores e frequências naturais + autovalores, autovetores, frequencias = self.modal_analysis() + self.modal_analysis_plot() + #Exibindo as frequências naturais e modos de vibração da estrutura + print("\\n Frequências Naturais (ω) da estrutura:") + print(frequencias) + F_global = np.zeros(self.K_global.size) # Force vector + F_global[2+5*6] = 100 + F_global[2+5*9] = -50 + fixed_dofs = [0, 1, 2, 3, 4, 5] + + # Perform analysis + displacements = estrutura.static_analysis(self.K_global,F_global, fixed_dofs) + print("Displacement Vector:", displacements) + estrutura.plot_colored_wireframe(displacements) + strain=self.compute_strain(displacements) + print("Strain:",strain) + #print(strain.size) + stress = self.compute_stress(strain,210e9,0.8) + print(stress) + +#nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Nodes.csv" +#elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nós e Elementos modelo de chassi básico - Elements.csv" + +nodes_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Nos_final.csv" +elements_file_path = "C:\\Users\\dudua\\OneDrive\\Documentos\\GitHub\\EduardoChassi\\Conectividade_final.csv" + +# Carregar o arquivo para inspecionar seu conteúdo +nodes = pd.read_csv(nodes_file_path) +element_data = pd.read_csv(elements_file_path) + +# Selecionando as colunas de conectividades e propriedades dos elementos + +elements = element_data[['Element ID','Node a', 'Node b']] +element_properties = element_data[['Element ID','A', 'I', 'J', 'E', 'G','X']] + + + +#Atribuir forças +F_flexao1 = np.array([1000, 2000, 3000, 4000, 5000]) +F_flexao2 = np.array([1000, 1000, 1000, 1000, 1000]) +F_axial = np.array([1000, 2000, 3000, 4000, 5000]) +F_torcao = np.array([1000, 2000, 3000, 4000, 5000]) + +#Inicializando a Estrutura +estrutura = Estrutura(elements, element_properties, nodes, 8.33e-6, 8.33e-6) +estrutura.run(F_flexao1, F_flexao2, F_axial, F_torcao) + + + +""" + +Estrutura.plot_colored_wireframe(nodes, elements, torcao/(np.max(np.max(torcao)))) +Estrutura.plot_colored_wireframe(nodes, elements, deformacao_axial) +Estrutura.plot_colored_wireframe(nodes, elements, flexao1) +Estrutura.plot_colored_wireframe(nodes, elements, flexao2) +Estrutura.plot_colored_wireframe(nodes, elements, flexao3) + """ \ No newline at end of file