Vietnam: a localized detector, and a size cap that did not travel¶
Vietnam was composed and run between 23 and 26 September 2026. Like Zambia and Nigeria, it started from nothing local: no cached imagery, no calibration quadrats, no national register, no third-party PV dataset. It is the first country run under the owner's standing instruction to localize the segmentation on the country's own OpenStreetMap labels rather than run an existing checkpoint zero-shot, and to ship whichever of the two scores better on a held-out region.
Interactive: Vietnam PV evidence atlas.
Headline¶
| Verified | 8,179.8 MWp (90% 6,006 to 11,425) |
| Best estimate | 8,860.5 MWp (90% 7,881 to 11,655) |
| Grid | 2,611 of 2,611 cells of 0.1°, 64 provinces (geoBoundaries, pre-2025 boundaries) |
| Imagery | Sentinel-2 L2A, September 2025 to August 2026 |
| Model | v9_combined_vietnam epoch 36, localized (see below) |
A precision floor, not an estimate
Vietnam has no calibration quadrats, so there is no roofclf half and no
instrument at all below the 400 m² segmentation floor. It has no glint sample, so
p_unmapped is 0 and p_real collapses to the OpenStreetMap-mapped fraction. Recall
was deliberately skipped (--recall-reference none). For scale: EVN reported more than
11,200 MWp of rooftop solar alone at the end of July 2026
(pv magazine, 22 September 2026),
and national solar has been reported above 19 GW
(Vietnam Briefing).
This page is a floor at roughly half of that, and the missing half is overwhelmingly
rooftop.
Localizing the detector¶
Labels. 2,244 OpenStreetMap solar features inside Vietnam (720 rooftop, 1,522 ground), out of 2,853 in the country's bounding box. Unlike Nigeria's, this population is large-array heavy: the mapped polygons are mostly utility farms and large commercial roofs, which is exactly what a 10 m sensor can learn from.
Holdout, chosen from the label map before any training. The Central Highlands (Kon Tum,
Gia Lai, Dak Lak, Dak Nong): 385 cells holding 206 of the 1,199 spatially distinct
positives (17.2%), outside the Ninh Thuan / Binh Thuan farm core, and a mix of utility
farms and the farm-roof PV of the 2020 boom. Training chips in the one-cell ring around it
were dropped (scripts/mark_val_buffer.py, 26 chips) so that neither the ~900 m chip jitter
nor the 2.24 km window can put a held-out installation into training.
Corpus and recipe. 1,735 train and 359 val chips (1,289 with PV), repeated x4 and merged with v5's five corpora into 32,563 rows, so Vietnam is about a quarter of the training rows. The recipe is v5's verbatim; only the corpus changed. Early-stopped with the best epoch at 36, about 14 hours on the GTX 1060.
The ship decision, scripts/compare_local_vs_v5.py on the 359 held-out chips:
| v5 (zero-shot) | v9 (localized) | |
|---|---|---|
| pixel IoU | 0.724 | 0.762 |
| pixel F1 | 0.840 | 0.865 |
| recall, installations at or above 500 m² | 0.973 (434 of 446) | 0.960 (428 of 446) |
The rule compares recall first, and a difference inside 0.02 is a tie; pixel IoU then decides. v9 draws markedly cleaner outlines at the cost of six missed installation instances in 446, which is noise at this size. Two things to read it with: v9's best epoch was selected on a pooled validation set that includes these chips, which is a small advantage v5 did not get; and both checkpoints already find nearly every large array here, so the gain is in shape, not in detection.
The whole-country comparison, after the atlas (compare_checkpoints_vs_mapped.py, both
checkpoints inferred over all 2,611 cells, scored against the 2,071 dissolved mapped
installations of 400 m² and up):
| v5 (zero-shot) | v9 (localized) | |
|---|---|---|
| pooled recall over mapped installations | 0.850 | 0.860 |
| candidates | 17,125 | 6,343 |
| median candidate area | 2,801 m² | 10,396 m² |
| total candidate area | 151.5 km² | 95.1 km² |
| rooftop share | 46.0% | 57.6% |
The recall row is not evidence for v9: it trained on most of these installations, so it is scored largely in-sample, and the decision above rests on the held-out region alone (McNemar p = 0.043 on this in-sample set, 65 against 43 discordant). The population shape is the real finding. v9 is a far more conservative detector, with 63% fewer candidates and 37% less candidate area. Without a quadrat or a register nobody can say how many of v5's extra 10,782 small candidates are unmapped arrays rather than false positives. What can be said is that on held-out chips, where the truth is known, v9's pixels agree with the mapped outlines better.
The 5 km² ground cap did not travel¶
scripts/prepare_national_osm_solar.py drops every ground polygon above 5 km². That
threshold is a German measurement: against the national register, no German OSM ground
polygon above 5 km² contained a registered unit, and Germany's largest real park is
about 5 km². In Vietnam it removed the five largest mapped plants, 5.6 to 8.3 km²
each and 38.4 km² together, and every one of them is fully imaged and 92 to 99%
detected by the model. They sit in Binh Phuoc, Tay Ninh, Ninh Thuan and Dak Lak, where the
country's largest parks are.
The cap now has an evidence-gated exception (--keep-detected-screen): a ground feature
above it is kept only when the perimeter screen shows it imaged and at least 50% detected.
Default behaviour, and therefore Germany's, is unchanged. Keeping the five raised Verified
from 6,258 to 8,180 MWp. India, whose largest parks are tens of km², runs with the same
gate.
OpenStreetMap's ground population here is real¶
Zambia's screen found announced-but-unbuilt power=plant outlines carrying a third of its
hand-mapped capacity. Vietnam's does not: all 274 ground perimeters of 50,000 m² or
more are imaged, the model detects PV over a median 99% of each, and only three
(0.5 of 132.8 km²) fall below 60%. No exclusion list was needed.
Plausibility gate¶
earthpv check-density reports 14 fail and 16 suspect of 64 provinces. Every failure is
the ground-to-rooftop ratio above 5x, a single cell above 25% of its province, or both, and
the failing provinces are the solar-farm belt: Ninh Thuan, Binh Thuan, Tay Ninh, Binh Phuoc,
Dak Lak, Long An, Binh Dinh, Phu Yen. The check exists to catch ground-mount false positives
on bright bare land, so it was tested directly: the share of the model's ground-mount
candidate area that lies inside a hand-mapped OSM plant perimeter is 82 to 99% in 12 of
the 14 failing provinces, and 78% nationally. These are real plants, and the top cells of
the four largest concentration failures (Tay Ninh, Binh Phuoc, Ninh Thuan, Dak Lak) are
exactly the five large parks above. Published with the flags
stated, per this project's precedent for a checked-genuine failure.
Two exceptions are not explained that way and are candidates for review rather than capacity anyone should quote: Dak Nong (35% of 4.1 km² inside mapped plants) and Gia Lai (45% of 5.3 km², suspect). A cluster of small northern mountain provinces (Cao Bang, Ha Giang, Lai Chau, Dien Bien and others, 0.15 to 0.72 km² each) has no mapped plant at all and is most likely false positives. The rooftop side of the ratio is also understated, for the reason in the warning above, which inflates every ratio the gate computes.
Candidate population¶
6,536 candidates: 3,678 rooftop, 1,688 ground-adjacent, 1,170 with no building. Only 90
exceed the 100,000 m² merge cap, 17.7% of candidate area, against 69.1% for v5 on
Nigeria; 103 more are OSM-verified plant geometries and are exempt. The model's own
estimate (est_mwp_rc) is 4,225 MWp, 823 rooftop and 3,403 ground, and the
probability-weighted ceiling (est_mwp_exp) is 8,039 MWp. Best exceeds Verified by only
681 MWp because the model mostly re-finds mapped plants and, with no glint sample, its
unmapped detections are discounted to the mapped fraction.
Operational notes¶
Compose built 2,611 cells in about 59 hours on a 12-month window: northern Vietnam is
overcast for much of its dry season (Hanoi had 16 scenes under 30% cloud in 20 months), and
annual_composite keeps the 12 least-cloudy scenes per cell whatever the window. Three
network failure modes surfaced and were fixed along the way, taking the rate from about 36
to about 90 cells per hour:
- the Planetary Computer patience was raised to 1,800 s, since 30% of cells were being downloaded twice under a shared link;
- GDAL now keeps pooled connections alive and aborts a transfer that stalls for 60 s. Dead keep-alive connections had been waiting out the kernel's ~15 minute retransmission timeout per request;
- a socket timeout was added on the Python side, because one hung STAC search holds the process-wide search lock and stalls every worker, including the Earth Search fallback.
The cost of the stall abort is about one dropped band read per cell, so the median for that band is over 11 scenes rather than 12.
Postprocess was OOM-killed three times at 14 GB holding every building within 2 km of any
candidate. It now runs with --building-buffer-m 500; placement and the rank prior only use
buildings within about 40 m.
What Vietnam needs next¶
A calibration quadrat. Vietnam's rooftop PV, more than 11 GWp registered, is almost
entirely outside what this page can see. One exhaustively mapped box in a rooftop-dense area
(the Ho Chi Minh City industrial belt, or the Central Highlands' farm roofs) is what would
unlock a roofclf half. Check the imagery date before drawing it; see the
quadrat protocol.
The land constant. Ground-mount converts at 0.05 kWp/m² of site area, calibrated on two Pakistani plants. Germany's register put German plants at 0.081 and Zambia's two measured plants at 0.085. If Vietnam's parks are as dense, the ground half here is understated by a similar factor; checking it against published plant capacities is the obvious next measurement and has not been done.
Reproducing¶
bash scripts/download_vida.sh VNM
pixi run python scripts/overpass_labels_chunked.py --bbox 102.1,8.4,109.5,23.4 \
--iso3 VNM --name vietnam --tiles 3x8 --timeout 300 --retries 8
systemd-run --user --collect --unit=earthpv-vn-in-queue -p WorkingDirectory="$PWD" \
-p LimitNOFILE=65536:65536 -p MemoryMax=4G bash scripts/run_vn_in_queue.sh
The queue composes Vietnam, then hands it to scripts/run_country_compute.sh vietnam (chips,
training, the v5 comparison, inference and the atlas chain, behind marker files). Status at
any time: bash scripts/vn_in_status.sh.