{ "cells": [ { "cell_type": "markdown", "id": "390509bb", "metadata": {}, "source": [ "# 🌑️ Entropyk β€” Simulateur interactif\n", "\n", "Configurez les paramΓ¨tres du cycle frigorifique R410A via les sliders, puis cliquez **β–Ά Simuler**." ] }, { "cell_type": "code", "execution_count": 1, "id": "164510d5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "βœ… PrΓͺt\n" ] } ], "source": [ "import json, subprocess, tempfile, os\n", "import ipywidgets as widgets\n", "import matplotlib.pyplot as plt\n", "import matplotlib.patches as mpatches\n", "from IPython.display import display, clear_output\n", "\n", "CLI = r\"C:\\Users\\serameza\\impact\\dev\\Entropyk-main\\entropyk\\target\\debug\\entropyk-cli.exe\"\n", "BASE_CONFIG = r\"C:\\Users\\serameza\\impact\\dev\\Entropyk-main\\entropyk\\crates\\cli\\examples\\chiller_r410a_full_physics.json\"\n", "print('βœ… PrΓͺt')" ] }, { "cell_type": "code", "execution_count": 2, "id": "c55d1ff0", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "36f6e74bf5a64e9f8a4972460fbc2b00", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HTML(value='

βš™οΈ ParamΓ¨tres du cycle

'), FloatSlider(value=50.0, description='T condensat…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# ── Widgets de saisie ────────────────────────────────────────────────────────\n", "style = {'description_width': '200px'}\n", "layout = widgets.Layout(width='500px')\n", "\n", "w_t_cond = widgets.FloatSlider(value=50, min=30, max=70, step=1,\n", " description='T condensation (Β°C)', style=style, layout=layout)\n", "\n", "w_t_evap = widgets.FloatSlider(value=2, min=-20, max=20, step=1,\n", " description='T Γ©vaporation (Β°C)', style=style, layout=layout)\n", "\n", "w_eta = widgets.FloatSlider(value=0.75, min=0.50, max=0.95, step=0.01,\n", " description='Ξ· isentropique (βˆ’)', style=style, layout=layout,\n", " readout_format='.2f')\n", "\n", "w_superheat = widgets.FloatSlider(value=5, min=0, max=20, step=0.5,\n", " description='Surchauffe (K)', style=style, layout=layout)\n", "\n", "w_ua_cond = widgets.IntSlider(value=5000, min=1000, max=20000, step=500,\n", " description='UA condenseur (W/K)', style=style, layout=layout)\n", "\n", "w_ua_evap = widgets.IntSlider(value=6000, min=1000, max=20000, step=500,\n", " description='UA Γ©vaporateur (W/K)', style=style, layout=layout)\n", "\n", "btn_run = widgets.Button(description='β–Ά Simuler', button_style='success',\n", " layout=widgets.Layout(width='200px', height='40px'))\n", "\n", "out = widgets.Output()\n", "\n", "display(\n", " widgets.VBox([\n", " widgets.HTML('

βš™οΈ ParamΓ¨tres du cycle

'),\n", " w_t_cond, w_t_evap, w_eta, w_superheat,\n", " widgets.HTML('

πŸ” Γ‰changeurs

'),\n", " w_ua_cond, w_ua_evap,\n", " btn_run,\n", " out\n", " ])\n", ")" ] }, { "cell_type": "code", "execution_count": 3, "id": "fd926e11", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "🎯 Cliquez β–Ά Simuler dans la cellule prΓ©cΓ©dente\n" ] } ], "source": [ "def parse_cli_output(text):\n", " \"\"\"Extract edges and status from CLI stdout.\"\"\"\n", " edges, status, residual, iterations = [], 'UNKNOWN', None, None\n", " for line in text.splitlines():\n", " line = line.strip()\n", " if 'Status:' in line:\n", " status = 'CONVERGED' if 'CONVERGED' in line else 'DIVERGED'\n", " if 'Residual:' in line:\n", " try: residual = float(line.split()[-1])\n", " except: pass\n", " if 'Iterations:' in line:\n", " try: iterations = int(line.split()[-1])\n", " except: pass\n", " if line.startswith('Edge'):\n", " # Edge 0: P = 30.711 bar, h = 433.16 kJ/kg\n", " try:\n", " parts = line.split(':')\n", " idx = int(parts[0].replace('Edge','').strip())\n", " nums = [float(x.split()[0]) for x in parts[1].split(',')]\n", " p_bar, h_kjkg = nums[0], nums[1]\n", " edges.append({'idx': idx, 'P_bar': p_bar, 'H_kJkg': h_kjkg})\n", " except: pass\n", " return {'status': status, 'residual': residual, 'iterations': iterations, 'edges': edges}\n", "\n", "\n", "def run_simulation(b):\n", " with out:\n", " clear_output(wait=True)\n", "\n", " # Build config from widgets\n", " t_cond_k = w_t_cond.value + 273.15\n", " t_evap_k = w_t_evap.value + 273.15\n", "\n", " with open(BASE_CONFIG) as f:\n", " cfg = json.load(f)\n", "\n", " for comp in cfg['circuits'][0]['components']:\n", " if comp['type'] == 'IsentropicCompressor':\n", " comp['isentropic_efficiency'] = w_eta.value\n", " comp['t_cond_k'] = t_cond_k\n", " comp['t_evap_k'] = t_evap_k\n", " comp['superheat_k'] = w_superheat.value\n", " elif comp['type'] == 'Condenser':\n", " comp['ua'] = w_ua_cond.value\n", " comp['t_sat_k'] = t_cond_k\n", " elif comp['type'] == 'IsenthalpicExpansionValve':\n", " comp['t_evap_k'] = t_evap_k\n", " elif comp['type'] == 'Evaporator':\n", " comp['ua'] = w_ua_evap.value\n", " comp['t_sat_k'] = t_evap_k\n", " comp['superheat_k'] = w_superheat.value\n", "\n", " with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:\n", " json.dump(cfg, f, indent=2)\n", " tmp_path = f.name\n", "\n", " try:\n", " result = subprocess.run([CLI, 'run', '--config', tmp_path],\n", " capture_output=True, text=True, timeout=120)\n", " output = result.stdout + result.stderr\n", " data = parse_cli_output(output)\n", "\n", " if data['status'] == 'CONVERGED':\n", " print(f\"βœ… CONVERGΓ‰ en {data['iterations']} itΓ©rations | rΓ©sidu = {data['residual']:.2e}\")\n", " print()\n", "\n", " edges = data['edges']\n", " labels = ['Sortie compresseur', 'Sortie condenseur', 'Sortie EXV', 'Sortie Γ©vaporateur']\n", " print(f\"{'Point':<25} {'P (bar)':>10} {'h (kJ/kg)':>12}\")\n", " print('-' * 50)\n", " for e in edges[:4]:\n", " lbl = labels[e['idx']] if e['idx'] < len(labels) else f\"Edge {e['idx']}\"\n", " print(f\"{lbl:<25} {e['P_bar']:>10.3f} {e['H_kJkg']:>12.2f}\")\n", "\n", " # Compute COP\n", " if len(edges) >= 4:\n", " h_comp_in = edges[3]['H_kJkg'] # evap outlet = comp inlet\n", " h_comp_out = edges[0]['H_kJkg'] # comp outlet\n", " h_exv_in = edges[1]['H_kJkg'] # cond outlet = exv inlet\n", " w_comp = h_comp_out - h_comp_in\n", " q_evap = h_comp_in - h_exv_in\n", " cop = q_evap / w_comp if w_comp > 0 else 0\n", " print()\n", " print(f\"{'W compresseur':<25} {w_comp:>10.2f} kJ/kg\")\n", " print(f\"{'Q Γ©vaporateur':<25} {q_evap:>10.2f} kJ/kg\")\n", " print(f\"{'COP (froid)':<25} {cop:>10.2f}\")\n", "\n", " plot_cycle(edges[:4], labels)\n", "\n", " else:\n", " print('❌ DIVERGENCE')\n", " print(output[-2000:])\n", "\n", " except subprocess.TimeoutExpired:\n", " print('⏱ Timeout')\n", " finally:\n", " os.unlink(tmp_path)\n", "\n", "\n", "def plot_cycle(edges, labels):\n", " if len(edges) < 4:\n", " return\n", " h = [e['H_kJkg'] for e in edges]\n", " p = [e['P_bar'] for e in edges]\n", "\n", " # Close the cycle\n", " h_cycle = h + [h[0]]\n", " p_cycle = p + [p[0]]\n", "\n", " fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n", "\n", " # ── P-h diagram ──────────────────────────────────────────────────────────\n", " ax = axes[0]\n", " colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12']\n", " ax.plot(h_cycle, p_cycle, 'k-', linewidth=2, zorder=2)\n", " for i, (hi, pi) in enumerate(zip(h[:4], p[:4])):\n", " ax.scatter([hi], [pi], color=colors[i], s=100, zorder=3)\n", " ax.annotate(f\"{i+1}. {labels[i]}\\n({pi:.1f} bar, {hi:.0f} kJ/kg)\",\n", " (hi, pi), textcoords='offset points', xytext=(8, 5), fontsize=8)\n", " ax.set_xlabel('Enthalpie h (kJ/kg)', fontsize=11)\n", " ax.set_ylabel('Pression P (bar)', fontsize=11)\n", " ax.set_title('Diagramme P-h du cycle', fontsize=12)\n", " ax.grid(True, alpha=0.3)\n", "\n", " # ── Bar chart: enthalpies per state ──────────────────────────────────────\n", " ax2 = axes[1]\n", " short_labels = ['1\\nSortie\\ncompresseur', '2\\nSortie\\ncondenseur',\n", " '3\\nSortie\\nEXV', '4\\nSortie\\nΓ©vaporateur']\n", " bars = ax2.bar(short_labels, h[:4], color=colors, edgecolor='black', linewidth=0.7)\n", " for bar, val in zip(bars, h[:4]):\n", " ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 2,\n", " f'{val:.1f}', ha='center', va='bottom', fontsize=9)\n", " ax2.set_ylabel('Enthalpie (kJ/kg)', fontsize=11)\n", " ax2.set_title('Enthalpies aux points du cycle', fontsize=12)\n", " ax2.grid(True, axis='y', alpha=0.3)\n", "\n", " plt.tight_layout()\n", " plt.show()\n", "\n", "\n", "btn_run.on_click(run_simulation)\n", "print('🎯 Cliquez β–Ά Simuler dans la cellule prΓ©cΓ©dente')" ] }, { "cell_type": "code", "execution_count": null, "id": "ae140c88-c298-4fe6-a13b-53d61640ddcf", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.12.8" } }, "nbformat": 4, "nbformat_minor": 5 }