Пример #1
0
        COMMODITY_GROUP GetKCoeffsGroup(COMMODITY_GROUP grp, double api60)
        {
            if (grp != COMMODITY_GROUP.GENERALIZED_REFINED_PRODUCT)
            {
                return(grp);
            }
            // Try to match density range
            var             uom = uoms["api"];
            COMMODITY_GROUP ret = COMMODITY_GROUP.ANY;

            foreach (var limKv in uom.Limits)
            {
                if (((int)limKv.Key) >= 100)  // Limit to refined product subgroups
                {
                    if (limKv.Value.ValueIsWithin(api60))
                    {
                        // Found it!
                        ret = limKv.Key;
                        break;
                    }
                }
            }

            if (ret == COMMODITY_GROUP.ANY)
            {
                throw (new ArgumentException("Density is not in the range of any of the Generalized Refined Products"));
            }
            return(ret);
        }
Пример #2
0
        // API 12.1 - Tank Roof Corrections
        public double GetBarrelsDueToTankRoof(COMMODITY_GROUP grp, double api60, double tempF, double presPsi = 0, double vapPresPsi = 0, double roofWgtLb = 0, double bblPerApi = 0, double refApi = 0)
        {
            // Get CTPL
            double CTPL = GetCTPLFromApiDegFPsig(grp, api60, tempF, presPsi, vapPresPsi);

            return(GetBarrelsDueToTankRoof(api60, CTPL, roofWgtLb, bblPerApi, refApi));
        }
Пример #3
0
        void checkRange(double val, string units, COMMODITY_GROUP grp = COMMODITY_GROUP.ANY)
        {
            // Validate and get UoM that contains the limits
            UnitOfMeasure uom = null;

            if (!uoms.TryGetValue(units.ToLower(), out uom))
            {
                throw (new ArgumentException("Units {0} not recognized or supported"));
            }

            // If not limits defined, there is nothig to check
            if (uom.Limits == null || uom.Limits.Count < 1)
            {
                return;
            }

            bool isLiquidGas = (grp == COMMODITY_GROUP.LPG_NGL);
            bool isGeneralizedRefinedProduct = ((int)grp >= 100);

            // Ignore commodity if limits are for ANY commodity
            if (!isLiquidGas && uom.Limits.ContainsKey(COMMODITY_GROUP.ANY))
            {
                try
                {
                    uom.Limits[COMMODITY_GROUP.ANY].CheckLimits(val);
                }
                catch (ArgumentOutOfRangeException e)
                {
                    throw(new ArgumentOutOfRangeException(String.Format("{0} with units={1}", e.Message, units)));
                }
                return;
            }

            // Check limit for commodity
            ValueLimit limit = null;
            var        type  = grp;

            if (isGeneralizedRefinedProduct)
            {
                type = COMMODITY_GROUP.GENERALIZED_REFINED_PRODUCT;   // Needed to allow density variations within refined products group
            }
            if (!uom.Limits.TryGetValue(type, out limit))
            {
                throw (new ArgumentException("Limits for commodity group {0} not supported"));
            }
            try
            {
                limit.CheckLimits(val);
            }
            catch (ArgumentOutOfRangeException e)
            {
                throw (new ArgumentOutOfRangeException(String.Format("{0} with units={1} and commodity group={2}", e.Message, units, grp)));
            }

            return;
        }
Пример #4
0
 // Convenient method to handle all in a single call
 public double GetCTPLFromApiDegFPsig(COMMODITY_GROUP grp, double api60, double tempF, double?presPsig = null, double?vapPress = null)
 {
     if (grp == COMMODITY_GROUP.LPG_NGL)
     {
         if (vapPress == null)
         {
             vapPress = GetVapPressPsia(api60, tempF) + Conversions.pressAtmPsi;  // From API 11.2.5 estimation
         }
         return(GetCTPLFromAPIDegFPsigLiqGas(api60, tempF, presPsig == null ? 0 : presPsig.Value, vapPress.Value));
     }
     return(GetCTPLFromApiDegFPsigNONLiqGas(grp, api60, tempF, presPsig == null ? 0 : presPsig.Value));
 }
Пример #5
0
        // API 11.1. - Equation (7)
        public double GetDensityFromDensity60(COMMODITY_GROUP grp, double api60, double tempF, double presPsi = 0, double vapPresPsi = 0)
        {
            if (tempF == 60.0 && presPsi == 0.0)
            {
                return(api60);
            }

            // Get CTPL
            double CTPL = GetCTPLFromApiDegFPsig(grp, api60, tempF, presPsi, vapPresPsi);

            // Use to computed API - Remember API is inverse of density
            return(api60 / CTPL);
        }
Пример #6
0
        // Section 11.1.6.1 Step 3 Ki Table
        public KCoeffs GetKCoeffs(COMMODITY_GROUP cgroup, double api60 = 0.0)
        {
            // If density is passed, try to match commodity group to density
            if (api60 > 0.0)
            {
                cgroup = GetKCoeffsGroup(cgroup, api60);
            }

            KCoeffs coeffs = null;

            if (!kCoeffs.TryGetValue(cgroup, out coeffs))
            {
                throw (new ArgumentException(String.Format("Coefficients for COMMODITY_GROUP {0} not found", cgroup)));
            }
            return(coeffs);
        }
