... Demande de devis B2B

Le diagramme de Mollier interactif (aussi appelé diagramme enthalpique ou diagramme des frigoristes) permet de visualiser et calculer en temps réel un cycle frigorifique thermodynamique à 4 points pour 7 fluides frigorigènes courants : R32, R410A, R134A, R404A, R290, R1234yf, R744 (CO₂). Ajustez les températures d’évaporation et de condensation, la surchauffe, le sous-refroidissement et le rendement compresseur, et visualisez instantanément le COP, l’effet frigorifique, le travail de compression et la chaleur cédée au condenseur.

🧊 Diagramme de Mollier interactif 7 FLUIDES

Simulateur thermodynamique du cycle frigorifique · Calcul en direct de COP, surchauffe, sous-refroidissement · R32, R410A, R134A, R404A, R290, R1234YF, R744 (CO₂)

⚙️ Paramètres cycle







Pression évap (P1) :
Pression cond (P2) :
Ratio compression :
Effet frigo (h1−h4) :
Travail compresseur :
Chaleur cédée (cond) :
COP = —

📊 Diagramme log(P)-h

Courbe de saturation
Cycle frigorifique (1→2→3→4)
Évaporation (4→1)
Compression (1→2)
Condensation (2→3)
Détente (3→4)

const FLUIDS = { 'R32': { name:'R32', Tcrit:78.1, Pcrit:57.82, /* T(°C), P(bar), h_liq, h_vap, s_vap */ pts:[[-40,0.66,144,498,2.31],[-30,1.08,158,504,2.25],[-20,1.68,172,510,2.19],[-10,2.53,186,516,2.14],[0,3.69,200,522,2.09],[10,5.22,215,527,2.04],[20,7.19,230,531,2.00],[30,9.70,246,534,1.96],[40,12.88,262,537,1.92],[50,16.80,279,538,1.88],[60,21.70,297,537,1.84],[70,27.78,318,532,1.79]], cp_liq:1.96, cp_vap:1.12 }, 'R410A': { name:'R410A', Tcrit:71.4, Pcrit:49.03, pts:[[-40,1.00,152,424,1.91],[-30,1.60,166,429,1.86],[-20,2.50,180,434,1.81],[-10,3.80,195,438,1.77],[0,5.70,210,442,1.73],[10,8.20,225,446,1.69],[20,11.48,241,449,1.66],[30,15.75,257,451,1.62],[40,21.16,274,452,1.58],[50,27.81,292,451,1.54],[60,36.25,313,447,1.49]], cp_liq:1.73, cp_vap:1.03 }, 'R134A': { name:'R134A', Tcrit:101.1, Pcrit:40.59, pts:[[-40,0.51,148,372,1.76],[-30,0.84,161,380,1.75],[-20,1.33,174,387,1.73],[-10,2.01,187,395,1.72],[0,2.93,200,401,1.72],[10,4.15,213,407,1.72],[20,5.72,227,412,1.72],[30,7.70,241,417,1.72],[40,10.16,256,420,1.72],[50,13.18,271,423,1.72],[60,16.82,287,424,1.72],[70,21.16,304,423,1.71],[80,26.33,323,421,1.69],[90,32.44,343,414,1.66]], cp_liq:1.45, cp_vap:0.97 }, 'R404A': { name:'R404A', Tcrit:72.1, Pcrit:37.32, pts:[[-40,1.06,153,344,1.59],[-30,1.62,167,351,1.58],[-20,2.40,181,358,1.58],[-10,3.45,195,364,1.57],[0,4.81,210,370,1.57],[10,6.55,225,374,1.56],[20,8.72,241,378,1.55],[30,11.38,257,381,1.54],[40,14.60,274,383,1.53],[50,18.46,293,383,1.51],[60,23.03,314,379,1.49]], cp_liq:1.58, cp_vap:1.00 }, 'R290': { name:'R290', Tcrit:96.7, Pcrit:42.47, pts:[[-40,1.12,100,520,2.48],[-30,1.72,121,532,2.43],[-20,2.50,142,544,2.39],[-10,3.50,164,556,2.35],[0,4.74,186,566,2.32],[10,6.37,209,576,2.28],[20,8.36,232,586,2.25],[30,10.79,257,593,2.22],[40,13.69,282,599,2.19],[50,17.13,309,603,2.16],[60,21.17,337,605,2.13],[70,25.85,367,604,2.09],[80,31.26,400,599,2.05]], cp_liq:2.60, cp_vap:1.87 }, 'R1234YF':{ name:'R1234YF', Tcrit:94.7, Pcrit:33.82, pts:[[-40,0.51,139,330,1.60],[-30,0.84,152,337,1.58],[-20,1.32,166,344,1.57],[-10,2.00,180,350,1.56],[0,2.90,194,356,1.55],[10,4.10,208,361,1.55],[20,5.64,223,365,1.54],[30,7.60,238,369,1.53],[40,10.04,254,371,1.52],[50,13.04,270,372,1.51],[60,16.67,288,372,1.49],[70,21.02,308,369,1.47]], cp_liq:1.40, cp_vap:0.96 }, 'R744': { name:'R744 CO₂', Tcrit:31.0, Pcrit:73.77, pts:[[-40,10.04,99,435,1.93],[-30,14.28,118,437,1.89],[-20,19.70,137,438,1.86],[-10,26.49,158,435,1.82],[0,34.85,180,429,1.78],[10,45.02,206,418,1.73],[20,57.29,237,399,1.67],[25,64.34,259,385,1.64]], cp_liq:2.50, cp_vap:1.20 } };

