├── 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 | [![Cover](https://github.com/pro-well-plan/opensource_apps/raw/master/resources/pwp-bgd.gif)](https://prowellplan.com) 2 | 3 | [![Open Source Love svg2](https://badges.frapsoft.com/os/v2/open-source.svg?v=103)](https://github.com/pro-well-plan/pwptemp/blob/master/LICENSE.md) 4 | [![PyPI version](https://badge.fury.io/py/pwptemp.svg)](https://badge.fury.io/py/pwptemp) 5 | [![License: LGPL v3](https://img.shields.io/badge/License-LGPL_v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) 6 | [![Webapp](https://img.shields.io/badge/WebApp-On-green.svg)](https://share.streamlit.io/jcamiloangarita/opensource_apps/app.py) 7 | [![Documentation Status](https://readthedocs.org/projects/pwptemp/badge/?version=latest)](http://pwptemp.readthedocs.io/?badge=latest) 8 | [![Build Status](https://www.travis-ci.com/pro-well-plan/pwptemp.svg?branch=master)](https://www.travis-ci.com/pro-well-plan/pwptemp) 9 | [![Downloads](https://pepy.tech/badge/pwptemp)](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://user-images.githubusercontent.com/52009346/69100304-2eb3e800-0a5d-11ea-9a3a-8e502af2120b.png)](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 | ![dens](https://user-images.githubusercontent.com/52009346/65387330-9d6b1280-dd46-11e9-8b45-5b75667ac428.PNG) 70 | 71 | ![shc](https://user-images.githubusercontent.com/52009346/65387362-0eaac580-dd47-11e9-9128-9b7f547cd167.PNG) 72 | 73 | ![vel](https://user-images.githubusercontent.com/52009346/65387364-1c604b00-dd47-11e9-8631-d175d25f4a12.PNG) 74 | 75 | ![temp](https://user-images.githubusercontent.com/52009346/65387368-271ae000-dd47-11e9-8962-01cd71615208.PNG) 76 | 77 | ![chtc](https://user-images.githubusercontent.com/52009346/65387390-3732bf80-dd47-11e9-8da7-66ccd55a9399.PNG) 78 | 79 | ![thermal](https://user-images.githubusercontent.com/52009346/65387394-4154be00-dd47-11e9-8d6c-e6452cf5bf03.PNG) 80 | 81 | ![radius](https://user-images.githubusercontent.com/52009346/65387398-4b76bc80-dd47-11e9-909f-c8c572f2c5c8.PNG) 82 | 83 | ![heatsource](https://user-images.githubusercontent.com/52009346/65387403-56315180-dd47-11e9-9fd2-0f08a3361513.PNG) 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 | --------------------------------------------------------------------------------