// ------------------------------------------------------------------------------------- // !!! PRIVATE METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Calculation of total electrical energy consumption /// /// returns energy consumption of /// - pumps /// - mixer /// /// in kWh/d /// </summary> /// <param name="myPlant"></param> /// <param name="mySensors"></param> /// <param name="energyConsumptionPump">el. energy consumption of pumps [kWh/d]</param> /// <param name="energyConsumptionMixer">el. energy consumption of stirrer [kWh/d]</param> /// <returns></returns> private static double getElEnergyConsumption(biogas.plant myPlant, biogas.sensors mySensors, out double energyConsumptionPump, out double energyConsumptionMixer) { double energyConsumption = 0; energyConsumptionPump = 0; energyConsumptionMixer = 0; // int n_digester = myPlant.getNumDigesters(); for (int idigester = 0; idigester < n_digester; idigester++) { string digester_id = myPlant.getDigesterID(idigester + 1); // Idee: zu energieverbrauch gehören auch Rührwerke, Rührwerksleistung soll // von TS im Fermenter abhängig sein. // folgende Formel: // Energieverbrauch [kWh/d] = V_fermenter * 1 kWh/(d * 100 m³) * TS [%] // // frei nach der Quelle: // Empfehlung für die Auswahl von Rührwerken... // mixer //double V_digester= myPlant.getDigesterParam(digester_id, "Vliq"); //double TS= mySensors.getCurrentMeasurement("TS" + "_" + digester_id + "_3").Value; double Pel_mixer; physValue[] e_mixer = mySensors.getCurrentMeasurementVector("stirrer_" + digester_id); Pel_mixer = e_mixer[0].Value; energyConsumptionMixer += Pel_mixer;//V_digester / 100 * TS; } // pumps for (int ipump = 0; ipump < myPlant.getNumPumps(); ipump++) { string pump_id = myPlant.getPumpID(ipump + 1); double pump_energy; mySensors.getCurrentMeasurementD("pumpEnergy_" + pump_id, out pump_energy); // pump energy per day // P(t) [kWh/d] energyConsumptionPump += pump_energy; } // substrate_transport for (int isubstrate_transport = 0; isubstrate_transport < myPlant.getNumSubstrateTransports(); isubstrate_transport++) { string substrate_transport_id = myPlant.getSubstrateTransportID(isubstrate_transport + 1); double pump_energy; mySensors.getCurrentMeasurementD("pumpEnergy_" + substrate_transport_id, out pump_energy); // pump energy per day // P(t) [kWh/d] energyConsumptionPump += pump_energy; mySensors.getCurrentMeasurementD("transportEnergy_" + substrate_transport_id, out pump_energy); // pump energy per day // P(t) [kWh/d] energyConsumptionPump += pump_energy; } // sum in kWh/d // Vorsicht: energy von bakterien wird als produktion nicht als verbrauch // angesehen, deshalb hier neg. VZ energyConsumption = energyConsumptionPump + energyConsumptionMixer; return(energyConsumption); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// get all objectives /// </summary> /// <param name="mySensors"></param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="myFitnessParams"></param> /// <param name="Stability_punishment"></param> /// <param name="energyBalance">cost - benefit [1000 €/d]</param> /// <param name="energyProd_fitness"></param> /// <param name="energyConsumption">total el. energy consumption [kWh/d]</param> /// <param name="energyThConsumptionHeat">thermal energy consumption [kWh/d]</param> /// <param name="energyConsumptionPump">el. energy consumption for pumps [kWh/d]</param> /// <param name="energyConsumptionMixer">el. energy consumption for mixer [kWh/d]</param> /// <param name="energyProdMicro">thermal energy prod. microbiology [kWh/d]</param> /// <param name="moneyEnergy"> /// money I get for selling the produced total energy (el. + therm.) in €/d /// </param> /// <param name="fitness_constraints">sum of fitness functions</param> /// <param name="fitness">fitness vector</param> public static void getObjectives(biogas.sensors mySensors, biogas.plant myPlant, biogas.substrates mySubstrates, fitness_params myFitnessParams, /*out double SS_COD_fitness, out double VS_COD_fitness,*/ /*out double SS_COD_degradationRate, out double VS_COD_degradationRate, */ /*out double CH4_fitness, */ out double Stability_punishment, out double energyBalance, out double energyProd_fitness, out double energyConsumption, out double energyThConsumptionHeat, out double energyConsumptionPump, out double energyConsumptionMixer, out double energyProdMicro, /*out double energyThermProduction, * out double energyProduction,*/out double moneyEnergy, out double fitness_constraints, out double[] fitness) { double pHvalue_fitness, VFA_TAC_fitness, TS_fitness, VFA_fitness, AcVsPro_fitness, TAC_fitness, OLR_fitness, HRT_fitness, N_fitness, biogasExcess_fitness, diff_setpoints, CH4_fitness, SS_COD_fitness, VS_COD_fitness; // // normalized between 0 and 1 mySensors.getCurrentMeasurementD("SS_COD_fit", out SS_COD_fitness); // normalized between 0 and 1 mySensors.getCurrentMeasurementD("VS_COD_fit", out VS_COD_fitness); // fitness > 0 if pH value under or over boundaries // normalized between 0 and 1 mySensors.getCurrentMeasurementD("pH_fit", out pHvalue_fitness); // da mit tukey gearbeitet wird, kann der term auch etwas größer als 1 sein mySensors.getCurrentMeasurementD("VFA_TAC_fit", out VFA_TAC_fitness); // this is the fitness of the TS in the digester // da mit tukey gearbeitet wird, kann der term auch etwas größer als 1 sein mySensors.getCurrentMeasurementD("TS_fit", out TS_fitness); // TODO // Calculation of TS concentration in inflow // gibt es auch schon in Individuum Überprüfung: nonlcon_substrate // braucht hier dann eigentlich nicht mehr gemacht werden // verhältnis von propionic acid to acetic acid // max. grenze bei 1.4, s. PhD für Quellen // hier ist der Kehrwert, also min grenze, hier wird mit tukey gearbeitet mySensors.getCurrentMeasurementD("AcVsPro_fit", out AcVsPro_fitness); // da mit tukey gearbeitet wird, kann der term auch etwas größer als 1 sein mySensors.getCurrentMeasurementD("VFA_fit", out VFA_fitness); // tukey mySensors.getCurrentMeasurementD("TAC_fit", out TAC_fitness); // tukey mySensors.getCurrentMeasurementD("OLR_fit", out OLR_fitness); // da mit tukey gearbeitet wird, kann der term auch etwas größer als 1 sein mySensors.getCurrentMeasurementD("HRT_fit", out HRT_fitness); // sum of Snh4 + Snh3, mit tukey mySensors.getCurrentMeasurementD("N_fit", out N_fitness); // CH4 > 50 % als tukey implementiert mySensors.getCurrentMeasurementD("CH4_fit", out CH4_fitness); // biogasExcess_fitness is lossbiogasExcess / 1000 // measured in tausend € / d mySensors.getCurrentMeasurementD("gasexcess_fit", out biogasExcess_fitness); // TODO // // calculate OLR and HRT of plant // TODO - ich könnte auch faecal_fit_sensor schreiben // faecal bacteria removal capacity // intestinal enterococci // faecal coliforms double etaIE = 0, etaFC = 0; for (int idigester = 0; idigester < myPlant.getNumDigesters(); idigester++) { string digesterID = myPlant.getDigesterID(idigester + 1); etaIE += mySensors.getCurrentMeasurementDind("faecal_" + digesterID, 0); etaFC += mySensors.getCurrentMeasurementDind("faecal_" + digesterID, 1); } if (myPlant.getNumDigesters() > 0) { etaIE /= myPlant.getNumDigesters(); etaFC /= myPlant.getNumDigesters(); } // TODO - als ausgabeargumente definieren - nö double fitness_etaIE, fitness_etaFC; // wird in 100 % gemessen fitness_etaIE = 1.0f - etaIE / 100.0f; fitness_etaFC = 1.0f - etaFC / 100.0f; // TODO // stability //stateIsStable(1:n_fermenter,1)= 0; //for ifermenter= 1:n_fermenter // %digester_id= char( plant.getDigesterID(ifermenter) ); // %% TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // stateIsStable(ifermenter,1)= 1;%... // %getStateIsStable(measurements, digester_id, plant); //end //% d.h instabil? //if any(stateIsStable == 0) // Stability_punishment= 1; //else // Stability_punishment= 0; //end Stability_punishment = 0; // TODO // double mbonus; mySensors.getCurrentMeasurementD("manurebonus", out mbonus); myFitnessParams.manurebonus = Convert.ToBoolean(mbonus); // energyConsumption = getElEnergyConsumption(myPlant, mySensors, out energyConsumptionPump, out energyConsumptionMixer); // double energyThProdMixer; double energyThConsumption = getThermalEnergyConsumption(myPlant, mySensors, out energyThConsumptionHeat, out energyThProdMixer, out energyProdMicro); // TODO: einheiten nicht sauber // costs for heating in €/d // // kosten für heizung werden direkt in geld umgerechnet // wenn thermisch produzierte wärme zum heizen des fermenters benutzt wird, // dann werden hier virtuelle kosten berechnet mit kosten revenueTherm, // welche unten bei sellEnergy wieder als erlös mit dem gleichen Wert // berechnet werden, d.h. +/- das gleiche. // double costs_heating = myPlant.calcCostsForHeating_Total( new physValue(energyThConsumption, "kWh/d"), myPlant.myFinances.revenueTherm.Value, myPlant.myFinances.priceElEnergy.Value); // // total el. energy production in kWh/d double energyProduction = mySensors.getCurrentMeasurementDind("energyProdSum", 0); // total thermal energy production in kWh/d double energyThermProduction = mySensors.getCurrentMeasurementDind("energyProdSum", 1); //energyProduction= getEnergyProduction(myPlant, mySensors, out energyThermProduction); // double energyProductionMax = getMaxElEnergyProduction(myPlant); // measured in 100 % energyProd_fitness = (1 - energyProduction / energyProductionMax); // Calculation of costs of substrate inflow // € / d double substrate_costs; mySensors.getCurrentMeasurementD("substrate_cost", out substrate_costs); // double udot; mySensors.getCurrentMeasurementD("udot", out udot); // // TODO // was ist wenn wir weniger thermische energie im BHKW erzeugen als wir verbrauchen? // dann ist costs_heating (virt. kosten) > moneyEnergy thermisch (verkauf von thermischer Energie) // die differenz muss dann elektrisch erzeugt werden, wird allerdings nicht gemacht. // die differenz wird aktuell alas virtuelle Kosten verbucht (Verlust den man hat da man nicht wärme verkauft) // und nicht als reale kosten (erzeugungskosten: thermische energie erzeugt durch heizung) // um das zu lösen, warum ruft man nicht berechnung von costs_heating nach berechnung // von energyThermProduction auf und übergibt dann differenz zw. energyThConsumption und // energyThermProduction? // // TODO - was ist wenn die produzierte elektrische energie von niemanden abgenommen wird // das ist der fall, wenn nach sollwert gefahren wird, dann wird nur so viel energie bezahlt // wie nach sollwert verlangt wurde, das geht so ab eeg 2012 - direktvermarktung // must be in kWh/d double energyElSold = 0; // electrical energy that would be sold // dann gibt es eine referenz kurve welche angibt wieviel energie verkauft würde wenn sie produziert // würde, hier nur elektrische energie if (mySensors.exist("ref_energy_sold")) { // wichtig, dass man sich die messung zur aktuellen zeit holt, da // ref_energy_sold eine referenz vorgibt double time = mySensors.getCurrentTime(); energyElSold = mySensors.getMeasurementDAt("ref_energy_sold", "", time, 0, false); energyElSold = Math.Min(energyElSold, energyProduction); } else { energyElSold = energyProduction; } // moneyEnergy : €/d moneyEnergy = biogas.gasexcess_fit_sensor.sellEnergy(energyElSold, energyThermProduction, myPlant, myFitnessParams); // € / d // is negative when we make more money as we have to pay energyBalance = energyConsumption * myPlant.myFinances.priceElEnergy.Value + costs_heating - moneyEnergy + substrate_costs; // tausend € / d energyBalance = energyBalance / 1000; // // TODO bool noisy = false; // calc setpoint control error //diff_setpoints = calc_setpoint_errors(mySensors, myPlant, myFitnessParams, noisy); mySensors.getCurrentMeasurementD("setpoint_fit", noisy, out diff_setpoints); // calc total fitness of all the constraints fitness_constraints = calcFitnessConstraints(myFitnessParams, SS_COD_fitness, VS_COD_fitness, /*VS_COD_degradationRate,*/ pHvalue_fitness, VFA_TAC_fitness, TS_fitness, VFA_fitness, AcVsPro_fitness, TAC_fitness, OLR_fitness, HRT_fitness, N_fitness, CH4_fitness, biogasExcess_fitness, Stability_punishment, energyProd_fitness, fitness_etaIE, fitness_etaFC, diff_setpoints); // calc fitness vector fitness = calcFitnessVector(myFitnessParams, energyBalance, fitness_constraints, udot); }
/// <summary> /// Calculate fitness value of digester measurements which must be between min and/or max /// </summary> /// <param name="myPlant"></param> /// <param name="mySensors"></param> /// <param name="var_id">"pH", "TS", ...</param> /// <param name="min_max">"min", "max" or "min_max"</param> /// <param name="myFitnessParams"></param> /// <param name="append_var_id">"_2" or "_3"</param> /// <param name="use_tukey">if true, then tukey function is used, leads to /// that returned value can be > 1. if false, then the fitness value /// is between 0 and 1.</param> /// <returns></returns> public static double calcFitnessDigester_min_max(biogas.plant myPlant, biogas.sensors mySensors, string var_id, string min_max, fitness_params myFitnessParams, string append_var_id, bool use_tukey) { double fitness = 0; int n_digester = myPlant.getNumDigesters(); for (int idigester = 0; idigester < n_digester; idigester++) { string digester_id = myPlant.getDigesterID(idigester + 1); double variable; mySensors.getCurrentMeasurementD(var_id + "_" + digester_id + append_var_id, out variable); // double punish_digester = 0; if ((min_max == "min") || (min_max == "min_max")) { double min_bound = myFitnessParams.get_param_of(var_id + "_min", idigester); if (!use_tukey) { punish_digester = Convert.ToDouble(variable < min_bound); } else { // if a boundary is violated then the penalty is at least 1, and bounded // by tukey biweight rho function // TODO : warum wollte ich das 1 + ... haben??? das macht die sache // nicht stetig, was sehr schlecht ist für skalierung und auch // für kriging punish_digester = Convert.ToDouble(variable < min_bound) * (0 * 1 + math.tukeybiweight(variable - min_bound)); } } if ((min_max == "max") || (min_max == "min_max")) { double max_bound = myFitnessParams.get_param_of(var_id + "_max", idigester); if (!use_tukey) { punish_digester = Math.Max(punish_digester, Convert.ToDouble(variable > max_bound)); } else { // if a boundary is violated then the penalty is at least 1, and bounded // by tukey biweight rho function // TODO : warum wollte ich das 1 + ... haben??? das macht die sache // nicht stetig, was sehr schlecht ist für skalierung und auch // für kriging punish_digester = Math.Max(punish_digester, Convert.ToDouble(variable > max_bound) * (0 * 1 + math.tukeybiweight(variable - max_bound))); } } fitness += punish_digester; } // values between 0 and 1, but if tukey function used, then may also be > 1 fitness = fitness / n_digester; return(fitness); }