III. Post-Procesamiento y Resultados 📊#
Una vez que hemos resuelto el sistema reducido \(\mathbf{K}_{LL} \mathbf{u}_L = \mathbf{f}_L\) y hemos encontrado los desplazamientos desconocidos (\(\mathbf{u}_L\)), el vector de desplazamientos global \(\mathbf{u}\) está completo (contiene \(\mathbf{u}_L\) y \(\mathbf{u}_R\)). La etapa de post-procesamiento utiliza este vector \(\mathbf{u}\) para calcular las fuerzas que son de interés estructural: las reacciones y las fuerzas internas de cada elemento.
1. Cálculo de Reacciones de Apoyo ↩️#
Las reacciones son las fuerzas que los soportes ejercen sobre la estructura para mantenerla en equilibrio. Estas fuerzas son incógnitas ubicadas en los Grados de Libertad (GDL) que fueron restringidos (\(\mathbf{u}_R\)), es decir, donde el desplazamiento es conocido (usualmente cero).
Desarrollo Matemático#
El vector de fuerzas global \(\mathbf{f}\) contiene tanto las cargas aplicadas en los GDL libres (\(\mathbf{f}_L\)) como las reacciones desconocidas en los GDL restringidos (\(\mathbf{f}_R\)).
Utilizamos la segunda fila del sistema de ecuaciones particionado (visto en la Sección II):
$$\mathbf{K}_{RL} \mathbf{u}_L + \mathbf{K}_{RR} \mathbf{u}_R = \mathbf{f}_R$$Donde:
- \(\mathbf{K}_{RL}\) y \(\mathbf{K}_{RR}\) son submatrices extraídas de la matriz \(\mathbf{K}\) ensamblada.
- \(\mathbf{u}_L\) es el vector de desplazamientos libres (calculados en la Sección II).
- \(\mathbf{u}_R\) es el vector de desplazamientos restringidos (conocidos, generalmente \(\mathbf{0}\)).
- \(\mathbf{f}_R\) es el vector de fuerzas totales en los GDL restringidos. Contiene las reacciones incógnitas \(\mathbf{R}\) (nuestro objetivo) más cualquier carga externa \(\mathbf{f}_R^{\text{ap}}\) aplicada directamente sobre un apoyo: \(\mathbf{f}_R = \mathbf{R} + \mathbf{f}_R^{\text{ap}}\).
Despejando las reacciones se obtiene la expresión general:
$$\mathbf{R} = \mathbf{K}_{RL} \mathbf{u}_L + \mathbf{K}_{RR} \mathbf{u}_R - \mathbf{f}_R^{\text{ap}}$$En el caso más común, los apoyos no tienen asentamientos (\(\mathbf{u}_R = \mathbf{0}\)) y no hay cargas aplicadas directamente sobre ellos (\(\mathbf{f}_R^{\text{ap}} = \mathbf{0}\)), por lo que la ecuación se simplifica a:
$$\mathbf{R} = \mathbf{K}_{RL} \mathbf{u}_L$$Conclusión: En el caso habitual, las reacciones son simplemente el producto de la submatriz \(\mathbf{K}_{RL}\) por los desplazamientos libres \(\mathbf{u}_L\). Si hay asentamientos o cargas sobre los apoyos, se debe usar la expresión general.
2. Cálculo de Fuerzas Internas y Esfuerzos 🛡️#
Las fuerzas internas son las tensiones (o compresiones) que cada elemento debe soportar.
Desarrollo Matemático#
El cálculo se realiza en el sistema de coordenadas local del elemento, ya que allí el estado de esfuerzo es axial puro.
- Obtener Desplazamientos Globales del Elemento (\(\mathbf{U}\)): Extraer los 4 componentes de \(\mathbf{u}\) global que corresponden a los nudos del elemento.
- Transformar a Desplazamientos Locales (\(\mathbf{u}'\)): Se utiliza la matriz de transformación \(\mathbf{T}\) (vista en la Sección I): $$\mathbf{u}' = \mathbf{T} \mathbf{U}$$
- Calcular la Deformación (\(\delta\)): Para el elemento de barra axial, la deformación total \(\delta\) es la diferencia de los desplazamientos axiales locales: $$\delta = u'_2 - u'_1$$
- Calcular la Fuerza Axial (\(P\)): Utilizando la Ley de Hooke aplicada a la barra: $$P = \frac{AE}{L} \cdot \delta$$ Un valor de \(P\) positivo indica tensión (tracción); un valor negativo indica compresión.
Cálculo de Esfuerzo (Stress)#
El esfuerzo \(\sigma\) es la fuerza por unidad de área, que es un valor crucial para el diseño:
$$\sigma = \frac{P}{A}$$3. Ejercicio Demostrativo en Python (Continuación) 💻#
Continuaremos con el ejercicio de la armadura simple de 2 barras de la Sección II, donde ya calculamos \(\mathbf{u}_L = [U_{2x}, U_{2y}]\).

Nudo 1 fijo (GDL 1, 2), Nudo 2 libre (GDL 3, 4) y Nudo 3 fijo (GDL 5, 6), con las cargas \(F_x, F_y\) aplicadas en el Nudo 2. En el código, estos GDL corresponden a los índices 0 a 5 (ver la convención de numeración de la Sección II).
Objetivos del Código:
- Reconstruir el vector \(\mathbf{u}\) global completo.
- Calcular las reacciones \(\mathbf{R}\) (GDL 1, 2, 5, 6).
- Calcular las fuerzas internas \(P_1\) y \(P_2\).
# --- Continuación del Ejercicio Demostrativo de la Sección II ---
import matplotlib.pyplot as plt
# Se asume que K (6x6), u_L, A, E, L1, L2, theta1_deg, theta2_deg ya están definidos.
# Índices de los GDL en el código (base 0)
gdl_libres = [2, 3]
gdl_restringidos = [0, 1, 4, 5]
# 1. Reconstrucción del Vector de Desplazamientos Global (u)
u = np.zeros(6)
# Colocamos los desplazamientos incógnitos (calculados previamente) en las posiciones libres
u[gdl_libres] = u_L
print("\n--- Desplazamientos Globales u ---")
print(u) # u = [0, 0, U_2x, U_2y, 0, 0]
# 2. Cálculo de Reacciones (R)
# Extraer submatrices necesarias: K_RL (4x2)
# K_RL: Filas restringidas, Columnas libres
K_RL = K[np.ix_(gdl_restringidos, gdl_libres)]
# u_R = 0 y no hay cargas sobre los apoyos, por lo que R = K_RL * u_L
R = K_RL @ u_L
print("\n--- Reacciones de Apoyo R (N) ---")
print(f"GDL 1 (R_1x): {R[0]:.2f} N")
print(f"GDL 2 (R_1y): {R[1]:.2f} N")
print(f"GDL 5 (R_3x): {R[2]:.2f} N")
print(f"GDL 6 (R_3y): {R[3]:.2f} N")
# 3. Cálculo de Fuerzas Internas (P)
def calcular_fuerza_interna(u, A, E, L, theta_deg, gdl_elem):
"""Calcula la fuerza axial P y el esfuerzo sigma para un elemento."""
# 3.1 Obtener sub-vector de desplazamientos globales (4x1)
U_elem = u[gdl_elem]
# 3.2 Matriz de Transformación (T)
theta_rad = np.radians(theta_deg)
c = np.cos(theta_rad)
s = np.sin(theta_rad)
T = np.array([
[c, s, 0, 0],
[0, 0, c, s]
])
# 3.3 Transformar a desplazamientos locales u' = T @ U
u_local = T @ U_elem
# 3.4 Calcular Deformación y Fuerza Axial
delta = u_local[1] - u_local[0] # delta = u'_2 - u'_1
P = (A * E / L) * delta
sigma = P / A
return P, sigma
# Barra 1 (índices 0, 1, 2, 3 = GDL 1, 2, 3, 4)
P1, sigma1 = calcular_fuerza_interna(u, A, E, L1, theta1_deg, [0, 1, 2, 3])
# Barra 2 (GDL: 4, 5, 2, 3 - El orden de Nudo Inicial/Final debe ser consistente con la definición de theta)
# Usamos el orden [4, 5, 2, 3] que corresponde a Nudo 3 (inicial) y Nudo 2 (final)
P2, sigma2 = calcular_fuerza_interna(u, A, E, L2, theta2_deg, [4, 5, 2, 3])
print("\n--- Fuerzas Internas y Esfuerzos ---")
print(f"Barra 1 (P1): {P1:.2f} N \t| Esfuerzo (sigma1): {sigma1:.2e} Pa")
print(f"Barra 2 (P2): {P2:.2f} N \t| Esfuerzo (sigma2): {sigma2:.2e} Pa")
# 4. Verificación del equilibrio global: reacciones + cargas = 0
print("\n--- Verificación de Equilibrio ---")
print(f"Suma Fx: {R[0] + R[2] + Fx:.2e} N")
print(f"Suma Fy: {R[1] + R[3] + Fy:.2e} N")
# 5. Gráfico de la estructura deformada
nudos = np.array([[0.0, 0.0], [L, H], [2*L, 0.0]]) # coordenadas (x, y) de cada nudo
barras = [(0, 1), (2, 1)] # nudos de cada barra (índices base 0)
escala = 500 # factor de amplificación de los desplazamientos
nudos_def = nudos + escala * u.reshape(-1, 2) # cada fila de u.reshape: (ux, uy) de un nudo
fig, ax = plt.subplots()
for (i, j), P in zip(barras, [P1, P2]):
ax.plot(*nudos[[i, j]].T, "k--", lw=1) # geometría original
color = "tab:red" if P > 0 else "tab:blue" # rojo: tracción, azul: compresión
ax.plot(*nudos_def[[i, j]].T, color=color, lw=3)
ax.set_aspect("equal")
ax.set_title(f"Deformada (amplificada x{escala}): rojo = tracción, azul = compresión")
ax.set_xlabel("x (m)")
ax.set_ylabel("y (m)")
plt.show()
Como ambas barras resultan en compresión, las dos se dibujan en azul. El factor escala es necesario porque los desplazamientos (décimas de milímetro) son miles de veces menores que las dimensiones de la estructura.

Con tu Módulo rigidez.py 🧰#
Agrega a tu rigidez.py las funciones de post-procesamiento:
reaccionesusa la expresión general \(\mathbf{R} = \mathbf{K}_{RL}\mathbf{u}_L + \mathbf{K}_{RR}\mathbf{u}_R - \mathbf{f}_R^{\text{ap}}\), escrita de forma compacta como las filas restringidas de \(\mathbf{K}\mathbf{u} - \mathbf{f}\).fuerzas_barrascalcula \(\delta = u'_2 - u'_1\) proyectando los desplazamientos de los nudos sobre el eje de cada barra (las filas de \(\mathbf{T}\)).graficar_armaduradibuja la estructura y, si se le entregan \(\mathbf{u}\) y \(P\), su deformada coloreada.
# ============================ Sección III ============================
def reacciones(K, u, f, gdl_restringidos):
"""Reacciones R = K_RL u_L + K_RR u_R - f_R^ap.
Es lo mismo que evaluar (K u - f) en los GDL restringidos.
"""
return (K @ u - f)[gdl_restringidos]
def fuerzas_barras(nudos, barras, A, E, u):
"""Fuerza axial P de cada barra de una armadura 2D (positiva en tracción)."""
A = np.broadcast_to(A, len(barras))
E = np.broadcast_to(E, len(barras))
P = np.zeros(len(barras))
for b, ((i, j), A_e, E_e) in enumerate(zip(barras, A, E)):
L, c, s = geometria_barra(nudos[i], nudos[j])
ux1, uy1, ux2, uy2 = u[gdl_barra(i, j)]
delta = (ux2 - ux1)*c + (uy2 - uy1)*s # u'_2 - u'_1
P[b] = A_e * E_e / L * delta
return P
def graficar_armadura(nudos, barras, u=None, P=None, escala=1.0, ax=None):
"""Dibuja una armadura 2D, numerando los nudos desde 1.
Si se entrega u, dibuja también la deformada (desplazamientos multiplicados
por escala). Si además se entrega P, colorea las barras deformadas:
rojo = tracción, azul = compresión.
"""
nudos = np.asarray(nudos, dtype=float)
if ax is None:
_, ax = plt.subplots()
for b, (i, j) in enumerate(barras):
ax.plot(*nudos[[i, j]].T, "k--" if u is not None else "k-", lw=1)
if u is not None:
deformada = nudos + escala * np.reshape(u, (-1, 2))
color = "k" if P is None else ("tab:red" if P[b] > 0 else "tab:blue")
ax.plot(*deformada[[i, j]].T, color=color, lw=3)
for n, (x, y) in enumerate(nudos, start=1):
ax.annotate(str(n), (x, y), textcoords="offset points", xytext=(5, 5))
ax.set_aspect("equal")
ax.set_xlabel("x (m)")
ax.set_ylabel("y (m)")
return ax
Con el módulo, el análisis completo de la armadura de dos barras queda así:
import numpy as np
import matplotlib.pyplot as plt
from rigidez import ensamblar_armadura, resolver, reacciones, fuerzas_barras, graficar_armadura
nudos = [(0, 0), (4, 3), (8, 0)]
barras = [(0, 1), (2, 1)]
A, E = 0.001, 210e9
gdl_restringidos = [0, 1, 4, 5]
K = ensamblar_armadura(nudos, barras, A, E)
f = np.zeros(6)
f[[2, 3]] = [5000, -10000]
u = resolver(K, f, gdl_restringidos)
R = reacciones(K, u, f, gdl_restringidos)
P = fuerzas_barras(nudos, barras, A, E, u)
print("Reacciones R1x, R1y, R3x, R3y (N):", R)
print("Fuerzas axiales P1, P2 (N):", P)
graficar_armadura(nudos, barras, u, P, escala=500)
plt.show()
4. Ejercicios Propuestos 📝#
- Concepto: ¿Cuál es el significado físico de una reacción \(R\) positiva en la dirección \(Y\)?
- Partición: Si en un sistema de 8 GDL los GDL 1, 2, 5 y 6 están restringidos, ¿cuáles son las dimensiones de \(\mathbf{K}_{LL}\), \(\mathbf{K}_{RL}\) y \(\mathbf{K}_{RR}\)?
- Cálculo de reacciones: Si \(\mathbf{K}_{RL} = \begin{pmatrix} 10 & 0 \\ 0 & 20 \end{pmatrix}\) y \(\mathbf{u}_L = \begin{pmatrix} 0.003 \\ -0.001 \end{pmatrix}\) (unidades compatibles), sin asentamientos ni cargas sobre los apoyos, calcula el vector de reacciones \(\mathbf{R}\).
- Cálculo rápido: Una barra tiene \(AE/L = 10 \times 10^6 \, \text{N/m}\). Sus nudos se desplazan, en la dirección axial local, \(u'_1 = 0.005 \, \text{m}\) y \(u'_2 = 0.007 \, \text{m}\). Calcula \(P\) e indica si la barra está en tracción o compresión.
- Transformación: Escribe \(\delta = u'_2 - u'_1\) en función de los desplazamientos globales para una barra horizontal (\(\theta = 0^\circ\)) y para una vertical (\(\theta = 90^\circ\)). ¿Por qué, en general, no se puede calcular \(\delta\) directamente con los desplazamientos globales?
- Programación: Calcula
K @ ucon los resultados del ejercicio demostrativo. ¿Qué valores aparecen en los GDL libres? ¿Y en los restringidos? Explica por qué. - Programación: El código verifica \(\sum F_x = 0\) y \(\sum F_y = 0\). Agrega la verificación de la suma de momentos respecto al Nudo 1 (\(\sum M_1 = 0\)).
- Asentamiento: Continúa el ejercicio 9 de la Sección II (asentamiento de \(-0.005\) m en el Nudo 3). Calcula las reacciones con la expresión general \(\mathbf{R} = \mathbf{K}_{RL} \mathbf{u}_L + \mathbf{K}_{RR} \mathbf{u}_R - \mathbf{f}_R^{\text{ap}}\) y verifica el equilibrio. ¿Qué ocurre si olvidas el término \(\mathbf{K}_{RR} \mathbf{u}_R\)? Compara con las reacciones sin asentamiento: ¿por qué no cambian? (Pista: la armadura es isostática).
- Concepto: Si las reacciones calculadas no satisfacen \(\sum F_x = 0\) o \(\sum F_y = 0\), ¿en qué etapas del proceso matricial buscarías el error? Ordénalas de la más a la menos probable.
- Diseño: ¿Qué resultado (desplazamientos \(\mathbf{u}\), reacciones \(\mathbf{R}\) o fuerzas internas \(P\)) usarías para dimensionar los pernos de anclaje de los apoyos? ¿Y para verificar que una barra no se pandee? Justifica.
5. Problema Integrador 🏗️#
Este problema reúne todo lo visto en las Secciones I, II y III. Escribe un programa completo que use tu módulo rigidez.py para analizar la siguiente armadura:
Nudos (coordenadas en metros):
| Nudo | \(x\) | \(y\) | Apoyo |
|---|---|---|---|
| 1 | 0 | 0 | Fijo (\(U_x = U_y = 0\)) |
| 2 | 4 | 0 | — |
| 3 | 8 | 0 | Rodillo (\(U_y = 0\)) |
| 4 | 2 | 3 | — |
| 5 | 6 | 3 | — |
Barras: 1-2, 2-3, 1-4, 2-4, 2-5, 3-5 y 4-5. Todas con \(A = 0.002 \, \text{m}^2\) y \(E = 200 \, \text{GPa}\).
Cargas: \(F_y = -30 \, \text{kN}\) en el Nudo 2 y \(F_x = +10 \, \text{kN}\) en el Nudo 4.
Se pide:
- Dibuja la armadura para verificar los datos (puedes usar
graficar_armadurasin el argumentou). - Ensambla \(\mathbf{K}\) (\(10 \times 10\)), aplica los apoyos y resuelve los desplazamientos.
- Calcula las reacciones y verifica el equilibrio global.
- Calcula la fuerza axial y el esfuerzo de cada barra, indicando si está en tracción o compresión.
- Grafica la deformada amplificada, coloreando las barras según su estado.
- Verificación independiente: como la armadura es isostática (7 barras + 3 reacciones = 10 GDL), resuelve a mano con el método de los nudos (o de las secciones) la fuerza en las barras 1-2 y 4-5 y compáralas con tu programa.
Resultados para autoverificación:
| Resultado | Valor esperado |
|---|---|
| Reacciones \(R_{1x}\), \(R_{1y}\), \(R_{3y}\) | \(-10.00\) kN, \(11.25\) kN, \(18.75\) kN |
| Desplazamiento del Nudo 2 \((U_x, U_y)\) | \((0.175, -0.657)\) mm |
| Fuerza en las barras 1-2 y 2-3 | \(+17.50\) kN y \(+12.50\) kN |
| Fuerza en la barra 4-5 | \(-25.00\) kN |