Skip to content

Commit 7a600fd

Browse files
committed
Add vulnerability functions.
We add vulnerability functions with the aim of replicating the paper: 'Climate change-related statistical indicators' Statistics Paper Series No 48 from the European Central Bank. https://www.ecb.europa.eu/pub/pdf/scpsps/ecb.sps48~e3fd21dd5a.en.pdf?4826f6ee77d3ac7a681916b6d419b751 Co-authored-by: Víctor de Luna <vdeluna@arfima.com> Co-authored-by: Arfima Dev <dev@arfima.com> Signed-off-by: Virginia Morales <vmorales@arfima.com>
1 parent d4f98fa commit 7a600fd

22 files changed

+2049
-256
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,5 @@ quote-style = "double"
181181
indent-style = "space"
182182
skip-magic-trailing-comma = false
183183
line-ending = "auto"
184-
#docstring-code-format = false
184+
docstring-code-format = true
185185
docstring-code-line-length = "dynamic"

src/physrisk/api/v1/common.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
from enum import Enum
12
from typing import Dict, List, Optional, Union
23

34
import numpy as np
4-
from pydantic import BaseModel, Field
5+
from pydantic import BaseModel, ConfigDict, Field
56

67

78
class TypedArray(np.ndarray):
@@ -23,13 +24,25 @@ class Array(np.ndarray, metaclass=ArrayMeta):
2324
pass
2425

2526

