Material not displaying physical properties issue

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?

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

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