/// <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);
        }
Example #2
0
        /// <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));
        }
Example #3
0
 /// <summary>
 /// Creates and returns a copy of the template
 /// </summary>
 /// <param name="template">template of substrates list</param>
 public substrates(substrates template)
 {
     foreach (substrate mySubstrate in template)
     {
         addSubstrate(mySubstrate.copy());
     }
 }
        /// <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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
        // -------------------------------------------------------------------------------------
        //                              !!! 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"));
        }
        // -------------------------------------------------------------------------------------
        //                              !!! PUBLIC METHODS !!!
        // -------------------------------------------------------------------------------------

        /// <summary>
        /// Returns measurement at a given time t of a sensor_array
        /// </summary>
        /// <param name="mySubstrates">list of substrates that are included in the sum, mean</param>
        /// <param name="id_sensor_array">id of sensor_array</param>
        /// <param name="s_operator">operator: mean or sum</param>
        /// <param name="t">simulation time in days</param>
        /// <param name="index">index inside sensor: 0, 1, 2, ...</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></returns>
        /// <exception cref="exception">Unknown sensor array id</exception>
        /// <exception cref="exception">Invalid index</exception>
        public double getArrayMeasurementDAt(substrates mySubstrates, string id_sensor_array,
                                             string s_operator, double t, int index, bool noisy)
        {
            sensor_array mySensorArray = getArray(id_sensor_array);

            return(mySensorArray.getMeasurementDAt(mySubstrates, s_operator, t, index, noisy));
        }
Example #8
0
        /// <summary>
        /// Returns mean or sum of measurements at a given time t for the sensor array
        /// only for substrates, pumps are in the same sensor_array Q, do not include them
        /// </summary>
        /// <param name="mySubstrates">list of substrates that are included in the sum, mean</param>
        /// <param name="s_operator">operator: mean, sum</param>
        /// <param name="t">simulation time in days</param>
        /// <param name="index">index inside sensor: 0, 1, 2, ...</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></returns>
        /// <exception cref="exception">Invalid index</exception>
        public double getMeasurementDAt(substrates mySubstrates, string s_operator, double t,
                                        int index, bool noisy)
        {
            List <double> data = new List <double>();

            foreach (sensor mySensor in this)
            {
                if (mySubstrates.ids.Contains(mySensor.id_suffix))
                {
                    data.Add(mySensor.getMeasurementDAt(index, t, noisy));
                }
            }

            if (s_operator == "sum")
            {
                return(math.sum(data));
            }
            else if (s_operator == "mean")
            {
                return(math.mean(data));
            }
            else
            {
                // TODO - throw error
                return(0);
            }
        }
Example #9
0
        /// <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));
        }
Example #10
0
        // -------------------------------------------------------------------------------------
        //                              !!! 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));
        }
Example #11
0
        ///// <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));
        }
Example #12
0
        /// <summary>
        /// Returns the ADM params vector, but the substrate dependent parameters
        /// are returned as normal means, not weighted means
        /// </summary>
        /// <param name="mySubstrates"></param>
        /// <returns></returns>
        public double[] getParams(substrates mySubstrates)
        {
            double[] Q = new double[mySubstrates.getNumSubstrates()];

            for (int isubstrate = 0; isubstrate < mySubstrates.getNumSubstrates(); isubstrate++)
            {
                Q[isubstrate] = 1;
            }

            return(getParams(0, Q, math.sum(Q), mySubstrates)); // , true
        }
Example #13
0
        /// <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);
        }
Example #14
0
        /// <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>
        /// measures mean value of given parameter in mySubstrates depending on Q
        ///
        /// type 3
        /// </summary>
        /// <param name="x">not used</param>
        /// <param name="mySubstrates"></param>
        /// <param name="Q">
        /// substrate feed in m^3/d
        /// doesn't matter if this is substrate mix for plant or
        /// substrate mix to digester, depends on what we want to have</param>
        /// <param name="par">not used</param>
        /// <returns>mean value of given parameter</returns>
        override protected physValue[] doMeasurement(double[] x, biogas.substrates mySubstrates,
                                                     double[] Q, params double[] par)
        {
            physValue[] values = new physValue[1];

            // id_suffix is here the parameter to be measured, see above
            mySubstrates.get_weighted_sum_of(Q, id_suffix, out values[0]);

            values[0].Symbol = id_suffix;

            return(values);
        }
