Python and Data Science
Software, Tools and Workflows

Beginner's Guide to Developing a Streamlit App for Relative Permeability and Capillary Pressure Curves

Shubham B. Patel
February 19, 2025

Some time ago, I started a collaboration with Alan at CrowdField, where he provided guidance and support for me to develop my first Streamlit app.

I was eager to learn how to use Python to build simple, user-friendly applications that run in a browser and have real-world applications in the oil and gas industry. After discussing several options with Alan, I decided to take on the challenge of building an interactive interface for relative permeability curves.

The process and dynamics of our collaboration were described in a blog post Alan published on the CrowdField website:

Case Study: Collaborating with a Young Engineer to Develop a Streamlit Relative Permeability App

Here’s a short YouTube demo showcasing an app run-through:

And here’s a link to the Streamlit app itself, which you can run in any browser:

👉 Link to Rel Perm StreamLit App

In this article, I want to go deeper into the technical aspects and provide a detailed, step-by-step guide on how to develop a Streamlit app like this one.

My goal is to encourage others to explore this powerful tool.

Key Features of Streamlit:

Why Use Streamlit for Saturation Function Calculations?

Envisioning the Streamlit App

When we decided to implement a Streamlit app for relative permeability curves, we envisioned an interface that would:

Step-by-Step Guide to Building a Streamlit App

For the following sections, you can either paste this code and modify it as you go on your editor, or simply skim through it to understand the general structure.

Don’t worry if the code feels overwhelming—it will all make sense by the end.

Setting Up Your Environment

To get started, you need Python installed on your system along with the necessary libraries.

Install Python

  1. Download and install Python from python.org.
  2. Verify the installation by running:

python --version


You should see an output like Python 3.x.x.

Install Required Libraries

Create a virtual environment to isolate dependencies and install necessary libraries.

Open your terminal or command prompt and navigate to your working directory:

mkdir streamlit_app
cd streamlit_app



Create and activate a virtual environment:

python -m venv env
env\\Scripts\\activate# Windows
source env/bin/activate# Mac/Linux



Install the required libraries:

pip install streamlit numpy pandas plotly openpyxl requests PyPDF2



Creating the App Structure

Inside your project folder, create a new Python file:

touch Myapp.py



Open Myapp.py in your preferred text editor (e.g. VSCode) and add the following imports:

import streamlit as st
import numpy as np
import pandas as pd
import plotly.graph_objs as go
from PyPDF2 import PdfReader



Explanation:

  1. streamlit (st): Creates the interactive user interface for our web application.
  2. numpy (np): Provides efficient tools for numerical computations.
  3. pandas (pd): Handles tabular data (e.g., creating data tables for permeability).
  4. plotly.graph_objs (go): Enables interactive and visually appealing graphs.
  5. requests: Fetches external files from the internet, such as PDFs used in this app.
  6. PyPDF2.PdfReader: Extracts data from PDF files.
  7. io.BytesIO: Allows in-memory file handling for exporting and downloading files.

Defining Functions for Calculations

Functions serve as the backbone of the application, each dedicated to a specific task such as calculating permeability, exporting data, or generating plots.

For relative permeability calculations, I've implemented Corey functions, using exact formulas sourced from the references listed at the end of this guide.

Now, let's define the key functions that will compute relative permeability and capillary pressure curves.

Function for Water-Oil System

def calculate_properties_water_oil(Sw, Swc, Sorw, Kro_max, Krw_max, no, nw, pc_max, npc):
   kro = Kro_max * np.maximum(((1 - Sw - Sorw) / (1 - Swc - Sorw)), 0) ** no
   krw = Krw_max * np.maximum(((Sw - Swc) / (1 - Swc - Sorw)), 0) ** nw
   pcwo = pc_max * np.maximum(((1 - Sw - Sorw) / (1 - Swc - Sorw)), 0) ** npc
   return kro, krw, pcwo


Function for Gas-Oil System

def calculate_properties_gas_oil(Sg, Sgc, Sl, Kro_max, krg_max, ng, ngo, pc_max, npg):
   kro = Kro_max * np.maximum(((1 - Sg - Sl) / (1 - Sgc - Sl)), 0) ** ngo
   krg = krg_max * np.maximum(((Sg - Sgc) / (1 - Sl - Sgc)), 0) ** ng
   pcgo = pc_max * np.maximum(((Sg - Sgc) / (1 - Sl - Sgc)), 0) ** npg
   return kro, krg, pcgo


Function for Gas-Water System

