Skip to content

Commit bb8a20d

Browse files
AudunSektnanNRFredrikNevjenNRvegardkvjorgesicachanr
authored
CO2Leakage: Include box plot (#1326)
* Merge latest from develop (#51) * Fix scale factor for Unit set to Fraction * Attempt to fix issue with containment plots failing to show up initially * Update encrypted contact information * Improve hover-information * Fix bug that removed legend text in containment plots * Fix bug with UNSMRY assuming an older csv format * Add ContainmentDataProvider wrapper class Intended to wrap an EnsembleTableProvider that reads containment summary files (either csv or arrow) * Add UnsmryDataProvider wrapper class Intended to wrap an EnsembleTableProvider that reads unsmry summary files (either csv or arrow) * Add validation to containment classes * Change summary file read order Arrow format is attempted first instead of csv * Change summary file read order Fix issue with unsmry option and missing files * Fix issue with legend when first real is not "0" * Fix bug with unsmry reset * Remove large csv file check * Remove unused file size function * Changes to adapt CO2Leakage for residual trapping maps visualization * Format changes * Solve minor issues * Solve typo * Fix issue in property_origin * Minor fixes * Rename MapGroup CO2_MASS * Add visualization menu with separate filters for each attribute * Formatting with mypy, pylint and black * Simplify MapThresholds class * Fix visualization threshold bug for plume maps * Add mean in tornado table VolumetricAnalysis (#1297) * Fix merge conflict after rebase * Fix issues related to missing polygon data * Fix black format issues * Fix another conflict from rebase * Fix another rebase conflict * Handle missing well picks and mypy errors * Remove surface cache file mode (#1298) * OS temp path for image server (#1302) * Add missing file handling for well picks * Enable usage without either maps or containment tables * CCS-194: Fix after change of units for co2 mass. (#34) * Tweak visualization filter * Add polygon provider Basically a copy of the provider for fault polygons. Renamed "fault polygon" to "polygon" and added a couple of TODOs * CCS-213: Handle plume_groups (#36) * CCS-213: Start using plume group from containment results. * CCS-213: Cont using plume group from containment results. * CCS-213: Fixes for plume groups. * CCS-213: Add plume groups to filter. * CCS-213: Some minor fixes. * CCS-213: Minor fix. * Some tests. * CCS-213: Join lines before/after merge when coloring by plume groups. * CCS-213: Some refactoring. * CCS-213: Fix combine plume group with none as mark. * CCS-213: Minor fix. * CCS-213: Remove some prints. * CCS-213: Use starting 0-values for unmerged wells. * CCS-213: Fix for multiple realization. * CCS-213: Fix case without plume groups. * CCS-213: Minor fix. * CCS-213: Fix black. * CCS-213: Some refactoring. * CCS-213: Minor change, remove temp code. * Combine time plots, add mean/P10/P90 option * Fix formatting * Fix missing issues with EnsemblePolygonProvider * Distinguish p10 and p90 from mean, turn select-all-realizations into button * Introduce new polygon provider in plugin * Fix black/lint/mypy errorss * CCS-213: Rename "?" to "undetermined" and fix warning messages (#38) * CCS-213: Fix warning message. * CCS-213: Rename ? to undetermined. * Fix issues found while testing * Minor fix in _get_menu_options() (#39) * Minor fix in _get_menu_options(). * New minor fix. * Minor change. (#40) * CCS-212: Renaming (#41) * CCS-212: Renaming, graph plots. * CCS-212: Renaming, map plots. * CCS-212: More renaming, map plots. * CCS-212: More renaming, map plots. * CCS-191: Add statistics plot (#44) * CCS-191: Start on statistics plot. * CCS-191: Minor change. * CCS-191: Use color and mark options. * CCS-191: Cont use color and mark options. * CCS-191: Cont using color and mark options. * CCS-191: Fix in label names. * CCS-191: Add default category plotted, single option. * CCS-191: Minor fig fixes. * CCS-191: Add hovering. * CCS-191: Fix some filtering. * CCS-191: Remove prints and comments. * CCS-191: Fill date options, use in plot. * CCS-191: Same fixes for date, and use in first plot. * CCS-191: Change some names, add titles. * CCS-191: Clean up. * CCS-191: Fix in set_date_option(). * Black * isort * CCS-191: Minor change. * CCS-212: Renaming, changing same labels, changing back gas_phase to sgas etc, add code for XMF2 (#45) * CCS-212: Use old file names for sgas etc maps. * CCS-212: Allow XMF2-files as default in addition to AMFG. Some changes to labels for 2D maps. * CCS-212: Some clean up. * CCS-212: Add SGSTR as option. * CCS-212: Remove prints. * Fix linting (#49) * Fix linting * Fix linting 2 * Fix linting 3 * Fix linting 4 * Fix linting 5 * Fix linting 6 * Fix linting 7 * Fix linting 8 * Develop master merged (#50) * Add mean in tornado table VolumetricAnalysis (#1297) * Remove surface cache file mode (#1298) * OS temp path for image server (#1302) * Update README.md with deprecation warning (#1303) * No Active Rfts error message (#1304) Co-authored-by: Øyvind Lind-Johansen <olind@equinor.com> * Pandas 2 compatibility for RFT plotter (#1305) * Pandas 2 compability for RFT plotter * black * Remove use of `wcc.ColorScales` (#1306) * Fix FutureWarning - cast to dtype (#1309) * Pandas 2 compability for ParameterResponseCorrelation (#1310) * Add field outline and custom well picks colors to MapViewerFMU (#1311) * Replaced use of `DashSubsurfaceViewer` with `SubsurfaceViewer` (#1312) * Expose rft input files as arguments (#1313) * exposed rft input files as arguments * black version updated * pylint fixes * pylint fixes * more fixes --------- Co-authored-by: Øyvind Lind-Johansen <olind@equinor.com> * Disable cache for statistical surfaces (#1314) * Add check for length of numpy array (#1316) * Fix ensemble colors in relperm (#1317) --------- Co-authored-by: FredrikNevjenNR <fnevjen@nr.no> Co-authored-by: FredrikNevjenNR <150343101+FredrikNevjenNR@users.noreply.github.com> Co-authored-by: Vegard Kvernelv <vegard@nr.no> Co-authored-by: Jorge Sicacha <sicacha@nr.no> Co-authored-by: jorgesicachanr <114475076+jorgesicachanr@users.noreply.github.com> Co-authored-by: vegardkv <vkvernelv@gmail.com> * Manual new commit for changes in CCS-101. (#52) * CCS-192: Add box plot, plus CCS-249: Add zone/region/phase etc in title of plots (#53) * CCS-192: Start on new box plot. * CCS-192: Fix color order, and fix none as option for mark_by. * CCS-192: Add option to plot poitns. * CCS-192: Default false for show realization points. * CCS-192: Adjust layout, add default option. * CCS-192: Option for choosing probability plot vs box plot. * CCS-192: Minor change. * CCS-192: Minor fix. * CCS-192: Remove prints. * CCS-192: Minor fix. * CCS-192: Add custom hovering template for realization points. * CCS-192: Add info to plot titles. * CCS-192: Add info to time plots. * CCS-192: Minor fix. * Black * CCS-192: Improvements to box plot (#54) * CCS-192: Change options for plotting points. * CCS-192: Fix titles. * CCS-192: Try adding hoovering info. * CCS-192: Cont attempt hoovering info. * CCS-192: Change to using go.Box, more flexibility. * CCS-192: Fix hoovering for points. * CCS-192: Better hoovering info, placement. * CCS-192: Fix in hoovering info for realization points. * CCS-192: Visual improvements. * CCS-192: Change q1-q3 calc to numpy default, and fix whiskers calc. * CCS-192: Change default box, only single if >20 categories. * CCS-192: Remove prints, fix in q1 q3. * CCS-192: Minor fixes. * CCS-192: Remove prints, old code, new fix q1 q3. * CCS-192: Minor fix, and black. * CCS-192: Change order hoovering info. * CCS-192: Some fixes in box plot (#55) * CCS-192: Make sort by color possible. * CCS-192: Print p10, p90 in hoovering * CCS-192: Remove None option for points. * CCS-192: Some label changes. * CCS-266: Fix warning. (#56) * CCS-274: Remove default for actual volume csv file. (#57) * Lint fixes. --------- Co-authored-by: FredrikNevjenNR <fnevjen@nr.no> Co-authored-by: FredrikNevjenNR <150343101+FredrikNevjenNR@users.noreply.github.com> Co-authored-by: Vegard Kvernelv <vegard@nr.no> Co-authored-by: Jorge Sicacha <sicacha@nr.no> Co-authored-by: jorgesicachanr <114475076+jorgesicachanr@users.noreply.github.com> Co-authored-by: vegardkv <vkvernelv@gmail.com>
1 parent 42a18a1 commit bb8a20d

File tree

6 files changed

+372
-32
lines changed

6 files changed

+372
-32
lines changed

webviz_subsurface/plugins/_co2_leakage/_plugin.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def __init__(
104104
ensembles: List[str],
105105
well_pick_file: Optional[str] = None,
106106
plume_mass_relpath: str = TABLES_PATH + "/plume_mass.csv",
107-
plume_actual_volume_relpath: str = TABLES_PATH + "/plume_actual_volume.csv",
107+
plume_actual_volume_relpath: Optional[str] = None,
108108
unsmry_relpath: Optional[str] = None,
109109
fault_polygon_attribute: str = "dl_extracted_faultlines",
110110
initial_surface: Optional[str] = None,
@@ -296,6 +296,13 @@ def _set_callbacks(self) -> None:
296296
Input(self._settings_component(ViewSettings.Ids.SORT_PLOT), "value"),
297297
Input(self._settings_component(ViewSettings.Ids.REAL_OR_STAT), "value"),
298298
Input(self._settings_component(ViewSettings.Ids.DATE_OPTION), "value"),
299+
Input(
300+
self._settings_component(ViewSettings.Ids.STATISTICS_TAB_OPTION),
301+
"value",
302+
),
303+
Input(
304+
self._settings_component(ViewSettings.Ids.BOX_SHOW_POINTS), "value"
305+
),
299306
)
300307
@callback_typecheck
301308
def update_graphs(
@@ -317,6 +324,8 @@ def update_graphs(
317324
sorting: str,
318325
lines_to_show: str,
319326
date_option: str,
327+
statistics_tab_option: str,
328+
box_show_points: str,
320329
) -> Tuple[Dict, go.Figure, go.Figure, go.Figure]:
321330
# pylint: disable=too-many-locals
322331
figs = [no_update] * 3
@@ -331,6 +340,8 @@ def update_graphs(
331340
sorting,
332341
lines_to_show,
333342
date_option,
343+
statistics_tab_option,
344+
box_show_points,
334345
self._menu_options[ensemble][source],
335346
)
336347
if source in [
@@ -344,6 +355,7 @@ def update_graphs(
344355
cont_info,
345356
realizations,
346357
lines_to_show,
358+
statistics_tab_option,
347359
len(figs),
348360
)
349361
cont_info["update_first_figure"] = self._plot_id != plot_ids[0]

webviz_subsurface/plugins/_co2_leakage/_utilities/callbacks.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
)
2323
from webviz_subsurface.plugins._co2_leakage._utilities import plume_extent
2424
from webviz_subsurface.plugins._co2_leakage._utilities.co2volume import (
25+
generate_co2_box_plot_figure,
2526
generate_co2_statistics_figure,
2627
generate_co2_time_containment_figure,
2728
generate_co2_time_containment_one_realization_figure,
@@ -414,12 +415,20 @@ def generate_containment_figures(
414415
containment_info,
415416
)
416417
)
417-
fig2 = generate_co2_statistics_figure(
418-
table_provider,
419-
realizations,
420-
co2_scale,
421-
containment_info,
422-
)
418+
if containment_info["statistics_tab_option"] == "probability_plot":
419+
fig2 = generate_co2_statistics_figure(
420+
table_provider,
421+
realizations,
422+
co2_scale,
423+
containment_info,
424+
)
425+
else: # "box_plot"
426+
fig2 = generate_co2_box_plot_figure(
427+
table_provider,
428+
realizations,
429+
co2_scale,
430+
containment_info,
431+
)
423432
except KeyError as exc:
424433
warnings.warn(f"Could not generate CO2 figures: {exc}")
425434
raise exc
@@ -477,6 +486,8 @@ def process_containment_info(
477486
sorting: str,
478487
lines_to_show: str,
479488
date_option: str,
489+
statistics_tab_option: str,
490+
box_show_points: str,
480491
menu_options: MenuOptions,
481492
) -> Dict[str, Union[str, None, List[str], int]]:
482493
if mark_choice is None:
@@ -518,6 +529,8 @@ def plume_sort_key(name: str) -> int:
518529
"plume_groups": plume_groups,
519530
"use_stats": lines_to_show == "stat",
520531
"date_option": date_option,
532+
"statistics_tab_option": statistics_tab_option,
533+
"box_show_points": box_show_points,
521534
}
522535

523536

@@ -528,6 +541,7 @@ def make_plot_ids(
528541
containment_info: Dict,
529542
realizations: List[int],
530543
lines_to_show: str,
544+
statistics_tab_option: str,
531545
num_figs: int,
532546
) -> List[str]:
533547
zone_str = (
@@ -565,6 +579,7 @@ def make_plot_ids(
565579
ids = [plot_id]
566580
ids += [plot_id + f"-{realizations}"] * (num_figs - 1)
567581
ids[1] += f"-{lines_to_show}"
582+
ids[2] += f"-{statistics_tab_option}"
568583
return ids
569584

570585

webviz_subsurface/plugins/_co2_leakage/_utilities/co2volume.py

Lines changed: 185 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -422,19 +422,18 @@ def _change_names(
422422
df["name"] = df["name"].replace(f"{m}, all", m)
423423

424424

425-
def _adjust_figure(fig: go.Figure, plot_title: Optional[str] = None) -> None:
425+
def _adjust_figure(fig: go.Figure, plot_title: str) -> None:
426426
fig.layout.legend.orientation = "v"
427427
fig.layout.legend.title.text = ""
428428
fig.layout.legend.itemwidth = 40
429429
fig.layout.xaxis.exponentformat = "power"
430-
if plot_title is not None:
431-
fig.layout.title.text = plot_title
432-
fig.layout.title.font = {"size": 14}
433-
fig.layout.margin.t = 40
434-
fig.layout.title.y = 0.95
435-
else:
436-
fig.layout.margin.t = 15
430+
431+
fig.layout.title.text = plot_title
432+
fig.layout.title.font = {"size": 14}
433+
fig.layout.margin.t = 40
434+
fig.layout.title.y = 0.95
437435
fig.layout.title.x = 0.4
436+
438437
fig.layout.paper_bgcolor = "rgba(0,0,0,0)"
439438
fig.layout.margin.b = 6
440439
fig.layout.margin.l = 10
@@ -508,7 +507,7 @@ def generate_co2_volume_figure(
508507
)
509508
fig.layout.yaxis.title = "Realization"
510509
fig.layout.xaxis.title = scale.value
511-
_adjust_figure(fig, plot_title=containment_info["date_option"])
510+
_adjust_figure(fig, plot_title=_make_title(containment_info))
512511
return fig
513512

514513

@@ -562,7 +561,7 @@ def generate_co2_time_containment_one_realization_figure(
562561
fig.layout.yaxis.range = y_limits
563562
fig.layout.xaxis.title = "Time"
564563
fig.layout.yaxis.title = scale.value
565-
_adjust_figure(fig)
564+
_adjust_figure(fig, plot_title=_make_title(containment_info, include_date=False))
566565
return fig
567566

568567

@@ -756,7 +755,7 @@ def generate_co2_time_containment_figure(
756755
df_grouped = df_no_real.groupby(
757756
["date", "name", color_choice, mark_choice], as_index=False
758757
)
759-
df_mean = df_grouped.agg(np.mean)
758+
df_mean = df_grouped.agg("mean")
760759
df_mean["realization"] = ["mean"] * df_mean.shape[0]
761760
df_p10 = df_grouped.agg(lambda x: np.quantile(x, 0.1))
762761
df_p10["realization"] = ["p10"] * df_p10.shape[0]
@@ -808,7 +807,7 @@ def generate_co2_time_containment_figure(
808807
fig.layout.xaxis.title = "Time"
809808
fig.layout.yaxis.title = scale.value
810809
fig.layout.yaxis.autorange = True
811-
_adjust_figure(fig)
810+
_adjust_figure(fig, plot_title=_make_title(containment_info, include_date=False))
812811
return fig
813812

814813

@@ -860,6 +859,179 @@ def generate_co2_statistics_figure(
860859
fig.layout.legend.tracegroupgap = 0
861860
fig.layout.xaxis.title = scale.value
862861
fig.layout.yaxis.title = "Probability"
863-
_adjust_figure(fig, plot_title=containment_info["date_option"])
862+
_adjust_figure(fig, plot_title=_make_title(containment_info))
863+
864+
return fig
865+
866+
867+
def generate_co2_box_plot_figure(
868+
table_provider: ContainmentDataProvider,
869+
realizations: List[int],
870+
scale: Union[Co2MassScale, Co2VolumeScale],
871+
containment_info: Dict[str, Any],
872+
) -> go.Figure:
873+
eps = 0.00001
874+
date_option = containment_info["date_option"]
875+
df = _read_co2_volumes(table_provider, realizations, scale)
876+
df = df[df["date"] == date_option]
877+
df = df.drop(columns=["date"]).reset_index(drop=True)
878+
879+
color_choice = containment_info["color_choice"]
880+
mark_choice = containment_info["mark_choice"]
881+
_filter_columns(df, color_choice, mark_choice, containment_info)
882+
cat_ord, colors, _ = _prepare_pattern_and_color_options_statistics_plot(
883+
df,
884+
containment_info,
885+
color_choice,
886+
mark_choice,
887+
)
888+
889+
fig = go.Figure()
890+
for count, type_val in enumerate(cat_ord["type"], 0):
891+
df_sub = df[df["type"] == type_val]
892+
values = df_sub["amount"].to_numpy()
893+
real = df_sub["realization"].to_numpy()
894+
895+
median_val = df_sub["amount"].median()
896+
q1 = _calculate_plotly_quantiles(values, 0.25)
897+
q3 = _calculate_plotly_quantiles(values, 0.75)
898+
p10 = np.percentile(values, 90)
899+
p90 = np.percentile(values, 10)
900+
min_fence, max_fence = _calculate_plotly_whiskers(values, q1, q3)
901+
902+
fig.add_trace(
903+
go.Box(
904+
x=[count] * len(values),
905+
y=values,
906+
name=type_val,
907+
marker_color=colors[count],
908+
boxpoints="all"
909+
if containment_info["box_show_points"] == "all_points"
910+
else "outliers",
911+
customdata=real,
912+
hovertemplate="<span style='font-family:Courier New;'>"
913+
"Type : %{data.name}<br>Amount : %{y:.3f}<br>"
914+
"Realization: %{customdata}"
915+
"</span><extra></extra>",
916+
legendgroup=type_val,
917+
width=0.55,
918+
)
919+
)
920+
921+
fig.add_trace(
922+
go.Bar(
923+
x=[count],
924+
y=[values.max() - values.min() + 2 * eps],
925+
base=[values.min() - eps],
926+
opacity=0.0,
927+
hoverinfo="none",
928+
hovertemplate=(
929+
"<span style='font-family:Courier New;'>"
930+
f"Type : {type_val}<br>"
931+
f"Max : {values.max():.3f}<br>"
932+
f"Top whisker : {max_fence:.3f}<br>"
933+
f"p10 (not shown): {p10:.3f}<br>"
934+
f"Q3 : {q3:.3f}<br>"
935+
f"Median : {median_val:.3f}<br>"
936+
f"Q1 : {q1:.3f}<br>"
937+
f"p90 (not shown): {p90:.3f}<br>"
938+
f"Lower whisker : {min_fence:.3f}<br>"
939+
f"Min : {values.min():.3f}"
940+
"</span><extra></extra>"
941+
),
942+
showlegend=False,
943+
legendgroup=type_val,
944+
name=type_val,
945+
marker_color=colors[count],
946+
width=0.56,
947+
)
948+
)
949+
950+
fig.update_layout(
951+
xaxis=dict(
952+
tickmode="array",
953+
tickvals=[i for i in range(len(cat_ord["type"]))],
954+
ticktext=cat_ord["type"],
955+
)
956+
)
957+
958+
if len(cat_ord["type"]) > 20:
959+
default_option = _find_default_option_statistics_figure(df, cat_ord["type"])
960+
for trace in fig.data:
961+
if trace.name != default_option:
962+
trace.visible = "legendonly"
963+
964+
fig.layout.yaxis.autorange = True
965+
fig.layout.legend.tracegroupgap = 0
966+
fig.layout.yaxis.title = scale.value
967+
_adjust_figure(fig, plot_title=_make_title(containment_info))
864968

865969
return fig
970+
971+
972+
def _make_title(c_info: Dict[str, Any], include_date: bool = True) -> str:
973+
components = []
974+
if include_date:
975+
components.append(c_info["date_option"])
976+
if len(c_info["phases"]) > 0 and "phase" not in [
977+
c_info["color_choice"],
978+
c_info["mark_choice"],
979+
]:
980+
if c_info["phase"] != "total":
981+
components.append(c_info["phase"].capitalize())
982+
else:
983+
components.append("Phase: Total")
984+
if len(c_info["containments"]) > 0 and "containment" not in [
985+
c_info["color_choice"],
986+
c_info["mark_choice"],
987+
]:
988+
if c_info["containment"] != "total":
989+
components.append(c_info["containment"].capitalize())
990+
else:
991+
components.append("All containments areas")
992+
if len(c_info["zones"]) > 0 and "zone" not in [
993+
c_info["color_choice"],
994+
c_info["mark_choice"],
995+
]:
996+
if c_info["zone"] != "all":
997+
components.append(c_info["zone"])
998+
else:
999+
components.append("All zones")
1000+
if len(c_info["regions"]) > 0 and "region" not in [
1001+
c_info["color_choice"],
1002+
c_info["mark_choice"],
1003+
]:
1004+
if c_info["region"] != "all":
1005+
components.append(c_info["region"])
1006+
else:
1007+
components.append("All regions")
1008+
if len(c_info["plume_groups"]) > 0 and "plume_group" not in [
1009+
c_info["color_choice"],
1010+
c_info["mark_choice"],
1011+
]:
1012+
if c_info["plume_group"] != "all":
1013+
components.append(c_info["plume_group"])
1014+
else:
1015+
components.append("All plume groups")
1016+
return " - ".join(components)
1017+
1018+
1019+
def _calculate_plotly_quantiles(values: np.ndarray, percentile: float) -> float:
1020+
values_sorted = values.copy()
1021+
values_sorted.sort()
1022+
n_val = len(values_sorted)
1023+
a = n_val * percentile - 0.5
1024+
if a.is_integer():
1025+
return float(values_sorted[int(a)])
1026+
else:
1027+
return float(np.interp(a, [x for x in range(0, n_val)], values_sorted))
1028+
1029+
1030+
def _calculate_plotly_whiskers(
1031+
values: np.ndarray, q1: float, q3: float
1032+
) -> Tuple[float, float]:
1033+
values_sorted = values.copy()
1034+
values_sorted.sort()
1035+
a = q1 - 1.5 * (q3 - q1)
1036+
b = q3 + 1.5 * (q3 - q1)
1037+
return values[values >= a].min(), values[values <= b].max()

webviz_subsurface/plugins/_co2_leakage/_utilities/initialization.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,10 @@ def init_unsmry_data_providers(
157157

158158
def init_containment_data_providers(
159159
ensemble_roots: Dict[str, str],
160-
table_rel_path: str,
160+
table_rel_path: Optional[str],
161161
) -> Dict[str, ContainmentDataProvider]:
162+
if table_rel_path is None:
163+
return {}
162164
factory = EnsembleTableProviderFactory.instance()
163165
providers = {
164166
ens: _init_ensemble_table_provider(factory, ens, ens_path, table_rel_path)

0 commit comments

Comments
 (0)