Esfex

Latest version: v0.2.6

Safety actively analyzes 993455 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 1 of 3

0.2.6

Added

- **`esfex add-solver` / `remove-solver` / `list-solvers`** — install optional,
commercial solvers (Gurobi, CPLEX, CBC, SCIP, Xpress) on demand into a
dedicated Julia environment that is stacked on the load path, so it survives
ESFEX upgrades and always targets the Julia the app actually uses. The install
verifies the solver loads, so a licence/build problem is reported up front
rather than mid-solve.

Fixed

- **Solver availability** is now read from ESFEX's Julia environment (the shipped
Project.toml plus the optional-solvers env), not from unrelated Python
companion packages — the GUI no longer offers a solver the backend cannot
load. A project that names a solver unavailable on this machine is coerced to a
usable one on load instead of failing mid-solve with an empty result.
- **Method-specific solver options** (e.g. HiGHS `pdlp_scaling`) are no longer
forwarded while the LP method is on *choose*; forwarding them aborted the whole
model build on some solver builds.
- **Runner** now fails loudly when no operational window solves, instead of
logging `completed: objective=$0` and exporting an empty result.
- **Grid Builder — transmission-only model.** The minimum-voltage floor is now
enforced on the *built* network: the sub-floor distribution layer that leaked
in (unknown-voltage features assigned a low voltage during the build) is
collapsed, relocating its generation to the nearest transmission bus. Auto-
transformers between voltage levels are sized by level (275→600, 220→400 MVA…)
instead of a flat 100 MVA. Together these remove phantom transmission
congestion — measured load shedding on a Hokkaido UC run fell from ~37 % to
~5 %.
- **Grid Builder — step 3 data fetching.** World Bank macro indicators get a
30 s budget with retries (was a single 15 s attempt), and Google Open Buildings
(unavailable over source.coop's HTTPS glob) transparently falls back to
Overture instead of failing the demand step.
- **Results charts** group generators by fuel when a system has no
`TechnologyConfig` catalog (Grid Builder imports), instead of collapsing the
whole fleet into a single *Other* bucket.
- **Map Results** system selector shows only the current run's systems; previous
runs in the results directory (often other regions) no longer leak in.

Changed

- **OSM fetching is faster.** Tiles are fetched concurrently across the Overpass
mirrors, dense tiles subdivide on the first server-side timeout, and the query
requests only what the builder consumes — `out center` (centroid) for
point-like elements and `out geom` (full trace) for lines, dropping the blanket
recursion that pulled every substation polygon vertex.

0.2.5

Added

- **Grid Builder — bounded reconnection & island cleanup** — a *Reconnect
isolated ≤ N km* control connects each substation within N km of the backbone
(tapping a nearby same-voltage line, or linking to the nearest same-voltage
bus with a real-length line), then absorbs any component that remains
dysfunctional — generation XOR demand — into the connected grid PyPSA-style:
its generation/demand is moved to the nearest bus and the dead topology
removed, while self-sufficient islands (generation **and** demand) are kept.
Default 8 km; set to 0 to keep the network exactly as mapped.
- **Isolated load-island detection** — validation now reports a component that
carries demand but has no generation and no path to any generator (unservable
load the solver can only shed); this previously passed clean.

Changed

- **Grid Builder data sources** — OSM, WRI, GEM and GridFinder are laid out in a
single row and all enabled by default; combining them yields the most
complete network.
- **Minimum generator capacity floor scales with the minimum voltage** — the
wizard default tracks the voltage floor (~0.3 MW/kV; e.g. 110 kV → 35 MW) so
distribution-scale plants don't clutter a transmission model as stranded
islands. Edit by hand to pin a value; set to 0 to include every generator.
- Narrower element-tree and properties panels; compact *New System* dialog.

Fixed

- **OSM fetch no longer silently returns partial data** — the Overpass fetch
tiles the region at 1° (was 5°, which blew past the server timeout on dense
regions and returned a fraction as a clean success), subdivides a tile that
still times out down to 0.25°, re-queries a zero-element response once to rule
out a dispatcher load-shed empty, retries transiently-failed tiles once, and
warns loudly when coverage is incomplete.
- **Project save/load is faithful** — three data-loss bugs fixed: the
generator→bus assignment (previously re-guessed by nearest coordinate and
scrambled on every export/import), AC/DC & frequency converters collapsing to
a self-loop, and a valid demand distribution being drained out of
disconnected micro-grids. A Grid Builder network now round-trips with
identical connectivity, stable across repeated export/import.
- **Generation deduplication** — OSM is authoritative and never merges its own
units, so real multi-unit plants survive; a GEM/WRI plant is kept only where
OSM is silent; a bare OSM location marker (no capacity tag) no longer drops
the capacitated GEM entry; an OSM `power=plant` polygon is reconciled against
its unit nodes (no double count; the polygon output excludes under-
construction / decommissioning units); and only operating GEM plants are kept.
- **Transmission line ratings** — line circuit count is derived correctly from
the OSM `cables` tag (3 cables = 1 three-phase circuit, not 3) and preserved
as the maximum across merged segments, so a double-circuit backbone keeps its
real thermal capacity.
- **Tooltips wrap to a logical width** instead of a single screen-wide line —
implemented without a global event filter (which segfaulted alongside the
embedded QtWebEngine map).
- Project export/import runs off the GUI thread; negative fuel `price_base` is
accepted (waste-to-energy tipping fees); the QtWebEngine raster-tile memory
budget is raised so large maps stop dropping tiles.

0.2.4

Added

- **Deterministic scheduled interruptions (maintenance / forced outages)** — a
new interruptions calendar lets you take any electrical or primary-energy
element out of service — fully or as a derate — over explicit time windows,
and the optimizer respects them. Supported for generators, batteries, fuel
sources, transmission lines, AC/DC and frequency converters, and transformers.
Windows are edited in a D3 Gantt-timeline dialog (reachable from the toolbar
and from **right-click → Schedule interruption** on an element in the tree),
persist with the project, and compile into per-element, per-timestep capacity
masks for the model — with no change to the operational formulation itself.

Changed

- **Grid Builder — Bus Distribution unified** — the two-button flow (separate
*Fetch demand* and *Distribute*) is replaced by a single method dropdown
(spatial demand + building-footprint proxies) and one **Distribute demand**
button; footprint options appear only for footprint methods. The step-3 demand
and distribution labels were also tidied.
- **Validation dialog** — the *Auto-fix errors* button was removed (the
underlying validation library function is unchanged).
- **System attributes** — the system name field now sits directly after its
*Name:* label instead of being right-justified.
- **Interruptions calendar** follows the Studio's visual theme.

Fixed

- **Large config and project save/load froze — or crashed — the GUI** — parsing,
serialising and `.esfexp` bundling ran on the GUI thread, so a large
multi-system file painted the progress dialog only partway and could crash Qt.
Config load/save and project export/import now run on background workers while
the window and its progress dialog stay responsive.
- **Grid Builder: demand distribution broke when CMIP6 was rate-limited** — a
429 during the forecast fell back to a climate year that could carry NaN,
which propagated into per-cell demand and then failed the whole bus
distribution. Non-finite temperature and cell demand are now scrubbed, so
demand and distribution compute from the fallback climate instead of breaking.
- **Grid Builder: CMIP6 climate fetch now retries** transient 429 / 5xx /
network errors with exponential backoff instead of losing a node's future
climate on the first throttle.
- **Grid Builder: availability profiles came back all-zeros** when the weather
API rate-limited the concurrent requests; concurrency is capped and all-zero
responses now trigger a retry.
- **Grid Builder: building footprints never downloaded** during bus
distribution (Microsoft quadkey, Web-Mercator and GeoJSON parsing bugs).
- **Grid Builder: demand distribution reported "0 multi-bus nodes"** on all-HV
grids where no bus was pre-tagged as load; it now falls back to all busbars.

0.2.3

Added

- **View a node's centroid on the map** — nodes are abstract and carry no
permanent map marker (one would overlap the electrical and primary-energy
elements), so a transient cue was added: the map flies to the centroid and
drops a pulsing marker that removes itself after a few seconds. Reachable from
a new **View** button next to the node form's centroid button (the former
*Pick on map* is shortened to **Place**) and from **right-click → View on map**
on a node in the element tree.

