feat: inserting concrete architecture part 2
This commit is contained in:
@@ -9,7 +9,11 @@ from speckle_automate import (
|
||||
|
||||
from typing import Dict, Generator, Any, List
|
||||
|
||||
from src.domain.carbon.databases.enums import SteelDatabase, TimberDatabase
|
||||
from src.domain.carbon.databases.enums import (
|
||||
SteelDatabase,
|
||||
TimberDatabase,
|
||||
ConcreteDatabase,
|
||||
)
|
||||
from src.infrastructure.logging import Logging
|
||||
from src.services.carbon_calculator import CarbonCalculator
|
||||
from src.services.element_processor import ElementProcessor
|
||||
@@ -42,7 +46,7 @@ class FunctionInputs(AutomateBase):
|
||||
)
|
||||
|
||||
concrete_database: str = Field(
|
||||
default=ConcreteDatabase.GUL_LOW_AIR.value,
|
||||
default=ConcreteDatabase.GulLowAir.value,
|
||||
title="Concrete Database",
|
||||
description="Database used for the GWP of concrete objects",
|
||||
json_schema_extra={"oneOf": create_one_of_enum(ConcreteDatabase)},
|
||||
@@ -125,18 +129,24 @@ class FunctionInputs(AutomateBase):
|
||||
class RevitCarbonAnalyzer:
|
||||
"""Main application for analyzing carbon in Revit models."""
|
||||
|
||||
def __init__(self, steel_database: str, timber_database: str):
|
||||
def __init__(
|
||||
self,
|
||||
steel_database: str,
|
||||
timber_database: str,
|
||||
concrete_database: str,
|
||||
country: str,
|
||||
reinforcement_rates: Dict[str, float],
|
||||
):
|
||||
self.material_processor = MaterialProcessor()
|
||||
self.element_processor = ElementProcessor(
|
||||
material_processor=self.material_processor, logger=Logging()
|
||||
)
|
||||
self.carbon_calculator = CarbonCalculator(
|
||||
steel_database=steel_database.value
|
||||
if isinstance(steel_database, SteelDatabase)
|
||||
else steel_database,
|
||||
timber_database=timber_database.value
|
||||
if isinstance(timber_database, SteelDatabase)
|
||||
else timber_database,
|
||||
steel_database=steel_database,
|
||||
timber_database=timber_database,
|
||||
concrete_database=concrete_database,
|
||||
country=country,
|
||||
custom_reinforcement_rates=reinforcement_rates,
|
||||
)
|
||||
|
||||
def analyze_model(self, model_root) -> dict:
|
||||
@@ -146,7 +156,7 @@ class RevitCarbonAnalyzer:
|
||||
"skipped_elements": [],
|
||||
"errors": [],
|
||||
"total_carbon": 0.0,
|
||||
"missing_factors": {"timber": [], "steel": []},
|
||||
"missing_factors": {"timber": [], "steel": [], "concrete": []},
|
||||
}
|
||||
|
||||
# Process each element
|
||||
@@ -170,9 +180,14 @@ class RevitCarbonAnalyzer:
|
||||
)
|
||||
|
||||
# Get missing factors
|
||||
missing_timber, missing_steel = self.carbon_calculator.get_missing_factors()
|
||||
(
|
||||
missing_timber,
|
||||
missing_steel,
|
||||
missing_concrete,
|
||||
) = self.carbon_calculator.get_missing_factors()
|
||||
results["missing_factors"]["timber"] = missing_timber
|
||||
results["missing_factors"]["steel"] = missing_steel
|
||||
results["missing_factors"]["concrete"] = missing_concrete
|
||||
|
||||
# Log missing factors
|
||||
if missing_timber:
|
||||
@@ -185,6 +200,11 @@ class RevitCarbonAnalyzer:
|
||||
for item in missing_steel:
|
||||
print(f" - {item}")
|
||||
|
||||
if missing_concrete:
|
||||
print(f"Missing concrete factors ({len(missing_concrete)}):")
|
||||
for item in missing_concrete:
|
||||
print(f" - {item}")
|
||||
|
||||
return results
|
||||
|
||||
def _process_single_element(self, element: Dict) -> Dict:
|
||||
@@ -246,17 +266,32 @@ def automate_function(
|
||||
# Get string values from enums if needed
|
||||
steel_db = function_inputs.steel_database
|
||||
timber_db = function_inputs.timber_database
|
||||
concrete_db = function_inputs.concrete_database
|
||||
country = function_inputs.country
|
||||
|
||||
# Ensure we're working with string values
|
||||
if hasattr(steel_db, "value"):
|
||||
steel_db = steel_db.value
|
||||
if hasattr(timber_db, "value"):
|
||||
timber_db = timber_db.value
|
||||
# Create custom reinforcement rates dictionary
|
||||
custom_reinforcement_rates = {
|
||||
"Grade Beam": function_inputs.reinforcement_grade_beam,
|
||||
"Slab on Grade": function_inputs.reinforcement_slab_on_grade,
|
||||
"Pad Footing": function_inputs.reinforcement_pad_footing,
|
||||
"Pile": function_inputs.reinforcement_pile,
|
||||
"Strip Footing": function_inputs.reinforcement_strip_footing,
|
||||
"Pile Cap": function_inputs.reinforcement_pile_cap,
|
||||
"Walls - wind/gravity": function_inputs.reinforcement_gravity_wall,
|
||||
"Column": function_inputs.reinforcement_column,
|
||||
"Shear Walls": function_inputs.reinforcement_shear_wall,
|
||||
"Concrete Slabs": function_inputs.reinforcement_concrete_slab,
|
||||
"Beams": function_inputs.reinforcement_beam,
|
||||
"Topping Slabs": function_inputs.reinforcement_topping_slab,
|
||||
}
|
||||
|
||||
# Initialize analyzer
|
||||
analyzer = RevitCarbonAnalyzer(
|
||||
steel_database=steel_db,
|
||||
timber_database=timber_db,
|
||||
concrete_database=concrete_db,
|
||||
country=country,
|
||||
reinforcement_rates=custom_reinforcement_rates,
|
||||
)
|
||||
|
||||
# Get commit root
|
||||
|
||||
@@ -51,7 +51,10 @@ class CarbonCalculator:
|
||||
|
||||
for material in element.materials:
|
||||
try:
|
||||
result = self._calculate_material_carbon(material)
|
||||
if material.type == MaterialType.CONCRETE:
|
||||
result = self._calculate_concrete_carbon(material, element.category)
|
||||
else:
|
||||
result = self._calculate_material_carbon(material)
|
||||
results[material.properties.name] = result
|
||||
except Exception as e:
|
||||
# Track missing factors
|
||||
@@ -66,6 +69,13 @@ class CarbonCalculator:
|
||||
self._missing_steel_factors.add(
|
||||
material.grade or material.properties.name
|
||||
)
|
||||
elif material.type == MaterialType.CONCRETE:
|
||||
# Track missing concrete factors
|
||||
strength = str(int(material.properties.compressive_strength))
|
||||
element_type = self._map_element_category_to_concrete_type(
|
||||
element.category
|
||||
)
|
||||
self._missing_concrete_factors.add(f"{strength}_{element_type}")
|
||||
|
||||
print(
|
||||
f"Error calculating carbon for {material.properties.name}: {str(e)}"
|
||||
@@ -73,14 +83,20 @@ class CarbonCalculator:
|
||||
|
||||
return results
|
||||
|
||||
def _calculate_material_carbon(self, material: Material) -> CarbonResult:
|
||||
def _calculate_material_carbon(
|
||||
self, material: Material, element_category: Optional[ElementCategory] = None
|
||||
) -> CarbonResult:
|
||||
"""Calculate carbon emissions for a single material."""
|
||||
if material.type == MaterialType.METAL:
|
||||
return self._calculate_metal_carbon(material)
|
||||
elif material.type == MaterialType.WOOD:
|
||||
return self._calculate_wood_carbon(material)
|
||||
elif material.type == MaterialType.CONCRETE:
|
||||
return self._calculate_concrete_carbon(material)
|
||||
if element_category is None:
|
||||
raise ValueError(
|
||||
"Element category is required for concrete carbon calculation"
|
||||
)
|
||||
return self._calculate_concrete_carbon(material, element_category)
|
||||
else:
|
||||
raise ValueError(f"Unsupported material type: {material.type}")
|
||||
|
||||
@@ -135,7 +151,7 @@ class CarbonCalculator:
|
||||
)
|
||||
|
||||
def _calculate_concrete_carbon(
|
||||
self, material: Material, element: BuildingElement
|
||||
self, material: Material, element_category: ElementCategory
|
||||
) -> CarbonResult:
|
||||
"""Calculate carbon emissions for concrete, including reinforcement."""
|
||||
if not material.properties.compressive_strength:
|
||||
@@ -157,7 +173,7 @@ class CarbonCalculator:
|
||||
strength = str(strength_mpa)
|
||||
|
||||
# Map element category to concrete element type for the database
|
||||
element_type = self._map_element_category_to_concrete_type(element)
|
||||
element_type = self._map_element_category_to_concrete_type(element_category)
|
||||
|
||||
# Create cache key for concrete factors
|
||||
concrete_cache_key = f"{strength}_{element_type}"
|
||||
@@ -218,12 +234,10 @@ class CarbonCalculator:
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _map_element_category_to_concrete_type(element: BuildingElement) -> str:
|
||||
def _map_element_category_to_concrete_type(
|
||||
element_category: ElementCategory,
|
||||
) -> str:
|
||||
"""Map BuildingElement category to concrete element type for database lookup."""
|
||||
# Start with the element name
|
||||
element_name = (
|
||||
getattr(element, "name", "").lower() if hasattr(element, "name") else ""
|
||||
)
|
||||
|
||||
# Default mappings
|
||||
category_mapping = {
|
||||
@@ -234,24 +248,13 @@ class CarbonCalculator:
|
||||
ElementCategory.FOUNDATION: "Foundation",
|
||||
}
|
||||
|
||||
# Check for specific element names in BuildingElement
|
||||
if element_name:
|
||||
if "grade beam" in element_name:
|
||||
return "Grade Beam"
|
||||
elif "slab on grade" in element_name:
|
||||
return "Slab on Grade"
|
||||
elif "pad footing" in element_name:
|
||||
return "Foundation" # Or more specific if needed
|
||||
# Add other specific mappings as needed
|
||||
# Return the mapped type or default to "Beam" if unknown
|
||||
return category_mapping.get(element_category, "Beam")
|
||||
|
||||
# Fall back to category mapping
|
||||
return category_mapping.get(
|
||||
element.category, "Beam"
|
||||
) # Default to Beam if unknown
|
||||
|
||||
def get_missing_factors(self) -> Tuple[List[str], List[str]]:
|
||||
def get_missing_factors(self) -> Tuple[List[str], List[str], List[str]]:
|
||||
"""Return lists of materials that had no emission factor."""
|
||||
return (
|
||||
sorted(list(self._missing_timber_factors)),
|
||||
sorted(list(self._missing_steel_factors)),
|
||||
sorted(list(self._missing_concrete_factors)),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user