This library is organized around two distinct responsibilities that must not be confused:
- Describing a modification — the serializable input data exchanged over REST and persisted by the
server (the
dtopackage, suffixed*Infos). - Applying a modification — the executable logic that mutates a PowSyBl
Network(themodificationspackage).
Keeping these two concerns separated is the central design goal: it lets the execution code stand on its own, ultimately as a standalone library that does not need the server-oriented DTO layer.
org.gridsuite.modification
├── (root) Cross-cutting contracts & enums: NetworkModificationException,
│ ModificationType, IFilterService, ILoadFlowService, VariationMode, …
├── modifications The execution layer. One class per modification (e.g. LoadCreation),
│ │ each extending AbstractModification and implementing apply(Network, …).
│ ├── data Plain data holders that belong to the execution layer but are not yet
│ │ moved out of dto (see "Shared data objects" below).
│ ├── byfilter "By filter" / "by formula" modification execution.
│ ├── tabular Tabular (bulk) modification execution — special case, see below.
│ └── olg Operational-limits-group execution helpers.
├── dto The description layer. One *Infos class per modification, all extending
│ │ ModificationInfos. REST contract (Swagger) + JPA/serialization model.
│ ├── annotation DTO annotations (@ModificationErrorTypeName, …).
│ ├── byfilter DTO for by-filter/by-formula modifications.
│ └── tabular DTO for tabular modifications.
├── utils Stateless helpers operating on the network (ModificationUtils, …).
└── report PowSyBl report resource bundle.
Every modification exists as a pair:
| Layer | Example | Base class | Role |
|---|---|---|---|
dto |
LoadCreationInfos |
ModificationInfos |
Serializable description: REST contract, persistence, Swagger. |
modifications |
LoadCreation |
AbstractModification |
Executable logic: check(network) then apply(network, …). |
ModificationInfos is the polymorphic root of the DTO layer (@JsonTypeInfo + @JsonSubTypes); it carries
the server-specific fields (uuid, date, stashed, activated, messageType, messageValues,
description) and the Swagger annotations.
The single bridge from description to execution is ModificationInfos#toModification(). Each leaf *Infos
builds its matching modification from its own fields:
// LoadCreationInfos
public AbstractModification toModification() {
return LoadCreation.builder()
.equipmentId(getEquipmentId())
.p0(p0)
.q0(q0)
// … copy every field …
.build();
}
This is why the modification classes redeclare their business fields instead of inheriting them from the
*Infos DTO: it severs the dependency from modifications to the server-oriented ModificationInfos
hierarchy. The modification is a self-contained, builder-constructed object; the DTO merely knows how to
populate it.
Both layers mirror the same equipment taxonomy, but in two independent trees.
Every applied modification descends from AbstractModification (itself a PowSyBl
AbstractNetworkModification). The abstract bases factor out the shared check/apply/reporting logic:
AbstractNetworkModification (powsybl)
└── AbstractModification
├── AbstractEquipmentBase
│ ├── AbstractEquipmentCreation
│ │ ├── AbstractInjectionCreation ........ implements InjectionCreation
│ │ │ ├── LoadCreation
│ │ │ ├── GeneratorCreation ............ implements ReactiveLimitsHolderInfos
│ │ │ ├── BatteryCreation .............. implements ReactiveLimitsHolderInfos
│ │ │ ├── ShuntCompensatorCreation
│ │ │ └── StaticVarCompensatorCreation
│ │ ├── AbstractBranchCreation
│ │ │ ├── LineCreation
│ │ │ └── TwoWindingsTransformerCreation
│ │ ├── SubstationCreation
│ │ ├── VoltageLevelCreation
│ │ ├── LccCreation
│ │ └── VscCreation
│ ├── AbstractEquipmentModification
│ │ ├── AbstractInjectionModification .... implements InjectionModification
│ │ │ ├── LoadModification
│ │ │ ├── GeneratorModification
│ │ │ ├── BatteryModification
│ │ │ └── ShuntCompensatorModification
│ │ ├── AbstractBranchModification
│ │ │ ├── LineModification
│ │ │ └── TwoWindingsTransformerModification
│ │ ├── SubstationModification
│ │ ├── VoltageLevelModification
│ │ ├── LccModification
│ │ └── VscModification
│ ├── EquipmentAttributeModification
│ ├── EquipmentDeletion
│ ├── OperatingStatusModification
│ └── VoltageLevelTopologyModification
├── AbstractScaling
│ ├── GeneratorScaling
│ └── LoadScaling
└── (standalone) CompositeModification, ModificationReference, GroovyScript,
GenerationDispatch, ByFilterDeletion, VoltageInitModification, …
The InjectionCreation and InjectionModification interfaces (in modifications) declare the common
injection field accessors (equipmentId, voltageLevelId, properties, connection info, …). They are
implemented by two independent abstract bases, which is the reason the contract is an interface rather
than a shared superclass:
modifications.AbstractInjectionCreation— the base for top-level injection modifications (above).modifications.data.AbstractInjectionCreation— a base for injection data components that are not modifications themselves.LccConverterStationCreationandVscConverterStationCreationextend it; they are sub-objects embedded insideLccCreation/VscCreationrather than standalone modifications, which is why they live inmodifications.dataand only implement the interface instead of joining theAbstractModificationchain.
ReactiveLimitsHolderInfos is another shared interface (currently in dto) implemented by the
reactive-limits-bearing creations (GeneratorCreation, BatteryCreation, VscConverterStationCreation).
The DTO tree parallels the execution tree, rooted at the polymorphic ModificationInfos:
ModificationInfos
├── EquipmentModificationInfos
│ ├── EquipmentCreationInfos
│ │ ├── InjectionCreationInfos
│ │ │ ├── LoadCreationInfos
│ │ │ ├── GeneratorCreationInfos ........... implements ReactiveLimitsHolderInfos
│ │ │ ├── BatteryCreationInfos ............. implements ReactiveLimitsHolderInfos
│ │ │ ├── ShuntCompensatorCreationInfos
│ │ │ ├── StaticVarCompensatorCreationInfos
│ │ │ ├── LccConverterStationCreationInfos
│ │ │ └── ConverterStationCreationInfos .... implements ReactiveLimitsHolderInfos
│ │ ├── BranchCreationInfos
│ │ │ ├── LineCreationInfos
│ │ │ └── TwoWindingsTransformerCreationInfos
│ │ ├── SubstationCreationInfos
│ │ ├── VoltageLevelCreationInfos
│ │ └── LccCreationInfos
│ ├── BasicEquipmentModificationInfos
│ │ ├── InjectionModificationInfos
│ │ │ ├── LoadModificationInfos
│ │ │ ├── GeneratorModificationInfos
│ │ │ └── … (Battery, ShuntCompensator, ConverterStation, Lcc, …)
│ │ ├── BranchModificationInfos
│ │ │ ├── LineModificationInfos
│ │ │ └── TwoWindingsTransformerModificationInfos
│ │ └── SubstationModificationInfos
│ ├── EquipmentAttributeModificationInfos
│ ├── EquipmentDeletionInfos
│ └── OperatingStatusModificationInfos
├── ScalingInfos
│ ├── GeneratorScalingInfos
│ └── LoadScalingInfos
└── (direct) CompositeModificationInfos, ModificationReferenceInfos, GroovyScriptInfos,
TabularModificationInfos, ByFilterDeletionInfos, VoltageInitModificationInfos, …
Note the two trees are not linked by inheritance: a LoadCreation is not a subclass of
LoadCreationInfos. They are connected only through LoadCreationInfos.toModification(), which copies the
DTO's fields into a freshly built LoadCreation.
The dependency direction is deliberate and one-way:
dto ───────────────► modifications ───► utils ───► (root contracts)
(description) (execution)
dtodepends onmodifications— every*Infos.toModification()constructs a concrete modification. This is expected and correct.modificationsmust NOT depend onModificationInfossubclasses. The execution layer should never reference a server-oriented DTO. Today, after redeclaring the business fields in the modifications, this rule holds for the whole creation/modification family.- The only
dtotypes themodificationspackage is still allowed to reference are plain data objects that happen to live indtofor now — see below.
A handful of dto classes are not modifications: they are simple, reusable value/data objects that do
not extend ModificationInfos. The execution layer references them legitimately, for example:
FreePropertyInfos,AttributeModification<T>,ReactiveCapabilityCurvePointsInfos,IdentifiableAttributes,ScalingVariationInfos,FilterInfos,OperationalLimitsGroupInfos, …
These currently sit in the dto package only for historical reasons. Conceptually they belong to the
execution model and are intended to move into the modifications package (the modifications.data
subpackage already hosts some of them, e.g. the injection creation/modification data holders and the LCC/VSC
converter-station data). Until that move is complete, treat any dto type that does not extend
ModificationInfos as part of the execution model, not as a DTO.
The same applies to value types nested inside an *Infos class: a modification that imports an *Infos
purely to reach a nested enum or record is not depending on a ModificationInfos subclass — it is using a
plain data object that simply lives in the wrong place. For example OperatingStatusModification references
only OperatingStatusModificationInfos.ActionType, a plain enum. Such nested types should be promoted to
top-level data objects and moved alongside the execution layer (modifications.data).
A few families break the "modifications never depend on ModificationInfos" rule by nature, and are
explicitly excluded from it:
CompositeModificationaggregates an ordered list of child modifications.- Tabular modifications (
modifications.tabular) apply the same modification to many equipments in bulk. ModificationReferencewraps a single other modification, holding it as aModificationInfosand delegating to itstoModification()at apply time.
All of these are containers of modifications, so they intrinsically manipulate the polymorphic
ModificationInfos type (and the tabular *Infos). They should be understood as a layer above the
ordinary modifications rather than peers of them, and they are not expected to satisfy the
"no ModificationInfos dependency" constraint that applies to every other modification.
- Create the executable
XxxModification(orXxxCreation) inmodifications, extendingAbstractModification. Declare its own business fields, implementcheck(Network)andapply(Network, ReportNode), expose a Lombok@Builder. - Create the
XxxInfosDTO indto, extending the appropriateModificationInfossubtype, carrying the Swagger annotations and any persistence concerns. - Implement
XxxInfos#toModification()to build the modification from the DTO's fields. - Register the DTO in the
@JsonSubTypeslist ofModificationInfosand add its@JsonTypeName/@ModificationErrorTypeNameannotations. - Reuse the shared data objects (
FreePropertyInfos,AttributeModification, …) for common fields; do not let the modification reference anyModificationInfossubclass.