Topology Optimizer#
toop_engine_topology_optimizer.interfaces.messages.commands
#
Defines the interaction between the optimizer and a backend.
The backend will send commands in the form of messages to the optimizer, which will trigger a certain behaviour. The Optimizer will respond with results, for this see results.py
StartOptimizationCommand
#
Bases: BaseModel
Command with parameters for starting an optimization run.
message_type
class-attribute
instance-attribute
#
The command type for deserialization, don't change this
dc_params
class-attribute
instance-attribute
#
dc_params = DCOptimizerParameters()
The parameters for the DC optimizer
ac_params
class-attribute
instance-attribute
#
ac_params = ACOptimizerParameters()
The parameters for the AC optimizer
grid_files
instance-attribute
#
The grid files to load, where each gridfile represents one timestep. The grid files also include coupling information for the timesteps.
optimization_id
instance-attribute
#
The id of the optimization run, used to identify the optimization run in the results. Should stay the same for the whole optimization run and should be equal to the kafka event key
ShutdownCommand
#
Command
#
Bases: BaseModel
Base class for all commands to the optimizer.
validate_first_gridfile_uncoupled
#
Check that the first gridfile is uncoupled
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/interfaces/messages/commands.py
toop_engine_topology_optimizer.interfaces.messages.dc_params
#
The DC optimizer is the GPU stage where massive amounts of topologies are being checked.
This holds the parameters to start the optimization. Some parameters can not be changed (mainly the names of the kafka streams) and are included in the command line start parameters instead.
BatchedMEParameters
#
Bases: BaseModel
Parameters for starting the batched genetic algorithm (In this case Map-Elites)
substation_split_prob
class-attribute
instance-attribute
#
The probability to split an unsplit substation. If not split, a reconfiguration is applied
substation_unsplit_prob
class-attribute
instance-attribute
#
The probability to reset a split substation to the unsplit state
disconnect_prob
class-attribute
instance-attribute
#
The probability to disconnect a new branch
reconnect_prob
class-attribute
instance-attribute
#
The probability to reconnect a disconnected branch, will overwrite a possible disconnect
pst_mutation_sigma
class-attribute
instance-attribute
#
The sigma to use for the normal distribution that mutates the PST taps. The mutation is applied by adding a random value drawn from this distribution to the current tap position. A value of 0.0 means no PST mutation.
n_subs_mutated_lambda
class-attribute
instance-attribute
#
The number of substations to mutate in a single iteration is drawn from a poisson with this lambda
proportion_crossover
class-attribute
instance-attribute
#
The proportion of the first topology to take in the crossover
crossover_mutation_ratio
class-attribute
instance-attribute
#
The ratio of crossovers to mutations
target_metrics
class-attribute
instance-attribute
#
The list of metrics to optimize for with their weights
observed_metrics
class-attribute
instance-attribute
#
observed_metrics = (
"max_flow_n_0",
"overload_energy_n_0",
"overload_energy_limited_n_0",
"max_flow_n_1",
"overload_energy_n_1",
"overload_energy_limited_n_1",
"split_subs",
"switching_distance",
)
The observed metrics, i.e. which metrics are to be computed for logging purposes. The target_metrics and me_descriptors must be included in the observed metrics and will be added automatically by the validator if they are missing
me_descriptors
class-attribute
instance-attribute
#
me_descriptors = (
DescriptorDef(metric="split_subs", num_cells=5),
DescriptorDef(
metric="switching_distance", num_cells=45
),
)
The descriptors to use for MAP-Elites. This includes a metric that determines the cell index and a number of cells. If the metric exceeds the number of cells, it will be clipped to the largest cell index. Currently, this must be integer metrics
iterations_per_epoch
class-attribute
instance-attribute
#
The number of iterations per epoch
random_seed
class-attribute
instance-attribute
#
The random seed to use for reproducibility
cell_depth
class-attribute
instance-attribute
#
When applicable, each cell contains cell_depth unique topologies. Use 1 to retain the original map-elites behaviour
mutation_repetition
class-attribute
instance-attribute
#
More chance to get unique mutations by mutating multiple copies of the repertoire
n_worst_contingencies
class-attribute
instance-attribute
#
The number of worst contingencies to consider in the scoring function. This is used to determine the worst cases for overloads.
infer_missing_observed_metrics
#
Add potentially missing target and descriptor metrics to the observed metrics.
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/interfaces/messages/dc_params.py
LoadflowSolverParameters
#
Bases: BaseModel
Parameters for the loadflow solver.
max_num_splits
class-attribute
instance-attribute
#
The maximum number of splits per topology
max_num_disconnections
class-attribute
instance-attribute
#
The maximum number of disconnections to apply per topology
batch_size
class-attribute
instance-attribute
#
The batch size for the genetic algorithm
distributed
class-attribute
instance-attribute
#
Whether to run the genetic algorithm distributed over multiple devices
cross_coupler_flow
class-attribute
instance-attribute
#
Whether to compute cross-coupler flows
DCOptimizerParameters
#
Bases: BaseModel
The set of parameters that are used in the DC optimizer only
ga_config
class-attribute
instance-attribute
#
ga_config = BatchedMEParameters()
The configuration options for the genetic algorithm
loadflow_solver_config
class-attribute
instance-attribute
#
loadflow_solver_config = LoadflowSolverParameters()
The configuration options for the loadflow solver
double_limits
class-attribute
instance-attribute
#
The double limits for the optimization, if they should be updated
summary_frequency
class-attribute
instance-attribute
#
The frequency to push back results, based on number of iterations. Default is after every 10 iterations.
check_command_frequency
class-attribute
instance-attribute
#
The frequency to check for new commands, based on number of iterations. Should be a multiple of summary_frequency
toop_engine_topology_optimizer.interfaces.messages.ac_params
#
The parameters for the AC optimizer.
On AC, some subtelties are different to the DC optimization such as that the optimization is not batched, and the parameters are slightly different.
ACGAParameters
#
Bases: BaseModel
Parameters for the AC genetic algorithm
runtime_seconds
class-attribute
instance-attribute
#
The maximum runtime of the AC optimization in seconds
pull_prob
class-attribute
instance-attribute
#
The probability of pulling a strategy from the DC repertoire
me_descriptors
class-attribute
instance-attribute
#
me_descriptors = (
DescriptorDef(
metric="split_subs", num_cells=2, range=(0, 5)
),
DescriptorDef(
metric="switching_distance",
num_cells=5,
range=(0, 50),
),
DescriptorDef(
metric="disconnected_branches", num_cells=2
),
)
The descriptors for the aggregated map elites repertoire.
reconnect_prob
class-attribute
instance-attribute
#
The probability of reconnecting a disconnected branch in a strategy
close_coupler_prob
class-attribute
instance-attribute
#
The probability of closing an opened coupler in a strategy
n_worst_contingencies
class-attribute
instance-attribute
#
How many worst contingencies to consider for the initial metrics, i.e. the top k contingencies that are used to compute the initial metrics. This is used to compute the top_k_overloads_n_1
timestep_processes
class-attribute
instance-attribute
#
How many processes to spawn for computing the timesteps in parallel
runner_processes
class-attribute
instance-attribute
#
How many processes to spawn for computing the N-1 cases in each timestep in parallel. Note that this multiplies with timestep_processes and you might run out of memory if you set both too high
runner_batchsize
class-attribute
instance-attribute
#
Whether to batch the N-1 definition into smaller chunks, might conserve memory
filter_strategy
class-attribute
instance-attribute
#
The filter strategy to use for the optimization, used to filter out strategies based on the discriminator, median or dominator filter.
enable_ac_rejection
class-attribute
instance-attribute
#
Whether to enable the AC rejection, i.e. no messages will be sent to the results topic in case of non-acceptance.
reject_convergence_threshold
class-attribute
instance-attribute
#
The rejection threshold for the convergence rate, i.e. the split case must have at most the same amount of non converging loadflows as the unsplit case or it will be rejected.
reject_overload_threshold
class-attribute
instance-attribute
#
The rejection threshold for the overload energy improvement, i.e. the split case must have at least 5% lower overload energy than the unsplit case or it will be rejected.
reject_critical_branch_threshold
class-attribute
instance-attribute
#
The rejection threshold for the critical branches increase, i.e. the split case must have less than 10% more critical branches than the unsplit case or it will be rejected.
early_stop_validation
class-attribute
instance-attribute
#
Whether to enable early stopping during the optimization process.
early_stopping_non_convergence_percentage_threshold
class-attribute
instance-attribute
#
The threshold for the early stopping criterion, i.e. if the percentage of non-converging cases is greater than this value, the ac validation will be stopped early.
max_initial_wait_seconds
class-attribute
instance-attribute
#
The maximum amount of seconds to wait for the initial DC results. If no results have arrived within this time, we assume the DC optimizer had some problem and abort the optimization run.
probabilities_sum_to_one
#
Ensure that the probabilities sum to one
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/interfaces/messages/ac_params.py
ACOptimizerParameters
#
Bases: BaseModel
The set of parameters that are used in the AC optimizer only
initial_loadflow
class-attribute
instance-attribute
#
If an initial AC loadflow was computed before the start of the optimization run, this can be passed and will be used e.g. to compute double limits. It will be sent back through the initial topology push.
ga_config
class-attribute
instance-attribute
#
ga_config = ACGAParameters()
The genetic algorithm configuration
AC Validation#
toop_engine_topology_optimizer.ac.evolution_functions
#
Implements an adjusted AC evolution
Instead of the original GA evolution which only knows mutate and crossover, we introduce the following operations: - The pull operator will take a promising topology from the DC repertoire and re-evaluate it on AC. The notion of promising is defined through a interest-scoring function which tries to balance the explore/exploit trade-off. - The reconnect operator will take a promising topology from the DC repertoire and reconnect a single branch in all timesteps. The idea is that the DC part might have disconnected too many branches, so we try to simplify the topology by reconnecting a single branch. - The close_coupler operator will take a promising topology from the DC repertoire and close a coupler in all timesteps. The idea is that the DC part might have too many open couplers, so we try to simplify the topology by closing a single coupler.
select_repertoire
#
Select the topologies that are suitable for mutation and crossover
In this case, all topologies that satisfy the filter criteria are suitable, however a check after the mutate is necessary to ensure that the topology is not already in the database.
The unsplit strategy is never selected for mutation or crossover.
| PARAMETER | DESCRIPTION |
|---|---|
optimization_id
|
The optimization ID to filter for
TYPE:
|
optimizer_type
|
The optimizer types to filter for (whitelist)
TYPE:
|
session
|
The database session to use
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[ACOptimTopology]
|
The topologies that are suitable for mutation and crossover |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
get_unsplit_ac_topology
#
Get the unsplit AC topology for the given optimization ID
| PARAMETER | DESCRIPTION |
|---|---|
optimization_id
|
The optimization ID to filter for
TYPE:
|
session
|
The database session to use
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ACOptimTopology
|
The unsplit AC topology for the given optimization ID, or None if not found |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
default_scorer
#
Score the topologies based on their fitness only (greedy selection)
| PARAMETER | DESCRIPTION |
|---|---|
metrics
|
The metrics DataFrame to score
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Series
|
The fitness scores |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
get_contingency_indices_from_ids
#
Map contingency ids to their indices in the N-1 definition for each timestep.
This is a helper method used in update_initial_metrics_with_worst_k_contingencies method.
| PARAMETER | DESCRIPTION |
|---|---|
case_ids_all_t
|
A list of lists, where each inner list contains the contingency ids for a specific timestep.
TYPE:
|
n_minus1_definitions
|
A list of N-1 definitions, one for each timestep, containing the contingencies.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[list[int]]
|
A list of lists, where each inner list contains the indices of the contingencies in the N-1 definition for the corresponding timestep. If a contingency id is not found, it will be skipped. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
pull
#
Pull a promising topology from the DC repertoire to AC
This only copies the topology without any changes other than setting the optimizer type to AC. This function takes a list of selected DC topologies and creates corresponding AC topologies by copying relevant attributes, setting the optimizer type to AC, and merging contingency case indices from both the DC and unsplit AC topologies.
| PARAMETER | DESCRIPTION |
|---|---|
selected_strategy
|
The selected strategy to pull
TYPE:
|
session
|
The database session to use, by default None. The session object is used to fetch the unsplit AC topology which is then used to add the critical contingency cases to the pulled strategy. These critical cases can then be used for early stopping of AC N-1 contingency analysis.
TYPE:
|
n_minus1_definitions
|
The N-1 definitions to use for the pulled strategy. If not provided, the pulled strategy will not include any N-1 contingency cases while calculating the top critical contingencies for early stopping.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[ACOptimTopology]
|
The a copy of the input topologies with the optimizer type set to AC |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | |
reconnect
#
Reconnect a disconnected branch
The idea behind this mutation operation is that the DC part might have disconnected too many branches, so we try to simplify the topology by reconnecting a single branch in all timesteps
This might accidentally create the unsplit topology and does not check for that case. A check for this happens upon insertion into the database where the unique constraint will prevent duplicates.
| PARAMETER | DESCRIPTION |
|---|---|
rng
|
The random number generator to use
TYPE:
|
selected_strategy
|
The selected DC or AC strategy to reconnect
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[ACOptimTopology]
|
The a copy of the input topologies with the optimizer type set to AC and a branch reconnected or the empty list if no disconnections were present in the input topologies |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
close_coupler
#
Close a coupler in the selected strategy.
A station that has an open coupler in any of the timesteps is selected and the coupler is closed for all timesteps in the topology.
This might accidentally create the unsplit topology and does not prohibit that case. A check for this happens upon insertion into the database where the unique constraint will prevent duplicates. The unsplit flag will be set correctly though, so in case the unsplit topology was not yet in the database for some reason, this will add it.
| PARAMETER | DESCRIPTION |
|---|---|
rng
|
The random number generator to use
TYPE:
|
selected_strategy
|
The selected DC or AC strategy to close a coupler. The number of timesteps must be equal to the number of timesteps in the branches_per_sub and injections_per_sub arrays. If the empty list is passed, the function will return an empty list.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[ACOptimTopology]
|
The a copy of the input topologies with the optimizer type set to AC and a coupler closed or the empty list if no open couplers were present in the input topologies |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
evolution
#
evolution(
rng,
session,
optimization_id,
close_coupler_prob,
reconnect_prob,
pull_prob,
max_retries,
n_minus1_definitions=None,
filter_strategy=None,
)
Perform the AC evolution.
| PARAMETER | DESCRIPTION |
|---|---|
rng
|
The random number generator to use
TYPE:
|
session
|
The database session to use, will write the new topologies to the database
TYPE:
|
optimization_id
|
The optimization ID to filter for
TYPE:
|
close_coupler_prob
|
The probability of closing a coupler
TYPE:
|
reconnect_prob
|
The probability of reconnecting a branch
TYPE:
|
pull_prob
|
The probability of pulling a strategy
TYPE:
|
max_retries
|
The maximum number of retries to perform if a strategy is already in the database
TYPE:
|
n_minus1_definitions
|
A list of N-1 definitions, one for each timestep, containing the contingencies.
TYPE:
|
filter_strategy
|
The filter strategy to use for the optimization, used to filter out strategies that are too far away from the original topology.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[ACOptimTopology]
|
The strategy that was created during the evolution or an empty list if something went wrong at all retries |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
evolution_try
#
evolution_try(
rng,
session,
optimization_id,
close_coupler_prob,
reconnect_prob,
pull_prob,
n_minus1_definition=None,
filter_strategy=None,
)
Perform a single try of the AC evolution.
| PARAMETER | DESCRIPTION |
|---|---|
rng
|
The random number generator to use
TYPE:
|
session
|
The database session to use, will write the new topologies to the database
TYPE:
|
optimization_id
|
The optimization ID to filter for
TYPE:
|
close_coupler_prob
|
The probability of closing a coupler
TYPE:
|
reconnect_prob
|
The probability of reconnecting a branch
TYPE:
|
pull_prob
|
The probability of pulling a strategy
TYPE:
|
n_minus1_definition
|
A list of N-1 definitions, one for each timestep, containing the contingencies.
TYPE:
|
filter_strategy
|
The filter strategy to use for the optimization, used to filter out strategies that are too far away from the original topology.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[ACOptimTopology]
|
The strategy that was created during the evolution or an empty list if something went wrong. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/evolution_functions.py
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 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | |
toop_engine_topology_optimizer.ac.listener
#
A listener that listens for new topologies on a kafka result stream and saves them to db
This is intended for usage both in the AC optimizer and the backend, as they both listen for topologies on the result kafka stream. The backend has some further logic as it watches all optimizations and might change the state of the optimization to "running" if it receives a start optimization result and "stopped" if a stop optimization result is received.
One of the requirements is that the AC listener will only want to listen to a single optimization_id and not to messages by itself. Hence a filtering mechanic is added.
poll_results_topic
#
Poll the results topic for new topologies to store in the DB
We store topologies from all optimization jobs, as it could be that we will later optimize the same job in this worker.
| PARAMETER | DESCRIPTION |
|---|---|
db
|
The database session to use for saving the topologies
TYPE:
|
consumer
|
The kafka consumer to poll messages from. It should already be subscribed to the result topic.
TYPE:
|
first_poll
|
If True, we assume the optimizatin has just started and we can afford to wait for the first DC results for a longer time (30 seconds). If False, we assume the optimization is already running and we don't want to block the consumer for too long (100 ms), by default True
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[ACOptimTopology]
|
A list of topologies that were added to the database |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/listener.py
toop_engine_topology_optimizer.ac.optimizer
#
Implements initialize and run_epoch functions for the AC optimizer
AcNotConvergedError
#
Bases: Exception
An exception that is raised when the AC optimization did not converge in the base grid
OptimizerData
dataclass
#
OptimizerData(
params,
session,
evolution_fn,
scoring_fn,
store_loadflow_fn,
load_loadflow_fn,
acceptance_fn,
rng,
action_sets,
framework,
runners,
)
The epoch-to-epoch storage for the AC optimizer
update_initial_metrics_with_worst_k_contingencies
#
Update the initial metrics with the worst k contingencies.
This function computes the worst k contingencies for each timestep in the initial loadflow results and updates the initial metrics with the case ids and the top k overloads.
| PARAMETER | DESCRIPTION |
|---|---|
initial_loadflow
|
The initial loadflow results containing the branch results.
TYPE:
|
initial_metrics
|
The initial metrics for each timestep.
TYPE:
|
worst_k
|
The number of worst contingencies to consider for the initial metrics.
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/optimizer.py
make_runner
#
make_runner(
action_set,
nminus1_definition,
grid_file,
n_processes,
batch_size,
processed_gridfile_fs,
)
Initialize a runner for a gridfile, action set and n-1 def
| PARAMETER | DESCRIPTION |
|---|---|
action_set
|
The action set to use
TYPE:
|
nminus1_definition
|
The N-1 definition to use
TYPE:
|
grid_file
|
The grid file to use
TYPE:
|
n_processes
|
The number of processes to use, from the ACGAParameters
TYPE:
|
batch_size
|
The batch size to use, if any, from the ACGAParameters
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AbstractLoadflowRunner
|
The initialized loadflow runner, either Pandapower or Powsybl |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/optimizer.py
initialize_optimization
#
initialize_optimization(
params,
session,
optimization_id,
grid_files,
loadflow_result_fs,
processed_gridfile_fs,
)
Initialize an optimization run for the AC optimizer
| PARAMETER | DESCRIPTION |
|---|---|
params
|
The parameters for the AC optimizer
TYPE:
|
session
|
The database session to use for storing topologies
TYPE:
|
optimization_id
|
The ID of the optimization run
TYPE:
|
grid_files
|
The grid files to optimize on, must contain at least one file
TYPE:
|
loadflow_result_fs
|
A filesystem where the loadflow results are stored. Loadflows will be stored here using the uuid generation process and passed as a StoredLoadflowReference which contains the subfolder in this filesystem.
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
OptimizerData
|
The initial optimizer data |
Strategy
|
The initial strategy |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/optimizer.py
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 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 | |
wait_for_first_dc_results
#
Wait an initial period for DC results to arrive before proceeding with the optimization.
Call this after initialize optimization and before run epoch to ensure that the DC optimizer has started, and avoid the AC optimizer idling while waiting for the first DC results to arrive.
| PARAMETER | DESCRIPTION |
|---|---|
results_consumer
|
The consumer where to listen for DC results
TYPE:
|
session
|
The database session to use for storing topologies
TYPE:
|
max_wait_time
|
The maximum time to wait for DC results, in seconds
TYPE:
|
optimization_id
|
The ID of the optimization run, used to filter the incoming topologies and only proceed when DC results from the correct optimization run arrive. Note that other DC runs could be active.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
TimeoutError
|
If no DC results arrive within the maximum wait time |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/optimizer.py
run_epoch
#
Run a single epoch of the AC optimizer
This shall send the investigated topology to the result topic upon completion.
| PARAMETER | DESCRIPTION |
|---|---|
optimizer_data
|
The optimizer data, will be updated in-place
TYPE:
|
results_consumer
|
The consumer where to listen for DC results
TYPE:
|
send_result_fn
|
The function to send results
TYPE:
|
epoch
|
The current epoch number, used for logging and heartbeat purposes. Also, on epoch 1 the wait time for the consumer is longer to allow for dc optim startup.
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/optimizer.py
toop_engine_topology_optimizer.ac.scoring_functions
#
Scoring functions for the AC optimizer - in this case this runs an N-1 and computes metrics for it
get_threshold_n_minus1_overload
#
Extract the 'top_k_overloads_n_1' thresholds and corresponding case indices from a list of ACOptimTopology strategies.
overload_threshold is defined as the maximum allowed overload energy for the worst k AC N-1 contingency analysis of the split topologies. This threshold is computed using the worst k AC contingencies of the unsplit grid and the worst k DC contingencies of the split grid. Refer to the "pull" method in evolution_functions.py for more details.
| PARAMETER | DESCRIPTION |
|---|---|
strategy
|
A list of ACOptimTopology objects, each containing a 'metrics' dictionary with overload thresholds and case indices.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple of (Optional[list of float], Optional[list of list of int])
|
A tuple containing: - A list of overload thresholds for each topology, or None if any required metric is missing. - A list of lists of case indices for each topology, or None if any required metric is missing. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
update_runner_nminus1
#
Update the N-1 definitions in the runners to only include the worst k contingencies.
This modifies the N-1 definitions in the runners to only include the contingencies at the given indices.
| PARAMETER | DESCRIPTION |
|---|---|
runners
|
The loadflow runners to update. The length of the list equals the number of timesteps.
TYPE:
|
nminus1_defs
|
The original N-1 definitions to use as a template. The length of the list equals the number of timesteps.
TYPE:
|
case_ids_all_t
|
A list of contingency ids for each runner, indicating which contingencies to keep in the N-1 definition. Each element should be an index to the contingencies in the original N-1 definition.
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
compute_loadflow_and_metrics
#
Compute loadflow results and associated metrics for a given set of strategies.
This function runs loadflow simulations for each provided strategy using the specified runners, then computes additional metrics based on the simulation results.
| PARAMETER | DESCRIPTION |
|---|---|
runners
|
List of loadflow runner instances to use for simulations.
TYPE:
|
strategy
|
List of topology strategies to evaluate.
TYPE:
|
base_case_ids
|
List of base case identifiers corresponding to each strategy. Can be None.
TYPE:
|
n_timestep_processes
|
Number of parallel processes to use for timestep simulations (default is 1).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
lfs
|
The results of the loadflow simulations.
TYPE:
|
additional_info
|
Additional information for each action taken in the strategies.
TYPE:
|
metrics
|
Computed metrics for each strategy.
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
compute_loadflow_and_metrics_with_early_stopping
#
compute_loadflow_and_metrics_with_early_stopping(
runners,
strategy,
base_case_ids,
threshold_overload_all_t,
threshold_case_ids_all_t,
n_timestep_processes=1,
early_stop_non_converging_threshold=0.1,
)
Run N-1 loadflow analysis with early stopping based on overload thresholds.
This function first runs loadflow analysis for the worst k contingencies, checking if overload energy exceeds the specified thresholds. If so, it stops further analysis and returns the results. If overload energy is within thresholds, it continues to run loadflow for non-critical contingencies.
This optimizes loadflow analysis by focusing on critical contingencies first, defined by the provided thresholds. Early stopping avoids unnecessary computations if overload energy exceeds the specified thresholds.
| PARAMETER | DESCRIPTION |
|---|---|
runners
|
Loadflow runner instances for each timestep.
TYPE:
|
strategy
|
AC optimization topologies for each timestep.
TYPE:
|
base_case_ids
|
Base case identifiers for each timestep. If None, N-0 analysis is skipped.
TYPE:
|
threshold_overload_all_t
|
Overload energy thresholds for early stopping at each timestep.
TYPE:
|
threshold_case_ids_all_t
|
Case IDs of critical contingencies for each timestep.
TYPE:
|
n_timestep_processes
|
Number of parallel processes for loadflow computation (default is 1).
TYPE:
|
early_stop_non_converging_threshold
|
The threshold for the early stopping criterion, i.e. if the percentage of non-converging cases is greater than this value, the ac validation will be stopped early.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
lfs
|
Concatenated loadflow results for critical and non-critical contingencies.
TYPE:
|
metrics
|
Computed metrics for the strategy and loadflow results.
TYPE:
|
Notes
- Early stopping is triggered if overload energy for any timestep exceeds the threshold.
- Critical contingencies are processed first; if early stopping is triggered, non-critical contingencies are skipped.
- Runner definitions are updated to focus on critical or non-critical contingencies as needed.
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | |
scoring_function
#
scoring_function(
strategy,
runners,
base_case_ids,
n_timestep_processes=1,
early_stop_validation=True,
early_stop_non_converging_threshold=0.1,
)
Compute loadflows and metrics for a given strategy
| PARAMETER | DESCRIPTION |
|---|---|
strategy
|
The strategy to score, length n_timesteps
TYPE:
|
runners
|
The loadflow runners to use, length n_timesteps.
TYPE:
|
base_case_ids
|
The base case ids for the loadflow runners, length n_timesteps (used to separately compute the N-0 flows)
TYPE:
|
n_timestep_processes
|
The number of processes to use for computing timesteps in parallel, by default 1
TYPE:
|
early_stop_validation
|
Whether to enable early stopping during the optimization process, by default True
TYPE:
|
early_stop_non_converging_threshold
|
The threshold for the early stopping criterion, i.e. if the percentage of non-converging cases is greater than this value, the ac validation will be stopped early.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
LoadflowResultsPolars
|
The loadflow results for the strategy |
list[Metrics]
|
The metrics for the strategy |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
compute_metrics
#
Compute the metrics for a given strategy. Just calls compute_metrics_single_timestep for each timestep
| PARAMETER | DESCRIPTION |
|---|---|
strategy
|
The strategy to score, length n_timesteps
TYPE:
|
lfs
|
The loadflow results for the strategy, length n_timesteps
TYPE:
|
additional_info
|
Additional information about the actions taken, such as switching distance or other metrics. The length of the list is n_timesteps.
TYPE:
|
base_case_ids
|
The base case ids for the loadflow runners, length n_timesteps (used to separately compute the N-0 flows)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[Metrics]
|
The metrics for the strategy, length n_timesteps |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
extract_switching_distance
#
Extract the switching distance from the additional action info
| PARAMETER | DESCRIPTION |
|---|---|
additional_info
|
The additional action info containing the switching distance
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
int
|
The switching distance, or 0 if not available |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
compute_metrics_single_timestep
#
compute_metrics_single_timestep(
actions,
disconnections,
loadflow,
additional_info,
base_case_id=None,
)
Compute the metrics for a single timestep
| PARAMETER | DESCRIPTION |
|---|---|
actions
|
The reconfiguration assignment for the timestep
TYPE:
|
disconnections
|
The disconnections for the timestep
TYPE:
|
loadflow
|
The loadflow results for the timestep, use select_timestep to get the results for a specific timestep
TYPE:
|
additional_info
|
Additional information about the actions taken, such as switching distance or other metrics.
TYPE:
|
base_case_id
|
The base case id from the nminus1 definition, to separate N-0 flows from N-1
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Metrics
|
The metrics for the timestep |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
compute_loadflow
#
Compute the loadflow for a given strategy
| PARAMETER | DESCRIPTION |
|---|---|
actions
|
The reconfiguration actions for each timestep, where the outer list is the timestep dimension and the inner list the split substation identified through an index into the action set.
TYPE:
|
disconnections
|
The disconnections for each timestep, where the outer list is the timestep dimension and the inner list the disconnection indices
TYPE:
|
pst_setpoints
|
The PST setpoints for each timestep, where the outer list is the timestep dimension and the inner list the PST taps if computed.
TYPE:
|
runners
|
The loadflow runners to use
TYPE:
|
n_timestep_processes
|
The number of processes to use for each timestep
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
LoadflowResultsPolars
|
The loadflow results for all timesteps in the strategy |
list[AdditionalActionInfo]
|
Additional information about the actions taken, such as switching distance or other metrics. The length of the list is n_timesteps. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
evaluate_acceptance
#
evaluate_acceptance(
loadflow_results_split,
metrics_split,
loadflow_results_unsplit,
metrics_unsplit,
reject_convergence_threshold=1.0,
reject_overload_threshold=0.95,
reject_critical_branch_threshold=1.1,
)
Evaluate if the split loadflow results are acceptable compared to the unsplit results.
Compares the unsplit metrics * the thresholds to the split metrics. If all split metrics are better than the unsplit metrics * thresholds, the split results are accepted.
Checked metrics are: non_converging_loadflows: the number of non-converging loadflows should be less than or equal to reject_convergence_threshold * unsplit[non_converging_loadflows] overload_energy_n_1: the overload energy should be less than or equal to reject_overload_threshold * unsplit[overload_energy_n_1] critical_branch_count_n_1: the number of critical branches should be less than or equal to reject_critical_branch_threshold * unsplit[critical_branch_count_n_1] TODO: Check Voltage Jumps between N0 and N1
| PARAMETER | DESCRIPTION |
|---|---|
loadflow_results_split
|
The loadflow results for the split case.
TYPE:
|
metrics_split
|
The metrics for the split case.
TYPE:
|
loadflow_results_unsplit
|
The loadflow results for the unsplit case.
TYPE:
|
metrics_unsplit
|
The metrics for the unsplit case.
TYPE:
|
reject_convergence_threshold
|
The threshold for the convergence rate, by default 1. (i.e. the split case must have at most the same amount of nonconverging loadflows as the unsplit case.)
TYPE:
|
reject_overload_threshold
|
The threshold for the overload energy improvement, by default 0.95 (i.e. the split case must have at least 5% lower overload energy than the unsplit case).
TYPE:
|
reject_critical_branch_threshold
|
The threshold for the critical branches increase, by default 1.1 (i.e. the split case must not have more than 110 % of the critical branches in the unsplit case).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
bool
|
True if the split results are acceptable, False if rejected. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/scoring_functions.py
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 | |
toop_engine_topology_optimizer.ac.select_strategy
#
Selection strategy for AC optimization topologies.
select_strategy
#
Select a promising strategy from the repertoire
Make sure the repertoire only contains topologies with the right optimizer type and optimization id as select_topology will not filter for this.
| PARAMETER | DESCRIPTION |
|---|---|
rng
|
The random number generator to use
TYPE:
|
repertoire
|
The filtered repertoire to select from
TYPE:
|
interest_scorer
|
The function to score the topologies in the repertoire. The higher the score, the more interesting the topology is. Eventually, the topology will be selected with a probability proportional to its score.
TYPE:
|
filter_strategy
|
Whether to filter the repertoire based on discriminator, median or dominator filter.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Union[list[ACOptimTopology], Tuple[list[ACOptimTopology], list[ACOptimTopology]]]
|
The selected strategy which is represented as a list of topologies with similar strategy_hash and optimizer type.. If two is True, a tuple of two lists is returned with two different strategy_hashes. If no strategy could be selected because the repertoire wasn't containing enough strategies, return an empty list |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/select_strategy.py
filter_metrics_df
#
Get a mask for the metrics DataFrame that filters out rows based on discriminator and median masks.
This function applies a discriminator, median and dominator mask.
| PARAMETER | DESCRIPTION |
|---|---|
metrics_df
|
The DataFrame containing the metrics to filter. This is typically the DC repertoire from which new results shall be pulled.
TYPE:
|
discriminator_df
|
The DataFrame containing the discriminator metrics. These are topologies that have previously been AC validated
TYPE:
|
filter_strategy
|
The filter strategy to use for the optimization, used to filter out strategies based on the discriminator, median or dominator filter.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
The filtered metrics DataFrame with similar topologies removed. If all topologies are filtered out, the original metrics DataFrame is returned. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/select_strategy.py
get_repertoire_filter_mask
#
Get a mask for the metrics DataFrame that filters out rows based on discriminator and median masks.
This function applies a discriminator, median and dominator mask.
| PARAMETER | DESCRIPTION |
|---|---|
metrics_df
|
The DataFrame containing the metrics to filter.
TYPE:
|
discriminator_df
|
The DataFrame containing the discriminator metrics.
TYPE:
|
filter_strategy
|
The filter strategy to use for the optimization, used to filter out strategies based on the discriminator, median or dominator filter.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Bool[ndarray, ' metrics_df.shape[0]']
|
A boolean mask where True indicates the row is not filtered out. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/select_strategy.py
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | |
get_median_mask
#
Get a mask for fitness values below the median for each discrete value of the target metrics.
Note: expects the target metrics to be discrete values, not continuous.
| PARAMETER | DESCRIPTION |
|---|---|
metrics_df
|
The DataFrame containing the metrics to filter.
TYPE:
|
target_metrics
|
A list of metrics with discrete values to consider for filtering. example: ["split_subs"].
TYPE:
|
fitness_col
|
The column name that contains the fitness values. Defaults to "fitness".
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
filter_mask
|
A boolean mask where True indicates the row is not below the median for any of the target metrics.
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/select_strategy.py
get_dominator_mask
#
Get a mask for rows from a DataFrame that are dominated by other rows based on specified metrics.
A metric entry if there is any other metric entry with a better fitness, in respect to the distance to the original topology. The distance in measured by the metric, assuming that lower values are better.
The target metric is used to fix the discrete value for which the dominance is checked. The fitness column is used to determine the fitness of the rows. Each observed metric is checked against the minimum fitness of all discrete target values.
Intended use is target_metrics = ["switching_distance", "split_subs"] and observed_metrics = Any additional metric to the target metrics, e.g. "disconnections"
| PARAMETER | DESCRIPTION |
|---|---|
metrics_df
|
The DataFrame to filter.
TYPE:
|
target_metrics
|
A list of metrics to consider for dominance. A target metric is expected to have discrete values (e.g. not fitness, overload_energy, or max_flow) If None, defaults to ["switching_distance", "split_subs"].
TYPE:
|
observed_metrics
|
A list of metrics to observe for dominance. If None, defaults to ["switching_distance", "split_subs"].
TYPE:
|
fitness_col
|
The column name that contains the fitness values. Defaults to "fitness". Note: the values are expected to be negative, best fitness converges to zero.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
filter_mask
|
A boolean mask where True indicates the row is not dominated by another row.
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/select_strategy.py
get_discriminator_mask
#
Get a mask for rows in metrics_df that are within a certain distance from the discriminator_df.
The distance is defined by the metric_distances dictionary, which contains the metrics and their respective distances.
Use the use_split_sub_multiplier flag to apply a multiplier in respect to the split_subs_col.
e.g. use_split_sub_multiplier=False, the metric_distances is applied directly to the metrics_df.
If use_split_sub_multiplier=True, the metric_distances are multiplied by the split_subs, leading to a
larger distance for larger split_subs values.
| PARAMETER | DESCRIPTION |
|---|---|
metrics_df
|
The DataFrame containing the metrics to filter.
TYPE:
|
discriminator_df
|
The DataFrame containing the discriminator metrics.
TYPE:
|
metric_distances
|
A dictionary defining the metric distances for filtering. The keys are metric names and the values are sets of distances. example: metric_distances = { "split_subs": {0}, "switching_distance": {-0.9, 0.9}, "fitness": {-0.1, 0.1}, } Note: the fitness is treated as a percentage.
TYPE:
|
metric_multiplier
|
A dictionary defining multiplier for the metric distances.
The keys are metric names and the values are sets of distances.
If None, defaults to an empty dictionary.
Multiple values are added by:
distance_multiplier = (
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
filter_mask
|
A boolean mask where True indicates the row is not within the distance defined by the discriminator_df and metric_distances.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If not all metric distances are present in the discriminator DataFrame columns. If the metric_distances is None, it will use default values based on the use_split_sub_multiplier flag. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/select_strategy.py
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 | |
get_discriminator_df
#
Get a discriminator DataFrame from the metrics DataFrame.
The discriminator DataFrame is a subset of the metrics DataFrame that contains only the target metrics. It is used to filter out similar topologies from the metrics DataFrame.
| PARAMETER | DESCRIPTION |
|---|---|
metrics_df
|
The DataFrame containing the metrics to filter. Note: expects the metrics_df to contain only AC topologies that have as a metric the "fitness_dc" column.
TYPE:
|
target_metrics
|
A list of metrics to consider for filtering.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
A DataFrame containing only the target metrics. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/select_strategy.py
toop_engine_topology_optimizer.ac.storage
#
The database models to store topologies in the AC optimizer
ACOptimTopology
#
Bases: BaseDBTopology
Inherits from the base topology to make a database table for AC optimizer topologies
This can include both AC and DC topologies, with the specific needs of the AC optimizer
parent_id
class-attribute
instance-attribute
#
The mutation parent id, i.e. the topology that this topology was mutated from. This is mostly for debugging purposes to see where a topology came from and understand the mutation process.
acceptance
class-attribute
instance-attribute
#
Whether the strategy was accepted or not.
id
class-attribute
instance-attribute
#
The table primary key
actions
class-attribute
instance-attribute
#
The branch/injection reconfiguration actions as indices into the action set.
disconnections
class-attribute
instance-attribute
#
A list of disconnections, indexing into the disconnectable branches set in the action set.
pst_setpoints
class-attribute
instance-attribute
#
The setpoints for the PSTs if they have been computed. This is an index into the range of pst taps, i.e. the smallest tap is 0 and the neutral tap somewhere in the middle of the range. The tap range is defined in the action set. The list always has the same length, i.e. the number of controllable PSTs in the system, and each entry corresponds to the PST at the same position in the action set.
unsplit
instance-attribute
#
Whether all topologies in the strategy including this one have no branch assignments, disconnections or injections.
strategy_hash
instance-attribute
#
The hash of the strategy - this hashes actions, disconnections and pst_setpoints for all timesteps in the strategy, making it possible to form a unique constraint on the strategy. This value will be set to the same for all topologies in the same strategy, furthermore making it possible to group timesteps.
metrics
class-attribute
instance-attribute
#
The metrics of this topology
worst_k_contingency_cases
class-attribute
instance-attribute
#
The worst k contingency case IDs for the topology.
created_at
class-attribute
instance-attribute
#
The time the topology was recorded in the database
stored_loadflow_reference
class-attribute
instance-attribute
#
The file reference for the loadflow results of this topology/strategy, if they were computed. Multiple topologies belonging to the same strategy will have the same serialized loadflow results object as there is a timestep notion in the loadflow results. To obtain the correct loadflow results, use the timestep attribute. This is stored as a json serialized StoredLoadflowReference object
__table_args__
class-attribute
instance-attribute
#
__table_args__ = (
UniqueConstraint(
"optimization_id",
"optimizer_type",
"strategy_hash",
"timestep",
name="topo_unique",
),
)
get_loadflow_reference
#
Get the loadflow reference as a StoredLoadflowReference object
| RETURNS | DESCRIPTION |
|---|---|
Optional[StoredLoadflowReference]
|
The loadflow reference, or None if it is not set |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/interfaces/models/base_storage.py
set_loadflow_reference
#
Set the loadflow reference from a StoredLoadflowReference object
| PARAMETER | DESCRIPTION |
|---|---|
loadflow_reference
|
The loadflow reference to set, or None to unset it
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/interfaces/models/base_storage.py
convert_single_topology
#
convert_single_topology(
topology,
optimization_id,
optimizer_type,
timestep,
strategy_hash,
unsplit,
)
Convert a single Topology to a ACOptimTopology
| PARAMETER | DESCRIPTION |
|---|---|
topology
|
The topology to convert
TYPE:
|
optimization_id
|
The optimization ID to assign to the db topology
TYPE:
|
optimizer_type
|
The optimizer type to assign to the db topology
TYPE:
|
timestep
|
The timestep of the topology
TYPE:
|
strategy_hash
|
The hash of the strategy computed through hash_strategy
TYPE:
|
unsplit
|
Whether the strategy is unsplit
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ACOptimTopology
|
The converted topology |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/storage.py
convert_message_topo_to_db_topo
#
Convert a TopologyPushResult to a list of ACOptimTopology
| PARAMETER | DESCRIPTION |
|---|---|
message_strategies
|
The strategies to convert, usually from a TopologyPushResult or OptimizationStartedResult. Strategies are flattened into the list of topologies that are being returned.
TYPE:
|
optimization_id
|
The optimization ID to assign to the topologies. This was sent through with the parent result message and is not part of TopologyPushResult
TYPE:
|
optimizer_type
|
The optimizer type to assign. This was sent through with the parent result message and is not part of TopologyPushResult
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[ACOptimTopology]
|
A list of converted topologies where for each topology and for each timestep a new ACOptimTopology instance is created. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/storage.py
create_session
#
Create an in-memory SQLite session with the ACOptimTopology table created.
| RETURNS | DESCRIPTION |
|---|---|
Session
|
The created session with the in-memory SQLite database |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/storage.py
scrub_db
#
Scrub the database of the worker to prevent memory issues.
The AC worker has to store topologies from all runs, not only the current one, as it might have to pick up an optimization later. Meaning, the AC worker busily collects all topologies into memory. To prevent memory leak issues, we scrub the database of topologies that are older than a day as we do not expect to ever have to go back to those.
| PARAMETER | DESCRIPTION |
|---|---|
session
|
The database session. The session object will be modified in-place
TYPE:
|
max_age_seconds
|
The maximum age of topologies to keep in the database, in seconds. Topologies older than this will be removed.
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/storage.py
toop_engine_topology_optimizer.ac.worker
#
The AC worker that listens to the kafka topics, organizes optimization runs, etc.
Args
#
Bases: Args
Command line arguments for the AC worker.
Mostly the same as the DC worker except for an additional loadflow results folder
kafka_broker
class-attribute
instance-attribute
#
The Kafka broker to connect to.
optimizer_command_topic
class-attribute
instance-attribute
#
The Kafka topic to listen for commands on.
optimizer_results_topic
class-attribute
instance-attribute
#
The topic to push results to.
optimizer_heartbeat_topic
class-attribute
instance-attribute
#
The topic to push heartbeats to.
heartbeat_interval_ms
class-attribute
instance-attribute
#
The interval in milliseconds to send heartbeats.
WorkerData
dataclass
#
Data that is stored across optimization runs
command_consumer
instance-attribute
#
A kafka consumer listening in for optimization commands
result_consumer
instance-attribute
#
A kafka consumer listening on the results topic, constantly writing results to the database. This is polled both during the optimization and idle loop to keep the database up to date.
optimization_loop
#
optimization_loop(
ac_params,
grid_files,
worker_data,
send_result_fn,
send_heartbeat_fn,
optimization_id,
loadflow_result_fs,
processed_gridfile_fs,
)
Run the main loop for the AC optimizer.
This function will run the AC optimizer on the given grid files with the given parameters.
| PARAMETER | DESCRIPTION |
|---|---|
ac_params
|
The parameters for the AC optimizer
TYPE:
|
grid_files
|
The grid files to optimize on
TYPE:
|
worker_data
|
The dataclass with the results consumer and database
TYPE:
|
send_result_fn
|
The function to send results
TYPE:
|
send_heartbeat_fn
|
The function to send heartbeats
TYPE:
|
optimization_id
|
The ID of the optimization run
TYPE:
|
loadflow_result_fs
|
A filesystem where the loadflow results are stored. Loadflows will be stored here using the uuid generation process and passed as a StoredLoadflowReference which contains the subfolder in this filesystem.
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/worker.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
idle_loop
#
Run idle loop of the AC optimizer worker.
This will be running when the worker is currently not optimizing This will wait until a StartOptimizationCommand is received and return it. In case a ShutdownCommand is received, the worker will exit with the exit code provided in the command.
| PARAMETER | DESCRIPTION |
|---|---|
worker_data
|
The dataclass with the command consumer, results consumer and database
TYPE:
|
send_heartbeat_fn
|
A function to call when there were no messages received for a while.
TYPE:
|
heartbeat_interval_ms
|
The time to wait for a new command in milliseconds. If no command has been received, a heartbeat will be sent and then the receiver will wait for commands again.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
StartOptimizationCommand
|
The start optimization command to start the optimization run with |
| RAISES | DESCRIPTION |
|---|---|
SystemExit
|
If a ShutdownCommand is received |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/worker.py
main
#
main(
args,
loadflow_result_fs,
processed_gridfile_fs,
producer,
command_consumer,
result_consumer,
)
Run the main AC worker loop.
| PARAMETER | DESCRIPTION |
|---|---|
args
|
The command line arguments
TYPE:
|
loadflow_result_fs
|
A filesystem where the loadflow results are stored. Loadflows will be stored here using the uuid generation process and passed as a StoredLoadflowReference which contains the subfolder in this filesystem.
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
producer
|
A kafka producer to send heartbeats and results
TYPE:
|
command_consumer
|
A kafka consumer listening in for optimization commands
TYPE:
|
result_consumer
|
A kafka consumer listening in for results
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
SystemExit
|
If the worker receives a ShutdownCommand |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/ac/worker.py
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 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 | |
Benchmarks#
toop_engine_topology_optimizer.benchmark.benchmark
#
Run benchmark.
This module provides functionality to run benchmark tasks based on a provided configuration.
It supports grid search for sweeping through the values of number of CUDA devices or a single parameter
not defined inside lf_config and ga_config. Multiple parameter sweeps are not supported yet.
| FUNCTION | DESCRIPTION |
|---|---|
main |
|
main
#
Run benchmark tasks based on the provided configuration.
| PARAMETER | DESCRIPTION |
|---|---|
cfg
|
Configuration object containing benchmark tasks and output settings.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
None
|
|
Notes
The function performs the following steps: 1. Iterates over the benchmark tasks specified in the configuration. 2. Checks for grid search configurations and generates new configurations for each value in the grid. 3. Sets environment variables for each benchmark task. 4. Runs each benchmark task in a separate process to avoid side-effects. 5. Collects the results and logs them into a JSON file specified in the configuration.
Grid search currently supports sweeping through the values of number of CUDA devices or
a single parameter not defined inside lf_config and ga_config. Multiple parameter sweeps are not supported yet.
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/benchmark/benchmark.py
DC Optimizer#
toop_engine_topology_optimizer.dc.ga_helpers
#
Helper functions for genetic algorithms.
TrackingMixingEmitter
#
Bases: MixingEmitter
A MixingEmitter that tracks the number of branch and injection combinations and splits.
init
#
Overwrite the Emitter.init function to seed an EmitterState.
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/ga_helpers.py
state_update
#
Overwrite the state update to store information for the running means.
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/ga_helpers.py
RunningMeans
dataclass
#
RunningMeans(
start_time,
last_time,
time_step,
total_branch_combis,
total_inj_combis,
br_per_sec,
inj_per_sec,
split_per_iter,
last_emitter_state,
n_outages,
n_devices,
)
A dataclass to hold the running means estimations for the TQDM progress bar.
init_running_means
#
Initialize an empty RunningMeans object.
| PARAMETER | DESCRIPTION |
|---|---|
n_outages
|
The number of outages
TYPE:
|
n_devices
|
The number of devices
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
RunningMeans
|
The initialized running means |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/ga_helpers.py
update_running_means
#
Aggregate the emitter state statistics into the running means.
| PARAMETER | DESCRIPTION |
|---|---|
running_means
|
The running means to be updated
TYPE:
|
emitter_state
|
The emitter state of the current iteration
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
RunningMeans
|
The updated running means |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/ga_helpers.py
make_description_string
#
Create a string representation of the running means for the command line tqdm output.
| PARAMETER | DESCRIPTION |
|---|---|
running_means
|
The running means to be displayed
TYPE:
|
runtime_seconds
|
The total runtime of the optimization in seconds
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
str
|
The string representation of the running means |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/ga_helpers.py
toop_engine_topology_optimizer.dc.main
#
Launcher for the Map-Elites optimizer.
example args:
--fixed_files /workspaces/AICoE_HPC_RL_Optimizer/data/static_information.hdf5 --stats_dir /workspaces/AICoE_HPC_RL_Optimizer/stats/ --tensorboard_dir /workspaces/AICoE_HPC_RL_Optimizer/stats/tensorboard/ --ga_config.target_metrics overload_energy_n_1 1.0 # The metrics to optimize with their weight --ga_config.me_descriptors.0.metric split_subs --ga_config.me_descriptors.0.num_cells 5 --ga_config.me_descriptors.1.metric switching_distance --ga_config.me_descriptors.1.num_cells 45 # The metrics to use as descriptors along with their maximum value --ga_config.observed_metrics overload_energy_n_1 split_subs switching_distance # All the relevant metrics, including target, descriptors and extra metrics you want to see in the report --ga_config.substation_unsplit_prob 0.5 # Instinctively better suited for mapelites --ga_config.substation_split_prob 0.5 # Instinctively better suited for mapelites --ga_config.plot # Enable plot generation and saving in stats_dir/plots/ --ga_config.iterations_per_epoch 50 # Basically how often you wanna get a report. Suggested range : 50 - 1000 --ga_config.runtime_seconds 300 # How many seconds to run the optimization for The first descriptor metric is the vertical axis, the second the horizontal axis.
CLIArgs
#
Bases: BaseModel
The arguments for a CLI invocation, mostly equal to OptimizationStartCommand.
ga_config
class-attribute
instance-attribute
#
ga_config = Field(default_factory=BatchedMEParameters)
The configuration for the genetic algorithm
lf_config
class-attribute
instance-attribute
#
lf_config = Field(default_factory=LoadflowSolverParameters)
The configuration for the loadflow solver
fixed_files
class-attribute
instance-attribute
#
The file containing the static information. You can pass multiple files here
tensorboard_dir
class-attribute
instance-attribute
#
The directory to store the tensorboard logs
stats_dir
class-attribute
instance-attribute
#
The directory to store the json summaries
summary_frequency
class-attribute
instance-attribute
#
How often to write tensorboard summaries
checkpoint_frequency
class-attribute
instance-attribute
#
How often to write json summaries
double_limits
class-attribute
instance-attribute
#
The double limits to use in the form (lower_limit, upper_limit). lower_limit: float The relative lower limit to set, for branches whose n-1 flows are below the lower limit upper_limit: float The relative upper_limit determining at what relative load a branch is considered overloaded. Branches in the band between lower and upper limit are considered overloaded if more load is added.
log_tensorboard
#
Log fitness and metrics to tensorboard.
| PARAMETER | DESCRIPTION |
|---|---|
fitness
|
The fitness value
TYPE:
|
metrics
|
The metrics to log
TYPE:
|
iteration
|
The current iteration number
TYPE:
|
writer
|
The tensorboard writer
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/main.py
write_summary
#
write_summary(
optimizer_data,
repertoire,
emitter_state,
iteration,
folder,
cli_args,
processed_gridfile_fs,
final_results=False,
)
Write a summary to a json file.
Watch out : here, the optimizer data is out of sync with the jax_data
| PARAMETER | DESCRIPTION |
|---|---|
optimizer_data
|
The optimizer data of the optimization. This includes a jax_data, however as the jax_data is updated per-iteration and the optimizer_data only per-epoch, we need to pass the jax_data separately
TYPE:
|
repertoire
|
The current repertoire for this iteration, will be used instead of the one in the optimizer_data |
emitter_state
|
The emitter state for this iteration, will be used instead of the one in the optimizer_data
TYPE:
|
iteration
|
The iteration number
TYPE:
|
folder
|
The folder to write the summary to, relative to the processed_gridfile_fs
TYPE:
|
cli_args
|
The arguments used for invocation, will be added to the summary for documentation purposes
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
final_results
|
Whether this is the final results summary "res.json" or an intermediate one "res_{iteration}.json"
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict
|
The summary that was written |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/main.py
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | |
main
#
Run main optimization function for CLI execution.
| PARAMETER | DESCRIPTION |
|---|---|
args
|
The arguments for the optimization
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict
|
The final results of the optimization |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/main.py
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 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 | |
Genetic Algorithm Functions#
toop_engine_topology_optimizer.dc.genetic_functions.evolution_functions
#
Contains the genetic operations for the topologies.
This module contains the functions to perform the genetic operations of mutation and crossover on the topologies of the grid. The topologies are represented as Genotype dataclasses, which contain the substation ids, the branch topology, the injection topology and the disconnections.
Genotype
#
A single genome in the repertoire representing a topology.
disconnections
instance-attribute
#
The disconnections to apply, padded with int_max for disconnection slots that are unused. These are indices into the disconnectable branches set.
nodal_injections_optimized
instance-attribute
#
The results of the nodal injection optimization, if any was performed.
deduplicate_genotypes
#
Deduplicate the genotypes in the repertoire.
This version is jittable because we set the size
| PARAMETER | DESCRIPTION |
|---|---|
genotypes
|
The genotypes to deduplicate
TYPE:
|
desired_size
|
How many unique values you are expecting. If not given, this is not jittable
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Genotype
|
The deduplicated genotypes |
Int[Array, ' n_unique']
|
The indices of the unique genotypes |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
fix_dtypes
#
Fix the dtypes of the genotypes to their native type.
For some reason, qdax aggressively converts everything to float
| PARAMETER | DESCRIPTION |
|---|---|
genotypes
|
The genotypes to fix
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Genotype
|
The genotypes with fixed dtypes |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
empty_repertoire
#
empty_repertoire(
batch_size,
max_num_splits,
max_num_disconnections,
n_timesteps,
starting_taps=None,
)
Create an initial genotype repertoire with all zeros for all entries and int_max for all subs.
| PARAMETER | DESCRIPTION |
|---|---|
batch_size
|
The batch size
TYPE:
|
max_num_splits
|
The maximum number of splits per topology
TYPE:
|
max_num_disconnections
|
The maximum number of disconnections as topological measures per topology
TYPE:
|
n_timesteps
|
The number of timesteps in the optimization horizon, used for the nodal injection optimization results
TYPE:
|
starting_taps
|
The starting taps for the psts. If None, no nodal inj optimization will be enabled and nodal_injections_optimized will be set to None. If provided, nodal_injections_optimized will be initialized with these starting taps.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Genotype
|
The initial genotype |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
mutate
#
mutate(
topologies,
random_key,
substation_split_prob,
substation_unsplit_prob,
action_set,
n_disconnectable_branches,
n_subs_mutated_lambda,
disconnect_prob,
reconnect_prob,
pst_mutation_sigma,
pst_n_taps,
mutation_repetition=1,
)
Mutate the topologies by splitting substations and changing the branch and injection topos.
Makes sure that at all times, a substation is split at most once and that all branch and injection actions are in range of the available actions for the substation. If a substation is not split, this is indicated by the value int_max in the substation, branch and injection.
We mutate mutation_repetition copies of the initial repertoire to increase the chance of getting unique mutations.
| PARAMETER | DESCRIPTION |
|---|---|
topologies
|
The topologies to mutate
TYPE:
|
random_key
|
The random key to use for the mutation
TYPE:
|
substation_split_prob
|
The probability to split a substation. In case all substations are already split, this probability is ignored
TYPE:
|
substation_unsplit_prob
|
The probability to reset a substation to the unsplit state
TYPE:
|
action_set
|
The action set containing available actions on a per-substation basis.
TYPE:
|
n_disconnectable_branches
|
The number of disconnectable branches in the action set.
TYPE:
|
n_subs_mutated_lambda
|
The lambda for the poisson distribution to determine the number of substations to mutate
TYPE:
|
disconnect_prob
|
The probability to disconnect a new branch
TYPE:
|
reconnect_prob
|
The probability to reconnect a disconnected branch, will overwrite a possible disconnect
TYPE:
|
pst_mutation_sigma
|
The sigma to use for the normal distribution to sample the PST tap mutation from.
TYPE:
|
pst_n_taps
|
The number of taps for each PST, from nodal_injection_information.pst_n_taps.
TYPE:
|
mutation_repetition
|
More chance to get unique mutations by mutating mutation_repetition copies of the repertoire
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Genotype
|
The mutated topologies |
PRNGKey
|
The new random key |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | |
mutate_sub_id
#
Mutate the substation ids of a single topology.
| PARAMETER | DESCRIPTION |
|---|---|
random_key
|
The random key to use for the mutation
TYPE:
|
sub_ids
|
The substation ids before mutation
TYPE:
|
n_subs_rel
|
The number of relevant substations in the grid
TYPE:
|
substation_split_prob
|
The probability to split a substation
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Int[Array, ' max_num_splits']
|
The mutated substation ids |
Int[Array, ' ']
|
The index of the substation that was mutated. If no substation was mutated, this is set to a random substation that was already split, so branch and injection mutations can still happen despite no new split. If no substation was split yet and no split happened, this is set to int_max |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
mutate_disconnections
#
mutate_disconnections(
random_key,
disconnections,
n_disconnectable_branches,
disconnect_prob,
reconnect_prob,
)
Mutate the disconnections of a single topology.
| PARAMETER | DESCRIPTION |
|---|---|
random_key
|
The random key to use for the mutation
TYPE:
|
disconnections
|
The disconnections before mutation of one individual
TYPE:
|
n_disconnectable_branches
|
The number of disconnectable branches in the action set. It is not necessary to know the contents of the action set here because we only sample an index into the action set.
TYPE:
|
disconnect_prob
|
The probability to disconnect a new branch
TYPE:
|
reconnect_prob
|
The probability to reconnect a disconnected branch, will overwrite a possible disconnect
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Int[Array, ' max_num_disconnections']
|
The mutated disconnections |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
mutate_sub
#
mutate_sub(
sub_ids,
action,
random_key,
substation_split_prob,
substation_unsplit_prob,
action_set,
)
Mutate a single substation, changing the sub_ids, branch and inj topos.
The sub-ids are implicit to the branch topo action index, however we pass them explicitely to aid substation mutation.
| PARAMETER | DESCRIPTION |
|---|---|
sub_ids
|
The substation ids before mutation
TYPE:
|
action
|
The branch topology before mutation
TYPE:
|
random_key
|
The random key to use for the mutation
TYPE:
|
substation_split_prob
|
The probability to split a substation
TYPE:
|
substation_unsplit_prob
|
The probability to reset a split substation to the unsplit state
TYPE:
|
action_set
|
The actions for every substation. If not provided, will sample from all possible actions per substation
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
sub_ids
|
The substation ids after mutation
TYPE:
|
action
|
The branch topology after mutation
TYPE:
|
random_key
|
The random key used for the mutation
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | |
mutate_nodal_injections
#
Mutate the nodal injection optimization results, currently only the PST taps.
| PARAMETER | DESCRIPTION |
|---|---|
random_key
|
The random key to use for the mutation
TYPE:
|
nodal_inj_info
|
The nodal injection optimization results before mutation. If None, no mutation is performed and None is returned.
TYPE:
|
pst_mutation_sigma
|
The sigma to use for the normal distribution to sample the mutation from. The mutation will be sampled as an integer from a normal distribution with mean 0 and sigma pst_mutation_sigma.
TYPE:
|
pst_n_taps
|
The number of taps for each PST. If a PST has N taps in this array, then it is assumed that all taps from 0 to N-1 are valid tap positions. Output taps will be clipped to this range.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Optional[NodalInjOptimResults]
|
The mutated nodal injection optimization results. If nodal_inj_info was None, returns None. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
mutate_psts
#
Mutate the PST taps of a single topology.
| PARAMETER | DESCRIPTION |
|---|---|
random_key
|
The random key to use for the mutation
TYPE:
|
pst_taps
|
The PST tap positions before mutation
TYPE:
|
pst_n_taps
|
The number of taps for each PST. If a PST has N taps in this array, then it is assumed that all taps from 0 to N-1 are valid tap positions. Output taps will be clipped to this range.
TYPE:
|
pst_mutation_sigma
|
The sigma to use for the normal distribution to sample the mutation from. The mutation will be sampled as an integer from a normal distribution with mean 0 and sigma pst_mutation_sigma.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Int[Array, ' num_psts']
|
The mutated PST tap positions, clipped to the valid range of taps for each PST. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
sample_unique_from_array
#
Sample n unique elements from an array (returning indices), counting int_max as always unique.
| PARAMETER | DESCRIPTION |
|---|---|
random_key
|
The random key to use for the sampling
TYPE:
|
sample_pool
|
The array to sample from. Only unique elements are sampled, however int_max entries are not checked for uniqueness
TYPE:
|
sample_probs
|
The probabilities to sample each element
TYPE:
|
n_samples
|
The number of samples to take, should be less than the number of non-int_max entries in array.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Int[Array, ' n_samples']
|
The sampled indices into sample_pool |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
crossover_unbatched
#
Crossover two topologies while making sure that no substation is present twice.
This version is unbatched, i.e. it only works on a single topology. Use crossover for batched inputs.
| PARAMETER | DESCRIPTION |
|---|---|
topologies_a
|
The first topology
TYPE:
|
topologies_b
|
The second topology
TYPE:
|
random_key
|
The random key to use for the crossover
TYPE:
|
action_set
|
The branch action set containing available actions on a per-substation basis. This is needed to resolve the sub_ids for the branch actions
TYPE:
|
prob_take_a
|
The probability to take the value from topology_a, otherwise the value from topology_b is taken
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Genotype
|
The new topology |
PRNGKey
|
The new random key |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | |
crossover
#
Crossover two topologies while making sure that no substation is present twice.
| PARAMETER | DESCRIPTION |
|---|---|
topologies_a
|
The first topology
TYPE:
|
topologies_b
|
The second topology
TYPE:
|
random_key
|
The random key to use for the crossover
TYPE:
|
action_set
|
The branch action set containing available actions on a per-substation basis.
TYPE:
|
prob_take_a
|
The probability to take the value from topology_a, otherwise the value from topology_b is taken
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Genotype
|
The new topology |
PRNGKey
|
The new random key |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
unsplit_substation
#
Reset a split substation to the unsplit state, with a certain probability.
| PARAMETER | DESCRIPTION |
|---|---|
random_key
|
The random key to use for the reset
TYPE:
|
sub_ids
|
The substation ids before the reset
TYPE:
|
action
|
The branch/inj topology action before the reset
TYPE:
|
split_idx
|
The index of the substation to reset
TYPE:
|
substation_unsplit_prob
|
The probability to reset the substation to the unsplit state
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Int[Array, ' max_num_splits']
|
The substation ids after the reset. If a reset occurred, the substation id at split_idx will be set to int_max |
Int[Array, ' max_num_splits']
|
The topology action after the reset. If a reset occurred, the topology at split_idx will be set to int_max |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/evolution_functions.py
toop_engine_topology_optimizer.dc.genetic_functions.initialization
#
Initialization of the genetic algorithm for branch and injection choice optimization.
JaxOptimizerData
#
The part of the optimizer data that lives on GPU.
If distributed is enabled, every item will have a leading device dimension.
update_max_mw_flows_according_to_double_limits
#
update_max_mw_flows_according_to_double_limits(
dynamic_informations,
solver_configs,
lower_limit,
upper_limit,
)
Update all dynamic informations max mw loads.
Runs an initial n-1 analysis to determine limits in mw.
| PARAMETER | DESCRIPTION |
|---|---|
dynamic_informations
|
List of static informations to calculate with max_mw_flow limits set at 1.0
TYPE:
|
solver_configs
|
List of solver configurations to use for the loadflow
TYPE:
|
lower_limit
|
The relative lower limit to set, for branches whose n-1 flows are below the lower limit
TYPE:
|
upper_limit
|
The relative upper_limit determining at what relative load a branch is considered overloaded. Branches in the band between lower and upper limit are considered overloaded if more load is added.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple[DynamicInformation, ...]
|
The updated dynamic informations with new limits set. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/initialization.py
verify_static_information
#
Verify the static information.
This function will be called after loading the static information. It should be used to verify that the static information is correct and can be used for the optimization run.
| PARAMETER | DESCRIPTION |
|---|---|
static_informations
|
The static information to verify
TYPE:
|
max_num_disconnections
|
The maximum number of disconnections that can be made
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
AssertionError
|
If the static information is not correct |
| RETURNS | DESCRIPTION |
|---|---|
None
|
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/initialization.py
update_static_information
#
Perform any necessary preprocessing on the static information.
This harmonizes the static informations and makes sure some information that is optional in the solver is always there.
| PARAMETER | DESCRIPTION |
|---|---|
static_informations
|
The list of static informations to preprocess
TYPE:
|
batch_size
|
The batch size to use, will replace the batch size in the solver config
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[StaticInformation]
|
The updated static informations |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/initialization.py
initialize_genetic_algorithm
#
initialize_genetic_algorithm(
batch_size,
max_num_splits,
max_num_disconnections,
static_informations,
target_metrics,
substation_split_prob,
substation_unsplit_prob,
action_set,
n_subs_mutated_lambda,
disconnect_prob,
reconnect_prob,
pst_mutation_sigma,
proportion_crossover,
crossover_mutation_ratio,
random_seed,
observed_metrics,
me_descriptors,
distributed,
devices=None,
cell_depth=1,
mutation_repetition=1,
n_worst_contingencies=10,
)
Initialize the mapelites algorithm.
| PARAMETER | DESCRIPTION |
|---|---|
batch_size
|
The batch size to use
TYPE:
|
max_num_splits
|
The maximum number of substations that can be split
TYPE:
|
max_num_disconnections
|
The maximum number of disconnections that can be made
TYPE:
|
static_informations
|
The static information to use for the optimization run
TYPE:
|
target_metrics
|
The target metrics to use for the optimization run
TYPE:
|
substation_split_prob
|
The probability to split a substation
TYPE:
|
substation_unsplit_prob
|
The probability to reset a split substation to the unsplit state
TYPE:
|
action_set
|
The action set to use for mutations
TYPE:
|
n_subs_mutated_lambda
|
The lambda parameter for the Poisson distribution to determine the number of substations to mutate
TYPE:
|
disconnect_prob
|
The probability to disconnect a new branch
TYPE:
|
reconnect_prob
|
The probability to reconnect a disconnected branch, will overwrite a possible disconnect
TYPE:
|
pst_mutation_sigma
|
The sigma to use for the normal distribution to sample the PST tap mutation from.
TYPE:
|
proportion_crossover
|
The proportion of crossover to mutation
TYPE:
|
crossover_mutation_ratio
|
The ratio of crossover to mutation
TYPE:
|
random_seed
|
The random seed to use for reproducibility
TYPE:
|
observed_metrics
|
The observed metrics, i.e. which metrics are to be computed for logging purposes.
TYPE:
|
me_descriptors
|
The descriptors to use for map elites
TYPE:
|
distributed
|
Whether to run the optimization on multiple devices
TYPE:
|
devices
|
The devices to run the optimization on, if distributed
TYPE:
|
cell_depth
|
The cell depth to use if applicable
TYPE:
|
mutation_repetition
|
More chance to get unique mutations by mutating mutation_repetition copies of the repertoire
TYPE:
|
n_worst_contingencies
|
The number of worst contingencies to consider in the scoring function for calculating top_k_overloads_n_1.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DiscreteMapElites
|
The genetic algorithm object including scoring, mutate and crossover functions |
JaxOptimizerData
|
The initialized jax dataclass |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/initialization.py
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 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 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | |
flatten_fitnesses_if_distributed
#
Flatten the fitnesses if distributed.
| PARAMETER | DESCRIPTION |
|---|---|
fitnesses
|
The fitnesses to flatten
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Float[Array, ' individuals metrics']
|
The flattened fitnesses |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/initialization.py
get_repertoire_metrics
#
Get the metrics of the best individual in the Mapelites repertoire.
| PARAMETER | DESCRIPTION |
|---|---|
repertoire
|
The repertoire |
observed_metrics
|
The metrics to observe (max_flow_n_0, median_flow_n_0 ...)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float
|
The fitness |
dict[MetricType, float]
|
The metrics as defined in METRICS |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/initialization.py
algo_setup
#
Set up the genetic algorithm run.
| PARAMETER | DESCRIPTION |
|---|---|
ga_args
|
The genetic algorithm parameters
TYPE:
|
lf_args
|
The loadflow solver parameters
TYPE:
|
double_limits
|
The lower and upper limit for the relative max mw flow if double limits are used
TYPE:
|
static_information_files
|
A list of files with static information to load
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DiscreteMapElites
|
The initialized genetic algorithm object, can be used to update the optimization run |
JaxOptimizerData
|
The jax dataclass of all GPU data including dynamic information and the GA data |
tuple[SolverConfig, ...]
|
The solver configurations for every timestep (the dynamic information is part of the jax dataclass) |
float
|
The initial fitness, for logging purposes |
dict
|
The initial metrics, for logging purposes |
list[StaticInformationDescription]
|
Some statistics on the static information dataclasses that were loaded |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/initialization.py
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | |
toop_engine_topology_optimizer.dc.genetic_functions.scoring_functions
#
Create scoring functions for the genetic algorithm.
METRICS
module-attribute
#
METRICS = {
"max_flow_n_0": maximum,
"median_flow_n_0": maximum,
"overload_energy_n_0": add,
"overload_energy_limited_n_0": add,
"underload_energy_n_0": add,
"transport_n_0": add,
"exponential_overload_energy_n_0": add,
"exponential_overload_energy_limited_n_0": add,
"critical_branch_count_n_0": maximum,
"critical_branch_count_limited_n_0": maximum,
"max_flow_n_1": maximum,
"median_flow_n_1": maximum,
"overload_energy_n_1": add,
"overload_energy_limited_n_1": add,
"underload_energy_n_1": add,
"transport_n_1": add,
"exponential_overload_energy_n_1": add,
"exponential_overload_energy_limited_n_1": add,
"critical_branch_count_n_1": maximum,
"critical_branch_count_limited_n_1": maximum,
"n0_n1_delta": add,
"cross_coupler_flow": add,
"switching_distance": maximum,
"split_subs": maximum,
"n_2_penalty": add,
}
compute_overloads
#
compute_overloads(
topologies,
dynamic_information,
solver_config,
observed_metrics,
n_worst_contingencies=10,
)
Compute the overloads for a single timestep by invoking the solver and aggregating the results.
| PARAMETER | DESCRIPTION |
|---|---|
topologies
|
The topologies to score, where the first max_num_splits entries are the substations, the second max_num_splits entries are the branch topos and the last max_num_splits entries are the injection topos
TYPE:
|
dynamic_information
|
The dynamic information of the grid
TYPE:
|
solver_config
|
The static solver configuration
TYPE:
|
observed_metrics
|
The metrics to observe
TYPE:
|
n_worst_contingencies
|
The number of worst contingencies to return, by default 10
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Float[Array, ' batch_size']]
|
A dictionary with the overload energy, transport, max flow and other metrics, from aggregate_to_metric_batched |
NodalInjOptimResults
|
The results of the nodal injection optimization |
Bool[Array, ' batch_size']
|
Whether the topologies were successfully solved |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/scoring_functions.py
scoring_function
#
scoring_function(
topologies,
random_key,
dynamic_informations,
solver_configs,
target_metrics,
observed_metrics,
descriptor_metrics,
n_worst_contingencies=10,
)
Create scoring function for the genetic algorithm.
| PARAMETER | DESCRIPTION |
|---|---|
topologies
|
The topologies to score
TYPE:
|
random_key
|
The random key to use for the scoring (currently not used)
TYPE:
|
dynamic_informations
|
The dynamic information of the grid for every timestep
TYPE:
|
solver_configs
|
The solver configuration for every timestep
TYPE:
|
target_metrics
|
The list of metrics to optimize for with their weights
TYPE:
|
observed_metrics
|
The observed metrics
TYPE:
|
descriptor_metrics
|
The metrics to use as descriptors
TYPE:
|
n_worst_contingencies
|
The number of worst contingencies to consider for calculating top_k_overloads_n_1
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Float[Array, ' batch_size']
|
The metrics of the topologies |
Int[Array, ' batch_size n_dims']
|
The descriptors of the topologies |
dict
|
The extra scores |
dict
|
Emitter Information |
PRNGKey
|
The random key that was passed in, unused |
Genotype
|
The genotypes that were passed in, but updated to account for in-the-loop optimizations such as the nodal injection optimization. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/scoring_functions.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | |
translate_topology
#
Translate the topology into the format used by the solver.
| PARAMETER | DESCRIPTION |
|---|---|
topology
|
The topology in genotype form
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ActionIndexComputations
|
The topology computations |
Int[Array, ' batch_size max_num_disconnections']
|
The branch disconnections to apply |
NodalInjStartOptions | None
|
The nodal injection optimization start options containing pst taps, or None if no PST optimization |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/scoring_functions.py
filter_repo
#
Reduce the repertoire to only valid and deduplicated topologies.
This will not return any topologies that are worse than the initial fitness and deduplicate the topologies. The function is currently not jax jit compatible.
| PARAMETER | DESCRIPTION |
|---|---|
repertoire
|
The repertoire to reduce |
initial_fitness
|
The initial fitness of the grid. This is used to filter out topologies that are worse than this fitness.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DiscreteMapElitesRepertoire
|
The reduced repertoire with only valid and deduplicated topologies. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/scoring_functions.py
convert_to_topologies
#
Take a repertoire and convert it to a list of kafka-sendable topologies.
| PARAMETER | DESCRIPTION |
|---|---|
repertoire
|
The repertoire to convert. You might want to filter it using filter_repo first. |
contingency_ids
|
The contingency IDs for each topology
TYPE:
|
grid_model_low_tap
|
The lowest tap value in the grid model, used to convert the relative tap values in the genotype to absolute tap values that can be sent to the kafka topics. This will only be read if nodal_injection results are present in the genotype.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[Topology]
|
The list of topologies in the format used by the kafka topics. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/scoring_functions.py
summarize_repo
#
Summarize the repertoire into a list of topologies.
| PARAMETER | DESCRIPTION |
|---|---|
repertoire
|
The repertoire to summarize |
initial_fitness
|
The initial fitness of the grid
TYPE:
|
contingency_ids
|
The contingency IDs for each topology. Here we assume that this list is common for all the topologies in the repertoire. TODO: Fix me to have per topology contingency ids if needed
TYPE:
|
grid_model_low_tap
|
The lowest tap value in the grid model, from nodal_injection_information.grid_model_low_tap.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[Topology]
|
The summarized topologies |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/scoring_functions.py
summarize
#
summarize(
repertoire,
emitter_state,
initial_fitness,
initial_metrics,
contingency_ids,
grid_model_low_tap=None,
)
Summarize the repertoire and emitter state into a serializable dictionary.
| PARAMETER | DESCRIPTION |
|---|---|
repertoire
|
The repertoire to summarize |
emitter_state
|
The emitter state to summarize
TYPE:
|
initial_fitness
|
The initial fitness of the grid
TYPE:
|
initial_metrics
|
The initial metrics of the grid
TYPE:
|
contingency_ids
|
A list of contingency ids. Here we assume that the list of contingency ids is common for all the topologies
TYPE:
|
grid_model_low_tap
|
The lowest tap value in the grid model, from nodal_injection_information.grid_model_low_tap.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict
|
The summarized dictionary, json serializable |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/scoring_functions.py
Repertoire#
toop_engine_topology_optimizer.dc.repertoire.discrete_map_elites
#
Core components of the MAP-Elites algorithm.
Adapted from QDax (https://github.com/adaptive-intelligent-robotics/QDax)
DiscreteMapElites
#
DiscreteMapElites(
scoring_function,
emitter,
metrics_function,
n_cells_per_dim,
cell_depth=1,
distributed=False,
)
Discrete MAP-Elites algorithm.
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_map_elites.py
init
#
Initialize a Map-Elites repertoire with an initial population of genotypes.
Requires the definition of centroids that can be computed with any method such as CVT or Euclidean mapping.
Before the repertoire is initialised, individuals are gathered from all the devices.
Args: genotypes: initial genotypes, pytree in which leaves have shape (batch_size, num_features) random_key: a random key used for stochastic operations. n_cells_per_dim: number of cells per dimension in the repertoire
| RETURNS | DESCRIPTION |
|---|---|
An initialized MAP-Elite repertoire with the initial state of the emitter,
|
and a random key. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_map_elites.py
update
#
Perform one iteration of the MAP-Elites algorithm.
- A batch of genotypes is sampled in the repertoire and the genotypes are copied.
- The copies are mutated and crossed-over
- The obtained offsprings are scored and then added to the repertoire.
Before the repertoire is updated, individuals are gathered from all the devices.
Args: repertoire: the MAP-Elites repertoire emitter_state: state of the emitter random_key: a jax PRNG random key
| RETURNS | DESCRIPTION |
|---|---|
the updated MAP-Elites repertoire
|
the updated (if needed) emitter state metrics about the updated repertoire a new jax PRNG key |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_map_elites.py
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | |
toop_engine_topology_optimizer.dc.repertoire.discrete_me_repertoire
#
Contains the DiscreteMapElitesRepertoire class and utility functions.
This file contains util functions and a class to define a repertoire, used to store individuals in the MAP-Elites algorithm as well as several variants. Adapted from QDax (https://github.com/adaptive-intelligent-robotics/QDax)
DiscreteMapElitesRepertoire
#
A class to store the MAP-Elites repertoire.
genotypes
instance-attribute
#
The genotypes in the repertoire.
The PyTree can be a simple Jax array or a more complex nested structure such as to represent parameters of neural network in Flax.
descriptors
instance-attribute
#
The descriptors of solutions in each cell of the repertoire.
extra_scores
instance-attribute
#
The extra scores of solutions in each cell of the repertoire. Usually the metrics
cell_depth
class-attribute
instance-attribute
#
Each cell contains cell_depth unique individuals
sample
#
Sample elements in the repertoire.
| PARAMETER | DESCRIPTION |
|---|---|
random_key
|
the random key to be used for sampling
TYPE:
|
num_samples
|
the number of samples to be drawn from the repertoire
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
samples
|
the sampled genotypes
TYPE:
|
random_key
|
the updated random key
TYPE:
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_me_repertoire.py
__getitem__
#
Get a slice of the repertoire.
| PARAMETER | DESCRIPTION |
|---|---|
index
|
the index of the elements to be selected
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
a new repertoire with the selected elements
|
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_me_repertoire.py
get_cells_indices
#
Compute the index for many descriptors at once.
Args: descriptors: an array that contains the descriptors of the solutions. Its shape is (batch_size, num_descriptors) n_cells_per_dim: the number of cells per dimension
| RETURNS | DESCRIPTION |
|---|---|
The index of the cell in which each descriptor belongs.
|
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_me_repertoire.py
get_cell_index
#
Compute the index of the cell in which each descriptor belongs.
The index is effectively like the reshape operation, spreading multiple dimensions to a flat single dimension.
Args: descriptor: an array that contains the descriptors. There is one descriptor per dimension n_cells_per_dim: the number of cells per dimension
| RETURNS | DESCRIPTION |
|---|---|
The index of the cell in which each descriptor belongs.
|
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_me_repertoire.py
add_to_repertoire
#
add_to_repertoire(
repertoire,
batch_of_genotypes,
batch_of_descriptors,
batch_of_fitnesses,
batch_of_extra_scores=None,
)
Add a batch of elements to the repertoire.
Args: repertoire: the MAP-Elites repertoire to which the elements will be added. batch_of_genotypes: a batch of genotypes to be added to the repertoire. Similarly to the repertoire.genotypes argument, this is a PyTree in which the leaves have a shape (batch_size, num_features) batch_of_descriptors: an array that contains the descriptors of the aforementioned genotypes. Its shape is (batch_size, num_descriptors). This will determine the cell in which the genotype will be stored, all descriptors are clipped to be within the bounds of n_cells_per_dim. batch_of_fitnesses: an array that contains the fitnesses of the aforementioned genotypes. Its shape is (batch_size,) batch_of_extra_scores: tree that contains the extra_scores of aforementioned genotypes.
| RETURNS | DESCRIPTION |
|---|---|
The updated MAP-Elites repertoire.
|
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_me_repertoire.py
add_to_repertoire_without_cell_depth
#
add_to_repertoire_without_cell_depth(
repertoire,
batch_of_genotypes,
batch_of_descriptors,
batch_of_fitnesses,
batch_of_extra_scores=None,
)
Add a batch of elements to the repertoire.
| PARAMETER | DESCRIPTION |
|---|---|
repertoire
|
the MAP-Elites repertoire to which the elements will be added. |
batch_of_genotypes
|
a batch of genotypes to be added to the repertoire.
TYPE:
|
batch_of_descriptors
|
an array that contains the descriptors of the genotypes.
TYPE:
|
batch_of_fitnesses
|
an array that contains the fitnesses of the genotypes.
TYPE:
|
batch_of_extra_scores
|
tree that contains the extra_scores of genotypes.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DiscreteMapElitesRepertoire
|
The updated MAP-Elites repertoire. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_me_repertoire.py
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | |
add_to_repertoire_with_cell_depth
#
add_to_repertoire_with_cell_depth(
repertoire,
batch_of_genotypes,
batch_of_descriptors,
batch_of_fitnesses,
batch_of_extra_scores=None,
)
Add a batch of elements to a repertoire with depth.
Assumes the repertoire puts elements on a same depth level next to another (layer1 layer1 layer2 layer2 ...) Manipulates abstract indices instead of moving genotypes directly.
| PARAMETER | DESCRIPTION |
|---|---|
repertoire
|
the MAP-Elites repertoire to which the elements will be added. |
batch_of_genotypes
|
a batch of genotypes to be added to the repertoire.
TYPE:
|
batch_of_descriptors
|
an array that contains the descriptors of the genotypes.
TYPE:
|
batch_of_fitnesses
|
an array that contains the fitnesses of the genotypes.
TYPE:
|
batch_of_extra_scores
|
tree that contains the extra_scores of genotypes.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DiscreteMapElitesRepertoire
|
The updated MAP-Elites repertoire. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_me_repertoire.py
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 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 | |
init_repertoire
#
Initialize a Map-Elites repertoire with an initial population of genotypes.
Requires the definition of centroids that can be computed with any method such as CVT or Euclidean mapping.
Note: this function has been kept outside of the object MapElites, so it can be called easily called from other modules.
Args: genotypes: initial genotypes, pytree in which leaves have shape (batch_size, num_features) fitnesses: fitness of the initial genotypes of shape (batch_size,) descriptors: descriptors of the initial genotypes of shape (batch_size, num_descriptors) extra_scores: the observed load flow metrics n_cells_per_dim: the number of cells per dimension cell_depth: the number of topologies per cell
| RETURNS | DESCRIPTION |
|---|---|
an initialized MAP-Elite repertoire
|
|
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/discrete_me_repertoire.py
toop_engine_topology_optimizer.dc.repertoire.plotting
#
Plotting functions for the Map-Elites algorithm.
plot_repertoire_1d
#
Plot a bar chart of the repertoire's fitnesses.
| PARAMETER | DESCRIPTION |
|---|---|
fitnesses
|
The fitnesses of the repertoire
TYPE:
|
n_cells_per_dim
|
The number of cells per dimension
TYPE:
|
descriptor_metrics
|
The descriptor metrics
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Figure
|
The plot |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/plotting.py
plot_repertoire_2d
#
Plot a heatmap of the repertoire's fitnesses.
| PARAMETER | DESCRIPTION |
|---|---|
fitnesses
|
The fitnesses of the repertoire
TYPE:
|
n_cells_per_dim
|
The number of cells per dimension
TYPE:
|
descriptor_metrics
|
The descriptor metrics
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Axes
|
The plot |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/plotting.py
plot_repertoire
#
Plot the repertoire (1D or 2D) and saves the figure.
| PARAMETER | DESCRIPTION |
|---|---|
fitnesses
|
The fitnesses of the repertoire
TYPE:
|
iteration
|
The current iteration number
TYPE:
|
folder
|
The folder to save the plot. Will create a "plots" folder inside.
TYPE:
|
n_cells_per_dim
|
The number of cells per dimension
TYPE:
|
descriptor_metrics
|
The descriptor metrics
TYPE:
|
save_plot
|
Whether to save the plot
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Axes | Figure
|
The plot. Axes for heatmap, plt.Figure for barchart. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/repertoire/plotting.py
Worker#
toop_engine_topology_optimizer.dc.worker.optimizer
#
Callable functions for the optimizer worker.
OptimizerData
dataclass
#
OptimizerData(
start_params,
optimization_id,
solver_configs,
algo,
initial_fitness,
initial_metrics,
jax_data,
start_time,
)
A wrapper dataclass for all the data stored by the optimizer.
Because this dataclass holds irrelevant information for the GPU optimization, it is split into two dataclasses. The parent (this one) is used to store all the information and the child (JaxOptimizerData) is used to store the information that is needed on the GPU.
jax_data
instance-attribute
#
Everything that needs to live on GPU. This dataclass is updated per-iteration while OptimizerData is only updated per-epoch, hence there are points in the command flow where this variable is out of sync. At the end of an epoch, this dataclass is updated to match the state in the optimization.
initialize_optimization
#
initialize_optimization(
params,
optimization_id,
static_information_files,
processed_gridfile_fs,
)
Initialize the optimization run.
This function will be called at the start of the optimization run. It should be used to load the static information files and set up the optimization run.
| PARAMETER | DESCRIPTION |
|---|---|
params
|
The parameters for the optimization run
TYPE:
|
optimization_id
|
The id of the optimization run, used to annotate results and heartbeats
TYPE:
|
static_information_files
|
The paths to the static information files to load
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
OptimizerData
|
The data to store for the optimization run |
list[StaticInformationStats]
|
The static information descriptions, will be sent via the heartbeats channel |
Strategy
|
The initial strategy (unsplit) for the grid, including the initial fitness and metrics |
| RAISES | DESCRIPTION |
|---|---|
Exception
|
If an error occurs during the initialization. It will be caught by the worker and sent back to the results topic |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/optimizer.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | |
convert_metrics
#
Convert a metrics dictionary to a Metrics dataclass.
| PARAMETER | DESCRIPTION |
|---|---|
fitness
|
The fitness value
TYPE:
|
metrics_dict
|
The metrics dictionary
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Metrics
|
The metrics dataclass |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/optimizer.py
run_single_iteration
#
Run a single iteration of the optimization.
This involves updating the genetic algorithm and calling the metrics callback
| PARAMETER | DESCRIPTION | ||
|---|---|---|---|
_i
|
The iteration number, will be ignored. Its only purpose is to make the function signature compatible with lax.fori_loop
TYPE:
|
||
jax_data
|
The data stored for the optimization run from the last epoch or from initialize_optimization
TYPE:
|
||
update_fn
|
The update function of the genetic algorithm
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
JaxOptimizerData
|
The updated data for the optimization run |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/optimizer.py
run_single_device_epoch
#
Run one epoch of the optimization on a single device.
Basically this is just a fori loop over the iterations_per_epoch, calling run_single_iteration This can be used to pass to pmap
| PARAMETER | DESCRIPTION | ||
|---|---|---|---|
jax_data
|
The data stored for the optimization run from the last epoch or from initialize_optimization
TYPE:
|
||
iterations_per_epoch
|
The number of iterations to run in this epoch
TYPE:
|
||
update_fn
|
The update function of the genetic algorithm
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
JaxOptimizerData
|
The updated data for the optimization run |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/optimizer.py
run_epoch
#
Run one epoch of the optimization.
This function will be called repeatedly by the worker. It should include multiple iterations, according to the configuration of the optimizer. Furthermore it should call the metrics_callback during the epoch, at least at the beginning. This could happen through a jax callback to not block the main thread.
| PARAMETER | DESCRIPTION |
|---|---|
optimizer_data
|
The data stored for the optimization run from the last epoch or from initialize_optimization
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
OptimizerData
|
The updated data for the optimization run |
| RAISES | DESCRIPTION |
|---|---|
Exception
|
If an error occurs during the epoch. It will be caught by the worker and sent back to the results topic |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/optimizer.py
extract_results
#
Pull the results from the optimizer.
This should give the current best topologies along with metrics for each topology, where the number of saved topologies is a configuration parameter in the StartOptimizationCommand.
| PARAMETER | DESCRIPTION |
|---|---|
optimizer_data
|
The data stored for the optimization run
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
TopologyPushResult
|
The current best topologies extracted and in topo-vect form. |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/optimizer.py
toop_engine_topology_optimizer.dc.worker.worker
#
Kafka worker for the genetic algorithm optimization.
Args
#
Bases: BaseModel
Launch arguments for the worker, which can not be changed during the optimization run.
kafka_broker
class-attribute
instance-attribute
#
The Kafka broker to connect to.
optimizer_command_topic
class-attribute
instance-attribute
#
The Kafka topic to listen for commands on.
optimizer_results_topic
class-attribute
instance-attribute
#
The topic to push results to.
optimizer_heartbeat_topic
class-attribute
instance-attribute
#
The topic to push heartbeats to.
heartbeat_interval_ms
class-attribute
instance-attribute
#
The interval in milliseconds to send heartbeats.
idle_loop
#
Run idle loop of the worker.
This will be running when the worker is currently not optimizing This will wait until a StartOptimizationCommand is received and return it. In case a ShutdownCommand is received, the worker will exit with the exit code provided in the command.
| PARAMETER | DESCRIPTION |
|---|---|
consumer
|
The initialized Kafka consumer to listen for commands on.
TYPE:
|
send_heartbeat_fn
|
A function to call when there were no messages received for a while.
TYPE:
|
heartbeat_interval_ms
|
The time to wait for a new command in milliseconds. If no command has been received, a heartbeat will be sent and then the receiver will wait for commands again.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
StartOptimizationCommand
|
The start optimization command to start the optimization run with |
| RAISES | DESCRIPTION |
|---|---|
SystemExit
|
If a ShutdownCommand is received |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/worker.py
optimization_loop
#
optimization_loop(
dc_params,
grid_files,
send_result_fn,
send_heartbeat_fn,
optimization_id,
processed_gridfile_fs,
)
Run an optimization until the optimization has converged
| PARAMETER | DESCRIPTION |
|---|---|
dc_params
|
The parameters for the optimization run, usually from the start command
TYPE:
|
grid_files
|
The grid files to load, where each gridfile represents one timestep.
TYPE:
|
send_result_fn
|
A function to call to send results back to the results topic.
TYPE:
|
send_heartbeat_fn
|
A function to call after every epoch to signal that the worker is still alive.
TYPE:
|
optimization_id
|
The id of the optimization run. This will be used to identify the optimization run in the results. Should stay the same for the whole optimization run and should be equal to the kafka event key.
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
SystemExit
|
If a ShutdownCommand is received |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/worker.py
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | |
main
#
Run the main DC worker loop.
| PARAMETER | DESCRIPTION |
|---|---|
args
|
The command line arguments
TYPE:
|
processed_gridfile_fs
|
The target filesystem for the preprocessing worker. This contains all processed grid files. During the import job, a new folder import_results.data_folder was created which will be completed with the preprocess call to this function. Internally, only the data folder is passed around as a dirfs. Note that the unprocessed_gridfile_fs is not needed here anymore, as all preprocessing steps that need the unprocessed gridfiles were already done.
TYPE:
|
producer
|
The Kafka producer to send results and heartbeats with.
TYPE:
|
command_consumer
|
The Kafka consumer to receive commands with.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
SystemExit
|
If the worker receives a ShutdownCommand |
Source code in packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/worker/worker.py
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | |