Well Comparison Report Template#
The variables in this cell can be specified via papermill.
To support this the cell metadata must be defined as follows: { “tags”: [ “parameters” ] }
[ ]:
investigation_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
output_name="WellComparison"
Attach to Investigator#
Connect to Investigator#
To connect the notebook to Investigator is it necessary to import the CegalPrizm Investigator package and create a connection.
[ ]:
#papermill_description=Connect_Investigator
import numpy as np
import pandas as pd
pd.set_option('display.max_colwidth', 1000)
import cegalprizm.investigator as investigator
from cegalprizm.investigator import InvestigatorConnection
[ ]:
inv_conn = InvestigatorConnection(use_licensed_features=True)
Attach to the investigation#
Attach to the investigation for which a report notebook should be generated.
[ ]:
#papermill_description=Load_Investigation
inv = inv_conn.load_investigation(investigation_id=investigation_id)
Create a report notebook#
This creates an in-memory notebook.
[ ]:
#papermill_description=Create_Output_Notebook
import nbformat as nbf
nb = nbf.v4.new_notebook()
kernelspec = {
"display_name": "python3",
"language": "python",
"name": "python3"
}
nb.metadata['kernelspec'] = kernelspec
Add cells to setup the notebook#
Add a text cell which describes the report contained in the notebook.
[ ]:
text = """
# Well report
This is an auto-generated report showing some summary plots followed by a standard set of plots for every well.
"""
nb['cells'].append(nbf.v4.new_markdown_cell(text))
Add a code cell which imports the necessary python packages
[ ]:
code = """
#papermill_description=Initialise_Report
import numpy as np
import pandas as pd
import cegalprizm.investigator as investigator
from cegalprizm.investigator import InvestigatorConnection
from cegalprizm.investigator.views import *
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Add a code cell which defines the dimensions that must be defined in the investigation to be able to generate a report from this notebook.
The property name field will be used to check if a valid dimension is defined. The name field will then be used to override the name of the dimension in the investigation. This ensures that reports generated from different investigations use consistent labelling.
[ ]:
code = """
from cegalprizm.investigator import DimensionPropertyNameTuple
required_dimensions = []
required_dimensions.append(DimensionPropertyNameTuple(name="Porosity", property_name="porosity"))
required_dimensions.append(DimensionPropertyNameTuple(name="Permeability", property_name="perm"))
required_dimensions.append(DimensionPropertyNameTuple(name="Gamma ray", property_name="gamma"))
required_dimensions.append(DimensionPropertyNameTuple(name="Z", property_name="depth"))
required_dimensions.append(DimensionPropertyNameTuple(name="Facies", property_name="facies"))
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Add a code cell tha creates a connection to investigator and loads the investigation.
[ ]:
code = """
#papermill_description=Load_Investigation
inv_conn = InvestigatorConnection(use_licensed_features=True)
inv = inv_conn.load_investigation(investigation_id='{}')
if inv is None:
sys.exit("Investigation cannot be accessed")
""".format(investigation_id)
nb['cells'].append(nbf.v4.new_code_cell(code))
Verify the investigation and apply settings to ensure all investigations are consistent#
Add a code cell that loops over the required dimension tuples, checks a dimension of the required property type exists and then sets the dimension name.
Once all required dimensions have been found, the investigation is changed are applied to update the investigation.
[ ]:
code = """
continuous_dimension_names = [ ]
discrete_dimension_names = [ ]
for required_dimension in required_dimensions:
for dimension in inv.continuous_dimension_property_names:
settings = inv.get_continuous_settings(dimension.name)
name = dimension.name
found = False
if required_dimension.property_name in dimension.property_name.lower():
name = required_dimension.name
found = True
if found:
continuous_dimension_names.append(name)
settings.set_name(name)
for dimension in inv.discrete_dimension_property_names:
if dimension.name is not investigator.DATASET_DIMENSION_NAME:
settings = inv.get_discrete_settings(dimension.name)
name = dimension.name
found = False
if required_dimension.property_name in dimension.property_name.lower():
name = required_dimension.name
found = True
if found:
discrete_dimension_names.append(name)
settings.set_name(name)
inv.update()
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Add a code cell which verifies that all the required dimensions exist in the investigation. If they do not then the notebook will stop running.
[ ]:
code = """
#papermill_description=Verify_Investigation
import sys
#if len(continuous_dimension_names) + len(discrete_dimension_names) != len(required_dimensions):
# sys.exit("The required dimensions are not available in the investigation")
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Add a code cell that queries the investigation to extract some useful variables that will be used later.
In this case, all well datasets are identified
[ ]:
code = """
raw_wells = [x for x in inv.dataset_names if x.startswith("Wells")]
well_names = [x.replace("Wells/", "") for x in raw_wells]
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Add a code cell that sets the bin size for Z histograms to every 10m
[ ]:
code = """
settings = inv.get_continuous_settings("Z")
settings.set_bin_size(10)
inv.update()
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Although a Z dimension is required in the investigation we have decided not to include it in all plots.
Add a code cell to remove “Z” from the list of continuous dimension names
[ ]:
code = """
continuous_dimension_names = [x for x in continuous_dimension_names if x != "Z"]
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Add a code cell that loops through all the well datasets and sets the symbol to be a filled circle of size 6.
[ ]:
code = """
for well in raw_wells:
settings = inv.get_dataset_settings(well)
settings.set_shape("circlefill", 6)
inv.update()
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
The investigation is now verified as containing the correct data and has been configured to use the common settings we want for the report.
Overview section#
Add a text cell containing a heading and describes what the overview section of the report will contain.
[ ]:
text = """
## Overview map
A map showing the well head locations for each well in this report.
"""
nb['cells'].append(nbf.v4.new_markdown_cell(text))
Add a code cell which displays a map plot showing the location of all the well heads in this report.
[ ]:
code = """
#papermill_description=Write_Overview
view = MapView(inv)
view.show_boreholes(True, True, True)
investigator.plot(view)
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Add a code cell which displays a matrix plot of the continuous data for all wells in this report.
[ ]:
text = """
A matrix plot of the continuous data for all wells.
"""
nb['cells'].append(nbf.v4.new_markdown_cell(text))
[ ]:
code = """
view = MatrixView(inv)
view.set_datasets_visible(raw_wells)
view.set_dimensions(continuous_dimension_names)
view.set_count_axis("percentage")
view.show_histograms(True)
view.show_legend(True)
investigator.plot(view)
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Well QC section#
The well QC section of the report will be repeated for each well in the investigation.
Add a code cell that defines the different views as required for each plot. These views are defined once which ensures that the same view is shown for each well and reduces code duplication.
[ ]:
code = """
logtracks_view = LogTracksView(inv)
logtracks_view.group_tracks_by("property")
logtracks_view.show_continuous_fill(True)
matrix_view = MatrixView(inv)
matrix_view.set_dimensions(continuous_dimension_names)
matrix_view.set_color_by(investigator.DATASET_DIMENSION_NAME)
matrix_view.set_count_axis("percentage")
matrix_view.show_histograms(True)
matrix_view.show_histogram_as("bars")
matrix_view.show_legend(True)
multi_histo_view = HistogramView(inv)
multi_histo_view.show_multi_histograms(True)
multi_histo_view.set_dimensions(continuous_dimension_names)
multi_histo_view.set_count_axis("percentage")
multi_histo_view.set_draw_statistics(True)
multi_histo_view.show_histogram_as("bars")
multi_histo_view.show_legend(True)
facies_proportion_view = HistogramView(inv)
facies_proportion_view.set_dimension("Z")
facies_proportion_view.set_count_axis("proportional")
facies_proportion_view.set_color_by("Facies")
facies_proportion_view.show_histogram_as("bars")
facies_proportion_view.swap_axes()
facies_proportion_view.show_legend(True)
"""
nb['cells'].append(nbf.v4.new_code_cell(code))
Add a code cell which loops over all the wells in the investigation and adds text and codes to display the various plots.
Each time a plot is displayed, the view defined above will be copied and then modified to only show the required well.
[ ]:
raw_wells = [x for x in inv.dataset_names if x.startswith("Wells")]
well_names = [x.replace("Wells/", "") for x in raw_wells]
for well_index in range(0, len(raw_wells)):
code = """
#papermill_description=Write_Well_QC_{0}
""".format(well_names[well_index].replace(' ', '_'))
nb['cells'].append(nbf.v4.new_code_cell(code))
text = """
## {0} QC
The data from the {0} well are plotted in different ways to allow QC of the individual well and comparison between this wells and the other wells in the investigation.
""".format(well_names[well_index])
nb['cells'].append(nbf.v4.new_markdown_cell(text))
text = """
A well section showing log data.
"""
nb['cells'].append(nbf.v4.new_markdown_cell(text))
code = """
view = logtracks_view.copy()
view.set_boreholes(['{0}'])
investigator.plot(view, width=400)
""".format(well_names[well_index])
nb['cells'].append(nbf.v4.new_code_cell(code))
text = """
A matrix plot of the continuous properties.
"""
nb['cells'].append(nbf.v4.new_markdown_cell(text))
code = """
view = matrix_view.copy()
view.set_datasets_visible(['{0}'])
investigator.plot(view)
""".format(raw_wells[well_index])
nb['cells'].append(nbf.v4.new_code_cell(code))
text = """
A multi-dimension histogram showing the continuous data for each log plotted alongside the same data from all the other wells in the investigation.
"""
nb['cells'].append(nbf.v4.new_markdown_cell(text))
code = """
view = multi_histo_view.copy()
view.set_highlight('{0}')
investigator.plot(view)
""".format(well_names[well_index])
nb['cells'].append(nbf.v4.new_code_cell(code))
text = """
A histogram showing the proportion of facies in 10m TVD intervals for the well.
"""
nb['cells'].append(nbf.v4.new_markdown_cell(text))
code = """
view = facies_proportion_view.copy()
view.set_datasets_visible(['{0}'])
investigator.plot(view)
""".format(raw_wells[well_index])
nb['cells'].append(nbf.v4.new_code_cell(code))
Save report notebook#
Write the in-memory notebook to an notebook called WellComparison
[ ]:
#papermill_description=Save_Output_Notebook
nbf.write(nb, output_name)