Parameter sweeps¶
Solve one fault once per parameter combination and stack the results into
long-format frames that carry the parameters as columns. This is what the
statistics below operate on — until such a frame exists there is nothing to
summarise, because every accessor on Network reports a single solve.
rho_f_points builds the points from a catalogue of five-parameter rho-f
vectors, which is the form groundfield exports.
sweep ¶
Run one fault over a grid of parameter variations and collect it into one frame.
run_outage_study already answers "what changes when an element drops out".
This module answers the other half: what changes when a parameter moves --
the rho-f characteristic of the faulted station, the soil resistivity, the
harmonic content of the source, the fault location itself.
The unit of work is a :class:SweepPoint: a label plus the overrides that
define it. Every point is applied, solved and restored, and the results are
stacked into long-format Polars frames that carry the point's label and its
parameters as ordinary columns. That last part is the point of the module --
until a frame like this exists there is nothing for a statistic to operate on,
which is why :mod:groundinsight.analysis.statistics starts here.
Overrides are applied by writing directly onto Bus.impedance and friends,
which reaches the solver untouched because run_fault does not recompute
impedances (see :class:~groundinsight.electrical_network.ElectricalNetwork).
Everything is restored in a finally block, so an exception in the middle of
a sweep leaves the network exactly as it was found -- including the path cache
and the active fault.
Example
Vary the rho-f characteristic of the faulted station over a grid and watch what it does to the potential rise and to the parallel impedances of the two feeder directions::
points = gi.rho_f_points(
bus="Station_7",
k_vectors={f"k1={k1:g}": (k1, 1e-4, 3e-4, 0.0, 0.0)
for k1 in (0.01, 0.02, 0.05, 0.1)},
)
study = gi.run_sweep(
net, fault="F_Station_7", points=points,
cuts=[gi.Cut(name="left", branches=["C6_7"]),
gi.Cut(name="right", branches=["C7_8"])],
)
study.impedances() # Z_G and both reduction factors per point
study.cuts() # Z_left / Z_right / r_left / r_right per point
SweepPoint ¶
Bases: BaseModel
One parameter combination to solve.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
Identifies the point in every result frame. Must be unique in a sweep. |
bus_impedance |
dict of str to dict of float to complex, optional
|
Impedance tables written straight onto |
bus_rho |
dict of str to float, optional
|
New |
fault |
(str, optional)
|
Solve a different fault at this point. Defaults to the sweep's fault. |
fault_scalings |
dict of float to float, optional
|
Replace the active fault's per-frequency scalings -- the harmonic content of the source. |
parameters |
dict of str to object, optional
|
Free-form values copied into every result row as columns, so a plot can be made against the physical parameter rather than against the label. |
SweepResult ¶
Bases: BaseModel
Everything a sweep collected, as long-format frames.
Attributes:
| Name | Type | Description |
|---|---|---|
fault |
str
|
The fault the sweep was run for (points may override it individually). |
labels |
list of str
|
Point labels in the order they were solved. |
failures |
dict of str to str
|
Points that raised, mapped to the exception text. They are absent from the frames; the sweep does not abort on one bad point. |
rho_f_points ¶
rho_f_points(
*,
bus: str,
k_vectors: Dict[str, KVector],
frequencies: Sequence[float],
rho: float
) -> List[SweepPoint]
Build sweep points from a catalogue of rho-f parameter vectors.
Each vector is evaluated into an impedance table for bus over
frequencies at the given rho, so the study varies the fitted
characteristic of one station while the rest of the network stays put.
k1 ... k5 and rho are copied into parameters, which puts
them in the result frames as plottable columns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bus
|
str
|
Name of the bus whose characteristic is varied. |
required |
k_vectors
|
dict of str to tuple
|
Label to |
required |
frequencies
|
sequence of float
|
Frequencies to evaluate the form at -- normally |
required |
rho
|
float
|
Soil resistivity the form is evaluated at. |
required |
Returns:
| Type | Description |
|---|---|
list of SweepPoint
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If a vector produces a non-positive real part at any frequency, which the solver rejects as non-passive. The offending label and frequency are named, because an unconstrained least-squares fit can land there and the failure is otherwise reported far from its cause. |
Source code in src/groundinsight/simulation/sweep.py
run_sweep ¶
run_sweep(
network: Network,
*,
fault: str,
points: Sequence[SweepPoint],
cuts: Optional[Sequence[Cut]] = None,
phase_current_mode: str = "auto",
collect_branches: bool = False,
on_error: str = "record"
) -> SweepResult
Solve one fault once per parameter point and stack the results.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
network
|
Network
|
The network. Left exactly as found, whatever happens. |
required |
fault
|
str
|
Fault to solve, unless a point names its own. |
required |
points
|
sequence of SweepPoint
|
The parameter grid. Labels must be unique. |
required |
cuts
|
sequence of Cut
|
When given, :func: |
None
|
phase_current_mode
|
('auto', 'paths')
|
Forwarded to :func: |
"auto"
|
collect_branches
|
bool
|
Branch results multiply the row count by the number of branches and are rarely what a parameter study plots, so they are off by default. |
False
|
on_error
|
('record', 'raise')
|
|
"record"
|
Returns:
| Type | Description |
|---|---|
SweepResult
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/groundinsight/simulation/sweep.py
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | |