Ejemplo n.º 1
0
class Cost(Record):
    convertername = 'Exposure'
    pkey = Unique('asset_ref', 'type')

    asset_ref = Field(str)
    type = Field(str)
    value = Field(valid.positivefloat)
    retrofitted = Field(valid.NoneOr(valid.positivefloat))
    deductible = Field(valid.NoneOr(valid.positivefloat))
    insurance_limit = Field(valid.NoneOr(valid.positivefloat))

    def to_node(self):
        """
        Here are two examples:

        <cost type="structural" value="150000" deductible=".1"
              insuranceLimit="0.8" retrofitted="109876"/>
        <cost type="non_structural" value="25000" deductible=".09"
              insuranceLimit="0.82"/>
        """
        node = Node('cost', dict(type=self['type'], value=self['value']))
        if self['retrofitted']:
            node['retrofitted'] = self['retrofitted']
        if self['deductible']:
            node['deductible'] = self['deductible']
        if self['insurance_limit']:
            node['insuranceLimit'] = self['insurance_limit']
        return node
Ejemplo n.º 2
0
class FFSetContinuous(Record):
    convertername = 'FragilityContinuous'
    pkey = Unique('ordinal')

    ordinal = Field(int)
    taxonomy = Field(valid.not_empty)
    noDamageLimit = Field(valid.NoneOr(valid.positivefloat))
    type = Field(str)
    IMT = Field(str)
    imlUnit = Field(str)
    minIML = Field(float)
    maxIML = Field(float)

    def to_node(self):
        node = Node('ffs')
        node.append(Node('taxonomy', text=self['taxonomy']))
        ndl = self['noDamageLimit']
        if ndl:
            node['noDamageLimit'] = ndl
        typ = self['type']
        if typ:
            node['type'] = typ
        node.append(
            Node(
                'IML',
                dict(IMT=self['IMT'],
                     imlUnit=self['imlUnit'],
                     minIML=self['minIML'],
                     maxIML=self['maxIML'])))
        return node
Ejemplo n.º 3
0
class Exposure(Record):
    convertername = 'Exposure'
    pkey = Unique('id')

    id = Field(valid.not_empty)
    category = Field(valid.category)
    taxonomySource = Field(str)
    description = Field(str)
    area_type = Field(valid.NoneOr(valid.Choice('aggregated', 'per_asset')))
    area_unit = Field(valid.NoneOr(str))
    deductible_is_absolute = Field(valid.NoneOr(valid.boolean))
    insurance_limit_is_absolute = Field(valid.NoneOr(valid.boolean))

    def check_area(self):
        return (self.area_type is None and self.area_unit is None) or \
               (self.area_type is not None and self.area_unit is not None)

    _constraints = [check_area]

    def to_node(self):
        node = Node(
            'exposureModel',
            dict(id=self['id'],
                 category=self['category'],
                 taxonomySource=self['taxonomySource']))
        node.append(Node('description', text=self['description']))
        if node['category'] == 'buildings':
            conv = Node('conversions')
            conv.append(
                Node('area',
                     dict(type=self['area_type'], unit=self['area_unit'])))
            conv.append(Node('costTypes'))
            conv.append(
                Node('deductible',
                     dict(isAbsolute=self['deductible_is_absolute'])))
            conv.append(
                Node('insuranceLimit',
                     dict(isAbsolute=self['insurance_limit_is_absolute'])))
            node.append(conv)
        node.append(Node('assets'))
        return node
Ejemplo n.º 4
0
class FragilityNode(LiteralNode):
    """
    Literal Node class used to validate fragility functions
    """
    validators = dict(
        format=valid.ChoiceCI('discrete', 'continuous'),
        lossCategory=valid.name,
        IML=valid.IML,
        params=valid.fragilityparams,
        limitStates=valid.namelist,
        description=valid.utf8,
        type=valid.ChoiceCI('lognormal'),
        poEs=valid.probabilities,
        noDamageLimit=valid.NoneOr(valid.positivefloat),
    )
Ejemplo n.º 5
0
class Asset(Record):
    convertername = 'Exposure'
    pkey = Unique('asset_ref')

    location_id = Field(valid.positiveint)
    asset_ref = Field(str)
    taxonomy = Field(valid.not_empty)
    number = Field(valid.positivefloat)
    area = Field(valid.NoneOr(valid.positivefloat))

    def to_node(self):
        attr = dict(id=self['asset_ref'],
                    taxonomy=self['taxonomy'],
                    number=self['number'])
        if self['area']:
            attr['area'] = self['area']
        return Node('asset', attr)
Ejemplo n.º 6
0
class CostType(Record):
    convertername = 'Exposure'
    pkey = Unique('name')

    name = Field(str)
    type = Field(valid.Choice('aggregated', 'per_asset', 'per_area'))
    unit = Field(str)
    retrofittedType = Field(
        valid.NoneOr(valid.Choice('aggregated', 'per_asset', 'per_area')))
    retrofittedUnit = Field(str)

    def to_node(self):
        attr = dict(name=self['name'], type=self['type'], unit=self['unit'])
        if self['retrofittedType']:
            attr['retrofittedType'] = self['retrofittedType']
        if self['retrofittedUnit']:
            attr['retrofittedUnit'] = self['retrofittedUnit']
        return Node('costType', attr)
