diff --git a/Zero_engine.alpx b/Zero_engine.alpx index b3a60fb7..8b87ebda 100644 --- a/Zero_engine.alpx +++ b/Zero_engine.alpx @@ -687,7 +687,11 @@ 1755522163223 - + + + + 1758191806152 + 1668371973959 @@ -1705,6 +1709,11 @@ 1756210851750 + + 1758192949084 + + 1755154772688 + com.anylogic.libraries.modules.markup_descriptors diff --git a/_alp/Agents/EnergyCoop/Code/Functions.java b/_alp/Agents/EnergyCoop/Code/Functions.java index be4d1f01..accc13c7 100644 --- a/_alp/Agents/EnergyCoop/Code/Functions.java +++ b/_alp/Agents/EnergyCoop/Code/Functions.java @@ -1535,7 +1535,7 @@ EnergyCoop f_addAssetFlow(OL_AssetFlowCategories AC) EnumSet activeEnergyCarriers_rapidRun = EnumSet.copyOf(v_liveData.activeEnergyCarriers); EnumSet activeConsumptionEnergyCarriers_rapidRun = EnumSet.copyOf(v_liveData.activeConsumptionEnergyCarriers); EnumSet activeProductionEnergyCarriers_rapidRun = EnumSet.copyOf(v_liveData.activeProductionEnergyCarriers); - +EnumSet activeAssetFlows_rapidRun = EnumSet.copyOf(v_liveAssetsMetaData.activeAssetFlows); //Need to do this, for if the sliders have changed, otherwise potential errors/missing data boolean storeTotalAssetFlows = true; for(GridConnection GC : c_memberGridConnections){ @@ -1559,7 +1559,7 @@ EnergyCoop f_addAssetFlow(OL_AssetFlowCategories AC) v_rapidRunData.connectionMetaData = v_liveConnectionMetaData.getClone(); //Initialize the rapid run data -v_rapidRunData.initializeAccumulators(energyModel.p_runEndTime_h - energyModel.p_runStartTime_h, energyModel.p_timeStep_h, activeEnergyCarriers_rapidRun, activeConsumptionEnergyCarriers_rapidRun, activeProductionEnergyCarriers_rapidRun); +v_rapidRunData.initializeAccumulators(energyModel.p_runEndTime_h - energyModel.p_runStartTime_h, energyModel.p_timeStep_h, activeEnergyCarriers_rapidRun, activeConsumptionEnergyCarriers_rapidRun, activeProductionEnergyCarriers_rapidRun, activeAssetFlows_rapidRun); /*ALCODEEND*/} diff --git a/_alp/Agents/EnergyModel/Code/Functions.java b/_alp/Agents/EnergyModel/Code/Functions.java index 884b610a..0f6b99f1 100644 --- a/_alp/Agents/EnergyModel/Code/Functions.java +++ b/_alp/Agents/EnergyModel/Code/Functions.java @@ -247,18 +247,14 @@ if (GC.v_rapidRunData != null) { if (b_storePreviousRapidRunData) { - GC.v_previousRunData = GC.v_rapidRunData.getClone(); + GC.v_previousRunData = GC.v_rapidRunData; } - GC.v_rapidRunData.assetsMetaData = GC.v_liveAssetsMetaData.getClone(); - GC.v_rapidRunData.connectionMetaData = GC.v_liveConnectionMetaData.getClone(); - } else { - GC.v_rapidRunData = new J_RapidRunData(GC); - GC.v_rapidRunData.assetsMetaData = GC.v_liveAssetsMetaData.getClone(); - GC.v_rapidRunData.connectionMetaData = GC.v_liveConnectionMetaData.getClone(); - GC.v_rapidRunData.initializeAccumulators(p_runEndTime_h - p_runStartTime_h, p_timeStep_h, GC.v_liveData.activeEnergyCarriers, GC.v_liveData.activeConsumptionEnergyCarriers, GC.v_liveData.activeProductionEnergyCarriers); //f_initializeAccumulators(); + } + GC.v_rapidRunData = new J_RapidRunData(GC); + GC.v_rapidRunData.assetsMetaData = GC.v_liveAssetsMetaData.getClone(); + GC.v_rapidRunData.connectionMetaData = GC.v_liveConnectionMetaData.getClone(); + GC.v_rapidRunData.initializeAccumulators(p_runEndTime_h - p_runStartTime_h, p_timeStep_h, GC.v_liveData.activeEnergyCarriers, GC.v_liveData.activeConsumptionEnergyCarriers, GC.v_liveData.activeProductionEnergyCarriers, GC.v_liveAssetsMetaData.activeAssetFlows); //f_initializeAccumulators(); - } - GC.f_resetStates(); GC.c_tripTrackers.forEach(tt->{ @@ -287,21 +283,22 @@ for (EnergyCoop EC : pop_energyCoops) { if (EC.v_rapidRunData != null) { if (b_storePreviousRapidRunData) { - EC.v_previousRunData = EC.v_rapidRunData.getClone(); + EC.v_previousRunData = EC.v_rapidRunData; } - EC.v_rapidRunData.assetsMetaData = EC.v_liveAssetsMetaData.getClone(); + /*EC.v_rapidRunData.assetsMetaData = EC.v_liveAssetsMetaData.getClone(); EC.v_rapidRunData.connectionMetaData = EC.v_liveConnectionMetaData.getClone(); if(EC.v_rapidRunData.getStoreTotalAssetFlows() == false){ EC.v_rapidRunData.setStoreTotalAssetFlows(true); - EC.v_rapidRunData.initializeAccumulators(p_runEndTime_h - p_runStartTime_h, p_timeStep_h, EC.v_liveData.activeEnergyCarriers, EC.v_liveData.activeConsumptionEnergyCarriers, EC.v_liveData.activeProductionEnergyCarriers); - } - } else { - EC.v_rapidRunData = new J_RapidRunData(EC); - EC.v_rapidRunData.assetsMetaData = EC.v_liveAssetsMetaData.getClone(); - EC.v_rapidRunData.connectionMetaData = EC.v_liveConnectionMetaData.getClone(); - - EC.v_rapidRunData.initializeAccumulators(p_runEndTime_h - p_runStartTime_h, p_timeStep_h, EC.v_liveData.activeEnergyCarriers, EC.v_liveData.activeConsumptionEnergyCarriers, EC.v_liveData.activeProductionEnergyCarriers); - } + EC.v_rapidRunData.initializeAccumulators(p_runEndTime_h - p_runStartTime_h, p_timeStep_h, EC.v_liveData.activeEnergyCarriers, EC.v_liveData.activeConsumptionEnergyCarriers, EC.v_liveData.activeProductionEnergyCarriers, EC.v_liveAssetsMetaData.activeAssetFlows); + }*/ + } + EC.v_rapidRunData = new J_RapidRunData(EC); + EC.v_rapidRunData.assetsMetaData = EC.v_liveAssetsMetaData.getClone(); + EC.v_rapidRunData.connectionMetaData = EC.v_liveConnectionMetaData.getClone(); + //if(EC.v_rapidRunData.getStoreTotalAssetFlows() == false){ + EC.v_rapidRunData.setStoreTotalAssetFlows(true); + //} + EC.v_rapidRunData.initializeAccumulators(p_runEndTime_h - p_runStartTime_h, p_timeStep_h, EC.v_liveData.activeEnergyCarriers, EC.v_liveData.activeConsumptionEnergyCarriers, EC.v_liveData.activeProductionEnergyCarriers, EC.v_liveAssetsMetaData.activeAssetFlows); EC.f_resetStates(); } @@ -313,22 +310,15 @@ c_profiles.forEach(p -> p.updateValue(p_runStartTime_h)); c_forecasts.forEach(p -> p.initializeForecast(p_runStartTime_h)); -if (v_rapidRunData != null) { - if (b_storePreviousRapidRunData) { - v_previousRunData = v_rapidRunData.getClone(); - } - v_rapidRunData.assetsMetaData = v_liveAssetsMetaData.getClone(); - v_rapidRunData.connectionMetaData = v_liveConnectionMetaData.getClone(); -} else { - v_rapidRunData = new J_RapidRunData(this); - v_rapidRunData.assetsMetaData = v_liveAssetsMetaData.getClone(); - v_rapidRunData.connectionMetaData = v_liveConnectionMetaData.getClone(); - v_rapidRunData.initializeAccumulators(p_runEndTime_h - p_runStartTime_h, p_timeStep_h, v_liveData.activeEnergyCarriers, v_liveData.activeConsumptionEnergyCarriers, v_liveData.activeProductionEnergyCarriers); //f_initializeAccumulators(); +if (v_rapidRunData != null && b_storePreviousRapidRunData) { + v_previousRunData = v_rapidRunData; } - +v_rapidRunData = new J_RapidRunData(this); +v_rapidRunData.assetsMetaData = v_liveAssetsMetaData.getClone(); +v_rapidRunData.connectionMetaData = v_liveConnectionMetaData.getClone(); +v_rapidRunData.initializeAccumulators(p_runEndTime_h - p_runStartTime_h, p_timeStep_h, v_liveData.activeEnergyCarriers, v_liveData.activeConsumptionEnergyCarriers, v_liveData.activeProductionEnergyCarriers, v_liveAssetsMetaData.activeAssetFlows); //f_initializeAccumulators(); f_resetAnnualValues(); - v_isRapidRun = true; ////Run energy calculations loop diff --git a/_alp/Agents/EnergyModel/EmbeddedObjects.xml b/_alp/Agents/EnergyModel/EmbeddedObjects.xml index 8a3bd0fe..998000a3 100644 --- a/_alp/Agents/EnergyModel/EmbeddedObjects.xml +++ b/_alp/Agents/EnergyModel/EmbeddedObjects.xml @@ -160,9 +160,6 @@ - - - @@ -924,9 +921,6 @@ - - - @@ -1104,9 +1098,6 @@ - - - @@ -1257,9 +1248,6 @@ - - - @@ -1407,9 +1395,6 @@ - - - @@ -1614,9 +1599,6 @@ - - - @@ -1755,9 +1737,6 @@ - - - @@ -2244,9 +2223,6 @@ - - - @@ -2431,9 +2407,6 @@ - - - diff --git a/_alp/Agents/GCDistrictHeating/AOC.GCDistrictHeating.xml b/_alp/Agents/GCDistrictHeating/AOC.GCDistrictHeating.xml index a38a4a51..959a98b2 100644 --- a/_alp/Agents/GCDistrictHeating/AOC.GCDistrictHeating.xml +++ b/_alp/Agents/GCDistrictHeating/AOC.GCDistrictHeating.xml @@ -21,9 +21,6 @@ - - - diff --git a/_alp/Agents/GCEnergyConversion/AOC.GCEnergyConversion.xml b/_alp/Agents/GCEnergyConversion/AOC.GCEnergyConversion.xml index 9b77225d..0c77b79b 100644 --- a/_alp/Agents/GCEnergyConversion/AOC.GCEnergyConversion.xml +++ b/_alp/Agents/GCEnergyConversion/AOC.GCEnergyConversion.xml @@ -25,9 +25,6 @@ - - - diff --git a/_alp/Agents/GCEnergyProduction/AOC.GCEnergyProduction.xml b/_alp/Agents/GCEnergyProduction/AOC.GCEnergyProduction.xml index c9888452..365f45aa 100644 --- a/_alp/Agents/GCEnergyProduction/AOC.GCEnergyProduction.xml +++ b/_alp/Agents/GCEnergyProduction/AOC.GCEnergyProduction.xml @@ -21,9 +21,6 @@ - - - diff --git a/_alp/Agents/GCGridBattery/AOC.GCGridBattery.xml b/_alp/Agents/GCGridBattery/AOC.GCGridBattery.xml index 08c06571..cb1ef033 100644 --- a/_alp/Agents/GCGridBattery/AOC.GCGridBattery.xml +++ b/_alp/Agents/GCGridBattery/AOC.GCGridBattery.xml @@ -21,9 +21,6 @@ - - - diff --git a/_alp/Agents/GCHouse/AOC.GCHouse.xml b/_alp/Agents/GCHouse/AOC.GCHouse.xml index e84154c3..a0432ba7 100644 --- a/_alp/Agents/GCHouse/AOC.GCHouse.xml +++ b/_alp/Agents/GCHouse/AOC.GCHouse.xml @@ -21,9 +21,6 @@ - - - diff --git a/_alp/Agents/GCIndustry/AOC.GCIndustry.xml b/_alp/Agents/GCIndustry/AOC.GCIndustry.xml index 00df246c..9c677d47 100644 --- a/_alp/Agents/GCIndustry/AOC.GCIndustry.xml +++ b/_alp/Agents/GCIndustry/AOC.GCIndustry.xml @@ -22,9 +22,6 @@ - - - diff --git a/_alp/Agents/GCNeighborhood/AOC.GCNeighborhood.xml b/_alp/Agents/GCNeighborhood/AOC.GCNeighborhood.xml index 28406959..7f1457ec 100644 --- a/_alp/Agents/GCNeighborhood/AOC.GCNeighborhood.xml +++ b/_alp/Agents/GCNeighborhood/AOC.GCNeighborhood.xml @@ -24,9 +24,6 @@ - - - diff --git a/_alp/Agents/GCPublicCharger/AOC.GCPublicCharger.xml b/_alp/Agents/GCPublicCharger/AOC.GCPublicCharger.xml index 662695f4..257fe5c5 100644 --- a/_alp/Agents/GCPublicCharger/AOC.GCPublicCharger.xml +++ b/_alp/Agents/GCPublicCharger/AOC.GCPublicCharger.xml @@ -24,9 +24,6 @@ - - - diff --git a/_alp/Agents/GCUtility/AOC.GCUtility.xml b/_alp/Agents/GCUtility/AOC.GCUtility.xml index d5a4f3b7..bf38a8eb 100644 --- a/_alp/Agents/GCUtility/AOC.GCUtility.xml +++ b/_alp/Agents/GCUtility/AOC.GCUtility.xml @@ -24,9 +24,6 @@ - - - diff --git a/_alp/Agents/GridConnection/Code/Functions.java b/_alp/Agents/GridConnection/Code/Functions.java index c1816ac3..80ac80ea 100644 --- a/_alp/Agents/GridConnection/Code/Functions.java +++ b/_alp/Agents/GridConnection/Code/Functions.java @@ -477,8 +477,8 @@ c_hydrogenVehicles.add(hydrogenVehicle); } else if (vehicle instanceof J_EAEV ev) { c_electricVehicles.add(ev); - //c_vehiclesAvailableForCharging.add(ev); energyModel.c_EVs.add(ev); + ev.setV2GActive(p_chargingManagement.getV2GActive()); } c_vehicleAssets.add(vehicle); J_ActivityTrackerTrips tripTracker = vehicle.getTripTracker(); @@ -1180,6 +1180,9 @@ else if (flowsMap.get(EC) > 0){ coop.v_liveConnectionMetaData.contractedFeedinCapacityKnown = false; } } + + //Fast forward time dependent energy assets (if present) + c_chargers.forEach(charger -> charger.fastForwardCharingSessions(energyModel.t_h)); //Initialize/reset dataset maps to 0 double startTime = energyModel.v_liveData.dsm_liveDemand_kW.get(OL_EnergyCarriers.ELECTRICITY).getXMin(); @@ -1361,26 +1364,18 @@ EnergyCoop f_addAssetFlow(OL_AssetFlowCategories AC) } /*ALCODEEND*/} -double f_activateV2GChargingMode(boolean enable) +double f_activateV2GChargingMode(boolean enableV2G) {/*ALCODESTART::1754582754934*/ if(energyModel.b_isInitialized){ - - //if(p_chargingAttitudeVehicles == OL_ChargingAttitude.V2G){ - c_electricVehicles.forEach(ev -> ev.setV2GActive(enable)); - c_chargers.forEach(charger -> charger.setV2GActive(enable)); - //Check needed to make sure v2g is displayed correctly in the graphs - if (enable){ + p_chargingManagement.setV2GActive(enableV2G); + c_chargers.forEach(charger -> charger.setV2GActive(enableV2G)); + if (enableV2G){ f_addAssetFlow(OL_AssetFlowCategories.V2GPower_kW); - } - /*} - else{ - c_electricVehicles.forEach(ev -> ev.setV2GActive(false)); - c_chargers.forEach(charger -> charger.setV2GActive(false)); - }*/ + } } /*ALCODEEND*/} -double f_addChargingManagementToGC(OL_ChargingAttitude chargingType,boolean isGhost) +double f_addChargingManagementToGC(OL_ChargingAttitude chargingType) {/*ALCODESTART::1755702594182*/ if (chargingType == null) { if (c_electricVehicles.size()>0){ @@ -1388,12 +1383,8 @@ EnergyCoop f_addAssetFlow(OL_AssetFlowCategories AC) } } -/*if (isGhost) { - engineGC.p_chargingManagement = new J_ChargingManagementSimple(engineGC); - return; -}*/ if (chargingType == OL_ChargingAttitude.CUSTOM) { - throw new RuntimeException("f_addChargingManagementToGC called with heating type CUSTOM"); + throw new RuntimeException("f_addChargingManagementToGC called with charging type CUSTOM"); } /*Triple triple = Triple.of( heatingType, hasThermalBuilding, hasHeatBuffer ); @@ -1407,7 +1398,10 @@ EnergyCoop f_addAssetFlow(OL_AssetFlowCategories AC) case PRICE: managementClass = J_ChargingManagementPrice.class; break; - case BALANCE: + case BALANCE_LOCAL: + managementClass = J_ChargingManagementLocalBalancing.class; + break; + case BALANCE_GRID: managementClass = J_ChargingManagementLocalBalancing.class; break; case MAX_POWER: @@ -1426,5 +1420,19 @@ EnergyCoop f_addAssetFlow(OL_AssetFlowCategories AC) } p_chargingManagement = chargingManagement; + +//TEMPORARY UNTIL CHARGEPOINT MANAGEMENT AND EV CHARGING MANAGEMENT ARE COMBINED +if (c_chargers.size()>0){ + if (chargingType == null) { + throw new RuntimeException("Charging strategy needed when chargers are present!"); + } + else{ + c_chargers.forEach(charger -> charger.setChargingAttitude(chargingType)); + } +} + + + + /*ALCODEEND*/} diff --git a/_alp/Agents/GridConnection/Code/Functions.xml b/_alp/Agents/GridConnection/Code/Functions.xml index b4a0bc4f..cb2c9efd 100644 --- a/_alp/Agents/GridConnection/Code/Functions.xml +++ b/_alp/Agents/GridConnection/Code/Functions.xml @@ -776,8 +776,8 @@ double 1754582754934 - 290 - 80 + 270 + 70 10 0 @@ -786,7 +786,7 @@ true true - + @@ -810,10 +810,6 @@ - - - - diff --git a/_alp/Agents/GridConnection/Variables.xml b/_alp/Agents/GridConnection/Variables.xml index 75ea91f8..91acb666 100644 --- a/_alp/Agents/GridConnection/Variables.xml +++ b/_alp/Agents/GridConnection/Variables.xml @@ -680,35 +680,6 @@ - - 1668693393498 - - true - 270 - 60 - - 10 - 0 - - false - true - true - - - NONE - false - - - - - 1668693393496 - TEXT_BOX - 0 - 100 - NO_DELIMETER - - - 1668695364192 diff --git a/_alp/Agents/GridNode/Code/Functions.java b/_alp/Agents/GridNode/Code/Functions.java index 179a76c5..d856bd6e 100644 --- a/_alp/Agents/GridNode/Code/Functions.java +++ b/_alp/Agents/GridNode/Code/Functions.java @@ -291,6 +291,7 @@ //traceln("GridNode " + p_gridNodeID + " update at time " + time(HOUR)); f_nodeMetering(); +f_getCurrentChargingInformation(); /*ALCODEEND*/} double f_addGridBatteryLoad() @@ -555,3 +556,25 @@ DataSet f_getPeakExportWeekDataSet() return ds; /*ALCODEEND*/} +double f_getCurrentChargingInformation() +{/*ALCODESTART::1758209089894*/ +v_currentChargingPower_kW = 0; +v_currentNumberOfChargingChargePoints = 0; + +for(GridConnection GC : c_connectedGridConnections){ + if(GC instanceof GCPublicCharger){ + for(J_EAChargePoint charger : GC.c_chargers){ + v_currentChargingPower_kW += charger.getLastFlows().get(OL_EnergyCarriers.ELECTRICITY); + v_currentNumberOfChargingChargePoints += charger.getCurrentNumberOfChargingSockets(); + } + } +} + +// Low pass filter +double filterTimeScale_h = 5*24; +double filterDiffGain_r = 1/(filterTimeScale_h/energyModel.p_timeStep_h); +v_lowPassedLoadFilter_kW += (v_currentLoad_kW - v_currentChargingPower_kW - v_lowPassedLoadFilter_kW) * filterDiffGain_r; +//v_lowPassedLoadFilter_kW += (v_currentLoad_kW - v_lowPassedLoadFilter_kW) * filterDiffGain_r; + +/*ALCODEEND*/} + diff --git a/_alp/Agents/GridNode/Code/Functions.xml b/_alp/Agents/GridNode/Code/Functions.xml index ec91d764..e3a212fa 100644 --- a/_alp/Agents/GridNode/Code/Functions.xml +++ b/_alp/Agents/GridNode/Code/Functions.xml @@ -401,4 +401,20 @@ true + + VOID + double + 1758209089894 + + 810 + 600 + + 10 + 0 + + false + true + true + + diff --git a/_alp/Agents/GridNode/Variables.xml b/_alp/Agents/GridNode/Variables.xml index 65590450..2cac2668 100644 --- a/_alp/Agents/GridNode/Variables.xml +++ b/_alp/Agents/GridNode/Variables.xml @@ -1197,6 +1197,92 @@ geupdate vanuit + + 1758209121923 + + 810 + 630 + + 10 + 0 + + false + true + true + + + + + + + + + 1758209124347 + + 810 + 650 + + 10 + 0 + + false + true + true + + + + + + + + + 1758209126431 + + true + 810 + 670 + + 10 + 0 + + false + true + true + + + + + + 1758536215526 + + 810 + 700 + + 10 + 0 + + false + true + true + + + + + + + 1658477613290 diff --git a/_alp/Classes/Class.I_ChargingManagement.java b/_alp/Classes/Class.I_ChargingManagement.java index d0a645d0..e902a257 100644 --- a/_alp/Classes/Class.I_ChargingManagement.java +++ b/_alp/Classes/Class.I_ChargingManagement.java @@ -6,16 +6,18 @@ @JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class, property = "@id") @JsonTypeInfo( - use = JsonTypeInfo.Id.NAME, + use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type" // 👈 this will be the field name in your JSON ) +/* @JsonSubTypes({ @JsonSubTypes.Type(value = J_ChargingManagementLocalBalancing.class, name = "J_ChargingManagementLocalBalancing"), + @JsonSubTypes.Type(value = J_ChargingManagementOffPeak.class, name = "J_ChargingManagementOffPeak"), @JsonSubTypes.Type(value = J_ChargingManagementMaxAvailablePower.class, name = "J_ChargingManagementMaxAvailablePower"), @JsonSubTypes.Type(value = J_ChargingManagementPrice.class, name = "J_ChargingManagementPrice"), @JsonSubTypes.Type(value = J_ChargingManagementSimple.class, name = "J_ChargingManagementSimple"), -}) +})*/ public interface I_ChargingManagement { @@ -24,6 +26,9 @@ public interface I_ChargingManagement void initialize(); OL_ChargingAttitude getCurrentChargingType(); + + void setV2GActive(boolean activateV2G); + boolean getV2GActive(); } diff --git a/_alp/Classes/Class.J_AccumulatorMap.java b/_alp/Classes/Class.J_AccumulatorMap.java index 6b60ead6..b2606925 100644 --- a/_alp/Classes/Class.J_AccumulatorMap.java +++ b/_alp/Classes/Class.J_AccumulatorMap.java @@ -24,6 +24,7 @@ public J_AccumulatorMap(Class enumClass) { } public void createEmptyAccumulators(EnumSet selectedFlows, boolean hasTimeSeries, double signalResolution_h, double duration_h) { + this.clear(); for (E key : selectedFlows) { this.put(key, new ZeroAccumulator(hasTimeSeries, signalResolution_h, duration_h)); } diff --git a/_alp/Classes/Class.J_ChargingManagementLocalBalancing.java b/_alp/Classes/Class.J_ChargingManagementLocalBalancing.java index 49aafaeb..bfccc155 100644 --- a/_alp/Classes/Class.J_ChargingManagementLocalBalancing.java +++ b/_alp/Classes/Class.J_ChargingManagementLocalBalancing.java @@ -14,11 +14,12 @@ public class J_ChargingManagementLocalBalancing implements I_ChargingManagement { private GridConnection gc; - private OL_ChargingAttitude activeChargingType = OL_ChargingAttitude.BALANCE; + private OL_ChargingAttitude activeChargingType = OL_ChargingAttitude.BALANCE_LOCAL; private double filterTimeScale_h = 5*24; private double filterDiffGain_r; private double GCdemandLowPassed_kW = 0.5; - + + private boolean V2GActive = false; /** * Default constructor */ @@ -58,7 +59,7 @@ public void manageCharging() { //traceln("Urgency charging in GC: %s! May exceed connection capacity!", gc.p_gridConnectionID)); chargeSetpoint_kW = ev.getCapacityElectric_kW(); } else { - double flexGain_r = 0.5; // how strongly so 'follow' currentBalanceBeforeEV_kW + double flexGain_r = 0.5; // how strongly to 'follow' currentBalanceBeforeEV_kW chargeSetpoint_kW = max(0, avgPowerDemandTillTrip_kW + (GCdemandLowPassed_kW - currentBalanceBeforeEV_kW) * (min(1,remainingFlexTime_h*flexGain_r))); if ( ev.getV2GActive() && remainingFlexTime_h > 1 && chargeSetpoint_kW == 0 ) { // Surpluss flexibility chargeSetpoint_kW = min(0, avgPowerDemandTillTrip_kW - (currentBalanceBeforeEV_kW - GCdemandLowPassed_kW) * (min(1,remainingFlexTime_h*flexGain_r))); @@ -70,7 +71,15 @@ public void manageCharging() { } } - + public void setV2GActive(boolean activateV2G) { + this.V2GActive = activateV2G; + this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(activateV2G)); // not really wanted but NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory + } + + public boolean getV2GActive() { + return this.V2GActive; + } + @Override public String toString() { return "Active charging type: " + this.activeChargingType; diff --git a/_alp/Classes/Class.J_ChargingManagementMaxAvailablePower.java b/_alp/Classes/Class.J_ChargingManagementMaxAvailablePower.java index 50fa2fc5..9bd1330c 100644 --- a/_alp/Classes/Class.J_ChargingManagementMaxAvailablePower.java +++ b/_alp/Classes/Class.J_ChargingManagementMaxAvailablePower.java @@ -16,6 +16,8 @@ public class J_ChargingManagementMaxAvailablePower implements I_ChargingManagement { private GridConnection gc; private OL_ChargingAttitude activeChargingType = OL_ChargingAttitude.MAX_POWER; + + private boolean V2GActive = false; /** * Default constructor */ @@ -70,6 +72,15 @@ public void manageCharging() { } } + public void setV2GActive(boolean activateV2G) { + this.V2GActive = activateV2G; + this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(false)); // not really wanted but NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory + } + + public boolean getV2GActive() { + return this.V2GActive; + } + @Override public String toString() { return "Active charging type: " + this.activeChargingType; diff --git a/_alp/Classes/Class.J_ChargingManagementOffPeak.java b/_alp/Classes/Class.J_ChargingManagementOffPeak.java new file mode 100644 index 00000000..23c40228 --- /dev/null +++ b/_alp/Classes/Class.J_ChargingManagementOffPeak.java @@ -0,0 +1,126 @@ +/** + * J_ChargingManagementOffPeak + */ +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_ChargingManagementOffPeak implements I_ChargingManagement { + + private GridConnection gc; + private OL_ChargingAttitude activeChargingType = OL_ChargingAttitude.BALANCE_GRID; + private double filterTimeScale_h = 5*24; + private double filterDiffGain_r; + private double GCdemandLowPassed_kW = 0.5; + + private double startTimeOfReducedChargingInterval_hr = 17; // Hour of the day + private double endTimeOfReducedChargingInterval_hr = 21; // Hour of the day + + private boolean V2GActive = false; + + /** + * Default constructor + */ + public J_ChargingManagementOffPeak() { + + } + + public J_ChargingManagementOffPeak( GridConnection gc ) { + this.gc = gc; + this.filterDiffGain_r = 1/(filterTimeScale_h/gc.energyModel.p_timeStep_h); + } + + public void initialize() { + + } + + public OL_ChargingAttitude getCurrentChargingType() { + return activeChargingType; + } + /** + * One of the simplest charging algorithms. + * + */ + public void manageCharging() { + double t_h = gc.energyModel.t_h; + // Use current GC-load (so without EV charging!) as an 'equivalent price' signal, and use EV battery flexibility to make local load flatter. + double currentBalanceBeforeEV_kW = gc.fm_currentBalanceFlows_kW.get(OL_EnergyCarriers.ELECTRICITY); + GCdemandLowPassed_kW += (currentBalanceBeforeEV_kW - GCdemandLowPassed_kW) * filterDiffGain_r; + + //Determine if time is currently in reduced charging interval + double hourOfTheDay = t_h % 24; + + double intervalLength_hr = (endTimeOfReducedChargingInterval_hr - startTimeOfReducedChargingInterval_hr + 24) % 24; + if(intervalLength_hr == 0) intervalLength_hr = 24; // (Start time == End time: for now defined as charge as little as possible all day. + double intervalEndTimeSinceModelStart_hr = t_h - ((t_h - startTimeOfReducedChargingInterval_hr + 24) % 24) + intervalLength_hr; + boolean timeIsInReducedChargingInterval = ((hourOfTheDay - startTimeOfReducedChargingInterval_hr + 24) % 24) < intervalLength_hr; + + for (J_EAEV ev : gc.c_electricVehicles) { + if (ev.available) { + double chargeNeedForNextTrip_kWh = ev.energyNeedForNextTrip_kWh - ev.getCurrentStateOfCharge_kWh(); // Can be negative if recharging is not needed for next trip! + double chargeSetpoint_kW = 0; + if ( t_h >= (ev.getChargeDeadline_h()) && chargeNeedForNextTrip_kWh > 0) { // Must-charge time at max charging power + //traceln("Urgency charging in GC: %s! May exceed connection capacity!", gc.p_gridConnectionID)); + chargeSetpoint_kW = ev.getCapacityElectric_kW(); + } else { + if(timeIsInReducedChargingInterval && chargeNeedForNextTrip_kWh > 0) { + double chargeTimeMargin_h = 0.5; // Margin to be ready with charging before start of next trip + double timeBetweenEndOfIntervalAndNextTripStartTime_hr = max(0, ev.getNextTripStartTime_h() - intervalEndTimeSinceModelStart_hr - chargeTimeMargin_h); + double energyThatCanBeChargedAfterIntervalEnded_kWh = timeBetweenEndOfIntervalAndNextTripStartTime_hr * ev.getCapacityElectric_kW(); + double energyThatNeedsToBeChargedDuringInterval_kWh = max(0, chargeNeedForNextTrip_kWh - energyThatCanBeChargedAfterIntervalEnded_kWh); + + double avgPowerDemandTillEndOfInterval_kW = energyThatNeedsToBeChargedDuringInterval_kWh / (intervalEndTimeSinceModelStart_hr - t_h); + chargeSetpoint_kW = avgPowerDemandTillEndOfInterval_kW; + } + else { // Dom laden (??????) // Of max spread laden? + chargeSetpoint_kW = ev.getCapacityElectric_kW(); + } + } + ev.f_updateAllFlows( chargeSetpoint_kW / ev.getCapacityElectric_kW() ); + } + } + } + + public void setStartTimeOfReducedChargingInterval_hr(double startTimeOfReducedChargingInterval_hr) { + this.startTimeOfReducedChargingInterval_hr = startTimeOfReducedChargingInterval_hr; + } + + public void setEndTimeOfReducedChargingInterval_hr(double endTimeOfReducedChargingInterval_hr) { + this.endTimeOfReducedChargingInterval_hr = endTimeOfReducedChargingInterval_hr; + } + + public double getStartTimeOfReducedChargingInterval_hr() { + return this.startTimeOfReducedChargingInterval_hr; + } + + public double getEndTimeOfReducedChargingInterval_hr() { + return this.endTimeOfReducedChargingInterval_hr; + } + + public void setV2GActive(boolean activateV2G) { + this.V2GActive = activateV2G; + this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(activateV2G)); // not really wanted but NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory + } + + public boolean getV2GActive() { + return this.V2GActive; + } + + @Override + public String toString() { + return "Active charging type: " + this.activeChargingType; + } + + /** + * This number is here for model snapshot storing purpose + * It needs to be changed when this class gets changed + */ + private static final long serialVersionUID = 1L; + +} \ No newline at end of file diff --git a/_alp/Classes/Class.J_ChargingManagementPrice.java b/_alp/Classes/Class.J_ChargingManagementPrice.java index 54ff1cc0..b2c372ec 100644 --- a/_alp/Classes/Class.J_ChargingManagementPrice.java +++ b/_alp/Classes/Class.J_ChargingManagementPrice.java @@ -19,7 +19,7 @@ public class J_ChargingManagementPrice implements I_ChargingManagement { private double priceFilterTimeScale_h = 5*24; private double priceFilterDiffGain_r; - + private boolean V2GActive = false; /** * Default constructor */ @@ -81,6 +81,15 @@ public void manageCharging() { } } + public void setV2GActive(boolean activateV2G) { + this.V2GActive = activateV2G; + this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(false)); // not really wanted but NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory + } + + public boolean getV2GActive() { + return this.V2GActive; + } + @Override public String toString() { return "Active charging type: " + this.activeChargingType; diff --git a/_alp/Classes/Class.J_ChargingManagementSimple.java b/_alp/Classes/Class.J_ChargingManagementSimple.java index 1c295edb..bf142999 100644 --- a/_alp/Classes/Class.J_ChargingManagementSimple.java +++ b/_alp/Classes/Class.J_ChargingManagementSimple.java @@ -21,6 +21,9 @@ public class J_ChargingManagementSimple implements I_ChargingManagement { private double priceFilterTimeScale_h = 5*24; private double priceFilterDiffGain_r; private EnumSet supportedChargingTypes = EnumSet.noneOf(OL_ChargingAttitude.class); + + private boolean V2GActive = false; + //private double GCdemandLowPassed_kW = 0.5; //private double GCdemandFilterTimeScale_h = 5*24; /** @@ -84,7 +87,7 @@ public void manageCharging() { 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, ev.getCapacityElectric_kW() * (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 ( ev.getV2GActive() && remainingFlexTime_h > 1 && chargeSetpoint_kW == 0 ) { // Surpluss SOC and high energy price + if ( this.V2GActive && ev.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 chargeSetpoint_kW = min(0, -ev.getCapacityElectric_kW() * (currentElectricityPriceConsumption_eurpkWh / WTSV2G_eurpkWh - 1) * priceGain_r); @@ -98,7 +101,16 @@ public void manageCharging() { } } } - + + public void setV2GActive(boolean activateV2G) { + this.V2GActive = activateV2G; + this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(false)); // not really wanted but NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory + } + + public boolean getV2GActive() { + return this.V2GActive; + } + @Override public String toString() { return "Active charging type: " + this.activeChargingType; diff --git a/_alp/Classes/Class.J_ChargingSession.java b/_alp/Classes/Class.J_ChargingSession.java index a54b18b7..72b6fb01 100644 --- a/_alp/Classes/Class.J_ChargingSession.java +++ b/_alp/Classes/Class.J_ChargingSession.java @@ -7,28 +7,17 @@ public class J_ChargingSession implements Serializable { double endTime_h; double timeStep_h; double chargingDemand_kWh; + double avgPowerDemand_kW; double batterySize_kWh; double stateOfCharge_kWh; double vehicleMaxChargingPower_kW; int socketNb; - boolean V1GCapable = true; - boolean V2GCapable = true; + //boolean V1GCapable = true; + //boolean V2GCapable = true; double chargedDuringSession_kWh = 0; double dischargedDuringSession_kWh = 0; - /* - int availableStepsForV2G; - int availableStepsForV1G; - int timeStepsToDisconnect; - int openTimeSlots; - int V2GRemainingTimesteps = 0; - int V1GRemainingTimesteps = 0; - double currentPower; - - double shiftedLoadV1GThisTimestep; - double shiftedLoadV2GThisTimestep; - */ /** * Default constructor @@ -39,6 +28,7 @@ public J_ChargingSession(double startTime_quarterhours, double endTime_quarterho this.endTime_h = 0.25 * endTime_quarterhours; this.timeStep_h = timeStep_h; this.chargingDemand_kWh = chargingDemand_kWh; + this.avgPowerDemand_kW = this.chargingDemand_kWh / (this.endTime_h - this.startTime_h); this.batterySize_kWh = batterySize_kWh; this.socketNb = socketNb-1; //stateOfCharge_kWh = batterySize_kWh - chargingDemand_kWh; // bold assumption... basically means every vehicle ends full. The reality is somewhere between: vehicle starts empty and vehicle ends full. @@ -66,92 +56,6 @@ public J_ChargingSession getClone() { return new J_ChargingSession((this.startTime_h*4), (this.endTime_h*4), this.chargingDemand_kWh, this.batterySize_kWh, this.vehicleMaxChargingPower_kW, this.socketNb, this.timeStep_h); } - /* - public double operate(boolean doV1G, boolean doV2G) { - this.V1GCapable = doV1G; - this.V2GCapable = doV2G; - timeStepsToDisconnect --; - openTimeSlots --; - double power = determineChargingPower(); - setSmartChargingAvailabilities(); - currentPower = power; - stateOfCharge_kWh += power * timeStep_hr; - return currentPower; - } - - - public double determineChargingPower(){ - double power; - if (V2GRemainingTimesteps > 0) { - power = - chargingPower_kW; - shiftedLoadV2GThisTimestep = 2 * power; - V2GRemainingTimesteps--; - //if you are doing V2G this timestep you reduce your opentimeslots by 2 - openTimeSlots --; - openTimeSlots --; - } - else if( V1GRemainingTimesteps > 0){ - power = 0; - shiftedLoadV1GThisTimestep = power; - V1GRemainingTimesteps--; - //if you are doing V1G this timestep you reduce your opentimeslots by 1 - openTimeSlots --; - } - else if (batterySize_kWh > stateOfCharge_kWh) { - power = chargingPower_kW; - shiftedLoadV1GThisTimestep = 0; - shiftedLoadV2GThisTimestep = 0; - } - else { - power = 0; - shiftedLoadV1GThisTimestep = 0; - shiftedLoadV2GThisTimestep =0; - } - return power; - } - - public void setSmartChargingAvailabilities() { - //determine if the vehicle if this session is available for V2G and V1G next timestep - if( openTimeSlots > 3 && V2GCapable && (stateOfCharge_kWh > (0.2 * batterySize_kWh + 0.5 * chargingPower_kW )) ) { //you need at least 4 timesteps for V2G (a minimum for 2 timestep V2G sessions were defined) - availableStepsForV2G = (int) Math.floor(openTimeSlots / 2); - } - else { - availableStepsForV2G = 0; - } - - if (openTimeSlots > 1 && (stateOfCharge_kWh < batterySize_kWh)) { - availableStepsForV1G = openTimeSlots; - } - else { - availableStepsForV1G = 0; - } - } - - public void requestV2G(int timeStepsV2G) { - if( V2GRemainingTimesteps != 0 || V1GRemainingTimesteps != 0) { - traceln( "ERROR TRYING TO SET CHARGING SESSION TO V2G, BUT IS ALREADY IN A V1G or V2G SESSION"); - } - V2GRemainingTimesteps = timeStepsV2G; - - } - - public void requestV1G(int timeStepsV1G) { - if( V2GRemainingTimesteps != 0 || V1GRemainingTimesteps != 0) { - traceln( "ERROR TRYING TO SET CHARGING SESSION TO V1G, BUT IS ALREADY IN A V1G or V2G SESSION"); - } - V1GRemainingTimesteps = timeStepsV1G; - } - - - public double getShiftedLoadV1GCurrentTimestep() { - return shiftedLoadV1GThisTimestep; - } - - public double getShiftedLoadV2GCurrentTimestep() { - return shiftedLoadV2GThisTimestep; - } - */ - @Override public String toString() { diff --git a/_alp/Classes/Class.J_EAChargePoint.java b/_alp/Classes/Class.J_EAChargePoint.java index d2ec648f..ff439d93 100644 --- a/_alp/Classes/Class.J_EAChargePoint.java +++ b/_alp/Classes/Class.J_EAChargePoint.java @@ -6,17 +6,19 @@ public class J_EAChargePoint extends zero_engine.J_EA implements Serializable { public double charged_kWh; public double capacityElectric_kW; public List chargeSessionList; - public boolean V1GCapable; - public boolean V2GCapable; + private boolean V1GCapable; + private boolean V2GCapable; private boolean V2GActive = false; private int nbSockets; private int[] nextSessionIdxs;// = 0; private J_ChargingSession[] currentChargingSessions; + private int currentNumberOfChargingSockets = 0; // Used for certain charging algorithms + // For filtered price private double electricityPriceLowPassed_eurpkWh = 0.1; - private double priceFilterTimeScale_h = 5*24; + private double priceFilterTimeScale_h = 5*24; // private double dischargedStored_kWh; @@ -24,7 +26,8 @@ public class J_EAChargePoint extends zero_engine.J_EA implements Serializable { private J_ChargingSession[] currentChargingSessionsStored; private int[] nextSessionIdxsStored; - public boolean gridAwareMode = true; + //Charging attitude + private OL_ChargingAttitude chargingAttitude = OL_ChargingAttitude.SIMPLE; /** * Default constructor @@ -53,7 +56,7 @@ public void f_updateAllFlows( double t_h) { double currentElectricityPriceConsumption_eurpkWh = ((GridConnection)parentAgent).energyModel.pp_dayAheadElectricityPricing_eurpMWh.getCurrentValue() * 0.001; this.electricityPriceLowPassed_eurpkWh += (currentElectricityPriceConsumption_eurpkWh-electricityPriceLowPassed_eurpkWh) / (priceFilterTimeScale_h/timestep_h); - + /*if (gridAwareMode) { // Add gridload-factor to current price currentElectricityPriceConsumption_eurpkWh = currentElectricityPriceConsumption_eurpkWh + (max(0,((GridConnection)parentAgent).p_parentNodeElectric.v_currentLoad_kW/((GridConnection)parentAgent).p_parentNodeElectric.p_capacity_kW-0.5))*0.1; if ( ((GridConnection)parentAgent).p_parentNodeElectric.v_currentLoad_kW/((GridConnection)parentAgent).p_parentNodeElectric.p_capacity_kW > 0.5) { @@ -61,8 +64,7 @@ public void f_updateAllFlows( double t_h) { } }*/ - // Check if the charger is capable of smart charging - boolean doV1G = this.V1GCapable; + // Check if V2G is activated and the charger is capable boolean doV2G = this.V2GActive && this.V2GCapable; // Update the J_ChargingSessions of the sockets @@ -72,13 +74,14 @@ public void f_updateAllFlows( double t_h) { // Calculate the power output of the sockets double power_kW = 0.0; + currentNumberOfChargingSockets = 0; for (int i = 0; i= currentChargingSessions[i].startTime_h) { - power_kW += this.operateChargerSocket(i, t_h, currentElectricityPriceConsumption_eurpkWh, doV1G, doV2G); - + power_kW += this.operateChargerSocket(i, t_h, currentElectricityPriceConsumption_eurpkWh, doV2G); discharged_kWh += min(0,-power_kW) * timestep_h; charged_kWh += max(0,power_kW) * timestep_h; + currentNumberOfChargingSockets++; } } @@ -105,7 +108,7 @@ public void operate(double ratioOfCapacity) { if (charge_kW > 0) { // charging assetFlowsMap.put(OL_AssetFlowCategories.evChargingPower_kW, electricityConsumption_kW); } else if(charge_kW < 0){ - if(this.V2GCapable && this.V2GActive) { + if(this.V2GCapable) { // && this.V2GActive) { assetFlowsMap.put(OL_AssetFlowCategories.V2GPower_kW, electricityProduction_kW); } else { @@ -115,52 +118,136 @@ public void operate(double ratioOfCapacity) { } } - private double operateChargerSocket(int socketNb, double t_h, double currentElectricityPriceConsumption_eurpkWh, boolean doV1G, boolean doV2G) { + + private double operateChargerSocket(int socketNb, double t_h, double currentElectricityPriceConsumption_eurpkWh, boolean doV2G) { + double chargeSetpoint_kW = 0; + if(!this.V1GCapable) { + chargeSetpoint_kW = determineChargeSetpoint_simple_kW(socketNb); + } + else { + switch(this.chargingAttitude) { + case SIMPLE: + chargeSetpoint_kW = determineChargeSetpoint_simple_kW(socketNb); + break; + case PRICE: + chargeSetpoint_kW = determineChargeSetpoint_price_kW(socketNb, t_h, doV2G, currentElectricityPriceConsumption_eurpkWh); + break; + case BALANCE_LOCAL: + throw new RuntimeException("BALANCE LOCAL CHARGING STRATEGY HAS NOT BEEN CREATED YET FOR CHARGEPOINTS"); + //chargeSetpoint_kW = determineChargeSetpoint_balanceLocal_kW(socketNb, t_h, doV2G); + case BALANCE_GRID: + chargeSetpoint_kW = determineChargeSetpoint_balanceGrid_kW(socketNb, t_h, doV2G); + break; + default: + throw new RuntimeException("UNSUPPORTED CHARGING ATTITUDE SELECTED FOR CHARGEPOINT"); + } + } + + return currentChargingSessions[socketNb].charge(chargeSetpoint_kW); + } + + + private double determineChargeSetpoint_simple_kW(int socketNb) { + double maxChargePower = capacityElectric_kW;//min(currentChargingSessions[socketNb].vehicleMaxChargingPower_kW, capacityElectric_kW); + double remainingChargeDemand_kWh = currentChargingSessions[socketNb].getRemainingChargeDemand_kWh(); // Can be negative if recharging is not needed for next trip! + double chargeSetpoint_kW = max(0, min(maxChargePower, remainingChargeDemand_kWh/timestep_h)); + + return chargeSetpoint_kW; + } + + private double determineChargeSetpoint_price_kW(int socketNb, double t_h, boolean doV2G, double currentElectricityPriceConsumption_eurpkWh) { double maxChargePower = capacityElectric_kW;//min(currentChargingSessions[socketNb].vehicleMaxChargingPower_kW, capacityElectric_kW); double remainingChargeDemand_kWh = currentChargingSessions[socketNb].getRemainingChargeDemand_kWh(); // Can be negative if recharging is not needed for next trip! double chargeSetpoint_kW = 0; - if (!doV1G) { - chargeSetpoint_kW = min(maxChargePower, remainingChargeDemand_kWh/timestep_h); // just max power charging to start with - //traceln("ChargePoint simple charging active"); - } else { - //traceln("Smart charging active at chargePoint"); - double nextTripStartTime_h = currentChargingSessions[socketNb].endTime_h; - double chargeTimeMargin_h = 0.5; // Margin to be ready with charging before start of next trip - double chargeDeadline_h = nextTripStartTime_h - remainingChargeDemand_kWh / maxChargePower - chargeTimeMargin_h; - double remainingFlexTime_h = chargeDeadline_h - t_h; // measure of flexiblity left in current charging session. - if ( t_h >= chargeDeadline_h && remainingChargeDemand_kWh > 0) { // Must-charge time at max charging power - //traceln("Urgency charging on charge point GC: %s! May exceed connection capacity!", this.parentAgent); - chargeSetpoint_kW = min(maxChargePower, remainingChargeDemand_kWh/timestep_h); - } else { - double WTPoffset_eurpkW = 0.01; // Drops willingness to pay price for charging, combined with remainingFlexTime_h. - 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, maxChargePower * min(1,(WTPCharging_eurpkWh / currentElectricityPriceConsumption_eurpkWh - 1) * priceGain_r)); // min(1,...) is needed to prevent devide by zero leading to infinity/NaN results. - if ( doV2G && remainingFlexTime_h > 1 && chargeSetpoint_kW == 0 ) { // Surpluss SOC and high energy price - //traceln("Conditions for V2G met in chargePoint"); - 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; // Can become zero!! - chargeSetpoint_kW = min(0, -maxChargePower * min(1,(currentElectricityPriceConsumption_eurpkWh / WTSV2G_eurpkWh - 1) * priceGain_r)); // min(1,...) is needed to prevent devide by zero leading to infinity/NaN results. - //if (chargeSetpoint_kW < 0) {traceln(" V2G Active! Power: " + chargeSetpoint_kW );} - } + //traceln("Smart charging active at chargePoint"); + double nextTripStartTime_h = currentChargingSessions[socketNb].endTime_h; + double chargeTimeMargin_h = 0.5; // Margin to be ready with charging before start of next trip + double chargeDeadline_h = nextTripStartTime_h - remainingChargeDemand_kWh / maxChargePower - chargeTimeMargin_h; + double remainingFlexTime_h = chargeDeadline_h - t_h; // measure of flexiblity left in current charging session. + + if ( t_h >= chargeDeadline_h && remainingChargeDemand_kWh > 0) { // Must-charge time at max charging power + //traceln("Urgency charging on charge point GC: %s! May exceed connection capacity!", this.parentAgent); + chargeSetpoint_kW = min(maxChargePower, remainingChargeDemand_kWh/timestep_h); + } else { + double WTPoffset_eurpkW = 0.01; // Drops willingness to pay price for charging, combined with remainingFlexTime_h. + 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, maxChargePower * min(1,(WTPCharging_eurpkWh / currentElectricityPriceConsumption_eurpkWh - 1) * priceGain_r)); // min(1,...) is needed to prevent devide by zero leading to infinity/NaN results. + if ( doV2G && remainingFlexTime_h > 1 && chargeSetpoint_kW == 0 ) { // Surpluss SOC and high energy price + //traceln("Conditions for V2G met in chargePoint"); + 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; // Can become zero!! + chargeSetpoint_kW = min(0, -maxChargePower * min(1,(currentElectricityPriceConsumption_eurpkWh / WTSV2G_eurpkWh - 1) * priceGain_r)); // min(1,...) is needed to prevent devide by zero leading to infinity/NaN results. + //if (chargeSetpoint_kW < 0) {traceln(" V2G Active! Power: " + chargeSetpoint_kW );} } + } + + return chargeSetpoint_kW; + } + + private double determineChargeSetpoint_balanceLocal_kW(int socketNb, double t_h, boolean doV2G) { + // DOES NOT MAKE SENSE TO USE THIS FOR GCPUBLIC CHARGER, ONLY FOR GC THAT HAVE THEIR OWN CONSUMPTION -> WHICH SHOULD BE SUPPORTED EVENTUALLY + if( parentAgent instanceof GCPublicCharger) { + throw new RuntimeException("This strategy only works for GCs that have their own consumption, do not use this for GCPublicChargers"); + } + + //CREATE STRATEGY HERE + double chargeSetpoint_kW = 0; + return chargeSetpoint_kW; + } + + private double determineChargeSetpoint_balanceGrid_kW(int socketNb, double t_h, boolean doV2G) { + double chargeSetpoint_kW = 0; + double maxChargePower = capacityElectric_kW; + double remainingChargeDemand_kWh = currentChargingSessions[socketNb].getRemainingChargeDemand_kWh(); // Can be negative if recharging is not needed for next trip! + + GridNode parentNode = ((GridConnection)parentAgent).p_parentNodeElectric; + double currentBalanceOnGridNodeWithoutEV_kW = parentNode.v_currentLoad_kW - parentNode.v_currentChargingPower_kW; + double gridNodeLowPassedLoad_kW = parentNode.v_lowPassedLoadFilter_kW; + + double nextTripStartTime_h = currentChargingSessions[socketNb].endTime_h; + double chargeTimeMargin_h = 0.5; // Margin to be ready with charging before start of next trip + double chargeDeadline_h = nextTripStartTime_h - remainingChargeDemand_kWh / maxChargePower - chargeTimeMargin_h; + double remainingFlexTime_h = chargeDeadline_h - t_h; // measure of flexiblity left in current charging session. + double remainingTimeToCharge = nextTripStartTime_h - t_h - chargeTimeMargin_h; + + /*double avgPowerDemandTillTrip_kW = 0; + if(remainingTimeToCharge != 0) { + avgPowerDemandTillTrip_kW = remainingChargeDemand_kWh / (nextTripStartTime_h - t_h - chargeTimeMargin_h); } - return currentChargingSessions[socketNb].charge(chargeSetpoint_kW); + else if(remainingChargeDemand_kWh > 0) { + avgPowerDemandTillTrip_kW = min(maxChargePower, remainingChargeDemand_kWh/timestep_h); + }*/ + + if ( t_h >= chargeDeadline_h && remainingChargeDemand_kWh > 0) { // Must-charge time at max charging power + chargeSetpoint_kW = min(maxChargePower, remainingChargeDemand_kWh/timestep_h); + } else { + double flexGain_r_manual = 0.8; + double flexGain_r = 1/max(1, (double)parentNode.v_currentNumberOfChargingChargePoints) * flexGain_r_manual; // how strongly to 'follow' currentBalanceBeforeEV_kW -> influenced by the amount of charging chargers at this momment + chargeSetpoint_kW = max(0, currentChargingSessions[socketNb].avgPowerDemand_kW + (gridNodeLowPassedLoad_kW - currentBalanceOnGridNodeWithoutEV_kW) * (min(1,flexGain_r))); + if ( doV2G && remainingFlexTime_h > 1 && chargeSetpoint_kW == 0 ) { // Surpluss flexibility + chargeSetpoint_kW = min(0, currentChargingSessions[socketNb].avgPowerDemand_kW + (gridNodeLowPassedLoad_kW - currentBalanceOnGridNodeWithoutEV_kW) * (min(1,flexGain_r))); + } + } + return chargeSetpoint_kW; } - + + + private void manageSocket(int socketNb, double t_h) { if (this.currentChargingSessions[socketNb] != null && t_h >= this.currentChargingSessions[socketNb].endTime_h) { // end session if (this.currentChargingSessions[socketNb].getRemainingChargeDemand_kWh() > 0.001 ) { traceln("!!Chargesession ended but charge demand not fullfilled!! Remaining demand: %s kWh", this.currentChargingSessions[socketNb].getRemainingChargeDemand_kWh()); } this.currentChargingSessions[socketNb] = null; } + if ( this.currentChargingSessions[socketNb] == null ) { // socket currently free // check if we are not already past the last charging session. - // Find next charging session on this socket + while (this.nextSessionIdxs[socketNb] < this.chargeSessionList.size() && this.chargeSessionList.get(this.nextSessionIdxs[socketNb]).socketNb != socketNb) { this.nextSessionIdxs[socketNb]++; } @@ -182,9 +269,62 @@ private void manageSocket(int socketNb, double t_h) { } } - public void setChargingCapabilities(boolean smartCapable, boolean V2Gcapable) { - this.V1GCapable = smartCapable; - this.V2GCapable = V2Gcapable; + + public void fastForwardCharingSessions(double t_h) { + for (int socketNb = 0; socketNb= this.chargeSessionList.size()) { // no more sessions available + break; + } else { // Clone upcomming charger session and increase next session index + this.currentChargingSessions[socketNb] = this.chargeSessionList.get(this.nextSessionIdxs[socketNb]).getClone(); + this.nextSessionIdxs[socketNb]++; + } + } + } + + + public void setV1GCapability(boolean V1GCapable) { + this.V1GCapable = V1GCapable; + } + public void setV2GCapability(boolean V2GCapable) { + this.V2GCapable = V2GCapable; + } + public boolean getV1GCapable() { + return this.V1GCapable; + + } + public boolean getV2GCapable() { + return this.V2GCapable; + + } + + public void setChargingAttitude(OL_ChargingAttitude chargingAttitude) { + this.chargingAttitude = chargingAttitude; + } + + public void setV2GActive(boolean activateV2G) { + this.V2GActive = activateV2G; + if(this.V2GCapable && activateV2G) { + this.assetFlowCategory = OL_AssetFlowCategories.V2GPower_kW; + } + else { + this.assetFlowCategory = OL_AssetFlowCategories.evChargingPower_kW; + } + } + + public boolean getV2GActive() { + return this.V2GActive; + } + public int getCurrentNumberOfChargingSockets() { + return this.currentNumberOfChargingSockets; } @Override @@ -204,7 +344,7 @@ public void storeStatesAndReset() { clear(); } - + @Override public void restoreStates() { energyUsed_kWh = energyUsedStored_kWh; @@ -215,22 +355,14 @@ public void restoreStates() { nextSessionIdxs = nextSessionIdxsStored; } - public void setV2GActive(boolean activateV2G) { - this.V2GActive = activateV2G; - if(this.V2GCapable && activateV2G) { - this.assetFlowCategory = OL_AssetFlowCategories.V2GPower_kW; - } - else { - this.assetFlowCategory = OL_AssetFlowCategories.evChargingPower_kW; - } - } - + @Override public String toString() { return "Power: " + getLastFlows().get(OL_EnergyCarriers.ELECTRICITY) + " kW, capacity: " + capacityElectric_kW + " kW" + - "/n Smart charging capacble: " + this.V1GCapable + - ",/n V2G capable: " + this.V2GCapable + - "/n V2G active: " + this.V2GActive; + ", Active charging attitude " + this.chargingAttitude + + ", Smart charging capable: " + this.V1GCapable + + ", V2G capable: " + this.V2GCapable + + ", V2G active: " + this.V2GActive; } diff --git a/_alp/Classes/Class.J_EAEV.java b/_alp/Classes/Class.J_EAEV.java index 907b184a..8ec26a3e 100644 --- a/_alp/Classes/Class.J_EAEV.java +++ b/_alp/Classes/Class.J_EAEV.java @@ -148,12 +148,16 @@ public boolean endTrip(double tripDist_km) { public double getChargeDeadline_h() { double chargeNeedForNextTrip_kWh = max(0, this.getEnergyNeedForNextTrip_kWh() - this.getCurrentStateOfCharge_kWh()); double chargeTimeMargin_h = 0.5; // Margin to be ready with charging before start of next trip - double nextTripStartTime_h = this.tripTracker.v_nextEventStartTime_min / 60; + double nextTripStartTime_h = getNextTripStartTime_h(); double chargeDeadline_h = nextTripStartTime_h - chargeNeedForNextTrip_kWh / this.capacityElectric_kW - chargeTimeMargin_h; //double chargeDeadline_h = floor((this.tripTracker.v_nextEventStartTime_min / 60 - chargeNeedForNextTrip_kWh / this.getCapacityElectric_kW() / timestep_h) * timestep_h; return chargeDeadline_h; } + public double getNextTripStartTime_h() { + return this.tripTracker.v_nextEventStartTime_min / 60; + } + public void updateChargingHistory(double electricityProduced_kW, double electricityConsumed_kW) { discharged_kWh += electricityProduced_kW * timestep_h; charged_kWh += electricityConsumed_kW * timestep_h; @@ -204,6 +208,9 @@ public double getEnergyChargedOutsideModelArea_kWh() { public void setV2GCapable(boolean isV2GCapable) { this.V2GCapable = isV2GCapable; + + setV2GActive(getV2GActive()); + if(isV2GCapable) { minimumRatioOfChargeCapacity_r = -1; } @@ -212,11 +219,15 @@ public void setV2GCapable(boolean isV2GCapable) { } } + public boolean getV2GCapable() { + return this.V2GCapable; + } + public boolean getV2GActive() { return this.V2GActive; } - public void setV2GActive(boolean activateV2G) { + protected void setV2GActive(boolean activateV2G) { // Should only be called by the chargingManagement class or J_EAEV during initialization itself. (No such thing as friend class in java, so only can put on protected). this.V2GActive = activateV2G; if(this.V2GCapable && activateV2G) { this.assetFlowCategory = OL_AssetFlowCategories.V2GPower_kW; diff --git a/_alp/Classes/Class.J_RapidRunData.java b/_alp/Classes/Class.J_RapidRunData.java index 3f067ba2..d7b06e35 100644 --- a/_alp/Classes/Class.J_RapidRunData.java +++ b/_alp/Classes/Class.J_RapidRunData.java @@ -98,8 +98,12 @@ public class J_RapidRunData { public J_RapidRunData(Agent parentAgent) { this.parentAgent = parentAgent; if (parentAgent instanceof GridConnection) { - if (!((GridConnection)parentAgent).p_owner.p_detailedCompany && !((GridConnection)parentAgent).v_hasQuarterHourlyValues) { + if (((GridConnection)parentAgent).p_owner == null) { storeTotalAssetFlows = false; + } else { + if (!((GridConnection)parentAgent).p_owner.p_detailedCompany && !((GridConnection)parentAgent).v_hasQuarterHourlyValues) { + storeTotalAssetFlows = false; + } } } } @@ -113,11 +117,12 @@ public boolean getStoreTotalAssetFlows() { return this.storeTotalAssetFlows; } - public void initializeAccumulators(double simDuration_h, double timeStep_h, EnumSet v_activeEnergyCarriers, EnumSet v_activeConsumptionEnergyCarriers, EnumSet v_activeProductionEnergyCarriers) { + public void initializeAccumulators(double simDuration_h, double timeStep_h, EnumSet v_activeEnergyCarriers, EnumSet v_activeConsumptionEnergyCarriers, EnumSet v_activeProductionEnergyCarriers, EnumSet activeAssetFlows) { this.timeStep_h = timeStep_h; this.activeEnergyCarriers = EnumSet.copyOf(v_activeEnergyCarriers); this.activeConsumptionEnergyCarriers = EnumSet.copyOf(v_activeConsumptionEnergyCarriers); this.activeProductionEnergyCarriers = EnumSet.copyOf(v_activeProductionEnergyCarriers); + //========== TOTAL ACCUMULATORS ==========// am_totalBalanceAccumulators_kW.createEmptyAccumulators( this.activeEnergyCarriers, true, 24.0, simDuration_h ); am_totalBalanceAccumulators_kW.put( OL_EnergyCarriers.ELECTRICITY, new ZeroAccumulator(true, timeStep_h, simDuration_h) ); @@ -131,15 +136,16 @@ public void initializeAccumulators(double simDuration_h, double timeStep_h, Enum acc_totalPrimaryEnergyProductionHeatpumps_kW = new ZeroAccumulator(true, 24.0, simDuration_h); //========== ASSET FLOWS ==========// + if (storeTotalAssetFlows) { - am_assetFlowsAccumulators_kW.createEmptyAccumulators( this.assetsMetaData.activeAssetFlows, true, timeStep_h, simDuration_h); + am_assetFlowsAccumulators_kW.createEmptyAccumulators( activeAssetFlows, true, timeStep_h, simDuration_h); if (this.assetsMetaData.activeAssetFlows.contains(OL_AssetFlowCategories.batteriesChargingPower_kW)) { ts_dailyAverageBatteriesStoredEnergy_MWh = new ZeroTimeSeries(timeStep_h, simDuration_h); } /*else { ts_dailyAverageBatteriesStoredEnergy_MWh = new ZeroTimeSeries(24, simDuration_h); }*/ } else { - am_assetFlowsAccumulators_kW.createEmptyAccumulators( this.assetsMetaData.activeAssetFlows, true, 24.0, simDuration_h); + am_assetFlowsAccumulators_kW.createEmptyAccumulators( activeAssetFlows, true, 24.0, simDuration_h); if (this.assetsMetaData.activeAssetFlows.contains(OL_AssetFlowCategories.batteriesChargingPower_kW)) { ts_dailyAverageBatteriesStoredEnergy_MWh = new ZeroTimeSeries(24, simDuration_h); } /*else {