├── pwptemp
├── tests
│ ├── __init__.py
│ ├── trajectory1.xlsx
│ ├── test_temp_time.py
│ ├── test_temp_calc.py
│ ├── test_temp_behavior.py
│ ├── test_init_cond.py
│ ├── test_temp.py
│ ├── test_heat_coef.py
│ └── test_set_well.py
├── MANIFEST.in
├── injection
│ ├── __init__.py
│ ├── initcond.py
│ ├── fluid.py
│ ├── main.py
│ ├── heatcoefficients.py
│ ├── linearsystem.py
│ └── input.py
├── production
│ ├── __init__.py
│ ├── initcond.py
│ ├── fluid.py
│ ├── main.py
│ ├── heatcoefficients.py
│ ├── linearsystem.py
│ └── input.py
├── __init__.py
├── heat_coefficients.py
├── inputs.py
├── plot.py
├── linearsystem.py
├── main.py
└── well_system.py
├── pytest.ini
├── setup.cfg
├── requirements.txt
├── docs
├── source
│ ├── figures
│ │ ├── temp_behavior_circ.png
│ │ ├── temp_behavior_drill.png
│ │ ├── temp_profile_circ.png
│ │ └── temp_profile_drill.png
│ ├── installation.rst
│ ├── index.rst
│ ├── conf.py
│ ├── circulating.rst
│ └── drilling.rst
├── Makefile
└── make.bat
├── physics
├── Ideas.md
└── drilling
│ ├── inputs.md
│ └── model.md
├── Tutorial.md
├── .travis.yml
├── setup.py
├── README.md
├── CONTRIBUTING.md
├── CHANGELOG.md
└── LICENSE.md
/pwptemp/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pwptemp/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE.md README.md
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | testpaths = pwptemp/tests
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
--------------------------------------------------------------------------------
/pwptemp/injection/__init__.py:
--------------------------------------------------------------------------------
1 | from .main import temp, input_info
--------------------------------------------------------------------------------
/pwptemp/production/__init__.py:
--------------------------------------------------------------------------------
1 | from .main import temp, input_info
2 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 | plotly
3 | scipy
4 | well_profile==0.5.1
5 | torque_drag==0.0.8
--------------------------------------------------------------------------------
/pwptemp/__init__.py:
--------------------------------------------------------------------------------
1 | from .main import calc_temp, temperature_behavior
2 | from .plot import plot_distribution
3 |
--------------------------------------------------------------------------------
/pwptemp/tests/trajectory1.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pro-well-plan/pwptemp/HEAD/pwptemp/tests/trajectory1.xlsx
--------------------------------------------------------------------------------
/docs/source/figures/temp_behavior_circ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pro-well-plan/pwptemp/HEAD/docs/source/figures/temp_behavior_circ.png
--------------------------------------------------------------------------------
/docs/source/figures/temp_behavior_drill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pro-well-plan/pwptemp/HEAD/docs/source/figures/temp_behavior_drill.png
--------------------------------------------------------------------------------
/docs/source/figures/temp_profile_circ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pro-well-plan/pwptemp/HEAD/docs/source/figures/temp_profile_circ.png
--------------------------------------------------------------------------------
/docs/source/figures/temp_profile_drill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pro-well-plan/pwptemp/HEAD/docs/source/figures/temp_profile_drill.png
--------------------------------------------------------------------------------
/physics/Ideas.md:
--------------------------------------------------------------------------------
1 | # Sharing Ideas
2 |
3 | ## To add
4 | * Production module: new module to generate temperature distributions for production cases.
5 | *
6 | *
7 |
8 | ## To improve
9 | * wellpath.get(): be able to generate more types of wellpaths (horizontal, J and S)
10 | *
11 | *
12 |
--------------------------------------------------------------------------------
/Tutorial.md:
--------------------------------------------------------------------------------
1 | ## TUTORIAL - Using 'pwptemp'
2 |
3 | Check out the [documentation](https://pwptemp.readthedocs.io/en/latest/). There are some examples using *pwptemp*.
4 |
5 | Take also a look at our [web-app](https://share.streamlit.io/jcamiloangarita/opensource_apps/app.py) based on pwptemp and among others.
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 |
3 | python:
4 | - 3.8
5 |
6 | before_install:
7 | - pip install -U pip
8 | - pip install -U pytest
9 |
10 | install:
11 | - pip install -r requirements.txt
12 |
13 | script:
14 | - pytest
15 |
16 | notifications:
17 | email:
18 | recipients:
19 | - juan@prowellplan.com
20 | on_success: change
21 | on_failure: change
--------------------------------------------------------------------------------
/pwptemp/tests/test_temp_time.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import well_profile as wp
3 |
4 | trajectory = wp.load(r'https://github.com/pro-well-plan/pwptemp/raw/master/pwptemp/tests/trajectory1.xlsx',
5 | equidistant=True)
6 |
7 |
8 | class TestMain(TestCase):
9 |
10 | def test_temp_time_injection(self):
11 | pass
12 |
13 | def test_temp_time_production(self):
14 | pass
--------------------------------------------------------------------------------
/pwptemp/tests/test_temp_calc.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import well_profile as wp
3 |
4 | trajectory = wp.load(r'https://github.com/pro-well-plan/pwptemp/raw/master/pwptemp/tests/trajectory1.xlsx',
5 | equidistant=True)
6 |
7 |
8 | class TestLinearSystem(TestCase):
9 |
10 | def test_temp_calc_production(self):
11 | pass
12 |
13 | def test_temp_calc_injection(self):
14 | pass
15 |
--------------------------------------------------------------------------------
/pwptemp/tests/test_temp_behavior.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import pwptemp.production as ptp
3 | import pwptemp.injection as pti
4 | import well_profile as wp
5 |
6 | trajectory = wp.load(r'https://github.com/pro-well-plan/well_profile/raw/master/well_profile/tests/trajectory1.xlsx',
7 | equidistant=True).trajectory
8 |
9 |
10 | class TestMain(TestCase):
11 | def test_temp_behavior(self):
12 | pass
13 |
--------------------------------------------------------------------------------
/pwptemp/tests/test_init_cond.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import well_profile as wp
3 |
4 | trajectory = wp.load(r'https://github.com/pro-well-plan/pwptemp/raw/master/pwptemp/tests/trajectory1.xlsx',
5 | equidistant=True)
6 |
7 |
8 | class TestInitCond(TestCase):
9 |
10 | def test_init_cond_drilling(self):
11 | pass
12 |
13 | def test_init_cond_production(self):
14 | pass
15 |
16 | def test_init_cond_injection(self):
17 | pass
18 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/source/installation.rst:
--------------------------------------------------------------------------------
1 | |bgd|
2 |
3 | .. |bgd| image:: https://github.com/pro-well-plan/opensource_apps/raw/master/resources/pwp-bgd.gif
4 |
5 |
6 | Installation
7 | ============
8 |
9 | pwptemp is written to be compatible with Python 3+. The best way to install is using pip.
10 |
11 | .. code-block:: bash
12 |
13 | $ pip install pwptemp
14 |
15 | This will make sure that all the dependencies are installed. This requirements are listed below.
16 |
17 |
18 | Requirements
19 | ------------
20 |
21 | * `numpy`_
22 | * `torque_drag`_
23 | * `scipy`_
24 | * `plotly`_
25 |
26 | .. _numpy: https://pypi.org/project/numpy/
27 | .. _torque_drag: https://pypi.org/project/torque-drag/
28 | .. _scipy: https://pypi.org/project/scipy/
29 | .. _plotly: https://pypi.org/project/plotly/
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | with open("README.md", "r") as fh:
3 | long_description = fh.read()
4 |
5 | setup(
6 | name='pwptemp',
7 | packages=['pwptemp'],
8 | version='0.3.6',
9 | license='LGPL v3',
10 | description='Well Temperature Distribution',
11 | long_description=long_description,
12 | long_description_content_type="text/markdown",
13 | author='Pro Well Plan AS',
14 | author_email='juan@prowellplan.com',
15 | url='https://github.com/pro-well-plan/pwptemp',
16 | keywords='well temperature distribution',
17 | classifiers=['Programming Language :: Python :: 3',
18 | 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',
19 | 'Natural Language :: English',
20 | 'Topic :: Scientific/Engineering',
21 | 'Topic :: Software Development',
22 | 'Topic :: Software Development :: Libraries',
23 | 'Topic :: Utilities'],
24 | install_requires=['numpy', 'plotly', 'torque_drag', 'scipy', 'well_profile']
25 | )
26 |
--------------------------------------------------------------------------------
/pwptemp/tests/test_temp.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import pwptemp as pt
3 | import pwptemp.production as ptp
4 | import pwptemp.injection as pti
5 | import well_profile as wp
6 |
7 | trajectory = wp.load(r'https://github.com/pro-well-plan/pwptemp/raw/master/pwptemp/tests/trajectory1.xlsx',
8 | equidistant=True).trajectory
9 |
10 |
11 | class TestHC(TestCase):
12 | def test_temp_drilling(self):
13 | t = pt.calc_temp(trajectory)
14 | tdsi = t.temperatures['in_pipe']
15 | tds = t.temperatures['pipe']
16 | ta = t.temperatures['annulus']
17 | tcsg = t.temperatures['casing']
18 | tr = t.temperatures['riser']
19 | tsr = t.temperatures['sr']
20 | tfm = t.temperatures['formation']
21 | self.assertIsInstance(tdsi, list)
22 | self.assertIsInstance(tds, list)
23 | self.assertIsInstance(ta, list)
24 | self.assertIsInstance(tcsg, list)
25 | self.assertIsInstance(tr, list)
26 | self.assertIsInstance(tsr, list)
27 | self.assertIsInstance(tfm, list)
28 |
29 | def test_temp_production(self):
30 | pass
31 |
32 | def test_temp_injection(self):
33 | pass
34 |
--------------------------------------------------------------------------------
/pwptemp/tests/test_heat_coef.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import well_profile as wp
3 |
4 | trajectory = wp.load(r'https://github.com/pro-well-plan/pwptemp/raw/master/pwptemp/tests/trajectory1.xlsx',
5 | equidistant=True).trajectory
6 |
7 |
8 | class TestHC(TestCase):
9 | def test_heat_coef_drilling(self):
10 | from pwptemp import well_system, inputs, heat_coefficients
11 | well = well_system.set_well(inputs.inputs_dict(), trajectory, 'drilling')
12 | well.op = 'drilling'
13 | bit_pos = 1
14 | heat_coefficients.add_heat_coefficients(well, 600, bit_pos)
15 | hc = well.sections
16 | self.assertTrue('comp_N/S' and 'comp_E' and 'comp_HeatSource' and 'comp_time' in hc[0][0]) # Inside Pipe
17 | self.assertTrue('comp_N/S' and 'comp_E' and 'comp_W' and 'comp_time' in hc[1][0]) # Pipe Wall
18 | self.assertTrue('comp_N/S' and 'comp_E' and 'comp_W' and 'comp_time' and 'comp_HeatSource' in hc[2][0])
19 | # Annulus
20 | self.assertTrue('comp_N/S' and 'comp_E' and 'comp_W' and 'comp_time' in hc[3][0]) # First Casing
21 | self.assertTrue('comp_N/S' and 'comp_E' and 'comp_W' and 'comp_time' in hc[4][0]) # Surrounding space
22 |
23 | def test_heat_coef_production(self):
24 | pass
25 |
26 | def test_heat_coef_injection(self):
27 | pass
28 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | |bgd|
2 |
3 | .. |bgd| image:: https://github.com/pro-well-plan/opensource_apps/raw/master/resources/pwp-bgd.gif
4 |
5 |
6 | pwptemp - pwellbore temperature distribution
7 | ============================================
8 |
9 | |Version| |License|
10 |
11 | Find here the documentation of pwptemp. Here you will find all the relevant
12 | information about any function included in this package.
13 |
14 | .. raw:: html
15 |
16 |
17 |
18 |
19 | .. |License| image:: https://img.shields.io/badge/License-LGPL_v3-blue.svg
20 | .. |Version| image:: https://badge.fury.io/py/pwptemp.svg
21 | .. toctree::
22 | :maxdepth: 2
23 | :caption: Contents:
24 |
25 | installation
26 | drilling
27 | circulating
28 |
29 | About Pro Well Plan
30 | -------------------
31 |
32 | Pro Well Plan offers an easy and effective
33 | well planning platform for the entire team.
34 | Check out `our website`_ to know more about us.
35 |
36 | .. raw:: html
37 |
38 |
39 |
40 | .. _our website: https://prowellplan.com/
41 |
--------------------------------------------------------------------------------
/pwptemp/tests/test_set_well.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import well_profile as wp
3 |
4 | trajectory = wp.load(r'https://github.com/pro-well-plan/pwptemp/raw/master/pwptemp/tests/trajectory1.xlsx',
5 | equidistant=True).trajectory
6 |
7 |
8 | class TestSetWell(TestCase):
9 | def test_set_well_drilling(self):
10 | # with casings
11 | from pwptemp import well_system, inputs
12 | casings = [{'od': 9.5, 'id': 8.5, 'depth': 25.0}, {'od': 20.0, 'id': 18.0, 'depth': 10.0}]
13 | tdata = inputs.inputs_dict(casings)
14 | rop_list = [50, 45, 40]
15 | tdata['rop'] = rop_list
16 | well = well_system.set_well(tdata, trajectory, 'drilling')
17 | self.assertEqual(len(well.casings[0]), len(well.casings[1]))
18 | self.assertEqual(len(well.md), len(well.tvd))
19 | del tdata['casings']
20 | for x, value in tdata.items():
21 | if value is None:
22 | value = 0
23 | if value is float:
24 | self.assertEqual(value - value, 0)
25 |
26 | # without casings
27 | tdata = inputs.inputs_dict()
28 | well = well_system.set_well(tdata, trajectory, 'drilling')
29 | self.assertEqual(len(well.md), len(well.tvd))
30 | del tdata['casings']
31 | for x, value in tdata.items():
32 | if value is None:
33 | value = 0
34 | if value is float:
35 | self.assertEqual(value - value, 0)
36 |
37 | def test_set_well_production(self):
38 | pass
39 |
40 | def test_set_well_injection(self):
41 | pass
42 |
--------------------------------------------------------------------------------
/pwptemp/injection/initcond.py:
--------------------------------------------------------------------------------
1 | def init_cond(well):
2 | """
3 | Generates the temperature profiles at time 0, before starting the operation.
4 | :param well: a well object created from the function set_well()
5 | :return: object with initial temperature profiles
6 | """
7 |
8 | # Initial Conditions
9 | Tfto = [well.ts] # Temperature of the fluid inside the tubing at RKB
10 | Tto = [well.ts] # Temperature of the tubing at RKB, t=0
11 | Tao = [well.ts] # Temperature of the fluid inside the annulus at RKB, t=0
12 | Tco = [well.ts] # Temperature of the casing at RKB, t=0
13 | Tsro = [well.ts] # Temperature of the surrounding space at RKB, t=0
14 | Tfm = [well.ts] # Temperature of the formation at RKB
15 |
16 | for j in range(1, well.zstep):
17 |
18 | if j <= well.riser:
19 | Tg = well.wtg # Water Thermal Gradient for the Riser section
20 | else:
21 | Tg = well.gt # Geothermal Gradient below the Riser section
22 |
23 | deltaT = Tsro[j - 1] + Tg*(well.tvd[j]-well.tvd[j-1])/well.deltaz
24 |
25 | # Generating the Temperature Profile at t=0
26 | Tfto.append(deltaT)
27 | Tto.append(deltaT)
28 | Tao.append(deltaT)
29 | Tco.append(deltaT)
30 | Tsro.append(deltaT)
31 | Tfm.append(deltaT)
32 |
33 | class InitCond(object):
34 | def __init__(self):
35 | self.tfto = Tfto
36 | self.tto = Tto
37 | self.tao = Tao
38 | self.tco = Tco
39 | self.tsro = Tsro
40 | self.tfm = Tfm
41 |
42 | return InitCond()
43 |
--------------------------------------------------------------------------------
/pwptemp/production/initcond.py:
--------------------------------------------------------------------------------
1 | def init_cond(well):
2 | """
3 | Generates the temperature profiles at time 0, before starting the operation.
4 | :param well: a well object created from the function set_well()
5 | :return: object with initial temperature profiles
6 | """
7 |
8 | # Initial Conditions
9 | Tfto = [well.ts] # Temperature of the fluid inside the tubing at RKB
10 | Tto = [well.ts] # Temperature of the tubing at RKB, t=0
11 | Tao = [well.ts] # Temperature of the fluid inside the annulus at RKB, t=0
12 | Tco = [well.ts] # Temperature of the casing at RKB, t=0
13 | Tsro = [well.ts] # Temperature of the surrounding space at RKB, t=0
14 | Tfm = [well.ts] # Temperature of the formation at RKB
15 |
16 | for j in range(1, well.zstep):
17 |
18 | if j <= well.riser:
19 | Tg = well.wtg # Water Thermal Gradient for the Riser section
20 | else:
21 | Tg = well.gt # Geothermal Gradient below the Riser section
22 |
23 | deltaT = Tsro[j - 1] + Tg*(well.tvd[j]-well.tvd[j-1])/well.deltaz
24 |
25 | # Generating the Temperature Profile at t=0
26 | Tfto.append(deltaT)
27 | Tto.append(deltaT)
28 | Tao.append(deltaT)
29 | Tco.append(deltaT)
30 | Tsro.append(deltaT)
31 | Tfm.append(deltaT)
32 |
33 | class InitCond(object):
34 | def __init__(self):
35 | self.tfto = Tfto
36 | self.tto = Tto
37 | self.tao = Tao
38 | self.tco = Tco
39 | self.tsro = Tsro
40 | self.tfm = Tfm
41 |
42 | return InitCond()
43 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 | sys.path.insert(0, os.path.abspath('../..'))
16 |
17 |
18 | # -- Project information -----------------------------------------------------
19 |
20 | project = 'pwptemp'
21 | copyright = '2020, Pro Well Plan AS'
22 | author = 'Pro Well Plan AS'
23 |
24 | # The full version, including alpha/beta/rc tags
25 | release = '0.3.1'
26 |
27 |
28 | # -- General configuration ---------------------------------------------------
29 |
30 | # Add any Sphinx extension module names here, as strings. They can be
31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
32 | # ones.
33 | extensions = [
34 | "sphinx.ext.autodoc",
35 | "sphinx.ext.doctest",
36 | "sphinx.ext.intersphinx",
37 | "sphinx.ext.todo",
38 | "sphinx.ext.coverage",
39 | "sphinx.ext.imgmath",
40 | "sphinx.ext.ifconfig",
41 | "sphinx.ext.viewcode",
42 | "sphinx.ext.napoleon"
43 | ]
44 |
45 | # Add any paths that contain templates here, relative to this directory.
46 | templates_path = ['_templates']
47 |
48 | autodoc_member_order = "bysource"
49 |
50 | source_suffix = ".rst"
51 |
52 | master_doc = "index"
53 |
54 | # List of patterns, relative to source directory, that match files and
55 | # directories to ignore when looking for source files.
56 | # This pattern also affects html_static_path and html_extra_path.
57 | exclude_patterns = []
58 |
59 |
60 | # -- Options for HTML output -------------------------------------------------
61 |
62 | # The theme to use for HTML and HTML Help pages. See the documentation for
63 | # a list of builtin themes.
64 | #
65 | import sphinx_rtd_theme
66 | html_theme = 'sphinx_rtd_theme'
67 |
68 | # Add any paths that contain custom static files (such as style sheets) here,
69 | # relative to this directory. They are copied after the builtin static files,
70 | # so a file named "default.css" will overwrite the builtin "default.css".
71 | html_static_path = ['_static']
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://prowellplan.com)
2 |
3 | [](https://github.com/pro-well-plan/pwptemp/blob/master/LICENSE.md)
4 | [](https://badge.fury.io/py/pwptemp)
5 | [](https://www.gnu.org/licenses/lgpl-3.0)
6 | [](https://share.streamlit.io/jcamiloangarita/opensource_apps/app.py)
7 | [](http://pwptemp.readthedocs.io/?badge=latest)
8 | [](https://www.travis-ci.com/pro-well-plan/pwptemp)
9 | [](https://pepy.tech/project/pwptemp)
10 |
11 | ## Contributors
12 |
13 | * **Juan Camilo Gonzalez Angarita** - [jcamiloangarita](https://github.com/jcamiloangarita)
14 | * **Muhammad Suleman** - [msfazal](https://github.com/msfazal)
15 | * **Eirik Lyngvi** - [elyngvi](https://github.com/elyngvi)
16 | * **Magnus Tvedt** - [magnusbj](https://github.com/magnusbj)
17 |
18 | See the full list of [contributors](https://github.com/pro-well-plan/pwptemp/graphs/contributors) involved in this project.
19 |
20 | ## Introduction
21 | Pwptemp is a LGPL licensed library for easy calculation of the
22 | temperature distribution along the well. Features are added as they
23 | are needed; suggestions and contributions of all kinds are very welcome.
24 |
25 | To catch up on the latest development and features, see the [changelog](CHANGELOG.md).
26 |
27 | ## Documentation
28 |
29 | See here for the [complete pwptemp package documentation](https://pwptemp.readthedocs.io/en/latest/).
30 |
31 | ## Getting Started
32 |
33 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
34 |
35 | ### Get pwptemp
36 |
37 | * Users: Wheels for Python from [PyPI](https://pypi.python.org/pypi/pwptemp/)
38 | * `pip install pwptemp`
39 | * Developers: Source code from [Github](https://github.com/pro-well-plan/pwptemp)
40 | * `git clone https://github.com/pro-well-plan/pwptemp`
41 |
42 | ## Quick Use
43 |
44 | Take a look at the [tutorial](https://github.com/pro-well-plan/pwptemp/blob/master/Tutorial.md)
45 | to check how to use the different functions available.
46 |
47 | ## Contributing
48 |
49 | Please read [CONTRIBUTING](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
50 |
51 | ## History ##
52 | Pwptemp was initially written and is maintained by [Pro Well Plan
53 | AS](http://www.prowellplan.com/) as a free, simple, easy-to-use way of getting
54 | temperature data that can be tailored to our needs, and as contribution to the
55 | free software community.
56 |
57 | ## License
58 |
59 | This project is licensed under the GNU Lesser General Public License v3.0 - see the [LICENSE](LICENSE.md) file for details
60 |
61 |
62 | *for further information contact juan@prowellplan.com*
63 |
64 | [](https://prowellplan.com)
65 |
--------------------------------------------------------------------------------
/pwptemp/injection/fluid.py:
--------------------------------------------------------------------------------
1 | def initial_density(well, initcond, section='tubing'):
2 | """
3 | Function to calculate the density profile for the first time step
4 | :param section: 'tubing' or 'annular'
5 | :param well: a well object created from the function set_well()
6 | :param initcond: a initial conditions object with the formation temperature profile
7 | :return: the density profile and the initial density at surface conditions
8 | """
9 |
10 | if section == 'tubing':
11 | beta = well.beta
12 | alpha = well.alpha
13 | rho = well.rhof
14 | if section == 'annular':
15 | beta = well.beta_a
16 | alpha = well.alpha_a
17 | rho = well.rhof_a
18 |
19 | rhof_initial = rho
20 | pressure = [rho * 9.81 * i for i in well.tvd]
21 | rhof = [rhof_initial * (1 + (x - 10 ** 5) / beta - alpha * (y - well.ts)) for x, y in
22 | zip(pressure, initcond.tfto)]
23 | pressure = [x * 9.81 * y for x, y in zip(rhof, well.tvd)]
24 | rhof = [rhof_initial * (1 + (x - 10 ** 5) / beta - alpha * (y - well.ts)) for x, y in
25 | zip(pressure, initcond.tfto)]
26 |
27 | return rhof, rhof_initial
28 |
29 |
30 | def calc_density(well, initcond, rhof_initial, section='tubing'):
31 | """
32 | Function to calculate the density profile
33 | :param section: 'tubing' or 'annular'
34 | :param well: a well object created from the function set_well()
35 | :param initcond: a initial conditions object with the formation temperature profile
36 | :param rhof_initial: initial density at surface conditions
37 | :param flow: boolean to define if the section is flowing
38 | :return: density profile
39 | """
40 |
41 | if section == 'tubing':
42 | beta = well.beta
43 | alpha = well.alpha
44 | flow = True
45 | temp = initcond.tfto
46 | rho = well.rhof
47 | if section == 'annular':
48 | beta = well.beta_a
49 | alpha = well.alpha_a
50 | flow = False
51 | temp = initcond.tao
52 | rho = well.rhof_a
53 |
54 | pressure_h = [x * 9.81 * y for x, y in zip(rho, well.tvd)]
55 |
56 | if flow:
57 | pressure_f = [x * (well.md[-1] / well.dti) * (1/2) * y * well.vp **2 for x, y in zip(well.f_p, rho)]
58 | else:
59 | pressure_f = [0] * len(well.md)
60 |
61 | pressure = [x + y for x, y in zip(pressure_h, pressure_f)]
62 |
63 | rhof = [rhof_initial * (1 + (x - 10 ** 5) / beta - alpha * (y - well.ts)) for x, y in
64 | zip(pressure, temp)]
65 |
66 | return rhof
67 |
68 |
69 | def calc_vicosity(initcond, a=1.856 * 10 ** -11, b=4209, c=0.04527, d=-3.376 * 10 ** -5):
70 | """
71 | Function to calculate the viscosity profile (Reid et al., 1987)
72 | :param initcond: a initial conditions object with the formation temperature profile
73 | :param a: constant
74 | :param b: constant
75 | :param c: constant
76 | :param d: constant
77 | :return: viscosity profile
78 | """
79 |
80 | from math import exp
81 |
82 | visc_t, visc_a = [], []
83 | for x, y in zip(initcond.tfto, initcond.tao):
84 | x += 273.15 # converting °C to K
85 | y += 273.15 # converting °C to K
86 | visc_t.append((a * exp((b / x) + (c * x) + (d * x ** 2)))/1000)
87 | visc_a.append((a * exp((b / y) + (c * y) + (d * y ** 2)))/1000)
88 |
89 | return visc_t, visc_a
90 |
--------------------------------------------------------------------------------
/pwptemp/production/fluid.py:
--------------------------------------------------------------------------------
1 | def initial_density(well, initcond, section='tubing'):
2 | """
3 | Function to calculate the density profile for the first time step
4 | :param section: 'tubing' or 'annular'
5 | :param well: a well object created from the function set_well()
6 | :param initcond: a initial conditions object with the formation temperature profile
7 | :return: the density profile and the initial density at surface conditions
8 | """
9 |
10 | if section == 'tubing':
11 | beta = well.beta
12 | alpha = well.alpha
13 | rho = well.rhof
14 |
15 | if section == 'annular':
16 | beta = well.beta_a
17 | alpha = well.alpha_a
18 | rho = well.rhof_a
19 |
20 | rhof_initial = rho
21 | pressure = [rho * 9.81 * i for i in well.tvd]
22 | rhof = [rhof_initial * (1 + (x - 10 ** 5) / beta - alpha * (y - well.ts)) for x, y in
23 | zip(pressure, initcond.tfto)]
24 | pressure = [x * 9.81 * y for x, y in zip(rhof, well.tvd)]
25 |
26 | rhof = [rhof_initial * (1 + (x - 10 ** 5) / beta - alpha * (y - well.ts)) for x, y in
27 | zip(pressure, initcond.tfto)]
28 |
29 | return rhof, rhof_initial
30 |
31 |
32 | def calc_density(well, initcond, rhof_initial, section='tubing'):
33 | """
34 | Function to calculate the density profile
35 | :param section: 'tubing' or 'annular'
36 | :param well: a well object created from the function set_well()
37 | :param initcond: a initial conditions object with the formation temperature profile
38 | :param rhof_initial: initial density at surface conditions
39 | :param flow: boolean to define if the section is flowing
40 | :return: density profile
41 | """
42 |
43 | if section == 'tubing':
44 | beta = well.beta
45 | alpha = well.alpha
46 | flow = True
47 | temp = initcond.tfto
48 | rho = well.rhof
49 | if section == 'annular':
50 | beta = well.beta_a
51 | alpha = well.alpha_a
52 | flow = False
53 | temp = initcond.tao
54 | rho = well.rhof_a
55 |
56 | pressure_h = [x * 9.81 * y for x, y in zip(rho, well.tvd)]
57 |
58 | if flow:
59 | pressure_f = [x * (well.md[-1] / well.dti) * (1/2) * y * well.vp **2 for x, y in zip(well.f_p, rho)]
60 | else:
61 | pressure_f = [0] * len(well.md)
62 |
63 | pressure = [x + y for x, y in zip(pressure_h, pressure_f)]
64 |
65 | rhof = [rhof_initial * (1 + (x - 10 ** 5) / beta - alpha * (y - well.ts)) for x, y in
66 | zip(pressure, temp)]
67 |
68 | return rhof
69 |
70 |
71 | def calc_vicosity(api, initcond):
72 | """
73 | Function to calculate the viscosity profile (Glaso, 1980)
74 | :param api: API gravity of the produced hydrocarbon
75 | :param initcond: a initial conditions object with the formation temperature profile
76 | :return: viscosity profile
77 | """
78 |
79 | from math import log10
80 |
81 | visc_t, visc_a = [], []
82 | for x, y in zip(initcond.tfto, initcond.tao):
83 | x = (x * 1.8) + 32 # converting °C to °F
84 | y = (y * 1.8) + 32 # converting °C to °F
85 | c = 3.141 * (10 ** 10) * (x ** (-3.444))
86 | d = (10.313 * log10(x)) - 36.447
87 | visc_t.append((c * log10(api) ** d)/1000)
88 | c = 3.141 * (10 ** 10) * (y ** (-3.444))
89 | d = (10.313 * log10(y)) - 36.447
90 | visc_a.append((c * log10(api) ** d)/1000)
91 |
92 | return visc_t, visc_a
--------------------------------------------------------------------------------
/pwptemp/heat_coefficients.py:
--------------------------------------------------------------------------------
1 | from math import pi, log
2 |
3 |
4 | def add_heat_coefficients(well, delta_time, bit_position):
5 | for x, y in enumerate(well.sections[0][:bit_position+1]):
6 | y['comp_N/S'] = ((y['rho'] * y['shc'] * well.vp) / y['cellDepth']) / 2
7 | y['comp_E'] = (2 * well.h1[x] / well.r1) / 2
8 | y['comp_HeatSource'] = calc_heat_source(well, well.torque[x], y['f'], y['rho'], case='pipe',
9 | operation=well.op)
10 | y['comp_time'] = y['rho'] * y['shc'] / delta_time
11 | for x, y in enumerate(well.sections[1][:bit_position+1]):
12 | y['comp_N/S'] = (y['tc'] / (y['cellDepth'] ** 2)) / 2
13 | y['comp_E'] = (2 * well.r2 * well.h2[x] / ((well.r2 ** 2) - (well.r1 ** 2))) / 2
14 | y['comp_W'] = (2 * well.r1 * well.h1[x] / ((well.r2 ** 2) - (well.r1 ** 2))) / 2
15 | y['comp_time'] = y['rho'] * y['shc'] / delta_time
16 | for x, y in enumerate(well.sections[2][:bit_position+1]):
17 | y['comp_N/S'] = (y['rho'] * y['shc'] * well.va / y['cellDepth']) / 2
18 | y['comp_E'] = (2 * well.annular_or * well.h3[x] / ((well.annular_or ** 2) - (well.r2 ** 2))) / 2
19 | y['comp_W'] = (2 * well.r2 * well.h2[x] / ((well.annular_or ** 2) - (well.r2 ** 2))) / 2
20 | y['comp_HeatSource'] = calc_heat_source(well, well.torque[x], y['f'], y['rho'], case='annular',
21 | operation=well.op)
22 | y['comp_time'] = y['rho'] * y['shc'] / delta_time
23 | for x, y in enumerate(well.sections[3][:bit_position+1]):
24 | y['comp_N/S'] = (y['tc'] / (y['cellDepth'] ** 2)) / 2
25 | y['comp_E'] = (2 * y['tc'] / ((well.sr_ir ** 2) - (well.annular_or ** 2))) / 2
26 | y['comp_W'] = (2 * well.annular_or * well.h3[x] / ((well.sr_ir ** 2) - (well.annular_or ** 2))) / 2
27 | y['comp_time'] = y['rho'] * y['shc'] / delta_time
28 | for x, y in enumerate(well.sections[4][:bit_position+1]):
29 | y['comp_N/S'] = (y['tc'] / (y['cellDepth'] ** 2)) / 2
30 | y['comp_E'] = (y['tc'] / (well.sr_or * (well.sr_or - well.sr_ir) * log(well.sr_or / well.sr_ir))) / 2
31 | y['comp_W'] = (y['tc'] / (well.sr_or * (well.sr_or - well.sr_ir) * log(well.fm_rad / well.sr_or))) / 2
32 | y['comp_time'] = y['rho'] * y['shc'] / delta_time
33 |
34 | if well.op == 'drilling':
35 | # heat coefficients at drill bit
36 | joule = 4.1868 # Joule's constant [Nm/cal]
37 | print(bit_position)
38 | bit_cell = well.sections[0][bit_position]
39 | q_bit = (1 / joule) * (1 - well.bit_n) * (well.wob * (well.rop / 3600) + 2 * pi * (well.rpm / 60) * well.tbit) \
40 | + 0.7 * (well.q / 3600) * (bit_cell['rho'] / (2 * 9.81)) * ((well.q / 3600) / (0.95 * well.an)) ** 2
41 | v_bit = well.q / well.an
42 | bit_cell['comp_N/S'] = ((bit_cell['rho'] * bit_cell['shc'] * v_bit) / y['cellDepth']) / 2
43 | bit_cell['comp_E'] = (2 * well.h1[bit_position] / well.annular_or) / 2 # East component
44 | bit_cell['comp_HeatSource'] = q_bit / well.an # Heat source term
45 | bit_cell['comp_time'] = bit_cell['rho'] * bit_cell['shc'] / delta_time # Time component
46 |
47 |
48 | def calc_heat_source(well, torque, f, rho_fluid, case='pipe', operation='drilling'):
49 |
50 | heat_source_term = 0
51 |
52 | if operation == 'drilling':
53 | if case == 'pipe':
54 | qp = 2 * pi * (well.rpm / 60) * torque
55 | + 0.2 * well.q * 2 * f * rho_fluid * (well.vp ** 2) * (well.md[-1] / (well.pipe_id * 127.094 * 10 ** 6))
56 | heat_source_term = qp / (pi * (well.r1 ** 2))
57 |
58 | else:
59 | qa = (0.085 * (2 * well.k * well.md[-1] / ((well.annular_or - well.r2) * (127.094 * 10 ** 6))) *
60 | ((2 * (well.n + 1) * well.q) / (well.n * pi * (well.annular_or + well.r2) *
61 | (well.annular_or - well.r2) ** 2)) ** well.n) * (
62 | 1 + (3 / 2) * well.dp_e ** 2) ** 0.5
63 | heat_source_term = qa / (pi * ((well.annular_or ** 2) - (well.r2 ** 2)))
64 |
65 | return heat_source_term
66 |
--------------------------------------------------------------------------------
/pwptemp/inputs.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | def inputs_dict(casings=None):
5 | inputs = {'temp_inlet': None, # fluid inlet temperature, °C
6 | 'temp_surface': 15.0, # surface temperature, °C
7 | 'water_depth': 0.0, # water depth, m
8 | 'pipe_id': 4.0, # pipe inner diameter, in
9 | 'pipe_od': 4.5, # pipe outer diameter, in
10 | 'riser_id': 17.716, # riser inner diameter, in
11 | 'riser_od': 21.0, # riser outer diameter, in
12 | 'fm_diam': 80.0, # undisturbed formation diameter, in
13 | 'flowrate': 0.79, # flow rate, m3/min
14 | 'time': 10, # operation time if circulating, h
15 |
16 | # thermal conductivities, W / (m *°C)
17 | 'tc_fluid': 0.635, # fluid in pipe
18 | 'tc_csg': 43.3, # casing wall
19 | 'tc_cem': 0.7, # cement
20 | 'tc_pipe': 40.0, # pipe wall
21 | 'tc_fm': 2.249, # formation
22 | 'tc_riser': 15.49, # riser wall
23 | 'tc_seawater': 0.6, # seawater
24 |
25 | # specific heat capacities, J / (kg *°C)
26 | 'shc_fluid': 3713.0, # fluid in pipe
27 | 'shc_csg': 469.0, # casing wall
28 | 'shc_cem': 2000.0, # cement
29 | 'shc_pipe': 400.0, # pipe wall
30 | 'shc_riser': 464.0, # riser wall
31 | 'shc_seawater': 4000.0, # seawater
32 | 'shc_fm': 800.0, # formation
33 |
34 | # densities, sg
35 | 'rho_fluid': 1.198, # fluid in pipe
36 | 'rho_pipe': 7.8, # pipe wall
37 | 'rho_csg': 7.8, # casing wall
38 | 'rho_riser': 7.8, # riser wall
39 | 'rho_fm': 2.245, # formation
40 | 'rho_seawater': 1.029, # seawater
41 | 'rho_cem': 2.7, # cement
42 |
43 | 'th_grad_fm': 0.0238, # geothermal gradient, °C/m
44 | 'th_grad_seawater': -0.005, # seawater thermal gradient, °C/m
45 | 'hole_diam': 0.216, # diameter of open hole section, m
46 | 'rpm': 100.0, # revolutions per minute
47 | 'tbit': 5.0, # torque on the bit, kN*m
48 | 'wob': 90.0, # weight on bit, kN
49 | 'rop': [30.4], # rate of penetration, m/h
50 | 'an': 3100.0, # area of the nozzles, in^2
51 | 'bit_n': 1.0, # drill bit efficiency, 0 to 1
52 | 'dp_e': 0.0, # drill pipe eccentricity
53 | 'thao_o': 1.82, # yield stress, Pa
54 | 'beta': 44983 * 10 ** 5, # isothermal bulk modulus, Pa
55 | 'alpha': 960 * 10 ** -6, # expansion coefficient, 1/°C
56 | 'k': 0.3832, # consistency index, Pa*s^n
57 | 'n': 0.7, # flow behavior index, dimensionless
58 | 'visc': 0.009 # fluid viscosity
59 | }
60 |
61 | dict_with_casings = add_casings(casings, inputs)
62 |
63 | return dict_with_casings
64 |
65 |
66 | def add_casings(casings, inputs):
67 | if casings is None:
68 | inputs['casings'] = [[(inputs['hole_diam'] + inputs['riser_od'] * 0.0254), inputs['hole_diam'], 0]]
69 | inputs['casings'] = np.asarray(inputs['casings'])
70 | else:
71 | csg_od = sorted([x['od'] * 0.0254 for x in casings]) # in to m
72 | csg_id = sorted([x['id'] * 0.0254 for x in casings]) # in to m
73 | depth = sorted([x['depth'] for x in casings], reverse=True)
74 | inputs['casings'] = [[csg_od[x], csg_id[x], depth[x]] for x in range(len(casings))]
75 | inputs['casings'] = np.asarray(inputs['casings'])
76 |
77 | return inputs
78 |
--------------------------------------------------------------------------------
/physics/drilling/inputs.md:
--------------------------------------------------------------------------------
1 | |#| Input | Value | Units |
2 | |-|-----------------------------------------------------------------------|--------|---------|
3 | |1| Inlet Fluid Temperature | 20 |°C |
4 | |2| Surface Temperature | 15 |°C |
5 | |3| Well Depth | - |m |
6 | |4| Water Depth | 0 |m |
7 | |5| Drill Pipe Inner Diameter | 4 |in |
8 | |6| Drill Pipe Outer Diameter | 4.5 |in |
9 | |7| First Casing / Borehole Inner Diameter | 8.5 |in |
10 | |8| Riser Inner Diameter | 17.716|in |
11 | |9| Riser Outer Diameter | 21 |in |
12 | |10| Nearby Formation Diameter | 80 |in |
13 | |11| Circulation Rate | 794.93 |lpm |
14 | |12| Fluid Thermal Conductivity | 0.635 |W/(m°K) |
15 | |13| Casing Thermal Conductivity | 43.3 |W/(m°K) |
16 | |14| Drill Pipe Thermal Conductivity | 40 |W/(m°K) |
17 | |15| Comprehensive Csg-Fm Thermal Conductivity | 2.249 |W/(m°K) |
18 | |16| Riser Thermal Conductivity | 15.49 |W/(m°K) |
19 | |17| Comprehensive Riser-Water Thermal Conductivity | 5 |W/(m°K) |
20 | |18| Seawater Thermal Conductivity | 0.6 ||W/(m°K) |
21 | |19| Fluid Heat Capacity | 3713 |J/(kg°C) |
22 | |20| Casing Heat Capacity | 469 |J/(kg°C) |
23 | |21| Drill Pipe Heat Capacity | 400 |J/(kg°C) |
24 | |22| Riser Heat Capacity | 464 |J/(kg°C) |
25 | |23| Seawater Heat Capacity | 4000 |J/(kg°C) |
26 | |24| Surrounding Space Heat Capacity | 800 |J/(kg°C) |
27 | |25| Convective Heat Transfer Coefficient at the Inner Face of Drill Pipe | 1800 |W/(m^2°C)|
28 | |26| Convective Heat Transfer Coefficient at the Outer Face of Drill Pipe | 2000 |W/(m^2°C)|
29 | |27| Convective Heat Transfer Coefficient at the Inner Face of Casing | 2200 |W/(m^2°C)|
30 | |28| Convective Heat Transfer Coefficient at the Inner Face of Riser | 200 |W/(m^2°C)|
31 | |29| Geothermal Gradient | 0.0238|°C/m |
32 | |30| Seawater Thermal Gradient | -0.005|°C/m |
33 | |31| Fluid Density | 1198 |sg |
34 | |32| Drill Pipe Density | 7.6 |sg |
35 | |33| Casing Density | 7.8 |sg |
36 | |34| Riser Density | 7.8 |sg |
37 | |35| Formation Density | 2.245 |sg |
38 | |36| Seawater Density | 1.029 |sg |
39 | |37| Average Density of Surrouding Formation | - |sg |
40 | |38| Weight on bit (WOB) | 22.41 |kN |
41 | |39| Rate of Penetration (ROP) | 14.4 |m/h |
42 | |40| Area of Drill Bit Nozzles | 3100 |in^2 |
43 | |41| Reynolds number | - |unit less|
44 | |42| Measured Depth at Target | - |m |
45 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Contributing
4 |
5 | When contributing to this repository, please first discuss the change you wish to make via issue,
6 | email, or any other method with the owners of this repository before making a change.
7 |
8 | Please note we have a code of conduct, please follow it in all your interactions with the project.
9 |
10 |
11 | ## Pull Request Process
12 |
13 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
14 | build.
15 | 2. Update the [CHANGELOG](CHANGELOG.md) with details of changes to the interface, this includes new environment
16 | variables, exposed ports, useful file locations and container parameters.
17 | 3. Increase the version numbers in any examples files and the [CHANGELOG](CHANGELOG.md) to the new version that this
18 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
19 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
20 | do not have permission to do that, you may request the second reviewer to merge it for you.
21 |
22 |
23 | ## Code of Conduct
24 |
25 | ### Our Pledge
26 |
27 | In the interest of fostering an open and welcoming environment, we as
28 | contributors and maintainers pledge to making participation in our project and
29 | our community a harassment-free experience for everyone, regardless of age, body
30 | size, disability, ethnicity, gender identity and expression, level of experience,
31 | nationality, personal appearance, race, religion, or sexual identity and
32 | orientation.
33 |
34 | ### Our Standards
35 |
36 | Examples of behavior that contributes to creating a positive environment
37 | include:
38 |
39 | * Using welcoming and inclusive language
40 | * Being respectful of differing viewpoints and experiences
41 | * Gracefully accepting constructive criticism
42 | * Focusing on what is best for the community
43 | * Showing empathy towards other community members
44 |
45 | Examples of unacceptable behavior by participants include:
46 |
47 | * The use of sexualized language or imagery and unwelcome sexual attention or
48 | advances
49 | * Trolling, insulting/derogatory comments, and personal or political attacks
50 | * Public or private harassment
51 | * Publishing others' private information, such as a physical or electronic
52 | address, without explicit permission
53 | * Other conduct which could reasonably be considered inappropriate in a
54 | professional setting
55 |
56 | ### Our Responsibilities
57 |
58 | Project maintainers are responsible for clarifying the standards of acceptable
59 | behavior and are expected to take appropriate and fair corrective action in
60 | response to any instances of unacceptable behavior.
61 |
62 | Project maintainers have the right and responsibility to remove, edit, or
63 | reject comments, commits, code, wiki edits, issues, and other contributions
64 | that are not aligned to this Code of Conduct, or to ban temporarily or
65 | permanently any contributor for other behaviors that they deem inappropriate,
66 | threatening, offensive, or harmful.
67 |
68 | ### Scope
69 |
70 | This Code of Conduct applies both within project spaces and in public spaces
71 | when an individual is representing the project or its community. Examples of
72 | representing a project or community include using an official project e-mail
73 | address, posting via an official social media account, or acting as an appointed
74 | representative at an online or offline event. Representation of a project may be
75 | further defined and clarified by project maintainers.
76 |
77 | ### Enforcement
78 |
79 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
80 | reported by contacting the project team at `juan@prowellplan.com`. All
81 | complaints will be reviewed and investigated and will result in a response that
82 | is deemed necessary and appropriate to the circumstances. The project team is
83 | obligated to maintain confidentiality with regard to the reporter of an incident.
84 | Further details of specific enforcement policies may be posted separately.
85 |
86 | Project maintainers who do not follow or enforce the Code of Conduct in good
87 | faith may face temporary or permanent repercussions as determined by other
88 | members of the project's leadership.
89 |
90 | ### Attribution
91 |
92 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
93 | available at [http://contributor-covenant.org/version/1/4][version]
94 |
95 | [homepage]: http://contributor-covenant.org
96 | [version]: http://contributor-covenant.org/version/1/4/
97 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/)
5 | and this project adheres to [Semantic Versioning](http://semver.org/).
6 |
7 | ## [v0.3.3] - 2021-01-13
8 | ### Fixed
9 | - raise ROP error only for drilling op
10 |
11 | ## [v0.3.2] - 2021-01-12
12 | ### Added
13 | - Input to set the nuber of time steps for the calculations.
14 |
15 | ## [v0.3.1] - 2020-11-19
16 | ### Added
17 | - Filter patch for temperature behavior results
18 |
19 | ## [v0.3.0] - 2020-11-17
20 | ### Added
21 | - Circulating operation
22 | - Temperature_behavior calcs and plot on new structure
23 | - Testing new structure as drilling2
24 | ### Changed
25 | - Drilling is calculated from ROP
26 |
27 | ## [v0.2.9] - 2020-09-15
28 | ### Changed
29 | - Managing annular fluid properties separately
30 | - Using plotly from now, instead of matplotlib
31 | - Wellpath module is deprecated
32 | ### Added
33 | - Input to set the sliding fraction coefficient between DP-wellbore (T&D model)
34 | - Viscosity variation due to changes in temperature
35 | ### Fixed
36 | - Get wellpath from well object
37 |
38 | ## [v0.1.9] - 2020-05-26
39 | ### Added
40 | - More formulas for calculating Nusselt number
41 | - Full integration of Johancsik - Torque & Drag model
42 | - Logging calculations at every time step
43 | ### Fixed
44 | - Loop for torque & drag calculation
45 | - Heat source terms
46 |
47 | ## [v0.1.8] - 2020-05-03
48 | ### Added
49 | - English system of units for modules drilling, production and injection
50 | - Function input_info() for production and injection modules
51 | ### Fixed
52 | - Minor issues
53 |
54 | ## [v0.1.7] - 2020-04-24
55 | ### Added
56 | - Injection module
57 | - Viscosity model for Drilling module
58 | - Effects of the well inclination on the heat transfer
59 | ### Fixed
60 | - Linearsystem for injection module
61 | - Casings integration
62 | - Heat transfer coefficients
63 |
64 | ## [v0.1.6] - 2020-04-04
65 | ### Added
66 | - Attribute plot_multi() for temp_time() function in production module
67 |
68 | ## [v0.1.5] - 2020-04-03
69 | ### Added
70 | - Production Module
71 | - Sub-module for Torque and Drag
72 | - Density Model
73 | - Attribute plot_multi() for temp_time() function in drilling module
74 | ### Changed
75 | - 3D concept for Wellpath module
76 |
77 | ## [v0.1.4] - 2020-02-13
78 | ### Changed
79 | - Units on inputs: diameters [in], flow rate [lpm], area of nuzzles [in2], densities [sg].
80 |
81 | ## [v0.1.2 -> v0.1.3] - 2019-12-15
82 | ### Added
83 | - Horizontal displacement added in wellpath.get() function.
84 | - Method plot() wellpath.get().
85 | - Function `ptd.temps` for calculating at multiple time steps.
86 |
87 | ## [v0.1.1] - 2019-12-02
88 | ### Changed
89 | - Inputs wellpath_mode and wellpath_load_mode are no longer required.
90 | - Function param_effect() now shows contributions by heat source terms and convection/conduction.
91 | ### Fixed
92 | - Data shape recognition in wellpath.load()
93 |
94 | ## [v0.0.9 -> v0.1.0] - 2019-11-19
95 | ### Added
96 | - Function `input.info()`.
97 | - Raise-error messages for the function `input.set_well()`.
98 | ### Changed
99 | - Names of casings-related variables.
100 | - Module `graph` is now module `plot`.
101 | - Function `analysis.param_effect()`, new input `md_length`.
102 | - Function `wellpath.get()`, now it is possible to generate *J-type wells*, *S-type wells* and *Horizontal wells*.
103 | - Function `wellpath.load()`, load a wellpath as a list of dictionaries.
104 | - Function `input.tdict()`, include unlimited number of casings as a list of dictionaries.
105 | ### Fixed
106 | - Module `wellpath`, error when using float numbers.
107 |
108 | ## [v0.0.8] - 2019-10-16
109 | ### Added
110 | - Documentation.
111 | ### Changed
112 | - Physics Module (descriptions regarding modelling).
113 | - Tutorial.
114 | - Tests.
115 | ### Fixed
116 | - Minor issues.
117 |
118 | ## [v0.0.7] - 2019-09-23
119 | ### Added
120 | - Functions `wellpath.get()` and `analysis.hs_ratio()`.
121 | - Analysis Module.
122 | - Testing Module.
123 | - Web server in server.py, runs default script and returns plot to template
124 | - Add environment variable FLASK_APP=server.py
125 | - for continuous updates of web server add FLASK_DEBUG=1
126 | - run `python -m flask run` to start in pwptemp folder
127 | - axis plot for the web server in Graph.py
128 | - Time plot in flask server.py and Graph.py
129 | - form for entry of timesteps in jinja
130 | - added __init__.py to test directory for easier test runs
131 | ### Changed
132 | - Web visualization app moved to [new repo](https://github.com/pro-well-plan/WebVisual-for-pwptemp)
133 | - Stab_time function.
134 | ### Fixed
135 | - Title on plot_temp_time()
136 | - Change of density - Surrounding Space (rhosr)
137 |
138 | ## [v0.0.6] - 2019-08-27
139 | ### Added
140 | ### Changed
141 | ### Fixed
142 | - Minor issues for packaging.
143 |
144 | ## [v0.0.5] - 2019-08-27
145 | ### Added
146 | ### Changed
147 | ### Fixed
148 | - Dictionary with default parameters is now preloaded in Input.py, thus it is easily accessible.
149 |
150 | ## [v0.0.4] - 2019-08-23
151 | ### Added
152 | ### Changed
153 | - Casings shoes depth now included into the class WellTemperature.
154 | ### Fixed
155 |
156 | ## [v0.0.3] - 2019-08-22
157 | ### Added
158 | - New file for description of inputs.
159 | ### Changed
160 | ### Fixed
161 |
162 | ## [v0.0.2] - 2019-08-22
163 | ### Added
164 | ### Changed
165 | ### Fixed
166 | - Minor issues for packaging.
167 |
168 | ## [v0.0.1] - 2019-08-21
169 | ### Added
170 | - Riser section in the temperature profile.
171 | - Surrounding space (from 1st cement sheath up to undisturbed formation point).
172 | ### Changed
173 | - Files distribution for packaging ('pwptemp' in PyPI)
174 | ### Fixed
175 |
--------------------------------------------------------------------------------
/physics/drilling/model.md:
--------------------------------------------------------------------------------
1 | # Heat Transfer Model
2 | The temperature distribution of wellbore and surrounding formation has a significant influence on safe and fast drilling. This transient temperature model investigate the temperature distribution of wellbores during circulation and was established based on the energy conservation law. The model was discretized by the finite difference method and solved by the successive over relaxation
3 | iterative method.
4 |
5 | Heat convection and heat transfer are the main forms of heat exchange of the drilling fluid and the surrounding formation.The formation temperature increases with the increase of vertical depth. In the vertical section, the surrounding formation temperature increases linearly with the increase of the well depth. In the inclined section, the surrounding formation temperature increases more and more slowly as the well depth increases. In the horizontal section, the formation temperature does not change as the well depth increases.
6 |
7 | # Assumptions
8 | (1) The wellbore is a regular cylinder;
9 |
10 | (2) Drilling fluid is incompressible, and the density, specific heat capacity, and thermal conductivity of drilling fluid are constant
11 |
12 | (3) When the drilling fluid flows in the drill string and in the annulus, only the velocity in the axial direction is taken into account, irrespective of the velocity in the radial direction.
13 |
14 | # Inside the drill string
15 | When the drilling fluid is flowing inside the drill string, following three reasons causes the change in internal energy of drilling fluid:
16 | (1) heat transfer within the drilling fluid because of flowing down inside the drill string in the axial direction;
17 | (2) heat convection of the drilling fluid and the inner wall of drill string;
18 | (3) heat generated due to the friction losses of drilling fluid
19 |
20 | Heat transfer control equation inside the drill string is:
21 |
22 |
24 |
25 | # Drill string wall
26 | The changes in internal energy of drill string volume element are caused by following three reasons:
27 | (1) heat conduction of the drill string in the axial direction;
28 | (2) heat convection of the inner wall of drill string and drilling fluid inside the drill string;
29 | (3) heat convection of the outer wall of drill string and the annular drilling fluid.
30 |
31 | Heat transfer control equation of the drill string is given as:
32 |
33 |
35 |
36 |
37 | # In the annulus
38 | When the drilling fluid is flowing in the annulus, changes in internal energy of drilling fluid volume element are caused by four reasons:
39 | (1) heat transfer within the drilling fluid because of flowing up in the annular in the axial direction;
40 | (2) heat convection of drilling fluid and casing or surrounding formation;
41 | (3) heat convection of drilling fluid and outside wall of drill string;
42 | (4) heat produced due to the frictional flow losses of drilling fluid.
43 |
44 | Heat transfer control equation in the annulus is given by the following equation:
45 |
46 |
48 |
49 | # In First layer of casing
50 | Changes in internal energy of volume element of the first layer of casing are caused by three reasons:
51 | (1) heat conduction of the first layer of casing in the axial direction;
52 | (2) heat transfer between the first layer of casing and the first layer of cement sheath;
53 | (3) heat convection between the first layer of casing and the annulus drilling fluid.
54 |
55 | Heat transfer control equation of the first layer of casing is given by the following equation:
56 |
57 |
59 |
60 |
61 | # In Surrounding Space (Casings and Cement Sheaths / formation)
62 | Changes in internal energy of volume element of casing, cement sheath, and surrounding formation are caused by heat conduction in the axial direction and heat transfer between adjacent layers in the radial direction. On the basis of the law of conservation of energy, heat transfer control equation in sorrounding space is given by the following equation:
63 |
64 |
66 |
67 | # whereas:
68 |
69 | 
70 |
71 | 
72 |
73 | 
74 |
75 | 
76 |
77 | 
78 |
79 | 
80 |
81 | 
82 |
83 | 
84 |
85 | ```
86 | References:
87 |
88 | - Zhang, Z., Xiong, Y., & Guo, F. (2018). Analysis of Wellbore Temperature Distribution and Influencing Factors During
89 | Drilling Horizontal Wells. Journal of Energy Resources Technology, 140(9), 092901.
90 |
91 | - Chang, X., Zhou, J., Guo, Y., He, S., Wang, L., Chen, Y., ... & Jian, R. (2018). Heat Transfer Behaviors in Horizontal
92 | Wells Considering the Effects of Drill Pipe Rotation, and Hydraulic and Mechanical Frictions during Drilling Procedures.
93 | Energies, 11(9), 2414.
94 | ```
95 |
96 | **For any discussion/contribution regarding the model please check if there is an
97 | [issue](https://github.com/pro-well-plan/pwptemp/issues) covering the same point, otherwise you can open a new one.**
98 |
--------------------------------------------------------------------------------
/pwptemp/production/main.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | def temp_time(n, well, log=True, units='metric', time_delta=900):
5 | """
6 | Function to calculate the well temperature distribution during certain production time (n)
7 | :param n: production time, hours
8 | :param well: a well object created with the function set_well() from input.py
9 | :param log: save distributions between initial time and circulation time n (each 1 hour)
10 | :param units: system of units ('metric' or 'english')
11 | :param time_delta: duration of each time step (seconds)
12 | :return: a well temperature distribution object
13 | """
14 | from .initcond import init_cond
15 | from .heatcoefficients import heat_coef
16 | from .linearsystem import temp_calc
17 | from ..plot import profile
18 | from math import log, nan
19 | # Simulation main parameters
20 | time = n # circulating time, h
21 | tcirc = time * 3600 # circulating time, s
22 | deltat = 60 * time
23 | if type(time_delta) == int:
24 | deltat = time_delta
25 | tstep = int(tcirc / deltat)
26 | ic = init_cond(well)
27 | tfm = ic.tfm
28 | tt = ic.tto
29 | t3 = ic.tco
30 |
31 | well = well.define_viscosity(ic)
32 | well = well.define_density(ic, cond=0)
33 |
34 | hc = heat_coef(well, deltat, tt, t3)
35 | temp = temp_calc(well, ic, hc)
36 | temp.tft = temp.tt = temp.ta = temp.t3 = temp.tsr = tfm
37 | for x in range(len(tfm)):
38 | if temp.tc[x] != nan:
39 | temp.tc[x] = tfm[x]
40 | if temp.tr[x] != nan:
41 | temp.tr[x] = tfm[x]
42 | if temp.toh[x] != nan:
43 | temp.toh[x] = tfm[x]
44 |
45 | temp_initial = temp
46 | temp_initial.tft = ic.tfm
47 | temp_initial.tt = ic.tfm
48 | temp_initial.ta = ic.tfm
49 |
50 | temp_log = [temp_initial, temp]
51 | time_log = [0, deltat / 3600]
52 |
53 | for x in range(tstep-1):
54 | well = well.define_viscosity(ic)
55 | well = well.define_density(ic, cond=1)
56 |
57 | ic.tfto = temp.tft
58 | ic.tto = temp.tt
59 | ic.tao = temp.ta
60 | ic.tco = temp.t3
61 | ic.tsr = temp.tsr
62 | hc_new = heat_coef(well, deltat, ic.tto, ic.tco)
63 | temp = temp_calc(well, ic, hc_new)
64 |
65 | if units == 'english':
66 | temp.tft_output = [(i/(5/9)+32) for i in temp.tft]
67 | temp.tt_output = [(i/(5/9)+32) for i in temp.tt]
68 | temp.ta_output = [(i/(5/9)+32) for i in temp.ta]
69 | temp.tc_output = [(i/(5/9)+32) for i in temp.tc if type(i) == np.float64]
70 | temp.tr_output = [(i/(5/9)+32) for i in temp.tr if type(i) == np.float64]
71 | temp.tsr_output = [(i/(5/9)+32) for i in temp.tsr]
72 | temp.md_output = [i*3.28 for i in well.md]
73 |
74 | if log:
75 | temp_log.append(temp)
76 | time_log.append(time_log[-1] + time_log[1])
77 |
78 | if units == 'english':
79 | temp.tft = temp.tft_output
80 | temp.tt = temp.tt_output
81 | temp.ta = temp.ta_output
82 | temp.tc = temp.tc_output
83 | temp.tr = temp.tr_output
84 | temp.sr = temp.tsr_output
85 | temp.md = temp.md_output
86 | tfm = [(i / (5 / 9) + 32) for i in tfm]
87 |
88 | class TempDist(object):
89 | def __init__(self):
90 | self.tft = temp.tft
91 | self.tt = temp.tt
92 | self.ta = temp.ta
93 | self.tc = temp.tc
94 | self.tr = temp.tr
95 | self.tsr = temp.tsr
96 | self.tfm = tfm
97 | self.time = time
98 | self.md = well.md
99 | self.riser = well.riser
100 | self.deltat = deltat
101 | self.csgs_reach = temp.csgs_reach
102 | if log:
103 | self.temp_log = temp_log
104 | self.time_log = time_log
105 |
106 | def well(self):
107 | return well
108 |
109 | def plot(self):
110 | return profile(self, units, operation='production')
111 |
112 | def behavior(self):
113 | temp_behavior_production = temp_behavior(self)
114 | return temp_behavior_production
115 |
116 | return TempDist()
117 |
118 |
119 | def temp_behavior(temp_dist):
120 |
121 | ta = [x.ta for x in temp_dist.temp_log]
122 |
123 | tout = []
124 |
125 | for n in range(len(ta)):
126 | tout.append(ta[n][0])
127 |
128 | class Behavior(object):
129 | def __init__(self):
130 | self.finaltime = temp_dist.time
131 | self.tout = tout
132 | self.tfm = temp_dist.tfm
133 | self.time = temp_dist.time_log
134 |
135 | def plot(self):
136 | from ..plot import behavior
137 | return behavior(self, operation='production')
138 |
139 | return Behavior()
140 |
141 |
142 | def temp(well_trajectory, n, casings=[], d_openhole=0.216, change_input={}, log=False, units='metric', time_delta=900):
143 | """
144 | Main function to calculate the well temperature distribution during production operation. This function allows to
145 | set the wellpath and different parameters involved.
146 | :param well_trajectory: wellbore trajectory object
147 | :param n: production time, hours
148 | :param casings: list of dictionaries with casings characteristics (od, id and depth)
149 | :param change_input: dictionary with parameters to set.
150 | :param log: save distributions between initial time and circulation time n (each 1 hour)
151 | :param units: system of units ('metric' or 'english')
152 | :param time_delta: duration of each time step (seconds)
153 | :return: a well temperature distribution object
154 | """
155 | from .input import data, set_well
156 |
157 | tdata = data(casings, d_openhole, units)
158 | for x in change_input: # changing default values
159 | if x in tdata:
160 | tdata[x] = change_input[x]
161 | else:
162 | raise TypeError('%s is not a parameter' % x)
163 |
164 | well = set_well(tdata, well_trajectory, units)
165 | temp_distribution = temp_time(n, well, log, units, time_delta)
166 |
167 | return temp_distribution
168 |
169 |
170 | def input_info(about='all'):
171 | from .input import info
172 | info(about)
173 |
--------------------------------------------------------------------------------
/pwptemp/injection/main.py:
--------------------------------------------------------------------------------
1 | def temp_time(n, well, log=True, units='metric', time_delta=900):
2 | """
3 | Function to calculate the well temperature distribution during certain production time (n)
4 | :param n: production time, hours
5 | :param well: a well object created with the function set_well() from input.py
6 | :param log: save distributions between initial time and circulation time n (each 1 hour)
7 | :param units: system of units ('metric' or 'english')
8 | :param time_delta: duration of each time step (seconds)
9 | :return: a well temperature distribution object
10 | """
11 | from .initcond import init_cond
12 | from .heatcoefficients import heat_coef
13 | from .linearsystem import temp_calc
14 | from ..plot import profile
15 | from math import log, nan
16 | import numpy as np
17 | # Simulation main parameters
18 | time = n # circulating time, h
19 | tcirc = time * 3600 # circulating time, s
20 | deltat = 60 * time
21 | if type(time_delta) == int:
22 | deltat = time_delta
23 | tstep = int(tcirc / deltat)
24 | ic = init_cond(well)
25 | tfm = ic.tfm
26 | tt = ic.tto
27 | t3 = ic.tco
28 |
29 | well = well.define_viscosity(ic)
30 | well = well.define_density(ic, cond=0)
31 |
32 | hc = heat_coef(well, deltat, tt, t3)
33 | temp = temp_calc(well, ic, hc)
34 | temp.tft = temp.tt = temp.ta = temp.t3 = temp.tsr = tfm
35 | for x in range(len(tfm)):
36 | if temp.tc[x] != nan:
37 | temp.tc[x] = tfm[x]
38 | if temp.tr[x] != nan:
39 | temp.tr[x] = tfm[x]
40 | if temp.toh[x] != nan:
41 | temp.toh[x] = tfm[x]
42 |
43 | temp_initial = temp
44 | temp_initial.tft = ic.tfm
45 | temp_initial.tt = ic.tfm
46 | temp_initial.ta = ic.tfm
47 |
48 | temp_log = [temp_initial, temp]
49 | time_log = [0, deltat / 3600]
50 |
51 | for x in range(tstep-1):
52 | well = well.define_viscosity(ic)
53 | well = well.define_density(ic, cond=1)
54 |
55 | ic.tfto = temp.tft
56 | ic.tto = temp.tt
57 | ic.tao = temp.ta
58 | ic.tco = temp.t3
59 | ic.tsr = temp.tsr
60 | hc_new = heat_coef(well, deltat, ic.tto, ic.tco)
61 | temp = temp_calc(well, ic, hc_new)
62 |
63 | if units == 'english':
64 | temp.tft_output = [(i / (5 / 9) + 32) for i in temp.tft]
65 | temp.tt_output = [(i / (5 / 9) + 32) for i in temp.tt]
66 | temp.ta_output = [(i / (5 / 9) + 32) for i in temp.ta]
67 | temp.tc_output = [(i / (5 / 9) + 32) for i in temp.tc if type(i) == np.float64]
68 | temp.tr_output = [(i / (5 / 9) + 32) for i in temp.tr if type(i) == np.float64]
69 | temp.tsr_output = [(i / (5 / 9) + 32) for i in temp.tsr]
70 | temp.md_output = [i * 3.28 for i in well.md]
71 |
72 | if log:
73 | temp_log.append(temp)
74 | time_log.append(time_log[-1] + time_log[1])
75 |
76 | if units == 'english':
77 | temp.tft = temp.tft_output
78 | temp.tt = temp.tt_output
79 | temp.ta = temp.ta_output
80 | temp.tc = temp.tc_output
81 | temp.tr = temp.tr_output
82 | temp.sr = temp.tsr_output
83 | temp.md = temp.md_output
84 | tfm = [(i / (5 / 9) + 32) for i in tfm]
85 |
86 | class TempDist(object):
87 | def __init__(self):
88 | self.tft = temp.tft
89 | self.tt = temp.tt
90 | self.ta = temp.ta
91 | self.tc = temp.tc
92 | self.tr = temp.tr
93 | self.tsr = temp.tsr
94 | self.tfm = tfm
95 | self.time = time
96 | self.md = well.md
97 | self.riser = well.riser
98 | self.deltat = deltat
99 | self.csgs_reach = temp.csgs_reach
100 | if log:
101 | self.temp_log = temp_log
102 | self.time_log = time_log
103 |
104 | def well(self):
105 | return well
106 |
107 | def plot(self):
108 | return profile(self, units, operation='injection')
109 |
110 | def behavior(self):
111 | temp_behavior_injection = temp_behavior(self)
112 | return temp_behavior_injection
113 |
114 | return TempDist()
115 |
116 |
117 | def temp_behavior(temp_dist):
118 |
119 | tft = [x.tft for x in temp_dist.temp_log]
120 |
121 | tbot = []
122 |
123 | for n in range(len(tft)):
124 | tbot.append(tft[n][-1])
125 |
126 | class Behavior(object):
127 | def __init__(self):
128 | self.finaltime = temp_dist.time
129 | self.tbot = tbot
130 | self.tfm = temp_dist.tfm
131 | self.time = temp_dist.time_log
132 |
133 | def plot(self):
134 | from ..plot import behavior
135 | return behavior(self, operation='injection')
136 |
137 | return Behavior()
138 |
139 |
140 | def temp(well_trajectory, n, casings=[], d_openhole=0.216, change_input={}, log=False, units='metric', time_delta=900):
141 | """
142 | Main function to calculate the well temperature distribution during production operation. This function allows to
143 | set the wellpath and different parameters involved.
144 | :param well_trajectory: wellbore trajectory object
145 | :param n: production time, hours
146 | :param casings: list of dictionaries with casings characteristics (od, id and depth)
147 | :param change_input: dictionary with parameters to set.
148 | :param log: save distributions between initial time and circulation time n (each 1 hour)
149 | :param units: system of units ('metric' or 'english')
150 | :param time_delta: duration of each time step (seconds)
151 | :return: a well temperature distribution object
152 | """
153 | from .input import data, set_well
154 |
155 | tdata = data(casings, d_openhole, units)
156 | for x in change_input: # changing default values
157 | if x in tdata:
158 | tdata[x] = change_input[x]
159 | else:
160 | raise TypeError('%s is not a parameter' % x)
161 |
162 | well = set_well(tdata, well_trajectory, units)
163 | temp_distribution = temp_time(n, well, log, units, time_delta)
164 |
165 | return temp_distribution
166 |
167 |
168 | def input_info(about='all'):
169 | from .input import info
170 | info(about)
171 |
--------------------------------------------------------------------------------
/docs/source/circulating.rst:
--------------------------------------------------------------------------------
1 | |bgd|
2 |
3 | .. |bgd| image:: https://github.com/pro-well-plan/opensource_apps/raw/master/resources/pwp-bgd.gif
4 |
5 |
6 | Temperature during Circulating
7 | ==============================
8 |
9 | .. autofunction:: pwptemp.calc_temp
10 |
11 | .. autofunction:: pwptemp.temperature_behavior
12 |
13 | Example
14 | -------
15 |
16 | .. code-block:: python
17 |
18 | >>> import pwptemp as pt
19 | >>> import well_profile as wp
20 |
21 | >>> trajectory = wp.load('trajectory1.xlsx', equidistant=True) # using well_profile to load a trajectory
22 |
23 | >>> casings = [{'od': 12, 'id': 11, 'depth': 1200}, # creating 3 casings with respective parameters
24 | >>> {'od': 10, 'id': 9, 'depth': 1500}, # diameter [in] and depth [m]
25 | >>> {'od': 8, 'id': 7, 'depth': 2400}]
26 |
27 | >>> well = pt.calc_temp(trajectory, # calculate the well temperature distribution using pwptemp
28 | >>> casings,
29 | >>> set_inputs={'water_depth': 0, # water depth [m]
30 | >>> 'temp_inlet': 20, # inlet fluid temperature [°C]
31 | >>> 'time': 10} # circulation time [h]
32 | >>> operation='circulating',
33 |
34 | >>> pt.plot_distribution(well).show()
35 |
36 | |temp_drill|
37 |
38 | |temp_behavior|
39 |
40 | .. |temp_drill| image:: /figures/temp_profile_circ.png
41 | :scale: 85%
42 |
43 | .. |temp_behavior| image:: /figures/temp_behavior_circ.png
44 | :scale: 85%
45 |
46 | The table below shows the available inputs that can be set when using the parameter *set_inputs*
47 |
48 | +------------------+------------------+
49 | | Name | Units |
50 | +==================+==================+
51 | | time | h |
52 | +------------------+------------------+
53 | | temp_inlet | °C |
54 | +------------------+------------------+
55 | | temp_surface | °C |
56 | +------------------+------------------+
57 | | water_depth | in |
58 | +------------------+------------------+
59 | | pipe_id | in |
60 | +------------------+------------------+
61 | | pipe_od | in |
62 | +------------------+------------------+
63 | | riser_id | in |
64 | +------------------+------------------+
65 | | riser_od | in |
66 | +------------------+------------------+
67 | | fm_diam | in |
68 | +------------------+------------------+
69 | | flowrate | m3/min |
70 | +------------------+------------------+
71 | | Thermal Conductivities------------- |
72 | +------------------+------------------+
73 | | tc_fluid | W / (m °C) |
74 | +------------------+------------------+
75 | | tc_csg | W / (m °C) |
76 | +------------------+------------------+
77 | | tc_cem | W / (m °C) |
78 | +------------------+------------------+
79 | | tc_pipe | W / (m °C) |
80 | +------------------+------------------+
81 | | tc_fm | W / (m °C) |
82 | +------------------+------------------+
83 | | tc_riser | W / (m °C) |
84 | +------------------+------------------+
85 | | tc_seawater | W / (m °C) |
86 | +------------------+------------------+
87 | | Specific Heat Capacities----------- |
88 | +------------------+------------------+
89 | | shc_fluid | J / (kg °C) |
90 | +------------------+------------------+
91 | | shc_csg | J / (kg °C) |
92 | +------------------+------------------+
93 | | shc_cem | J / (kg °C) |
94 | +------------------+------------------+
95 | | shc_pipe | J / (kg °C) |
96 | +------------------+------------------+
97 | | shc_riser | J / (kg °C) |
98 | +------------------+------------------+
99 | | shc_seawater | J / (kg °C) |
100 | +------------------+------------------+
101 | | shc_fm | J / (kg °C) |
102 | +------------------+------------------+
103 | | Densities-------------------------- |
104 | +------------------+------------------+
105 | | rho_fluid | sg |
106 | +------------------+------------------+
107 | | rho_pipe | sg |
108 | +------------------+------------------+
109 | | rho_csg | sg |
110 | +------------------+------------------+
111 | | rho_riser | sg |
112 | +------------------+------------------+
113 | | rho_fm | sg |
114 | +------------------+------------------+
115 | | rho_seawater | sg |
116 | +------------------+------------------+
117 | | rho_cem | sg |
118 | +------------------+------------------+
119 | | Others----------------------------- |
120 | +------------------+------------------+
121 | | th_grad_fm | °C/m |
122 | +------------------+------------------+
123 | | th_grad_seawater | °C/m |
124 | +------------------+------------------+
125 | | hole_diam | m |
126 | +------------------+------------------+
127 | | rpm | rev. per min. |
128 | +------------------+------------------+
129 | | tbit | kN*m |
130 | +------------------+------------------+
131 | | wob | kN |
132 | +------------------+------------------+
133 | | rop | m/h |
134 | +------------------+------------------+
135 | | an | in^2 |
136 | +------------------+------------------+
137 | | bit_n | 0 to 1 |
138 | +------------------+------------------+
139 | | dp_e | 0 to 1 |
140 | +------------------+------------------+
141 | | thao_o | Pa |
142 | +------------------+------------------+
143 | | beta | Pa |
144 | +------------------+------------------+
145 | | alpha | 1/°C |
146 | +------------------+------------------+
147 | | k | Pa*s^n |
148 | +------------------+------------------+
149 | | n | dimensionless |
150 | +------------------+------------------+
151 | | visc | cP |
152 | +------------------+------------------+
153 |
154 |
155 | Web Application
156 | ---------------
157 |
158 | There is also the web-app based on pwptemp:
159 |
160 | .. raw:: html
161 |
162 |
--------------------------------------------------------------------------------
/pwptemp/plot.py:
--------------------------------------------------------------------------------
1 | import plotly.graph_objects as go
2 | from numpy import polyfit, poly1d
3 |
4 |
5 | def profile(temp_distribution, units='metric', operation='drilling'):
6 |
7 | pipe_name = {'drilling': 'Drill String',
8 | 'production': 'Production Tubing',
9 | 'injection': 'Injection Tubing'}
10 |
11 | # Plotting Temperature PROFILE
12 |
13 | fig = go.Figure()
14 | md = temp_distribution.md
15 | if units == 'english':
16 | md = [i * 3.28 for i in md]
17 | riser = temp_distribution.riser
18 | csg = temp_distribution.csgs_reach
19 |
20 | if operation == 'drilling':
21 | pipe_fluid = temp_distribution.tdsi
22 |
23 | else:
24 | pipe_fluid = temp_distribution.tft
25 |
26 | fig.add_trace(go.Scatter(x=pipe_fluid, y=md,
27 | mode='lines',
28 | name='Fluid in ' + pipe_name[operation]))
29 | fig.add_trace(go.Scatter(x=temp_distribution.ta, y=md,
30 | mode='lines',
31 | name='Fluid in Annulus'))
32 |
33 | if riser > 0:
34 | fig.add_trace(go.Scatter(x=temp_distribution.tr, y=md,
35 | mode='lines',
36 | name='Riser'))
37 | if csg > 0:
38 | fig.add_trace(go.Scatter(x=temp_distribution.tcsg, y=md,
39 | mode='lines',
40 | name='Casing'))
41 | fig.add_trace(go.Scatter(x=temp_distribution.tfm, y=md,
42 | mode='lines',
43 | name='Formation')) # Temp. due to gradient vs Depth
44 |
45 | if units == 'metric':
46 | fig.update_layout(
47 | xaxis_title='Temperature, °C',
48 | yaxis_title='Depth, m')
49 | else:
50 | fig.update_layout(
51 | xaxis_title='Temperature, °F',
52 | yaxis_title='Depth, ft')
53 |
54 | title = 'Temperature Profile at %1.1f hours' % temp_distribution.time + ' of ' + operation
55 | fig.update_layout(title=title)
56 |
57 | fig.update_yaxes(autorange="reversed")
58 |
59 | return fig
60 |
61 |
62 | def behavior(behavior_obj, operation='drilling'):
63 | """
64 | Plotting Tbottom and Tout through time
65 | """
66 |
67 | fig = go.Figure()
68 |
69 | time = behavior_obj.time
70 |
71 | if behavior_obj.finaltime <= 10:
72 | poly_order = 2
73 | else:
74 | poly_order = 10
75 |
76 | if operation == 'drilling' or operation == 'injection':
77 | tbot_smooth = polyfit(time, behavior_obj.tbot, poly_order)
78 | tbot = poly1d(tbot_smooth)(time)
79 | fig.add_trace(go.Scatter(x=time, y=tbot,
80 | mode='lines',
81 | name='Bottom')) # Temp. Bottom vs Time
82 |
83 | if operation == 'drilling' or operation == 'production':
84 | tout_smooth = polyfit(time, behavior_obj.tout, poly_order)
85 | tout = poly1d(tout_smooth)(time)
86 | fig.add_trace(go.Scatter(x=time, y=tout,
87 | mode='lines',
88 | name='Outlet')) # Temp. Oulet vs Time
89 |
90 | fig.add_trace(go.Scatter(x=time, y=[behavior_obj.tfm[-1]]*len(time),
91 | mode='lines',
92 | name='Formation @ TD')) # Formation Temp. vs Time
93 |
94 | fig.update_layout(
95 | xaxis_title='Time, h',
96 | yaxis_title='Temperature, °C')
97 |
98 | title = 'Temperature behavior (%1.1f hours)' % behavior_obj.finaltime + ' Operation: ' + operation
99 | fig.update_layout(title=title)
100 |
101 | return fig
102 |
103 |
104 | def plot_distribution(temp_distribution, time=None):
105 | """
106 | Plot the well temperature distribution.
107 |
108 | Arguments:
109 | temp_distribution (obj): well object with temperature data
110 | time (int or float): set respective time in hours
111 |
112 | Returns:
113 | plotly figure
114 | """
115 |
116 | if time is None:
117 | time = temp_distribution.time
118 |
119 | operation = temp_distribution.op
120 | pipe_name = {'drilling': 'Drill String',
121 | 'circulating': 'Pipe',
122 | 'production': 'Production Tubing',
123 | 'injection': 'Injection Tubing'}
124 |
125 | # Plotting Temperature PROFILE
126 |
127 | fig = go.Figure()
128 | md = temp_distribution.temperatures['md']
129 | riser = temp_distribution.riser_cells
130 | csg = temp_distribution.casings[0, 2]
131 |
132 | fig.add_trace(go.Scatter(x=temp_distribution.temperatures['in_pipe'], y=md,
133 | mode='lines',
134 | name='Fluid in ' + pipe_name[operation]))
135 |
136 | fig.add_trace(go.Scatter(x=temp_distribution.temperatures['annulus'], y=md,
137 | mode='lines',
138 | name='Fluid in Annulus'))
139 |
140 | if riser > 0:
141 | fig.add_trace(go.Scatter(x=temp_distribution.temperatures['riser'], y=md,
142 | mode='lines',
143 | name='Riser'))
144 | if csg > 0:
145 | fig.add_trace(go.Scatter(x=temp_distribution.temperatures['casing'], y=md,
146 | mode='lines',
147 | name='Casing'))
148 | fig.add_trace(go.Scatter(x=temp_distribution.temperatures['formation'], y=md,
149 | mode='lines',
150 | name='Formation')) # Temp. due to gradient vs Depth
151 |
152 | fig.update_layout(
153 | xaxis_title='Temperature, °C',
154 | yaxis_title='Depth, m')
155 |
156 | title = 'Temperature Profile at %1.1f hours' % time + ' of ' + operation
157 | fig.update_layout(title=title)
158 |
159 | fig.update_yaxes(autorange="reversed")
160 |
161 | return fig
162 |
163 |
164 | def plot_behavior(temp_behavior, title=True):
165 | fig = go.Figure()
166 | fig.add_trace(go.Scatter(x=temp_behavior.time, y=temp_behavior.bottom, mode='lines', name='Bottom'))
167 | fig.add_trace(go.Scatter(x=temp_behavior.time, y=temp_behavior.outlet, mode='lines', name='Outlet - annulus'))
168 | fig.add_trace(go.Scatter(x=temp_behavior.time, y=temp_behavior.max, mode='lines', name='Max. temp'))
169 | fig.add_trace(go.Scatter(x=temp_behavior.time, y=temp_behavior.formation_td, mode='lines', name='Formation at TD'))
170 | fig.update_layout(
171 | xaxis_title='time, h',
172 | yaxis_title='Temperature, °C')
173 | if title:
174 | fig.update_layout(title=str(temp_behavior.time[-1]) + ' hours of operation')
175 |
176 | return fig
177 |
--------------------------------------------------------------------------------
/docs/source/drilling.rst:
--------------------------------------------------------------------------------
1 | |bgd|
2 |
3 | .. |bgd| image:: https://github.com/pro-well-plan/opensource_apps/raw/master/resources/pwp-bgd.gif
4 |
5 |
6 | Temperature during Drilling
7 | ===========================
8 |
9 | .. autofunction:: pwptemp.calc_temp
10 |
11 | .. autofunction:: pwptemp.temperature_behavior
12 |
13 | Example
14 | -------
15 |
16 | .. code-block:: python
17 |
18 | >>> import pwptemp as pt
19 | >>> import well_profile as wp
20 |
21 | >>> trajectory = wp.load('trajectory1.xlsx', equidistant=True) # using well_profile to load a trajectory
22 |
23 | >>> casings = [{'od': 12, 'id': 11, 'depth': 1200}, # creating 3 casings with respective parameters
24 | >>> {'od': 10, 'id': 9, 'depth': 1500}, # diameter [in] and depth [m]
25 | >>> {'od': 8, 'id': 7, 'depth': 2400}]
26 |
27 | >>> rop_list = [50, 45, 40, 35] # setting respective ROP [m/h] for each section
28 |
29 | >>> well = pt.calc_temp(trajectory, # calculate the well temperature distribution using pwptemp
30 | >>> casings,
31 | >>> set_inputs={'water_depth': 0, 'temp_inlet': 20, 'rop':rop_list,
32 | >>> operation='drilling',}
33 |
34 | >>> pt.plot_distribution(well).show()
35 |
36 | |temp_drill|
37 |
38 | |temp_behavior|
39 |
40 | .. |temp_drill| image:: /figures/temp_profile_drill.png
41 | :scale: 85%
42 |
43 | .. |temp_behavior| image:: /figures/temp_behavior_drill.png
44 | :scale: 85%
45 |
46 | .. admonition:: Notice!
47 |
48 | The total time of drilling is calculated based on the ROP's set for the sections. The simulation
49 | assumes a break after each section is drilled (here is when casing is run and cemented) so the temperature
50 | becomes stable again. i.e. for this particular case, it takes 92.9 hours only *drilling* the whole wellbore.
51 |
52 | The table below shows the available inputs that can be set when using the parameter *set_inputs*
53 |
54 | +------------------+------------------+
55 | | Name | Units |
56 | +==================+==================+
57 | | temp_inlet | °C |
58 | +------------------+------------------+
59 | | temp_surface | °C |
60 | +------------------+------------------+
61 | | water_depth | in |
62 | +------------------+------------------+
63 | | pipe_id | in |
64 | +------------------+------------------+
65 | | pipe_od | in |
66 | +------------------+------------------+
67 | | riser_id | in |
68 | +------------------+------------------+
69 | | riser_od | in |
70 | +------------------+------------------+
71 | | fm_diam | in |
72 | +------------------+------------------+
73 | | flowrate | m3/min |
74 | +------------------+------------------+
75 | | Thermal Conductivities------------- |
76 | +------------------+------------------+
77 | | tc_fluid | W / (m °C) |
78 | +------------------+------------------+
79 | | tc_csg | W / (m °C) |
80 | +------------------+------------------+
81 | | tc_cem | W / (m °C) |
82 | +------------------+------------------+
83 | | tc_pipe | W / (m °C) |
84 | +------------------+------------------+
85 | | tc_fm | W / (m °C) |
86 | +------------------+------------------+
87 | | tc_riser | W / (m °C) |
88 | +------------------+------------------+
89 | | tc_seawater | W / (m °C) |
90 | +------------------+------------------+
91 | | Specific Heat Capacities----------- |
92 | +------------------+------------------+
93 | | shc_fluid | J / (kg °C) |
94 | +------------------+------------------+
95 | | shc_csg | J / (kg °C) |
96 | +------------------+------------------+
97 | | shc_cem | J / (kg °C) |
98 | +------------------+------------------+
99 | | shc_pipe | J / (kg °C) |
100 | +------------------+------------------+
101 | | shc_riser | J / (kg °C) |
102 | +------------------+------------------+
103 | | shc_seawater | J / (kg °C) |
104 | +------------------+------------------+
105 | | shc_fm | J / (kg °C) |
106 | +------------------+------------------+
107 | | Densities-------------------------- |
108 | +------------------+------------------+
109 | | rho_fluid | sg |
110 | +------------------+------------------+
111 | | rho_pipe | sg |
112 | +------------------+------------------+
113 | | rho_csg | sg |
114 | +------------------+------------------+
115 | | rho_riser | sg |
116 | +------------------+------------------+
117 | | rho_fm | sg |
118 | +------------------+------------------+
119 | | rho_seawater | sg |
120 | +------------------+------------------+
121 | | rho_cem | sg |
122 | +------------------+------------------+
123 | | Others----------------------------- |
124 | +------------------+------------------+
125 | | th_grad_fm | °C/m |
126 | +------------------+------------------+
127 | | th_grad_seawater | °C/m |
128 | +------------------+------------------+
129 | | hole_diam | m |
130 | +------------------+------------------+
131 | | rpm | rev. per min. |
132 | +------------------+------------------+
133 | | tbit | kN*m |
134 | +------------------+------------------+
135 | | wob | kN |
136 | +------------------+------------------+
137 | | rop | m/h |
138 | +------------------+------------------+
139 | | an | in^2 |
140 | +------------------+------------------+
141 | | bit_n | 0 to 1 |
142 | +------------------+------------------+
143 | | dp_e | 0 to 1 |
144 | +------------------+------------------+
145 | | thao_o | Pa |
146 | +------------------+------------------+
147 | | beta | Pa |
148 | +------------------+------------------+
149 | | alpha | 1/°C |
150 | +------------------+------------------+
151 | | k | Pa*s^n |
152 | +------------------+------------------+
153 | | n | dimensionless |
154 | +------------------+------------------+
155 | | visc | cP |
156 | +------------------+------------------+
157 |
158 |
159 | Web Application
160 | ---------------
161 |
162 | There is also the web-app based on pwptemp:
163 |
164 | .. raw:: html
165 |
166 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/pwptemp/injection/heatcoefficients.py:
--------------------------------------------------------------------------------
1 | def heat_coef(well, deltat, tt, t3):
2 | """
3 | Calculate heat transfer coefficients for each cell.
4 | :param t3: current temperature profile at section 3 (first casing)
5 | :param tt: current temperature profile at tubing wall
6 | :param well: a well object created from the function set_well()
7 | :param deltat: duration of each time step (seconds)
8 | :return: list with distribution of heat transfer coefficients
9 | """
10 |
11 | from math import pi, log
12 | from numpy import interp
13 |
14 | sections = [well.wd]
15 | if len(well.casings) > 0 and well.casings[0, 2] > 0:
16 | for i in range(len(well.casings))[::-1]:
17 | sections.append(well.casings[i, 2])
18 |
19 | vb = well.q / (pi * well.r3 ** 2)
20 | cbz = ((well.rhof[-1] * well.cf * vb) / well.deltaz) / 2 # Vertical component (North-South)
21 | cbe = (2 * well.h1[-1] / well.r3) / 2 # East component
22 | cbt = well.rhof[-1] * well.cf / deltat # Time component
23 |
24 | # Creating empty lists
25 |
26 | # Section 1: Fluid in Tubing
27 | c1z = []
28 | c1e = []
29 | c1 = []
30 | c1t = []
31 |
32 | # Section 2: Tubing Wall
33 | c2z = []
34 | c2e = []
35 | c2w = []
36 | c2t = []
37 |
38 | # Section 3: Fluid in Annulus
39 | c3z = []
40 | c3e = []
41 | c3w = []
42 | c3t = []
43 |
44 | # Section 4: First casing
45 | c4z = []
46 | c4e = []
47 | c4w = []
48 | c4t = []
49 |
50 | # Section 5: Surrounding Space
51 | c5z = []
52 | c5e = []
53 | c5w = []
54 | c5t = []
55 |
56 | in_section = 1
57 | section_checkpoint = sections[0]
58 |
59 | for x in range(well.zstep):
60 | if x*well.deltaz >= section_checkpoint and in_section < len(sections)+1:
61 | in_section += 1
62 | if section_checkpoint != sections[-1]:
63 | section_checkpoint = sections[in_section-1]
64 |
65 | gr_t = 9.81 * well.alpha * abs((tt[x] - t3[x])) * (well.rhof[x] ** 2) * (well.dti ** 3) / (well.visc_a[x] ** 2)
66 | gr_c = 9.81 * well.alpha * abs((tt[x] - t3[x])) * (well.rhof[x] ** 2) * (((well.r3 - well.r2) * 2) ** 3) / (
67 | well.visc_a[x] ** 2)
68 | ra_t = gr_t * well.pr_a[x]
69 | ra_c = gr_c * well.pr_a[x]
70 | inc = [0, 30, 45, 60, 90]
71 | c_base = [0.069, 0.065, 0.059, 0.057, 0.049]
72 | c = interp(well.inclination[x], inc, c_base, right=0.049)
73 | nu_a_t = c * (ra_t ** (1/3)) * (well.pr_a[x] ** 0.074)
74 | nu_a_c = c * (ra_c ** (1/3)) * (well.pr_a[x] ** 0.074)
75 | h2 = well.lambdaf_a * nu_a_t / (well.r2 * log(well.r3/well.r2))
76 | h3 = well.lambdaf_a * nu_a_c / (well.r2 * log(well.r3/well.r2))
77 | h3r = h3
78 | lambdal_eq = well.lambdaf_a * nu_a_t
79 |
80 | # fluid inside tubing
81 | qp = 0.2 * well.q * 2 * (well.f_p[x] * well.rhof[x] * (well.vp ** 2) *
82 | (well.md[-1] / (well.dti * 127.094 * 10 ** 6)))
83 |
84 | c1z.append(((well.rhof[x] * well.cf * well.vp) / well.deltaz) / 2) # Vertical component (North-South)
85 | c1e.append((2 * well.h1[x] / well.r1) / 2) # East component
86 | c1.append(qp / (pi * (well.r1 ** 2))) # Heat source term
87 | c1t.append(well.rhof[x] * well.cf / deltat) # Time component
88 |
89 | # tubing wall
90 | c2z.append((well.lambdat / (well.deltaz ** 2)) / 2) # Vertical component (North-South)
91 | c2e.append((2 * well.r2 * h2 / ((well.r2 ** 2) - (well.r1 ** 2))) / 2) # East component
92 | c2w.append((2 * well.r1 * well.h1[x] / ((well.r2 ** 2) - (well.r1 ** 2))) / 2) # West component
93 | c2t.append(well.rhot * well.ct / deltat) # Time component
94 |
95 | if in_section == 1:
96 | lambda4 = well.lambdar # Thermal conductivity of the casing (riser in this section)
97 | lambda5 = well.lambdaw # Thermal conductivity of the surrounding space (seawater)
98 | lambda45 = (lambda4 * (well.r4r - well.r3r) + lambda5 * (well.r5 - well.r4r)) / (
99 | well.r5 - well.r3r) # Comprehensive Thermal conductivity of the casing (riser) and
100 | # surrounding space (seawater)
101 | lambda56 = well.lambdaw # Comprehensive Thermal conductivity of the surrounding space (seawater) and
102 | # formation (seawater)
103 | c4 = well.cr # Specific Heat Capacity of the casing (riser)
104 | c5 = well.cw # Specific Heat Capacity of the surrounding space (seawater)
105 | rho4 = well.rhor # Density of the casing (riser)
106 | rho5 = well.rhow # Density of the surrounding space (seawater)
107 |
108 | # fluid inside annular
109 | c3z.append((lambdal_eq / (well.deltaz ** 2)) / 2) # Vertical component (North-South)
110 | c3e.append((2 * well.r3 * h3 / ((well.r3 ** 2) - (well.r2 ** 2))) / 2) # East component
111 | c3w.append((2 * well.r2 * h2 / ((well.r3 ** 2) - (well.r2 ** 2))) / 2) # West component
112 | c3t.append(well.rhof_a[x] * well.cf_a / deltat) # Time component
113 |
114 | else:
115 | # fluid inside annular
116 | c3z.append((lambdal_eq / (well.deltaz ** 2)) / 2) # Vertical component (North-South)
117 | c3e.append((2 * well.r3 * h3r / ((well.r3r ** 2) - (well.r2 ** 2))) / 2) # East component
118 | c3w.append((2 * well.r2 * h2 / ((well.r3r ** 2) - (well.r2 ** 2))) / 2) # West component
119 | c3t.append(well.rhof_a[x] * well.cf_a / deltat) # Time component
120 |
121 | if 1 < in_section < len(sections):
122 |
123 | # calculation for surrounding space
124 | # thickness
125 | tcsr = 0
126 | tcem = 0
127 | for i in range(len(well.casings) - in_section):
128 | tcsr += (well.casings[i + 1, 0] - well.casings[i + 1, 1]) / 2
129 | tcem += (well.casings[i + 1, 1] - well.casings[i, 0]) / 2
130 |
131 | tcem += (well.casings[len(well.casings)-in_section+1, 1] -
132 | well.casings[len(well.casings)-in_section, 0]) / 2
133 | if in_section == 2:
134 | tcem += (well.dsro - well.casings[-1, 0])
135 | xcsr = tcsr / (well.r5 - well.r4) # fraction of surrounding space that is casing
136 | xcem = tcem / (well.r5 - well.r4) # fraction of surrounding space that is cement
137 | xfm = 1 - xcsr - xcem # fraction of surrounding space that is formation
138 |
139 | # thermal conductivity
140 | lambdasr = well.lambdac * xcsr + well.lambdacem * xcem + well.lambdafm * xfm
141 | lambdacsr = (well.lambdac * (well.r4 - well.r3) + lambdasr * (well.r5 - well.r4)) / (well.r5 - well.r3)
142 | lambdasrfm = (well.lambdac * (well.r5 - well.r4) + lambdasr * (well.rfm - well.r5)) / (well.rfm - well.r4)
143 |
144 | # Specific Heat Capacity
145 | csr = (well.cc * tcsr + well.ccem * tcem) / (well.r5 - well.r4)
146 |
147 | # Density
148 | rhosr = xcsr * well.rhoc + xcem * well.rhocem + xfm * well.rhofm
149 |
150 | lambda4 = well.lambdac
151 | lambda45 = lambdacsr
152 | lambda5 = lambdasr
153 | lambda56 = lambdasrfm
154 | c4 = well.cc # Specific Heat Capacity of the casing
155 | c5 = csr # Specific Heat Capacity of the surrounding space
156 | rho4 = well.rhoc # Density of the casing
157 | rho5 = rhosr # Density of the surrounding space
158 |
159 | if in_section == len(sections)+1:
160 | lambda4 = well.lambdafm
161 | lambda45 = well.lambdafm
162 | lambda5 = well.lambdafm
163 | lambda56 = well.lambdafm
164 | c4 = well.cfm # Specific Heat Capacity of the casing (formation)
165 | c5 = well.cfm # Specific Heat Capacity of the surrounding space (formation)
166 | rho4 = well.rhofm # Density of the casing (formation)
167 | rho5 = well.rhofm # Density of the surrounding space (formation)
168 |
169 | # first casing wall
170 | c4z.append((lambda4 / (well.deltaz ** 2)) / 2)
171 | c4e.append((2 * lambda45 / ((well.r4 ** 2) - (well.r3 ** 2))) / 2)
172 | c4w.append((2 * well.r3 * h3 / ((well.r4 ** 2) - (well.r3 ** 2))) / 2)
173 | c4t.append(rho4 * c4 / deltat)
174 |
175 | # surrounding space
176 | c5z.append((lambda5 / (well.deltaz ** 2)) / 2)
177 | c5w.append((lambda56 / (well.r5 * (well.r5 - well.r4) * log(well.r5 / well.r4))) / 2)
178 | c5e.append((lambda56 / (well.r5 * (well.r5 - well.r4) * log(well.rfm / well.r5))) / 2)
179 | c5t.append(rho5 * c5 / deltat)
180 |
181 | hc_1 = [c1z, c1e, c1, c1t]
182 | hc_2 = [c2z, c2e, c2w, c2t]
183 | hc_3 = [c3z, c3e, c3w, c3t]
184 | hc_4 = [c4z, c4e, c4w, c4t]
185 | hc_5 = [c5z, c5e, c5w, c5t]
186 | coefficients = [hc_1, hc_2, hc_3, hc_4, hc_5, cbe, cbt, cbz]
187 |
188 | return coefficients
189 |
190 |
--------------------------------------------------------------------------------
/pwptemp/production/heatcoefficients.py:
--------------------------------------------------------------------------------
1 | def heat_coef(well, deltat, tt, t3):
2 | """
3 | Calculate heat transfer coefficients for each cell.
4 | :param t3: current temperature profile at section 3 (first casing)
5 | :param tt: current temperature profile at tubing wall
6 | :param well: a well object created from the function set_well()
7 | :param deltat: duration of each time step (seconds)
8 | :return: list with distribution of heat transfer coefficients
9 | """
10 |
11 | from math import pi, log
12 | from numpy import interp
13 |
14 | sections = [well.wd]
15 | if len(well.casings) > 0 and well.casings[0, 2] > 0:
16 | for i in range(len(well.casings))[::-1]:
17 | sections.append(well.casings[i, 2])
18 |
19 | vb = well.q / (pi * well.r3 ** 2)
20 | cbz = ((well.rhof[-1] * well.cf * vb) / well.deltaz) / 2 # Vertical component (North-South)
21 | cbe = (2 * well.h1[-1] / well.r3) / 2 # East component
22 | cbt = well.rhof[-1] * well.cf / deltat # Time component
23 |
24 | # Creating empty lists
25 |
26 | # Section 1: Fluid in Tubing
27 | c1z = []
28 | c1e = []
29 | c1 = []
30 | c1t = []
31 |
32 | # Section 2: Tubing Wall
33 | c2z = []
34 | c2e = []
35 | c2w = []
36 | c2t = []
37 |
38 | # Section 3: Fluid in Annulus
39 | c3z = []
40 | c3e = []
41 | c3w = []
42 | c3t = []
43 |
44 | # Section 4: First casing
45 | c4z = []
46 | c4e = []
47 | c4w = []
48 | c4t = []
49 |
50 | # Section 5: Surrounding Space
51 | c5z = []
52 | c5e = []
53 | c5w = []
54 | c5t = []
55 |
56 | in_section = 1
57 | section_checkpoint = sections[0]
58 |
59 | for x in range(well.zstep):
60 | if x*well.deltaz >= section_checkpoint and in_section < len(sections)+1:
61 | in_section += 1
62 | if section_checkpoint != sections[-1]:
63 | section_checkpoint = sections[in_section-1]
64 |
65 | gr_t = 9.81 * well.alpha * abs((tt[x] - t3[x])) * (well.rhof_a[x] ** 2) * (well.dti ** 3) / (well.visc_a[x]**2)
66 | gr_c = 9.81 * well.alpha * abs((tt[x] - t3[x])) * (well.rhof_a[x] ** 2) * (((well.r3 - well.r2) * 2) ** 3) / (
67 | well.visc_a[x] ** 2)
68 | ra_t = gr_t * well.pr_a[x]
69 | ra_c = gr_c * well.pr_a[x]
70 | inc = [0, 30, 45, 60, 90]
71 | c_base = [0.069, 0.065, 0.059, 0.057, 0.049]
72 | c = interp(well.inclination[x], inc, c_base, right=0.049)
73 | nu_a_t = c * (ra_t ** (1/3)) * (well.pr_a[x] ** 0.074)
74 | nu_a_c = c * (ra_c ** (1/3)) * (well.pr_a[x] ** 0.074)
75 | h2 = well.lambdaf_a * nu_a_t / (well.r2 * log(well.r3/well.r2))
76 | h3 = well.lambdaf_a * nu_a_c / (well.r2 * log(well.r3/well.r2))
77 | h3r = h3
78 | lambdal_eq = well.lambdaf_a * nu_a_t
79 |
80 | # fluid inside tubing
81 | qp = 0.2 * well.q * 2 * (well.f_p[x] * well.rhof[x] * (well.vp ** 2) *
82 | (well.md[-1] / (well.dti * 127.094 * 10 ** 6)))
83 |
84 | c1z.append(((well.rhof[x] * well.cf * well.vp) / well.deltaz) / 2) # Vertical component (North-South)
85 | c1e.append((2 * well.h1[x] / well.r1) / 2) # East component
86 | c1.append(qp / (pi * (well.r1 ** 2))) # Heat source term
87 | c1t.append(well.rhof[x] * well.cf / deltat) # Time component
88 |
89 | # tubing wall
90 | c2z.append((well.lambdat / (well.deltaz ** 2)) / 2) # Vertical component (North-South)
91 | c2e.append((2 * well.r2 * h2 / ((well.r2 ** 2) - (well.r1 ** 2))) / 2) # East component
92 | c2w.append((2 * well.r1 * well.h1[x] / ((well.r2 ** 2) - (well.r1 ** 2))) / 2) # West component
93 | c2t.append(well.rhot * well.ct / deltat) # Time component
94 |
95 | if in_section == 1:
96 | lambda4 = well.lambdar # Thermal conductivity of the casing (riser in this section)
97 | lambda5 = well.lambdaw # Thermal conductivity of the surrounding space (seawater)
98 | lambda45 = (lambda4 * (well.r4r - well.r3r) + lambda5 * (well.r5 - well.r4r)) / (
99 | well.r5 - well.r3r) # Comprehensive Thermal conductivity of the casing (riser) and
100 | # surrounding space (seawater)
101 | lambda56 = well.lambdaw # Comprehensive Thermal conductivity of the surrounding space (seawater) and
102 | # formation (seawater)
103 | c4 = well.cr # Specific Heat Capacity of the casing (riser)
104 | c5 = well.cw # Specific Heat Capacity of the surrounding space (seawater)
105 | rho4 = well.rhor # Density of the casing (riser)
106 | rho5 = well.rhow # Density of the surrounding space (seawater)
107 |
108 | # fluid inside annular
109 | c3z.append((lambdal_eq / (well.deltaz ** 2)) / 2) # Vertical component (North-South)
110 | c3e.append((2 * well.r3 * h3 / ((well.r3 ** 2) - (well.r2 ** 2))) / 2) # East component
111 | c3w.append((2 * well.r2 * h2 / ((well.r3 ** 2) - (well.r2 ** 2))) / 2) # West component
112 | c3t.append(well.rhof_a[x] * well.cf_a / deltat) # Time component
113 |
114 | else:
115 | # fluid inside annular
116 | c3z.append((lambdal_eq / (well.deltaz ** 2)) / 2) # Vertical component (North-South)
117 | c3e.append((2 * well.r3 * h3r / ((well.r3r ** 2) - (well.r2 ** 2))) / 2) # East component
118 | c3w.append((2 * well.r2 * h2 / ((well.r3r ** 2) - (well.r2 ** 2))) / 2) # West component
119 | c3t.append(well.rhof_a[x] * well.cf_a / deltat) # Time component
120 |
121 | if 1 < in_section < len(sections):
122 |
123 | # calculation for surrounding space
124 | # thickness
125 | tcsr = 0
126 | tcem = 0
127 | for i in range(len(well.casings) - in_section):
128 | tcsr += (well.casings[i + 1, 0] - well.casings[i + 1, 1]) / 2
129 | tcem += (well.casings[i + 1, 1] - well.casings[i, 0]) / 2
130 |
131 | tcem += (well.casings[len(well.casings) - in_section + 1, 1] -
132 | well.casings[len(well.casings) - in_section, 0]) / 2
133 | if in_section == 2:
134 | tcem += (well.dsro - well.casings[-1, 0])
135 | xcsr = tcsr / (well.r5 - well.r4) # fraction of surrounding space that is casing
136 | xcem = tcem / (well.r5 - well.r4) # fraction of surrounding space that is cement
137 | xfm = 1 - xcsr - xcem # fraction of surrounding space that is formation
138 |
139 | # thermal conductivity
140 | lambdasr = well.lambdac * xcsr + well.lambdacem * xcem + well.lambdafm * xfm
141 | lambdacsr = (well.lambdac * (well.r4 - well.r3) + lambdasr * (well.r5 - well.r4)) / (well.r5 - well.r3)
142 | lambdasrfm = (well.lambdac * (well.r5 - well.r4) + lambdasr * (well.rfm - well.r5)) / (well.rfm - well.r4)
143 |
144 | # Specific Heat Capacity
145 | csr = (well.cc * tcsr + well.ccem * tcem) / (well.r5 - well.r4)
146 |
147 | # Density
148 | rhosr = xcsr * well.rhoc + xcem * well.rhocem + xfm * well.rhofm
149 |
150 | lambda4 = well.lambdac
151 | lambda45 = lambdacsr
152 | lambda5 = lambdasr
153 | lambda56 = lambdasrfm
154 | c4 = well.cc # Specific Heat Capacity of the casing
155 | c5 = csr # Specific Heat Capacity of the surrounding space
156 | rho4 = well.rhoc # Density of the casing
157 | rho5 = rhosr # Density of the surrounding space
158 |
159 | if in_section == len(sections)+1:
160 | lambda4 = well.lambdafm
161 | lambda45 = well.lambdafm
162 | lambda5 = well.lambdafm
163 | lambda56 = well.lambdafm
164 | c4 = well.cfm # Specific Heat Capacity of the casing (formation)
165 | c5 = well.cfm # Specific Heat Capacity of the surrounding space (formation)
166 | rho4 = well.rhofm # Density of the casing (formation)
167 | rho5 = well.rhofm # Density of the surrounding space (formation)
168 |
169 | # first casing wall
170 | c4z.append((lambda4 / (well.deltaz ** 2)) / 2)
171 | c4e.append((2 * lambda45 / ((well.r4 ** 2) - (well.r3 ** 2))) / 2)
172 | c4w.append((2 * well.r3 * h3 / ((well.r4 ** 2) - (well.r3 ** 2))) / 2)
173 | c4t.append(rho4 * c4 / deltat)
174 |
175 | # surrounding space
176 | c5z.append((lambda5 / (well.deltaz ** 2)) / 2)
177 | c5w.append((lambda56 / (well.r5 * (well.r5 - well.r4) * log(well.r5 / well.r4))) / 2)
178 | c5e.append((lambda56 / (well.r5 * (well.r5 - well.r4) * log(well.rfm / well.r5))) / 2)
179 | c5t.append(rho5 * c5 / deltat)
180 |
181 | hc_1 = [c1z, c1e, c1, c1t]
182 | hc_2 = [c2z, c2e, c2w, c2t]
183 | hc_3 = [c3z, c3e, c3w, c3t]
184 | hc_4 = [c4z, c4e, c4w, c4t]
185 | hc_5 = [c5z, c5e, c5w, c5t]
186 | coefficients = [hc_1, hc_2, hc_3, hc_4, hc_5, cbe, cbt, cbz]
187 |
188 | return coefficients
189 |
190 |
--------------------------------------------------------------------------------
/pwptemp/linearsystem.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from .heat_coefficients import add_heat_coefficients
3 |
4 |
5 | def define_system_section0(well, sections, bit_position):
6 | for x, y in enumerate(sections[0][:bit_position+1]):
7 | if x == 0:
8 | y['N'] = 0
9 | y['W'] = 0
10 | y['C'] = 0
11 | y['E'] = 0
12 | y['S'] = 0
13 | y['B'] = 0
14 | y['temp'] = well.temp_inlet
15 |
16 | if x == 1:
17 | y['N'] = 0
18 | y['W'] = 0
19 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_N/S']
20 | y['E'] = - y['comp_E']
21 | y['S'] = 0
22 | y['B'] = y['comp_time'] * y['temp'] \
23 | + y['comp_HeatSource'] \
24 | + y['comp_E'] * (sections[1][x]['temp'] - y['temp']) \
25 | + y['comp_N/S'] * (sections[0][x - 1]['temp'] - y['temp']) \
26 | + y['comp_N/S'] * (sections[0][x - 1]['temp'])
27 |
28 | if 1 < x <= bit_position:
29 | y['N'] = - y['comp_N/S']
30 | y['W'] = 0
31 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_N/S']
32 | y['E'] = - y['comp_E']
33 | y['S'] = 0
34 | y['B'] = y['comp_time'] * y['temp'] \
35 | + y['comp_HeatSource'] \
36 | + y['comp_E'] * (sections[1][x]['temp'] - y['temp']) \
37 | + y['comp_N/S'] * (sections[0][x - 1]['temp'] - y['temp'])
38 |
39 | return well
40 |
41 |
42 | def define_system_section1(well, sections, bit_position):
43 | for x, y in enumerate(sections[1][:bit_position+1]):
44 | if x == 0:
45 | y['N'] = 0
46 | y['W'] = 0
47 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + y['comp_N/S']
48 | y['E'] = - y['comp_E']
49 | y['S'] = - y['comp_N/S']
50 | y['B'] = y['comp_time'] * y['temp'] \
51 | + y['comp_E'] * (sections[2][x]['temp'] - y['temp']) \
52 | + y['comp_W'] * (sections[0][x]['temp'] - y['temp']) \
53 | + y['comp_W'] * sections[0][x]['temp'] \
54 | + y['comp_N/S'] * (sections[1][x + 1]['temp'] - y['temp'])
55 |
56 | if 0 < x < bit_position:
57 | y['N'] = - y['comp_N/S']
58 | y['W'] = - y['comp_W']
59 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + 2 * y['comp_N/S']
60 | y['E'] = - y['comp_E']
61 | y['S'] = - y['comp_N/S']
62 | y['B'] = y['comp_time'] * y['temp'] \
63 | + y['comp_E'] * (sections[2][x]['temp'] - y['temp']) \
64 | + y['comp_W'] * (sections[0][x]['temp'] - y['temp']) \
65 | + y['comp_N/S'] * (sections[1][x - 1]['temp'] - y['temp']) \
66 | + y['comp_N/S'] * (sections[1][x + 1]['temp'] - y['temp'])
67 |
68 | if x == bit_position:
69 | y['N'] = 0
70 | y['W'] = 0
71 | y['C'] = 0
72 | y['E'] = 0
73 | y['S'] = 0
74 | y['B'] = 0
75 |
76 | return well
77 |
78 |
79 | def define_system_section2(well, sections, bit_position):
80 | for x, y in enumerate(sections[2][:bit_position+1]):
81 | if x < len(well.trajectory) - 1:
82 | y['N'] = 0
83 | y['W'] = - y['comp_W']
84 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + y['comp_N/S']
85 | y['E'] = - y['comp_E']
86 | y['S'] = - y['comp_N/S']
87 | y['B'] = y['comp_time'] * y['temp'] \
88 | + y['comp_HeatSource'] \
89 | + y['comp_E'] * (sections[3][x]['temp'] - y['temp']) \
90 | + y['comp_W'] * (sections[1][x]['temp'] - y['temp']) \
91 | + y['comp_N/S'] * (sections[2][x + 1]['temp'] - y['temp'])
92 |
93 | if x == bit_position:
94 | y['N'] = 0
95 | y['W'] = 0
96 | y['C'] = 0
97 | y['E'] = 0
98 | y['S'] = 0
99 | y['B'] = 0
100 |
101 | return well
102 |
103 |
104 | def define_system_section3(well, sections, bit_position):
105 | for x, y in enumerate(sections[3][:bit_position+1]):
106 | if x == 0:
107 | y['N'] = 0
108 | y['W'] = - y['comp_W']
109 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + y['comp_N/S']
110 | y['E'] = - y['comp_E']
111 | y['S'] = - y['comp_N/S']
112 | y['B'] = y['comp_time'] * y['temp'] \
113 | + y['comp_E'] * (sections[4][x]['temp'] - y['temp']) \
114 | + y['comp_W'] * (sections[2][x]['temp'] - y['temp']) \
115 | + y['comp_N/S'] * (sections[3][x + 1]['temp'] - y['temp'])
116 |
117 | if 0 < x < bit_position:
118 | y['N'] = - y['comp_N/S']
119 | y['W'] = - y['comp_W']
120 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + 2 * y['comp_N/S']
121 | y['E'] = - y['comp_E']
122 | y['S'] = - y['comp_N/S']
123 | y['B'] = y['comp_time'] * y['temp'] \
124 | + y['comp_E'] * (sections[4][x]['temp'] - y['temp']) \
125 | + y['comp_W'] * (sections[2][x]['temp'] - y['temp']) \
126 | + y['comp_N/S'] * (sections[3][x - 1]['temp'] - y['temp']) \
127 | + y['comp_N/S'] * (sections[3][x + 1]['temp'] - y['temp'])
128 |
129 | if x == bit_position:
130 | y['N'] = - y['comp_N/S']
131 | y['W'] = - y['comp_W']
132 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + y['comp_N/S']
133 | y['E'] = - y['comp_E']
134 | y['S'] = 0
135 | y['B'] = y['comp_time'] * y['temp'] \
136 | + y['comp_E'] * (sections[4][x]['temp'] - y['temp']) \
137 | + y['comp_W'] * (sections[2][x]['temp'] - y['temp']) \
138 | + y['comp_N/S'] * (sections[3][x - 1]['temp'] - y['temp'])
139 |
140 | return well
141 |
142 |
143 | def define_system_section4(well, sections, bit_position):
144 | for x, y in enumerate(sections[4][:bit_position+1]):
145 | if x == 0:
146 | y['N'] = 0
147 | y['W'] = - y['comp_W']
148 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + y['comp_N/S']
149 | y['E'] = 0
150 | y['S'] = - y['comp_N/S']
151 | y['B'] = y['comp_time'] * y['temp'] \
152 | + y['comp_E'] * y['temp_fm'] \
153 | + y['comp_W'] * (sections[3][x]['temp'] - y['temp']) \
154 | + y['comp_N/S'] * (sections[4][x + 1]['temp'] - y['temp'])
155 |
156 | if 0 < x < bit_position:
157 | y['N'] = - y['comp_N/S']
158 | y['W'] = - y['comp_W']
159 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + 2 * y['comp_N/S']
160 | y['E'] = 0
161 | y['S'] = - y['comp_N/S']
162 | y['B'] = y['comp_time'] * y['temp'] \
163 | + y['comp_E'] * y['temp_fm'] \
164 | + y['comp_W'] * (sections[3][x]['temp'] - y['temp']) \
165 | + y['comp_N/S'] * (sections[4][x - 1]['temp'] - y['temp']) \
166 | + y['comp_N/S'] * (sections[4][x + 1]['temp'] - y['temp'])
167 |
168 | if x == bit_position:
169 | y['N'] = - y['comp_N/S']
170 | y['W'] = - y['comp_W']
171 | y['C'] = y['comp_time'] + y['comp_E'] + y['comp_W'] + y['comp_N/S']
172 | y['E'] = 0
173 | y['S'] = 0
174 | y['B'] = y['comp_time'] * y['temp'] \
175 | + y['comp_E'] * y['temp_fm'] \
176 | + y['comp_W'] * (sections[3][x]['temp'] - y['temp']) \
177 | + y['comp_N/S'] * (sections[4][x - 1]['temp'] - y['temp'])
178 |
179 | return well
180 |
181 |
182 | def solve_pentadiagonal_system(well, bit_position):
183 | # Creating penta-diagonal matrix
184 | number_of_cells = bit_position + 1
185 | a = np.zeros((5 * number_of_cells, 5 * number_of_cells + 10))
186 |
187 | matrix = populate_matrix(a, well, bit_position)
188 |
189 | matrix = crop_matrix(matrix)
190 |
191 | constant_values = define_b_list(well, bit_position)
192 |
193 | temp_list = np.linalg.solve(matrix, constant_values)
194 |
195 | return temp_list
196 |
197 |
198 | def populate_matrix(matrix, well, bit_position):
199 | row = 0
200 | column_base = 0
201 |
202 | for x in range(bit_position + 1):
203 | for y in well.sections:
204 | matrix[row, column_base] = y[x]['N']
205 | matrix[row, column_base + 4] = y[x]['W']
206 | matrix[row, column_base + 5] = y[x]['C']
207 | matrix[row, column_base + 6] = y[x]['E']
208 | matrix[row, column_base + 10] = y[x]['S']
209 | row += 1
210 | column_base += 1
211 |
212 | """# TRY THE CODE BELOW TO CHECK THAT THERE IS NOT ANY ROW WITH ONLY 0
213 | import pandas as pd
214 | df = pd.DataFrame(matrix)
215 | #df["sum"] = df.sum(axis=1)
216 | print(df)
217 | #print(df[df["sum"] == 0.0])"""
218 |
219 | return matrix
220 |
221 |
222 | def crop_matrix(matrix):
223 | matrix = np.delete(matrix, 0, axis=0)
224 | matrix = np.delete(matrix, range(6), axis=1)
225 | matrix = np.delete(matrix, [-1, -2, -3, -4, -5], axis=1)
226 | matrix = np.delete(matrix, [-1, -2], axis=0)
227 | matrix = np.delete(matrix, [-1, -2], axis=1)
228 |
229 | matrix[-7, -3] = matrix[-7, -2]
230 | matrix[-7, -2] = 0
231 | matrix[-6, -3] = matrix[-6, -1]
232 | matrix[-6, -1] = 0
233 |
234 | """# Uncomment this to check the matrix cropped as a dataframe
235 | import pandas as pd
236 | df = pd.DataFrame(matrix)
237 | print(df.to_string())"""
238 |
239 | return matrix
240 |
241 |
242 | def define_b_list(well, bit_position):
243 | b_list = []
244 |
245 | for x in range(bit_position + 1):
246 | for y in well.sections:
247 | b_list.append(y[x]['B'])
248 |
249 | b_list = b_list[1:-2]
250 |
251 | return b_list
252 |
253 |
254 | def calc_temperature_distribution(well, time_step, bit_position):
255 | add_heat_coefficients(well, time_step, bit_position)
256 | well = add_values(well, bit_position)
257 | temp_list = solve_pentadiagonal_system(well, bit_position)
258 | well = update_temp(well, temp_list, bit_position)
259 |
260 | return well
261 |
262 |
263 | def add_values(well, bit_position):
264 | well = define_system_section0(well, well.sections, bit_position) # System section 0
265 |
266 | well = define_system_section1(well, well.sections, bit_position) # System section 1
267 |
268 | well = define_system_section2(well, well.sections, bit_position) # System section 2
269 |
270 | well = define_system_section3(well, well.sections, bit_position) # System section 3
271 |
272 | well = define_system_section4(well, well.sections, bit_position) # System section 4
273 |
274 | for x in ['N', 'W', 'C', 'E', 'S', 'B']:
275 | well.sections[1][bit_position][x] = well.sections[3][bit_position][x]
276 | well.sections[2][bit_position][x] = well.sections[4][bit_position][x]
277 |
278 | return well
279 |
280 |
281 | def update_temp(well, temp_list, bit_position):
282 | rebuilt = [well.temp_inlet] + list(temp_list[:-3]) + [temp_list[-3]] * 3 + list(temp_list[-2:])
283 |
284 | list_index = 0
285 | for x in range(bit_position + 1):
286 | for y in well.sections:
287 | y[x]['temp'] = rebuilt[list_index]
288 | list_index += 1
289 |
290 | return well
291 |
--------------------------------------------------------------------------------
/pwptemp/injection/linearsystem.py:
--------------------------------------------------------------------------------
1 | def define_coef(coefficients, zstep):
2 | """
3 | Retrieves respective heat transfer coefficients for certain depth point.
4 | :param coefficients: list with distribution of heat transfer coefficients
5 | :param zstep: depth step
6 | :return: values of heat coefficients for each section at the same depth
7 | """
8 |
9 | hc_1 = coefficients[0]
10 | c1z = hc_1[0][zstep]
11 | c1e = hc_1[1][zstep]
12 | c1 = hc_1[2][zstep]
13 | c1t = hc_1[3][zstep]
14 |
15 | hc_2 = coefficients[1]
16 | c2z = hc_2[0][zstep]
17 | c2e = hc_2[1][zstep]
18 | c2w = hc_2[2][zstep]
19 | c2t = hc_2[3][zstep]
20 |
21 | hc_3 = coefficients[2]
22 | c3z = hc_3[0][zstep]
23 | c3e = hc_3[1][zstep]
24 | c3w = hc_3[2][zstep]
25 | c3t = hc_3[3][zstep]
26 |
27 | hc_4 = coefficients[3]
28 | c4z = hc_4[0][zstep]
29 | c4e = hc_4[1][zstep]
30 | c4w = hc_4[2][zstep]
31 | c4t = hc_4[3][zstep]
32 |
33 | hc_5 = coefficients[4]
34 | c5z = hc_5[0][zstep]
35 | c5e = hc_5[1][zstep]
36 | c5w = hc_5[2][zstep]
37 | c5t = hc_5[3][zstep]
38 |
39 | cbe = coefficients[5]
40 | cbt = coefficients[6]
41 | cbz = coefficients[7]
42 |
43 | return c1z, c1e, c1, c1t, c2z, c2e, c2w, c2t, c3z, c3e, c3w, c3t, c4z, c4e, c4w, c4t, c5z, c5e, c5w, c5t, cbe, \
44 | cbt, cbz
45 |
46 |
47 | def temp_calc(well, initcond, heatcoeff):
48 | """
49 | Build the penta-diagonal matrix and solve it to get the well temperature distribution.
50 | :param well: a well object created from the function set_well()
51 | :param initcond: object with initial temperature profiles
52 | :param heatcoeff: list with distribution of heat transfer coefficients
53 | :return: object with final well temperature distribution
54 | """
55 |
56 | from numpy import zeros, linalg
57 |
58 | Tft = [well.tin]
59 | Tt = []
60 | Ta = []
61 | tc = []
62 | Tsr = []
63 | xi = 5
64 |
65 | # Creating vectors N,W,C,E,S,B
66 | N = []
67 | W = []
68 | C = []
69 | E = []
70 | S = []
71 | B = []
72 |
73 | for j in range(well.zstep):
74 | c1z, c1e, c1, c1t, c2z, c2e, c2w, c2t, c3z, c3e, c3w, c3t, c4z, c4e, c4w, c4t, c5z, c5e, c5w, c5t, cbe, \
75 | cbt, cbz = define_coef(heatcoeff, j)
76 | for i in range(xi):
77 |
78 | if i == 0: # Inside Tubing
79 |
80 | if j == 1:
81 | W.append(0)
82 | C.append(c1t + c1e + c1z)
83 | E.append(-c1e)
84 | S.append(0)
85 | B.append(c1t * initcond.tfto[j] # Center(t=0)
86 | + c1 # Heat Source
87 | + c1e * (initcond.tto[j] - initcond.tfto[j]) # East(t=0)
88 | + c1z * (initcond.tfto[j - 1] - initcond.tfto[j])
89 | + c1z * well.tin) # N/S(t=0)
90 |
91 | if 1 < j < well.zstep - 1:
92 | N.append(-c1z)
93 | W.append(0)
94 | C.append(c1t + c1e + c1z)
95 | E.append(-c1e)
96 | S.append(0)
97 | B.append(c1t * initcond.tfto[j] # Center(t=0)
98 | + c1 # Heat Source
99 | + c1e * (initcond.tto[j] - initcond.tfto[j]) # East(t=0)
100 | + c1z * (initcond.tfto[j - 1] - initcond.tfto[j])) # N/S(t=0)
101 |
102 | if j == well.zstep - 1:
103 | N.append(-c1z)
104 | W.append(0)
105 | C.append(cbt + c1z)
106 | E.append(0)
107 | B.append(cbt * initcond.tfto[j] # Center(t=0)
108 | + c1z * (initcond.tfto[j - 1] - initcond.tfto[j])) # N/S(t=0)
109 |
110 | if i == 1: # Tubing wall
111 |
112 | if j == 0:
113 | C.append(c2t + c2e + c2w + c2z)
114 | E.append(-c2e)
115 | S.append(-c2z)
116 | B.append(c2t * initcond.tto[j]
117 | + c2e * (initcond.tao[j] - initcond.tto[j])
118 | + c2w * (initcond.tfto[j] - initcond.tto[j])
119 | + c2z * (initcond.tto[j + 1] - initcond.tto[j])
120 | + c2w * well.tin)
121 |
122 | if 0 < j < well.zstep - 1:
123 | N.append(-c2z)
124 | W.append(-c2w)
125 | C.append(c2t + c2e + c2w + 2 * c2z)
126 | E.append(-c2e)
127 | S.append(-c2z)
128 | B.append(c2t * initcond.tto[j]
129 | + c2e * (initcond.tao[j] - initcond.tto[j])
130 | + c2w * (initcond.tfto[j] - initcond.tto[j])
131 | + c2z * (initcond.tto[j + 1] - initcond.tto[j])
132 | + c2z * (initcond.tto[j - 1] - initcond.tto[j]))
133 |
134 | if j == well.zstep - 1:
135 | N.append(-c2z)
136 | W.append(0)
137 | C.append(c2t + c2z)
138 | E.append(0)
139 | B.append(c2t * initcond.tto[j]
140 | + c2z * (initcond.tto[j - 1] - initcond.tto[j]))
141 |
142 | if i == 2: # Annular
143 |
144 | if j == 0:
145 | W.append(-c3w)
146 | C.append(c3t + c3e + c3w + c3z)
147 | E.append(-c3e)
148 | S.append(-c3z)
149 | B.append(c3t * initcond.tao[j]
150 | + c3e * (initcond.tco[j] - initcond.tao[j])
151 | + c3w * (initcond.tto[j] - initcond.tao[j])
152 | + c3z * (initcond.tao[j + 1] - initcond.tao[j]))
153 |
154 | if 0 < j < well.zstep - 1:
155 | N.append(-c3z)
156 | W.append(-c3w)
157 | C.append(c3t + c3e + c3w + 2 * c3z)
158 | E.append(-c3e)
159 | S.append(-c3z)
160 | B.append(c3t * initcond.tao[j]
161 | + c3e * (initcond.tco[j] - initcond.tao[j])
162 | + c3w * (initcond.tto[j] - initcond.tao[j])
163 | + c3z * (initcond.tao[j + 1] - initcond.tao[j])
164 | + c3z * (initcond.tao[j - 1] - initcond.tao[j]))
165 |
166 | if j == well.zstep - 1:
167 | N.append(-c3z)
168 | W.append(0)
169 | C.append(c3t + c3e + c3z)
170 | E.append(-c3e)
171 | B.append(c3t * initcond.tao[j]
172 | + c3e * (initcond.tco[j] - initcond.tao[j])
173 | + c3z * (initcond.tao[j - 1] - initcond.tao[j]))
174 |
175 | if i == 3: # Casing
176 |
177 | if j == 0:
178 | W.append(-c4w)
179 | C.append(c4t + c4e + c4w + c4z)
180 | E.append(-c4e)
181 | S.append(-c4z)
182 | B.append(c4t * initcond.tco[j] # Center(t=0)
183 | + c4e * (initcond.tsro[j] - initcond.tco[j]) # East(t=0)
184 | + c4w * (initcond.tao[j] - initcond.tco[j]) # West(t=0)
185 | + c4z * (initcond.tco[j + 1] - initcond.tco[j])) # N/S(t=0)
186 |
187 | if 0 < j < well.zstep - 1:
188 | N.append(-c4z)
189 | W.append(-c4w)
190 | C.append(c4t + c4e + c4w + 2 * c4z)
191 | E.append(-c4e)
192 | S.append(-c4z)
193 | B.append(c4t * initcond.tco[j] # Center(t=0)
194 | + c4e * (initcond.tsro[j] - initcond.tco[j]) # East(t=0)
195 | + c4w * (initcond.tao[j] - initcond.tco[j]) # West(t=0)
196 | + c4z * (initcond.tco[j + 1] - 2 * initcond.tco[j] + initcond.tco[j - 1])) # N/S(t=0)
197 |
198 | if j == well.zstep - 1:
199 | N.append(-c4z)
200 | W.append(-c4w)
201 | C.append(c4t + c4e + c4w + c4z)
202 | E.append(-c4e)
203 | B.append(c4t * initcond.tco[j] # Center(t=0)
204 | + c4e * (initcond.tsro[j] - initcond.tco[j]) # East(t=0)
205 | + c4w * (initcond.tao[j] - initcond.tco[j]) # West(t=0)
206 | + c4z * (initcond.tco[j - 1] - initcond.tco[j])) # N/S(t=0))
207 |
208 | if i == 4: # Surrounding Space
209 |
210 | if j == 0:
211 | W.append(-c5w)
212 | C.append(c5w + c5z + c5e + c5t)
213 | E.append(0)
214 | S.append(-c5z)
215 | B.append(c5w * (initcond.tco[j] - initcond.tsro[j])
216 | + c5z * (initcond.tsro[j + 1] - initcond.tsro[j])
217 | + c5e * initcond.tsro[j])
218 |
219 | if 0 < j < well.zstep - 1:
220 | N.append(-c5z)
221 | W.append(-c5w)
222 | C.append(c5w + c5e + 2 * c5z + c5t)
223 | E.append(0)
224 | S.append(-c5z)
225 | B.append(c5w * (initcond.tco[j] - initcond.tsro[j])
226 | + c5z * (initcond.tsro[j + 1] - initcond.tsro[j])
227 | + c5z * (initcond.tsro[j - 1] - initcond.tsro[j])
228 | + c5e * initcond.tsro[j])
229 |
230 | if j == well.zstep - 1:
231 | N.append(-c5z)
232 | W.append(-c5w)
233 | C.append(c5w + c5e + c5z + c5t)
234 | B.append(c5w * (initcond.tco[j] - initcond.tsro[j])
235 | + c5z * (initcond.tsro[j - 1] - initcond.tsro[j])
236 | + c5t * initcond.tsro[j]
237 | + c5e * initcond.tsro[j])
238 |
239 | #LINEARSYSTEM
240 | # Creating pentadiagonal matrix
241 | A = zeros((xi * well.zstep - 1, xi * well.zstep - 1))
242 |
243 | # Filling up Pentadiagonal Matrix A
244 | lenC = xi * well.zstep - 1
245 | lenN = lenC - xi
246 | lenW = lenC - 1
247 | lenE = lenC - 1
248 | lenS = lenC - xi
249 |
250 | for it in range(lenC): # Inserting list C
251 | A[it, it] = C[it]
252 | for it in range(lenE): # Inserting list E
253 | A[it, it + 1] = E[it]
254 | for it in range(lenW): # Inserting list W
255 | A[it + 1, it] = W[it]
256 | for it in range(lenN): # Inserting list N
257 | A[it + xi, it] = N[it]
258 | for it in range(lenS): # Inserting list S
259 | A[it, it + xi] = S[it]
260 |
261 | Temp = linalg.solve(A, B)
262 |
263 | for x in range(well.zstep):
264 | Tt.append(Temp[5 * x])
265 | if x < well.zstep-1:
266 | Tft.append(Temp[5 * x + 4])
267 | Ta.append(Temp[5 * x + 1])
268 | if x == well.zstep - 1:
269 | Ta.append(Tft[-1])
270 | tc.append(Temp[5 * x + 2])
271 | Tsr.append(Temp[5 * x + 3])
272 |
273 |
274 | t3 = tc.copy()
275 |
276 | tr = tc[:well.riser] + [None] * (well.zstep - well.riser)
277 | for x in range(well.riser):
278 | tc[x] = None
279 |
280 | csgs_reach = int(well.casings[0, 2] / well.deltaz) # final depth still covered with casing(s)
281 |
282 | Toh = [None] * csgs_reach + tc[csgs_reach:]
283 | for x in range(csgs_reach, well.zstep):
284 | tc[x] = None
285 |
286 | class TempCalc(object):
287 | def __init__(self):
288 | self.tft = Tft
289 | self.tt = Tt
290 | self.ta = Ta
291 | self.t3 = t3
292 | self.tc = tc
293 | self.tr = tr
294 | self.tsr = Tsr
295 | self.toh = Toh
296 | self.csgs_reach = csgs_reach
297 |
298 | return TempCalc()
299 |
--------------------------------------------------------------------------------
/pwptemp/main.py:
--------------------------------------------------------------------------------
1 | from .inputs import inputs_dict
2 | from .well_system import set_well
3 | from .linearsystem import calc_temperature_distribution
4 | from .plot import plot_behavior
5 | from scipy.interpolate import make_interp_spline
6 | import numpy as np
7 | import scipy.signal
8 | import well_profile as wp
9 |
10 |
11 | def calc_temp(trajectory, casings=None, set_inputs=None, operation='drilling', time_steps=210, smooth=True,
12 | cells_no=None):
13 | """
14 | Function to calculate the well temperature distribution during a specific operation at a certain time.
15 |
16 | Arguments:
17 | trajectory: wellbore trajectory excel|csv|dataframe|list
18 | casings: list of dictionaries with casings characteristics (od, id and depth)
19 | set_inputs: dictionary with parameters to set.
20 | operation: define operation type. ('drilling', 'circulating')
21 | time_steps: number of time steps to run calculations.
22 | smooth: smooth the temperature profiles.
23 | cells_no: (int) number of cells. If None -> keep same number of cells than trajectory
24 |
25 | Returns:
26 | Well temperature distribution object
27 | """
28 |
29 | tdata = inputs_dict(casings)
30 |
31 | if set_inputs is not None:
32 | for x in set_inputs: # changing default values
33 | if x in tdata:
34 | tdata[x] = set_inputs[x]
35 | else:
36 | raise TypeError('%s is not a parameter' % x)
37 |
38 | if cells_no is not None:
39 | survey = wp.load(trajectory, points=cells_no).trajectory
40 | else:
41 | survey = wp.load(trajectory, equidistant=False).trajectory
42 |
43 | md_initial = tdata['water_depth']
44 | md_final = survey[-1]['md']
45 |
46 | well = set_well(tdata, survey, operation)
47 |
48 | time = []
49 | rop_steps = []
50 | depths = sorted([x[2] for x in well.casings])
51 | if operation == 'drilling':
52 |
53 | prev_point = md_initial
54 |
55 | cummulative_time = []
56 |
57 | for x in range(len(depths)):
58 | distance = depths[x] - prev_point
59 | time_section = distance / well.rop_list[x]
60 | time.append(time_section)
61 | cummulative_time.append(sum(time))
62 | prev_point = depths[x]
63 | time.append((md_final - prev_point) / well.rop_list[-1])
64 | cummulative_time.append(sum(time))
65 | depths = list(depths) + [md_final]
66 | if depths[0] == 0:
67 | depths = depths[1:]
68 | time = time[1:]
69 | cummulative_time = cummulative_time[1:]
70 |
71 | tcirc = sum(time) * 3600 # drilling time, s
72 | for x in cummulative_time:
73 | rop_steps.append(round(x * 3600 / tcirc / time_steps))
74 | else:
75 | tcirc = tdata['time'] * 3600 # circulating time, s
76 |
77 | time_step = tcirc / time_steps # seconds per time step
78 |
79 | log_temp_values(well, initial=True) # log initial temperature distribution
80 | well.delta_time = time_step
81 | time_n = time_step
82 | well.op = operation
83 |
84 | td = len(well.trajectory)-1
85 |
86 | for x in range(time_steps):
87 |
88 | if well.op == 'drilling':
89 | d = depths[0]
90 | rop = well.rop_list[0]
91 | t = time[0] * 3600
92 | for y in range(len(time)):
93 | if time_n < sum(time[:y+1])*3600:
94 | d = depths[y]
95 | if len(well.rop_list) > 1:
96 | rop = well.rop_list[y]
97 | t = sum(time[:y+1])*3600
98 | break
99 |
100 | bit_depth = d - rop/3600 * (t - time_n)
101 | bit_position = [cell for cell, point in enumerate(well.trajectory) if point['md'] <= bit_depth][-1]
102 | td = bit_position
103 |
104 | if time_steps > 1:
105 |
106 | if td > 0:
107 | well = calc_temperature_distribution(well, time_step, td)
108 | well = define_temperatures(well, td)
109 |
110 | log_temp_values(well, time_n)
111 |
112 | if well.op == 'drilling':
113 | if x in range(len(rop_steps)):
114 | well.temperatures['in_pipe'] = well.temp_fm
115 | well.temperatures['pipe'] = well.temp_fm
116 | well.temperatures['annulus'] = well.temp_fm
117 | well.temperatures['casing'] = well.temp_fm
118 | well.temperatures['riser'] = well.temp_fm
119 | well.temperatures['sr'] = well.temp_fm
120 | for i in well.sections:
121 | for j in range(len(well.trajectory)):
122 | i[j]['temp'] = well.temp_fm[j]
123 | i[j]['temp_fm'] = well.temp_fm[j]
124 |
125 | well.time = time_n / 3600
126 |
127 | time_n += time_step
128 |
129 | if smooth:
130 | smooth_results(well)
131 |
132 | additional_points(well)
133 |
134 | return well
135 |
136 |
137 | def define_temperatures(well, bit_position):
138 | """
139 | Make the temperature values more reachable since they are is a dictionary along the entire well. Once this function
140 | takes place, the temperatures will be available as lists.
141 | :return: a dictionary with lists of temperature values and also a list with respective depth points.
142 | """
143 |
144 | temp_in_pipe = [x['temp'] for x in well.sections[0][:bit_position+1]] + \
145 | [None] * (len(well.trajectory) - (bit_position + 1))
146 | temp_pipe = [x['temp'] for x in well.sections[1][:bit_position+1]] + \
147 | [None] * (len(well.trajectory) - (bit_position + 1))
148 | temp_annulus = [x['temp'] for x in well.sections[2][:bit_position+1]] + \
149 | [None] * (len(well.trajectory) - (bit_position + 1))
150 | temp_casing = []
151 | temp_riser = []
152 | temp_sr = [x['temp'] for x in well.sections[4]]
153 | for y, x in enumerate(well.md):
154 |
155 | if well.riser_cells > 0 and x < well.water_depth:
156 | temp_casing.append(None)
157 | temp_riser.append(well.sections[3][y]['temp'])
158 | else:
159 | temp_riser.append(None)
160 | if x <= well.casings[0, 2]:
161 | temp_casing.append(well.sections[3][y]['temp'])
162 | else:
163 | temp_casing.append(None)
164 |
165 | well.temperatures = {'md': list(well.md),
166 | 'formation': well.temp_fm,
167 | 'in_pipe': temp_in_pipe,
168 | 'pipe': temp_pipe,
169 | 'annulus': temp_annulus,
170 | 'casing': temp_casing,
171 | 'riser': temp_riser,
172 | 'sr': temp_sr}
173 |
174 | return well
175 |
176 |
177 | def log_temp_values(well, time=0.0, initial=False):
178 | time = round(time/3600, 2)
179 | if initial:
180 | well.temp_log = [{'time': time,
181 | 'in_pipe': well.temp_fm,
182 | 'pipe': well.temp_fm,
183 | 'annulus': well.temp_fm,
184 | 'casing': well.temp_fm,
185 | 'riser': well.temp_fm,
186 | 'sr': well.temp_fm}]
187 | else:
188 | well.temp_log.append(
189 | {'time': time,
190 | 'in_pipe': [x['temp'] for x in well.sections[0]],
191 | 'pipe': [x['temp'] for x in well.sections[1]],
192 | 'annulus': well.temperatures['annulus'],
193 | 'casing': well.temperatures['casing'],
194 | 'riser': well.temperatures['riser'],
195 | 'sr': well.temperatures['sr']}
196 | )
197 |
198 |
199 | def temperature_behavior(well):
200 | """
201 | Function to simulate the temperature behavior.
202 |
203 | Arguments:
204 | well (obj): well temperature distribution object
205 |
206 | Returns:
207 | temperature behavior object
208 | """
209 |
210 | time = []
211 | temp_bottom = []
212 | temp_outlet = []
213 | temp_max = []
214 | temp_fm = []
215 |
216 | for x in well.temp_log[1:]:
217 | cells = len(well.md) - x['annulus'].count(None)
218 | time.append(x['time'])
219 | temp_bottom.append(x['in_pipe'][cells-1])
220 | temp_outlet.append(x['annulus'][0])
221 | temp_max.append(max(x['annulus'][:cells-1]))
222 | temp_fm.append(well.temp_fm[cells-1])
223 |
224 | temp_bottom = list(scipy.signal.savgol_filter(temp_bottom, 59, 3))
225 | temp_max = list(scipy.signal.savgol_filter(temp_max, 59, 3))
226 | temp_outlet = list(scipy.signal.savgol_filter(temp_outlet, 59, 3))
227 | temp_fm = list(scipy.signal.savgol_filter(temp_fm, 59, 3))
228 |
229 | class TempBehavior(object):
230 | def __init__(self):
231 | self.time = time
232 | self.bottom = temp_bottom
233 | self.outlet = temp_outlet
234 | self.max = temp_max
235 | self.formation_td = temp_fm
236 |
237 | def plot(self, title=True):
238 | fig = plot_behavior(self, title)
239 |
240 | return fig
241 |
242 | return TempBehavior()
243 |
244 |
245 | def smooth_results(well):
246 |
247 | well.temperatures['in_pipe'] = list(scipy.signal.savgol_filter(well.temperatures['in_pipe'], 15, 3))
248 | well.temperatures['annulus'] = list(scipy.signal.savgol_filter(well.temperatures['annulus'], 15, 2))
249 |
250 | cells = len(well.md) - np.count_nonzero(np.isnan(well.temperatures['annulus']))
251 | ref = int(0.9 * cells - well.time*0.2)
252 | t_bottom = np.mean(well.temperatures['annulus'][ref:])
253 |
254 | x_new = well.md[ref:]
255 |
256 | # Smooth annulus
257 | interp_ann = make_interp_spline([well.md[ref - 1], well.md[ref], well.md[-1]],
258 | [well.temperatures['annulus'][ref - 1], well.temperatures['annulus'][ref],
259 | t_bottom],
260 | k=2)
261 | temp_annulus = interp_ann(x_new)
262 |
263 | # Smooth in_pipe
264 | interp_in_pipe = make_interp_spline(well.md[:ref-2] + [well.md[-1]],
265 | well.temperatures['in_pipe'][:ref-2] + [t_bottom],
266 | k=2)
267 | temp_in_pipe = interp_in_pipe(x_new)
268 |
269 | well.temperatures['in_pipe'] = well.temperatures['in_pipe'][:ref] + list(temp_in_pipe)
270 | well.temperatures['annulus'] = well.temperatures['annulus'][:ref] + list(temp_annulus)
271 |
272 |
273 | def additional_points(well):
274 | if well.water_depth > 0:
275 | depths = [point['md'] for point in well.trajectory]
276 | seabed_point = False
277 | for idx, md in enumerate(depths):
278 | if idx > 0:
279 | if md > well.water_depth and not seabed_point:
280 | well.temperatures['md'].insert(idx, well.water_depth)
281 |
282 | well.temperatures['formation'].insert(idx,
283 | np.interp(well.water_depth, [depths[idx - 1], md],
284 | [well.temperatures['formation'][idx - 1],
285 | well.temperatures['formation'][idx]]))
286 | well.temperatures['in_pipe'].insert(idx,
287 | np.interp(well.water_depth, [depths[idx - 1], md],
288 | [well.temperatures['in_pipe'][idx - 1],
289 | well.temperatures['in_pipe'][idx]]))
290 | well.temperatures['pipe'].insert(idx,
291 | np.interp(well.water_depth, [depths[idx - 1], md],
292 | [well.temperatures['pipe'][idx - 1],
293 | well.temperatures['pipe'][idx]]))
294 | well.temperatures['annulus'].insert(idx,
295 | np.interp(well.water_depth, [depths[idx - 1], md],
296 | [well.temperatures['annulus'][idx - 1],
297 | well.temperatures['annulus'][idx]]))
298 | well.temperatures['riser'].insert(idx, well.temperatures['riser'][idx - 1])
299 | well.temperatures['casing'].insert(idx, well.temperatures['casing'][idx])
300 | well.temperatures['sr'].insert(idx,
301 | np.interp(well.water_depth, [depths[idx - 1], md],
302 | [well.temperatures['sr'][idx - 1],
303 | well.temperatures['sr'][idx]]))
304 | seabed_point = True
305 |
306 | reference = well.casings[0][2]
307 | if md > reference != 0:
308 | well.temperatures['md'].insert(idx + 1, reference)
309 |
310 | well.temperatures['formation'].insert(idx + 1,
311 | np.interp(reference, [depths[idx - 1], md],
312 | [well.temperatures['formation'][idx],
313 | well.temperatures['formation'][idx + 1]]))
314 | well.temperatures['in_pipe'].insert(idx + 1,
315 | np.interp(reference, [depths[idx - 1], md],
316 | [well.temperatures['in_pipe'][idx],
317 | well.temperatures['in_pipe'][idx + 1]]))
318 | well.temperatures['pipe'].insert(idx + 1,
319 | np.interp(reference, [depths[idx - 1], md],
320 | [well.temperatures['pipe'][idx],
321 | well.temperatures['pipe'][idx + 1]]))
322 | well.temperatures['annulus'].insert(idx + 1,
323 | np.interp(reference, [depths[idx - 1], md],
324 | [well.temperatures['annulus'][idx],
325 | well.temperatures['annulus'][idx + 1]]))
326 | well.temperatures['riser'].insert(idx + 1, None)
327 | well.temperatures['casing'].insert(idx + 1, well.temperatures['casing'][idx])
328 | well.temperatures['sr'].insert(idx,
329 | np.interp(reference, [depths[idx - 1], md],
330 | [well.temperatures['sr'][idx],
331 | well.temperatures['sr'][idx + 1]]))
332 | break
333 |
--------------------------------------------------------------------------------
/pwptemp/production/linearsystem.py:
--------------------------------------------------------------------------------
1 | def define_coef(coefficients, zstep):
2 | """
3 | Retrieves respective heat transfer coefficients for certain depth point.
4 | :param coefficients: list with distribution of heat transfer coefficients
5 | :param zstep: depth step
6 | :return: values of heat coefficients for each section at the same depth
7 | """
8 |
9 | hc_1 = coefficients[0]
10 | c1z = hc_1[0][zstep]
11 | c1e = hc_1[1][zstep]
12 | c1 = hc_1[2][zstep]
13 | c1t = hc_1[3][zstep]
14 |
15 | hc_2 = coefficients[1]
16 | c2z = hc_2[0][zstep]
17 | c2e = hc_2[1][zstep]
18 | c2w = hc_2[2][zstep]
19 | c2t = hc_2[3][zstep]
20 |
21 | hc_3 = coefficients[2]
22 | c3z = hc_3[0][zstep]
23 | c3e = hc_3[1][zstep]
24 | c3w = hc_3[2][zstep]
25 | c3t = hc_3[3][zstep]
26 |
27 | hc_4 = coefficients[3]
28 | c4z = hc_4[0][zstep]
29 | c4e = hc_4[1][zstep]
30 | c4w = hc_4[2][zstep]
31 | c4t = hc_4[3][zstep]
32 |
33 | hc_5 = coefficients[4]
34 | c5z = hc_5[0][zstep]
35 | c5e = hc_5[1][zstep]
36 | c5w = hc_5[2][zstep]
37 | c5t = hc_5[3][zstep]
38 |
39 | cbe = coefficients[5]
40 | cbt = coefficients[6]
41 | cbz = coefficients[7]
42 |
43 | return c1z, c1e, c1, c1t, c2z, c2e, c2w, c2t, c3z, c3e, c3w, c3t, c4z, c4e, c4w, c4t, c5z, c5e, c5w, c5t, cbe, \
44 | cbt, cbz
45 |
46 |
47 | def temp_calc(well, initcond, heatcoeff):
48 | """
49 | Build the penta-diagonal matrix and solve it to get the well temperature distribution.
50 | :param well: a well object created from the function set_well()
51 | :param initcond: object with initial temperature profiles
52 | :param heatcoeff: list with distribution of heat transfer coefficients
53 | :return: object with final well temperature distribution
54 | """
55 |
56 | from numpy import zeros, linalg
57 |
58 | Tft = []
59 | Tt = []
60 | Ta = []
61 | tc = []
62 | Tsr = []
63 | xi = 5
64 |
65 | # Creating vectors N,W,C,E,S,B
66 | N = []
67 | W = []
68 | C = []
69 | E = []
70 | S = []
71 | B = []
72 |
73 | for j in range(well.zstep):
74 | c1z, c1e, c1, c1t, c2z, c2e, c2w, c2t, c3z, c3e, c3w, c3t, c4z, c4e, c4w, c4t, c5z, c5e, c5w, c5t, cbe, \
75 | cbt, cbz = define_coef(heatcoeff, j)
76 | for i in range(xi):
77 |
78 | if i == 0: # Inside Tubing
79 | if j == 0:
80 | C.append(c1t + c1e + c1z)
81 | E.append(-c1e)
82 | S.append(-c1z)
83 | B.append(c1t * initcond.tfto[j] # Center(t=0)
84 | + c1 # Heat Source
85 | + c1e * (initcond.tto[j] - initcond.tfto[j]) # East(t=0)
86 | + c1z * (initcond.tfto[j + 1] - initcond.tfto[j])) # N/S(t=0)
87 |
88 | if 0 < j < well.zstep - 2:
89 | N.append(0)
90 | W.append(0)
91 | C.append(c1t + c1e + c1z)
92 | E.append(-c1e)
93 | S.append(-c1z)
94 | B.append(c1t * initcond.tfto[j] # Center(t=0)
95 | + c1 # Heat Source
96 | + c1e * (initcond.tto[j] - initcond.tfto[j]) # East(t=0)
97 | + c1z * (initcond.tfto[j + 1] - initcond.tfto[j])) # N/S(t=0)
98 |
99 | if j == well.zstep - 2:
100 | N.append(0)
101 | W.append(0)
102 | C.append(c1t + c1e + c1z)
103 | E.append(-c1e)
104 | S.append(0)
105 | B.append(c1t * initcond.tfto[j] # Center(t=0)
106 | + c1 # Heat Source
107 | + c1e * (initcond.tto[j] - initcond.tfto[j]) # East(t=0)
108 | + c1z * (initcond.tfto[j + 1] - initcond.tfto[j]) # N/S(t=0)
109 | + c1z * initcond.tfto[-1])
110 |
111 | if j == well.zstep - 1:
112 | N.append(0)
113 | W.append(0)
114 | C.append(cbt + cbe)
115 | E.append(0)
116 | B.append(cbt * initcond.tfto[j] # Center(t=0)
117 | + cbe * (initcond.tto[j] - initcond.tfto[j]) # East(t=0)
118 | + cbe * initcond.tto[-1])
119 |
120 | if i == 1: # Tubing wall
121 |
122 | if j == 0:
123 | W.append(-c2w)
124 | C.append(c2t + c2e + c2w + c2z)
125 | E.append(-c2e)
126 | S.append(-c2z)
127 | B.append(c2t * initcond.tto[j]
128 | + c2e * (initcond.tao[j] - initcond.tto[j])
129 | + c2w * (initcond.tfto[j] - initcond.tto[j])
130 | + c2z * (initcond.tto[j + 1] - initcond.tto[j]))
131 |
132 | if 0 < j < well.zstep - 1:
133 | N.append(-c2z)
134 | W.append(-c2w)
135 | C.append(c2t + c2e + c2w + 2 * c2z)
136 | E.append(-c2e)
137 | if j < well.zstep - 3:
138 | S.append(-c2z)
139 | B.append(c2t * initcond.tto[j]
140 | + c2e * (initcond.tao[j] - initcond.tto[j])
141 | + c2w * (initcond.tfto[j] - initcond.tto[j])
142 | + c2z * (initcond.tto[j + 1] - initcond.tto[j])
143 | + c2z * (initcond.tto[j - 1] - initcond.tto[j]))
144 | else:
145 | S.append(0)
146 | B.append(c2t * initcond.tto[j]
147 | + c2e * (initcond.tao[j] - initcond.tto[j])
148 | + c2w * (initcond.tfto[j] - initcond.tto[j])
149 | + c2z * (initcond.tto[j + 1] - initcond.tto[j])
150 | + c2z * (initcond.tto[j - 1] - initcond.tto[j])
151 | + c2z * initcond.tfto[-1])
152 |
153 | if j == well.zstep - 1:
154 | N.append(-c2z)
155 | W.append(0)
156 | C.append(c2t + c2e + c2w + c2z)
157 | E.append(0)
158 | B.append(c2t * initcond.tto[j]
159 | + c2e * (initcond.tao[j] - initcond.tto[j])
160 | + c2w * initcond.tfto[j]
161 | + c2e * initcond.tao[j]
162 | + c2w * (initcond.tfto[j] - initcond.tto[j])
163 | + c2z * (initcond.tto[j - 1] - initcond.tto[j]))
164 |
165 | if i == 2: # Annular
166 |
167 | if j == 0:
168 | W.append(-c3w)
169 | C.append(c3t + c3e + c3w + c3z)
170 | E.append(-c3e)
171 | S.append(-c3z)
172 | B.append(c3t * initcond.tao[j]
173 | + c3e * (initcond.tco[j] - initcond.tao[j])
174 | + c3w * (initcond.tto[j] - initcond.tao[j])
175 | + c3z * (initcond.tao[j + 1] - initcond.tao[j]))
176 |
177 | if 0 < j < well.zstep - 1:
178 | N.append(-c3z)
179 | W.append(-c3w)
180 | C.append(c3t + c3e + c3w + 2 * c3z)
181 | E.append(-c3e)
182 | if j < well.zstep - 3:
183 | S.append(-c3z)
184 | B.append(c3t * initcond.tao[j]
185 | + c3e * (initcond.tco[j] - initcond.tao[j])
186 | + c3w * (initcond.tto[j] - initcond.tao[j])
187 | + c3z * (initcond.tao[j + 1] - initcond.tao[j])
188 | + c3z * (initcond.tao[j - 1] - initcond.tao[j]))
189 | else:
190 | S.append(0)
191 | B.append(c3t * initcond.tao[j]
192 | + c3e * (initcond.tco[j] - initcond.tao[j])
193 | + c3w * (initcond.tto[j] - initcond.tao[j])
194 | + c3z * (initcond.tao[j + 1] - initcond.tao[j])
195 | + c3z * (initcond.tao[j - 1] - initcond.tao[j])
196 | + c3z * initcond.tfto[-1])
197 |
198 | if j == well.zstep - 1:
199 | N.append(-c3z)
200 | W.append(0)
201 | C.append(c3t + c3e + c3w + c3z)
202 | E.append(0)
203 | B.append(c3t * initcond.tao[j]
204 | + c3e * (initcond.tao[j] - initcond.tto[j])
205 | + c3w * (initcond.tfto[j] - initcond.tto[j])
206 | + c3e * initcond.tco[j]
207 | + c3w * initcond.tto[j]
208 | + c3z * (initcond.tto[j - 1] - initcond.tto[j]))
209 |
210 | if i == 3: # Casing
211 |
212 | if j == 0:
213 | W.append(-c4w)
214 | C.append(c4t + c4e + c4w + c4z)
215 | E.append(-c4e)
216 | S.append(-c4z)
217 | B.append(c4t * initcond.tco[j] # Center(t=0)
218 | + c4e * (initcond.tsro[j] - initcond.tco[j]) # East(t=0)
219 | + c4w * (initcond.tao[j] - initcond.tco[j]) # West(t=0)
220 | + c4z * (initcond.tco[j + 1] - initcond.tco[j])) # N/S(t=0)
221 |
222 | if 0 < j < well.zstep - 2:
223 | N.append(-c4z)
224 | W.append(-c4w)
225 | C.append(c4t + c4e + c4w + 2 * c4z)
226 | E.append(-c4e)
227 | S.append(-c4z)
228 | B.append(c4t * initcond.tco[j] # Center(t=0)
229 | + c4e * (initcond.tsro[j] - initcond.tco[j]) # East(t=0)
230 | + c4w * (initcond.tao[j] - initcond.tco[j]) # West(t=0)
231 | + c4z * (initcond.tco[j + 1] - 2 * initcond.tco[j] + initcond.tco[j - 1])) # N/S(t=0)
232 |
233 | if j == well.zstep - 2:
234 | N.append(-c4z)
235 | W.append(-c4w)
236 | C.append(c4t + c4e + c4w + 2 * c4z)
237 | E.append(- c4e)
238 | S.append(0)
239 | B.append(c4t * initcond.tco[j] # Center(t=0)
240 | + c4e * (initcond.tsro[j] - initcond.tco[j]) # East(t=0)
241 | + c4w * (initcond.tao[j] - initcond.tco[j]) # West(t=0)
242 | + c4z * (initcond.tco[j + 1] - 2 * initcond.tco[j] + initcond.tco[j - 1]) # N/S(t=0)
243 | + c4z * initcond.tfto[-1])
244 |
245 | if j == well.zstep - 1:
246 | N.append(-c4z)
247 | W.append(0)
248 | C.append(c4t + c4e + c4w + c4z)
249 | E.append(0)
250 | B.append(c4t * initcond.tco[j] # Center(t=0)
251 | + c4e * (initcond.tsro[j] - initcond.tco[j]) # East(t=0)
252 | + c4w * (initcond.tao[j] - initcond.tco[j]) # West(t=0)
253 | + c4z * (initcond.tco[j - 1] - initcond.tco[j]) # N/S(t=0)
254 | + c4z * initcond.tfto[-1]
255 | + c4w * initcond.tfto[-1])
256 |
257 | if i == 4: # Surrounding Space
258 |
259 | if j == 0:
260 | W.append(-c5w)
261 | C.append(c5w + c5z + c5e + c5t)
262 | E.append(-c5e)
263 | S.append(-c5z)
264 | B.append(c5w * (initcond.tco[j] - initcond.tsro[j])
265 | + c5z * (initcond.tsro[j + 1] - initcond.tsro[j])
266 | + c5t * initcond.tsro[j])
267 |
268 | if 0 < j < well.zstep - 2:
269 | N.append(-c5z)
270 | W.append(-c5w)
271 | C.append(c5w + c5e + 2 * c5z + c5t)
272 | E.append(-c5e)
273 | S.append(-c5z)
274 | B.append(c5w * (initcond.tco[j] - initcond.tsro[j])
275 | + c5z * (initcond.tsro[j + 1] - initcond.tsro[j])
276 | + c5z * (initcond.tsro[j - 1] - initcond.tsro[j])
277 | + c5t * initcond.tsro[j])
278 |
279 | if j == well.zstep - 2:
280 | N.append(-c5z)
281 | W.append(-c5w)
282 | C.append(c5w + c5e + 2 * c5z + c5t)
283 | E.append(-c5e)
284 | S.append(0)
285 | B.append(c5w * (initcond.tco[j] - initcond.tsro[j])
286 | + c5z * (initcond.tsro[j + 1] - initcond.tsro[j])
287 | + c5z * (initcond.tsro[j - 1] - initcond.tsro[j])
288 | + c5t * initcond.tsro[j]
289 | + c5z * initcond.tfto[-1])
290 |
291 | if j == well.zstep - 1:
292 | N.append(-c5z)
293 | W.append(0)
294 | C.append(c5w + c5e + c5z + c5t)
295 | B.append(c5w * (initcond.tco[j] - initcond.tsro[j])
296 | + c5z * (initcond.tsro[j - 1] - initcond.tsro[j])
297 | + c5z * initcond.tsro[j]
298 | + c5t * initcond.tsro[j])
299 |
300 | #LINEARSYSTEM
301 | # Creating pentadiagonal matrix
302 | A = zeros((xi * well.zstep - 0, xi * well.zstep - 0))
303 |
304 | # Filling up Pentadiagonal Matrix A
305 | lenC = xi * well.zstep - 0
306 | lenN = lenC - xi
307 | lenW = lenC - 1
308 | lenE = lenC - 1
309 | lenS = lenC - xi
310 |
311 | for it in range(lenC): # Inserting list C
312 | A[it, it] = C[it]
313 | for it in range(lenE): # Inserting list E
314 | A[it, it + 1] = E[it]
315 | for it in range(lenW): # Inserting list W
316 | A[it + 1, it] = W[it]
317 | for it in range(lenN): # Inserting list N
318 | A[it + xi, it] = N[it]
319 | for it in range(lenS): # Inserting list S
320 | A[it, it + xi] = S[it]
321 |
322 | Temp = linalg.solve(A, B)
323 |
324 | for x in range(well.zstep):
325 | if x < well.zstep - 1:
326 | Tft.append(Temp[5 * x])
327 | if x == well.zstep - 1:
328 | Tft.append(initcond.tfto[-1])
329 | for x in range(well.zstep):
330 | if x < well.zstep - 1:
331 | Tt.append(Temp[5 * x + 1])
332 | if x == well.zstep - 1:
333 | Tt.append(initcond.tto[-1])
334 | for x in range(well.zstep):
335 | if x < well.zstep - 1:
336 | Ta.append(Temp[5 * x + 2])
337 | if x == well.zstep - 1:
338 | Ta.append(initcond.tao[-1])
339 | for x in range(well.zstep):
340 | if x < well.zstep - 1:
341 | tc.append(Temp[5 * x + 3])
342 | if x == well.zstep - 1:
343 | tc.append(initcond.tco[-1])
344 | for x in range(well.zstep):
345 | if x < well.zstep - 1:
346 | Tsr.append(Temp[5 * x + 4])
347 | if x == well.zstep - 1:
348 | Tsr.append(initcond.tsro[-1])
349 |
350 | t3 = tc.copy()
351 |
352 | tr = tc[:well.riser] + [None] * (well.zstep - well.riser)
353 | for x in range(well.riser):
354 | tc[x] = None
355 |
356 | csgs_reach = int(well.casings[0, 2] / well.deltaz) # final depth still covered with casing(s)
357 |
358 | Toh = [None] * csgs_reach + tc[csgs_reach:]
359 | for x in range(csgs_reach, well.zstep):
360 | tc[x] = None
361 |
362 | class TempCalc(object):
363 | def __init__(self):
364 | self.tft = Tft
365 | self.tt = Tt
366 | self.ta = Ta
367 | self.t3 = t3
368 | self.tc = tc
369 | self.tr = tr
370 | self.tsr = Tsr
371 | self.toh = Toh
372 | self.csgs_reach = csgs_reach
373 |
374 | return TempCalc()
375 |
--------------------------------------------------------------------------------
/pwptemp/well_system.py:
--------------------------------------------------------------------------------
1 | import torque_drag as td
2 | import numpy as np
3 | from math import pi, log
4 | from copy import deepcopy
5 |
6 |
7 | def create_depth_cells(trajectory, cells_no=None):
8 |
9 | if cells_no is not None:
10 | md_new = list(np.linspace(0, max(trajectory.md), num=cells_no))
11 | tvd_new, inclination, azimuth = [], [], []
12 | for i in md_new:
13 | tvd_new.append(np.interp(i, trajectory.md, trajectory.tvd))
14 | inclination.append(np.interp(i, trajectory.md, trajectory.inclination))
15 | azimuth.append(np.interp(i, trajectory.md, trajectory.azimuth))
16 | depth_step = md_new[1]
17 |
18 | return md_new, tvd_new, depth_step, inclination, azimuth
19 |
20 | else:
21 | depth_step = trajectory.md[1] - trajectory.md[0]
22 | return trajectory.md, trajectory.tvd, depth_step, trajectory.inclination, trajectory.azimuth
23 |
24 |
25 | def set_well(temp_dict, trajectory, operation):
26 | q_conv = 60 # from m^3/min to m^3/h
27 | an_conv = 1 / 1500 # from in^2 to m^2
28 | diameter_conv = 0.0254 # from in to m
29 |
30 | class NewWell(object):
31 | def __init__(self):
32 |
33 | # DIMENSIONS
34 | self.trajectory = trajectory
35 | self.casings = temp_dict["casings"] # casings array
36 | self.pipe_id = temp_dict["pipe_id"] * diameter_conv # Drill String Inner Diameter, m
37 | self.pipe_od = temp_dict["pipe_od"] * diameter_conv # Drill String Outer Diameter, m
38 | # OFFSHORE
39 | self.water_depth = temp_dict["water_depth"] # Water Depth, m
40 | # number of grid cells for the riser
41 | self.riser_cells = len([point for point in trajectory if point['md'] <= self.water_depth])
42 | self.riser_id = temp_dict["riser_id"] * diameter_conv # Riser diameter Inner Diameter, m
43 | self.riser_od = temp_dict["riser_od"] * diameter_conv # Riser diameter Outer Diameter, m
44 | self.th_grad_seawater = temp_dict["th_grad_seawater"] # Seawater thermal grad., °C/cell
45 | # RADIUS (CALCULATED)
46 | self.r1 = self.pipe_id / 2 # Drill String Inner Radius, m
47 | self.r2 = self.pipe_od / 2 # Drill String Outer Radius, m
48 | self.annular_or = self.casings[0, 1] / 2 # Casing Inner Radius, m
49 | self.riser_ir = self.riser_id / 2 # Riser Inner Radius, m
50 | self.riser_or = self.riser_od / 2 # Riser Outer Radius, m
51 | # Surrounding Space Outer Diameter, m
52 | self.sr_od = sorted([self.riser_od + 0.03, self.casings[-1, 0] + 0.03])[-1]
53 | self.fm_diam = temp_dict["fm_diam"] * diameter_conv # Undisturbed Formation Diameter, m
54 | self.sr_diam = self.casings[0, 0] # Surrounding Space Inner Diameter, m
55 | self.sr_ir = self.sr_diam / 2 # Surrounding Space Inner Radius m
56 | self.sr_or = self.sr_od / 2 # Surrounding Space Outer Radius, m
57 | self.sr_thickness = self.sr_or - self.sr_ir # Surrounding thickness, m
58 | self.sr_fractions = get_fractions(self)
59 | self.fm_rad = self.fm_diam * diameter_conv / 2 # Undisturbed Formation Radius, m
60 |
61 | # OPERATIONAL
62 | self.temp_surface = temp_dict["temp_surface"] # Surface Temperature (RKB), °C
63 | if temp_dict["temp_inlet"] is None:
64 | self.temp_inlet = self.temp_surface
65 | else:
66 | self.temp_inlet = temp_dict["temp_inlet"] # Inlet Fluid temperature, °C
67 | self.th_grad_fm = temp_dict["th_grad_fm"] # Geothermal gradient, from °C/m to °C/cell
68 | self.q = temp_dict["flowrate"] * q_conv # Flow rate, m^3/h
69 | # Fluid velocity through the annular
70 | self.va = (self.q / (pi * ((self.annular_or ** 2) - (self.r2 ** 2)))) / 3600
71 | self.vp = (self.q / (pi * (self.r1 ** 2))) / 3600 # Fluid velocity through the drill pipe
72 | self.rpm = temp_dict["rpm"] # Revolutions per minute
73 | self.tbit = temp_dict["tbit"] # Torque on the bit, kN*m
74 | self.wob = temp_dict["wob"] # Weight on bit, kN
75 | self.rop_list = temp_dict["rop"]
76 | self.rop = temp_dict["rop"][0] # Rate of Penetration, m/h
77 | self.an = temp_dict["an"] * an_conv # Area of the nozzles, m^2
78 | self.bit_n = temp_dict["bit_n"] # drill bit efficiency
79 | self.dp_e = temp_dict["dp_e"] # drill pipe eccentricity
80 | self.thao_o = temp_dict["thao_o"]
81 | self.k = temp_dict["k"]
82 | self.n = temp_dict["n"]
83 | self.visc = temp_dict['visc']
84 |
85 | # DENSITIES
86 | self.rho_fluid = temp_dict["rho_fluid"] # Fluid
87 | self.rho_pipe = temp_dict["rho_pipe"] # Drill Pipe
88 | self.rho_csg = temp_dict["rho_csg"] # Casing
89 | self.rho_riser = temp_dict["rho_riser"] # Riser
90 | self.rho_cem = temp_dict["rho_cem"] # Cement Sheath
91 | self.rho_fm = temp_dict["rho_fm"] # Formation
92 | self.rho_seawater = temp_dict["rho_seawater"] # Seawater
93 |
94 | # Thermal conductivity W/(m*°C)
95 | self.tc_fluid = temp_dict["tc_fluid"] # Fluid
96 | self.tc_csg = temp_dict["tc_csg"] # Casing
97 | self.tc_cem = temp_dict["tc_cem"] # Cement
98 | self.tc_pipe = temp_dict["tc_pipe"] # Drill Pipe
99 | self.tc_fm = temp_dict["tc_fm"] # Formation
100 | self.tc_riser = temp_dict["tc_riser"] # Riser
101 | self.tc_seawater = temp_dict["tc_seawater"] # Seawater
102 |
103 | self.beta = temp_dict["beta"] # isothermal bulk modulus, Pa
104 | self.alpha = temp_dict['alpha'] # Fluid Thermal Expansion Coefficient, 1/°C
105 |
106 | # Specific heat capacity, J/(kg*°C)
107 | self.shc_fluid = temp_dict["shc_fluid"] # Fluid
108 | self.shc_csg = temp_dict["shc_csg"] # Casing
109 | self.shc_cem = temp_dict["shc_cem"] # Cement
110 | self.shc_pipe = temp_dict["shc_pipe"] # Drill Pipe
111 | self.shc_riser = temp_dict["shc_riser"] # Riser
112 | self.shc_seawater = temp_dict["shc_seawater"] # Seawater
113 | self.shc_fm = temp_dict["shc_fm"] # Formation
114 |
115 | self.sections = create_system(self)
116 |
117 | self.sections, self.temp_fm = calc_formation_temp(self)
118 |
119 | self.md = [x['md'] for x in self.trajectory]
120 | self.tvd = [x['tvd'] for x in self.trajectory]
121 | self.inclination = [x['inc'] for x in self.trajectory]
122 | self.azimuth = [x['azi'] for x in self.trajectory]
123 | self.dls = [x['dls'] for x in self.trajectory]
124 | td_obj = td.calc(self,
125 | dimensions={'od_pipe': self.pipe_od, 'id_pipe': self.pipe_id,
126 | 'length_pipe': self.trajectory[-1]['md'],
127 | 'od_annular': self.annular_or * 2},
128 | case='static',
129 | densities={'rhof': self.rho_fluid, 'rhod': self.rho_pipe},
130 | fric=0.24, wob=self.wob, tbit=self.tbit, torque_calc=True)
131 |
132 | self.drag = td_obj.force['static'] # drag forces, kN
133 | self.torque = td_obj.torque['static'] # torque, kN*m
134 |
135 | self.sections, self.h1, self.h2, self.h3, self.h3r = extra_calcs(self)
136 | self.temperatures = None
137 |
138 | if operation == 'drilling':
139 | if len(self.casings) != len(self.rop_list) - 1 and self.casings[0, 2] != 0:
140 | raise ValueError('ROP must be set for all the sections.')
141 |
142 | return NewWell()
143 |
144 |
145 | def create_system(well):
146 | section_0, section_1, section_2, section_3, section_4 = [], [], [], [], []
147 | depth_base = -1
148 | for x in range(len(well.trajectory)):
149 | initial_dict = {'component': '', 'material': '', 'rho': 2.24, 'tc': '', 'shc': '',
150 | 'depth': well.trajectory[x]['md'], 'cellDepth': well.trajectory[x]['md'] - depth_base}
151 | depth_base = well.trajectory[x]['md']
152 | section_0.append(deepcopy(initial_dict))
153 | section_1.append(deepcopy(initial_dict))
154 | section_2.append(deepcopy(initial_dict))
155 | section_3.append(deepcopy(initial_dict))
156 | section_4.append(deepcopy(initial_dict))
157 | sections = [section_0, section_1, section_2, section_3, section_4]
158 | sections_names = ['section_0', 'section_1', 'section_2', 'section_3', 'section_4']
159 |
160 | for x, i in zip(sections, sections_names):
161 | for y in range(len(well.trajectory)):
162 | x[y]['material'] = get_material(i, well.trajectory[y]['md'],
163 | first_casing_depth=well.casings[0, 2],
164 | water_depth=well.water_depth)
165 | if x[y]['material'] == 'mixture':
166 | pipe_fraction, cement_fraction = get_fractions_at_depth(well, y)
167 | x[y]['rho'] = get_mixture_density(pipe_fraction, cement_fraction, well) * 1000
168 | x[y]['tc'] = get_mixture_thermal_conductivity(pipe_fraction, cement_fraction, well)
169 | x[y]['shc'] = get_mixture_heat_capacity(pipe_fraction, cement_fraction, well)
170 |
171 | else:
172 | x[y]['rho'] = get_density(x[y]['material'], well) * 1000
173 | x[y]['tc'] = get_thermal_conductivity(x[y]['material'], well)
174 | x[y]['shc'] = get_heat_capacity(x[y]['material'], well)
175 | if x[y]['material'] == 'fluid':
176 | x[y]['visc'] = get_viscosity(well)
177 |
178 | return sections
179 |
180 |
181 | def get_fractions_at_depth(well, cell):
182 | the_index = -1
183 | casing_depths = reversed([x[2] for x in well.casings])
184 | for x in casing_depths:
185 | if well.trajectory[cell]['md'] > x:
186 | the_index -= 1
187 |
188 | pipe_fraction = well.sr_fractions[the_index][0]
189 | cement_fraction = well.sr_fractions[the_index][1]
190 |
191 | return pipe_fraction, cement_fraction
192 |
193 |
194 | def get_density(material, well):
195 | rho = {'formation': well.rho_fm,
196 | 'fluid': well.rho_fluid,
197 | 'pipe': well.rho_fm,
198 | 'casing': well.rho_csg,
199 | 'riser': well.rho_riser,
200 | 'seawater': well.rho_seawater,
201 | 'cement': well.rho_cem}
202 |
203 | return rho[material]
204 |
205 |
206 | def get_mixture_density(pipe_fraction, cement_fraction, well):
207 | formation_fraction = 1 - pipe_fraction - cement_fraction
208 | rho = pipe_fraction * get_density('casing', well) + \
209 | cement_fraction * get_density('cement', well) + \
210 | formation_fraction * get_density('formation', well)
211 |
212 | return rho
213 |
214 |
215 | def get_viscosity(well):
216 | rho = {'fluid': well.visc}
217 |
218 | return rho['fluid']
219 |
220 |
221 | def get_thermal_conductivity(material, well):
222 | tc = {'formation': well.tc_fm,
223 | 'fluid': well.tc_fluid,
224 | 'pipe': well.tc_fm,
225 | 'casing': well.tc_csg,
226 | 'riser': well.tc_riser,
227 | 'seawater': well.tc_seawater,
228 | 'cement': well.tc_cem}
229 |
230 | return tc[material]
231 |
232 |
233 | def get_mixture_thermal_conductivity(pipe_fraction, cement_fraction, well):
234 | formation_fraction = 1 - pipe_fraction - cement_fraction
235 | rho = pipe_fraction * get_thermal_conductivity('casing', well) + \
236 | cement_fraction * get_thermal_conductivity('cement', well) + \
237 | formation_fraction * get_thermal_conductivity('formation', well)
238 |
239 | return rho
240 |
241 |
242 | def get_heat_capacity(material, well):
243 | shc = {'formation': well.shc_fm,
244 | 'fluid': well.shc_fluid,
245 | 'pipe': well.shc_fm,
246 | 'casing': well.shc_csg,
247 | 'riser': well.shc_riser,
248 | 'seawater': well.shc_seawater,
249 | 'cement': well.shc_cem}
250 |
251 | return shc[material]
252 |
253 |
254 | def get_mixture_heat_capacity(pipe_fraction, cement_fraction, well):
255 | formation_fraction = 1 - pipe_fraction - cement_fraction
256 | rho = pipe_fraction * get_heat_capacity('casing', well) + \
257 | cement_fraction * get_heat_capacity('cement', well) + \
258 | formation_fraction * get_heat_capacity('formation', well)
259 |
260 | return rho
261 |
262 |
263 | def get_fractions(well):
264 | fractions = []
265 | for x in range(len(well.casings) - 1):
266 | pipe_thickness = (well.casings[x, 0] - well.casings[x, 1]) / 2
267 | pipe_fraction = pipe_thickness / well.sr_thickness
268 | cement_thickness = (well.casings[x + 1, 1] - well.casings[x, 0]) / 2
269 | cement_fraction = cement_thickness / well.sr_thickness
270 | fractions.append([pipe_fraction, cement_fraction])
271 |
272 | last_pipe_fraction = ((well.casings[-1, 0] - well.casings[-1, 1]) / 2) / well.sr_thickness
273 | last_cement_fraction = ((well.sr_or - well.casings[-1, 0]) / 2) / well.sr_thickness
274 |
275 | fractions.append([last_pipe_fraction, last_cement_fraction])
276 |
277 | fractions[0][0] = 0
278 |
279 | cement = 0 # cement layer fraction for the first casing (the deepest)
280 | pipe = 0
281 | fractions_per_section = [[pipe, cement]]
282 | for x in fractions:
283 | cement += x[1]
284 | pipe += x[0]
285 | fractions_per_section.append([pipe, cement])
286 |
287 | return fractions_per_section
288 |
289 |
290 | def get_material(section, md, operation='drilling', first_casing_depth=None, water_depth=0):
291 | if first_casing_depth is None:
292 | first_casing_depth = -1
293 |
294 | if md <= first_casing_depth:
295 | if md >= water_depth:
296 | section_3 = 'casing'
297 | else:
298 | section_3 = 'riser'
299 | else:
300 | section_3 = 'formation'
301 |
302 | if md >= water_depth:
303 | section_4 = 'mixture'
304 | if first_casing_depth != -1:
305 | section_4 = 'mixture'
306 | else:
307 | section_4 = 'seawater'
308 |
309 | if operation == 'drilling':
310 | component = {'section_0': 'fluid',
311 | 'section_1': 'pipe',
312 | 'section_2': 'fluid',
313 | 'section_3': section_3,
314 | 'section_4': section_4}
315 | else:
316 | component = None
317 | return component[section]
318 |
319 |
320 | def extra_calcs(well):
321 | h1, h2, h3, h3r = [], [], [], []
322 | sections = well.sections.copy()
323 | for x, y in zip(sections[0], sections[2]):
324 | # Reynolds number
325 | x['re'] = calc_re(well, x['rho'], x['visc'], section=0) # Section 0
326 | y['re'] = calc_re(well, y['rho'], y['visc'], section=2) # Section 2 - Annulus
327 | # Prandtl number
328 | x['pr'] = calc_pr(x['shc'], x['tc'], x['visc']) # Section 0
329 | y['pr'] = calc_pr(y['shc'], y['tc'], y['visc']) # Section 2 - Annulus
330 | # Friction factor
331 | x['f'] = calc_friction_factor(x['re']) # Section 0
332 | y['f'] = calc_friction_factor(y['re']) # Section 2 - Annulus
333 | # Nusselt number
334 | nu_int = calc_nu(well, x['f'], x['re'], x['pr'], section=1) # Section 1
335 | nu_ext = calc_nu(well, y['f'], y['re'], y['pr'], section=1) # Section 1
336 | nu_ann = calc_nu(well, y['f'], y['re'], y['pr'], section=2) # Section 2 - Annulus
337 | # Heat transfer coefficients
338 | h1.append(calc_heat_transfer_coef(x['tc'], nu_int, well.pipe_id))
339 | h2.append(calc_heat_transfer_coef(x['tc'], nu_ext, well.pipe_od))
340 | h3.append(calc_heat_transfer_coef(x['tc'], nu_ann, well.annular_or * 2))
341 | h3r.append(calc_heat_transfer_coef(x['tc'], nu_ann, well.riser_ir * 2))
342 |
343 | return sections, h1, h2, h3, h3r
344 |
345 |
346 | def calc_re(well, rho_fluid, viscosity, section=0):
347 | if section == 0:
348 | # Reynolds number inside pipe
349 | re = rho_fluid * well.vp * 2 * well.r1 / viscosity
350 | else:
351 | # Reynolds number - annular
352 | re = rho_fluid * well.va * 2 * (well.annular_or - well.r2) / viscosity
353 |
354 | return re
355 |
356 |
357 | def calc_pr(shc_fluid, tc_fluid, viscosity):
358 | pr = viscosity * shc_fluid / tc_fluid
359 |
360 | return pr
361 |
362 |
363 | def calc_friction_factor(re):
364 | f = 64 / re
365 |
366 | if 2300 < re < 10000:
367 | f = 1.63 / log(6.9 / re) ** 2
368 |
369 | if re >= 10000:
370 | f = 1.63 / log(6.9 / re) ** 2
371 |
372 | return f
373 |
374 |
375 | def calc_nu(well, f, re, pr, section=1):
376 | nu = 4.36
377 | if section != 1 and re < 2300:
378 | nu = 1.86 * ((re * pr) ** (1 / 3)) * \
379 | ((2 * (well.annular_or - well.r2) / well.md[-1]) ** (1 / 3)) \
380 | * (1 ** 0.14)
381 |
382 | if 2300 < re < 10000:
383 | nu = (f / 8) * (re - 1000) * pr / (1 + (12.7 * (f / 8) ** 0.5) * (pr ** (2 / 3) - 1))
384 |
385 | if re >= 10000:
386 | nu = 0.027 * (re ** (4 / 5)) * (pr ** (1 / 3)) * (1 ** 0.14)
387 |
388 | return nu
389 |
390 |
391 | def calc_heat_transfer_coef(tc, nu, diameter):
392 | h = tc * nu / diameter
393 |
394 | return h
395 |
396 |
397 | def calc_formation_temp(well):
398 | temp_fm = [well.temp_surface]
399 | for j in range(1, len(well.trajectory)):
400 |
401 | if j <= well.riser_cells:
402 | th_grad = well.th_grad_seawater # Water Thermal Gradient for the Riser section
403 | else:
404 | th_grad = well.th_grad_fm # Geothermal Gradient below the Riser section
405 | temp_delta = temp_fm[j - 1] + th_grad * (well.trajectory[j]['tvd'] - well.trajectory[j - 1]['tvd'])
406 |
407 | # Generating the Temperature Profile at t=0
408 | temp_fm.append(temp_delta)
409 |
410 | for x in well.sections:
411 | for y in range(len(well.trajectory)):
412 | x[y]['temp'] = temp_fm[y]
413 | x[y]['temp_fm'] = temp_fm[y]
414 |
415 | return well.sections, temp_fm
416 |
--------------------------------------------------------------------------------
/pwptemp/production/input.py:
--------------------------------------------------------------------------------
1 | def data(casings=[], d_openhole=0.216, units='metric'):
2 | """
3 | Parameters involved within the operation calculations
4 | :param casings: list of dictionaries with casings characteristics (od, id and depth)
5 | :param d_openhole: diameter of open hole section, m
6 | :param units: system of units ('metric' or 'english')
7 | :return: a dictionary with default values for the required parameters
8 | """
9 |
10 | from numpy import asarray
11 |
12 | dict_met = {'ts': 15.0, 'wd': 100.0, 'dti': 4.0, 'dto': 4.5, 'dri': 17.716, 'dro': 21.0, 'dfm': 80.0,
13 | 'q': 2000, 'lambdaf': 0.635, 'lambdaf_a': 0.635, 'lambdac': 43.3, 'lambdacem': 0.7, 'lambdat': 40.0,
14 | 'lambdafm': 2.249, 'lambdar': 15.49, 'lambdaw': 0.6, 'cf': 3713.0, 'cf_a': 3713.0, 'cc': 469.0,
15 | 'ccem': 2000.0, 'ct': 400.0, 'cr': 464.0, 'cw': 4000.0, 'cfm': 800.0, 'rhof': 0.85, 'rhof_a': 1.2,
16 | 'rhot': 7.8, 'rhoc': 7.8, 'rhor': 7.8, 'rhofm': 2.245, 'rhow': 1.029, 'rhocem': 2.7, 'gt': 0.0238,
17 | 'wtg': -0.005, 'api': 30, 'beta': 44983 * 10 ** 5, 'alpha': 960 * 10 ** -6, 'beta_a': 44983 * 10 ** 5,
18 | 'alpha_a': 960 * 10 ** -6}
19 |
20 | dict_eng = {'ts': 59.0, 'wd': 328.0, 'dti': 4.0, 'dto': 4.5, 'dri': 17.716, 'dro': 21.0, 'dfm': 80.0,
21 | 'q': 366.91, 'lambdaf': 1.098, 'lambdaf_a': 1.098, 'lambdac': 74.909, 'lambdacem': 1.21,
22 | 'lambdat': 69.2, 'lambdafm': 3.89, 'lambdar': 26.8, 'lambdaw': 1.038, 'cf': 0.887, 'cf_a': 0.887,
23 | 'cc': 0.112, 'ccem': 0.478, 'ct': 0.096, 'cr': 0.1108, 'cw': 0.955, 'cfm': 0.19, 'rhof': 7.09,
24 | 'rhof_a': 10, 'rhot': 65.09, 'rhoc': 65.09, 'rhor': 65.09, 'rhofm': 18.73, 'rhow': 8.587,
25 | 'rhocem': 22.5, 'gt': 0.00403, 'wtg': -8.47*10**-4, 'api': 30, 'beta': 652423,
26 | 'alpha': 5.33 * 10 ** -4, 'beta_a': 652423, 'alpha_a': 5.33 * 10 ** -4}
27 |
28 | if units == 'metric':
29 | dict = dict_met
30 | else:
31 | dict = dict_eng
32 |
33 | if len(casings) > 0:
34 | od = sorted([x['od'] * 0.0254 for x in casings])
35 | id = sorted([x['id'] * 0.0254 for x in casings])
36 | depth = sorted([x['depth'] for x in casings], reverse=True)
37 | dict['casings'] = [[od[x], id[x], depth[x]] for x in range(len(casings))]
38 | dict['casings'] = asarray(dict['casings'])
39 | else:
40 | dict['casings'] = [[(d_openhole + dict['dro'] * 0.0254), d_openhole, 0]]
41 | dict['casings'] = asarray(dict['casings'])
42 |
43 | return dict
44 |
45 |
46 | def info(about='all'):
47 | """
48 | Retrieves information about the parameters (description and units)
49 | :param about: type of parameters
50 | :return: description and units of parameters
51 | """
52 |
53 | print("Use the ID of a parameter to change the default value (e.g. tdict['tin']=30 to change the fluid inlet "
54 | "temperature from the default value to 30° Celsius)")
55 | print('Notice that the information is provided as follows:' + '\n' +
56 | 'parameter ID: general description, units' + '\n')
57 |
58 | tubular_parameters = 'VALUES RELATED TO TUBULAR SIZES' + '\n' + \
59 | 'dti: tubing inner diameter, in' + '\n' + \
60 | 'dto: tubing outer diameter, in' + '\n' + \
61 | 'dri: riser inner diameter, in' + '\n' + \
62 | 'dro: riser outer diameter, in' + '\n'
63 |
64 | conditions_parameters = 'PARAMETERS RELATED TO SIMULATION CONDITIONS' + '\n' + \
65 | 'ts: surface temperature, °C or °F' + '\n' + \
66 | 'wd: water depth, m or ft' + '\n' + \
67 | 'dfm: undisturbed formation diameter, m or ft' + '\n'
68 |
69 | heatcoeff_parameters = 'PARAMETERS RELATED TO HEAT COEFFICIENTS' + '\n' + \
70 | 'lambdaf: production fluid - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
71 | 'lambdaf_a: annular fluid - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
72 | 'lambdac: casing - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
73 | 'lambdacem: cement - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
74 | 'lambdat: tubing - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
75 | 'lambdafm: formation - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
76 | 'lambdar: riser - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
77 | 'lambdaw: water - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
78 | 'cf: production fluid - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
79 | 'cf_a: annular fluid - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
80 | 'cc: casing - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
81 | 'ccem: cement - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
82 | 'ct: tubing - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
83 | 'cr: riser - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
84 | 'cw: water - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
85 | 'cfm: formation - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
86 | 'gt: geothermal gradient, °C/m or °F/ft' + '\n' + \
87 | 'wtg: seawater thermal gradient, °C/m or °F/ft' + '\n'
88 |
89 | densities_parameters = 'PARAMETERS RELATED TO DENSITIES' + '\n' + \
90 | 'rhof: production fluid density, sg or ppg' + '\n' + \
91 | 'rhof_a: annular fluid density, sg or ppg' + '\n' + \
92 | 'rhot: tubing density, sg or ppg' + '\n' + \
93 | 'rhoc: casing density, sg or ppg' + '\n' + \
94 | 'rhor: riser density, sg or ppg' + '\n' + \
95 | 'rhofm: formation density, sg or ppg' + '\n' + \
96 | 'rhow: seawater density, sg or ppg' + '\n' + \
97 | 'rhocem: cement density, sg or ppg' + '\n' + \
98 | 'api: api gravity, °API' + '\n' + \
99 | 'beta: isothermal bulk modulus of production fluid, Pa' + '\n' + \
100 | 'alpha: expansion coefficient of production fluid, 1/°C' + '\n' + \
101 | 'beta_a: isothermal bulk modulus of fluid in annular, Pa' + '\n' + \
102 | 'alpha_a: expansion coefficient of fluid in annular, 1/°C or 1/°F' + '\n'
103 |
104 |
105 | operational_parameters = 'PARAMETERS RELATED TO THE OPERATION' + '\n' + \
106 | 'q: flow rate, m^3/day or bbl/day' + '\n'
107 |
108 | if about == 'casings':
109 | print(tubular_parameters)
110 |
111 | if about == 'conditions':
112 | print(conditions_parameters)
113 |
114 | if about == 'heatcoeff':
115 | print(heatcoeff_parameters)
116 |
117 | if about == 'densities':
118 | print(densities_parameters)
119 |
120 | if about == 'operational':
121 | print(operational_parameters)
122 |
123 | if about == 'all':
124 | print(tubular_parameters + '\n' + conditions_parameters + '\n' + heatcoeff_parameters + '\n' +
125 | densities_parameters + '\n' + operational_parameters)
126 |
127 |
128 | def set_well(temp_dict, depths, units='metric'):
129 | """
130 | Define properly the parameters and respective values within an object well.
131 | :param temp_dict: dictionary with inputs and default values.
132 | :param depths: wellpath object
133 | :param units: system of units ('metric' or 'english')
134 | :return: a well object with conditions and parameters defined
135 | """
136 |
137 | from math import pi, log
138 |
139 | class NewWell(object):
140 | def __init__(self):
141 | # DEPTH
142 | self.md = depths.md
143 | self.tvd = depths.tvd
144 | self.deltaz = self.md[-1] - self.md[-2]
145 | self.zstep = depths.cells_no
146 | self.sections = depths.sections
147 | self.north = depths.north
148 | self.east = depths.east
149 | self.inclination = depths.inclination
150 | self.dogleg = depths.dogleg
151 | self.azimuth = depths.azimuth
152 | if units != 'metric':
153 | self.md = [i / 3.28 for i in self.md]
154 | self.tvd = [i / 3.28 for i in self.tvd]
155 | self.deltaz = self.deltaz / 3.28
156 | self.north = [i / 3.28 for i in self.north]
157 | self.east = [i / 3.28 for i in self.east]
158 |
159 | # TUBULAR
160 | if units == 'metric':
161 | d_conv = 0.0254 # from in to m
162 | else:
163 | d_conv = 0.0254 # from in to m
164 | self.casings = temp_dict["casings"] # casings array
165 | self.dti = temp_dict["dti"] * d_conv # Tubing Inner Diameter, m
166 | self.dto = temp_dict["dto"] * d_conv # Tubing Outer Diameter, m
167 | self.dri = temp_dict["dri"] * d_conv # Riser diameter Inner Diameter, m
168 | self.dro = temp_dict["dro"] * d_conv # Riser diameter Outer Diameter, m
169 |
170 | # CONDITIONS
171 | if units == 'metric':
172 | depth_conv = 1 # from m to m
173 | self.ts = temp_dict["ts"] # Surface Temperature (RKB), °C
174 | else:
175 | depth_conv = 1 / 3.28 # from ft to m
176 | self.ts = (temp_dict["ts"] - 32) * (5 / 9) # Surface Temperature (RKB), from °F to °C
177 | self.wd = temp_dict["wd"] * depth_conv # Water Depth, m
178 | self.riser = round(self.wd / self.deltaz) # number of grid cells for the riser
179 | self.dsr = self.casings[0, 0] # Surrounding Space Inner Diameter, m
180 | self.dsro = sorted([self.dro + 0.03, self.casings[-1, 0] + 0.03])[-1] # Surrounding Space Outer Diameter, m
181 | self.dfm = temp_dict["dfm"] * d_conv # Undisturbed Formation Diameter, m
182 |
183 | # RADIUS (CALCULATED)
184 | self.r1 = self.dti / 2 # Tubing Inner Radius, m
185 | self.r2 = self.dto / 2 # Tubing Outer Radius, m
186 | self.r3 = self.casings[0, 1] / 2 # Casing Inner Radius, m
187 | self.r3r = self.dri / 2 # Riser Inner Radius, m
188 | self.r4r = self.dro / 2 # Riser Outer Radius, m
189 | self.r4 = self.casings[0, 0] / 2 # Surrounding Space Inner Radius m
190 | self.r5 = self.dsro / 2 # Surrounding Space Outer Radius, m
191 | self.rfm = self.dfm / 2 # Undisturbed Formation Radius, m
192 |
193 | # DENSITIES kg/m3
194 | if units == 'metric':
195 | dens_conv = 1000 # from sg to kg/m3
196 | else:
197 | dens_conv = 119.83 # from ppg to kg/m3
198 | self.rhof = temp_dict["rhof"] * dens_conv # Fluid
199 | self.rhof_a = temp_dict["rhof_a"] * dens_conv # Fluid
200 | self.rhot = temp_dict["rhot"] * dens_conv # Tubing
201 | self.rhoc = temp_dict["rhoc"] * dens_conv # Casing
202 | self.rhor = temp_dict["rhor"] * dens_conv # Riser
203 | self.rhocem = temp_dict["rhocem"] * dens_conv # Cement Sheath
204 | self.rhofm = temp_dict["rhofm"] * dens_conv # Formation
205 | self.rhow = temp_dict["rhow"] * dens_conv # Seawater
206 |
207 | # OPERATIONAL
208 | if units == 'metric':
209 | q_conv = 0.04167 # from m^3/day to m^3/h
210 | else:
211 | q_conv = 0.0066244706 # from bbl/day to m^3/h
212 | self.q = temp_dict["q"] * q_conv # Flow rate, m^3/h
213 | self.vp = (self.q / (pi * (self.r1 ** 2))) / 3600 # Fluid velocity through the tubing
214 |
215 | # HEAT COEFFICIENTS
216 | if units == 'metric':
217 | lambda_conv = 1 # from W/(m*°C) to W/(m*°C)
218 | c_conv = 1 # from J/(kg*°C) to J/(kg*°C)
219 | gt_conv = 1 # from °C/m to °C/m
220 | beta_conv = 1 # from Pa to Pa
221 | alpha_conv = 1 # from 1/°F to 1/°C
222 | else:
223 | lambda_conv = 1/1.73 # from BTU/(h*ft*°F) to W/(m*°C)
224 | c_conv = 4187.53 # from BTU/(lb*°F) to J/(kg*°C)
225 | gt_conv = 3.28*1.8 # from °F/ft to °C/m
226 | beta_conv = 6894.76 # from psi to Pa
227 | alpha_conv = 1.8 # from 1/°F to 1/°C
228 |
229 | # Thermal conductivity W/(m*°C)
230 | self.lambdaf = temp_dict["lambdaf"] * lambda_conv # Production Fluid
231 | self.lambdaf_a = temp_dict["lambdaf_a"] * lambda_conv # Annular Fluid
232 | self.lambdac = temp_dict["lambdac"] * lambda_conv # Casing
233 | self.lambdacem = temp_dict["lambdacem"] * lambda_conv # Cement
234 | self.lambdat = temp_dict["lambdat"] * lambda_conv # Tubing wall
235 | self.lambdafm = temp_dict["lambdafm"] * lambda_conv # Formation
236 | self.lambdar = temp_dict["lambdar"] * lambda_conv # Riser
237 | self.lambdaw = temp_dict["lambdaw"] * lambda_conv # Seawater
238 |
239 | self.beta = temp_dict["beta"] * beta_conv # isothermal bulk modulus in tubing, Pa
240 | self.alpha = temp_dict['alpha'] * alpha_conv # Fluid Thermal Expansion Coefficient in tubing, 1/°C
241 | self.beta_a = temp_dict["beta_a"] * beta_conv # isothermal bulk modulus in annular, Pa
242 | self.alpha_a = temp_dict['alpha_a'] * alpha_conv # Fluid Thermal Expansion Coefficient in annular, 1/°C
243 |
244 | # Specific heat capacity, J/(kg*°C)
245 | self.cf = temp_dict["cf"] * c_conv # Production Fluid
246 | self.cf_a = temp_dict["cf"] * c_conv # Annular Fluid
247 | self.cc = temp_dict["cc"] * c_conv # Casing
248 | self.ccem = temp_dict["ccem"] * c_conv # Cement
249 | self.ct = temp_dict["ct"] * c_conv # Tubing
250 | self.cr = temp_dict["cr"] * c_conv # Riser
251 | self.cw = temp_dict["cw"] * c_conv # Seawater
252 | self.cfm = temp_dict["cfm"] * c_conv # Formation
253 |
254 | self.gt = temp_dict["gt"] * gt_conv * self.deltaz # Geothermal gradient, °C/m
255 | self.wtg = temp_dict["wtg"] * gt_conv * self.deltaz # Seawater thermal gradient, °C/m
256 |
257 | # Raise Errors:
258 |
259 | if self.casings[-1, 0] > self.dsro:
260 | raise ValueError('Last casing outer diameter must be smaller than the surrounding space diameter.')
261 |
262 | if self.casings[0, 2] > self.md[-1]:
263 | raise ValueError('MD must be higher than the first casing depth.')
264 |
265 | if self.casings[0, 1] < self.dto:
266 | raise ValueError('Tubing outer diameter must be smaller than the first casing inner diameter.')
267 |
268 | if self.wd > 0 and self.dro > self.dsro:
269 | raise ValueError('Riser diameter must be smaller than the surrounding space diameter.')
270 |
271 | if self.dsro > self.dfm:
272 | raise ValueError('Surrounding space diameter must be smaller than the undisturbed formation diameter.')
273 |
274 | def wellpath(self):
275 | """
276 | :return: wellpath object
277 | """
278 | return depths
279 |
280 | def define_density(self, ic, cond=0):
281 | """
282 | Calculate the density profile
283 | :param ic: current temperature distribution
284 | :param cond: '0' to calculate the initial profile
285 | :return: density profile and derived calculations
286 | """
287 |
288 | from .fluid import initial_density, calc_density
289 |
290 | if cond == 0:
291 | self.rhof, self.rhof_initial = initial_density(self, ic)
292 | self.rhof_a, self.rhof_a_initial = initial_density(self, ic, section='annular')
293 | else:
294 | self.rhof = calc_density(self, ic, self.rhof_initial)
295 | self.rhof_a = calc_density(self, ic, self.rhof_initial, section='annular')
296 | # Reynolds number inside tubing
297 | self.re_p = [x * self.vp * 2 * self.r1 / y for x, y in zip(self.rhof, self.visc_t)]
298 | self.f_p = [] # Friction factor inside tubing
299 | self.nu_dpi = []
300 | for x in range(len(self.md)):
301 | if self.re_p[x] < 2300:
302 | self.f_p.append(64 / self.re_p[x])
303 | self.nu_dpi.append(4.36)
304 | else:
305 | self.f_p.append(1.63 / log(6.9 / self.re_p[x]) ** 2)
306 | self.nu_dpi.append(
307 | (self.f_p[x] / 8) * (self.re_p[x] - 1000) *
308 | self.pr_t[x] / (1 + (12.7 * (self.f_p[x] / 8) ** 0.5) *
309 | (self.pr_t[x] ** (2 / 3) - 1)))
310 | # convective heat transfer coefficients, W/(m^2*°C)
311 | self.h1 = [self.lambdaf * x / self.dti for x in self.nu_dpi] # Tubing inner wall
312 |
313 | return self
314 |
315 | def define_viscosity(self, ic):
316 | """
317 | Calculate the viscosity profile
318 | :param ic: current temperature distribution
319 | :return: viscosity profile and derived calculations
320 | """
321 |
322 | from .fluid import calc_vicosity
323 | self.api = temp_dict["api"]
324 | self.visc_t, self.visc_a = calc_vicosity(self.api, ic)
325 |
326 | self.pr_t = [x * self.cf / self.lambdaf for x in self.visc_t] # Prandtl number - inside tubing
327 | self.pr_a = [x * self.cf / self.lambdaf for x in self.visc_a] # Prandtl number - annulus
328 |
329 | return self
330 |
331 | return NewWell()
332 |
--------------------------------------------------------------------------------
/pwptemp/injection/input.py:
--------------------------------------------------------------------------------
1 | def data(casings=[], d_openhole=0.216, units='metric'):
2 | """
3 | Parameters involved within the operation calculations
4 | :param casings: list of dictionaries with casings characteristics (od, id and depth)
5 | :param d_openhole: diameter of open hole section, m
6 | :param units: system of units ('metric' or 'english')
7 | :return: a dictionary with default values for the required parameters
8 | """
9 |
10 | from numpy import asarray
11 |
12 | dict_met = {'ts': 15.0, 'wd': 100.0, 'dti': 4.0, 'dto': 4.5, 'dri': 17.716, 'dro': 21.0, 'dfm': 80.0,
13 | 'q': 144, 'lambdaf': 0.635, 'lambdaf_a': 0.635, 'lambdac': 43.3, 'lambdacem': 0.7, 'lambdat': 40.0,
14 | 'lambdafm': 2.249, 'lambdar': 15.49, 'lambdaw': 0.6, 'cf': 3713.0, 'cf_a': 3713.0, 'cc': 469.0,
15 | 'ccem': 2000.0, 'ct': 400.0, 'cr': 464.0, 'cw': 4000.0, 'cfm': 800.0, 'rhof': 1.198, 'rhof_a': 1.2,
16 | 'rhot': 7.6, 'rhoc': 7.8, 'rhor': 7.8, 'rhofm': 2.245, 'rhow': 1.029, 'rhocem': 2.7, 'gt': 0.0238,
17 | 'wtg': -0.005, 'tin': 20, 'beta': 44983 * 10 ** 5, 'alpha': 960 * 10 ** -6,
18 | 'beta_a': 44983 * 10 ** 5, 'alpha_a': 960 * 10 ** -6}
19 |
20 | dict_eng = {'ts': 59.0, 'wd': 328.0, 'dti': 4.0, 'dto': 4.5, 'dri': 17.716, 'dro': 21.0, 'dfm': 80.0,
21 | 'q': 26.42, 'lambdaf': 1.098, 'lambdaf_a': 1.098, 'lambdac': 74.909, 'lambdacem': 1.21, 'lambdat': 69.2,
22 | 'lambdafm': 3.89, 'lambdar': 26.8, 'lambdaw': 1.038, 'cf': 0.887, 'cf_a': 0.887, 'cc': 0.112,
23 | 'ccem': 0.478, 'ct': 0.096, 'cr': 0.1108, 'cw': 0.955, 'cfm': 0.19, 'rhof': 9.997, 'rhof_a': 10,
24 | 'rhot': 65.09, 'rhoc': 65.09, 'rhor': 65.09, 'rhofm': 18.73, 'rhow': 8.587, 'rhocem': 22.5,
25 | 'gt': 0.00403, 'wtg': -8.47*10**-4, 'tin': 68, 'beta': 652423, 'alpha': 5.33 * 10 ** -4,
26 | 'beta_a': 652423, 'alpha_a': 5.33 * 10 ** -4}
27 |
28 | if units == 'metric':
29 | dict = dict_met
30 | else:
31 | dict = dict_eng
32 |
33 | if len(casings) > 0:
34 | od = sorted([x['od'] * 0.0254 for x in casings])
35 | id = sorted([x['id'] * 0.0254 for x in casings])
36 | depth = sorted([x['depth'] for x in casings], reverse=True)
37 | dict['casings'] = [[od[x], id[x], depth[x]] for x in range(len(casings))]
38 | dict['casings'] = asarray(dict['casings'])
39 | else:
40 | dict['casings'] = [[(d_openhole + dict['dro'] * 0.0254), d_openhole, 0]]
41 | dict['casings'] = asarray(dict['casings'])
42 |
43 | return dict
44 |
45 |
46 | def info(about='all'):
47 | """
48 | Retrieves information about the parameters (description and units)
49 | :param about: type of parameters
50 | :return: description and units of parameters
51 | """
52 |
53 | print("Use the ID of a parameter to change the default value (e.g. tdict['tin']=30 to change the fluid inlet "
54 | "temperature from the default value to 30° Celsius)")
55 | print('Notice that the information is provided as follows:' + '\n' +
56 | 'parameter ID: general description, units' + '\n')
57 |
58 | tubular_parameters = 'VALUES RELATED TO TUBULAR SIZES' + '\n' + \
59 | 'dti: tubing inner diameter, in' + '\n' + \
60 | 'dto: tubing outer diameter, in' + '\n' + \
61 | 'dri: riser inner diameter, in' + '\n' + \
62 | 'dro: riser outer diameter, in' + '\n'
63 |
64 | conditions_parameters = 'PARAMETERS RELATED TO SIMULATION CONDITIONS' + '\n' + \
65 | 'ts: surface temperature, °C or °F' + '\n' + \
66 | 'wd: water depth, m or ft' + '\n' + \
67 | 'dfm: undisturbed formation diameter, m or ft' + '\n'
68 |
69 | heatcoeff_parameters = 'PARAMETERS RELATED TO HEAT COEFFICIENTS' + '\n' + \
70 | 'lambdaf: injection fluid - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
71 | 'lambdaf_a: annular fluid - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
72 | 'lambdac: casing - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
73 | 'lambdacem: cement - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
74 | 'lambdat: tubing - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
75 | 'lambdafm: formation - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
76 | 'lambdar: riser - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
77 | 'lambdaw: water - thermal conductivity, W/(m*°C) or BTU/(h*ft*°F)' + '\n' + \
78 | 'cf: injection fluid - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
79 | 'cf_a: annular fluid - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
80 | 'cc: casing - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
81 | 'ccem: cement - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
82 | 'ct: tubing - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
83 | 'cr: riser - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
84 | 'cw: water - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
85 | 'cfm: formation - specific heat capacity, J/(kg*°C) or BTU/(lb*°F)' + '\n' + \
86 | 'gt: geothermal gradient, °C/m or °F/ft' + '\n' + \
87 | 'wtg: seawater thermal gradient, °C/m or °F/ft' + '\n'
88 |
89 | densities_parameters = 'PARAMETERS RELATED TO DENSITIES' + '\n' + \
90 | 'rhof: fluid density, sg or ppg' + '\n' + \
91 | 'rhot: tubing density, sg or ppg' + '\n' + \
92 | 'rhoc: casing density, sg or ppg' + '\n' + \
93 | 'rhor: riser density, sg or ppg' + '\n' + \
94 | 'rhofm: formation density, sg or ppg' + '\n' + \
95 | 'rhow: seawater density, sg or ppg' + '\n' + \
96 | 'rhocem: cement density, sg or ppg' + '\n' + \
97 | 'beta: isothermal bulk modulus of injection fluid, Pa' + '\n' + \
98 | 'alpha: expansion coefficient of injection fluid, 1/°C' + '\n' + \
99 | 'beta_a: isothermal bulk modulus of fluid in annular, Pa or psi' + '\n' + \
100 | 'alpha_a: expansion coefficient of fluid in annular, 1/°C or 1/°F' + '\n'
101 |
102 | operational_parameters = 'PARAMETERS RELATED TO THE OPERATION' + '\n' + \
103 | 'tin: fluid inlet temperature, °C or °F' + '\n' + \
104 | 'q: flow rate, m^3/day or gpm' + '\n'
105 |
106 | if about == 'casings':
107 | print(tubular_parameters)
108 |
109 | if about == 'conditions':
110 | print(conditions_parameters)
111 |
112 | if about == 'heatcoeff':
113 | print(heatcoeff_parameters)
114 |
115 | if about == 'densities':
116 | print(densities_parameters)
117 |
118 | if about == 'operational':
119 | print(operational_parameters)
120 |
121 | if about == 'all':
122 | print(tubular_parameters + '\n' + conditions_parameters + '\n' + heatcoeff_parameters + '\n' +
123 | densities_parameters + '\n' + operational_parameters)
124 |
125 |
126 | def set_well(temp_dict, depths, units='metric'):
127 | """
128 | Define properly the parameters and respective values within an object well.
129 | :param temp_dict: dictionary with inputs and default values.
130 | :param depths: wellpath object
131 | :param units: system of units ('metric' or 'english')
132 | :return: a well object with conditions and parameters defined
133 | """
134 |
135 | from math import pi, log
136 |
137 | class NewWell(object):
138 | def __init__(self):
139 | # DEPTH
140 | self.md = depths.md
141 | self.tvd = depths.tvd
142 | self.deltaz = self.md[-1] - self.md[-2]
143 | self.zstep = depths.cells_no
144 | self.sections = depths.sections
145 | self.north = depths.north
146 | self.east = depths.east
147 | self.inclination = depths.inclination
148 | self.dogleg = depths.dogleg
149 | self.azimuth = depths.azimuth
150 | if units != 'metric':
151 | self.md = [i / 3.28 for i in self.md]
152 | self.tvd = [i / 3.28 for i in self.tvd]
153 | self.deltaz = self.deltaz / 3.28
154 | self.north = [i / 3.28 for i in self.north]
155 | self.east = [i / 3.28 for i in self.east]
156 |
157 | # TUBULAR
158 | if units == 'metric':
159 | d_conv = 0.0254 # from in to m
160 | else:
161 | d_conv = 0.0254 # from in to m
162 | self.casings = temp_dict["casings"] # casings array
163 | self.dti = temp_dict["dti"] * d_conv # Tubing Inner Diameter, m
164 | self.dto = temp_dict["dto"] * d_conv # Tubing Outer Diameter, m
165 | self.dri = temp_dict["dri"] * d_conv # Riser diameter Inner Diameter, m
166 | self.dro = temp_dict["dro"] * d_conv # Riser diameter Outer Diameter, m
167 |
168 | # CONDITIONS
169 | if units == 'metric':
170 | depth_conv = 1 # from m to m
171 | self.ts = temp_dict["ts"] # Surface Temperature (RKB), °C
172 | else:
173 | depth_conv = 1 / 3.28 # from ft to m
174 | self.ts = (temp_dict["ts"] - 32) * (5 / 9) # Surface Temperature (RKB), from °F to °C
175 | self.wd = temp_dict["wd"] * depth_conv # Water Depth, m
176 | self.riser = round(self.wd / self.deltaz) # number of grid cells for the riser
177 | self.dsr = self.casings[0, 0] # Surrounding Space Inner Diameter, m
178 | self.dsro = sorted([self.dro + 0.03, self.casings[-1, 0] + 0.03])[-1] # Surrounding Space Outer Diameter, m
179 | self.dfm = temp_dict["dfm"] # Undisturbed Formation Diameter, m
180 |
181 | # RADIUS (CALCULATED)
182 | self.r1 = self.dti / 2 # Tubing Inner Radius, m
183 | self.r2 = self.dto / 2 # Tubing Outer Radius, m
184 | self.r3 = self.casings[0, 1] / 2 # Casing Inner Radius, m
185 | self.r3r = self.dri / 2 # Riser Inner Radius, m
186 | self.r4r = self.dro / 2 # Riser Outer Radius, m
187 | self.r4 = self.casings[0, 0] / 2 # Surrounding Space Inner Radius m
188 | self.r5 = self.dsro / 2 # Surrounding Space Outer Radius, m
189 | self.rfm = self.dfm / 2 # Undisturbed Formation Radius, m
190 |
191 | # DENSITIES kg/m3
192 | if units == 'metric':
193 | dens_conv = 1000 # from sg to kg/m3
194 | else:
195 | dens_conv = 119.83 # from ppg to kg/m3
196 | self.rhof = temp_dict["rhof"] * dens_conv # Fluid
197 | self.rhof_a = temp_dict["rhof_a"] * dens_conv # Fluid
198 | self.rhot = temp_dict["rhot"] * dens_conv # Tubing
199 | self.rhoc = temp_dict["rhoc"] * dens_conv # Casing
200 | self.rhor = temp_dict["rhor"] * dens_conv # Riser
201 | self.rhocem = temp_dict["rhocem"] * dens_conv # Cement Sheath
202 | self.rhofm = temp_dict["rhofm"] * dens_conv # Formation
203 | self.rhow = temp_dict["rhow"] * dens_conv # Seawater
204 |
205 | # OPERATIONAL
206 | if units == 'metric':
207 | self.tin = temp_dict["tin"] # Inlet Fluid temperature, °C
208 | q_conv = 0.04167 # from m^3/day to m^3/h
209 | else:
210 | self.tin = (temp_dict["tin"] - 32) * (5/9) # Inlet Fluid temperature, from °F to °C
211 | q_conv = 0.2271 # from gpm to m^3/h
212 | self.q = temp_dict["q"] * q_conv # Flow rate, m^3/h
213 | self.vp = (self.q / (pi * (self.r1 ** 2))) / 3600 # Fluid velocity through the tubing
214 |
215 | # HEAT COEFFICIENTS
216 | if units == 'metric':
217 | lambda_conv = 1 # from W/(m*°C) to W/(m*°C)
218 | c_conv = 1 # from J/(kg*°C) to J/(kg*°C)
219 | gt_conv = 1 # from °C/m to °C/m
220 | beta_conv = 1 # from Pa to Pa
221 | alpha_conv = 1 # from 1/°F to 1/°C
222 | else:
223 | lambda_conv = 1/1.73 # from BTU/(h*ft*°F) to W/(m*°C)
224 | c_conv = 4187.53 # from BTU/(lb*°F) to J/(kg*°C)
225 | gt_conv = 3.28*1.8 # from °F/ft to °C/m
226 | beta_conv = 6894.76 # from psi to Pa
227 | alpha_conv = 1.8 # from 1/°F to 1/°C
228 |
229 | # Thermal conductivity W/(m*°C)
230 | self.lambdaf = temp_dict["lambdaf"] * lambda_conv # Injection Fluid
231 | self.lambdaf_a = temp_dict["lambdaf_a"] * lambda_conv # Annular Fluid
232 | self.lambdac = temp_dict["lambdac"] * lambda_conv # Casing
233 | self.lambdacem = temp_dict["lambdacem"] * lambda_conv # Cement
234 | self.lambdat = temp_dict["lambdat"] * lambda_conv # Tubing wall
235 | self.lambdafm = temp_dict["lambdafm"] * lambda_conv # Formation
236 | self.lambdar = temp_dict["lambdar"] * lambda_conv # Riser
237 | self.lambdaw = temp_dict["lambdaw"] * lambda_conv # Seawater
238 |
239 | self.beta = temp_dict["beta"] * beta_conv # isothermal bulk modulus in tubing, Pa
240 | self.alpha = temp_dict['alpha'] * alpha_conv # Fluid Thermal Expansion Coefficient in tubing, 1/°C
241 | self.beta_a = temp_dict["beta_a"] * beta_conv # isothermal bulk modulus in annular, Pa
242 | self.alpha_a = temp_dict['alpha_a'] * alpha_conv # Fluid Thermal Expansion Coefficient in annular, 1/°C
243 |
244 | # Specific heat capacity, J/(kg*°C)
245 | self.cf = temp_dict["cf"] * c_conv # Injection Fluid
246 | self.cf_a = temp_dict["cf"] * c_conv # Annular Fluid
247 | self.cc = temp_dict["cc"] * c_conv # Casing
248 | self.ccem = temp_dict["ccem"] * c_conv # Cement
249 | self.ct = temp_dict["ct"] * c_conv # Tubing
250 | self.cr = temp_dict["cr"] * c_conv # Riser
251 | self.cw = temp_dict["cw"] * c_conv # Seawater
252 | self.cfm = temp_dict["cfm"] * c_conv # Formation
253 |
254 | self.gt = temp_dict["gt"] * gt_conv * self.deltaz # Geothermal gradient, °C/m
255 | self.wtg = temp_dict["wtg"] * gt_conv * self.deltaz # Seawater thermal gradient, °C/m
256 |
257 | # Raise Errors:
258 |
259 | if self.casings[-1, 0] > self.dsro:
260 | raise ValueError('Last casing outer diameter must be smaller than the surrounding space diameter.')
261 |
262 | if self.casings[0, 2] > self.md[-1]:
263 | raise ValueError('MD must be higher than the first casing depth.')
264 |
265 | if self.casings[0, 1] < self.dto:
266 | raise ValueError('Tubing outer diameter must be smaller than the first casing inner diameter.')
267 |
268 | if self.wd > 0 and self.dro > self.dsro:
269 | raise ValueError('Riser diameter must be smaller than the surrounding space diameter.')
270 |
271 | if self.dsro > self.dfm:
272 | raise ValueError('Surrounding space diameter must be smaller than the undisturbed formation diameter.')
273 |
274 | def wellpath(self):
275 | """
276 | :return: wellpath object
277 | """
278 | return depths
279 |
280 | def define_density(self, ic, cond=0):
281 | """
282 | Calculate the density profile
283 | :param ic: current temperature distribution
284 | :param cond: '0' to calculate the initial profile
285 | :return: density profile and derived calculations
286 | """
287 |
288 | from .fluid import initial_density, calc_density
289 |
290 | if cond == 0:
291 | self.rhof, self.rhof_initial = initial_density(self, ic)
292 | self.rhof_a, self.rhof_a_initial = initial_density(self, ic, section='annular')
293 | else:
294 | self.rhof = calc_density(self, ic, self.rhof_initial)
295 | self.rhof_a = calc_density(self, ic, self.rhof_initial, section='annular')
296 | # Reynolds number inside tubing
297 | self.re_p = [x * self.vp * 2 * self.r1 / y for x, y in zip(self.rhof, self.visc_t)]
298 | self.f_p = [] # Friction factor inside tubing
299 | self.nu_dpi = []
300 | for x in range(len(self.md)):
301 | if self.re_p[x] < 2300:
302 | self.f_p.append(64 / self.re_p[x])
303 | self.nu_dpi.append(4.36)
304 | else:
305 | self.f_p.append(1.63 / log(6.9 / self.re_p[x]) ** 2)
306 | self.nu_dpi.append(
307 | (self.f_p[x] / 8) * (self.re_p[x] - 1000) *
308 | self.pr_t[x] / (1 + (12.7 * (self.f_p[x] / 8) ** 0.5) *
309 | (self.pr_t[x] ** (2 / 3) - 1)))
310 | # convective heat transfer coefficients, W/(m^2*°C)
311 | self.h1 = [self.lambdaf * x / self.dti for x in self.nu_dpi] # Tubing inner wall
312 |
313 | return self
314 |
315 | def define_viscosity(self, ic, a=1.856 * 10 ** -11, b=4209, c=0.04527, d=-3.376 * 10 ** -5):
316 | """
317 | Calculate the viscosity profile
318 | :param ic: current temperature distribution
319 | :param a: constant
320 | :param b: constant
321 | :param c: constant
322 | :param d: constant
323 | :return: viscosity profile and derived calculations
324 | """
325 |
326 | from .fluid import calc_vicosity
327 | self.visc_t, self.visc_a = calc_vicosity(ic, a, b, c, d)
328 |
329 | self.pr_t = [x * self.cf / self.lambdaf for x in self.visc_t] # Prandtl number - inside tubing
330 | self.pr_a = [x * self.cf / self.lambdaf for x in self.visc_a] # Prandtl number - annulus
331 |
332 | return self
333 |
334 | return NewWell()
335 |
336 |
--------------------------------------------------------------------------------