26-
class Asset(BaseModel, extra="allow"):
27-
"""Defines an asset. An asset is identified first by its asset_class and then by its type within the class.
27+
class UseCaseId(Enum):
28+
"""Enumeration to select the 'use case' identifier used in vulnerability models and risk measures calculations."""
29+
30+
DEFAULT = 0
31+
STRESS_TEST = 1
32+
GENERIC = 2
33+
34+
35+
class Asset(BaseModel):
36+
"""Defines an asset.
37+
38+
An asset is identified first by its asset_class and then by its type within the class.
2839
An asset's value may be impacted through damage or through disruption
2940
disruption being reduction of an asset's ability to generate cashflows
3041
(or equivalent value, e.g. by reducing expenses or increasing sales).
3142
"""
3243

44+
model_config = ConfigDict(extra="allow")
45+
3346
asset_class: str = Field(
3447
description="name of asset class; corresponds to physrisk class names, e.g. PowerGeneratingAsset"
3548
)
@@ -104,30 +117,27 @@ class IntensityCurve(BaseModel):
104117
class ExceedanceCurve(BaseModel):
105118
"""General exceedance curve (e.g. hazazrd, impact)."""
106119

120+
model_config = ConfigDict(arbitrary_types_allowed=True)
107121
values: np.ndarray = Field(default_factory=lambda: np.zeros(10), description="")
108122
exceed_probabilities: np.ndarray = Field(
109123
default_factory=lambda: np.zeros(10), description=""
110124
)
111125

112-
class Config:
113-
arbitrary_types_allowed = True
114-
115126

116127
class Distribution(BaseModel):
117128
"""General exceedance curve (e.g. hazazrd, impact)."""
118129

130+
model_config = ConfigDict(arbitrary_types_allowed=True)
119131
bin_edges: np.ndarray = Field(default_factory=lambda: np.zeros(11), description="")
120132
probabilities: np.ndarray = Field(
121133
default_factory=lambda: np.zeros(10), description=""
122134
)
123135

124-
class Config:
125-
arbitrary_types_allowed = True
126-
127136

128137
class HazardEventDistrib(BaseModel):
129138
"""Intensity curve of an acute hazard."""
130139

140+
model_config = ConfigDict(arbitrary_types_allowed=True)
131141
intensity_bin_edges: np.ndarray = Field(
132142
default_factory=lambda: np.zeros(10), description=""
133143
)
@@ -136,13 +146,11 @@ class HazardEventDistrib(BaseModel):
136146
)
137147
path: List[str] = Field([], description="Path to the hazard indicator data source.")
138148

139-
class Config:
140-
arbitrary_types_allowed = True
141-
142149

143150
class VulnerabilityCurve(BaseModel):
144151
"""Defines a damage or disruption curve."""
145152

153+
model_config = ConfigDict(arbitrary_types_allowed=True)
146154
asset_type: str = Field(...)
147155
location: str = Field(...)
148156
event_type: str = Field(description="hazard event type, e.g. RiverineInundation")
@@ -156,9 +164,6 @@ class VulnerabilityCurve(BaseModel):
156164
description="standard deviation of impact (damage or disruption)"
157165
)
158166

159-
class Config:
160-
arbitrary_types_allowed = True
161-
162167

163168
class VulnerabilityCurves(BaseModel):
164169
"""List of VulnerabilityCurve."""
@@ -169,6 +174,7 @@ class VulnerabilityCurves(BaseModel):
169174
class VulnerabilityDistrib(BaseModel):
170175
"""Defines a vulnerability matrix."""
171176

177+
model_config = ConfigDict(arbitrary_types_allowed=True)
172178
intensity_bin_edges: np.ndarray = Field(
173179
default_factory=lambda: np.zeros(10), description=""
174180
)
@@ -178,6 +184,3 @@ class VulnerabilityDistrib(BaseModel):
178184
prob_matrix: np.ndarray = Field(
179185
default_factory=lambda: np.zeros(10), description=""
180186
)
181-
182-
class Config:
183-
arbitrary_types_allowed = True

src/physrisk/api/v1/exposure_req_resp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ class AssetExposureRequest(BaseModel):
1111

1212
assets: Assets
1313
calc_settings: CalcSettings = Field(
14-
default_factory=CalcSettings,
15-
description="Interpolation method.", # type:ignore
14+
default_factory=CalcSettings, # type:ignore
15+
description="Interpolation method.",
1616
)
1717
scenario: str = Field("rcp8p5", description="Name of scenario ('rcp8p5')")
1818
year: int = Field(

src/physrisk/api/v1/impact_req_resp.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from enum import Enum
22
from typing import Dict, List, NamedTuple, Optional, Sequence
33

4-
from pydantic import BaseModel, Field, computed_field
4+
from pydantic import BaseModel, ConfigDict, Field, computed_field
55

66
from physrisk.api.v1.common import (
77
Assets,
@@ -10,6 +10,7 @@
1010
VulnerabilityDistrib,
1111
)
1212
from physrisk.api.v1.hazard_data import Scenario
13+
from physrisk.api.v1.common import UseCaseId
1314

1415

1516
class CalcSettings(BaseModel):
@@ -24,8 +25,8 @@ class AssetImpactRequest(BaseModel):
2425

2526
assets: Assets
2627
calc_settings: CalcSettings = Field(
27-
default_factory=CalcSettings,
28-
description="Interpolation method.", # type:ignore
28+
default_factory=CalcSettings, # type:ignore
29+
description="Interpolation method.",
2930
)
3031
include_asset_level: bool = Field(
3132
True, description="If true, include asset-level impacts."
@@ -36,9 +37,13 @@ class AssetImpactRequest(BaseModel):
3637
include_calc_details: bool = Field(
3738
True, description="If true, include impact calculation details."
3839
)
39-
use_case_id: str = Field(
40-
"",
41-
description="Identifier for 'use case' used in the risk measures calculation.",
40+
use_case_id: UseCaseId = Field(
41+
UseCaseId.DEFAULT,
42+
description=(
43+
"Enumeration to select the 'use case' identifier"
44+
"used in vulnerability models and risk measures calculations."
45+
"Options: DEFAULT, STRESSTEST, GENERIC"
46+
),
4247
)
4348
provider_max_requests: Dict[str, int] = Field(
4449
{},
@@ -70,6 +75,8 @@ class Category(int, Enum):
7075
HIGH = 3
7176
REDFLAG = 4
7277

78+
NORISK = -1
79+
7380

7481
class RiskMeasureDefinition(BaseModel):
7582
measure_id: str = Field(None, description="Identifier for the risk measure.")
@@ -95,7 +102,7 @@ class RiskScoreValue(BaseModel):
95102
)
96103

97104

98-
class ScoreBasedRiskMeasureDefinition(BaseModel, frozen=True):
105+
class ScoreBasedRiskMeasureDefinition(BaseModel):
99106
hazard_types: List[str] = Field(
100107
[], description="Defines the hazards that the measure is used for."
101108
)
@@ -111,7 +118,7 @@ class ScoreBasedRiskMeasureDefinition(BaseModel, frozen=True):
111118
# [], description="The identifiers of the underlying risk measures from which the scores are inferred."
112119
# )
113120

114-
# should be sufficient to pass frozen=True, but does not seem to work (pydantic docs says feature in beta)
121+
# It is not enough to pass frozen=True, since not all attributes are hashable
115122
def __hash__(self):
116123
return id(self)
117124

@@ -166,6 +173,7 @@ class ImpactKey(BaseModel):
166173
class AssetSingleImpact(BaseModel):
167174
"""Impact at level of single asset and single type of hazard."""
168175

176+
model_config = ConfigDict(arbitrary_types_allowed=True)
169177
key: ImpactKey
170178

171179
@computed_field # deprecated: use key instead
@@ -191,9 +199,6 @@ def year(self) -> str:
191199
description="""Details of impact calculation for acute hazard calculations.""",
192200
)
193201

194-
class Config:
195-
arbitrary_types_allowed = True
196-
197202

198203
class AssetLevelImpact(BaseModel):
199204
"""Impact at asset level. Each asset can have impacts for multiple hazard types."""

src/physrisk/container.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@
22

33
from dependency_injector import containers, providers
44

5+
from physrisk.kernel import calculation as calc
56
from physrisk.data.hazard_data_provider import SourcePath
67
from physrisk.data.inventory import EmbeddedInventory
78
from physrisk.data.inventory_reader import InventoryReader
89
from physrisk.data.pregenerated_hazard_model import ZarrHazardModel
910
from physrisk.data.zarr_reader import ZarrReader
10-
from physrisk.kernel import calculation as calc
1111
from physrisk.kernel.hazard_model import HazardModelFactory
12-
from physrisk.kernel.vulnerability_model import (
13-
DictBasedVulnerabilityModels,
14-
VulnerabilityModels,
15-
VulnerabilityModelsFactory,
16-
)
12+
from physrisk.kernel.vulnerability_model import DictBasedVulnerabilityModelsFactory
1713
from physrisk.requests import Requester, _create_inventory, create_source_paths
1814

1915

@@ -41,11 +37,6 @@ def hazard_model(
4137
)
4238

4339

44-
class DictBasedVulnerabilityModelsFactory(VulnerabilityModelsFactory):
45-
def vulnerability_models(self) -> VulnerabilityModels:
46-
return DictBasedVulnerabilityModels(calc.get_default_vulnerability_models())
47-
48-
4940
class Container(containers.DeclarativeContainer):
5041
config = providers.Configuration(default={"zarr_sources": ["embedded", "hazard"]})
5142

src/physrisk/data/inventory.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99

1010
import physrisk.data.colormap_provider as colormap_provider
1111
import physrisk.data.static.hazard
12+
from physrisk.api.v1.hazard_data import HazardResource, Period
1213
from physrisk.data.inventory_reader import HazardModels
1314

14-
from ..api.v1.hazard_data import HazardResource, Period
15-
1615
# from physrisk.kernel.hazards import ChronicHeat
1716

1817

0 commit comments

Comments
 (0)