def calculate_properties_gas_water(Sg, Sw, Sgc, Swc, Krg_max, krw_max, ng, nw, pc_max, npc):
   krg = Krg_max * np.maximum(((Sg - Sgc) / (1 - Swc - Sgc)), 0) ** ng
   krw = krw_max * np.maximum(((Sw - Swc) / (1 - Swc)), 0) ** nw
   pcgw = pc_max * np.maximum(((Sg - Sgc) / (1 - Swc - Sgc)), 0) ** npc
   return krg, krw, pcgw


Function for Exporting ECL Tables

def export_to_ecl(system, saturation, kro, krw_or_krg, pc):
   lines = []

   if system == 'Water-Oil System':
       lines.append("SWOF")
       lines.append("-- Sw    Krw    Kro    Pcwo")
   elif system == 'Gas-Oil System':
       lines.append("SGOF")
       lines.append("-- Sg    Krg    Kro    Pcgo")
   elif system == 'Gas-Water System':
       lines.append("SGWFN")
       lines.append("-- Sg    Krg    Krw    Pcgw")

   for row in zip(saturation, kro, krw_or_krg, pc):
       lines.append(f"{row[0]:.4f}    {row[1]:.4f}    {row[2]:.4f}    {row[3]:.4f}")

   lines.append("/")
    return "\\n".join(lines)



Function for Plotting Data

def plot_properties(system, x_values, y_values_1, y_values_2):
   fig = go.Figure()
   if system == 'Water-Oil System':
       x_label = 'Water Saturation (Sw)'
       y1_label = 'Oil Relative Permeability (kro)'
       y2_label = 'Water Relative Permeability (krw)'
   elif system == 'Gas-Oil System':
       x_label = 'Liquid Saturation (Sl)'
       y1_label = 'Gas Relative Permeability (krg)'
       y2_label = 'Oil Relative Permeability (kro)'
   elif system == 'Gas-Water System':
       x_label = 'Water Saturation (Sw)'
       y1_label = 'Gas Relative Permeability (krg)'
       y2_label = 'Water Relative Permeability (krw)'

   fig.add_trace(go.Scatter(x=x_values, y=y_values_1, mode='lines', name=y1_label))
   fig.add_trace(go.Scatter(x=x_values, y=y_values_2, mode='lines', name=y2_label))
   return fig

def plot_capillary_pressure(system, S, pc):
   fig = go.Figure()
    if system == 'Water-Oil System':
       x_label = 'Water Saturation (Sw)'
   elif system == 'Gas-Oil System':
       x_label = 'Liquid Saturation (Sl)'
   elif system == 'Gas-Water System':
       x_label = 'Water Saturation (Sw)'

   fig.add_trace(go.Scatter(x=S, y=pc, mode='lines', name='Capillary Pressure (pc)'))
   return fig

def generate_plots_and_data(system, params):
   if system == 'Water-Oil System':
       Sw = np.linspace(params['Swc'], 1 - params['Sorw'], 100)
       kro, krw, pcwo = calculate_properties_water_oil(Sw, params['Swc'], params['Sorw'],
           params['Kro_max'], params['krw_max'], params['no'], params['nw'],
           params['pc_max'], params['npc'])
       fig = plot_properties(system, Sw, kro, krw)
       pc_fig = plot_capillary_pressure(system, Sw, pcwo)
       return fig, pc_fig


Build the User Interface

The Streamlit library allows us to create an intuitive interface where users can select the system type, adjust parameters, and view results dynamically.

App Header and Introduction

st.title('Relative Permeability Curves Generator')

default_params = {
   'Water-Oil System': {'Swc': 0.25, 'Kro_max': 0.85, 'Sorw': 0.35, 'krw_max': 0.4,
                       'no': 0.9, 'nw': 1.5, 'npc': 0.71, 'pc_max': 20.0},
   'Gas-Oil System': {'Sgc': 0.05, 'Kro_max': 0.60, 'Sl': 0.48, 'krg_max': 0.95,
                     'ng': 0.6, 'ngo': 1.2, 'npg': 0.51, 'pc_max': 30.0},
   'Gas-Water System': {'Sgc': 0.05, 'Krg_max': 0.90, 'Swc': 0.2, 'krw_max': 0.35,
                       'ng': 0.8, 'nw': 1.4, 'npc': 0.61, 'pc_max': 20.0}
}


System Selection

system = st.selectbox('Select System Type', ['Water-Oil System', 'Gas-Oil System', 'Gas-Water System'])
params = default_params[system]


Parameter Adjustment

Example: Water-Oil System Parameters

