// ------------------------------------------------------------------------------------- // !!! PRIVATE METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Adds a new measurement, taken at time t, to the lists. /// </summary> /// <param name="t">simulation time in days</param> /// <param name="value">an array of physical values measured at that time</param> private void addMeasurement(double t, physValue[] value) { double last_t = 0; if (time.Count > 0) { last_t = time[time.Count - 1]; } physValue[] last_signals; if (values.Count > 0) { last_signals = values[values.Count - 1]; } else // init with 0 elements, needed below in if of getNoisyMeasurement { last_signals = new physValue[0]; } values.Add(value); // just add value to list // add noisy value to list values_noise.Add(sensor_config.getNoisyMeasurement(t, last_t, value, last_signals, myConfigs)); time.Add(t); // add current time to time vector }
// diese überladung dürfte nicht benötigt werden, da in sensor dies überladung existiert // ruft die methode unten für noisy= false auf ///// <summary> ///// Get value at time t of measured variable with the id: param ///// </summary> ///// <param name="param">id of the variable, the correct values of the ///// ids are defined inside this function ///// ids are: H2_%, CH4_%, CO2_%, and biogas_m3_d</param> ///// <param name="t">some simulation time [days]</param> ///// <returns>measured values at time t for given param</returns> ///// <exception cref="exception">Unknown param</exception> //override public physValue getMeasurementAt(string param, double t) //{ // return getMeasurementAt(param, t, false); //} /// <summary> /// Get value at time t of measured variable with the id: param /// </summary> /// <param name="param">id of the variable, the correct values of the /// ids are defined inside this function /// ids are: H2_%, CH4_%, CO2_%, and biogas_m3_d</param> /// <param name="t">some simulation time [days]</param> /// <param name="noisy">if true, then noisy measurement values are returned. /// they are only noisy if the parameter apply_real_sensor was true before and during /// the simulation</param> /// <returns>measured values at time t for given param</returns> /// <exception cref="exception">Unknown param</exception> override public physValue getMeasurementAt(string param, double t, bool noisy) { switch (param) { // TODO : überarbeiten!!! case "H2_%": return(getMeasurementAt((int)BioGas.n_gases + BioGas.pos_h2 - 1, t, noisy)); case "CH4_%": return(getMeasurementAt((int)BioGas.n_gases + BioGas.pos_ch4 - 1, t, noisy)); case "CO2_%": return(getMeasurementAt((int)BioGas.n_gases + BioGas.pos_co2 - 1, t, noisy)); case "biogas_m3_d": physValue[] values = getMeasurementVectorAt(t, noisy); physValue myValue = values[0]; for (int igas = 0; igas < (int)BioGas.n_gases; igas++) { myValue = myValue + values[igas]; } myValue.Symbol = "biogas"; return(myValue); default: throw new exception(String.Format("biogas_sensor: Unknown param: {0}!", param)); } }
/// <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> /// Calculates the extended buswell equation for the given molecule. /// Calculates CH4, CO2, NH3 and H2S in "mol gas / mol molecule" /// </summary> /// <param name="c">mol C / mol molecule</param> /// <param name="h">mol H / mol molecule</param> /// <param name="o">mol O / mol molecule</param> /// <param name="n">mol N / mol molecule</param> /// <param name="s">mol S / mol molecule</param> /// <param name="ch4">CH4 in "mol gas / mol molecule"</param> /// <param name="co2">CO2 in "mol gas / mol molecule"</param> /// <param name="nh3">NH3 in "mol gas / mol molecule"</param> /// <param name="h2s">H2S in "mol gas / mol molecule"</param> public static void buswell_extended(physValue c, physValue h, physValue o, physValue n, physValue s, out physValue ch4, out physValue co2, out physValue nh3, out physValue h2s) { ch4 = c / 2 + h / 8 - o / 4 - 3 * n / 8 - s / 4; ch4.Symbol = "ch4"; ch4.Label = String.Format("mol CH4 / mol C{0}H{1}O{2}N{3}S{4} (fermentation)", c.Value, h.Value, o.Value, n.Value, s.Value); co2 = c / 2 - h / 8 + o / 4 + 3 * n / 8 + s / 4; co2.Symbol = "co2"; co2.Label = String.Format("mol CO2 / mol C{0}H{1}O{2}N{3}S{4} (fermentation)", c.Value, h.Value, o.Value, n.Value, s.Value); nh3 = n; nh3.Symbol = "nh3"; nh3.Label = String.Format("mol NH3 / mol C{0}H{1}O{2}N{3}S{4} (fermentation)", c.Value, h.Value, o.Value, n.Value, s.Value); h2s = s; h2s.Symbol = "h2s"; h2s.Label = String.Format("mol H2S / mol C{0}H{1}O{2}N{3}S{4} (fermentation)", c.Value, h.Value, o.Value, n.Value, s.Value); }
/// <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); }
/// <summary> /// Returns C, H, O and N content of molecule. mol / molecule /// </summary> /// <param name="molecule">string of molecule</param> /// <param name="C">mol C / mol molecule</param> /// <param name="H">mol H / mol molecule</param> /// <param name="O">mol O / mol molecule</param> /// <param name="N">mol N / mol molecule</param> /// <exception cref="exception">Unknown molecule</exception> public static void get_CHON_of(string molecule, out physValue C, out physValue H, out physValue O, out physValue N) { physValue S; get_CHONS_of(molecule, out C, out H, out O, out N, out S); }
/// <summary> /// Set two physValues to the given value. Only the Value of the given values is /// set, so you must make sure that the unit and the rest is ok /// </summary> /// <param name="sym1">1st parameter to be set</param> /// <param name="val1">value of 1st parameter to be set</param> /// <param name="sym2">2nd parameter to be set</param> /// <param name="val2">value of 2nd parameter to be set</param> /// <exception cref="exception">Unknown parameter</exception> public void set_params_of(string sym1, physValue val1, string sym2, physValue val2) { object[] values = { sym1, val1.Value, sym2, val2.Value }; this.set_params_of(values); }
/// <summary> /// Calculates energy flow needed to get the thermal power pP_loss /// from the heating. /// </summary> /// <param name="pP_loss">thermal power loss</param> /// <param name="P_loss_kW">thermal or electrical power</param> /// <param name="P_loss_kWh_d">thermal or electrical energy per day</param> /// <exception cref="exception">efficiency is zero, division by zero</exception> /// <exception cref="exception">heating failed</exception> public void compensateHeatLoss(physValue pP_loss, out physValue P_loss_kW, out physValue P_loss_kWh_d) { if (eta == 0) { throw new exception("electrical/thermal degree of efficiency of the heating: eta == 0"); } try { if (status) { physValue P_loss = pP_loss / eta; P_loss.Symbol = "Ploss"; P_loss_kW = P_loss.convertUnit("kW"); } else { P_loss_kW = new physValue("Ploss", 0, "kW"); } P_loss_kWh_d = P_loss_kW.convertUnit("kWh/d"); } catch (exception e) { Console.WriteLine(e.Message); throw new exception("compensateHeatLoss: heating failed!"); } }
// ------------------------------------------------------------------------------------- // !!! PROTECTED METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Calculates faecal bacteria removal capacity of the digester, measured in % /// /// type 0 aktuell type 1 /// </summary> /// <param name="x">ADM state vector</param> /// <param name="par">HRT of the digester in m^3, temperature inside the digester in °C</param> /// <returns>measured faecal bacteria removal capacity in %</returns> override protected physValue[] doMeasurement(double[] x, params double[] par) { physValue[] values = new physValue[dimension]; if (par.Length != 2) { throw new exception(String.Format( "Length of params is != 2: {0}!", par.Length)); } double HRT = par[0]; double T = par[1]; if (HRT == 0) { throw new exception("HRT == 0"); } // intestinal enterococci values[0] = new physValue("eta_IE", 98.29 - 2.2 * Math.Pow(1 / HRT, 2) + 0.031 * T, "%"); // faecal coliforms values[1] = new physValue("eta_FC", 98.29 - 1.0 * Math.Pow(1 / HRT, 2) + 0.031 * T, "%"); return(values); }
// ------------------------------------------------------------------------------------- // !!! CONSTRUCTOR METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Standard Constructor sets default values of ADM parameters /// </summary> /// <param name="T">digester temperature in °C</param> public ADM(physValue T) { T = T.convertUnit("°C"); // set default parameter values setDefaultParams(T.Value); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- ///// <summary> ///// Calculates energy flow needed to heat the substrate flows Q ///// up to the temperature Tend. The volumeflows Q are assumed to ///// be measured in m^3/d ///// </summary> ///// <param name="Q">array of volumeflows of the substrates in [m³/d]</param> ///// <param name="mySubstrates"></param> ///// <param name="Tend"></param> ///// <param name="Pel_kWh_d">thermal or electrical energy per day</param> ///// <param name="Pel_kW">thermal or electrical power</param> ///// <exception cref="exception">Q.Length != mySubstrates.Count</exception> ///// <exception cref="exception">efficiency is zero, division by zero</exception> //public void heatSubstrates(double[] Q, substrates mySubstrates, // physValue Tend, out physValue Pel_kWh_d, // out physValue Pel_kW) //{ // physValue[] pQ= new physValue[Q.Length]; // for (int iel= 0; iel < Q.Length; iel++) // pQ[iel]= new physValue("Q", Q[iel], "m^3/d"); // heatSubstrates(pQ, mySubstrates, Tend, out Pel_kWh_d, out Pel_kW); //} ///// <summary> ///// Calculates energy flow needed to heat the substrate flows Q ///// up to the temperature Tend. The volumeflows Q are assumed to ///// be measured in m^3/d ///// </summary> ///// <param name="Q">array of volumeflows of the substrates in [m³/d]</param> ///// <param name="mySubstrates"></param> ///// <param name="Tend"></param> ///// <param name="Pel_kWh_d">thermal or electrical energy per day</param> ///// <exception cref="exception">Q.Length != mySubstrates.Count</exception> ///// <exception cref="exception">efficiency is zero, division by zero</exception> //public void heatSubstrates(double[] Q, substrates mySubstrates, // physValue Tend, out physValue Pel_kWh_d) //{ // physValue[] pQ = new physValue[Q.Length]; // for (int iel = 0; iel < Q.Length; iel++) // pQ[iel] = new physValue("Q", Q[iel], "m^3/d"); // heatSubstrates(pQ, mySubstrates, Tend, out Pel_kWh_d); //} ///// <summary> ///// Calculates energy flow needed to heat the substrate flows Q ///// up to the temperature Tend. ///// </summary> ///// <param name="Q"></param> ///// <param name="mySubstrates"></param> ///// <param name="Tend"></param> ///// <param name="Pel_kWh_d">thermal or electrical energy per day</param> ///// <param name="Pel_kW">thermal or electrical power</param> ///// <exception cref="exception">Q.Length != mySubstrates.Count</exception> ///// <exception cref="exception">efficiency is zero, division by zero</exception> //public void heatSubstrates(physValue[] Q, substrates mySubstrates, // physValue Tend, out physValue Pel_kWh_d, // out physValue Pel_kW) //{ // physValue Qtherm_day= // mySubstrates.calcSumQuantityOfHeatPerDay(Q, Tend); // heatSubstrate(Qtherm_day, out Pel_kWh_d, out Pel_kW); //} ///// <summary> ///// Calculates energy flow needed to heat the substrate flows Q ///// up to the temperature Tend. ///// </summary> ///// <param name="Q"></param> ///// <param name="mySubstrates"></param> ///// <param name="Tend"></param> ///// <param name="Pel_kWh_d">thermal or electrical energy per day</param> ///// <exception cref="exception">Q.Length != mySubstrates.Count</exception> ///// <exception cref="exception">efficiency is zero, division by zero</exception> //public void heatSubstrates(physValue[] Q, substrates mySubstrates, // physValue Tend, out physValue Pel_kWh_d) //{ // physValue Qtherm_day = // mySubstrates.calcSumQuantityOfHeatPerDay(Q, Tend); // heatSubstrate(Qtherm_day, out Pel_kWh_d); //} ///// <summary> ///// Calculates energy flow needed to get the thermal energy/day pQtherm_day ///// from the heating. ///// </summary> ///// <param name="pQtherm_day"></param> ///// <param name="Pel_kWh_d">thermal or electrical energy per day</param> ///// <param name="Pel_kW">thermal or electrical power</param> ///// <exception cref="exception">efficiency is zero, division by zero</exception> //public void heatSubstrate(physValue pQtherm_day, out physValue Pel_kWh_d, // out physValue Pel_kW) //{ // heatSubstrate(pQtherm_day, out Pel_kWh_d); // Pel_kW = Pel_kWh_d.convertUnit("kW"); //} ///// <summary> ///// Calculates energy flow needed to get the thermal energy/day pQtherm_day ///// from the heating. ///// </summary> ///// <param name="pQtherm_day"></param> ///// <param name="Pel_kWh_d">thermal or electrical energy per day</param> ///// <exception cref="exception">efficiency is zero, division by zero</exception> //public void heatSubstrate(physValue pQtherm_day, out physValue Pel_kWh_d) //{ // if (status) // { // physValue Qtherm_day= pQtherm_day.convertUnit("kWh/d"); // if (eta != 0) // { // Pel_kWh_d= Qtherm_day / eta; // Pel_kWh_d.Symbol= "Pel_sub"; // } // else // throw new exception("electrical degree of efficiency of the heating: eta == 0"); // } // else // { // Pel_kWh_d= new physValue("Pel", 0, "kWh/d"); // } //} ///// <summary> ///// Calculates energy flow needed to get the thermal power pP_radiation_loss ///// from the heating. ///// </summary> ///// <param name="pP_radiation_loss"></param> ///// <param name="P_radiation_loss_kW">thermal or electrical power</param> ///// <param name="P_radiation_loss_kWh_d">thermal or electrical energy per day</param> ///// <exception cref="exception">efficiency is zero, division by zero</exception> //public void compensateHeatLossDueToRadiation(physValue pP_radiation_loss, // out physValue P_radiation_loss_kW, // out physValue P_radiation_loss_kWh_d) //{ // if (eta == 0) // throw new exception("electrical degree of efficiency of the heating: eta == 0"); // if (status) // { // physValue P_radiation_loss= pP_radiation_loss / eta; // P_radiation_loss.Symbol= "Pel_rad"; // P_radiation_loss_kW= P_radiation_loss.convertUnit("kW"); // } // else // { // P_radiation_loss_kW= new physValue("Pel", 0, "kW"); // } // P_radiation_loss_kWh_d= P_radiation_loss_kW.convertUnit("kWh/d"); //} /// <summary> /// Calculates costs for heating the digester in €/d. in case of a thermal heating /// costs are lost gain which we had if we sell the thermal energy. In case /// of an electrical heating it is the cost producing the electrical energy. /// </summary> /// <param name="pP_loss">thermal power loss</param> /// <param name="sell_heat">€/kWh for selling thermal energy, this is a virtual price here /// missed gain, needed for a thermal heating</param> /// <param name="cost_elEnergy">costs for electricity in €/kWh, needed if we have a electrical /// heating</param> /// <param name="P_loss_kW">thermal or electrical power</param> /// <param name="P_loss_kWh_d">thermal or electrical energy per day</param> /// <returns>costs in €/d</returns> /// <exception cref="exception">efficiency is zero, division by zero</exception> public double calcCostsForHeating(physValue pP_loss, double sell_heat, double cost_elEnergy, out physValue P_loss_kW, out physValue P_loss_kWh_d) { compensateHeatLoss(pP_loss, out P_loss_kW, out P_loss_kWh_d); // falls im fermenter th. energie produziert wird, mehr als verbraucht, dann // sind kosten == 0, man kann diese thermische energie nicht verkaufen, also kein // gewinn machen if (pP_loss.Value < 0) { return(0); } switch (type) { case 0: // electrical heating return(P_loss_kWh_d.Value * cost_elEnergy); // € case 1: // thermal heating return(P_loss_kWh_d.Value * sell_heat); // € default: throw new exception(String.Format("Unknown heating type: {0}", type)); } }
/// <summary> /// Calculates the f-Factor: fXI_Xc, defining the fraction of particulate inerts in /// composites Xc /// </summary> /// <param name="pADL">ADL</param> /// <param name="pNDF">NDF</param> /// <param name="pVS">VS</param> /// <param name="pD_VS">degradation level</param> /// <returns>fXI_Xc</returns> /// <exception cref="exception">value out of bounds</exception> public static double calcfXI_Xc(physValue pADL, physValue pNDF, physValue pVS, physValue pD_VS) { physValue ADL = pADL.convertUnit("% TS"); physValue NDF = pNDF.convertUnit("% TS"); physValue VS = pVS.convertUnit("% TS"); physValue D_VS = pD_VS.convertUnit("100 %"); double d = calc_d(ADL, NDF, D_VS, VS); physValue pfXI_Xc; if (VS.Value > 0) { pfXI_Xc = (ADL + (1 - d) * (NDF - ADL)) / VS; } else { pfXI_Xc = new physValue(0, "-"); } // 0.8 ist Test, noch nicht in thesis, s.u. fSI_XC, da steht 0.2f double fXI_Xc = pfXI_Xc.Value;// *0.6f; if (fXI_Xc < 0 || fXI_Xc > 1) { throw new exception(String.Format("fXI_Xc is out of bounds [0,1]: {0}!", fXI_Xc)); } return(fXI_Xc); }
/// <summary> /// Calculates the f-Factor: fPr_Xc, defining the fraction of proteins in /// composites Xc /// </summary> /// <param name="pRP">RP</param> /// <param name="pVS">VS</param> /// <returns>fPr_Xc</returns> /// <exception cref="exception">value out of bounds</exception> public static double calcfPr_Xc(physValue pRP, physValue pVS) { physValue RP = pRP.convertUnit("% TS"); physValue VS = pVS.convertUnit("% TS"); physValue pfPr_Xc; if (VS.Value > 0) { pfPr_Xc = RP / VS; } else { pfPr_Xc = new physValue(0, "-"); } double fPr_Xc = pfPr_Xc.Value; if (fPr_Xc < 0 || fPr_Xc > 1) { throw new exception(String.Format("fPr_Xc is out of bounds [0,1]: {0}!", fPr_Xc)); } return(fPr_Xc); }
/// <summary> /// Calculates the f-Factor: fCh_Xc, defining the fraction of carbohydrates in /// composites Xc /// The values given are converted to the correct unit, thus the units /// of the given values are not important. /// </summary> /// <param name="pRF">raw fiber</param> /// <param name="pNfE">nitrogen free extracts</param> /// <param name="pADL">acid detergent lignin</param> /// <param name="pNDF">neutral detergent fiber</param> /// <param name="pVS">volatile solids</param> /// <param name="pD_VS">degradation level</param> /// <returns>fCh_Xc</returns> /// <exception cref="exception">value out of bounds</exception> public static double calcfCh_Xc(physValue pRF, physValue pNfE, physValue pADL, physValue pNDF, physValue pVS, physValue pD_VS) { physValue RF = pRF.convertUnit("% TS"); physValue NfE = pNfE.convertUnit("% TS"); physValue ADL = pADL.convertUnit("% TS"); physValue NDF = pNDF.convertUnit("% TS"); physValue VS = pVS.convertUnit("% TS"); physValue D_VS = pD_VS.convertUnit("100 %"); double d = calc_d(ADL, NDF, D_VS, VS); physValue pfCh_Xc; if (VS.Value > 0) { pfCh_Xc = (calcNFC(RF, NfE, NDF) + d * (NDF - ADL)) / VS; } else { pfCh_Xc = new physValue(0, "-"); } double fCh_Xc = pfCh_Xc.Value; if (fCh_Xc < 0 || fCh_Xc > 1) { throw new exception(String.Format("fCh_Xc is out of bounds [0,1]: {0}!", fCh_Xc)); } return(fCh_Xc); }
/// <summary> /// Returns O content of molecule. mol O / molecule /// </summary> /// <param name="molecule">string of molecule</param> /// <param name="O">mol O / mol molecule</param> /// <exception cref="exception">Unknown molecule</exception> public static void get_O_of(string molecule, out physValue O) { physValue H; physValue C; get_CHO_of(molecule, out C, out H, out O); }
/// <summary> /// Combusts the chemical formula C_cH_hO_oN_n. /// Calculates O2, CO2, H2O and NH3 in mol/mol /// </summary> /// <param name="c">mol C in molecule</param> /// <param name="h">mol H in molecule</param> /// <param name="o">mol O in molecule</param> /// <param name="n">mol N in molecule</param> /// <param name="o2">mol O2 oxydized in the reaction</param> /// <param name="co2">mol CO2 created during combustion</param> /// <param name="h2o">mol H2O created during combustion</param> /// <param name="nh3">mol NH3 created during combustion</param> public static void combust(physValue c, physValue h, physValue o, physValue n, out physValue o2, out physValue co2, out physValue h2o, out physValue nh3) { // in meinem PDF steht hier fehlerhaft + o/2, muss - o/2 sein, sonst geht O Bilanz nicht auf o2 = c + h / 4f - o / 2f - 3f * n / 4f; co2 = c; h2o = h / 2f - 3f * n / 2f; nh3 = n; o2.Symbol = "o2"; o2.Label = String.Format("mol molecular oxygen / mol C{0}H{1}O{2}N{3} (combustion)", c.Value, h.Value, o.Value, n.Value); co2.Symbol = "co2"; co2.Label = String.Format("mol carbon dioxide / mol C{0}H{1}O{2}N{3} (combustion)", c.Value, h.Value, o.Value, n.Value); h2o.Symbol = "o2"; h2o.Label = String.Format("mol water / mol C{0}H{1}O{2}N{3} (combustion)", c.Value, h.Value, o.Value, n.Value); nh3.Symbol = "nh3"; nh3.Label = String.Format("mol ammonia / mol C{0}H{1}O{2}N{3} (combustion)", c.Value, h.Value, o.Value, n.Value); }
/// <summary> /// Returns C, H and O content of molecule. mol / molecule /// </summary> /// <param name="molecule">string of molecule</param> /// <param name="C">mol C / mol molecule</param> /// <param name="H">mol H / mol molecule</param> /// <param name="O">mol O / mol molecule</param> /// <exception cref="exception">Unknown molecule</exception> public static void get_CHO_of(string molecule, out physValue C, out physValue H, out physValue O) { physValue N; get_CHON_of(molecule, out C, out H, out O, out N); }
// ------------------------------------------------------------------------------------- // !!! PUBLIC METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// Combusts the chemical formula C_cH_hO_oN_n. /// Calculates O2 in mol/mol /// </summary> /// <param name="c">mol C in molecule</param> /// <param name="h">mol H in molecule</param> /// <param name="o">mol O in molecule</param> /// <param name="n">mol N in molecule</param> /// <param name="o2">mol O2 oxydized in the reaction</param> public static void combust(physValue c, physValue h, physValue o, physValue n, out physValue o2) { physValue co2; combust(c, h, o, n, out o2, out co2); }
/// <summary> /// aktuell type 4 /// </summary> /// <param name="myPlant"></param> /// <param name="u">Qin in m³/d</param> /// <param name="par"> /// par[0] is the density of the substrate in kg/m^3 /// </param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.plant myPlant, double u, params double[] par) { // physValue[] values = new physValue[1]; // 1. komponente ist rho als double, unten in density def. dann anstatt // 1000 einsetzen if (par.Length != 1) // 2, da rho auch übergeben werden muss { throw new exception(String.Format( "Length of par is != 1: {0}!", par.Length)); } transportation myTransportations = myPlant.myTransportation; // get pump from transportation class using id biogas.substrate_transport mySubsTransport = myTransportations.getSubstrateTransportByID(id_suffix); // is the energy_per_ton [kWh/t] double energy_per_ton = mySubsTransport.energy_per_ton; // calc energy consumption in kWh/d // hier muss man schon rho der zu fördernden menge kennen values[0] = new physValue("Pel_trans", u * par[0] / 1000 * energy_per_ton, "kWh/d", "transport energy"); return(values); }
/// <summary> /// Combusts the chemical formula C_cH_hO_oN_n. /// Calculates O2 and CO2 in mol/mol /// </summary> /// <param name="c">mol C in molecule</param> /// <param name="h">mol H in molecule</param> /// <param name="o">mol O in molecule</param> /// <param name="n">mol N in molecule</param> /// <param name="o2">mol O2 oxydized in the reaction</param> /// <param name="co2">mol CO2 created during combustion</param> /// <param name="h2o">mol H2O created during combustion</param> public static void combust(physValue c, physValue h, physValue o, physValue n, out physValue o2, out physValue co2, out physValue h2o) { physValue nh3; combust(c, h, o, n, out o2, out co2, out h2o, out nh3); }
/// <summary> /// Calculates costs for heating the digester. in case of a thermal heating /// costs are lost gain which we had if we sell the thermal energy. In case /// of an electrical heating it is the cost producing the electrical energy. /// </summary> /// <param name="pP_loss">thermal power loss</param> /// <param name="sell_heat">€/kWh for selling thermal energy, this is a virtual price here /// missed gain, needed for a thermal heating</param> /// <param name="cost_elEnergy">costs for electricity in €/kWh, needed if we have a electrical /// heating</param> /// <returns>costs in €/d</returns> /// <exception cref="exception">efficiency is zero, division by zero</exception> public double calcCostsForHeating(physValue pP_loss, double sell_heat, double cost_elEnergy) { physValue P_loss_kW, P_loss_kWh_d; return(heating.calcCostsForHeating(pP_loss, sell_heat, cost_elEnergy, out P_loss_kW, out P_loss_kWh_d)); }
///// <summary> ///// Calculate thermal power needed to compensate heat loss due ot radiation ///// through the digesters surface. ///// </summary> ///// <param name="digester_id"></param> ///// <param name="T_ambient"></param> ///// <param name="P_radiation_loss_kW"></param> ///// <param name="P_radiation_loss_kWh_d"></param> //public void compensateHeatLossDueToRadiation(string digester_id, // physValue T_ambient, // out physValue P_radiation_loss_kW, // out physValue P_radiation_loss_kWh_d) //{ // digester myDigester= get(digester_id); // myDigester.compensateHeatLossDueToRadiation(T_ambient, out P_radiation_loss_kW, // out P_radiation_loss_kWh_d); //} /// <summary> /// Calculates costs for heating the digester. in case of a thermal heating /// costs are lost gain which we had if we sell the thermal energy. In case /// of an electrical heating it is the cost producing the electrical energy. /// </summary> /// <param name="digester_id">digester id</param> /// <param name="pP_loss">thermal power loss</param> /// <param name="sell_heat">€/kWh for selling thermal energy, this is a virtual price here /// missed gain, needed for a thermal heating</param> /// <param name="cost_elEnergy">costs for electricity in €/kWh, needed if we have a electrical /// heating</param> /// <returns>costs in €/d</returns> /// <exception cref="exception">Unknown digester id</exception> /// <exception cref="exception">efficiency is zero, division by zero</exception> public double calcCostsForHeating(string digester_id, physValue pP_loss, double sell_heat, double cost_elEnergy) { digester myDigester = get(digester_id); return(myDigester.calcCostsForHeating(pP_loss, sell_heat, cost_elEnergy)); }
/// <summary> /// converts double biogas stream u, given in ppm, % and % to /// corresponding physValues /// </summary> /// <param name="u_rel">n_gases dim vector of h2 in ppm, /// ch4 in % and co2 in %</param> /// <returns>u as physValue vector measured in % and ppm resp.</returns> /// <exception cref="exception">u_rel.Length < _n_gases</exception> public static physValue[] convert(double[] u_rel) { if (u_rel.Length < _n_gases) { throw new exception(String.Format( "The gas stream u has not the right dimension ({0}). Must be >= {1}!", u_rel.Length, _n_gases)); } physValue[] values = new physValue[u_rel.Length]; // for (int igas = 0; igas < u_rel.Length; igas++) { if (igas == pos_h2 - 1) { values[igas] = new physValue(symGases[igas] + "_t", u_rel[igas], "ppm", labelGases[igas]); } else { values[igas] = new physValue(symGases[igas] + "_t", u_rel[igas], "%", labelGases[igas]); } } 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> /// calc methane yield [m^3 CH4 / kgVS] /// /// http://www.bioconverter.com/technology/primer.htm#Methane%20Yield%20%28m3%20CH4%20/%20kg%20VS%20added%29 /// /// if methane yield is high, everything is ok, if it is too low /// maybe decrease substrate feed, digester could be overburdened /// /// </summary> /// <param name="x">ADM state vector</param> /// <param name="pQ">Q : m^3/d : total feed into the digester</param> /// <param name="pVS">VS : % TS : volatile solids content of feed</param> /// <param name="pTS">TS : % FM : total solids content of feed</param> /// <param name="prho">rho : kg/m^3 : density of feed</param> /// <returns></returns> public physValue calcCH4Yield(double[] x, physValue pQ, physValue pVS, physValue pTS, physValue prho) { physValue Qgas_h2, Qgas_ch4; ADMstate.calcBiogasOfADMstate(x, Vliq, T, out Qgas_h2, out Qgas_ch4); physValue Q = pQ.convertUnit("m^3/d"); physValue VS = pVS.convertUnit("% TS"); physValue TS = pTS.convertUnit("% FM"); physValue rho = prho.convertUnit("kg/m^3"); VS = substrate.convertFrom_TS_To_FM(VS, TS); // VS [% FM] * [100 % / % FM] * m^3/d * kg/m^3 = VS kg/d physValue VS_kg = VS.convertUnit("100 %") * Q * rho; if (VS_kg.Value == 0) { return(new physValue(0, "m^3/kg")); } // m^3/d / kgVS/d= m^3/kgVS // Einheit ist: m^3/kg physValue CH4Yield = Qgas_ch4 / VS_kg; return(CH4Yield); }
/// <summary> /// Returns the current measurement vector in the list, so the values last saved /// in the list. If list is empty a zero vector with dimension dimension is returned. /// </summary> /// <param name="noisy">if true, then noisy measurement values are returned. /// they are only noisy if the parameter apply_real_sensor was true before and during /// the simulation</param> /// <returns>current measurement vector</returns> public physValue[] getCurrentMeasurementVector(bool noisy) { if (values.Count > 0) { if (noisy) { return(values_noise[values_noise.Count - 1]); // return noisy values } else { return(values[values.Count - 1]); // return normal values } } else { physValue[] myValues = new physValue[dimension]; for (int ivalue = 0; ivalue < dimension; ivalue++) { myValues[ivalue] = new physValue(); } return(myValues); } }
// ------------------------------------------------------------------------------------- // !!! PROTECTED METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// here it is defined what is measured in the sensor and in which position /// /// not type 0 /// </summary> /// <param name="u">biogas stream: dimension: BioGas.n_gases</param> /// <param name="par">not used</param> /// <returns> /// measured biogas values /// first n_gases values are biogas in m^3/d /// next n_gases values are biogas concentrations in % /// </returns> /// <exception cref="exception">u.Length < _n_gases</exception> /// <exception cref="exception">u is empty</exception> override protected physValue[] doMeasurement(double[] u, params double[] par) { physValue[] QgasP = new physValue[(int)BioGas.n_gases]; // is already checked for in calcPercentualBiogasComposition //if (u.Length != (int)BioGas.n_gases) // throw new exception(String.Format( // "u is not of correct dimension: {0} != {1}!", // u.Length, (int)BioGas.n_gases)); BioGas.calcPercentualBiogasComposition(u, out QgasP); physValue[] values = new physValue[dimension]; for (int ivalue = 0; ivalue < values.Length; ivalue++) { if (ivalue < (int)BioGas.n_gases) { values[ivalue] = new physValue(BioGas.symGases[ivalue], u[ivalue], "m^3/d", BioGas.labelGases[ivalue]); } else { values[ivalue] = QgasP[ivalue - (int)BioGas.n_gases]; } } return(values); }
// ------------------------------------------------------------------------------------- // !!! PROTECTED METHODS !!! // ------------------------------------------------------------------------------------- /// <summary> /// measures ratio of acetoclastic to hydrogenotrophic methanogenesis of /// given digester /// </summary> /// <param name="intvars">ADM1 internal variables</param> /// <param name="par">2dim. /// 0: Yac /// 1: Yh2 /// </param> /// <returns>ratio of acetoclastic to hydrogenotrophic methanogenesis /// inside digester</returns> override protected physValue[] doMeasurement(double[] intvars, params double[] par) { physValue[] values = new physValue[dimension]; // Uptake of acetate double p_ac = intvars[48]; // Uptake of hydrogen double p_h2 = intvars[49]; double Yac = par[0]; // ADMparams.Get(46 - 1); double Yh2 = par[1]; // ADMparams.Get(47 - 1); double ch4_prod_ac = p_ac * (1 - Yac); double ch4_prod_h2 = p_h2 * (1 - Yh2); double aceto_ratio, hydro_ratio; if (ch4_prod_ac + ch4_prod_h2 != 0) { aceto_ratio = Math.Round(ch4_prod_ac / (ch4_prod_ac + ch4_prod_h2) * 100.0, 2); hydro_ratio = Math.Round(ch4_prod_h2 / (ch4_prod_ac + ch4_prod_h2) * 100.0, 2); } else { aceto_ratio = 0; hydro_ratio = 0; } values[0] = new physValue("aceto", aceto_ratio, "100 %", "acetoclastic methanogenesis"); values[1] = new physValue("hydro", hydro_ratio, "100 %", "hydrogenotrophic methanogenesis"); return(values); }
/// <summary> /// Calculate g C / kg substrate fresh matter /// </summary> /// <param name="mySubstrate">a substrate</param> /// <returns>g C / kg fresh matter</returns> private static physValue calcC(substrate mySubstrate) { physValue[] values = new physValue[6]; try { mySubstrate.get_params_of(out values, "RF", "RP", "RL", "NfE", "ADL", "TS"); } catch (exception e) { Console.WriteLine(e.Message); return(new physValue("error")); } physValue RF = values[0]; physValue RP = values[1]; physValue RL = values[2]; physValue NfE = values[3]; physValue ADL = values[4]; physValue TS = values[5]; return(calcC(RF, RP, RL, NfE, ADL, TS)); }
/// <summary> /// Measure NH4 content inside a digester /// 1st measurement is Snh4 in g/l /// 2nd : Snh4 + NH4 in Xc in g/l /// /// type 5 /// </summary> /// <param name="myPlant"></param> /// <param name="x">ADM state vector</param> /// <param name="param">not used - but OK</param> /// <param name="par">not used</param> /// <returns></returns> override protected physValue[] doMeasurement(biogas.plant myPlant, double[] x, string param, params double[] par) { physValue[] values = new physValue[dimension]; // hier wird umrechnungsfaktor 18 genutzt, deshalb nur ammonium und nicht // ammonium nitrogen values[0] = ADMstate.calcFromADMstate(x, "Snh4", "g/l"); // // -2 wegen _2 bzw. _3 string digester_id = id.Substring(("Snh4_").Length, id.Length - 2 - ("Snh4_").Length); // kmol N/m^3 double NH4 = ADMstate.calcNH4(x, digester_id, myPlant); // // erstmal Snh4 nennen, damit convertUnit funktioniert // TODO - könnte es auch N nennen, dann wird allerdings nur mit 14 als // umrechnungsfaktor gerechnet und nicht wie jetzt mit 18. Vorsicht // bei vergleich mit Ntot, TKN und Norg, dort wird nur mit 14 gerechnet. // da ich die Größe ammonium nitrogen nenne, wird umrechnungsfaktor 14 genutzt values[1] = new physValue("N", NH4, "mol/l", "total ammonium nitrogen"); values[1] = values[1].convertUnit("g/l"); values[1].Symbol = "NH4"; return(values); }