/// <summary> /// type 7 /// /// called in ADMstate_stoichiometry.cs -> measure_type7 /// </summary> /// <param name="x">ADM state vector - not used</param> /// <param name="myPlant"></param> /// <param name="mySubstrates">not yet used</param> /// <param name="mySensors">used to get TS inside digester</param> /// <param name="Q">not used</param> /// <param name="par">not used</param> /// <returns>measured values</returns> override protected physValue[] doMeasurement(double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[] Q, params double[] par) { // 1st Pel in kWh/d for mixing digester // 2nd Pdissipated in kWh/d for mixing digester, dissipated in digester physValue[] values = new physValue[dimension]; // digester myDigester = myPlant.getDigesterByID(id_suffix); // calc stirrer(s) power for digester in kWh/d values[0] = myDigester.calcStirrerPower(mySensors); // calc stirrer(s) power dissipated to digester in kWh/d values[1] = myDigester.calcStirrerDissipation(mySensors); values[0].Label = "electrical energy of stirrer"; values[1].Label = "thermal energy dissipated by stirrer"; // return(values); }
/// <summary> /// Calculate thermal/electrical power needed by heating to compensate heat loss in digester. /// </summary> /// <param name="digester_id">digester id</param> /// <param name="Q">substrate feed measured in m^3/d</param> /// <param name="mySubstrates"></param> /// <param name="T_ambient">ambient temperature</param> /// <param name="mySensors"></param> /// <returns>thermal/electrical energy needed by heating in kWh/d</returns> /// <exception cref="exception">Unknown digester id</exception> /// <exception cref="exception">Q.Length != mySubstrates.Count</exception> /// <exception cref="exception">efficiency is zero, division by zero</exception> public double calcHeatPower(string digester_id, double[] Q, substrates mySubstrates, physValue T_ambient, sensors mySensors) { digester myDigester = get(digester_id); return(myDigester.calcHeatPower(Q, mySubstrates, T_ambient, mySensors)); }
/// <summary> /// write measured variable at time t in given mySubstrate /// (mySubstrates.get(substrate_id)) /// </summary> /// <param name="t">current simulation time</param> /// <param name="mySensors"></param> /// <param name="mySubstrates">changed in this call</param> /// <param name="substrate_id">ID of substrate to be set</param> public static void set_substrate_params_from_sensor(double t, biogas.sensors mySensors, biogas.substrates mySubstrates, string substrate_id) { biogas.substrate mySubstrate = mySubstrates.get(substrate_id); set_substrate_params_from_sensor(t, mySensors, mySubstrate); }
/// <summary> /// Calculates the thermal energy balance of the digester. It compares /// thermal sinks (negative) with thermal sources (positive) inside the digester /// thermal sinks are: /// - heat substrates /// - radiation /// thermal sources are: /// - microbiology /// - stirrer dissipation /// </summary> /// <param name="digester_id">digester id</param> /// <param name="Q">substrate feed measured in m^3/d</param> /// <param name="mySubstrates"></param> /// <param name="T_ambient">ambient temperature</param> /// <param name="mySensors"></param> /// <returns>thermal energy balance measured in kWh/d</returns> /// <exception cref="exception">Unknown digester id</exception> /// <exception cref="exception">Q.Length != mySubstrates.Count</exception> public double calcThermalEnergyBalance(string digester_id, double[] Q, substrates mySubstrates, physValue T_ambient, sensors mySensors) { digester myDigester = get(digester_id); return(myDigester.calcThermalEnergyBalance(Q, mySubstrates, T_ambient, mySensors)); }
///// <summary> ///// Calculate power needed to comepnsate thermal loss due to ///// radiation through the digesters surface. ///// </summary> ///// <param name="T_ambient">ambient temperature</param> ///// <param name="P_radiation_loss_kW">electrical or thermal power</param> ///// <param name="P_radiation_loss_kWh_d">electrical or thermal energy per day</param> ///// <exception cref="exception">efficiency is zero, division by zero</exception> //public void compensateHeatLossDueToRadiation(physValue T_ambient, // out physValue P_radiation_loss_kW, // out physValue P_radiation_loss_kWh_d) //{ // physValue P_radiation_loss= calcHeatLossDueToRadiation(T_ambient); // heating.compensateHeatLossDueToRadiation(P_radiation_loss, // out P_radiation_loss_kW, // out P_radiation_loss_kWh_d); //} /// <summary> /// Calculates the thermal energy balance of the digester. It compares /// thermal sinks (negative) with thermal sources (positive) inside the digester /// /// At the moment the following processes are reflected: /// /// thermal sinks are: /// /// 1) heat energy needed to heat the substrates up to the digesters temperature /// (heat substrates) /// 2) heat energy loss due to radiation through the surface of the fermenter /// (radiation) /// /// thermal sources are: /// /// 1) microbiology /// 2) stirrer dissipation /// /// For further effects see /// /// 1) Lübken, M., Wichern, M., Schlattmann, M., Gronauer, A., and Horn, H.: /// Modelling the energy balance of an anaerobic digester fed with cattle manure /// and renewable energy crops, Water Research 41, pp. 4085-4096, 2007 /// 2) Lindorfer, H., Kirchmayr, R., Braun, R.: /// Self-heating of anaerobic digesters using energy crops, 2005 /// /// /// </summary> /// <param name="Q">substrate feed measured in m^3/d</param> /// <param name="mySubstrates"></param> /// <param name="T_ambient">ambient temperature</param> /// <param name="mySensors"></param> /// <returns>thermal energy balance mesasured in kWh/d</returns> /// <exception cref="exception">Q.Length != mySubstrates.Count</exception> public double calcThermalEnergyBalance(double[] Q, substrates mySubstrates, physValue T_ambient, sensors mySensors) { physValue Psubsheat, Pradloss, Pmicros, Pstirdiss; return(calcThermalEnergyBalance(Q, mySubstrates, T_ambient, mySensors, out Psubsheat, out Pradloss, out Pmicros, out Pstirdiss)); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC GET METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Returns the ADM params vector, depending on the current substrate feed. /// The current substrate feed is taken out of the current substrate feed /// measurement in mySensors /// /// Attention!!! Changes the values of the ADM params!!! /// /// the following params depend on the substrate feed: /// - XC fractions (fCH_XC, fLI_XC, ...] /// - disintegration constant: kdis /// - hydrolysis constant: khyd_ch, khyd_pr, khyd_li /// </summary> /// <param name="index">1-based index of digester</param> /// <param name="t">current simulation time measured in days</param> /// <param name="mySensors"></param> /// <param name="mySubstrates"></param> /// <param name="substrate_network_digester"></param> /// <returns></returns> /// <exception cref="exception">Invalid digester index</exception> public double[] getADMparams(int index, double t, sensors mySensors, substrates mySubstrates, double[] substrate_network_digester /*, * double deltatime*/) { return(get(index).getADMparams(t, mySensors, mySubstrates, substrate_network_digester)); }
/// <summary> /// ... /// /// type 9 /// </summary> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.substrates mySubstrates, biogas.sensors mySensors, params double[] par) { physValue[] values = new physValue[1]; // bool manurebonus = biogas.eeg2009.check_manurebonus(mySubstrates, mySensors); values[0] = new physValue("manurebonus", Convert.ToDouble(manurebonus), "-"); return(values); }
/// <summary> /// ... /// /// type 9 /// </summary> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.substrates mySubstrates, biogas.sensors mySensors, params double[] par) { physValue[] values = new physValue[1]; // double udot = calcudot(mySensors, mySubstrates); values[0] = new physValue("udot", udot, "-"); return(values); }
/// <summary> /// Calculate electrical power of stirrers inside digester /// </summary> /// <param name="mySensors"></param> /// <returns>electrical power of stirrers inside digester in kWh/d</returns> /// <exception cref="exception">calculation of stirrer power failed</exception> public physValue calcStirrerPower(sensors mySensors) { double Tdigester = T.convertUnit("°C").Value; double TSdigester; // get TS measurement inside this digester mySensors.getCurrentMeasurementD("TS_" + id + "_3", out TSdigester); // calc electrical power of all mixers in this digester, measure in kWh/d double Pel = mixers.calcPelectrical(Tdigester, TSdigester); return(new physValue("Pel_mix", Pel, "kWh/d")); }
/// <summary> /// Calculate dissipated power of stirrers inside digester, dissipated to digester /// in kWh/d /// </summary> /// <param name="mySensors"></param> /// <returns>dissipated power of stirrers inside digester in kWh/d</returns> /// <exception cref="exception">calculation of stirrer power failed</exception> public physValue calcStirrerDissipation(sensors mySensors) { double Tdigester = T.convertUnit("°C").Value; double TSdigester; // get TS measurement inside this digester mySensors.getCurrentMeasurementD("TS_" + id + "_3", out TSdigester); // calc dissipated power of all mixers in this digester, measure in kWh/d double Pdiss = mixers.calcPdissipation(Tdigester, TSdigester); return(new physValue("Pdiss_mix", Pdiss, "kWh/d")); }
/// <summary> /// calculates fitness values that have to do with biogas production /// at the moment two fitness values are calculated: /// - methane concentration smaller 50 % /// - biogas excess /// /// TODO: erweitern um H2_fitness? use a H2_max boundary /// / da simulation für h2 bisher teilweise recht ungenau, macht /// die nutzun einer upper boundary momentan nicht so viel sinn /// </summary> /// <param name="mySensors"></param> /// <param name="CH4_fitness">1 if CH4 concentration is smaller 50 %</param> private static void getBiogas_fitness(biogas.sensors mySensors, out double CH4_fitness) { // Calculation of methane amount in Biogas physValue[] biogas_v = mySensors.getCurrentMeasurementVector("total_biogas_"); double methaneConcentration = biogas_v[2].Value; CH4_fitness = Convert.ToDouble(methaneConcentration < 50) * (0 * 1 + math.tukeybiweight(methaneConcentration - 50)); //CH4_fitness = Convert.ToDouble(methaneConcentration < 50); }
/// <summary> /// Calculates VS_COD degradation rate and return its normalized /// value as VS_COD_fitness /// </summary> /// <param name="mySensors"></param> /// <param name="VS_COD_degradationRate"></param> /// <returns>VS_COD_fitness: normalized between 0 and 1</returns> private static double getVS_COD_fitness(biogas.sensors mySensors, out double VS_COD_degradationRate) { // Volatile Solids COD degradation // volatile COD in the final storage tank // TODO stimmt so nicht, masseabgang durch biogas // da der FLuss welcher in das Endlager rein geht immer identisch mit // dem Fluss der Eingangssubstrate ist, kürzen sich die beiden werte // immer raus, also hier nicht benötigt physValue Q_final_storage = mySensors.getCurrentMeasurement("Q_finalstorage_2"); // gCOD/l physValue VS_COD_final_storage = mySensors.getCurrentMeasurement("VS_COD_finalstorage_2"); // gCOD/l * m^3 == also eine Menge, keine Konzentration double VS_COD_amount_final = VS_COD_final_storage.Value * Q_final_storage.Value; // volatile COD in the substrate feed // TODO // measure Q_total_mix_2 physValue Q_total_mix = mySensors.getCurrentMeasurement("Q_total_mix_2"); // gCOD/l physValue VS_COD_substrate = mySensors.getCurrentMeasurement("VS_COD_total_mix_2"); // gCOD/l * m^3 == also eine Menge, keine Konzentration double VS_COD_amount_total = VS_COD_substrate.Value * Q_total_mix.Value; // // a value between 0 and 100 VS_COD_degradationRate = (1 - Math.Max(VS_COD_amount_final, 0) / Math.Max(VS_COD_amount_total, double.Epsilon)) * 100; // double VS_COD_degradationMin = 0; double VS_COD_degradationMax = 100; // values between 0 and 1 double VS_COD_fitness = Math.Abs((1 - math.normalize(VS_COD_degradationRate, VS_COD_degradationMin, VS_COD_degradationMax))); return(VS_COD_fitness); }
// ------------------------------------------------------------------------------------- // !!! PRIVATE METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Calculates sum of setpoint control errors /// </summary> /// <param name="mySensors">sensors object with all measurements</param> /// <param name="myPlant">plant object</param> /// <param name="myFitnessParams">fitness params</param> /// <param name="noisy">if true then noisy measurements are used</param> /// <returns></returns> private static double calc_setpoint_errors(sensors mySensors, plant myPlant, biooptim.fitness_params myFitnessParams, bool noisy) { double diff_setpoints = 0; // to be returned value: control setpoint error double[] sim_t = mySensors.getTimeStream(); double t = mySensors.getCurrentTime(); double sim_val, ref_val; // last simulated value and reference value at time t // if (sim_t.Length > 3) { foreach (biooptim.setpoint mySetpoint in myFitnessParams.mySetpoints) { if (mySetpoint.s_operator == "") // then compare measurement of one sensor { string sensor_id = mySetpoint.sensor_id + "_" + mySetpoint.location; // get reference values always not noisy ref_val = mySensors.getMeasurementDAt(String.Format("ref_{0}_{1}", sensor_id, mySetpoint.index), "", t, 0, false); sim_val = mySensors.getCurrentMeasurementDind(sensor_id, mySetpoint.index, noisy); } else // compare measurement of a group of sensors, energy of all chps, gas of all digesters, ... { string s_operator = mySetpoint.location + "_" + mySetpoint.s_operator; // get reference values always not noisy ref_val = mySensors.getMeasurementDAt( String.Format("ref_{0}_{1}", mySetpoint.sensor_id, mySetpoint.index), "", t, 0, false); sim_val = mySensors.getCurrentMeasurementDind(myPlant, mySetpoint.sensor_id, s_operator, mySetpoint.index, noisy); } diff_setpoints += mySetpoint.scalefac * Math.Pow(ref_val - sim_val, 2); } } return(diff_setpoints); }
// ------------------------------------------------------------------------------------- // !!! PRIVATE METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// returns a value between 0 and 1. if the pH value is lower or upper /// some constraints, then the value is greater 0, else 0. /// /// TODO: diese methode überdenken /// </summary> /// <param name="mySensors"></param> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <returns></returns> private static double getpHvalue_fitness(biogas.sensors mySensors, biogas.plant myPlant, biooptim.fitness_params myFitnessParams) { double pH_Punishment = 0; double pH_value; int n_digester = myPlant.getNumDigesters(); for (int idigester = 0; idigester < n_digester; idigester++) { string digester_id = myPlant.getDigesterID(idigester + 1); mySensors.getCurrentMeasurementD("pH_" + digester_id + "_3", out pH_value); // punish values bigger than 8 or smaller than 7 // Der Faktor gibt die Steilheit der Strafe an, bei max. 2, dann ist // schon bei 8 bzw. 7 der Ausdruck ( 2.0 .* (pH(ifermenter,1) - 7.5) ) // == 1 // TODO - maybe use tukey function here instead // macht es überhaupt sinn mit optimal values zu arbeiten? // oder einfacher die calcFitnessDigester_min_max() methode nutzen double pH_punish_digester = Math.Min(1 / (10 ^ 4) * ( Math.Pow(1.8 * (pH_value - myFitnessParams.get_param_of("pH_optimum", idigester)), 12)), Math.Abs(pH_value - myFitnessParams.get_param_of("pH_optimum", idigester))); pH_punish_digester = Math.Max(pH_punish_digester, Convert.ToDouble(pH_value < myFitnessParams.get_param_of("pH_min", idigester))); pH_punish_digester = Math.Max(pH_punish_digester, Convert.ToDouble(pH_value > myFitnessParams.get_param_of("pH_max", idigester))); // diese zeile begrenzt pH Strafe zwischen 0 und 1 pH_Punishment = pH_Punishment + Math.Min(pH_punish_digester, 1); } if (n_digester > 0) { pH_Punishment = pH_Punishment / n_digester; } // values between 0 and 1, can be hard constraints return(pH_Punishment); }
/// <summary> /// calc electrical or thermal power needed to heat the digester /// using the heating, in kWh/d /// </summary> /// <param name="Q">substrate feed measured in m^3/d</param> /// <param name="mySubstrates"></param> /// <param name="T_ambient">ambient temperature</param> /// <param name="mySensors"></param> /// <returns>thermal/electrical energy needed by heating in kWh/d</returns> /// <exception cref="exception">Q.Length != mySubstrates.Count</exception> /// <exception cref="exception">efficiency is zero, division by zero</exception> public double calcHeatPower(double[] Q, substrates mySubstrates, physValue T_ambient, sensors mySensors) { // thermal energy balance in kWh/d double balance = calcThermalEnergyBalance(Q, mySubstrates, T_ambient, mySensors); physValue P_loss_kW, P_loss_kWh_d; if (balance < 0) { heating.compensateHeatLoss(new physValue(-balance, "kWh/d"), out P_loss_kW, out P_loss_kWh_d); return(P_loss_kWh_d.Value); } else { return(0); } }
/// <summary> /// type 7 /// /// called in ADMstate_stoichiometry.cs /// /// unterschied zu typ 7 ist, dass Q hier nur aus Substraten besteht und nicht /// aus zusätzlichem sludge, da sludge nicht aufgewärmt werden muss, /// nein ist jetzt ein Typ 7 sensor, sludge wird ignoriert /// </summary> /// <param name="x">not used</param> /// <param name="myPlant"></param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <param name="Q">zufuhr aller substrate in fermenter (nicht in Anlage), /// da heatConsumption_sensor am fermenter angebracht ist</param> /// <param name="par">not used</param> /// <returns>measured values</returns> override protected physValue[] doMeasurement(double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[] Q, params double[] par) { // wegen 4 siehe oben // 1. Ptherm kWh/d for heating substrates // 2. Ptherm kWh/d loss due to radiation // 3. Ptherm kWh/d produced by microbiology // 4. Ptherm kWh/d produced by stirrer dissipation physValue[] values = new physValue[dimension]; // TODO - rufe hier calcThermalEnergyBalance auf von digester_energy.cs // benötigt als weiteren Parameter allerdings mySensors digester myDigester = myPlant.getDigesterByID(id_suffix); // Q ist nur das was in fermenter rein geht // bspw. gäbe es bei einem nicht gefütterten aber beheiztem fermenter // keine substrate welche aufgeheizt werden müssten //myDigester.heatInputSubstrates(Q, mySubstrates, out values[0]); myDigester.calcThermalEnergyBalance(Q, mySubstrates, myPlant.Tout, mySensors, out values[0], out values[1], out values[2], out values[3]); // values[0].Label = "thermal energy to heat substrates"; values[1].Label = "thermal energy loss due to radiation"; values[2].Label = "thermal energy produced by microorganisms"; values[3].Label = "thermal energy dissipated by stirrer"; //physValue P_radiation_loss_kW; // because in heating first power in kW is calculated, it does not // make the code faster to not have power in kW as parameter //myPlant.compensateHeatLossDueToRadiation(id_suffix, // out P_radiation_loss_kW, out values[1]); // return(values); }
/// <summary> /// Returns a vector of volumeflows for the given digester digester_id. /// first elements are the feeds for each substrate followed by the flows /// going over each incoming connection to the digester. /// </summary> /// <param name="t">current simulation time</param> /// <param name="mySubstrates"></param> /// <param name="myPlant"></param> /// <param name="mySensors"></param> /// <param name="substrate_network"></param> /// <param name="plant_network"></param> /// <param name="digester_id"></param> /// <returns></returns> private physValue[] getInputVolumeflowForFermenter(double t, biogas.substrates mySubstrates, biogas.plant myPlant, sensors mySensors, double[,] substrate_network, double[,] plant_network, string digester_id) { // get volumeflow of substrates into fermenter as column vector physValue[] Q_digester = getSubstrateMixFlowForFermenter(t, mySubstrates, myPlant, mySensors, substrate_network, digester_id); // get recirculated volumflow of other fermenters into this fermenter as // column vector physValue[] Q_pumped = getPumpedInputFlowForFermenter(t, mySubstrates, myPlant, mySensors, substrate_network, plant_network, digester_id); // concatenate both vectors vertically return(physValue.concat(Q_digester, Q_pumped)); }
/// <summary> /// Calc u prime for substrate feed of all substrates saved in sensors at the /// current time /// </summary> /// <param name="mySensors"></param> /// <param name="mySubstrates"></param> /// <returns>|| u'(t) ||_2^2, where u is the vector of substrate feeds</returns> private double calcudot(biogas.sensors mySensors, biogas.substrates mySubstrates) { double t2 = mySensors.getCurrentTime(); if (t2 < 0) // no recorded value yet in no sensor { return(0); } double t1 = mySensors.getPreviousTime(); double[] Q1, Q2; // TODO // wenn init substrate feed nicht gegeben ist, dann ist am anfang der simulation // Q2 = Q1 // aktuell berechne ich in MATLAb noch init_substrate feed und addiere das am ende // zu udot hinzu, sollte erstmal ok sein mySensors.getMeasurementsAt("Q", "Q", t1, mySubstrates, out Q1); mySensors.getMeasurementsAt("Q", "Q", t2, mySubstrates, out Q2); // double udot = 0; for (int isubstrate = 0; isubstrate < mySubstrates.getNumSubstrates(); isubstrate++) { double u1 = Q1[isubstrate]; double u2 = Q2[isubstrate]; double udot1 = calcudot(t1, t2, u1, u2); udot += udot1 * udot1; } return(udot); }
/// <summary> /// Returns the ADM params vector, depending on the current substrate feed. /// The current substrate feed is taken out of the current substrate feed /// measurement in mySensors /// /// Attention!!! Changes the values of the ADM params!!! /// /// the following params depend on the substrate feed: /// - XC fractions (fCH_XC, fLI_XC, ...] /// - disintegration constant: kdis /// - hydrolysis constant: khyd_ch, khyd_pr, khyd_li /// </summary> /// <param name="t">current simulation time measured in days</param> /// <param name="mySensors"></param> /// <param name="mySubstrates"></param> /// <param name="substrate_network_digester"></param> /// <param name="digesterID">digester id</param> /// <returns></returns> public double[] getParams(double t, sensors mySensors, substrates mySubstrates, double[] substrate_network_digester, String digesterID /*, * double deltatime*/) { double[] Q; mySensors.getMeasurementsAt("Q", "Q", t, mySubstrates, out Q); // multiply the two vectors with the vector product // two column vector are multiplied therefore a column vector is returned Q = math.times(substrate_network_digester, Q); double QdigesterIn; mySensors.getCurrentMeasurementD(String.Format("Q_{0}_2", digesterID), out QdigesterIn); double[] ADMparams = getParams(t, Q, QdigesterIn, mySubstrates); // , mySensors.isEmpty() // measurement of ADM1 params to sensors mySensors.measure(t, "ADMparams_" + digesterID, ADMparams); return(ADMparams); }
/// <summary> /// ... /// /// type 8 /// </summary> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <param name="mySensors"></param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.plant myPlant, biooptim.fitness_params myFitnessParams, biogas.sensors mySensors, params double[] par) { physValue[] values = new physValue[1]; // this is the fitness of the VS_COD in the digester double VS_COD_degradationRate; double VS_COD_fitness = getVS_COD_fitness(mySensors, out VS_COD_degradationRate); values[0] = new physValue("VS_COD_fitness", VS_COD_fitness, "-"); return(values); }
/// <summary> /// ... /// /// type 8 /// </summary> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <param name="mySensors"></param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.plant myPlant, biooptim.fitness_params myFitnessParams, biogas.sensors mySensors, params double[] par) { physValue[] values = new physValue[1]; // da mit tukey gearbeitet wird, kann der term auch etwas größer als 1 sein double VFA_TAC_fitness = sensors.calcFitnessDigester_min_max(myPlant, mySensors, "VFA_TAC", "min_max", myFitnessParams, "_3", true); values[0] = new physValue("VFA_TAC_fitness", VFA_TAC_fitness, "-"); return(values); }
/// <summary> /// Measure TS content inside a digester /// /// type 7 /// </summary> /// <param name="x">ADM state vector</param> /// <param name="myPlant"></param> /// <param name="mySubstrates">list of substrates</param> /// <param name="mySensors"></param> /// <param name="Q"> /// substrate feed and recirculation sludge going into the digester /// first values are Q for substrates, then pumped sludge going into digester /// dimension: always number of substrates + number of digesters /// </param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[] Q, params double[] par) { // TODO // so erweitern, dass auch TS in Fermenter Input gemessen werden kann // evtl. substrate_sensor nutzen, kann aber auch direkt über mySubstrates gemessen werden physValue[] values = new physValue[1]; // number of substrates int n_substrate = mySubstrates.getNumSubstrates(); if (Q.Length < n_substrate) { throw new exception(String.Format( "Q.Length < n_substrate: {0} < {1}!", Q.Length, n_substrate)); } double[] Qsubstrates = new double[n_substrate]; // volumeflow for substrates for (int isubstrate = 0; isubstrate < n_substrate; isubstrate++) { Qsubstrates[isubstrate] = Q[isubstrate]; } // biogas.substrates substrates_or_sludge; // List <double> Q_s_or_s = new List <double>(); // if no substrate is going into the fermenter we have to take the // TS from the digesters sludge going into this digester to calculate a // sludge. If there is substrate going into the digester we ignore // recirculation sludge, because the COD content inside the digester is // already influenced by the recirculated COD, so a high recirculation leads // to a reduction of the COD content inside the digester and then also to a // TS content reduction. if (math.sum(Qsubstrates) == 0) { substrates_or_sludge = new biogas.substrates(); int ifermenter = 0; // for (int iflux = n_substrate; iflux < Q.Length; iflux++) { string digester_id = myPlant.getDigesterID(ifermenter + 1); double TS_digester = 0; try { mySensors.getCurrentMeasurementD("TS_" + digester_id + "_3", out TS_digester); if (TS_digester < double.Epsilon) // vermutlich wurde noch nicht gemessen? { TS_digester = 11; } } catch // TODO: wann passiert das??, sollte eigentlich nicht passieren { TS_digester = 11; } substrates_or_sludge.addSubstrate( new biogas.sludge(mySubstrates, math.ones(n_substrate), TS_digester)); ifermenter = ifermenter + 1; } // for (int isubstrate = n_substrate; isubstrate < Q.Length; isubstrate++) { Q_s_or_s.Add(Q[isubstrate]); } } else { substrates_or_sludge = mySubstrates; for (int isubstrate = 0; isubstrate < Qsubstrates.Length; isubstrate++) { Q_s_or_s.Add(Qsubstrates[isubstrate]); } } if (id.EndsWith("3")) // out sensor { // calc TS inside digester due to the substrates or the sludge if digester is not fed values[0] = biogas.digester.calcTS(x, substrates_or_sludge, Q_s_or_s.ToArray()); } else if (id.EndsWith("2")) // in sensor { // TODO // ändern, da momentan entweder substrate oder sludge genommen wird und nicht beides // selbe Problem wie bei OLR_sensor denke ich substrates_or_sludge.get_weighted_mean_of(Q_s_or_s.ToArray(), "TS", out values[0]); values[0].Symbol = "TS"; } else { throw new exception(String.Format("id of TS sensor not valid: {0}", id)); } // values[0].Label = "total solids"; return(values); }
static void Main(string[] args) { physValue COD = biogas.chemistry.calcTOC("Xch"); //physValueBounded mol_mass= new physValueBounded("Sac"); //double a= 1; //physValueBounded c= a* mol_mass; fitness_params myparams = new fitness_params("fitness_params_geiger.xml"); myparams.set_params_of("manurebonus", true); //double[] state= new double[37]; //physValueBounded fostac= biogas.ADMstate.calcFOSTACOfADMstate(state); //physValue C; //physValue H; //physValue Hh2= biogas.chemistry.Hh2; //biogas.chemistry.get_CHONS_of("Sac_", out C, out H); //physValue ThODch= biogas.chemistry.calcTheoreticalOxygenDemand("Xch"); //biogas.substrate.createMe(); //double[] a= biogas.ADMstate.getDefaultADMstate(33); //double[] b= biogas.ADMstate.getDefaultADMstate(33); //a[33]= 10; //b[33]= 15; //double[,] c= new double[a.Length, 2]; //for (int ii= 0; ii < a.Length; ii++) //{ // c[ii, 0]= a[ii]; // c[ii, 1]= b[ii]; //} //double[] d= biogas.ADMstate.mixADMstreams(c); //biogas.substrate mySubstrate= new biogas.substrate("manure_cattle_1.xml"); //mySubstrate.print(); //mySubstrate= new biogas.substrate("wheat_silage_3.xml"); //mySubstrate.print(); substrates mySubstrates = new biogas.substrates("substrate_geiger.xml"); mySubstrates.print(); //mySubstrates.saveAsXML("test.xml"); plant myPlant = new biogas.plant("plant_geiger.xml"); //myPlant.saveAsXML("test_plant.xml"); //double[] Q= {1,2,3,4,5,6,7}; //myPlant.myDigesters.get("main").AD_Model.getParams(Q, mySubstrates); digester myDigester = myPlant.myDigesters.get(1); myDigester.calcHeatLossDueToRadiation(new physValue(40, "°C")); double[] Q = { 15, 20, 0, 0, 0, 0, 0, 0, 0, 0 }; double QdigesterIn = 35; double[] ADMparams = myDigester.AD_Model.getParams(0, Q, QdigesterIn, mySubstrates); //double[] x= biogas.ADMstate.getDefaultADMstate(); // double[] x= { 0.00890042014469482, //0.00393026902393572, //0.0898975418574202, //0.0105303688871217, //0.0188083338344299, //0.0891872294599488, //4.06518710070508, //1.48197701713060e-06, //0.0463395911897687, //0.0137825768581825, //0.231507824368968, //3.42633334388719, //8.90655977614532, //0.129429796425159, //0.0457772931408461, //0.0271093313710146, //3.81638964565446, //0.988123176978798, //0.410214653254316, //0.633820130695941, //0.537171143510719, //1.81695029971744, //0.992459517922010, //8.07171990003056, //19.9716874568380, //0.000997039324889569, //0.0155655622013352, //0.0104958374141159, //0.0187566838733815, //0.0889172491178123, //4.05462904506225, //0.152623707143880, //0.00590111878589172, //8.13313521280990e-05, //0.465153466604388, //0.506049932143382, //0.971284730099898 ////8.05130545875131e-05, ////0.460473427139200, ////0.500958421870263, ////0.961512362064050 // }; double[] x = { 0.00719913836418267, 0.00322799288019040, 0.0546025712168657, 0.0115793536955362, 0.0229074890746752, 0.0600482037552511, 0.463364086785563, 7.72721521995682e-08, 0.0469910866922296, 0.0479055807768373, 0.154317294840363, -1.35975859128971e-47, 21.5301936374036, 0.750446550594154, 0.161739337648583, 0.0603411370260300, 4.96860324952978, 0.832215069627251, 0.222662822346057, 0.629071151058018, 0.547835241984119, 2.00168204821595, 1.05831074978528, 29.5159593610614, 1.79228974953266, 1.27233772322647e-52, 1.77592375955463e-55, 0.0115411203828431, 0.0228383440861995, 0.0598411508360539, 0.462166407598810, 0.2406363011183078, 0.00374878408176311, 4.31650348109949e-06, 0.476514182736779, 0.487815404290303, 0.964333903530564 }; //biogas.sensors mySensors= // new biogas.sensors(new biogas.VFA_TAC_sensor("1")); //physValue VFA_TAC= mySensors.getCurrentMeasurement("VFA_TAC_1", 2); //VFA_TAC= mySensors.measure(0, "VFA_TAC_1", 1, x); //VFA_TAC= mySensors.measure(2, "VFA_TAC_1", 1, x); biogas.sensors mySensors = new biogas.sensors(new biogas.TS_sensor("postdigester_3")); biogas.sensor_array mySensorArray = new biogas.sensor_array("Q"); for (int isubstrate = 0; isubstrate < mySubstrates.getNumSubstrates(); isubstrate++) { mySensorArray.addSensor(new biogas.Q_sensor(mySubstrates.getID(isubstrate + 1))); } mySensorArray.addSensor(new biogas.Q_sensor("postdigester_digester")); mySensors.addSensorArray(mySensorArray); mySensors.addSensor(new biogas.substrate_sensor("cost")); mySensors.addSensor(new biogas.pumpEnergy_sensor("postdigester_digester")); mySensors.addSensor(new biogas.total_biogas_sensor("", myPlant)); //physValue VFA_TACs= mySensors.measure(0, "VS_1", 1, new double[]{1,2}, mySubstrates); double[,] substrate_network = { { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 0 } }; double[,] plant_network = { { 0, 1, 0 }, { 1, 0, 1 } }; double TS; //mySensors.measure(0, "TS_digester_3", 1, x, // myPlant, mySubstrates, mySensors, // substrate_network, plant_network, "digester", out TS); mySensors.measure(0, "substrate_cost", 0, mySubstrates, out TS); mySensors.measure(1, "substrate_cost", 0, mySubstrates, out TS); mySensors.measure(2, "substrate_cost", 0, mySubstrates, out TS); double energy; double[] pump = { 5 }; mySensors.measure(0, "pumpEnergy_postdigester_digester", 0, myPlant, 10, pump, out energy); double[] data; mySensors.getMeasurementStream("substrate_cost", out data); double[] u = new double[6]; u[0] = 10; u[1] = 100; u[2] = 100; u[3] = 20; u[4] = 200; u[5] = 200; mySensors.measureVec(0, "total_biogas_", 0, myPlant, u, "threshold", 5); physValue Qgas_h2; physValue Qgas_ch4; physValue Qgas_co2; physValue T = new physValue("T", 41.4, "°C"); biogas.ADMstate.calcBiogasOfADMstate(x, new physValue(3000, "m^3"), T, out Qgas_h2, out Qgas_ch4, out Qgas_co2); double ph = biogas.ADMstate.calcPHOfADMstate(x); substrate mySubstrate = mySubstrates.get("swinemanure"); biogas.ADMstate.calcADMstream(mySubstrate, 30); double mypH = mySubstrate.get_param_of("pH"); }
/// <summary> /// ... /// /// type 8 /// </summary> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <param name="mySensors"></param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.plant myPlant, biooptim.fitness_params myFitnessParams, biogas.sensors mySensors, params double[] par) { physValue[] values = new physValue[1]; // Grenzwerte, welche ich mal auf einer Konferenz (vermutlich VDI Tagung) aufgeschnappt habe // // TAC < 50 mmol/l gefährlich // 50 < TAC < 100 mmol/l gering Warnung // 100 < TAC < 250 mmol/l OK // da mit tukey gearbeitet wird, kann der term auch etwas größer als 1 sein double TAC_fitness = sensors.calcFitnessDigester_min_max(myPlant, mySensors, "TAC", "min", myFitnessParams, "_3", true); values[0] = new physValue("TAC_fitness", TAC_fitness, "-"); return(values); }
/// <summary> /// Calculates the thermal energy balance of the digester. It compares /// thermal sinks (negative) with thermal sources (positive) inside the digester /// /// At the moment the following processes are reflected: /// /// thermal sinks are: /// /// 1) heat energy needed to heat the substrates up to the digesters temperature /// (heat substrates) /// 2) heat energy loss due to radiation through the surface of the fermenter /// (radiation) /// /// thermal sources are: /// /// 1) microbiology /// 2) stirrer dissipation /// /// For further effects see /// /// 1) Lübken, M., Wichern, M., Schlattmann, M., Gronauer, A., and Horn, H.: /// Modelling the energy balance of an anaerobic digester fed with cattle manure /// and renewable energy crops, Water Research 41, pp. 4085-4096, 2007 /// 2) Lindorfer, H., Kirchmayr, R., Braun, R.: /// Self-heating of anaerobic digesters using energy crops, 2005 /// /// /// </summary> /// <param name="Q">substrate feed measured in m^3/d</param> /// <param name="mySubstrates"></param> /// <param name="T_ambient">ambient temperature</param> /// <param name="mySensors"></param> /// <param name="Psubsheat">thermal energy needed to heat substrates in kWh/d</param> /// <param name="Pradloss">thermal energy loss due to radiation in kWh/d</param> /// <param name="Pmicros">Wärme produziert durch Bakterien in kWh/d</param> /// <param name="Pstirdiss">thermal energy created by stirrer in kWh/d</param> /// <returns>thermal energy balance mesasured in kWh/d</returns> /// <exception cref="exception">Q.Length < mySubstrates.Count</exception> /// <exception cref="exception">energy calculations failed</exception> public double calcThermalEnergyBalance(double[] Q, substrates mySubstrates, physValue T_ambient, sensors mySensors, out physValue Psubsheat, out physValue Pradloss, out physValue Pmicros, out physValue Pstirdiss) { //physValue Pel_kWh_d; //physValue Pel_kW; // thermal energy needed to heat substrates in kWh/d try { Psubsheat = mySubstrates.calcSumQuantityOfHeatPerDay(Q, T).convertUnit("kWh/d"); } catch (exception e) { Console.WriteLine(e.Message); throw new exception("calcThermalEnergyBalance: heat substrates failed!"); } //physValue P_radiation_loss_kW; //physValue P_radiation_loss_kWh_d; // energy needed to compensate loss due to radiation //compensateHeatLossDueToRadiation(T_ambient, out P_radiation_loss_kW, out P_radiation_loss_kWh_d); // thermal energy loss due to radiation in kWh/d try { Pradloss = calcHeatLossDueToRadiation(T_ambient).convertUnit("kWh/d"); } catch (exception e) { Console.WriteLine(e.Message); throw new exception("calcThermalEnergyBalance: heat loss calculation failed!"); } // Wärme produziert durch Bakterien // in kWh/d try { Pmicros = mySensors.getCurrentMeasurement("energyProdMicro_" + id); // wenn noch nichts aufgezeichnet wurde, dann ist der Wert 0 if (Pmicros.Value != 0) { Pmicros = Pmicros.convertUnit("kWh/d"); } else // setzte zu 0 kWh/d, da einheit sonst nicht stimmt { Pmicros = new physValue(0, "kWh/d"); } } catch (exception e) { Console.WriteLine(e.Message); throw new exception("calcThermalEnergyBalance: microorganisms failed!"); } // Wärme welche das Rühwerk erzeugt ist eine Wärmequelle, muss hier addiert werden // In Ganzheitliche stoffliche und energetische Modellierung S. 65 // Dissipation Rührwerk - ist identisch mit der aufgebrachten Leistung des // rührwerks. dafür muss erstmal rührwerksleistung berechnet werden, s. ebenfalls // In Ganzheitliche stoffliche und energetische Modellierung S. 45 ff. // für rührwerksleitung muss auch viskosität berechnet werden s. S. 81 für verschiedene // TS im fermenter // thermal energy created by stirrer in kWh/d try { Pstirdiss = calcStirrerDissipation(mySensors).convertUnit("kWh/d"); } catch (exception e) { Console.WriteLine(e.Message); throw new exception("calcThermalEnergyBalance: stirrer dissipation failed!"); } // sinks are negative, themal sources are positive physValue balance = -Psubsheat - Pradloss + Pstirdiss + Pmicros; // + produzierteWärme im Fermenter return(balance.Value); }
/// <summary> /// ... /// /// type 8 /// </summary> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <param name="mySensors"></param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.plant myPlant, biooptim.fitness_params myFitnessParams, biogas.sensors mySensors, params double[] par) { physValue[] values = new physValue[1]; // // <param name="biogasExcess_fitness">lost money due to biogas excess [1000 €/d]</param> // <param name="biogasExcess">in excess produced biogas [m^3/d]</param> // <param name="lossBiogasExcess">lost money due to biogas excess [€/d]</param> physValue[] biogas_v = mySensors.getCurrentMeasurementVector("total_biogas_"); // // excess biogas in [m^3/d] double biogasExcess;// = biogas_v[biogas_v.Length - 1].Value; // // Calculation of costs of substrate inflow // € / d double substrate_costs; mySensors.getCurrentMeasurementD("substrate_cost", out substrate_costs); // loss of excess methane prod. in €/d // if there is a loss, then positive value // if there is a gain, then negative value, should not be the case double lossBiogasExcess = calcLossDueToBiogasExcess(biogas_v, substrate_costs, myPlant, myFitnessParams, out biogasExcess); // tausend € / d double biogasExcess_fitness = lossBiogasExcess / 1000; // values[0] = new physValue("biogasExcess_fitness", biogasExcess_fitness, "-"); return(values); }
/// <summary> /// ... /// /// type 8 /// </summary> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <param name="mySensors"></param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.plant myPlant, biooptim.fitness_params myFitnessParams, biogas.sensors mySensors, params double[] par) { physValue[] values = new physValue[1]; // TODO - was muss ich hier machen, damit sensor daten noisy aufzeichnet??? bool noisy = false; // calc setpoint control error - errors of setpoint controls double diff_setpoints = calc_setpoint_errors(mySensors, myPlant, myFitnessParams, noisy); // wende tukey biweight an diff_setpoints = math.tukeybiweight(diff_setpoints); values[0] = new physValue("diff_setpoints", diff_setpoints, "-"); return(values); }
/// <summary> /// TODO - die ganze funktion ist noch nicht ausgegoren /// /// write measured variable at time t in given mySubstrate /// /// </summary> /// <param name="t">current simulation time</param> /// <param name="mySensors"></param> /// <param name="mySubstrate">changed in this call</param> public static void set_substrate_params_from_sensor(double t, biogas.sensors mySensors, biogas.substrate mySubstrate) { string substrate_id = mySubstrate.id; substrateparams_sensor substrate_param_sensor = (substrateparams_sensor)mySensors.get("substrateparams_" + substrate_id); if (!substrate_param_sensor.isEmpty()) { physValue[] measVec = substrate_param_sensor.getMeasurementVectorAt(t); // hier werden nur TS und VS geändert mySubstrate.set_params_of("TS", measVec[0], // umrechung in % TS wird in c# bib gemacht (substrateparams_sensor) // s.unten in doMeasurement methode // this value measVec[1] is measured in % TS, this OK "VS", measVec[1], "pH", measVec[2] //, /*"Sac", measVec[3], * // umrechnung von g/l in mmol/l * // g/l / 50 g/mol * 1000 m = 1000 / 50 mmol/l * // TODO convertTo nutzen * "TAC", measVec[4] * 1000 / 50, */ /*"Snh4", measVec[5]*/); //'COD', measVec.Get(6)); //mySubstrate.calc //if (mySubstrate.ismanure) //{ // mySubstrate.set_params_of("RF", measVec[6]); // // TODO outcomment // // measured in gCOD/l // //mySubstrate.set_params_of("COD", measVec[7]); // //mySubstrate.set_params_of("COD_S", 0.3 * measVec[7].Value); //} //else //{ // // TODO - delete // //if (substrate_id == "bullmanure") // // throw new exception("AAAAAAAAAAAA"); // mySubstrate.set_params_of("RL", measVec[6], "RP", measVec[7], // "RF", measVec[8]); // // measured in kgCOD/m^3, same as gCOD/l // //physValue COD_c= biogas.substrate.calcXc(mySubstrate);//.convertUnit("gCOD/l"); // //// TODO // //// 0.3 // //double COD_c2 = 0; // //// TODO delete // //if (substrate_id == "maize") // für geiger kalibrierung // // COD_c2= COD_c.Value * 1.1;//5; // //else // // COD_c2= COD_c.Value; // //mySubstrate.set_params_of("COD", COD_c2); // //mySubstrate.set_params_of("COD_S", 0.3 * COD_c2); //} } }
/// <summary> /// Calculates the thermal energy balance of the digester. It compares /// thermal sinks (negative) with thermal sources (positive) inside the digester /// thermal sinks are: /// - heat substrates /// - radiation /// thermal sources are: /// - microbiology /// - stirrer dissipation /// </summary> /// <param name="digester_id">digester id</param> /// <param name="Q">substrate feed measured in m^3/d</param> /// <param name="mySubstrates"></param> /// <param name="mySensors"></param> /// <returns>thermal energy balance mesasured in kWh/d</returns> public double calcThermalEnergyBalance(string digester_id, double[] Q, substrates mySubstrates, sensors mySensors) { return(myDigesters.calcThermalEnergyBalance(digester_id, Q, mySubstrates, Tout, mySensors)); }
/// <summary> /// ... /// /// type 8 /// </summary> /// <param name="myPlant"></param> /// <param name="myFitnessParams"></param> /// <param name="mySensors"></param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.plant myPlant, biooptim.fitness_params myFitnessParams, biogas.sensors mySensors, params double[] par) { physValue[] values = new physValue[1]; // this is the fitness of the CH4 in the digester // da mit tukey gearbeitet wird, kann der term auch etwas größer als 1 sein double CH4_fitness; getBiogas_fitness(mySensors, out CH4_fitness); values[0] = new physValue("CH4_fitness", CH4_fitness, "-"); return(values); }