In this guide, our main focus is on leveraging the whitson+ API for uploading bottomhole pressure related data. The example can be broken down in four steps.
Uploading Wells and Wellheader Data, e.g. wellnames, UWI, etc.
Uploading Wellbore Data:, e.g. well deviation surveys, top/bottom perforations and
Uploading Production Data:, e.g. oil, gas and water rates.
Running BHP Calculations:, e.g. to be used in FMB, RTA, nodal or numerical modeling.
BHP Input Data Schema Visualization
This data schema shows how the different data tables are related.
Difference between WELL_ID and WELL_DATA_ID
WELL_ID and WELL_DATA_ID serve different purposes. WELL_ID uniquely identifies a specific well, while WELL_DATA_ID is assigned to each wellbore configuration. Over time, multiple configurations can occur. For instance, initially flowing through casing and later installing tubing would result in two separate wellbore configurations, each with its own WELL_DATA_ID and installation DATE
Connect BHP data to whitson+: Python Example
What does the bhp.py file do?
This file shows an example of how one connect BHP data to the whitson+ domain. The file uses a helper class WhitsonConnection in whitson_connect.py provided here. The example data is from a standard whitson+MASS UPLOAD example that can be downloaded here. Modifying the code to e.g. pulling data from a SQL table is straight forward, as exemplified below.
importwhitson_connectimportpandasaspdCLIENT="your_domain_here"#This is the company suffix in your whitson urls ex. 'courses' in courses.whitson.comCLIENT_ID="your_client_id_here"# Available on requestCLIENT_SECRET="your_client_secret"# Available on requestPROJECT_ID="your_project_id_here"# This is the int numeric value of the project, available from the URL like '638' in https://courses.whitson.com/fields/5/projects/638/whitson_connection=whitson_connect.WhitsonConnection(CLIENT,CLIENT_ID,CLIENT_SECRET)whitson_connection.access_token=whitson_connection.get_access_token_smart()excel_file='docs\\files\\onboarding\\mass-upload-example\\whitson+_mass_well_upload_field_Eagle-Ford_20231110.xlsx'df=pd.read_excel(excel_file,sheet_name='Well Data',header=1).drop(0)# ------------------------------------------------------------------------------------------------------------------# 1. Upload Wells# ------------------------------------------------------------------------------------------------------------------forindex,rowindf.iterrows():well_name=row['Well Name']uwi_api=row['UWI | API']# Add more things here if wanted, all attributes here: https://internal.whitson.com/api-external/swagger/#/Base%20Data/post_api_external_v1_wellswell_info={"project_id":PROJECT_ID,"name":well_name,"uwi_api":uwi_api}whitson_connection.create_well(payload=well_info)whitson_wells=whitson_connection.get_wells(PROJECT_ID)# ------------------------------------------------------------------------------------------------------------------# 2.1 Upload well deviation surveys# ------------------------------------------------------------------------------------------------------------------deviation_df=pd.read_excel(excel_file,sheet_name='Deviation Survey Data').drop(0)payloads_by_well={}forindex,rowindeviation_df.iterrows():well_name=row['Well Name']md_value=row['MD']tvd_value=row['TVD']ifwell_nameinpayloads_by_well:payloads_by_well[well_name].append({"md":md_value,"tvd":tvd_value})else:payloads_by_well[well_name]=[{"md":md_value,"tvd":tvd_value}]forwell_name,payloadinpayloads_by_well.items():well_id=whitson_connection.get_well_id_by_wellname(whitson_wells,well_name)whitson_connection.edit_well_deviation_survey(well_id=well_id,payload=payload)# ------------------------------------------------------------------------------------------------------------------# 2.2 Upload top and bottom perforations# ------------------------------------------------------------------------------------------------------------------df_perf=pd.read_excel(excel_file,sheet_name='Wellbore Data').drop(0)processed_wells={}forindex,rowindf_perf.iterrows():well_name=row['Well Name']top_md=row['Top Perforation MD']bottom_md=row['Bottom Perforation MD']ifwell_nameinprocessed_wells:continuepayload={"top_perforation_md":top_md,"bottom_perforation_md":bottom_md}well_id=whitson_connection.get_well_id_by_wellname(whitson_wells,well_name)whitson_connection.edit_perf_interval(well_id,payload=payload)processed_wells[well_name]=True# ------------------------------------------------------------------------------------------------------------------# 2.3 Upload wellbore configs# ------------------------------------------------------------------------------------------------------------------df_perf=pd.read_excel(excel_file,sheet_name='Wellbore Data').drop(0)well_payloads={}forindex,rowindf_perf.iterrows():well_name=row['Well Name']use_from_date=row['Use from Date'].strftime("%Y-%m-%d")ifpd.notna(row['Use from Date'])elseNoneflow_path=row['Flowpath'].lower().replace(" ","_")lift_method=row['Lift Method'].lower().replace(" ","_")ifpd.notna(row['Lift Method'])else'none'gas_lift_config=row.get('Gas Lift Configuration','poorboy').lower()ifpd.notna(row.get('Gas Lift Configuration'))else'poorboy'gauge_depth=row['Gauge Depth']ifpd.notna(row['Gauge Depth'])elseNonegas_lift_valve_md=row.get('Gas Lift Valve MD',0)ifpd.notna(row.get('Gas Lift Valve MD'))else0# Example values, replace with actual logic to determine theselast_valve_open=Trueiflift_method=='Gas Lift'elseFalsecalculate_from_gauge=False# If gauge in well, put to Truecompute_through="flowing_side"# use static_side if relevantcompute_static_down_to="end_of_tubing"roughness=0.0006# default in whitson+well_data_casing=[{"d_casing_inner":row['Casing 1 ID'],"pipe_number":1,"k_casing":roughness,"bottom_md":row['Casing 1 Bottom MD'],"top_md":row['Casing 1 Top MD']},]ifpd.notna(row['Casing 2 ID']):# check and append data only if it existswell_data_casing.append({"d_casing_inner":row['Casing 2 ID'],"pipe_number":2,"k_casing":roughness,"bottom_md":row['Casing 2 Bottom MD'],"top_md":row['Casing 2 Top MD']})# Example for additional casing IDs:# if pd.notna(row['Casing 3 ID'])::# well_data_casing.append({# "d_casing_inner": row['Casing 3 ID'],# "pipe_number": 3,# "k_casing": roughness,# "bottom_md": row['Casing 3 Bottom MD'],# "top_md": row['Casing 3 Top MD']# })well_data_tubing=[]ifpd.notna(row['Tubing ID']):well_data_tubing.append({"pipe_number":1,"bottom_md":row['Tubing Bottom MD'],"d_tubing_inner":row['Tubing ID'],"d_tubing_outer":row['Tubing OD'],"k_tubing":roughness})# Example for additional tubing IDs:# if pd.notna(row['Tubing ID 2 ID'])::# well_data_tubing.append({# "pipe_number": 2,# "bottom_md": row['Tubing 2 Bottom MD'],# "d_tubing_inner": row['Tubing 2 ID'],# "d_tubing_outer": row['Tubing 2 OD'],# "k_tubing": roughness # })gas_lift_data=[]iflift_method=='gas_lift':gas_lift_data.append({"ps_close":0,"valve_md":gas_lift_valve_md,"ps_open":0,"joint":0})ifwell_namenotinwell_payloads:well_payloads[well_name]=[]well_payloads[well_name].append({"use_from_date":use_from_date,"flow_path":flow_path,"lift_method":lift_method,"gas_lift_config":gas_lift_config,"last_valve_open":last_valve_open,"gauge_depth":gauge_depth,"calculate_from_gauge":calculate_from_gauge,"compute_through":compute_through,"compute_static_down_to":compute_static_down_to,"well_data_casing":well_data_casing,"well_data_tubing":well_data_tubing,"gas_lift_data":gas_lift_data})forwell_name,payload_listinwell_payloads.items():well_id=whitson_connection.get_well_id_by_wellname(whitson_wells,well_name)wellbore_data=whitson_connection.get_well_data(well_id)# Delete the default wellbore. Not needed if you're simply appending wellbore configurations.ifwellbore_data:well_data_id=wellbore_data[0]['id']whitson_connection.delete_wellbore_config_by_well_data_id(well_data_id=well_data_id)whitson_connection.upload_well_data_to_well(well_id,payload_list)# ----------------------------------------------------------------------------------------------------------------# 3. Upload production data# ----------------------------------------------------------------------------------------------------------------df_prod=pd.read_excel(excel_file,sheet_name='Production-Data',skiprows=2)production_payload=[]prior_wellname=Noneforindex,rowindf_prod.iterrows():well_name=row.iloc[0]date=pd.to_datetime(row.iloc[1],format='%Y-%m-%d %H:%M:%S')qo=row.iloc[2]qg=row.iloc[3]qw=row.iloc[4]p_wf_measured=row.iloc[5]p_tubing=row.iloc[6]p_casing=row.iloc[7]qg_gas_lift=row.iloc[8]liquid_level=row.iloc[9]this_prod_time={"date":date.strftime("%Y-%m-%dT%H:%M:%S.%fZ")ifdateelseNone,"qo_sc":max(0,qo)ifpd.notnull(qo)elseNone,"qg_sc":max(0,qg)ifpd.notnull(qg)elseNone,"qw_sc":max(0,qw)ifpd.notnull(qw)elseNone,"p_wf_measured":max(0,p_wf_measured)ifpd.notnull(p_tubing)elseNone,"p_tubing":max(0,p_tubing)ifpd.notnull(p_tubing)elseNone,"p_casing":max(0,p_casing)ifpd.notnull(p_casing)elseNone,"qg_gas_lift":max(0,qg_gas_lift)ifpd.notnull(qg_gas_lift)elseNone,"liquid_level":max(0,liquid_level)ifpd.notnull(liquid_level)elseNone,"choke_size":None,"line_pressure":None,}ifwell_name==prior_wellname:production_payload.append(this_prod_time)else:ifprior_wellnameisnotNone:well_id=whitson_connection.get_well_id_by_wellname(whitson_wells,prior_wellname)whitson_connection.upload_production_to_well(well_id,{"production_data":production_payload},append_only=True,)production_payload=[]production_payload.append(this_prod_time)prior_wellname=well_name# Don't forget to upload the last batchifproduction_payload:well_id=whitson_connection.get_well_id_by_wellname(whitson_wells,prior_wellname)whitson_connection.upload_production_to_well(well_id,{"production_data":production_payload},append_only=True,)# ----------------------------------------------------------------------------------------------------------------# 4. Run BHP calculations# ----------------------------------------------------------------------------------------------------------------# Run BHP calcforwellinwhitson_wells:well_id=well['id']whitson_connection.run_bhp_calc(well_id)# Output BHP calc# for well in whitson_wells: # well_id = well['id']# calculated_bhp = whitson_connection.get_bhp_calc(well_id)# # To something with it, e.g. third-party dashboard
Reading Data from a SQL Table
The example above demonstrates how to read data from an excel template into whitson+. The code can be easily modified to read from a SQL database into whitson+ instead. Example of how to read SQL into a Pandas Dataframe can be seen below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
importpandasaspdimportpyodbc# Establish a connection to your SQL Serverconn=pyodbc.connect('DRIVER={SQL Server};SERVER=your_server;DATABASE=your_database;UID=your_username;PWD=your_password')# Define your SQL query to select data from your tablesql_query="SELECT * FROM your_table_name"# Replace 'your_table_name' with your actual table name# Read data from SQL table into a pandas DataFramedf=pd.read_sql(sql_query,conn)# Close the connectionconn.close()# Now you have your data in the DataFrame 'df'print(df.head())# Display the first few rows of the DataFrame
WellView Queries: Relevant Tables and Queries
Below are example SQL queries designed to extract specific information from the a WellView database. These queries demonstrate how to retrieve tubing components, survey data, perforation details, and casing specifications for wells in select counties. Each query includes placeholders for conditions and patterns to be adjusted according to specific requirements. Note that these examples reference relevant WellView tables and are provided for illustrative purposes only.