diff --git a/openquake/hme/model_test_frameworks/relm/relm_test_functions.py b/openquake/hme/model_test_frameworks/relm/relm_test_functions.py index 45ce72a..316945a 100644 --- a/openquake/hme/model_test_frameworks/relm/relm_test_functions.py +++ b/openquake/hme/model_test_frameworks/relm/relm_test_functions.py @@ -195,6 +195,7 @@ def m_test_function( "fractile": fractile, "test_pass": test_pass, "test_res": test_res, + "N_norm": N_norm, "test_data": { "stoch_geom_mean_likes": stoch_geom_mean_likes.tolist(), "obs_geom_mean_like": obs_geom_mean_like, @@ -336,6 +337,7 @@ def s_test_function( "fractile": fractile, "test_pass": bool(test_pass), "test_res": test_res, + "N_norm": N_norm, "bad_bins": bad_bins, "unmatched_eqs": unmatched_eqs, "test_data": { diff --git a/openquake/hme/reporting/reporting.py b/openquake/hme/reporting/reporting.py index 3712ed6..fa054ff 100644 --- a/openquake/hme/reporting/reporting.py +++ b/openquake/hme/reporting/reporting.py @@ -51,6 +51,48 @@ def _init_env() -> Environment: return env +def _format_completeness_table(completeness_table: list) -> str: + rows = "\n".join( + f"{row[0]}{row[1]}" + for row in completeness_table + ) + return ( + "" + "" + f"{rows}" + "
Start YearMin. Magnitude
" + ) + + +def _build_eval_info(cfg: dict) -> dict: + bins = cfg.get("input", {}).get("bins", {}) + seis_catalog = cfg.get("input", {}).get("seis_catalog", {}) + + settings = { + "min_magnitude": bins.get("mfd_bin_min"), + "max_magnitude": bins.get("mfd_bin_max"), + "bin_width": bins.get("mfd_bin_width"), + "h3_resolution": bins.get("h3_res"), + } + + completeness_table = seis_catalog.get("completeness_table") + if completeness_table is not None: + comp_table_html = _format_completeness_table(completeness_table) + start_date = None + else: + comp_table_html = None + start_date = seis_catalog.get("start_date") + + catalog = { + "name": seis_catalog.get("seis_catalog_file"), + "start_date": start_date, + "completeness_table": comp_table_html, + "stop_date": seis_catalog.get("stop_date"), + } + + return {"settings": settings, "catalog": catalog} + + def generate_basic_report( cfg: dict, results: dict, @@ -94,7 +136,9 @@ def generate_basic_report( env=env, cfg=cfg, results=results, input_data=input_data ) - report = report_template.render(cfg=cfg, results=results) + eval_info = _build_eval_info(cfg) + + report = report_template.render(cfg=cfg, results=results, eval_info=eval_info) with open(cfg["report"]["basic"]["outfile"], "w") as report_file: report_file.write(report) diff --git a/openquake/hme/reporting/templates/basic_report.html b/openquake/hme/reporting/templates/basic_report.html index 7849f7e..937b796 100644 --- a/openquake/hme/reporting/templates/basic_report.html +++ b/openquake/hme/reporting/templates/basic_report.html @@ -21,6 +21,31 @@

Evaluation info

Branch analyzed: {{ cfg.input.ssm.branch }}

Tectonic region types analyzed: {{ cfg.input.ssm.tectonic_region_types }}

+ {% if eval_info is defined %} +

Settings

+ + + {% if eval_info.settings.min_magnitude is not none %}{% endif %} + {% if eval_info.settings.max_magnitude is not none %}{% endif %} + {% if eval_info.settings.bin_width is not none %}{% endif %} + {% if eval_info.settings.h3_resolution is not none %}{% endif %} + +
Minimum magnitude:{{ eval_info.settings.min_magnitude }}
Maximum magnitude:{{ eval_info.settings.max_magnitude }}
Bin width:{{ eval_info.settings.bin_width }}
H3 resolution:{{ eval_info.settings.h3_resolution }}
+ +

Catalog

+ + + {% if eval_info.catalog.name is not none %}{% endif %} + {% if eval_info.catalog.completeness_table is not none %} + + {% elif eval_info.catalog.start_date is not none %} + + {% endif %} + {% if eval_info.catalog.stop_date is not none %}{% endif %} + +
Catalog:{{ eval_info.catalog.name }}
Completeness table:{{ eval_info.catalog.completeness_table | safe }}
Start date:{{ eval_info.catalog.start_date }}
Stop date:{{ eval_info.catalog.stop_date }}
+ {% endif %} +

Results

{% if branches is defined and branches %} {% for branch_name, branch_data in branches.items() %} diff --git a/openquake/hme/reporting/templates/m_test.html b/openquake/hme/reporting/templates/m_test.html index bcb5b7f..225d711 100644 --- a/openquake/hme/reporting/templates/m_test.html +++ b/openquake/hme/reporting/templates/m_test.html @@ -11,4 +11,7 @@

GEM M-Test

Observed catalog fractile (compared to stochastic event sets): {{ res.fractile }}
Critical fractile: {{ res.critical_frac }}
-Test pass: {{ res.test_res }}
\ No newline at end of file +Test pass: {{ res.test_res }}
+{% if res.N_norm is defined and res.N_norm != 1.0 %} +N normalization factor (N_obs / N_pred): {{ "%.3f"|format(res.N_norm) }}
+{% endif %} \ No newline at end of file diff --git a/openquake/hme/reporting/templates/s_test.html b/openquake/hme/reporting/templates/s_test.html index cb9f334..5b5c771 100644 --- a/openquake/hme/reporting/templates/s_test.html +++ b/openquake/hme/reporting/templates/s_test.html @@ -77,6 +77,9 @@

GEM S-Test

Observed catalog fractile (compared to stochastic event sets): {{ res.fractile }}

Critical fractile: {{ res.critical_frac }}

Test pass: {{ res.test_res }}

+ {% if res.N_norm is defined and res.N_norm != 1.0 %} +

N normalization factor (N_obs / N_pred): {{ "%.3f"|format(res.N_norm) }}

+ {% endif %} diff --git a/openquake/hme/utils/plots.py b/openquake/hme/utils/plots.py index 0fb7c39..5554e98 100644 --- a/openquake/hme/utils/plots.py +++ b/openquake/hme/utils/plots.py @@ -15,6 +15,7 @@ from matplotlib.collections import LineCollection from matplotlib.cm import ScalarMappable from matplotlib.colors import Normalize, BoundaryNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable import io from .stats import sample_event_times_in_interval @@ -342,7 +343,11 @@ def plot_mfd( ) ax.legend(loc="upper right") - ylabel = "Annual frequency of exceedance" if annualize else "Frequency of exceedance" + ylabel = ( + "Annual frequency of exceedance" + if annualize + else "Frequency of exceedance" + ) ax.set_ylabel(ylabel) ax.set_xlabel("Magnitude") @@ -447,27 +452,20 @@ def plot_S_test_map( else: bad_bin_gdf = GeoDataFrame(cell_gdf.loc[bad_bins]) + plot_kwargs = dict( + column=f"{model_test_framework}_S_test_frac", + ax=ax, + vmin=0.0, + vmax=1.0, + cmap="OrRd_r", + ) + if map_epsg is None: - cell_gdf.plot( - column=f"{model_test_framework}_S_test_frac", - ax=ax, - vmin=0.0, - vmax=1.0, - cmap="OrRd_r", - legend=True, - ) + cell_gdf.plot(**plot_kwargs) if len(bad_bins) > 0: bad_bin_gdf.plot(ax=ax, color="blue") - else: - cell_gdf.to_crs(epsg=map_epsg).plot( - column=f"{model_test_framework}_S_test_frac", - ax=ax, - vmin=0.0, - vmax=1.0, - cmap="OrRd_r", - legend=True, - ) + cell_gdf.to_crs(epsg=map_epsg).plot(**plot_kwargs) if len(bad_bins) > 0: bad_bin_gdf.plot(ax=ax, color="blue") @@ -484,6 +482,16 @@ def plot_S_test_map( ax.set_xlim(x_lims) ax.set_ylim(y_lims) + # Add a horizontal colorbar the same width as the axes frame + divider = make_axes_locatable(ax) + cax = divider.append_axes("bottom", size="5%", pad=0.4) + sm = ScalarMappable(cmap="OrRd_r", norm=Normalize(vmin=0.0, vmax=1.0)) + sm.set_array([]) + cbar = fig.colorbar(sm, cax=cax, orientation="horizontal") + cbar.set_label( + "Cell fractile (observed likelihood compared to stochastic event sets)" + ) + if save_fig is not False: fig.savefig(save_fig) logging.info(f"S-Test map saved to: {save_fig}") @@ -776,7 +784,9 @@ def plot_rup_match_mag_dist( if save_fig is not False: fig.savefig(save_fig) - logging.info(f"Rupture matching magnitude-distance plot saved to: {save_fig}") + logging.info( + f"Rupture matching magnitude-distance plot saved to: {save_fig}" + ) if return_str: plt.switch_backend("svg")