Contents — find the section you need

Tanques de nutrientes en ACE: contabilidad de la reposición y la dilución

Restaurar el volumen y la concentración juntos

Un tanque contiene 90 L a 80 mg/L de un componente rastreado. Añadir 10 L de agua sin ese componente restaura el volumen a 100 L pero reduce la concentración a 72 mg/L. Un nivel bajo y un déficit del componente, por lo tanto, requieren contabilidades separadas.

Esta guía extiende los fundamentos de la zona radicular a operaciones discretas. Todos los valores son sintéticos; 100 mg/L es un objetivo aritmético, no una recomendación de cultivo. Rastreamos un componente de concentración conocida, sin calcular una receta de fertilizante, la concentración a partir de la EC, ni dosis de ácido/base.

Defina el límite

Suponga un solo tanque perfectamente mezclado con el suministro y retorno del circuito de cultivo detenidos. Durante cada operación no hay absorción, evaporación, precipitación ni reacción, y los volúmenes de líquido son aditivos. Un tanque en operación necesita el volumen de suministro y retorno y los flujos del componente en su balance. Si el límite encierra todo el sistema de circulación, la circulación interna no es una reposición externa.

Para el volumen V [L], la concentración C [mg/L] y la masa del componente disuelto M [mg], M=VC. Añadir el volumen V_a a la concentración C_a da

V_1=V_0+V_a,\qquad M_1=V_0C_0+V_aC_a,\qquad C_1=\frac{M_1}{V_1}

Use C_a=0 solo si el agua añadida no contiene nada del componente rastreado. La guía de manejo de nutrientes de OSU describe el análisis del agua de origen y la atención a la concentración de sales, el pH, la alcalinidad y las proporciones de nutrientes.

Compare tres adiciones

Comience con 90 L a 80 mg/L: 7,200 mg. Suponga una concentración de solución madre de 10,000 mg/L para este componente.

Operación Volumen final Masa final del componente Concentración final
Añadir 10 L de agua sin el componente 100 L 7,200 mg 72.00 mg/L
Añadir unos 0.181818 L de solo solución madre Unos 90.181818 L Unos 9,018.18 mg 100.00 mg/L
Añadir 0.28 L de solución madre y 9.72 L de agua 100 L 10,000 mg 100.00 mg/L

La solución madre también añade volumen. Para la concentración objetivo C_t, la concentración de la solución madre C_s y el volumen de solución madre x,

\frac{V_0C_0+xC_s}{V_0+x}=C_t \quad\Rightarrow\quad x=\frac{V_0(C_t-C_0)}{C_s-C_t}

Elevar la concentración requiere C_s>C_t>C_0. Una solución madre a la concentración objetivo no puede llevar exactamente a esa meta una concentración inicial menor con una adición finita. Una solución negativa indica que la adición propuesta no puede cumplir el objetivo.

Si también se prescribe el volumen final V_f y el agua de reposición no contiene el componente,

x=\frac{V_fC_t-V_0C_0}{C_s},\qquad V_{water}=V_f-V_0-x

Ambas adiciones deben ser no negativas y el volumen final debe caber en el recipiente. Aquí son 0.28 L de solución madre y 9.72 L de agua. Especificar solo la concentración da una respuesta diferente a especificar tanto la concentración como el volumen.

El drenaje y la dilución tienen efectos distintos

Ahora comience con 100 L a 120 mg/L: 12,000 mg. Drenar 10 L de solución bien mezclada elimina 1,200 mg. El resto es 90 L y 10,800 mg, aún a 120 mg/L. Añadir después 10 L de agua sin el componente da entonces 100 L a 108 mg/L.

Añadir 20 L de agua sin drenar da en cambio 120 L a 100 mg/L. El tanque sigue conteniendo 12,000 mg, y necesita más capacidad de reserva.

Condición Ecuación Límite
Drenar D L primero, luego reemplazar con agua C_1=C_0(1-D/V_0) Mezcla perfecta; relleno sin el componente
Añadir W L de agua sin drenar C_1=V_0C_0/(V_0+W) El volumen aumenta
Entrada y salida simultáneas a volumen constante C(t)=C_{in}+(C_0-C_{in})e^{-Qt/V} Mezcla perfecta, flujo constante, sin absorción ni reacción

