Skip to content
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
11 changes: 6 additions & 5 deletions src/main/java/org/gridsuite/filter/server/FilterController.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.gridsuite.filter.identifierlistfilter.FilteredIdentifiables;
import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes;
import org.gridsuite.filter.server.dto.FilterAttributes;
import org.gridsuite.filter.server.dto.FiltersWithEquipmentTypes;
import org.gridsuite.filter.server.dto.IdsByGroup;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -201,15 +202,15 @@ public ResponseEntity<List<FilterEquipments>> exportFilters(@RequestParam("ids")
.body(ret);
}

@GetMapping(value = "/filters/evaluate/identifiables", produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = "/filters/evaluate/identifiables", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Export matched identifiables elements to JSON format")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "The list of matched elements")
})
public ResponseEntity<FilteredIdentifiables> evaluateFilters(@RequestParam("ids") List<UUID> ids,
@RequestParam(value = "networkUuid") UUID networkUuid,
@RequestParam(value = "variantUuid", required = false) String variantUuid) {
FilteredIdentifiables identifiableAttributes = service.evaluateFilters(ids, networkUuid, variantUuid);
public ResponseEntity<FilteredIdentifiables> evaluateFiltersWithEquipmentTypes(@RequestParam(value = "networkUuid") UUID networkUuid,
@RequestParam(value = "variantUuid", required = false) String variantUuid,
@RequestBody FiltersWithEquipmentTypes filters) {
FilteredIdentifiables identifiableAttributes = service.evaluateFiltersWithEquipmentTypes(filters, networkUuid, variantUuid);
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(identifiableAttributes);
}

Expand Down
49 changes: 40 additions & 9 deletions src/main/java/org/gridsuite/filter/server/FilterService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@
package org.gridsuite.filter.server;

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.IdentifiableType;
import com.powsybl.iidm.network.Network;
import com.powsybl.network.store.client.NetworkStoreService;
import com.powsybl.network.store.client.PreloadingStrategy;
import org.apache.commons.collections4.CollectionUtils;
import org.gridsuite.filter.AbstractFilter;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.IFilterAttributes;
import org.gridsuite.filter.expertfilter.ExpertFilter;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes;
import org.gridsuite.filter.identifierlistfilter.FilteredIdentifiables;
import org.gridsuite.filter.server.dto.EquipmentTypesByFilterId;
import org.gridsuite.filter.server.dto.FilterAttributes;
import org.gridsuite.filter.server.dto.FiltersWithEquipmentTypes;
import org.gridsuite.filter.server.dto.IdsByGroup;
import org.gridsuite.filter.server.entities.AbstractFilterEntity;
import org.gridsuite.filter.server.repositories.FilterRepository;
Expand All @@ -27,6 +31,8 @@
import org.gridsuite.filter.server.repositories.proxies.expertfiler.ExpertFilterRepositoryProxy;
import org.gridsuite.filter.server.repositories.proxies.identifierlistfilter.IdentifierListFilterRepositoryProxy;
import org.gridsuite.filter.server.service.DirectoryService;
import org.gridsuite.filter.server.utils.FilterWithEquipmentTypesUtils;
import org.gridsuite.filter.utils.EquipmentType;
import org.gridsuite.filter.utils.FilterServiceUtils;
import org.gridsuite.filter.utils.FilterType;
import org.gridsuite.filter.utils.expertfilter.FilterCycleDetector;
Expand All @@ -37,6 +43,7 @@
import org.springframework.web.server.ResponseStatusException;

import java.util.*;
import java.util.Comparator;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -277,32 +284,56 @@ public List<IdentifiableAttributes> evaluateFilter(AbstractFilter filter, UUID n
}

@Transactional(readOnly = true)
public FilteredIdentifiables evaluateFilters(List<UUID> filters, UUID networkUuid, String variantId) {
public FilteredIdentifiables evaluateFiltersWithEquipmentTypes(FiltersWithEquipmentTypes filtersWithEquipmentTypes, UUID networkUuid, String variantId) {
Map<String, IdentifiableAttributes> result = new TreeMap<>();
Map<String, IdentifiableAttributes> notFound = new TreeMap<>();
Network network = getNetwork(networkUuid, variantId);
FilterLoader filterLoader = new FilterLoaderImpl(filterRepositories);

filters.forEach((UUID filterUuid) -> {
filtersWithEquipmentTypes.filters().forEach((FilterAttributes filterAttributes) -> {
UUID filterUuid = filterAttributes.getId();
Optional<AbstractFilter> optFilter = getFilterFromRepository(filterUuid);
if (optFilter.isEmpty()) {
return;
}
AbstractFilter filter = optFilter.get();
Objects.requireNonNull(filter);
FilterLoader filterLoader = new FilterLoaderImpl(filterRepositories);
FilteredIdentifiables filterIdentiables = filter.toFilteredIdentifiables(FilterServiceUtils.getIdentifiableAttributes(filter, network, filterLoader));
EquipmentType filterEquipmentType = filter.getEquipmentType();
FilteredIdentifiables filteredIdentifiables = filter.toFilteredIdentifiables(FilterServiceUtils.getIdentifiableAttributes(filter, network, filterLoader));

// unduplicate equipments and merge in common lists
if (filterIdentiables.notFoundIds() != null) {
filterIdentiables.notFoundIds().forEach(element -> notFound.put(element.getId(), element));
if (filteredIdentifiables.notFoundIds() != null) {
filteredIdentifiables.notFoundIds().forEach(element -> notFound.put(element.getId(), element));
}

if (filterIdentiables.equipmentIds() != null) {
filterIdentiables.equipmentIds().forEach(element -> result.put(element.getId(), element));
if (filteredIdentifiables.equipmentIds() != null) {
if (filterEquipmentType != EquipmentType.SUBSTATION && filterEquipmentType != EquipmentType.VOLTAGE_LEVEL) {
filteredIdentifiables.equipmentIds().forEach(element -> result.put(element.getId(), element));
} else {
Set<IdentifiableType> selectedEquipmentTypes = filtersWithEquipmentTypes.selectedEquipmentTypesByFilter()
.stream()
.filter(equipmentTypesByFilterId -> equipmentTypesByFilterId.filterId().equals(filterUuid))
.findFirst()
.map(EquipmentTypesByFilterId::equipmentTypes)
.orElseThrow(
() -> new IllegalStateException("No selected equipment types for filter " + filterUuid
+ " : substation and voltage level filters should contain an equipment types list")
);

// This list is the result of the original filter and so necessarily contains a list of IDs of substations or voltage levels
Set<String> filteredEquipmentIds = filteredIdentifiables.equipmentIds().stream().map(IdentifiableAttributes::getId).collect(Collectors.toSet());
List<ExpertFilter> filters = FilterWithEquipmentTypesUtils.createFiltersForSubEquipments(filterEquipmentType,
filteredEquipmentIds,
selectedEquipmentTypes);
filters.stream().flatMap(expertFilter -> getIdentifiableAttributes(expertFilter, networkUuid, variantId, filterLoader).stream())
.forEach(element -> result.put(element.getId(), element));
}
}
}
);
return new FilteredIdentifiables(result.values().stream().toList(), notFound.values().stream().toList());
return new FilteredIdentifiables(
result.values().stream().sorted(Comparator.comparing(e -> e.getType().ordinal())).toList(),
notFound.values().stream().sorted(Comparator.comparing(e -> e.getType().ordinal())).toList());
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2025, 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 org.gridsuite.filter.server.dto;

import com.powsybl.iidm.network.IdentifiableType;

import java.util.Set;
import java.util.UUID;

/**
* Store a list of equipment types for a given filter ID
*
* @author Florent MILLOT <[email protected]>
*/
public record EquipmentTypesByFilterId(UUID filterId, Set<IdentifiableType> equipmentTypes) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2025, 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 org.gridsuite.filter.server.dto;

import java.util.List;

/**
* Store a list of filters and the equipment types that are associated with.
*
* @author Florent MILLOT <[email protected]>
*/
public record FiltersWithEquipmentTypes(List<FilterAttributes> filters,
List<EquipmentTypesByFilterId> selectedEquipmentTypesByFilter) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* Copyright (c) 2025, 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 org.gridsuite.filter.server.utils;

import com.powsybl.iidm.network.IdentifiableType;
import org.gridsuite.filter.expertfilter.ExpertFilter;
import org.gridsuite.filter.expertfilter.expertrule.AbstractExpertRule;
import org.gridsuite.filter.expertfilter.expertrule.CombinatorExpertRule;
import org.gridsuite.filter.expertfilter.expertrule.StringExpertRule;
import org.gridsuite.filter.utils.EquipmentType;
import org.gridsuite.filter.utils.expertfilter.CombinatorType;
import org.gridsuite.filter.utils.expertfilter.FieldType;
import org.gridsuite.filter.utils.expertfilter.OperatorType;

import java.util.List;
import java.util.Set;

import static org.gridsuite.filter.utils.expertfilter.FieldType.*;

/**
* Sometimes, when we apply a filter on a substation or voltage level,
* we actually want the equipments related to these substations or voltage levels.
* This class is used to build these special filters,
* and we can specify which equipment types we are interested into.
* PS : We could have used a mix of expert filters with the operator IS_PART_OF
* but in this case we also need the notFoundIds of the original filter,
* which is not possible because it is different equipment types.
* @author Florent MILLOT <florent.millot at rte-france.com>
*/
public final class FilterWithEquipmentTypesUtils {

private FilterWithEquipmentTypesUtils() {
// Utility class
}

/**
* Create one filter for each equipment type we want to retrieve from a previous filter result.
* @param filterEquipmentType the equipment type of the original filter (substation or voltage level)
* @param filteredEquipmentIDs the filtered equipment IDs of the original filter
* @param subEquipmentTypes the equipment types we want to collect from the original filter result (so the equipments related to filteredEquipmentIDs)
* @return the list of filters
*/
public static List<ExpertFilter> createFiltersForSubEquipments(EquipmentType filterEquipmentType, Set<String> filteredEquipmentIDs, Set<IdentifiableType> subEquipmentTypes) {
return switch (filterEquipmentType) {
case SUBSTATION -> subEquipmentTypes.stream().map(identifiableType -> new ExpertFilter(
null,
null,
EquipmentType.valueOf(identifiableType.name()),
createSubstationRuleByEquipmentType(identifiableType, filteredEquipmentIDs))).toList();
case VOLTAGE_LEVEL -> subEquipmentTypes.stream().map(identifiableType -> new ExpertFilter(
null,
null,
EquipmentType.valueOf(identifiableType.name()),
createVoltageLevelRuleByEquipmentType(identifiableType, filteredEquipmentIDs))).toList();
default ->
throw new UnsupportedOperationException("Unsupported filter equipment type " + filterEquipmentType
+ " : we can only filter sub equipments from substation and voltage level");
};
}

public static AbstractExpertRule createSubstationRuleByEquipmentType(IdentifiableType equipmentType, Set<String> substationIds) {
return switch (equipmentType) {
case LOAD, GENERATOR, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR, BUSBAR_SECTION, BATTERY,
DANGLING_LINE, TWO_WINDINGS_TRANSFORMER, THREE_WINDINGS_TRANSFORMER -> createRuleWithOneField(substationIds, SUBSTATION_ID);
case LINE, HVDC_LINE -> createRuleWithTwoFields(substationIds, SUBSTATION_ID_1, SUBSTATION_ID_2);
default -> throw new UnsupportedOperationException("Unsupported equipment type " + equipmentType);
};
}

public static AbstractExpertRule createVoltageLevelRuleByEquipmentType(IdentifiableType equipmentType, Set<String> voltageLevelIds) {
return switch (equipmentType) {
case LOAD, GENERATOR, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR, BUSBAR_SECTION, BATTERY,
DANGLING_LINE -> createRuleWithOneField(voltageLevelIds, VOLTAGE_LEVEL_ID);
case LINE, HVDC_LINE, TWO_WINDINGS_TRANSFORMER -> createRuleWithTwoFields(voltageLevelIds, VOLTAGE_LEVEL_ID_1, VOLTAGE_LEVEL_ID_2);
case THREE_WINDINGS_TRANSFORMER -> createRuleWithThreeFields(voltageLevelIds, VOLTAGE_LEVEL_ID_1, VOLTAGE_LEVEL_ID_2, VOLTAGE_LEVEL_ID_3);
default -> throw new UnsupportedOperationException("Unsupported equipment type " + equipmentType);
};
}

public static AbstractExpertRule createRuleWithOneField(Set<String> equipmentIds, FieldType field) {
return StringExpertRule.builder()
.operator(OperatorType.IN).field(field)
.values(equipmentIds).build();
}

public static AbstractExpertRule createRuleWithTwoFields(Set<String> equipmentIds, FieldType field1, FieldType field2) {
StringExpertRule rule1 = StringExpertRule.builder()
.operator(OperatorType.IN).field(field1)
.values(equipmentIds).build();
StringExpertRule rule2 = StringExpertRule.builder()
.operator(OperatorType.IN).field(field2)
.values(equipmentIds).build();
return CombinatorExpertRule.builder().combinator(CombinatorType.OR).rules(List.of(rule1, rule2)).build();
}

public static AbstractExpertRule createRuleWithThreeFields(Set<String> equipmentIds, FieldType field1, FieldType field2, FieldType field3) {
StringExpertRule rule1 = StringExpertRule.builder()
.operator(OperatorType.IN).field(field1)
.values(equipmentIds).build();
StringExpertRule rule2 = StringExpertRule.builder()
.operator(OperatorType.IN).field(field2)
.values(equipmentIds).build();
StringExpertRule rule3 = StringExpertRule.builder()
.operator(OperatorType.IN).field(field3)
.values(equipmentIds).build();
return CombinatorExpertRule.builder().combinator(CombinatorType.OR).rules(List.of(rule1, rule2, rule3)).build();
}
}

Loading