if system == 'Water-Oil System':
   params['Swc'] = st.slider('Swc', 0.0, 1.0, value=params['Swc'], step=0.01)
   params['Kro_max'] = st.slider('Kro_max', 0.0, 1.0, value=params['Kro_max'], step=0.01)
   params['Sorw'] = st.slider('Sorw', 0.0, 1.0, value=params['Sorw'], step=0.01)
   params['krw_max'] = st.slider('krw_max', 0.0, 1.0, value=params['krw_max'], step=0.01)
   params['no'] = st.slider('no', 0.3, 5.0, value=params['no'], step=0.01)
   params['nw'] = st.slider('nw', 0.3, 5.0, value=params['nw'], step=0.01)
   params['npc'] = st.slider('npc', 0.3, 5.0, value=params['npc'], step=0.01)
   params['pc_max'] = st.number_input('pc_max [psi]', min_value=0.0, max_value=1000.0, value=params['pc_max'], step=0.1)


Key Elements:

  1. Sliders (st.slider):
    • Allow users to adjust numerical parameters interactively.
    • For example, Swc (critical water saturation) can be modified between 0 and 1 in steps of 0.01.
  2. Number Input (st.number_input):
    • Allows fine-tuned control for larger values like pc_max (maximum capillary pressure).

This block dynamically changes based on the selected system. Similar sliders are defined for Gas-Oil and Gas-Water systems.

Generate and Display Results

Call the generate_plots_and_data Function:

try:
   fig, pc_fig, df = generate_plots_and_data(system, params)


Display the Plots in Streamlit:

col1, col2 = st.columns([0.4, 0.6])
with col2:
   st.plotly_chart(fig, use_container_width=True)
   st.plotly_chart(pc_fig, use_container_width=True)


File Downloads

Export Data as Excel:

buffer = io.BytesIO()
with pd.ExcelWriter(buffer, engine="xlsxwriter") as writer:
   df.to_excel(writer, sheet_name="Relative_Permeability", index=False)
buffer.seek(0)

  1. Export to Excel:
    • The data table (df) is written to an Excel file using Pandas
  2. Prepare for Download:
    • The file is stored in memory using io.BytesIO

Add a Download Button:

st.download_button(
   label="Download Excel File",
   data=buffer,
   file_name="relative_permeability.xlsx",
   use_container_width=True
)



Export Data as Simulation Input File:


ecl_data = export_to_ecl(system, df.iloc[:, 0], df.iloc[:, 1], df.iloc[:, 2], df.iloc[:, 3])
st.download_button(
   label="Download E-100 Sim INC File",
   data=ecl_data.encode('utf-8'),
   file_name="relative_permeability.INC",
   mime="text/plain",
   use_container_width=True
)


Include PDF Documentation for Download:

pdf_data = BytesIO(requests.get(pdf_url).content)
st.download_button(
   label="Equations Used",
    data=pdf_data,
   file_name="Equations_Used.pdf",
   mime="application/pdf",
   use_container_width=True
)


Add Attribution

st.markdown(
"""
---
Developed by [Shubham B. Patel](<https://www.linkedin.com/in/shubham-patel-1045/>) under the guidance of [Alan Mourgues](<https://www.linkedin.com/in/alan-mourgues/>).
Visit [CrowdField.net](<https://www.crowdfield.net/>) for more case studies.
"""
)



Testing and Deployment

Run the App Locally


streamlit run Saturation functions app.py


Deploy the App

Set Up a GitHub Repository:

  1. Go to GitHub
    • Open GitHub and sign in or create a GitHub account
  2. Create a New Repository
    • Click the "+" icon (top-right) > "New Repository"
    • Fill in details:
      • Repository Name: e.g., my-streamlit-app
      • Description: Add a description (optional)
      • Public: Make the repository public (required for free Streamlit Cloud hosting)
      • Initialize with README: Check this box
      • Click Create Repository

Upload Your App to GitHub

Clone the repository to your local machine:

git clone <https://github.com/your-username/my-streamlit-app.git>



Move your app files (e.g., app.py) into the cloned folder

Add a .gitignore file to exclude unnecessary files:

echo "__pycache__/" > .gitignore


Commit and push your code:

git add .
git commit -m "Initial commit"
git push


Add a Requirements File

Create a file named requirements.txt in the repository folder.

Add the Python libraries your app needs:



streamlit==1.24.0# Streamlit for web applications
numpy>=1.21.0# NumPy for numerical operations
pandas>=1.4.0# Pandas for data manipulation
matplotlib>=3.5.0# Matplotlib for plotting
scikit-learn>=1.1.0# Scikit-learn for machine learning
requests>=2.27.0# Requests for HTTP requests
plotly>=5.10.0# Plotly for interactive plotting
PyPDF2>=3.0.0
xlsxwriter>=3.2.0


Save and push this file to GitHub:


git add requirements.txt
git commit -m "Add requirements file"
git push


