REDO10
March 24, 2025, 11:56am
1
Hi All,
When I run the code below, the concrete material is created successfully and listed within the Revit materials library. However, its physical properties are not displayed, even though its StructuralAsset
properties are calculated and set correctly, as shown in the console prints in the image below. What could be the issue?
import clr
import sys
import System
import math
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
class ConcreteMaterial:
"""Represents a concrete material with a specific compressive strength."""
def __init__(self, compressive_strength):
self.compressive_strength = compressive_strength
@property
def name(self):
"""Generate a name for the concrete material based on its compressive strength."""
return "Béton - Coulé sur place - Béton {}".format(self.compressive_strength)
def __hash__(self):
return hash(self.name)
def __eq__(self, other):
return isinstance(other, ConcreteMaterial) and self.name == other.name
def get_or_create_concrete_material(material, doc):
"""Find or create a concrete material with the specified compressive strength."""
materials = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Materials).ToElements()
existing_material = next((m for m in materials if m.Name == material.name), None)
if existing_material:
return existing_material.Id
return _create_concrete_material(material, doc)
def _create_concrete_material(material, doc):
#Create a new concrete material with the specified properties.
Appearence = FilteredElementCollector(doc).OfClass(AppearanceAssetElement).ToElements()
appearId = next((a.Id for a in Appearence if a.GetRenderingAsset().Name == "ConcreteSchema"), None)
patterns = FilteredElementCollector(doc).OfClass(FillPatternElement).ToElements()
filpat = next((p for p in patterns if p.GetFillPattern().Name == "Béton"), None)
with Transaction(doc, "Create Concrete Material") as t:
t.Start()
c = float(material.compressive_strength)
print("Input concrete compressive strength (f_cj): {} MPa".format(c))
material_id = Material.Create(doc, material.name)
new_material = doc.GetElement(material_id)
new_material.MaterialClass = "Béton"
new_material.MaterialCategory = "Béton"
new_material.Color = Color(192, 192, 192)
new_material.Shininess = 128
new_material.Smoothness = 50
# Create and assign structural properties
asset = StructuralAsset("BETON{}".format(material.compressive_strength), StructuralAssetClass.Concrete)
asset.ConcreteCompression = UnitUtils.ConvertToInternalUnits(c, UnitTypeId.Megapascals)
asset.Density = UnitUtils.ConvertToInternalUnits(2500, UnitTypeId.KilogramsPerCubicMeter)
asset.SetPoissonRatio(0.2)
# Calculate and set Young's Modulus
E = 11000 * math.pow(c, 1.0 / 3.0)
print("Young's Modulus (E_ij) calculated: {} MPa".format(E))
asset.SetYoungModulus(UnitUtils.ConvertToInternalUnits(E, UnitTypeId.Megapascals))
# Create the property set and assign it to the material
property_set = PropertySetElement.Create(doc, asset)
new_material.SetMaterialAspectByPropertySet(MaterialAspect.Structural, property_set.Id)
#appearId = concrete_asset.Id
appear = doc.GetElement(appearId)
appear.Name = material.name
print(appear.Name)
new_material.AppearanceAssetId = appear.Id
#filpat = FillPatternElement.Create(doc, filpatId)
#new_material.CutForegroundPatternId = FillPatternElement.GetFillPatternElementByName(doc, FillPatternTarget.Drafting, filpat.Name).Id
new_material.CutForegroundPatternId = filpat.Id
t.Commit()
return material_id
beton_37 = ConcreteMaterial(37)
material_id = get_or_create_concrete_material(beton_37, doc)
OUT = material_id
Thanks.
Haven’t looked at this in detail… but I think what is happening is that you are trying to assign an asset to a material all in one transaction. The material doesn’t exist in the database until the commit of the transaction. So, you can’t assign the asset until it is in database. I think you either need to make the material in one transaction and then assign it the asset in another transaction. Or use a sub-transaction with a doc.Regenerate() to ensure the material is in the database. Just a guess at first glance.
ChatGPT?
REDO10
March 24, 2025, 1:44pm
3
How about AppearanceAssetElement
which is assigned to material in the same transation and it works?
Creating a new material defaults to adding an appearance asset by default. (See the basic UI,)
Note that you can delete a physical and thermal asset - but you cannot delete an appearance asset,
1 Like
REDO10
March 25, 2025, 1:03pm
5
Phew! After several attempts to create separate transactions, I finally resolved my issue!
It was simply a matter of steps order in the code. Apparently, assigning the StructuralAsset
to the material before setting its AppearanceAsset
in the same transaction is not allowed!
As you can see in the image below, I now have both appearance and physical properties!
Here the updated code:
import clr
import sys
import System
import math
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
class ConcreteMaterial:
#Represents a concrete material with a specific compressive strength.
def __init__(self, compressive_strength):
self.compressive_strength = compressive_strength
@property
def name(self):
#Generate a name for the concrete material based on its compressive strength.
return "Béton - Coulé sur place - Béton {}".format(self.compressive_strength)
def __hash__(self):
return hash(self.name)
def __eq__(self, other):
return isinstance(other, ConcreteMaterial) and self.name == other.name
def get_or_create_concrete_material(material, doc):
#Find or create a concrete material with the specified compressive strength.
materials = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Materials).ToElements()
existing_material = next((m for m in materials if m.Name == material.name), None)
if existing_material:
return existing_material.Id
return _create_concrete_material(material, doc)
def _create_concrete_material(material, doc):
#Create a new concrete material with the specified properties.
Appearence = FilteredElementCollector(doc).OfClass(AppearanceAssetElement).ToElements()
appearId = next((a.Id for a in Appearence if a.GetRenderingAsset().Name == "ConcreteSchema"), None)
patterns = FilteredElementCollector(doc).OfClass(FillPatternElement).ToElements()
filpat = next((p for p in patterns if p.GetFillPattern().Name == "Béton"), None)
with Transaction(doc, "Create Concrete Material") as t:
t.Start()
c = float(material.compressive_strength)
material_id = Material.Create(doc, material.name)
new_material = doc.GetElement(material_id)
new_material.MaterialClass = "Concrete"
new_material.MaterialCategory = "Béton"
new_material.Color = Color(192, 192, 192)
new_material.Shininess = 128
new_material.Smoothness = 50
appear = doc.GetElement(appearId)
appear.Name = material.name
new_material.AppearanceAssetId = appear.Id
new_material.CutForegroundPatternId = filpat.Id
# Create and assign structural properties
asset = StructuralAsset("BETON{}".format(material.compressive_strength), StructuralAssetClass.Concrete)
asset.ConcreteCompression = UnitUtils.ConvertToInternalUnits(c, UnitTypeId.Megapascals)
asset.Density = UnitUtils.ConvertToInternalUnits(2500, UnitTypeId.KilogramsPerCubicMeter)
asset.SetPoissonRatio(0.2)
# Calculate and set Young's Modulus
E = 11000 * math.pow(c, 1.0 / 3.0)
asset.SetYoungModulus(UnitUtils.ConvertToInternalUnits(E, UnitTypeId.Megapascals))
# Create the property set and assign it to the material
pse = PropertySetElement.Create(doc, asset)
new_material.SetMaterialAspectByPropertySet(MaterialAspect.Structural, pse.Id)
t.Commit()
return material_id
beton_18 = ConcreteMaterial(18)
material_id = get_or_create_concrete_material(beton_18, doc)
OUT = material_id
Thanks