openepd 2.0.0__py3-none-any.whl → 3.0.0__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 +1 -1
- openepd/__version__.py +2 -2
- openepd/api/__init__.py +19 -0
- openepd/api/base_sync_client.py +550 -0
- openepd/api/category/__init__.py +19 -0
- openepd/api/category/dto.py +25 -0
- openepd/api/category/sync_api.py +44 -0
- openepd/api/common.py +239 -0
- openepd/api/dto/__init__.py +19 -0
- openepd/api/dto/base.py +41 -0
- openepd/api/dto/common.py +115 -0
- openepd/api/dto/meta.py +69 -0
- openepd/api/dto/mf.py +59 -0
- openepd/api/dto/params.py +19 -0
- openepd/api/epd/__init__.py +19 -0
- openepd/api/epd/dto.py +121 -0
- openepd/api/epd/sync_api.py +105 -0
- openepd/api/errors.py +86 -0
- openepd/api/pcr/__init__.py +19 -0
- openepd/api/pcr/dto.py +41 -0
- openepd/api/pcr/sync_api.py +49 -0
- openepd/api/sync_client.py +67 -0
- openepd/api/test/__init__.py +19 -0
- openepd/bundle/__init__.py +1 -1
- openepd/bundle/base.py +1 -1
- openepd/bundle/model.py +5 -6
- openepd/bundle/reader.py +5 -5
- openepd/bundle/writer.py +5 -4
- openepd/compat/__init__.py +19 -0
- openepd/compat/pydantic.py +29 -0
- openepd/model/__init__.py +1 -1
- openepd/model/base.py +114 -15
- openepd/model/category.py +39 -0
- openepd/model/common.py +33 -25
- openepd/model/epd.py +97 -78
- openepd/model/factory.py +48 -0
- openepd/model/lcia.py +24 -13
- openepd/model/org.py +28 -18
- openepd/model/pcr.py +42 -14
- openepd/model/specs/README.md +19 -0
- openepd/model/specs/__init__.py +20 -4
- openepd/model/specs/aluminium.py +67 -0
- openepd/model/specs/asphalt.py +87 -0
- openepd/model/specs/base.py +60 -0
- openepd/model/specs/concrete.py +453 -23
- openepd/model/specs/glass.py +404 -0
- openepd/model/specs/steel.py +193 -0
- openepd/model/specs/wood.py +130 -0
- openepd/model/standard.py +2 -3
- openepd/model/validation/__init__.py +19 -0
- openepd/model/validation/common.py +59 -0
- openepd/model/validation/numbers.py +26 -0
- openepd/model/validation/quantity.py +131 -0
- openepd/model/versioning.py +129 -0
- {openepd-2.0.0.dist-info → openepd-3.0.0.dist-info}/METADATA +36 -5
- openepd-3.0.0.dist-info/RECORD +59 -0
- openepd-2.0.0.dist-info/RECORD +0 -22
- {openepd-2.0.0.dist-info → openepd-3.0.0.dist-info}/LICENSE +0 -0
- {openepd-2.0.0.dist-info → openepd-3.0.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,404 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# This software was developed with support from the Skanska USA,
|
17
|
+
# Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
|
18
|
+
# Find out more at www.BuildingTransparency.org
|
19
|
+
#
|
20
|
+
from enum import StrEnum
|
21
|
+
|
22
|
+
from openepd.compat.pydantic import pyd
|
23
|
+
from openepd.model.base import BaseOpenEpdSchema
|
24
|
+
from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
|
25
|
+
from openepd.model.validation.numbers import PositiveInt, RatioFloat
|
26
|
+
from openepd.model.validation.quantity import HeatConductanceUCIStr, LengthMmStr, PressureMPaStr, QuantityStr
|
27
|
+
|
28
|
+
|
29
|
+
class SolarHeatGainMixin(BaseOpenEpdSchema):
|
30
|
+
"""Solar heat gain mixin."""
|
31
|
+
|
32
|
+
solar_heat_gain: RatioFloat | None = pyd.Field(
|
33
|
+
default=None,
|
34
|
+
description="Solar heat gain, measured at a certain level of Differential Pressure.",
|
35
|
+
example=0.3,
|
36
|
+
le=1,
|
37
|
+
ge=0,
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
class GlazingOptionsMixin(BaseOpenEpdSchema):
|
42
|
+
"""Glazing options mixin."""
|
43
|
+
|
44
|
+
low_emissivity: bool | None = pyd.Field(default=None, description="Low Emissivity coatings")
|
45
|
+
electrochromic: bool | None = pyd.Field(
|
46
|
+
default=None, description="Glazing with an electrically controllable solar heat gain and/or other properties."
|
47
|
+
)
|
48
|
+
acid_etched: bool | None = pyd.Field(
|
49
|
+
default=None, description="Flat glass that has undergone a chemical etching process."
|
50
|
+
)
|
51
|
+
tempered: bool | None = pyd.Field(
|
52
|
+
default=None,
|
53
|
+
description="Consists of a single pane that has been heat-treated "
|
54
|
+
"to give the glass increased impact resistance. Standard typically used in North America.",
|
55
|
+
)
|
56
|
+
toughened: bool | None = pyd.Field(
|
57
|
+
default=None,
|
58
|
+
description="Consists of a single pane that has been specially heat-treated to give the glass increased "
|
59
|
+
"impact resistance. Standard typically used in Europe.",
|
60
|
+
)
|
61
|
+
|
62
|
+
laminated: bool | None = pyd.Field(
|
63
|
+
default=None,
|
64
|
+
description="Consists of at least two glass panes lying one on top of the other, with one or several layers "
|
65
|
+
"of a tear-resistant, viscoelastic film positioned between the panes, which consist of polyvinyl "
|
66
|
+
"butyral (PVB)",
|
67
|
+
)
|
68
|
+
fire_resistant: bool | None = pyd.Field(default=None, description="Fire resistant")
|
69
|
+
fire_protection: bool | None = pyd.Field(
|
70
|
+
default=None,
|
71
|
+
description="Specifically tested for its ability to block flames and smoke, but not radiant heat. Ranges"
|
72
|
+
" from specialty tempered products rated for ~20 minutes to glass ceramics rated up to 3 hours.",
|
73
|
+
)
|
74
|
+
pyrolytic_coated: bool | None = pyd.Field(
|
75
|
+
default=None,
|
76
|
+
description="At least one coating is applied in a pyrolytic process, typically during float glass production.",
|
77
|
+
)
|
78
|
+
sputter_coat: bool | None = pyd.Field(
|
79
|
+
default=None, description="At least one coating is applied using sputter (vacuum deposition) coating."
|
80
|
+
)
|
81
|
+
|
82
|
+
|
83
|
+
class GlassPanesMixin(BaseOpenEpdSchema):
|
84
|
+
"""Glass panes mixin."""
|
85
|
+
|
86
|
+
glass_panes: PositiveInt | None = pyd.Field(
|
87
|
+
default=None,
|
88
|
+
description="Number of panes, each separated by a cavity. A 3 pane unit has 2 cavities. example: 3",
|
89
|
+
example=3,
|
90
|
+
)
|
91
|
+
|
92
|
+
|
93
|
+
class DPRatingMixin(BaseOpenEpdSchema):
|
94
|
+
"""Differential pressure rating mixin."""
|
95
|
+
|
96
|
+
dp_rating: PressureMPaStr | None = pyd.Field(
|
97
|
+
default=None, description="Maximum Differential Pressure, a measure of wind tolerance.", example="75 psf"
|
98
|
+
)
|
99
|
+
|
100
|
+
|
101
|
+
class AirInfiltrationMixin(BaseOpenEpdSchema):
|
102
|
+
"""Air infiltration mixin."""
|
103
|
+
|
104
|
+
air_infiltration: QuantityStr | None = pyd.Field(
|
105
|
+
default=None,
|
106
|
+
description="Air infiltration, measured at a certain level of Differential Pressure.",
|
107
|
+
example="0.3 m3/(sec * m2)",
|
108
|
+
)
|
109
|
+
|
110
|
+
|
111
|
+
class AssemblyUFactorMixin(BaseOpenEpdSchema):
|
112
|
+
"""Assembly U factor mixin."""
|
113
|
+
|
114
|
+
assembly_u_factor: HeatConductanceUCIStr | None = pyd.Field(
|
115
|
+
default=None,
|
116
|
+
description="Weighted average conductance of heat across assembly (including frame).",
|
117
|
+
example="0.3 UCI",
|
118
|
+
)
|
119
|
+
|
120
|
+
|
121
|
+
class GlassIntendedApplicationMixin(BaseOpenEpdSchema):
|
122
|
+
"""Glass intended application mixin."""
|
123
|
+
|
124
|
+
glazing_intended_application_curtain_wall: bool | None = pyd.Field(
|
125
|
+
default=None, description="Intended for curtain walls. Relevant for IGUs."
|
126
|
+
)
|
127
|
+
glazing_intended_application_r_windows: bool | None = pyd.Field(
|
128
|
+
default=None,
|
129
|
+
description="Intended for residential (NAFS 'R') and similar windows, doors, or skylights. Relevant for IGUs.",
|
130
|
+
)
|
131
|
+
glazing_intended_application_lc_windows: bool | None = pyd.Field(
|
132
|
+
default=None, description="Intended for light commercial (NAFS 'LC') and similar windows. Relevant for IGUs."
|
133
|
+
)
|
134
|
+
glazing_intended_application_cw_windows: bool | None = pyd.Field(
|
135
|
+
default=None, description="Intended for commercial (NAFS 'CW') and similar windows. Relevant for IGUs."
|
136
|
+
)
|
137
|
+
glazing_intended_application_aw_windows: bool | None = pyd.Field(
|
138
|
+
default=None, description="Intended for architectural (NAFS 'AW') and similar windows. Relevant for IGUs."
|
139
|
+
)
|
140
|
+
glazing_intended_application_storefronts: bool | None = pyd.Field(
|
141
|
+
default=None, description="Intended for Storefronts and similar applications. Relevant for IGUs."
|
142
|
+
)
|
143
|
+
glazing_intended_application_glazed_doors: bool | None = pyd.Field(
|
144
|
+
default=None, description="Intended for Glazed Doors and similar applications. Relevant for IGUs."
|
145
|
+
)
|
146
|
+
glazing_intended_application_unit_skylights: bool | None = pyd.Field(
|
147
|
+
default=None, description="Intended for Unit Skylights and similar applications. Relevant for IGUs."
|
148
|
+
)
|
149
|
+
glazing_intended_application_sloped_skylights: bool | None = pyd.Field(
|
150
|
+
default=None,
|
151
|
+
description="Intended for sloped glazing, and architectural skylights, and similar. Relevant for IGUs.",
|
152
|
+
)
|
153
|
+
glazing_intended_application_other: bool | None = pyd.Field(
|
154
|
+
default=None, description="Intended for other application not listed. Relevant for IGUs."
|
155
|
+
)
|
156
|
+
|
157
|
+
|
158
|
+
class ThermalSeparationEnum(StrEnum):
|
159
|
+
"""Thermal separation enum."""
|
160
|
+
|
161
|
+
ALUMINIUM = "Aluminium"
|
162
|
+
STEEL = "Steel"
|
163
|
+
THERMALLY_IMPROVED_METAL = "Thermally Improved Metal"
|
164
|
+
THERMALLY_BROKEN_METAL = "Thermally Broken Metal"
|
165
|
+
NON_METAL = "Nonmetal"
|
166
|
+
|
167
|
+
|
168
|
+
class ThermalSeparationMixin(BaseOpenEpdSchema):
|
169
|
+
"""Thermal separation mixin."""
|
170
|
+
|
171
|
+
thermal_separation: ThermalSeparationEnum | None = pyd.Field(default=None, description="Thermal separation.")
|
172
|
+
|
173
|
+
|
174
|
+
class HurricaneResistantMixin(BaseOpenEpdSchema):
|
175
|
+
"""Hurricane resistant mixin."""
|
176
|
+
|
177
|
+
hurricane_resistant: bool | None = pyd.Field(
|
178
|
+
default=None, description="The product has been designed to resist windborne debris."
|
179
|
+
)
|
180
|
+
|
181
|
+
|
182
|
+
class NAFSPerformanceGrade(StrEnum):
|
183
|
+
"""NAFS Performance Grade enum."""
|
184
|
+
|
185
|
+
GRADE_15_PSF = "15 psf"
|
186
|
+
GRADE_20_PSF = "20 psf"
|
187
|
+
GRADE_25_PSF = "25 psf"
|
188
|
+
GRADE_30_PSF = "30 psf"
|
189
|
+
GRADE_35_PSF = "35 psf"
|
190
|
+
GRADE_40_PSF = "40 psf"
|
191
|
+
GRADE_45_PSF = "45 psf"
|
192
|
+
GRADE_50_PSF = "50 psf"
|
193
|
+
GRADE_55_PSF = "55 psf"
|
194
|
+
GRADE_60_PSF = "60 psf"
|
195
|
+
GRADE_65_PSF = "65 psf"
|
196
|
+
GRADE_70_PSF = "70 psf"
|
197
|
+
GRADE_75_PSF = "75 psf"
|
198
|
+
GRADE_80_PSF = "80 psf"
|
199
|
+
GRADE_85_PSF = "85 psf"
|
200
|
+
GRADE_90_PSF = "90 psf"
|
201
|
+
GRADE_95_PSF = "95 psf"
|
202
|
+
GRADE_100_PSF = "100 psf"
|
203
|
+
GRADE_105_PSF = "105 psf"
|
204
|
+
GRADE_110_PSF = "110 psf"
|
205
|
+
GRADE_115_PSF = "115 psf"
|
206
|
+
GRADE_120_PSF = "120 psf"
|
207
|
+
GRADE_125_PSF = "125 psf"
|
208
|
+
GRADE_130_PSF = "130 psf"
|
209
|
+
GRADE_135_PSF = "135 psf"
|
210
|
+
GRADE_140_PSF = "140 psf"
|
211
|
+
GRADE_145_PSF = "145 psf"
|
212
|
+
GRADE_150_PSF = "150 psf"
|
213
|
+
GRADE_155_PSF = "155 psf"
|
214
|
+
GRADE_160_PSF = "160 psf"
|
215
|
+
GRADE_165_PSF = "165 psf"
|
216
|
+
GRADE_170_PSF = "170 psf"
|
217
|
+
GRADE_175_PSF = "175 psf"
|
218
|
+
GRADE_180_PSF = "180 psf"
|
219
|
+
GRADE_185_PSF = "185 psf"
|
220
|
+
GRADE_190_PSF = "190 psf"
|
221
|
+
GRADE_195_PSF = "195 psf"
|
222
|
+
GRADE_200_PSF = "200 psf"
|
223
|
+
GRADE_205_PSF = "205 psf"
|
224
|
+
GRADE_210_PSF = "210 psf"
|
225
|
+
|
226
|
+
|
227
|
+
class SpacerEnum(StrEnum):
|
228
|
+
"""Spacer enum."""
|
229
|
+
|
230
|
+
ALUMINIUM = "Aluminium"
|
231
|
+
STAINLESS_STEEL = "Stainless steel"
|
232
|
+
PLASTIC_AND_STAINLESS_STEEL = "Plastic and stainless steel"
|
233
|
+
THERMOPLASTIC = "Thermoplastic"
|
234
|
+
FOAM = "Foam"
|
235
|
+
STAINLESS_STEEL_AND_TIN = "Stainless steel or tin plate U-channel"
|
236
|
+
PLASTIC = "Plastic"
|
237
|
+
|
238
|
+
|
239
|
+
class FenestrationHardwareFunctionEnum(StrEnum):
|
240
|
+
"""Fenestration hardware function enum."""
|
241
|
+
|
242
|
+
LOCK = "Lock"
|
243
|
+
HINGE = "Hinge"
|
244
|
+
HANDLE = "Handle"
|
245
|
+
OPERATOR = "Operator"
|
246
|
+
BALANCE = "Balance"
|
247
|
+
OTHER = "Other"
|
248
|
+
|
249
|
+
|
250
|
+
class FrameMaterialEnum(StrEnum):
|
251
|
+
"""Framing material enum."""
|
252
|
+
|
253
|
+
VINYL = "Vinyl"
|
254
|
+
ALUMINIUM = "Aluminium"
|
255
|
+
STEEL = "Steel"
|
256
|
+
WOOD = "Wood"
|
257
|
+
FIBERGLASS = "Fiberglass"
|
258
|
+
COMPOSITE = "Composite"
|
259
|
+
NONE = "None"
|
260
|
+
OTHER = "Other"
|
261
|
+
|
262
|
+
|
263
|
+
class NAFSFenestrationV1(
|
264
|
+
GlassPanesMixin,
|
265
|
+
DPRatingMixin,
|
266
|
+
AirInfiltrationMixin,
|
267
|
+
SolarHeatGainMixin,
|
268
|
+
AssemblyUFactorMixin,
|
269
|
+
ThermalSeparationMixin,
|
270
|
+
GlazingOptionsMixin,
|
271
|
+
HurricaneResistantMixin,
|
272
|
+
BaseOpenEpdHierarchicalSpec,
|
273
|
+
):
|
274
|
+
"""NAFS Fenestration V1 spec."""
|
275
|
+
|
276
|
+
_EXT_VERSION = "1.0"
|
277
|
+
nafs_performance_class_r: bool | None = pyd.Field(
|
278
|
+
default=None, description="Residential; commonly used in one- and two-family dwellings."
|
279
|
+
)
|
280
|
+
nafs_performance_class_lc: bool | None = pyd.Field(
|
281
|
+
default=None,
|
282
|
+
description="Light Commercial: commonly used in low-rise and mid-rise multi-family dwellings and other "
|
283
|
+
"buildings where larger sizes and higher loading requirements are expected.",
|
284
|
+
)
|
285
|
+
nafs_performance_class_cw: bool | None = pyd.Field(
|
286
|
+
default=None,
|
287
|
+
description="Commercial Window: commonly used in low-rise and mid-rise buildings where larger sizes, higher "
|
288
|
+
"loading requirements, limits on deflection, and heavy use are expected.",
|
289
|
+
)
|
290
|
+
nafs_performance_class_aw: bool | None = pyd.Field(
|
291
|
+
default=None,
|
292
|
+
description="Architectural Window: commonly used in high-rise and mid-rise buildings to meet increased "
|
293
|
+
"loading requirements and limits on deflection, and in buildings where frequent and extreme use "
|
294
|
+
"of the fenestration products is expected.",
|
295
|
+
)
|
296
|
+
|
297
|
+
nafs_performance_grade: NAFSPerformanceGrade | None = pyd.Field(
|
298
|
+
default=None,
|
299
|
+
description="NAFS Performance Grade. The NAFS Performance Grade is a number that represents the performance "
|
300
|
+
"of the glazing product. The higher the number, the better the performance. The NAFS Performance "
|
301
|
+
"Grade is calculated using the NAFS Performance Class, the NAFS Performance Index, and the NAFS "
|
302
|
+
"Performance Factor. While it is expressed as pressure, there are specific values which are "
|
303
|
+
"allowed. The values are listed in the enum.",
|
304
|
+
)
|
305
|
+
|
306
|
+
|
307
|
+
class InsulatingGlazingUnitsV1(
|
308
|
+
GlassPanesMixin,
|
309
|
+
DPRatingMixin,
|
310
|
+
AirInfiltrationMixin,
|
311
|
+
SolarHeatGainMixin,
|
312
|
+
GlazingOptionsMixin,
|
313
|
+
HurricaneResistantMixin,
|
314
|
+
GlassIntendedApplicationMixin,
|
315
|
+
BaseOpenEpdHierarchicalSpec,
|
316
|
+
):
|
317
|
+
"""Insulating glazing units V1 spec."""
|
318
|
+
|
319
|
+
_EXT_VERSION = "1.0"
|
320
|
+
|
321
|
+
cog_u_factor: HeatConductanceUCIStr | None = pyd.Field(
|
322
|
+
default=None, description="Conductance of heat at center of glass.", example="0.3 UCI"
|
323
|
+
)
|
324
|
+
spacer: SpacerEnum | None = pyd.Field(default=None, description="Spacer material for Integrated Glass Unit.")
|
325
|
+
|
326
|
+
|
327
|
+
class FenestrationFramingV1(GlassIntendedApplicationMixin, BaseOpenEpdHierarchicalSpec):
|
328
|
+
"""Fenestration framing V1 spec."""
|
329
|
+
|
330
|
+
_EXT_VERSION = "1.0"
|
331
|
+
|
332
|
+
frame_material: FrameMaterialEnum | None = pyd.Field(default=None, description="Frame material.")
|
333
|
+
|
334
|
+
|
335
|
+
class FenestrationHardwareV1(BaseOpenEpdHierarchicalSpec):
|
336
|
+
"""Fenestration hardware V1 spec."""
|
337
|
+
|
338
|
+
_EXT_VERSION = "1.0"
|
339
|
+
|
340
|
+
hardware_function: FenestrationHardwareFunctionEnum | None = pyd.Field(
|
341
|
+
default=None, description="Hardware function."
|
342
|
+
)
|
343
|
+
|
344
|
+
|
345
|
+
class FenestrationPartsV1(GlassIntendedApplicationMixin, BaseOpenEpdHierarchicalSpec):
|
346
|
+
"""Fenestration parts V1 spec."""
|
347
|
+
|
348
|
+
_EXT_VERSION = "1.0"
|
349
|
+
|
350
|
+
FenestrationHardware: FenestrationHardwareV1 | None = pyd.Field(
|
351
|
+
default=None, title="FenestrationHardwareV1", description="Fenestration hardware V1 spec."
|
352
|
+
)
|
353
|
+
FenestrationFraming: FenestrationFramingV1 | None = pyd.Field(
|
354
|
+
default=None, title="FenestrationFramingV1", description="Fenestration framing V1 spec."
|
355
|
+
)
|
356
|
+
|
357
|
+
|
358
|
+
class ProcessedNonInsulatedGlassPanesV1(
|
359
|
+
GlassPanesMixin, SolarHeatGainMixin, GlazingOptionsMixin, BaseOpenEpdHierarchicalSpec
|
360
|
+
):
|
361
|
+
"""Processed non-insulated glass panes V1 spec."""
|
362
|
+
|
363
|
+
_EXT_VERSION = "1.0"
|
364
|
+
|
365
|
+
|
366
|
+
class FlatGlassPanesV1(BaseOpenEpdHierarchicalSpec):
|
367
|
+
"""Flat glass panes."""
|
368
|
+
|
369
|
+
_EXT_VERSION = "1.0"
|
370
|
+
flat_glass_panes_thickness: LengthMmStr | None = pyd.Field(
|
371
|
+
default=None, description="Thickness of the flat glass panes.", example="8 mm"
|
372
|
+
)
|
373
|
+
|
374
|
+
|
375
|
+
class GlassPanesV1(BaseOpenEpdHierarchicalSpec):
|
376
|
+
"""Glass panes V1 spec."""
|
377
|
+
|
378
|
+
_EXT_VERSION = "1.0"
|
379
|
+
|
380
|
+
ProcessedNonInsulatedGlassPanes: ProcessedNonInsulatedGlassPanesV1 | None = pyd.Field(
|
381
|
+
default=None,
|
382
|
+
title="ProcessedNonInsulatedGlassPanesV1",
|
383
|
+
description="Processed non-insulated glass panes V1 spec.",
|
384
|
+
)
|
385
|
+
FlatGlassPanes: FlatGlassPanesV1 | None = pyd.Field(
|
386
|
+
default=None, title="FlatGlassPanes", description="Flat glass panes spec."
|
387
|
+
)
|
388
|
+
|
389
|
+
|
390
|
+
class GlazingV1(BaseOpenEpdHierarchicalSpec):
|
391
|
+
"""Glazing V1 spec."""
|
392
|
+
|
393
|
+
_EXT_VERSION = "1.0"
|
394
|
+
thickness: str | None = pyd.Field(default=None, description="Thickness of the glazing.")
|
395
|
+
GlassPanes: GlassPanesV1 | None = pyd.Field(default=None, title="GlassPanesV1", description="Glass panes V1 spec.")
|
396
|
+
InsulatingGlazingUnits: InsulatingGlazingUnitsV1 | None = pyd.Field(
|
397
|
+
default=None, title="InsulatingGlazingUnitsV1", description="Insulating glazing units V1 spec."
|
398
|
+
)
|
399
|
+
NAFSFenestration: NAFSFenestrationV1 | None = pyd.Field(
|
400
|
+
default=None, title="NAFSFenestrationV1", description="NAFS Fenestration V1 spec."
|
401
|
+
)
|
402
|
+
FenestrationParts: FenestrationPartsV1 | None = pyd.Field(
|
403
|
+
default=None, title="FenestrationPartsV1", description="Fenestration parts V1 spec."
|
404
|
+
)
|
@@ -0,0 +1,193 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2024 by C Change Labs Inc. www.c-change-labs.com
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# This software was developed with support from the Skanska USA,
|
17
|
+
# Charles Pankow Foundation, Microsoft Sustainability Fund, Interface, MKA Foundation, and others.
|
18
|
+
# Find out more at www.BuildingTransparency.org
|
19
|
+
#
|
20
|
+
from enum import StrEnum
|
21
|
+
|
22
|
+
from openepd.compat.pydantic import pyd
|
23
|
+
from openepd.model.base import BaseOpenEpdSchema
|
24
|
+
from openepd.model.common import OpenEPDUnit
|
25
|
+
from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
|
26
|
+
from openepd.model.standard import Standard
|
27
|
+
from openepd.model.validation.numbers import RatioFloat
|
28
|
+
from openepd.model.validation.quantity import LengthMmStr, validate_unit_factory
|
29
|
+
|
30
|
+
|
31
|
+
class SteelMakingRoute(BaseOpenEpdSchema):
|
32
|
+
"""Steel making route."""
|
33
|
+
|
34
|
+
bof: bool | None = pyd.Field(default=None, description="Basic oxygen furnace")
|
35
|
+
eaf: bool | None = pyd.Field(default=None, description="Electric arc furnace")
|
36
|
+
ohf: bool | None = pyd.Field(default=None, description="Open hearth furnace")
|
37
|
+
|
38
|
+
|
39
|
+
class SteelComposition(StrEnum):
|
40
|
+
"""Steel composition enum."""
|
41
|
+
|
42
|
+
CARBON = "Carbon"
|
43
|
+
ALLOY = "Alloy"
|
44
|
+
STAINLESS = "Stainless"
|
45
|
+
TOOL = "Tool"
|
46
|
+
OTHER = "Other"
|
47
|
+
|
48
|
+
|
49
|
+
class FabricatedOptionsMixin(pyd.BaseModel):
|
50
|
+
"""Fabricated options mixin."""
|
51
|
+
|
52
|
+
_EXT_VERSION = "1.0"
|
53
|
+
|
54
|
+
fabricated: bool | None = pyd.Field(default=None, description="Fabricated")
|
55
|
+
|
56
|
+
|
57
|
+
class WireMeshSteelV1(BaseOpenEpdHierarchicalSpec):
|
58
|
+
"""Spec for wire mesh steel."""
|
59
|
+
|
60
|
+
_EXT_VERSION = "1.0"
|
61
|
+
|
62
|
+
class Options(BaseOpenEpdSchema, FabricatedOptionsMixin):
|
63
|
+
"""Wire Mesh Options."""
|
64
|
+
|
65
|
+
pass
|
66
|
+
|
67
|
+
options: Options = pyd.Field(description="Rebar Steel options", default_factory=Options)
|
68
|
+
|
69
|
+
|
70
|
+
class RebarGrade(StrEnum):
|
71
|
+
"""Rebar grade enum."""
|
72
|
+
|
73
|
+
USA_60_KSI = "60 ksi"
|
74
|
+
USA_75_KIS = "75 ksi"
|
75
|
+
USA_80_KSI = "80 ksi"
|
76
|
+
USA_90_KSI = "90 ksi"
|
77
|
+
USA_100_KSI = "100 ksi"
|
78
|
+
USA_120_KSI = "120 ksi"
|
79
|
+
USA_40_KSI = "40 ksi"
|
80
|
+
USA_50_KSI = "50 ksi"
|
81
|
+
|
82
|
+
METRIC_420_MPA = "420 Mpa"
|
83
|
+
METRIC_520_MPA = "520 Mpa"
|
84
|
+
METRIC_550_MPA = "550 Mpa"
|
85
|
+
METRIC_620_MPA = "620 Mpa"
|
86
|
+
METRIC_690_MPA = "690 MPa"
|
87
|
+
METRIC_830_MPA = "830 Mpa"
|
88
|
+
METRIC_280_MPA = "280 MPa"
|
89
|
+
METRIC_350_MPA = "350 Mpa"
|
90
|
+
|
91
|
+
|
92
|
+
class RebarSteelV1(BaseOpenEpdHierarchicalSpec):
|
93
|
+
"""Rebar steel spec."""
|
94
|
+
|
95
|
+
_EXT_VERSION = "1.0"
|
96
|
+
|
97
|
+
class Options(BaseOpenEpdSchema, FabricatedOptionsMixin):
|
98
|
+
"""Rebar Steel Options."""
|
99
|
+
|
100
|
+
epoxy: bool | None = pyd.Field(default=None, description="Epoxy Coated")
|
101
|
+
|
102
|
+
options: Options = pyd.Field(description="Rebar Steel options", default_factory=Options)
|
103
|
+
|
104
|
+
steel_rebar_grade: RebarGrade | None = pyd.Field(default=None, description="Rebar steel grade")
|
105
|
+
steel_rebar_diameter_min: LengthMmStr | None = pyd.Field(default=None, description="Minimum rebar diameter")
|
106
|
+
_steel_rebar_diameter_min = pyd.validator("steel_rebar_diameter_min", allow_reuse=True)(
|
107
|
+
validate_unit_factory(OpenEPDUnit.m)
|
108
|
+
)
|
109
|
+
|
110
|
+
steel_rebar_bending_pin_max: float | None = pyd.Field(
|
111
|
+
default=None, description="Maximum rebar bending pin in diameters of this rebar", example=6.2
|
112
|
+
)
|
113
|
+
steel_rebar_ts_ys_ratio_max: float | None = pyd.Field(
|
114
|
+
default=None, description="Max ratio of ultimate tensile to yield tensile strength"
|
115
|
+
)
|
116
|
+
|
117
|
+
|
118
|
+
class PlateSteelV1(BaseOpenEpdHierarchicalSpec):
|
119
|
+
"""Plate Steel Spec."""
|
120
|
+
|
121
|
+
_EXT_VERSION = "1.0"
|
122
|
+
|
123
|
+
class Options(BaseOpenEpdSchema, FabricatedOptionsMixin):
|
124
|
+
"""Plate Steel Options."""
|
125
|
+
|
126
|
+
pass
|
127
|
+
|
128
|
+
options: Options = pyd.Field(description="Plate Steel options", default_factory=Options)
|
129
|
+
|
130
|
+
|
131
|
+
class HollowV1(BaseOpenEpdHierarchicalSpec):
|
132
|
+
"""Hollow Sections Spec."""
|
133
|
+
|
134
|
+
_EXT_VERSION = "1.0"
|
135
|
+
|
136
|
+
class Options(FabricatedOptionsMixin, BaseOpenEpdSchema):
|
137
|
+
"""Hollow Sections Options."""
|
138
|
+
|
139
|
+
pass
|
140
|
+
|
141
|
+
options: Options = pyd.Field(description="Hollow Steel options", default_factory=Options)
|
142
|
+
|
143
|
+
|
144
|
+
class HotRolledV1(BaseOpenEpdHierarchicalSpec):
|
145
|
+
"""Hot Rolled spec."""
|
146
|
+
|
147
|
+
_EXT_VERSION = "1.0"
|
148
|
+
|
149
|
+
class Options(FabricatedOptionsMixin, BaseOpenEpdSchema):
|
150
|
+
"""Hot Rolled options."""
|
151
|
+
|
152
|
+
pass
|
153
|
+
|
154
|
+
options: Options = pyd.Field(description="Hollow Steel options", default_factory=Options)
|
155
|
+
|
156
|
+
|
157
|
+
class SteelV1(BaseOpenEpdHierarchicalSpec):
|
158
|
+
"""Steel spec."""
|
159
|
+
|
160
|
+
_EXT_VERSION = "1.0"
|
161
|
+
|
162
|
+
class Options(BaseOpenEpdSchema):
|
163
|
+
"""Steel spec options."""
|
164
|
+
|
165
|
+
galvanized: bool | None = pyd.Field(default=None, description="Galvanized")
|
166
|
+
cold_finished: bool | None = pyd.Field(default=None, description="Cold Finished")
|
167
|
+
|
168
|
+
form_factor: str | None = pyd.Field(description="Product's form factor", example="Steel >> RebarSteel")
|
169
|
+
steel_composition: SteelComposition | None = pyd.Field(default=None, description="Basic chemical composition")
|
170
|
+
recycled_content: RatioFloat | None = pyd.Field(
|
171
|
+
default=None,
|
172
|
+
example=0.3,
|
173
|
+
description="Scrap steel inputs from other processes. Includes "
|
174
|
+
"Post-Consumer content, if any. This percentage may be "
|
175
|
+
"used to evaluate the EPD w.r.t. targets or limits that are"
|
176
|
+
" different for primary and recycled content.",
|
177
|
+
)
|
178
|
+
ASTM: list[Standard] = pyd.Field(description="ASTM standard to which this product complies", default_factory=list)
|
179
|
+
SAE: list[Standard] = pyd.Field(
|
180
|
+
description="AISA/SAE standard to which this product complies", default_factory=list
|
181
|
+
)
|
182
|
+
EN: list[Standard] = pyd.Field(description="EN 10027 number(s)", default_factory=list)
|
183
|
+
|
184
|
+
options: Options | None = pyd.Field(description="Steel options", default_factory=Options)
|
185
|
+
making_route: SteelMakingRoute | None = pyd.Field(default=None, description="Steel making route")
|
186
|
+
|
187
|
+
# Nested specs
|
188
|
+
|
189
|
+
WireMeshSteel: WireMeshSteelV1 | None = None
|
190
|
+
RebarSteel: RebarSteelV1 | None = None
|
191
|
+
PlateSteel: PlateSteelV1 | None = None
|
192
|
+
Hollow: HollowV1 | None = None
|
193
|
+
HotRolled: HotRolledV1 | None = None
|