Biomass steam in both plans, PV on the factory roof only
Deploy test site / deploy (push) Successful in 49s

User, 17 Oct 2026: "only put solar on the factory, prepare the other
buildings for solar, option a and b should operate on biomass".

- Steam: two 500 kg/h biomass modules (Getabec class, 6M to 10M THB) plus
  a fuel store and handling block (1M to 2M, estimate) in both plans; the
  electric bank becomes the alternative. ~0.67M THB of fuel a year against
  ~4.9M for electric steam; grid demand ~264 kW instead of ~980 kW.
- Solar: ~520 kWp on the factory roof (the biomass-case optimum is ~500 to
  550 kWp, hourly sketch in sustainability.md "PV size with biomass
  steam"); office roof and parking canopies built PV-ready (~120 kWp
  later). ~87 % used on site, ~61 % of the plant's electricity.
- Calculator: biomass default, bFuel row, pvKWp 520, pvSelf 87, iso fills
  the factory roof first and shows the other roofs as PV-ready.
- Numbers: launch plant ~168M / 232M / 296M with contingency; Plan A
  ~182M (~2M above 180M: the Chinese-route lever priced only electric
  boilers; a Chinese biomass quote is the way back), Plan B ~217M (label
  ~215M), ~35M more. Calculator EBITDA A 2.17M, B 2.23M USD.
- CSV, total-investment, revenue-model, sustainability, scale, biomass,
  site-candidates, boi, steam-cooling, construction RFQ (canopies
  PV-ready), README, roadmap; business plan draft 0.3 rebuilt (A and B
  63 pages) with new renders.
This commit is contained in:
grabowski committed 2026-10-17 20:24:55 +07:00
1 parent 9ff5feef11
commit 984ced2485
35 files changed
+849 -665

No files matched your search

