/// <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> /// 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")); }
// ------------------------------------------------------------------------------------- // !!! 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> /// ... /// /// 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> /// 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> /// 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); }
/// <summary> /// Measure OLR of a digester /// /// type 7 /// /// TODO: größtenteils identisch mit TS_sensor, man könnte was zusammen legen /// </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 fermenter in m³/d /// first values are Q for substrates, then pumped sludge going into digester /// dimension: always number of substrates + number of digesters /// </param> /// <param name="par"></param> /// <returns></returns> override protected physValue[] doMeasurement(double[] x, biogas.plant myPlant, biogas.substrates mySubstrates, biogas.sensors mySensors, double[] Q, params double[] par) { physValue[] values = new physValue[1]; // TODO: so abändern, dass die aufgerufene methode calcOLR immer // feed und sludge übergeben werden. nicht oder // number of substrates int n_substrate = mySubstrates.getNumSubstrates(); double Qsum = math.sum(Q); 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, VS_digester = 0; try { mySensors.getCurrentMeasurementD("TS_" + digester_id + "_3", out TS_digester); mySensors.getCurrentMeasurementD("VS_" + digester_id + "_3", out VS_digester); // TODO - warum 4, wenn Fermenter abgestürzt ist, dann ist TS < 2 // was zu doofen Fehlermeldungen führt mit calcNfE und boundNDF, wenn man // VS unten in biogas.sludge setzt. deshalb hier abfrage if (TS_digester < 4 /*double.Epsilon*/) { TS_digester = 11; } // TODO - herausfinden warum 15 if (VS_digester < 20 /*double.Epsilon*/) { VS_digester = 85; // 85 % TS } } catch { TS_digester = 11; VS_digester = 85; } try { substrates_or_sludge.addSubstrate( new biogas.sludge(mySubstrates, math.ones(n_substrate), TS_digester, VS_digester)); } catch (exception e) { LogError.Log_Err(String.Format("OLR_sensor.doMeasurement, VS: {0}, TS: {1}", VS_digester, TS_digester), e); throw (e); } 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]); } } // digester myDigester = myPlant.getDigesterByID(id_suffix); try { values[0] = myDigester.calcOLR(x, substrates_or_sludge, Q_s_or_s.ToArray(), Qsum); } catch (exception e) { LogError.Log_Err("OLR_sensor.doMeasurement2", e); throw (e); } // return(values); }