Solar on every roof, BOI solar correction, open source section
Deploy test site / deploy (push) Successful in 54s

Solar (user, 17 Oct 2026: "use as much roof space as possible")
- PV on every roof and canopy: ~640 kWp on the Payap scenario plot
  (factory roof ~523, canopies ~54, flat office roof ~65 kWp around the
  AC units); ~300 kWp on the Mueang Chi hall. Calculator: pvKWp 640,
  new pvSelf 85 % (zero export, hourly sketch), PV cost per kWp times the
  array, office-roof PV in the iso view, roofs drawn full.
- 12.8M to 16.6M THB at 20 to 26 THB/W; CSV baseline 34.37M to 56.34M;
  launch plant ~168M / 231M / 294M THB with contingency; Plan B ~216M
  (label ~215M), ~39M above Plan A; Plan A unchanged (it leases the
  array), its solar lever ~17.5M. PV payback ~4.2 to 5.9 years.
- Permits (rooftop solar no factory licence since 28 Dec 2024, ERC
  exemption under 1,000 kVA, PEA zero export) and PPA discounts sourced
  in sustainability.md.

BOI correction
- Announcement 3/2568 (in force 1 July 2025) ended BOI promotion for
  solar installations; only the battery may qualify (12M THB per MW cap).
  Removed the "BOI solar measure" from boi.md, total-investment,
  revenue-model, sustainability, site-candidates and both plan versions;
  Plan B's case for owning the array is the PPA bill it avoids
  (~0.07M USD a year).

Open source (user, 17 Oct 2026)
- digital-factory.md "Open source throughout, and working with other
  plants": everything we run and build is open source on NixOS, our code
  published (licence to decide), vendor firmware the stated exception;
  later the stack and factory-automation services for other pressing
  plants, not in the revenue.
- Business plan section 5.6 plus an executive-summary bullet, reading
  guide and not-modelled mentions; draft 0.3 rebuilt (A 60, B 61 pages),
  renders re-made with the roofs full.
This commit is contained in:
grabowski committed 2026-10-17 17:22:57 +07:00
1 parent ab303618d8
commit 9ff5feef11
32 files changed
+693 -367

No files matched your search

+39 -27
View File
@@ -599,7 +599,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
presses, an EX 50 and an EX 35 colour extruder each) and Newbilt Classic
automatic presses, starting from the launch plant: one Duplex system and one
Classic. It builds the investment from the blocks in total-investment.md,
checks it against 180M THB and 180M plus 25 % (Plan A ~180M, Plan B ~210M), runs the P&L and a
checks it against 180M THB and 180M plus 25 % (Plan A ~180M, Plan B ~215M), runs the P&L and a
ten-year cash sketch, and lists what the plant needs: people, power, central
hydraulics, steam, cooling, compressed air, floor, compound and stampers.
Vendor figures are marked as such; the rest are planning figures and
@@ -610,7 +610,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
<span>Presets:</span>
<button type="button" data-p="launch">Launch plant (decided)</button>
<button type="button" data-p="planA">Plan A (~180M)</button>
<button type="button" data-p="planB">Plan B (~210M, all in)</button>
<button type="button" data-p="planB">Plan B (~215M, all in)</button>
<button type="button" data-p="lean">Lean: bought stampers, outsourced cutting</button>
<button type="button" data-p="full">Full hall (5 D + 4 C)</button>
</div>
@@ -732,7 +732,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
<section class="card">
<h2>Budget: Plan A and Plan B</h2>
<p class="note" style="margin-top:0">Two plans for the investor (October 2026): Plan A at ~180M THB keeps every
machine of the launch plant with six cost levers, Plan B at ~210M THB keeps the same launch plant (one Duplex system
machine of the launch plant with six cost levers, Plan B at ~215M THB keeps the same launch plant (one Duplex system
and one Classic) with everything the levers cut; both priced without contingency and with the ramp-up reserve,
the second Duplex a growth step in both. Marks at 180M and 180M plus 25 % (225M). Bars: the investment
at the low end, the midpoint and the high end of every block (contingency as set under Plant configuration),
@@ -884,8 +884,8 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
line, Sibert, cutting room, metrology, ZLD and DI water) to the capex and shows the import VAT as cash
paid at the border, reclaimable as input tax. Raw material: no duty on compound for exported
records, 90 % off for domestic sales. Revenue is net of VAT (exports zero-rated, 7 % on domestic
sales passed on). Corporate income tax and BOI's CIT exemptions (solar, automation, R&D) are not
modelled.</p>
sales passed on). Corporate income tax and BOI's CIT exemptions (automation, R&D, possibly the battery; solar
installations are no longer promoted since July 2025) are not modelled.</p>
<p><b>Labour.</b> Base salaries per role (revenue-model.md, its loaded rates divided by 1.3; the cutting
engineer a team figure), plus the employer's 5 % social security up to the 17,500 THB ceiling, 25 %
for overtime, bonus and PPE, and the company's health care for every member of staff, office
@@ -931,7 +931,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
var VERSION = "1.1";
var DEF = {
duplex: 1, classic: 1, galvanics: true, cutting: true, metrology: "full",
cassette: true, cdr: true, solar: "buy", pvKwp: 300, batteryKwh: 271,
cassette: true, cdr: true, solar: "buy", pvKwp: 640, batteryKwh: 271,
fuel: "electric", boilerModules: 2, chillerModules: 2, crane: "buy",
corridor: true, phase2: false, running: true, labels: true
};
@@ -1314,6 +1314,9 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
// ------------------------------------------------------------ PV placement
var PV_ROWS = [1, 3, 5, 7, 10, 12, 14, 16, 19, 21];
// kWp per drawn module place (~2 m2): every place on the factory roof, the office roof and the two canopies
// together (1,467) is the ~640 kWp design (site-candidates.md parts model plus the office roof)
var PV_MOD = 0.4363;
function pvFields(cfg, al) {
var f = [];
// the parking canopies carry PV first (construction RFQ: canopy PV), the factory roof takes the rest
@@ -1321,12 +1324,17 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
f.push({ key: "scoot", x0: 20.35, cols: 13, y0: 26, rows: [0.4, 2.85], z: function (y) { return 2.45 + (y - 26) / 5.5 * 1.05 + 0.08; } });
f.push({ key: "roofW", x0: X(1), cols: 103, y0: FY, rows: PV_ROWS, z: function (y) { return roofZ(y - FY) + 0.12; } });
f.push({ key: "roofE", x0: X(107.25), cols: 19, y0: FY, rows: PV_ROWS, z: function (y) { return roofZ(y - FY) + 0.12; } });
// office roof: flat, the three AC units in the middle keep a clear strip around them
var oz = function () { return OFC.z1 + 0.14; };
f.push({ key: "offW", x0: OFC.x0 + 1, cols: 18, y0: OFC.y0, rows: [0.8, 3.1, 5.4, 7.7, 10.0, 12.3], z: oz });
f.push({ key: "offM", x0: OFC.x0 + 20, cols: 7, y0: OFC.y0, rows: [0.8, 12.3], z: oz });
f.push({ key: "offE", x0: OFC.x0 + 28, cols: 3, y0: OFC.y0, rows: [0.8, 3.1, 5.4, 7.7, 10.0, 12.3], z: oz });
if (al.p2show) f.push({ key: "p2", x0: P2.x0 + 1, cols: 40, y0: P2.y0, rows: [1, 3, 5, 7, 10, 12, 14, 16, 19, 21, 23],
z: function (y) { return FFL + P2.eave + (P2.high - P2.eave) * (y - P2.y0) / (P2.y1 - P2.y0) + 0.12; } });
return f;
}
function pvPlace(cfg, al) {
var want = cfg.solar === "none" ? 0 : Math.round(cfg.pvKwp / 0.45), left = want, out = {}, fields = pvFields(cfg, al), i, k, r, c;
var want = cfg.solar === "none" ? 0 : Math.round(cfg.pvKwp / PV_MOD), left = want, out = {}, fields = pvFields(cfg, al), i, k, r, c;
for (i = 0; i < fields.length; i++) {
var F = fields[i], list = [];
for (r = 0; r < F.rows.length && left > 0; r++) {
@@ -1535,8 +1543,8 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
sp.prism(rect(20, 26, 33.7, 31.5), [2.3, 2.3, 3.35, 3.35], [2.45, 2.45, 3.5, 3.5], "iso-canopy");
pvDraw(sp, pv.by.scoot.F, pv.by.scoot.panels);
var nc = pv.by.cars.panels.length, ns = pv.by.scoot.panels.length;
sc.inf("canopy-cars", "Car canopy, 10 bays", "27.7 x 7.0 m at 4.1 m (construction RFQ)" + (nc ? "; " + nc + " PV modules (~" + fmt(nc * 0.45) + " kWp)" : ""));
sc.inf("canopy-scooters", "Scooter canopy, ~30 spaces", "13.7 x 5.5 m at 3.5 m (construction RFQ)" + (ns ? "; " + ns + " PV modules (~" + fmt(ns * 0.45) + " kWp)" : ""));
sc.inf("canopy-cars", "Car canopy, 10 bays", "27.7 x 7.0 m at 4.1 m (construction RFQ)" + (nc ? "; " + nc + " PV modules (~" + fmt(nc * PV_MOD) + " kWp)" : ""));
sc.inf("canopy-scooters", "Scooter canopy, ~30 spaces", "13.7 x 5.5 m at 3.5 m (construction RFQ)" + (ns ? "; " + ns + " PV modules (~" + fmt(ns * PV_MOD) + " kWp)" : ""));
}
// ------------------------------------------------------------ factory
@@ -2253,7 +2261,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
});
s.open(S.N[0], S.N[1], [[o.x1 - LINK.x1 + 0.7, o.x1 - LINK.x0 - 0.7, o.z0, o.z0 + 2.5]], "iso-door", "link");
}
function office(sc, cfg, level) {
function office(sc, cfg, level, pv) {
var o = OFC, x0 = o.x0, y0 = o.y0, x1 = o.x1, y1 = o.y1, S = sides(x0, y0, x1, y1);
officeInfo(sc, cfg);
// covered link (all levels)
@@ -2267,6 +2275,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
s.box(x0, y0, x1, y1, o.z0, o.z1, "iso-office", { top: "iso-roof3" });
officeWindows(s, S, o, true);
s.flat(rect(x0 + 0.4, y0 + 0.4, x1 - 0.4, y1 - 0.4), o.z1 + 0.01, "iso-roofin");
if (pv) ["offW", "offM", "offE"].forEach(function (k) { if (pv.by[k]) pvDraw(s, pv.by[k].F, pv.by[k].panels); });
s.seq([[x0 + 20.8, y0 + 5.6], [x0 + 23.4, y0 + 5.6], [x0 + 26, y0 + 9.4]].map(function (p) {
return [p[0], p[1], function (q) { q.box(p[0] - 0.8, p[1] - 0.6, p[0] + 0.8, p[1] + 0.6, o.z1, o.z1 + 1.0, "iso-ac"); q.disc(p[0], p[1], o.z1 + 1.01, 0.42, "iso-fanring"); }];
}));
@@ -2486,7 +2495,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
factoryInfo(sc, cfg, al);
if (inside) { sc.dim = up; factoryInside(sc, ctx, al, pv, up); sc.dim = false; if (up) blockUpper(sc, cfg); }
else factoryRoof(sc, cfg, pv);
office(sc, cfg, level);
office(sc, cfg, level, pv);
var y = yard(sc, cfg, inside, pv);
if (al.p2show) { sc.dim = up; phase2(sc, ctx, inside, al, pv); sc.dim = false; }
else {
@@ -2496,14 +2505,16 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
vehicles(sc, cfg);
// PV info and roof tags
var ppa = cfg.solar === "ppa", onRoof = pv.by.roofW.panels.length + pv.by.roofE.panels.length,
onCp = pv.by.cars.panels.length + pv.by.scoot.panels.length, onP2 = pv.by.p2 ? pv.by.p2.panels.length : 0;
onCp = pv.by.cars.panels.length + pv.by.scoot.panels.length, onP2 = pv.by.p2 ? pv.by.p2.panels.length : 0,
onOf = pv.by.offW.panels.length + pv.by.offM.panels.length + pv.by.offE.panels.length;
if (cfg.solar !== "none" && pv.want > 0) {
var kwp = cfg.pvKwp, parts = [onRoof + " on the factory roof"];
if (onOf) parts.push(onOf + " on the office roof");
if (onCp) parts.push(onCp + " on the parking canopies");
if (onP2) parts.push(onP2 + " on the phase 2 roof");
sc.inf("pv", "PV " + fmt(kwp) + " kWp, " + fmt(cfg.batteryKwh) + " kWh battery" + (ppa ? " (PPA)" : ""),
fmt(pv.want) + " modules of ~0.45 kWp (~2 m2 each): " + parts.join(", ") + (pv.left ? "; " + fmt(pv.left) + " do not fit the roofs (ground mount)" : "") +
". About " + fmt(kwp * 1.4) + " to " + fmt(kwp * 1.5) + " MWh a year at 1,400 to 1,500 kWh/kWp (scale.md)" + (ppa ? ". PPA: the EPC owns the panels, the plant buys the power." : "."));
fmt(pv.want) + " modules of ~" + PV_MOD.toFixed(2) + " kWp (~2 m2 each): " + parts.join(", ") + (pv.left ? "; " + fmt(pv.left) + " do not fit the roofs (ground mount)" : "") +
". About " + fmt(kwp * 1.4) + " to " + fmt(kwp * 1.5) + " MWh a year at 1,400 to 1,500 kWh/kWp (scale.md), ~85 % of it used on site (zero export)" + (ppa ? ". PPA: the EPC owns the panels, the plant buys the power." : "."));
if (!inside && onRoof) {
sc.tag(X(52), Y(10), roofZ(10) + 1.5, "PV " + fmt(kwp) + " kWp", 10, "pv");
if (ppa) sc.tag(X(80), Y(6), roofZ(6) + 1.0, "PPA panels", 100, "pv", "iso-signtag");
@@ -2955,8 +2966,9 @@ grp("Energy", [
["kwhPrice", "Electricity, blended", 3.81, "THB/kWh", "doc", "scale.md: PEA TOU 3.2.2 at two shifts, before the demand charge (Ft September to December 2026)"],
["demandPrice", "Demand charge", 132.93, "THB/kW a month", "doc", "PEA 3.2.2 (scale.md)"],
["restKW", "Rest of the plant while running", 120, "kW", "est", "Estimate: HVAC, lighting, galvanics, finishing, offices; puts the start fleet in scale.md's 4.5 to 6.8M THB a year before PV"],
["pvKWp", "Roof PV", 247, "kWp", "doc", "scale.md"],
["pvYield", "PV yield, all self-consumed", 1400, "kWh/kWp", "doc", "scale.md: 1,400 to 1,500 kWh/kWp a year in northern Thailand"],
["pvKWp", "PV array: factory roof, office roof and parking canopies, all full", 640, "kWp", "est", "Payap scenario: factory roof ~523 kWp and canopies ~54 kWp (site-candidates.md parts model, 580 W placeholder module) plus the office roof ~65 kWp (estimate from this drawing); on the Mueang Chi hall the roof and canopies take ~300 kWp (scale.md)"],
["pvYield", "PV yield", 1400, "kWh/kWp", "doc", "scale.md: 1,400 to 1,500 kWh/kWp a year in northern Thailand"],
["pvSelf", "Share of the PV yield used on site", 85, "%", "est", "Hourly sketch (October 2026): two shifts on 300 days, ~60 kW off-shift base load, the 271 kWh battery; the rest is curtailed, because a factory on PEA's grid runs zero export (sustainability.md)"],
["heatPrice", "Steam heat from biomass", 0.7, "THB/kWh", "doc", "biomass.md: 0.5 to 0.9 at 1.35 to 2 THB/kg delivered"],
["steamKWh", "Heat per kg of steam", 0.68, "kWh/kg", "est", "Estimate: 10 bar saturated steam from ~80 °C feed water"],
["eBoilEff", "Electric boiler efficiency", 98, "%", "est", "Estimate for an electric steam boiler (~98 %)"],
@@ -3028,8 +3040,8 @@ rng("Investment blocks (USD unless THB)", [
["airT", "Compressed air, Thai supplier", 350000, 600000, "THB", "est", "Utilities CSV: screw compressor skid, dryer, receiver"],
["airC", "Compressed air, Chinese brand through its Thai office", 150000, 300000, "THB", "est", "china-utilities.md: PM VSD screw set 0.15 to 0.3M THB installed"],
["clR", "Clean-room adder (galvanics and cutting room)", 1200000, 3400000, "THB", "est", "Utilities CSV, ~2 × 60 m² ISO 7/8; half with only one of the two rooms (estimate)"],
["uRest", "Rest of the utilities baseline: softener, loop treatment, racking, whole-factory HVAC", 8620000, 17300000, "THB", "est", "Utilities CSV baseline 27.57 to 47.50M THB less boilers, chillers, air, clean room, PV and battery"],
["pv", "Rooftop PV EPC", 6000000, 7800000, "THB", "est", "Utilities CSV: illustrative 300 kWp at 20 to 26 THB/W installed (DEDE Thailand PV status: factories 10 kW to 1 MW 22 to 25 THB/W, 2023; installers 18 to 25 in 2026) incl. Deye hybrid inverters; final kWp open (scale.md 247 kWp for the yield)"],
["uRest", "Rest of the utilities baseline: softener, loop treatment, racking, whole-factory HVAC", 8620000, 17300000, "THB", "est", "Utilities CSV baseline 34.37 to 56.34M THB less boilers, chillers, air, clean room, PV and battery"],
["pv", "PV EPC per kWp, roofs and canopies", 20000, 26000, "THB", "est", "Utilities CSV: 20 to 26 THB/W installed (DEDE Thailand PV status: factories 10 kW to 1 MW 22 to 25 THB/W, 2023; installers 18 to 25 in 2026) incl. the inverters; times the array size above; canopy steel is in the building and site works (construction RFQ)"],
["bat", "Battery 271 kWh (Gobel), installed", 2000000, 3500000, "THB", "est", "Utilities CSV: Gobel list price USD 39,900 to 58,000 (battery only, matches DEDE's 5,000 to 7,000 THB/kWh for LiFePO4) plus enclosure, fire protection, controls and installation, below the 12,000 to 15,000 THB/kWh of a fully installed factory BESS because the hybrid inverters do the power conversion (estimate)"],
["bld", "Building ~1,500 m² plus civil and site works", 650000, 1100000, "USD", "est", "total-investment.md: 12,000 to 20,000 THB/m² plus ~20 % site works; soil is the main variable"],
["ph2", "Phase 2 building, when the press floor outgrows the press hall", 750000, 1250000, "USD", "est", "Business plan draft 0.1: ~750k to 1,250k on 1,710 m², estimate"],
@@ -3052,7 +3064,7 @@ grp("Investment rules", [
["wcRef", "Records a year the working capital is sized for", 840000, "", "doc", "revenue-model.md launch plant, central volume"],
["wcMin", "Working capital never below", 60, "%", "est", "Estimate"],
["budgetLo", "Plan A budget", 180000000, "THB", "doc", "total-investment.md: Plan A, ~180M THB without contingency, ramp-up reserve included"],
["budgetHi", "Upper budget mark, 180M plus 25 %", 225000000, "THB", "doc", "total-investment.md: the +25 % envelope set for Plan B (user, October 2026); Plan B is ~210M"],
["budgetHi", "Upper budget mark, 180M plus 25 %", 225000000, "THB", "doc", "total-investment.md: the +25 % envelope set for Plan B (user, October 2026); Plan B is ~215M"],
]);
grp("Ten years of cash", [
["ramp1", "Sold output in year 1", 35, "% of the mature year", "est", "Estimate"],
@@ -3092,7 +3104,7 @@ const CFG0 = Object.fromEntries(CFGK.map(k => [k, S0[k]]));
const LAUNCH = { mode: "target", target: 850000, grow: "classic", dMin: 1, cMin: 1, dFleet: 1, cFleet: 1, ...CFG0 };
const PRESETS = {
launch: LAUNCH,
// Plan A (~180M THB, six levers) and Plan B (~210M THB, the launch plant all in): total-investment.md "Two plans", priced without contingency
// Plan A (~180M THB, six levers) and Plan B (~215M THB, the launch plant all in): total-investment.md "Two plans", priced without contingency
planA: { ...LAUNCH, solar: "ppa", metro: "launch", corridor: "later", cass: "off", cdr: "off", crane: "rent", sourcing: "china", cont: "sep" },
planB: { ...LAUNCH, cont: "sep" },
lean: { ...LAUNCH, galv: "bought", cut: "out" },
@@ -3239,7 +3251,7 @@ function capexOf(r, c = S, boiOn = c.boi !== "off") {
if (cr > 0) blk("Utilities", "clR", cr === 1 ? "Clean-room adder: galvanics and cutting room" : `Clean-room adder, half: ${galvIn ? "galvanics" : "cutting room"} only`, cr, true);
blk("Utilities", "uRest", "Rest of the utilities baseline: softener, loop treatment, racking, whole-factory HVAC", 1, true);
if (c.solar === "buy") {
blk("Utilities", "pv", "Rooftop PV, EPC", 1, true);
blk("Utilities", "pv", `PV on the roofs and canopies, ${f0(A.pvKWp)} kWp, EPC`, A.pvKWp, true);
blk("Utilities", "bat", "Battery 271 kWh (Gobel)", 1, true);
}
blk("Building and site", "bld", "Building ~1,500 m² plus civil and site works");
@@ -3319,10 +3331,10 @@ function econ(r, b, boiOn = S.boi !== "off") {
const steamT = r.P * (A.steamLo + A.steamHi) / 2 * runH / 1000;
const heatMWh = steamT * A.steamKWh;
const elecMWh = X > 0 ? r.mwh + heatMWh / Math.max(0.5, A.cop) + (r.compKW + A.restKW) * b.hoursYr / 1000 : 0;
const pvMWh = S.solar === "none" ? 0 : Math.min(elecMWh, A.pvKWp * A.pvYield / 1000);
const pvMWh = S.solar === "none" ? 0 : Math.min(elecMWh, A.pvKWp * A.pvYield / 1000 * Math.min(100, Math.max(0, A.pvSelf)) / 100);
const demandKW = X > 0 ? r.kw + r.chillerKW + r.compKW + A.restKW : 0;
add("energy", "Electricity", t2u((elecMWh - pvMWh) * 1000 * A.kwhPrice + demandKW * A.demandPrice * 12),
`${f0(elecMWh - pvMWh)} MWh from the grid after ${f0(pvMWh)} MWh roof PV${S.solar === "none" ? " (no PV)" : S.solar === "ppa" ? " (PPA)" : ""}; ~${f0(demandKW)} kW demand`);
`${f0(elecMWh - pvMWh)} MWh from the grid after ${f0(pvMWh)} MWh of PV used on site${S.solar === "none" ? " (no PV)" : S.solar === "ppa" ? " (PPA)" : ""}; ~${f0(demandKW)} kW demand`);
if (S.solar === "ppa") add("energy", "Solar PPA power", t2u(pvMWh * 1000 * A.ppaPrice), `${f0(pvMWh)} MWh at ${f2(A.ppaPrice)} THB/kWh (estimate, to quote)`);
if (S.steamFuel === "biomass")
add("energy", "Steam heat (biomass)", t2u(heatMWh * 1000 * A.heatPrice), `${f0(steamT)} t of steam, ${f0(heatMWh)} MWh of heat at ${f2(A.heatPrice)} THB/kWh`);
@@ -3634,9 +3646,9 @@ const CFG = [
h: { off: "Illustrative volumes and prices with no market data; left out of the money.",
on: "Counted at the illustrative volumes and prices, with no running costs on file: it flatters the result." },
dis: () => S.cass === "off" && S.cdr === "off" ? "Not applicable: neither line is in the plant." : "" },
{ k: "solar", t: "Solar PV and 271 kWh battery", sub: "utilities CSV: illustrative 300 kWp EPC plus the Gobel battery",
{ k: "solar", t: "Solar PV and 271 kWh battery", sub: "every roof and canopy covered (~640 kWp) plus the Gobel battery",
o: [["buy", "Buy"], ["ppa", "Solar PPA"], ["none", "None"]],
h: { buy: () => `${mT("pv")} THB PV plus ${mT("bat")} battery; the yield offsets the bill.`,
h: { buy: () => `${nf(1).format(A.pvLo * A.pvKWp / 1e6)}M to ${nf(1).format(A.pvHi * A.pvKWp / 1e6)}M THB for ${f0(A.pvKWp)} kWp plus ${mT("bat")} battery; the yield offsets the bill.`,
ppa: "No capex: the EPC owns the array, the plant pays the PPA price on the yield.",
none: "No PV: the whole bill comes from the grid." },
extra: `<label class="row" id="row_ppa"><span>PPA power price, THB/kWh<br><small>estimate, to quote</small></span><input type="number" id="ppaIn" data-a="ppaPrice" min="0" step="0.05"></label>` },
@@ -3710,7 +3722,7 @@ function renderInvest(e, r) {
H.push(`<tr class="grp"><td colspan="5">Totals</td></tr>`);
TR("Subtotal", cx.sub, "", true);
TR(cx.ctgIn ? `Contingency ${f1(A.contPct)} %` : `Contingency ${f1(A.contPct)} % (not in the total)`, cx.ctg, cx.ctgIn ? "total-investment.md" : "shown separately, as in Plan A and Plan B");
TR("Total investment", cx.tot, "for reference, total-investment.md: the launch plant ~160M to 283M THB with contingency (midpoint ~222M); Plan A ~162M and Plan B ~193M at the midpoint without it, plus the ramp-up reserve", true);
TR("Total investment", cx.tot, "for reference, total-investment.md: the launch plant ~168M to 294M THB with contingency (midpoint ~231M); Plan A ~162M and Plan B ~201M at the midpoint without it, plus the ramp-up reserve", true);
if (!cx.boiOn) {
TR(`Import duty without BOI, ${f1(A.dutyMach)} %`, cx.duty, `on ${money(cx.imp.lo)} to ${money(cx.imp.hi)} of imported machinery; import VAT ${money(cx.vat.mid)} at the midpoint, reclaimable`);
TR("Total with import duty", cx.all, "", true);
@@ -3790,7 +3802,7 @@ function renderCash(e, r) {
let ISO = null, ISOK = "";
function isoCfg(r) {
return { duplex: r.D, classic: r.C, galvanics: S.galv, cutting: S.cut, metrology: S.metro,
cassette: S.cass, cdr: S.cdr, solar: S.solar, pvKwp: 300, batteryKwh: 271, fuel: S.steamFuel,
cassette: S.cass, cdr: S.cdr, solar: S.solar, pvKwp: A.pvKWp, batteryKwh: 271, fuel: S.steamFuel,
boilerModules: r.nb, chillerModules: r.nc, crane: S.crane, corridor: S.corridor, phase2: r.floor > A.hall };
}
function isoUpdate(r) {