using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using HydraulicSimulator.Models;
using CtrEditor.ObjetosSim;
namespace CtrEditor.HydraulicSimulator
{
///
/// Gestiona la simulación hidráulica de manera unificada, similar a SimulationManagerBEPU
/// Utiliza solo el patrón osBase + IHydraulicComponent + ApplyHydraulicResults
///
public class HydraulicSimulationManager : IDisposable
{
#region Properties and Fields
///
/// Red hidráulica principal que contiene todos los nodos y ramas
///
public HydraulicNetwork Network { get; private set; }
///
/// Lista de objetos simulables que tienen componentes hidráulicos
///
public List HydraulicObjects { get; private set; }
///
/// Fluido utilizado en la simulación (agua por defecto)
///
public Fluid SimulationFluid { get; set; }
///
/// Resultado de la última simulación
///
public SolutionResult LastSolutionResult { get; private set; }
///
/// Tiempo global de la simulación hidráulica
///
public float GlobalTime { get; private set; }
///
/// Indica si la simulación hidráulica está habilitada
///
public bool IsHydraulicSimulationEnabled { get; set; } = true;
///
/// Indica si la verificación de NPSH está habilitada
///
public bool EnableNPSHVerification { get; set; } = true;
///
/// Parámetros del solver
///
public int MaxIterations { get; set; } = 300;
public double Tolerance { get; set; } = 1e-3;
public double RelaxationFactor { get; set; } = 0.8;
public bool VerboseOutput { get; set; } = true;
///
/// Contador de pasos de simulación para optimizaciones
///
private int _stepCount = 0;
///
/// Cronómetro para medir tiempo de ejecución
///
private Stopwatch _stopwatch;
///
/// Diccionario para mapear IDs de objetos a elementos hidráulicos
///
private Dictionary _objectMapping;
///
/// Indica si hay cambios pendientes en la red que requieren reconstrucción
///
private bool _networkNeedsRebuild = true;
#endregion
#region Constructor
///
/// Constructor del gestor de simulación hidráulica
///
public HydraulicSimulationManager()
{
// Inicializar componentes
SimulationFluid = Fluid.Water20C; // Agua a 20°C por defecto
Network = new HydraulicNetwork(SimulationFluid);
HydraulicObjects = new List();
_objectMapping = new Dictionary();
_stopwatch = new Stopwatch();
// Inicializar resultado vacío
LastSolutionResult = new SolutionResult(false)
{
ErrorMessage = "Simulación no ejecutada"
};
GlobalTime = 0.0f;
Trace.WriteLine("HydraulicSimulationManager inicializado");
}
#endregion
#region Object Management
///
/// Registra un objeto simulable que tiene componentes hidráulicos
///
/// Objeto con componentes hidráulicos
public void RegisterHydraulicObject(osBase hydraulicObject)
{
if (hydraulicObject == null)
return;
if (!HydraulicObjects.Contains(hydraulicObject))
{
HydraulicObjects.Add(hydraulicObject);
_objectMapping[hydraulicObject.Nombre] = hydraulicObject;
_networkNeedsRebuild = true;
Trace.WriteLine($"Objeto hidráulico registrado: {hydraulicObject.Nombre}");
}
}
///
/// Desregistra un objeto simulable hidráulico
///
/// Objeto a desregistrar
public void UnregisterHydraulicObject(osBase hydraulicObject)
{
if (hydraulicObject == null)
return;
if (HydraulicObjects.Remove(hydraulicObject))
{
_objectMapping.Remove(hydraulicObject.Nombre);
_networkNeedsRebuild = true;
Trace.WriteLine($"Objeto hidráulico desregistrado: {hydraulicObject.Nombre}");
}
}
///
/// Limpia todos los objetos hidráulicos registrados
///
public void ClearHydraulicObjects()
{
HydraulicObjects.Clear();
_objectMapping.Clear();
_networkNeedsRebuild = true;
Trace.WriteLine("Todos los objetos hidráulicos han sido limpiados");
}
#endregion
#region Network Building
///
/// Reconstruye la red hidráulica basada en los objetos registrados
///
private void RebuildNetwork()
{
if (!_networkNeedsRebuild)
return;
Trace.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;
Trace.WriteLine($"Red reconstruida: {Network.Nodes.Count} nodos, {Network.Branches.Count} ramas");
}
///
/// Construye los nodos de la red basándose en los objetos hidráulicos
///
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);
if (VerboseOutput)
{
Trace.WriteLine($"Nodo agregado: {nodeDef.Name} " +
$"(Presión fija: {nodeDef.IsFixedPressure}, " +
$"Presión: {nodeDef.Pressure?.ToString() ?? "libre"})");
}
}
}
}
}
///
/// Construye las ramas (conexiones) entre nodos
///
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 { elemDef.Element };
Network.AddBranch(elemDef.FromNode, elemDef.ToNode, elements, elemDef.Name);
if (VerboseOutput)
{
Trace.WriteLine($"Rama agregada: {elemDef.Name} " +
$"({elemDef.FromNode} -> {elemDef.ToNode})");
}
}
}
}
}
#endregion
#region Simulation Step
///
/// Ejecuta un paso de simulación hidráulica (equivalente al Step de BEPU)
///
public void Step(float deltaTime = 0.01f) // 10ms por defecto (100 FPS hidráulico)
{
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 usando ApplyHydraulicResults
ApplyResultsToObjects();
if (VerboseOutput && _stepCount % 100 == 0) // Log cada segundo aprox
{
Trace.WriteLine($"Simulación hidráulica - Paso {_stepCount}: Convergió exitosamente");
}
}
else
{
Trace.WriteLine($"❌ Simulación hidráulica no convergió: {LastSolutionResult.ErrorMessage}");
Trace.WriteLine($" Iteraciones: {LastSolutionResult.Iterations}, Residual: {LastSolutionResult.Residual:E6}");
Trace.WriteLine($" Tolerancia requerida: {Tolerance:E6}");
}
}
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}"
};
Trace.WriteLine($"Error en HydraulicSimulationManager.Step: {ex}");
}
finally
{
_stopwatch.Stop();
if (VerboseOutput && _stepCount % 100 == 0) // Log cada segundo aprox
{
Trace.WriteLine($"Simulación hidráulica - Paso {_stepCount}: {_stopwatch.ElapsedMilliseconds}ms, " +
$"Objetos: {HydraulicObjects.Count}, Convergió: {LastSolutionResult.Converged}");
}
}
}
///
/// Actualiza las propiedades de los objetos antes de resolver la red
///
private void UpdateObjectProperties()
{
// Actualizar objetos hidráulicos antes de la simulación
foreach (var obj in HydraulicObjects)
{
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
{
hydraulicComponent.UpdateHydraulicProperties();
}
}
}
///
/// Aplica los resultados de la simulación a los objetos usando ApplyHydraulicResults
/// Este es el único punto donde se actualizan los resultados, siguiendo el patrón BEPU
///
private void ApplyResultsToObjects()
{
// Aplicar resultados a todos los objetos hidráulicos usando ApplyHydraulicResults
foreach (var obj in HydraulicObjects)
{
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
{
hydraulicComponent.ApplyHydraulicResults(LastSolutionResult.Flows, LastSolutionResult.Pressures);
}
}
}
#endregion
#region Public Methods
///
/// Fuerza la reconstrucción de la red en el próximo paso
///
public void InvalidateNetwork()
{
_networkNeedsRebuild = true;
}
///
/// Obtiene estadísticas de la simulación
///
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}";
}
///
/// Reinicia la simulación
///
public void Reset()
{
GlobalTime = 0.0f;
_stepCount = 0;
_networkNeedsRebuild = true;
// Reinicializar resultado
LastSolutionResult = new SolutionResult(false)
{
ErrorMessage = "Simulación reiniciada"
};
Trace.WriteLine("Simulación hidráulica reiniciada");
}
///
/// Configura los parámetros de verificación de NPSH
///
public void ConfigureNPSHSettings(bool enableNPSH, double npshRequired = 3.0, double vaporPressure = 2337.0, double suctionLosses = 0.5)
{
EnableNPSHVerification = enableNPSH;
// Invalidar la red para que se reconstruya con los nuevos parámetros
InvalidateNetwork();
Trace.WriteLine($"Verificación NPSH {(enableNPSH ? "habilitada" : "deshabilitada")}: NPSH_req={npshRequired}m, P_vapor={vaporPressure}Pa");
}
#endregion
#region IDisposable
private bool _disposed = false;
///
/// Libera recursos del gestor de simulación
///
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();
Trace.WriteLine("HydraulicSimulationManager disposed");
}
_disposed = true;
}
}
#endregion
}
}