Ejemplo n.º 7
0
class FFSetDiscrete(Record):
    convertername = 'FragilityDiscrete'
    pkey = Unique('ordinal')

    ordinal = Field(int)
    taxonomy = Field(valid.not_empty)
    noDamageLimit = Field(valid.NoneOr(valid.positivefloat))
    IMT = Field(imt.from_string)
    imlUnit = Field(str)

    def to_node(self):
        node = Node('ffs')
        ndl = self['noDamageLimit']
        if ndl:
            node['noDamageLimit'] = ndl
        node.append(Node('taxonomy', text=self['taxonomy']))
        node.append(Node('IML', dict(IMT=self['IMT'],
                                     imlUnit=self['imlUnit'])))
        return node
Ejemplo n.º 8
0
class FragilityNode(LiteralNode):
    """
    Literal Node class used to validate fragility functions and consequence
    functions.
    """
    validators = dict(
        id=valid.utf8,  # no constraints on the taxonomy
        format=valid.ChoiceCI('discrete', 'continuous'),
        assetCategory=valid.utf8,
        dist=valid.Choice('LN'),
        mean=valid.positivefloat,
        stddev=valid.positivefloat,
        lossCategory=valid.name,
        poes=lambda text, **kw: valid.positivefloats(text),
        IML=valid.IML,
        minIML=valid.positivefloat,
        maxIML=valid.positivefloat,
        limitStates=valid.namelist,
        description=valid.utf8_not_empty,
        type=valid.ChoiceCI('lognormal'),
        poEs=valid.probabilities,
        noDamageLimit=valid.NoneOr(valid.positivefloat),
    )
Ejemplo n.º 9
0
 def test_none_or(self):
     validator = valid.NoneOr(valid.positiveint)
     self.assertEqual(validator(''), None)
     self.assertEqual(validator('1'), 1)
