Skip to content
Merged
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
4 changes: 3 additions & 1 deletion apparun/gui/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ def run(self):
)
if self.input_panel is not None:
with self.input_col:
self.input_panel.run()
self.input_panel.run(
impact_model=self.impact_model, lca_data=self.lca_data
)
with self.output_col:
for output_panel in self.output_panels:
output_panel.run(
Expand Down
147 changes: 132 additions & 15 deletions apparun/gui/panels/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ class Panel(BaseModel):

st_component: Callable = None

def spawn(self):
return

@property
def state(self):
return self._state
Expand All @@ -95,6 +92,14 @@ class DynamicOutputPanel(OutputPanel):
type: Literal["dynamic_output_panel"]
result: Optional[ImpactModelResult] = None

def run(
self,
entry_data,
impact_model: Optional[ImpactModel] = None,
lca_data: Optional[pd.DataFrame] = None,
):
return

def compute_from_impact_model(self, entry_data, impact_model):
return

Expand All @@ -103,7 +108,7 @@ def fetch_from_lca_data(self, entry_data, lca_data):

def get_results(
self,
entry_data,
entry_data: Dict,
impact_model: ImpactModel = None,
lca_data: pd.DataFrame = None,
):
Expand All @@ -119,7 +124,11 @@ def get_results(
class StaticOutputPanel(OutputPanel):
type: Literal["static_output_panel"]

def run(self, impact_model: ImpactModel = None, lca_data: pd.DataFrame = None):
def run(
self,
impact_model: Optional[ImpactModel] = None,
lca_data: Optional[pd.DataFrame] = None,
):
return


Expand All @@ -131,21 +140,29 @@ def __init__(self, **args):
super().__init__(**args)
self._uuid = uuid.uuid4().hex

def submit(self):
def run(
self,
impact_model: Optional[ImpactModel] = None,
lca_data: Optional[pd.DataFrame] = None,
):
return


@register_panel("input_scenario_form_panel")
class InputScenarioFormPanel(InputPanel):
fields: Optional[List[Dict[str, Any]]] = []
fields: Optional[List[Dict[str, Any]]] = None
type: Literal["input_scenario_form_panel"]

def __init__(self, **args):
super().__init__(**args)
self._state["parameters"] = {}
self._state["action"] = None

def run(self):
def run(
self,
impact_model: Optional[ImpactModel] = None,
lca_data: Optional[pd.DataFrame] = None,
):
self.st_component = st.form(self._uuid)

if self.name is not None:
Expand All @@ -154,19 +171,32 @@ def run(self):
self._state["scenario_name"] = self.st_component.text_input(
label="Scenario name"
)
for input_field in self.fields:
if input_field["type"] == "float":
selected_params = (
self.fields
if self.fields is not None
else impact_model.parameters.to_list()
)

for selected_param in selected_params:
if selected_param["type"] == "float":
self._state["parameters"][
input_field["name"]
selected_param["name"]
] = self.st_component.text_input(
label=input_field["name"], value=input_field["default"]
label=selected_param["name"], value=selected_param["default"]
)
if selected_param["type"] == "enum":
options = (
selected_param["options"]
if self.fields is not None
else selected_param["weights"].keys()
)
if input_field["type"] == "enum":

self._state["parameters"][
input_field["name"]
selected_param["name"]
] = self.st_component.selectbox(
label=input_field["name"], options=input_field["options"]
label=selected_param["name"], options=options
)

col_button1, col_button2 = st.columns(2)
with col_button1:
scenarios_add = self.st_component.form_submit_button("Add")
Expand All @@ -176,3 +206,90 @@ def run(self):
self._state["action"] = ACTION_ADD
if scenarios_clear:
self._state["action"] = ACTION_CLEAR


@register_panel("selectable_input_range_form_panel")
class SelectableInputRangeFormPanel(InputPanel):
dimensions: Optional[int] = 2
type: Literal["selectable_input_range_form_panel"]

def __init__(self, **args):
super().__init__(**args)
self._state["parameters"] = {}
self._state["action"] = None

def run(
self,
impact_model: Optional[ImpactModel] = None,
lca_data: Optional[pd.DataFrame] = None,
):
self.st_component = st.form(self._uuid)

if self.name is not None:
self.st_component.markdown(f"### {self.name}")

for i in range(self.dimensions):
col_button1, col_button2, col_button3 = self.st_component.columns(
[0.5, 0.25, 0.25]
)
selected_param = {}
selected_param["name"] = col_button1.selectbox(
label=f"Param {i + 1}",
options=[
parameter.name
for parameter in impact_model.parameters
if parameter.type == "float"
],
)
selected_param["min"] = float(
col_button2.text_input(label="Min", value=0, key=f"{i}-min")
)
selected_param["max"] = float(
col_button3.text_input(label="Max", value=0, key=f"{i}-max")
)
self._state["parameters"][str(i)] = selected_param

scenarios_add = self.st_component.form_submit_button("Compute")

if scenarios_add:
self._state["action"] = ACTION_ADD


@register_panel("input_range_form_panel")
class InputRangeFormPanel(InputPanel):
fields: Dict[str, Dict[str, Any]]
type: Literal["input_range_form_panel"]

def __init__(self, **args):
super().__init__(**args)
self._state["parameters"] = self.fields
self._state["action"] = None

def run(
self,
impact_model: Optional[ImpactModel] = None,
lca_data: Optional[pd.DataFrame] = None,
):
self.st_component = st.form(self._uuid)

if self.name is not None:
self.st_component.markdown(f"### {self.name}")

for param_axis, param in self.fields.items():
self.st_component.markdown(f'{param["name"]}')

self._state["parameters"][param_axis]["min"] = float(
self.st_component.text_input(
label="Min", value=param["min"], key=f"{param_axis}-min"
)
)
self._state["parameters"][param_axis]["max"] = float(
self.st_component.text_input(
label="Max", value=param["max"], key=f"{param_axis}-max"
)
)

scenarios_add = self.st_component.form_submit_button("Compute")

if scenarios_add:
self._state["action"] = ACTION_ADD
34 changes: 33 additions & 1 deletion apparun/gui/panels/output_dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
register_panel,
)
from apparun.impact_model import ImpactModel
from apparun.results import ImpactModelResult, ScenarioComparisonResult
from apparun.results import HeatmapResult, ImpactModelResult, ScenarioComparisonResult


@register_panel("scenario_comparison_dynamic_output_panel")
Expand Down Expand Up @@ -54,3 +54,35 @@ def run(
st.plotly_chart(fig)
if entry_data["action"] == ACTION_CLEAR:
self._state["scenario_parameters"] = {}


@register_panel("heatmap_dynamic_output_panel")
class HeatmapDynamicOutputPanel(DynamicOutputPanel):
type: Literal["heatmap_dynamic_output_panel"]
impact_method: str
resolution: int = 64

def compute_from_impact_model(self, entry_data, impact_model):
self.result = HeatmapResult(
impact_model=impact_model,
x_parameter=entry_data["0"],
y_parameter=entry_data["1"],
impact_method=self.impact_method,
resolution=self.resolution,
)
result_table = self.result.get_table()
return result_table

def fetch_from_lca_data(self, entry_data, lca_data):
raise NotImplementedError()

def run(
self,
entry_data,
impact_model: ImpactModel = None,
lca_data: pd.DataFrame = None,
):
if entry_data["action"] == ACTION_ADD:
scores = self.get_results(entry_data["parameters"], impact_model, lca_data)
fig = self.result.get_figure(scores)
st.plotly_chart(fig)
51 changes: 51 additions & 0 deletions apparun/results.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

import itertools
import os
from typing import Any, Dict, List, Optional, Union

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
Expand Down Expand Up @@ -413,3 +415,52 @@ def get_figure(self, table: pd.DataFrame, save: bool = False):
if save:
self.save_figure(fig)
return fig


@register_result("heatmap")
class HeatmapResult(ImpactModelResult):
x_parameter: Dict[str, Union[str, float]]
y_parameter: Dict[str, Union[str, float]]
resolution: Optional[int] = 64
impact_method: str

def get_table(self) -> pd.DataFrame:
df = list(
itertools.product(
list(
np.arange(
self.x_parameter["min"],
self.x_parameter["max"],
(self.x_parameter["max"] - self.x_parameter["min"])
/ self.resolution,
)
),
list(
np.arange(
self.y_parameter["min"],
self.y_parameter["max"],
(self.y_parameter["max"] - self.y_parameter["min"])
/ self.resolution,
)
),
)
)
df = pd.DataFrame(
df, columns=[self.x_parameter["name"], self.y_parameter["name"]]
)
scores = self.impact_model.get_scores(**df.to_dict(orient="list"))
df["score"] = scores.scores[self.impact_method]
df = df.pivot(
index=self.x_parameter["name"],
columns=self.y_parameter["name"],
values="score",
)
return df

def get_figure(self, table: pd.DataFrame, save: bool = False):
fig = px.imshow(
table, text_auto=False, aspect="auto", color_continuous_scale="RdBu_r"
)
if save:
self.save_figure(fig)
return fig
31 changes: 10 additions & 21 deletions samples/conf/sample_gui.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,6 @@ modules:
input_panel:
type: input_scenario_form_panel
name: "GPU parameters"
fields:
- type: float
name: cuda_core
min: 0
max: 1024
default: 512
- type: enum
name: architecture
options:
- Pascal
- Maxwell
- type: float
name: lifespan
min: 0
max: 5
default: 2
- type: enum
name: usage_location
options:
- FR
- EU
output_panels:
- type: scenario_comparison_dynamic_output_panel
y: EFV3_CLIMATE_CHANGE
Expand Down Expand Up @@ -57,3 +36,13 @@ modules:
- type: scenario_comparison_dynamic_output_panel
y: EFV3_CLIMATE_CHANGE
hue: component
- impact_model_path: "samples/impact_models/nvidia_ai_gpu_chip.yaml"
name: "Heatmap"
input_panel:
type: selectable_input_range_form_panel
name: "Select parameters"
dimension: 2
output_panels:
- type: heatmap_dynamic_output_panel
impact_method: EFV3_CLIMATE_CHANGE
resolution: 64
13 changes: 13 additions & 0 deletions samples/scripts/python_api_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@
scenario_comparison_table = scenario_comparison_result.get_table()
scenario_comparison_result.get_figure(scenario_comparison_table, save=True)

heatmap_result = get_result("heatmap")(
impact_model=impact_model,
impact_method="EFV3_CLIMATE_CHANGE",
x_parameter={"name": "cuda_core", "min": 256, "max": 2048},
y_parameter={"name": "lifespan", "min": 1, "max": 5},
output_name="heatmap",
pdf_save_path=os.path.join(OUTPUT_FILES_PATH, "figures/"),
table_save_path=os.path.join(OUTPUT_FILES_PATH, "tables/"),
html_save_path=os.path.join(OUTPUT_FILES_PATH, "figures/"),
)
heatmap_table = heatmap_result.get_table()
heatmap_result.get_figure(heatmap_table, save=True)


# New types of results can be generated in user script, without modifying Appa Run
# source code, thanks to register_result decorator.
Expand Down
Loading