Compare commits

...

31 Commits

Author SHA1 Message Date
817dcf6df6 Update src/seismic_hazard_forecasting.py 2025-07-01 16:42:07 +02:00
97a44db302 Update src/seismic_hazard_forecasting.py 2025-07-01 16:39:10 +02:00
09e21014d1 Update src/seismic_hazard_forecasting.py 2025-07-01 11:55:34 +02:00
52d2c5b0f7 Update src/seismic_hazard_forecasting.py 2025-07-01 11:53:20 +02:00
85ece9b87a Update src/seismic_hazard_forecasting.py 2025-07-01 11:36:11 +02:00
7f97ec7151 Update src/seismic_hazard_forecasting.py 2025-07-01 10:06:50 +02:00
d5ed8b8aa6 Update src/seismic_hazard_forecasting.py 2025-07-01 10:04:26 +02:00
adb9fcbea6 Update src/seismic_hazard_forecasting.py 2025-07-01 10:00:47 +02:00
5661cb364d Update src/seismic_hazard_forecasting.py 2025-07-01 09:57:17 +02:00
dade43d84d Update src/seismic_hazard_forecasting.py 2025-07-01 09:55:31 +02:00
b1efc7a748 Update src/seismic_hazard_forecasting.py 2025-07-01 09:52:47 +02:00
634175a6f2 Update src/seismic_hazard_forecasting.py 2025-06-30 18:46:46 +02:00
d06e24f826 Update src/seismic_hazard_forecasting.py 2025-06-30 18:44:39 +02:00
4f6aef4359 Update src/seismic_hazard_forecasting.py 2025-06-30 18:40:09 +02:00
152f1cf9c3 Update src/seismic_hazard_forecasting.py 2025-06-30 18:30:15 +02:00
8bf954f135 Update src/seismic_hazard_forecasting.py 2025-06-30 18:29:01 +02:00
a0dcc808bc Update src/seismic_hazard_forecasting.py 2025-06-30 18:27:44 +02:00
d904101ba1 Update src/seismic_hazard_forecasting.py 2025-06-30 18:21:22 +02:00
4aef02a31e Update src/seismic_hazard_forecasting.py 2025-06-30 18:18:36 +02:00
b822019942 Update src/seismic_hazard_forecasting.py 2025-06-30 18:15:24 +02:00
6085979207 Update src/seismic_hazard_forecasting.py 2025-06-30 18:14:30 +02:00
3d12388da9 Update src/seismic_hazard_forecasting.py 2025-06-30 18:13:04 +02:00
1d6002e818 Update src/seismic_hazard_forecasting.py 2025-06-30 18:01:06 +02:00
1d787f8b81 Update src/seismic_hazard_forecasting.py 2025-06-30 17:58:14 +02:00
3ba57fc7c8 Update src/seismic_hazard_forecasting.py 2025-06-30 17:56:58 +02:00
bf0f876984 Update src/seismic_hazard_forecasting.py 2025-06-30 17:44:02 +02:00
dff7be35a4 Update src/seismic_hazard_forecasting.py 2025-06-30 17:41:17 +02:00
2df8086117 Update src/seismic_hazard_forecasting.py 2025-06-30 17:31:09 +02:00
ee0ffd90c4 Update src/seismic_hazard_forecasting.py 2025-06-30 17:28:31 +02:00
8d65c65d5b Update src/seismic_hazard_forecasting.py 2025-06-30 16:55:48 +02:00
e9fd82b040 Update src/seismic_hazard_forecasting.py
log more package version numbers
2025-06-30 16:24:35 +02:00

View File

