DEV Community

Cover image for From a Site Polygon to Short-Circuit Currents: Building a Substation Generator in Kotlin
Aleksandr Savichev
Aleksandr Savichev

Posted on Edited on

From a Site Polygon to Short-Circuit Currents: Building a Substation Generator in Kotlin

Most side projects begin with an API, a framework, or a familiar product clone. Mine began with a polygon on a map and a question:
Could software generate a plausible electrical substation layout, calculate the resulting short-circuit currents, and explain why the result makes engineering sense?

I spent about fifteen years in the power industry, working in relay protection and automation at Lenenergo, a regional distribution grid company in Saint-Petersburg, Russia. Five years ago I returned to programming, which I had enjoyed at school and university, and very soon moved into commercial software development for urban planning and land development.

At some point, it began to feel wasteful to leave all of that power-engineering experience behind. I wanted to combine it with my newer experience in Kotlin, backend development, maps, and layout-generation algorithms.

The main project at my current job deals with urban development, geospatial data, and automated layout generation. In that model, electrical networks were simplified: power effectively arrived from outside the project boundary. Real districts, of course, are often supplied through nearby step-down substations.

That led to the idea for this project. A user who knows the required power demand and has a candidate site should be able to generate several conceptual layouts of a typical step-down substation within seconds. The result should include the main equipment, cable trenches, safety zones, and preliminary electrical calculations, making it possible to answer questions such as:

• Can a substation of this type physically fit on the site?
• Which voltage class and typical arrangement are feasible?
• What short-circuit levels should be expected?
• Are there obvious equipment or spatial constraints?
• How does the primary layout map into an IEC 61850 SCL model?

The project can be viewed as a planning prototype, an early-stage discussion tool, or an educational system. The educational use case may be the most interesting one: the generated layout connects physical equipment, an electrical network model, fault calculations, protection logic, and a digital-substation representation.

Process of generation

Important disclaimer: this is a research prototype for conceptual and demonstration purposes. It is not a detailed-design system or a certified calculation package. It formalizes a limited set of layout rules, generates preliminary alternatives, and performs calculations that must be checked by an engineer using real project data.

Where generation begins

The user first draws the candidate site boundary and provides the main inputs:
• required power;
• high-voltage class;
• optional low-voltage class;
• reserve margin;
• external-grid short-circuit power or current (Sk / Ik);
• circuit-breaker type;
• number of layout alternatives to generate

For the frontend, I used MapLibre GL because I already knew it from other projects. The editing mode uses maplibre-gl-draw, allowing the user to draw the site polygon. The result view uses MapLibre fill-extrusion layers for equipment and deck.gl for conductors with visual sag.
My original plan was to use detailed glTF equipment models through Three.js. I even found several free transformer models, but rendering them correctly inside the map became a separate frontend project. I decided not to turn the whole application into an exercise in AI-assisted trial and error, so I replaced the models with simple geometric solids rendered by MapLibre.

The transformer, for example, is essentially a box with a row of cooling fins. It loads quickly and, surprisingly, looks quite decent.
The frontend was never the core of the project, though. The real question was how to generate a layout that looked plausible rather than random.

What counts as an acceptable layout?
After revisiting my memories of the substations I had maintained, I defined several basic constraints:

  1. Equipment must use realistic standard dimensions and ratings so that the map retains a believable physical scale.
  2. Equipment, safety zones, fire clearances, and roads must respect minimum distances from the Russian Electrical Installation Code (PUE), fire-safety rules, and the simplified rule set implemented by the prototype.
  3. The complete minimum equipment set must fit. A layout is useless if the high-voltage yard fits but there is no room left for the medium-voltage switchgear.
  4. Equipment must not overlap and must remain inside the site boundary.
  5. The physical layout must correspond to the electrical single-line diagram. Circuit breakers and disconnectors must appear in a logical connection order.
  6. Equipment orientation must reflect its physical connections. It cannot be rotated freely without considering its construction and terminals.