Ejemplo n.º 10
0
class OqParam(valid.ParamSet):
    siteparam = dict(
        vs30measured='reference_vs30_type',
        vs30='reference_vs30_value',
        z1pt0='reference_depth_to_1pt0km_per_sec',
        z2pt5='reference_depth_to_2pt5km_per_sec',
        backarc='reference_backarc',
    )
    area_source_discretization = valid.Param(
        valid.NoneOr(valid.positivefloat), None)
    asset_correlation = valid.Param(valid.NoneOr(valid.FloatRange(0, 1)), 0)
    asset_life_expectancy = valid.Param(valid.positivefloat)
    asset_loss_table = valid.Param(valid.boolean, False)
    avg_losses = valid.Param(valid.boolean, False)
    base_path = valid.Param(valid.utf8, '.')
    calculation_mode = valid.Param(valid.Choice(*CALCULATORS), '')
    coordinate_bin_width = valid.Param(valid.positivefloat)
    compare_with_classical = valid.Param(valid.boolean, False)
    concurrent_tasks = valid.Param(
        valid.positiveint, parallel.executor.num_tasks_hint)
    conditional_loss_poes = valid.Param(valid.probabilities, [])
    continuous_fragility_discretization = valid.Param(valid.positiveint, 20)
    description = valid.Param(valid.utf8_not_empty)
    distance_bin_width = valid.Param(valid.positivefloat)
    mag_bin_width = valid.Param(valid.positivefloat)
    export_dir = valid.Param(valid.utf8, None)
    export_multi_curves = valid.Param(valid.boolean, False)
    exports = valid.Param(valid.export_formats, ())
    filter_sources = valid.Param(valid.boolean, True)
    ground_motion_correlation_model = valid.Param(
        valid.NoneOr(valid.Choice(*GROUND_MOTION_CORRELATION_MODELS)), None)
    ground_motion_correlation_params = valid.Param(valid.dictionary)
    ground_motion_fields = valid.Param(valid.boolean, False)
    gsim = valid.Param(valid.gsim, None)
    hazard_calculation_id = valid.Param(valid.NoneOr(valid.positiveint), None)
    hazard_curves_from_gmfs = valid.Param(valid.boolean, False)
    hazard_output_id = valid.Param(valid.NoneOr(valid.positiveint))
    hazard_maps = valid.Param(valid.boolean, False)
    hypocenter = valid.Param(valid.point3d)
    ignore_missing_costs = valid.Param(valid.namelist, [])
    individual_curves = valid.Param(valid.boolean, True)
    inputs = valid.Param(dict, {})
    insured_losses = valid.Param(valid.boolean, False)
    intensity_measure_types = valid.Param(valid.intensity_measure_types, None)
    intensity_measure_types_and_levels = valid.Param(
        valid.intensity_measure_types_and_levels, None)
    interest_rate = valid.Param(valid.positivefloat)
    investigation_time = valid.Param(valid.positivefloat, None)
    loss_curve_resolution = valid.Param(valid.positiveint, 50)
    loss_ratios = valid.Param(valid.loss_ratios, ())
    lrem_steps_per_interval = valid.Param(valid.positiveint, 0)
    steps_per_interval = valid.Param(valid.positiveint, 1)
    master_seed = valid.Param(valid.positiveint, 0)
    maximum_distance = valid.Param(valid.floatdict)  # km
    asset_hazard_distance = valid.Param(valid.positivefloat, 5)  # km
    maximum_tile_weight = valid.Param(valid.positivefloat)
    mean_hazard_curves = valid.Param(valid.boolean, False)
    minimum_intensity = valid.Param(valid.floatdict, {})  # IMT -> minIML
    number_of_ground_motion_fields = valid.Param(valid.positiveint)
    number_of_logic_tree_samples = valid.Param(valid.positiveint, 0)
    num_epsilon_bins = valid.Param(valid.positiveint)
    poes = valid.Param(valid.probabilities)
    poes_disagg = valid.Param(valid.probabilities, [])
    quantile_hazard_curves = valid.Param(valid.probabilities, [])
    quantile_loss_curves = valid.Param(valid.probabilities, [])
    random_seed = valid.Param(valid.positiveint, 42)
    reference_depth_to_1pt0km_per_sec = valid.Param(
        valid.positivefloat, numpy.nan)
    reference_depth_to_2pt5km_per_sec = valid.Param(
        valid.positivefloat, numpy.nan)
    reference_vs30_type = valid.Param(
        valid.Choice('measured', 'inferred'), 'measured')
    reference_vs30_value = valid.Param(
        valid.positivefloat, numpy.nan)
    reference_backarc = valid.Param(valid.boolean, False)
    region = valid.Param(valid.coordinates, None)
    region_constraint = valid.Param(valid.wkt_polygon, None)
    region_grid_spacing = valid.Param(valid.positivefloat, None)
    risk_imtls = valid.Param(valid.intensity_measure_types_and_levels, {})
    risk_investigation_time = valid.Param(valid.positivefloat, None)
    rupture_mesh_spacing = valid.Param(valid.positivefloat, None)
    complex_fault_mesh_spacing = valid.Param(
        valid.NoneOr(valid.positivefloat), None)
    ses_per_logic_tree_path = valid.Param(valid.positiveint, 1)
    sites = valid.Param(valid.NoneOr(valid.coordinates), None)
    sites_disagg = valid.Param(valid.NoneOr(valid.coordinates), [])
    sites_per_tile = valid.Param(valid.positiveint, 1000)
    specific_assets = valid.Param(valid.namelist, [])
    taxonomies_from_model = valid.Param(valid.boolean, False)
    time_event = valid.Param(str, None)
    truncation_level = valid.Param(valid.NoneOr(valid.positivefloat), None)
    uniform_hazard_spectra = valid.Param(valid.boolean, False)
    width_of_mfd_bin = valid.Param(valid.positivefloat, None)

    @property
    def risk_files(self):
        try:
            return self._risk_files
        except AttributeError:
            self._file_type, self._risk_files = get_risk_files(self.inputs)
            return self._risk_files

    @property
    def file_type(self):
        try:
            return self._file_type
        except AttributeError:
            self._file_type, self._risk_files = get_risk_files(self.inputs)
            return self._file_type

    def __init__(self, **names_vals):
        super(OqParam, self).__init__(**names_vals)
        self.risk_investigation_time = (
            self.risk_investigation_time or self.investigation_time)
        if ('intensity_measure_types_and_levels' in names_vals and
                'intensity_measure_types' in names_vals):
            logging.warn('Ignoring intensity_measure_types since '
                         'intensity_measure_types_and_levels is set')
        if 'intensity_measure_types_and_levels' in names_vals:
            self.hazard_imtls = self.intensity_measure_types_and_levels
            delattr(self, 'intensity_measure_types_and_levels')
        elif 'intensity_measure_types' in names_vals:
            self.hazard_imtls = dict.fromkeys(self.intensity_measure_types)
            delattr(self, 'intensity_measure_types')
        self._file_type, self._risk_files = get_risk_files(self.inputs)

        # check the IMTs vs the GSIMs
        if 'gsim_logic_tree' in self.inputs:
            if self.gsim:
                raise ValueError('If `gsim_logic_tree_file` is set, there '
                                 'must be no `gsim` key')
            path = os.path.join(
                self.base_path, self.inputs['gsim_logic_tree'])
            self._gsims_by_trt = logictree.GsimLogicTree(path, []).values
            for gsims in self._gsims_by_trt.values():
                self.check_gsims(gsims)
        elif self.gsim is not None:
            self.check_gsims([self.gsim])

    def check_gsims(self, gsims):
        """
        :param gsims: a sequence of GSIM instances
        """
        imts = set('SA' if imt.startswith('SA') else imt for imt in self.imtls)
        for gsim in gsims:
            restrict_imts = gsim.DEFINED_FOR_INTENSITY_MEASURE_TYPES
            if restrict_imts:
                names = set(cls.__name__ for cls in restrict_imts)
                invalid_imts = ', '.join(imts - names)
                if invalid_imts:
                    raise ValueError(
                        'The IMT %s is not accepted by the GSIM %s' %
                        (invalid_imts, gsim))

            if 'site_model' not in self.inputs:
                # look at the required sites parameters: they must have
                # a valid value; the other parameters can keep a NaN
                # value since they are not used by the calculator
                for param in gsim.REQUIRES_SITES_PARAMETERS:
                    if param in ('lons', 'lats'):  # no check
                        continue
                    param_name = self.siteparam[param]
                    param_value = getattr(self, param_name)
                    if (isinstance(param_value, float) and
                            numpy.isnan(param_value)):
                        raise ValueError(
                            'Please set a value for %r, this is required by '
                            'the GSIM %s' % (param_name, gsim))

    @property
    def tses(self):
        """
        Return the total time as investigation_time * ses_per_logic_tree_path *
        (number_of_logic_tree_samples or 1)
        """
        return (self.investigation_time * self.ses_per_logic_tree_path *
                (self.number_of_logic_tree_samples or 1))

    @property
    def ses_ratio(self):
        """
        The ratio

        risk_investigation_time / investigation_time / ses_per_logic_tree_path
        """
        return (self.risk_investigation_time or self.investigation_time) / (
            self.investigation_time * self.ses_per_logic_tree_path)

    @property
    def imtls(self):
        """
        Returns an OrderedDict with the risk intensity measure types and
        levels, if given, or the hazard ones.
        """
        imtls = getattr(self, 'hazard_imtls', None) or self.risk_imtls
        return collections.OrderedDict(sorted(imtls.items()))

    @property
    def all_cost_types(self):
        """
        Return the cost types of the computation (including `occupants`
        if it is there) in order.
        """
        return sorted(self.risk_files)

    def set_risk_imtls(self, risk_models):
        """
        :param risk_models:
            a dictionary taxonomy -> loss_type -> risk_function

        Set the attribute risk_imtls.
        """
        # NB: different loss types may have different IMLs for the same IMT
        # in that case we merge the IMLs
        imtls = {}
        for taxonomy, risk_functions in risk_models.items():
            for loss_type, rf in risk_functions.items():
                imt = rf.imt
                from_string(imt)  # make sure it is a valid IMT
                imls = list(rf.imls)
                if imt in imtls and imtls[imt] != imls:
                    logging.debug(
                        'Different levels for IMT %s: got %s, expected %s',
                        imt, imls, imtls[imt])
                    imtls[imt] = sorted(set(imls + imtls[imt]))
                else:
                    imtls[imt] = imls
        self.risk_imtls = imtls

        if self.uniform_hazard_spectra:
            self.check_uniform_hazard_spectra()

    def loss_dt(self, dtype=numpy.float32):
        """
        Return a composite dtype based on the loss types, including occupants
        """
        loss_types = self.all_cost_types
        dts = [(lt, dtype) for lt in loss_types]
        if self.insured_losses:
            for lt in loss_types:
                dts.append((lt + '_ins', dtype))
        return numpy.dtype(dts)

    def no_imls(self):
        """
        Return True if there are no intensity measure levels
        """
        return all(ls is None for ls in self.imtls.values())

    def is_valid_truncation_level_disaggregation(self):
        """
        Truncation level must be set for disaggregation calculations
        """
        if self.calculation_mode == 'disaggregation':
            return self.truncation_level is not None
        else:
            return True

    def is_valid_region(self):
        """
        If there is a region a region_grid_spacing must be given
        """
        return self.region_grid_spacing if self.region else True

    def is_valid_geometry(self):
        """
        It is possible to infer the geometry only if exactly
        one of sites, sites_csv, hazard_curves_csv, gmfs_csv,
        region and exposure_file is set. You did set more than
        one, or nothing.
        """
        if self.calculation_mode not in HAZARD_CALCULATORS:
            return True  # no check on the sites for risk
        flags = dict(
            sites=bool(self.sites),
            sites_csv=self.inputs.get('sites', 0),
            hazard_curves_csv=self.inputs.get('hazard_curves', 0),
            gmfs_csv=self.inputs.get('gmvs', 0),
            region=bool(self.region),
            exposure=self.inputs.get('exposure', 0))
        # NB: below we check that all the flags
        # are mutually exclusive
        return sum(bool(v) for v in flags.values()) == 1 or self.inputs.get(
            'site_model')

    def is_valid_poes(self):
        """
        When computing hazard maps and/or uniform hazard spectra,
        the poes list must be non-empty.
        """
        if self.hazard_maps or self.uniform_hazard_spectra:
            return bool(self.poes)
        else:
            return True

    def is_valid_maximum_distance(self):
        """
        Invalid maximum_distance={maximum_distance}: {error}
        """
        if 'source_model_logic_tree' not in self.inputs:
            return True  # don't apply validation
        gsim_lt = self.inputs['gsim_logic_tree']
        trts = set(self.maximum_distance)
        unknown = ', '.join(trts - set(self._gsims_by_trt) - set(['default']))
        if unknown:
            self.error = ('setting the maximum_distance for %s which is '
                          'not in %s' % (unknown, gsim_lt))
            return False
        for trt, val in self.maximum_distance.items():
            if val <= 0:
                self.error = '%s=%r < 0' % (trt, val)
                return False
            elif trt not in self._gsims_by_trt and trt != 'default':
                self.error = 'tectonic region %r not in %s' % (trt, gsim_lt)
                return False
        if 'default' not in trts and trts < set(self._gsims_by_trt):
            missing = ', '.join(set(self._gsims_by_trt) - trts)
            self.error = 'missing distance for %s and no default' % missing
            return False
        return True

    def is_valid_intensity_measure_types(self):
        """
        If the IMTs and levels are extracted from the risk models,
        they must not be set directly. Moreover, if
        `intensity_measure_types_and_levels` is set directly,
        `intensity_measure_types` must not be set.
        """
        if self.ground_motion_correlation_model:
            for imt in self.imtls:
                if not (imt.startswith('SA') or imt == 'PGA'):
                    raise ValueError(
                        'Correlation model %s does not accept IMT=%s' % (
                            self.ground_motion_correlation_model, imt))
        if self.risk_files:  # IMTLs extracted from the risk files
            return (self.intensity_measure_types is None and
                    self.intensity_measure_types_and_levels is None)
        elif not hasattr(self, 'hazard_imtls') and not hasattr(
                self, 'risk_imtls'):
            return False
        return True

    def is_valid_intensity_measure_levels(self):
        """
        In order to compute hazard curves, `intensity_measure_types_and_levels`
        must be set or extracted from the risk models.
        """
        invalid = self.no_imls() and not self.risk_files and (
            self.hazard_curves_from_gmfs or self.calculation_mode in
            ('classical', 'disaggregation'))
        return not invalid

    def is_valid_sites_disagg(self):
        """
        The option `sites_disagg` (when given) requires `specific_assets` to
        be set.
        """
        if self.sites_disagg:
            return self.specific_assets or 'specific_assets' in self.inputs
        return True  # a missing sites_disagg is valid

    def is_valid_specific_assets(self):
        """
        Read the special assets from the parameters `specific_assets` or
        `specific_assets_csv`, if present. You cannot have both. The
        concept is meaninful only for risk calculators.
        """
        if self.specific_assets and 'specific_assets' in self.inputs:
            return False
        else:
            return True

    def is_valid_hazard_curves(self):
        """
        You must set `hazard_curves_from_gmfs` if `mean_hazard_curves`
        or `quantile_hazard_curves` are set.
        """
        if self.calculation_mode == 'event_based' and (
           self.mean_hazard_curves or self.quantile_hazard_curves):
            return self.hazard_curves_from_gmfs
        return True

    def is_valid_export_dir(self):
        """
        The `export_dir` parameter must refer to a directory,
        and the user must have the permission to write on it.
        """
        if not self.export_dir:
            self.export_dir = os.path.expanduser('~')  # home directory
            logging.warn('export_dir not specified. Using export_dir=%s'
                         % self.export_dir)
            return True
        elif not os.path.exists(self.export_dir):
            # check that we can write on the parent directory
            pdir = os.path.dirname(self.export_dir)
            can_write = os.path.exists(pdir) and os.access(pdir, os.W_OK)
            if can_write:
                os.mkdir(self.export_dir)
            return can_write
        return os.path.isdir(self.export_dir) and os.access(
            self.export_dir, os.W_OK)

    def is_valid_inputs(self):
        """
        Invalid calculation_mode="{calculation_mode}" or missing
        fragility_file/vulnerability_file in the .ini file.
        """
        if 'damage' in self.calculation_mode:
            return any(key.endswith('_fragility') for key in self.inputs)
        elif 'risk' in self.calculation_mode:
            return any(key.endswith('_vulnerability') for key in self.inputs)
        return True

    def is_valid_complex_fault_mesh_spacing(self):
        """
        The `complex_fault_mesh_spacing` parameter can be None only if
        `rupture_mesh_spacing` is set. In that case it is identified with it.
        """
        rms = getattr(self, 'rupture_mesh_spacing', None)
        if rms and not getattr(self, 'complex_fault_mesh_spacing', None):
            self.complex_fault_mesh_spacing = self.rupture_mesh_spacing
        return True

    def check_uniform_hazard_spectra(self):
        ok_imts = [imt for imt in self.imtls if imt == 'PGA' or
                   imt.startswith('SA')]
        if not ok_imts:
            raise ValueError('The `uniform_hazard_spectra` can be True only '
                             'if the IMT set contains SA(...) or PGA, got %s'
                             % list(self.imtls))