Deploy on Streamlit Community Cloud

  1. Go to Streamlit Community Cloud
  2. Create a New App
    • Click "New App"
  3. Configure Deployment
    • Select the GitHub repository that contains your app
    • Branch: Leave as main (or select your branch if it's different)
    • File path: Enter the name of your app file (e.g., app.py)
  4. Click "Deploy"
    • Streamlit will install the dependencies from requirements.txt and deploy your app
    • Wait for the deployment process to complete (a few seconds to a minute)

Test and Share Your App

Once deployed, your app will be live, and you'll get a URL like:

https://your-username-your-repo-name.streamlit.app

Share this link with others to allow them to view your app.

Update Your App

Make changes to your app code locally.

Push changes to GitHub:



git add .
git commit -m "Update app"
git push


Streamlit Cloud will automatically detect changes and redeploy your app.

Troubleshooting Tips

That's it! Your app is now live on Streamlit and linked with GitHub for easy updates.

The Impact

This project taught me several valuable lessons:

  1. Modern engineering tools don't have to be complicated to be useful.
  2. Python and Streamlit enable the creation of professional applications without requiring extensive software development expertise.
  3. Hosting calculations in a web app simplifies sharing and collaboration with colleagues.

This Streamlit app offers a powerful yet user-friendly tool for calculating and visualizing relative permeability and capillary pressure curves. By combining technical rigor with modern visualization, it enhances understanding and accelerates decision-making in reservoir engineering.

References

These are the resources I used while developing this body of work:

  1. Corey, A. T., "The Interrelation Between Gas and Oil Relative Permeabilities," Production Mon., 19, 38 (1954).
  2. Purcell, W. R., "Capillary Pressures—Their Measurement Using Mercury and the Calculation of Permeability Therefrom," Transactions AIME, 186, 39 (1949).
  3. Burdine, N. T., "Relative Permeability Calculations from Pore Size Distribution Data," Transactions AIME, 198, 71 (1953).
  4. Lomeland, F., Ebeltoft, E., and Thomas, W. H., "A New Versatile Relative Permeability Correlation," Paper SCA2005-32, presented at the International Symposium of the Society of Core Analysts, Toronto, Canada, August 21–25, 2005.
  5. SPE PEH: Relative Permeability and Capillary Pressure
  6. IHS Energy: Relative Permeability Correlations
  7. Ahmed, T., Reservoir Engineering Handbook, 4th Edition, Chapter 5: "Relative Permeability Concepts."
  8. Streamlit Documentation

Want to Get Started? Claim Your $20 Pre-Sale Offer!

I'm putting together a complete package to help you build this app:

To gauge interest, I’m offering this package as a pre-sale for only $20. This early release will help me gather user feedback and fine-tune the product before its final public launch at a higher price point.

With this pre-purchase, you'll get access to the final product, giving you everything you need to start building your own engineering applications.

Pre-purchase for only $20:

👉 https://subscribepage.io/qKBwNA

I hope this article has shown that creating useful tools is within your reach—even if you're just starting with Python.

Feel free to reach out if you have any questions. Happy coding!

More Tools

Explore a curated collection of valuable resources in our Store, both free and paid, all designed to help you upskill.

Shubham is a Reservoir Engineer who specializes in deep-water and unconventional gas fields, focusing on reserve estimation, production forecasting, and reservoir modeling.

With expertise in well test interpretation, reservoir characterization, and economic analysis, he integrates analytical and numerical methods to optimize production and maximize asset value.

Committed to efficiency and sustainability, he enhances operations while promoting responsible reservoir management.

Featured...

All blog posts

"O&G AI Wave"
Newsletter

Subscribe to O&G AI Wave – your indispensable guide to the evolving world of AI and NoCode and their practical applications for Oil & Gas professionals.

Our newsletter brings you the latest trends and insights in AI and NoCode technologies directly impacting the O&G sector.

Every issue is packed with expert analyses and practical tips to help you navigate and leverage these transformative technologies.

Don’t miss out on the opportunity to stay ahead of the trend and future-proof your career in this incredibly dynamic field.

How we can help

At CrowdField, our mission is to empower YOU—helping you showcase your skills in the open market and monetize them effectively. Here's how you benefit from being with us:

👉 Make your mark and find freelance opportunities by listing yourself in our freelancer directory to get noticed. Join us for free here.

👉 Turn your skills into digital products that sell. We'll help you polish, launch, and list them in our Store, promote them on our LinkedIn page, to our email subscribers, and feature your success in a case study on our Blog, amplifying both your product and personal brand. Whether you're at the idea stage, midway through implementation, or nearing completion, if you see potential for monetization, we're interested. Take the first step by getting in touch to start a conversation at hello@crowdfield.net.

👉 Discover bargains in our digital store with heavily discounted prices during our market discovery period. Take advantage of these limited-time offers as we expand our network. Dive in now and find your gem! Got to Store