Source code for openquake.hazardlib.amp_lt

# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2010-2026 GEM Foundation
#
# OpenQuake 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.
#
# OpenQuake 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 OpenQuake. If not, see <http://www.gnu.org/licenses/>.
"""
Epistemic uncertainty on the site-amplification function: a NRML logic
tree whose single uncertaintyType="amplificationModel" branchset lists
per-branch amplification CSV files with weights summing to 1
"""
import os
import numpy

from openquake.baselib.general import BASE183
from openquake.hazardlib import InvalidFile, nrml


[docs]class AmplificationLogicTree(object): """ Parser for an amplification-function logic tree NRML XML """ filename = ''
[docs] @classmethod def is_amp_lt(cls, filename): """ :returns: True if the given file is an amplification logic tree XML """ if not filename.lower().endswith('.xml'): return False root = nrml.read(filename) if not hasattr(root, 'logicTree'): return False for child in root.logicTree: if (child.tag.endswith('logicTreeBranchSet') and child.attrib.get('uncertaintyType') == 'amplificationModel'): return True return False
def __init__(self, filename, base_path=None): self.filename = filename self.base_path = base_path or os.path.dirname(filename) self.branches = [] # List of (branchID, filename, weight) self.branchset_id = '' self._parse() def _parse(self): root = nrml.read(self.filename) if not hasattr(root, 'logicTree'): raise InvalidFile( '%s: missing <logicTree> element' % self.filename) ltree = root.logicTree bsets = [c for c in ltree if c.tag.endswith('logicTreeBranchSet')] if not bsets: raise InvalidFile( '%s: no amplificationModel branchset found' % self.filename) if len(bsets) > 1: raise InvalidFile( '%s: only one <logicTreeBranchSet> is supported' % self.filename) [bset] = bsets utype = bset.attrib.get('uncertaintyType') if utype != 'amplificationModel': raise InvalidFile( '%s: only uncertaintyType="amplificationModel" is supported ' 'in an amplification logic tree, got %r' % (self.filename, utype)) self.branchset_id = bset.attrib.get('branchSetID', 'bs_ampl') for br in bset: brid = br.attrib.get('branchID', '') rel = br.uncertaintyModel.text.strip() weight = float(br.uncertaintyWeight.text) fname = os.path.normpath(os.path.join(self.base_path, rel)) self.branches.append((brid, fname, weight)) brids = [b for b, _, _ in self.branches] if len(set(brids)) != len(brids): dups = sorted({b for b in brids if brids.count(b) > 1}) raise InvalidFile( '%s: duplicate branchID(s) in amplification logic tree: %s' % (self.filename, dups)) # Keeps the amp leg of the composite path a single BASE183 char if len(self.branches) > len(BASE183): raise InvalidFile( '%s: too many branches (%d > %d)' % (self.filename, len(self.branches), len(BASE183))) wsum = sum(w for _, _, w in self.branches) if abs(wsum - 1.) > 1e-5: raise InvalidFile( '%s: amplificationModel branch weights sum to %s, expected 1.0' % (self.filename, wsum)) @property def filenames(self): return [f for _, f, _ in self.branches] @property def weights(self): return numpy.array([w for _, _, w in self.branches]) @property def branch_ids(self): return [b for b, _, _ in self.branches]
[docs] def get_num_paths(self): return len(self.branches)
def __repr__(self): return '<AmplificationLogicTree %s, %d branches>' % ( os.path.basename(self.filename), len(self.branches))