# 📖 Documentación - Simulador Hidráulico C# .NET 8 ## 🎯 Descripción General Esta librería permite simular redes hidráulicas complejas con bombas, tuberías, válvulas y tanques. Está diseñada para calcular flujos y presiones en cada punto de la red, permitiendo posteriormente calcular niveles en tanques basados en los flujos calculados. ## 🏗️ Arquitectura de la Librería ### 📁 Estructura del Proyecto ``` HydraulicSimulator/ ├── Models/ # Modelos principales │ ├── Fluid.cs # Propiedades del fluido │ ├── Node.cs # Nodos de la red │ ├── Branch.cs # Ramas que conectan nodos │ ├── Element.cs # Clase base para elementos │ ├── Pipe.cs # Tuberías │ ├── PumpHQ.cs # Bombas con curva H-Q │ ├── ValveKv.cs # Válvulas con coeficiente Kv │ ├── MinorLoss.cs # Pérdidas menores │ └── HydraulicNetwork.cs # Red hidráulica y solver ├── Core/ # Funcionalidades auxiliares │ ├── TestRunner.cs # Casos de prueba │ └── InteractiveSimulator.cs # Simulador interactivo └── Program.cs # Punto de entrada ``` ## 🧱 Componentes Principales ### 1. **Fluid** - Propiedades del Fluido ```csharp var fluid = new Fluid( rho: 1000, // Densidad (kg/m³) mu: 0.001, // Viscosidad dinámica (Pa·s) name: "Water", // Nombre del fluido temp: 20.0, // Temperatura (°C) pVapor: 2337.0 // Presión de vapor (Pa) ); // Fluido predefinido var water = Fluid.Water20C; // Agua a 20°C ``` ### 2. **Node** - Nodos de la Red ```csharp // Nodo con presión fija (tanques, descargas) var tank = new Node("TANQUE_01", fixedP: true, p: 0); // Nodo con presión libre (calculada por el solver) var junction = new Node("UNION_01", fixedP: false, p: 0); ``` **Propiedades:** - `Name`: Nombre identificador - `FixedP`: Si la presión es fija (true) o calculada (false) - `P`: Presión en Pascal (Pa) ### 3. **Pipe** - Tuberías ```csharp var pipe = new Pipe( length: 100, // Longitud (m) diameter: 0.1, // Diámetro interno (m) roughness: 0.0015 // Rugosidad absoluta (m) ); ``` **Características:** - Usa ecuación de Darcy-Weisbach - Factor de fricción por Swamee-Jain - Cálculo automático de pérdidas por fricción ### 4. **PumpHQ** - Bombas ```csharp var pump = new PumpHQ( h0: 100, // Cabeza a caudal cero (m) q0: 0.02, // Caudal a cabeza cero (m³/s) speedRel: 1.0, // Velocidad relativa (1.0 = nominal) direction: 1 // Dirección (+1 o -1) ); ``` **Curva característica:** `H = H0 * (1 - (Q/Q0)²)` ### 5. **ValveKv** - Válvulas ```csharp var valve = new ValveKv( kvFull: 25, // Kv a apertura completa (m³/h/√bar) opening: 1.0 // Apertura (0.0 a 1.0) ); ``` ### 6. **MinorLoss** - Pérdidas Menores ```csharp var elbow = new MinorLoss(k: 0.9); // Codo 90° var tee = new MinorLoss(k: 1.8); // Té ``` ## 🔧 Uso Básico en tu Aplicación ### 1. **Crear una Red Hidráulica** ```csharp using HydraulicSimulator.Models; // 1. Crear la red var network = new HydraulicNetwork(); // 2. Agregar nodos network.AddNode("TANQUE_A", 0); // Tanque a presión atmosférica network.AddNode("UNION_1"); // Nodo intermedio network.AddNode("PROCESO_1", 50000); // Proceso a 0.5 bar network.AddNode("DESCARGA", 0); // Descarga a atmósfera // 3. Crear elementos var pump = new PumpHQ(h0: 80, q0: 0.01); var mainPipe = new Pipe(length: 50, diameter: 0.08, roughness: 0.0015); var valve = new ValveKv(kvFull: 20, opening: 0.8); var dischargePipe = new Pipe(length: 30, diameter: 0.06, roughness: 0.0015); // 4. Conectar elementos en ramas network.AddBranch("TANQUE_A", "UNION_1", new List { pump, mainPipe }); network.AddBranch("UNION_1", "PROCESO_1", new List { valve }); network.AddBranch("PROCESO_1", "DESCARGA", new List { dischargePipe }); // 5. Resolver la red var result = network.Solve(); if (result.Converged) { Console.WriteLine("✅ Simulación exitosa"); network.Report(); // Mostrar resultados } ``` ### 2. **Obtener Resultados** ```csharp // Flujos en cada rama (m³/s) foreach (var branch in network.Branches) { Console.WriteLine($"Rama {branch.Name}: {branch.Q:F6} m³/s"); } // Presiones en cada nodo (Pa) foreach (var node in network.Nodes) { double pressureBar = node.Value.P / 100000.0; Console.WriteLine($"Nodo {node.Key}: {pressureBar:F2} bar"); } // Resultados específicos var flowRate = result.Flows["TANQUE_A->UNION_1"]; var pressure = result.Pressures["UNION_1"]; ``` ## 🎮 Integración con Objetos Gráficos ### Mapeo de Objetos Gráficos a Elementos ```csharp public class GraphicToHydraulicMapper { public HydraulicNetwork CreateNetworkFromGraphics(List graphics) { var network = new HydraulicNetwork(); // 1. Agregar todos los nodos foreach (var graphic in graphics.Where(g => g.Type == "Node")) { var isFixed = graphic.Properties.ContainsKey("FixedPressure"); var pressure = isFixed ? Convert.ToDouble(graphic.Properties["Pressure"]) : 0; network.AddNode(graphic.Id, isFixed ? pressure : null); } // 2. Crear elementos hidráulicos desde gráficos foreach (var graphic in graphics.Where(g => g.Type != "Node")) { Element element = graphic.Type switch { "Pump" => new PumpHQ( h0: GetProperty(graphic, "Head"), q0: GetProperty(graphic, "MaxFlow") / 3600.0 // Convertir m³/h a m³/s ), "Pipe" => new Pipe( length: GetProperty(graphic, "Length"), diameter: GetProperty(graphic, "Diameter"), roughness: GetProperty(graphic, "Roughness", 0.0015) ), "Valve" => new ValveKv( kvFull: GetProperty(graphic, "Kv"), opening: GetProperty(graphic, "Opening", 1.0) ), _ => throw new NotSupportedException($"Elemento {graphic.Type} no soportado") }; // 3. Conectar elemento entre nodos network.AddBranch( graphic.FromNodeId, graphic.ToNodeId, new List { element }, graphic.Id ); } return network; } private T GetProperty(GraphicElement graphic, string key, T defaultValue = default) { return graphic.Properties.ContainsKey(key) ? (T)Convert.ChangeType(graphic.Properties[key], typeof(T)) : defaultValue; } } ``` ## 🏭 Casos Especiales: Tanques y Descargas ### 1. **Tanque de Suministro** ```csharp // Tanque con nivel fijo (presión constante) network.AddNode("TANQUE_SUMINISTRO", 0); // Presión atmosférica // Tanque con altura (presión hidrostática) double height = 10; // metros double pressure = 1000 * 9.81 * height; // ρgh network.AddNode("TANQUE_ELEVADO", pressure); ``` ### 2. **Tanque de Proceso (Volumen Variable)** ```csharp // Nodo con presión libre - el nivel se calcula después network.AddNode("TANQUE_PROCESO"); // Presión calculada por el solver // Después de la simulación, calcular nivel public double CalculateLevel(double pressure, double density = 1000) { return pressure / (density * 9.81); // h = P/(ρg) } ``` ### 3. **Descarga a Atmósfera** ```csharp // Descarga libre (presión atmosférica) network.AddNode("DESCARGA", 0); // Descarga con contrapresión network.AddNode("DESCARGA_PRESURIZADA", 20000); // 0.2 bar ``` ### 4. **Tanque con Cálculo de Nivel Dinámico** ```csharp public class Tank { public string NodeId { get; set; } public double Area { get; set; } // m² - área del tanque public double CurrentVolume { get; set; } // m³ - volumen actual public double Height => CurrentVolume / Area; // m - altura actual // Actualizar volumen basado en flujos public void UpdateVolume(Dictionary flows, double deltaTime) { double netFlow = 0; // Sumar flujos entrantes, restar salientes foreach (var flow in flows) { if (flow.Key.EndsWith($"->{NodeId}")) netFlow += flow.Value; // Entrante else if (flow.Key.StartsWith($"{NodeId}->")) netFlow -= flow.Value; // Saliente } // Actualizar volumen CurrentVolume += netFlow * deltaTime; CurrentVolume = Math.Max(0, CurrentVolume); // No puede ser negativo } // Calcular presión en el fondo del tanque public double BottomPressure(double density = 1000) { return density * 9.81 * Height; // Presión hidrostática } } ``` ## 📊 Ejemplo Completo: Sistema de Mixers ```csharp public class MixerSystemSimulation { public void RunSimulation() { var network = new HydraulicNetwork(); // Nodos del sistema network.AddNode("TANQUE_PRINCIPAL", 0); // Suministro network.AddNode("MIXER_1", 100000); // Proceso 1 (1 bar) network.AddNode("MIXER_2", 100000); // Proceso 2 (1 bar) network.AddNode("DESCARGA", 50000); // Descarga (0.5 bar) // Equipos var pump1 = new PumpHQ(h0: 120, q0: 0.009); var pump2 = new PumpHQ(h0: 110, q0: 0.008); var supply1 = new Pipe(length: 50, diameter: 0.1, roughness: 0.0015); var supply2 = new Pipe(length: 45, diameter: 0.08, roughness: 0.0015); var valve1 = new ValveKv(kvFull: 25, opening: 1.0); var valve2 = new ValveKv(kvFull: 20, opening: 1.0); var discharge1 = new Pipe(length: 20, diameter: 0.06, roughness: 0.0015); var discharge2 = new Pipe(length: 25, diameter: 0.06, roughness: 0.0015); // Conexiones network.AddBranch("TANQUE_PRINCIPAL", "MIXER_1", new List { pump1, supply1, valve1 }); network.AddBranch("TANQUE_PRINCIPAL", "MIXER_2", new List { pump2, supply2, valve2 }); network.AddBranch("MIXER_1", "DESCARGA", new List { discharge1 }); network.AddBranch("MIXER_2", "DESCARGA", new List { discharge2 }); // Simular var result = network.Solve(); if (result.Converged) { // Procesar resultados ProcessResults(result); } else { Console.WriteLine($"❌ No convergió después de {result.Iterations} iteraciones"); } } private void ProcessResults(SolutionResult result) { Console.WriteLine("🎯 RESULTADOS DE SIMULACIÓN:"); foreach (var flow in result.Flows) { double flowM3h = flow.Value * 3600; // Convertir a m³/h Console.WriteLine($" {flow.Key}: {flowM3h:F2} m³/h"); } foreach (var pressure in result.Pressures) { double pressureBar = pressure.Value / 100000.0; // Convertir a bar Console.WriteLine($" {pressure.Key}: {pressureBar:F2} bar"); } } } ``` ## 🚀 Ejecución desde Línea de Comandos ```bash # Casos de prueba disponibles dotnet run test simple-pipe --verbose dotnet run test mixer-system --json dotnet run test complex-network --flow 25 --pressure 30 # Ayuda dotnet run help ``` ## ⚙️ Parámetros de Configuración ### Solver Parameters ```csharp var result = network.Solve( maxIterations: 200, // Máximo de iteraciones tolerance: 1e-4, // Tolerancia de convergencia relaxationFactor: 0.7, // Factor de relajación verbose: true // Mostrar progreso ); ``` ### Unidades del Sistema - **Presión**: Pascal (Pa) - Convertir: `bar = Pa / 100000` - **Flujo**: m³/s - Convertir: `m³/h = m³/s * 3600` - **Longitud**: metros (m) - **Diámetro**: metros (m) - **Rugosidad**: metros (m) ## 🎯 Recomendaciones para tu Aplicación 1. **📋 Mapea tus objetos gráficos** a los elementos hidráulicos 2. **🔄 Implementa actualización dinámica** de niveles de tanques 3. **⚡ Usa tolerancias apropiadas** según la complejidad de tu red 4. **📊 Implementa visualización** de resultados en tiempo real 5. **💾 Guarda configuraciones** para reutilizar casos de simulación ¿Te gustaría que desarrolle alguna sección específica o que agregue ejemplos para casos particulares de tu aplicación?