Skip to content
Open
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
368 changes: 368 additions & 0 deletions Team 58- EnergySync/README

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions Team 58- EnergySync/smartgrid/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package smartgrid;

import smartgrid.services.SimulationEngine;
import smartgrid.ui.GridUI;

import javax.swing.SwingUtilities;

/**
* Application entry point.
* Instantiates SimulationEngine and passes it to GridUI.
* UI is launched on the Swing Event Dispatch Thread.
*/
public class Main {

public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
SimulationEngine engine = new SimulationEngine();
new GridUI(engine);
});
}
}
85 changes: 85 additions & 0 deletions Team 58- EnergySync/smartgrid/algorithms/BFSAlgorithm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package smartgrid.algorithms;

import smartgrid.model.*;

import java.util.*;

/**
* BFS fault impact detection.
* Finds CityZones that are TRULY cut off after a fault — i.e. zones that
* were exclusively supplied through the faulted node and have no alternate
* path from any surviving active EnergySource.
*
* Why this matters: a zone like Zone-2 may be reachable via both
* Solar→Sub-A and Wind→Sub-B. Losing Solar should NOT mark Zone-2 as FAULT
* because Wind can still supply it via Sub-B.
*
* Time complexity: O(V + E)
*/
public class BFSAlgorithm {

private final Graph graph;

public BFSAlgorithm(Graph graph) {
this.graph = graph;
}

/**
* Runs BFS from {@code faultedNodeId} to find affected CityZones.
* Only marks a zone FAULT if it is NOT reachable from any surviving
* active EnergySource.
*
* @return number of CityZones marked FAULT (truly isolated zones)
*/
public int run(int faultedNodeId) {
// Step 1: find all CityZones reachable from the faulted node
Set<Integer> reachableFromFault = new HashSet<>();
Queue<Integer> queue = new LinkedList<>();
queue.offer(faultedNodeId);
reachableFromFault.add(faultedNodeId);

while (!queue.isEmpty()) {
int cur = queue.poll();
for (Edge e : graph.getNeighbors(cur)) {
if (!reachableFromFault.contains(e.to)) {
reachableFromFault.add(e.to);
queue.offer(e.to);
}
}
}

// Step 2: find all nodes reachable from surviving active EnergySources
Set<Integer> reachableFromSurvivors = new HashSet<>();
for (Node n : graph.getNodes()) {
if (n.type == NodeType.ENERGY_SOURCE
&& n.status == NodeStatus.ACTIVE
&& n.id != faultedNodeId) {
Queue<Integer> q = new LinkedList<>();
q.offer(n.id);
reachableFromSurvivors.add(n.id);
while (!q.isEmpty()) {
int cur = q.poll();
for (Edge e : graph.getNeighbors(cur)) {
if (!reachableFromSurvivors.contains(e.to)) {
reachableFromSurvivors.add(e.to);
q.offer(e.to);
}
}
}
}
}

// Step 3: mark FAULT only zones that lost their ONLY supply path
int affectedZones = 0;
for (int id : reachableFromFault) {
Node n = graph.getNode(id);
if (n != null && n.type == NodeType.CITY_ZONE
&& !reachableFromSurvivors.contains(id)) {
n.status = NodeStatus.FAULT;
affectedZones++;
}
}

return affectedZones;
}
}
148 changes: 148 additions & 0 deletions Team 58- EnergySync/smartgrid/algorithms/BellmanFordAlgorithm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package smartgrid.algorithms;

import smartgrid.model.*;

import java.util.*;