@ -1,5 +1,189 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
import Rbeast as rb
import matplotlib.dates as mdates
from matplotlib.dates import DateFormatter, AutoDateLocator
import matplotlib.pyplot as plt
import pip
pip.main(['install', '--verbose', 'igfash==0.2.2'])
from igfash.rate import datenum_to_datetime, av_rates
def apply_beast(act_rate, **kwargs):
"""
Applies BEAST to the smmothed rate data using different smoothing windows.
Input
act_rate : The activity rate data array to smooth and apply BEAST.
Output
out : A list of BEAST results for each smoothed rate array.
prob : A list of probabilities and change points extracted from BEAST results.
"""
out = []; prob = []
# Define the range of smoothing windows based on 15-20% of the size of rate array
ll = int(np.ceil(0.15 * len(act_rate)))
ul = int(np.ceil(0.20 * len(act_rate)))
# Apply BEAST for each smoothed rate array
for ii in range(ll, ul + 1):
# Apply Gaussian smoothing with window size ~15-20%
bb=pd.Series(act_rate).rolling(window=ii, win_type='gaussian', center=True, min_periods=1).mean(std=ii).to_numpy()
# Apply BEAST on the smoothed array discarding periodicity
beast_result = rb.beast(bb, season='none', **kwargs)
out.append(beast_result)
# Extract probabilities and change points from BEAST results
# Gets the median number of changepoints or if median = 0 gets 90th percentile
for result in out:
if result.trend.ncp_median != 0 and not np.isnan(result.trend.ncp_median):
ncp_median = int(result.trend.ncp_median) # No need for np.max if it's scalar
cp_probs = result.trend.cpPr[:ncp_median]
cps = result.trend.cp[:ncp_median]
sorted_indices = np.argsort(cps)
sorted_cps = cps[sorted_indices]
sorted_probs = cp_probs[sorted_indices]
else:
if not np.isnan(result.trend.ncp_pct90):
ncp_pct90 = int(result.trend.ncp_pct90)
cp_probs = result.trend.cpPr[:ncp_pct90]
cps = result.trend.cp[:ncp_pct90]
sorted_indices = np.argsort(cps)
sorted_cps = cps[sorted_indices]
sorted_probs = cp_probs[sorted_indices]
else:
# Optional fallback in case both are NaN
cp_probs = []
cps = []
# for result in out:
# if result.trend.ncp_median != 0:
# ncp_median = int(np.max(result.trend.ncp_median)) #Ensure it's integer
# cp_probs = result.trend.cpPr[:ncp_median]
# cps = result.trend.cp[:ncp_median]
# else:
# ncp_pct90 = int(np.max(result.trend.ncp_pct90)) #Ensure it's integer
# cp_probs = result.trend.cpPr[:ncp_pct90]
# cps = result.trend.cp[:ncp_pct90]
# Sort the change points and corresponding probabilities
#sorted_indices = np.argsort(cps)
#sorted_cps = cps[sorted_indices]
#sorted_probs = cp_probs[sorted_indices]
# Store the sorted change points and probabilities
prob.append(np.column_stack((sorted_probs, sorted_cps)))
return out, prob
def calc_bins2(dates, unit, bin_dur, dates_calc, rate_forecast, rate_unc_high, rate_unc_low, multiplicator, filename = "activity_rate.png", **kwargs):
start_date = dates.min()
end_date = dates.max()
end_date_dt=datenum_to_datetime(end_date)
# Unit and duration check, default to 15 DAYS or 12 HOURS
# valid_units = ['hours', 'days']
# if unit not in valid_units:
# unit = 'days'
# bin_dur = 15
# Adjust to '12 hours' if data duration is less than 15 days
# if (end_date - start_date) < 15 and unit == 'days':
# unit = 'hours'
# bin_dur = 12
# -------- bin, bin edges, bin midpoint calc -------------
# Determine number of bins
bin_edges=[end_date]
while bin_edges[-1] > start_date:
bin_edges.append(bin_edges[-1] - (bin_dur/multiplicator))
bin_edges = bin_edges[::-1]
bin_edges_dt = [datenum_to_datetime(d) for d in bin_edges]
# Discretize the time data
bin_counts, _ = np.histogram(dates, bins=bin_edges)
# Calculate the rate
act_rate = [count / ((bin_edges[i + 1] - bin_edges[i]) * multiplicator) * multiplicator for i, count in enumerate(bin_counts)]
# ---------Apply BEAST (Zhao et al., 2019) 10 times--------
ppr_all = []
for _ in range(10):
out, ppr = apply_beast(act_rate, **kwargs) # Apply BEAST
ppr_iter = np.concatenate([p for p in ppr if p is not None], axis=0) # Concatenate results
ppr_all.append(ppr_iter[:, 1])
# Concatenate all probabilities from all BEAST runs
pprs = np.concatenate(ppr_all, axis=0)
pprs = pprs[~np.isnan(pprs)]
# -------Changepoint decision and rate calculations--------
rt, idx, u_e = av_rates(act_rate, pprs)
# --------------------- Line Plot -------------------------
fig, ax = plt.subplots(figsize=(14, 5))
# Plot activiry rate for all catalog
ax.plot(bin_edges_dt[1:len(bin_edges)], act_rate, '-o', linewidth=2, markersize=6)
# Plot forecasted value with uncertainties with red line connected to last value
next_date = end_date + (bin_dur/multiplicator)
ax.plot(datenum_to_datetime(next_date), rate_forecast, 'ro', label='Forecasted Rate', markersize=6)
ax.plot([bin_edges_dt[-1], datenum_to_datetime(next_date)], [act_rate[-1], rate_forecast], 'r-')
ax.vlines(datenum_to_datetime(next_date), rate_unc_low, rate_unc_high, colors='r', linewidth=2, label='Bootstrap uncertainty') # Vertical line for the uncertainty range
# Format the x-axis with dates
ax.xaxis.set_major_locator(AutoDateLocator())
ax.xaxis.set_major_formatter(DateFormatter('%d-%b-%Y'))
plt.xticks(rotation=45)
plt.title(f'Activity rate (Time Unit: {unit}, Time Window Duration: {bin_dur} {unit})')
plt.xlabel('Time (Window Center Date)')
plt.ylabel('Activity rate per selected time window')
plt.grid(True)
# Add Vertical Line and Shaded Area
sel_data = bin_edges[-3]
sel_data_dt=datenum_to_datetime(sel_data)
ax.axvline(sel_data_dt, color='r', linestyle='--', linewidth=2, label='Time window used')
def convert_dates_to_num(dates):
return [mdates.date2num(date) if isinstance(date, datetime) else date for date in dates]
# Shade the area to the right of the vertical line
ylim = ax.get_ylim()
# Add the shaded area using the Polygon function
ax.fill_between([sel_data_dt, end_date_dt], 0.0, ylim[1]+0.1, color='r', alpha=0.5)
# Add rates and changepoints from BEAST - changepoint belongs to NEXT period
if 'rt' in locals():
for i in range(len(idx)):
# Plot first period
if i == 0 and len(idx) > 1:
ax.plot(bin_edges_dt[1:len(bin_edges)][:int(u_e[idx[i]])], [rt[i]] * int(u_e[idx[i]]), linewidth=2)
# Plot Last and second to last periods when more than 1 changepoints
elif i == len(idx) - 1 and len(idx) > 1:
ax.plot(bin_edges_dt[1:len(bin_edges)][int(u_e[idx[i - 1]]):int(u_e[idx[i]])],
[rt[i]] * (int(u_e[idx[i]]) - int(u_e[idx[i - 1]])), linewidth=2)
ax.plot(bin_edges_dt[1:len(bin_edges)][int(u_e[idx[i]]):],
[rt[i + 1]] * (len(act_rate) - int(u_e[idx[i]])), linewidth=2)
# Plot first and last period if only one changepoint
elif i == 0 and len(idx) == 1:
ax.plot(bin_edges_dt[1:len(bin_edges)][:int(u_e[idx[i]])], [rt[i]] * int(u_e[idx[i]]), linewidth=2)
ax.plot(bin_edges_dt[1:len(bin_edges)][int(u_e[idx[i]]):],
[rt[i + 1]] * (len(act_rate) - int(u_e[idx[i]])), linewidth=2)
# Plot intermediate value if it's only one
elif len(bin_edges_dt[1:len(bin_edges)][int(u_e[idx[1 - 1]]):int(u_e[idx[1]])])==1:
nnn = multiplicator / bin_dur
ax.plot(bin_edges_dt[1:len(bin_edges)][int(u_e[idx[i - 1]]):int(u_e[idx[i]]) + 1],
[rt[i]] * (int(u_e[idx[i]]) + 1 - int(u_e[idx[i - 1]])), linewidth=2)
# Plot intermediate periods
else:
ax.plot(bin_edges_dt[1:len(bin_edges)][int(u_e[idx[i - 1]]):int(u_e[idx[i]])],
[rt[i]] * (int(u_e[idx[i]]) - int(u_e[idx[i - 1]])), linewidth=2)
plt.legend(loc='best')
plt.savefig(filename)
plt.show()
return act_rate, bin_counts, bin_edges, out, pprs, rt, idx, u_e
def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max, def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max,
m_kde_method, xy_select, grid_dim, xy_win_method, rate_select, time_win_duration, m_kde_method, xy_select, grid_dim, xy_win_method, rate_select, time_win_duration,
@ -45,21 +229,18 @@ def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max
""" """
import sys import sys
from importlib.metadata import version
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, isnan from math import ceil, floor, isnan
import numpy as np import numpy as np
import scipy
import obspy
import dask import dask
from dask.diagnostics import ProgressBar # use Dask progress bar from dask.diagnostics import ProgressBar # use Dask progress bar
import kalepy as kale import kalepy as kale
import utm import utm
from skimage.transform import resize from skimage.transform import resize
import psutil import psutil
import openquake.engine
import igfash
from igfash.io import read_mat_cat, read_mat_m, read_mat_pdf, read_csv from igfash.io import read_mat_cat, read_mat_m, read_mat_pdf, read_csv
from igfash.window import win_CTL, win_CNE from igfash.window import win_CTL, win_CNE
import igfash.kde as kde import igfash.kde as kde
@ -105,11 +286,13 @@ verbose: {verbose}")
# print key package version numbers # print key package version numbers
logger.debug(f"Python version {sys.version}") logger.debug(f"Python version {sys.version}")
logger.debug(f"Numpy version {np.__version__}") logger.debug(f"Numpy version {version('numpy')}")
logger.debug(f"Scipy version {scipy.__version__}") logger.debug(f"Scipy version {version('scipy')}")
logger.debug(f"Obspy version {obspy.__version__}") logger.debug(f"Obspy version {version('obspy')}")
logger.debug(f"Openquake version {openquake.engine.__version__}") logger.debug(f"Openquake version {version('openquake.engine')}")
logger.debug(f"Igfash version {igfash.__version__}") logger.debug(f"Igfash version {version('igfash')}")
logger.debug(f"Rbeast version {version('rbeast')}")
logger.debug(f"Dask version {version('dask')}")
# print number of cpu cores available # print number of cpu cores available
ncpu = psutil.cpu_count(logical=False) ncpu = psutil.cpu_count(logical=False)
@ -365,7 +548,7 @@ verbose: {verbose}")
act_rate, bin_counts, bin_edges, out, pprs, rt, idx, u_e = calc_bins(np.array(datenum_data), time_unit, act_rate, bin_counts, bin_edges, out, pprs, rt, idx, u_e = calc_bins(np.array(datenum_data), time_unit,
time_win_duration, dates_calc, time_win_duration, dates_calc,
rate_forecast, rate_unc_high, rate_unc_low, rate_forecast, rate_unc_high, rate_unc_low,
multiplicator, quiet=True) multiplicator, quiet=True, figsize=(14,9))
# Assign probabilities # Assign probabilities
lambdas, lambdas_perc = lambda_probs(act_rate, dates_calc, bin_edges) lambdas, lambdas_perc = lambda_probs(act_rate, dates_calc, bin_edges)