A continuación se transcriben tres ejercicios propuestos en español a partir de los resueltos en Solar_Dryers-APENDICES.pdf. Cada ejercicio va acompañado de un checklist breve para guiar a los estudiantes en GitHub Codespaces, siguiendo la estructura solicitada.
Un secador solar se utiliza para deshidratar 100 kg de pimientos frescos con 80% de humedad en base húmeda (bh) hasta alcanzar 5% bh en 3 días. El área efectiva del colector es de 15 m², con un flujo de aire de 0,5 m³/s. La insolación media es de 20 MJ/m² por día de 12 horas y la temperatura ambiente promedio es 25 °C con 70% de humedad relativa. La temperatura media de entrada al secador es de 35 °C.
Pregunta: Calcule la eficiencia de secado del sistema y la eficiencia de pick-up del secador.
En un secador solar tipo chimenea se desea secar arroz. El aire ambiente a 25 °C y 60% HR se calienta a 40 °C. La cámara de secado tiene 0,6 m de altura y se encuentra a 1,0 m del suelo. Se requiere un flujo de aire de 5,5 mm/s a través de una capa de arroz de 0,2 m de espesor.
Preguntas:
Un secador solar forzado de 2 m × 2 m × 1,5 m de profundidad se usa para secar 3 toneladas de cereal con densidad aparente de 780 kg/m³. Se sabe que la resistencia al flujo de aire es de 325 Pa por metro de profundidad.
Preguntas:
Creación del repositorio
solar_dryers_practica
..gitignore
para Python.Iniciar GitHub Codespaces
Instalar extensiones para Python
Crear entorno virtual
python -m venv .venv
source .venv/bin/activate # Linux/Mac
.venv\Scripts\activate # Windows
Archivos con ayuda del chatbot
README.md
: descripción del proyecto y ejercicios.requirements.txt
: incluir numpy
,
pandas
, matplotlib
,
streamlit
.Instalación de módulos
pip install -r requirements.txt
Desplegar en Streamlit
Crear archivo app.py
que muestre cálculo paso a paso
con interfaz.
Ejecutar localmente:
streamlit run app.py
Alojar en Streamlit Community Cloud
main
y archivo de inicio
app.py
.¿Desea que redacte también un modelo de app.py
en Python con Streamlit para cada ejercicio, de modo que los
estudiantes solo tengan que adaptarlo?
Aquí tienes un modelo de aplicación Streamlit multipágina
(una sola app.py
) para que tus estudiantes
trabajen los tres ejercicios en GitHub Codespaces.
Incluye funciones bien documentadas, supuestos editables y cálculos paso
a paso.
La app implementa tres módulos:
Eficiencia de secado y “pick-up”
Altura de chimenea en convección natural
Flujo y potencia de ventilador en secado forzado
Cada sección trae valores pre-cargados según tu planteamiento. Los alumnos pueden modificarlos, observar el impacto y citar supuestos que adopten.
app.py
(Streamlit, listo para Codespaces)# app.py
# Aplicación Streamlit con 3 secciones:
# 1) Eficiencia de secado y "pick-up"
# 2) Chimenea: altura requerida por convección natural
# 3) Secado forzado: caudal y potencia de ventilador
#
# Autoría: plantilla docente para práctica en GitHub Codespaces
# Requisitos: streamlit, numpy, pandas
import math
import numpy as np
import streamlit as st
# -----------------------------
# Utilidades psicrométricas
# -----------------------------
def sat_vapor_pressure_Pa(T_C: float) -> float:
"""
Presión de vapor de saturación (Pa) usando fórmula tipo Tetens/Magnus.
Válida ~0–50 °C para propósitos didácticos.
Es: esat(kPa) = 0.61078*exp(17.2694*T/(T+237.3)) => Pa multiplicando por 1000.
"""
esat_kPa = 0.61078 * math.exp((17.2694 * T_C) / (T_C + 237.3))
return esat_kPa * 1000.0
def humidity_ratio(Pv_Pa: float, P_atm_Pa: float = 101325.0) -> float:
"""
Razón de humedad (omega) en kg_vapor/kg_aire_seco.
omega = 0.62198 * Pv / (P - Pv)
"""
return 0.62198 * Pv_Pa / max(P_atm_Pa - Pv_Pa, 1e-6)
def partial_pressure_from_T_RH(T_C: float, RH_frac: float) -> float:
"""
Presión parcial de vapor a partir de T (°C) y HR (0-1).
"""
esat = sat_vapor_pressure_Pa(T_C)
return RH_frac * esat
def latent_heat_kJ_per_kg(T_C: float) -> float:
"""
Calor latente de vaporización aproximado en kJ/kg.
Aproximación lineal en torno a 0–100 °C: Lv ≈ 2501 - 2.361*T(°C)
"""
return 2501.0 - 2.361 * T_C
# -----------------------------
# Encabezado
# -----------------------------
st.set_page_config(page_title="Secadores Solares - Práctica", layout="wide")
st.title("Práctica: Secadores Solares en GitHub Codespaces")
st.markdown("""
Esta app implementa modelos sencillos para **tres ejercicios**:
1) Eficiencia de secado y *pick-up*
2) Altura de chimenea en convección natural
3) Caudal y potencia de ventilador en secado forzado
> Nota: Los modelos son **didácticos**. Ajuste supuestos y documente las decisiones en el `README.md`.
""")
# -----------------------------
# Sección 1: Eficiencia y pick-up
# -----------------------------
st.header("1) Eficiencia de secado y 'pick-up'")
with st.expander("Parámetros del problema", expanded=True):
col1, col2, col3 = st.columns(3)
with col1:
Mi = st.number_input("Masa inicial producto, Mi [kg]", 1.0, 1e6, 100.0, step=1.0)
wi_bh = st.number_input("Humedad inicial (base húmeda) wi", 0.0, 0.999, 0.80, step=0.01, format="%.2f")
wf_bh = st.number_input("Humedad final (base húmeda) wf", 0.0, 0.999, 0.05, step=0.01, format="%.2f")
dias = st.number_input("Días de secado", 0.1, 60.0, 3.0, step=0.5)
with col2:
A = st.number_input("Área efectiva del colector A [m²]", 0.1, 1e4, 15.0, step=0.5)
insol_MJ_m2_d = st.number_input("Insolación media [MJ/m²·día]", 0.1, 40.0, 20.0, step=0.5, format="%.1f")
horas_sol = st.number_input("Horas solares efectivas por día", 1.0, 24.0, 12.0, step=1.0)
T_aire_secado = st.number_input("T de secado promedio [°C]", 0.0, 80.0, 35.0, step=0.5)
with col3:
Q_m3_s = st.number_input("Caudal de aire [m³/s]", 0.001, 10.0, 0.5, step=0.01)
T_amb = st.number_input("T ambiente [°C]", -5.0, 60.0, 25.0, step=0.5)
RH_amb = st.number_input("HR ambiente [%]", 0.0, 100.0, 70.0, step=1.0) / 100.0
P_atm = st.number_input("Presión atmosférica [Pa]", 50000.0, 120000.0, 101325.0, step=100.0)
# Cálculos de masa de agua removida
Md = Mi * (1.0 - wi_bh) # masa seca
Mf = Md / max(1.0 - wf_bh, 1e-6) # masa final
m_agua = Mi - Mf # agua removida
# Energía mínima para evaporar
Lv = latent_heat_kJ_per_kg(T_aire_secado) # kJ/kg
E_req_kJ = m_agua * Lv
E_req_MJ = E_req_kJ / 1000.0
# Energía solar disponible
H_solar_MJ = insol_MJ_m2_d * A * dias
# Eficiencia global idealizada
eta = E_req_MJ / H_solar_MJ if H_solar_MJ > 0 else np.nan
# Pick-up efficiency psicrométrica
# Humidity ratio de entrada
Pv_in = partial_pressure_from_T_RH(T_amb, RH_amb)
w_in = humidity_ratio(Pv_in, P_atm)
# Cálculo de masa de aire seco que atravesó el sistema (aprox. densidad 1.2 kg/m3)
rho_air = 1.2 # kg/m³ (aproximado)
t_seg = dias * horas_sol * 3600.0
m_da = rho_air * Q_m3_s * t_seg # masa aire total [kg] ~ aire húmedo ~ aire seco
# Incremento de razón de humedad por balance
delta_w = m_agua / max(m_da, 1e-9)
w_out = w_in + delta_w
# Límite por saturación a T_secado
w_sat = humidity_ratio(sat_vapor_pressure_Pa(T_aire_secado), P_atm)
pickup = (w_out - w_in) / max(w_sat - w_in, 1e-9)
pickup = float(np.clip(pickup, 0.0, 1.0))
# Resultados
colA, colB, colC, colD = st.columns(4)
colA.metric("Agua removida [kg]", f"{m_agua:,.2f}")
colB.metric("Energía mínima [MJ]", f"{E_req_MJ:,.1f}")
colC.metric("Energía solar disp. [MJ]", f"{H_solar_MJ:,.1f}")
colD.metric("Eficiencia η [-]", f"{eta:.3f}")
colE, colF, colG = st.columns(3)
colE.metric("ω_in [kg/kg_da]", f"{w_in:.4f}")
colF.metric("ω_out [kg/kg_da]", f"{w_out:.4f}")
colG.metric("Pick-up ε [-]", f"{pickup:.3f}")
st.caption("Supuestos clave: Lv(T) aproximado, densidad aire = 1.2 kg/m³, balance en horas solares efectivas.")
# -----------------------------
# Sección 2: Chimenea natural
# -----------------------------
st.header("2) Altura de chimenea por convección natural")
with st.expander("Parámetros del problema", expanded=True):
c1, c2, c3 = st.columns(3)
with c1:
Ta = st.number_input("T ambiente [°C]", -5.0, 60.0, 25.0, step=0.5, key="Ta2")
Ti = st.number_input("T aire de secado [°C]", 10.0, 80.0, 40.0, step=0.5, key="Ti2")
Cd = st.number_input("Coef. descarga Cd [-]", 0.2, 1.0, 0.65, step=0.01)
with c2:
u_obj = st.number_input("Velocidad objetivo en lecho [m/s]", 0.0001, 0.5, 0.0055, step=0.0001, format="%.4f")
rho_i = st.number_input("Densidad aire interno [kg/m³]", 0.5, 1.5, 1.15, step=0.01)
g = 9.81
with c3:
# Alturas de referencia para escenarios ±1/3
pass
# Modelo de tiro térmico simplificado:
# ΔP ≈ ρ_a g H ((Ti - Ta)/(Ta_K))
# u ≈ Cd * sqrt(2*ΔP/ρ_i) -> despejar H para u objetivo
Ta_K = Ta + 273.15
Ti_K = Ti + 273.15
def H_requerida(u_target, Ta_K, Ti_K, rho_i, Cd, rho_a=1.2, g=9.81):
deltaP_factor = rho_a * g * ((Ti_K - Ta_K) / Ta_K) # Pa por metro
if deltaP_factor <= 0 or Cd <= 0:
return np.nan
# u = Cd * sqrt(2 * (deltaP_factor * H) / rho_i)
# H = (u^2 * rho_i) / (2 * Cd^2 * deltaP_factor)
H = (u_target**2 * rho_i) / (2.0 * (Cd**2) * deltaP_factor)
return H
H_req = H_requerida(u_obj, Ta_K, Ti_K, rho_i, Cd)
colh1, colh2, colh3 = st.columns(3)
colh1.metric("Altura chimenea H requerida [m]", f"{H_req:.2f}" if np.isfinite(H_req) else "No aplica")
# Escenarios: H±1/3, y clima nublado Ti=30 °C
H_plus = H_req * (4.0/3.0) if np.isfinite(H_req) else np.nan
H_minus = H_req * (2.0/3.0) if np.isfinite(H_req) else np.nan
def u_from_H(H, Ta_K, Ti_K, rho_i, Cd, rho_a=1.2, g=9.81):
deltaP = rho_a * g * H * ((Ti_K - Ta_K) / Ta_K)
if deltaP <= 0 or Cd <= 0:
return np.nan
return Cd * math.sqrt(max(2.0 * deltaP / rho_i, 0.0))
u_plus = u_from_H(H_plus, Ta_K, Ti_K, rho_i, Cd) if np.isfinite(H_plus) else np.nan
u_minus = u_from_H(H_minus, Ta_K, Ti_K, rho_i, Cd) if np.isfinite(H_minus) else np.nan
colh2.metric("u con H aumentado 1/3 [m/s]", f"{u_plus:.4f}" if np.isfinite(u_plus) else "NA")
colh3.metric("u con H reducido 1/3 [m/s]", f"{u_minus:.4f}" if np.isfinite(u_minus) else "NA")
Ti_nub = 30.0
u_nublado = u_from_H(H_req, Ta_K, Ti_nub + 273.15, rho_i, Cd) if np.isfinite(H_req) else np.nan
st.metric("u con clima nublado (Ti=30 °C) [m/s]", f"{u_nublado:.4f}" if np.isfinite(u_nublado) else "NA")
st.caption("Modelo idealizado. En diseño real considere pérdidas adicionales, pérdidas de carga localizadas y sección de chimenea.")
# -----------------------------
# Sección 3: Forzado: Q y Potencia
# -----------------------------
st.header("3) Secador forzado: caudal y potencia del ventilador")
with st.expander("Parámetros del problema", expanded=True):
d1, d2, d3 = st.columns(3)
with d1:
L = st.number_input("Largo cámara [m]", 0.1, 100.0, 2.0, step=0.1)
W = st.number_input("Ancho cámara [m]", 0.1, 100.0, 2.0, step=0.1)
Prof = st.number_input("Profundidad cámara [m]", 0.1, 20.0, 1.5, step=0.1)
with d2:
masa_grano = st.number_input("Masa de cereal [kg]", 10.0, 1e6, 3000.0, step=10.0)
dens_bulk = st.number_input("Densidad aparente [kg/m³]", 100.0, 2000.0, 780.0, step=10.0)
k_Pa_por_m = st.number_input("Resistencia al flujo [Pa/m]", 10.0, 5000.0, 325.0, step=5.0)
with d3:
u_superf = st.number_input("Velocidad superficial objetivo [m/s]", 0.001, 0.5, 0.02, step=0.001, format="%.3f")
eta_mec = st.number_input("Eficiencia mecánica ventilador [-]", 0.05, 1.0, 0.60, step=0.01)
P_atm3 = P_atm # sólo informativo
A_sec = L * W # m²
Q = A_sec * u_superf # m³/s
# Geometría del lecho
V_grano = masa_grano / dens_bulk # m³
prof_lecho = V_grano / max(A_sec, 1e-9)
# Pérdida de presión total (en el lecho dominante)
deltaP = k_Pa_por_m * prof_lecho # Pa
P_eje_W = (deltaP * Q) / max(eta_mec, 1e-6)
c3a, c3b, c3c, c3d = st.columns(4)
c3a.metric("Área sección [m²]", f"{A_sec:.2f}")
c3b.metric("Caudal Q [m³/s]", f"{Q:.3f}")
c3c.metric("ΔP estimada [Pa]", f"{deltaP:.0f}")
c3d.metric("Potencia eje [W]", f"{P_eje_W:.1f}")
st.caption("La velocidad superficial objetivo es un **parámetro de diseño**. Ajuste según literatura del producto y tamaño de partícula.")
# -----------------------------
# Pie de página: guía de uso
# -----------------------------
st.markdown("""
---
### Guía mínima de uso en Codespaces
1. Cree el repo y abra un Codespace.
2. Instale extensiones: **Python**, **Pylance**, **Streamlit**.
3. Cree `.venv` y active.
4. `pip install -r requirements.txt`
5. `streamlit run app.py`
6. Documente supuestos y resultados en `README.md`.
""")
requirements.txt
sugeridostreamlit>=1.36
numpy>=1.26
pandas>=2.2
solar_dryers_practica/
├─ app.py
├─ requirements.txt
├─ README.md
└─ .gitignore
En README.md
los estudiantes deben:
Repositorio: crear
solar_dryers_practica
con
.gitignore (Python)
.
Codespaces: abrir un Codespace nuevo desde el repo.
Extensiones: instalar Python, Pylance y Streamlit.
Entorno:
python -m venv .venv
source .venv/bin/activate # Linux/Mac
.venv\Scripts\activate # Windows (si aplica)
Archivos con el chatbot de Codespaces: redactar
README.md
; generar requirements.txt
.
Instalación:
pip install -r requirements.txt
Despliegue local:
streamlit run app.py
Streamlit Community Cloud: conectar repo,
seleccionar rama main
, app app.py
,
desplegar.
¿Quieres que agregue notebooks de validación con cálculos manuales para contrastar los resultados de la app?
Listo. Tres notebooks de validación creados. Descargas:
Sugerencia de uso en Codespaces:
.ipynb
al repo..venv
y active.pip install -r requirements.txt
app.py
y documente hallazgos en
README.md
.