Para el reemplazo simultáneo de 10 L, Qt/V=0.1 y C_{in}=0 dan unos 108.58 mg/L. Esto difiere de los 108 mg/L obtenidos drenando primero. El orden de la operación pertenece al registro.

Reproduzca la contabilidad en Python

Guarde esto como tank_accounting.py y ejecute python3 tank_accounting.py. Usa solo la biblioteca estándar.

from math import isfinite


def mix(v, c, added_v, added_c):
    values = (v, c, added_v, added_c)
    if not all(isfinite(x) for x in values):
        raise ValueError("finite values required")
    if v <= 0 or min(c, added_v, added_c) < 0:
        raise ValueError("invalid volume or concentration")
    return (v * c + added_v * added_c) / (v + added_v)


v, c, target, stock = 90.0, 80.0, 100.0, 10000.0
x = v * (target - c) / (stock - target)
print(f"water top-up: {mix(v, c, 10, 0):.2f} mg/L")
print(f"stock addition: {x:.6f} L, {mix(v, c, x, stock):.2f} mg/L")
y = (100 * target - v * c) / stock
print(f"fixed final volume: stock {y:.2f} L, water {10-y:.2f} L")
print(f"drain then refill: {mix(90, 120, 10, 0):.2f} mg/L")
print(f"dilute only: {mix(100, 120, 20, 0):.2f} mg/L")

Las salidas son 72.00 mg/L; 0.181818 L y 100.00 mg/L; 0.28 L y 9.72 L; 108.00 mg/L; y 100.00 mg/L. Antes de experimentar con otra concentración de solución madre, verifique las condiciones de alcanzabilidad anteriores y asegúrese de que el denominador no sea cero.

De la contabilidad al control

La guía de análisis de agua de riego de OSU explica que la EC y los TDS no identifican concentraciones de sales individuales. Sustituir la EC por C no puede calcular la reposición de un componente específico. Un fertilizante mixto cambia varios componentes a la vez, por lo que cumplir un objetivo no restaura toda la composición.

Un sensor tampoco necesita alcanzar de inmediato la predicción de mezcla perfecta. La guía de manejo de nutrientes de OSU pide agitar y dejar que las lecturas se estabilicen. Basándose en esa distinción, este artículo propone el siguiente punto de partida para el diseño de control:

  1. Registre el momento inicial, el volumen, la ubicación de muestreo, la base de la concentración del componente, la EC, la temperatura del líquido y el pH.
  2. Registre la composición de la solución madre, los volúmenes reales de adición y drenaje, y el estado de circulación. La duración del comando de la bomba por sí sola no establece el flujo real.
  3. Espere un intervalo de mezcla establecido según el equipo y mediciones estables antes de reevaluar. Evite apilar comandos adicionales en respuesta a una lectura local inmediatamente después de la inyección.
  4. Conserve los errores de predicción. Detenga las adiciones adicionales para investigar niveles anormales, discrepancias de flujo o mediciones obsoletas. Establezca tolerancias y límites de dosis en la instalación real.

Esto no es una especificación completa de un sistema de dosificación. Los ejemplos trabajados validan la consistencia aritmética del volumen, la masa del componente y la concentración bajo los supuestos establecidos. El siguiente paso es conectar las operaciones ordenadas con las observaciones usando el flujo medido de la bomba y la respuesta de mezcla.

Bombas de dosificación en ACE: calibración de volumen y esperas de mezcla

Dos comprobaciones antes de llamar «20 mL» a una adición

La guía de contabilidad del tanque calculó las adiciones a partir de balances de agua y componentes. Un cálculo de 20 mL aún no especifica un tiempo de ejecución de la bomba. Terminar la inyección tampoco significa que una lectura del tanque sea representativa.

Esta guía separa la calibración de comando a volumen de la verificación de cuándo pueden reevaluarse las mediciones posteriores a la inyección. Cada conjunto de datos a continuación es un ejemplo sintético original, no el rendimiento medido de un dispositivo o cultivo. El código no opera equipos ni calcula recetas de fertilizante.

Mantenga constantes las condiciones de calibración

La guía de inyectores de fertilizante de la Universidad de Georgia explica la calibración y el mantenimiento periódicos, los rangos de flujo de operación y las condiciones de presión. La proporción de un inyector proporcional al agua es diferente de los mL/s de una bomba comandada por tiempo considerada aquí.

Para un experimento de recolección, siga el manual del equipo y registre las condiciones de manera consistente:

