/// <summary> /// Функция получения точек кривых притока P2 и оттока P1 для узлового анализа сухой скважины /// </summary> /// <param name="num">Число точек</param> /// <param name="P1">Массив значений забойного давления для точек кривой оттока с забоя (МПа)</param> /// <param name="P2">Массив значений забойного давления для точек кривой притока из пласта к забою (МПа)</param> /// <param name="Q">Массив значений дебита газа (тыс.куб.м/сут)</param> public virtual void GetPointsForNodeAnalize(int num, out double[] P1, out double[] P2, out double[] Q) { P1 = new double[num]; P2 = new double[num]; Q = new double[num]; double maxQ = Layer.CalcMaxGasRate(); double step = maxQ / num; double gasQ = 0; //создается новый Tubing чтобы не перезаписывать SegmentFlows Tubing tubing = new Tubing(Tubing); for (int i = 0; i < num; i++) { Q[i] = gasQ; GasFlow gasFlow = new GasFlow(GasFluid, gasQ, WellheadPressure, WellheadTemperature); if (Layer.NGLFactor > 0) { double nglQ = NglFluid.CalcVolumeRate(Layer.NGLFactor, gasQ); LiquidFlow liquidFlow = new LiquidFlow(nglQ, NglFluid); P1[i] = Tubing.CalcBottomholePressure(gasFlow, liquidFlow, BottomholeTemperature); } else { P1[i] = tubing.CalcBottomholePressure(gasFlow, BottomholeTemperature); } P2[i] = Layer.CalcBottomholePressure(gasQ); gasQ += step; } }
/// <summary> /// Функция получения точек кривых притока P2 и оттока P1 для узлового анализа обводненной скважины (без откачки) /// </summary> /// <param name="num">Число точек</param> /// <param name="P1">Массив значений забойного давления для точек кривой оттока с забоя (МПа)</param> /// <param name="P2">Массив значений забойного давления для точек кривой притока из пласта к забою (МПа)</param> /// <param name="gasDischarge">Массив значений дебита газа (тыс.куб.м/сут)</param> /// <param name="waterDischarge">Массив значений дебит воды (м3/сут)</param> public void GetPointsForNodeAnalize(int num, out double[] P1, out double[] P2, out double[] gasDischarge, out double[] waterDischarge) { P1 = new double[num]; P2 = new double[num]; gasDischarge = new double[num]; waterDischarge = new double[num]; double maxQ = Layer.CalcMaxGasRate(); double step = maxQ / num; double gasQ = 0; //создается новый Tubing чтобы не перезаписывать SegmentFlows Tubing tubing = new Tubing(Tubing); for (int i = 0; i < num; i++) { gasDischarge[i] = gasQ; P2[i] = Layer.CalcBottomholePressure(gasQ); double waterQ = Layer.CalcWaterRate(P2[i]); double nglQ = NglFluid.CalcVolumeRate(Layer.NGLFactor, gasQ); LiquidFlow liquidFlow = new LiquidFlow(nglQ, NglFluid, waterQ, WaterFluid); GasFlow gasFlow = new GasFlow(GasFluid, gasQ, WellheadPressure, WellheadTemperature); P1[i] = tubing.CalcBottomholePressure(gasFlow, liquidFlow, BottomholeTemperature); waterDischarge[i] = waterQ; gasQ += step; } }
/// <summary> /// Функция моделирования работы сухой скважины /// </summary> /// <param name="eps">Точность с которой будет расчитываться забойное давление (безразмерная)</param> /// <param name="bottomHolePressure">Забойное давление (МПа)</param> /// <param name="nglRate">Дебит конденсата (т/сут)</param> /// <returns>Дебит газа в стандартных условиях (тыс.куб.м/сут)</returns> public virtual double Modeling(double eps, out double bottomHolePressure, out double nglRate) { double maxQ = Layer.CalcMaxGasRate(); double gasQ = maxQ / 2; double step = gasQ / 2; double P1, P2; do { GasFlow gasFlow = new GasFlow(GasFluid, gasQ, WellheadPressure, WellheadTemperature); if (Layer.NGLFactor > 0) { double nglQ = NglFluid.CalcVolumeRate(Layer.NGLFactor, gasQ); LiquidFlow liquidFlow = new LiquidFlow(nglQ, NglFluid); P1 = Tubing.CalcBottomholePressure(gasFlow, liquidFlow, BottomholeTemperature); } else { P1 = Tubing.CalcBottomholePressure(gasFlow, BottomholeTemperature); } P2 = Layer.CalcBottomholePressure(gasQ); // Если увеличиваем Q, то: // - P1 увеличивается, т.к. устьевое давление константно, а дебит увеличен, следовательно, // увеличивается забойное давление. // - P2 уменьшается, т.к. пластовое давление константно, а приток к забою увеличивается, следовательно, // должно уменьшится забойное давление. // // Аналогично, когда Q уменьшается, то P1 уменьшается а P2 увеличивается. // // Отсюда, если давление P1 больше давления P2, значит нужно брать левое половинное деление, иначе // берем правое половинное значение. if (Math.Abs(P1 - P2) <= eps) { break; } else if (P1 > P2) { gasQ = gasQ - step; } else { gasQ = gasQ + step; } if (gasQ < 0 || gasQ > maxQ) { gasQ = 0; break; } step = step / 2; } while (true); bottomHolePressure = P1; nglRate = CalcNglRate(gasQ); return(gasQ); }
/// <summary> /// Функция моделирования работы плунжерного лифта /// </summary> /// <param name="eps">Точность с которой будет расчитываться забойное давление (безразмерная)</param> /// <param name="plunger">Плунжер</param> public PlungerModelingResult ModelingPlunger(double eps, Plunger plunger) { PlungerModelingResult mr = new PlungerModelingResult(); mr.StartGasRate = Modeling(eps, out mr.StartBtmhPressure, out mr.StartWaterRate, out mr.StartNglRate); double P_avg = (WellheadPressure + mr.StartBtmhPressure) / 2; mr.CritQ = CalcCriticalGasRate(P_avg); //создается новый Tubing чтобы не перезаписывать SegmentFlows Tubing tubing = new Tubing(Tubing); mr.CloseGasRate = (mr.StartGasRate - mr.CritQ) / 2; double step = (mr.StartGasRate - mr.CritQ) / 4; int i = 0; do { i++; mr.CloseBtmhPressure = Layer.CalcBottomholePressure(mr.CloseGasRate); mr.CloseWaterRate = Layer.CalcWaterRate(mr.CloseBtmhPressure); double closeNglVolumeRate = NglFluid.CalcVolumeRate(Layer.NGLFactor, mr.CloseGasRate); mr.CloseNglRate = CalcNglRate(mr.CloseGasRate); LiquidFlow liquidFlow = new LiquidFlow(closeNglVolumeRate, NglFluid, mr.CloseWaterRate, WaterFluid); GasFlow gasFlow = new GasFlow(GasFluid, mr.CloseGasRate, WellheadPressure, WellheadTemperature); //Давление выше столба жидкость высотой h double Ph = tubing.CalcBottomholePressure(gasFlow, liquidFlow, BottomholeTemperature); //разница (P2 - P1) = rho*g*h создается столбом жидкости на забое mr.ColumnHeight = WaterFluid.CalcColumnHeight(mr.CloseBtmhPressure - Ph); // Объем столба воды над плунжером mr.ColumnWaterVolume = WaterFluid.CalcVolumeOfColumnHeight(mr.ColumnHeight, Tubing.CrossSectionArea); //Находим затрубное давление необходимое для поднятия плунжера с водой double Pc_max = plunger.CalcMaxCasingPressure(WellheadPressure, Tubing.Length, mr.ColumnWaterVolume, Tubing.AnnularCrossSectionArea, Tubing.CrossSectionArea); //Забойное давление необходимое для поднятия плунжера с водой mr.OpenBtmhPressure = CalcStaticBottomholePressureByAnnularTubing(Pc_max); if (mr.OpenBtmhPressure < Layer.ReservoirPressure) { return(mr); } if (Ph > mr.CloseBtmhPressure) { mr.CloseGasRate = mr.CloseGasRate - step; } else { mr.CloseGasRate = mr.CloseGasRate + step; } step = step / 2; if (i > 100) { break; } } while (true); mr = new PlungerModelingResult(); return(mr); }