Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .stagedfright/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_allowfile_matches_if_present(staged: StagedFile, allowfile: AllowFile):
"primo/opt_model/tests/test_efficiency_block.py": 11,
"primo/opt_model/tests/test_model_options.py": 360,
"primo/opt_model/tests/test_opt_model.py": 22,
"primo/opt_model/tests/test_result_parser.py": 466,
"primo/opt_model/tests/test_result_parser.py": 613,
"primo/utils/__init__.py": 6,
"primo/utils/age_depth_estimation.py": 18,
"primo/utils/census_utils.py": 12,
Expand Down
10 changes: 8 additions & 2 deletions primo/opt_model/model_with_clustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@
1 - self.model_inputs.config.min_budget_usage / 100
) * self.total_budget

# Define upper bound for unutilized budget
# Define upper bound for the budget amount that is not utilized
# pylint: disable=no-member
self.unused_budget.setub(max_unused_budget)

Expand Down Expand Up @@ -564,6 +564,7 @@
"""
optimal_campaign = {}
plugging_cost = {}
efficiency_scores_projects = {}

for c in self.set_clusters:
blk = self.cluster[c]
Expand All @@ -579,8 +580,13 @@
# Well w is chosen, so store it in the dict
optimal_campaign[c].append(w)

if hasattr(blk, "efficiency_model"):
efficiency_scores_projects[c] = (

Check warning on line 584 in primo/opt_model/model_with_clustering.py

View check run for this annotation

Codecov / codecov/patch

primo/opt_model/model_with_clustering.py#L584

Added line #L584 was not covered by tests
blk.efficiency_model.get_efficiency_scores()
)

wd = self.model_inputs.config.well_data
return Campaign(wd, optimal_campaign, plugging_cost)
return Campaign(wd, optimal_campaign, plugging_cost, efficiency_scores_projects)

def get_solution_pool(self, solver):
"""
Expand Down
49 changes: 41 additions & 8 deletions primo/opt_model/result_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,13 @@ class Campaign:
Represents an optimal campaign that consists of multiple projects.
"""

def __init__(self, wd: WellData, clusters_dict: dict, plugging_cost: dict):
def __init__(
self,
wd: WellData,
clusters_dict: dict,
plugging_cost: dict,
efficiency_model_scores: Optional[dict[int, dict[str, float]]] = None,
):
"""
Represents an optimal campaign that consists of multiple projects.

Expand All @@ -321,12 +327,19 @@ def __init__(self, wd: WellData, clusters_dict: dict, plugging_cost: dict):
plugging_cost : dict
A dictionary where keys are cluster numbers and values
are plugging cost for that cluster

efficiency_model_scores : dict[int, dict[str, float]]
A dictionary where keys are cluster numbers and the values
are a dictionary of efficiency score names mapped to values
"""
# for now include a pointer to well data, so that I have column names
self.wd = wd
self.projects = {}
self.clusters_dict = clusters_dict

if efficiency_model_scores is None:
efficiency_model_scores = {}

index = 1
for cluster, wells in self.clusters_dict.items():
self.projects[cluster] = Project(
Expand All @@ -338,7 +351,7 @@ def __init__(self, wd: WellData, clusters_dict: dict, plugging_cost: dict):
index += 1

self.num_projects = len(self.projects)
self.efficiency_calculator = EfficiencyCalculator(self)
self.efficiency_calculator = EfficiencyCalculator(self, efficiency_model_scores)

def get_project_id_by_well_id(self, well_id: str) -> Optional[int]:
"""
Expand Down Expand Up @@ -665,7 +678,9 @@ class EfficiencyCalculator:
A class to compute efficiency scores for projects.
"""

def __init__(self, campaign: Campaign):
def __init__(
self, campaign: Campaign, efficiency_model_scores: dict[dict[str, float]]
):
"""
Constructs the object for all of the efficiency computations for a campaign

Expand All @@ -675,9 +690,14 @@ def __init__(self, campaign: Campaign):
campaign : Campaign
The final campaign for efficiencies to be computed

