Source code for openquake.hmtk.parsers.faults.fault_yaml_parser

# -*- 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.0) > 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