Skip to content

Commit

Permalink
CNX-619 Revit Analytical Panel (#3645)
Browse files Browse the repository at this point in the history
* File Added: ConvertStructuralMaterial

Separated methods pertaining to StructuralMaterial outside of the ConvertAnalyticalStick.cs file. Didn't make sense that these functions were in the ConvertAnalyticalStick.cs when the ConvertAnalyticalSurface.cs referenced them

* ScaleToSpeckle

Material properties were sent as revit internal units. Inconsistent with the Revit model / project units. These can't be used for connection applications (e.g. receiving analytical elements in ETABS)

* ETABS Receive Property2D

ETABS currently only created properties for Element2Ds with a CSIProperty2D, but what about Property2D? These should also be received without us defaulting to the "Slab1" ETABS section.

* ETABS Receive Wall Property

Walls were previously assigned with slab sections which is incorrect. The WallPropertyToNative() was implemented (previously raised a ConversionNotSupportedException for some reason)

* RVT 22 Scaling Updates

Testing on Revit 2022 - ETABS connection

* Default Fallback

Assign at least something to Element2D

* Fixed shared project case sensitivity

* IDE0005

---------

Co-authored-by: Jedd Morgan <[email protected]>
  • Loading branch information
bjoernsteinhagen and JR-Morgan authored Jan 9, 2025
1 parent a737b8e commit 7c3b5b2
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ public void AreaToNative(Element2D area, ApplicationObject appObj)
throw new ConversionException($"There is already a frame object named {area.name} in the model");
}