Ejemplo n.º 11
0
class OqParam(valid.ParamSet):
    area_source_discretization = valid.Param(valid.NoneOr(valid.positivefloat),
                                             None)
    asset_correlation = valid.Param(valid.NoneOr(valid.FloatRange(0, 1)), 0)
    asset_life_expectancy = valid.Param(valid.positivefloat)
    base_path = valid.Param(valid.utf8, '.')
    calculation_mode = valid.Param(valid.Choice(*CALCULATORS), '')
    coordinate_bin_width = valid.Param(valid.positivefloat)
    compare_with_classical = valid.Param(valid.boolean, False)
    concurrent_tasks = valid.Param(valid.positiveint,
                                   parallel.executor.num_tasks_hint)
    conditional_loss_poes = valid.Param(valid.probabilities, [])
    continuous_fragility_discretization = valid.Param(valid.positiveint, 20)
    description = valid.Param(valid.utf8_not_empty)
    distance_bin_width = valid.Param(valid.positivefloat)
    mag_bin_width = valid.Param(valid.positivefloat)
    epsilon_sampling = valid.Param(valid.positiveint, 1000)
    export_dir = valid.Param(valid.utf8, None)
    export_multi_curves = valid.Param(valid.boolean, False)
    exports = valid.Param(valid.export_formats, ())
    ground_motion_correlation_model = valid.Param(
        valid.NoneOr(valid.Choice(*GROUND_MOTION_CORRELATION_MODELS)), None)
    ground_motion_correlation_params = valid.Param(valid.dictionary)
    ground_motion_fields = valid.Param(valid.boolean, False)
    gsim = valid.Param(valid.gsim, None)
    hazard_calculation_id = valid.Param(valid.NoneOr(valid.positiveint), None)
    hazard_curves_from_gmfs = valid.Param(valid.boolean, False)
    hazard_output_id = valid.Param(valid.NoneOr(valid.positiveint))
    hazard_maps = valid.Param(valid.boolean, False)
    hypocenter = valid.Param(valid.point3d)
    ignore_missing_costs = valid.Param(valid.namelist, [])
    individual_curves = valid.Param(valid.boolean, True)
    inputs = valid.Param(dict, {})
    insured_losses = valid.Param(valid.boolean, False)
    intensity_measure_types = valid.Param(valid.intensity_measure_types, None)
    intensity_measure_types_and_levels = valid.Param(
        valid.intensity_measure_types_and_levels, None)
    # hazard_imtls = valid.Param(valid.intensity_measure_types_and_levels, {})
    interest_rate = valid.Param(valid.positivefloat)
    investigation_time = valid.Param(valid.positivefloat, None)
    loss_curve_resolution = valid.Param(valid.positiveint, 50)
    loss_ratios = valid.Param(valid.loss_ratios, ())
    lrem_steps_per_interval = valid.Param(valid.positiveint, 0)
    steps_per_interval = valid.Param(valid.positiveint, 0)
    master_seed = valid.Param(valid.positiveint, 0)
    maximum_distance = valid.Param(valid.positivefloat)  # km
    asset_hazard_distance = valid.Param(valid.positivefloat, 5)  # km
    maximum_tile_weight = valid.Param(valid.positivefloat)
    mean_hazard_curves = valid.Param(valid.boolean, False)
    number_of_ground_motion_fields = valid.Param(valid.positiveint, 0)
    number_of_logic_tree_samples = valid.Param(valid.positiveint, 0)
    num_epsilon_bins = valid.Param(valid.positiveint)
    poes = valid.Param(valid.probabilities)
    poes_disagg = valid.Param(valid.probabilities, [])
    quantile_hazard_curves = valid.Param(valid.probabilities, [])
    quantile_loss_curves = valid.Param(valid.probabilities, [])
    random_seed = valid.Param(valid.positiveint, 42)
    reference_depth_to_1pt0km_per_sec = valid.Param(valid.positivefloat, 1.)
    reference_depth_to_2pt5km_per_sec = valid.Param(valid.positivefloat, 1.)
    reference_vs30_type = valid.Param(valid.Choice('measured', 'inferred'),
                                      'measured')
    reference_vs30_value = valid.Param(valid.positivefloat, 1.)
    reference_backarc = valid.Param(valid.boolean, False)
    region = valid.Param(valid.coordinates, None)
    region_constraint = valid.Param(valid.wkt_polygon, None)
    region_grid_spacing = valid.Param(valid.positivefloat, None)
    risk_imtls = valid.Param(valid.intensity_measure_types_and_levels, {})
    risk_investigation_time = valid.Param(valid.positivefloat, None)
    rupture_mesh_spacing = valid.Param(valid.positivefloat, None)
    complex_fault_mesh_spacing = valid.Param(valid.NoneOr(valid.positivefloat),
                                             None)
    ses_per_logic_tree_path = valid.Param(valid.positiveint, 1)
    sites = valid.Param(valid.NoneOr(valid.coordinates), None)
    sites_disagg = valid.Param(valid.NoneOr(valid.coordinates), [])
    specific_assets = valid.Param(valid.namelist, [])
    statistics = valid.Param(valid.boolean, True)
    taxonomies_from_model = valid.Param(valid.boolean, False)
    time_event = valid.Param(str, None)
    truncation_level = valid.Param(valid.NoneOr(valid.positivefloat), None)
    uniform_hazard_spectra = valid.Param(valid.boolean, False)
    width_of_mfd_bin = valid.Param(valid.positivefloat, None)

    def __init__(self, **names_vals):
        super(OqParam, self).__init__(**names_vals)
        self.risk_investigation_time = (self.risk_investigation_time
                                        or self.investigation_time)
        if ('intensity_measure_types_and_levels' in names_vals
                and 'intensity_measure_types' in names_vals):
            logging.warn('Ignoring intensity_measure_types since '
                         'intensity_measure_types_and_levels is set')
        if 'intensity_measure_types_and_levels' in names_vals:
            self.hazard_imtls = self.intensity_measure_types_and_levels
            delattr(self, 'intensity_measure_types_and_levels')
        elif 'intensity_measure_types' in names_vals:
            self.hazard_imtls = dict.fromkeys(self.intensity_measure_types)
            delattr(self, 'intensity_measure_types')
        if vulnerability_files(self.inputs):
            self.risk_imtls = get_imtls_from_vulnerabilities(self.inputs)
        elif fragility_files(self.inputs):
            fname = self.inputs['fragility']
            ffs = get_fragility_functions(
                fname, self.continuous_fragility_discretization)
            self.risk_imtls = {fset.imt: fset.imls for fset in ffs.values()}

        # check the IMTs vs the GSIMs
        if 'gsim_logic_tree' in self.inputs:
            if self.gsim:
                raise ValueError('If `gsim_logic_tree_file` is set, there '
                                 'must be no `gsim` key')
            path = os.path.join(self.base_path, self.inputs['gsim_logic_tree'])
            for gsims in logictree.GsimLogicTree(path, []).values.values():
                self.check_imts_gsims(list(map(valid.gsim, gsims)))
        elif self.gsim is not None:
            self.check_imts_gsims([self.gsim])

    def check_imts_gsims(self, gsims):
        """
        :param gsims: a sequence of GSIM instances
        """
        imts = set('SA' if imt.startswith('SA') else imt for imt in self.imtls)
        for gsim in gsims:
            restrict_imts = gsim.DEFINED_FOR_INTENSITY_MEASURE_TYPES
            if restrict_imts:
                names = set(cls.__name__ for cls in restrict_imts)
                invalid_imts = ', '.join(imts - names)
                if invalid_imts:
                    raise ValueError(
                        'The IMT %s is not accepted by the GSIM %s' %
                        (invalid_imts, gsim))

    @property
    def tses(self):
        """
        Return the total time as investigation_time * ses_per_logic_tree_path *
        (number_of_logic_tree_samples or 1)
        """
        return (self.investigation_time * self.ses_per_logic_tree_path *
                (self.number_of_logic_tree_samples or 1))

    @property
    def imtls(self):
        """
        Returns an OrderedDict with the risk intensity measure types and
        levels, if given, or the hazard ones.
        """
        imtls = getattr(self, 'hazard_imtls', None) or self.risk_imtls
        return collections.OrderedDict(sorted(imtls.items()))

    def no_imls(self):
        """
        Return True if there are no intensity measure levels
        """
        return all(ls is None for ls in self.imtls.values())

    def is_valid_truncation_level_disaggregation(self):
        """
        Truncation level must be set for disaggregation calculations
        """
        if self.calculation_mode == 'disaggregation':
            return self.truncation_level is not None
        else:
            return True

    def is_valid_geometry(self):
        """
        It is possible to infer the geometry only if exactly
        one of sites, sites_csv, hazard_curves_csv, gmfs_csv,
        region and exposure_file is set. You did set more than
        one, or nothing.
        """
        if self.calculation_mode not in HAZARD_CALCULATORS:
            return True  # no check on the sites for risk
        flags = dict(sites=bool(self.sites),
                     sites_csv=self.inputs.get('sites', 0),
                     hazard_curves_csv=self.inputs.get('hazard_curves', 0),
                     gmfs_csv=self.inputs.get('gmvs', 0),
                     region=bool(self.region),
                     exposure=self.inputs.get('exposure', 0))
        # NB: below we check that all the flags
        # are mutually exclusive
        return sum(
            bool(v)
            for v in flags.values()) == 1 or self.inputs.get('site_model')

    def is_valid_poes(self):
        """
        When computing hazard maps and/or uniform hazard spectra,
        the poes list must be non-empty.
        """
        if self.hazard_maps or self.uniform_hazard_spectra:
            return bool(self.poes)
        else:
            return True

    def is_valid_site_model(self):
        """
        In absence of a site_model file the site model parameters
        must be all set.
        """
        if self.calculation_mode in HAZARD_CALCULATORS and (
                'site_model' not in self.inputs):
            return (self.reference_vs30_type and self.reference_vs30_value
                    and self.reference_depth_to_2pt5km_per_sec
                    and self.reference_depth_to_1pt0km_per_sec)
        else:
            return True

    def is_valid_maximum_distance(self):
        """
        The maximum_distance must be set for all hazard calculators
        """
        return (self.calculation_mode not in HAZARD_CALCULATORS
                or getattr(self, 'maximum_distance', None))

    def is_valid_intensity_measure_types(self):
        """
        If the IMTs and levels are extracted from the risk models,
        they must not be set directly. Moreover, if
        `intensity_measure_types_and_levels` is set directly,
        `intensity_measure_types` must not be set.
        """
        if self.ground_motion_correlation_model:
            for imt in self.imtls:
                if not (imt.startswith('SA') or imt == 'PGA'):
                    raise ValueError(
                        'Correlation model %s does not accept IMT=%s' %
                        (self.ground_motion_correlation_model, imt))
        if fragility_files(self.inputs) or vulnerability_files(self.inputs):
            return (self.intensity_measure_types is None
                    and self.intensity_measure_types_and_levels is None)
        elif not hasattr(self, 'hazard_imtls') and not hasattr(
                self, 'risk_imtls'):
            return False
        return True

    def is_valid_intensity_measure_levels(self):
        """
        In order to compute hazard curves, `intensity_measure_types_and_levels`
        must be set or extracted from the risk models.
        """
        invalid = self.no_imls() and (
            self.hazard_curves_from_gmfs or self.calculation_mode
            in ('classical', 'classical_tiling', 'disaggregation'))
        return not invalid

    def is_valid_sites_disagg(self):
        """
        The option `sites_disagg` (when given) requires `specific_assets` to
        be set.
        """
        if self.sites_disagg:
            return self.specific_assets or 'specific_assets' in self.inputs
        return True  # a missing sites_disagg is valid

    def is_valid_specific_assets(self):
        """
        Read the special assets from the parameters `specific_assets` or
        `specific_assets_csv`, if present. You cannot have both. The
        concept is meaninful only for risk calculators.
        """
        if self.specific_assets and 'specific_assets' in self.inputs:
            return False
        else:
            return True

    def is_valid_hazard_curves(self):
        """
        You must set `hazard_curves_from_gmfs` if `mean_hazard_curves`
        or `quantile_hazard_curves` are set.
        """
        if self.calculation_mode == 'event_based' and (
                self.mean_hazard_curves or self.quantile_hazard_curves):
            return self.hazard_curves_from_gmfs
        return True

    def is_valid_export_dir(self):
        """
        The `export_dir` parameter must refer to a directory,
        and the user must have the permission to write on it.
        """
        if not self.export_dir:
            self.export_dir = os.path.expanduser('~')  # home directory
            logging.warn('export_dir not specified. Using export_dir=%s' %
                         self.export_dir)
            return True
        elif not os.path.exists(self.export_dir):
            # check that we can write on the parent directory
            pdir = os.path.dirname(self.export_dir)
            can_write = os.path.exists(pdir) and os.access(pdir, os.W_OK)
            if can_write:
                os.mkdir(self.export_dir)
            return can_write
        return os.path.isdir(self.export_dir) and os.access(
            self.export_dir, os.W_OK)

    def is_valid_inputs(self):
        """
        Invalid calculation_mode="{calculation_mode}" or missing
        fragility_file/vulnerability_file in the .ini file.
        """
        if 'damage' in self.calculation_mode:
            return 'fragility' in self.inputs
        elif 'risk' in self.calculation_mode:
            return any(key.endswith('_vulnerability') for key in self.inputs)
        return True

    def is_valid_complex_fault_mesh_spacing(self):
        """
        The `complex_fault_mesh_spacing` parameter can be None only if
        `rupture_mesh_spacing` is set. In that case it is identified with it.
        """
        rms = getattr(self, 'rupture_mesh_spacing', None)
        if rms and not getattr(self, 'complex_fault_mesh_spacing', None):
            self.complex_fault_mesh_spacing = self.rupture_mesh_spacing
        return True
