Entropyk/bindings/python/solver_control_examples.ipynb

949 lines
36 KiB
Plaintext

{
"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
}