/// <summary> /// Calc mix of substrates for all digesters /// /// TODO: s. auch in funktion unten /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="mySubstrates"></param> /// <param name="myPlant"></param> /// <param name="substrate_network"></param> /// <param name="mySensors"></param> /// <param name="Q">only used if datasource_type == extern</param> /// <param name="dilution_rates">double vector with the wanted dilution rates for each digester. /// Could be given by a dilution rate control. The size of the vector must /// be equal to number of digesters.</param> /// <returns>[34dim stream for digester1; 34dim stream for digester2; ...]</returns> public static double[] calcADMstreamMix(double t, biogas.substrates mySubstrates, biogas.plant myPlant, double[,] substrate_network, biogas.sensors mySensors, double[] Q, //double deltatime, double[] dilution_rates) { double[,] myStreams = new double[biogas.ADMstate._dim_stream, myPlant.getNumDigesters()]; for (int idigester = 0; idigester < myPlant.getNumDigesters(); idigester++) { double Vliq = myPlant.getDigesterParam(idigester + 1, "Vliq"); // D ist < 0, wenn Anlage nicht geregelt wird double D = dilution_rates[idigester]; double Q_new = D * Vliq; myStreams = math.insertColumn(myStreams, calcADMstreamMixForDigester(t, mySubstrates, substrate_network, mySensors, idigester, Q, Q_new), 0, idigester); // measure energy needed to transport substrates biogas.substrate_transport.run(t, mySensors, "substratemix", myPlant.getDigesterID(idigester + 1), myPlant, mySubstrates, substrate_network); } double[] myStream = math.concat(myStreams); return(myStream); }
/// <summary> /// calculates ADMstream mix of substrates for each digester /// and measures in the mix of all digesters together the COD SS and VS /// content. /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="mySubstrates"></param> /// <param name="myPlant"></param> /// <param name="substrate_network"></param> /// <param name="mySensors"></param> /// <param name="dilution_rates">double vector with the wanted dilution rates for each digester. /// Could be given by a dilution rate control. The size of the vector must /// be equal to number of digesters.</param> /// <returns></returns> public static double[] calc_measureADMstreamMix(double t, biogas.substrates mySubstrates, biogas.plant myPlant, double[,] substrate_network, biogas.sensors mySensors, //double deltatime, double[] dilution_rates) { double[] mystream = calcADMstreamMix(t, mySubstrates, myPlant, substrate_network, mySensors, dilution_rates); // double[] mixed_stream = mixADMstreams(mystream); // measure COD SS and VS of mixed stream // TODO : evtl. von total_mix in substratemix umbenennen // muss dann auch überall anders gemacht werden. bsp: objectives und // sensors: create_sensor_network mySensors.measure(t, "SS_COD_total_mix_2", mixed_stream); mySensors.measure(t, "VS_COD_total_mix_2", mixed_stream); mySensors.measure(t, "Q_total_mix_2", mixed_stream); // messe VS in Substratzufuhr mySensors.measure(t, "VS_total_mix_2", mixed_stream, mySubstrates); return(mystream); }
/// <summary> /// called by TS sensor, OLR sensor, density sensor and heatConsumption_sensor /// /// type 7 /// </summary> /// <param name="x">stream vector</param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="Q">substrate feed and recirculation sludge going into the digester</param> /// <param name="par">some doubles</param> /// <returns>measured values</returns> /// <exception cref="exception">Not implemented</exception> virtual protected physValue[] doMeasurement(double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[] Q, params double[] par) { throw new exception("Not implemented!"); }
// ------------------------------------------------------------------------------------- // !!! CONSTRUCTOR METHODS !!! // ------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// simulates all chps of the plant /// </summary> /// <param name="t"></param> /// <param name="u"> /// number of digesters times n_gases dimensional vector /// with the biogas streams for each digester measured in m^3/d</param> /// <param name="mySensors"></param> /// <param name="myPlant"></param> /// <returns> /// produced electrical power for each chp in kW and the biogas excess in m³/d /// dimension: number of digesters + 1 /// </returns> public static double[] run(double t, double[] u, //double deltatime, biogas.sensors mySensors, biogas.plant myPlant) { // TODO - Parameter aus plant bekommen string gas2chpsplittype = "threshold"; return(run(t, u, mySensors, myPlant, gas2chpsplittype)); }
/// <summary> /// Do measurements which depend on plant and substrates and other measurements. /// /// 7th type /// /// example sensors: /// called by TS sensor /// OLR_sensor /// density_sensor /// </summary> /// <param name="time">current simulation time</param> /// <param name="id">id of sensor</param> /// <param name="x">could be statevector</param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="Q"> /// first values are Q for substrates, then pumped sludge going into digester /// </param> /// <param name="par">some doubles</param> /// <returns>measured values</returns> /// <exception cref="exception">Unknown sensor id</exception> public physValue[] measureVec(double time, string id, //double deltatime, double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[] Q, params double[] par) { sensor mySensor = get(id); return(mySensor.measure(time, sampling_time, x, myPlant, mySubstrates, mySensors, Q, par)); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Do measurements which depend on plant and substrates and other measurements. /// /// 7th type /// /// example sensors: /// called by TS sensor /// OLR_sensor /// density_sensor /// </summary> /// <param name="time">current simulation time</param> /// <param name="id">id of sensor</param> /// <param name="x">could be statevector</param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="Q">could be substrate feed</param> /// <param name="value">first measured value</param> /// <exception cref="exception">Unknown sensor id</exception> public void measure(double time, string id, //double deltatime, double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[] Q, out double value) { physValue[] vals = measureVec(time, id, x, myPlant, mySubstrates, mySensors, Q); value = vals[0].Value; }
/// <summary> /// TODO /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="mySensors"></param> /// <param name="u">stream going into the pump measured in m^3/d</param> /// <param name="Q_pump">to be pumped amount in m^3/d</param> /// <param name="unit_start"></param> /// <param name="unit_destiny"></param> /// <param name="myPlant"></param> /// <returns></returns> public static double run(double t, //double deltatime, biogas.sensors mySensors, double u, double Q_pump, string unit_start, string unit_destiny, biogas.plant myPlant /*, biogas.substrates mySubstrates, * double[,] substrate_network*/) { string pump_id = getid(unit_start, unit_destiny); // determine rho - default value for digester or storagetank //physValue density = new physValue("rho", 1000, "kg/m^3", "density"); if (unit_start == "substratemix") { throw new exception("pumps may not pump the substratemix"); // get mean rho out of substrates and double[] Q // nutze hier getSubstrateMixFlowForFermenter //double[] Q; // TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // id_in_array einführen und durch "Q" ersetzen // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //mySensors.getCurrentMeasurements("Q", "Q", mySubstrates, out Q); //getMeasurementsAt("Q", "Q", time, mySubstrates, out Q); // unit_destiny here must be a digester_id, because you cannot // pump substratemix directly into final storage tank //physValue[] Q_digester = // sensors.getSubstrateMixFlowForFermenter(t, mySubstrates, myPlant, // mySensors, substrate_network, unit_destiny); //// values are Q for all substrates //double[] Q = physValue.getValues(Q_digester); //mySubstrates.get_weighted_mean_of(Q, "rho", out density); } // measure energy needed to pump stuff double P_kWh_d; // es ist wichtig, dass hier rho nicht mit übergeben wird // wird genutzt als erkennungsmerkmal in pumpEnergy_sensor // im unterschied zu substrate_transport double[] parvec = { Q_pump };//, density.Value }; mySensors.measure(t, "pumpEnergy_" + pump_id, myPlant, u, parvec, out P_kWh_d); // TODO - DEFINE WHAT SHOULD be returned //double[] retvals = { P_kWh_d, Q_pump }; return(P_kWh_d);// retvals; }
/// <summary> /// Do measurements which depend on plant and substrates and other measurements. /// /// 7th type /// /// example sensors: /// called by TS sensor /// OLR_sensor /// density_sensor /// </summary> /// <param name="time">current simulation time</param> /// <param name="id">id of sensor</param> /// <param name="x">could be statevector</param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="Q"> /// first values are Q for substrates, then pumped sludge going into digester /// dimension: always number of substrates + number of digesters /// </param> /// <returns>measured values</returns> /// <exception cref="exception">Unknown sensor id</exception> public physValue[] measureVec(double time, string id, //double deltatime, double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[] Q) { double[] par = new double[1]; return(measureVec(time, id, x, myPlant, mySubstrates, mySensors, Q, par)); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Check whether current substrate feed saved in sensors qualifies for the manure bonus /// /// </summary> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <returns></returns> public static bool check_manurebonus(biogas.substrates mySubstrates, biogas.sensors mySensors) { double t = mySensors.getCurrentTime(); // get current time // get recorded substrate feeds in m³/d physValue[] Q = mySensors.getMeasurementsAt("Q", "Q", t, mySubstrates); double[] Qdbl = physValue.getValues(Q); return(check_manurebonus(mySubstrates, Qdbl)); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// TODO /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="mySensors"></param> /// <param name="unit_start"></param> /// <param name="unit_destiny"></param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="substrate_network"></param> /// <returns></returns> public static double run(double t, //double deltatime, biogas.sensors mySensors, string unit_start, string unit_destiny, biogas.plant myPlant, biogas.substrates mySubstrates, double[,] substrate_network) { double Q_pump, Q_solids; return(run(t, mySensors, unit_start, unit_destiny, myPlant, mySubstrates, substrate_network, out Q_pump, out Q_solids)); }
/// <summary> /// Calc mix of substrates for all digesters /// /// Q is gotten out of sensor /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="mySubstrates"></param> /// <param name="myPlant"></param> /// <param name="substrate_network"></param> /// <param name="mySensors"></param> /// <param name="dilution_rates">double vector with the wanted dilution rates for each digester. /// Could be given by a dilution rate control. The size of the vector must /// be equal to number of digesters.</param> /// <returns>a column vector with dimension equal to dim_stream * n_digester</returns> public static double[] calcADMstreamMix(double t, biogas.substrates mySubstrates, biogas.plant myPlant, double[,] substrate_network, biogas.sensors mySensors, //double deltatime, double[] dilution_rates) { double[] Q; mySensors.getMeasurementsAt("Q", "Q", t, mySubstrates, out Q); return(calcADMstreamMix(t, mySubstrates, myPlant, substrate_network, mySensors, Q, dilution_rates)); }
/// <summary> /// Do measurements which depend on plant and substrates and other measurements. /// /// 7th type /// /// example sensors: /// called by TS sensor /// OLR_sensor /// density_sensor /// </summary> /// <param name="time">current simulation time</param> /// <param name="id">id of sensor</param> /// <param name="x">could be statevector</param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="substrate_network"></param> /// <param name="plant_network"></param> /// <param name="digester_id">digester ID</param> /// <param name="value">first measured value</param> /// <exception cref="exception">Unknown sensor id</exception> public void measure(double time, string id, //double deltatime, double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[,] substrate_network, double[,] plant_network, string digester_id, out double value) { physValue[] vals = measureVec(time, id, x, myPlant, mySubstrates, mySensors, substrate_network, plant_network, digester_id); value = vals[0].Value; }
/// <summary> /// Calculation of total thermal energy consumption /// /// returns thermal energy consumption of /// - heating /// - microbiology (production) /// - mixer (production) /// /// in kWh/d /// </summary> /// <param name="myPlant"></param> /// <param name="mySensors"></param> /// <param name="energyConsumptionHeat">thermal energy consumption of heat losses [kWh/d]</param> /// <param name="energyProdMixer">th. energy prod. of stirrer [kWh/d]</param> /// <param name="energyProdMicro">th. energy prod. by micros [kWh/d]</param> /// <returns></returns> private static double getThermalEnergyConsumption(biogas.plant myPlant, biogas.sensors mySensors, out double energyConsumptionHeat, out double energyProdMixer, out double energyProdMicro) { double energyConsumption = 0; energyConsumptionHeat = 0; energyProdMixer = 0; // heat energy dissipated to digester in kWh/d energyProdMicro = 0; // int n_digester = myPlant.getNumDigesters(); for (int idigester = 0; idigester < n_digester; idigester++) { string digester_id = myPlant.getDigesterID(idigester + 1); biogas.digester myDigester = myPlant.getDigester(idigester + 1); // heating physValue[] heat_v = mySensors.getCurrentMeasurementVector("heatConsumption_" + digester_id); // inflow heating // thermal Energie welche benötigt wird um das Substrat aufzuheizen // \frac{kWh}{d} energyConsumptionHeat += heat_v[0].Value; // radiation loss energy // thermal Energie, welche die Heizungen benötigen um die // Wärmeverluste auszugleichen // \frac{kWh}{d} energyConsumptionHeat += heat_v[1].Value; // produced heat by bacteria in digester [kWh/d] // das ist eine thermische, keine elektrische energie energyProdMicro += heat_v[2].Value; // thermal energy production by stirrer through dissipation // \frac{kWh}{d} energyProdMixer += heat_v[3].Value; } // sum in kWh/d // Vorsicht: energy von bakterien wird als produktion nicht als verbrauch // angesehen, deshalb hier neg. VZ energyConsumption = energyConsumptionHeat - energyProdMicro - energyProdMixer; return(energyConsumption); }
/// <summary> /// run (emulate) the final storage tank /// just measures concentration of SS and VS COD in output /// stream as well as Q. /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="x">ADM stream vector</param> /// <param name="mySensors"></param> public static void run(double t, double[] x, biogas.sensors mySensors /*, * double deltatime*/) { // measure COD SS and VS and Q of outlet stream mySensors.measure(t, "SS_COD_finalstorage_2", x); mySensors.measure(t, "VS_COD_finalstorage_2", x); mySensors.measure(t, "Q_finalstorage_2", x); // TODO - messen von volatile solids // TODO - substrate müsste noch als parameter übergeben werden }
/// <summary> /// Do measurements which depend on plant and substrates and other measurements. /// /// 7th type /// /// example sensors: /// called by TS sensor /// OLR_sensor /// density_sensor /// </summary> /// <param name="time">current simulation time</param> /// <param name="id">id of sensor</param> /// <param name="x">could be statevector</param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="substrate_network"></param> /// <param name="plant_network"></param> /// <param name="digester_id">ID of digester</param> /// <returns>measured values</returns> /// <exception cref="exception">Unknown sensor id</exception> public physValue[] measureVec(double time, string id, //double deltatime, double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[,] substrate_network, double[,] plant_network, string digester_id) { physValue[] pQ = getInputVolumeflowForFermenter(time, mySubstrates, myPlant, mySensors, substrate_network, plant_network, digester_id); // first values are Q for substrates, then pumped sludge going into digester double[] Q = physValue.getValues(pQ); return(measureVec(time, id, x, myPlant, mySubstrates, mySensors, Q)); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// TODO /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="mySensors"></param> /// <param name="u">stream going into the pump measured in m^3/d</param> /// <param name="unit_start"></param> /// <param name="unit_destiny"></param> /// <param name="myPlant"></param> /// <param name="Q_pump">to be pumped amount in m^3/d</param> /// <returns></returns> public static double run(double t, //double deltatime, biogas.sensors mySensors, double u, string unit_start, string unit_destiny, biogas.plant myPlant, /*biogas.substrates mySubstrates, * double[,] substrate_network,*/out double Q_pump) { string pump_id = getid(unit_start, unit_destiny); // determine Q //double Q_pump; // to be pumped amount mySensors.getMeasurementAt("Q", "Q_" + pump_id, t, out Q_pump); return(run(t, mySensors, u, Q_pump, unit_start, unit_destiny, myPlant));//, mySubstrates, substrate_network); }
/// <summary> /// Do measurements which depend on substrate and sensors. /// /// 9th type /// /// example sensor: /// used by manurebonus sensor /// </summary> /// <param name="time">current simulation time [days]</param> /// <param name="deltatime">sample time of the sensor [days]</param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="par">some doubles</param> /// <returns>measured values</returns> public physValue[] measure(double time, double deltatime, biogas.substrates mySubstrates, biogas.sensors mySensors, params double[] par) { physValue[] values; if (time - getCurrentTime() >= deltatime) { values = doMeasurement(mySubstrates, mySensors, par); addMeasurement(time, values); } else { values = getCurrentMeasurementVector(); } return(values); }
/// <summary> /// Do measurements which depend on fitness_params. /// /// 8th type /// /// example sensor: /// used by all fitness sensors /// </summary> /// <param name="time">current simulation time [days]</param> /// <param name="deltatime">sample time of the sensor [days]</param> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <param name="mySensors"></param> /// <param name="par">some doubles</param> /// <returns>measured values</returns> public physValue[] measure(double time, double deltatime, biogas.plant myPlant, biooptim.fitness_params myFitnessParams, biogas.sensors mySensors, params double[] par) { physValue[] values; if (time - getCurrentTime() >= deltatime) { values = doMeasurement(myPlant, myFitnessParams, mySensors, par); addMeasurement(time, values); } else { values = getCurrentMeasurementVector(); } return(values); }
/// <summary> /// Call measure of all type 7 sensors /// /// type 7 /// /// used by OLR, TS and density sensor /// </summary> /// <param name="time">current simulation time in days</param> /// <param name="x">stream vector usually 34 dim.</param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="substrate_network"></param> /// <param name="plant_network"></param> /// <param name="digester_id">ID of digester</param> public void measure_type7(double time, //double deltatime, double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[,] substrate_network, double[,] plant_network, string digester_id) { double value; foreach (string id in ids_type7) { // TODO weiterhin ein Problem, wenn 1. Dig: dig1 und zweiter digester // dummy_dig1 heißt, hatten wir nicht ohnehin definiert, dass digester // ID keinen Unterstrich haben darf? s. gui_plant??? dann wäre das hier ok. if (id.Contains("_" + digester_id)) { measure(time, id, x, myPlant, mySubstrates, mySensors, substrate_network, plant_network, digester_id, out value); } } }
/// <summary> /// Calculates Ash content in digester /// </summary> /// <param name="x"></param> /// <param name="digester_id"></param> /// <param name="mySensors"></param> /// <param name="mySubstrates"></param> /// <param name="Q"></param> /// <param name="myPlant"></param> /// <returns></returns> public static physValue calcAsh(double[] x, string digester_id, biogas.sensors mySensors, substrates mySubstrates, double[] Q, plant myPlant) { physValue ash_digester, ash_substrate; try { ash_digester = mySensors.getCurrentMeasurement("Ash_" + digester_id + "_3"); } catch (exception e) { Console.WriteLine(e.Message); ash_digester = new physValue("ash_digester", 11, "% TS"); } try { mySubstrates.get_weighted_mean_of(Q, "Ash", out ash_substrate); } catch (exception e) { ash_substrate = new physValue("ash_substrate", 0, "% FM"); return(ash_digester); } double volume_substrate = math.sum(Q); // m^3/d double volume_dig = myPlant.get_param_of_d("Vliq"); // m^3 // TODO - achtung mit einheiten, mix von % TS und % FM ash_digester = ash_digester + volume_substrate / volume_dig * ash_substrate; ash_digester = ash_digester.convertUnit("% TS"); ash_digester.Symbol = "Ash"; return(ash_digester); }
// ------------------------------------------------------------------------------------- // !!! 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); }
/// <summary> /// Calculates the ADM input mix for the given digester /// /// OK, s. oben: diese Funktion noch überladen, ohne Q, aber mit sensors /// dann wird Q aus sensor geholt, mit übergabe von t /// /// OK, ist drin: was ist mit substrate parameter, müsste auch hier rein, oder? /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="mySubstrates"></param> /// <param name="substrate_network"></param> /// <param name="mySensors"></param> /// <param name="digester_index">0-based</param> /// <param name="Q">Q for each substrate for total plant</param> /// <param name="Q_new">wanted total substrate feed into digester, in m^3/d</param> public static double[] calcADMstreamMixForDigester(double t, biogas.substrates mySubstrates, double[,] substrate_network, biogas.sensors mySensors, int digester_index, double[] Q, double Q_new) { int isubstrate = 0; // normalize substrate_network double[,] norm_substrate_network = math.normalize(substrate_network, 1); // double[] substrate_network_digester = science.math.getcol(norm_substrate_network, digester_index); // // total feed into digester double myQ = science.math.mtimes(substrate_network_digester, Q); double myQnew = myQ; // if (Q_new >= 0) // wenn es < 0 wäre, dann wäre der Fermenter nicht geregelt { myQnew = Q_new; } // double[,] myStreams = new double[biogas.ADMstate._dim_stream, mySubstrates.getNumSubstrates()]; foreach (biogas.substrate mySubstrate in mySubstrates) { // change params of substrate biogas.substrateparams_sensor.set_substrate_params_from_sensor(t, mySensors, mySubstrate); // Q of substrate double Q_subs = Q[isubstrate] * substrate_network_digester[isubstrate]; if (myQ > 0) { Q_subs = Q_subs * myQnew / myQ; } // calc ADM stream out of substrate myStreams = math.insertColumn(myStreams, calcADMstream(mySubstrate, Q_subs), 0, isubstrate); isubstrate++; } return(mixADMstreams(myStreams)); //if strcmp(substrate_name, 'futterkalk') // % CO2 auf - Hco3/2 setzen // % da durch zugabe von CaCO3 Co2 abgebaut wird und reagiert zu zweifacher // % menge von Hco3 und Ca2+ // y_adm1xp(10)= -y_adm1xp(32) / 2; // %y_adm1xp(26)= 0; //end }
/// <summary> /// used by all fitness sensors /// /// type 8 /// </summary> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <param name="mySensors"></param> /// <param name="par">some doubles</param> /// <returns>measured values</returns> /// <exception cref="exception">Not implemented</exception> virtual protected physValue[] doMeasurement(biogas.plant myPlant, biooptim.fitness_params myFitnessParams, biogas.sensors mySensors, params double[] par) { throw new exception("Not implemented!"); }
/// <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); }
/// <summary> /// simulates all chps of the plant /// </summary> /// <param name="t"></param> /// <param name="u"> /// number of digesters times n_gases dimensional vector /// with the biogas streams for each digester measured in m^3/d</param> /// <param name="mySensors"></param> /// <param name="myPlant"></param> /// <param name="gas2chpsplittype">"threshold", "one2one", "fiftyfifty"</param> /// <returns> /// produced electrical power for each chp in kW and the biogas excess in m³/d /// dimension: number of chps + 1 /// </returns> public static double[] run(double t, double[] u, //double deltatime, biogas.sensors mySensors, biogas.plant myPlant, string gas2chpsplittype) { // // total biogas production [m³/d] // total biogas production splitted into components [m³d] // biogas for each chp splitted into components // biogas excess [m³/d] double[] biogas_vec; try { // split biogas upon available chps mySensors.measureVec(t, "total_biogas_", myPlant, u, gas2chpsplittype, out biogas_vec); } catch (exception e) { biogas_vec = math.zeros(11); //throw(e); } // int num_chps = myPlant.getNumCHPs(); // number of digesters // 2dim vector, 1st value, produced electrical energy / day // 2nd value: produced thermal energy / day double[] P_kWh_d; // returned vector by this method // produced electrical power for each chp in kW and the biogas excess in m³/d // dimension: number of chps + 1 double[] outvec = new double[num_chps + 1]; // double[] Psum_el_th = new double[2]; Psum_el_th[0] = 0; Psum_el_th[1] = 0; // for (int ichp = 0; ichp < num_chps; ichp++) { // biogas which is burned by chp ichp double[] biogas_chp; try { // biogas which is burned by chp ichp biogas_chp = math.getrows(biogas_vec, 1 + (1 + ichp) * (int)biogas.BioGas.n_gases, 1 + (2 + ichp) * (int)biogas.BioGas.n_gases - 1); } catch (exception e) { biogas_chp = math.zeros(3); //throw (e); } try { // burn biogas in chp ichp mySensors.measureVec(t, "energyProduction_" + myPlant.getCHPID(ichp + 1), myPlant, biogas_chp, out P_kWh_d); } catch (exception e) { P_kWh_d = math.zeros(2); //throw (e); } // electrical energy in kW outvec[ichp] = P_kWh_d[0] / 24; // convert from kWh/d -> kW Psum_el_th[0] += P_kWh_d[0]; Psum_el_th[1] += P_kWh_d[1]; } // insert biogas excess in m^3/d outvec[num_chps] = biogas_vec[biogas_vec.Length - 1]; try { // // measure total energy production of plant in kWh/d mySensors.measureVec(t, "energyProdSum", Psum_el_th); } catch (exception e) { //throw(e); } // return(outvec); }
// ------------------------------------------------------------------------------------- // !!! 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> /// TODO /// </summary> /// <param name="t">current simulation time in days</param> /// <param name="mySensors"></param> /// <param name="unit_start"></param> /// <param name="unit_destiny"></param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="substrate_network"></param> /// <param name="Q_pump">to be pumped amount in m^3/d</param> /// <param name="Q_solids"> /// amount that can not be pumped because TS content is too high /// </param> /// <returns></returns> public static double run(double t, //double deltatime, biogas.sensors mySensors, string unit_start, string unit_destiny, biogas.plant myPlant, biogas.substrates mySubstrates, double[,] substrate_network, out double Q_pump, out double Q_solids) { string substrate_transport_id = getid(unit_start, unit_destiny); // determine rho - default value for digester or storagetank physValue density_liq = new physValue("rho", 1000, "kg/m^3", "density"); physValue density_sol = new physValue("rho", 1000, "kg/m^3", "density"); Q_pump = 0; Q_solids = 0; // if (unit_start == "substratemix") { // get mean rho out of substrates and double[] Q // nutze hier getSubstrateMixFlowForFermenter //double[] Q; // TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // id_in_array einführen und durch "Q" ersetzen // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //mySensors.getCurrentMeasurements("Q", "Q", mySubstrates, out Q); //getMeasurementsAt("Q", "Q", time, mySubstrates, out Q); // unit_destiny here must be a digester_id, because you cannot // pump substratemix directly into final storage tank physValue[] Q_digester = sensors.getSubstrateMixFlowForFermenter(t, mySubstrates, myPlant, mySensors, substrate_network, unit_destiny); // values are Q for all substrates double[] Q = physValue.getValues(Q_digester); // an dieser Stelle muss man heraus finden, welche Elemente // von Q flüssig und welche fest sind, an Hand TS Gehalt bestimmen < 11 % FM biogas.substrates mySubsSol = new biogas.substrates(); biogas.substrates mySubsLiq = new biogas.substrates(); List <double> Q_liq_subs = new List <double>(); List <double> Q_sol_subs = new List <double>(); // for (int iel = 0; iel < Q.Length; iel++) { double TS = mySubstrates.get_param_of(iel + 1, "TS"); if (TS < 11) // liquid substrate (pumpable) { mySubsLiq.addSubstrate(mySubstrates.get(iel + 1)); Q_liq_subs.Add(Q[iel]); } else { mySubsSol.addSubstrate(mySubstrates.get(iel + 1)); Q_sol_subs.Add(Q[iel]); } } if (Q_liq_subs.Count > 0) { double[] Q_liq_s_a = Q_liq_subs.ToArray(); Q_pump = math.sum(Q_liq_s_a); // mySubsLiq.get_weighted_mean_of(Q_liq_s_a, "rho", out density_liq); } if (Q_sol_subs.Count > 0) { double[] Q_sol_s_a = Q_sol_subs.ToArray(); Q_solids = math.sum(Q_sol_s_a); // mySubsSol.get_weighted_mean_of(Q_sol_s_a, "rho", out density_sol); } } else { throw new exception("substrate_transport may only pump the substratemix"); } // measure energy needed to pump stuff // dann als summe raus geben (solids + liquids) double P_kWh_d; // its important that we pass two arguments here // is used in energyPump_sensor to ditinguish between this call // the one from pump.cs double[] parvec = { Q_pump, density_liq.Value }; mySensors.measure(t, "pumpEnergy_" + substrate_transport_id, myPlant, Q_pump, parvec, out P_kWh_d); // double[] parv = { density_sol.Value }; double P_solids; mySensors.measure(t, "transportEnergy_" + substrate_transport_id, myPlant, Q_solids, parv, out P_solids); P_kWh_d += P_solids; // TODO - DEFINE WHAT SHOULD be returned //double[] retvals = { P_kWh_d, Q_pump }; return(P_kWh_d);// retvals; }