Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

inter-tempoal RAO API #1236

Merged
merged 4 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading