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
9 changes: 9 additions & 0 deletions Zero_engine.alpx
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,10 @@
<Id>1752585441102</Id>
<Name><![CDATA[PEAK_SHAVING_FORECAST]]></Name>
</Option>
<Option>
<Id>1773842689011</Id>
<Name><![CDATA[LOCAL_BALANCING]]></Name>
</Option>
<Option>
<Id>1752585822134</Id>
<Name><![CDATA[CUSTOM]]></Name>
Expand Down Expand Up @@ -2023,6 +2027,11 @@ EXCLUDE_PV => Use PV profile to preprocess gridnode profile to create a more acc
<Name><![CDATA[I_AggregatorManagement]]></Name>
<Folder>1772100333519</Folder>
</JavaClass>
<JavaClass>
<Id>1773841546071</Id>
<Name><![CDATA[J_BatteryManagementLocalBalancing]]></Name>
<Folder>1772100322546</Folder>
</JavaClass>
<JavaClass>
<Id>1773139549641</Id>
<Name><![CDATA[J_EAConversionAirConditioner]]></Name>
Expand Down
18 changes: 7 additions & 11 deletions _alp/Classes/Class.J_ActivityTrackerTrips.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/**
* J_ActivityTrackerTrips
*/
import java.util.ArrayList;
import java.util.ListIterator;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class, property = "@id")
Expand Down Expand Up @@ -175,24 +178,22 @@ public void prepareNextActivity(double time_min, I_ChargePointRegistration charg
} else {
nextEventStartTime_h = (nextEventStartTime_min + time_min - timeSinceWeekStart_min)/60;
}
// traceln("Prepare next activity, trip startTime: %s hours. Time since week start: %s", nextEventStartTime_h, (timeSinceWeekStart_min)/60);
idleTimeToNextTrip_min = (nextEventStartTime_min - timeSinceWeekStart_min) % (24*7*60); // Modulo 24*7*60 needed because otherwise negative values can occur when trip starts 'next week'.
tripDistance_km = distanceScaling_fr * distances_km.get( eventIndex ); // Update upcoming trip distance

if (vehicle instanceof J_EAEV ev) {

double energyNeedForNextTrip_kWh = ev.getEnergyConsumption_kWhpkm() * tripDistance_km;
if (idleTimeToNextTrip_min > 0 && (energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh())> idleTimeToNextTrip_min/60 * ev.capacityElectric_kW) {
if (idleTimeToNextTrip_min > 0 && (energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh())> idleTimeToNextTrip_min/60 * ev.getVehicleChargingCapacity_kW()) {
traceln("TripTracker reports: charging need for next trip is not feasible! Time till next trip: %s hours, chargeNeed_kWh: %s", roundToDecimal(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!
double nextTripDist_km = 0;
double nextTripStartTime_min = 0;

if ( eventIndex == starttimes_min.size() - 1 ) {
nextTripDist_km = 0;//distances_km.get( 0 );
nextTripStartTime_min = endtimes_min.get(eventIndex);
nextTripDist_km = distances_km.get( 0 );
nextTripStartTime_min = endtimes_min.get( 0 );
} else {
nextTripDist_km = distanceScaling_fr*distances_km.get( eventIndex+1 );
nextTripStartTime_min = starttimes_min.get( eventIndex+1 );
Expand All @@ -201,13 +202,8 @@ public void prepareNextActivity(double time_min, I_ChargePointRegistration charg

energyNeedForNextTrip_kWh += additionalChargingNeededForNextTrip_kWh;
energyNeedForNextTrip_kWh = min(energyNeedForNextTrip_kWh+10,ev.getStorageCapacity_kWh());
//traceln("TripTracker, energyNeedForNextTrip: %s", v_energyNeedForNextTrip_kWh);
ev.setEnergyNeedForNextTrip_kWh(energyNeedForNextTrip_kWh);
/*if ( (v_energyNeedForNextTrip_kWh - EV.getCurrentStateOfCharge() * EV.getStorageCapacity_kWh()) / (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()) / (idleTimeToNextTrip_min/60) + " kW");
traceln("RowIndex: " + rowIndex + " tripDistance: " + tripDistance_km + " km, time to next trip: " + idleTimeToNextTrip_min + " minutes");
} */


//Register EV at the chargepoint
chargePointRegistration.registerChargingRequest(ev);
}
Expand Down
104 changes: 104 additions & 0 deletions _alp/Classes/Class.J_BatteryManagementLocalBalancing.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* J_BatteryManagementLocalBalancing
*/
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;

@JsonAutoDetect(
fieldVisibility = Visibility.ANY, //
getterVisibility = Visibility.NONE,
isGetterVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE,
creatorVisibility = Visibility.NONE
)

public class J_BatteryManagementLocalBalancing implements I_BatteryManagement {

private GridConnection gc;
private J_TimeParameters timeParameters;

// Parameters used:
private double filterTimeScale_h = 5*24;
private double filterDiffGain_r;
private double initialValueGCdemandLowPassed_kW = 0.5;
private double GCdemandLowPassed_kW = this.initialValueGCdemandLowPassed_kW;
private double storedGCdemandLowPassed_kW;

private double SOC_setpoint_fr = 0.5; // If there are no other influences such as vehicles or production the battery will aim for this SOC_fr
//private double feedbackGain_fr = 1.5; // This parameter determines how strongly to aim for the SOC setpoint
private double feedbackGain_kWpSOC;
private double balancingGain_fr = 0.25; // How much 'peakshaving' around the average load? 1.0 is 100% peakshaving, which would result in an approximately flat profile, if the battery is big enough.
/**
* Default constructor
*/
public J_BatteryManagementLocalBalancing() {

}

public J_BatteryManagementLocalBalancing( GridConnection gc, J_TimeParameters timeParameters ) {
this.gc = gc;
this.timeParameters = timeParameters;
this.filterDiffGain_r = 1/(filterTimeScale_h/timeParameters.getTimeStep_h());
double feedbackGain_ph = 1/5;
this.feedbackGain_kWpSOC = gc.p_batteryAsset.getStorageCapacity_kWh() * feedbackGain_ph;
}

public J_BatteryManagementLocalBalancing( GridConnection gc, double SOC_setpoint_fr, double feedbackGain_ph, J_TimeParameters timeParameters ) {
this.gc = gc;
this.timeParameters = timeParameters;
this.filterDiffGain_r = 1/(filterTimeScale_h/timeParameters.getTimeStep_h());
this.SOC_setpoint_fr = SOC_setpoint_fr;
this.feedbackGain_kWpSOC = gc.p_batteryAsset.getStorageCapacity_kWh() * feedbackGain_ph;
}

/**
* This algorithm tries to aim for a flat load profile by using the battery to steer towards the a weighted averaged load of the past filterTimeScale hours
*/
public void manageBattery(J_TimeVariables timeVariables) {
double currentBalanceBeforeBattery_kW = getBalanceElectricity_kW();
GCdemandLowPassed_kW += (currentBalanceBeforeBattery_kW - GCdemandLowPassed_kW) * filterDiffGain_r;
if(gc.p_batteryAsset != null && gc.p_batteryAsset.getStorageCapacity_kWh() > 0) {
double chargeSetpoint_kW = (SOC_setpoint_fr - gc.p_batteryAsset.getCurrentStateOfCharge_fr()) * feedbackGain_kWpSOC + balancingGain_fr * (GCdemandLowPassed_kW - currentBalanceBeforeBattery_kW);

// Try to stay within the target connection capacity
double v_allowedDeliveryCapacity_kW = getDeliveryCapacity_kW();
double v_allowedFeedinCapacity_kW = getFeedinCapacity_kW();
double availableChargePower_kW = v_allowedDeliveryCapacity_kW - currentBalanceBeforeBattery_kW; // Max battery charging power within safety margins
double availableDischargePower_kW = v_allowedFeedinCapacity_kW + currentBalanceBeforeBattery_kW; // Max discharging power within safety margins

chargeSetpoint_kW = min(max(chargeSetpoint_kW, -availableDischargePower_kW),availableChargePower_kW); // Don't allow too much (dis)charging!
gc.f_updateFlexAssetFlows(gc.p_batteryAsset, chargeSetpoint_kW / gc.p_batteryAsset.getCapacityElectric_kW(), timeVariables);
}
}

private double getDeliveryCapacity_kW() {
return gc.v_liveConnectionMetaData.contractedDeliveryCapacity_kW;
}

private double getFeedinCapacity_kW() {
return gc.v_liveConnectionMetaData.contractedFeedinCapacity_kW;
}

private double getBalanceElectricity_kW() {
return gc.fm_currentBalanceFlows_kW.get(OL_EnergyCarriers.ELECTRICITY);
}

public Agent getParentAgent() {
return this.gc;
}

public void storeStatesAndReset() {
this.storedGCdemandLowPassed_kW = this.GCdemandLowPassed_kW;
this.GCdemandLowPassed_kW = this.initialValueGCdemandLowPassed_kW;
}
public void restoreStates() {
this.GCdemandLowPassed_kW = this.storedGCdemandLowPassed_kW;
}

@Override
public String toString() {
return super.toString();
}

}

9 changes: 4 additions & 5 deletions _alp/Classes/Class.J_ChargePoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,13 @@ public J_ChargePoint(boolean V1GCapable, boolean V2GCapable, List<Double> socket
this.addSocketRestrictions(socketCapacitiesList_kW);
}



//Charge chargingRequest trough socket
public void charge( I_ChargingRequest chargingRequest, double charge_kW, J_TimeVariables timeVariables, GridConnection parentGC ) { //GC is TEMPORARY FIX
if (charge_kW < 0 && !this.V2GCapable) {
throw new RuntimeException("Trying to do V2G trough a ChargePoint that is not V2GCapable");
}
J_FlowPacket flowPacket = chargingRequest.f_updateAllFlows( charge_kW / chargingRequest.getVehicleChargingCapacity_kW(), timeVariables);
double powerFraction_fr = DoubleCompare.equalsZero(chargingRequest.getVehicleChargingCapacity_kW()) ? 0.0 : charge_kW / chargingRequest.getVehicleChargingCapacity_kW();
J_FlowPacket flowPacket = chargingRequest.f_updateAllFlows( powerFraction_fr, timeVariables);
parentGC.f_addFlows(flowPacket, (J_EA)chargingRequest);
}

Expand Down Expand Up @@ -115,8 +114,8 @@ public double getMaxChargingCapacity_kW(I_ChargingRequest chargingRequest) {
}

public double getChargeDeadline_h(I_ChargingRequest chargingRequest) {
double chargeNeedForNextTrip_kWh = chargingRequest.getRemainingChargeDemand_kWh();
double chargeTimeMargin_h = 0.25;//5; // Margin to be ready with charging before start of next trip
double chargeNeedForNextTrip_kWh = chargingRequest.getRemainingChargeDemand_kWh(); //
double chargeTimeMargin_h = 0.25;// Margin to be ready with charging before start of next trip
double nextTripStartTime_h = chargingRequest.getLeaveTime_h();
double chargeDeadline_h = nextTripStartTime_h - (chargeNeedForNextTrip_kWh / this.getMaxChargingCapacity_kW(chargingRequest)) - chargeTimeMargin_h;
return chargeDeadline_h;
Expand Down
27 changes: 12 additions & 15 deletions _alp/Classes/Class.J_ChargingManagementLocalBalancing.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,20 @@ 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.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);
double chargeNeedForFullBattery_kWh = chargingRequest.getStorageCapacity_kWh() - chargingRequest.getCurrentSOC_kWh(); // try to charge completely.
double remainingFlexTime_h = chargePoint.getChargeDeadline_h(chargingRequest) - t_h; // measure of flexiblity left in current charging session. (this relates to minimum SoC need for next trip, not to a full battery!)
double avgPowerDemandTillTrip_kW = chargeNeedForFullBattery_kWh / (chargingRequest.getLeaveTime_h() - t_h); // Avg power needed to achieve full battery
double chargeSetpoint_kW = 0;
if ( remainingFlexTime_h <= 0.25 ) { // 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(1,remainingFlexTime_h*flexGain_r)));
double flexGain_r_manual = 0.2; // 'Optimal' value depends on the relative magnitude of the peaks/dips in the GCdemand-before-EV compared to the total charging volume. Too high flexgain could quickly 'drain' flexiblity, too small would mean that peaks/valleys are not filled as much as possible.
int currentNbActiveChargingSessions = chargePoint.getCurrentNumberOfChargeRequests();
double flexGain_r = flexGain_r_manual/(double)max(1,currentNbActiveChargingSessions); // how strongly to 'follow' currentBalanceBeforeEV_kW
chargeSetpoint_kW = max(0, avgPowerDemandTillTrip_kW + (GCdemandLowPassed_kW - currentBalanceBeforeEV_kW) * (min(1/(double)currentNbActiveChargingSessions,remainingFlexTime_h*flexGain_r))); // limit 'local-balance-term' to 1/currentNbActiveChargingSessions to prevent overcompensation from multiple chargeSessions.
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)));
}
chargeSetpoint_kW = min(0, avgPowerDemandTillTrip_kW - (currentBalanceBeforeEV_kW - GCdemandLowPassed_kW) * (min(1/(double)currentNbActiveChargingSessions,remainingFlexTime_h*flexGain_r)));
}
}
//Send the chargepower setpoint to the chargepoint
chargePoint.charge(chargingRequest, chargeSetpoint_kW, timeVariables, gc);
Expand All @@ -82,14 +83,10 @@ public boolean getV2GActive() {
return this.V2GActive;
}



//Get parentagent
public Agent getParentAgent() {
return this.gc;
}

//Store and reset states
public void storeStatesAndReset() {
this.storedGCdemandLowPassed_kW = this.GCdemandLowPassed_kW;
this.GCdemandLowPassed_kW = this.initialValueGCdemandLowPassed_kW;
Expand Down
27 changes: 5 additions & 22 deletions _alp/Classes/Class.J_ChargingManagementPrice.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ public class J_ChargingManagementPrice implements I_ChargingManagement {
private double priceFilterDiffGain_r;

private boolean V2GActive = false;

//Stored
private double storedElectricityPriceLowPassed_eurpkWh;

/**
Expand All @@ -41,35 +39,26 @@ public J_ChargingManagementPrice( GridConnection gc, J_TimeParameters timeParame
public OL_ChargingAttitude getCurrentChargingType() {
return activeChargingType;
}
/**
* One of the simplest charging algorithms.
*
*/

public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariables) {
double t_h = timeVariables.getT_h();

//double currentElectricityPriceConsumption_eurpkWh = gc.p_owner.f_getElectricityPrice(gc.v_liveConnectionMetaData.contractedDeliveryCapacity_kW);
double currentElectricityPriceConsumption_eurpkWh = gc.energyModel.pp_dayAheadElectricityPricing_eurpMWh.getCurrentValue() * 0.001;
electricityPriceLowPassed_eurpkWh += (currentElectricityPriceConsumption_eurpkWh-electricityPriceLowPassed_eurpkWh) * priceFilterDiffGain_r ;
for (I_ChargingRequest chargingRequest : chargePoint.getCurrentActiveChargingRequests()) {
double duration_h = chargingRequest.getLeaveTime_h() - t_h;
if (duration_h <= 0) {
traceln("ChargingRequest starting after endtime! Skipping session! Duration_h: %s", duration_h);
//throw new RuntimeException("ChargingRequest starting after endtime!");
double timeToCharge_h = chargingRequest.getLeaveTime_h() - t_h;
if (timeToCharge_h <= 0) {
traceln("ChargingRequest starting after endtime! Skipping session! Duration_h: %s", timeToCharge_h);
}
double chargeNeedForNextTrip_kWh = chargingRequest.getEnergyNeedForNextTrip_kWh() - chargingRequest.getCurrentSOC_kWh(); // Can be negative if recharging is not needed for next trip!
//traceln("Charging need: %s, getEnergyNeedForNextTrip_kWh: %s", chargeNeedForNextTrip_kWh, chargingRequest.getEnergyNeedForNextTrip_kWh());
double remainingFlexTime_h = chargePoint.getChargeDeadline_h(chargingRequest) - t_h; // measure of flexiblity left in current charging session.
double WTPoffset_eurpkW = 0.01; // Drops willingness to pay price for charging, combined with remainingFlexTime_h.
double chargeSetpoint_kW = 0;
if ( t_h >= chargePoint.getChargeDeadline_h(chargingRequest) && chargeNeedForNextTrip_kWh > 0) { // Must-charge time at max charging power
if ( remainingFlexTime_h <= 0 && chargeNeedForNextTrip_kWh > 0) { // Must-charge time at max charging power
chargeSetpoint_kW = chargePoint.getMaxChargingCapacity_kW(chargingRequest);
} else {
double WTPCharging_eurpkWh = electricityPriceLowPassed_eurpkWh - WTPoffset_eurpkW * remainingFlexTime_h; //+ urgencyGain_eurpkWh * ( max(0,maxSpreadChargingPower_kW) / ev.getCapacityElectric_kW() ); // Scale WTP based on flexibility expressed in terms of power-fraction
//WTPprice_eurpkWh = WTPoffset_eurpkWh + (main.v_epexNext24hours_eurpkWh+v_electricityPriceLowPassed_eurpkWh)/2 + flexibilityGain_eurpkWh * sqrt(maxSpreadChargingPower_kW/maxChargingPower_kW);
double priceGain_r = 0.5; // When WTP is higher than current electricity price, ramp up charging power with this gain based on the price-delta.
chargeSetpoint_kW = max(0, chargePoint.getMaxChargingCapacity_kW(chargingRequest) * (WTPCharging_eurpkWh / currentElectricityPriceConsumption_eurpkWh - 1) * priceGain_r);
//if ( chargeNeedForNextTrip_kWh < -ev.getCapacityElectric_kW()*gc.energyModel.p_timeStep_h && chargeSetpoint_kW == 0 ) { // Surpluss SOC and high energy price
if ( this.V2GActive && chargePoint.getV2GCapable() && chargingRequest.getV2GCapable() && remainingFlexTime_h > 1 && chargeSetpoint_kW == 0 ) { // Surpluss SOC and high energy price
double V2G_WTS_offset_eurpkWh = 0.02; // Price must be at least this amount above the moving average to decide to discharge EV battery.
double WTSV2G_eurpkWh = V2G_WTS_offset_eurpkWh + electricityPriceLowPassed_eurpkWh; // Scale WillingnessToSell based on flexibility expressed in terms of power-fraction
Expand All @@ -91,15 +80,10 @@ public boolean getV2GActive() {
return this.V2GActive;
}




//Get parentagent
public Agent getParentAgent() {
return this.gc;
}

//Store and reset states
public void storeStatesAndReset() {
this.storedElectricityPriceLowPassed_eurpkWh = this.electricityPriceLowPassed_eurpkWh;
this.electricityPriceLowPassed_eurpkWh = this.initialValueElectricityPriceLowPassed_eurpkWh;
Expand All @@ -108,7 +92,6 @@ public void restoreStates() {
this.electricityPriceLowPassed_eurpkWh = this.storedElectricityPriceLowPassed_eurpkWh;
}


@Override
public String toString() {
return "Active charging type: " + this.activeChargingType;
Expand Down
Loading
Loading