Ejemplo n.º 12
0
class OqParam(valid.ParamSet):
    params = valid.parameters(
        area_source_discretization=valid.positivefloat,
        asset_correlation=valid.NoneOr(valid.FloatRange(0, 1)),
        asset_life_expectancy=valid.positivefloat,
        base_path=valid.utf8,
        calculation_mode=valid.Choice(*CALCULATORS),
        coordinate_bin_width=valid.positivefloat,
        conditional_loss_poes=valid.probabilities,
        description=valid.utf8_not_empty,
        distance_bin_width=valid.positivefloat,
        mag_bin_width=valid.positivefloat,
        export_dir=valid.utf8,
        export_multi_curves=valid.boolean,
        ground_motion_correlation_model=valid.NoneOr(
            valid.Choice(*GROUND_MOTION_CORRELATION_MODELS)),
        ground_motion_correlation_params=valid.dictionary,
        ground_motion_fields=valid.boolean,
        gsim=valid.Choice(*GSIMS),
        hazard_calculation_id=valid.NoneOr(valid.positiveint),
        hazard_curves_from_gmfs=valid.boolean,
        hazard_output_id=valid.NoneOr(valid.positiveint),
        hazard_maps=valid.boolean,
        hypocenter=valid.point3d,
        individual_curves=valid.boolean,
        inputs=dict,
        insured_losses=valid.boolean,
        intensity_measure_types=valid.intensity_measure_types,
        intensity_measure_types_and_levels=valid.
        intensity_measure_types_and_levels,
        interest_rate=valid.positivefloat,
        investigation_time=valid.positivefloat,
        loss_curve_resolution=valid.positiveint,
        lrem_steps_per_interval=valid.positiveint,
        master_seed=valid.positiveint,
        maximum_distance=valid.positivefloat,
        mean_hazard_curves=valid.boolean,
        number_of_ground_motion_fields=valid.positiveint,
        number_of_logic_tree_samples=valid.positiveint,
        num_epsilon_bins=valid.positiveint,
        poes=valid.probabilities,
        poes_disagg=valid.probabilities,
        quantile_hazard_curves=valid.probabilities,
        quantile_loss_curves=valid.probabilities,
        random_seed=valid.positiveint,
        reference_depth_to_1pt0km_per_sec=valid.positivefloat,
        reference_depth_to_2pt5km_per_sec=valid.positivefloat,
        reference_vs30_type=valid.Choice('measured', 'inferred'),
        reference_vs30_value=valid.positivefloat,
        region=valid.coordinates,
        region_constraint=valid.coordinates,
        region_grid_spacing=valid.positivefloat,
        risk_investigation_time=valid.positivefloat,
        rupture_mesh_spacing=valid.positivefloat,
        ses_per_logic_tree_path=valid.positiveint,
        sites=valid.NoneOr(valid.coordinates),
        sites_disagg=valid.NoneOr(valid.coordinates),
        taxonomies_from_model=valid.boolean,
        time_event=str,
        truncation_level=valid.NoneOr(valid.positivefloat),
        uniform_hazard_spectra=valid.boolean,
        width_of_mfd_bin=valid.positivefloat,
    )

    def is_valid_hazard_calculation_and_output(self):
        """
        The parameters `hazard_calculation_id` and `hazard_output_id`
        are not correct for this calculator.
        """
        if self.calculation_mode in HAZARD_CALCULATORS:
            return (self.hazard_calculation_id is None
                    and self.hazard_output_id is None)
        return (self.hazard_calculation_id is None and
                self.hazard_output_id is not None) or \
            (self.hazard_calculation_id is not None and
             self.hazard_output_id is None)

    def is_valid_truncation_level_disaggregation(self):
        """
        Truncation level must be set for disaggregation calculations
        """
        if self.calculation_mode == 'disaggregation':
            return self.truncation_level is not None
        else:
            return True

    def is_valid_geometry(self):
        """
        Must specify either region, sites or exposure_file.
        """
        if self.calculation_mode not in HAZARD_CALCULATORS:
            return True  # no check on the sites for risk
        sites = getattr(self, 'sites', None)
        if getattr(self, 'region', None):
            return sites is None and not 'exposure' in self.inputs
        elif 'exposure' in self.inputs:
            return sites is None
        else:
            return sites is not None

    def is_valid_poes(self):
        """
        When computing hazard maps and/or uniform hazard spectra,
        the poes list must be non-empty.
        """
        if getattr(self, 'hazard_maps', None) or getattr(
                self, 'uniform_hazard_spectra', None):
            return bool(self.poes)
        else:
            return True

    def is_valid_site_model(self):
        """
        In absence of a site_model file the site model parameters
        must be all set.
        """
        if self.calculation_mode in HAZARD_CALCULATORS and (
                'site_model' not in self.inputs):
            return (self.reference_vs30_type and self.reference_vs30_value
                    and self.reference_depth_to_2pt5km_per_sec
                    and self.reference_depth_to_1pt0km_per_sec)
        else:
            return True

    def is_valid_maximum_distance(self):
        """
        The maximum_distance must be set for all hazard calculators
        """
        return self.calculation_mode in RISK_CALCULATORS or (getattr(
            self, 'maximum_distance', None))