Registro Propósito
Bomba, ajuste de velocidad, tubería, válvulas e historial de servicio Identificar la configuración calibrada
Líquido, temperatura y densidad si se usa Mantener una prueba con agua separada de una prueba con solución madre
Nivel de succión, elevación/presión de descarga y plomería Preservar las diferencias entre la recolección y la operación
Cebado, burbujas y tiempo de inactividad previo Identificar diferencias de arranque
Inicio/fin del comando, fin de la recolección y volumen recolectado Definir si el goteo posterior a la detención está incluido

Desconectar la plomería de descarga para la recolección puede cambiar la contrapresión. Una configuración de recolección sencilla no establece por sí misma la entrega en el sistema instalado. Mantenga las comprobaciones preliminares con agua separadas de la confirmación con el líquido previsto.

Al pesar, convierta la masa neta recolectada \Delta m [g] usando la densidad del líquido \rho [g/mL]: V=\Delta m/\rho [mL]. No suponga que todo líquido tiene una densidad de 1 g/mL. Registre la resolución de la balanza, el estado de calibración y la precisión del tiempo.

Un promedio de largo plazo puede tergiversar una adición corta

Suponga tres pruebas de cada duración, todas comenzando en el mismo estado.

Comando [s] Tres volúmenes sintéticos [mL] Media [mL]
10 15, 16, 17 16
20 35, 36, 37 36
30 55, 56, 57 56
60 115, 116, 117 116

Las medias siguen V(t)=at+b=2t-4 [mL], con a en mL/s y b en mL. Un objetivo de 20 mL da (20-b)/a=12 s. Usar solo la pendiente da 10 s y apenas 16 mL en este ejemplo.

Usar el flujo promedio de 60 s, 116/60 mL/s, da en cambio unos 10.34 s para 20 mL. La ecuación sintética predice unos 16.69 mL en esa duración. Cuál «flujo» se use importa.

El intercepto de −4 mL es un coeficiente de regresión sobre el rango muestreado, no una prueba de un retardo puro físico de dos segundos. No extrapole fuera de 10–60 s, especialmente hacia cero. Para datos medidos, inspeccione los residuos y considere un modelo separado de duración corta si la relación se curva.

Cada fila sintética tiene una desviación estándar muestral de 1 mL. Un error de regresión cero en las medias no elimina la variación entre disparos individuales. Tres repeticiones tampoco establecen la variabilidad día a día o el sesgo de la balanza. Verifique las predicciones medidas usando duraciones y pruebas adicionales excluidas del ajuste.

Separe la finalización de la inyección de la confirmación de mezcla

La guía de manejo de nutrientes de OSU pide agitar y obtener lecturas estables antes de la evaluación. Establecer una espera requiere una serie temporal posterior a la inyección distinta de la calibración de volumen.

Registre mediciones sincronizadas lejos del punto de inyección así como cerca. Documente el volumen, el flujo de circulación, las ubicaciones de inyección y sensor, la temperatura y el filtrado del sensor. La mezcla, el transporte y la respuesta del sensor contribuyen todos a la dinámica observada; un tiempo de respuesta ajustado no es automáticamente un tiempo de mezcla puro.

Para ilustrar, normalice un cambio desde el valor inicial y_0 hasta un valor final conocido y_\infty:

z(t)=\frac{y(t)-y_0}{y_\infty-y_0}=1-e^{-t/\tau}

El tiempo cero es la finalización de la inyección. Esta es una respuesta local sintética de medición, no un modelo de tanque que conserve masa. Suponga constantes de tiempo de 40 s y 80 s en las ubicaciones A y B. Sus respuestas en tiempo continuo entran en una banda del 5% alrededor del cambio final después de aproximadamente 120 s y 240 s.

Diagram 1 · Use the button to switch views
Datos sintéticos de comando-volumen y dos respuestas normalizadas. La línea discontinua marca el umbral ilustrativo de respuesta del 95%.

Arriba: tres volúmenes recolectados por duración y la línea ajustada a sus medias. Abajo: respuestas sintéticas A y B. El tiempo está en segundos, el volumen en mL y la respuesta normalizada es adimensional. No se muestran datos medidos.

