88 */
99package com .powsybl .openloadflow .ac ;
1010
11+ import com .powsybl .commons .PowsyblException ;
1112import com .powsybl .commons .report .ReportNode ;
1213import com .powsybl .computation .local .LocalComputationManager ;
1314import com .powsybl .iidm .network .*;
2324import com .powsybl .openloadflow .OpenLoadFlowProvider ;
2425import com .powsybl .openloadflow .network .DistributedSlackNetworkFactory ;
2526import com .powsybl .openloadflow .network .EurostagFactory ;
27+ import com .powsybl .openloadflow .network .FirstSlackBusSelector ;
28+ import com .powsybl .openloadflow .network .LfBus ;
29+ import com .powsybl .openloadflow .network .LfNetwork ;
2630import com .powsybl .openloadflow .network .ReferenceBusSelectionMode ;
2731import com .powsybl .openloadflow .network .SlackBusSelectionMode ;
32+ import com .powsybl .openloadflow .network .impl .LfNetworkLoaderImpl ;
33+ import com .powsybl .openloadflow .network .util .ActivePowerDistribution ;
2834import com .powsybl .openloadflow .util .LoadFlowAssert ;
2935import org .junit .jupiter .api .BeforeEach ;
3036import org .junit .jupiter .api .Test ;
3137
3238import java .util .EnumSet ;
39+ import java .util .List ;
40+ import java .util .OptionalDouble ;
41+ import java .util .Set ;
3342import java .util .concurrent .CompletionException ;
3443
3544import static com .powsybl .openloadflow .util .LoadFlowAssert .*;
@@ -187,7 +196,7 @@ void testProportionalToParticipationFactor() {
187196 }
188197
189198 @ Test
190- void testProportionalToRemainingMargin () {
199+ void testProportionalToRemainingMarginUp () {
191200 // decrease g1 max limit power, so that distributed slack algo reach the g1 max
192201 g1 .setMaxP (105 );
193202
@@ -201,6 +210,53 @@ void testProportionalToRemainingMargin() {
201210 assertActivePowerEquals (-122.0 , g4 .getTerminal ());
202211 }
203212
213+ @ Test
214+ void testProportionalToRemainingMarginDown () {
215+ // Decrease load P0, so that active mismatch is negative
216+ network .getLoad ("l1" ).setP0 (400 );
217+
218+ parameters .setBalanceType (LoadFlowParameters .BalanceType .PROPORTIONAL_TO_GENERATION_REMAINING_MARGIN );
219+ LoadFlowResult result = loadFlowRunner .run (network , parameters );
220+
221+ assertTrue (result .isFullyConverged ());
222+ assertActivePowerEquals (-71.428 , g1 .getTerminal ());
223+ assertActivePowerEquals (-171.428 , g2 .getTerminal ());
224+ assertActivePowerEquals (-78.571 , g3 .getTerminal ());
225+ assertActivePowerEquals (-78.571 , g4 .getTerminal ());
226+ }
227+
228+ @ Test
229+ void testGetParticipatingElementsWithMismatch () {
230+ LfNetwork lfNetwork = LfNetwork .load (network , new LfNetworkLoaderImpl (), new FirstSlackBusSelector (Set .of ())).get (0 );
231+ final OptionalDouble mismatch = OptionalDouble .of (30 );
232+ final List <LfBus > buses = lfNetwork .getBuses ();
233+ for (LoadFlowParameters .BalanceType balanceType : LoadFlowParameters .BalanceType .values ()) {
234+ ActivePowerDistribution .Step step = ActivePowerDistribution .getStep (balanceType , parametersExt .isLoadPowerFactorConstant (), parametersExt .isUseActiveLimits ());
235+ switch (balanceType ) {
236+ case PROPORTIONAL_TO_GENERATION_P_MAX , PROPORTIONAL_TO_GENERATION_P , PROPORTIONAL_TO_GENERATION_REMAINING_MARGIN -> assertEquals (4 , step .getParticipatingElements (buses , mismatch ).size ());
237+ case PROPORTIONAL_TO_LOAD , PROPORTIONAL_TO_CONFORM_LOAD -> assertEquals (1 , step .getParticipatingElements (buses , mismatch ).size ());
238+ case PROPORTIONAL_TO_GENERATION_PARTICIPATION_FACTOR -> assertEquals (0 , step .getParticipatingElements (buses , mismatch ).size ());
239+ }
240+ }
241+ }
242+
243+ @ Test
244+ void testGetParticipatingElementsWithoutMismatch () {
245+ LfNetwork lfNetwork = LfNetwork .load (network , new LfNetworkLoaderImpl (), new FirstSlackBusSelector (Set .of ())).get (0 );
246+ final OptionalDouble emptyMismatch = OptionalDouble .empty ();
247+ final List <LfBus > buses = lfNetwork .getBuses ();
248+ for (LoadFlowParameters .BalanceType balanceType : LoadFlowParameters .BalanceType .values ()) {
249+ ActivePowerDistribution .Step step = ActivePowerDistribution .getStep (balanceType , parametersExt .isLoadPowerFactorConstant (), parametersExt .isUseActiveLimits ());
250+ switch (balanceType ) {
251+ case PROPORTIONAL_TO_GENERATION_P_MAX , PROPORTIONAL_TO_GENERATION_P -> assertEquals (4 , step .getParticipatingElements (buses , emptyMismatch ).size ());
252+ case PROPORTIONAL_TO_LOAD , PROPORTIONAL_TO_CONFORM_LOAD -> assertEquals (1 , step .getParticipatingElements (buses , emptyMismatch ).size ());
253+ case PROPORTIONAL_TO_GENERATION_PARTICIPATION_FACTOR -> assertEquals (0 , step .getParticipatingElements (buses , emptyMismatch ).size ());
254+ case PROPORTIONAL_TO_GENERATION_REMAINING_MARGIN -> assertThrows (PowsyblException .class , () -> step .getParticipatingElements (buses , emptyMismatch ),
255+ "The sign of the active power mismatch is unknown, it is mandatory for REMAINING_MARGIN participation type" );
256+ }
257+ }
258+ }
259+
204260 @ Test
205261 void testProportionalToRemainingMarginPmaxBelowTargetP () {
206262 // decrease g1 max limit power below target P
0 commit comments