Compare commits

..

16 Commits

Author SHA1 Message Date
3ffcf2c4db Added licence 2025-06-23 14:51:08 +02:00
6f3d95974e Merge pull request 'fixes' (#13) from fixes into master
Reviewed-on: official-apps/SeismicHazardForecasting#13
Reviewed-by: mieszkomakuch <mieszkomakuch@noreply.example.org>
2025-06-09 12:36:33 +02:00
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: official-apps/SeismicHazardForecasting#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
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 77 additions and 22 deletions

13
LICENCE.txt Normal file
View File

@ -0,0 +1,13 @@
Copyright 2025 Institute of Geophysics, Polish Academy of Sciences
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

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
from base_logger import getDefaultLogger
from timeit import default_timer as timer
from math import ceil, floor
from math import ceil, floor, isnan
import numpy as np
import scipy
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
from matplotlib.ticker import MultipleLocator
from matplotlib.contour import ContourSet
import xml.etree.ElementTree as ET
import json
logger = getDefaultLogger('igfash')
@ -239,6 +241,9 @@ verbose: {verbose}")
grid_y_max = int(ceil(y_max / 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
nx = int((grid_x_max - grid_x_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))
# use dask parallel computing
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,
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) for i in iter]
iml = dask.compute(*imls)
iml_grid_raw.append(list(iml))
use_pp = False
if use_pp: # use dask parallel computing
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,
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()
logger.info(f"Ground motion exceedance computation time: {round(end - start, 1)} seconds")
@ -481,6 +503,12 @@ verbose: {verbose}")
mode='reflect', anti_aliasing=False)
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
north, south = lat.max(), lat.min() # Latitude 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])]
# Create an image from the grid
cmap_name = 'YlOrRd'
cmap = plt.get_cmap(cmap_name)
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')
# Save the figure
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)
# 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
cmap_name = 'viridis'
width = 50
height = 500
@ -507,16 +545,20 @@ verbose: {verbose}")
gradient = np.vstack((gradient, gradient)).T
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),
dpi=100) # Increase fig size for labels
ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(cmap_name),
extent=[0, 1, vmin, vmax]) # Note: extent order is different for vertical
ax.imshow(gradient, aspect='auto', cmap=cmap.reversed(),
extent=[0, 1, vmin, vmax_hd]) # Note: extent order is different for vertical
ax.set_xticks([]) # Remove x-ticks for vertical colorbar
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_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.savefig("colorbar_" + str(j) + ".svg", bbox_inches='tight')
plt.close(fig)