Ahora imponga una regla educativa: con muestras cada 10 s, ambas ubicaciones deben permanecer dentro del 5% durante 30 s antes de la confirmación. B califica primero a los 240 s. Las cuatro muestras a los 240, 250, 260 y 270 s proporcionan confirmación a los 270 s. Verificar solo A calificaría a los 150 s y pasaría por alto a B.

Ni el 5% ni los 30 s son un criterio de aceptación universal. Elija los criterios usando el ruido/precisión de la medición, la desviación de concentración aceptable y el intervalo de muestreo. Estime los valores finales de manera independiente, por ejemplo a partir de registros suficientemente largos, e inspeccione también las diferencias entre los valores finales de las ubicaciones. Normalizar cada ubicación por separado puede ocultar diferencias espaciales de concentración persistentes. No use esta normalización cuando el cambio de inicial a final sea pequeño en relación con el ruido.

Reproduzca los cálculos

Guarde como pump_mixing.py y ejecute python3 pump_mixing.py. Solo se requiere la biblioteca estándar.

from math import exp
from statistics import mean

# Synthetic data only; time [s], collected volume [mL].
runs = {10: [15, 16, 17], 20: [35, 36, 37],
        30: [55, 56, 57], 60: [115, 116, 117]}
ts = list(runs)
vs = [mean(runs[t]) for t in ts]
tm, vm = mean(ts), mean(vs)
a = sum((t-tm)*(v-vm) for t, v in zip(ts, vs)) / sum((t-tm)**2 for t in ts)
b = vm - a*tm
command = (20-b)/a
if not min(ts) <= command <= max(ts):
    raise ValueError("command outside calibration range")
print(f"fit: V = {a:.2f} t {b:+.2f}; 20 mL command = {command:.2f} s")
print(f"held-out 40 s: predicted {a*40+b:.2f} mL; reference 76.00 mL")

# Two synthetic normalized responses, sampled every 10 s.
# Four consecutive samples span 30 s. No missing samples here.
times = list(range(0, 401, 10))
errors = [max(exp(-t/40), exp(-t/80)) for t in times]
ready = next((times[i] for i in range(3, len(times))
              if all(e <= 0.05 for e in errors[i-3:i+1])), None)
print(f"two-location confirmation = {ready} s")

Las salidas son pendiente 2.00, intercepto −4.00, un comando de 12.00 s para 20 mL, 76.00 mL a los 40 s, y confirmación a los 270 s. La referencia de 40 s excluida del ajuste también proviene de la misma ecuación sintética, así que no es evidencia de generalización a equipos reales. Cambiar la constante de tiempo de 80 s a 120 s da confirmación a los 390 s. Si no existe una ventana calificante en el registro, el código devuelve None.

Conecte las verificaciones con el permiso para otra adición

Una implementación de control necesita más que un temporizador. Este artículo propone etapas separadas para verificaciones previas, una adición, la espera de mezcla/medición y la reevaluación. Los comandos fuera de rango, las lecturas faltantes u obsoletas, la circulación detenida, los niveles anormales o el no estabilizarse dentro del período de observación deben impedir adiciones adicionales pendientes de investigación. No rellene muestras faltantes con el valor anterior y lo cuente como estabilidad continua.

Una lectura de EC por debajo del objetivo tras la espera no establece de inmediato un déficit particular de un componente; conserve la distinción entre EC y composición explicada en los fundamentos de la zona radicular. El siguiente paso es evaluar las predicciones de entrega y los criterios de espera frente a registros medidos antes de diseñar el control de dosificación con límites por adición, acumulados y de reintento.

Control de dosificación en ACE: límites, esperas de medición y estados de falla

Una lectura baja no justifica adiciones repetidas

La calibración de bombas y las esperas de mezcla separaron el volumen entregado del momento en que las observaciones se estabilizan. La siguiente pregunta es si otra adición está actualmente permitida. Una lectura baja durante la mezcla, una muestra antigua o una circulación detenida no establecen ese permiso.

Esta guía propone un diseño educativo con estados operativos separados y verificaciones de volumen, número de intentos y observaciones. Todos los valores y entradas son sintéticos, no límites de operación ni recomendaciones de fertilizante. El ejemplo en Python reproduce únicamente una puerta de autorización; no opera una bomba.

Calcule un candidato, luego decida si puede ejecutarse

Un candidato derivado de la contabilidad del tanque aún necesita verificaciones contra el rango de calibración, el estado del equipo y las observaciones que puedan evaluar la operación anterior.

