# ------------------- The OpenQuake Model Building Toolkit --------------------
# Copyright (C) 2026 GEM Foundation and Électricité de France
# _______ _______ __ __ _______ _______ ___ _
# | || | | |_| || _ || || | | |
# | _ || _ | ____ | || |_| ||_ _|| |_| |
# | | | || | | ||____|| || | | | | _|
# | |_| || |_| | | || _ | | | | |_
# | || | | ||_|| || |_| | | | | _ |
# |_______||____||_| |_| |_||_______| |___| |___| |_|
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This script is produced within the scope of Work Package 5, named Simulation
# platform, under SIGMA3 project. For more detailed information about
# the project, please visit to https://sigma-programs.com/.
# -----------------------------------------------------------------------------
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# coding: utf-8
"""
module :mod:`openquake.man.ses_cat` provides functions to build an HMTK-formatted
catalogue from OpenQuake SES event/rupture outputs.
"""
import os
import numpy as np
import pandas as pd
[docs]
def merge_ses_event_rupture(event_file: str, rupture_file: str) -> pd.DataFrame:
"""
Merging of earthquake ruptures and events of Event-based PSHA using 'rup_id' as key.
Params:
event_file : Path to SES event csv
rupture_file : Path to SES rupture csv
Returns:
pd.DataFrame: Merged catalogue containing events matched with ruptures
"""
events = pd.read_csv(event_file, comment='#', sep=None, engine='python')
ruptures = pd.read_csv(rupture_file, comment='#', sep=None, engine='python')
return events.merge(ruptures, on="rup_id", how="left")
[docs]
def add_random_datetime(df: pd.DataFrame, seed: int = 42) -> pd.DataFrame:
"""
Assigning random date time (month/day/hour/minute/second)
Params:
df : pd.DataFrame (SES catalogue containing a 'year' column)
seed : int (Random seed for reproducibility)
Returns:
pd.DataFrame: Dataframe with added datetime columns and formatted "Date"
"""
np.random.seed(seed)
n = len(df)
months = np.random.randint(1, 13, size=n)
days = np.zeros(n, dtype=int)
for i in range(n):
year = df.iloc[i]["year"]
month = months[i]
if month in (1, 3, 5, 7, 8, 10, 12):
max_day = 31
elif month in (4, 6, 9, 11):
max_day = 30
else: # February
max_day = 29 if ((year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)) else 28
days[i] = np.random.randint(1, max_day + 1)
df["month"] = months
df["day"] = days
df["hour"] = np.random.randint(0, 24, size=n)
df["minute"] = np.random.randint(0, 60, size=n)
df["second"] = np.random.randint(0, 60, size=n)
df["Date"] = df.apply(lambda r: f"{int(r['month']):02d}/{int(r['day']):02d}/{int(r['year']):04d}", axis=1)
return df
[docs]
def convert_to_hmtk(df: pd.DataFrame) -> pd.DataFrame:
"""
Converting SES merged catalogue to HMTK-compatible column names.
Params:
df : pd.DataFrame (Merged SES dataframe)
Returns:
pd.DataFrame (Reformatted dataframe following HMTK format)
"""
df = df.rename(columns={
"centroid_lon": "longitude",
"centroid_lat": "latitude",
"mag": "magnitude",
"centroid_depth": "depth",
"event_id": "eventID"
})
cols = [
"longitude", "latitude",
"year", "month", "day", "hour", "minute", "second",
"magnitude", "depth",
"source_id", "eventID", "rlz_id", "rup_id", "ses_id",
"multiplicity", "trt", "strike", "dip", "rake",
"Date"
]
return df.reindex(columns=cols)
[docs]
def build_hmtk_ses_catalogue(event_file: str,
rupture_file: str,
output_file: str,
seed: int = 42) -> str:
"""
Building an HMTK-formatted SES catalogue from OpenQuake
SES event/rupture outputs.
Params:
event_file : Path to SES event file
rupture_file : Path to SES rupture file
output_file : Output CSV path
seed : Random seed
Returns:
Path to the generated SES catalogue CSV
"""
df = merge_ses_event_rupture(event_file, rupture_file)
df = add_random_datetime(df, seed=seed)
df = convert_to_hmtk(df)
output_dir = os.path.dirname(output_file)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
df.to_csv(output_file, index=False)
return output_file