LoadFlowFuzzer.java
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.powsybl.cgmes.conversion.*;
import com.powsybl.commons.*;
import com.powsybl.commons.datasource.*;
import com.powsybl.commons.report.*;
import com.powsybl.computation.local.*;
import com.powsybl.contingency.*;
import com.powsybl.ieeecdf.converter.*;
import com.powsybl.iidm.network.*;
import com.powsybl.loadflow.*;
import com.powsybl.loadflow.LoadFlowParameters.*;
import com.powsybl.matpower.converter.*;
import com.powsybl.nad.*;
import com.powsybl.openloadflow.*;
import com.powsybl.powerfactory.converter.*;
import com.powsybl.powerfactory.model.*;
import com.powsybl.psse.converter.*;
import com.powsybl.security.*;
import com.powsybl.sensitivity.*;
import com.powsybl.shortcircuit.*;
import com.powsybl.sld.*;
import com.powsybl.ucte.converter.*;
import com.univocity.parsers.common.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;
public class LoadFlowFuzzer {
private static Path tempFile;
public static void fuzzerInitialize() {
try {
tempFile = Files.createTempFile("fuzz-", "-fuzz");
tempFile.toFile().deleteOnExit();
} catch (Throwable ignored) {
tempFile = null;
}
}
private static Contingency getContingency(FuzzedDataProvider data) {
List<ContingencyElement> allElements = new ArrayList<>();
allElements.add(new BatteryContingency("fuzz"));
allElements.add(new BranchContingency("fuzz"));
allElements.add(new BusbarSectionContingency("fuzz"));
allElements.add(new BusContingency("fuzz"));
allElements.add(new DanglingLineContingency("fuzz"));
allElements.add(new GeneratorContingency("fuzz"));
allElements.add(new HvdcLineContingency("fuzz"));
allElements.add(new LineContingency("fuzz"));
allElements.add(new LoadContingency("fuzz"));
allElements.add(new ShuntCompensatorContingency("fuzz"));
allElements.add(new StaticVarCompensatorContingency("fuzz"));
allElements.add(new SwitchContingency("fuzz"));
allElements.add(new ThreeWindingsTransformerContingency("fuzz"));
allElements.add(new TieLineContingency("fuzz"));
allElements.add(new TwoWindingsTransformerContingency("fuzz"));
// Build random contingency with repetition
Contingency contingency = new Contingency("Fuzz");
for (Integer i = 0; i < 10; i++) {
ContingencyElement elem = allElements.get(data.consumeInt(0, allElements.size() - 1));
contingency.addElement(elem);
}
return contingency;
}
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
// 14 Doubles + 11 Integers + 15 Booleans + 7 pick values + bytes for network
Integer requiredBytes = (14 * 8) + (11 * 4) + (15 * 1) + (7 * 4) + 1;
if ((data.remainingBytes() < requiredBytes) || (tempFile == null)) {
return;
}
// Make the fuzzer consume this first. Do not call
// any data.consume*() methods before this
byte[] loadBytes = data.consumeBytes(10000);
try {
Importer importer = null;
int choice = data.consumeInt(1, 6);
double[] randomDouble = new double[12];
for (int i = 0; i < randomDouble.length; i++) {
randomDouble[i] = data.consumeDouble();
}
Contingency contingency = getContingency(data);
LoadFlowParameters loadFlowParameters = new LoadFlowParameters();
loadFlowParameters.setBalanceType(data.pickValue(EnumSet.allOf(BalanceType.class)));
loadFlowParameters.setVoltageInitMode(data.pickValue(EnumSet.allOf(VoltageInitMode.class)));
loadFlowParameters.setConnectedComponentMode(
data.pickValue(EnumSet.allOf(ConnectedComponentMode.class)));
loadFlowParameters.setDc(data.consumeBoolean());
loadFlowParameters.setDcPowerFactor(data.consumeProbabilityDouble());
loadFlowParameters.setDcUseTransformerRatio(data.consumeBoolean());
loadFlowParameters.setDistributedSlack(data.consumeBoolean());
loadFlowParameters.setHvdcAcEmulation(data.consumeBoolean());
loadFlowParameters.setNoGeneratorReactiveLimits(data.consumeBoolean());
loadFlowParameters.setPhaseShifterRegulationOn(data.consumeBoolean());
loadFlowParameters.setReadSlackBus(data.consumeBoolean());
loadFlowParameters.setShuntCompensatorVoltageControlOn(data.consumeBoolean());
loadFlowParameters.setSimulShunt(data.consumeBoolean());
loadFlowParameters.setTransformerVoltageControlOn(data.consumeBoolean());
loadFlowParameters.setTwtSplitShuntAdmittance(data.consumeBoolean());
loadFlowParameters.setUseReactiveLimits(data.consumeBoolean());
ContingencyContext context =
new ContingencyContext("ID", data.pickValue(EnumSet.allOf(ContingencyContextType.class)));
SensitivityFactor factor =
new SensitivityFactor(
data.pickValue(EnumSet.allOf(SensitivityFunctionType.class)),
"ID",
data.pickValue(EnumSet.allOf(SensitivityVariableType.class)),
"ID",
data.consumeBoolean(),
context);
Properties properties = new Properties();
properties.setProperty("solver", data.pickValue(new String[] {"DEFAULT", "NEWTON", "GAUSS"}));
properties.setProperty("convergence", String.valueOf(data.consumeDouble()));
ReadOnlyMemDataSource ds = new ReadOnlyMemDataSource();
switch (choice) {
case 1:
ds.putData("fuzz", loadBytes);
importer = new CgmesImport();
break;
case 2:
ds.putData(".txt", loadBytes);
importer = new IeeeCdfImporter();
break;
case 3:
ds.putData(".mat", loadBytes);
importer = new MatpowerImporter();
break;
case 4:
ds.putData(".dgs", loadBytes);
byte[] loadBytes2 = data.consumeBytes(10000);
ds.putData(".json", loadBytes2);
importer = new PowerFactoryImporter();
break;
case 5:
ds.putData(".raw", loadBytes);
importer = new PsseImporter();
break;
case 6:
ds.putData(".uct", loadBytes);
importer = new UcteImporter();
break;
default:
return;
}
if (importer != null) {
Network network = null;
try {
network = importer.importData(ds, NetworkFactory.findDefault(), properties);
} catch (NullPointerException e) {
if (importer instanceof PowerFactoryImporter) {
// Wrong format handling
return;
} else {
throw e;
}
}
if (network.getBusView().getBuses().spliterator().getExactSizeIfKnown() > 0
&& network.getGeneratorCount() > 0) {
for (Bus bus : network.getBusView().getBuses()) {
bus.setV(Math.abs(randomDouble[0]));
bus.setAngle(randomDouble[1]);
}
for (Generator generator : network.getGenerators()) {
generator.setMaxP(Math.abs(randomDouble[2]));
generator.setMinP(Math.max(0, randomDouble[3]));
generator.setTargetV(randomDouble[4]);
generator.setTargetP(randomDouble[5]);
generator.setTargetQ(randomDouble[6]);
generator.setRatedS(Math.max(1.0, randomDouble[7]));
}
for (Load load : network.getLoads()) {
load.setP0(randomDouble[8]);
load.setQ0(randomDouble[9]);
}
for (Line line : network.getLines()) {
line.setR(randomDouble[10]);
line.setX(randomDouble[11]);
}
LoadFlow.run(network, loadFlowParameters);
new OpenLoadFlowProvider()
.run(
network,
LocalComputationManager.getDefault(),
"Fuzz",
loadFlowParameters,
ReportNode.NO_OP);
List<Fault> faults = new ArrayList<>();
faults.add(new BranchFault("id", "elemId", randomDouble[12]));
faults.add(new BusFault("id", "elemId"));
ShortCircuitAnalysis.run(network, faults);
List<Contingency> contingencies = new ArrayList<>();
contingencies.add(contingency);
SecurityAnalysis.run(network, contingencies);
List<SensitivityFactor> factors = new ArrayList<>();
factors.add(factor);
SensitivityAnalysis.run(network, factors);
if (tempFile != null) {
NetworkAreaDiagram.draw(network, tempFile);
SingleLineDiagram.draw(network, "fuzz", tempFile);
}
}
}
} catch (PowsyblException
| UncheckedIOException
| PowerFactoryException
| IllegalArgumentException
| TextParsingException e) {
// Fuzzer: silently ignore
} catch (NullPointerException e) {
// Capture known NPE from malformed JSON
if (!isExpected(e)) {
throw e;
}
}
}
private static boolean isExpected(Throwable e) {
String[] expectedString = {
"java.util.Objects.requireNonNull",
"Cannot invoke \"String.hashCode()\"",
"Name is null",
"Cannot invoke \"com.fasterxml.jackson.databind.JsonNode.get(String)\""
};
for (String expected : expectedString) {
if (e.toString().contains(expected)) {
return true;
}
for (StackTraceElement ste : e.getStackTrace()) {
if (ste.toString().contains(expected)) {
return true;
}
}
}
return false;
}
}