16 Commits
v2.64 ... fixes

Author SHA1 Message Date
4adfbb6a54 add 'g' as unit of colorbar for PGA and SA 2025-06-06 17:09:48 +02:00
bd468927f3 Update src/seismic_hazard_forecasting.py 2025-06-06 16:40:46 +02:00
bda00e225c Update src/seismic_hazard_forecasting.py 2025-06-06 14:08:24 +02:00
a0f29de47f Merge pull request 'ISEPOS-2373 Add coordinates to SVG overlay files' (#10) from ISEPOS-2373-add-coordinates-for-svg-overlay into master
Reviewed-on: #10
Reviewed-by: ftong <ftong@noreply.example.org>
2025-06-05 14:26:35 +02:00
86fb03c792 Merge branch 'master' into ISEPOS-2373-add-coordinates-for-svg-overlay 2025-06-05 14:09:06 +02:00
846078352b revert f1472bf250
revert add non-parallel processing and set as default; fix colourbar
2025-06-04 17:49:54 +02:00
f1472bf250 add non-parallel processing and set as default; fix colourbar 2025-06-04 17:15:19 +02:00
d12e91d951 Merge pull request 'Fix to boolean parsing in shf_wrapper.py' (#7) from boolean_value_parsing_fiz into master
Reviewed-on: #7
Reviewed-by: h.siejkowski <h.siejkowski@noreply.example.org>
2025-05-27 19:56:30 +02:00
7c484e3974 Fix to boolean parsing in shf_wrapper.py 2025-05-26 16:35:04 +02:00
7a39d5a07e Update src/seismic_hazard_forecasting.py 2025-05-23 16:43:15 +02:00
9c58664770 ISEPOS-2373 Simplify code 2025-05-09 10:30:00 +02:00
17cbcc8e79 ISEPOS-2373 Simplify code 2025-05-09 10:26:27 +02:00
cce0cd258d ISEPOS-2373 Also store the bounding box as a data attribute for quick access 2025-05-07 12:59:21 +02:00
c20f7c06a7 ISEPOS-2373 Fix: json not defined 2025-05-07 12:54:42 +02:00
22fc9f7c07 ISEPOS-2373 Fix: name 'svg_path' is not defined 2025-05-07 12:52:06 +02:00
8f1ab5518a ISEPOS-2373 Add saving geographical coordinates to SVG files as metadata 2025-05-07 12:35:14 +02:00
2 changed files with 79 additions and 28 deletions

View File

@@ -48,7 +48,7 @@ def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max
import logging import logging
from base_logger import getDefaultLogger from base_logger import getDefaultLogger
from timeit import default_timer as timer from timeit import default_timer as timer
from math import ceil, floor from math import ceil, floor, isnan
import numpy as np import numpy as np
import scipy import scipy
import obspy import obspy
@@ -70,6 +70,8 @@ def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator from matplotlib.ticker import MultipleLocator
from matplotlib.contour import ContourSet from matplotlib.contour import ContourSet
import xml.etree.ElementTree as ET
import json
logger = getDefaultLogger('igfash') logger = getDefaultLogger('igfash')
@@ -239,6 +241,9 @@ verbose: {verbose}")
grid_y_max = int(ceil(y_max / grid_dim) * grid_dim) grid_y_max = int(ceil(y_max / grid_dim) * grid_dim)
grid_y_min = int(floor(y_min / grid_dim) * grid_dim) grid_y_min = int(floor(y_min / grid_dim) * grid_dim)
grid_lat_max, grid_lon_max = utm.to_latlon(grid_x_max, grid_y_max, utm_zone_number, utm_zone_letter)
grid_lat_min, grid_lon_min = utm.to_latlon(grid_x_min, grid_y_min, utm_zone_number, utm_zone_letter)
# rectangular grid # rectangular grid
nx = int((grid_x_max - grid_x_min) / grid_dim) + 1 nx = int((grid_x_max - grid_x_min) / grid_dim) + 1
ny = int((grid_y_max - grid_y_min) / grid_dim) + 1 ny = int((grid_y_max - grid_y_min) / grid_dim) + 1
@@ -434,22 +439,39 @@ verbose: {verbose}")
PGA = np.zeros(shape=(nx * ny)) PGA = np.zeros(shape=(nx * ny))
# use dask parallel computing
start = timer() start = timer()
pbar = ProgressBar()
pbar.register()
# iter = range(0,len(distances))
iter = indices
iml_grid_raw = [] # raw ground motion grids
for imt in products:
logger.info(f"Estimating {imt}")
imls = [dask.delayed(compute_IMT_exceedance)(rx_lat[i], rx_lon[i], distances[i].flatten(), fr, p, lambdas, use_pp = False
forecast_len, lambdas_perc, m_range, m_pdf, m_cdf, model,
log_level=logging.DEBUG, imt=imt, IMT_min=0.0, IMT_max=2.0, if use_pp: # use dask parallel computing
rx_label=i) for i in iter] pbar = ProgressBar()
iml = dask.compute(*imls) pbar.register()
iml_grid_raw.append(list(iml)) # iter = range(0,len(distances))
iter = indices
iml_grid_raw = [] # raw ground motion grids
for imt in products:
logger.info(f"Estimating {imt}")
imls = [dask.delayed(compute_IMT_exceedance)(rx_lat[i], rx_lon[i], distances[i].flatten(), fr, p, lambdas,
forecast_len, lambdas_perc, m_range, m_pdf, m_cdf, model,
log_level=logging.DEBUG, imt=imt, IMT_min=0.0, IMT_max=2.0, rx_label=i,
rtol=0.1, use_cython=False) for i in iter]
iml = dask.compute(*imls)
iml_grid_raw.append(list(iml))
else:
iml_grid_raw = []
iter = indices
for imt in products:
iml = []
for i in iter:
iml_i = compute_IMT_exceedance(rx_lat[i], rx_lon[i], distances[i].flatten(), fr, p, lambdas, forecast_len,
lambdas_perc, m_range, m_pdf, m_cdf, model, imt=imt, IMT_min = 0.0,
IMT_max = 2.0, rx_label = i, rtol = 0.1, use_cython=False)
iml.append(iml_i)
logger.info(f"Estimated {imt} at rx {i} is {iml_i}")
iml_grid_raw.append(iml)
end = timer() end = timer()
logger.info(f"Ground motion exceedance computation time: {round(end - start, 1)} seconds") logger.info(f"Ground motion exceedance computation time: {round(end - start, 1)} seconds")
@@ -481,6 +503,12 @@ verbose: {verbose}")
mode='reflect', anti_aliasing=False) mode='reflect', anti_aliasing=False)
iml_grid_hd[iml_grid_hd == 0.0] = np.nan # change zeroes back to nan iml_grid_hd[iml_grid_hd == 0.0] = np.nan # change zeroes back to nan
# trim edges so the grid is not so blocky
vmin_hd = min(x for x in iml_grid_hd.flatten() if not isnan(x))
vmax_hd = max(x for x in iml_grid_hd.flatten() if not isnan(x))
trim_thresh = vmin
iml_grid_hd[iml_grid_hd < trim_thresh] = np.nan
# generate image overlay # generate image overlay
north, south = lat.max(), lat.min() # Latitude range north, south = lat.max(), lat.min() # Latitude range
east, west = lon.max(), lon.min() # Longitude range east, west = lon.max(), lon.min() # Longitude range
@@ -489,17 +517,27 @@ verbose: {verbose}")
map_center = [np.mean([north, south]), np.mean([east, west])] map_center = [np.mean([north, south]), np.mean([east, west])]
# Create an image from the grid # Create an image from the grid
cmap_name = 'YlOrRd'
cmap = plt.get_cmap(cmap_name)
fig, ax = plt.subplots(figsize=(6, 6)) fig, ax = plt.subplots(figsize=(6, 6))
ax.imshow(iml_grid_hd, origin='lower', cmap='viridis') ax.imshow(iml_grid_hd, origin='lower', cmap=cmap, vmin=vmin, vmax=vmax)
ax.axis('off') ax.axis('off')
# Save the figure # Save the figure
fig.canvas.draw() fig.canvas.draw()
plt.savefig("overlay_" + str(j) + ".svg", bbox_inches="tight", pad_inches=0, transparent=True) overlay_filename = f"overlay_{j}.svg"
plt.savefig(overlay_filename, bbox_inches="tight", pad_inches=0, transparent=True)
plt.close(fig) plt.close(fig)
# Embed geographic bounding box into the SVG
map_bounds = dict(zip(("south", "west", "north", "east"),
map(float, (grid_lat_min, grid_lon_min, grid_lat_max, grid_lon_max))))
tree = ET.parse(overlay_filename)
tree.getroot().set("data-map-bounds", json.dumps(map_bounds))
tree.write(overlay_filename, encoding="utf-8", xml_declaration=True)
logger.info(f"Saved geographic bounds to SVG metadata (data-map-bounds): {overlay_filename} → {map_bounds}")
# Make the color bar # Make the color bar
cmap_name = 'viridis'
width = 50 width = 50
height = 500 height = 500
@@ -507,16 +545,20 @@ verbose: {verbose}")
gradient = np.vstack((gradient, gradient)).T gradient = np.vstack((gradient, gradient)).T
gradient = np.tile(gradient, (1, width)) gradient = np.tile(gradient, (1, width))
colorbar_title = products[j]
if "PGA" in colorbar_title or "SA" in colorbar_title:
colorbar_title = colorbar_title + " (g)"
fig, ax = plt.subplots(figsize=((width + 40) / 100.0, (height + 20) / 100.0), fig, ax = plt.subplots(figsize=((width + 40) / 100.0, (height + 20) / 100.0),
dpi=100) # Increase fig size for labels dpi=100) # Increase fig size for labels
ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(cmap_name), ax.imshow(gradient, aspect='auto', cmap=cmap.reversed(),
extent=[0, 1, vmin, vmax]) # Note: extent order is different for vertical extent=[0, 1, vmin, vmax_hd]) # Note: extent order is different for vertical
ax.set_xticks([]) # Remove x-ticks for vertical colorbar ax.set_xticks([]) # Remove x-ticks for vertical colorbar
num_ticks = 11 # Show more ticks num_ticks = 11 # Show more ticks
tick_positions = np.linspace(vmin, vmax, num_ticks) tick_positions = np.linspace(vmin, vmax_hd, num_ticks)
ax.set_yticks(tick_positions) ax.set_yticks(tick_positions)
ax.set_yticklabels([f"{tick:.2f}" for tick in tick_positions]) # format tick labels ax.set_yticklabels([f"{tick:.2f}" for tick in tick_positions]) # format tick labels
ax.set_title(imt, pad=15) ax.set_title(colorbar_title, loc='right', pad=15)
fig.subplots_adjust(left=0.25, right=0.75, bottom=0.05, top=0.95) # Adjust Layout fig.subplots_adjust(left=0.25, right=0.75, bottom=0.05, top=0.95) # Adjust Layout
fig.savefig("colorbar_" + str(j) + ".svg", bbox_inches='tight') fig.savefig("colorbar_" + str(j) + ".svg", bbox_inches='tight')
plt.close(fig) plt.close(fig)

View File

@@ -23,6 +23,15 @@ from seismic_hazard_forecasting import main as shf
def main(argv): def main(argv):
def str2bool(v):
if v.lower() in ("True", "TRUE", "yes", "true", "t", "y", "1"):
return True
elif v.lower() in ("False", "FALSE", "no", "false", "f", "n", "0"):
return False
else:
raise argparse.ArgumentTypeError("Boolean value expected.")
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("catalog_file", help="Path to input file of type 'catalog'") parser.add_argument("catalog_file", help="Path to input file of type 'catalog'")
@@ -30,23 +39,23 @@ def main(argv):
parser.add_argument("--pdf_file", help="Path to input file of type 'PDF'", type=str, default=None, required=False) parser.add_argument("--pdf_file", help="Path to input file of type 'PDF'", type=str, default=None, required=False)
parser.add_argument("--m_file", help="Path to input file of type 'm'", type=str, default=None, required=False) parser.add_argument("--m_file", help="Path to input file of type 'm'", type=str, default=None, required=False)
parser.add_argument("--m_select", type=bool) parser.add_argument("--m_select", type=str2bool)
parser.add_argument("--mag_label", type=str, default=None, required=False) parser.add_argument("--mag_label", type=str, default=None, required=False)
parser.add_argument("--mc", type=float, default=None, required=False) parser.add_argument("--mc", type=float, default=None, required=False)
parser.add_argument("--m_max", type=float, default=None, required=False) parser.add_argument("--m_max", type=float, default=None, required=False)
parser.add_argument("--m_kde_method", type=str) parser.add_argument("--m_kde_method", type=str)
parser.add_argument("--xy_select", type=bool) parser.add_argument("--xy_select", type=str2bool)
parser.add_argument("--grid_dim", type=int) parser.add_argument("--grid_dim", type=int)
parser.add_argument("--xy_win_method", type=bool) parser.add_argument("--xy_win_method", type=str2bool)
parser.add_argument("--rate_select", type=bool) parser.add_argument("--rate_select", type=str2bool)
parser.add_argument("--time_win_duration", type=float) parser.add_argument("--time_win_duration", type=float)
parser.add_argument("--forecast_select", type=bool) parser.add_argument("--forecast_select", type=str2bool)
parser.add_argument("--custom_rate", type=float, default=None, required=False) parser.add_argument("--custom_rate", type=float, default=None, required=False)
parser.add_argument("--forecast_len", type=float) parser.add_argument("--forecast_len", type=float)
parser.add_argument("--time_unit", type=str) parser.add_argument("--time_unit", type=str)
parser.add_argument("--model", type=str) parser.add_argument("--model", type=str)
parser.add_argument("--products_string", type=str) parser.add_argument("--products_string", type=str)
parser.add_argument("--verbose", type=bool) parser.add_argument("--verbose", type=str2bool)
args = parser.parse_args() args = parser.parse_args()
shf(**vars(args)) shf(**vars(args))