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
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2021, 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.identifierlistfilter;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.IFilterAttributes;
import org.gridsuite.filter.utils.EquipmentType;
import org.gridsuite.filter.utils.FilterType;

import java.util.Date;
import java.util.UUID;

/**
* @author Jacques Borsenberger <jacques.borsenberger at rte-france.com>
*/

@Getter
@Setter
@NoArgsConstructor
@SuperBuilder
public class FilterAttributes implements IFilterAttributes {
UUID id;
Date modificationDate;
FilterType type;
EquipmentType equipmentType;
String name;

public FilterAttributes(FilterMetadata filterMetadata, FilterType type, EquipmentType equipmentType) {
id = filterMetadata.getId();
modificationDate = filterMetadata.getModificationDate();
this.type = type;
this.equipmentType = equipmentType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2021, 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.identifierlistfilter;

/**
* @author Jacques Borsenberger <jacques.borsenberger at rte-france.com>
*/
public interface FilterMetadata {
java.util.UUID getId();

java.util.Date getModificationDate();
}
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.utils;

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 <florent.millot@rte-france.com>
*/
public record EquipmentTypesByFilterId(UUID filterId, Set<IdentifiableType> equipmentTypes) {
}
61 changes: 53 additions & 8 deletions src/main/java/org/gridsuite/filter/utils/FilterServiceUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
*/
package org.gridsuite.filter.utils;

import com.powsybl.iidm.network.IdentifiableType;
import com.powsybl.iidm.network.Network;
import org.gridsuite.filter.FilterLoader;
import org.gridsuite.filter.AbstractFilter;
import org.gridsuite.filter.identifierlistfilter.FilterEquipments;
import org.gridsuite.filter.identifierlistfilter.IdentifiableAttributes;
import org.gridsuite.filter.identifierlistfilter.IdentifierListFilter;
import org.gridsuite.filter.*;
import org.gridsuite.filter.expertfilter.ExpertFilter;
import org.gridsuite.filter.identifierlistfilter.*;

import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
Expand Down Expand Up @@ -60,4 +58,51 @@ public static List<FilterEquipments> getFilterEquipmentsFromUuid(Network network
.map(filter -> filter.toFilterEquipments(FilterServiceUtils.getIdentifiableAttributes(filter, network, filterLoader)))
.toList();
}

public static FilteredIdentifiables evaluateFiltersWithEquipmentTypes(FiltersWithEquipmentTypes filtersWithEquipmentTypes, Network network, FilterLoader filterLoader) {
Map<String, IdentifiableAttributes> result = new TreeMap<>();
Map<String, IdentifiableAttributes> notFound = new TreeMap<>();

List<AbstractFilter> abstractFilters = filterLoader.getFilters(filtersWithEquipmentTypes.filters().stream().map(FilterAttributes::getId).toList());

abstractFilters.forEach(filter -> {
Objects.requireNonNull(filter);
UUID filterUuid = filter.getId();
EquipmentType filterEquipmentType = filter.getEquipmentType();
FilteredIdentifiables filteredIdentifiables = filter.toFilteredIdentifiables(FilterServiceUtils.getIdentifiableAttributes(filter, network, filterLoader));

// unduplicate equipments and merge in common lists
if (filteredIdentifiables.notFoundIds() != null) {
filteredIdentifiables.notFoundIds().forEach(element -> notFound.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 contais 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, network, filterLoader).stream())
.forEach(element -> result.put(element.getId(), element));
}
}
}
);
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());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* 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.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.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();
}
}

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.utils;

import org.gridsuite.filter.identifierlistfilter.FilterAttributes;

import java.util.List;

/**
* Store a list of filters and the equipment types that are associated with.
*
* @author Florent MILLOT <florent.millot@rte-france.com>
*/
public record FiltersWithEquipmentTypes(List<FilterAttributes> filters,
List<EquipmentTypesByFilterId> selectedEquipmentTypesByFilter) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void testDetectCycleBetweenExpertFilters() {
ExpertFilter filterB = new ExpertFilter(filterIdB, new Date(), EquipmentType.LINE, combB);

Map<UUID, AbstractFilter> filters = Map.of(filterIdA, filterA, filterIdB, filterB);
FilterLoader loader = ids -> ids.stream().map(filters::get).toList();
FilterLoader loader = uuids -> uuids.stream().map(filters::get).toList();

FilterCycleException ex = assertThrows(FilterCycleException.class, () -> FilterCycleDetector.checkNoCycle(filterA, loader));
assertEquals("Cycle detected in filters", ex.getMessage());
Expand Down Expand Up @@ -90,7 +90,7 @@ void testNoCycle() {
CombinatorExpertRule.builder().combinator(CombinatorType.AND).rules(List.of()).build());

Map<UUID, AbstractFilter> filters = Map.of(filterIdA, filterA, filterIdB, filterB, filterIdC, filterC);
FilterLoader loader = ids -> ids.stream().map(filters::get).toList();
FilterLoader loader = uuids -> uuids.stream().map(filters::get).toList();

try {
FilterCycleDetector.checkNoCycle(filterA, loader);
Expand Down
Loading