Example #16
0
        /// <summary>
        /// Returns true if given list contains a substrate with the given
        /// substrate_id
        /// </summary>
        /// <param name="mySubstrates">list of substrates</param>
        /// <param name="substrate_id">substrate id</param>
        /// <returns>true, if id can be found in substrate list, else false</returns>
        public static bool contains(substrates mySubstrates, string substrate_id)
        {
            foreach (substrate mySubstrate in mySubstrates)
            {
                if (mySubstrate.id == substrate_id)
                {
                    return(true);
                }
            }

            return(false);
        }
        // -------------------------------------------------------------------------------------
        //                              !!! 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 &lt; 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);
        }
Example #18
0
        /// <summary>
        /// Calculated volatile solids inside digester
        ///
        /// called in ADMstate_stoichiometry
        ///
        /// scheint mir typ 3 zu sein, ist auch so
        /// </summary>
        /// <param name="x">ADM state vector</param>
        /// <param name="mySubstrates">list of all substrates of plant</param>
        /// <param name="Q">substrate feed of total plant in m³/d</param>
        /// <param name="par">not used</param>
        /// <returns>volatile solids inside digester in % TS</returns>
        override protected physValue[] doMeasurement(double[] x, biogas.substrates mySubstrates,
                                                     double[] Q, params double[] par)
        {
            physValue[] values = new physValue[1];

            // durch diesen Aufruf werden einige Annahmen gemacht
            // der TS Gehalt in dem Fermenter wird berechnet aus dem COD Gehalt im Fermenter
            // mit der Annahme, dass die Verteilung von RF, RP, RL, .. in dem Fermenter
            // so ist wie die der gesamten Substratzufuhr der Anlage, also unabhängig davon
            // ob fermenter überhaupt gefüttert wird oder nicht
            // weiterhin wird angenommen, das Asche Gehalt in jedem Fermenter so groß ist
            // wie der Asche Gehalt der gesamten Substratzufuhr. das ist nur eine Annahme,
            // könnte auch völlig anders sein. bspw. bei 2 fermentern müsste in jedem Fermenter
            // Asche der Substrate halbe sein...
            values[0] = biogas.digester.calcVS(x, mySubstrates, Q);

            return(values);
        }
Example #19
0
        /// <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);
        }
Example #21
0
        /// <summary>
        /// opens the file for reading
        /// </summary>
        /// <param name="fileName">name of xml file</param>
        /// <returns>true on success, else false</returns>
        private bool openSubstrateFile(String fileName)
        {
            // read config from xml file

            this.Cursor = Cursors.WaitCursor;

            setStatusMessage(String.Format("Laden der Datei {0}", fileName));

            // create substrate object

            bool success;

            try
            {
                mySubstrates = new biogas.substrates(fileName);

                success = true;
            }
            catch
            {
                MessageBox.Show(String.Format("Kann Datei {0} nicht öffnen!", fileName),
                                "Fehler beim einlesen der Substratdatei", MessageBoxButtons.OK, MessageBoxIcon.Error);

                success = false;
            }

            // read out mySubstrates
            // before this method is called, new is called always, therefore
            // the gui is empty at this point, correct to call init here

            success = success && init_gui_with_substrate(mySubstrates);

            //

            resetStatusMessage();

            this.Cursor = Cursors.Default;

            return(success);
        }
Example #22
0
        /// <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);
        }
Example #23
0
        /// <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>
        /// 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);
        }
Example #25
0
 /// <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));
 }
