# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# LICENSE
#
# Copyright (C) 2010-2023 GEM Foundation, G. Weatherill, M. Pagani,
# D. Monelli.
#
# The Hazard Modeller's Toolkit 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.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>
#
# DISCLAIMER
#
# The software Hazard Modeller's Toolkit (openquake.hmtk) provided herein
# is released as a prototype implementation on behalf of
# scientists and engineers working within the GEM Foundation (Global
# Earthquake Model).
#
# It is distributed for the purpose of open collaboration and in the
# hope that it will be useful to the scientific, engineering, disaster
# risk and software design communities.
#
# The software is NOT distributed as part of GEM's OpenQuake suite
# (https://www.globalquakemodel.org/tools-products) and must be considered as a
# separate entity. The software provided herein is designed and implemented
# by scientific staff. It is not developed to the design standards, nor
# subject to same level of critical review by professional software
# developers, as GEM's OpenQuake software suite.
#
# Feedback and contribution to the software is welcome, and can be
# directed to the hazard scientific staff of the GEM Model Facility
# (hazard@globalquakemodel.org).
#
# The Hazard Modeller's Toolkit (openquake.hmtk) is therefore distributed WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# The GEM Foundation, and the authors of the software, assume no
# liability for use of the software.
'''
Module: openquake.hmtk.parsers.fault.fault_yaml_parser implements parser of a fault
model from the TOML format
'''
import toml
import numpy as np
from math import fabs
from openquake.hazardlib.geo.point import Point
from openquake.hazardlib.geo.line import Line
from openquake.hazardlib.scalerel import get_available_scalerel
from openquake.hmtk.faults.fault_geometries import (
SimpleFaultGeometry, ComplexFaultGeometry)
from openquake.hmtk.faults.fault_models import mtkActiveFault
from openquake.hmtk.faults.active_fault_model import mtkActiveFaultModel
from openquake.hmtk.faults.tectonic_regionalisation import (
TectonicRegionalisation)
SCALE_REL_MAP = get_available_scalerel()
[docs]def weight_list_to_tuple(data, attr_name):
'''
Converts a list of values and corresponding weights to a tuple of values
'''
if len(data['Value']) != len(data['Weight']):
raise ValueError('Number of weights do not correspond to number of '
'attributes in %s' % attr_name)
weight = np.array(data['Weight'])
if fabs(np.sum(weight) - 1.) > 1E-7:
raise ValueError('Weights do not sum to 1.0 in %s' % attr_name)
data_tuple = []
for iloc, value in enumerate(data['Value']):
data_tuple.append((value, weight[iloc]))
return data_tuple
[docs]def parse_tect_region_dict_to_tuples(region_dict):
'''
Parses the tectonic regionalisation dictionary attributes to tuples
'''
output_region_dict = []
tuple_keys = ['Displacement_Length_Ratio', 'Shear_Modulus']
# Convert MSR string name to openquake.hazardlib.scalerel object
for region in region_dict:
for val_name in tuple_keys:
region[val_name] = weight_list_to_tuple(region[val_name],
val_name)
# MSR works differently - so call get_scaling_relation_tuple
region['Magnitude_Scaling_Relation'] = weight_list_to_tuple(
region['Magnitude_Scaling_Relation'],
'Magnitude Scaling Relation')
output_region_dict.append(region)
return output_region_dict
[docs]def get_scaling_relation_tuple(msr_dict):
'''
For a dictionary of scaling relation values convert string list to
object list and then to tuple
'''
# Convert MSR string name to openquake.hazardlib.scalerel object
for iloc, value in enumerate(msr_dict['Value']):
if value not in SCALE_REL_MAP:
raise ValueError('Scaling relation %s not supported!' % value)
msr_dict['Value'][iloc] = SCALE_REL_MAP[value]()
return weight_list_to_tuple(msr_dict,
'Magnitude Scaling Relation')
[docs]class FaultYmltoSource(object):
'''
Class to parse a fault model definition from TOML format to a fault model
class
'''
def __init__(self, filename):
'''
:param str filename:
Name of input file (in yml format)
'''
self.data = toml.load(open(filename, 'rt'))
if 'Fault_Model' not in self.data:
raise ValueError('Fault Model not defined in input file!')
[docs] def read_file(self, mesh_spacing=1.0):
'''
Reads the file and returns an instance of the FaultSource class.
:param float mesh_spacing:
Fault mesh spacing (km)
'''
# Process the tectonic regionalisation
tectonic_reg = self.process_tectonic_regionalisation()
model = mtkActiveFaultModel(self.data['Fault_Model_ID'],
self.data['Fault_Model_Name'])
for fault in self.data['Fault_Model']:
fault_geometry = self.read_fault_geometry(fault['Fault_Geometry'],
mesh_spacing)
if fault['Shear_Modulus']:
fault['Shear_Modulus'] = weight_list_to_tuple(
fault['Shear_Modulus'], '%s Shear Modulus' % fault['ID'])
if fault['Displacement_Length_Ratio']:
fault['Displacement_Length_Ratio'] = weight_list_to_tuple(
fault['Displacement_Length_Ratio'],
'%s Displacement to Length Ratio' % fault['ID'])
fault_source = mtkActiveFault(
fault['ID'],
fault['Fault_Name'],
fault_geometry,
weight_list_to_tuple(fault['Slip'], '%s - Slip' % fault['ID']),
float(fault['Rake']),
fault['Tectonic_Region'],
float(fault['Aseismic']),
weight_list_to_tuple(
fault['Scaling_Relation_Sigma'],
'%s Scaling_Relation_Sigma' % fault['ID']),
neotectonic_fault=None,
scale_rel=get_scaling_relation_tuple(
fault['Magnitude_Scaling_Relation']),
aspect_ratio=fault['Aspect_Ratio'],
shear_modulus=fault['Shear_Modulus'],
disp_length_ratio=fault['Displacement_Length_Ratio'])
if tectonic_reg:
fault_source.get_tectonic_regionalisation(
tectonic_reg,
fault['Tectonic_Region'])
assert isinstance(fault['MFD_Model'], list)
fault_source.generate_config_set(fault['MFD_Model'])
model.faults.append(fault_source)
return model, tectonic_reg
[docs] def process_tectonic_regionalisation(self):
'''
Processes the tectonic regionalisation from the TOML file
'''
if 'tectonic_regionalisation' in self.data:
tectonic_reg = TectonicRegionalisation()
tectonic_reg.populate_regions(
parse_tect_region_dict_to_tuples(
self.data['tectonic_regionalisation']))
else:
tectonic_reg = None
return tectonic_reg
[docs] def read_fault_geometry(self, geo_dict, mesh_spacing=1.0):
'''
Creates the fault geometry from the parameters specified in the
dictionary.
:param dict geo_dict:
Sub-dictionary of main fault dictionary containing only
the geometry attributes
:param float mesh_spacing:
Fault mesh spacing (km)
:returns:
Instance of SimpleFaultGeometry or ComplexFaultGeometry, depending
on typology
'''
if geo_dict['Fault_Typology'] == 'Simple':
# Simple fault geometry
raw_trace = geo_dict['Fault_Trace']
trace = Line([Point(raw_trace[ival], raw_trace[ival + 1])
for ival in range(0, len(raw_trace), 2)])
geometry = SimpleFaultGeometry(trace,
geo_dict['Dip'],
geo_dict['Upper_Depth'],
geo_dict['Lower_Depth'],
mesh_spacing)
elif geo_dict['Fault_Typology'] == 'Complex':
# Complex Fault Typology
trace = []
for raw_trace in geo_dict['Fault_Trace']:
fault_edge = Line(
[Point(raw_trace[ival], raw_trace[ival + 1],
raw_trace[ival + 2]) for ival in range(0, len(raw_trace),
3)])
trace.append(fault_edge)
geometry = ComplexFaultGeometry(trace, mesh_spacing)
else:
raise ValueError('Unrecognised or unsupported fault geometry!')
return geometry