/**
* Bellman-Ford with negative-cycle detection for GRID STRESS ANALYSIS.
*
* Use case (why not a threshold check):
* A threshold check tells you one edge is overloaded.
* Bellman-Ford tells you a CYCLE of mutually-overloaded edges exists —
* a cascading failure loop that a threshold check cannot detect.
* That distinction is why Bellman-Ford belongs here.
*
* Stress cost assignment:
* Edges at >90% capacity get cost = -(overcapacity_amount).
* If a negative-cost cycle exists in the residual graph, it means
* a stress loop is present → automatically triggers Topological Sort
* for load shedding.
*
* Time complexity: O(V * E)
*/
public class BellmanFordAlgorithm {

private final Graph graph;

public static class Result {
public final Map<Integer, Double> dist;
public final Map<Integer, Integer> prev;
public final boolean hasNegativeCycle;
public final List<Integer> stressedNodeIds; // nodes in negative cycle
public final String summary;

public Result(Map<Integer, Double> dist, Map<Integer, Integer> prev,
boolean hasNegativeCycle, List<Integer> stressedNodeIds, String summary) {
this.dist = dist;
this.prev = prev;
this.hasNegativeCycle = hasNegativeCycle;
this.stressedNodeIds = stressedNodeIds;
this.summary = summary;
}
}

public BellmanFordAlgorithm(Graph graph) {
this.graph = graph;
}

/**
* Runs Bellman-Ford from {@code sourceId} using STRESS costs:
* - Edges at >90% capacity: cost = -(flow - 0.9*capacity) [negative = stressed]
* - All other edges: cost = effectiveCost (normal)
*
* A negative cycle means a loop of stressed edges exists → cascade risk.
*/
public Result run(int sourceId) {
Map<Integer, Double> dist = new HashMap<>();
Map<Integer, Integer> prev = new HashMap<>();

for (Node n : graph.getNodes()) {
dist.put(n.id, Double.MAX_VALUE / 2);
prev.put(n.id, -1);
}
dist.put(sourceId, 0.0);

// Build stress-adjusted edge list
List<Edge> allEdges = new ArrayList<>();
for (Node n : graph.getNodes()) {
if (n.status != NodeStatus.ISOLATED) {
for (Edge e : graph.getNeighbors(n.id)) {
allEdges.add(e);
}
}
}

int V = graph.getNodes().size();

// Relax V-1 times using stress costs
for (int i = 0; i < V - 1; i++) {
boolean updated = false;
for (Edge e : allEdges) {
Node src = graph.getNode(e.from);
Node dst = graph.getNode(e.to);
if (src == null || dst == null) continue;
if (src.status == NodeStatus.ISOLATED || dst.status == NodeStatus.ISOLATED) continue;
if (dist.get(e.from) >= Double.MAX_VALUE / 2) continue;

double stressCost = stressCost(e);
double newDist = dist.get(e.from) + stressCost;
if (newDist < dist.get(e.to)) {
dist.put(e.to, newDist);
prev.put(e.to, e.from);
updated = true;
}
}
if (!updated) break;
}

// V-th relaxation: detect negative cycles
boolean hasNegativeCycle = false;
List<Integer> stressedNodes = new ArrayList<>();
for (Edge e : allEdges) {
Node src = graph.getNode(e.from);
Node dst = graph.getNode(e.to);
if (src == null || dst == null) continue;
if (src.status == NodeStatus.ISOLATED || dst.status == NodeStatus.ISOLATED) continue;
if (dist.get(e.from) >= Double.MAX_VALUE / 2) continue;

double stressCost = stressCost(e);
if (dist.get(e.from) + stressCost < dist.get(e.to)) {
hasNegativeCycle = true;
if (!stressedNodes.contains(e.from)) stressedNodes.add(e.from);
if (!stressedNodes.contains(e.to)) stressedNodes.add(e.to);
}
}

String summary = hasNegativeCycle
? "Bellman-Ford: stress cycle detected → load shed initiated (" + stressedNodes.size() + " nodes)"
: "Bellman-Ford: grid stable, no stress cycles";

return new Result(dist, prev, hasNegativeCycle, stressedNodes, summary);
}

/**
* Stress cost for an edge:
* >90% capacity → negative cost (signals stress)
* otherwise → effectiveCost (normal routing cost)
*/
private double stressCost(Edge e) {
if (e.capacity > 0 && e.flow > 0.9 * e.capacity) {
return -(e.flow - 0.9 * e.capacity); // negative = stressed
}
return e.effectiveCost;
}

public List<Integer> reconstructPath(Map<Integer, Integer> prev, int sourceId, int targetId) {
List<Integer> path = new ArrayList<>();
int cur = targetId;
int safety = 0;
while (cur != -1 && cur != sourceId && safety++ < 100) {
path.add(0, cur);
cur = prev.getOrDefault(cur, -1);
}
if (cur == sourceId) path.add(0, sourceId);
else path.clear();
return path;
}
}
84 changes: 84 additions & 0 deletions Team 58- EnergySync/smartgrid/algorithms/DFSAlgorithm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package smartgrid.algorithms;

import smartgrid.model.*;

import java.util.*;

/**
* DFS faulty subgraph isolation.
* Traverses forward-directed edges from the faulted node and marks
* every visited node as ISOLATED — BUT only if that node has no
* alternate supply path from a remaining active EnergySource.
*
* Why: a node reachable from the faulted source may also be reachable
* from Wind or Hydro. Marking it ISOLATED would wrongly cut it off from
* rerouting. We only truly isolate nodes that are EXCLUSIVELY fed by
* the faulted source.
*
* Time complexity: O(V + E)
*/
public class DFSAlgorithm {

private final Graph graph;

public DFSAlgorithm(Graph graph) {
this.graph = graph;
}

/**
* Runs DFS from {@code faultedNodeId}, marking reachable nodes ISOLATED
* only when they have no alternate path from a surviving active EnergySource.
*
* @param faultedNodeId the node that just failed (already removed from graph
* or marked ISOLATED by the caller before this runs)
*/
public void run(int faultedNodeId) {
// Step 1: collect all nodes reachable from the faulted node via DFS
Set<Integer> reachableFromFault = new HashSet<>();
Deque<Integer> stack = new ArrayDeque<>();
stack.push(faultedNodeId);

while (!stack.isEmpty()) {
int cur = stack.pop();
if (reachableFromFault.contains(cur)) continue;
reachableFromFault.add(cur);
for (Edge e : graph.getNeighbors(cur)) {
if (!reachableFromFault.contains(e.to)) stack.push(e.to);
}
}

// Step 2: collect all nodes reachable from ANY other active EnergySource
// These nodes still have a live supply path — do NOT isolate them
Set<Integer> reachableFromSurvivors = new HashSet<>();
for (Node n : graph.getNodes()) {
if (n.type == NodeType.ENERGY_SOURCE
&& n.status == NodeStatus.ACTIVE
&& n.id != faultedNodeId) {
// BFS/DFS from this surviving source
Deque<Integer> q = new ArrayDeque<>();
q.push(n.id);
while (!q.isEmpty()) {
int cur = q.pop();
if (reachableFromSurvivors.contains(cur)) continue;
reachableFromSurvivors.add(cur);
for (Edge e : graph.getNeighbors(cur)) {
if (!reachableFromSurvivors.contains(e.to)) q.push(e.to);
}
}
}
}

// Step 3: mark ISOLATED only nodes that are reachable from the fault
// but NOT reachable from any surviving source
// — these are truly cut off and should be excluded from routing
for (int id : reachableFromFault) {
if (id == faultedNodeId) continue; // already marked by caller
if (!reachableFromSurvivors.contains(id)) {
Node n = graph.getNode(id);
if (n != null) n.status = NodeStatus.ISOLATED;
}
// If reachable from a survivor, leave status as-is (ACTIVE or FAULT)
// autoReroute will set it to REROUTED once flow reaches it
}
}
}
Loading