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

+153 -57
View File
@@ -1,6 +1,6 @@
"""Part B: what makes it top-notch (traceability, metrology, QC, efficiency); site and building;
regulatory (BOI and licences). Draft 0.3, October 2026: one text for both versions, Plan A (~180M
THB, the launch plant with six cost levers) and Plan B (~210M THB, the same launch plant with
THB, the launch plant with six cost levers) and Plan B (~215M THB, the same launch plant with
everything in, none of the levers); the plan-specific passages switch with plan_variant.pick(),
called inside blocks()."""
from plan_lib import H1, H2, H3, P, B, N, T, KV, IMG, CALL, PB
@@ -351,56 +351,91 @@ def blocks():
B([
"**Biomass heat is far cheaper than electric steam** (below): the demand charge follows the "
"boiler's full-power draw, and the peak window covers 13 of the 16 production hours.",
"**The roof gets PV, not solar thermal.** Self-consumed PV is worth ~3.7 THB/kWh" + pick(
"**Every roof gets PV, not solar thermal.** Self-consumed PV is worth ~3.7 THB/kWh" + pick(
" of grid power displaced, less the PPA price the plant pays for it (below)", "") +
"; it cannot lower the demand charge, because the peak window runs to 22:00, long after "
"sunset.",
"; surplus earns nothing, because a factory on PEA's grid runs zero export (below); and PV "
"cannot lower the demand charge, because the peak window runs to 22:00, long after sunset.",
"**The battery is for ride-through, not peak shaving.** Charging off-peak and discharging at "
"peak earns ~70k THB a year for the 271 kWh stack, and it can hold demand down by only ~20 kW "
"(~30k THB a year; estimates). Its case is riding through grid dips and outages.",
]),
H3("Renewable energy"),
KV([
["Rooftop PV", "~247 kWp on the 1,523 m² monopitch roof (425 modules of a generic 580 W "
"placeholder; no module chosen yet), ~335 to 360 MWh a year, ~25 to 35% of mature "
"two-shift use (1,000 to 1,500 MWh). " + pick(
"Taken through a solar PPA (below): the plant pays the PPA price for this "
"power instead of buying the array, so its saving is the gap between that "
"price and the ~3.7 THB/kWh of grid power it displaces",
"Saves ~1.2M to 1.3M THB a year and pays back in ~3.8 to 5.4 years at 20,000 "
"to 26,000 THB/kWp installed (estimates)")],
["Canopies and the second building", "~40 kWp on the parking canopies (construction RFQ; ~54 "
"kWp if fully covered). A phase 2 building's roof adds "
"~251 kWp, ~498 kWp across both roofs"],
["PV on every roof and canopy", "~640 kWp (decided October 2026: as much roof as possible). In "
"the Payap scenario model the calculator draws the factory roof "
"at ~523 kWp (901 modules of a generic 580 W placeholder; no "
"module chosen yet), the car and scooter canopies at ~54 kWp and "
"the office roof at ~65 kWp; the covered link and the utility "
"yard are left out. ~900 MWh a year at 1,400 to 1,500 kWh/kWp, "
"~85% of it used on site (~760 MWh: two shifts on 300 days, a "
"~60 kW base load off-shift and the battery), about 38% of the "
"launch plant's ~2,000 MWh a year with electric steam "
"(estimates). " + pick(
"Taken through a solar PPA (below): the plant pays the PPA price for this power instead of "
"buying the array, so its saving is the gap between that price and the ~3.7 THB/kWh of "
"grid power it displaces",
"Saves ~2.8M to 3.0M THB a year at ~3.7 THB/kWh self-consumed and pays back in ~4.2 to 5.9 "
"years at 20 to 26 THB/W installed; the last ~340 kWp beyond the first ~300 pay back in "
"~4.9 to 6.3 years, because more of their yield is curtailed on Sundays (estimates)")],
["Zero export", "The ~15% the plant cannot use, mostly on Sundays and holidays, is curtailed, not "
"sold: a factory on PEA's grid runs zero export, with no payment for surplus and "
"a zero-export controller (reverse-power relay) as part of the interconnection "
"(CapSolar and Sahawatthanakit, 2026)"],
["On the Mueang Chi hall", "The 60 × 25 m hall of section 6.2 takes only ~300 kWp: ~247 kWp on "
"the 1,523 m² monopitch roof (425 modules) and ~40 to 54 kWp on the "
"parking canopies. The rest would need ground mount on the ~70 m of "
"plot behind the building (not covered by the rooftop exemption, so "
"it may still need a factory permit) or the phase 2 roof (~251 "
"kWp)"],
["Battery", "271 kWh Gobel Power LiFePO4 rack (15 stacked 18 kWh packs, 8,000+ cycles), USD "
"39,900 to 58,000 listed (battery only; ~2.0M to 3.5M THB installed with enclosure, fire protection and "
"controls, estimate), 50 to 60 day lead. Roughly an hour of the chiller bank's "
"peak draw: ride-through, not off-grid autonomy"],
["Inverters", "Deye three-phase hybrid units (30 to 80 kW class), Thai distribution via Solar "
"Nicolo; Deye is a supported protocol on the Gobel battery management"],
["Inverters", "Deye three-phase hybrid units (30 to 80 kW class), ~500 kVA in all (estimate); "
"Thai distribution via Solar Nicolo; Deye is a supported protocol on the Gobel "
"battery management"],
["Roof design", "Monopitch at 10° falling toward ESE puts the whole roof on one plane at ~98% "
"of ideal yield; at this latitude orientation barely matters (under 2% across a "
"wide azimuth swing). 10° rather than shallower so the rains self-clean "
"burning-season dust"],
["Permits", "Since Ministerial Regulation No. 3 B.E. 2567 (in force 28 December 2024) rooftop "
"solar outside industrial estates is not a factory, so it needs no Ror.Ngor. 4 "
"licence at any size. At ~500 kVA of inverters, under the 1,000 kVA line of the "
"Energy Regulatory Commission (ERC), an ERC exemption notification replaces a "
"generation licence. PEA approves the interconnection with zero-export control in 3 "
"to 6 months, filed in parallel with procurement (CapSolar, 2026); the roof load is "
"in the building permit. Inside an industrial estate IEAT rules apply on top"],
["Open checks", "Battery rack voltage against the chosen Deye model's window; Gobel safety "
"certifications (not published); EPC warranty on a mixed-vendor DC bus; yield "
"against PVGIS before committing" + pick("; the PPA offer itself, price and "
"terms", "")],
pick(["Solar PPA (this plan)", "An installer owns and finances the PV and the battery, and the "
"plant buys the power at a price still to quote (the calculator carries 3.0 THB/kWh, an "
"estimate, ~0.03M USD a year on the energy line). That keeps the PV EPC (USD 182k to "
"236k) and the battery (USD 61k to 106k) out of the capital round: ~9.7M THB less at the "
"midpoint, one of the six levers in section 8. The BOI solar measure (section 7) rewards "
"the plant's own solar investment, so its tax value very likely goes with the array (to "
"confirm with the BOI consultant). The PPA offer is asked against the specification "
"above"],
["Owned (this plan)", "The plant buys the array and the battery: rooftop PV EPC USD 182k "
"to 236k (6.0M to 7.8M THB, 20 to 26 THB/W at Thai 2026 market prices) and the battery, installed, "
"USD 61k to 106k (2.0M to 3.5M THB), "
"estimates, section 8. Owning them is what the BOI solar measure rewards (section 7). A "
"solar PPA (an installer owns and finances them, the plant buys the power at a price "
"still to quote) would take ~9.7M THB out of the capital round at the midpoint, but "
"very likely gives up that measure"]),
"plant buys the power at a price still to quote. The PPA covers the full ~640 kWp; the "
"calculator carries 3.0 THB/kWh (estimate) for the ~760 MWh used on site, ~2.3M THB or "
"~0.07M USD a year on the energy line. That keeps the PV EPC (USD 388k to 504k, 12.8M to "
"16.6M THB at 20 to 26 THB/W) and the battery (USD 61k to 106k) out of the capital round: "
"~17.5M THB less at the midpoint, one of the six levers in section 8. The launch plant's "
"power bill (~0.75M THB a month before solar, estimate) is well above the 100,000 THB a "
"month CapSolar asks of a PPA site; PPA discounts run 10 to 15 % off the tariff at MEAei "
"and 10 to 30 % at CapSolar, so the calculator's ~21 % is to be quoted. Leasing gives up no BOI benefit on the array: BOI stopped promoting solar "
"installations on 1 July 2025 (Announcement 3/2568, section 7.1). A PPA developer selling "
"the power can still be promoted (activity 7.1.2); whether that shows in the price is for "
"the negotiation (CapSolar, 2026). What the PPA costs against owning is the power bill "
"above and, if the PPA includes the battery, the battery's possible BOI eligibility as "
"storage (to confirm with the BOI consultant). The PPA offer is asked against the "
"specification above"],
["Owned (this plan)", "The plant buys the array and the battery: PV EPC USD 388k to 504k "
"(12.8M to 16.6M THB for ~640 kWp, 20 to 26 THB/W installed with inverters at Thai 2026 "
"market prices; the canopy steel is in the building and site works) and the battery, "
"installed, USD 61k to 106k (2.0M to 3.5M THB): ~17.5M THB together at the midpoint, "
"estimates, section 8. Owning them saves the power bill a PPA would carry: at the "
"calculator's 3.0 THB/kWh (estimate, to quote) for the ~760 MWh used on site, ~2.3M THB "
"or ~0.07M USD a year. A solar PPA (an installer owns and finances them, the plant buys "
"the power at a price still to quote) would take ~17.5M THB out of the capital round at "
"the midpoint at the cost of that bill. The array itself carries no BOI benefit: BOI "
"stopped promoting solar installations on 1 July 2025 (Announcement 3/2568, section "
"7.1); the owned battery may still qualify as storage (to confirm with the BOI "
"consultant)"]),
]),
H3("Steam: the open engineering choice"),
P("The boilers' fuel is still open; the modular bank works with either. There is no natural gas "
@@ -487,13 +522,16 @@ def blocks():
"acceptable the cost drops materially, and that question is on the RFQ list."),
CALL("Efficiency in one paragraph, for the investor", "Steam and cooling are built once as a "
"backbone and filled with identical modules as presses arrive; heat is recovered from the "
"flue gas, the condensate and the press returns before more is generated; the roof carries PV "
"worth a quarter to a third of mature electricity use" + pick(" (through a solar PPA)", "") +
", with a battery for ride-through; no "
"flue gas, the condensate and the press returns before more is generated; every roof and "
"canopy carries PV" + pick(" through a solar PPA", "") + " (~640 kWp, over a third of the "
"launch plant's electricity even with electric steam, estimate), with a battery for "
"ride-through; no "
"process water is discharged; a fifth of the plastic is reclaimed; and biomass steam, if "
"chosen, costs a quarter to a tenth of electric steam. The same choices push the energy line "
"toward its low end, hedge Thai tariff rises, and convert into BOI tax value" + pick(
" through the Smart and Sustainable Industry measure", "") + " (section 7). They "
"toward its low end and hedge Thai tariff rises; the heat recovery and automation can count "
"toward BOI's Smart and Sustainable Industry measure" + pick(
"", " and the owned battery may qualify as storage") + " (section 7.1; BOI no longer "
"promotes solar installations). They "
"also make the plant's sustainability story true rather than claimed, which matters to the "
"labels it sells to."),
@@ -570,6 +608,59 @@ def blocks():
"chain from master to shipment, with the repress path from the archived father and mother. IDs "
"and times are simulated."),
H2("5.6 Open source throughout, and a service for other plants"),
P("**Everything the plant runs and everything the project builds is open source,** with a short "
"list of exceptions named below. Every server is a NixOS machine declared in one repository, "
"B4L's fleet repository built on clan-core; the fleet it declares already runs B4L's DNS, mail, "
"monitoring, backups, a public reverse proxy and the first site gateway. The plant adds a gateway, a core "
"server, a backup server, edge collectors and station kiosks to the same inventory. Any machine "
"is rebuilt from the repository, and a second plant is one more inventory entry. ERPNext is the "
"only system of record (section 5.1), self-hosted on NixOS through the community packaging "
"frappe-nix."),
T(["Component", "What it does", "Licence"], [
["NixOS (nixpkgs), clan-core", "Every server, declared and deployed from one repository",
"MIT"],
["ERPNext on the Frappe framework", "The only system of record: orders, gates, stock, job "
"cards, metal genealogy, inspections, accounts", "GPLv3 (ERPNext), MIT (Frappe); no licence "
"or per-seat fee"],
["PostgreSQL, VictoriaMetrics, Telegraf, Mosquitto (MQTT)", "Machine-data store, time series, "
"collection and messaging from presses, utilities and instruments", "PostgreSQL licence, "
"Apache-2.0, MIT, EPL-2.0"],
["Grafana, Caddy, BorgBackup", "Dashboards and alerts, the web proxy, encrypted backups",
"AGPLv3, Apache-2.0, BSD"],
["cnx-factory (ours)", "Kiosks, press floor, QC, genealogy, customer portal; Rust (axum, SQLx, "
"minijinja, HTMX), packaged as a Nix flake", "To be published"],
["Plant overview, artist platform (name undecided), plant and business plan calculator (ours)",
"The software of section 5.5", "To be published"],
["FreeCAD, LibreOffice", "The plant model; this business plan's build", "LGPL, MPL-2.0"],
], widths_mm=[52, 82, 36], font_pt=8.5,
caption="Licences as published by each project, checked October 2026. Firmware inside the "
"machines is not in the table (below)."),
B([
"**Our own code is published as open source.** The licence is still to decide: AGPL-3.0 keeps "
"hosted copies open as well; Apache-2.0 or MIT is the easiest for other plants to adopt.",
"**What is not open, said plainly:** the firmware and HMIs inside the machines and instruments "
"(Newbilt's Siemens PLC, Pheenix's Beckhoff PLC, Keyence and the vision controllers), the "
"inverter and battery firmware, the vendors' OPC UA licences, and the access-point controller "
"in the fleet's router module. They are treated as appliances: they export data into the NixOS "
"estate under the data clause of every equipment RFQ (section 5.1) and host none of our "
"services.",
"**What it gives the business:** no licence fees (ERPNext charges no fee per seat, so a new "
"kiosk or office user costs nothing in licences); no lock-in to a software vendor; every "
"machine reproducible from the repository; and a record customers can audit, because the "
"hash-chained event log and the code that writes it can both be inspected.",
]),
P("**Later: a service for other pressing plants.** Once the plant has run the stack in production "
"for at least a year, the project can work with other pressing plants worldwide in three ways: "
"an open vinyl plant stack (the NixOS modules, an ERPNext app for pressing with stampers, test "
"pressings and genealogy, and press adapters for Newbilt and Pheenix machines over OPC UA, S7 "
"and ADS); services (factory automation and data acquisition integration and commissioning at "
"other plants, remote monitoring and support, hosting of ERPNext and the factory application, "
"training); and shared development, with each plant contributing the adapters for its own "
"machines. **None of this is in the plan's revenue:** like stamper supply to other plants, it is "
"an upside with no price or volume yet, and it starts only after the plant has proved the stack "
"on its own floor."),
# -------------------------------------------- 6 site and building
H1("6. Site and building"),
H2("6.1 Land: a long lease, three candidates"),
@@ -700,12 +791,11 @@ def blocks():
"figures and their source."),
IMG(pick("renders/calculator/plan-a-iso-roof.png", "renders/calculator/plan-b-iso-roof.png"), 170,
"Figure 8. The site from above, " + label() + " preset of the plant and business plan calculator: "
"the factory with roof PV" + pick(" supplied through the solar PPA (the battery is drawn too; "
"whether a PPA offer includes it is open)",
" and battery, owned") +
", the office, the utility yard (boiler hall and chiller compound built for four modules each, "
"pump and heat-exchanger room, transformer, water treatment, PVC silos), parking with PV canopies "
"and the phase 2 reserve. Schematic render of the Payap scenario model (docs/fleet-calculator.html); "
"every roof and canopy carries PV, ~640 kWp on the factory, the office and the car and scooter "
"parking" + pick(", supplied through the solar PPA (the battery is drawn too; whether a PPA offer "
"includes it is open)", ", owned with the battery") +
"; the utility yard (boiler hall and chiller compound built for four modules each, pump and "
"heat-exchanger room, transformer, water treatment, PVC silos) and the phase 2 reserve. Schematic render of the Payap scenario model (docs/fleet-calculator.html); "
"positions and envelopes are estimates."),
IMG(pick("renders/calculator/plan-a-iso-gf.png", "renders/calculator/plan-b-iso-gf.png"), 170,
"Figure 9. Ground floor with the roofs cut away, " + label() + " preset: from the west end the "
@@ -814,7 +904,8 @@ def blocks():
H3("Site works and the second building"),
P("On the Mueang Chi parcel the site works are a ~444 m perimeter wall, one 10 m sliding gate with "
"a guard house, 4,387 m² of paving in two classes (1,608 m² heavy-duty for trucks, 2,779 m² "
"general), roofed parking for 10 cars and 30 scooters carrying ~40 kWp of PV, a ring foundation "
"general), roofed parking for 10 cars and 30 scooters with its canopies fully covered in PV (~54 "
"kWp; ~40 kWp in the construction RFQ), a ring foundation "
"for a 9 m fire-water tank with its pump house and one for a 5 m raw-water tank, a fire ring main "
"and hydrants, stormwater drainage and domestic sanitation. With the buildings, ~6,549 m² or 68% "
"of the modelled plot is sealed, so stormwater attenuation, quite possibly a detention pond, may "
@@ -923,9 +1014,10 @@ def blocks():
"tax holiday on the core activity, but the full non-tax package and machinery duty exemption. "
"The tax value is engineered on top through measures where this project happens to be strong, so "
"the application is pitched on " + pick("automation, technology content and energy efficiency",
"solar, automation and technology content") +
"automation, full metrology and energy efficiency") +
", not on pressing plastic "
"discs. The research behind this dates from July 2026, BOI revises its measures roughly every "
"discs. The research behind this dates from July 2026 (the solar rules rechecked in October "
"2026); BOI revises its measures roughly every "
"year, and the exact activity sub-code needs confirmation by a BOI-registered consultant before "
"filing."),
T(["What BOI gives", "Why it matters here"], [
@@ -942,15 +1034,17 @@ def blocks():
"technical consultant, without the four-Thai-employees-per-permit rule."],
["Raw material duty relief", "Exemption for export production; up to 90% reduction for "
"domestic sales. Relevant for imported PVC compound until a Thai source is qualified."],
["Solar and battery measure (Announcement 2/2568, April 2025)", "CIT exemption based on the "
"solar investment; PV modules, inverters, battery and battery inverters, and mounting are "
"eligible, capped at THB 12M, 30M or 40M of investment per MW depending on investment type. "
"It rewards a plant-owned array. " + pick(
"This plan takes the PV and the battery through a solar PPA, so the installer owns them "
"and the plant very likely gives up this measure (to confirm with the BOI consultant; "
"section 5.4).",
"This plan buys the ~247 kWp array and the 271 kWh battery, so their cost is the base of "
"the exemption (section 5.4).")],
["Battery storage, alternative-energy measure (Announcement 3/2568, in force 1 July 2025)",
"BOI no longer promotes solar installations, rooftop or farm, for applications from 1 July "
"2025: Announcement 3/2568 took them out of the measure and ended the per-MW solar caps set in "
"April 2025, so the array earns no CIT exemption whoever owns it. What remains is battery "
"energy storage and its inverters, with the investment eligible for the CIT exemption capped "
"at THB 12M per MW (B Global Law, January 2026; to confirm with the BOI consultant). " + pick(
"In this plan the battery comes through the solar PPA if the offer includes it, and then "
"the plant owns nothing that qualifies; bought by the plant, the 271 kWh battery (2.0M to "
"3.5M THB installed, estimate) may qualify (section 5.4).",
"This plan owns the 271 kWh battery (2.0M to 3.5M THB installed, estimate), which may "
"qualify; its eligible value under the cap is for the BOI consultant (section 5.4).")],
["Smart and Sustainable Industry measure (Announcement 15/2565, refreshed July 2025)",
"Rewards automation, smart machinery and energy efficiency, typically 3 years of CIT exemption "
"capped around 50% of the qualifying upgrade spend. The automatic Classic, inline inspection, "
@@ -967,7 +1061,7 @@ def blocks():
P("**Process:** file via the e-Investment system and present at an analyst meeting. BOI quotes "
"about 40 working days to a decision for projects under THB 200M and 60 to 90 working days for "
"larger tiers; at " + pick("~127M to 226M THB with the ramp-up reserve (~177M at the midpoint)",
"~149M to 266M THB with the ramp-up reserve (~208M at the midpoint)") +
"~156M to 275M THB with the ramp-up reserve (~216M at the midpoint)") +
" the plan sits around that line, so the consultant "
"confirms which applies. In practice it is roughly 4 to 6 months end to end to the certificate; "
"the company is incorporated under it; machinery is imported within a window commonly 30 months "
@@ -1022,8 +1116,10 @@ def blocks():
"confirm against the text before the capacity is chosen); hazardous-substance registration and "
"locked, inventoried storage for silver nitrate and nickel salts; the occupational health "
"check-ups (section 6.3); PDPA for CCTV, personnel and operator-linked production data; local "
"fire codes and the building permit during construction; and DIW manifests for used oil and "
"other controlled waste."),
"fire codes and the building permit during construction; the solar papers (no factory licence "
"for rooftop PV since 28 December 2024, an ERC exemption notification under 1,000 kVA, and PEA "
"interconnection approval with zero-export control in 3 to 6 months; section 5.4); and DIW "
"manifests for used oil and other controlled waste."),
CALL("Three things to settle before filing", [
"**Zoning before filing.** The BOI clocks (the import window and the deadline for full "
"operation) start at the certificate, and the margin on them is consumed by one scenario: "