// ------------------------------------------------------------------------------------- // !!! CONSTRUCTOR METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// constructor /// /// creates a new sludge object, which has parameters the same as the mean /// substrate feed (Rf, RP, RL, ADL, VS), the given TS content and /// a density of 1000 kg/m³ /// /// TS measured in % FM /// /// sludge is created out of the weighted mean of the given substrates, /// which should be the substrates fed on the plant. /// /// </summary> /// <param name="mySubstrates">list of substrates</param> /// <param name="Q">must be measured in m³/d</param> /// <param name="TS">must be measured in % FM</param> public sludge(substrates mySubstrates, double[] Q, double TS) : base() { physValue RF; physValue RP; physValue RL; physValue ADL; physValue VS; try { mySubstrates.get_weighted_mean_of(Q, "RF", out RF); mySubstrates.get_weighted_mean_of(Q, "RP", out RP); mySubstrates.get_weighted_mean_of(Q, "RL", out RL); mySubstrates.get_weighted_mean_of(Q, "ADL", out ADL); mySubstrates.get_weighted_mean_of(Q, "VS", out VS); set_params_of("RF", RF.Value, "RP", RP.Value, "RL", RL.Value, "ADL", ADL.Value, "VS", VS.Value); set_params_of("TS", TS); } catch (exception e) { Console.WriteLine(e.Message); // TODO - maybe do something LogError.Log_Err("sludge constructor1", e); } // TODO: could calculate rho here instead of taking 1000 kg/m^3 //set_params_of("rho", new physValue(1000, "kg/m^3")); }
/// <summary> /// Calculates the organic loading rate of the digester. /// Assumption that Q and Qsum is measured in m^3/d. /// /// As a reference see: /// /// Handreichung Biogasgewinnung und -nutzung: Grundlagen der anaeroben /// Fermentation, S. 29. /// /// </summary> /// <param name="x">digester state vector</param> /// <param name="mySubstrates"> /// substrates or sludge but not both!!! Because TS content is calculated /// out of COD in digester. If there is a mixture of substrate and sludge going /// into the digester, then the COD is aleardy decreased by the sludge, so no need /// to account for the lower TS of the sludge here. /// TODO: hier sollten es beide sein! /// </param> /// <param name="Q"> /// Q of the substrates or sludge, but not both! /// TODO: hier sollten es beide sein! /// </param> /// <param name="Qsum"> /// if there is sludge and substrate going into the digester, then Qsum will be /// bigger then math.sum(Q). Qsum is needed to account for full volumeflow /// going into the digester no matter if substrate or sludge. /// TODO: nach Änderung, dass Q alle feed enthält, ist Qsum= sum(Q) /// kann also als Parameter entfernt werden /// </param> /// <returns></returns> public physValue calcOLR(double[] x, substrates mySubstrates, double[] Q, double Qsum) { physValue Vliq = this.Vliq; physValue TS; // TODO // was mache ich hier!!!! ??????????????????? // warum nehme ich nicht einfach VS aus den Substraten??? physValue VS;//= calcVS(x, mySubstrates, Q, out TS); mySubstrates.get_weighted_mean_of(Q, "VS", out VS); mySubstrates.get_weighted_mean_of(Q, "TS", out TS); VS = biogas.substrate.convertFrom_TS_To_FM(VS, TS); VS = VS.convertUnit("100 %"); physValue rho; mySubstrates.get_weighted_mean_of(Q, "rho", out rho); // hier wird die Annahme gemacht, dass rho von schlamm gleich ist // wie rho der substrate, aber egal (TODO) // evtl. wäre es besser gemessenes rho aus density_sensor zu nehmen // das ist dichte des inputs in den fermenter (Gemisch aus substrate und rezischlamm) physValue OLR = new physValue(Qsum, "m^3/d") * rho * VS / Vliq; OLR.Symbol = "OLR"; return(OLR); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Calculates TS content inside the digester. Therefore we calculate the COD /// inside the digester using the state vector x. Furthermore we calculate out /// of the substrates going into the digester the ash and TS content. If /// no substrate is going into the digester we take the sludge and pass it to /// this method. But never mix substrates and sludge!!! /// /// Basic formula is: /// /// Xc,digester= rho_substrate * TS_digester [100 % FM] * /// VS_digester [% TS] / VS_substrate [% TS] * ( ... ) /// /// /// !!!!!!!!!!!!!!!!!!!! ATTENTION !!!!!!!!!!!!!!!!!!!!! /// this function only calculates the total solids in steady state!!! /// because the ash content in the digester is not taken into account, just the ash of the substrate, which is only /// a small part of volume compared with ash in digester. in steady state we assume that ash content in digester is /// the same as the one of the substrate, but in dynamic simulation this is not true at all /// /// As a reference see: /// /// Koch, K., Lübken, M., Gehring, T., Wichern, M., and Horn, H.: /// Biogas from grass silage – Measurements and modeling with ADM1, /// Bioresource Technology 101, pp. 8158-8165, 2010. /// /// </summary> /// <param name="x">digester state vector</param> /// <param name="mySubstrates"> /// substrates or sludge but not both!!! Because TS content is calculated /// out of COD in digester. If there is a mixture of substrate and sludge going /// into the digester, then the COD is already decreased by the sludge, so no need /// to account for the lower TS of the sludge here. /// </param> /// <param name="Q"> /// array of substrate mix stream or recirculations in m³/d /// wie soll das mit recirculations funktionieren? /// Q welches an get_weighted_mean_of übergeben wird, muss mindestens /// anzahl der substrate beinhalten. das funktioniert, weil /// mySubstrates einmal eine liste von schlamm ist, und einmal /// eine liste der substrate /// </param> /// <returns>TS in % FM</returns> /// <exception cref="exception">Q.Length < mySubstrates.Count</exception> /// <exception cref="exception">TS value out of bounds</exception> public static physValue calcTS(double[] x, substrates mySubstrates, double[] Q) { physValue RF; physValue RP; physValue RL; physValue ADL; physValue VS; physValue rho; physValue ash; //physValue TS_substrate; mySubstrates.get_weighted_mean_of(Q, "RF", out RF); mySubstrates.get_weighted_mean_of(Q, "RP", out RP); mySubstrates.get_weighted_mean_of(Q, "RL", out RL); mySubstrates.get_weighted_mean_of(Q, "ADL", out ADL); mySubstrates.get_weighted_mean_of(Q, "VS", out VS); mySubstrates.get_weighted_mean_of(Q, "rho", out rho); mySubstrates.get_weighted_mean_of(Q, "Ash", out ash); //mySubstrates.get_weighted_mean_of(Q, "TS", out TS_substrate); // particulate COD inside the digester physValue COD = biogas.ADMstate.calcVSOfADMstate(x, "kgCOD/m^3"); VS = VS.convertUnit("% TS"); ash = ash.convertUnit("% FM"); // ash of the substrates physValue TS; // we assume that the COD inside the digester is composed as is the substrate mix TS = biogas.substrate.calcTS(RF, RP, RL, ADL, VS, COD, rho);//.convertUnit("100 %"); //VS= biogas.substrate.convertFrom_TS_To_FM(VS, TS_substrate); VS = VS.convertUnit("100 %"); // VS of substrate mix, weiterhin gemessen in 100 % TS // TS_digester [% FM] * VS_substrates [100 % TS] + ash_substrate [% FM] // we know that the ash content of the substrates does not change inisde the digester // TODO: explain a bit better // wenn COD oben 0 wäre, dann wäre TS ebenfalls 0, durch die nächste Zeile // wird TS auf ash content angehoben. TS = TS * VS + ash; TS.Symbol = "TS"; return(TS); }
/// <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); }
/// <summary> /// Calculates Volatile Solids inside the digester. /// We make the assumption, that the ash content inside the digester /// is equal to the ash content of the mean of the substrates going into it. /// /// As the TS content is decreasing in a digester cascade, and the ash /// content keeps constant the VS content will decrease as well. /// </summary> /// <param name="x">digester state vector</param> /// <param name="mySubstrates"> /// substrates or sludge but not both!!! Because TS content is calculated /// out of COD in digester. If there is a mixture of substrate and sludge going /// into the digester, then the COD is already decreased by the sludge, so no need /// to account for the lower TS of the sludge here. /// </param> /// <param name="Q">array of substrate mix stream or recirculations in m³/d</param> /// <param name="TS">returns TS content as well</param> /// <returns></returns> public static physValue calcVS(double[] x, substrates mySubstrates, double[] Q, out physValue TS) { physValue ash; try { TS = calcTS(x, mySubstrates, Q); mySubstrates.get_weighted_mean_of(Q, "Ash", out ash); } catch (exception e) { Console.WriteLine(e.Message); TS = new physValue("TS", 0, "% FM"); return(new physValue("VS", 0, "% TS")); } if (TS.Value == 0) { return(new physValue("VS", 0, "% TS")); } // ash [% FM] := ( 1 - VS [100 % TS] ) * TS [% FM] // // <=> // // VS [100 % TS] = 1 - ( ash [% FM] )/( TS [% FM] ) // physValue VS = new physValue(1, "100 %") - ash / TS; VS = VS.convertUnit("% TS"); VS.Symbol = "VS"; return(VS); }