Skip to content

Commit

Permalink
inter-tempoal RAO API (#1236)
Browse files Browse the repository at this point in the history
* inter-tempoal RAO API

Signed-off-by: Thomas Bouquet <[email protected]>

* move to right module

Signed-off-by: Thomas Bouquet <[email protected]>

* fix InterTemporalRaoInput creation

Signed-off-by: Godelaine de Montmorillon <[email protected]>

---------

Signed-off-by: Thomas Bouquet <[email protected]>
Signed-off-by: Godelaine de Montmorillon <[email protected]>
Co-authored-by: Godelaine <[email protected]>
Co-authored-by: Godelaine de Montmorillon <[email protected]>
  • Loading branch information
3 people authored Dec 19, 2024
1 parent da19936 commit f23fd92
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package com.powsybl.openrao.raoapi;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.powsybl.commons.Versionable;
import com.powsybl.commons.config.PlatformConfig;
import com.powsybl.commons.util.ServiceLoaderCache;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.TemporalData;
import com.powsybl.openrao.data.raoresult.api.RaoResult;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;
import com.powsybl.tools.Version;

import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader;

import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS;

/**
* @author Thomas Bouquet {@literal <thomas.bouquet at rte-france.com>}
*/
public final class InterTemporalRao {
private InterTemporalRao() {
throw new AssertionError("Utility class should not been instantiated");
}

private static final Supplier<List<InterTemporalRaoProvider>> RAO_PROVIDERS
= Suppliers.memoize(() -> new ServiceLoaderCache<>(InterTemporalRaoProvider.class).getServices());

/**
* An inter-temporal RA optimisation runner is responsible for providing convenient methods on top of
* {@link InterTemporalRaoProvider}: several variants of synchronous and asynchronous run with default parameters.
*/
public static class Runner implements Versionable {

private final InterTemporalRaoProvider provider;

public Runner(InterTemporalRaoProvider provider) {
this.provider = Objects.requireNonNull(provider);
}

public TemporalData<RaoResult> run(InterTemporalRaoInput raoInput, RaoParameters parameters) {
Objects.requireNonNull(raoInput, "RAO input should not be null");
Objects.requireNonNull(parameters, "parameters should not be null");

Version openRaoVersion = ServiceLoader.load(Version.class).findFirst().orElseThrow();
BUSINESS_WARNS.warn("Running RAO using Open RAO version {} from git commit {}.", openRaoVersion.getMavenProjectVersion(), openRaoVersion.getGitVersion());

return provider.run(raoInput, parameters).join();
}

public TemporalData<RaoResult> run(InterTemporalRaoInput raoInput) {
return run(raoInput, RaoParameters.load());
}

@Override
public String getName() {
return provider.getName();
}

@Override
public String getVersion() {
return provider.getVersion();
}
}

/**
* Get a runner for an inter-temporal RAO named {@code name}. In the case of a null {@code name}, default
* implementation is used.
*
* @param name name of the RAO implementation, null if we want to use default one
* @return a runner for RAO implementation named {@code name}
*/
public static InterTemporalRao.Runner find(String name) {
return find(name, RAO_PROVIDERS.get(), PlatformConfig.defaultConfig());
}

/**
* Get a runner for default inter-temporal RAO implementation.
*
* @throws OpenRaoException in case we cannot find a default implementation
* @return a runner for default RAO implementation
*/
public static InterTemporalRao.Runner find() {
return find(null);
}

/**
* A variant of {@link Rao#find(String)} intended to be used for unit testing that allow passing
* an explicit provider list instead of relying on service loader and an explicit {@link PlatformConfig}
* instead of global one.
*
* @param name name of the RAO implementation, null if we want to use default one
* @param providers flowbased provider list
* @param platformConfig platform config to look for default flowbased implementation name
* @return a runner for flowbased implementation named {@code name}
*/
public static InterTemporalRao.Runner find(String name, List<InterTemporalRaoProvider> providers, PlatformConfig platformConfig) {
Objects.requireNonNull(providers);
Objects.requireNonNull(platformConfig);

if (providers.isEmpty()) {
throw new OpenRaoException("No inter-temporal RAO providers found");
}

// if no RAO implementation name is provided through the API we look for information
// in platform configuration
String raOptimizerName = name != null ? name : platformConfig.getOptionalModuleConfig("rao")
.flatMap(mc -> mc.getOptionalStringProperty("default"))
.orElse(null);
InterTemporalRaoProvider provider;
if (providers.size() == 1 && raOptimizerName == null) {
// no information to select the implementation but only one provider, so we can use it by default
// (that is the most common use case)
provider = providers.get(0);
} else {
if (providers.size() > 1 && raOptimizerName == null) {
// several providers and no information to select which one to choose, we can only throw
// an exception
List<String> raOptimizerNames = providers.stream().map(InterTemporalRaoProvider::getName).toList();
throw new OpenRaoException("Several RAO implementations found (" + raOptimizerNames
+ "), you must add configuration to select the implementation");
}
provider = providers.stream()
.filter(p -> p.getName().equals(raOptimizerName))
.findFirst()
.orElseThrow(() -> new OpenRaoException("RA optimizer provider '" + raOptimizerName + "' not found"));
}

return new InterTemporalRao.Runner(provider);
}

public static TemporalData<RaoResult> run(InterTemporalRaoInput raoInput, RaoParameters parameters) {
return find().run(raoInput, parameters);
}

public static TemporalData<RaoResult> run(InterTemporalRaoInput raoInput) {
return find().run(raoInput);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package com.powsybl.openrao.raoapi;

import com.powsybl.commons.Versionable;
import com.powsybl.openrao.commons.TemporalData;
import com.powsybl.openrao.data.raoresult.api.RaoResult;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;

import java.util.concurrent.CompletableFuture;

/**
* @author Thomas Bouquet {@literal <thomas.bouquet at rte-france.com>}
*/
public interface InterTemporalRaoProvider extends Versionable {

/**
* @param raoInput: Data to optimize. Contains Cracs and Networks for each timestamp and gradient constraints
* @param parameters: RAO parameters.
* @return A completable future of a RaoComputationResult for each timestamp.
*/
CompletableFuture<TemporalData<RaoResult>> run(InterTemporalRaoInput raoInput, RaoParameters parameters);
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public static Runner find(String name, List<RaoProvider> providers, PlatformConf
RaoProvider provider;
if (providers.size() == 1 && raOptimizerName == null) {
// no information to select the implementation but only one provider, so we can use it by default
// (that is be the most common use case)
// (that is the most common use case)
provider = providers.get(0);
} else {
if (providers.size() > 1 && raOptimizerName == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package com.powsybl.openrao.raoapi;

import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.powsybl.commons.config.InMemoryPlatformConfig;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.VariantManager;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.TemporalData;
import com.powsybl.openrao.commons.TemporalDataImpl;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.raoresult.api.ComputationStatus;
import com.powsybl.openrao.data.raoresult.api.RaoResult;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;
import com.powsybl.openrao.raoapi.raomock.AnotherInterTemporalRaoProviderMock;
import com.powsybl.openrao.raoapi.raomock.InterTemporalRaoProviderMock;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.nio.file.FileSystem;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* @author Thomas Bouquet {@literal <thomas.bouquet at rte-france.com>}
*/
class InterTemporalRaoTest {

private FileSystem fileSystem;
private InMemoryPlatformConfig platformConfig;
private InterTemporalRaoInput raoInput;

@BeforeEach
public void setUp() {
fileSystem = Jimfs.newFileSystem(Configuration.unix());
platformConfig = new InMemoryPlatformConfig(fileSystem);
Network network = Mockito.mock(Network.class);
Crac crac = Mockito.mock(Crac.class);
VariantManager variantManager = Mockito.mock(VariantManager.class);
Mockito.when(network.getVariantManager()).thenReturn(variantManager);
Mockito.when(variantManager.getWorkingVariantId()).thenReturn("v");
raoInput = new InterTemporalRaoInput(new TemporalDataImpl<>(Map.of(OffsetDateTime.of(2024, 12, 13, 16, 17, 0, 0, ZoneOffset.UTC), RaoInput.build(network, crac).build())), new HashSet<>());
}

@AfterEach
public void tearDown() throws Exception {
fileSystem.close();
}

@Test
void testDefaultOneProvider() {
// case with only one provider, no need for config
// find rao
InterTemporalRao.Runner defaultRao = InterTemporalRao.find(null, List.of(new InterTemporalRaoProviderMock()), platformConfig);
assertEquals("RandomInterTemporalRAO", defaultRao.getName());
assertEquals("1.0", defaultRao.getVersion());

// run rao
OffsetDateTime timestamp = OffsetDateTime.of(2024, 12, 13, 16, 17, 0, 0, ZoneOffset.UTC);
TemporalData<RaoResult> result = defaultRao.run(raoInput, new RaoParameters());
assertNotNull(result);
assertEquals(List.of(timestamp), result.getTimestamps());
assertTrue(result.getData(timestamp).isPresent());
assertEquals(ComputationStatus.DEFAULT, result.getData(timestamp).get().getComputationStatus());
}

@Test
void testDefaultTwoProviders() {
// case with two providers : should throw as no config defines which provider must be selected
List<InterTemporalRaoProvider> raoProviders = List.of(new InterTemporalRaoProviderMock(), new AnotherInterTemporalRaoProviderMock());
assertThrows(OpenRaoException.class, () -> InterTemporalRao.find(null, raoProviders, platformConfig));
}

@Test
void testDefinedAmongTwoProviders() {
// case with two providers where one the two RAOs is specifically selected
InterTemporalRao.Runner definedRao = InterTemporalRao.find("GlobalRAOptimizer", List.of(new InterTemporalRaoProviderMock(), new AnotherInterTemporalRaoProviderMock()), platformConfig);
assertEquals("GlobalRAOptimizer", definedRao.getName());
assertEquals("2.3", definedRao.getVersion());
}

@Test
void testDefaultNoProvider() {
// case with no provider
List<InterTemporalRaoProvider> raoProviders = List.of();
assertThrows(OpenRaoException.class, () -> InterTemporalRao.find(null, raoProviders, platformConfig));
}

@Test
void testDefaultTwoProvidersPlatformConfig() {
// case with 2 providers without any config but specifying which one to use in platform config
platformConfig.createModuleConfig("rao").setStringProperty("default", "GlobalRAOptimizer");
InterTemporalRao.Runner globalRaOptimizer = InterTemporalRao.find(null, List.of(new InterTemporalRaoProviderMock(), new AnotherInterTemporalRaoProviderMock()), platformConfig);
assertEquals("GlobalRAOptimizer", globalRaOptimizer.getName());
assertEquals("2.3", globalRaOptimizer.getVersion());
}

@Test
void testOneProviderAndMistakeInPlatformConfig() {
// case with 1 provider with config but with a name that is not the one of provider.
platformConfig.createModuleConfig("rao").setStringProperty("default", "UnknownRao");
List<InterTemporalRaoProvider> raoProviders = List.of(new InterTemporalRaoProviderMock());
assertThrows(OpenRaoException.class, () -> InterTemporalRao.find(null, raoProviders, platformConfig));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package com.powsybl.openrao.raoapi.raomock;

import com.google.auto.service.AutoService;
import com.powsybl.openrao.commons.TemporalData;
import com.powsybl.openrao.commons.TemporalDataImpl;
import com.powsybl.openrao.data.raoresult.api.ComputationStatus;
import com.powsybl.openrao.data.raoresult.api.RaoResult;
import com.powsybl.openrao.data.raoresult.impl.RaoResultImpl;
import com.powsybl.openrao.raoapi.InterTemporalRaoInput;
import com.powsybl.openrao.raoapi.InterTemporalRaoProvider;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;

import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
* @author Thomas Bouquet {@literal <thomas.bouquet at rte-france.com>}
*/
@AutoService(InterTemporalRaoProvider.class)
public class AnotherInterTemporalRaoProviderMock implements InterTemporalRaoProvider {
@Override
public CompletableFuture<TemporalData<RaoResult>> run(InterTemporalRaoInput raoInput, RaoParameters parameters) {
Map<OffsetDateTime, RaoResult> raoResultPerTimestamp = new HashMap<>();
for (OffsetDateTime timestamp : raoInput.getTimestampsToRun()) {
RaoResultImpl raoResult = new RaoResultImpl(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getCrac());
raoResult.setComputationStatus(ComputationStatus.FAILURE);
raoResultPerTimestamp.put(timestamp, raoResult);
}
return CompletableFuture.completedFuture(new TemporalDataImpl<>(raoResultPerTimestamp));
}

@Override
public String getName() {
return "GlobalRAOptimizer";
}

@Override
public String getVersion() {
return "2.3";
}
}
Loading

0 comments on commit f23fd92

Please sign in to comment.