Get Structural properties of a material

Hi everyone,

I’m trying to get the structural properties of the material of my revit elements through a Dynamo script (eg. Unit weight, poisson ratio, Young Modulus etc. ). I first thought of using GetParameterByName nodes but the values of the structural parameters are strangely all equal to zero…

On the REVIT API documentation site, I found a code in C# that seems to do what I want to. However I have troubles to translate this code into python in order to insert it in my Dynamo script and everytime I try something I got a different Warning over my Python Script… (Attribute error, “Read only” parameter, “object is not callable”…)

If anyone knows how to translate this C# script into a Python node and could explain me how to do it, that would be awesome!

Thanks in advance for any help :pray:

Arthur

Here is the C# code:

private void ReadMaterialProps(Document document, Material material)
{
   ElementId strucAssetId = material.StructuralAssetId;
   if (strucAssetId != ElementId.InvalidElementId)
   {
      PropertySetElement pse = document.GetElement(strucAssetId) as PropertySetElement;
      if (pse != null)
      {
         StructuralAsset asset = pse.GetStructuralAsset();

         // Check the material behavior and only read if Isotropic
         if (asset.Behavior == StructuralBehavior.Isotropic)
         {
            // Get the class of material
            StructuralAssetClass assetClass = asset.StructuralAssetClass;

            // Get other material properties
            double poisson = asset.PoissonRatio.X;
            double youngMod = asset.YoungModulus.X;
            double thermCoeff = asset.ThermalExpansionCoefficient.X;
            double unitweight = asset.Density;
            double shearMod = asset.ShearModulus.X;
            double dampingRatio = asset.DampingRatio;

            if (assetClass == StructuralAssetClass.Metal)
            {
               double dMinStress = asset.MinimumYieldStress;
            }
            else if (assetClass == StructuralAssetClass.Concrete)
            {
               double dConcComp = asset.ConcreteCompression;
            }
         }
      }
   }
}

Hi @Arthur1, here is a python version of the C# code…

import clr

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript import Geometry as geom

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc =  DocumentManager.Instance.CurrentDBDocument
app = DocumentManager.Instance.CurrentUIApplication.Application

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

def tolist(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]

def ReadMaterialProps(mat):
	arr = []
	assId = mat.StructuralAssetId
	
	if not assId == ElementId.InvalidElementId:
		pse = doc.GetElement(assId)
		if not pse == None:
			ass = pse.GetStructuralAsset()
			if ass.Behavior == StructuralBehavior.Isotropic:
				assClass = ass.StructuralAssetClass	

				arr.append(ass.Name)
				arr.append(ass.PoissonRatio.X)
				arr.append(ass.YoungModulus.X)
				arr.append(ass.ThermalExpansionCoefficient.X)
				arr.append(ass.Density)
				arr.append(ass.ShearModulus.X)
				arr.append(ass.DampingRatio)
			
				if assClass == StructuralAssetClass.Metal:
					arr.append(ass.MinimumYieldStress)
				elif assClass == StructuralAssetClass.Concrete:
					arr.append(ass.ConcreteCompression)
	if not arr == None and len(arr) > 0:					
		return arr

#mats = tolist(UnwrapElement(IN[0]))

# getting all materials in the document... remove this and uncomment the line above to use the IN[0] port...
mats = FilteredElementCollector(doc).OfClass(Material).ToElements()

outList = []

for m in mats:
	outList.append(ReadMaterialProps(m))
	
OUT = outList

Take time to look at the similarities and differences between the C# and python code… they are very similar, but syntax is slightly different. Also, the C# version doesn’t return anything, in my example I am returning an Array with the values (as I assume you want these?)

Cheers,
Dan

3 Likes

@Daniel_Woodcock1 you’re awesome! Thank you so much for answering so quickly!

I think I get the idea of the way to convert it now

Could you also just explain me what is the goal of the tolist function?

Thanks again,

Arthur

@Arthur1, no problem. I know it can be a little confusing at first when most of the examples are written in C# and you need to convert to python! :slight_smile:

Yup, the tolist() function is there to ensure that your input is always a list of objects even if you pass in a single object. For instance, if you plugged in just a single object (material in your case), the for loop would fail as the material is not an iterable object (meaning it’s not a list and you can’t loop through it), the tolist() function checks if the object is not an iterable object and wraps it in a list so it is a list with a single object and we can now loop through it (even though the list length is 1).

Hope that makes sense!

Cheers,
Dan

@Daniel_Woodcock1, yes it makes totally sense now

Thank you a lot for the explanations and for the script :slight_smile:

Arthur

No problem @Arthur1, don’t forget to mark as Solved using the checkbox button. :slight_smile:

Hi , Thank you so much for your code in advance.

I would like to ask the problem of StructuralAssetId property.
now, I want to get the ElementId of a material but why it do not work?
The picture below showing my problem, and the StructuralAssetId get wrong. Or, I misleaded the usage?

Thank you so much

Hi @hctungBHP7B, instead you should write that in Python. Here is a reiteration of the code above in separate nodes, one to get StructualAssetElement and the other to pull out properties…

Material.GetStructuralAsset | Py

import clr

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc =  DocumentManager.Instance.CurrentDBDocument

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

def to_list(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]
	
mats = to_list(UnwrapElement(IN[0]))

OUT = [doc.GetElement(m.StructuralAssetId).GetStructuralAsset() for m in mats if not m.StructuralAssetId == ElementId.InvalidElementId]

StructuralAsset.GetProperties | Py

import clr

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc =  DocumentManager.Instance.CurrentDBDocument

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

def to_list(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]
	
def get_structural_asset_props(sa):

	arr = [sa.Name,
			sa.StructuralAssetClass,
			sa.Behavior,
			sa.Density,
			sa.Lightweight,
			sa.MinimumTensileStrength,
			sa.MinimumYieldStress,
			sa.PoissonRatio,
			sa.ShearModulus,
			sa.ThermalExpansionCoefficient,
			sa.YoungModulus]
	
	saClass = sa.StructuralAssetClass
	
	if saClass == StructuralAssetClass.Concrete:
		arr.append([sa.ConcreteBendingReinforcement,
					sa.ConcreteCompression,
					sa.ConcreteShearReinforcement,
					sa.ConcreteShearStrengthReduction])
					
	if saClass == StructuralAssetClass.Metal:
		arr.append([sa.MetalReductionFactor, sa.MetalResistanceCalculationStrength])
		
	if saClass == StructuralAssetClass.Wood:
		arr.append([sa.WoodBendingStrength,
					sa.WoodGrade,
					sa.WoodParallelCompressionStrength,
					sa.WoodParallelShearStrength,
					sa.WoodPerpendicularCompressionStrength,
					sa.WoodPerpendicularShearStrength,
					sa.WoodSpecies])

	return arr
	
str_ass = to_list(UnwrapElement(IN[0]))

OUT = [get_structural_asset_props(sa) for sa in str_ass]

If you want to get the MaterialId, just use the Element.Id node on the Material. If you want to get the StructuralAssetId, then you can use this code and plug in the material…

Material.GetStructuralAssetID | Py

import clr

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc =  DocumentManager.Instance.CurrentDBDocument

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

def to_list(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]
	
mats = to_list(UnwrapElement(IN[0]))

OUT = [m.StructuralAssetId for m in mats if not m.StructuralAssetId == ElementId.InvalidElementId]

I hope this helps.