Пример #7
0
        // Section 11.1.6.1  CTPL (commonly known as VCF)
        double GetCTPLFromApiDegFPsigNONLiqGas(COMMODITY_GROUP grp, double api60, double tempF, double presPsig = 0)
        {
            // Step 1 - Check range for density,temperature and pressure
            checkRange(api60, "API", grp);
            checkRange(tempF, "degF");
            if (presPsig < 0)
            {
                presPsig = 0;
            }
            checkRange(presPsig, "psig");

            KCoeffs coeffs = GetKCoeffs(grp, api60);

            // Step 2 and 3  - Get corrected temp and density at ITP68 basis
            double tempITPS68 = Conversions.TempITS90toITPS68(tempF, "degF");
            double densITSP68 = Conversions.Api60ITS90tokgm3ITPS68(api60, coeffs);             // kg/m3

            // Step 4 - Compute coefficient of thermal expansion
            double thermExpCoeff60 = GetThermExpCoeff60(densITSP68, coeffs);

            // Step 5 - Compute temperature correction factor
            double CTL = GetCTL(thermExpCoeff60, tempITPS68);

            double CPL = 1.0;             // No compensations

            // If not ATM pressure correct for pressure
            if (presPsig > 0)
            {
                // Step 6 - Compute compressibility factor
                double Fp = GetCompressFactor(densITSP68, tempITPS68);

                // Step 7 - Compute pressure correction
                CPL = GetCPL(Fp, presPsig);
            }

            // Step 8
            double CTPL = CTL * CPL;

            return(RoundUpAPI11_1(CTPL, "CTPL"));
        }
Пример #8
0
        public double GetDensity60FromDensity(COMMODITY_GROUP grp, double api, double tempF, out double CTPL, double presPsi = 0, double vapPresPsi = 0)
        {
            CTPL = 1.0;

            if (tempF == 60.0 && presPsi == 0.0)
            {
                return(api);
            }

            // Guess desired initially set to the same as input density
            double api60      = api;
            double apiGuessed = api;
            // Iterate
            int i = 0;

            do
            {
                if (i > MAXITERATIONS)
                {
                    throw(new OperationCanceledException("Maximum iterations {0} have been exceeded when trying to compute api60 from api"));
                }

                api60 = api60 - 0.61803 * (apiGuessed - api);  // Using golden ratio for simplicity

                // Get CTPL using guessed number
                CTPL = GetCTPLFromApiDegFPsig(grp, api60, tempF, presPsi, vapPresPsi);

                // Get guessed api
                apiGuessed = api60 / CTPL;

                i++;
            }while (CTPL * Math.Abs(api - apiGuessed) > 0.1 * uoms["api"].Precision);  // A little better than precision

            // Check ranges
            checkRange(api60, "api", grp);


            // Return reached at api60
            return(api60);
        }
Пример #9
0
        // API 11.2.4 Section 5.1.1.3
        // Temperature compensation CTL
        public double GetCTLLiqGas(double tempF, double api60)
        {
            // Step T24/3
            // Check Density and Temperature range
            COMMODITY_GROUP grp = COMMODITY_GROUP.LPG_NGL;

            checkRange(api60, "API", grp);
            checkRange(tempF, "degF", grp);             // Group must be specified

            // Step T24/2
            // Convert temp to Kelvin and roundup
            tempF = RoundUp(tempF, -1);
            double Tx = Conversions.DegFtoDegK(tempF);

            // Convert to relative density and roundup
            double relDens60 = Conversions.APItoSG(api60);

            relDens60 = RoundUp(relDens60, -4);

            // Step T24/4
            // Chose reference fluid subscripts (1,2)
            LiqGasProperties f1   = null;
            LiqGasProperties f2   = null;
            LiqGasProperties prev = null;

            foreach (var lgProp in lgProps)
            {
                if (prev != null)
                {
                    if (lgProp.Value.relDens60 >= relDens60)
                    {
                        f2 = lgProp.Value;
                        f1 = prev;
                        break;
                    }
                }
                prev = lgProp.Value;
            }
            if (f2 == null || f1 == null)
            {
                throw (new ArgumentException(String.Format("Relative density {0} is out of the range of API 11.2.4 Table 1", api60)));
            }

            // Step T24/5
            // Compute interpolation variable
            double delta = (relDens60 - f1.relDens60) / (f2.relDens60 - f1.relDens60);

            // Step T24/6
            // Compute interpolated critical temperature
            double Tc = f1.tempCritK + delta * (f2.tempCritK - f1.tempCritK);

            // Step T24/7
            // Compute reduced temperature ratio
            double Trx = Tx / Tc;

            if (Trx > 1)
            {
                throw (new ArgumentException(String.Format("Temperature {0} will result in supercritical conditions which are not supported by this computation", tempF)));
            }

            // Step 24/8
            // Compute reduced temperature at 60F
            double t60K = Conversions.DegFtoDegK(60);
            double Tr60 = t60K / Tc;

            // Step 24/9
            // Compute scaling factor
            double h2 = (f1.comprFactCrit * f1.densCrit) / (f2.comprFactCrit * f2.densCrit);

            // Step 24/10
            // Compute saturation density of both fluids at 60F
            double dens60_1 = getSatDensityAtTemp(Tr60, f1);
            double dens60_2 = getSatDensityAtTemp(Tr60, f2);

            // Step 24/11
            // Calculate interpolating factor
            Func <double, double, double> ratio = (dens1, dens2) => dens1 / (1 + delta * (dens1 / (h2 * dens2) - 1));
            double X = ratio(dens60_1, dens60_2);

            // Step 24/12
            // Opbtain saturation density of both fluids at Trx
            double densX_1 = getSatDensityAtTemp(Trx, f1);
            double densX_2 = getSatDensityAtTemp(Trx, f2);

            // Step 24/13
            // Obtain CTL
            double CTL = ratio(densX_1, densX_2) / X;

            return(CTL);
        }