These rules are deliberately incomplete. The goal is not to reproduce every possible engineering standard, but to create coherent conceptual alternatives that can be inspected and compared.

How the layout is generated

Result page

The backend is written in Kotlin with Spring Boot. Geometry operations use JTS and GeoTools.
The site polygon arrives in WGS 84 and is immediately projected into a local metric coordinate system. Calculating equipment clearances in geographic degrees would be an unnecessarily creative form of suffering, so all subsequent geometry is handled in metres.

The generation process is a pipeline:
-> site polygon
-> coordinate projection
-> transformer candidates
-> high-voltage bays
-> bus couplers and instrument transformers
-> medium-voltage switchgear
-> buswork and conductors
-> secondary cable trenches
-> roads, control building, fence and safety zones
-> electrical model and calculations
-> DXF and IEC 61850 SCL export

The pipeline builds the substation roughly in the order in which I would reason about the physical constraints: place the largest and most restrictive objects first, then add progressively smaller components around them.

In a real project, the incoming transmission lines would strongly influence the layout and might be the true starting point. The prototype does not yet receive line-entry coordinates, although adding explicit connection points or deriving surrounding context from OpenStreetMap is one possible extension.

Transformers first

Power transformers are generated first because they dominate the layout. They are large, require oil-containment structures and fire clearances, and can invalidate the entire site if placed badly.

I use a genetic algorithm to place two transformers. A chromosome encodes their positions and angles. A population of approximately 60 candidates evolves for about 120 generations. The best quarter survives, while the rest are produced through crossover and mutations that shift or rotate the transformers.

Clearly invalid candidates are rejected immediately:
• a transformer lies outside the site;
• the transformers are not aligned as required;
• they are not parallel to the reference boundary;
• the clearance between oil-containment structures is below the allowed value.

Valid candidates accumulate penalties for less severe issues, such as being too close to the boundary, deviating from the target spacing, or producing an awkward fire-barrier arrangement.

Once transformers are placed, the pipeline adds:
• high-voltage bays with circuit breakers, disconnectors, and line gantries;
• surge arresters;
• a high-voltage bus-section circuit breaker;
• voltage transformers;
• outdoor medium-voltage switchgear;
• conductors and buswork;
• secondary cable trenches;
• internal roads;
• the control building;
• the fence, gates, and safety zones.

Each stage knows about the geometry already generated and treats it as a set of obstacles.

Why a single best candidate was not enough

The first implementation had a structural problem. It did not matter how good the transformer placement was if a later stage failed to place the high-voltage yard, bus coupler, or switchgear. One failed stage invalidated the whole pipeline.

I therefore changed the transformer stage so that it no longer returned one supposedly best pair. Instead, it returned a lazy stream of candidates ordered by fitness.

If a later stage failed, the pipeline could backtrack to the next transformer candidate and try again, including alternative orientations relative to different site boundaries. This also made it natural to generate several distinct layout variants when the user requested more than one.

While implementing this, I added a visual trace of the search process. Instead of showing a spinner, the frontend periodically polls the generation trace and renders rejected attempts in red and accepted placements in green.
It adds almost no engineering value, but it makes the algorithm much easier to understand and is rather satisfying to watch.

Generating

Cable trenches: the “small” feature that took almost as long as everything else

Secondary cable trenches carry protection, control, and measurement circuits from the yard equipment to the control building and medium-voltage switchgear. They became a project of their own.
I split routing into two zones:
• the structured area next to the high-voltage equipment;
• the remaining area outside it.

Inside the structured equipment area, trenches can follow the equipment rows while maintaining the required clearances. Short branches connect individual devices. The model keeps separate main routes for the two bus sections.

Outside this area, the route should remain reasonably short, but the geometrically shortest path is not necessarily a useful engineering route. It may create diagonal segments, too many bends, or an unstructured layout.

