fix: swallowed exceptions
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled

This commit is contained in:
Björn Steinhagen
2025-02-27 23:04:16 +01:00
parent 53fbfc0139
commit 98341cc99f
5 changed files with 107 additions and 49 deletions
+68 -35
View File
@@ -156,7 +156,7 @@ class RevitCarbonAnalyzer:
results = { results = {
"processed_elements": [], "processed_elements": [],
"skipped_elements": [], "skipped_elements": [],
"warning_elements": [], # For invalid elements "warning_elements": [],
"errors": [], "errors": [],
"total_carbon": 0.0, "total_carbon": 0.0,
"missing_factors": {"timber": [], "steel": [], "concrete": []}, "missing_factors": {"timber": [], "steel": [], "concrete": []},
@@ -243,7 +243,19 @@ class RevitCarbonAnalyzer:
# Calculate carbon # Calculate carbon
try: try:
carbon_results = self.carbon_calculator.calculate_carbon(processed_element) carbon_results, material_errors = self.carbon_calculator.calculate_carbon(
processed_element
)
if not carbon_results:
error_details = "; ".join(
[f"{e['material']}: {e['error']}" for e in material_errors]
)
return {
"id": element_id,
"status": "error",
"reason": f"No carbon could be calculated: {error_details}",
}
# Initialize Embodied Carbon Calculation dictionary # Initialize Embodied Carbon Calculation dictionary
embodied_carbon_data = {} embodied_carbon_data = {}
@@ -265,13 +277,13 @@ class RevitCarbonAnalyzer:
"value": result.database, "value": result.database,
"units": None, "units": None,
}, },
"ecf": { "embodiedCarbonFactor": {
"name": "ecf", "name": "embodiedCarbonFactor",
"value": result.factor, "value": result.factor,
"units": "kgCO₂e/m³", "units": "kgCO₂e/m³",
}, },
"embodied carbon": { "embodiedCarbon": {
"name": "embodied carbon", "name": "embodiedCarbon",
"value": result.total_carbon, "value": result.total_carbon,
"units": "kgCO₂e", "units": "kgCO₂e",
}, },
@@ -279,8 +291,8 @@ class RevitCarbonAnalyzer:
elif result.category == "Concrete": elif result.category == "Concrete":
# For concrete (include both concrete and reinforcement) # For concrete (include both concrete and reinforcement)
material_data = { material_data = {
"volume": { "concreteVolume": {
"name": "volume", "name": "concreteVolume",
"value": result.concrete_volume, "value": result.concrete_volume,
"units": "", "units": "",
}, },
@@ -289,38 +301,38 @@ class RevitCarbonAnalyzer:
"value": result.database, "value": result.database,
"units": None, "units": None,
}, },
"ecf": { "concreteEmbodiedCarbonFactor": {
"name": "ecf", "name": "concreteEmbodiedCarbonFactor",
"value": result.factor, "value": result.factor,
"units": "kgCO₂e/m³", "units": "kgCO₂e/m³",
}, },
"concrete carbon": { "concreteEmbodiedCarbon": {
"name": "concrete carbon", "name": "concreteEmbodiedCarbon",
"value": result.concrete_carbon, "value": result.concrete_carbon,
"units": "kgCO₂e", "units": "kgCO₂e",
}, },
"reinforcement mass": { "reinforcementMass": {
"name": "reinforcement mass", "name": "reinforcementMass",
"value": result.reinforcement_mass, "value": result.reinforcement_mass,
"units": "kg", "units": "kg",
}, },
"reinforcement rate": { "reinforcementRate": {
"name": "reinforcement rate", "name": "reinforcementRate",
"value": result.reinforcement_rate, "value": result.reinforcement_rate,
"units": "kg/m³", "units": "kg/m³",
}, },
"reinforcement ecf": { "reinforcementEmbodiedCarbonFactor": {
"name": "reinforcement ecf", "name": "reinforcementEmbodiedCarbonFactor",
"value": result.reinforcement_factor, "value": result.reinforcement_factor,
"units": "kgCO₂e/kg", "units": "kgCO₂e/kg",
}, },
"reinforcement carbon": { "reinforcementEmbodiedCarbon": {
"name": "reinforcement carbon", "name": "reinforcementEmbodiedCarbon",
"value": result.reinforcement_carbon, "value": result.reinforcement_carbon,
"units": "kgCO₂e", "units": "kgCO₂e",
}, },
"embodied carbon": { "embodiedCarbon": {
"name": "embodied carbon", "name": "embodiedCarbon",
"value": result.total_carbon, "value": result.total_carbon,
"units": "kgCO₂e", "units": "kgCO₂e",
}, },
@@ -338,13 +350,13 @@ class RevitCarbonAnalyzer:
"value": result.database, "value": result.database,
"units": None, "units": None,
}, },
"ecf": { "embodiedCarbonFactor": {
"name": "ecf", "name": "embodiedCarbonFactor",
"value": result.factor, "value": result.factor,
"units": "kgCO₂e/kg", "units": "kgCO₂e/kg",
}, },
"embodied carbon": { "embodiedCarbon": {
"name": "embodied carbon", "name": "embodiedCarbon",
"value": result.total_carbon, "value": result.total_carbon,
"units": "kgCO₂e", "units": "kgCO₂e",
}, },
@@ -357,9 +369,9 @@ class RevitCarbonAnalyzer:
if hasattr(element, "properties"): if hasattr(element, "properties"):
element.properties["Embodied Carbon Calculation"] = embodied_carbon_data element.properties["Embodied Carbon Calculation"] = embodied_carbon_data
return { element_result = {
"id": element_id, "id": element_id,
"status": "processed", "status": "processed" if not material_errors else "warning",
"level": processed_element.level, "level": processed_element.level,
"category": processed_element.category, "category": processed_element.category,
"materials": [ "materials": [
@@ -373,11 +385,21 @@ class RevitCarbonAnalyzer:
"carbon_results": carbon_results, "carbon_results": carbon_results,
"total_carbon": sum(r.total_carbon for r in carbon_results.values()), "total_carbon": sum(r.total_carbon for r in carbon_results.values()),
} }
# If there were material errors, include them in the result
if material_errors:
element_result["material_errors"] = material_errors
element_result[
"reason"
] = f"Issues with {len(material_errors)} materials"
return element_result
except Exception as e: except Exception as e:
return { return {
"id": element_id, "id": element_id,
"status": "error", "status": "error",
"error": f"Carbon calculation failed: {str(e)}", "reason": f"Carbon calculation failed: {str(e)}",
} }
@staticmethod @staticmethod
@@ -485,8 +507,8 @@ def automate_function(
element_id, element_id,
key, key,
"{:0.2f} {}".format( "{:0.2f} {}".format(
value["embodied carbon"]["value"], value["embodiedCarbon"]["value"],
value["embodied carbon"]["units"], value["embodiedCarbon"]["units"],
), ),
] ]
) )
@@ -550,9 +572,9 @@ def automate_function(
) )
# Upload mutated model # Upload mutated model
# automate_context.create_new_version_in_project( automate_context.create_new_version_in_project(
# model_root, f"{commit_root.branchName}_embodied_carbon" model_root, f"{commit_root.branchName}_embodied_carbon"
# ) )
# Mark success with detailed message # Mark success with detailed message
automate_context.mark_run_success(success_message) automate_context.mark_run_success(success_message)
@@ -574,10 +596,21 @@ def _process_automation_results(
"""Process results and attach them to the automation context.""" """Process results and attach them to the automation context."""
# Process each category and attach to objects # Process each category and attach to objects
# Successes # Successes with gradient metadata
if results["processed_elements"]: if results["processed_elements"]:
# Create a dictionary mapping element IDs to their total carbon values
embodied_carbon_values = {}
# Extract the total carbon for each element
for element in results["processed_elements"]:
element_id = element["id"]
# The total carbon is already calculated and stored in each element result
total_carbon = element["total_carbon"]
embodied_carbon_values[element_id] = {"gradientValue": total_carbon}
automate_context.attach_success_to_objects( automate_context.attach_success_to_objects(
category="Carbon Analysis", category="Carbon Analysis",
metadata={"gradient": True, "gradientValues": embodied_carbon_values},
object_ids=[e["id"] for e in results["processed_elements"]], object_ids=[e["id"] for e in results["processed_elements"]],
message="Carbon calculations completed successfully for these elements!", message="Carbon calculations completed successfully for these elements!",
) )
+20 -2
View File
@@ -12,9 +12,27 @@ class MaterialAliasService:
"glue laminated timber", "glue laminated timber",
"glued laminated timber", "glued laminated timber",
"glulam beam", "glulam beam",
"GL36h",
"GL36h(1)",
"GL24h",
"GL28h",
"GL30h",
"GL32h",
"GL36c",
"GL36c(1)",
"GL24c",
"GL28c",
"GL30c",
"GL32c",
"softwood",
], ],
"lvl": ["laminated veneer lumber"], "lvl": ["laminated veneer lumber"],
"softwood lumber": ["dimensional lumber", "sawn lumber", "softwood"], "softwood lumber": [
"dimensional lumber",
"sawn lumber",
"softwood",
"FE_Wood - Dimensional Lumber",
],
"softwood plywood": ["plywood", "softwood ply"], "softwood plywood": ["plywood", "softwood ply"],
"oriented strand board": ["osb", "osb board"], "oriented strand board": ["osb", "osb board"],
"glt/nlt/dlt": [ "glt/nlt/dlt": [
@@ -41,7 +59,7 @@ class MaterialAliasService:
"rebar": ["reinforcing bar", "reinforcement"], "rebar": ["reinforcing bar", "reinforcement"],
"owsj": ["open web steel joist", "steel joist"], "owsj": ["open web steel joist", "steel joist"],
"fasteners": ["bolts", "screws", "nails", "rivets"], "fasteners": ["bolts", "screws", "nails", "rivets"],
"metal deck": ["deck", "decking"], "metal deck": ["deck", "decking", "metal - decking"],
} }
self._concrete_aliases = { self._concrete_aliases = {
+10 -7
View File
@@ -45,9 +45,12 @@ class CarbonCalculator:
self._missing_steel_factors = set() self._missing_steel_factors = set()
self._missing_concrete_factors = set() self._missing_concrete_factors = set()
def calculate_carbon(self, element: BuildingElement) -> Dict[str, CarbonResult]: def calculate_carbon(
"""Calculate carbon emissions for an element's materials.""" self, element: BuildingElement
) -> tuple[Dict[str, CarbonResult], List[Dict[str, str]]]:
"""Calculate carbon emissions for an element's materials and return results and errors."""
results = {} results = {}
errors = []
for material in element.materials: for material in element.materials:
try: try:
@@ -77,11 +80,13 @@ class CarbonCalculator:
) )
self._missing_concrete_factors.add(f"{strength}_{element_type}") self._missing_concrete_factors.add(f"{strength}_{element_type}")
print( # Store error with material name instead of just printing
f"Error calculating carbon for {material.properties.name}: {str(e)}" error_msg = (
f"No emission factor found for {material.properties.name}: {str(e)}"
) )
errors.append({"material": material.properties.name, "error": str(e)})
return results return results, errors
def _calculate_material_carbon( def _calculate_material_carbon(
self, material: Material, element_category: Optional[ElementCategory] = None self, material: Material, element_category: Optional[ElementCategory] = None
@@ -102,8 +107,6 @@ class CarbonCalculator:
def _calculate_metal_carbon(self, material: Material) -> CarbonResult: def _calculate_metal_carbon(self, material: Material) -> CarbonResult:
"""Calculate carbon emissions for metal.""" """Calculate carbon emissions for metal."""
if not material.mass:
raise ValueError("Mass required for metal carbon calculation")
# Get factor from cache or registry # Get factor from cache or registry
if material.grade not in self._steel_factors_cache: if material.grade not in self._steel_factors_cache:
+2 -2
View File
@@ -14,7 +14,7 @@ class ElementProcessor:
"Objects.Geometry.Circle", "Objects.Geometry.Circle",
] ]
SKIP_FAMILIES = ["Grid", "JS_SF_Centerline Only"] SKIP_FAMILIES = ["Grid", "JS_SF_Centerline Only", "none"]
def __init__(self, material_processor: MaterialProcessor, logger: Logging): def __init__(self, material_processor: MaterialProcessor, logger: Logging):
self.material_processor = material_processor self.material_processor = material_processor
@@ -108,7 +108,7 @@ class ElementProcessor:
properties = getattr(element, "properties") properties = getattr(element, "properties")
material_quantities = properties["Material Quantities"] material_quantities = properties["Material Quantities"]
for material_data in material_quantities.values(): # Added .values() for material_data in material_quantities.values():
try: try:
material = self.material_processor.process_material(material_data) material = self.material_processor.process_material(material_data)
materials.append(material) materials.append(material)
+7 -3
View File
@@ -35,9 +35,13 @@ class MaterialProcessor:
"""Process materials with structural assets.""" """Process materials with structural assets."""
if "concrete" in props.name.lower(): if "concrete" in props.name.lower():
return self._process_concrete(props) return self._process_concrete(props)
elif "steel" in props.name.lower(): elif "steel" in props.name.lower() or "metal" in props.name.lower():
return self._process_steel(props) return self._process_steel(props)
elif "clt" in props.name.lower() or "timber" in props.name.lower(): elif (
"clt" in props.name.lower()
or "timber" in props.name.lower()
or "glulam" in props.name.lower()
):
return Material(type=MaterialType.WOOD, properties=props) return Material(type=MaterialType.WOOD, properties=props)
else: else:
raise ValueError(f"Unknown high-grade material: {props.name}") raise ValueError(f"Unknown high-grade material: {props.name}")
@@ -60,7 +64,7 @@ class MaterialProcessor:
mass=mass, mass=mass,
grade="default_steel", grade="default_steel",
) )
elif "clt" in name or "timber" in name: elif "clt" in name or "timber" in name or "wood" in name:
return Material(type=MaterialType.WOOD, properties=props) return Material(type=MaterialType.WOOD, properties=props)
else: else:
raise ValueError(f"Unknown material type: {props.name}") raise ValueError(f"Unknown material type: {props.name}")