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.


Ejercicio 1. Evaluación de la eficiencia de un secador solar

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.


Ejercicio 2. Altura de chimenea en un secador solar por convección natural

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:

  1. ¿Qué altura de chimenea es necesaria para lograr el flujo de aire requerido?
  2. ¿Cómo cambiaría el flujo de aire si la altura de la chimenea se incrementa en un tercio?
  3. ¿Cómo cambiaría si se reduce en un tercio?
  4. Si el clima se torna nublado y la temperatura del aire de secado baja a 30 °C, ¿cuál sería el nuevo flujo de aire?

Ejercicio 3. Flujo de aire y potencia de ventilador en un secador forzado

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:

  1. Estime el flujo volumétrico de aire a través de la cama de granos.
  2. Calcule la potencia de ventilador necesaria, considerando una eficiencia mecánica del 60%.

Checklist para resolver en GitHub Codespaces

  1. Creación del repositorio

    • Crear repositorio en GitHub con nombre solar_dryers_practica.
    • Añadir .gitignore para Python.
  2. Iniciar GitHub Codespaces

    • Abrir el repositorio en un Codespace nuevo.
  3. Instalar extensiones para Python

    • Instalar en Codespaces: Python, Pylance, Streamlit.
  4. Crear entorno virtual

    python -m venv .venv
    source .venv/bin/activate   # Linux/Mac
    .venv\Scripts\activate      # Windows
  5. Archivos con ayuda del chatbot

    • README.md: descripción del proyecto y ejercicios.
    • requirements.txt: incluir numpy, pandas, matplotlib, streamlit.
  6. Instalación de módulos

    pip install -r requirements.txt
  7. Desplegar en Streamlit

    • Crear archivo app.py que muestre cálculo paso a paso con interfaz.

    • Ejecutar localmente:

      streamlit run app.py
  8. Alojar en Streamlit Community Cloud

    • Conectar el repositorio de GitHub.
    • Configurar rama main y archivo de inicio app.py.
    • Hacer despliegue y compartir el enlace.

¿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.


Explicación técnica (Markdown)

La app implementa tres módulos:

  1. Eficiencia de secado y “pick-up”

    • Balance de masa para agua removida usando humedad base húmeda.
    • Energía mínima de evaporación con (\(L_v(T)\)) aproximado.
    • Eficiencia del sistema: (\(\eta = \dfrac{m_{\text{agua}};L_v}{H_{\text{solar}} , A , \text{días}}\)).
    • “Pick-up efficiency” psicrométrica: (\(\varepsilon=\dfrac{\omega_{\text{out}}-\omega_{\text{in}}}{\omega_{\text{sat}}(T_{\text{secado}})-\omega_{\text{in}}}\)), donde (\(\omega\)) es la razón de humedad (kg vapor/kg aire seco).
    • (\(\omega\)) se estima con presión de vapor de saturación (fórmula de Tetens/Magnus) y presión atmosférica.
  2. Altura de chimenea en convección natural

    • Se usa tiro térmico idealizado: \[[ \Delta P \approx \rho_a g H \left(\frac{T_i-T_a}{T_a}\right), \quad u \approx C_d \sqrt{\frac{2,\Delta P}{\rho_i}} ]\] con (\(u\)) velocidad media. Se despeja (\(H\)) para un (\(u\)) objetivo y se analizan variaciones de (\(H\)) y (\(T_i\)).
  3. Flujo y potencia de ventilador en secado forzado

    • El usuario fija velocidad superficial objetivo (\(u_s\)).
    • (\(Q= A_{\text{sección}},u_s\)).
    • Pérdida de presión: (\(\Delta P = k_{\text{Pa/m}} \times \text{profundidad}\)).
    • Potencia de eje: (\(P = \dfrac{\Delta P , Q}{\eta_{\text{mec}}}\)).

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 sugerido

streamlit>=1.36
numpy>=1.26
pandas>=2.2

Estructura mínima del repositorio

solar_dryers_practica/
├─ app.py
├─ requirements.txt
├─ README.md
└─ .gitignore

En README.md los estudiantes deben:

  • Resumir los supuestos adoptados y justificar parámetros.
  • Pegar capturas de resultados con distintos escenarios.
  • Registrar el flujo de trabajo: creación del repo, Codespaces, entorno, instalación, ejecución, despliegue.

Checklist breve solicitado

  1. Repositorio: crear solar_dryers_practica con .gitignore (Python).

  2. Codespaces: abrir un Codespace nuevo desde el repo.

  3. Extensiones: instalar Python, Pylance y Streamlit.

  4. Entorno:

    python -m venv .venv
    source .venv/bin/activate   # Linux/Mac
    .venv\Scripts\activate      # Windows (si aplica)
  5. Archivos con el chatbot de Codespaces: redactar README.md; generar requirements.txt.

  6. Instalación:

    pip install -r requirements.txt
  7. Despliegue local:

    streamlit run app.py
  8. 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:

  1. Suba los .ipynb al repo.
  2. Abra Codespaces.
  3. Cree .venv y active.
  4. pip install -r requirements.txt
  5. Abra cada notebook y ejecute.
  6. Compare con la app app.py y documente hallazgos en README.md.