{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Entropyk \u2014 Guide Complet: Solveurs, Contr\u00f4le Inverse & API\n", "\n", "Ce notebook pr\u00e9sente l'**API compl\u00e8te** d'Entropyk pour:\n", "\n", "1. **Solveurs** : Newton-Raphson, Picard, Fallback\n", "2. **Contr\u00f4le Inverse** : Constraints + BoundedVariables\n", "3. **Types physiques** : Pressure, Temperature, Enthalpy, MassFlow\n", "4. **Composants** : Compressor, Condenser, Evaporator, etc.\n", "\n", "> \u26a0\ufe0f **Note**: Les composants Python actuels sont des placeholders. Les vrais composants thermodynamiques sont impl\u00e9ment\u00e9s en Rust et n\u00e9cessitent l'activation des features appropri\u00e9es." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import entropyk\n", "import numpy as np\n", "\n", "print(\"=== ENTROPYK API ===\\n\")\n", "\n", "# Lister toutes les classes disponibles\n", "classes = [x for x in dir(entropyk) if not x.startswith('_') and x[0].isupper()]\n", "print(\"Classes disponibles:\")\n", "for c in sorted(classes):\n", " print(f\" \u2022 {c}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 1: TYPES PHYSIQUES\n", "\n", "Types forts avec conversion d'unit\u00e9s automatique." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== PRESSURE ===\\n\")\n", "\n", "# Cr\u00e9ation avec diff\u00e9rentes unit\u00e9s\n", "p1 = entropyk.Pressure(bar=12.0)\n", "p2 = entropyk.Pressure(kpa=350.0)\n", "p3 = entropyk.Pressure(psi=150.0)\n", "p4 = entropyk.Pressure(pa=101325.0)\n", "\n", "print(f\"p1 = {p1}\")\n", "print(f\" \u2192 {p1.to_bar():.2f} bar = {p1.to_kpa():.1f} kPa = {p1.to_psi():.1f} psi\")\n", "\n", "print(f\"\\np2 = {p2}\")\n", "print(f\" \u2192 {p2.to_bar():.2f} bar\")\n", "\n", "# Arithm\u00e9tique\n", "p_sum = p1 + entropyk.Pressure(bar=2.0)\n", "p_diff = p1 - entropyk.Pressure(bar=2.0)\n", "print(f\"\\nArithm\u00e9tique:\")\n", "print(f\" {p1} + 2 bar = {p_sum}\")\n", "print(f\" {p1} - 2 bar = {p_diff}\")\n", "\n", "# Conversion en float\n", "print(f\"\\nfloat(p1) = {float(p1)} Pa\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== TEMPERATURE ===\\n\")\n", "\n", "# Cr\u00e9ation\n", "t1 = entropyk.Temperature(celsius=45.0)\n", "t2 = entropyk.Temperature(kelvin=273.15)\n", "t3 = entropyk.Temperature(fahrenheit=100.0)\n", "\n", "print(f\"t1 = {t1}\")\n", "print(f\" \u2192 {t1.to_celsius():.2f}\u00b0C = {t1.to_kelvin():.2f} K = {t1.to_fahrenheit():.2f}\u00b0F\")\n", "\n", "print(f\"\\nt2 (point de cong\u00e9lation) = {t2}\")\n", "print(f\" \u2192 {t2.to_celsius():.2f}\u00b0C\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== ENTHALPY ===\\n\")\n", "\n", "h1 = entropyk.Enthalpy(kj_per_kg=420.0)\n", "h2 = entropyk.Enthalpy(j_per_kg=250000.0)\n", "\n", "print(f\"h1 = {h1}\")\n", "print(f\" \u2192 {h1.to_kj_per_kg():.1f} kJ/kg = {h1.to_j_per_kg():.0f} J/kg\")\n", "\n", "print(f\"\\nh2 = {h2}\")\n", "print(f\" \u2192 {h2.to_kj_per_kg():.1f} kJ/kg\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== MASSFLOW ===\\n\")\n", "\n", "m1 = entropyk.MassFlow(kg_per_s=0.05)\n", "m2 = entropyk.MassFlow(g_per_s=50.0)\n", "\n", "print(f\"m1 = {m1}\")\n", "print(f\" \u2192 {m1.to_kg_per_s():.4f} kg/s = {m1.to_g_per_s():.1f} g/s\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 2: COMPOSANTS\n", "\n", "Cr\u00e9ation et configuration des composants du cycle." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== COMPRESSOR ===\\n\")\n", "\n", "comp = entropyk.Compressor(\n", " speed_rpm=3000.0, # Vitesse de rotation\n", " displacement=0.0001, # Cylindr\u00e9e (m\u00b3/rev) = 100 cc\n", " efficiency=0.85, # Rendement volum\u00e9trique\n", " fluid=\"R134a\", # Fluide frigorig\u00e8ne\n", " # Coefficients AHRI 540 (optionnel, d\u00e9fauts fournis)\n", " m1=0.85, m2=2.5, m3=500.0, m4=1500.0, m5=-2.5, m6=1.8,\n", " m7=600.0, m8=1600.0, m9=-3.0, m10=2.0\n", ")\n", "\n", "print(f\"Compresseur cr\u00e9\u00e9: {comp}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== CONDENSER ===\\n\")\n", "\n", "cond = entropyk.Condenser(ua=5000.0) # Coefficient global W/K\n", "print(f\"Condenseur: {cond}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== EVAPORATOR ===\\n\")\n", "\n", "evap = entropyk.Evaporator(ua=3000.0) # Coefficient global W/K\n", "print(f\"\u00c9vaporateur: {evap}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== EXPANSION VALVE ===\\n\")\n", "\n", "exv = entropyk.ExpansionValve(\n", " fluid=\"R134a\",\n", " opening=0.5 # Ouverture 0-1\n", ")\n", "print(f\"Vanne d'expansion: {exv}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== AUTRES COMPOSANTS ===\\n\")\n", "\n", "# Pipe\n", "pipe = entropyk.Pipe()\n", "print(f\"Pipe: {pipe}\")\n", "\n", "# Pump\n", "pump = entropyk.Pump()\n", "print(f\"Pump: {pump}\")\n", "\n", "# Fan\n", "fan = entropyk.Fan()\n", "print(f\"Fan: {fan}\")\n", "\n", "# Economizer\n", "eco = entropyk.Economizer()\n", "print(f\"Economizer: {eco}\")\n", "\n", "# Flow sources/sinks (boundaries)\n", "source = entropyk.FlowSource()\n", "sink = entropyk.FlowSink()\n", "print(f\"FlowSource: {source}\")\n", "print(f\"FlowSink: {sink}\")\n", "\n", "# Flow splitters/mergers\n", "splitter = entropyk.FlowSplitter()\n", "merger = entropyk.FlowMerger()\n", "print(f\"FlowSplitter: {splitter}\")\n", "print(f\"FlowMerger: {merger}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 3: SYST\u00c8ME ET TOPOLOGIE" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== CR\u00c9ATION DU SYST\u00c8ME ===\\n\")\n", "\n", "# Cr\u00e9er le syst\u00e8me\n", "system = entropyk.System()\n", "print(f\"Syst\u00e8me vide: {system}\")\n", "\n", "# Ajouter les composants\n", "comp_idx = system.add_component(entropyk.Compressor(\n", " speed_rpm=3000.0, displacement=0.0001, efficiency=0.85, fluid=\"R134a\"\n", "))\n", "cond_idx = system.add_component(entropyk.Condenser(ua=5000.0))\n", "exv_idx = system.add_component(entropyk.ExpansionValve(fluid=\"R134a\", opening=0.5))\n", "evap_idx = system.add_component(entropyk.Evaporator(ua=3000.0))\n", "\n", "print(f\"\\nComposants ajout\u00e9s:\")\n", "print(f\" Compresseur \u2192 index {comp_idx}\")\n", "print(f\" Condenseur \u2192 index {cond_idx}\")\n", "print(f\" EXV \u2192 index {exv_idx}\")\n", "print(f\" \u00c9vaporateur \u2192 index {evap_idx}\")\n", "\n", "print(f\"\\nSyst\u00e8me: {system}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== CONNEXIONS (EDGES) ===\\n\")\n", "\n", "# Connecter les composants en cycle\n", "e1 = system.add_edge(comp_idx, cond_idx) # Compresseur \u2192 Condenseur\n", "e2 = system.add_edge(cond_idx, exv_idx) # Condenseur \u2192 EXV\n", "e3 = system.add_edge(exv_idx, evap_idx) # EXV \u2192 \u00c9vaporateur\n", "e4 = system.add_edge(evap_idx, comp_idx) # \u00c9vaporateur \u2192 Compresseur\n", "\n", "print(f\"Edges cr\u00e9\u00e9s: {e1}, {e2}, {e3}, {e4}\")\n", "print(f\"\\nFlux: Comp \u2192 Cond \u2192 EXV \u2192 Evap \u2192 Comp (cycle)\")\n", "print(f\"\\nSyst\u00e8me: {system}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== NOMS DE COMPOSANTS ===\\n\")\n", "\n", "# Enregistrer des noms lisibles pour le contr\u00f4le inverse\n", "system.register_component_name(\"evaporator\", evap_idx)\n", "system.register_component_name(\"condenser\", cond_idx)\n", "system.register_component_name(\"valve\", exv_idx)\n", "system.register_component_name(\"compressor\", comp_idx)\n", "\n", "print(\"Noms enregistr\u00e9s pour le contr\u00f4le inverse:\")\n", "print(\" 'evaporator' \u2192 index\", evap_idx)\n", "print(\" 'condenser' \u2192 index\", cond_idx)\n", "print(\" 'valve' \u2192 index\", exv_idx)\n", "print(\" 'compressor' \u2192 index\", comp_idx)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== FINALISATION ===\\n\")\n", "\n", "system.finalize()\n", "\n", "print(f\"Syst\u00e8me finalis\u00e9!\")\n", "print(f\" Nodes (composants): {system.node_count}\")\n", "print(f\" Edges (connexions): {system.edge_count}\")\n", "print(f\" State vector length: {system.state_vector_len}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 4: SOLVEURS" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== NEWTON-RAPHSON CONFIG ===\\n\")\n", "\n", "newton = entropyk.NewtonConfig(\n", " max_iterations=200, # Max 200 it\u00e9rations\n", " tolerance=1e-6, # Convergence: ||r|| < 1e-6\n", " line_search=True, # Recherche lin\u00e9aire activ\u00e9e\n", " timeout_ms=10000 # Timeout: 10 secondes\n", ")\n", "\n", "print(f\"Config: {newton}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== PICARD CONFIG ===\\n\")\n", "\n", "picard = entropyk.PicardConfig(\n", " max_iterations=500,\n", " tolerance=1e-4,\n", " relaxation=0.5 # 0.5 = sous-relaxation\n", ")\n", "\n", "print(f\"Config: {picard}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== FALLBACK CONFIG (Newton \u2192 Picard) ===\\n\")\n", "\n", "fallback = entropyk.FallbackConfig(\n", " newton=entropyk.NewtonConfig(max_iterations=100, tolerance=1e-6),\n", " picard=entropyk.PicardConfig(max_iterations=300, tolerance=1e-4, relaxation=0.5)\n", ")\n", "\n", "print(f\"Config: {fallback}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== R\u00c9SOLUTION ===\\n\")\n", "\n", "# Note: Avec les composants placeholder actuels, le solveur peut\n", "# converger imm\u00e9diatement (r\u00e9sidus nuls) ou diverger selon la config\n", "\n", "try:\n", " result = fallback.solve(system)\n", " \n", " print(f\"\u2705 R\u00e9sultat obtenu:\")\n", " print(f\" It\u00e9rations: {result.iterations}\")\n", " print(f\" R\u00e9sidu final: {result.final_residual:.2e}\")\n", " print(f\" Statut: {result.status}\")\n", " print(f\" Converg\u00e9: {result.is_converged}\")\n", " \n", "except entropyk.SolverError as e:\n", " print(f\"\u274c SolverError: {e}\")\n", "except entropyk.TimeoutError as e:\n", " print(f\"\u23f1\ufe0f TimeoutError: {e}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 5: R\u00c9SULTAT ET STATE VECTOR" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== ACC\u00c8S AU R\u00c9SULTAT ===\\n\")\n", "\n", "try:\n", " result = fallback.solve(system)\n", " \n", " # Propri\u00e9t\u00e9s du r\u00e9sultat\n", " print(f\"It\u00e9rations: {result.iterations}\")\n", " print(f\"R\u00e9sidu final: {result.final_residual:.6e}\")\n", " print(f\"Converg\u00e9: {result.is_converged}\")\n", " print(f\"Statut: {result.status}\")\n", " \n", " # State vector comme liste\n", " state_list = result.state_vector\n", " print(f\"\\nState vector (list): {len(state_list)} \u00e9l\u00e9ments\")\n", " if len(state_list) > 0:\n", " print(f\" Premiers: {state_list[:min(6, len(state_list))]}\")\n", " \n", " # State vector comme NumPy array\n", " state_array = result.to_numpy()\n", " print(f\"\\nState vector (numpy): shape={state_array.shape}, dtype={state_array.dtype}\")\n", " \n", "except entropyk.SolverError as e:\n", " print(f\"Erreur: {e}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== CONVERGENCE STATUS ===\\n\")\n", "\n", "print(\"Valeurs possibles de result.status:\")\n", "print(\" \u2022 'Converged' \u2192 Solution trouv\u00e9e\")\n", "print(\" \u2022 'ControlSaturation' \u2192 Converg\u00e9 mais variable born\u00e9e satur\u00e9e\")\n", "print(\" \u2022 'TimedOut' \u2192 Timeout atteint\")\n", "\n", "print(\"\\nExemple de v\u00e9rification:\")\n", "print(\"\"\"\n", "if result.is_converged:\n", " if result.status == \"Converged\":\n", " print(\"Solution optimale trouv\u00e9e\")\n", " elif result.status == \"ControlSaturation\":\n", " print(\"Contr\u00f4le \u00e0 la borne - objectif non atteignable\")\n", "else:\n", " print(\"Non converg\u00e9\")\n", "\"\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 6: CONTR\u00d4LE INVERSE - CONSTRAINTS" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== CONSTRAINT: SUPERHEAT ===\\n\")\n", "\n", "# Contrainte de surchauffe: Superheat = 5.0 K\n", "sh_constraint = entropyk.Constraint.superheat(\n", " id=\"sh_5k\", # ID unique\n", " component_id=\"evaporator\", # Composant cible (nom enregistr\u00e9)\n", " target_value=5.0, # Surchauffe cible (K)\n", " tolerance=1e-4 # Tol\u00e9rance de convergence\n", ")\n", "\n", "print(f\"Constraint: {sh_constraint}\")\n", "print(\"\\nUsage typique:\")\n", "print(\" \u2022 Maintenir 5-10K de surchauffe \u00e0 l'aspiration compresseur\")\n", "print(\" \u2022 Contr\u00f4l\u00e9 par ouverture de vanne EXV\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== CONSTRAINT: SUBCOOLING ===\\n\")\n", "\n", "sc_constraint = entropyk.Constraint.subcooling(\n", " id=\"sc_3k\",\n", " component_id=\"condenser\",\n", " target_value=3.0,\n", " tolerance=1e-4\n", ")\n", "\n", "print(f\"Constraint: {sc_constraint}\")\n", "print(\"\\nUsage typique:\")\n", "print(\" \u2022 Maintenir 2-5K de sous-refroidissement\")\n", "print(\" \u2022 Am\u00e9liore le rendement et \u00e9vite le flash gas\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== CONSTRAINT: CAPACITY ===\\n\")\n", "\n", "cap_constraint = entropyk.Constraint.capacity(\n", " id=\"cap_10kw\",\n", " component_id=\"evaporator\",\n", " target_value=10000.0, # Watts\n", " tolerance=10.0\n", ")\n", "\n", "print(f\"Constraint: {cap_constraint}\")\n", "print(\"\\nUsage typique:\")\n", "print(\" \u2022 Dimensionnement \u00e0 capacit\u00e9 fixe\")\n", "print(\" \u2022 Contr\u00f4l\u00e9 par vitesse compresseur ou d\u00e9bit\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 7: BOUNDED VARIABLES" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== BOUNDED VARIABLE ===\\n\")\n", "\n", "# Variable de contr\u00f4le avec bornes physiques\n", "exv_opening = entropyk.BoundedVariable(\n", " id=\"exv_opening\", # ID unique\n", " value=0.5, # Valeur initiale (50%)\n", " min=0.0, # Minimum: ferm\u00e9\n", " max=1.0, # Maximum: ouvert\n", " component_id=\"valve\" # Composant associ\u00e9\n", ")\n", "\n", "print(f\"Variable: {exv_opening}\")\n", "print(\"\\nLe solveur ajustera cette variable entre 0 et 1\")\n", "print(\"pour satisfaire la contrainte li\u00e9e.\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== EXEMPLE: VITESSE COMPRESSEUR ===\\n\")\n", "\n", "speed_var = entropyk.BoundedVariable(\n", " id=\"comp_speed\",\n", " value=3000.0, # 3000 RPM initial\n", " min=1000.0, # Minimum: 1000 RPM\n", " max=6000.0, # Maximum: 6000 RPM\n", " component_id=\"compressor\"\n", ")\n", "\n", "print(f\"Variable: {speed_var}\")\n", "print(\"\\nContr\u00f4le de capacit\u00e9 par variation de vitesse (inverter)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 8: WORKFLOW COMPLET - CONTR\u00d4LE INVERSE" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== WORKFLOW COMPLET ===\\n\")\n", "\n", "# 1. Cr\u00e9er le syst\u00e8me\n", "system = entropyk.System()\n", "\n", "# 2. Ajouter composants\n", "comp_idx = system.add_component(entropyk.Compressor(\n", " speed_rpm=3000.0, displacement=0.0001, efficiency=0.85, fluid=\"R134a\"\n", "))\n", "cond_idx = system.add_component(entropyk.Condenser(ua=5000.0))\n", "exv_idx = system.add_component(entropyk.ExpansionValve(fluid=\"R134a\", opening=0.5))\n", "evap_idx = system.add_component(entropyk.Evaporator(ua=3000.0))\n", "\n", "# 3. Connecter\n", "system.add_edge(comp_idx, cond_idx)\n", "system.add_edge(cond_idx, exv_idx)\n", "system.add_edge(exv_idx, evap_idx)\n", "system.add_edge(evap_idx, comp_idx)\n", "\n", "# 4. Enregistrer les noms\n", "system.register_component_name(\"evaporator\", evap_idx)\n", "system.register_component_name(\"valve\", exv_idx)\n", "\n", "# 5. Ajouter la contrainte\n", "sh_constraint = entropyk.Constraint.superheat(\n", " id=\"sh_target\",\n", " component_id=\"evaporator\",\n", " target_value=5.0,\n", " tolerance=1e-4\n", ")\n", "system.add_constraint(sh_constraint)\n", "print(f\"1. Contrainte ajout\u00e9e: {sh_constraint}\")\n", "\n", "# 6. Ajouter la variable born\u00e9e\n", "exv_var = entropyk.BoundedVariable(\n", " id=\"exv_opening\",\n", " value=0.5,\n", " min=0.0,\n", " max=1.0,\n", " component_id=\"valve\"\n", ")\n", "system.add_bounded_variable(exv_var)\n", "print(f\"2. Variable born\u00e9e ajout\u00e9e: {exv_var}\")\n", "\n", "# 7. Lier contrainte \u2192 variable\n", "system.link_constraint_to_control(\"sh_target\", \"exv_opening\")\n", "print(\"3. Lien cr\u00e9\u00e9: sh_target \u2192 exv_opening\")\n", "\n", "# 8. Finaliser\n", "system.finalize()\n", "print(f\"4. Syst\u00e8me finalis\u00e9: {system.state_vector_len} variables\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== R\u00c9SOLUTION AVEC CONTR\u00d4LE INVERSE ===\\n\")\n", "\n", "config = entropyk.NewtonConfig(\n", " max_iterations=200,\n", " tolerance=1e-6,\n", " line_search=True\n", ")\n", "\n", "try:\n", " result = config.solve(system)\n", " \n", " print(f\"\u2705 R\u00e9sultat:\")\n", " print(f\" It\u00e9rations: {result.iterations}\")\n", " print(f\" R\u00e9sidu: {result.final_residual:.2e}\")\n", " print(f\" Statut: {result.status}\")\n", " \n", " # Si ControlSaturation, la variable est \u00e0 une borne\n", " if str(result.status) == \"ControlSaturation\":\n", " print(\"\\n \u26a0\ufe0f Variable de contr\u00f4le satur\u00e9e!\")\n", " print(\" \u2192 Impossible d'atteindre exactement Superheat = 5K\")\n", " print(\" \u2192 V\u00e9rifier les bornes de la variable\")\n", " \n", "except entropyk.SolverError as e:\n", " print(f\"\u274c Erreur: {e}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 9: GESTION D'ERREURS" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== TYPES D'ERREURS ===\\n\")\n", "\n", "errors = [\n", " (\"entropyk.SolverError\", \"Non-convergence du solveur\"),\n", " (\"entropyk.TimeoutError\", \"Timeout d\u00e9pass\u00e9\"),\n", " (\"entropyk.TopologyError\", \"Erreur de topologie (cycle non ferm\u00e9)\"),\n", " (\"entropyk.EntropykError\", \"Erreur g\u00e9n\u00e9rale\"),\n", "]\n", "\n", "for err_name, desc in errors:\n", " print(f\" {err_name:25s} \u2192 {desc}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== EXEMPLE GESTION D'ERREURS ===\\n\")\n", "\n", "print(\"\"\"\n", "try:\n", " result = config.solve(system)\n", " \n", " if result.is_converged:\n", " # Solution trouv\u00e9e\n", " state = result.to_numpy()\n", " print(f\"Converg\u00e9 en {result.iterations} it\u00e9rations\")\n", " else:\n", " # Non converg\u00e9 mais \u00e9tat disponible\n", " print(f\"Non converg\u00e9, r\u00e9sidu={result.final_residual:.2e}\")\n", " \n", "except entropyk.TimeoutError as e:\n", " print(f\"Timeout: {e}\")\n", " # Augmenter timeout ou simplifier le probl\u00e8me\n", " \n", "except entropyk.SolverError as e:\n", " print(f\"Erreur solveur: {e}\")\n", " # Essayer Picard ou changer les guess initiaux\n", " \n", "except entropyk.TopologyError as e:\n", " print(f\"Erreur topologie: {e}\")\n", " # V\u00e9rifier les connexions add_edge()\n", "\"\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# PARTIE 10: MULTI-FLUIDES" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== TEST AVEC DIFF\u00c9RENTS FLUIDES ===\\n\")\n", "\n", "fluids = [\n", " \"R134a\", # HFC standard\n", " \"R32\", # HFC low-GWP\n", " \"R290\", # Propane (naturel)\n", " \"R744\", # CO2 (transcritique)\n", " \"R1234yf\", # HFO\n", " \"R454B\", # M\u00e9lange\n", "]\n", "\n", "for fluid in fluids:\n", " try:\n", " s = entropyk.System()\n", " c = s.add_component(entropyk.Compressor(\n", " speed_rpm=3000, displacement=0.0001, efficiency=0.85, fluid=fluid\n", " ))\n", " cd = s.add_component(entropyk.Condenser(ua=5000))\n", " ex = s.add_component(entropyk.ExpansionValve(fluid=fluid, opening=0.5))\n", " ev = s.add_component(entropyk.Evaporator(ua=3000))\n", " \n", " s.add_edge(c, cd)\n", " s.add_edge(cd, ex)\n", " s.add_edge(ex, ev)\n", " s.add_edge(ev, c)\n", " s.finalize()\n", " \n", " print(f\" {fluid:10s} \u2192 \u2705 OK ({s.state_vector_len} vars)\")\n", " \n", " except Exception as e:\n", " print(f\" {fluid:10s} \u2192 \u274c {e}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "# R\u00c9SUM\u00c9 API" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"\"\"\n", "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n", "\u2551 R\u00c9SUM\u00c9 API ENTROPYK PYTHON \u2551\n", "\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n", "\u2551 \u2551\n", "\u2551 SYST\u00c8ME \u2551\n", "\u2551 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2551\n", "\u2551 system = entropyk.System() \u2551\n", "\u2551 idx = system.add_component(comp) \u2551\n", "\u2551 system.add_edge(src_idx, tgt_idx) \u2551\n", "\u2551 system.register_component_name(\"name\", idx) \u2551\n", "\u2551 system.finalize() \u2551\n", "\u2551 \u2551\n", "\u2551 COMPOSANTS \u2551\n", "\u2551 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2551\n", "\u2551 Compressor(speed_rpm, displacement, efficiency, fluid) \u2551\n", "\u2551 Condenser(ua) \u2551\n", "\u2551 Evaporator(ua) \u2551\n", "\u2551 ExpansionValve(fluid, opening) \u2551\n", "\u2551 Pipe(), Pump(), Fan(), Economizer(), FlowSource(), FlowSink() \u2551\n", "\u2551 \u2551\n", "\u2551 CONTR\u00d4LE INVERSE \u2551\n", "\u2551 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2551\n", "\u2551 Constraint.superheat(id, component_id, target_value, tolerance) \u2551\n", "\u2551 Constraint.subcooling(id, component_id, target_value, tolerance) \u2551\n", "\u2551 Constraint.capacity(id, component_id, target_value, tolerance) \u2551\n", "\u2551 BoundedVariable(id, value, min, max, component_id) \u2551\n", "\u2551 system.add_constraint(constraint) \u2551\n", "\u2551 system.add_bounded_variable(variable) \u2551\n", "\u2551 system.link_constraint_to_control(constraint_id, variable_id) \u2551\n", "\u2551 \u2551\n", "\u2551 SOLVEURS \u2551\n", "\u2551 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2551\n", "\u2551 NewtonConfig(max_iterations, tolerance, line_search, timeout_ms) \u2551\n", "\u2551 PicardConfig(max_iterations, tolerance, relaxation) \u2551\n", "\u2551 FallbackConfig(newton, picard) \u2551\n", "\u2551 result = config.solve(system) \u2551\n", "\u2551 \u2551\n", "\u2551 R\u00c9SULTAT \u2551\n", "\u2551 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2551\n", "\u2551 result.iterations \u2192 int (nombre d'it\u00e9rations) \u2551\n", "\u2551 result.final_residual \u2192 float (r\u00e9sidu L2 final) \u2551\n", "\u2551 result.status \u2192 str (\"Converged\"/\"ControlSaturation\"/...) \u2551\n", "\u2551 result.is_converged \u2192 bool (True si converg\u00e9) \u2551\n", "\u2551 result.state_vector \u2192 list ([P0, h0, P1, h1, ...]) \u2551\n", "\u2551 result.to_numpy() \u2192 np.ndarray \u2551\n", "\u2551 \u2551\n", "\u2551 TYPES PHYSIQUES \u2551\n", "\u2551 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2551\n", "\u2551 Pressure(bar=, kpa=, psi=, pa=) \u2551\n", "\u2551 Temperature(celsius=, kelvin=, fahrenheit=) \u2551\n", "\u2551 Enthalpy(kj_per_kg=, j_per_kg=) \u2551\n", "\u2551 MassFlow(kg_per_s=, g_per_s=) \u2551\n", "\u2551 \u2551\n", "\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n", "\"\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Advanced Configuration & SolverStrategy\n", "\n", "Entropyk exposes fine-grained control over the solver behavior, matching the Rust core.\n", "You can use `SolverStrategy` for a unified interface, and advanced configs like `JacobianFreezingConfig` to optimize Newton-Raphson speed by reusing the Jacobian matrix across iterations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Jacobian Freezing speeds up convergence by reusing the Jacobian matrix\n", "jf_config = entropyk.JacobianFreezingConfig(max_frozen_iters=5, threshold=0.1)\n", "\n", "# Custom convergence criteria\n", "cc = entropyk.ConvergenceCriteria(\n", " pressure_tolerance_pa=5.0,\n", " mass_balance_tolerance_kgs=1e-6,\n", " energy_balance_tolerance_w=1e-3\n", ")\n", "\n", "# Warm Starting: providing an initial guess\n", "initial_guess = [0.0] * system.state_vector_len\n", "\n", "strategy = entropyk.SolverStrategy.newton(\n", " max_iterations=150,\n", " tolerance=1e-5,\n", " line_search=True,\n", " jacobian_freezing=jf_config,\n", " convergence_criteria=cc,\n", " initial_state=initial_guess,\n", ")\n", "\n", "try:\n", " res_advanced = strategy.solve(system)\n", " print(f\"\\nStatus: {res_advanced.status}\")\n", " print(f\"Iterations: {res_advanced.iterations}\")\n", "except entropyk.SolverError as e:\n", " print(f\"Solver failed: {e}\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.0" } }, "nbformat": 4, "nbformat_minor": 4 }