/* ===== INTERPOLATION ===== */ function interp(pts, T, col) { if (T <= pts[0][0]) return pts[0][col]; if (T >= pts[pts.length-1][0]) return pts[pts.length-1][col]; for (let i=0; i

= pts[i][0] && T <= pts[i+1][0]) { const t1=pts[i][0], t2=pts[i+1][0], v1=pts[i][col], v2=pts[i+1][col]; return v1 + (v2-v1)*(T-t1)/(t2-t1); } } return null; } const getP = (f,T) => interp(FLUIDS[f].pts, T, 1); const getHliq = (f,T) => interp(FLUIDS[f].pts, T, 2); const getHvap = (f,T) => interp(FLUIDS[f].pts, T, 3); const getSvap = (f,T) => interp(FLUIDS[f].pts, T, 4);

/* Estimation T_sat à partir de P (inverse) */ function getT_sat(f, P) { const pts = FLUIDS[f].pts; if (P <= pts[0][1]) return pts[0][0]; if (P >= pts[pts.length-1][1]) return pts[pts.length-1][0]; for (let i=0; i

= pts[i][1] && P <= pts[i+1][1]) { const p1=pts[i][1], p2=pts[i+1][1], t1=pts[i][0], t2=pts[i+1][0]; return t1 + (t2-t1)*(P-p1)/(p2-p1); } } return null; } /* ===== CALCUL CYCLE FRIGORIFIQUE ===== */ function calcCycle(f, Tevap, Tcond, dSurch, dSousR, etaComp) { const P1 = getP(f, Tevap); // pression évap const P2 = getP(f, Tcond); // pression cond // Point 1 : sortie évap (vapeur surchauffée à P1) const h1_sat = getHvap(f, Tevap); const cp_vap = FLUIDS[f].cp_vap; const h1 = h1_sat + cp_vap * dSurch; const s1_sat = getSvap(f, Tevap); // approximation: s1 = s1_sat + cp_vap * ln((T_sat+dSurch+273.15)/(T_sat+273.15)) const T1_abs = Tevap + 273.15 + dSurch; const Tevap_abs = Tevap + 273.15; const s1 = s1_sat + cp_vap * Math.log(T1_abs/Tevap_abs); // Point 2s : compression isentropique (s2s = s1, P2) // Trouver T2s tel que s_vap_surchauffe(T2s, P2) = s1 // Approx: à P2, s_vap_sat = getSvap(f, Tcond), puis ajout cp_vap * ln((T2s_abs)/(Tcond_abs)) const s2_sat = getSvap(f, Tcond); const Tcond_abs = Tcond + 273.15; // s1 = s2_sat + cp_vap * ln(T2s_abs/Tcond_abs) // ln(T2s_abs/Tcond_abs) = (s1 - s2_sat) / cp_vap const T2s_abs = Tcond_abs * Math.exp((s1 - s2_sat) / cp_vap); const dSurch_comp = T2s_abs - Tcond_abs; const h2s_sat = getHvap(f, Tcond); const h2s = h2s_sat + cp_vap * dSurch_comp; // Rendement isentropique : h2 = h1 + (h2s - h1) / eta const h2 = h1 + (h2s - h1) / etaComp; const T2_abs = Tcond_abs + (h2 - h2s_sat)/cp_vap; const T2 = T2_abs - 273.15; // Point 3 : sortie cond (liquide sous-refroidi à P2) const h3_sat = getHliq(f, Tcond); const cp_liq = FLUIDS[f].cp_liq; const h3 = h3_sat - cp_liq * dSousR; // Point 4 : après détente (isenthalpique, P1) const h4 = h3; // Effets const qf = h1 - h4; // effet frigorifique const wc = h2 - h1; // travail compresseur const qc = h2 - h3; // chaleur cédée au condenseur const COP = qf / wc; return { P1,P2, h1,h2,h3,h4, T2, qf,wc,qc, COP, T1:Tevap+dSurch, T3:Tcond-dSousR }; } /* ===== CANVAS RENDER ===== */ const cv = document.getElementById('cv'); const ctx = cv.getContext('2d'); function resizeCV() { const w = cv.parentElement.clientWidth - 40; cv.style.width = w + 'px'; cv.width = Math.min(w*2, 1800); cv.height = Math.min((w*2) * 0.65, 1200); } resizeCV(); window.addEventListener('resize', () => { resizeCV(); render(); });

function getPlotRange(f) { const pts = FLUIDS[f].pts; let hMin = 1e9, hMax = -1e9, pMin = 1e9, pMax = -1e9; pts.forEach(p => { if (p[2] < hMin) hMin = p[2]; if (p[3] > hMax) hMax = p[3]; if (p[1] < pMin) pMin = p[1]; if (p[1] > pMax) pMax = p[1]; }); // extension pour superheat hMax += 60; hMin -= 10; pMin = pMin * 0.6; pMax = pMax * 1.4; return { hMin, hMax, pMin, pMax }; }

let currentCycle = null; let currentRange = null;

function h2x(h) { const w = cv.width; const pad = 80; return pad + (h - currentRange.hMin)/(currentRange.hMax - currentRange.hMin) * (w - pad*1.3); } function p2y(P) { const h = cv.height; const pad = 60; const logP = Math.log10(P); const logMin = Math.log10(currentRange.pMin); const logMax = Math.log10(currentRange.pMax); return h - pad - (logP - logMin)/(logMax - logMin) * (h - pad*1.8); }

function render() { const f = document.getElementById('fluide').value; currentRange = getPlotRange(f); ctx.clearRect(0,0,cv.width,cv.height);

/* Grille + axes */ ctx.strokeStyle = '#f3f4f6'; ctx.lineWidth = 1; ctx.fillStyle = '#6b7280'; ctx.font = '11px sans-serif'; // Grilles horizontales (pression log) const pTicks = [0.5, 1, 2, 5, 10, 20, 50, 100]; pTicks.forEach(P => { if (P >= currentRange.pMin && P <= currentRange.pMax) { const y = p2y(P); ctx.beginPath(); ctx.moveTo(h2x(currentRange.hMin), y); ctx.lineTo(h2x(currentRange.hMax), y); ctx.stroke(); ctx.fillText(P + ' bar', 10, y+4); } }); // Grilles verticales (enthalpie) const hStep = 50; for (let h = Math.ceil(currentRange.hMin/hStep)*hStep; h <= currentRange.hMax; h += hStep) { const x = h2x(h); ctx.beginPath(); ctx.moveTo(x, p2y(currentRange.pMin)); ctx.lineTo(x, p2y(currentRange.pMax)); ctx.stroke(); ctx.fillText(h + ' kJ/kg', x - 20, cv.height - 20); } /* Courbe de saturation (dome) */ ctx.strokeStyle = '#0284c7'; ctx.lineWidth = 2; ctx.beginPath(); const pts = FLUIDS[f].pts; // Partie liquide (gauche) pts.forEach((p,i) => { const x = h2x(p[2]); const y = p2y(p[1]); if (i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y); }); // Partie vapeur (droite) - en sens inverse for (let i = pts.length - 1; i >= 0; i--) { const p = pts[i]; ctx.lineTo(h2x(p[3]), p2y(p[1])); } ctx.closePath(); ctx.stroke();

/* Label fluide */ ctx.fillStyle = '#0284c7'; ctx.font = 'bold 14px sans-serif'; ctx.fillText(FLUIDS[f].name, h2x(currentRange.hMin) + 10, p2y(currentRange.pMax) + 20);

/* Cycle frigorifique */ if (currentCycle) { const c = currentCycle; // segments // 4→1 évaporation (horizontal à P1) ctx.strokeStyle = '#16a34a'; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(h2x(c.h4), p2y(c.P1)); ctx.lineTo(h2x(c.h1), p2y(c.P1)); ctx.stroke(); // 1→2 compression (courbe log, approx ligne droite en log-log) ctx.strokeStyle = '#ea580c'; ctx.beginPath(); ctx.moveTo(h2x(c.h1), p2y(c.P1)); ctx.lineTo(h2x(c.h2), p2y(c.P2)); ctx.stroke(); // 2→3 condensation (horizontal à P2) ctx.strokeStyle = '#9333ea'; ctx.beginPath(); ctx.moveTo(h2x(c.h2), p2y(c.P2)); ctx.lineTo(h2x(c.h3), p2y(c.P2)); ctx.stroke(); // 3→4 détente isenthalpique (vertical) ctx.strokeStyle = '#6b7280'; ctx.setLineDash([6,4]); ctx.beginPath(); ctx.moveTo(h2x(c.h3), p2y(c.P2)); ctx.lineTo(h2x(c.h4), p2y(c.P1)); ctx.stroke(); ctx.setLineDash([]);

// Points + labels const points = [ {h:c.h1, P:c.P1, label:'1', title:'Sortie évap'}, {h:c.h2, P:c.P2, label:'2', title:'Sortie compresseur'}, {h:c.h3, P:c.P2, label:'3', title:'Sortie condenseur'}, {h:c.h4, P:c.P1, label:'4', title:'Après détente'} ]; points.forEach(p => { const x = h2x(p.h), y = p2y(p.P); ctx.fillStyle = '#dc2626'; ctx.beginPath(); ctx.arc(x, y, 7, 0, Math.PI*2); ctx.fill(); ctx.strokeStyle = '#fff'; ctx.lineWidth = 2; ctx.stroke(); ctx.fillStyle = '#dc2626'; ctx.font = 'bold 14px sans-serif'; ctx.fillText(p.label, x + 12, y + 5); }); } }

/* ===== UI EVENTS ===== */ function update() { const f = document.getElementById('fluide').value; const Tevap = +document.getElementById('tevap').value; const Tcond = +document.getElementById('tcond').value; const dSurch = +document.getElementById('surch').value; const dSousR = +document.getElementById('sousr').value; const etaComp = +document.getElementById('eta').value / 100;

document.getElementById('tevap-val').textContent = Tevap + ' °C'; document.getElementById('tcond-val').textContent = '+' + Tcond + ' °C'; document.getElementById('surch-val').textContent = dSurch + ' K'; document.getElementById('sousr-val').textContent = dSousR + ' K'; document.getElementById('eta-val').textContent = (etaComp*100).toFixed(0) + ' %';

// Validate: Tevap < Tcond if (Tevap >= Tcond) { document.getElementById('cop').textContent = 'COP = — (T évap ≥ T cond invalide)'; currentCycle = null; render(); return; }

try { const c = calcCycle(f, Tevap, Tcond, dSurch, dSousR, etaComp); currentCycle = c; document.getElementById('p1').textContent = c.P1.toFixed(2) + ' bar'; document.getElementById('p2').textContent = c.P2.toFixed(2) + ' bar'; document.getElementById('ratio').textContent = (c.P2/c.P1).toFixed(2); document.getElementById('qf').textContent = c.qf.toFixed(1) + ' kJ/kg'; document.getElementById('wc').textContent = c.wc.toFixed(1) + ' kJ/kg'; document.getElementById('qc').textContent = c.qc.toFixed(1) + ' kJ/kg'; document.getElementById('cop').textContent = 'COP = ' + c.COP.toFixed(2); render(); } catch(e) { document.getElementById('cop').textContent = 'COP = — (erreur calc)'; currentCycle = null; render(); } }

['fluide','tevap','tcond','surch','sousr','eta'].forEach(id => { document.getElementById(id).addEventListener('input', update); document.getElementById(id).addEventListener('change', update); });

update();

À quoi sert le diagramme de Mollier ?

Le diagramme de Mollier (ou enthalpique) représente graphiquement les états thermodynamiques d’un fluide frigorigène sur un axe log(pression) vs enthalpie. Il permet aux frigoristes, techniciens PAC et étudiants en froid industriel de :

Lectures utiles

Précision : tables thermodynamiques compilées depuis NIST REFPROP et ASHRAE Handbook 2021, interpolation linéaire — précision ±2 % sur plage frigoriste usuelle. Pour calculs d’ingénierie précis, utiliser CoolProp ou NIST REFPROP.

Seraphinite AcceleratorBannerText_Seraphinite Accelerator
Turns on site high speed to be attractive for people and search engines.