diff --git a/Zero_engine.alpx b/Zero_engine.alpx index 604453f6..29de1bc0 100644 --- a/Zero_engine.alpx +++ b/Zero_engine.alpx @@ -1316,6 +1316,26 @@ LARGE_CONNECTION (grootverbruik, > 3x80) + + 1782815749074 + + + + + + @@ -2271,6 +2291,11 @@ LARGE_CONNECTION (grootverbruik, > 3x80) 1764938574089 + + 1782811550548 + + 1752680962144 + com.anylogic.libraries.modules.markup_descriptors diff --git a/_alp/Agents/EnergyModel/AOC.EnergyModel.xml b/_alp/Agents/EnergyModel/AOC.EnergyModel.xml index 9a63138c..07b7a330 100644 --- a/_alp/Agents/EnergyModel/AOC.EnergyModel.xml +++ b/_alp/Agents/EnergyModel/AOC.EnergyModel.xml @@ -50,7 +50,9 @@ import com.querydsl.core.types.dsl.TimeExpression; //import zero_engine.J_EAStorageElectric; //import zero_engine.J_EAConsumption; -import com.anylogic.cloud.util.DateUtils;]]> +import com.anylogic.cloud.util.DateUtils; + +import zero_engine.J_ActivityTrackerTrips.TripRecord;]]> I_EnergyData false diff --git a/_alp/Agents/EnergyModel/Levels/Level.level.xml b/_alp/Agents/EnergyModel/Levels/Level.level.xml index e80a2009..2b3bfccd 100644 --- a/_alp/Agents/EnergyModel/Levels/Level.level.xml +++ b/_alp/Agents/EnergyModel/Levels/Level.level.xml @@ -1419,4 +1419,121 @@ energyDataViewer.viewArea.navigateTo(); 0 0 + + false + 1782465008753 + + 900 + -320 + + true + true + false + SHAPE_DRAW_2D3D + + false + -16777216 + true + traceln("index of 1 jan: %s", p_timeParameters.getDayOfWeek1jan()); +traceln("day of 1 jan: %s", p_timeParameters.getDayFromDayIndex(p_timeParameters.getDayOfWeek1jan())); +traceln("p_timeParameters.getDayOfWeek1jan(): %s", p_timeParameters.getDayOfWeek1jan()); +traceln("p_timeParameters.getWeekIndex(12): %s", p_timeParameters.getWeekIndex(12)); +traceln("p_timeParameters.getWeekIndex(36): %s", p_timeParameters.getWeekIndex(36)); +traceln("p_timeParameters.getWeekIndex(200): %s", p_timeParameters.getWeekIndex(200)); +traceln("p_timeParameters.getWeekStart_h(0): %s", p_timeParameters.getWeekStart_h(0)); +traceln("p_timeParameters.getWeekStart_h(1): %s", p_timeParameters.getWeekStart_h(1)); +traceln("p_timeParameters.getWeekStart_h(2): %s", p_timeParameters.getWeekStart_h(2)); + + + + Date + + + + false + 1782475157232 + + 1040 + -320 + + true + true + false + SHAPE_DRAW_2D3D + + false + -16777216 + true + //GridConnection GC = findFirst(c_gridConnections, gc -> gc.c_tripTrackers.size() > 0); +GridConnection GC = findFirst(c_gridConnections, gc -> gc.p_gridConnectionID.equals("addressuid.school")); +J_ActivityTrackerTrips tt = GC.c_tripTrackers.get(0); + +double startTime_h = 0; +double endTime_h = 24+168; + +List<TripRecord> trips = tt.getTripsInRange(startTime_h, endTime_h); + +traceln(tt); + +for (TripRecord trip : trips) { + traceln("startTime: %s", trip.startTime_h()); + traceln("endTime: %s", trip.endTime_h()); + traceln("distance: %s", trip.distance_km()); +} + + + + Trips + + + + false + 1782826903548 + + 1180 + -320 + + true + true + false + SHAPE_DRAW_2D3D + + false + -16777216 + true + //GridConnection GC = findFirst(c_gridConnections, gc -> gc.c_tripTrackers.size() > 0); +GridConnection GC = findFirst(c_gridConnections, gc -> gc.p_gridConnectionID.equals("addressuid.school")); + +double timeOfIntervalStart_h = 0; +double timeOfIntervalEnd_h = 48; + +Map<Class< ? extends I_AssetManagement>, J_AssetTypeForecast> map = GC.f_getForecast(timeOfIntervalStart_h, timeOfIntervalEnd_h, p_timeParameters); + +J_AssetTypeForecast chargingForecast = map.get(I_ChargingManagement.class); + +traceln("chargingForecast: %s", chargingForecast); + +Double[] load = chargingForecast.load_kW().get(OL_EnergyCarriers.ELECTRICITY); + +traceln("load: "); +for (int i = 0; i < timeOfIntervalEnd_h / p_timeParameters.getTimeStep_h(); i++) { + double t = timeOfIntervalStart_h + i * p_timeParameters.getTimeStep_h(); + traceln("time t: %s, load: %s kW", t, load[i]); +} + + + + + Forecaster + + diff --git a/_alp/Agents/GridConnection/Code/Functions.java b/_alp/Agents/GridConnection/Code/Functions.java index 965d67e5..a086a5a0 100644 --- a/_alp/Agents/GridConnection/Code/Functions.java +++ b/_alp/Agents/GridConnection/Code/Functions.java @@ -1242,3 +1242,14 @@ else if (heatingAsset instanceof J_EAConversionHeatPump heatPump) { return nettoBalance_kW; /*ALCODEEND*/} +Map, J_AssetTypeForecast> f_getForecast(double timeOfIntervalStart_h,double timeOfIntervalEnd_h,J_TimeParameters timeParameters) +{/*ALCODESTART::1782459765981*/ +if (p_energyManagement != null) { + return p_energyManagement.getFlexAssetForecast(timeOfIntervalStart_h, timeOfIntervalEnd_h); +} + +else { + return new HashMap, J_AssetTypeForecast>(); +} +/*ALCODEEND*/} + diff --git a/_alp/Agents/GridConnection/Code/Functions.xml b/_alp/Agents/GridConnection/Code/Functions.xml index 4b6e4235..9286fe58 100644 --- a/_alp/Agents/GridConnection/Code/Functions.xml +++ b/_alp/Agents/GridConnection/Code/Functions.xml @@ -1210,4 +1210,32 @@ + + RETURNS_VALUE + Map<Class< ? extends I_AssetManagement>, J_AssetTypeForecast> + 1782459765981 + + 1490 + 480 + + false + true + true + + + + + + + + + + + + + + diff --git a/_alp/Classes/Class.I_AssetManagement.java b/_alp/Classes/Class.I_AssetManagement.java index 4e6fc143..ff9c51a5 100644 --- a/_alp/Classes/Class.I_AssetManagement.java +++ b/_alp/Classes/Class.I_AssetManagement.java @@ -16,5 +16,5 @@ public interface I_AssetManagement extends I_StoreStatesAndReset { //Get the AssetManagementInterface type (I_ChargingManagement.class, I_Heatingmanagement.class, etc.) Class getAssetManagementInterfaceType(); - + J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h); } \ No newline at end of file diff --git a/_alp/Classes/Class.I_EnergyManagement.java b/_alp/Classes/Class.I_EnergyManagement.java index 1906faf9..6e015715 100644 --- a/_alp/Classes/Class.I_EnergyManagement.java +++ b/_alp/Classes/Class.I_EnergyManagement.java @@ -73,8 +73,28 @@ default boolean isAssetManagementSupported(Class assetManagementType) { return getInternalAssetManagements().contains(assetManagementType) || getSupportedExternalAssetManagements().contains(assetManagementType); } + + default public Map, J_AssetTypeForecast> getFlexAssetForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map, J_AssetTypeForecast> forecasts = new HashMap, J_AssetTypeForecast>(); + Map, I_AssetManagement> activeExternalManagements = this.getActiveExternalAssetManagements(); + for (Class externalManagementClass : this.getSupportedExternalAssetManagements()) { + I_AssetManagement externalManagement = activeExternalManagements.get(externalManagementClass); + if (externalManagement != null) { + J_AssetTypeForecast forecast = externalManagement.getForecast(timeOfIntervalStart_h, timeOfIntervalEnd_h); + forecasts.put(externalManagementClass, forecast); + } + } + for (Class internalManagementClass : this.getInternalAssetManagements()) { + J_AssetTypeForecast forecast = forecastInternalAssetType(internalManagementClass, timeOfIntervalStart_h, timeOfIntervalEnd_h); + forecasts.put(internalManagementClass, forecast); + } + return forecasts; + } - + default J_AssetTypeForecast forecastInternalAssetType(Class type, double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + throw new RuntimeException("EMS declares " + type.getSimpleName() + " as an internal asset management but does not override forecastInternalAssetType() to handle it."); + } + ////Checks //Check configuration: Should be called whenever a management or asset has been removed/added/changed. default public void checkConfiguration(List flexAssetsGCList) { diff --git a/_alp/Classes/Class.J_ActivityTrackerTrips.java b/_alp/Classes/Class.J_ActivityTrackerTrips.java index d9f205a8..b534b90f 100644 --- a/_alp/Classes/Class.J_ActivityTrackerTrips.java +++ b/_alp/Classes/Class.J_ActivityTrackerTrips.java @@ -56,7 +56,7 @@ public J_ActivityTrackerTrips(TextFile tripsCSV, int CSVRowIndex, I_Vehicle vehi // 'forward' to next activity setStartIndex(timeVariables, chargePointRegistration); - } + } /** * Constructor using defined trips as input @@ -68,23 +68,19 @@ public J_ActivityTrackerTrips(List tripRecords, I_Vehicle vehicle, I // 'forward' to next activity setStartIndex(timeVariables, chargePointRegistration); - } + } - private double getTimeSinceWeekStart_h(double time_h) { - double timeSinceWeekStart_h = (time_h + (timeParameters.getDayOfWeek1jan()-1) * 24) % (7*24); - return timeSinceWeekStart_h; - } + private double getTimeSinceWeekStart_h(double time_h) { + double timeSinceWeekStart_h = (time_h + (timeParameters.getDayOfWeek1jan()-1) * 24) % (7*24); + return timeSinceWeekStart_h; + } - private void setNextTrip() { - eventIndex++; - if ( eventIndex > tripRecords.size() - 1 ) { - eventIndex = 0; - } - } - - - - + private void setNextTrip() { + eventIndex++; + if ( eventIndex > tripRecords.size() - 1 ) { + eventIndex = 0; + } + } /* * Main method that is called every timestep. @@ -93,128 +89,154 @@ private void setNextTrip() { * If the vehicle is already on a trip it progresses the trip to check if the trip ends this timestep. * If so, it also checks if a new trip is starting in that same timestep. */ - public void manageActivities(J_TimeVariables timeVariables, I_ChargePointRegistration chargePointRegistration) { - double time_h = timeVariables.getT_h(); - double timeSinceWeekStart_h = getTimeSinceWeekStart_h(time_h); // Trip start/end-times are all defined as minutes since monday 00:00h, trips are looped indefinitely - if (vehicle.getAvailability()) { // at start of timestep! check for multiple 'events' in timestep! - if ( timeSinceWeekStart_h >= tripRecords.get(eventIndex).startTime_h() && (timeSinceWeekStart_h-timeParameters.getTimeStep_h()) < tripRecords.get(eventIndex).startTime_h()) { // is a trip starting this timestep? - currentTripTimesteps_n = max(1,roundToInt(((tripRecords.get(eventIndex).endTime_h() - tripRecords.get(eventIndex).startTime_h()) / (timeParameters.getTimeStep_h())))); - - vehicle.startTrip(timeVariables); - if(vehicle instanceof J_EAEV EV) { - chargePointRegistration.deregisterChargingRequest(EV); - } - if (timeSinceWeekStart_h >= tripRecords.get(eventIndex).endTime_h() && (timeSinceWeekStart_h-timeParameters.getTimeStep_h()) < tripRecords.get(eventIndex).endTime_h()) { // is the trip also ending this timestep? - vehicle.endTrip(tripDistance_km); - setNextTrip(); - prepareNextActivity(timeVariables, chargePointRegistration); - } - } - } else { - if (vehicle instanceof J_EAFuelVehicle fuelVehicle) { - fuelVehicle.progressTrip(tripDistance_km / currentTripTimesteps_n); - } - if (timeSinceWeekStart_h >= tripRecords.get(eventIndex).endTime_h() && (timeSinceWeekStart_h-timeParameters.getTimeStep_h()) < tripRecords.get(eventIndex).endTime_h()) { // is a trip ending this timestep? - vehicle.endTrip(tripDistance_km); - setNextTrip(); - prepareNextActivity(timeVariables, chargePointRegistration); - if (timeSinceWeekStart_h >= tripRecords.get(eventIndex).startTime_h() && (timeSinceWeekStart_h-timeParameters.getTimeStep_h()) < tripRecords.get(eventIndex).startTime_h() ) { // is the next trip also starting this timestep? - currentTripTimesteps_n = max(1,roundToInt(((tripRecords.get(eventIndex).endTime_h() - tripRecords.get(eventIndex).startTime_h()) / timeParameters.getTimeStep_h()))); - vehicle.startTrip(timeVariables); - if(vehicle instanceof J_EAEV EV) { - chargePointRegistration.deregisterChargingRequest(EV); - } - } - } - } - } + public void manageActivities(J_TimeVariables timeVariables, I_ChargePointRegistration chargePointRegistration) { + double time_h = timeVariables.getT_h(); + double timeSinceWeekStart_h = getTimeSinceWeekStart_h(time_h); // Trip start/end-times are all defined as minutes since monday 00:00h, trips are looped indefinitely + if (vehicle.getAvailability()) { // at start of timestep! check for multiple 'events' in timestep! + if ( timeSinceWeekStart_h >= tripRecords.get(eventIndex).startTime_h() && (timeSinceWeekStart_h-timeParameters.getTimeStep_h()) < tripRecords.get(eventIndex).startTime_h()) { // is a trip starting this timestep? + currentTripTimesteps_n = max(1,roundToInt(((tripRecords.get(eventIndex).endTime_h() - tripRecords.get(eventIndex).startTime_h()) / (timeParameters.getTimeStep_h())))); + + vehicle.startTrip(timeVariables); + if(vehicle instanceof J_EAEV EV) { + chargePointRegistration.deregisterChargingRequest(EV); + } + if (timeSinceWeekStart_h >= tripRecords.get(eventIndex).endTime_h() && (timeSinceWeekStart_h-timeParameters.getTimeStep_h()) < tripRecords.get(eventIndex).endTime_h()) { // is the trip also ending this timestep? + vehicle.endTrip(tripDistance_km); + setNextTrip(); + prepareNextActivity(timeVariables, chargePointRegistration); + } + } + } else { + if (vehicle instanceof J_EAFuelVehicle fuelVehicle) { + fuelVehicle.progressTrip(tripDistance_km / currentTripTimesteps_n); + } + if (timeSinceWeekStart_h >= tripRecords.get(eventIndex).endTime_h() && (timeSinceWeekStart_h-timeParameters.getTimeStep_h()) < tripRecords.get(eventIndex).endTime_h()) { // is a trip ending this timestep? + vehicle.endTrip(tripDistance_km); + setNextTrip(); + prepareNextActivity(timeVariables, chargePointRegistration); + if (timeSinceWeekStart_h >= tripRecords.get(eventIndex).startTime_h() && (timeSinceWeekStart_h-timeParameters.getTimeStep_h()) < tripRecords.get(eventIndex).startTime_h() ) { // is the next trip also starting this timestep? + currentTripTimesteps_n = max(1,roundToInt(((tripRecords.get(eventIndex).endTime_h() - tripRecords.get(eventIndex).startTime_h()) / timeParameters.getTimeStep_h()))); + vehicle.startTrip(timeVariables); + if(vehicle instanceof J_EAEV EV) { + chargePointRegistration.deregisterChargingRequest(EV); + } + } + } + } + } - /* - * This method 'Forwards' to the activity at time in timeVariables - * It also immediately calls prepareNextActivity - */ - public void setStartIndex(J_TimeVariables timeVariables, I_ChargePointRegistration chargePointRegistration) { - double time_h = timeVariables.getT_h(); - double timeSinceWeekStart_h = getTimeSinceWeekStart_h(time_h); - boolean looped = false; - while ( tripRecords.get(eventIndex).startTime_h() < (timeSinceWeekStart_h ) ) { // If this occurs 'during' a trip, that trip is ignored, it is not executed. - setNextTrip(); // Skip to the next trip. - - if (eventIndex == tripRecords.size()-1 ) { - if (looped) { - setNextTrip(); // Increments eventIndex, reverts to first trip of week when 'overflowing' - break; - } else { - looped = true; - } - } - } - prepareNextActivity(timeVariables, chargePointRegistration); - } + /* + * This method 'Forwards' to the activity at time in timeVariables + * It also immediately calls prepareNextActivity + */ + public void setStartIndex(J_TimeVariables timeVariables, I_ChargePointRegistration chargePointRegistration) { + double time_h = timeVariables.getT_h(); + double timeSinceWeekStart_h = getTimeSinceWeekStart_h(time_h); + boolean looped = false; + while ( tripRecords.get(eventIndex).startTime_h() < (timeSinceWeekStart_h ) ) { // If this occurs 'during' a trip, that trip is ignored, it is not executed. + setNextTrip(); // Skip to the next trip. + + if (eventIndex == tripRecords.size()-1 ) { + if (looped) { + setNextTrip(); // Increments eventIndex, reverts to first trip of week when 'overflowing' + break; + } else { + looped = true; + } + } + } + prepareNextActivity(timeVariables, chargePointRegistration); + } - /* - * This method is called after the previous trip ended. - * It calculates the time to and distance of the coming trip. - * If the vehicle is an EV it also calculates the charging need, including possible future trips. - * The function passes this information to the EV and registers the charging request at the chargepoint. - */ - public void prepareNextActivity(J_TimeVariables timeVariables, I_ChargePointRegistration chargePointRegistration) { - double time_h = timeVariables.getT_h(); - - // Trip start/end-times are all defined as hours since monday 00:00h - double timeSinceWeekStart_h = getTimeSinceWeekStart_h(time_h); - nextEventStartTime_h = tripRecords.get(eventIndex).startTime_h(); - - if (eventIndex == 0 && timeSinceWeekStart_h > nextEventStartTime_h) { // Next week's trip! - nextEventStartTime_h = (nextEventStartTime_h + time_h - timeSinceWeekStart_h) + 168; - } else { - nextEventStartTime_h = (nextEventStartTime_h + time_h - timeSinceWeekStart_h); - } - idleTimeToNextTrip_h = (nextEventStartTime_h - timeSinceWeekStart_h) % (24*7); // Modulo 24*7 needed because otherwise negative values can occur when trip starts 'next week'. - tripDistance_km = distanceScaling_fr * tripRecords.get(eventIndex).distance_km(); // Update upcoming trip distance - - if (vehicle instanceof J_EAEV ev) { - - double energyNeedForNextTrip_kWh = ev.getEnergyConsumption_kWhpkm() * tripDistance_km; - if (idleTimeToNextTrip_h > 0 && (energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh())> idleTimeToNextTrip_h * 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_h,2), roundToDecimal(energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh(),2)); - } - // Check if more charging is needed for next trip! - double nextTripDist_km = 0; - double nextTripStartTime_h = 0; - - if ( eventIndex == tripRecords.size() - 1 ) { - nextTripDist_km = tripRecords.get(0).distance_km(); - nextTripStartTime_h = tripRecords.get(0).endTime_h(); - } else { - nextTripDist_km = distanceScaling_fr*tripRecords.get(eventIndex+1).distance_km(); - nextTripStartTime_h = tripRecords.get(eventIndex+1).startTime_h(); - } - double additionalChargingNeededForNextTrip_kWh = max(0,nextTripDist_km * ev.getEnergyConsumption_kWhpkm() - (nextTripStartTime_h - tripRecords.get(eventIndex).endTime_h())*ev.getVehicleChargingCapacity_kW()); - - energyNeedForNextTrip_kWh += additionalChargingNeededForNextTrip_kWh; - energyNeedForNextTrip_kWh = min(energyNeedForNextTrip_kWh+10,ev.getStorageCapacity_kWh()); - ev.setEnergyNeedForNextTrip_kWh(energyNeedForNextTrip_kWh); - - //Register EV at the chargepoint - chargePointRegistration.registerChargingRequest(ev); - } - } - + /* + * This method is called after the previous trip ended. + * It calculates the time to and distance of the coming trip. + * If the vehicle is an EV it also calculates the charging need, including possible future trips. + * The function passes this information to the EV and registers the charging request at the chargepoint. + */ + public void prepareNextActivity(J_TimeVariables timeVariables, I_ChargePointRegistration chargePointRegistration) { + double time_h = timeVariables.getT_h(); + + // Trip start/end-times are all defined as hours since monday 00:00h + double timeSinceWeekStart_h = getTimeSinceWeekStart_h(time_h); + nextEventStartTime_h = tripRecords.get(eventIndex).startTime_h(); + + if (eventIndex == 0 && timeSinceWeekStart_h > nextEventStartTime_h) { // Next week's trip! + nextEventStartTime_h = (nextEventStartTime_h + time_h - timeSinceWeekStart_h) + 168; + } else { + nextEventStartTime_h = (nextEventStartTime_h + time_h - timeSinceWeekStart_h); + } + idleTimeToNextTrip_h = (nextEventStartTime_h - timeSinceWeekStart_h) % (24*7); // Modulo 24*7 needed because otherwise negative values can occur when trip starts 'next week'. + tripDistance_km = distanceScaling_fr * tripRecords.get(eventIndex).distance_km(); // Update upcoming trip distance + + if (vehicle instanceof J_EAEV ev) { + + double energyNeedForNextTrip_kWh = ev.getEnergyConsumption_kWhpkm() * tripDistance_km; + if (idleTimeToNextTrip_h > 0 && (energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh())> idleTimeToNextTrip_h * 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_h,2), roundToDecimal(energyNeedForNextTrip_kWh-ev.getCurrentSOC_kWh(),2)); + } + // Check if more charging is needed for next trip! + double nextTripDist_km = 0; + double nextTripStartTime_h = 0; + + if ( eventIndex == tripRecords.size() - 1 ) { + nextTripDist_km = tripRecords.get(0).distance_km(); + nextTripStartTime_h = tripRecords.get(0).endTime_h(); + } else { + nextTripDist_km = distanceScaling_fr*tripRecords.get(eventIndex+1).distance_km(); + nextTripStartTime_h = tripRecords.get(eventIndex+1).startTime_h(); + } + double additionalChargingNeededForNextTrip_kWh = max(0,nextTripDist_km * ev.getEnergyConsumption_kWhpkm() - (nextTripStartTime_h - tripRecords.get(eventIndex).endTime_h())*ev.getVehicleChargingCapacity_kW()); + + energyNeedForNextTrip_kWh += additionalChargingNeededForNextTrip_kWh; + energyNeedForNextTrip_kWh = min(energyNeedForNextTrip_kWh+10,ev.getStorageCapacity_kWh()); + ev.setEnergyNeedForNextTrip_kWh(energyNeedForNextTrip_kWh); + + //Register EV at the chargepoint + chargePointRegistration.registerChargingRequest(ev); + } + } - //Setters - public void setVehicle(I_Vehicle vehicle) { - this.vehicle = vehicle; - } - public void setDistanceScaling_fr(double distanceScaling_fr) { - this.distanceScaling_fr = distanceScaling_fr; - } - public void setAnnualDistance_km(double desiredAnnualDistance_km) { // Scale trips to come to a certain total annual distance traveled. This can lead to unfeasibly long trips for EVs!! - double currentAnnualDistance_km = getAnnualDistance_km(); - double scalingFactor_f = desiredAnnualDistance_km / currentAnnualDistance_km; - - ListIterator iterator = tripRecords.listIterator(); - while (iterator.hasNext()) { + /* + * This method returns a list of trips, + * The included trips are those that have a startTime within the range of [startTime_h, endTime_h) + * For each of these start times the TripRecord includes the distance for that upcoming trip, which the J_EAEV can translate into required energy. + * The times in the TripRecords are 'absolute', i.e. relative to the first of January. + */ + public List getTripsInRange( double startTime_h, double endTime_h ) { + List trips = new ArrayList<>(); + if (endTime_h < startTime_h) { + // Could return an empty list instead, but prefer to throw an exception to hint at something going wrong. + throw new RuntimeException(String.format("Could not get trips in range [ %s, %s ), since startTime is after endTime_h", startTime_h, endTime_h)); + } + int startWeek = this.timeParameters.getWeekIndex(startTime_h); + int endWeek = this.timeParameters.getWeekIndex(endTime_h); + for (int week = startWeek; week <= endWeek; week++) { + double weekStartTime_h = this.timeParameters.getWeekStart_h(week); + for (TripRecord trip : tripRecords) { + double absStart = weekStartTime_h + trip.startTime_h(); + double absEnd = weekStartTime_h + trip.endTime_h(); + if (absStart >= startTime_h && absStart < endTime_h) { + trips.add(new TripRecord(absStart, absEnd, trip.distance_km())); + } + } + } + return trips; + } + + //Setters + public void setVehicle(I_Vehicle vehicle) { + this.vehicle = vehicle; + } + public void setDistanceScaling_fr(double distanceScaling_fr) { + this.distanceScaling_fr = distanceScaling_fr; + } + public void setAnnualDistance_km(double desiredAnnualDistance_km) { // Scale trips to come to a certain total annual distance traveled. This can lead to unfeasibly long trips for EVs!! + double currentAnnualDistance_km = getAnnualDistance_km(); + double scalingFactor_f = desiredAnnualDistance_km / currentAnnualDistance_km; + + ListIterator iterator = tripRecords.listIterator(); + while (iterator.hasNext()) { TripRecord record = iterator.next(); iterator.set(new TripRecord( record.startTime_h(), @@ -222,48 +244,52 @@ public void setAnnualDistance_km(double desiredAnnualDistance_km) { // Scale tri record.distance_km() * scalingFactor_f )); } - } - //Getters - public I_Vehicle getVehicle() { - return this.vehicle; - } - public double getNextEventStartTime_h() { - return nextEventStartTime_h; - } - - public double getDistanceScaling_fr( ) { - return this.distanceScaling_fr; - } - public double getAnnualDistance_km() { - double currentAnnualDistance_km = 52 * tripRecords.stream().mapToDouble(TripRecord::distance_km).sum(); // assumed trip-data is one week long!! Hence the 52, for 52 weeks in a year. - return currentAnnualDistance_km; - } - - public static record TripRecord(double startTime_h, double endTime_h, double distance_km) { - } - - @Override - public void storeStatesAndReset() { - eventIndexStored = eventIndex; - nextEventStartTimeStored_h = nextEventStartTime_h; - idleTimeToNextTripStored_h = idleTimeToNextTrip_h; - idleTimeToNextTrip_h = 0; - // Don't forget to call setStartIndex ! - } - - @Override - public void restoreStates() { - eventIndex = eventIndexStored; - nextEventStartTime_h = nextEventStartTimeStored_h; - idleTimeToNextTrip_h = idleTimeToNextTripStored_h; - tripDistance_km = distanceScaling_fr * tripRecords.get(eventIndex).distance_km(); // Update upcoming trip distance - } - - @Override - public String toString() { - String outputString = "J_ActivityTrackerTrips: \n"; - outputString += "Based on " + (CSVRowIndex != null ? "CSV data with row index: " + CSVRowIndex : "Custom input") + "\n"; - outputString += "Distance Scaling = " + this.distanceScaling_fr + " "; - return outputString; - } + } + //Getters + public I_Vehicle getVehicle() { + return this.vehicle; + } + public double getNextEventStartTime_h() { + return nextEventStartTime_h; + } + + public double getCurrentTripEndTime_h() { + return this.tripRecords.get(eventIndex).endTime_h(); + } + + public double getDistanceScaling_fr( ) { + return this.distanceScaling_fr; + } + public double getAnnualDistance_km() { + double currentAnnualDistance_km = 52 * tripRecords.stream().mapToDouble(TripRecord::distance_km).sum(); // assumed trip-data is one week long!! Hence the 52, for 52 weeks in a year. + return currentAnnualDistance_km; + } + + public static record TripRecord(double startTime_h, double endTime_h, double distance_km) {} + + + @Override + public void storeStatesAndReset() { + eventIndexStored = eventIndex; + nextEventStartTimeStored_h = nextEventStartTime_h; + idleTimeToNextTripStored_h = idleTimeToNextTrip_h; + idleTimeToNextTrip_h = 0; + // Don't forget to call setStartIndex ! + } + + @Override + public void restoreStates() { + eventIndex = eventIndexStored; + nextEventStartTime_h = nextEventStartTimeStored_h; + idleTimeToNextTrip_h = idleTimeToNextTripStored_h; + tripDistance_km = distanceScaling_fr * tripRecords.get(eventIndex).distance_km(); // Update upcoming trip distance + } + + @Override + public String toString() { + String outputString = "J_ActivityTrackerTrips: \n"; + outputString += "Based on " + (CSVRowIndex != null ? "CSV data with row index: " + CSVRowIndex : "Custom input") + "\n"; + outputString += "Distance Scaling = " + this.distanceScaling_fr + " "; + return outputString; + } } \ No newline at end of file diff --git a/_alp/Classes/Class.J_AssetTypeForecast.java b/_alp/Classes/Class.J_AssetTypeForecast.java new file mode 100644 index 00000000..cfec47ea --- /dev/null +++ b/_alp/Classes/Class.J_AssetTypeForecast.java @@ -0,0 +1,9 @@ +/** + * J_AssetTypeForecast + */ +public record J_AssetTypeForecast( + Class assetType, + Map load_kW, + OL_ForecastStatus status, + String reason // Optional field to give an explanation for the status +) {} \ No newline at end of file diff --git a/_alp/Classes/Class.J_BackupGeneratorManagementContractCapacity.java b/_alp/Classes/Class.J_BackupGeneratorManagementContractCapacity.java index d8b52757..19e7eb7c 100644 --- a/_alp/Classes/Class.J_BackupGeneratorManagementContractCapacity.java +++ b/_alp/Classes/Class.J_BackupGeneratorManagementContractCapacity.java @@ -37,6 +37,13 @@ public void manageBackupGenerator(J_TimeVariables timeVariables) { } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_BackupGeneratorManagement.class, loadMap, status, reason); + } + ////Store and reset states public void storeStatesAndReset() { //Nothing to store and reset diff --git a/_alp/Classes/Class.J_BackupGeneratorManagementExternalSetpoint.java b/_alp/Classes/Class.J_BackupGeneratorManagementExternalSetpoint.java index d6da0e81..55dd94a6 100644 --- a/_alp/Classes/Class.J_BackupGeneratorManagementExternalSetpoint.java +++ b/_alp/Classes/Class.J_BackupGeneratorManagementExternalSetpoint.java @@ -41,6 +41,13 @@ public void setBackupGeneratorSetpoint_kW(double backupGeneratorPowerSetpoint_kW this.backupGeneratorPowerSetpoint_kW = backupGeneratorPowerSetpoint_kW; } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "External Setpoint Management can not be forecasted."; + return new J_AssetTypeForecast(I_BackupGeneratorManagement.class, loadMap, status, reason); + } + ////Store and reset states public void storeStatesAndReset() { //Nothing to store and reset diff --git a/_alp/Classes/Class.J_BatteryManagementExternalSetpoint.java b/_alp/Classes/Class.J_BatteryManagementExternalSetpoint.java index e448015b..11cb5b7a 100644 --- a/_alp/Classes/Class.J_BatteryManagementExternalSetpoint.java +++ b/_alp/Classes/Class.J_BatteryManagementExternalSetpoint.java @@ -53,7 +53,12 @@ public void manageBattery(J_TimeVariables timeVariables) { } } - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "External Setpoint Management can not be forecasted."; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_BatteryManagementLocalBalancing.java b/_alp/Classes/Class.J_BatteryManagementLocalBalancing.java index 4c294c8d..dbdf9cf5 100644 --- a/_alp/Classes/Class.J_BatteryManagementLocalBalancing.java +++ b/_alp/Classes/Class.J_BatteryManagementLocalBalancing.java @@ -70,6 +70,13 @@ public void manageBattery(J_TimeVariables timeVariables) { gc.f_updateFlexAssetFlows(gc.p_batteryAsset, chargeSetpoint_kW / gc.p_batteryAsset.getCapacityElectric_kW(), timeVariables); } } + + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, reason); + } private double getDeliveryCapacity_kW() { return gc.v_liveConnectionMetaData.getContractedDeliveryCapacity_kW(); diff --git a/_alp/Classes/Class.J_BatteryManagementOff.java b/_alp/Classes/Class.J_BatteryManagementOff.java index 564475dc..aca2aaa3 100644 --- a/_alp/Classes/Class.J_BatteryManagementOff.java +++ b/_alp/Classes/Class.J_BatteryManagementOff.java @@ -36,9 +36,15 @@ public void manageBattery(J_TimeVariables timeVariables) { } } - - - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + int timeStepsInForecast = roundToInt((timeOfIntervalEnd_h - timeOfIntervalStart_h) / this.timeParameters.getTimeStep_h()); + Double[] electricityLoad_kW = new Double[timeStepsInForecast]; + Arrays.fill(electricityLoad_kW, 0.0); + Map loadMap = new HashMap<>(); + loadMap.put(OL_EnergyCarriers.ELECTRICITY, electricityLoad_kW); + OL_ForecastStatus status = OL_ForecastStatus.PERFECT_FORECAST; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, null); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_BatteryManagementPeakShaving.java b/_alp/Classes/Class.J_BatteryManagementPeakShaving.java index 2dc10a0e..7d776bac 100644 --- a/_alp/Classes/Class.J_BatteryManagementPeakShaving.java +++ b/_alp/Classes/Class.J_BatteryManagementPeakShaving.java @@ -149,8 +149,12 @@ private double getBalanceElectricity_kW() { } } - - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java b/_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java index 0024a11d..2550e0a3 100644 --- a/_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java +++ b/_alp/Classes/Class.J_BatteryManagementPeakShavingForecast.java @@ -198,7 +198,12 @@ else if (agent instanceof EnergyCoop) { this.initializeAssets(); } - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_BatteryManagementPrice.java b/_alp/Classes/Class.J_BatteryManagementPrice.java index 1f334367..8d212f01 100644 --- a/_alp/Classes/Class.J_BatteryManagementPrice.java +++ b/_alp/Classes/Class.J_BatteryManagementPrice.java @@ -90,8 +90,12 @@ else if (WTP_discharge_eurpkWh < currentElectricityPriceCharge_eurpkWh) { } } - - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_BatteryManagementProfile.java b/_alp/Classes/Class.J_BatteryManagementProfile.java index 33d4d60d..c34a378b 100644 --- a/_alp/Classes/Class.J_BatteryManagementProfile.java +++ b/_alp/Classes/Class.J_BatteryManagementProfile.java @@ -61,6 +61,13 @@ public void setProfilePointer(J_ProfilePointer batteryProfilePointer_kW) { } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, reason); + } + //Get parentagent public Agent getParentAgent() { return this.gc; diff --git a/_alp/Classes/Class.J_BatteryManagementSelfConsumption.java b/_alp/Classes/Class.J_BatteryManagementSelfConsumption.java index bf5380c1..135b8bcc 100644 --- a/_alp/Classes/Class.J_BatteryManagementSelfConsumption.java +++ b/_alp/Classes/Class.J_BatteryManagementSelfConsumption.java @@ -42,8 +42,12 @@ public void manageBattery(J_TimeVariables timeVariables) { } } - - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_BatteryManagementSelfConsumptionGridNode.java b/_alp/Classes/Class.J_BatteryManagementSelfConsumptionGridNode.java index 0f65273c..d9fb0d99 100644 --- a/_alp/Classes/Class.J_BatteryManagementSelfConsumptionGridNode.java +++ b/_alp/Classes/Class.J_BatteryManagementSelfConsumptionGridNode.java @@ -45,7 +45,12 @@ public void manageBattery(J_TimeVariables timeVariables) { } } - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_BatteryManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_ChargingManagementExternalSetpoint.java b/_alp/Classes/Class.J_ChargingManagementExternalSetpoint.java index d7fc1c9f..020c7019 100644 --- a/_alp/Classes/Class.J_ChargingManagementExternalSetpoint.java +++ b/_alp/Classes/Class.J_ChargingManagementExternalSetpoint.java @@ -56,6 +56,13 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab map_chargingSetpoints_kW = new HashMap<>(); } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "External Setpoint Management can not be forecasted."; + return new J_AssetTypeForecast(I_ChargingManagement.class, loadMap, status, reason); + } + public void setChargingSetpoints(Map map_chargingSetpoints_kW) { this.map_chargingSetpoints_kW = map_chargingSetpoints_kW; } diff --git a/_alp/Classes/Class.J_ChargingManagementGridBalancing.java b/_alp/Classes/Class.J_ChargingManagementGridBalancing.java index 5623762f..b3cfe419 100644 --- a/_alp/Classes/Class.J_ChargingManagementGridBalancing.java +++ b/_alp/Classes/Class.J_ChargingManagementGridBalancing.java @@ -75,6 +75,13 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_ChargingManagement.class, loadMap, status, reason); + } + public void setV2GActive(boolean activateV2G) { this.V2GActive = activateV2G; this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(activateV2G)); // NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory diff --git a/_alp/Classes/Class.J_ChargingManagementLocalBalancing.java b/_alp/Classes/Class.J_ChargingManagementLocalBalancing.java index 8108f93c..212e80df 100644 --- a/_alp/Classes/Class.J_ChargingManagementLocalBalancing.java +++ b/_alp/Classes/Class.J_ChargingManagementLocalBalancing.java @@ -73,6 +73,13 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_ChargingManagement.class, loadMap, status, reason); + } + public void setV2GActive(boolean activateV2G) { this.V2GActive = activateV2G; this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(activateV2G)); // NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory diff --git a/_alp/Classes/Class.J_ChargingManagementMaxAvailablePower.java b/_alp/Classes/Class.J_ChargingManagementMaxAvailablePower.java index 430fc8c9..9b5f39b5 100644 --- a/_alp/Classes/Class.J_ChargingManagementMaxAvailablePower.java +++ b/_alp/Classes/Class.J_ChargingManagementMaxAvailablePower.java @@ -69,6 +69,13 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_ChargingManagement.class, loadMap, status, reason); + } + public void setV2GActive(boolean activateV2G) { this.V2GActive = activateV2G; this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(activateV2G)); // NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory @@ -79,9 +86,6 @@ public boolean getV2GActive() { return this.V2GActive; } - - - //Get parentagent public Agent getParentAgent() { return this.gc; diff --git a/_alp/Classes/Class.J_ChargingManagementOffPeak.java b/_alp/Classes/Class.J_ChargingManagementOffPeak.java index 83b9bd0f..c3e08d0d 100644 --- a/_alp/Classes/Class.J_ChargingManagementOffPeak.java +++ b/_alp/Classes/Class.J_ChargingManagementOffPeak.java @@ -89,6 +89,13 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_ChargingManagement.class, loadMap, status, reason); + } + public void setStartTimeOfReducedChargingInterval_hr(double startTimeOfReducedChargingInterval_hr) { this.startTimeOfReducedChargingInterval_hr = startTimeOfReducedChargingInterval_hr; } diff --git a/_alp/Classes/Class.J_ChargingManagementPrice.java b/_alp/Classes/Class.J_ChargingManagementPrice.java index ca71e7f6..01a2aee8 100644 --- a/_alp/Classes/Class.J_ChargingManagementPrice.java +++ b/_alp/Classes/Class.J_ChargingManagementPrice.java @@ -70,6 +70,13 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_ChargingManagement.class, loadMap, status, reason); + } + public void setV2GActive(boolean activateV2G) { this.V2GActive = activateV2G; this.gc.c_electricVehicles.forEach(ev -> ev.setV2GActive(activateV2G)); // NEEDED TO HAVE EV ASSET IN CORRECT assetFlowCatagory diff --git a/_alp/Classes/Class.J_ChargingManagementPriceScheduled.java b/_alp/Classes/Class.J_ChargingManagementPriceScheduled.java index 9a3b97aa..97bb16f2 100644 --- a/_alp/Classes/Class.J_ChargingManagementPriceScheduled.java +++ b/_alp/Classes/Class.J_ChargingManagementPriceScheduled.java @@ -150,6 +150,13 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab } activeSessions.removeIf(session -> session.isFinished); // Must be outside of for-loop over this collection! } + + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_ChargingManagement.class, loadMap, status, reason); + } public void abortSession(I_ChargingRequest chargingRequest) { previousChargingRequests.remove(chargingRequest); diff --git a/_alp/Classes/Class.J_ChargingManagementSimple.java b/_alp/Classes/Class.J_ChargingManagementSimple.java index 8be8fa2f..b3d483ae 100644 --- a/_alp/Classes/Class.J_ChargingManagementSimple.java +++ b/_alp/Classes/Class.J_ChargingManagementSimple.java @@ -4,6 +4,9 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; + +import zero_engine.J_ActivityTrackerTrips.TripRecord; + import java.util.EnumSet; @JsonAutoDetect( @@ -51,6 +54,77 @@ public void manageCharging(J_ChargePoint chargePoint, J_TimeVariables timeVariab } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + // TODO: Check if this works for (public) charging sessions aswell + int timeStepsInForecast = roundToInt((timeOfIntervalEnd_h - timeOfIntervalStart_h) / this.timeParameters.getTimeStep_h()); + Double[] electricityLoad_kW = new Double[timeStepsInForecast]; + Arrays.fill(electricityLoad_kW, 0.0); + + for (J_EAEV ev : this.gc.c_electricVehicles) { + double[] EVLoad_kW = new double[timeStepsInForecast]; + J_ActivityTrackerTrips tripTracker = ev.getTripTracker(); + List trips = tripTracker.getTripsInRange(timeOfIntervalStart_h, timeOfIntervalEnd_h); + + double maximalStorageCapacity_kWh = ev.getStorageCapacity_kWh(); + double currentSOC_kWh = ev.getCurrentSOC_kWh(); // Assumes the SOC of the EV at start of forecast is current SOC + double work_kWh = maximalStorageCapacity_kWh - currentSOC_kWh; + double endTimeLastTrip_h = ev.getAvailability() ? timeOfIntervalStart_h : tripTracker.getCurrentTripEndTime_h()%24; // Assumes the current trip ends today, i.e. no trips > 24 hours exist) + double firstAvailableChargingTime_h = this.timeParameters.getTimeStep_h() * Math.ceil(endTimeLastTrip_h / this.timeParameters.getTimeStep_h()); + double startTimeNextTrip_h = trips.size() > 0 ? trips.get(0).startTime_h() : timeOfIntervalEnd_h; + double lastAvailableChargingTime_h = this.timeParameters.getTimeStep_h() * Math.floor(startTimeNextTrip_h / this.timeParameters.getTimeStep_h()); + double maxPower_kW = ev.getVehicleChargingCapacity_kW(); + double maxWork_kWh = (lastAvailableChargingTime_h - firstAvailableChargingTime_h + this.timeParameters.getTimeStep_h()) * maxPower_kW; + double initialWork_kWh = max(0, min(work_kWh, maxWork_kWh)); + double remainingWork_kWh = initialWork_kWh; + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i*this.timeParameters.getTimeStep_h(); + if (t >= firstAvailableChargingTime_h && t <= lastAvailableChargingTime_h) { + EVLoad_kW[i] = max(0, min(maxPower_kW, remainingWork_kWh / this.timeParameters.getTimeStep_h())); + remainingWork_kWh -= EVLoad_kW[i] * this.timeParameters.getTimeStep_h(); + if (DoubleCompare.equalsZero(remainingWork_kWh) || DoubleCompare.lessThanZero(remainingWork_kWh)) { + break; + } + } + } + currentSOC_kWh += initialWork_kWh - remainingWork_kWh; + + for (int tripIndex = 0; tripIndex < trips.size(); tripIndex++) { + TripRecord trip = trips.get(tripIndex); + double distance_km = trip.distance_km(); + currentSOC_kWh -= distance_km * ev.getEnergyConsumption_kWhpkm(); + work_kWh = maximalStorageCapacity_kWh - currentSOC_kWh; + endTimeLastTrip_h = trip.endTime_h(); + firstAvailableChargingTime_h = this.timeParameters.getTimeStep_h() * Math.ceil(endTimeLastTrip_h / this.timeParameters.getTimeStep_h()); + startTimeNextTrip_h = (tripIndex == trips.size() - 1) ? timeOfIntervalEnd_h : trips.get(tripIndex+1).startTime_h(); + lastAvailableChargingTime_h = this.timeParameters.getTimeStep_h() * Math.floor(startTimeNextTrip_h / this.timeParameters.getTimeStep_h()); + maxWork_kWh = (lastAvailableChargingTime_h - firstAvailableChargingTime_h + this.timeParameters.getTimeStep_h()) * maxPower_kW; + initialWork_kWh = max(0, min(work_kWh, maxWork_kWh)); + remainingWork_kWh = initialWork_kWh; + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i*this.timeParameters.getTimeStep_h(); + if (t >= firstAvailableChargingTime_h && t <= lastAvailableChargingTime_h) { + EVLoad_kW[i] = max(0, min(maxPower_kW, remainingWork_kWh / this.timeParameters.getTimeStep_h())); + double x = max(0, min(maxPower_kW, remainingWork_kWh / this.timeParameters.getTimeStep_h())); + remainingWork_kWh -= EVLoad_kW[i] * this.timeParameters.getTimeStep_h(); + if (DoubleCompare.equalsZero(remainingWork_kWh) || DoubleCompare.lessThanZero(remainingWork_kWh)) { + break; + } + } + } + currentSOC_kWh += initialWork_kWh - remainingWork_kWh; + } + for (int i = 0; i < timeStepsInForecast; i++) { + electricityLoad_kW[i] += EVLoad_kW[i]; + } + } + + OL_ForecastStatus status = OL_ForecastStatus.ESTIMATED_FORECAST; + Map loadMap = new HashMap<>(); + loadMap.put(OL_EnergyCarriers.ELECTRICITY, electricityLoad_kW); + String reason = "Forecast is not perfect as EV starting SOC is guessed. If forecast starttime is the current modeltime forecast is perfect."; + return new J_AssetTypeForecast(I_ChargingManagement.class, loadMap, status, reason); + } + public void setV2GActive(boolean activateV2G) { if(activateV2G) { throw new RuntimeException("Trying to Activate V2G for chargingManagement Simple -> Not supported"); @@ -61,7 +135,6 @@ public boolean getV2GActive() { return this.V2GActive; } - //Get parentagent public Agent getParentAgent() { return this.gc; @@ -75,10 +148,8 @@ public void restoreStates() { } - @Override public String toString() { return "Active charging type: " + this.activeChargingType; - } } \ No newline at end of file diff --git a/_alp/Classes/Class.J_CurtailManagementContractCapacity.java b/_alp/Classes/Class.J_CurtailManagementContractCapacity.java index c425246f..105457fa 100644 --- a/_alp/Classes/Class.J_CurtailManagementContractCapacity.java +++ b/_alp/Classes/Class.J_CurtailManagementContractCapacity.java @@ -45,6 +45,13 @@ public void manageCurtailment(J_TimeVariables timeVariables) { } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_CurtailManagement.class, loadMap, status, reason); + } + ////Store and reset states public void storeStatesAndReset() { //Nothing to store and reset diff --git a/_alp/Classes/Class.J_CurtailManagementExternalSetpoint.java b/_alp/Classes/Class.J_CurtailManagementExternalSetpoint.java index be9a3324..2a6e4a90 100644 --- a/_alp/Classes/Class.J_CurtailManagementExternalSetpoint.java +++ b/_alp/Classes/Class.J_CurtailManagementExternalSetpoint.java @@ -53,6 +53,13 @@ public void manageCurtailment(J_TimeVariables timeVariables) { curtailmentSetpoint_kW = 0; } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "External Setpoint Management can not be forecasted."; + return new J_AssetTypeForecast(I_CurtailManagement.class, loadMap, status, reason); + } + public void setCurtailmentSetpoint(double curtailmentSetpoint_kW) { this.curtailmentSetpoint_kW = curtailmentSetpoint_kW; } diff --git a/_alp/Classes/Class.J_CurtailManagementPrice.java b/_alp/Classes/Class.J_CurtailManagementPrice.java index e54f8b0d..9f2bdae3 100644 --- a/_alp/Classes/Class.J_CurtailManagementPrice.java +++ b/_alp/Classes/Class.J_CurtailManagementPrice.java @@ -46,6 +46,13 @@ public void manageCurtailment(J_TimeVariables timeVariables) { } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_CurtailManagement.class, loadMap, status, reason); + } + ////Store and reset states public void storeStatesAndReset() { //Nothing to store and reset diff --git a/_alp/Classes/Class.J_EAConversionHeatPump.java b/_alp/Classes/Class.J_EAConversionHeatPump.java index b5ce9646..f2f3b6d1 100644 --- a/_alp/Classes/Class.J_EAConversionHeatPump.java +++ b/_alp/Classes/Class.J_EAConversionHeatPump.java @@ -131,7 +131,7 @@ public OL_AmbientTempType getAmbientTempType() { public double calculateCOP(double outputTemperature_degC, double baseTemperature_degC) { //double COP_r = this.eta_r * ( 273.15 + this.outputTemperature_degC ) / ( this.outputTemperature_degC - this.baseTemperature_degC ); - double deltaT = max(1,this.outputTemperature_degC - this.baseTemperature_degC); // Limit deltaT to at least 1 degree. + double deltaT = max(1, outputTemperature_degC - baseTemperature_degC); // Limit deltaT to at least 1 degree. double COP_r = 8.74 - 0.190 * deltaT + 0.00126 * deltaT*deltaT; return COP_r; } diff --git a/_alp/Classes/Class.J_ElectrolyserManagementPowerSurplus.java b/_alp/Classes/Class.J_ElectrolyserManagementPowerSurplus.java index cb6fe5d9..1f4da9c0 100644 --- a/_alp/Classes/Class.J_ElectrolyserManagementPowerSurplus.java +++ b/_alp/Classes/Class.J_ElectrolyserManagementPowerSurplus.java @@ -193,6 +193,13 @@ else if(timeVariables.getTimeStepsElapsed() < (8760-forecast_time_h)/timeParamet } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_ElectrolyserManagement.class, loadMap, status, reason); + } + public void setTarget(Agent target) { this.target = target; } diff --git a/_alp/Classes/Class.J_ElectrolyserManagementPrice.java b/_alp/Classes/Class.J_ElectrolyserManagementPrice.java index 0815e466..17f45f74 100644 --- a/_alp/Classes/Class.J_ElectrolyserManagementPrice.java +++ b/_alp/Classes/Class.J_ElectrolyserManagementPrice.java @@ -141,6 +141,13 @@ private void f_electrolyserStateControl_Price(J_EAConversionElectrolyser electro } } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_ElectrolyserManagement.class, loadMap, status, reason); + } + public void setElectricityPriceMaxForProfit_eurpkWh(double electricityPriceMaxForProfit_eurpkWh) { this.electricityPriceMaxForProfit_eurpkWh = electricityPriceMaxForProfit_eurpkWh; } diff --git a/_alp/Classes/Class.J_FlexProfileManagementDefault.java b/_alp/Classes/Class.J_FlexProfileManagementDefault.java index 88cdf05e..fc6e6dbe 100644 --- a/_alp/Classes/Class.J_FlexProfileManagementDefault.java +++ b/_alp/Classes/Class.J_FlexProfileManagementDefault.java @@ -22,6 +22,26 @@ public void manageFlexProfiles(J_TimeVariables timeVariables) { gc.c_flexProfileAssets.forEach(flexProfile -> gc.f_updateFlexAssetFlows(flexProfile, 1.0, timeVariables)); } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + int timeStepsInForecast = roundToInt((timeOfIntervalEnd_h - timeOfIntervalStart_h) / this.timeParameters.getTimeStep_h()); + Map loadMap = new HashMap<>(); + for (J_EAFlexProfile flexProfile : gc.c_flexProfileAssets) { + OL_EnergyCarriers EC = flexProfile.getEnergyCarrier(); + if (loadMap.get(EC) == null) { + Double[] loadProfile_kW = new Double[timeStepsInForecast]; + Arrays.fill(loadProfile_kW, 0.0); + loadMap.put(EC, loadProfile_kW); + } + J_ProfilePointer pp = flexProfile.getProfilePointer(); + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i * this.timeParameters.getTimeStep_h(); + loadMap.get(EC)[i] += pp.getValue(t); + } + } + OL_ForecastStatus status = OL_ForecastStatus.PERFECT_FORECAST; + return new J_AssetTypeForecast(I_FlexProfileManagement.class, loadMap, status, null); + } + ////Store and reset states public void storeStatesAndReset() { //Nothing to store and reset diff --git a/_alp/Classes/Class.J_FlexProfileManagementHeatprofileHeatpump.java b/_alp/Classes/Class.J_FlexProfileManagementHeatprofileHeatpump.java index b4738f86..117fb42b 100644 --- a/_alp/Classes/Class.J_FlexProfileManagementHeatprofileHeatpump.java +++ b/_alp/Classes/Class.J_FlexProfileManagementHeatprofileHeatpump.java @@ -141,6 +141,13 @@ private void createNewFlexProfileSetpointArray(J_TimeVariables timeVariables) { this.flexProfileSetpointArray_fr = newFlexProfileSetpointArray; } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_FlexProfileManagement.class, loadMap, status, reason); + } + //Setters public void setMaxFlexProfileShift_fr(double maxFlexProfileShift_fr) { if(maxFlexProfileShift_fr<0) { diff --git a/_alp/Classes/Class.J_HeatingManagementCHP.java b/_alp/Classes/Class.J_HeatingManagementCHP.java index 7493105a..5496331b 100644 --- a/_alp/Classes/Class.J_HeatingManagementCHP.java +++ b/_alp/Classes/Class.J_HeatingManagementCHP.java @@ -95,7 +95,12 @@ public J_HeatingPreferences getHeatingPreferences() { return this.heatingPreferences; } - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_HeatingManagementDistrictHeating.java b/_alp/Classes/Class.J_HeatingManagementDistrictHeating.java index 34eb0e5c..ecb21687 100644 --- a/_alp/Classes/Class.J_HeatingManagementDistrictHeating.java +++ b/_alp/Classes/Class.J_HeatingManagementDistrictHeating.java @@ -120,7 +120,13 @@ public J_HeatingPreferences getHeatingPreferences() { return this.heatingPreferences; } - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } + //Get parentagent public Agent getParentAgent() { return this.gc; diff --git a/_alp/Classes/Class.J_HeatingManagementGhost.java b/_alp/Classes/Class.J_HeatingManagementGhost.java index d6ee563f..58f5443f 100644 --- a/_alp/Classes/Class.J_HeatingManagementGhost.java +++ b/_alp/Classes/Class.J_HeatingManagementGhost.java @@ -72,8 +72,12 @@ public J_HeatingPreferences getHeatingPreferences() { return this.heatingPreferences; } - - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java b/_alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java index 30d46a77..856c21ae 100644 --- a/_alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java +++ b/_alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java @@ -351,7 +351,12 @@ public J_HeatingPreferences getHeatingPreferences() { return this.heatingPreferences; } - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { diff --git a/_alp/Classes/Class.J_HeatingManagementNeighborhood.java b/_alp/Classes/Class.J_HeatingManagementNeighborhood.java index 93b50c8a..f6d75394 100644 --- a/_alp/Classes/Class.J_HeatingManagementNeighborhood.java +++ b/_alp/Classes/Class.J_HeatingManagementNeighborhood.java @@ -322,7 +322,13 @@ public I_ProfileAsset getHeatDemandProfile(String name) { return heatDemandProfiles.get(name); } - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + OL_ForecastStatus status = OL_ForecastStatus.NOT_FORECASTABLE; + String reason = "Not yet implemented."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } + //Get parentagent public Agent getParentAgent() { return this.gc; diff --git a/_alp/Classes/Class.J_HeatingManagementPIcontrol.java b/_alp/Classes/Class.J_HeatingManagementPIcontrol.java index 3fad5889..e9fc88af 100644 --- a/_alp/Classes/Class.J_HeatingManagementPIcontrol.java +++ b/_alp/Classes/Class.J_HeatingManagementPIcontrol.java @@ -237,6 +237,133 @@ public J_HeatingPreferences getHeatingPreferences() { return this.heatingPreferences; } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + int timeStepsInForecast = roundToInt((timeOfIntervalEnd_h - timeOfIntervalStart_h) / this.timeParameters.getTimeStep_h()); + Double[] loadProfile_kW = new Double[timeStepsInForecast]; + J_ProfilePointer ambientTemperatureProfile = this.gc.energyModel.pp_ambientTemperature_degC; + J_ProfileForecaster ambientTempeartureForecast = this.gc.energyModel.pf_ambientTemperature_degC; + double[] buildingHeatDemand_kW = this.getBuildingHeatDemandProfile(timeOfIntervalStart_h, timeStepsInForecast, ambientTemperatureProfile, ambientTempeartureForecast); + double[] otherFixedHeatDemand_kW = this.gc.f_getFixedAssetForecast(timeOfIntervalStart_h, timeOfIntervalEnd_h, OL_EnergyCarriers.HEAT, this.timeParameters); + + // We switch over the heating type twice, first to set the EnergyCarrier + OL_EnergyCarriers EC; + switch (this.gc.f_getCurrentHeatingType()) { + case GAS_BURNER: + EC = OL_EnergyCarriers.METHANE; + break; + case HYDROGENBURNER: + EC = OL_EnergyCarriers.HYDROGEN; + break; + case LT_DISTRICTHEAT: + case ELECTRIC_HEATPUMP: + EC = OL_EnergyCarriers.ELECTRICITY; + break; + case DISTRICTHEAT: + Double[] heatLoad = Arrays.stream(buildingHeatDemand_kW).boxed().toArray(Double[]::new); + loadMap.put(OL_EnergyCarriers.HEAT, heatLoad); + return new J_AssetTypeForecast(J_HeatingManagementPIcontrol.class, loadMap, OL_ForecastStatus.ESTIMATED_FORECAST, "GC connected to districtheating, so all heat demand is import. Building forecast simplified by omitting solar radiation & ventilation."); + default: + throw new RuntimeException(String.format("Tried to forecast J_HeatingManagementPIControl, but encountered an unexepected heating type %s in GC %s", this.gc.f_getCurrentHeatingType(), this.gc.p_gridConnectionID)); + } + + // Then to determine the profile. + // Here we make a distinction between fixed efficiency, and efficiency variable per timestep. + boolean fixedEfficiency = false; + double fixedEfficiency_fr = 1.0; + J_EAConversionHeatPump hp = null; + switch (this.gc.f_getCurrentHeatingType()) { + case LT_DISTRICTHEAT: + fixedEfficiency = true; + hp = ((J_EAConversionHeatPump)this.heatingAsset); + fixedEfficiency_fr = hp.getCOP(); + break; + case GAS_BURNER: + case HYDROGENBURNER: + fixedEfficiency = true; + fixedEfficiency_fr = this.heatingAsset.getEta_r(); + break; + case ELECTRIC_HEATPUMP: + hp = ((J_EAConversionHeatPump)this.heatingAsset); + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i * timeParameters.getTimeStep_h(); + double heatDemand_kW = buildingHeatDemand_kW[i] + otherFixedHeatDemand_kW[i]; + double efficiency_fr = hp.calculateCOP(hp.getOutputTemperature_degC(), ambientTemperatureProfile.getValue(t)); + double load_kW = heatDemand_kW / efficiency_fr; + loadProfile_kW[i] = load_kW; + } + break; + } + + if (fixedEfficiency) { + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i * timeParameters.getTimeStep_h(); + double heatDemand_kW = buildingHeatDemand_kW[i] + otherFixedHeatDemand_kW[i]; + double load_kW = heatDemand_kW / fixedEfficiency_fr; + loadProfile_kW[i] = load_kW; + } + } + loadMap.put(EC, loadProfile_kW); + // Building is a flex asset so its heat demand is included in system bounds of heating management, but fixed profiles are not. + Double[] heatLoad = Arrays.stream(otherFixedHeatDemand_kW).map(d -> -d).boxed().toArray(Double[]::new); + loadMap.put(OL_EnergyCarriers.HEAT, heatLoad); + OL_ForecastStatus status = OL_ForecastStatus.ESTIMATED_FORECAST; + String reason = "PI & Building states based on current timestep. Building forecast simplified by omitting solar radiation & ventilation."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } + + private double[] getBuildingHeatDemandProfile(double timeAtStartForecast_h, int timeStepsInForecast, J_ProfilePointer ambientTemperatureProfile, J_ProfileForecaster ambientTempeartureForecast) { + double[] buildingHeatDemandProfile_kW = new double[timeStepsInForecast]; + + double dayStartTime_h = this.heatingPreferences.getStartOfDayTime_h(); + double nightStartTime_h = this.heatingPreferences.getStartOfNightTime_h(); + double daySetpoint_degC = this.heatingPreferences.getDayTimeSetPoint_degC(); + double nightSetpoint_degC = this.heatingPreferences.getNightTimeSetPoint_degC(); + double avgTemp24h_degC = ambientTempeartureForecast.getForecast(); // Assumes timeAtStartForecast_h is current model time and forecasting horizon is 24h. + + double simBuildingTemp_degC = this.building.getCurrentTemperature(); + double simFilteredSetpoint_degC = this.filteredCurrentSetpoint_degC; + double simIState_hDegC = this.I_state_hDegC; + + double pGain_kWpDegC = this.P_gain_kWpDegC; + double iGain_kWphDegC = this.I_gain_kWphDegC; + double filterTimeScale_h = this.setpointFilterTimeScale_h; + double timeStep_h = this.timeParameters.getTimeStep_h(); + + double lossFactor_WpK = this.building.getLossFactor_WpK(); + double lossScalingFactor_fr = this.building.getLossScalingFactor_fr(); + double heatCapacity_kWhpK = this.building.getHeatCapacity_JpK() / 3.6e6; + + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeAtStartForecast_h + i * timeStep_h; + double timeOfDay_h = t % 24.0; + double ambientTemp_degC = ambientTemperatureProfile.getValue(t); + + // Same setpoint-selection logic as manageHeating, incl. the heating-season hysteresis + double currentSetpoint_degC = daySetpoint_degC; + if (avgTemp24h_degC > J_HeatingFunctionLibrary.heatingDaysAvgTempTreshold_degC) { + currentSetpoint_degC = nightSetpoint_degC; + } else if (timeOfDay_h < dayStartTime_h || timeOfDay_h >= nightStartTime_h) { + currentSetpoint_degC = nightSetpoint_degC; + } + + // Same order of operations as manageHeating: filter first, then deltaT vs *previous* building temp + simFilteredSetpoint_degC += (timeStep_h / filterTimeScale_h) * (currentSetpoint_degC - simFilteredSetpoint_degC); + double deltaT_degC = simFilteredSetpoint_degC - simBuildingTemp_degC; + simIState_hDegC = max(0, simIState_hDegC + deltaT_degC * timeStep_h); + double heatingDemand_kW = max(0, deltaT_degC * pGain_kWpDegC + simIState_hDegC * iGain_kWphDegC); + + buildingHeatDemandProfile_kW[i] = heatingDemand_kW; + + // Advance building temperature under this heat input for the next step + double heatLoss_kW = (lossFactor_WpK * (simBuildingTemp_degC - ambientTemp_degC) / 1000) * lossScalingFactor_fr; + double netHeat_kW = heatingDemand_kW - heatLoss_kW; + simBuildingTemp_degC += (netHeat_kW / heatCapacity_kWhpK) * timeStep_h; + } + + return buildingHeatDemandProfile_kW; + } + //Get parentagent public Agent getParentAgent() { return this.gc; diff --git a/_alp/Classes/Class.J_HeatingManagementPIcontrolHybridHeatpump.java b/_alp/Classes/Class.J_HeatingManagementPIcontrolHybridHeatpump.java index ef0eef4d..f35822c3 100644 --- a/_alp/Classes/Class.J_HeatingManagementPIcontrolHybridHeatpump.java +++ b/_alp/Classes/Class.J_HeatingManagementPIcontrolHybridHeatpump.java @@ -212,8 +212,7 @@ else if (gc.c_heatingAssets.get(1) instanceof J_EAConversionHeatPump) { this.filteredCurrentSetpoint_degC = heatingPreferences.getMinComfortTemperature_degC(); this.isInitialized = true; } - - + public void notInitialized() { this.isInitialized = false; } @@ -234,6 +233,95 @@ public J_HeatingPreferences getHeatingPreferences() { return this.heatingPreferences; } + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + int timeStepsInForecast = roundToInt((timeOfIntervalEnd_h - timeOfIntervalStart_h) / this.timeParameters.getTimeStep_h()); + Double[] electricityLoadProfile_kW = new Double[timeStepsInForecast]; + Double[] gasLoadProfile_kW = new Double[timeStepsInForecast]; + J_ProfilePointer ambientTemperatureProfile = this.gc.energyModel.pp_ambientTemperature_degC; + J_ProfileForecaster ambientTempeartureForecast = this.gc.energyModel.pf_ambientTemperature_degC; + double[] buildingHeatDemand_kW = this.getBuildingHeatDemandProfile(timeOfIntervalStart_h, timeStepsInForecast, ambientTemperatureProfile, ambientTempeartureForecast); + double[] otherFixedHeatDemand_kW = this.gc.f_getFixedAssetForecast(timeOfIntervalStart_h, timeOfIntervalEnd_h, OL_EnergyCarriers.HEAT, this.timeParameters); + + double gasBurnerEfficiency_fr = this.gasBurnerAsset.getEta_r(); + double gasBurnerMaxOutput_kW = this.gasBurnerAsset.getOutputCapacity_kW(); + double heatpumpInputCapacity_kW = this.heatPumpAsset.getInputCapacity_kW(); + + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i * timeParameters.getTimeStep_h(); + double totalHeatDemand_kW = buildingHeatDemand_kW[i] + otherFixedHeatDemand_kW[i]; + double COP_fr = this.heatPumpAsset.calculateCOP(this.heatPumpAsset.getOutputTemperature_degC(), ambientTemperatureProfile.getValue(t)); + if (COP_fr < 3.0) { + gasLoadProfile_kW[i] = min(gasBurnerMaxOutput_kW, totalHeatDemand_kW / gasBurnerEfficiency_fr); + electricityLoadProfile_kW[i] = max(0, (totalHeatDemand_kW - gasBurnerMaxOutput_kW) / COP_fr); + } + else { + electricityLoadProfile_kW[i] = min(heatpumpInputCapacity_kW, totalHeatDemand_kW / COP_fr); + gasLoadProfile_kW[i] = max(0, (totalHeatDemand_kW - heatpumpInputCapacity_kW * COP_fr) / gasBurnerEfficiency_fr); + } + } + loadMap.put(OL_EnergyCarriers.ELECTRICITY, electricityLoadProfile_kW); + loadMap.put(OL_EnergyCarriers.METHANE, gasLoadProfile_kW); + // Building is a flex asset so included in system bounds of heating management, but fixed profiles are not. + Double[] heatLoad = Arrays.stream(otherFixedHeatDemand_kW).map(d -> -d).boxed().toArray(Double[]::new); + loadMap.put(OL_EnergyCarriers.HEAT, heatLoad); + OL_ForecastStatus status = OL_ForecastStatus.ESTIMATED_FORECAST; + String reason = "PI & Building states based on current timestep. Building forecast simplified by omitting solar radiation & ventilation."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } + + private double[] getBuildingHeatDemandProfile(double timeAtStartForecast_h, int timeStepsInForecast, J_ProfilePointer ambientTemperatureProfile, J_ProfileForecaster ambientTempeartureForecast) { + double[] buildingHeatDemandProfile_kW = new double[timeStepsInForecast]; + + double dayStartTime_h = this.heatingPreferences.getStartOfDayTime_h(); + double nightStartTime_h = this.heatingPreferences.getStartOfNightTime_h(); + double daySetpoint_degC = this.heatingPreferences.getDayTimeSetPoint_degC(); + double nightSetpoint_degC = this.heatingPreferences.getNightTimeSetPoint_degC(); + double avgTemp24h_degC = ambientTempeartureForecast.getForecast(); // Assumes timeAtStartForecast_h is current model time and forecasting horizon is 24h. + + double simBuildingTemp_degC = this.building.getCurrentTemperature(); + double simFilteredSetpoint_degC = this.filteredCurrentSetpoint_degC; + double simIState_hDegC = this.I_state_hDegC; + + double pGain_kWpDegC = this.P_gain_kWpDegC; + double iGain_kWphDegC = this.I_gain_kWphDegC; + double filterTimeScale_h = this.setpointFilterTimeScale_h; + double timeStep_h = this.timeParameters.getTimeStep_h(); + + double lossFactor_WpK = this.building.getLossFactor_WpK(); + double lossScalingFactor_fr = this.building.getLossScalingFactor_fr(); + double heatCapacity_kWhpK = this.building.getHeatCapacity_JpK() / 3.6e6; + + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeAtStartForecast_h + i * timeStep_h; + double timeOfDay_h = t % 24.0; + double ambientTemp_degC = ambientTemperatureProfile.getValue(t); + + // Same setpoint-selection logic as manageHeating, incl. the heating-season hysteresis + double currentSetpoint_degC = daySetpoint_degC; + if (avgTemp24h_degC > J_HeatingFunctionLibrary.heatingDaysAvgTempTreshold_degC) { + currentSetpoint_degC = nightSetpoint_degC; + } else if (timeOfDay_h < dayStartTime_h || timeOfDay_h >= nightStartTime_h) { + currentSetpoint_degC = nightSetpoint_degC; + } + + // Same order of operations as manageHeating: filter first, then deltaT vs *previous* building temp + simFilteredSetpoint_degC += (timeStep_h / filterTimeScale_h) * (currentSetpoint_degC - simFilteredSetpoint_degC); + double deltaT_degC = simFilteredSetpoint_degC - simBuildingTemp_degC; + simIState_hDegC = max(0, simIState_hDegC + deltaT_degC * timeStep_h); + double heatingDemand_kW = max(0, deltaT_degC * pGain_kWpDegC + simIState_hDegC * iGain_kWphDegC); + + buildingHeatDemandProfile_kW[i] = heatingDemand_kW; + + // Advance building temperature under this heat input for the next step + double heatLoss_kW = (lossFactor_WpK * (simBuildingTemp_degC - ambientTemp_degC) / 1000) * lossScalingFactor_fr; + double netHeat_kW = heatingDemand_kW - heatLoss_kW; + simBuildingTemp_degC += (netHeat_kW / heatCapacity_kWhpK) * timeStep_h; + } + + return buildingHeatDemandProfile_kW; + } + //Get parentagent public Agent getParentAgent() { return this.gc; diff --git a/_alp/Classes/Class.J_HeatingManagementProfileHybridHeatPump.java b/_alp/Classes/Class.J_HeatingManagementProfileHybridHeatPump.java index 37666ecd..14ce2b32 100644 --- a/_alp/Classes/Class.J_HeatingManagementProfileHybridHeatPump.java +++ b/_alp/Classes/Class.J_HeatingManagementProfileHybridHeatPump.java @@ -134,16 +134,46 @@ public J_HeatingPreferences getHeatingPreferences() { return this.heatingPreferences; } - - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + int timeStepsInForecast = roundToInt((timeOfIntervalEnd_h - timeOfIntervalStart_h) / this.timeParameters.getTimeStep_h()); + Double[] electricityLoadProfile_kW = new Double[timeStepsInForecast]; + Double[] gasLoadProfile_kW = new Double[timeStepsInForecast]; + J_ProfilePointer ambientTemperatureProfile = this.gc.energyModel.pp_ambientTemperature_degC; + double[] fixedHeatDemand_kW = this.gc.f_getFixedAssetForecast(timeOfIntervalStart_h, timeOfIntervalEnd_h, OL_EnergyCarriers.HEAT, this.timeParameters); + + double gasBurnerEfficiency_fr = this.gasBurnerAsset.getEta_r(); + double gasBurnerMaxOutput_kW = this.gasBurnerAsset.getOutputCapacity_kW(); + double heatpumpInputCapacity_kW = this.heatPumpAsset.getInputCapacity_kW(); + + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i * timeParameters.getTimeStep_h(); + double totalHeatDemand_kW = fixedHeatDemand_kW[i]; + double COP_fr = this.heatPumpAsset.calculateCOP(this.heatPumpAsset.getOutputTemperature_degC(), ambientTemperatureProfile.getValue(t)); + if (COP_fr < 3.0) { + gasLoadProfile_kW[i] = min(gasBurnerMaxOutput_kW, totalHeatDemand_kW / gasBurnerEfficiency_fr); + electricityLoadProfile_kW[i] = max(0, (totalHeatDemand_kW - gasBurnerMaxOutput_kW) / COP_fr); + } + else { + electricityLoadProfile_kW[i] = min(heatpumpInputCapacity_kW, totalHeatDemand_kW / COP_fr); + gasLoadProfile_kW[i] = max(0, (totalHeatDemand_kW - heatpumpInputCapacity_kW * COP_fr) / gasBurnerEfficiency_fr); + } + } + loadMap.put(OL_EnergyCarriers.ELECTRICITY, electricityLoadProfile_kW); + loadMap.put(OL_EnergyCarriers.METHANE, gasLoadProfile_kW); + Double[] heatLoad = Arrays.stream(fixedHeatDemand_kW).map(d -> -d).boxed().toArray(Double[]::new); + loadMap.put(OL_EnergyCarriers.HEAT, heatLoad); + + OL_ForecastStatus status = OL_ForecastStatus.PERFECT_FORECAST; + String reason = "Forecaster has perfect foresight into heat demand profiles and ambient temperatures."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } //Get parentagent public Agent getParentAgent() { return this.gc; } - - public void storeStatesAndReset() { //Nothing to store/reset } @@ -151,7 +181,6 @@ public void restoreStates() { //Nothing to store/reset } - @Override public String toString() { return super.toString(); diff --git a/_alp/Classes/Class.J_HeatingManagementSimple.java b/_alp/Classes/Class.J_HeatingManagementSimple.java index ef791085..a810afa4 100644 --- a/_alp/Classes/Class.J_HeatingManagementSimple.java +++ b/_alp/Classes/Class.J_HeatingManagementSimple.java @@ -216,7 +216,129 @@ public void initializeAssets() { this.isInitialized = true; } - + public J_AssetTypeForecast getForecast(double timeOfIntervalStart_h, double timeOfIntervalEnd_h) { + Map loadMap = new HashMap<>(); + int timeStepsInForecast = roundToInt((timeOfIntervalEnd_h - timeOfIntervalStart_h) / this.timeParameters.getTimeStep_h()); + Double[] loadProfile_kW = new Double[timeStepsInForecast]; + J_ProfilePointer ambientTemperatureProfile = this.gc.energyModel.pp_ambientTemperature_degC; + J_ProfileForecaster ambientTempeartureForecast = this.gc.energyModel.pf_ambientTemperature_degC; + double[] buildingHeatDemand_kW = this.getBuildingHeatDemandProfile(timeOfIntervalStart_h, timeStepsInForecast, ambientTemperatureProfile, ambientTempeartureForecast); + double[] otherFixedHeatDemand_kW = this.gc.f_getFixedAssetForecast(timeOfIntervalStart_h, timeOfIntervalEnd_h, OL_EnergyCarriers.HEAT, this.timeParameters); + + // We switch over the heating type twice, first to set the EnergyCarrier + OL_EnergyCarriers EC; + switch (this.gc.f_getCurrentHeatingType()) { + case GAS_BURNER: + EC = OL_EnergyCarriers.METHANE; + break; + case HYDROGENBURNER: + EC = OL_EnergyCarriers.HYDROGEN; + break; + case LT_DISTRICTHEAT: + case ELECTRIC_HEATPUMP: + EC = OL_EnergyCarriers.ELECTRICITY; + break; + case DISTRICTHEAT: + Double[] heatLoad = Arrays.stream(buildingHeatDemand_kW).boxed().toArray(Double[]::new); + loadMap.put(OL_EnergyCarriers.HEAT, heatLoad); + if (this.building == null) { + String reason = "GC connected to districtheating, so all heat demand is import."; + return new J_AssetTypeForecast(J_HeatingManagementPIcontrol.class, loadMap, OL_ForecastStatus.PERFECT_FORECAST, reason); + } + else { + String reason = "GC connected to districtheating, so all heat demand is import. Building forecast simplified by omitting solar radiation & ventilation."; + return new J_AssetTypeForecast(J_HeatingManagementPIcontrol.class, loadMap, OL_ForecastStatus.ESTIMATED_FORECAST, reason); + } + default: + throw new RuntimeException(String.format("Tried to forecast J_HeatingManagementPIControl, but encountered an unexepected heating type %s in GC %s", this.gc.f_getCurrentHeatingType(), this.gc.p_gridConnectionID)); + } + + // Then to determine the profile. + // Here we make a distinction between fixed efficiency, and efficiency variable per timestep. + boolean fixedEfficiency = false; + double fixedEfficiency_fr = 1.0; + J_EAConversionHeatPump hp = null; + switch (this.gc.f_getCurrentHeatingType()) { + case LT_DISTRICTHEAT: + fixedEfficiency = true; + hp = ((J_EAConversionHeatPump)this.heatingAsset); + fixedEfficiency_fr = hp.getCOP(); + break; + case GAS_BURNER: + case HYDROGENBURNER: + fixedEfficiency = true; + fixedEfficiency_fr = this.heatingAsset.getEta_r(); + break; + case ELECTRIC_HEATPUMP: + hp = ((J_EAConversionHeatPump)this.heatingAsset); + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i * timeParameters.getTimeStep_h(); + double heatDemand_kW = buildingHeatDemand_kW[i] + otherFixedHeatDemand_kW[i]; + double efficiency_fr = hp.calculateCOP(hp.getOutputTemperature_degC(), ambientTemperatureProfile.getValue(t)); + double load_kW = heatDemand_kW / efficiency_fr; + loadProfile_kW[i] = load_kW; + } + break; + } + + if (fixedEfficiency) { + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeOfIntervalStart_h + i * timeParameters.getTimeStep_h(); + double heatDemand_kW = buildingHeatDemand_kW[i] + otherFixedHeatDemand_kW[i]; + double load_kW = heatDemand_kW / fixedEfficiency_fr; + loadProfile_kW[i] = load_kW; + } + } + loadMap.put(EC, loadProfile_kW); + // Building is a flex asset so included in system bounds of heating management, but fixed profiles are not. + Double[] heatLoad = Arrays.stream(otherFixedHeatDemand_kW).map(d -> -d).boxed().toArray(Double[]::new); + loadMap.put(OL_EnergyCarriers.HEAT, heatLoad); + if (this.building == null) { + OL_ForecastStatus status = OL_ForecastStatus.PERFECT_FORECAST; + String reason = "Forecast is perfect for GridConnections without ThermalBuilding."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } + else { + OL_ForecastStatus status = OL_ForecastStatus.ESTIMATED_FORECAST; + String reason = "Temperature & Building states based on current timestep. Building forecast simplified by omitting solar radiation & ventilation."; + return new J_AssetTypeForecast(I_HeatingManagement.class, loadMap, status, reason); + } + } + + private double[] getBuildingHeatDemandProfile(double timeAtStartForecast_h, int timeStepsInForecast, J_ProfilePointer ambientTemperatureProfile, J_ProfileForecaster ambientTempeartureForecast) { + double[] buildingHeatDemandProfile_kW = new double[timeStepsInForecast]; + if (this.building == null) { + return buildingHeatDemandProfile_kW; + } + double dayStartTime_h = this.heatingPreferences.getStartOfDayTime_h(); + double nightStartTime_h = this.heatingPreferences.getStartOfNightTime_h(); + double daySetpoint_degC = this.heatingPreferences.getDayTimeSetPoint_degC(); + double nightSetpoint_degC = this.heatingPreferences.getNightTimeSetPoint_degC(); + double avgTemp24h_degC = ambientTempeartureForecast.getForecast(); // Assumes timeAtStartForecast_h is current model time and forecasting horizon is 24h. + double timeStep_h = this.timeParameters.getTimeStep_h(); + + double lossFactor_WpK = this.building.getLossFactor_WpK(); + double lossScalingFactor_fr = this.building.getLossScalingFactor_fr(); + double heatCapacity_kWhpK = this.building.getHeatCapacity_JpK() / 3.6e6; + + for (int i = 0; i < timeStepsInForecast; i++) { + double t = timeAtStartForecast_h + i * timeStep_h; + double timeOfDay_h = t % 24.0; + double ambientTemp_degC = ambientTemperatureProfile.getValue(t); + + // Same setpoint-selection logic as manageHeating, incl. the heating-season hysteresis + double currentSetpoint_degC = daySetpoint_degC; + if (avgTemp24h_degC > J_HeatingFunctionLibrary.heatingDaysAvgTempTreshold_degC) { + currentSetpoint_degC = nightSetpoint_degC; + } else if (timeOfDay_h < dayStartTime_h || timeOfDay_h >= nightStartTime_h) { + currentSetpoint_degC = nightSetpoint_degC; + } + double heatLoss_kW = (lossFactor_WpK * (currentSetpoint_degC - ambientTemp_degC) / 1000) * lossScalingFactor_fr; + buildingHeatDemandProfile_kW[i] = max(0, heatLoss_kW); + } + return buildingHeatDemandProfile_kW; + } + public void notInitialized() { this.isInitialized = false; } diff --git a/_alp/Classes/Class.J_TimeParameters.java b/_alp/Classes/Class.J_TimeParameters.java index 77c3bc31..ae5ccd0d 100644 --- a/_alp/Classes/Class.J_TimeParameters.java +++ b/_alp/Classes/Class.J_TimeParameters.java @@ -108,19 +108,19 @@ public double getRunDuration_h() { //Static methods public static OL_Days getDayFromDayIndex(int dayIndex){ switch(dayIndex) { - case 0: - return OL_Days.MONDAY; case 1: - return OL_Days.TUESDAY; + return OL_Days.MONDAY; case 2: - return OL_Days.WEDNESDAY; + return OL_Days.TUESDAY; case 3: - return OL_Days.THURSDAY; + return OL_Days.WEDNESDAY; case 4: - return OL_Days.FRIDAY; + return OL_Days.THURSDAY; case 5: - return OL_Days.SATURDAY; + return OL_Days.FRIDAY; case 6: + return OL_Days.SATURDAY; + case 7: return OL_Days.SUNDAY; default: throw new RuntimeException("Day found that should not exist."); @@ -129,19 +129,19 @@ public static OL_Days getDayFromDayIndex(int dayIndex){ public static int getDayIndexFromDay(OL_Days day){ switch(day) { case MONDAY: - return 0; - case TUESDAY: return 1; - case WEDNESDAY: + case TUESDAY: return 2; - case THURSDAY: + case WEDNESDAY: return 3; - case FRIDAY: + case THURSDAY: return 4; - case SATURDAY: + case FRIDAY: return 5; - case SUNDAY: + case SATURDAY: return 6; + case SUNDAY: + return 7; default: throw new RuntimeException("Day found that should not exist."); } @@ -158,6 +158,19 @@ public static List getOrderedDaysList(){ ); } + public int getWeekIndex(double time_h) { + double offset = (this.getDayOfWeek1jan() - 1) * 24.0; + return (int) Math.floor((time_h + offset) / (7 * 24)); + } + + /* + * Note: This method can return a negative time for the first week if + */ + public double getWeekStart_h(int weekIndex) { + double offset = (this.getDayOfWeek1jan() - 1) * 24.0; + return weekIndex * (7 * 24) - offset; + } + @Override public String toString() { return "J_TimeParameters{" +