import entropyk
import math

def build_complete_system():
    # ── 1. Initialisation du graphe du système ──
    system = entropyk.System()
    print("Construction du système Entropyk complet...")

    # Paramètres fluides
    refrigerant = "R410A"
    water = "Water"

    # =========================================================================
    # BOUCLE 1 : CIRCUIT FRIGORIFIQUE (REFRIGERANT R410A)
    # =========================================================================
    
    # 1.1 Compresseur (Modèle Polynomial AHRI 540)
    compressor = system.add_component(entropyk.Compressor(
        m1=0.85, m2=2.5, m3=500.0, m4=1500.0, m5=-2.5, m6=1.8, m7=600.0, m8=1600.0, m9=-3.0, m10=2.0,
        speed_rpm=3600.0,
        displacement=0.00008,
        efficiency=0.88,
        fluid=refrigerant
    ))
    
    # 1.2 Tuyau de refoulement (vers condenseur)
    pipe_hot = system.add_component(entropyk.Pipe(
        length=5.0,
        diameter=0.02,
        fluid=refrigerant
    ))
    
    # 1.3 Condenseur (Rejet de chaleur)
    condenser = system.add_component(entropyk.Condenser(
        ua=4500.0,
        fluid=refrigerant,
        water_temp=30.0, # Température d'entrée côté eau/air
        water_flow=2.0
    ))
    
    # 1.4 Ligne liquide
    pipe_liquid = system.add_component(entropyk.Pipe(
        length=10.0,
        diameter=0.015,
        fluid=refrigerant
    ))
    
    # 1.5 Division du débit (FlowSplitter) vers 2 évaporateurs
    splitter = system.add_component(entropyk.FlowSplitter(n_outlets=2))
    
    # 1.6 Branche A : Détendeur + Évaporateur 1
    exv_a = system.add_component(entropyk.ExpansionValve(fluid=refrigerant, opening=0.5))
    evap_a = system.add_component(entropyk.Evaporator(
        ua=2000.0,
        fluid=refrigerant,
        water_temp=12.0,
        water_flow=1.0
    ))
    
    # 1.7 Branche B : Détendeur + Évaporateur 2
    exv_b = system.add_component(entropyk.ExpansionValve(fluid=refrigerant, opening=0.5))
    evap_b = system.add_component(entropyk.Evaporator(
        ua=2000.0,
        fluid=refrigerant,
        water_temp=15.0, # Température d'eau légèrement différente
        water_flow=1.0
    ))
    
    # 1.8 Fusion du débit (FlowMerger)
    merger = system.add_component(entropyk.FlowMerger(n_inlets=2))
    
    # 1.9 Tuyau d'aspiration (retour compresseur)
    pipe_suction = system.add_component(entropyk.Pipe(
        length=5.0,
        diameter=0.025,
        fluid=refrigerant
    ))

    # --- Connexions de la boucle frigo ---
    system.add_edge(compressor, pipe_hot)
    system.add_edge(pipe_hot, condenser)
    system.add_edge(condenser, pipe_liquid)
    
    # Splitter
    system.add_edge(pipe_liquid, splitter)
    system.add_edge(splitter, exv_a)
    system.add_edge(splitter, exv_b)
    
    # Branches parallèles
    system.add_edge(exv_a, evap_a)
    system.add_edge(exv_b, evap_b)
    
    # Merger
    system.add_edge(evap_a, merger)
    system.add_edge(evap_b, merger)
    
    system.add_edge(merger, pipe_suction)
    system.add_edge(pipe_suction, compressor)

    # =========================================================================
    # BOUCLE 2 : CIRCUIT RÉSEAU HYDRAULIQUE (EAU - Côté Évaporateur Principal)
    # (Juste de la tuyauterie et une pompe pour montrer les FlowSource/FlowSink)
    # =========================================================================
    
    water_source = system.add_component(entropyk.FlowSource(
        fluid=water,
        pressure_pa=101325.0, # 1 atm
        temperature_k=285.15  # 12 °C
    ))
    
    water_pump = system.add_component(entropyk.Pump(
        pressure_rise_pa=50000.0, # 0.5 bar
        efficiency=0.6
    ))
    
    water_pipe = system.add_component(entropyk.Pipe(
        length=20.0,
        diameter=0.05,
        fluid=water
    ))
    
    water_sink = system.add_component(entropyk.FlowSink())
    
    # --- Connexions Hydrauliques Principales ---
    system.add_edge(water_source, water_pump)
    system.add_edge(water_pump, water_pipe)
    system.add_edge(water_pipe, water_sink)


    # =========================================================================
    # BOUCLE 3 : CIRCUIT VENTILATION (AIR - Côté Condenseur)
    # =========================================================================
    
    air_source = system.add_component(entropyk.FlowSource(
        fluid="Air",
        pressure_pa=101325.0,
        temperature_k=308.15 # 35 °C d'air ambiant
    ))
    
    condenser_fan = system.add_component(entropyk.Fan(
        pressure_rise_pa=200.0, # 200 Pa de montée en pression par le ventilo
        efficiency=0.5
    ))
    
    air_sink = system.add_component(entropyk.FlowSink())
    
    # --- Connexions Ventilation ---
    system.add_edge(air_source, condenser_fan)
    system.add_edge(condenser_fan, air_sink)


    # ── 4. Finalisation du système ──
    print("Finalisation du graphe (Construction de la topologie)...")
    system.finalize()
    print(f"Propriétés du système : {system.node_count} composants, {system.edge_count} connexions.")
    print(f"Taille du vecteur d'état mathématique : {system.state_vector_len} variables.")
    
    return system