For the prototype, I use separate routes from the two sections toward the medium-voltage switchgear. This is not a universal substation-design requirement; it is a deliberate constraint that produces a more structured and partially redundant result.

Because the site is not always rectangular, irregular boundaries complicate the routing. I use A* on an orthogonal grid with a one-metre step and an additional penalty for turns. Movement is limited to four directions, so the algorithm prefers long straight segments and produces rectangular routes around obstacles.

The result is still conceptual, but it solved the main geometric problem without requiring a full civil-engineering routing system.

From geometry to electrical engineering

Up to this point, the generator was mainly a computational-geometry project. I wanted the picture to have an electrical model behind it.
The next task was to calculate short-circuit currents for the generated substation using realistic input data. Although I had worked in relay protection for many years, I rarely performed complete fault calculations myself because protection settings usually arrived already calculated for the selected operating conditions. I therefore had to refresh and expand that part of my knowledge while implementing it.

The generated layout is converted into a network equivalent:
• nodes represent the high-voltage buses and the low-voltage bus sections;
• branches represent the external grid and power transformers;
• electrical values are converted into per-unit quantities.

For each switching state, the application builds a reduced nodal admittance matrix Ybus. Inverting it produces the bus-impedance matrix Zbus.

The diagonal element Zkk is used as the Thevenin equivalent impedance seen from the faulted bus. The corresponding column is used to calculate voltages at the other buses, branch currents, and individual source contributions.

For a substation this small, Ybus / Zbus may look excessive. A simpler hand-derived equivalent would be enough. However, the matrix approach provides a consistent foundation for multiple switching states and future expansion of the network model.

The engine currently calculates:
• three-phase faults;
• line-to-line faults;
• peak short-circuit current;
• current contributions from individual branches;
• bus voltages during the fault;
• a simplified load-flow estimate and transformer loading

The calculations are repeated for four combinations of the high-voltage and low-voltage bus-section circuit breakers being open or closed.

On the frontend, the result is shown as an interactive single-line diagram. The user can switch between operating states and inspect currents and voltages for each part of the substation.

Single-line diagram

The application supports two calculation methods:
• GOST R 52735-2007, the Russian standard for short-circuit calculations in AC installations above 1 kV;
• IEC 60909.

I have not yet implemented single-line-to-ground or double-line-to-ground faults. Those require zero-sequence networks and additional transformer and grounding data, so they remain future work.

Trust, but verify: checking the generator by hand

A panel full of convincing numbers is not evidence that the calculation is correct. I therefore reproduced one operating case manually.
Consider operating mode D from the interface:
• the 110 kV bus-section circuit breaker is open;
• the 10 kV bus-section circuit breaker is closed;
• each high-voltage section is supplied by its own line;
• the low-voltage sections are connected in parallel;
• the fault is applied to the first 10 kV bus section.

The application reports:
I''k(3) = 38.1 kA
ip = 100.6 kA
Rsum = 0.006 p.u.
Xsum = 0.144 p.u.

Input data:
• two 40 MVA transformers;
• transformer short-circuit voltage uk = 10.5%;
• transformer short-circuit loss DeltaPk = 172 kW;
• external-grid short-circuit power Sk = 3800 MVA;
• external-grid X/R = 12;
• base power Sbase = 100 MVA;
• base voltages 115 / 10.5 kV;
• voltage factor c = 1.0.

Both Sk and uk define the magnitude of the total impedance, not its reactive component alone, so the impedance must be decomposed into R and X.

External grid
|Zgrid| = Sbase / Sk
= 100 / 3800
= 0.026316 p.u.

Rgrid = |Zgrid| / sqrt(1 + 12^2) = 0.002185 p.u.

Xgrid = 12 * Rgrid = 0.026225 p.u.

Zgrid = 0.002185 + j0.026225 p.u.

Transformer
|Ztr| = (uk / 100) * (Sbase / Sn)
= 0.105 * 100 / 40
= 0.2625 p.u.

