/// <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> /// Функция моделирования работы обводненной скважины с погружным насосом, возвращает дебит газа (тыс.м3/сут) /// </summary> /// <param name="eps">Точность с которой будет расчитываться забойное давление (безразмерная)</param> /// <param name="dynamicH">Динамический уровень пластовой жидкости, отсчитывается от входа в насос (м)</param> /// <param name="bottomHolePressure">Забойное давление (МПа)</param> /// <param name="waterRate">Расход жидкости (м3/сут)</param> /// <param name="nglRate">Дебит газового конденсата (т/сут)</param> /// <returns>Дебит газа в стандартных условиях (тыс.куб.м/сут)</returns> public double ModelingPump(double eps, double dynamicH, out double bottomHolePressure, out double waterRate, out double nglRate) { //Скважина делится на 3 части: //topPartWell - часть затрубного пространства, выше динамического уровня //bottomPartWell - часть затрубного пространства, ниже динамического уровня //innerPartWell - НКТ double topPartLength = Tubing.Length - dynamicH; double dynamicHTemperature = BottomholeTemperature + (WellheadTemperature - BottomholeTemperature) * (dynamicH / Tubing.Length); AnnularTubing = new Tubing(Tubing); AnnularTubing.TubingDiameter = Tubing.CalcEquivalentAnnularDiameter(); //Для затрубного пространства свободного от жидкости создаем эквивалент сухой скважины DryWell topPartWell = new DryWell(this); topPartWell.Tubing = AnnularTubing; topPartWell.BottomholePressure = dynamicHTemperature; double Ph; //давление в точке динамического уровня double gasQ = topPartWell.Modeling(eps, out Ph, out nglRate); bottomHolePressure = Ph + WaterFluid.CalcColumnPressure(dynamicH); //Давление для притока воды берется как давление на середине динамического уровня double waterPress = Ph + WaterFluid.CalcColumnPressure(dynamicH / 2); waterRate = Layer.CalcWaterRate(bottomHolePressure); return(gasQ); }
/// <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> public void InitTest() { WellHeadPressure = 3.18; WellHeadTemperature = 286; BottomholeTemperature = 302.74; Tubing = new Tubing(0.076, 0.000115, 1725, 1725); Fluid = Fluid.GetTestFluid(); Layer = new Layer(4.46, 0.012361, 0.00001427, 273 + 30); }
/// <summary> /// Конструктор копирования DryWell /// </summary> /// <param name="dryWell"></param> public DryWell(DryWell dryWell) { Layer = dryWell.Layer; GasFluid = dryWell.GasFluid; Tubing = new Tubing(dryWell.Tubing); WellheadPressure = dryWell.WellheadPressure; WellheadTemperature = dryWell.WellheadTemperature; BottomholeTemperature = dryWell.BottomholeTemperature; NglFluid = dryWell.NglFluid; }
/// <summary> /// Сухая скважина /// </summary> /// <param name="layer">Пласт</param> /// <param name="gasFluid">Газовый флюид</param> /// <param name="tubing">Обсадная колонна и НКТ</param> /// <param name="wellheadPressure">Устьевое давление (МПа)</param> /// <param name="wellheadTemperature">Устьевая температура (К)</param> /// <param name="bottomholeTemperature">Забойная температура (К)</param> /// <param name="nglFluid">Флюид конденсата</param> public DryWell(Layer layer, GasFluid gasFluid, Tubing tubing, double wellheadPressure, double wellheadTemperature, double bottomholeTemperature, NaturalGasLiquidsFluid nglFluid) { Layer = layer; GasFluid = gasFluid; Tubing = tubing; WellheadPressure = wellheadPressure; WellheadTemperature = wellheadTemperature; BottomholeTemperature = bottomholeTemperature; NglFluid = nglFluid; }
/// <summary> /// Функция высчитывания забойного давления остановленной скважины по затрубному давлению (МПа) /// </summary> public double CalcStaticBottomholePressureByAnnularTubing(double annularWellheadPressure) { AnnularTubing = new Tubing(Tubing); AnnularTubing.TubingDiameter = Tubing.CalcEquivalentAnnularDiameter(); DryWell well = new DryWell(this); well.Tubing = AnnularTubing; well.WellheadPressure = annularWellheadPressure; GasFlow gasFlow = new GasFlow(GasFluid, 0, annularWellheadPressure, WellheadTemperature); double Pb = well.Tubing.CalcStaticBottomholePressure(gasFlow, BottomholeTemperature); return(Pb); }
/// <summary> /// Функция высчитывания затрубного давления на устье остановленной скважины по забойному давлению (МПа) /// </summary> public double CalcStaticAnnularTubingByBottomholePressure(double bottomholePressure) { AnnularTubing = new Tubing(Tubing); AnnularTubing.TubingDiameter = Tubing.CalcEquivalentAnnularDiameter(); DryWell well = new DryWell(this); well.Tubing = AnnularTubing; well.WellheadPressure = 0; well.BottomholePressure = bottomholePressure; GasFlow gasFlow = new GasFlow(GasFluid, 0, bottomholePressure, BottomholeTemperature, true); double annularWellheadPressure = well.Tubing.CalcStaticWellheadPressure(gasFlow, WellheadTemperature); return(annularWellheadPressure); }
/// <summary> /// Функция получения точек кривых притока P2 и оттока P1 по затрубному пространству для узлового анализа /// обводненной скважины (с откачкой насосом) /// </summary> /// <param name="dynamicH">Динамический уровень пластовой жидкости, отсчитывается от входа в насос (м)</param> /// <param name="num">Число точек</param> /// <param name="P1">Массив значений забойного давления для точек кривой оттока с забоя (МПа)</param> /// <param name="P2">Массив значений забойного давления для точек кривой притока из пласта к забою (МПа)</param> /// <param name="gasDischarge">Массив значений дебита газа (тыс.куб.м/сут)</param> /// <param name="waterDischarge">Массив значений дебит воды (м3/сут)</param> public void GetPointsAnnularTubingNodeAnalize(double dynamicH, int num, out double[] P1, out double[] P2, out double[] gasDischarge, out double[] waterDischarge) { double topPartLength = Tubing.Length - dynamicH; double dynamicHTemperature = BottomholeTemperature + (WellheadTemperature - BottomholeTemperature) * (dynamicH / Tubing.Length); 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 annularTubing = new Tubing(AnnularTubing); //Для затрубного пространства свободного от жидкости создаем эквивалент сухой скважины DryWell topPartWell = new DryWell(this); topPartWell.Tubing = annularTubing; topPartWell.BottomholePressure = dynamicHTemperature; for (int i = 0; i < num; i++) { gasDischarge[i] = gasQ; GasFlow gasFlow = new GasFlow(GasFluid, gasQ, WellheadPressure, WellheadTemperature); P2[i] = topPartWell.Layer.CalcBottomholePressure(gasQ); P1[i] = topPartWell.Tubing.CalcBottomholePressure(gasFlow, dynamicHTemperature); //Давление для притока воды берется как давление на середине динамического уровня double waterPres = P2[i] + WaterFluid.CalcColumnPressure(dynamicH / 2); waterDischarge[i] = Layer.CalcWaterRate(waterPres); gasQ += step; } }
/// <summary> /// Обводненная скважина /// </summary> /// <param name="layer">Пласт</param> /// <param name="gasFluid">Газовый флюид</param> /// <param name="tubing">Обсадная колонна и НКТ</param> /// <param name="wellheadPressure">Давление на устье (МПа)</param> /// <param name="wellheadTemperature">Температура на устье (К)</param> /// <param name="bottomholeTemperature">Температура на забое (К)</param> /// <param name="nglFluid">Флиюд газового конденсата</param> public WateredWell(Layer layer, GasFluid gasFluid, Tubing tubing, double wellheadPressure, double wellheadTemperature, double bottomholeTemperature, NaturalGasLiquidsFluid nglFluid) : base(layer, gasFluid, tubing, wellheadPressure, wellheadTemperature, bottomholeTemperature, nglFluid) { }
/// <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); }