def solve_system(system):
    # ── 5. Configuration Avancée du Solveur (Story 6-6) ──
    print("\nConfiguration de la stratégie de résolution...")

    # (Optionnel) Critères de convergence fins
    convergence = entropyk.ConvergenceCriteria(
        pressure_tolerance_pa=5.0,
        mass_balance_tolerance_kgs=1e-6,
        energy_balance_tolerance_w=1e-3
    )

    # (Optionnel) Jacobian Freezing pour aller plus vite
    freezing = entropyk.JacobianFreezingConfig(
        max_frozen_iters=4,
        threshold=0.1
    )

    # Configuration Newton avec tolérances avancées
    newton_config = entropyk.NewtonConfig(
        max_iterations=150,
        tolerance=1e-5,
        line_search=True,
        use_numerical_jacobian=True,
        jacobian_freezing=freezing,
        convergence_criteria=convergence,
        initial_state=[1000000.0, 450000.0] * 17
    )

    # Configuration Picard robuste en cas d'échec de Newton
    picard_config = entropyk.PicardConfig(
        max_iterations=500,
        tolerance=1e-4,
        relaxation=0.4,
        convergence_criteria=convergence
    )

    # ── 6. Lancement du calcul ──
    print("Lancement de la simulation (Newton uniquement)...")
    try:
        result = newton_config.solve(system)
        
        status = result.status
        print(f"\n✅ Simulation terminée avec succès !")
        print(f"Statut : {status}")
        print(f"Itérations : {result.iterations}")
        print(f"Résidu final : {result.final_residual:.2e}")
        
        # Le résultat contient le vecteur d'état complet
        state_vec = result.state_vector
        print(f"Aperçu des 5 premières variables d'état : {state_vec[:5]}")
        
    except entropyk.TimeoutError:
        print("\n❌ Le solveur a dépassé le temps imparti (Timeout).")
    except entropyk.SolverError as e:
        print(f"\n❌ Erreur du solveur : {e}")
        print("Note: Ce comportement peut arriver si les paramètres (taille des tuyaux, coeffs, températures)")
        print("dépassent le domaine thermodynamique du fluide ou si le graphe manque de contraintes aux limites.")

if __name__ == "__main__":
    system = build_complete_system()
    solve_system(system)