Rtr = DeltaPk * Sbase / (1000 * Sn^2)
= 172 * 100 / (1000 * 40^2)
= 0.010750 p.u.

Xtr = sqrt(|Ztr|^2 - Rtr^2) = 0.262280 p.u.

Ztr = 0.010750 + j0.262280 p.u.
The impedance of one source-transformer branch is:
Zbranch = Zgrid + Ztr = 0.012935 + j0.288505 p.u.

With the 110 kV bus sections separated, each branch retains its own external-grid impedance. With the 10 kV bus coupler closed, the two identical branches are connected in parallel:
Rsum = 0.012935 / 2 = 0.006468 p.u. -> UI: 0.006
Xsum = 0.288505 / 2 = 0.144252 p.u. -> UI: 0.144

The magnitude of the equivalent impedance is:
|Zsum| = sqrt(0.006468^2 + 0.144252^2) = 0.144397 p.u.
The base current at 10.5 kV is:
Ibase = 100 / (sqrt(3) * 10.5) = 5.4986 kA
Therefore, the initial symmetrical three-phase short-circuit current is:
I''k(3) = c * Ibase / |Zsum|
= 5.4986 / 0.144397
= 38.079 kA

The detailed table shows 38.07 kA, while the summary rounds it to 38.1 kA.

I then performed several cross-checks:
• Source contributions: because the network is symmetrical, transformers T1 and T2 each contribute approximately 19.04 kA; the interface shows 19.03 + 19.03 = 38.07 kA after rounding.
• High-voltage bus voltage: the branch current is approximately 3.463 p.u., giving a grid-side voltage drop of about 0.0911 p.u. and a remaining voltage of 0.909 p.u., matching the interface.
• High-voltage through-current: referring 19.04 kA from 10.5 kV to 115 kV gives approximately 1.74 kA, again matching the interface.
• Peak current: Xsum / Rsum = 22.30, Ta approximately 0.0710 s, the peak factor is approximately 1.8686, and the resulting peak current is 100.63 kA, compared with 100.6 kA in the interface.

All six checkpoints - current, total resistance and reactance, source contributions, residual voltage, through-current, and peak current - were reproduced manually within rounding accuracy.

Protection settings: where the relay engineer in me took over

Once fault currents are available for the main operating states, the application can make preliminary protection-setting estimates.
The current version includes:
• time-overcurrent protection, ANSI 51;
• instantaneous overcurrent protection, ANSI 50;
• preliminary rated-current and starting-parameter estimates for transformer differential protection, ANSI 87T;
• a simplified thermal-overload estimate, ANSI 49;
• a basic time-grading selectivity check.

These are not ready-to-use operational settings. They demonstrate the logic of preliminary selection and require real current-transformer data, the exact protection relay model, detailed network data, and an engineering review.

The prototype also knows nothing about outgoing feeders: their cable types, lengths, downstream networks, and customer-side fault levels. It therefore does not calculate remote-end faults or feeder protection settings.

Protection

The basic distinction is important:
• equipment ratings are checked against maximum fault current;
• protection sensitivity is checked against minimum fault current.

For example, an instantaneous overcurrent pickup is preliminarily set above the maximum external fault current beyond the protected transformer, while sensitivity is checked against the minimum fault current inside the protected zone.

Each result is accompanied by the formula with substituted values, selected current-transformer ratios, and an explicit indication of whether the result is a preliminary calculation or only a rough estimate.

IEC 61850: representing the generated substation digitally

Once the generator had a primary layout and preliminary protection functions, exporting an IEC 61850 model was a natural next step.
Only one of the substations I worked with was partly digital, so IEC 61850 was not my strongest practical area. I had to study the standard family, its information model, and the Substation Configuration Language (SCL) while building the export module.

SCL export