var propName = CreateOrGetProp(area.property, out bool isExactMatch);
if (!isExactMatch)
var propName = CreateOrGetProp(area.property, out bool isPropertyHandled);
if (!isPropertyHandled)
{
appObj.Update(
logItem: $"Area section for object could not be created and was replaced with section named \"{propName}\""
Expand Down Expand Up @@ -297,7 +297,7 @@ private string CreateAreaFromPoints(IEnumerable<Point> points, string propName)
return name;
}

private string? CreateOrGetProp(Property2D property, out bool isExactMatch)
private string? CreateOrGetProp(Property2D property, out bool isPropertyHandled)
{
int numberNames = 0;
string[] propNames = Array.Empty<string>();
Expand All @@ -308,30 +308,45 @@ private string CreateAreaFromPoints(IEnumerable<Point> points, string propName)
throw new ConversionException("Failed to retrieve the names of all defined area properties");
}

isExactMatch = true;
isPropertyHandled = true;

// Use the property if it already exists in the analytical model
if (propNames.Contains(property?.name))
{
return property.name;
}

if (property is CSIProperty2D prop2D)
// Create detailed property if it is of type CSI and doesn't exist in the analytical model
if (property is CSIProperty2D csiProp2D)
{
try
{
return Property2DToNative(prop2D);
return Property2DToNative(csiProp2D);
}
catch (Exception ex) when (!ex.IsFatal())
{
SpeckleLog.Logger.Error(ex, "Unable to create property2d");
// something failed... replace the type
isPropertyHandled = false;
}
}

isExactMatch = false;
if (propNames.Any())
/* Create the property with information we can use if it is a Property2D (i.e. thickness)
* Furthermore, a property can only be created if it has a name (hence the two conditionals).
* Name is inherited from the physical association in Revit (i.e. it comes from the type name of the family) */
if (property is Property2D structuralProp2D && !string.IsNullOrEmpty(structuralProp2D.name))
{
try
{
return Property2DToNative(structuralProp2D);
}
catch (Exception ex) when (!ex.IsFatal())
{
SpeckleLog.Logger.Error(ex, "Unable to create property2d");
}
}
isPropertyHandled = false;
if (propNames.Length > 0)
{
// TODO: support creating of Property2D
return propNames.First();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CSiAPIv1;
using Objects.Structural.CSI.Analysis;
using Objects.Structural.CSI.Properties;
using Objects.Structural.Properties;

namespace Objects.Converter.CSI;

Expand All @@ -14,6 +15,20 @@ void setProperties(CSIProperty2D property2D, string matProp, double thickeness,
return;
}

private string Property2DToNative(Property2D property2D)
{
// Walls are typically shells (axially loaded)
if (property2D.type == Structural.PropertyType2D.Wall || property2D.type == Structural.PropertyType2D.Shell)
{
return WallPropertyToNative(property2D);
}
// Floors are typically plates (loaded in bending and shear)
else
{
return FloorPropertyToNative(property2D);
}
}

private string Property2DToNative(CSIProperty2D property2D)
{
if (property2D.type2D == CSIPropertyType2D.Wall)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using CSiAPIv1;
using Objects.Structural.CSI.Properties;
using Objects.Structural.Properties;
using Speckle.Core.Kits;

namespace Objects.Converter.CSI;
Expand Down Expand Up @@ -118,6 +119,24 @@ private string FloorSlabPropertyToNative(CSIProperty2D property2D)
return property2D.name;
}

// A Property2D can be created with the basic information we have i.e. thickness and material. No need to default to "Slab1"
public string FloorPropertyToNative(Property2D property2D)
{
var materialName = MaterialToNative(property2D.material);
int success = Model.PropArea.SetSlab(
property2D.name,
eSlabType.Slab,
eShellType.ShellThin,
materialName,
ScaleToNative(property2D.thickness, property2D.units)
);
if (success != 0)
{
throw new ConversionException("Failed to set slab property");
}
return property2D.name;
}

public string FloorPropertyToNative(CSIProperty2D property2D)
{
if (property2D.deckType != Structural.CSI.Analysis.DeckType.Null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
using CSiAPIv1;
using Objects.Structural.CSI.Properties;
using Objects.Structural.Properties;
using Speckle.Core.Kits;

namespace Objects.Converter.CSI;

public partial class ConverterCSI
{
public string WallPropertyToNative(CSIProperty2D Wall)
public string WallPropertyToNative(Property2D property2D)
{
throw new ConversionNotSupportedException("Wall properties are not currently supported on receive");
var materialName = MaterialToNative(property2D.material);
int success = Model.PropArea.SetWall(
property2D.name,
eWallPropType.Specified,
eShellType.ShellThin, // Lateral stability analysis typically has walls as thin shells
materialName,
ScaleToNative(property2D.thickness, property2D.units)
);
if (success != 0)
{
throw new ConversionException("Failed to set wall property");
}
return property2D.name;
}

public CSIProperty2D WallPropertyToSpeckle(string property)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<Compile Include="$(MSBuildThisFileDirectory)PartialClasses\ConvertCombinableElement.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PartialClasses\ConvertRevitElement.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PartialClasses\ConvertArea.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PartialClasses\ConvertStructuralMaterial.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PartialClasses\ConvertToposolid.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PartialClasses\ConvertView.Schedule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PartialClasses\ConvertSpace.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Objects.BuiltElements;
using Objects.BuiltElements.Revit;
using Objects.Structural.Geometry;
using Objects.Structural.Materials;
using Objects.Structural.Properties;
using Objects.Structural.Properties.Profiles;
using Speckle.Core.Models;
Expand Down Expand Up @@ -650,158 +649,4 @@ private void SetStructuralSectionProps(StructuralSection revitSection, SectionPr
//speckleSection["webToeOfFillet"] = u.WebToeOfFillet * scaleFactor; // this is in inches (or mm?) so it needs a different scaleFactor
}
}

private StructuralMaterial GetStructuralMaterial(Material material)
{
if (material == null)
{
return null;
}

StructuralAsset materialAsset = null;
string name = null;
if (material.StructuralAssetId != ElementId.InvalidElementId)
{
materialAsset = (
(PropertySetElement)material.Document.GetElement(material.StructuralAssetId)
).GetStructuralAsset();

name = material.Document.GetElement(material.StructuralAssetId)?.Name;
}
var materialName = material.MaterialClass;
var materialType = GetMaterialType(materialName);

var speckleMaterial = GetStructuralMaterial(materialType, materialAsset, name);
speckleMaterial.applicationId = material.UniqueId;

return speckleMaterial;
}

private StructuralMaterial GetStructuralMaterial(
StructuralMaterialType materialType,
StructuralAsset materialAsset,
string name
)
{
Structural.Materials.StructuralMaterial speckleMaterial = null;

if (materialType == StructuralMaterialType.Undefined && materialAsset != null)
{
materialType = GetMaterialType(materialAsset);
}

name ??= materialType.ToString();
switch (materialType)
{
case StructuralMaterialType.Concrete:
var concreteMaterial = new Concrete { name = name, materialType = Structural.MaterialType.Concrete, };

if (materialAsset != null)
{
concreteMaterial.compressiveStrength = materialAsset.ConcreteCompression; // Newtons per foot meter
concreteMaterial.lightweight = materialAsset.Lightweight;
}

speckleMaterial = concreteMaterial;
break;
case StructuralMaterialType.Steel:
var steelMaterial = new Steel
{
name = name,
materialType = Structural.MaterialType.Steel,
designCode = null,
codeYear = null,
maxStrain = 0,
dampingRatio = 0,
};

if (materialAsset != null)
{
steelMaterial.grade = materialAsset.Name;
steelMaterial.yieldStrength = materialAsset.MinimumYieldStress; // Newtons per foot meter
steelMaterial.ultimateStrength = materialAsset.MinimumTensileStrength; // Newtons per foot meter
}

speckleMaterial = steelMaterial;
break;
case StructuralMaterialType.Wood:
var timberMaterial = new Timber
{
name = name,
materialType = Structural.MaterialType.Timber,
designCode = null,
codeYear = null,
dampingRatio = 0
};

if (materialAsset != null)
{
timberMaterial.grade = materialAsset.WoodGrade;
timberMaterial.species = materialAsset.WoodSpecies;
timberMaterial["bendingStrength"] = materialAsset.WoodBendingStrength;
timberMaterial["parallelCompressionStrength"] = materialAsset.WoodParallelCompressionStrength;
timberMaterial["parallelShearStrength"] = materialAsset.WoodParallelShearStrength;
timberMaterial["perpendicularCompressionStrength"] = materialAsset.WoodPerpendicularCompressionStrength;
timberMaterial["perpendicularShearStrength"] = materialAsset.WoodPerpendicularShearStrength;
}

speckleMaterial = timberMaterial;
break;
default:
var defaultMaterial = new Objects.Structural.Materials.StructuralMaterial { name = name, };
speckleMaterial = defaultMaterial;
break;
}

// TODO: support non-isotropic materials
if (materialAsset != null)
{
// some of these are actually the dumbest units I've ever heard of
speckleMaterial.elasticModulus = materialAsset.YoungModulus.X; // Newtons per foot meter
speckleMaterial.poissonsRatio = materialAsset.PoissonRatio.X; // Unitless
speckleMaterial.shearModulus = materialAsset.ShearModulus.X; // Newtons per foot meter
speckleMaterial.density = materialAsset.Density; // kilograms per cubed feet
speckleMaterial.thermalExpansivity = materialAsset.ThermalExpansionCoefficient.X; // inverse Kelvin
}

return speckleMaterial;
}

private StructuralMaterialType GetMaterialType(string materialName)
{
StructuralMaterialType materialType = StructuralMaterialType.Undefined;
switch (materialName.ToLower())
{
case "concrete":
materialType = StructuralMaterialType.Concrete;
break;
case "steel":
materialType = StructuralMaterialType.Steel;
break;
case "wood":
materialType = StructuralMaterialType.Wood;
break;
}

return materialType;
}

private StructuralMaterialType GetMaterialType(StructuralAsset materialAsset)
{
StructuralMaterialType materialType = StructuralMaterialType.Undefined;
switch (materialAsset?.StructuralAssetClass)
{
case StructuralAssetClass.Metal:
materialType = StructuralMaterialType.Steel;
break;
case StructuralAssetClass.Concrete:
materialType = StructuralMaterialType.Concrete;
break;
case StructuralAssetClass.Wood:
materialType = StructuralMaterialType.Wood;
break;
}

return materialType;
}
}
Loading

0 comments on commit 7c3b5b2

Please sign in to comment.