CtrEditor/Documentation/HydraulicSimulator DOC.md

398 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 📖 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?