+26 -23
View File
@@ -931,8 +931,8 @@ 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: 640, batteryKwh: 271,
fuel: "electric", boilerModules: 2, chillerModules: 2, crane: "buy",
cassette: true, cdr: true, solar: "buy", pvKwp: 520, batteryKwh: 271,
fuel: "biomass", boilerModules: 2, chillerModules: 2, crane: "buy",
corridor: true, phase2: false, running: true, labels: true
};
var OFFW = { off: 1, out: 1, bought: 1, no: 1, none: 1, later: 1, "false": 1, "0": 1, "": 1 };
@@ -1314,16 +1314,17 @@ 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;
// kWp per drawn module place (~2 m2): the 1,220 places on the factory roof are the ~520 kWp array of the
// plans (decided October 2026: PV on the factory only; the office roof and the canopies are PV-ready and
// take panels only above that size)
var PV_MOD = 0.4262;
function pvFields(cfg, al) {
var f = [];
// the parking canopies carry PV first (construction RFQ: canopy PV), the factory roof takes the rest
f.push({ key: "cars", x0: 18.35, cols: 27, y0: 6, rows: [0.15, 2.45, 4.75], z: function (y) { return 2.9 + (y - 6) / 7 * 1.2 + 0.08; } });
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; } });
// the factory roof fills first; the PV-ready office roof and parking canopies take any kWp above it
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; } });
f.push({ key: "cars", x0: 18.35, cols: 27, y0: 6, rows: [0.15, 2.45, 4.75], z: function (y) { return 2.9 + (y - 6) / 7 * 1.2 + 0.08; } });
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; } });
// 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 });
@@ -1543,8 +1544,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 * 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)" : ""));
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)" : "; PV-ready: steel sized for ~42 kWp of panels, conduit to the main board; panels later"));
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)" : "; PV-ready: steel sized for ~13 kWp of panels, conduit to the main board; panels later"));
}
// ------------------------------------------------------------ factory
@@ -2224,7 +2225,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
var OFC_UP = 4.0; // storey height: first floor at z0 + 4.0 (8 m to the roof for two storeys)
function officeInfo(sc, cfg) {
sc.inf("office", "Office building, 32 x 15 m",
"Two storeys (~960 m2) with a stair and a lift. Ground floor: reception and lobby, canteen, changing rooms, toilets, mastering and cutting, listening room, archive. " +
"PV-ready flat roof (structure for ~65 kWp of panels, conduit to the main board; panels later). Two storeys (~960 m2) with a stair and a lift. Ground floor: reception and lobby, canteen, changing rooms, toilets, mastering and cutting, listening room, archive. " +
"First floor: offices, meeting and training, a meeting room, IT and server room, toilets, pantry, a flexible room. Isolated from the factory floor, joined by a covered link (site-candidates.md).");
sc.inf("cutting", cfg.cutting ? "Mastering and cutting room" : "Cutting room: cut abroad",
cfg.cutting ? "Sillitoe SDMS lathe (USD 125k list) on the ground slab, away from press vibration and outside the crane zone; inner acoustic lining" :
@@ -2514,7 +2515,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
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 ~" + 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." : "."));
". About " + fmt(kwp * 1.4) + " to " + fmt(kwp * 1.5) + " MWh a year at 1,400 to 1,500 kWh/kWp (scale.md), ~87 % 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");
@@ -2856,7 +2857,7 @@ const S0 = {
priceStd: 3.8, premSpec: 1.15, titleFee: 1155, cur: "USD", boi: "on", exportShare: 70, capexEnd: "mid",
// plant configuration (defaults = the launch plant, total-investment.md October 2026)
galv: "in", galvPrice: "pheenix", cut: "in", metro: "full", cass: "on", cdr: "on", countCD: "off",
solar: "buy", steamFuel: "electric", sourcing: "thai", crane: "buy", corridor: "now", reserve: "in", cont: "in", stSupply: 0,
solar: "buy", steamFuel: "biomass", sourcing: "thai", crane: "buy", corridor: "now", reserve: "in", cont: "in", stSupply: 0,
};
// allowed values of every switch, default first where it matters for readHash
const ENUM = {
@@ -2966,9 +2967,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", "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)"],
["pvKWp", "PV array on the factory roof", 520, "kWp", "est", "Decided October 2026: PV on the factory only, the office roof (~65 kWp) and the parking canopies (~54 kWp) PV-ready for later. Payap scenario factory roof ~523 kWp (site-candidates.md parts model, 580 W placeholder module), near the biomass-case optimum of ~500 kWp (sustainability.md); the Mueang Chi roof takes ~247 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)"],
["pvSelf", "Share of the PV yield used on site", 87, "%", "est", "Hourly sketch (October 2026), biomass steam: ~230 kW running load on two shifts and 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 %)"],
@@ -3034,14 +3035,15 @@ rng("Investment blocks (USD unless THB)", [
["pvcL", "PVC lab, day-one tier", 55000, 150000, "USD", "est", "quality-automation.md"],
["bTh", "Electric boiler module, 500 kg/h, Thai supplier, installed", 2250000, 3500000, "THB", "est", "Utilities CSV: bank of two 4.5 to 7.0M THB, engineering estimate"],
["bCn", "Electric boiler module, 500 kg/h, Chinese route, installed", 1400000, 2750000, "THB", "est", "china-utilities.md: two ASME-stamped modules 2.8 to 5.5M THB installed"],
["bBio", "Biomass boiler module, 500 kg/h, installed", 3000000, 5000000, "THB", "est", "Utilities CSV: biomass package 6 to 10M THB for ~1 t/h (Getabec class), estimate"],
["bBio", "Biomass boiler module, 500 kg/h, installed", 3000000, 5000000, "THB", "est", "Utilities CSV: biomass package 6 to 10M THB for ~1 t/h incl. fuel feed, grate and dust control (Getabec class), estimate"],
["bFuel", "Biomass fuel store and handling", 1000000, 2000000, "THB", "est", "Utilities CSV: covered fuel store (42 m2 drawn, ~6 to 7 weeks of fresh chips; ~80 m2 for a 3-month monsoon stock), chip bay, ash bins, a compact loader; estimate (biomass.md: fuel handling and storage come on top of the boiler package)"],
["cBk", "Cooling backbone: plate HX, tanks, pumps, mains for four modules", 1700000, 2700000, "THB", "est", "Utilities CSV: chiller bank of two 4.9 to 7.9M THB less two modules"],
["chM", "Chiller module, 250 kW air-cooled, installed", 1600000, 2600000, "THB", "est", "Utilities CSV: one module ~1.6 to 2.6M THB, estimate"],
["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 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)"],
["uRest", "Rest of the utilities baseline: softener, loop treatment, racking, whole-factory HVAC", 8620000, 17300000, "THB", "est", "Utilities CSV baseline 34.47 to 58.22M THB less boilers, fuel store, chillers, air, clean room, PV and battery"],
["pv", "PV EPC per kWp, factory roof", 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"],
@@ -3242,6 +3244,7 @@ function capexOf(r, c = S, boiOn = c.boi !== "off") {
else blk("Quality", "metF", "Metrology, full stack: XRF, laser confocal, wide-area profiler, inline vision, audio station", 1, false, { imp: 1 });
blk("Quality", "pvcL", "PVC lab, day-one tier");
const bio = c.steamFuel === "biomass", cn = c.sourcing === "china";
if (r.nb > 0 && bio) blk("Utilities", "bFuel", "Biomass fuel store, chip bay, ash bins and a compact loader", 1, true);
if (r.nb > 0) blk("Utilities", bio ? "bBio" : cn ? "bCn" : "bTh",
`Steam: ${pl(r.nb, `${f0(A.boilerMod)} kg/h ${bio ? "biomass" : "electric"} boiler module`)}${bio ? "" : cn ? ", Chinese route" : ", Thai supplier"}`, r.nb, true);
blk("Utilities", "cBk", "Cooling backbone: plate HX, tanks, pumps and mains for four modules", 1, true);
@@ -3251,7 +3254,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", `PV on the roofs and canopies, ${f0(A.pvKWp)} kWp, EPC`, A.pvKWp, true);
blk("Utilities", "pv", `PV on the factory roof, ${f0(A.pvKWp)} kWp, EPC (office roof and canopies PV-ready)`, 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");
@@ -3646,7 +3649,7 @@ 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: "every roof and canopy covered (~640 kWp) plus the Gobel battery",
{ k: "solar", t: "Solar PV and 271 kWh battery", sub: "the factory roof (~520 kWp; office and canopies PV-ready) plus the Gobel battery",
o: [["buy", "Buy"], ["ppa", "Solar PPA"], ["none", "None"]],
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.",
@@ -3655,8 +3658,8 @@ const CFG = [
{ k: "steamFuel", t: "Steam fuel", sub: "the boiler modules follow the fleet's peak steam",
o: [["electric", "Electric"], ["biomass", "Biomass"]],
h: { electric: "Electric modules on PEA power: energy charge plus demand charge per installed module.",
biomass: "Biomass modules (utilities CSV package); heat at the biomass price." } },
{ k: "sourcing", t: "Utilities sourcing", sub: "electric boiler modules and the compressor",
biomass: "Biomass modules (utilities CSV package) plus the fuel store; heat at the biomass price. Both plans run on biomass (October 2026)." } },
{ k: "sourcing", t: "Utilities sourcing", sub: "the compressor, and the boiler modules when steam is electric",
o: [["thai", "Thai suppliers"], ["china", "Chinese route"]],
h: { thai: "Thai suppliers, installed (utilities CSV).",
china: "ASME-stamped bodies installed by a Thai contractor, compressor through a Chinese brand's Thai office (china-utilities.md)." } },
@@ -3722,7 +3725,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 ~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);
TR("Total investment", cx.tot, "for reference, total-investment.md: the launch plant ~168M to 296M THB with contingency (midpoint ~232M); Plan A ~167M and Plan B ~202M 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);