# 📖 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<Element> { pump, mainPipe });
    
network.AddBranch("UNION_1", "PROCESO_1", 
    new List<Element> { valve });
    
network.AddBranch("PROCESO_1", "DESCARGA", 
    new List<Element> { 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<GraphicElement> 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<double>(graphic, "Head"),
                    q0: GetProperty<double>(graphic, "MaxFlow") / 3600.0 // Convertir m³/h a m³/s
                ),
                "Pipe" => new Pipe(
                    length: GetProperty<double>(graphic, "Length"),
                    diameter: GetProperty<double>(graphic, "Diameter"),
                    roughness: GetProperty<double>(graphic, "Roughness", 0.0015)
                ),
                "Valve" => new ValveKv(
                    kvFull: GetProperty<double>(graphic, "Kv"),
                    opening: GetProperty<double>(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> { element },
                graphic.Id
            );
        }
        
        return network;
    }
    
    private T GetProperty<T>(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<string, double> 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<Element> { pump1, supply1, valve1 });
        network.AddBranch("TANQUE_PRINCIPAL", "MIXER_2", 
            new List<Element> { pump2, supply2, valve2 });
        network.AddBranch("MIXER_1", "DESCARGA", 
            new List<Element> { discharge1 });
        network.AddBranch("MIXER_2", "DESCARGA", 
            new List<Element> { 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?