La guía de inyectores de UGA explica la calibración y el mantenimiento periódicos. La guía de manejo de nutrientes de OSU pide agitar y obtener lecturas estables antes de la evaluación. Los estados y límites a continuación son el diseño propuesto de este artículo, no una especificación de controlador proporcionada por ninguna de las dos fuentes.

Separe seis estados operativos

Estado Actividad Condición de transición
CHECK Validar observaciones, candidato, presupuesto restante y equipo Registrar una reserva antes de DOSE; ir a DONE si el objetivo ya se cumplió
DOSE Emitir un comando para un ID de operación único Confirmar la detención antes de WAIT; el tiempo de espera o el desajuste de flujo lleva a FAULT
WAIT Prohibir nuevas adiciones y recolectar observaciones posteriores a la detención Las observaciones calificantes llevan a REVIEW; el no estabilizarse antes del plazo lleva a FAULT
REVIEW Reevaluar observaciones e historial DONE si se cumple, CHECK si otro candidato se justifica, FAULT por inconsistencias
DONE Mantener la salida detenida y cerrar el registro Una operación posterior requiere una nueva decisión operativa
FAULT Solicitar la detención y prohibir más adiciones Recuperación explícita tras verificar la causa, la cantidad entregada y el equipo

Emitir un comando de detención es diferente de confirmar que el flujo se ha detenido. Los nombres de estado del software no pueden prevenir una válvula o bomba fallida. Una implementación también necesita retroalimentación de salida, mecanismos de detención del lado del equipo y un comportamiento de reinicio definido.

No haga que FAULT vuelva automáticamente a CHECK tras una espera corta. Repetir una operación sin resolver su causa puede acumular adiciones no deseadas.

Cargue el presupuesto antes de emitir el comando

Use límites ilustrativos de 16–20 mL por adición, 50 mL por sesión operativa y como máximo tres autorizaciones. El mínimo de 16 mL corresponde a la calibración sintética del artículo anterior a los 10 s. Estos números no son ajustes de equipo.

Para el volumen ya cargado B [mL] y el candidato d [mL], requiera

16\le d\le20,\qquad B+d\le50,\qquad n<3

Al autorizar, registre B\leftarrow B+d y n\leftarrow n+1 antes de proceder al comando. Este registro representa capacidad de autorización consumida, no entrega medida. Si la comunicación falla inmediatamente después de un comando, no suponga entrega cero ni reembolse su capacidad. Registre por separado la entrega medida y la incierta.

Operación sintética Candidato Total cargado tras la decisión Resultado
Primera 16 mL 16 mL Permitir
Segunda, tras detener, mezclar y revisar 16 mL 32 mL Permitir
Tercera, tras revisar 18 mL 50 mL Permitir
Cuarta 16 mL 50 mL Rechazar: límite de intentos

Una solicitud de 20 mL después de cargar 32 mL se rechaza porque alcanzaría 52 mL, incluso con un intento restante. Reconsidere las observaciones y la base del candidato en lugar de recortar automáticamente la dosis a la capacidad restante.

Aquí una sesión es una secuencia de corrección con un inicio y fin registrados. Una iteración de bucle, una actualización de página o una reconexión no es una nueva sesión. Un sistema operativo necesita condiciones explícitas de renovación de presupuesto, como una ventana de tiempo definida o el reemplazo del tanque, y registros que sobrevivan al reinicio. Este ejemplo no tiene persistencia.

Verifique la actualidad por separado de la estabilidad

El momento de recepción por sí solo no establece la actualidad. Considere el momento de la medición de origen, la sincronización de reloj y el orden relativo a la operación. Aquí age_s es una edad de medición ya convertida, aceptada solo entre cero y cinco segundos. También se rechaza una edad negativa proveniente de una marca de tiempo futura.

after_stop significa que la observación sigue a la confirmación de la detención anterior. Para la adición inicial, use una observación obtenida después de iniciar la verificación previa actual. stable es una decisión previa basada en una secuencia de observaciones, como se discutió en la guía anterior. Un solo valor sin cambios no establece estabilidad: evalúe los datos faltantes, los sensores atascados y el desacuerdo entre ubicaciones.

Cinco segundos es un supuesto educativo. Un límite incompatible con el intervalo de muestreo y el retardo de comunicación puede rechazar observaciones útiles. Simplemente aumentarlo no resuelve los problemas de datos obsoletos.

