diff --git a/Zero_engine.alpx b/Zero_engine.alpx index 35ef04d..06c68d2 100644 --- a/Zero_engine.alpx +++ b/Zero_engine.alpx @@ -361,6 +361,10 @@ 1770131381146 + + 1773156171459 + + 1659525715752 @@ -993,6 +997,10 @@ 1753968826413 + + 1773140446104 + + 1753968826414 @@ -1650,6 +1658,7 @@ EXCLUDE_PV => Use PV profile to preprocess gridnode profile to create a more acc 1749648342034 + true 1752677832758 @@ -2002,6 +2011,11 @@ EXCLUDE_PV => Use PV profile to preprocess gridnode profile to create a more acc 1772100333519 + + 1773139549641 + + 1752677832758 + com.anylogic.libraries.modules.markup_descriptors diff --git a/_alp/Agents/GCHouse/Code/Functions.java b/_alp/Agents/GCHouse/Code/Functions.java index dd53377..6144b35 100644 --- a/_alp/Agents/GCHouse/Code/Functions.java +++ b/_alp/Agents/GCHouse/Code/Functions.java @@ -1,7 +1,7 @@ double f_operateFlexAssets_overwrite(J_TimeVariables timeVariables) {/*ALCODESTART::1664963959146*/ f_manageCookingTracker(timeVariables); -f_manageAirco(timeVariables); +//f_manageAirco(timeVariables); super.f_operateFlexAssets(timeVariables); /*ALCODEEND*/} diff --git a/_alp/Agents/GCHouse/Code/Functions.xml b/_alp/Agents/GCHouse/Code/Functions.xml index c1ed00f..3c3f15b 100644 --- a/_alp/Agents/GCHouse/Code/Functions.xml +++ b/_alp/Agents/GCHouse/Code/Functions.xml @@ -104,6 +104,7 @@ double 1749648447119 + true 1160 240 diff --git a/_alp/Agents/GridConnection/Code/Functions.java b/_alp/Agents/GridConnection/Code/Functions.java index 72e6661..20dfc03 100644 --- a/_alp/Agents/GridConnection/Code/Functions.java +++ b/_alp/Agents/GridConnection/Code/Functions.java @@ -1042,9 +1042,13 @@ else if ( conversionAsset.energyAssetType == OL_EnergyAssetType.GAS_PIT || j_ea. p_cookingTracker.HOB = (J_EAConversion)j_ea; } } + else if (j_ea instanceof J_EAConversionAirConditioner aircoAsset) { + p_airco = aircoAsset; + energyModel.c_ambientDependentAssets.add(aircoAsset); + } } -else if (j_ea instanceof J_EAStorage storageAsset) { +else if (j_ea instanceof J_EAStorage storageAsset) { c_storageAssets.add(storageAsset); energyModel.c_storageAssets.add(storageAsset); if (j_ea instanceof J_EAStorageHeat) { @@ -1066,11 +1070,7 @@ else if (j_ea instanceof J_EAStorageElectric battery) { c_parentCoops.forEach( coop -> coop.v_liveAssetsMetaData.totalInstalledBatteryStorageCapacity_MWh += capacity_MWh); energyModel.v_liveAssetsMetaData.totalInstalledBatteryStorageCapacity_MWh += capacity_MWh; } -} -else if (j_ea instanceof J_EAAirco aircoAsset) { - p_airco = aircoAsset; -} -else{ +} else { throw new RuntimeException("Trying to connect GC with unrecognized J_EAFlex asset!"); } /*ALCODEEND*/} @@ -1281,6 +1281,10 @@ else if ( j_ea.energyAssetType == OL_EnergyAssetType.GAS_PIT || j_ea.energyAsset else if (j_ea instanceof J_EAConversionElectrolyser) { } + else if (j_ea instanceof J_EAConversionAirConditioner) { + p_airco = null; + energyModel.c_ambientDependentAssets.remove(j_ea); + } } else if (j_ea instanceof J_EAStorage) { c_storageAssets.remove((J_EAStorage)j_ea); @@ -1303,7 +1307,7 @@ else if (j_ea instanceof J_EAStorageElectric) { energyModel.v_liveAssetsMetaData.totalInstalledBatteryStorageCapacity_MWh -= ((J_EAStorageElectric)j_ea).getStorageCapacity_kWh()/1000; } } -else if (j_ea instanceof J_EAAirco) { +else if (j_ea instanceof J_EAConversionAirConditioner) { p_airco = null; } /*ALCODEEND*/} diff --git a/_alp/Agents/GridConnection/Variables.xml b/_alp/Agents/GridConnection/Variables.xml index a3bc585..877635a 100644 --- a/_alp/Agents/GridConnection/Variables.xml +++ b/_alp/Agents/GridConnection/Variables.xml @@ -1239,7 +1239,7 @@ true true - + NONE false diff --git a/_alp/Classes/Class.I_EnergyManagement.java b/_alp/Classes/Class.I_EnergyManagement.java index 4184a0d..ec32c56 100644 --- a/_alp/Classes/Class.I_EnergyManagement.java +++ b/_alp/Classes/Class.I_EnergyManagement.java @@ -89,14 +89,14 @@ default public void checkConfiguration(List flexAssetsGCList) { flexAssets.removeAll(findAll(flexAssets, vehicle -> vehicle instanceof J_EAEV || vehicle instanceof J_EAChargingSession)); break; } - else if(asset instanceof I_HeatingAsset || asset instanceof J_EAStorageHeat){ + else if(asset instanceof I_HeatingAsset || asset instanceof J_EAStorageHeat || asset instanceof J_EAConversionAirConditioner){ if(!isAssetManagementActive(I_HeatingManagement.class)) { throw new RuntimeException("A heating asset is found at GC that has an EMS that does not have active heating management."); } if(getExternalAssetManagement(I_HeatingManagement.class) != null) { getExternalAssetManagement(I_HeatingManagement.class).initializeAssets(); } - flexAssets.removeAll(findAll(flexAssets, heatAsset -> heatAsset instanceof I_HeatingAsset || heatAsset instanceof J_EAStorageHeat)); + flexAssets.removeAll(findAll(flexAssets, heatAsset -> heatAsset instanceof I_HeatingAsset || heatAsset instanceof J_EAStorageHeat || heatAsset instanceof J_EAConversionAirConditioner)); break; } else if(asset instanceof J_EAStorageElectric){ diff --git a/_alp/Classes/Class.J_EABuilding.java b/_alp/Classes/Class.J_EABuilding.java index 661182d..e0d7b99 100644 --- a/_alp/Classes/Class.J_EABuilding.java +++ b/_alp/Classes/Class.J_EABuilding.java @@ -8,7 +8,7 @@ public class J_EABuilding extends zero_engine.J_EAStorageHeat implements Seriali private double solarAbsorptionFactor_m2; private double solarRadiation_Wpm2 = 0; private double additionalVentilationLosses_fr = 0; - + private double coldExtracted_kWh = 0; //Slider scaling factor private double lossScalingFactor_fr = 1; @@ -80,9 +80,9 @@ public J_FlowPacket f_updateAllFlows(double powerFraction_fr, J_TimeVariables ti @Override public void operate(double powerFraction_fr, J_TimeVariables timeVariables) { - if (DoubleCompare.lessThanZero(powerFraction_fr)) { + /*if (DoubleCompare.lessThanZero(powerFraction_fr)) { throw new RuntimeException("Cooling of the J_EABuilding is not yet supported."); - } + }*/ double lossPower_kW = calculateLoss_kW(); // Heat exchange with environment through convection double additionalVentilationLoss_kW = calculateAdditionalVentilationLoss_kW(); double solarHeating_kW = solarHeating_kW(); // Heat influx from sunlight @@ -101,7 +101,8 @@ public void operate(double powerFraction_fr, J_TimeVariables timeVariables) { } updateStateOfCharge( deltaEnergy_kWh ); - this.heatCharged_kWh += inputPower_kW * this.timeParameters.getTimeStep_h(); + this.heatCharged_kWh += max(0,inputPower_kW * this.timeParameters.getTimeStep_h()); + this.coldExtracted_kWh +=max(0,-inputPower_kW * this.timeParameters.getTimeStep_h()); this.flowsMap.put(OL_EnergyCarriers.HEAT, inputPower_kW); diff --git a/_alp/Classes/Class.J_EAConversionAirConditioner.java b/_alp/Classes/Class.J_EAConversionAirConditioner.java new file mode 100644 index 0000000..60930ae --- /dev/null +++ b/_alp/Classes/Class.J_EAConversionAirConditioner.java @@ -0,0 +1,90 @@ +/** + * J_EAConversionAirConditioner + */ +public class J_EAConversionAirConditioner extends J_EAConversion { + + private double COP_r; + protected OL_AmbientTempType ambientTempType; + public double totalElectricityConsumed_kWh =0; + public J_EABuilding building; + //public double p_baseTemperatureReference; + + /** + * Default constructor + */ + public J_EAConversionAirConditioner() { + } + + //Only supports cooling functionality (extracting heat from building). In reality ACs can also be used for heating, in which case it's basically operating as a heatpump. This code is an adapted form of the J_EAConversionHeatPump. + + /** + * Constructor initializing the fields + */ + public J_EAConversionAirConditioner(I_AssetOwner owner, double inputElectricCapacity_kW, J_TimeParameters timeParameters, J_EABuilding building) { + this.energyAssetType = OL_EnergyAssetType.AIR_CONDITIONER; + + this.setOwner(owner); + this.timeParameters = timeParameters; + this.inputCapacity_kW = inputElectricCapacity_kW; + //this.eta_r = eta_r; + this.building = building; + this.ambientTempType = OL_AmbientTempType.AMBIENT_AIR; + this.updateAmbientTemperature( building.getAmbientTemperature_degC() ); // also updates COP + + this.energyCarrierProduced = OL_EnergyCarriers.HEAT; // Heat is actually 'consumed', but this is the 'useful energy' of the AC; heat is extracted from the building and exhausted outside. In terms of 'final energy consumption' however, this is counted as energy consumption. + this.energyCarrierConsumed= OL_EnergyCarriers.ELECTRICITY; + + this.activeProductionEnergyCarriers.add(this.energyCarrierProduced); + this.activeConsumptionEnergyCarriers.add(this.energyCarrierConsumed); + this.assetFlowCategory = OL_AssetFlowCategories.airConditionersElectricPower_kW; + registerEnergyAsset(timeParameters); + } + + public void updateAmbientTemperature(double ambientTemperature_degC) { + double buildingTemp_degC = building.getCurrentTemperature(); + this.COP_r = calculateCOP(ambientTemperature_degC, buildingTemp_degC); //this.eta_r * ( 273.15 + this.outputTemperature_degC ) / ( this.outputTemperature_degC - this.baseTemperature_degC ); + this.outputCapacity_kW = this.inputCapacity_kW * this.COP_r; // this represents the current maximum cooling power (heat extracted from building!) + } + + public double getCOP() { + //traceln("Heatpump output temperature: " + this.outputTemperature_degC); + return this.COP_r; + } + + @Override + public void operate(double powerFraction_fr, J_TimeVariables timeVariables) { + + double electricityConsumption_kW = powerFraction_fr * this.inputCapacity_kW; + this.totalElectricityConsumed_kWh += electricityConsumption_kW * this.timeParameters.getTimeStep_h(); + + double coldProduction_kW = electricityConsumption_kW * this.COP_r; + + this.energyUse_kW = electricityConsumption_kW + coldProduction_kW; + flowsMap.put(OL_EnergyCarriers.HEAT, coldProduction_kW); // coldProduction can be seen as the heat flowing into the asset (from the building), so from the GC-perspective it looks like heat consumption. + flowsMap.put(OL_EnergyCarriers.ELECTRICITY, electricityConsumption_kW); + + this.assetFlowsMap.addFlow(OL_AssetFlowCategories.airConditionersElectricPower_kW, electricityConsumption_kW); + this.energyUsed_kWh += energyUse_kW * this.timeParameters.getTimeStep_h(); + } + + public OL_AmbientTempType getAmbientTempType() { + return this.ambientTempType; + } + + private double calculateCOP(double ambientTemperature_degC, double buildingTemperature_degC) { // This is the cooling COP, defined as the extracted heat power divided by the input electric power. + double deltaT = max(0,ambientTemperature_degC - buildingTemperature_degC); // Limit deltaT to 0-or-higher, meaning outside temp is equal or higher than inside temp. In reality, it can happen that an AC runs with a lower outside temp, but we 'cap' the COP this way. + double COP_r = 5 - 0.10 * deltaT + 0.00126 * deltaT*deltaT; // 'expert judgement'-curve, not based on manufacturer or measurement data but on 'typical' efficiencies found online. + return COP_r; // Ratio of cooling power (extracted heat) to input electric power. + } + + @Override + public String toString() { + return this.energyAssetType + ", " + //+ this.energyCarrierConsumed + " -> " + this.energyCarrierProduced + ", " + + "Electric capacity: " + this.inputCapacity_kW + " kW, " + + "with efficiency: " + this.getCOP() + ", " + + "Energy used: " + this.energyUsed_kWh + ", " + + "Current output: " + -this.getLastFlows().get(this.energyCarrierProduced) + " kW"; + } + +} \ No newline at end of file diff --git a/_alp/Classes/Class.J_EAConversionHeatPump.java b/_alp/Classes/Class.J_EAConversionHeatPump.java index 7c10077..0f795ad 100644 --- a/_alp/Classes/Class.J_EAConversionHeatPump.java +++ b/_alp/Classes/Class.J_EAConversionHeatPump.java @@ -132,22 +132,10 @@ public void setSourceAssetHeatPower(double sourceAssetHeatPower_kW) { //traceln("sourceAssetHeatPower_kW is set to: "+sourceAssetHeatPower_kW); } - /*@Override - public double getCurrentTemperature() { - return outputTemperature_degC; - }*/ - public void setBaseTemperature_degC( double baseTemperature_degC) { this.baseTemperature_degC = baseTemperature_degC; this.updateParameters( this.baseTemperature_degC, this.outputTemperature_degC); } - - /*@Override - public void setEta_r( double efficiency_r) { - this.eta_r = efficiency_r; - this.COP_r = this.eta_r * ( 273.15 + this.outputTemperature_degC ) / ( this.outputTemperature_degC - this.baseTemperature_degC ); - this.outputCapacity_kW = this.inputCapacity_kW * this.COP_r; - }*/ public OL_AmbientTempType getAmbientTempType() { return this.ambientTempType; @@ -158,6 +146,7 @@ private double calculateCOP(double outputTemperature_degC, double baseTemperatur double COP_r = 8.74 - 0.190 * deltaT + 0.00126 * deltaT*deltaT; return COP_r; } + /* @Override public String toString() { diff --git a/_alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java b/_alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java index a308cf3..30d46a7 100644 --- a/_alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java +++ b/_alp/Classes/Class.J_HeatingManagementHeatpumpOffPeak.java @@ -24,6 +24,7 @@ public class J_HeatingManagementHeatpumpOffPeak implements I_HeatingManagement { private J_EABuilding building; private J_EAConversion heatingAsset; + private J_EAConversionAirConditioner AC; private J_HeatingPreferences heatingPreferences; private J_EAStorageHeat hotWaterBuffer; private List ptAssets; @@ -34,6 +35,8 @@ public class J_HeatingManagementHeatpumpOffPeak implements I_HeatingManagement { private double P_gain_kWpDegC = 1*1; private double I_gain_kWphDegC = 0.1*2; private double I_state_hDegC = 0; + private double I_state_AC_hDegC = 0; + private boolean AC_active = false; //Temperature setpoint low pass filter private double filteredCurrentSetpoint_degC; @@ -49,9 +52,10 @@ public class J_HeatingManagementHeatpumpOffPeak implements I_HeatingManagement { //Stored private double storedI_state_hDegC; + private double storedI_state_AC_hDegC; private double storedFilteredCurrentSetpoint_degC; private double storedrequiredTemperatureAtStartOfReducedHeatingInterval_degC; - + private boolean AC_active_stored = false; /** * Default constructor */ @@ -108,6 +112,10 @@ else if(this.hasHotWaterBuffer) { double currentSetpoint_degC; if(this.avgTemp24h_degC > J_HeatingFunctionLibrary.heatingDaysAvgTempTreshold_degC) { // If avg temperature is high enough, keep setpoint at nighttime setpoint. currentSetpoint_degC = heatingPreferences.getNightTimeSetPoint_degC(); + double buildingTemp_degC = building.getCurrentTemperature(); + if (this.AC != null && buildingTemp_degC > heatingPreferences.getMaxComfortTemperature_degC() && !AC_active ) { + AC_active = true; + } } else { //Determine if time is in reduced Heating interval @@ -131,6 +139,10 @@ else if(timeIsInReducedHeatingInterval) { else if (timeOfDay_h < heatingPreferences.getStartOfDayTime_h() || timeOfDay_h >= heatingPreferences.getStartOfNightTime_h()) { currentSetpoint_degC = heatingPreferences.getNightTimeSetPoint_degC(); } + if (AC_active) { + AC_active = false; + I_state_AC_hDegC = 0; + } } //Smooth the setpoint signal @@ -144,11 +156,30 @@ else if (timeOfDay_h < heatingPreferences.getStartOfDayTime_h() || timeOfDay_h > double buildingHeatingDemand_kW = max(0,deltaT_degC * P_gain_kWpDegC + I_state_hDegC * I_gain_kWphDegC); //Set asset power - double assetPower_kW = min(heatingAsset.getOutputCapacity_kW(), buildingHeatingDemand_kW + currentHeatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset. - gc.f_updateFlexAssetFlows(heatingAsset, assetPower_kW / heatingAsset.getOutputCapacity_kW(), timeVariables); + double heatingAssetPower_kW = min(heatingAsset.getOutputCapacity_kW(), buildingHeatingDemand_kW + currentHeatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset. + gc.f_updateFlexAssetFlows(heatingAsset, heatingAssetPower_kW / heatingAsset.getOutputCapacity_kW(), timeVariables); + double coolingPower_kW = 0; + if (AC_active) { + double deltaT_cooling_degC = (building.getCurrentTemperature() - heatingPreferences.getMaxComfortTemperature_degC()); + if (deltaT_cooling_degC < -1) { + this.AC_active=false; + //traceln("Building temp more than 1 degree below maxcomfort, turning off AC!"); + } else { + I_state_AC_hDegC = max(0,I_state_AC_hDegC + deltaT_cooling_degC * timeParameters.getTimeStep_h()); // max(0,...) to prevent buildup of negative integrator during warm periods. + coolingPower_kW = min(AC.getOutputCapacity_kW(),max(0,(deltaT_cooling_degC * P_gain_kWpDegC * 2 + I_state_AC_hDegC * I_gain_kWphDegC))); // max(0,...), so only cooling allowed, no heating. + /*if (coolingPower_kW > 0) { + traceln("Airconditioner active! Cooling power: %s kW", coolingPower_kW); + traceln("Current building temperature: %s deg C", building.getCurrentTemperature()); + //traceln("MaxComfortTemp: %s", heatingPreferences.getMaxComfortTemperature_degC()); + }*/ + } + gc.f_updateFlexAssetFlows(AC, coolingPower_kW / AC.getOutputCapacity_kW(), timeVariables); + + } + //Set building power (other heat demand gets bias if asset does not have enough capacity) - double heatIntoBuilding_kW = max(0, assetPower_kW - currentHeatDemand_kW); + double heatIntoBuilding_kW = max(0, heatingAssetPower_kW - currentHeatDemand_kW); gc.f_updateFlexAssetFlows(building, heatIntoBuilding_kW / building.getCapacityHeat_kW(), timeVariables); } @@ -283,7 +314,17 @@ public void initializeAssets() { } else { throw new RuntimeException(this.getClass() + " Unsupported heating asset!"); } - if(this.heatingPreferences == null) { + if ( gc instanceof GCHouse house) { + if (house.p_airco!=null) { + this.AC = house.p_airco; + } else { + this.AC = null; + this.AC_active = false; + this.I_state_AC_hDegC = 0; + } + } + + if(this.heatingPreferences == null) { heatingPreferences = new J_HeatingPreferences(); } this.filteredCurrentSetpoint_degC = heatingPreferences.getMinComfortTemperature_degC(); @@ -321,15 +362,20 @@ public Agent getParentAgent() { //Store and reset states public void storeStatesAndReset() { this.storedI_state_hDegC = this.I_state_hDegC; - this.storedFilteredCurrentSetpoint_degC = this.filteredCurrentSetpoint_degC; - this.storedrequiredTemperatureAtStartOfReducedHeatingInterval_degC = this.requiredTemperatureAtStartOfReducedHeatingInterval_degC; + this.storedI_state_AC_hDegC = this.I_state_AC_hDegC; this.I_state_hDegC = 0; + this.I_state_AC_hDegC = 0; + this.AC_active_stored = this.AC_active; + this.storedFilteredCurrentSetpoint_degC = this.filteredCurrentSetpoint_degC; + this.storedrequiredTemperatureAtStartOfReducedHeatingInterval_degC = this.requiredTemperatureAtStartOfReducedHeatingInterval_degC; this.filteredCurrentSetpoint_degC = heatingPreferences.getMinComfortTemperature_degC(); this.requiredTemperatureAtStartOfReducedHeatingInterval_degC = 20; this.isInitialized = false; } public void restoreStates() { this.I_state_hDegC = this.storedI_state_hDegC; + this.I_state_AC_hDegC = this.storedI_state_AC_hDegC; + this.AC_active = this.AC_active_stored; this.filteredCurrentSetpoint_degC = this.storedFilteredCurrentSetpoint_degC; this.requiredTemperatureAtStartOfReducedHeatingInterval_degC = storedrequiredTemperatureAtStartOfReducedHeatingInterval_degC; this.isInitialized = true; diff --git a/_alp/Classes/Class.J_HeatingManagementPIcontrol.java b/_alp/Classes/Class.J_HeatingManagementPIcontrol.java index ea140b6..3fad588 100644 --- a/_alp/Classes/Class.J_HeatingManagementPIcontrol.java +++ b/_alp/Classes/Class.J_HeatingManagementPIcontrol.java @@ -29,6 +29,7 @@ public class J_HeatingManagementPIcontrol implements I_HeatingManagement { private J_EABuilding building; private J_EAConversion heatingAsset; + private J_EAConversionAirConditioner AC; private J_HeatingPreferences heatingPreferences; private J_EAStorageHeat hotWaterBuffer; private List ptAssets; @@ -39,6 +40,8 @@ public class J_HeatingManagementPIcontrol implements I_HeatingManagement { private double P_gain_kWpDegC = 1*1; private double I_gain_kWphDegC = 0.1*2; private double I_state_hDegC = 0; + private double I_state_AC_hDegC = 0; + private boolean AC_active = false; //Temperature setpoint low pass filter private double filteredCurrentSetpoint_degC; @@ -46,7 +49,9 @@ public class J_HeatingManagementPIcontrol implements I_HeatingManagement { //Stored parameters private double storedI_state_hDegC; + private double storedI_state_AC_hDegC; private double storedFilteredCurrentSetpoint_degC; + private boolean AC_active_stored = false; /** * Default constructor */ @@ -96,8 +101,17 @@ else if(this.hasHotWaterBuffer) { double currentSetpoint_degC = heatingPreferences.getDayTimeSetPoint_degC(); if(avgTemp24h_degC > J_HeatingFunctionLibrary.heatingDaysAvgTempTreshold_degC) { currentSetpoint_degC = heatingPreferences.getNightTimeSetPoint_degC(); - } else if (timeOfDay_h < heatingPreferences.getStartOfDayTime_h() || timeOfDay_h >= heatingPreferences.getStartOfNightTime_h()) { - currentSetpoint_degC = heatingPreferences.getNightTimeSetPoint_degC(); + if (this.AC != null && buildingTemp_degC > heatingPreferences.getMaxComfortTemperature_degC() && !AC_active ) { + AC_active = true; + } + } else { + if (timeOfDay_h < heatingPreferences.getStartOfDayTime_h() || timeOfDay_h >= heatingPreferences.getStartOfNightTime_h()) { + currentSetpoint_degC = heatingPreferences.getNightTimeSetPoint_degC(); + } + if (AC_active) { + AC_active = false; + I_state_AC_hDegC = 0; + } } //Smooth the setpoint signal @@ -109,12 +123,33 @@ else if(this.hasHotWaterBuffer) { I_state_hDegC = max(0,I_state_hDegC + deltaT_degC * timeParameters.getTimeStep_h()); // max(0,...) to prevent buildup of negative integrator during warm periods. buildingHeatingDemand_kW = max(0,deltaT_degC * P_gain_kWpDegC + I_state_hDegC * I_gain_kWphDegC); + + double heatingAssetPower_kW = min(heatingAsset.getOutputCapacity_kW(),buildingHeatingDemand_kW + currentHeatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset. + gc.f_updateFlexAssetFlows(heatingAsset, heatingAssetPower_kW / heatingAsset.getOutputCapacity_kW(), timeVariables); - double assetPower_kW = min(heatingAsset.getOutputCapacity_kW(),buildingHeatingDemand_kW + currentHeatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset. - gc.f_updateFlexAssetFlows(heatingAsset, assetPower_kW / heatingAsset.getOutputCapacity_kW(), timeVariables); - - double heatIntoBuilding_kW = max(0, assetPower_kW - currentHeatDemand_kW); - gc.f_updateFlexAssetFlows(building, heatIntoBuilding_kW / building.getCapacityHeat_kW(), timeVariables); + double coolingPower_kW = 0; + + if (AC_active) { + double deltaT_cooling_degC = (building.getCurrentTemperature() - heatingPreferences.getMaxComfortTemperature_degC()); + if (deltaT_cooling_degC < -1) { + this.AC_active=false; + //traceln("Building temp more than 1 degree below maxcomfort, turning off AC!"); + } else { + I_state_AC_hDegC = max(0,I_state_AC_hDegC + deltaT_cooling_degC * timeParameters.getTimeStep_h()); // max(0,...) to prevent buildup of negative integrator during warm periods. + coolingPower_kW = min(AC.getOutputCapacity_kW(),max(0,(deltaT_cooling_degC * P_gain_kWpDegC * 2 + I_state_AC_hDegC * I_gain_kWphDegC))); // max(0,...), so only cooling allowed, no heating. + /*if (coolingPower_kW > 0) { + traceln("Airconditioner active! Cooling power: %s kW", coolingPower_kW); + traceln("Current building temperature: %s deg C", buildingTemp_degC); + traceln("MaxComfortTemp: %s", heatingPreferences.getMaxComfortTemperature_degC()); + }*/ + } + gc.f_updateFlexAssetFlows(AC, coolingPower_kW / AC.getOutputCapacity_kW(), timeVariables); + + } + + double heatIntoBuilding_kW = max(0, heatingAssetPower_kW - currentHeatDemand_kW); + + gc.f_updateFlexAssetFlows(building, (heatIntoBuilding_kW-coolingPower_kW) / building.getCapacityHeat_kW(), timeVariables); } @@ -165,6 +200,16 @@ public void initializeAssets() { } else { throw new RuntimeException(this.getClass() + " Unsupported heating asset!"); } + if ( gc instanceof GCHouse house) { + if (house.p_airco!=null) { + this.AC = house.p_airco; + } else { + this.AC = null; + this.AC_active = false; + this.I_state_AC_hDegC = 0; + } + } + if(this.heatingPreferences == null) { heatingPreferences = new J_HeatingPreferences(); } @@ -192,23 +237,26 @@ public J_HeatingPreferences getHeatingPreferences() { return this.heatingPreferences; } - - //Get parentagent public Agent getParentAgent() { return this.gc; } - //Store and reset states public void storeStatesAndReset() { this.storedI_state_hDegC = this.I_state_hDegC; - this.storedFilteredCurrentSetpoint_degC = this.filteredCurrentSetpoint_degC; + this.storedI_state_AC_hDegC = this.I_state_AC_hDegC; this.I_state_hDegC = 0; + this.I_state_AC_hDegC = 0; + this.AC_active_stored = this.AC_active; + this.storedFilteredCurrentSetpoint_degC = this.filteredCurrentSetpoint_degC; this.filteredCurrentSetpoint_degC = 0; + } public void restoreStates() { this.I_state_hDegC = this.storedI_state_hDegC; + this.I_state_AC_hDegC = this.storedI_state_AC_hDegC; + this.AC_active = this.AC_active_stored; this.filteredCurrentSetpoint_degC = this.storedFilteredCurrentSetpoint_degC; } diff --git a/_alp/Classes/Class.J_HeatingManagementPIcontrolHybridHeatpump.java b/_alp/Classes/Class.J_HeatingManagementPIcontrolHybridHeatpump.java index dc2ddb2..ef0eef4 100644 --- a/_alp/Classes/Class.J_HeatingManagementPIcontrolHybridHeatpump.java +++ b/_alp/Classes/Class.J_HeatingManagementPIcontrolHybridHeatpump.java @@ -27,6 +27,7 @@ public class J_HeatingManagementPIcontrolHybridHeatpump implements I_HeatingMana private J_EABuilding building; private J_EAConversionHeatPump heatPumpAsset; private J_EAConversionGasBurner gasBurnerAsset; + private J_EAConversionAirConditioner AC; private J_HeatingPreferences heatingPreferences; private J_EAStorageHeat hotWaterBuffer; private List ptAssets; @@ -36,6 +37,8 @@ public class J_HeatingManagementPIcontrolHybridHeatpump implements I_HeatingMana private double P_gain_kWpDegC = 1*1; private double I_gain_kWphDegC = 0.1*2; private double I_state_hDegC = 0; + private double I_state_AC_hDegC = 0; + private boolean AC_active = false; //Temperature setpoint low pass filter private double filteredCurrentSetpoint_degC; @@ -43,7 +46,9 @@ public class J_HeatingManagementPIcontrolHybridHeatpump implements I_HeatingMana //Stored parameters private double storedI_state_hDegC; + private double storedI_state_AC_hDegC; private double storedFilteredCurrentSetpoint_degC; + private boolean AC_active_stored = false; /** * Default constructor @@ -81,8 +86,15 @@ public void manageHeating(J_TimeVariables timeVariables) { double currentSetpoint_degC = heatingPreferences.getDayTimeSetPoint_degC(); if(avgTemp24h_degC > J_HeatingFunctionLibrary.heatingDaysAvgTempTreshold_degC) { currentSetpoint_degC = heatingPreferences.getNightTimeSetPoint_degC(); + if (this.AC != null && buildingTemp_degC > heatingPreferences.getMaxComfortTemperature_degC() && !AC_active ) { + AC_active = true; + } } else if (timeOfDay_h < heatingPreferences.getStartOfDayTime_h() || timeOfDay_h >= heatingPreferences.getStartOfNightTime_h()) { currentSetpoint_degC = heatingPreferences.getNightTimeSetPoint_degC(); + if (AC_active) { + AC_active = false; + I_state_AC_hDegC = 0; + } } //Smooth the setpoint signal @@ -112,10 +124,31 @@ public void manageHeating(J_TimeVariables timeVariables) { heatIntoBuilding_kW = max(0, gasBurnerAssetPower_kW - otherHeatDemand_kW); } + double coolingPower_kW = 0; + + if (AC_active) { + double deltaT_cooling_degC = (building.getCurrentTemperature() - heatingPreferences.getMaxComfortTemperature_degC()); + if (deltaT_cooling_degC < -1) { + this.AC_active=false; + //traceln("Building temp more than 1 degree below maxcomfort, turning off AC!"); + } else { + I_state_AC_hDegC = max(0,I_state_AC_hDegC + deltaT_cooling_degC * timeParameters.getTimeStep_h()); // max(0,...) to prevent buildup of negative integrator during warm periods. + coolingPower_kW = min(AC.getOutputCapacity_kW(),max(0,(deltaT_cooling_degC * P_gain_kWpDegC * 2 + I_state_AC_hDegC * I_gain_kWphDegC))); // max(0,...), so only cooling allowed, no heating. + /*if (coolingPower_kW > 0) { + traceln("Airconditioner active! Cooling power: %s kW", coolingPower_kW); + traceln("Current building temperature: %s deg C", buildingTemp_degC); + traceln("MaxComfortTemp: %s", heatingPreferences.getMaxComfortTemperature_degC()); + }*/ + } + gc.f_updateFlexAssetFlows(AC, coolingPower_kW / AC.getOutputCapacity_kW(), timeVariables); + + } + //Updat flows with found asset powers gc.f_updateFlexAssetFlows(heatPumpAsset, heatpumpAssetPower_kW / heatPumpAsset.getOutputCapacity_kW(), timeVariables); gc.f_updateFlexAssetFlows(gasBurnerAsset, gasBurnerAssetPower_kW / gasBurnerAsset.getOutputCapacity_kW(), timeVariables); - gc.f_updateFlexAssetFlows(building, heatIntoBuilding_kW / building.getCapacityHeat_kW(), timeVariables); + //gc.f_updateFlexAssetFlows(building, heatIntoBuilding_kW / building.getCapacityHeat_kW(), timeVariables); + gc.f_updateFlexAssetFlows(building, (heatIntoBuilding_kW-coolingPower_kW) / building.getCapacityHeat_kW(), timeVariables); } @@ -166,6 +199,16 @@ else if (gc.c_heatingAssets.get(1) instanceof J_EAConversionHeatPump) { if(this.heatingPreferences == null) { heatingPreferences = new J_HeatingPreferences(); } + if ( gc instanceof GCHouse house) { + if (house.p_airco!=null) { + this.AC = house.p_airco; + } else { + this.AC = null; + this.AC_active = false; + this.I_state_AC_hDegC = 0; + } + } + this.filteredCurrentSetpoint_degC = heatingPreferences.getMinComfortTemperature_degC(); this.isInitialized = true; } @@ -199,12 +242,17 @@ public Agent getParentAgent() { //Store and reset states public void storeStatesAndReset() { this.storedI_state_hDegC = this.I_state_hDegC; - this.storedFilteredCurrentSetpoint_degC = this.filteredCurrentSetpoint_degC; + this.storedI_state_AC_hDegC = this.I_state_AC_hDegC; this.I_state_hDegC = 0; + this.I_state_AC_hDegC = 0; + this.AC_active_stored = this.AC_active; + this.storedFilteredCurrentSetpoint_degC = this.filteredCurrentSetpoint_degC; this.filteredCurrentSetpoint_degC = 0; } public void restoreStates() { this.I_state_hDegC = this.storedI_state_hDegC; + this.I_state_AC_hDegC = this.storedI_state_AC_hDegC; + this.AC_active = this.AC_active_stored; this.filteredCurrentSetpoint_degC = this.storedFilteredCurrentSetpoint_degC; } diff --git a/_alp/Classes/Class.J_HeatingManagementSimple.java b/_alp/Classes/Class.J_HeatingManagementSimple.java index aa20e24..cc9c3d1 100644 --- a/_alp/Classes/Class.J_HeatingManagementSimple.java +++ b/_alp/Classes/Class.J_HeatingManagementSimple.java @@ -28,11 +28,23 @@ public class J_HeatingManagementSimple implements I_HeatingManagement { private J_EABuilding building; private J_EAConversion heatingAsset; + private J_EAConversionAirConditioner AC; private J_HeatingPreferences heatingPreferences; private J_EAStorageHeat hotWaterBuffer; private List ptAssets; private boolean hasPT = false; - + + // PI control gains for AC + private double P_gain_kWpDegC = 1*1; + private double I_gain_kWphDegC = 0.1*2; + //private double I_state_hDegC = 0; + private double I_state_AC_hDegC = 0; + private boolean AC_active = false; + + //Stored parameters + private double storedI_state_AC_hDegC; + private boolean AC_active_stored = false; + /** * Default constructor */ @@ -72,6 +84,10 @@ public void manageHeating(J_TimeVariables timeVariables) { double avgTemp24h_degC = gc.energyModel.pf_ambientTemperature_degC.getForecast(); if(avgTemp24h_degC > J_HeatingFunctionLibrary.heatingDaysAvgTempTreshold_degC) { buildingHeatingDemand_kW = max(0, heatingPreferences.getNightTimeSetPoint_degC() - buildingTemp_degC) * this.building.heatCapacity_JpK / 3.6e6 / timeParameters.getTimeStep_h(); + if (this.AC != null && !AC_active && buildingTemp_degC > heatingPreferences.getMaxComfortTemperature_degC() ) { + //traceln("Enabling airconditioner!"); + AC_active = true; + } } ///On heating days else { @@ -94,10 +110,36 @@ public void manageHeating(J_TimeVariables timeVariables) { // Daytime and building temperature acceptable } } + if (AC_active) { + //traceln("Disabling airconditioner!"); + AC_active = false; + I_state_AC_hDegC = 0; + } } heatingAssetPower_kW = min(heatingAsset.getOutputCapacity_kW(),buildingHeatingDemand_kW + heatDemand_kW); // minimum not strictly needed as asset will limit power by itself. Could be used later if we notice demand is higher than capacity of heating asset. double heatIntoBuilding_kW = max(0, heatingAssetPower_kW - heatDemand_kW); // Will lead to energy(heat) imbalance when heatDemand_kW is larger than heating asset capacity. - gc.f_updateFlexAssetFlows(building, heatIntoBuilding_kW / building.getCapacityHeat_kW(), timeVariables); + + double coolingPower_kW = 0; + + if (AC_active) { + double deltaT_cooling_degC = (building.getCurrentTemperature() - heatingPreferences.getMaxComfortTemperature_degC()); + if (deltaT_cooling_degC < -1) { + this.AC_active=false; + //traceln("Building temp more than 1 degree below maxcomfort, turning off AC!"); + } else { + I_state_AC_hDegC = max(0,I_state_AC_hDegC + deltaT_cooling_degC * timeParameters.getTimeStep_h()); // max(0,...) to prevent buildup of negative integrator during warm periods. + coolingPower_kW = min(AC.getOutputCapacity_kW(),max(0,(deltaT_cooling_degC * P_gain_kWpDegC * 2 + I_state_AC_hDegC * I_gain_kWphDegC))); // max(0,...), so only cooling allowed, no heating. + if (coolingPower_kW > 0) { + //traceln("Airconditioner active! Cooling power: %s kW, building temp: %s, maxComfortTemp: %s", coolingPower_kW, buildingTemp_degC, heatingPreferences.getMaxComfortTemperature_degC()); + //traceln("Current building temperature: %s deg C", buildingTemp_degC); + //traceln("MaxComfortTemp: %s", heatingPreferences.getMaxComfortTemperature_degC()); + } + } + gc.f_updateFlexAssetFlows(AC, coolingPower_kW / AC.getOutputCapacity_kW(), timeVariables); + } + + //gc.f_updateFlexAssetFlows(building, heatIntoBuilding_kW / building.getCapacityHeat_kW(), timeVariables); + gc.f_updateFlexAssetFlows(building, (heatIntoBuilding_kW-coolingPower_kW) / building.getCapacityHeat_kW(), timeVariables); } else { heatingAssetPower_kW = heatDemand_kW; // Will lead to energy(heat) imbalance when heatDemand_kW is larger than heating asset capacity. @@ -158,6 +200,18 @@ public void initializeAssets() { } else { throw new RuntimeException(this.getClass() + " Unsupported heating asset!"); } + if ( gc instanceof GCHouse house) { + if (house.p_airco!=null) { + if (this.building == null) { + throw new RuntimeException("AirConditioner can only be used in combination with J_EABuilding thermal model, but no J_EABuilding present on gridconnection!"); + } + this.AC = house.p_airco; + } else { + this.AC = null; + this.AC_active = false; + this.I_state_AC_hDegC = 0; + } + } this.isInitialized = true; } @@ -191,10 +245,13 @@ public Agent getParentAgent() { } public void storeStatesAndReset() { - //Nothing to store/reset + this.storedI_state_AC_hDegC = this.I_state_AC_hDegC; + this.I_state_AC_hDegC = 0; + this.AC_active_stored = this.AC_active; } public void restoreStates() { - //Nothing to store/reset + this.I_state_AC_hDegC = this.storedI_state_AC_hDegC; + this.AC_active = this.AC_active_stored; } @Override