Skip to content

Commit

Permalink
Merge pull request #10068 from gem/area
Browse files Browse the repository at this point in the history
Moved `area` inside the `cost_types` in the exposure
  • Loading branch information
micheles authored Oct 18, 2024
2 parents 3aa40f7 + 01b68d7 commit f939746
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 44 deletions.
3 changes: 0 additions & 3 deletions openquake/hazardlib/valid.py
Original file line number Diff line number Diff line change
Expand Up @@ -1209,9 +1209,6 @@ def host_port(value=None):


# used for the exposure validation
cost_type = Choice('structural', 'nonstructural', 'contents',
'business_interruption')

cost_type_type = Choice('aggregated', 'per_area', 'per_asset')


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<description>A flexible exposure model</description>

<conversions>
<area type="per_asset" unit="SQM" />
<costTypes>
<costType name="area" type="per_asset" unit="SQM" />
<costType name="structural" type="per_area" unit="USD" />
<costType name="nonstructural" type="per_asset" unit="USD" />
<costType name="contents" type="per_asset" unit="USD" />
Expand Down
41 changes: 7 additions & 34 deletions openquake/risklib/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,10 @@ class CostCalculator(object):
The same "formula" applies to retrofitting cost.
"""
def __init__(self, cost_types, area_types, units, tagi={'taxonomy': 0}):
if set(cost_types) != set(area_types):
raise ValueError('cost_types has keys %s, area_types has keys %s'
% (sorted(cost_types), sorted(area_types)))
def __init__(self, cost_types, units, tagi={'taxonomy': 0}):
for ct in cost_types.values():
assert ct in ('aggregated', 'per_asset', 'per_area'), ct
for at in area_types.values():
assert at in ('aggregated', 'per_asset'), at
self.cost_types = cost_types
self.area_types = area_types
self.units = units
self.tagi = tagi

Expand Down Expand Up @@ -142,7 +136,7 @@ def __call__(self, loss_type, assetcol):
if cost_type == "per_asset":
return cost * number
if cost_type == "per_area":
area_type = self.area_types[loss_type]
area_type = self.cost_types['area']
if area_type == "aggregated":
return cost * area
elif area_type == "per_asset":
Expand Down Expand Up @@ -587,14 +581,7 @@ def get_other_fields(fields):


def _get_exposure(fname, stop=None):
"""
:param fname:
path of the XML file containing the exposure
:param stop:
node at which to stop parsing (or None)
:returns:
a pair (Exposure instance, list of asset nodes)
"""
# returns (Exposure instance, asset nodes)
[xml] = nrml.read(fname, stop=stop)
if not xml.tag.endswith('exposureModel'):
raise InvalidFile('%s: expected exposureModel, got %s' %
Expand All @@ -617,14 +604,6 @@ def _get_exposure(fname, stop=None):
pairs.append((node['input'], noq))
except AttributeError:
pass # no fieldmap
try:
area = conversions.area
except AttributeError:
# NB: the area type cannot be an empty string because when sending
# around the CostCalculator object we would run into this numpy bug
# about pickling dictionaries with empty strings:
# https://github.com/numpy/numpy/pull/5475
area = Node('area', dict(type='?'))
try:
occupancy_periods = xml.occupancyPeriods.text.split()
except AttributeError:
Expand Down Expand Up @@ -666,14 +645,12 @@ def _get_exposure(fname, stop=None):
cost_types.sort(key=operator.itemgetter(0))
cost_types = numpy.array(cost_types, cost_type_dt)
cc = CostCalculator(
{}, {}, {}, {name: i for i, name in enumerate(tagnames)})
{}, {}, {name: i for i, name in enumerate(tagnames)})
for ct in cost_types:
name = ct['name'] # structural, nonstructural, ...
cc.cost_types[name] = ct['type'] # aggregated, per_asset, per_area
cc.area_types[name] = area['type']
cc.units[name] = ct['unit']
exp = Exposure(occupancy_periods, area.attrib, [], cc,
TagCollection(tagnames), pairs)
exp = Exposure(occupancy_periods, [], cc, TagCollection(tagnames), pairs)
assets_text = xml.assets.text.strip()
if assets_text:
# the <assets> tag contains a list of file names
Expand Down Expand Up @@ -887,18 +864,15 @@ class Exposure(object):
"""
A class to read the exposure from XML/CSV files
"""
fields = ['occupancy_periods', 'area', 'assets',
fields = ['occupancy_periods', 'assets',
'cost_calculator', 'tagcol', 'pairs']

def __toh5__(self):
cc = self.cost_calculator
loss_types = sorted(cc.cost_types)
dt = numpy.dtype([('cost_type', hdf5.vstr),
('area_type', hdf5.vstr),
('unit', hdf5.vstr)])
dt = numpy.dtype([('cost_type', hdf5.vstr), ('unit', hdf5.vstr)])
array = numpy.zeros(len(loss_types), dt)
array['cost_type'] = [cc.cost_types[lt] for lt in loss_types]
array['area_type'] = [cc.area_types[lt] for lt in loss_types]
array['unit'] = [cc.units[lt] for lt in loss_types]
attrs = dict(
loss_types=hdf5.array_of_vstr(loss_types),
Expand All @@ -910,7 +884,6 @@ def __fromh5__(self, array, attrs):
vars(self).update(attrs)
cc = self.cost_calculator = object.__new__(CostCalculator)
cc.cost_types = dict(zip(self.loss_types, decode(array['cost_type'])))
cc.area_types = dict(zip(self.loss_types, decode(array['area_type'])))
cc.units = dict(zip(self.loss_types, decode(array['unit'])))

@staticmethod
Expand Down
12 changes: 6 additions & 6 deletions openquake/risklib/read_nrml.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,8 @@ def get_fragility_model_04(fmodel, fname):
# ######################## validators ######################## #


valid_loss_types = valid.Choice('structural', 'nonstructural', 'contents',
'business_interruption', 'occupants')
valid_loss_type = valid.Choice(*[lt for lt in scientific.LOSSTYPE
if '+' not in lt and '_ins' not in lt])


def asset_mean_stddev(value, assetRef, mean, stdDev):
Expand Down Expand Up @@ -386,9 +386,9 @@ def update_validators():
'vulnerabilityFunction.id': valid.risk_id, # taxonomy
'consequenceFunction.id': valid.risk_id, # taxonomy
'asset.id': valid.asset_id,
'costType.name': valid.cost_type,
'costType.name': valid_loss_type,
'costType.type': valid.cost_type_type,
'cost.type': valid.cost_type,
'cost.type': valid_loss_type,
'area.type': valid.name,
'isAbsolute': valid.boolean,
'insuranceLimit': valid.positivefloat,
Expand Down Expand Up @@ -416,14 +416,14 @@ def update_validators():
'maxIML': valid.positivefloat,
'limitStates': valid.namelist,
'noDamageLimit': valid.NoneOr(valid.positivefloat),
'loss_type': valid_loss_types,
'loss_type': valid_loss_type,
'losses': valid.positivefloats,
'averageLoss': valid.positivefloat,
'stdDevLoss': valid.positivefloat,
'ffs.type': valid.ChoiceCI('lognormal'),
'assetLifeExpectancy': valid.positivefloat,
'interestRate': valid.positivefloat,
'lossType': valid_loss_types,
'lossType': valid_loss_type,
'aalOrig': valid.positivefloat,
'aalRetr': valid.positivefloat,
'ratio': valid.positivefloat,
Expand Down

0 comments on commit f939746

Please sign in to comment.