Example #26
0
        /// <summary>
        /// Returns the ADM params vector, depending on the current substrate feed
        ///
        /// 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 in days</param>
        /// <param name="Q">substrate feed measured in m^3/d</param>
        /// <param name="QdigesterIn">total volumetric flow rate in digester in m^3/d</param>
        /// <param name="mySubstrates"></param>
        /// <returns></returns>
        public double[] getParams(double t, double[] Q, double QdigesterIn, substrates mySubstrates)
        {
            double fCH_XC, fLI_XC, fPR_XC,
                   fSI_XC, fXI_XC, fXP_XC;

            // TODO
            // das Problem wenn fermenter nicht gefüttert wird, also Q am Anfang nur 0er hat, dann
            // werden alle Substrate in berechnung einbezogen der Parameter
            // fFactors, km_...
            // und nicht nur die Substrate, welche zu dem zeitpunkt gefüttert werden.
            // kdis und khyd haben nicht das problem, da sie mit gewichtet werden, eine
            // nicht gefütterte anlage arbeitet dann mit defualt werten s.u.

            // es gibt das problem, dass wenn man simulation in stücke teilt, dass dann adm1 parameter
            // am ende der simulation in mat datei gespeichert werden müssen. da hier nur am "start"
            // der simulation, d.h. bei t= 0, die parameter frei gesetzt werden, sonst werden die nur
            // verändert wenn unten angegebene bedingungen erfüllt sind wie in einer normalen simulation
            // wenn das speichern von parametern gewünscht ist, muss in matlab die save_ADMparams_to_mat_file
            // aufgerufen werden nach der simulation
            // true if we are at start of simulation else false
            bool start_of_simulation = (t == 0);

            // get current XC fractions
            mySubstrates.calcfFactors(Q, out fCH_XC, out fPR_XC, out fLI_XC,
                                      out fXI_XC, out fSI_XC, out fXP_XC);

            //

            double fSIN_XC = mySubstrates.calcfSIN_Xc(Q);

            //

            double kdis = mySubstrates.calcDisintegrationParam(Q);

            double khyd_ch, khyd_pr, khyd_li;

            mySubstrates.calcHydrolysisParams(Q, out khyd_ch, out khyd_pr, out khyd_li);


            double km_c4, km_pro, km_ac, km_h2;

            mySubstrates.calcMaxUptakeRateParams(Q, out km_c4, out km_pro, out km_ac, out km_h2);

            double f_ub = 0.2;

            // set params in params vector
            if (math.sum(Q) > 0 || QdigesterIn > 0 || start_of_simulation)
            // TODO: das bedeutet, dass nur gefütterter Fermenter gesetzt wird
            // ist das gewollt? mit dem || bedeutet, dass Fermenter gefüttert werden muss
            // egal ob mit substrat oder schlamm
            {
                // TODO: es darf hier nicht passieren, dass ein paar Parameter gesetzt werden
                // und andere nicht, da sonst die Summe == 1 nicht mehr stimmt

                // else do not set these parameters, use standard parameters instead
                if (fSI_XC >= 0 && (Math.Abs(_parameters[ADMparams.pos_fSI_XC - 1] - fSI_XC) < f_ub || start_of_simulation))
                {
                    _parameters[ADMparams.pos_fSI_XC - 1] = fSI_XC;
                }

                // TODO - es bringt eigentlich nichts diesen wert hier zu setzen, da er in ADM1
                // stoichiometry aus anderern Parametern berechnet wird
                if (fXI_XC >= 0 && (Math.Abs(_parameters[ADMparams.pos_fXI_XC - 1] - fXI_XC) < f_ub || start_of_simulation))
                {
                    _parameters[ADMparams.pos_fXI_XC - 1] = fXI_XC;
                }

                if (fCH_XC >= 0 && (Math.Abs(_parameters[ADMparams.pos_fCH_XC - 1] - fCH_XC) < f_ub || start_of_simulation))
                {
                    _parameters[ADMparams.pos_fCH_XC - 1] = fCH_XC;
                }

                if (fPR_XC >= 0 && (Math.Abs(_parameters[ADMparams.pos_fPR_XC - 1] - fPR_XC) < f_ub || start_of_simulation))
                {
                    _parameters[ADMparams.pos_fPR_XC - 1] = fPR_XC;
                }

                if (fLI_XC >= 0 && (Math.Abs(_parameters[ADMparams.pos_fLI_XC - 1] - fLI_XC) < f_ub || start_of_simulation))
                {
                    _parameters[ADMparams.pos_fLI_XC - 1] = fLI_XC;
                }

                // TODO: fXP_XC kann auch ganz leicht negativ werden, da er durch substraktion
                // der anderen Parameter von 1 entsteht
                if (/*fXP_XC >= 0 && */ (Math.Abs(_parameters[ADMparams.pos_fXP_XC - 1] - fXP_XC) < f_ub || start_of_simulation))
                {
                    _parameters[ADMparams.pos_fXP_XC - 1] = Math.Max(fXP_XC, 0.0);
                }


                if (fSIN_XC >= 0 && (Math.Abs(_parameters[ADMparams.pos_fSIN_XC - 1] - fSIN_XC) < f_ub || start_of_simulation))
                {
                    _parameters[ADMparams.pos_fSIN_XC - 1] = fSIN_XC;
                }


                if (km_c4 >= 0 && (Math.Abs(_parameters[ADMparams.pos_km_c4 - 1] - km_c4) < 5 || start_of_simulation))
                {
                    _parameters[ADMparams.pos_km_c4 - 1] = km_c4;
                }
                if (km_pro >= 0 && (Math.Abs(_parameters[ADMparams.pos_km_pro - 1] - km_pro) < 5 || start_of_simulation))
                {
                    _parameters[ADMparams.pos_km_pro - 1] = km_pro;
                }
                if (km_ac >= 0 && (Math.Abs(_parameters[ADMparams.pos_km_ac - 1] - km_ac) < 5 || start_of_simulation))
                {
                    _parameters[ADMparams.pos_km_ac - 1] = km_ac;
                }
                if (km_h2 >= 0 && (Math.Abs(_parameters[ADMparams.pos_km_h2 - 1] - km_h2) < 5 || start_of_simulation))
                {
                    _parameters[ADMparams.pos_km_h2 - 1] = km_h2;
                }
            }

            // TODO
            // was besseres überlegen
            // 150000 stehen für Schlamm, welcher schon abgebaut wurde
            // kdis hat im Nachgärer kaum Auswirkungen, Xc scheint schon sehr stark abgebaut zu sein
            // von hauptfermenter
            double kdis_default = 10.0;// 1000;// 0.25;// 150000; // _parameters[ADMparams.pos_kdis - 1];

            double Qsubstrate = math.sum(Q);

            if (Qsubstrate > 0) // wenn der Fermenter gefüttert wird, dann kdis default Wert kleiner wählen
            {
                kdis_default = Math.Max(0.25, kdis);
            }

            // TODO : was ist wenn QdigesterIn == 0 ???

            kdis = Qsubstrate / QdigesterIn * kdis + (1 - Qsubstrate / QdigesterIn) * kdis_default;

            // set disintegration constant
            if (kdis > 0 && (Math.Abs(_parameters[ADMparams.pos_kdis - 1] - kdis) < 1 || start_of_simulation))
            {
                _parameters[ADMparams.pos_kdis - 1] = kdis;
            }

            // TODO
            // was besseres überlegen
            // 150000 stehen für Schlamm, welcher schon abgebaut wurde
            double khyd_ch_default = 10; // 150000; // _parameters[ADMparams.pos_khyd_ch - 1];
            double khyd_pr_default = 10; // 150000; // _parameters[ADMparams.pos_khyd_pr - 1];
            double khyd_li_default = 10; // 150000; // _parameters[ADMparams.pos_khyd_li - 1];

            if (Qsubstrate > 0)          // wenn der Fermenter gefüttert wird, dann khyd default Werte kleiner wählen
            {
                khyd_ch_default = Math.Max(10, khyd_ch);
                khyd_pr_default = Math.Max(10, khyd_pr);
                khyd_li_default = Math.Max(10, khyd_li);
            }

            khyd_ch = Qsubstrate / QdigesterIn * khyd_ch + (1 - Qsubstrate / QdigesterIn) * khyd_ch_default;
            khyd_pr = Qsubstrate / QdigesterIn * khyd_pr + (1 - Qsubstrate / QdigesterIn) * khyd_pr_default;
            khyd_li = Qsubstrate / QdigesterIn * khyd_li + (1 - Qsubstrate / QdigesterIn) * khyd_li_default;

            // set hydrolysis constants
            if (khyd_ch > 0 && (Math.Abs(_parameters[ADMparams.pos_khyd_ch - 1] - khyd_ch) < 1 || start_of_simulation))
            {
                _parameters[ADMparams.pos_khyd_ch - 1] = khyd_ch;
            }

            if (khyd_pr > 0 && (Math.Abs(_parameters[ADMparams.pos_khyd_pr - 1] - khyd_pr) < 1 || start_of_simulation))
            {
                _parameters[ADMparams.pos_khyd_pr - 1] = khyd_pr;
            }

            if (khyd_li > 0 && (Math.Abs(_parameters[ADMparams.pos_khyd_li - 1] - khyd_li) < 1 || start_of_simulation))
            {
                _parameters[ADMparams.pos_khyd_li - 1] = khyd_li;
            }

            //

            return(_parameters);
        }
        /// <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>
        /// <returns></returns>
        public static physValue calcVS(double[] x, substrates mySubstrates, double[] Q)
        {
            physValue TS;

            return(calcVS(x, mySubstrates, Q, out TS));
        }
Example #29
0
        /// <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 &lt; 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);
        }
Example #30
0
        /// <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);
        }