efficiency_scores_dict: dict[dict[str, float]]
Dictionary of efficiency metrics and scores:
{cluster # : {efficiency_metric : efficiency score}}

"""
self.campaign = campaign
self.efficiency_weights = None
self.efficiency_model_scores = efficiency_model_scores

def set_efficiency_weights(self, eff_metrics: EfficiencyMetrics):
"""
Expand Down Expand Up @@ -785,8 +805,23 @@ def compute_overall_efficiency_scores_project(self, project: Project):
Parameters
----------
project : Project
project in an Campaign
project in a Campaign
"""
LOGGER.info(
f"Computing overall efficiency score for project {project.project_id}"
)

if len(self.efficiency_model_scores) > 0:
project.update_efficiency_score(
sum(
value
for _, value in self.efficiency_model_scores[
project.project_id
].items()
)
)
return

names_attributes = [
attribute_name
for attribute_name in dir(project)
Expand All @@ -799,9 +834,6 @@ def compute_overall_efficiency_scores_project(self, project: Project):
if metric.weight != 0 and not hasattr(metric, "submetric")
]
)
LOGGER.info(
f"Computing overall efficiency score for project {project.project_id}"
)
for attr in names_attributes:
project.update_efficiency_score(getattr(project, attr))

Expand All @@ -816,7 +848,8 @@ def compute_efficiency_scores(self):
"""
Function that wraps all the methods needed to compute efficiency scores for the campaign
"""
self.compute_efficiency_attributes_for_all_projects()
if len(self.efficiency_model_scores) == 0:
self.compute_efficiency_attributes_for_all_projects()
self.compute_overall_efficiency_scores_campaign()


Expand Down
123 changes: 123 additions & 0 deletions primo/opt_model/tests/test_result_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,121 @@ def get_minimal_campaign_fixture():
return Campaign(well_data, {1: [0, 1], 2: [2, 3], 3: [4]}, {1: 10, 2: 15, 3: 20})


@pytest.fixture(name="get_minimal_campaign_eff_model", scope="function")
def get_minimal_campaign_eff_model_fixture():
im_metrics = ImpactMetrics()

# Specify weights
im_metrics.set_weight(
primary_metrics={
"ch4_emissions": 35,
"sensitive_receptors": 20,
"ann_production_volume": 20,
"well_age": 15,
"well_count": 10,
},
submetrics={
"ch4_emissions": {
"leak": 40,
"compliance": 30,
"violation": 20,
"incident": 10,
},
"sensitive_receptors": {
"schools": 50,
"hospitals": 50,
},
"ann_production_volume": {
"ann_gas_production": 50,
"ann_oil_production": 50,
},
},
)

im_metrics.check_validity()

im_metrics.delete_metric(
"dac_impact"
) # Deletes the metric as well submetrics "fed_dac" and "state_dac"
im_metrics.delete_metric("other_emissions")
im_metrics.delete_metric("five_year_production_volume")
im_metrics.delete_metric("well_integrity")
im_metrics.delete_metric("environment")

# Submetrics can also be deleted in a similar manner
im_metrics.delete_submetric("buildings_near")
im_metrics.delete_submetric("buildings_far")

col_names = WellDataColumnNames(
well_id="API Well Number",
latitude="x",
longitude="y",
operator_name="Operator Name",
age="Age [Years]",
depth="Depth [ft]",
leak="Leak [Yes/No]",
compliance="Compliance [Yes/No]",
violation="Violation [Yes/No]",
incident="Incident [Yes/No]",
hospitals="Number of Nearby Hospitals",
schools="Number of Nearby Schools",
ann_gas_production="Gas [Mcf/Year]",
ann_oil_production="Oil [bbl/Year]",
# These are user-specific columns
)

data = {
"API Well Number": {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6},
"Leak [Yes/No]": {0: "No", 1: "No", 2: "No", 3: "No", 4: "No", 5: "No"},
"Violation [Yes/No]": {0: "No", 1: "No", 2: "No", 3: "No", 4: "No", 5: "No"},
"Incident [Yes/No]": {0: "Yes", 1: "Yes", 2: "No", 3: "No", 4: "Yes", 5: "Yes"},
"Compliance [Yes/No]": {0: "No", 1: "Yes", 2: "No", 3: "Yes", 4: "No", 5: "No"},
"Oil [bbl/Year]": {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6},
"Gas [Mcf/Year]": {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6},
"Age [Years]": {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6},
"Depth [ft]": {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6},
"Operator Name": {
0: "Owner 56",
1: "Owner 136",
2: "Owner 137",
3: "Owner 190",
4: "Owner 196",
5: "Owner 196",
},
"x": {0: 0.99982, 1: 0.99995, 2: 1.51754, 3: 1.51776, 4: 1.51964, 5: 1.51931},
"y": {0: 1.95117, 1: 1.9572, 2: 1.9584, 3: 1.95746, 4: 1.95678, 5: 1.95674},
"Number of Nearby Hospitals": {0: 1, 1: 1, 2: 2, 3: 2, 4: 3, 5: 3},
"Number of Nearby Schools": {0: 1, 1: 1, 2: 2, 3: 2, 4: 3, 5: 3},
}

well_data = WellData(pd.DataFrame(data), col_names, impact_metrics=im_metrics)

well_data.compute_priority_scores()

return Campaign(
well_data,
{1: [0, 1], 2: [2, 3], 3: [4]},
{1: 10, 2: 15, 3: 20},
efficiency_model_scores={
1: {
"Efficiency Metric 1": 10,
"Efficiency Metric 2": 15,
"Efficiency Metric 3": 20,
},
2: {
"Efficiency Metric 1": 25,
"Efficiency Metric 2": 30,
"Efficiency Metric 3": 35,
},
3: {
"Efficiency Metric 1": 40,
"Efficiency Metric 2": 45,
"Efficiency Metric 3": 50,
},
},
)


@pytest.fixture(name="get_project", scope="function")
def get_project_fixture(get_campaign):
return get_campaign.projects[2]
Expand Down Expand Up @@ -654,3 +769,11 @@ def test_export_data_to_excel(get_campaign):
def test_plot_campaign(get_campaign):
get_campaign.plot_campaign("Just some toy data")
plt.close()


def test_efficiency_scores_dict_eff_model(get_minimal_campaign_eff_model):
campaign = get_minimal_campaign_eff_model
campaign.efficiency_calculator.compute_efficiency_scores()
assert campaign.projects[1].efficiency_score == 45
assert campaign.projects[2].efficiency_score == 90
assert campaign.projects[3].efficiency_score == 135
Loading