951 lines
36 KiB
C#
951 lines
36 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using HydraulicSimulator.Models;
|
||
using CtrEditor.ObjetosSim;
|
||
|
||
namespace CtrEditor.HydraulicSimulator
|
||
{
|
||
/// <summary>
|
||
/// Gestiona la simulación hidráulica similar a SimulationManagerBEPU
|
||
/// </summary>
|
||
public class HydraulicSimulationManager : IDisposable
|
||
{
|
||
#region Properties and Fields
|
||
|
||
/// <summary>
|
||
/// Red hidráulica principal que contiene todos los nodos y ramas
|
||
/// </summary>
|
||
public HydraulicNetwork Network { get; private set; }
|
||
|
||
/// <summary>
|
||
/// Lista de objetos simulables que tienen componentes hidráulicos
|
||
/// </summary>
|
||
public List<osBase> HydraulicObjects { get; private set; }
|
||
|
||
/// <summary>
|
||
/// Fluido utilizado en la simulación (agua por defecto)
|
||
/// </summary>
|
||
public Fluid SimulationFluid { get; set; }
|
||
|
||
/// <summary>
|
||
/// Resultado de la última simulación
|
||
/// </summary>
|
||
public SolutionResult LastSolutionResult { get; private set; }
|
||
|
||
/// <summary>
|
||
/// Tiempo global de la simulación hidráulica
|
||
/// </summary>
|
||
public float GlobalTime { get; private set; }
|
||
|
||
/// <summary>
|
||
/// Indica si la simulación hidráulica está habilitada
|
||
/// </summary>
|
||
public bool IsHydraulicSimulationEnabled { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// Indica si la verificación de NPSH está habilitada
|
||
/// </summary>
|
||
public bool EnableNPSHVerification { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// Parámetros del solver
|
||
/// </summary>
|
||
public int MaxIterations { get; set; } = 300;
|
||
public double Tolerance { get; set; } = 1e-3; // Tolerancia más relajada para convergencia inicial
|
||
public double RelaxationFactor { get; set; } = 0.8;
|
||
public bool VerboseOutput { get; set; } = false; // Activado para debugging
|
||
|
||
/// <summary>
|
||
/// Contador de pasos de simulación para optimizaciones
|
||
/// </summary>
|
||
private int _stepCount = 0;
|
||
|
||
/// <summary>
|
||
/// Cronómetro para medir tiempo de ejecución
|
||
/// </summary>
|
||
private Stopwatch _stopwatch;
|
||
|
||
/// <summary>
|
||
/// Diccionario para mapear IDs de objetos a elementos hidráulicos
|
||
/// </summary>
|
||
private Dictionary<string, osBase> _objectMapping;
|
||
|
||
/// <summary>
|
||
/// Indica si hay cambios pendientes en la red que requieren reconstrucción
|
||
/// </summary>
|
||
private bool _networkNeedsRebuild = true;
|
||
|
||
#endregion
|
||
|
||
#region Constructor
|
||
|
||
/// <summary>
|
||
/// Constructor del gestor de simulación hidráulica
|
||
/// </summary>
|
||
public HydraulicSimulationManager()
|
||
{
|
||
// Inicializar componentes
|
||
SimulationFluid = Fluid.Water20C; // Agua a 20°C por defecto
|
||
Network = new HydraulicNetwork(SimulationFluid);
|
||
HydraulicObjects = new List<osBase>();
|
||
_objectMapping = new Dictionary<string, osBase>();
|
||
_stopwatch = new Stopwatch();
|
||
|
||
// Inicializar resultado vacío
|
||
LastSolutionResult = new SolutionResult(false)
|
||
{
|
||
ErrorMessage = "Simulación no ejecutada"
|
||
};
|
||
|
||
GlobalTime = 0.0f;
|
||
|
||
Debug.WriteLine("HydraulicSimulationManager inicializado");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Object Management
|
||
|
||
/// <summary>
|
||
/// Registra un objeto simulable que tiene componentes hidráulicos
|
||
/// </summary>
|
||
/// <param name="hydraulicObject">Objeto con componentes hidráulicos</param>
|
||
public void RegisterHydraulicObject(osBase hydraulicObject)
|
||
{
|
||
if (hydraulicObject == null)
|
||
return;
|
||
|
||
if (!HydraulicObjects.Contains(hydraulicObject))
|
||
{
|
||
HydraulicObjects.Add(hydraulicObject);
|
||
_objectMapping[hydraulicObject.Nombre] = hydraulicObject;
|
||
_networkNeedsRebuild = true;
|
||
|
||
// Debug.WriteLine($"Objeto hidráulico registrado: {hydraulicObject.Nombre}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Desregistra un objeto simulable hidráulico
|
||
/// </summary>
|
||
/// <param name="hydraulicObject">Objeto a desregistrar</param>
|
||
public void UnregisterHydraulicObject(osBase hydraulicObject)
|
||
{
|
||
if (hydraulicObject == null)
|
||
return;
|
||
|
||
if (HydraulicObjects.Remove(hydraulicObject))
|
||
{
|
||
_objectMapping.Remove(hydraulicObject.Nombre);
|
||
_networkNeedsRebuild = true;
|
||
|
||
// Debug.WriteLine($"Objeto hidráulico desregistrado: {hydraulicObject.Nombre}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Limpia todos los objetos hidráulicos registrados
|
||
/// </summary>
|
||
public void ClearHydraulicObjects()
|
||
{
|
||
HydraulicObjects.Clear();
|
||
_objectMapping.Clear();
|
||
_networkNeedsRebuild = true;
|
||
|
||
// Debug.WriteLine("Todos los objetos hidráulicos han sido limpiados");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Network Building
|
||
|
||
/// <summary>
|
||
/// Reconstruye la red hidráulica basada en los objetos registrados
|
||
/// </summary>
|
||
private void RebuildNetwork()
|
||
{
|
||
if (!_networkNeedsRebuild)
|
||
return;
|
||
|
||
// Debug.WriteLine("Reconstruyendo red hidráulica...");
|
||
|
||
// Crear nueva red
|
||
Network = new HydraulicNetwork(SimulationFluid);
|
||
|
||
// Agregar nodos y elementos basados en los objetos hidráulicos
|
||
BuildNodesFromObjects();
|
||
BuildBranchesFromObjects();
|
||
|
||
_networkNeedsRebuild = false;
|
||
|
||
// Debug.WriteLine($"Red reconstruida: {Network.Nodes.Count} nodos, {Network.Branches.Count} ramas");
|
||
|
||
// Verbose output deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput)
|
||
{
|
||
Debug.WriteLine("=== DETALLES DE LA RED HIDRÁULICA ===");
|
||
Debug.WriteLine("Nodos:");
|
||
foreach (var node in Network.Nodes)
|
||
{
|
||
string pressureInfo = node.Value.FixedP ? $"Presión fija: {node.Value.P:F0} Pa" : "Presión libre";
|
||
Debug.WriteLine($" - {node.Key}: {pressureInfo}");
|
||
}
|
||
Debug.WriteLine("Ramas:");
|
||
foreach (var branch in Network.Branches)
|
||
{
|
||
Debug.WriteLine($" - {branch.Name}: {branch.N1} -> {branch.N2} ({branch.Elements.Count} elementos)");
|
||
foreach (var element in branch.Elements)
|
||
{
|
||
Debug.WriteLine($" * {element.GetType().Name}");
|
||
}
|
||
}
|
||
Debug.WriteLine("=== FIN DETALLES RED ===");
|
||
}
|
||
*/
|
||
}
|
||
|
||
/// <summary>
|
||
/// Construye los nodos de la red basándose en los objetos hidráulicos
|
||
/// </summary>
|
||
private void BuildNodesFromObjects()
|
||
{
|
||
foreach (var obj in HydraulicObjects)
|
||
{
|
||
// Verificar si el objeto implementa la interfaz hidráulica
|
||
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||
{
|
||
// Obtener nodos definidos por el objeto
|
||
var nodeDefinitions = hydraulicComponent.GetHydraulicNodes();
|
||
|
||
foreach (var nodeDef in nodeDefinitions)
|
||
{
|
||
// Agregar nodo a la red
|
||
Network.AddNode(nodeDef.Name, nodeDef.IsFixedPressure ? nodeDef.Pressure : null);
|
||
|
||
// Verbose output deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput)
|
||
{
|
||
Debug.WriteLine($"Nodo agregado: {nodeDef.Name} " +
|
||
$"(Presión fija: {nodeDef.IsFixedPressure}, " +
|
||
$"Presión: {nodeDef.Pressure?.ToString() ?? "libre"})");
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Objeto sin interfaz hidráulica - crear nodos básicos por compatibilidad
|
||
string nodeName = $"Node_{obj.Nombre}";
|
||
bool isFixedPressure = DetermineIfFixedPressure(obj);
|
||
double? pressure = isFixedPressure ? GetObjectPressure(obj) : null;
|
||
|
||
Network.AddNode(nodeName, pressure);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Construye las ramas (conexiones) entre nodos
|
||
/// </summary>
|
||
private void BuildBranchesFromObjects()
|
||
{
|
||
foreach (var obj in HydraulicObjects)
|
||
{
|
||
// Verificar si el objeto implementa la interfaz hidráulica
|
||
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||
{
|
||
// Obtener elementos definidos por el objeto
|
||
var elementDefinitions = hydraulicComponent.GetHydraulicElements();
|
||
|
||
foreach (var elemDef in elementDefinitions)
|
||
{
|
||
// Crear rama con el elemento
|
||
var elements = new List<Element> { elemDef.Element };
|
||
Network.AddBranch(elemDef.FromNode, elemDef.ToNode, elements, elemDef.Name);
|
||
|
||
// Verbose output deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput)
|
||
{
|
||
Debug.WriteLine($"Rama agregada: {elemDef.Name} " +
|
||
$"({elemDef.FromNode} -> {elemDef.ToNode})");
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Objeto sin interfaz hidráulica - crear elementos básicos por compatibilidad
|
||
var elements = CreateHydraulicElementsFromObject(obj);
|
||
|
||
if (elements.Any())
|
||
{
|
||
string fromNode = GetSourceNode(obj);
|
||
string toNode = GetTargetNode(obj);
|
||
|
||
if (!string.IsNullOrEmpty(fromNode) && !string.IsNullOrEmpty(toNode))
|
||
{
|
||
Network.AddBranch(fromNode, toNode, elements, $"Branch_{obj.Nombre}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Helper Methods for Object Analysis
|
||
|
||
/// <summary>
|
||
/// Determina si un objeto debe tener presión fija
|
||
/// </summary>
|
||
private bool DetermineIfFixedPressure(osBase obj)
|
||
{
|
||
// Implementación que será extendida con tipos específicos
|
||
// Por ejemplo: tanques, descargas atmosféricas = presión fija
|
||
// Uniones, conexiones = presión libre
|
||
|
||
return false; // Por defecto, presión libre
|
||
}
|
||
|
||
/// <summary>
|
||
/// Obtiene la presión de un objeto (si tiene presión fija)
|
||
/// </summary>
|
||
private double GetObjectPressure(osBase obj)
|
||
{
|
||
// Implementación que será extendida
|
||
return 0.0; // Por defecto, presión atmosférica
|
||
}
|
||
|
||
/// <summary>
|
||
/// Crea elementos hidráulicos basándose en un objeto
|
||
/// </summary>
|
||
private List<Element> CreateHydraulicElementsFromObject(osBase obj)
|
||
{
|
||
var elements = new List<Element>();
|
||
|
||
// Implementación que será extendida con tipos específicos
|
||
// Por ejemplo:
|
||
// - Si es una bomba: crear PumpHQ
|
||
// - Si es una tubería: crear Pipe
|
||
// - Si es una válvula: crear ValveKv
|
||
|
||
return elements;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Obtiene el nodo fuente de un objeto
|
||
/// </summary>
|
||
private string GetSourceNode(osBase obj)
|
||
{
|
||
// Implementación que será extendida
|
||
return $"Node_{obj.Nombre}_In";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Obtiene el nodo destino de un objeto
|
||
/// </summary>
|
||
private string GetTargetNode(osBase obj)
|
||
{
|
||
// Implementación que será extendida
|
||
return $"Node_{obj.Nombre}_Out";
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Simulation Step
|
||
|
||
/// <summary>
|
||
/// Ejecuta un paso de simulación hidráulica (equivalente al Step de BEPU)
|
||
/// </summary>
|
||
public void Step(float deltaTime = 0.016f) // ~60 FPS por defecto
|
||
{
|
||
if (!IsHydraulicSimulationEnabled)
|
||
return;
|
||
|
||
_stopwatch.Restart();
|
||
|
||
try
|
||
{
|
||
// Actualizar tiempo global
|
||
GlobalTime += deltaTime;
|
||
_stepCount++;
|
||
|
||
// Reconstruir red si es necesario
|
||
RebuildNetwork();
|
||
|
||
// Solo resolver si tenemos objetos hidráulicos
|
||
if (HydraulicObjects.Count > 0 && Network.Branches.Count > 0)
|
||
{
|
||
// Actualizar propiedades de objetos antes de resolver
|
||
UpdateObjectProperties();
|
||
|
||
// Resolver la red hidráulica
|
||
LastSolutionResult = Network.Solve(
|
||
MaxIterations,
|
||
Tolerance,
|
||
RelaxationFactor,
|
||
VerboseOutput
|
||
);
|
||
|
||
if (LastSolutionResult.Converged)
|
||
{
|
||
// Aplicar resultados a los objetos
|
||
ApplyResultsToObjects();
|
||
|
||
// Verbose output de resultados deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput && _stepCount % 300 == 0) // Log cada 5 segundos aprox
|
||
{
|
||
//Debug.WriteLine("=== RESULTADOS SIMULACIÓN HIDRÁULICA ===");
|
||
//Debug.WriteLine("Flujos:");
|
||
foreach (var flow in LastSolutionResult.Flows)
|
||
{
|
||
Debug.WriteLine($" {flow.Key}: {flow.Value:F6} m³/s ({flow.Value * 3600:F2} m³/h)");
|
||
}
|
||
Debug.WriteLine("Presiones:");
|
||
foreach (var pressure in LastSolutionResult.Pressures)
|
||
{
|
||
Debug.WriteLine($" {pressure.Key}: {pressure.Value:F0} Pa ({pressure.Value / 100000:F2} bar)");
|
||
}
|
||
Debug.WriteLine("=== FIN RESULTADOS ===");
|
||
}
|
||
*/
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"❌ Simulación hidráulica no convergió: {LastSolutionResult.ErrorMessage}");
|
||
Debug.WriteLine($" Iteraciones: {LastSolutionResult.Iterations}, Residual: {LastSolutionResult.Residual:E6}");
|
||
Debug.WriteLine($" Tolerancia requerida: {Tolerance:E6}");
|
||
|
||
// Diagnóstico detallado deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput && _stepCount % 60 == 0) // Log detallado cada segundo aprox
|
||
{
|
||
Debug.WriteLine("=== DIAGNÓSTICO CONVERGENCIA ===");
|
||
Debug.WriteLine($"Nodos en red: {Network.Nodes.Count}");
|
||
Debug.WriteLine($"Ramas en red: {Network.Branches.Count}");
|
||
foreach (var node in Network.Nodes)
|
||
{
|
||
string info = node.Value.FixedP ? $"FIJA={node.Value.P:F0}Pa" : "LIBRE";
|
||
Debug.WriteLine($" Nodo {node.Key}: {info}");
|
||
}
|
||
Debug.WriteLine("=== FIN DIAGNÓSTICO ===");
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Sin objetos hidráulicos, resultado exitoso pero vacío
|
||
LastSolutionResult = new SolutionResult(true);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LastSolutionResult = new SolutionResult(false)
|
||
{
|
||
ErrorMessage = $"Error en simulación hidráulica: {ex.Message}"
|
||
};
|
||
|
||
Debug.WriteLine($"Error en HydraulicSimulationManager.Step: {ex}");
|
||
}
|
||
finally
|
||
{
|
||
_stopwatch.Stop();
|
||
|
||
// Logging de tiempo deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput && _stepCount % 60 == 0) // Log cada segundo aprox
|
||
{
|
||
Debug.WriteLine($"Simulación hidráulica - Paso {_stepCount}: {_stopwatch.ElapsedMilliseconds}ms, " +
|
||
$"Objetos: {HydraulicObjects.Count}, Convergió: {LastSolutionResult.Converged}");
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Actualiza las propiedades de los objetos antes de resolver la red
|
||
/// </summary>
|
||
private void UpdateObjectProperties()
|
||
{
|
||
// Actualizar objetos hidráulicos específicos
|
||
foreach (var simObj in HydraulicSimObjects)
|
||
{
|
||
simObj.UpdateProperties();
|
||
}
|
||
|
||
// Actualizar objetos hidráulicos generales (legacy)
|
||
foreach (var obj in HydraulicObjects)
|
||
{
|
||
// Aquí se implementará la actualización de propiedades específicas
|
||
// Por ejemplo: estado de válvulas, velocidad de bombas, etc.
|
||
UpdateObjectHydraulicProperties(obj);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Actualiza las propiedades hidráulicas específicas de un objeto
|
||
/// </summary>
|
||
private void UpdateObjectHydraulicProperties(osBase obj)
|
||
{
|
||
// Actualizar propiedades basándose en las interfaces implementadas
|
||
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||
{
|
||
// Llamar al método de actualización del objeto
|
||
hydraulicComponent.UpdateHydraulicProperties();
|
||
}
|
||
|
||
// Actualización específica por tipo de objeto
|
||
if (obj is IHydraulicPump pump)
|
||
{
|
||
// Verificar estado de la bomba, velocidad, etc.
|
||
UpdatePumpProperties(pump);
|
||
}
|
||
else if (obj is IHydraulicValve valve)
|
||
{
|
||
// Verificar apertura de la válvula
|
||
UpdateValveProperties(valve);
|
||
}
|
||
else if (obj is IHydraulicTank tank)
|
||
{
|
||
// Actualizar nivel y presión del tanque
|
||
UpdateTankProperties(tank);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Actualiza propiedades específicas de bombas
|
||
/// </summary>
|
||
private void UpdatePumpProperties(IHydraulicPump pump)
|
||
{
|
||
// Aquí se pueden hacer validaciones y ajustes específicos de bombas
|
||
// Por ejemplo, limitar velocidad, verificar estado de operación, etc.
|
||
|
||
if (pump.SpeedRatio < 0.0) pump.SpeedRatio = 0.0;
|
||
if (pump.SpeedRatio > 1.0) pump.SpeedRatio = 1.0;
|
||
|
||
// Debug output deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput)
|
||
{
|
||
Debug.WriteLine($"Bomba {pump.GetType().Name}: Velocidad={pump.SpeedRatio:F2}, " +
|
||
$"Funcionando={pump.IsRunning}, Dirección={pump.PumpDirection}");
|
||
}
|
||
*/
|
||
}
|
||
|
||
/// <summary>
|
||
/// Actualiza propiedades específicas de válvulas
|
||
/// </summary>
|
||
private void UpdateValveProperties(IHydraulicValve valve)
|
||
{
|
||
// Validar apertura de válvula
|
||
if (valve.Opening < 0.0) valve.Opening = 0.0;
|
||
if (valve.Opening > 1.0) valve.Opening = 1.0;
|
||
|
||
// Debug output deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput)
|
||
{
|
||
Debug.WriteLine($"Válvula {valve.GetType().Name}: Apertura={valve.Opening:F2}, " +
|
||
$"Cerrada={valve.IsClosed}, Abierta={valve.IsFullyOpen}");
|
||
}
|
||
*/
|
||
}
|
||
|
||
/// <summary>
|
||
/// Actualiza propiedades específicas de tanques
|
||
/// </summary>
|
||
private void UpdateTankProperties(IHydraulicTank tank)
|
||
{
|
||
// Calcular presión basada en nivel si no es presión fija
|
||
if (!tank.IsFixedPressure)
|
||
{
|
||
// P = ρgh + P_atmosferica
|
||
double pressureFromLevel = SimulationFluid.Rho * 9.80665 * tank.Level; // + presión atmosférica
|
||
tank.TankPressure = pressureFromLevel;
|
||
}
|
||
|
||
// Debug output deshabilitado para mejorar rendimiento
|
||
/*
|
||
if (VerboseOutput)
|
||
{
|
||
Debug.WriteLine($"Tanque {tank.GetType().Name}: Nivel={tank.Level:F2}m, " +
|
||
$"Presión={tank.TankPressure:F0}Pa, PresionFija={tank.IsFixedPressure}");
|
||
}
|
||
*/
|
||
}
|
||
|
||
/// <summary>
|
||
/// Aplica los resultados de la simulación a los objetos
|
||
/// </summary>
|
||
private void ApplyResultsToObjects()
|
||
{
|
||
// Aplicar resultados a objetos hidráulicos específicos
|
||
foreach (var simObj in HydraulicSimObjects)
|
||
{
|
||
ApplyResultsToSimObject(simObj);
|
||
simObj.ApplySimulationResults();
|
||
}
|
||
|
||
// Aplicar resultados a objetos hidráulicos generales (legacy)
|
||
foreach (var obj in HydraulicObjects)
|
||
{
|
||
// Aplicar caudales y presiones calculados al objeto
|
||
ApplyResultsToObject(obj);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Aplica los resultados de la simulación a un objeto específico
|
||
/// </summary>
|
||
private void ApplyResultsToSimObject(simHydraulicBase simObj)
|
||
{
|
||
// Aplicar resultados específicos según el tipo de objeto
|
||
if (simObj is simHydraulicPump pump)
|
||
{
|
||
// Buscar flujo y presión de la bomba en los resultados
|
||
string pumpBranchName = $"{pump.Nombre}_Pump";
|
||
if (LastSolutionResult.Flows.TryGetValue(pumpBranchName, out double flow))
|
||
{
|
||
pump.CurrentFlow = flow;
|
||
}
|
||
|
||
string inletNodeName = $"{pump.Nombre}_In";
|
||
if (LastSolutionResult.Pressures.TryGetValue(inletNodeName, out double pressure))
|
||
{
|
||
pump.CurrentPressure = pressure;
|
||
}
|
||
}
|
||
else if (simObj is simHydraulicTank tank)
|
||
{
|
||
// Buscar flujos de entrada y salida del tanque
|
||
// Esto requeriría mapeo de tuberías conectadas
|
||
// Por ahora, aplicar presión del tanque
|
||
string tankNodeName = $"{tank.Nombre}_Tank";
|
||
if (LastSolutionResult.Pressures.TryGetValue(tankNodeName, out double pressure))
|
||
{
|
||
tank.CurrentPressure = pressure;
|
||
}
|
||
}
|
||
else if (simObj is simHydraulicPipe pipe)
|
||
{
|
||
// Buscar flujo a través de la tubería
|
||
string pipeBranchName = $"{pipe.Nombre}_Pipe";
|
||
if (LastSolutionResult.Flows.TryGetValue(pipeBranchName, out double flow))
|
||
{
|
||
pipe.CurrentFlow = flow;
|
||
}
|
||
|
||
// Calcular pérdida de presión
|
||
string fromNode = pipe.ComponenteAId;
|
||
string toNode = pipe.ComponenteBId;
|
||
if (LastSolutionResult.Pressures.TryGetValue(fromNode, out double pressureA) &&
|
||
LastSolutionResult.Pressures.TryGetValue(toNode, out double pressureB))
|
||
{
|
||
pipe.PressureDrop = pressureA - pressureB;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Aplica los resultados a un objeto específico
|
||
/// </summary>
|
||
private void ApplyResultsToObject(osBase obj)
|
||
{
|
||
// Aplicar resultados usando las interfaces hidráulicas
|
||
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||
{
|
||
// Llamar al método de aplicación de resultados del objeto
|
||
hydraulicComponent.ApplyHydraulicResults(LastSolutionResult.Flows, LastSolutionResult.Pressures);
|
||
}
|
||
|
||
// Aplicación específica por tipo de interfaz
|
||
if (obj is IHydraulicFlowReceiver flowReceiver)
|
||
{
|
||
// Buscar caudal asociado al objeto
|
||
string branchName = FindBranchNameForObject(obj);
|
||
if (!string.IsNullOrEmpty(branchName) && LastSolutionResult.Flows.ContainsKey(branchName))
|
||
{
|
||
double flow = LastSolutionResult.Flows[branchName];
|
||
flowReceiver.SetFlow(flow);
|
||
}
|
||
}
|
||
|
||
if (obj is IHydraulicPressureReceiver pressureReceiver)
|
||
{
|
||
// Buscar presión asociada al objeto
|
||
string nodeName = FindNodeNameForObject(obj);
|
||
if (!string.IsNullOrEmpty(nodeName) && LastSolutionResult.Pressures.ContainsKey(nodeName))
|
||
{
|
||
double pressure = LastSolutionResult.Pressures[nodeName];
|
||
pressureReceiver.SetPressure(pressure);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Encuentra el nombre de rama asociado a un objeto
|
||
/// </summary>
|
||
private string FindBranchNameForObject(osBase obj)
|
||
{
|
||
// Buscar en las ramas de la red
|
||
foreach (var branch in Network.Branches)
|
||
{
|
||
if (branch.Name.Contains(obj.Nombre))
|
||
{
|
||
return branch.Name;
|
||
}
|
||
}
|
||
|
||
// Fallback a nombre por defecto
|
||
return $"Branch_{obj.Nombre}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Encuentra el nombre de nodo asociado a un objeto
|
||
/// </summary>
|
||
private string FindNodeNameForObject(osBase obj)
|
||
{
|
||
// Buscar en los nodos de la red
|
||
foreach (var nodeKvp in Network.Nodes)
|
||
{
|
||
if (nodeKvp.Key.Contains(obj.Nombre))
|
||
{
|
||
return nodeKvp.Key;
|
||
}
|
||
}
|
||
|
||
// Fallback a nombre por defecto
|
||
return $"Node_{obj.Nombre}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Establece el caudal en un objeto
|
||
/// </summary>
|
||
private void SetObjectFlow(osBase obj, double flow)
|
||
{
|
||
if (obj is IHydraulicFlowReceiver flowReceiver)
|
||
{
|
||
flowReceiver.SetFlow(flow);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Establece la presión en un objeto
|
||
/// </summary>
|
||
private void SetObjectPressure(osBase obj, double pressure)
|
||
{
|
||
if (obj is IHydraulicPressureReceiver pressureReceiver)
|
||
{
|
||
pressureReceiver.SetPressure(pressure);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Public Methods
|
||
|
||
/// <summary>
|
||
/// Fuerza la reconstrucción de la red en el próximo paso
|
||
/// </summary>
|
||
public void InvalidateNetwork()
|
||
{
|
||
_networkNeedsRebuild = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Obtiene estadísticas de la simulación
|
||
/// </summary>
|
||
public string GetSimulationStats()
|
||
{
|
||
return $"Objetos hidráulicos: {HydraulicObjects.Count}\n" +
|
||
$"Nodos: {Network.Nodes.Count}\n" +
|
||
$"Ramas: {Network.Branches.Count}\n" +
|
||
$"Última simulación convergió: {LastSolutionResult.Converged}\n" +
|
||
$"Iteraciones: {LastSolutionResult.Iterations}\n" +
|
||
$"Residual: {LastSolutionResult.Residual:E3}\n" +
|
||
$"Tiempo global: {GlobalTime:F2}s\n" +
|
||
$"Pasos: {_stepCount}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Reinicia la simulación
|
||
/// </summary>
|
||
public void Reset()
|
||
{
|
||
GlobalTime = 0.0f;
|
||
_stepCount = 0;
|
||
_networkNeedsRebuild = true;
|
||
|
||
// Reinicializar resultado
|
||
LastSolutionResult = new SolutionResult(false)
|
||
{
|
||
ErrorMessage = "Simulación reiniciada"
|
||
};
|
||
|
||
Debug.WriteLine("Simulación hidráulica reiniciada");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Configura los parámetros de verificación de NPSH
|
||
/// </summary>
|
||
public void ConfigureNPSHSettings(bool enableNPSH, double npshRequired = 3.0, double vaporPressure = 2337.0, double suctionLosses = 0.5)
|
||
{
|
||
EnableNPSHVerification = enableNPSH;
|
||
|
||
// Actualizar todas las bombas existentes con los nuevos parámetros
|
||
foreach (var obj in HydraulicObjects)
|
||
{
|
||
if (obj is osHydPump pump)
|
||
{
|
||
// Los parámetros se aplicarán cuando se reconstruya la red
|
||
InvalidateNetwork();
|
||
}
|
||
}
|
||
|
||
Debug.WriteLine($"Verificación NPSH {(enableNPSH ? "habilitada" : "deshabilitada")}: NPSH_req={npshRequired}m, P_vapor={vaporPressure}Pa");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region IDisposable
|
||
|
||
private bool _disposed = false;
|
||
|
||
/// <summary>
|
||
/// Libera recursos del gestor de simulación
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
Dispose(true);
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
protected virtual void Dispose(bool disposing)
|
||
{
|
||
if (!_disposed)
|
||
{
|
||
if (disposing)
|
||
{
|
||
// Limpiar recursos gestionados
|
||
HydraulicObjects?.Clear();
|
||
_objectMapping?.Clear();
|
||
Network = null;
|
||
_stopwatch?.Stop();
|
||
|
||
Debug.WriteLine("HydraulicSimulationManager disposed");
|
||
}
|
||
|
||
_disposed = true;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Hydraulic Object Creation
|
||
|
||
/// <summary>
|
||
/// Lista de objetos hidráulicos específicos del simulador
|
||
/// </summary>
|
||
public List<simHydraulicBase> HydraulicSimObjects { get; private set; } = new List<simHydraulicBase>();
|
||
|
||
/// <summary>
|
||
/// Crea una bomba hidráulica en la simulación
|
||
/// </summary>
|
||
public simHydraulicPump AddPump(double pumpHead, double maxFlow, double speedRatio, bool isRunning, int direction)
|
||
{
|
||
var pump = new simHydraulicPump(this)
|
||
{
|
||
PumpHead = pumpHead,
|
||
MaxFlow = maxFlow,
|
||
SpeedRatio = speedRatio,
|
||
IsRunning = isRunning,
|
||
PumpDirection = direction
|
||
};
|
||
|
||
HydraulicSimObjects.Add(pump);
|
||
_networkNeedsRebuild = true;
|
||
|
||
Debug.WriteLine($"Bomba hidráulica creada: H={pumpHead}m, Q={maxFlow}m³/s");
|
||
return pump;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Crea un tanque hidráulico en la simulación
|
||
/// </summary>
|
||
public simHydraulicTank AddTank(double pressure, double currentLevel, double maxLevel, double minLevel, double crossSectionalArea, bool isFixedPressure)
|
||
{
|
||
var tank = new simHydraulicTank(this)
|
||
{
|
||
TankPressure = pressure,
|
||
CurrentLevel = currentLevel,
|
||
MaxLevel = maxLevel,
|
||
MinLevel = minLevel,
|
||
CrossSectionalArea = crossSectionalArea,
|
||
IsFixedPressure = isFixedPressure
|
||
};
|
||
|
||
HydraulicSimObjects.Add(tank);
|
||
_networkNeedsRebuild = true;
|
||
|
||
Debug.WriteLine($"Tanque hidráulico creado: P={pressure}Pa, Nivel={currentLevel}m");
|
||
return tank;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Crea una tubería hidráulica en la simulación
|
||
/// </summary>
|
||
public simHydraulicPipe AddPipe(double length, double diameter, double roughness, string componenteAId, string componenteBId)
|
||
{
|
||
var pipe = new simHydraulicPipe(this)
|
||
{
|
||
Length = length,
|
||
Diameter = diameter,
|
||
Roughness = roughness,
|
||
ComponenteAId = componenteAId,
|
||
ComponenteBId = componenteBId
|
||
};
|
||
|
||
HydraulicSimObjects.Add(pipe);
|
||
_networkNeedsRebuild = true;
|
||
|
||
Debug.WriteLine($"Tubería hidráulica creada: L={length}m, D={diameter}m");
|
||
return pipe;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Remueve un objeto hidráulico de la simulación
|
||
/// </summary>
|
||
public void Remove(simHydraulicBase hydraulicObject)
|
||
{
|
||
if (hydraulicObject != null && HydraulicSimObjects.Contains(hydraulicObject))
|
||
{
|
||
HydraulicSimObjects.Remove(hydraulicObject);
|
||
_networkNeedsRebuild = true;
|
||
|
||
Debug.WriteLine($"Objeto hidráulico removido: {hydraulicObject.SimObjectType}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Limpia todos los objetos hidráulicos específicos
|
||
/// </summary>
|
||
public void ClearHydraulicSimObjects()
|
||
{
|
||
HydraulicSimObjects.Clear();
|
||
_networkNeedsRebuild = true;
|
||
|
||
Debug.WriteLine("Todos los objetos hidráulicos específicos han sido limpiados");
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|