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
Binary file modified ZeroMath.jar
Binary file not shown.
4 changes: 4 additions & 0 deletions Zero_engine.alpx
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,10 @@
<Id>1671011596610</Id>
<Name><![CDATA[PRICE]]></Name>
</Option>
<Option>
<Id>1770894434248</Id>
<Name><![CDATA[PRICE_MARKET_FEEDBACK]]></Name>
</Option>
<Option>
<Id>1755522163223</Id>
<Name><![CDATA[BALANCE_LOCAL]]></Name>
Expand Down
7 changes: 3 additions & 4 deletions _alp/Agents/EnergyModel/Code/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@

double startTime1 = System.currentTimeMillis();

int v_timeStepsElapsed_live = v_timeStepsElapsed;
v_timeStepsElapsed=0;
p_timeVariables.updateTimeVariables(v_timeStepsElapsed, p_timeParameters);

//// Store and reset model states
for (J_EA EA : c_energyAssets) {
Expand Down Expand Up @@ -303,10 +306,6 @@

}


int v_timeStepsElapsed_live = v_timeStepsElapsed;
v_timeStepsElapsed=0;

c_profiles.forEach(p -> p.updateValue(p_timeParameters.getRunStartTime_h()));
c_forecasts.forEach(p -> p.initializeForecast(p_timeParameters.getRunStartTime_h()));

