Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moved area inside the cost_types in the exposure #10068

Merged
merged 6 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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