Fixed

- **Generator type not preserved in non-English UIs** — the resource-type combo
(Renewable / Non-renewable) showed translated labels while the model stores
English literals, so editing generators in a non-English UI made every
generator take the last-shown type and saved a localized string that broke the
schema. The combo now keeps the English literal internally; legacy localized
values in already-saved projects are normalized on load.
- **OTEC cycle diagrams under NumPy 2.x** — fluid saturation/entropy/enthalpy
properties can come back as single-element arrays on some NumPy/CoolProp
combinations, and `float()` on such an array is a hard error under NumPy 2.x,
crashing the cycle-state and T-s / P-h diagram computations. These values are
now coerced version-robustly.

Changed

- **Grid Builder UI is now translated** — its labels, group boxes, checkboxes,
radio buttons, buttons and tooltips were hard-coded English and never followed
the selected language. All user-facing strings now use the translation system
(en/es/fr/pt/ja), matching the Solar/Wind/Demand wizards.
- **New ESFEX logo** across the wordmark, window icon and documentation, with
the splash logo no longer clipped and a smaller About-dialog logo.
- **Python support capped to `>=3.10,<3.14`** so installs on a too-new Python
(which has no prebuilt wheels for the scientific stack and fails to build from
source) get a clear resolver error instead of an obscure compiler failure;
Python 3.13 is now an official supported/classified version.

0.2.2

Fixed

- **Studio usable on small laptop screens** — the properties panel no longer
takes ~half the width and can only be hidden: its minimum width is now
screen-scaled and the main splitter defaults to an 18/58/24 split, so the
panel starts compact and can be dragged narrow. The default window size is
clamped to the available screen. Fonts also fit: the base font now shrinks on
small screens (it never did before), the ~46 hard-coded widget `font-size`
stylesheets scale with it, and a "QFont: Point size <= 0" warning is guarded.

Removed

- **Bundled Windows `.exe` installer** and its release build workflow. It
masked rather than fixed the real Windows launch failure, which comes from
installing Python via the **Microsoft Store** (a sandboxed, redirected
filesystem that breaks native PySide6/Qt DLL loading). The README, the
installation docs and the Studio's DLL-load error message now document the
cause and fix (install Python from python.org or conda). The console-less
`esfex-studio` launcher is unaffected.

0.2.1

Fixed

- **Windows: Studio launch crash from a Qt DLL conflict** — on systems with
another Qt on PATH (a conda `qt-main`/`pyqt`/`qt6`, common in base Anaconda),
`esfex studio` failed with `ImportError: DLL load failed while importing
QtWidgets: the specified procedure could not be found`. PySide6's own bundled
Qt is now put first on the DLL search path (and the conda dirs are skipped
entirely when PySide6 is self-contained), so a foreign Qt can no longer shadow
it. The error message for this case now explains the real cause (Qt conflict /
wrong environment) instead of the misleading "reinstall PySide6".

Page 1 of 3

© 2026 Safety CLI Cybersecurity Inc. All Rights Reserved.