Expand Down
13 changes: 9 additions & 4 deletions _alp/Agents/GridConnection/Code/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -567,8 +567,11 @@ else if (j_ea.energyAssetType == OL_EnergyAssetType.PHOTOTHERMAL){
}
} else if (j_ea instanceof J_EAProfile) {
c_profileAssets.remove((J_EAProfile)j_ea);
} else if (j_ea instanceof J_EAChargingSession) {
c_chargingSessions.remove(j_ea);
} else if (j_ea instanceof J_EAChargingSession chargeSession) {
c_chargingSessions.remove(chargeSession);
if(p_chargePoint.isRegistered(chargeSession)){
p_chargePoint.deregisterChargingRequest(chargeSession);
}
} else {
traceln("Unrecognized energy asset %s in gridconnection %s", j_ea, this);
}
Expand Down Expand Up @@ -1093,8 +1096,10 @@ EnergyCoop f_addAssetFlow(OL_AssetFlowCategories AC,J_TimeParameters timeParamet
managementClass = J_ChargingManagementSimple.class;
break;
case PRICE:
managementClass = J_ChargingManagementPrice.class;
//managementClass = J_ChargingManagementPriceScheduled.class;
managementClass = J_ChargingManagementPrice.class;
break;
case PRICE_MARKET_FEEDBACK:
managementClass = J_ChargingManagementPriceScheduled.class;
break;
case BALANCE_LOCAL:
managementClass = J_ChargingManagementLocalBalancing.class;
Expand Down
29 changes: 15 additions & 14 deletions _alp/Classes/Class.J_ActivityTrackerTrips.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public class J_ActivityTrackerTrips extends J_ActivityTracker implements Seriali
public double v_idleTimeToNextTrip_min;
public double v_idleTimeToNextTripStored_min;
public double v_tripDist_km;
public double v_energyNeedForNextTrip_kWh;
public double v_energyNeedForNextTripStored_kWh;
//public double v_energyNeedForNextTrip_kWh;
//public double v_energyNeedForNextTripStored_kWh;
public double v_nextEventStartTime_min;
public double distanceScaling_fr = 1.0;
public double currentTripTimesteps_n;
Expand Down Expand Up @@ -145,7 +145,7 @@ public void setStartIndex(J_TimeVariables timeVariables, I_ChargePointRegistrati

double timeSinceWeekStart_min = getTimeSinceWeekStart(time_min);
boolean looped = false;
while ( starttimes_min.get(v_eventIndex) < (timeSinceWeekStart_min ) ) {
while ( starttimes_min.get(v_eventIndex) < (timeSinceWeekStart_min ) ) { // If this occurs 'during' a trip, that trip is ignored, it is not executed.
setNextTrip(); // Skip to the next trip.

if (v_eventIndex == starttimes_min.size()-1 ) {
Expand Down Expand Up @@ -179,9 +179,9 @@ public void prepareNextActivity(double time_min, I_ChargePointRegistration charg

if (vehicle instanceof J_EAEV ev) {

v_energyNeedForNextTrip_kWh = ev.getEnergyConsumption_kWhpkm() * v_tripDist_km;
if (v_idleTimeToNextTrip_min > 0 && (v_energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh())> v_idleTimeToNextTrip_min/60 * ev.capacityElectric_kW) {
traceln("TripTracker reports: charging need for next trip is not feasible! Time till next trip: %s hours, chargeNeed_kWh: %s", roundToDecimal(v_idleTimeToNextTrip_min/60,2), roundToDecimal(v_energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh(),2));
double energyNeedForNextTrip_kWh = ev.getEnergyConsumption_kWhpkm() * v_tripDist_km;
if (v_idleTimeToNextTrip_min > 0 && (energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh())> v_idleTimeToNextTrip_min/60 * ev.capacityElectric_kW) {
traceln("TripTracker reports: charging need for next trip is not feasible! Time till next trip: %s hours, chargeNeed_kWh: %s", roundToDecimal(v_idleTimeToNextTrip_min/60,2), roundToDecimal(energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh(),2));
}
//v_energyNeedForNextTrip_kWh = min(v_energyNeedForNextTrip_kWh+10,ev.getStorageCapacity_kWh()); // added 10kWh margin 'just in case'. This is actually realistic; people will charge their cars a bit more than strictly needed for the next trip, if possible.
// Check if more charging is needed for next trip!
Expand All @@ -199,10 +199,10 @@ public void prepareNextActivity(double time_min, I_ChargePointRegistration charg
/*if (additionalChargingNeededForNextTrip_kWh>0) {
traceln("*******Additional charging required to prepare for trip after next trip!*********");
}*/
v_energyNeedForNextTrip_kWh += additionalChargingNeededForNextTrip_kWh;
v_energyNeedForNextTrip_kWh = min(v_energyNeedForNextTrip_kWh+10,ev.getStorageCapacity_kWh());
energyNeedForNextTrip_kWh += additionalChargingNeededForNextTrip_kWh;
energyNeedForNextTrip_kWh = min(energyNeedForNextTrip_kWh+10,ev.getStorageCapacity_kWh());
//traceln("TripTracker, energyNeedForNextTrip: %s", v_energyNeedForNextTrip_kWh);
ev.setEnergyNeedForNextTrip_kWh(v_energyNeedForNextTrip_kWh);
ev.setEnergyNeedForNextTrip_kWh(energyNeedForNextTrip_kWh);
/*if ( (v_energyNeedForNextTrip_kWh - EV.getCurrentStateOfCharge() * EV.getStorageCapacity_kWh()) / (v_idleTimeToNextTrip_min/60) > EV.capacityElectric_kW ) {
traceln("Infeasible trip pattern for EV, not enough time to charge for next trip! Required charging power is: " + (v_energyNeedForNextTrip_kWh - EV.getCurrentStateOfCharge() * EV.getStorageCapacity_kWh()) / (v_idleTimeToNextTrip_min/60) + " kW");
traceln("RowIndex: " + rowIndex + " tripDistance: " + v_tripDist_km + " km, time to next trip: " + v_idleTimeToNextTrip_min + " minutes");
Expand Down Expand Up @@ -232,10 +232,10 @@ public double getDistanceScaling_fr( ) {
@Override
public void storeAndResetState() {
v_eventIndexStored = v_eventIndex;
v_energyNeedForNextTripStored_kWh = v_energyNeedForNextTrip_kWh;
//v_energyNeedForNextTripStored_kWh = v_energyNeedForNextTrip_kWh;
v_idleTimeToNextTripStored_min = v_idleTimeToNextTrip_min;
v_eventIndex = 0;
v_energyNeedForNextTrip_kWh = 0;
//v_eventIndex = 0; Taken care of by setStartIndex() call!
//v_energyNeedForNextTrip_kWh = 0;
v_idleTimeToNextTrip_min = 0;
}

Expand All @@ -245,11 +245,12 @@ public void restoreState() {
v_nextEventStartTime_min = starttimes_min.get(v_eventIndex);
v_idleTimeToNextTrip_min = v_idleTimeToNextTripStored_min;
v_tripDist_km = distanceScaling_fr * distances_km.get( v_eventIndex ); // Update upcoming trip distance
v_energyNeedForNextTrip_kWh = v_energyNeedForNextTripStored_kWh;

/*
v_energyNeedForNextTrip_kWh = v_energyNeedForNextTripStored_kWh;
if(vehicle instanceof J_EAEV ev) {
ev.setEnergyNeedForNextTrip_kWh(v_energyNeedForNextTrip_kWh);
}
}*/
}


Expand Down
1 change: 1 addition & 0 deletions _alp/Classes/Class.J_ChargePoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ public List<I_ChargingRequest> getCurrentActiveChargingRequests(){
public void storeStatesAndReset() {
this.storedActiveChargingRequests = new ArrayList<>(this.currentActiveChargingRequests);
this.currentActiveChargingRequests.clear();
//traceln("ChargePoint sessions cleared!");
}

public void restoreStates() {
Expand Down
5 changes: 3 additions & 2 deletions _alp/Classes/Class.J_ChargingManagementLocalBalancing.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,16 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab
GCdemandLowPassed_kW += (currentBalanceBeforeEV_kW - GCdemandLowPassed_kW) * filterDiffGain_r;

for (I_ChargingRequest chargingRequest : chargePoint.getCurrentActiveChargingRequests()) {
double chargeNeedForNextTrip_kWh = chargingRequest.getEnergyNeedForNextTrip_kWh() - chargingRequest.getCurrentSOC_kWh(); // Can be negative if recharging is not needed for next trip!
//double chargeNeedForNextTrip_kWh = chargingRequest.getEnergyNeedForNextTrip_kWh() - chargingRequest.getCurrentSOC_kWh(); // Can be negative if recharging is not needed for next trip!
double chargeNeedForNextTrip_kWh = chargingRequest.getStorageCapacity_kWh() - chargingRequest.getCurrentSOC_kWh(); // Can be negative if recharging is not needed for next trip!
double remainingFlexTime_h = chargePoint.getChargeDeadline_h(chargingRequest) - t_h; // measure of flexiblity left in current charging session.
double avgPowerDemandTillTrip_kW = chargeNeedForNextTrip_kWh / (chargingRequest.getLeaveTime_h() - t_h);
double chargeSetpoint_kW = 0;
if ( t_h >= chargePoint.getChargeDeadline_h(chargingRequest) && chargeNeedForNextTrip_kWh > 0) { // Must-charge time at max charging power
chargeSetpoint_kW = chargePoint.getMaxChargingCapacity_kW(chargingRequest);
} else {
double flexGain_r = 1.0; // how strongly to 'follow' currentBalanceBeforeEV_kW
chargeSetpoint_kW = max(0, avgPowerDemandTillTrip_kW + (GCdemandLowPassed_kW - currentBalanceBeforeEV_kW) * (min(2,remainingFlexTime_h*flexGain_r)));
chargeSetpoint_kW = max(0, avgPowerDemandTillTrip_kW + (GCdemandLowPassed_kW - currentBalanceBeforeEV_kW) * (min(1,remainingFlexTime_h*flexGain_r)));
if ( this.V2GActive && chargePoint.getV2GCapable() && chargingRequest.getV2GCapable() && remainingFlexTime_h > 1 && chargeSetpoint_kW == 0 ) { // Surpluss flexibility
chargeSetpoint_kW = min(0, avgPowerDemandTillTrip_kW - (currentBalanceBeforeEV_kW - GCdemandLowPassed_kW) * (min(1,remainingFlexTime_h*flexGain_r)));
}
Expand Down
83 changes: 43 additions & 40 deletions _alp/Classes/Class.J_ChargingManagementPriceScheduled.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ private static class ActiveSession {
private int currentIdx = 0;
//private double endTime_h;

public ActiveSession(I_ChargingRequest chargingRequest, double[] chargeProfile_kW, double startTime_h) {
public ActiveSession(I_ChargingRequest chargingRequest, double[] chargeProfile_kW) {
this.chargingRequest = chargingRequest;
this.chargeProfile_kW = chargeProfile_kW;
//this.startTime_h = startTime_h;
Expand All @@ -48,11 +48,11 @@ public void charge(J_ChargePoint chargePoint, GridConnection gc, J_TimeVariables
}
}

public void checkIfFinished() {
/*public void checkIfFinished() {
if (currentIdx < chargeProfile_kW.length) {
traceln("Warning!! Charge sesssion aborted too early! Missing %s timesteps!", chargeProfile_kW.length-currentIdx);
}
}
}*/
}

private List<I_ChargingRequest> previousChargingRequests = new ArrayList<>();
Expand All @@ -73,7 +73,7 @@ public OL_ChargingAttitude getCurrentChargingType() {
return activeChargingType;
}
/**
* One of the simplest charging algorithms.
* This charging strategy creates a charging schedule that is quasi-cost-optimal, and allows the inclusion of a market feedback mechanism to reduce excessive charging spikes during minimum price intervals.
*
*/
public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariables) {
Expand All @@ -83,72 +83,74 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab
List<I_ChargingRequest> currentChargingRequests = chargePoint.getCurrentActiveChargingRequests();
for (I_ChargingRequest chargingRequest : currentChargingRequests) {
if (!previousChargingRequests.contains(chargingRequest)) { // Schedule new charging session!
double chargeNeedForNextTrip_kWh = chargingRequest.getEnergyNeedForNextTrip_kWh() - chargingRequest.getCurrentSOC_kWh(); // Can be negative if recharging is not needed for next trip!
//double chargeNeedForNextTrip_kWh = chargingRequest.getEnergyNeedForNextTrip_kWh() - chargingRequest.getCurrentSOC_kWh(); // Can be negative if recharging is not needed for next trip!
double chargeNeedForNextTrip_kWh = chargingRequest.getStorageCapacity_kWh() - chargingRequest.getCurrentSOC_kWh(); // Can be negative if recharging is not needed for next trip!
double maxChargePower_kW = chargePoint.getMaxChargingCapacity_kW(chargingRequest);
// Get session duration
double duration_h = chargingRequest.getLeaveTime_h() - t_h;
int length = (int)ceil(duration_h/timeParameters.getTimeStep_h()); //
if (abs(length*timeParameters.getTimeStep_h() - duration_h) < .0001 && (length*timeParameters.getTimeStep_h() - duration_h) != 0.0) {
traceln("Rounding errors in duration_h! duration_h: %s", duration_h);
}
if (duration_h <= 0) {
traceln("ChargingRequest starting after endtime! Duration_h: %s", duration_h);

//throw new RuntimeException("ChargingRequest starting after endtime!");
}
// Get price curve for duration
double[] priceCurve = Arrays.copyOfRange(gc.energyModel.pp_dayAheadElectricityPricing_eurpMWh.getAllValues(), roundToInt(t_h/timeParameters.getTimeStep_h()), length+roundToInt(t_h/timeParameters.getTimeStep_h()));
double marketFeedback_eurpMWhpkW = 20; // PLACEHOLDER VALUE!
double marketFeedback_eurpMWhpkW = 40; // PLACEHOLDER VALUE!
Market market = new Market(priceCurve, marketFeedback_eurpMWhpkW, 0, 0, 0);
FlexConsumptionAsset asset = new FlexConsumptionAsset(maxChargePower_kW, 20, timeParameters.getTimeStep_h(), length*timeParameters.getTimeStep_h(), null);
if (chargeNeedForNextTrip_kWh > (maxChargePower_kW * timeParameters.getTimeStep_h()*(length) )) {
/*if (chargeNeedForNextTrip_kWh > (maxChargePower_kW * timeParameters.getTimeStep_h()*(length) )) {
traceln("Warning! chargeNeedForNextTrip_kWh is too high for duration of charging session! Deficit: %s", chargeNeedForNextTrip_kWh - (maxChargePower_kW*(timeParameters.getTimeStep_h()*(length))));
} /*else {
} else {
traceln("Charging session long enough for chargeNeedForNextTrip_kWh");
}*/
chargeNeedForNextTrip_kWh=max(0,min(chargeNeedForNextTrip_kWh, maxChargePower_kW * timeParameters.getTimeStep_h()*(length)-0.001));
double[] loadProfile_kW = new double[length];
chargeNeedForNextTrip_kWh=max(0,min(chargeNeedForNextTrip_kWh, maxChargePower_kW * timeParameters.getTimeStep_h()*length));
double[] chargeProfile_kW = new double[length];
if (chargeNeedForNextTrip_kWh>0) {
//traceln("Profile length: %s, price-curve length: %s", loadProfile_kW.length, priceCurve.length);
loadProfile_kW = FlexAssetScheduler.scheduleWrapper(loadProfile_kW, asset, chargeNeedForNextTrip_kWh, market, timeParameters.getTimeStep_h(), false);
double chargeVolumeCheck_kWh = ZeroMath.arraySum(loadProfile_kW) * timeParameters.getTimeStep_h();
if (chargeVolumeCheck_kWh < chargeNeedForNextTrip_kWh - 1e-10) {
chargeProfile_kW = FlexAssetScheduler.scheduleWrapper(chargeProfile_kW, asset, chargeNeedForNextTrip_kWh, market, timeParameters.getTimeStep_h(), false);

/*
double chargeVolumeCheck_kWh = ZeroMath.arraySum(chargeProfile_kW) * timeParameters.getTimeStep_h();
if (DoubleCompare.greaterThanZero(chargeNeedForNextTrip_kWh - chargeVolumeCheck_kWh)) {
traceln("Warning!! Scheduled charging energy is lower than chargeNeedForNextTrip_kWh! Deficit: %s", chargeNeedForNextTrip_kWh - chargeVolumeCheck_kWh);
}
for (int i = 0; i< length; i++) {
if (chargeProfile_kW[i] > maxChargePower_kW) {
double excessChargePower_kW = chargeProfile_kW[i] - maxChargePower_kW;
String err = String.format("Scheduled chargePower greater than maxChargePower_kW!! Excess: %s", excessChargePower_kW);
System.err.print(err);
}
}*/
}
//traceln("Starting session! Profile length: %s, charging need: %s", loadProfile_kW.length, chargeNeedForNextTrip_kWh);
// Store loadProfile_kW. How to keep track of timestep within such a profile?

previousChargingRequests.add(chargingRequest);
activeSessions.add(new ActiveSession(chargingRequest, loadProfile_kW, t_h));
activeSessions.add(new ActiveSession(chargingRequest, chargeProfile_kW));
}
}
// Execute charging of active sessions
for (ActiveSession session : activeSessions) {
//int index = roundToInt((t_h - session.startTime_h)/timeParameters.getTimeStep_h());
//if (session.chargeProfile_kW.length>0) {
if (!currentChargingRequests.contains(session.chargingRequest)) { // || index == session.chargeProfile_kW.length) {
session.isFinished=true;
session.checkIfFinished();
//} else if (timeParameters.getTimeStep_h()<session.chargingRequest.getLeaveTime_h() && session.chargeProfile_kW.length>0) {
//throw new RuntimeException("ChargingSession was prematurely aborted!");
traceln("Warning! ChargingSession was prematurely aborted! Ignore this warning if it occurs after changing EV-sliders during live-sim.");
//traceln("ChargingRequest: %s", session.chargingRequest);
session.isFinished = true;
previousChargingRequests.remove(session.chargingRequest);
} else if (session.chargeProfile_kW.length>0) {
//chargePoint.charge(session.chargingRequest, session.chargeProfile_kW[index], timeVariables, gc);
session.charge(chargePoint, gc, timeVariables);
}

if (session.isFinished) {
previousChargingRequests.remove(session.chargingRequest);
}
/*if (index == session.chargeProfile_kW.length-1 || session.chargeProfile_kW.length==0) { // session ending, remove session from list
//traceln("Ending session! Profile length: %s", session.chargeProfile_kW.length);
previousChargingRequests.remove(session.chargingRequest);
//activeSessions.remove(session);
session.isFinished = true;
}*/
//traceln("Scheduled charging, profile length: %s, current index: %s, current power: %s kW", session.chargeProfile_kW.length, index, session.chargeProfile_kW[index]);
if (session.isFinished) {
previousChargingRequests.remove(session.chargingRequest);
}
}
}
activeSessions.removeIf(session -> session.isFinished); // Must be outside of for-loop over this collection!
}

public void abortSession(I_ChargingRequest chargingRequest) {
previousChargingRequests.remove(chargingRequest);
ActiveSession session = findFirst(activeSessions, x -> x.chargingRequest == chargingRequest);
activeSessions.remove(session);
}

public void setV2GActive(boolean activateV2G) {
throw new RuntimeException("ChargingManagementPriceScheduled does not support V2G charging!");
/*
Expand All @@ -172,8 +174,9 @@ public void storeStatesAndReset() {
previousChargingRequestsStored = previousChargingRequests;
activeSessionsStored = activeSessions;

previousChargingRequests = new ArrayList<>();
activeSessions = new ArrayList<>();
previousChargingRequests = new ArrayList<>(); // Don't use clear()! It will also clear the 'stored' list; as it's not a copy, just a pointer to the same list!
activeSessions = new ArrayList<>();
//traceln("active session reset!");
}
public void restoreStates() {
previousChargingRequests = previousChargingRequestsStored;
Expand Down
Loading