openepd 6.13.2__py3-none-any.whl → 7.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openepd/__init__.py +0 -6
- openepd/__version__.py +1 -1
- openepd/api/average_dataset/generic_estimate_sync_api.py +11 -10
- openepd/api/average_dataset/industry_epd_sync_api.py +9 -8
- openepd/api/base_sync_client.py +53 -9
- openepd/api/category/sync_api.py +1 -1
- openepd/api/dto/base.py +4 -4
- openepd/api/dto/common.py +24 -16
- openepd/api/dto/meta.py +15 -11
- openepd/api/dto/mf.py +9 -8
- openepd/api/epd/dto.py +43 -33
- openepd/api/epd/sync_api.py +9 -9
- openepd/api/pcr/sync_api.py +2 -2
- openepd/bundle/model.py +11 -10
- openepd/bundle/reader.py +12 -5
- openepd/bundle/writer.py +17 -6
- openepd/model/base.py +61 -44
- openepd/model/category.py +13 -10
- openepd/model/common.py +107 -59
- openepd/model/declaration.py +93 -64
- openepd/model/epd.py +51 -43
- openepd/model/generic_estimate.py +28 -13
- openepd/model/industry_epd.py +15 -9
- openepd/model/lcia.py +161 -136
- openepd/model/org.py +70 -37
- openepd/model/pcr.py +38 -32
- openepd/model/specs/asphalt.py +31 -22
- openepd/model/specs/base.py +14 -11
- openepd/model/specs/concrete.py +60 -39
- openepd/model/specs/range/aggregates.py +9 -9
- openepd/model/specs/range/aluminium.py +7 -7
- openepd/model/specs/range/asphalt.py +22 -19
- openepd/model/specs/range/cladding.py +16 -16
- openepd/model/specs/range/cmu.py +10 -9
- openepd/model/specs/range/concrete.py +36 -27
- openepd/model/specs/range/conveying_equipment.py +16 -15
- openepd/model/specs/range/electrical.py +24 -22
- openepd/model/specs/range/finishes.py +109 -104
- openepd/model/specs/range/fire_and_smoke_protection.py +7 -7
- openepd/model/specs/range/furnishings.py +16 -12
- openepd/model/specs/range/manufacturing_inputs.py +16 -16
- openepd/model/specs/range/masonry.py +16 -16
- openepd/model/specs/range/mechanical.py +47 -47
- openepd/model/specs/range/mechanical_insulation.py +7 -7
- openepd/model/specs/range/network_infrastructure.py +54 -46
- openepd/model/specs/range/openings.py +36 -31
- openepd/model/specs/range/plumbing.py +15 -13
- openepd/model/specs/range/precast_concrete.py +20 -16
- openepd/model/specs/range/sheathing.py +18 -18
- openepd/model/specs/range/steel.py +27 -25
- openepd/model/specs/range/thermal_moisture_protection.py +20 -20
- openepd/model/specs/range/utility_piping.py +9 -9
- openepd/model/specs/range/wood.py +19 -19
- openepd/model/specs/range/wood_joists.py +8 -8
- openepd/model/specs/singular/__init__.py +9 -5
- openepd/model/specs/singular/aggregates.py +22 -15
- openepd/model/specs/singular/aluminium.py +20 -5
- openepd/model/specs/singular/asphalt.py +44 -20
- openepd/model/specs/singular/cladding.py +38 -23
- openepd/model/specs/singular/cmu.py +26 -11
- openepd/model/specs/singular/common.py +3 -2
- openepd/model/specs/singular/concrete.py +85 -48
- openepd/model/specs/singular/conveying_equipment.py +30 -17
- openepd/model/specs/singular/deprecated/__init__.py +3 -2
- openepd/model/specs/singular/deprecated/concrete.py +68 -33
- openepd/model/specs/singular/deprecated/steel.py +28 -15
- openepd/model/specs/singular/electrical.py +69 -41
- openepd/model/specs/singular/finishes.py +250 -140
- openepd/model/specs/singular/fire_and_smoke_protection.py +9 -6
- openepd/model/specs/singular/furnishings.py +16 -14
- openepd/model/specs/singular/manufacturing_inputs.py +23 -14
- openepd/model/specs/singular/masonry.py +66 -21
- openepd/model/specs/singular/mechanical.py +48 -47
- openepd/model/specs/singular/mechanical_insulation.py +7 -6
- openepd/model/specs/singular/mixins/conduit_mixin.py +13 -10
- openepd/model/specs/singular/network_infrastructure.py +111 -52
- openepd/model/specs/singular/openings.py +127 -95
- openepd/model/specs/singular/plumbing.py +15 -12
- openepd/model/specs/singular/precast_concrete.py +68 -54
- openepd/model/specs/singular/sheathing.py +47 -27
- openepd/model/specs/singular/steel.py +69 -45
- openepd/model/specs/singular/thermal_moisture_protection.py +36 -20
- openepd/model/specs/singular/utility_piping.py +11 -8
- openepd/model/specs/singular/wood.py +48 -24
- openepd/model/specs/singular/wood_joists.py +19 -6
- openepd/model/standard.py +15 -8
- openepd/model/validation/common.py +9 -3
- openepd/model/validation/numbers.py +0 -13
- openepd/model/validation/quantity.py +88 -55
- openepd/model/versioning.py +9 -6
- {openepd-6.13.2.dist-info → openepd-7.0.1.dist-info}/METADATA +2 -2
- openepd-7.0.1.dist-info/RECORD +141 -0
- openepd/compat/__init__.py +0 -15
- openepd/compat/compat_functional_validators.py +0 -25
- openepd/compat/pydantic.py +0 -30
- openepd/patch_pydantic.py +0 -108
- openepd-6.13.2.dist-info/RECORD +0 -145
- {openepd-6.13.2.dist-info → openepd-7.0.1.dist-info}/LICENSE +0 -0
- {openepd-6.13.2.dist-info → openepd-7.0.1.dist-info}/WHEEL +0 -0
openepd/model/lcia.py
CHANGED
@@ -16,7 +16,10 @@
|
|
16
16
|
from enum import StrEnum
|
17
17
|
from typing import Any, ClassVar
|
18
18
|
|
19
|
-
|
19
|
+
import pydantic
|
20
|
+
from pydantic import ConfigDict
|
21
|
+
from typing_extensions import Self
|
22
|
+
|
20
23
|
from openepd.model.base import BaseOpenEpdSchema
|
21
24
|
from openepd.model.common import Measurement
|
22
25
|
from openepd.model.validation.quantity import ExternalValidationConfig
|
@@ -30,34 +33,34 @@ class EolScenario(BaseOpenEpdSchema):
|
|
30
33
|
otherwise the outcomes can be statistically combined.
|
31
34
|
"""
|
32
35
|
|
33
|
-
name: str =
|
36
|
+
name: str = pydantic.Field(
|
34
37
|
max_length=40,
|
35
|
-
|
38
|
+
examples=["Landfill"],
|
36
39
|
description="A brief text description of the scenario, preferably from list eol_scenario_names",
|
37
40
|
)
|
38
|
-
likelihood: float | None =
|
41
|
+
likelihood: float | None = pydantic.Field(
|
39
42
|
description="The weigting of this scenario used in the C1 .. C4 dataset. For example, the overall C1 shoudl be "
|
40
43
|
"equal to weighted sum of C1 from all the scenarios, weighted by likelihood.",
|
41
|
-
|
44
|
+
examples=[0.33],
|
42
45
|
default=None,
|
43
46
|
)
|
44
|
-
C1: Measurement | None =
|
47
|
+
C1: Measurement | None = pydantic.Field(
|
45
48
|
description="Deconstruction and Demolition under this scenario",
|
46
49
|
default=None,
|
47
50
|
)
|
48
|
-
C2: Measurement | None =
|
51
|
+
C2: Measurement | None = pydantic.Field(
|
49
52
|
description="Transport to waste processing or disposal under this scenario.",
|
50
53
|
default=None,
|
51
54
|
)
|
52
|
-
C3: Measurement | None =
|
55
|
+
C3: Measurement | None = pydantic.Field(
|
53
56
|
description="Waste Processing under this scenario",
|
54
57
|
default=None,
|
55
58
|
)
|
56
|
-
C4: Measurement | None =
|
59
|
+
C4: Measurement | None = pydantic.Field(
|
57
60
|
description="Disposal under this scenario",
|
58
61
|
default=None,
|
59
62
|
)
|
60
|
-
D: Measurement | None =
|
63
|
+
D: Measurement | None = pydantic.Field(
|
61
64
|
description="Potential net benefits from reuse, recycling, and/or energy recovery beyond "
|
62
65
|
"the system boundary under this scenario",
|
63
66
|
default=None,
|
@@ -74,35 +77,35 @@ class ScopeSet(BaseOpenEpdSchema):
|
|
74
77
|
|
75
78
|
allowed_units: ClassVar[str | tuple[str, ...] | None] = None
|
76
79
|
|
77
|
-
A1A2A3: Measurement | None =
|
80
|
+
A1A2A3: Measurement | None = pydantic.Field(
|
78
81
|
description="Sum of A1..A3",
|
79
82
|
default=None,
|
80
83
|
)
|
81
|
-
A1: Measurement | None =
|
84
|
+
A1: Measurement | None = pydantic.Field(
|
82
85
|
description="Raw Material Supply",
|
83
86
|
default=None,
|
84
87
|
)
|
85
|
-
A2: Measurement | None =
|
88
|
+
A2: Measurement | None = pydantic.Field(
|
86
89
|
description="Transport to Manufacturing",
|
87
90
|
default=None,
|
88
91
|
)
|
89
|
-
A3: Measurement | None =
|
92
|
+
A3: Measurement | None = pydantic.Field(
|
90
93
|
description="Manufacturing",
|
91
94
|
default=None,
|
92
95
|
)
|
93
|
-
A4: Measurement | None =
|
96
|
+
A4: Measurement | None = pydantic.Field(
|
94
97
|
description="Transport to Construction",
|
95
98
|
default=None,
|
96
99
|
)
|
97
|
-
A5: Measurement | None =
|
100
|
+
A5: Measurement | None = pydantic.Field(
|
98
101
|
description="Construction",
|
99
102
|
default=None,
|
100
103
|
)
|
101
|
-
B1: Measurement | None =
|
104
|
+
B1: Measurement | None = pydantic.Field(
|
102
105
|
description="Use impacts over Reference Service Life (Predicted)",
|
103
106
|
default=None,
|
104
107
|
)
|
105
|
-
B1_years: float | None =
|
108
|
+
B1_years: float | None = pydantic.Field(
|
106
109
|
gt=0,
|
107
110
|
lt=100,
|
108
111
|
description="Timeframe over which B1 is evaluated, in years. "
|
@@ -110,103 +113,106 @@ class ScopeSet(BaseOpenEpdSchema):
|
|
110
113
|
"B1_years=1.0 or B1=12.3kgCO2e, B1_years=10.0",
|
111
114
|
default=None,
|
112
115
|
)
|
113
|
-
B2: Measurement | None =
|
116
|
+
B2: Measurement | None = pydantic.Field(
|
114
117
|
description="Predicted Maintenance Impacts over Reference Service Life",
|
115
118
|
default=None,
|
116
119
|
)
|
117
|
-
B2_years: float | None =
|
120
|
+
B2_years: float | None = pydantic.Field(
|
118
121
|
gt=0,
|
119
122
|
lt=100,
|
120
123
|
description="Predicted Maintenance Impacts over Reference Service Life",
|
121
124
|
default=None,
|
122
125
|
)
|
123
|
-
B3: Measurement | None =
|
126
|
+
B3: Measurement | None = pydantic.Field(
|
124
127
|
description="Predicted Repair impacts over Reference Service Life",
|
125
128
|
default=None,
|
126
129
|
)
|
127
|
-
B3_years: float | None =
|
130
|
+
B3_years: float | None = pydantic.Field(
|
128
131
|
gt=0,
|
129
132
|
lt=100,
|
130
133
|
description="Timeframe over which B3 is evaluated, in years",
|
131
134
|
default=None,
|
132
135
|
)
|
133
|
-
B4: Measurement | None =
|
136
|
+
B4: Measurement | None = pydantic.Field(
|
134
137
|
description="Predicted Replacement Impacts over the Building lifetime "
|
135
138
|
"('Estimated Construction Works lifespan') specified in the PCR.",
|
136
139
|
default=None,
|
137
140
|
)
|
138
|
-
B4_years: float | None =
|
141
|
+
B4_years: float | None = pydantic.Field(
|
139
142
|
gt=0,
|
140
143
|
lt=100,
|
141
144
|
description="Timeframe over which B4 is evaluated, in years",
|
142
145
|
default=None,
|
143
146
|
)
|
144
|
-
B5: Measurement | None =
|
147
|
+
B5: Measurement | None = pydantic.Field(
|
145
148
|
description="Predicted Refurbishment Impacts over the Building lifetime "
|
146
149
|
"('Estimated Construction Works lifespan') specified in the PCR.",
|
147
150
|
default=None,
|
148
151
|
)
|
149
|
-
B5_years: float | None =
|
152
|
+
B5_years: float | None = pydantic.Field(
|
150
153
|
gt=0,
|
151
154
|
lt=100,
|
152
155
|
description="Timeframe over which B5 is evaluated, in years",
|
153
156
|
default=None,
|
154
157
|
)
|
155
|
-
B6: Measurement | None =
|
158
|
+
B6: Measurement | None = pydantic.Field(
|
156
159
|
description="Predicted Impacts related to Operational Energy Use",
|
157
160
|
default=None,
|
158
161
|
)
|
159
|
-
B6_years: float | None =
|
162
|
+
B6_years: float | None = pydantic.Field(
|
160
163
|
gt=0,
|
161
164
|
lt=100,
|
162
165
|
description="Timeframe over which B6 is evaluated, in years",
|
163
166
|
default=None,
|
164
167
|
)
|
165
|
-
B7: Measurement | None =
|
168
|
+
B7: Measurement | None = pydantic.Field(
|
166
169
|
description="Predicted Impacts related to Operational Water Use",
|
167
170
|
default=None,
|
168
171
|
)
|
169
|
-
B7_years: float | None =
|
172
|
+
B7_years: float | None = pydantic.Field(
|
170
173
|
gt=0,
|
171
174
|
lt=100,
|
172
175
|
description="Timeframe over which B7 is evaluated, in years",
|
173
176
|
default=None,
|
174
177
|
)
|
175
|
-
C_scenarios: list[EolScenario] | None =
|
178
|
+
C_scenarios: list[EolScenario] | None = pydantic.Field(
|
176
179
|
description="A list of possible end-of-life scenarios, "
|
177
180
|
"for use in analyses where the end-of-life can be predicted.",
|
178
181
|
default=None,
|
179
182
|
)
|
180
|
-
C1: Measurement | None =
|
183
|
+
C1: Measurement | None = pydantic.Field(
|
181
184
|
description="Deconstruction and Demolition",
|
182
185
|
default=None,
|
183
186
|
)
|
184
|
-
C2: Measurement | None =
|
187
|
+
C2: Measurement | None = pydantic.Field(
|
185
188
|
description="Transport to waste processing or disposal.",
|
186
189
|
default=None,
|
187
190
|
)
|
188
|
-
C3: Measurement | None =
|
191
|
+
C3: Measurement | None = pydantic.Field(
|
189
192
|
description="Waste Processing",
|
190
193
|
default=None,
|
191
194
|
)
|
192
|
-
C4: Measurement | None =
|
195
|
+
C4: Measurement | None = pydantic.Field(
|
193
196
|
description="Disposal",
|
194
197
|
default=None,
|
195
198
|
)
|
196
|
-
D: Measurement | None =
|
199
|
+
D: Measurement | None = pydantic.Field(
|
197
200
|
default=None,
|
198
201
|
description="Potential net benefits from reuse, recycling, and/or energy recovery beyond the system boundary.",
|
199
202
|
)
|
200
203
|
|
201
|
-
|
202
|
-
|
204
|
+
model_config = pydantic.ConfigDict(from_attributes=True)
|
205
|
+
|
206
|
+
@pydantic.model_validator(mode="after")
|
207
|
+
def _unit_validator(self) -> Self:
|
203
208
|
all_units = set()
|
204
209
|
|
205
|
-
for k
|
210
|
+
for k in self.model_fields:
|
211
|
+
v = getattr(self, k, None)
|
206
212
|
if isinstance(v, Measurement):
|
207
213
|
all_units.add(v.unit)
|
208
214
|
|
209
|
-
if not
|
215
|
+
if not self.allowed_units:
|
210
216
|
# For unknown units - only units should be the same across all measurements (textually)
|
211
217
|
if len(all_units) > 1:
|
212
218
|
raise ValueError("All scopes and measurements should be expressed in the same unit.")
|
@@ -219,9 +225,9 @@ class ScopeSet(BaseOpenEpdSchema):
|
|
219
225
|
ExternalValidationConfig.QUANTITY_VALIDATOR.validate_same_dimensionality(first, unit)
|
220
226
|
|
221
227
|
# can correctly validate unit
|
222
|
-
if
|
228
|
+
if self.allowed_units is not None and len(all_units) == 1 and ExternalValidationConfig.QUANTITY_VALIDATOR:
|
223
229
|
unit = next(iter(all_units))
|
224
|
-
allowed_units =
|
230
|
+
allowed_units = self.allowed_units if isinstance(self.allowed_units, tuple) else (self.allowed_units,)
|
225
231
|
|
226
232
|
matched_unit = False
|
227
233
|
for allowed_unit in allowed_units:
|
@@ -235,7 +241,7 @@ class ScopeSet(BaseOpenEpdSchema):
|
|
235
241
|
f"'{', '.join(allowed_units)}' is only allowed unit for this scopeset. Provided '{unit}'"
|
236
242
|
)
|
237
243
|
|
238
|
-
return
|
244
|
+
return self
|
239
245
|
|
240
246
|
|
241
247
|
class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
|
@@ -273,11 +279,11 @@ class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
|
|
273
279
|
# probably unknown impact, coming from 'extra' fields
|
274
280
|
return getattr(self, name, None)
|
275
281
|
|
276
|
-
@
|
282
|
+
@pydantic.model_validator(mode="before")
|
277
283
|
def _extra_scopeset_validator(cls, values: dict[str, Any]) -> dict[str, Any]:
|
278
284
|
for f in values:
|
279
285
|
# only interested in validating the extra fields
|
280
|
-
if f in cls.
|
286
|
+
if f in cls.model_fields:
|
281
287
|
continue
|
282
288
|
|
283
289
|
# extra impact of an unknown type - engage validation of ScopeSet
|
@@ -296,125 +302,132 @@ class ScopesetByNameBase(BaseOpenEpdSchema, extra="allow"):
|
|
296
302
|
class ScopeSetGwp(ScopeSet):
|
297
303
|
"""ScopeSet measured in kgCO2e."""
|
298
304
|
|
299
|
-
allowed_units = "kgCO2e"
|
305
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgCO2e"
|
300
306
|
|
301
307
|
|
302
308
|
class ScopeSetOdp(ScopeSet):
|
303
309
|
"""ScopeSet measured in kgCFC11e."""
|
304
310
|
|
305
|
-
allowed_units = "kgCFC11e"
|
311
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgCFC11e"
|
306
312
|
|
307
313
|
|
308
314
|
class ScopeSetAp(ScopeSet):
|
309
315
|
"""ScopeSet measured in kgSO2e."""
|
310
316
|
|
311
|
-
allowed_units = ("kgSO2e", "molHe")
|
317
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = ("kgSO2e", "molHe")
|
312
318
|
|
313
319
|
|
314
320
|
class ScopeSetEpNe(ScopeSet):
|
315
321
|
"""ScopeSet measured in kgNe."""
|
316
322
|
|
317
|
-
allowed_units = "kgNe"
|
323
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgNe"
|
318
324
|
|
319
325
|
|
320
326
|
class ScopeSetPocp(ScopeSet):
|
321
327
|
"""ScopeSet measured in kgO3e."""
|
322
328
|
|
323
|
-
allowed_units = ("kgO3e", "kgNMVOCe")
|
329
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = ("kgO3e", "kgNMVOCe")
|
324
330
|
|
325
331
|
|
326
332
|
class ScopeSetEpFresh(ScopeSet):
|
327
333
|
"""ScopeSet measured in kgPO4e."""
|
328
334
|
|
329
|
-
allowed_units = "kgPO4e"
|
335
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kgPO4e"
|
330
336
|
|
331
337
|
|
332
338
|
class ScopeSetEpTerr(ScopeSet):
|
333
339
|
"""ScopeSet measured in molNe."""
|
334
340
|
|
335
|
-
allowed_units = "molNe"
|
341
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "molNe"
|
336
342
|
|
337
343
|
|
338
344
|
class ScopeSetIrp(ScopeSet):
|
339
345
|
"""ScopeSet measured in kilo Becquerel equivalent of u235."""
|
340
346
|
|
341
|
-
allowed_units = "kBqU235e"
|
347
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kBqU235e"
|
342
348
|
|
343
349
|
|
344
350
|
class ScopeSetCTUh(ScopeSet):
|
345
351
|
"""ScopeSet measured in CTUh."""
|
346
352
|
|
347
|
-
allowed_units = "CTUh"
|
353
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "CTUh"
|
348
354
|
|
349
355
|
|
350
356
|
class ScopeSetM3Aware(ScopeSet):
|
351
357
|
"""ScopeSet measured in m3AWARE Water consumption by AWARE method."""
|
352
358
|
|
353
|
-
allowed_units = "m3AWARE"
|
359
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "m3AWARE"
|
354
360
|
|
355
361
|
|
356
362
|
class ScopeSetCTUe(ScopeSet):
|
357
363
|
"""ScopeSet measured in CTUe."""
|
358
364
|
|
359
|
-
allowed_units = "CTUe"
|
365
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "CTUe"
|
360
366
|
|
361
367
|
|
362
368
|
class ScopeSetDiseaseIncidence(ScopeSet):
|
363
369
|
"""ScopeSet measuring disease incidence measured in AnnualPerCapita (cases)."""
|
364
370
|
|
365
|
-
allowed_units = "AnnualPerCapita"
|
371
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "AnnualPerCapita"
|
366
372
|
|
367
373
|
|
368
374
|
class ScopeSetMass(ScopeSet):
|
369
375
|
"""ScopeSet measuring mass in kg."""
|
370
376
|
|
371
|
-
allowed_units = "kg"
|
377
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "kg"
|
372
378
|
|
373
379
|
|
374
380
|
class ScopeSetVolume(ScopeSet):
|
375
381
|
"""ScopeSet measuring mass in kg."""
|
376
382
|
|
377
|
-
allowed_units = "m3"
|
383
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "m3"
|
378
384
|
|
379
385
|
|
380
386
|
class ScopeSetMassOrVolume(ScopeSet):
|
381
387
|
"""ScopeSet measuring mass in kg OR volume in m3, example: radioactive waste."""
|
382
388
|
|
383
|
-
allowed_units = ("kg", "m3")
|
389
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = ("kg", "m3")
|
384
390
|
|
385
391
|
|
386
392
|
class ScopeSetEnergy(ScopeSet):
|
387
393
|
"""ScopeSet measuring mass in kg."""
|
388
394
|
|
389
|
-
allowed_units = "MJ"
|
395
|
+
allowed_units: ClassVar[str | tuple[str, ...] | None] = "MJ"
|
390
396
|
|
391
397
|
|
392
398
|
class ImpactSet(ScopesetByNameBase):
|
393
399
|
"""A set of impacts, such as GWP, ODP, AP, EP, POCP, EP-marine, EP-terrestrial, EP-freshwater, etc."""
|
394
400
|
|
395
|
-
gwp: ScopeSetGwp | None =
|
401
|
+
gwp: ScopeSetGwp | None = pydantic.Field(
|
396
402
|
default=None,
|
397
403
|
description="GWP100, calculated per IPCC guidelines. If any CO2 removals are "
|
398
404
|
"part of this figure, the gwp-fossil, gwp-bioganic, gwp-luluc, an "
|
399
405
|
"gwp-nonCO2 fields are required, as is "
|
400
406
|
"kg_C_biogenic_per_declared_unit.",
|
401
407
|
)
|
402
|
-
odp: ScopeSetOdp | None =
|
403
|
-
ap: ScopeSetAp | None =
|
404
|
-
ep: ScopeSetEpNe | None =
|
405
|
-
default=None,
|
408
|
+
odp: ScopeSetOdp | None = pydantic.Field(default=None, description="Ozone Depletion Potential")
|
409
|
+
ap: ScopeSetAp | None = pydantic.Field(default=None, description="Acidification Potential")
|
410
|
+
ep: ScopeSetEpNe | None = pydantic.Field(
|
411
|
+
default=None,
|
412
|
+
description="Eutrophication Potential in Marine Ecosystems. Has the same meaning as ep-marine.",
|
413
|
+
)
|
414
|
+
pocp: ScopeSetPocp | None = pydantic.Field(
|
415
|
+
default=None, description="Photochemical Smog (Ozone) creation potential"
|
406
416
|
)
|
407
|
-
|
408
|
-
ep_marine: ScopeSetEpNe | None = pyd.Field(
|
417
|
+
ep_marine: ScopeSetEpNe | None = pydantic.Field(
|
409
418
|
alias="ep-marine", default=None, description="Has the same meaning as 'ep'"
|
410
419
|
)
|
411
|
-
ep_fresh: ScopeSetEpFresh | None =
|
412
|
-
alias="ep-fresh",
|
420
|
+
ep_fresh: ScopeSetEpFresh | None = pydantic.Field(
|
421
|
+
alias="ep-fresh",
|
422
|
+
default=None,
|
423
|
+
description="Eutrophication Potential in Freshwater Ecosystems",
|
413
424
|
)
|
414
|
-
ep_terr: ScopeSetEpTerr | None =
|
415
|
-
alias="ep-terr",
|
425
|
+
ep_terr: ScopeSetEpTerr | None = pydantic.Field(
|
426
|
+
alias="ep-terr",
|
427
|
+
default=None,
|
428
|
+
description="Eutrophication Potential in Terrestrial Ecosystems",
|
416
429
|
)
|
417
|
-
gwp_biogenic: ScopeSetGwp | None =
|
430
|
+
gwp_biogenic: ScopeSetGwp | None = pydantic.Field(
|
418
431
|
alias="gwp-biogenic",
|
419
432
|
default=None,
|
420
433
|
description="Net GWP from removals of atmospheric CO2 into biomass and emissions of CO2 from biomass sources. "
|
@@ -423,7 +436,7 @@ class ImpactSet(ScopesetByNameBase):
|
|
423
436
|
"space (similar biome). They must not have been sold, committed, or credited to any other "
|
424
437
|
"product. Harvesting from native forests is handled under GWP_luluc for EN15804.",
|
425
438
|
)
|
426
|
-
gwp_luluc: ScopeSetGwp | None =
|
439
|
+
gwp_luluc: ScopeSetGwp | None = pydantic.Field(
|
427
440
|
alias="gwp-luluc",
|
428
441
|
default=None,
|
429
442
|
description="Climate change effects related to land use and land use change, for example biogenic carbon "
|
@@ -431,50 +444,52 @@ class ImpactSet(ScopesetByNameBase):
|
|
431
444
|
"emissions). All related emissions for native forests are included under this category. "
|
432
445
|
"Uptake for native forests is set to 0 kgCO2 for EN15804.",
|
433
446
|
)
|
434
|
-
gwp_nonCO2: ScopeSetGwp | None =
|
447
|
+
gwp_nonCO2: ScopeSetGwp | None = pydantic.Field(
|
435
448
|
alias="gwp-nonCO2",
|
436
449
|
default=None,
|
437
450
|
description="GWP from non-CO2, non-fossil sources, such as livestock-sourced CH4 and agricultural N2O.",
|
438
451
|
)
|
439
|
-
gwp_fossil: ScopeSetGwp | None =
|
452
|
+
gwp_fossil: ScopeSetGwp | None = pydantic.Field(
|
440
453
|
alias="gwp-fossil",
|
441
454
|
default=None,
|
442
455
|
description="Climate change effects due to greenhouse gas emissions originating from the oxidation or "
|
443
456
|
"reduction of fossil fuels or materials containing fossil carbon. [Source: EN15804]",
|
444
457
|
)
|
445
|
-
WDP: ScopeSetM3Aware | None =
|
458
|
+
WDP: ScopeSetM3Aware | None = pydantic.Field(
|
446
459
|
default=None,
|
447
460
|
description="Deprivation-weighted water consumption, calculated by the AWARE method "
|
448
461
|
"(https://wulca-waterlca.org/aware/what-is-aware)",
|
449
462
|
)
|
450
|
-
PM: ScopeSetDiseaseIncidence | None =
|
463
|
+
PM: ScopeSetDiseaseIncidence | None = pydantic.Field(
|
451
464
|
default=None,
|
452
465
|
description="Potential incidence of disease due to particulate matter emissions.",
|
453
466
|
)
|
454
|
-
IRP: ScopeSetIrp | None =
|
467
|
+
IRP: ScopeSetIrp | None = pydantic.Field(
|
455
468
|
default=None,
|
456
469
|
description="Potential ionizing radiation effect on human health, relative to U235.",
|
457
470
|
)
|
458
|
-
ETP_fw: ScopeSetCTUe | None =
|
471
|
+
ETP_fw: ScopeSetCTUe | None = pydantic.Field(
|
459
472
|
alias="ETP-fw",
|
460
473
|
default=None,
|
461
474
|
description="Ecotoxicity in freshwater, in potential Comparative Toxic Unit for ecosystems.",
|
462
475
|
)
|
463
|
-
HTP_c: ScopeSetCTUh | None =
|
476
|
+
HTP_c: ScopeSetCTUh | None = pydantic.Field(
|
464
477
|
alias="HTP-c",
|
465
478
|
default=None,
|
466
479
|
description="Human toxicity, cancer effects in potential Comparative Toxic Units for humans.",
|
467
480
|
)
|
468
|
-
HTP_nc: ScopeSetCTUh | None =
|
481
|
+
HTP_nc: ScopeSetCTUh | None = pydantic.Field(
|
469
482
|
alias="HTP-nc",
|
470
483
|
default=None,
|
471
484
|
description="Human toxicity, noncancer effects in potential Comparative Toxic Units for humans.",
|
472
485
|
)
|
473
|
-
SQP: ScopeSet | None =
|
486
|
+
SQP: ScopeSet | None = pydantic.Field(
|
474
487
|
default=None,
|
475
488
|
description="Land use related impacts / Soil quality, in potential soil quality parameters.",
|
476
489
|
)
|
477
490
|
|
491
|
+
model_config = pydantic.ConfigDict(from_attributes=True)
|
492
|
+
|
478
493
|
|
479
494
|
class LCIAMethod(StrEnum):
|
480
495
|
"""A list of available LCA methods."""
|
@@ -520,24 +535,31 @@ class LCIAMethod(StrEnum):
|
|
520
535
|
return cls.UNKNOWN
|
521
536
|
|
522
537
|
|
523
|
-
class Impacts(
|
538
|
+
class Impacts(pydantic.RootModel[dict[LCIAMethod, ImpactSet]]):
|
524
539
|
"""List of environmental impacts, compiled per one of the standard Impact Assessment methods."""
|
525
540
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
541
|
+
@staticmethod
|
542
|
+
def _update_schema_extra(schema, model):
|
543
|
+
schema.update(
|
544
|
+
{
|
545
|
+
"properties": {
|
546
|
+
str(lm): {
|
547
|
+
"description": str(lm),
|
548
|
+
# This is an internal representation of the reference which exists in Pydantic during
|
549
|
+
# generation process
|
550
|
+
"allOf": [{"$ref": "#/components/schemas/openepd__model__lcia__ImpactSet-Input__1"}],
|
551
|
+
}
|
552
|
+
for lm in LCIAMethod
|
553
|
+
},
|
554
|
+
"additionalProperties": None,
|
555
|
+
}
|
556
|
+
)
|
557
|
+
|
558
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(json_schema_extra=_update_schema_extra)
|
537
559
|
|
538
560
|
def set_unknown_lcia(self, impact_set: ImpactSet):
|
539
561
|
"""Set the impact set as an unknown LCIA method."""
|
540
|
-
self.
|
562
|
+
self.root[LCIAMethod.UNKNOWN] = impact_set
|
541
563
|
|
542
564
|
def set_impact_set(self, lcia_method: LCIAMethod | str | None, impact_set: ImpactSet):
|
543
565
|
"""
|
@@ -550,7 +572,7 @@ class Impacts(pyd.BaseModel):
|
|
550
572
|
else:
|
551
573
|
if isinstance(lcia_method, str):
|
552
574
|
lcia_method = LCIAMethod.get_by_name(lcia_method)
|
553
|
-
self.
|
575
|
+
self.root[lcia_method] = impact_set
|
554
576
|
|
555
577
|
def replace_lcia_method(self, lcia_method: LCIAMethod, new_lcia_method: LCIAMethod) -> None:
|
556
578
|
"""
|
@@ -562,95 +584,95 @@ class Impacts(pyd.BaseModel):
|
|
562
584
|
if impact_set is None:
|
563
585
|
return None
|
564
586
|
self.set_impact_set(new_lcia_method, impact_set)
|
565
|
-
del self.
|
587
|
+
del self.root[lcia_method]
|
566
588
|
|
567
589
|
def get_impact_set(
|
568
590
|
self, lcia_method: LCIAMethod | str | None, default_val: ImpactSet | None = None
|
569
591
|
) -> ImpactSet | None:
|
570
592
|
"""Return the impact set for the given LCIA method."""
|
571
593
|
if lcia_method is None:
|
572
|
-
return self.
|
594
|
+
return self.root.get(LCIAMethod.UNKNOWN, default_val)
|
573
595
|
if isinstance(lcia_method, str):
|
574
596
|
lcia_method = LCIAMethod.get_by_name(lcia_method)
|
575
|
-
return self.
|
597
|
+
return self.root.get(lcia_method, default_val)
|
576
598
|
|
577
599
|
def available_methods(self) -> set[LCIAMethod]:
|
578
600
|
"""Return a list of available LCIA methods."""
|
579
|
-
return set(self.
|
601
|
+
return set(self.root.keys())
|
580
602
|
|
581
603
|
def as_dict(self) -> dict[LCIAMethod, ImpactSet]:
|
582
604
|
"""Return the impacts as a dictionary."""
|
583
|
-
return self.
|
605
|
+
return self.root
|
584
606
|
|
585
607
|
|
586
608
|
class ResourceUseSet(ScopesetByNameBase):
|
587
609
|
"""A set of resource use indicators, such as RPRec, RPRm, etc."""
|
588
610
|
|
589
|
-
RPRec: ScopeSetEnergy | None =
|
611
|
+
RPRec: ScopeSetEnergy | None = pydantic.Field(
|
590
612
|
description="Renewable primary resources used as energy carrier (fuel). "
|
591
613
|
"First use bio-based materials used as an energy source. Hydropower, solar and wind power used "
|
592
614
|
"in the technosphere are also included in this indicator",
|
593
615
|
default=None,
|
594
616
|
)
|
595
|
-
RPRm: ScopeSetEnergy | None =
|
617
|
+
RPRm: ScopeSetEnergy | None = pydantic.Field(
|
596
618
|
description="Renewable primary resources with energy content used as material. "
|
597
619
|
"First use biobased materials used as materials (e.g. wood, hemp, etc.).",
|
598
620
|
default=None,
|
599
621
|
)
|
600
|
-
rpre: ScopeSetEnergy | None =
|
622
|
+
rpre: ScopeSetEnergy | None = pydantic.Field(
|
601
623
|
description="Renewable primary energy resources as energy",
|
602
624
|
default=None,
|
603
625
|
)
|
604
|
-
nrpre: ScopeSetEnergy | None =
|
626
|
+
nrpre: ScopeSetEnergy | None = pydantic.Field(
|
605
627
|
description="Non-renewable primary resources as energy (fuel)",
|
606
628
|
default=None,
|
607
629
|
)
|
608
|
-
nrprm: ScopeSetEnergy | None =
|
630
|
+
nrprm: ScopeSetEnergy | None = pydantic.Field(
|
609
631
|
description="Non-renewable primary resources as material",
|
610
632
|
default=None,
|
611
633
|
)
|
612
|
-
fw: ScopeSetVolume | None =
|
634
|
+
fw: ScopeSetVolume | None = pydantic.Field(
|
613
635
|
description="Use of net fresh water",
|
614
636
|
default=None,
|
615
637
|
)
|
616
|
-
sm: ScopeSetMass | None =
|
638
|
+
sm: ScopeSetMass | None = pydantic.Field(
|
617
639
|
description="Use of secondary materials",
|
618
640
|
default=None,
|
619
641
|
)
|
620
|
-
rsf: ScopeSetEnergy | None =
|
642
|
+
rsf: ScopeSetEnergy | None = pydantic.Field(
|
621
643
|
description="Use of renewable secondary materials",
|
622
644
|
default=None,
|
623
645
|
)
|
624
|
-
nrsf: ScopeSetEnergy | None =
|
646
|
+
nrsf: ScopeSetEnergy | None = pydantic.Field(
|
625
647
|
description="Use of non-renewable secondary fuels",
|
626
648
|
default=None,
|
627
649
|
)
|
628
|
-
re: ScopeSetEnergy | None =
|
650
|
+
re: ScopeSetEnergy | None = pydantic.Field(
|
629
651
|
description="Renewable energy resources",
|
630
652
|
default=None,
|
631
653
|
)
|
632
|
-
pere: ScopeSetEnergy | None =
|
654
|
+
pere: ScopeSetEnergy | None = pydantic.Field(
|
633
655
|
description="Use of renewable primary energy excluding renewable primary energy resources used as raw materials",
|
634
656
|
default=None,
|
635
657
|
)
|
636
|
-
perm: ScopeSetEnergy | None =
|
658
|
+
perm: ScopeSetEnergy | None = pydantic.Field(
|
637
659
|
description="Use of renewable primary energy resources used as raw materials",
|
638
660
|
default=None,
|
639
661
|
)
|
640
|
-
pert: ScopeSetEnergy | None =
|
662
|
+
pert: ScopeSetEnergy | None = pydantic.Field(
|
641
663
|
description="Total use of renewable primary energy resources",
|
642
664
|
default=None,
|
643
665
|
)
|
644
|
-
penre: ScopeSetEnergy | None =
|
666
|
+
penre: ScopeSetEnergy | None = pydantic.Field(
|
645
667
|
description="Use of non-renewable primary energy excluding "
|
646
668
|
"non-renewable primary energy resources used as raw materials",
|
647
669
|
default=None,
|
648
670
|
)
|
649
|
-
penrm: ScopeSetEnergy | None =
|
671
|
+
penrm: ScopeSetEnergy | None = pydantic.Field(
|
650
672
|
description="Use of non-renewable primary energy resources used as raw materials",
|
651
673
|
default=None,
|
652
674
|
)
|
653
|
-
penrt: ScopeSetEnergy | None =
|
675
|
+
penrt: ScopeSetEnergy | None = pydantic.Field(
|
654
676
|
description="Total use of non-renewable primary energy resources",
|
655
677
|
default=None,
|
656
678
|
)
|
@@ -659,51 +681,51 @@ class ResourceUseSet(ScopesetByNameBase):
|
|
659
681
|
class OutputFlowSet(ScopesetByNameBase):
|
660
682
|
"""A set of output flows, such as waste, emissions, etc."""
|
661
683
|
|
662
|
-
twd: ScopeSetMass | None =
|
684
|
+
twd: ScopeSetMass | None = pydantic.Field(
|
663
685
|
description="Total waste disposed",
|
664
686
|
default=None,
|
665
687
|
)
|
666
|
-
hwd: ScopeSetMass | None =
|
688
|
+
hwd: ScopeSetMass | None = pydantic.Field(
|
667
689
|
description="Hazardous waste disposed",
|
668
690
|
default=None,
|
669
691
|
)
|
670
|
-
nhwd: ScopeSetMass | None =
|
692
|
+
nhwd: ScopeSetMass | None = pydantic.Field(
|
671
693
|
description="Non-hazardous waste disposed",
|
672
694
|
default=None,
|
673
695
|
)
|
674
|
-
rwd: ScopeSetMass | None =
|
696
|
+
rwd: ScopeSetMass | None = pydantic.Field(
|
675
697
|
description="Radioactive waste disposed",
|
676
698
|
default=None,
|
677
699
|
)
|
678
|
-
hlrw: ScopeSetMassOrVolume | None =
|
700
|
+
hlrw: ScopeSetMassOrVolume | None = pydantic.Field(
|
679
701
|
description="High level radioactive waste disposed",
|
680
702
|
default=None,
|
681
703
|
)
|
682
|
-
illrw: ScopeSetMassOrVolume | None =
|
704
|
+
illrw: ScopeSetMassOrVolume | None = pydantic.Field(
|
683
705
|
description="Intermediate level radioactive waste disposed",
|
684
706
|
default=None,
|
685
707
|
)
|
686
|
-
cru: ScopeSetMass | None =
|
708
|
+
cru: ScopeSetMass | None = pydantic.Field(
|
687
709
|
description="Components for re-use",
|
688
710
|
default=None,
|
689
711
|
)
|
690
|
-
mr: ScopeSetMass | None =
|
712
|
+
mr: ScopeSetMass | None = pydantic.Field(
|
691
713
|
description="Recycled materials",
|
692
714
|
default=None,
|
693
715
|
)
|
694
|
-
mfr: ScopeSetMass | None =
|
716
|
+
mfr: ScopeSetMass | None = pydantic.Field(
|
695
717
|
description="Materials for recycling",
|
696
718
|
default=None,
|
697
719
|
)
|
698
|
-
mer: ScopeSetMass | None =
|
720
|
+
mer: ScopeSetMass | None = pydantic.Field(
|
699
721
|
description="Materials for energy recovery",
|
700
722
|
default=None,
|
701
723
|
)
|
702
|
-
ee: ScopeSetEnergy | None =
|
724
|
+
ee: ScopeSetEnergy | None = pydantic.Field(
|
703
725
|
description="Exported energy",
|
704
726
|
default=None,
|
705
727
|
)
|
706
|
-
eh: ScopeSetEnergy | None =
|
728
|
+
eh: ScopeSetEnergy | None = pydantic.Field(
|
707
729
|
description="Exported heat",
|
708
730
|
default=None,
|
709
731
|
)
|
@@ -712,16 +734,19 @@ class OutputFlowSet(ScopesetByNameBase):
|
|
712
734
|
class WithLciaMixin(BaseOpenEpdSchema):
|
713
735
|
"""Mixin for LCIA data."""
|
714
736
|
|
715
|
-
impacts: Impacts | None =
|
737
|
+
impacts: Impacts | None = pydantic.Field(
|
716
738
|
description="List of environmental impacts, compiled per one of the standard Impact Assessment methods",
|
717
|
-
|
739
|
+
examples=[{"TRACI 2.1": {"gwp": {"A1A2A3": {"mean": 22.4, "unit": "kgCO2e"}}}}],
|
740
|
+
default=None,
|
718
741
|
)
|
719
|
-
resource_uses: ResourceUseSet | None =
|
742
|
+
resource_uses: ResourceUseSet | None = pydantic.Field(
|
720
743
|
description="Set of Resource Use Indicators, over various LCA scopes",
|
721
|
-
|
744
|
+
examples=[{"RPRe": {"A1A2A3": {"mean": 12, "unit": "MJ", "rsd": 0.12}}}],
|
745
|
+
default=None,
|
722
746
|
)
|
723
|
-
output_flows: OutputFlowSet | None =
|
747
|
+
output_flows: OutputFlowSet | None = pydantic.Field(
|
724
748
|
description="Set of Waste and Output Flow indicators which describe the waste categories "
|
725
749
|
"and other material output flows derived from the LCI.",
|
726
|
-
|
750
|
+
examples=[{"hwd": {"A1A2A3": {"mean": 2300, "unit": "kg", "rsd": 0.22}}}],
|
751
|
+
default=None,
|
727
752
|
)
|