1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2869],{2869:(e,t,a)=>{a.r(t),a.d(t,{addressCarriesOwnState:()=>K,default:()=>D});var s=a(95155),r=a(12115),i=a(33789),n=a(33534),o=a(16110),l=a(69869),h=a(49480),u=a(12513),d=a(27784);let c=67,m={lowFPerHour:.5,highFPerHour:1.5},p={lowFPerHour:.1,highFPerHour:.5},g={lowFPerHour:3,highFPerHour:6},b=(e,t,a)=>e*Math.max(0,t-a),f=(e,t,a)=>a<=0?1/0:e+t/a,w=(e,t)=>.8*e*t,x=[{kw:1.5,breakerAmps:15,application:"Small tubs, entry level"},{kw:4,breakerAmps:25,application:"Medium 240 volt tubs"},{kw:5.5,breakerAmps:30,application:"Standard full sized residential"},{kw:11,breakerAmps:60,application:"Large commercial or swim spas"}],v={volts:120,heaterKwLow:1,heaterKwHigh:1.5,circuitAmps:[15,20]},y=["unanswered","covered","uncovered"],j=["unanswered","240","120"],F=(e,t,a)=>"string"==typeof e&&t.includes(e)?e:a,N=e=>"number"==typeof e&&Number.isFinite(e),k=(e,t)=>{let a=10**t;return Math.round(e*a)/a},T={gallons:0,startF:0,targetF:0,ambientF:0,cover:"unanswered",heaterKw:0,poundsOfWater:0,deltaF:0,btuRequired:0,heaterBtuPerHour:0,grossRiseFPerHour:0,zeroLossHours:0,band:null,readings:[],anyUnreachable:!1,allUnreachable:!1,notes:[]},A=e=>({...T,problem:e,verdict:"refuse",missing:[]}),$={heaterKw:0,volts:0,heaterBtuPerHour:0,runningAmps:0,minimumCircuitAmps:0,publishedRow:null,namedCircuits:[],nameplateAmps:null,nameplateCoversHeater:null,notes:[]},H=e=>({...$,problem:e,verdict:"refuse",missing:[]}),P=["It will not tell you what breaker to fit. It reports the minimum ampacity the continuous load rule asks the branch circuit to be rated for, which is the running current times 1.25, and it repeats the four heater sizes the research tabulates. For any other size the standard rating just above that ampacity comes off the code list, and reading that list is work for a licensed electrician.","It will not tell you what size wire to run. Conductor ampacity turns on the metal, the insulation temperature rating, the ambient temperature, how many current carrying conductors share the raceway and what the terminals are listed for, and a calculator that prints a wire size is inventing an approval nobody gave it.","It will not size the feed for the whole tub. Every ampere here is the heater by itself, and a packaged spa also runs pumps, a blower, an ozone generator and a control board. The total rating is on the equipment placard and that is the number your installation is built to.","It will not cover the disconnect, the bonding, the ground fault protection or the clearances a spa install needs. Those are code requirements rather than arithmetic, they change with the jurisdiction, and none of them follows from a kilowatt figure.","It will not scale the published loss figures by the size of your tub. The research states them in degrees per hour and never says what volume they were measured on, so this page applies them as published. That makes it pessimistic about a very large tub and optimistic about a very small one, because real heat loss follows surface area while the temperature drop it causes follows volume.","It will not grade your cover. The figure it computes with is published for a covered tub as a class, from a high quality cover down to a tired one, and a separate lookup puts a genuinely well sealed cover at 0.1 to 0.5 degrees an hour instead. Which of those describes the cover on your tub is something only you can see, and this page keeps the published band because the cautious end of it is the safer place to be wrong.","It will not model wind, humidity, rain or sun. The research says uncovered loss accelerates rapidly as wind picks up and gives no figure for it, so an exposed site loses more than anything printed here.","It will not work out your running cost. That needs a tariff, and it needs the loss over a whole day rather than over one climb, and a cheap number here would be quoted at people for years.","It will not say what temperature is safe to sit in. The 110 F ceiling on the target box is an input guard chosen for this page, not a published bathing limit.","It will not guess y
1our gallons from the tub dimensions. A spa shell is molded with footwells, seats and a lounger, so no length by width by depth expression describes it, and the manufacturer publishes the real figure on the spec sheet."],W="The energy half of this page is exact and the time half is not. Raising a known weight of water by a known number of degrees takes a fixed number of BTU, and every hour figure here also depends on a published heat loss band that is three times wide at both ends, so read the two times as a range rather than as an answer. The band was published in degrees per hour without the tub volume, the cover age or the wind speed it was measured at, and all three matter. Cover quality is the one this page can least afford to miss, because a separate lookup puts a genuinely well sealed cover at 0.1 to 0.5 degrees an hour against the 0.5 to 1.5 computed here, so a new cover beats every time printed and a tired one will not. The published band is kept anyway, since charging too much loss lengthens the climb and lowers the winter ceiling, and that is the safer direction to be wrong in. Uncovered is the reading to trust least, and it looks low: taken literally on a 400 gallon tub it implies a water surface of 12 to 25 square feet, where a real tub presents nearer 35 to 45, so a tub with the cover off may well lose faster than the worst case here. Two choices in the model belong to this page rather than to the research, and both are printed on the page. The loss band is anchored at 102 F water against 35 F air, the midpoint of the window it was quoted in, and the band width is treated as tub-to-tub variation rather than as weather. Anything on the electrical side is the heater by itself, the placard on your tub is what the installation is built to, and the breaker, the wire, the bond and the disconnect are work for a licensed electrician rather than for a web page.",I="toolbreezy:hot-tub-heater-calculator",E="flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",C=e=>""===e.trim()?null:(0,h.jO)(e),S=e=>k(e,0).toLocaleString(void 0,{maximumFractionDigits:0}),L=e=>k(e,1).toLocaleString(void 0,{maximumFractionDigits:1}),O=e=>k(e,2).toLocaleString(void 0,{maximumFractionDigits:2}),B=e=>{let t=Math.floor(e),a=Math.round((e-t)*60);return 60===a?`${t+1} h 0 min`:`${t} h ${a} min`},R=["unanswered","covered","uncovered"],z={unanswered:"Pick one before anything else",covered:"Covered, with the cover on for the whole climb",uncovered:"Uncovered, cover off or in use"},M=["unanswered","240","120"],U={unanswered:"Pick your supply",240:"240 V, hard wired to its own circuit",120:"120 V, plugs into a household outlet"},q=["c","s","g","t0","t1","a","kw","ml","md","np"];function K(e){let t=new URLSearchParams(e.replace(/^\?/,""));return(0,l.Yc)(t,q)}function D(){let[e,t]=(0,r.useState)("unanswered"),[a,h]=(0,r.useState)("unanswered"),[D,G]=(0,r.useState)("400"),[V,Y]=(0,r.useState)("50"),[_,Z]=(0,r.useState)("102"),[J,Q]=(0,r.useState)("35"),[X,ee]=(0,r.useState)("5.5"),[et,ea]=(0,r.useState)(""),[es,er]=(0,r.useState)(""),[ei,en]=(0,r.useState)("");(0,r.useEffect)(()=>{let e=new URLSearchParams(window.location.search),a=(t,a)=>{let s=e.get(t);return null!==s&&a.includes(s)?s:null};if(K(window.location.search)){let s=t=>e.get(t),r=a("c",R);r&&t(r);let i=a("s",M);i&&h(i),null!==s("g")&&G(s("g").slice(0,10)),null!==s("t0")&&Y(s("t0").slice(0,10)),null!==s("t1")&&Z(s("t1").slice(0,10)),null!==s("a")&&Q(s("a").slice(0,10)),null!==s("kw")&&ee(s("kw").slice(0,10)),null!==s("ml")&&ea(s("ml").slice(0,10)),null!==s("md")&&er(s("md").slice(0,10)),null!==s("np")&&en(s("np").slice(0,10));return}let s=(0,u.y5)(I);s.found&&(t(s.oneOf("c",R,"unanswered")),h(s.oneOf("s",M,"unanswered")),G(s.str("g","400",10)),Y(s.str("t0","50",10)),Z(s.str("t1","102",10)),Q(s.str("a","35",10)),ee(s.str("kw","5.5",10)),ea(s.str("ml","",10)),er(s.str("md","",10)),en(s.str("np","",10)))},[]),(0,d.Vd)(I,()=>JSON.stringify({c:e,s:a,g:D,t0:V,t1:_,a:J,kw:X,ml:et,md:es,np:ei}),[e,a,D,V,_,J,X,et,es,ei],{debounceMs:250});let eo=(0,r.useMemo)(()=>(function(e){let t=F(e.cover,y,"unanswered"),a=[];if("unanswered"===t&&a.push("Say whether the cover is on while it heats. It is the largest single number on this page and there is no safe default for it, because the answer that lets the calculation look good is the one most people get wrong."),N(e.gallons)||a.push("Enter how many gallons the tub holds."),N(e.startF)||a.push("Enter the water temperature it reads now."),N(e.targetF)||a.push("Enter the temperature you want it at."),N(e.ambientF)||a.push("Enter the outdoor air temperature it will be sitting in."),N(e.heaterKw)||a.push("Enter the heater output in kilowatts."),a.length>0)return{...T,problem:null,verdict:"incomplete",missing:a};let s=e.gallons,r=e.startF,i=e.targetF,n=e.ambientF,o=e.heaterKw;if(s<100||s>
12e3)return A(`A volume of ${s.toLocaleString()} gallons is outside the 100 to ${2e3.toLocaleString()} gallon range this page covers. The published loss figures were measured on residential spas, and outside that span they are describing a different vessel. Your tub's gallonage is on its spec sheet.`);if(r<33||r>110)return A(`A starting water temperature of ${r} F is outside the 33 to 110 F range this page covers.`);if(i<33||i>110)return A(`A target of ${i} F is outside the 33 to 110 F range this page covers. The 110 F end is an input guard chosen here rather than a published bathing limit, and this page makes no claim about what is safe to sit in.`);if(i<=r)return A(`The target of ${i} F is not above the ${r} F the water reads now, so there is nothing to heat. If the tub is already warmer than you want it, the answer is to stop heating it rather than to time a climb.`);if(n<-30||n>120)return A(`An air temperature of ${n} F is outside the -30 to 120 F range this page covers.`);if(n>=i)return A(`With the air at ${n} F and the target at ${i} F there is no temperature gap for the published loss figures to scale against. Those figures are winter numbers keyed entirely to the gap between the water and the air, and a tub in air that warm is losing heat by evaporation instead, which depends on humidity and wind rather than on air temperature. This page will not model it.`);if(o<.5||o>30)return A(`A heater of ${o} kW is outside the 0.5 to 30 kW range this page covers. The output in kilowatts is on the heater assembly or in the tub's manual, and it is not the same figure as the breaker size.`);let l=N(e.measuredLossFPerHour);if(l!==N(e.measuredAtDeltaF))return A("A measured loss rate needs the temperature gap it was measured at alongside it. A tub dropping one degree an hour with the air at 30 F and a tub dropping one degree an hour with the air at 60 F are losing heat at very different rates, and without the gap there is no way to scale the figure to tonight. Enter both or neither.");if(l){let t=e.measuredLossFPerHour,a=e.measuredAtDeltaF;if(t<.05||t>30)return A(`A measured loss of ${t} degrees an hour is outside the 0.05 to 30 degree range this page will read. A tub losing nothing at all has not been measured for long enough.`);if(a<5||a>140)return A(`A measured gap of ${a} degrees is outside the 5 to 140 degree range this page will read. The gap is the water temperature minus the air temperature over the period you watched.`)}let h="covered"===t?m:g,u=3412.141633127942*o/(8.34*s),d=i-r,p=8.34*s*1*d,w=(e,t,a,s)=>{let o=((e,t=c)=>e/t)(a,s),l=f(n,u,o),h=function(e,t,a,s,r){if(!(s>0))return null;if(t<=e)return 0;let i=0,n=e;if(n<a){let e=Math.min(t,a);if(i+=(e-n)/s,(n=e)>=t)return i}if(r<=0)return i+(t-n)/s;let o=f(a,s,r);return t>=o?null:i+1/r*Math.log((o-n)/(o-t))}(r,i,n,u,o),d=b(o,i,n);return{label:e,sourceClass:t,fPerHourAtAnchor:a,anchorDeltaF:s,kPerHour:o,fPerHourAtTarget:d,fPerHourAtStart:b(o,r,n),ceilingF:l,ceilingIsBinding:l<=120,reachesTarget:null!==h,hours:h,ruleOfThumbHours:function(e,t,a,s){let r=a-s;return r>0?(t-e)/r:null}(r,i,u,d)}},x=w("covered"===t?"Published best case, covered":"Published best case, uncovered","published",h.lowFPerHour,c),v=w("covered"===t?"Published worst case, covered":"Published worst case, uncovered","published",h.highFPerHour,c),j=l?w("Your own measurement","measured",e.measuredLossFPerHour,e.measuredAtDeltaF):null,k=null===j?[x,v]:[x,v,j],$=[],H=j??v,P=k.some(e=>!e.reachesTarget),W=k.every(e=>!e.reachesTarget);return W?$.push(`On every loss figure here this heater cannot get the water to ${i} F in ${n} F air. The loss catches the heater on the way up and the water settles below the target instead of reaching it. A cover, a warmer spell or a bigger heater are the three things that move it.`):P&&$.push(`The published loss figures disagree about whether ${i} F is reachable in ${n} F air, which means this tub is close to the line. Treat the target as reachable only if your cover and cabinet are in good condition, and watch what the water actually does over the first few hours.`),"covered"===t&&$.push("Cover quality is a variable this page has no term for, and it is the largest thing it cannot see. The band it computes with is the published figure for a covered tub as a class, running from a high quality cover down to a tired one. A separate lookup puts a cover that is genuinely well insulated and tightly sealed at 0.1 to 0.5 degrees an hour, which meets the slow end of that band rather than contradicting it. So a new cover that still seals will beat every time on this page, a waterlogged one nearer ten years old will not, and computing on the published band errs toward the longer heat-up and the lower winter ceiling, which is the safer direction to be wrong in."),"uncovered"===t&&$.push("Uncovered is the reading to trust least. The published band of 3 to 6 degrees an hour is quoted without a wind figure and the same research says the loss accelerates rapidly as wind picks up, so an exposed deck in a breeze is worse than anything on this page."),n>70&&$.push(`The published loss figures were measured in 30 to 40 F air and you have entered ${n} F. This page scales them by the temperature gap, and on a mild day evaporation carries off heat that the gap does not account for, so the real loss is higher than what is printed here.`),l&&$.push("Your own measurement supersedes the published band, because it was taken on your tub with your cover in your weather. The published pair is left on the page so you can see where your tub sits against them."),H.reachesTarget&&null!==H.hours&&null!==H.ruleOfThumbHours&&$.pu
1sh("The rule of thumb charges the full operating-temperature loss against every hour of the climb, including the hours when the water is cold and losing very little, so it always returns a longer time than a loss that grows with the gap."),{problem:null,verdict:"compute",missing:[],gallons:s,startF:r,targetF:i,ambientF:n,cover:t,heaterKw:o,poundsOfWater:8.34*s,deltaF:d,btuRequired:p,heaterBtuPerHour:3412.141633127942*o,grossRiseFPerHour:u,zeroLossHours:p/(3412.141633127942*o),band:h,readings:k,anyUnreachable:P,allUnreachable:W,notes:$}})({gallons:C(D),startF:C(V),targetF:C(_),ambientF:C(J),cover:e,heaterKw:C(X),measuredLossFPerHour:C(et),measuredAtDeltaF:C(es)}),[D,V,_,J,e,X,et,es]),el=(0,r.useMemo)(()=>(function(e){let t=F(e.supply,j,"unanswered"),a=[];if("unanswered"===t&&a.push("Say which supply the tub runs on. A 240 volt hard wired tub and a 120 volt plug-and-play tub follow different rules and neither is a safe default for the other."),N(e.heaterKw)||a.push("Enter the heater output in kilowatts."),a.length>0)return{...$,problem:null,verdict:"incomplete",missing:a};let s=e.heaterKw;if(s<.5||s>30)return H(`A heater of ${s} kW is outside the 0.5 to 30 kW range this page covers.`);let r=N(e.nameplateBreakerAmps);if(r){let t=e.nameplateBreakerAmps;if(t<15||t>200)return H(`A placard rating of ${t} amps is outside the 15 to 200 amp range this page will read. It is the total breaker size printed on the tub's equipment label, next to the voltage.`)}let i="240"===t?240:v.volts,n=1e3*s,o=n/i,l=1.25*o,h="240"===t?x.map(e=>e.breakerAmps):v.circuitAmps,u=new Set,d=[];for(let e of h){if(u.has(e))continue;u.add(e);let t=w(e,i);d.push({amps:e,volts:i,maxContinuousWatts:t,carriesThisHeater:n<=t})}d.sort((e,t)=>e.amps-t.amps);let c="240"===t?x.find(e=>e.kw===s)??null:null,m=["Every ampere figure on this page is the heater by itself. A packaged spa runs jet pumps, a circulation pump, a blower, an ozone generator and a control board off the same feed, and the research puts a standard 5.5 kW spa at a total system breaker of 50 or 60 amps so the heater can run while the jets are on high. The rating that governs your installation is printed on the equipment placard, and this page will not estimate it."];if("240"===t)null===c?m.push(`This page will not name a breaker for a ${s} kW heater. It reports the ${k(l,2)} amps the continuous load rule asks the branch circuit to be rated for, and the standard rating just above that comes off the code's own list, which is the electrician's to read rather than this page's to guess. The four sizes below are the ones the research tabulates.`):m.push(`${c.kw} kW is one of the four sizes the research tabulates, and it prints ${c.breakerAmps} amps for the heater alone. That is the smallest standard rating at or above the ${k(l,2)} amps the continuous load rule asks for, so the published figure and the arithmetic agree.`);else{m.push("A 120 volt tub cannot run the heater and the jets on high at the same time, so its control board cuts the heater whenever the jets go to high speed. Every hour figure on this page assumes the heater is energized for the full hour, which on a 120 volt tub means the tub is left alone to heat. Time spent in it with the jets running is time the water is only losing heat.");let e=d.filter(e=>e.carriesThisHeater);0===e.length?m.push(`At 120 volts this heater draws ${k(o,2)} amps and the continuous load rule asks for ${k(l,2)}, which is past both of the household circuits the research names. A tub needing that is not a plug-and-play tub and wants a dedicated 240 volt feed.`):e.length<d.length&&m.push(`The research names 15 and 20 amp circuits for plug-and-play tubs and 1.0 to 1.5 kW heaters, and never says which goes with which. Its own continuous load rule does: a 15 amp circuit carries ${k(w(15,120),0)} watts continuously, so a ${s} kW heater clears only the ${e.map(e=>e.amps).join(" and ")} amp circuit.`),(s<v.heaterKwLow||s>v.heaterKwHigh)&&m.push(`The research puts true plug-and-play heaters at ${v.heaterKwLow} to ${v.heaterKwHigh} kW and this one is ${s} kW, so check the figure against the heater assembly before acting on any of it.`)}let p=null;if(r){let t=e.nameplateBreakerAmps;(p=t>=l)?m.push(`The placard says ${t} amps, which is above the ${k(l,2)} amps this heater alone asks for. The difference is the pumps, the blower and the control board, and the placard is the figure the installation is built to.`):m.push(`The placard says ${t} amps and this heater alone asks the circuit to be rated for ${k(l,2)}. Those two do not fit together, so one of the two numbers is not describing this tub. The placard is the authoritative one, so check the kilowatt figure you entered against the heater assembly.`)}return{problem:null,verdict:"compute",missing:[],heaterKw:s,volts:i,heaterBtuPerHour:3412.141633127942*s,runningAmps:o,minimumCircuitAmps:l,publishedRow:c,namedCircuits:d,nameplateAmps:r?e.nameplateBreakerAmps:null,nameplateCoversHeater:p,notes:m}})({heaterKw:C(X),supply:a,nameplateBreakerAmps:C(ei)}),[X,a,ei]),eh={c:e,s:a,g:D||null,t0:V||null,t1:_||null,a:J||null,kw:X||null,ml:et||null,md:es||null,np:ei||null},eu=e=>null===e?"never reaches it":B(e),ed=["HOT TUB HEAT-UP TIME","",...eo.missing.map(e=>`STILL NEEDED: ${e}`),eo.problem?`PROBLEM: ${eo.problem}`:"","compute"===eo.verdict?[`Tub ${S(eo.gallons)} gal (${S(eo.poundsOfWater)} lb of water)`,`Climb ${L(eo.startF)} F to ${L(eo.targetF)} F, a rise of ${L(eo.deltaF)} F`,`Outside air ${L(eo.ambientF)} F, cover ${"covered"===eo.cover?"ON":"OFF"}`,`Heater ${O(eo.heaterKw)} kW = ${S(eo.heaterBtuPerHour)} BTU/hr`,"",`Energy needed ${S(eo.btuRequired)} BTU`,`Gross heater rise ${O(eo.grossRiseFPerHour)} F per hour, before any loss`,`Zero-loss time ${B(eo.zeroLossHours)} the textbook answer, which no tub achieves`,"","WITH THE HEAT IT IS LOSING WHILE IT GAINS",...eo.readings.map(e=>` ${e.label}
2 loss at the anchor ${O(e.fPerHourAtAnchor)} F/hr at a ${S(e.anchorDeltaF)} F gap 3 loss at ${L(eo.startF)} F water ${O(e.fPerHourAtStart)} F/hr 4 loss at ${L(eo.targetF)} F water ${O(e.fPerHourAtTarget)} F/hr 5 ceiling ${e.ceilingIsBinding?`${L(e.ceilingF)} F`:"above 120 F, so not what limits this tub"} 6 TIME ${eu(e.hours)} 7 rule of thumb time ${eu(e.ruleOfThumbHours)}`)].join("\n"):"","compute"===el.verdict?["",`ELECTRICAL, THE HEATER ALONE, AT ${el.volts} V`,` Running current ${O(el.runningAmps)} A`,` Minimum circuit ${O(el.minimumCircuitAmps)} A (running x 1.25, the continuous load rule)`,el.publishedRow?` Published breaker ${el.publishedRow.breakerAmps} A for a ${el.publishedRow.kw} kW heater (${el.publishedRow.application})`:" Published breaker not tabulated for this size, see the note below",...el.namedCircuits.map(e=>` ${e.amps} A at ${e.volts} V carries ${S(e.maxContinuousWatts)} W continuously, ${e.carriesThisHeater?"covers this heater":"does NOT cover this heater"}`),null!==el.nameplateAmps?` Placard on your tub ${el.nameplateAmps} A total`:""].filter(Boolean).join("\n"):el.missing.map(e=>`STILL NEEDED: ${e}`).join("\n"),el.problem?`PROBLEM: ${el.problem}`:"","",...eo.notes.map(e=>`NOTE: ${e}`),...el.notes.map(e=>`NOTE: ${e}`),"","WHAT THIS PAGE WILL NOT WORK OUT",...P.map(e=>` ${e}`),"",W].filter(e=>""!==e).join("\n");return(0,s.jsxs)("div",{className:"space-y-6",children:[(0,s.jsxs)("div",{className:"rounded-lg border p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"First, is the cover on while it heats?"}),(0,s.jsxs)("p",{className:"mt-2 text-sm text-muted-foreground",children:["The published figures put a covered tub at ",m.lowFPerHour," to"," ",m.highFPerHour," degrees an hour of loss in winter air and an uncovered one at ",g.lowFPerHour," to"," ",g.highFPerHour,", so this one answer can be worth four times the heat. There is no default for it here, because the answer that makes the arithmetic look good is the one people get wrong."]}),(0,s.jsxs)("p",{className:"mt-2 text-sm text-muted-foreground",children:["How good the cover is has no box of its own, and it is the largest thing this page cannot see. The band above is published for a covered tub as a class, and a separate lookup puts a cover that is genuinely well insulated and tightly sealed at"," ",p.lowFPerHour," to ",p.highFPerHour," ","degrees an hour, which meets the slow end of the band rather than arguing with it. A new cover that still seals will beat every time printed here, and a waterlogged one nearer ten years old will not."]}),(0,s.jsxs)("div",{className:"mt-4 grid gap-4 sm:grid-cols-2",children:[(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-cover",children:"Cover"}),(0,s.jsx)("select",{id:"hth-cover",className:E,value:e,onChange:e=>t(e.target.value),children:R.map(e=>(0,s.jsx)("option",{value:e,children:z[e]},e))})]}),(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-supply",children:"Supply the heater runs on"}),(0,s.jsx)("select",{id:"hth-supply",className:E,value:a,onChange:e=>h(e.target.value),children:M.map(e=>(0,s.jsx)("option",{value:e,children:U[e]},e))})]})]})]}),(0,s.jsxs)("div",{className:"rounded-lg border p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"The tub, the temperatures and the heater"}),(0,s.jsxs)("div",{className:"mt-4 grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:[(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-gal",children:"Water capacity, US gallons"}),(0,s.jsx)(i.p,{id:"hth-gal",inputMode:"decimal",value:D,onChange:e=>G(e.target.value)}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"On the spec sheet or the manual. This page will not guess it from the tub dimensions."})]}),(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-t0",children:"Water reads now, F"}),(0,s.jsx)(i.p,{id:"hth-t0",inputMode:"decimal",value:V,onChange:e=>Y(e.target.value)})]}),(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-t1",children:"Temperature you want, F"}
7),(0,s.jsx)(i.p,{id:"hth-t1",inputMode:"decimal",value:_,onChange:e=>Z(e.target.value)})]}),(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-air",children:"Outside air, F"}),(0,s.jsx)(i.p,{id:"hth-air",inputMode:"decimal",value:J,onChange:e=>Q(e.target.value)}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"The average over the hours it will be heating, not the daytime high."})]}),(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-kw",children:"Heater output, kW"}),(0,s.jsx)(i.p,{id:"hth-kw",inputMode:"decimal",value:X,onChange:e=>ee(e.target.value)}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"On the heater assembly. It is not the breaker size and it is not the pump rating."})]}),(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-np",children:"Placard breaker rating, amps"}),(0,s.jsx)(i.p,{id:"hth-np",inputMode:"decimal",value:ei,onChange:e=>en(e.target.value)}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"Optional, off the equipment label. It covers the whole tub rather than the heater."})]})]})]}),"incomplete"===eo.verdict&&(0,s.jsxs)("div",{className:"rounded-lg border bg-muted p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"Before this page will answer"}),(0,s.jsx)("ul",{className:"mt-2 list-disc space-y-2 pl-5 text-sm text-muted-foreground",children:eo.missing.map(e=>(0,s.jsx)("li",{children:e},e))})]}),"refuse"===eo.verdict&&eo.problem&&(0,s.jsxs)("div",{className:"rounded-lg border border-destructive/50 bg-destructive/5 p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"This page will not answer that"}),(0,s.jsx)("p",{className:"mt-2 text-sm text-muted-foreground",children:eo.problem})]}),"compute"===eo.verdict&&(0,s.jsxs)("div",{className:"rounded-lg border p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"The energy is exact, and the time depends on what it is losing"}),(0,s.jsxs)("div",{className:"mt-4 grid gap-4 sm:grid-cols-3",children:[(0,s.jsxs)("div",{className:"rounded-md bg-muted p-3 text-sm",children:[(0,s.jsx)("div",{className:"text-muted-foreground",children:"Energy the water needs"}),(0,s.jsxs)("div",{className:"mt-1 text-lg font-medium tabular-nums",children:[S(eo.btuRequired)," BTU"]}),(0,s.jsxs)("div",{className:"mt-1 text-xs text-muted-foreground",children:[S(eo.poundsOfWater)," lb of water lifted ",L(eo.deltaF)," F"]})]}),(0,s.jsxs)("div",{className:"rounded-md bg-muted p-3 text-sm",children:[(0,s.jsx)("div",{className:"text-muted-foreground",children:"Heater output"}),(0,s.jsxs)("div",{className:"mt-1 text-lg font-medium tabular-nums",children:[S(eo.heaterBtuPerHour)," BTU/hr"]}),(0,s.jsxs)("div",{className:"mt-1 text-xs text-muted-foreground",children:[O(eo.grossRiseFPerHour)," F per hour into this much water, before any loss"]})]}),(0,s.jsxs)("div",{className:"rounded-md bg-muted p-3 text-sm",children:[(0,s.jsx)("div",{className:"text-muted-foreground",children:"Zero-loss time"}),(0,s.jsx)("div",{className:"mt-1 text-lg font-medium tabular-nums",children:B(eo.zeroLossHours)}),(0,s.jsx)("div",{className:"mt-1 text-xs text-muted-foreground",children:"What the textbook formula alone says, and it is a floor rather than an answer"})]})]}),(0,s.jsx)("h4",{className:"mt-6 text-sm font-medium",children:"The same climb with the heat the tub is losing while it gains"}),(0,s.jsx)("div",{className:"mt-3 space-y-3",children:eo.readings.map(e=>(0,s.jsxs)("div",{className:`rounded-md border p-3 text-sm ${!e.reachesTarget?"border-destructive/50 bg-destructive/5":"bg-muted"}`,children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-baseline justify-between gap-2",children:[(0,s.jsx)("span",{className:"font-medium",children:e.label}),(0,s.jsx)("span",{className:"text-xs text-muted-foreground",children:"measured"===e.sourceClass?`your ${O(e.fPerHourAtAnchor)} F per hour at a ${S(e.anchorDeltaF)} F gap`:`${O(e.fPerHourAtAnchor)} F per hour at the published ${S(e.anchorDeltaF)} F gap`})]}),(0,s.jsxs)("div",{className:"mt-2 grid gap-2 sm:grid-cols-2",children:[(0,s.jsxs)("div",{className:"flex justify-between gap-4",children:[(0,s.jsxs)("span",{className:"text-muted-foreground",children:["Time to ",L(eo.targetF)," F"]}),(0,s.jsx)("span",{className:"tabular-nums font-medium",children:eu(e.hours)})]}),(0,s.jsxs)("div",{className:"flex justify-between gap-4",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Rule of thumb would say"}),(0,s.jsx)("span",{className:"tabular-nums",children:eu(e.ruleOfThumbHours)})]}),(0,s.jsxs)("div",{className:"flex justify-between gap-4",children:[(0,s.jsxs)("span",{className:"text-muted-foreground",children:["Losing at ",L(eo.startF)," F water"]}),(0,s.jsxs)("span",{className:"tabular-nums",children:[O(e.fPerHourAtStart)," F/hr"]})]}),(0,s.jsxs)("div",{className:"flex justify-between gap-4",children:[(0,s.jsxs)("span",{className:"text-muted-foreground",children:["Losing at ",L(eo.targetF)," F water"]}),(0,s.jsxs)("span",{className:"tabular-nums",children:[O(e.fPerHourAtTarget)," F/hr"]})]}),(0,s.jsxs)("div",{className:"flex justify-between gap-4 sm:col-span-2",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Temperature this heater settles at"}),(0,s.jsx)("span",{className:"tabular-nums font-medium",children:e.ceilingIsBinding?`${L(e.ceilingF)} F`:"above 120 F, so the ceiling is not what limits this tub"})]})]})]},e.label))}),eo.notes.length>0&&(0,s.jsx)("ul",{className:"mt-4 list-disc space-y-2 pl-5 text-sm text-muted-foreground",children:eo.notes.map(e=>(0,s.jsx)("li",{children:e},e))})]}),(0,s.jsxs)("div",{className:"rounded-lg border p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"If you have measured your own tub, use that instead"}),(0,s.jsx)("p",{className:"mt-2 text-sm text-muted-foreground",children:"Switch the heater off overnight, note the water temperature and the outside air at both ends, and divi
7de the drop by the hours. A figure taken on your tub with your cover in your weather beats a published band every time, and it replaces the two readings above with one."}),(0,s.jsxs)("div",{className:"mt-4 grid gap-4 sm:grid-cols-2",children:[(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-ml",children:"Measured drop, F per hour"}),(0,s.jsx)(i.p,{id:"hth-ml",inputMode:"decimal",value:et,onChange:e=>ea(e.target.value)})]}),(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)("label",{className:"text-sm font-medium",htmlFor:"hth-md",children:"Water minus air over that period, F"}),(0,s.jsx)(i.p,{id:"hth-md",inputMode:"decimal",value:es,onChange:e=>er(e.target.value)}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"Both boxes or neither. A drop rate without the gap it happened across cannot be scaled to tonight."})]})]})]}),(0,s.jsxs)("div",{className:"rounded-lg border p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"What the heater alone draws"}),"incomplete"===el.verdict&&(0,s.jsx)("ul",{className:"mt-2 list-disc space-y-2 pl-5 text-sm text-muted-foreground",children:el.missing.map(e=>(0,s.jsx)("li",{children:e},e))}),"refuse"===el.verdict&&el.problem&&(0,s.jsx)("p",{className:"mt-2 text-sm text-muted-foreground",children:el.problem}),"compute"===el.verdict&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("div",{className:"mt-4 grid gap-4 sm:grid-cols-3",children:[(0,s.jsxs)("div",{className:"rounded-md bg-muted p-3 text-sm",children:[(0,s.jsxs)("div",{className:"text-muted-foreground",children:["Running current at ",el.volts," V"]}),(0,s.jsxs)("div",{className:"mt-1 text-lg font-medium tabular-nums",children:[O(el.runningAmps)," A"]})]}),(0,s.jsxs)("div",{className:"rounded-md bg-muted p-3 text-sm",children:[(0,s.jsx)("div",{className:"text-muted-foreground",children:"Circuit must be rated for"}),(0,s.jsxs)("div",{className:"mt-1 text-lg font-medium tabular-nums",children:[O(el.minimumCircuitAmps)," A"]}),(0,s.jsxs)("div",{className:"mt-1 text-xs text-muted-foreground",children:["Running current times ",1.25,", because a spa heater is a continuous load"]})]}),(0,s.jsxs)("div",{className:"rounded-md bg-muted p-3 text-sm",children:[(0,s.jsx)("div",{className:"text-muted-foreground",children:"Heat delivered"}),(0,s.jsxs)("div",{className:"mt-1 text-lg font-medium tabular-nums",children:[S(el.heaterBtuPerHour)," BTU/hr"]})]})]}),(0,s.jsx)("div",{className:"mt-4 space-y-1 text-sm",children:el.namedCircuits.map(e=>(0,s.jsxs)("div",{className:"flex flex-wrap justify-between gap-2 border-t pt-1",children:[(0,s.jsxs)("span",{children:[e.amps," A at ",e.volts," V",(0,s.jsxs)("span",{className:"block text-xs text-muted-foreground",children:["carries ",S(e.maxContinuousWatts)," watts continuously"]})]}),(0,s.jsx)("span",{className:"shrink-0 text-xs",children:e.carriesThisHeater?"covers this heater":"does not cover this heater"})]},e.amps))}),el.publishedRow&&(0,s.jsxs)("p",{className:"mt-3 text-sm text-muted-foreground",children:["A ",el.publishedRow.kw," kW heater is one of the four sizes the research tabulates, under ",el.publishedRow.application.toLowerCase(),", and it prints"," ",el.publishedRow.breakerAmps," amps for the heater by itself."]}),(0,s.jsx)("ul",{className:"mt-3 list-disc space-y-2 pl-5 text-sm text-muted-foreground",children:el.notes.map(e=>(0,s.jsx)("li",{children:e},e))})]})]}),(0,s.jsxs)("div",{className:"rounded-lg border p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"The published spa heater sizes, for reference"}),(0,s.jsx)("p",{className:"mt-2 text-sm text-muted-foreground",children:"These four 240 volt rows are the ones the research tabulates. The BTU per hour and the amp columns below are worked out from the kilowatts rather than copied, and the breaker column is what the source prints for the heater on its own."}),(0,s.jsx)("div",{className:"mt-3 space-y-1 text-sm",children:x.map(e=>(0,s.jsxs)("div",{className:"flex flex-wrap justify-between gap-2 border-t pt-1",children:[(0,s.jsxs)("span",{children:[e.kw," kW",(0,s.jsx)("span",{className:"block text-xs text-muted-foreground",children:e.application})]}),(0,s.jsxs)("span",{className:"shrink-0 tabular-nums text-xs",children:[S(3412.141633127942*e.kw)," BTU/hr, ",O(1e3*e.kw/240)," A running,"," ",e.breakerAmps," A breaker"]})]},e.kw))}),(0,s.jsxs)("p",{className:"mt-3 text-sm text-muted-foreground",children:["A 120 volt plug-and-play tub is a different animal. Its heater runs"," ",v.heaterKwLow," to ",v.heaterKwHigh," kW on a"," ",v.circuitAmps.join(" or ")," amp household circuit, and the control board cuts the heater whenever the jets go to high speed, because the two together would exceed the outlet."]})]}),(0,s.jsxs)("div",{className:"rounded-lg border p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"The arithmetic in full"}),(0,s.jsx)("pre",{className:"mt-2 overflow-x-auto rounded bg-muted p-3 font-mono text-sm",children:`energy needed = gallons x 8.34 lb/gal x 1 BTU/lb.F x (target - start) 8heater output = kW x ${k(3412.141633127942,4)} BTU/hr per kW 9gross rise G = heater output / (gallons x 8.34) degrees F per hour 10 11loss coeff k = published F/hr / ${c} per hour 12 ${c}
12 F is the anchor gap: 102 F water against 35 F air, 13 the midpoint of the 30 to 40 F window the figures were quoted in 14 15loss now = k x (water - air) degrees F per hour 16ceiling = air + G / k the water settles here 17time = (1/k) x ln( (ceiling - start) / (ceiling - target) ) 18 and there is no time at all when target >= ceiling 19 20rule of thumb = (target - start) / (G - loss at target) 21 which charges the operating-temperature loss to every hour of the climb 22 23running amps = kW x 1000 / volts 24minimum circuit = running amps x 1.25`}),(0,s.jsxs)("p",{className:"mt-3 text-sm text-muted-foreground",children:["The two times differ because loss follows the gap between the water and the air. A tub starting at ",35," degrees below its setpoint is losing far less than one sitting at the setpoint, so charging the full published rate to every hour of the climb always returns a longer answer than the climb really takes."]})]}),(0,s.jsxs)("div",{className:"rounded-lg border p-4",children:[(0,s.jsx)("h3",{className:"text-sm font-medium",children:"What this page will not work out"}),(0,s.jsx)("ul",{className:"mt-2 list-disc space-y-2 pl-5 text-sm text-muted-foreground",children:P.map(e=>(0,s.jsx)("li",{children:e},e))})]}),(0,s.jsx)(o.i,{value:ed,label:"Copy the times, the ceiling and the circuit"}),(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:W}),(0,s.jsx)(n.ToolActions,{share:()=>(0,l.CZ)(q.map(e=>({key:e,value:eh[e]}))),title:"Hot Tub Heater Calculator",queryIsImpersonal:"the tub volume, water and air temperatures, heater output and placard rating you entered"})]})}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.