The generator exports:
• an SSD file containing the system specification, substation model, and required functions;
• a prototype SCD file that adds IEDs, communication configuration, and data-type templates.
Inside the Substation section:
• circuit breakers and disconnectors are represented as ConductingEquipment with CBR and DIS types;
• power transformers are represented as PowerTransformer elements;
• protection functions are linked to logical nodes such as PTOC, PIOC, and PDIF.
For each bay, the prototype creates a bay controller and a merging unit. It also defines:
• GOOSE communication for interlocking and signal exchange between IEDs;
• Sampled Values for digitized measurements;
• station-bus and process-bus networks.

I implemented the required subset of the SCL object model manually as JAXB classes. Generating classes from the complete XSD would have produced far more code than the prototype needed.

Every exported file is validated against the official XSD for the selected schema edition. This verifies the structural correctness of the XML, but it does not guarantee full semantic interoperability with every vendor tool or real IED. That depends on the standard edition, device profile, and the expectations of a particular system configurator.

An LLM assistant that explains but does not calculate

Near the end of the project, I added an LLM-based assistant.
The first version simply passed a lightly modified user question to the model. It quickly began inventing formulas, constants, and confident explanations for things that did not exist. I initially tested GigaChat and later added DeepSeek because it was inexpensive, but changing the model was not enough. The architecture had to change.

I replaced the free-form prompt with a card-based context system. The numerical engine performs all calculations. The model receives structured results and is allowed only to explain them:
• which input values were used;
• which switching state was selected;
• which formula or engineering rule was applied;
• why a warning was triggered;
• which standard or internal rule supports the explanation.

The important rule is simple:
The LLM explains numbers produced by deterministic code; it does not invent or calculate them itself.

Copilot

What the project became

The final prototype combines:
• Kotlin, Spring Boot, and JTS on the backend;
• Vue, MapLibre, and deck.gl on the frontend;
• a genetic algorithm for major-equipment placement;
• A* for orthogonal cable-trench routing;
• Ybus / Zbus calculations for fault analysis;
• preliminary relay-protection settings;
• DXF and IEC 61850 SCL export;
• an assistant that explains deterministic results instead of calculating them.

The project took roughly six months of spare-time work.
My main conclusion, however, is not about a particular framework or algorithm. I started this project because I wanted to build something that a general-purpose AI coding tool could not simply produce from a short prompt.

Of course, an AI assistant can generate code faster than I can. It helped with repetitive classes, refactoring, and debugging. But it could not independently decide which substation arrangement looked operationally believable, which switching state should be treated as the design case, where a simplification stopped being acceptable, or why a mathematically valid result contradicted actual substation practice.

Almost every meaningful decision in this project came not from programming alone, but from the years I had spent around real substations. Code was the mechanism used to formalize that knowledge.
For people who move into software from another field, previous domain experience is not obsolete baggage. It can be the most valuable part of the new combination.


Short glossary
• Relay protection and automation - protection, control, and automation functions used in power systems.
• HV / LV - the high-voltage and low-voltage sides of the substation.
• Short circuit / fault - an abnormal low-impedance connection that produces high current.
• Bus coupler / bus-section circuit breaker - a circuit breaker connecting two bus sections.
• Outdoor medium-voltage switchgear - prefabricated outdoor distribution switchgear used on the low-voltage side of the substation.
• Control building - the building containing protection, control, communication, and auxiliary systems.
• Surge arrester - equipment that limits overvoltage.
• CT - current transformer.
• PUE - the Russian Electrical Installation Code used for part of the prototype's spatial rule set.
• Ybus / Zbus - nodal admittance and bus-impedance matrices of the electrical network.
• SCL - IEC 61850 Substation Configuration Language; SSD describes system requirements, while SCD describes a configured system.
• IED / MU - intelligent electronic device / merging unit.
• GOOSE / SV - fast event messaging and digitized sampled measurements in IEC 61850.
• ANSI 50 / 51 / 87T / 49 - instantaneous overcurrent, time overcurrent, transformer differential, and thermal-overload protection.


This is an English, revised edition of an article originally published in Russian.

Project links

Top comments (0)