Pruebe la puerta de autorización en Python

Guarde como dosing_gate.py y ejecute python3 dosing_gate.py. Solo se requiere la biblioteca estándar.

from math import isfinite


def gate(state, dose_ml, charged_ml, attempts, age_s,
         after_stop, stable, circulation, level_ok):
    """Offline authorization check; no hardware commands or persistence."""
    if state != "CHECK":
        return "STATE"
    if not all(isfinite(x) for x in (dose_ml, charged_ml, age_s)):
        return "INVALID"
    if charged_ml < 0 or type(attempts) is not int or attempts < 0:
        return "INVALID"
    if not all(type(x) is bool for x in
               (after_stop, stable, circulation, level_ok)):
        return "INVALID"
    if not circulation or not level_ok:
        return "INTERLOCK"
    if not 0 <= age_s <= 5 or not after_stop:
        return "STALE"
    if not stable:
        return "UNSTABLE"
    if not 16 <= dose_ml <= 20:
        return "DOSE_RANGE"
    if attempts >= 3:
        return "ATTEMPTS"
    if charged_ml + dose_ml > 50:
        return "BUDGET"
    return "ALLOW"


if __name__ == "__main__":
    charged, attempts, state = 0.0, 0, "CHECK"
    for requested in (16, 16, 18, 16):
        result = gate(state, requested, charged, attempts, 1,
                      True, True, True, True)
        if result == "ALLOW":
            # Charge BEFORE a simulated command; never refund uncertainty.
            charged += requested
            attempts += 1
            state = "WAIT"
        print(f"{requested} mL: {result}; charged={charged:.0f} mL")
        # Synthetic successful stop/mixing/reassessment, not a timer.
        state = "CHECK"

Las primeras tres decisiones son ALLOW; la cuarta es ATTEMPTS. Los totales cargados son 16, 32, 50 y 50 mL. Reiniciar el estado de la demostración a CHECK al final de cada iteración representa una detención, mezcla y reevaluación sintéticas exitosas. Una implementación no debe ejecutar esa transición únicamente porque expiró un temporizador fijo.

Esta función es una puerta educativa fuera de línea con argumentos numéricos. No analiza entradas de comunicación, calcula una dosis, establece estabilidad, monitorea el flujo real ni ejecuta la máquina de estados. Rechaza NaN, infinito, volúmenes cargados negativos y recuentos de intentos no válidos, pero no es una API externa completa de entrada.

Ejercite los límites de rechazo

Entrada o situación Resultado esperado
Llega otro candidato en WAIT STATE; sin carga de presupuesto ni comando adicional
La edad es seis segundos o negativa STALE
La observación precede a la detención, o no es estable STALE o UNSTABLE
Circulación detenida o nivel anormal INTERLOCK
El candidato es 15 o 21 mL DOSE_RANGE
Solicitar 20 mL después de cargar 32 mL BUDGET
El candidato o el volumen cargado es NaN INVALID

Una implementación también debe probar que reenviar un ID de operación no pueda ejecutarse dos veces. Confirme los cambios de presupuesto y los ID de operación juntos para que solicitudes en competencia no puedan gastar ambas la misma capacidad restante. Tras una interrupción, reconcilie los comandos persistidos con el estado real del equipo antes de reanudar.

Lo que esto establece

El ejemplo verifica las decisiones de autorización frente a las entradas y límites suministrados. No infiere déficits individuales de componentes a partir de la EC ni cantidades de ácido a partir de diferencias de pH. Tampoco valida el crecimiento, el rendimiento, la seguridad del equipo ni el seguimiento de la concentración.

Los roles de la contabilidad, la calibración de entrega, la evaluación de mezcla y la autorización ahora están conectados. La validación con registros de equipos medidos sigue abierta. A continuación viene la ruta del agua a través de sistemas de producción como NFT, DWC y goteo, donde la relación entre el tanque y las raíces cambia.

What to read next

Revisar la diferencia entre EC, pH y masa de un componente.Detección y estimación por imagen en ACE — ubicación de sensores y área foliar proyectadaContinue the seriesRutas del agua e hidráulica multinivel en ACE: NFT, DWC, goteo y balance de cargaExplore another aspect of this fieldAcuaponía en ACE — límites de calidad del agua para peces, microbios y plantas